@moises.ai/design-system 3.6.2 → 3.6.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moises.ai/design-system",
3
- "version": "3.6.2",
3
+ "version": "3.6.4",
4
4
  "description": "Design System package based on @radix-ui/themes with custom defaults",
5
5
  "private": false,
6
6
  "type": "module",
@@ -9,15 +9,55 @@ import {
9
9
  PlusIcon,
10
10
  } from '../../icons'
11
11
  import { DropdownMenu } from '../DropdownMenu/DropdownMenu'
12
- import { useState, useEffect, useRef, useMemo } from 'react'
12
+ import { Tooltip } from '../Tooltip/Tooltip'
13
+ import { useState, useEffect, useRef, useMemo, Fragment } from 'react'
13
14
  import { MoreButton } from '../MoreButton/MoreButton'
14
15
 
16
+ const SetlistIconFallback = () => <SetlistIcon width={16} height={16} />
17
+
18
+ function renderSetlistAvatar(icon) {
19
+ if (!icon) return <SetlistIconFallback />
20
+ if (typeof icon === 'string') {
21
+ return (
22
+ <Avatar
23
+ src={icon}
24
+ fallback={<SetlistIconFallback />}
25
+ size="3"
26
+ radius="small"
27
+ />
28
+ )
29
+ }
30
+ if (Array.isArray(icon)) {
31
+ if (icon.length === 0) return <SetlistIconFallback />
32
+ if (icon.length >= 4) {
33
+ const urls = icon.slice(0, 4)
34
+ return (
35
+ <div className={styles.iconGrid}>
36
+ {urls.map((src, i) => (
37
+ <div key={i} className={styles.iconGridCell}>
38
+ <img src={src} alt="" />
39
+ </div>
40
+ ))}
41
+ </div>
42
+ )
43
+ }
44
+ return (
45
+ <Avatar
46
+ src={icon[0]}
47
+ fallback={<SetlistIconFallback />}
48
+ size="3"
49
+ radius="small"
50
+ />
51
+ )
52
+ }
53
+ return icon
54
+ }
55
+
15
56
  export const SetlistItem = ({
16
57
  isSelected,
17
58
  onClick,
18
59
  className,
19
60
  style,
20
- avatarStyle,
21
61
  avatar,
22
62
  text,
23
63
  subtitle,
@@ -77,14 +117,13 @@ export const SetlistItem = ({
77
117
  })}
78
118
  >
79
119
  {avatar && (
80
- <div className={styles.avatarSetlist} style={avatarStyle}>
120
+ <div className={styles.avatarSetlist}>
81
121
  {typeof avatar === 'string' ? (
82
122
  <Avatar
83
123
  src={avatar}
84
124
  fallback={<SetlistIcon width={16} height={16} />}
85
- // size="3"
125
+ size="3"
86
126
  radius="small"
87
- style={avatarStyle}
88
127
  />
89
128
  ) : (
90
129
  avatar
@@ -151,12 +190,13 @@ export const SetlistList = ({
151
190
  }) => {
152
191
  const [openSetlistMenuId, setOpenSetlistMenuId] = useState(null)
153
192
  const [isHoveredWhenCollapsed, setIsHoveredWhenCollapsed] = useState(false)
154
- const [isFullyCollapsed, setIsFullyCollapsed] = useState(false)
193
+ const [isShrinking, setIsShrinking] = useState(false)
155
194
  const hoverTimeoutRef = useRef(null)
156
- const collapseCenterTimeoutRef = useRef(null)
195
+ const shrinkTimeoutRef = useRef(null)
157
196
  const hoverRegionRef = useRef(null)
197
+ const shrinkDuration = 260
158
198
  const collapsedItemHeight = 44
159
- const collapsedVisibleOffset = 8
199
+ const collapsedVisibleOffset = 4
160
200
  const maxCollapsedItems = 4
161
201
  const collapsedNewItemOffset = collapsed && onNewSetlistClick ? 1 : 0
162
202
  const showCollapsedStack = collapsed && !isHoveredWhenCollapsed
@@ -170,6 +210,11 @@ export const SetlistList = ({
170
210
  clearTimeout(hoverTimeoutRef.current)
171
211
  hoverTimeoutRef.current = null
172
212
  }
213
+ if (shrinkTimeoutRef.current) {
214
+ clearTimeout(shrinkTimeoutRef.current)
215
+ shrinkTimeoutRef.current = null
216
+ }
217
+ setIsShrinking(false)
173
218
  setIsHoveredWhenCollapsed(true)
174
219
  }
175
220
  const handleMouseLeave = () => {
@@ -178,8 +223,13 @@ export const SetlistList = ({
178
223
  clearTimeout(hoverTimeoutRef.current)
179
224
  }
180
225
  hoverTimeoutRef.current = setTimeout(() => {
181
- setIsHoveredWhenCollapsed(false)
182
226
  hoverTimeoutRef.current = null
227
+ setIsShrinking(true)
228
+ shrinkTimeoutRef.current = setTimeout(() => {
229
+ shrinkTimeoutRef.current = null
230
+ setIsHoveredWhenCollapsed(false)
231
+ setIsShrinking(false)
232
+ }, shrinkDuration)
183
233
  }, 180)
184
234
  }
185
235
 
@@ -198,31 +248,10 @@ export const SetlistList = ({
198
248
  }, 0)
199
249
  }
200
250
 
201
- useEffect(() => {
202
- if (showCollapsedStack) {
203
- collapseCenterTimeoutRef.current = setTimeout(() => {
204
- setIsFullyCollapsed(true)
205
- collapseCenterTimeoutRef.current = null
206
- }, 200)
207
- } else {
208
- setIsFullyCollapsed(false)
209
- if (collapseCenterTimeoutRef.current) {
210
- clearTimeout(collapseCenterTimeoutRef.current)
211
- collapseCenterTimeoutRef.current = null
212
- }
213
- }
214
- return () => {
215
- if (collapseCenterTimeoutRef.current) {
216
- clearTimeout(collapseCenterTimeoutRef.current)
217
- }
218
- }
219
- }, [showCollapsedStack])
220
-
221
251
  useEffect(() => {
222
252
  return () => {
223
- if (hoverTimeoutRef.current) {
224
- clearTimeout(hoverTimeoutRef.current)
225
- }
253
+ if (hoverTimeoutRef.current) clearTimeout(hoverTimeoutRef.current)
254
+ if (shrinkTimeoutRef.current) clearTimeout(shrinkTimeoutRef.current)
226
255
  }
227
256
  }, [])
228
257
 
@@ -248,9 +277,7 @@ export const SetlistList = ({
248
277
  </div>
249
278
  )
250
279
 
251
- const collapsedStackAvatarSizes = ['38px', '37px', '36px', '35px']
252
-
253
- const getCollapsedStackStyle = (index, setlist) => {
280
+ const getCollapsedStackStyle = (index) => {
254
281
  if (!collapsed) return undefined
255
282
  if (!showCollapsedStack) {
256
283
  return {
@@ -301,8 +328,8 @@ export const SetlistList = ({
301
328
  direction="column"
302
329
  className={classNames(styles.setlistsContent, {
303
330
  [styles.collapsedStack]: showCollapsedStack,
304
- [styles.collapsedStackCentered]: isFullyCollapsed,
305
331
  [styles.collapsedMask]: collapsed,
332
+ [styles.collapsedShrinking]: isShrinking,
306
333
  })}
307
334
  >
308
335
  {onNewSetlistClick &&
@@ -336,24 +363,15 @@ export const SetlistList = ({
336
363
  ))}
337
364
  {visibleSetlists.map((setlist, index) => {
338
365
  const isSelected = selectedSetlistId === setlist.id
339
- return (
366
+ const setlistItem = (
340
367
  <SetlistItem
341
- key={setlist.id}
342
368
  isSelected={isSelected}
343
369
  onClick={() => onSetlistClick?.(setlist.id)}
344
370
  dropdownMenuOpen={openSetlistMenuId === setlist.id}
345
371
  onDropdownMenuOpenChange={(nextOpen) => {
346
372
  setOpenSetlistMenuId(nextOpen ? setlist.id : null)
347
373
  }}
348
- avatar={setlist.icon || <SetlistIcon width={16} height={16} />}
349
- avatarStyle={
350
- showCollapsedStack
351
- ? {
352
- width: collapsedStackAvatarSizes[index],
353
- height: collapsedStackAvatarSizes[index],
354
- }
355
- : undefined
356
- }
374
+ avatar={renderSetlistAvatar(setlist.icon)}
357
375
  group={setlist.group}
358
376
  moises={setlist.moises}
359
377
  text={setlist.label}
@@ -365,9 +383,18 @@ export const SetlistList = ({
365
383
  [styles.collapsedStackItem]: showCollapsedStack,
366
384
  [styles.collapsedTransition]: collapsed,
367
385
  })}
368
- style={getCollapsedStackStyle(index, setlist)}
386
+ style={getCollapsedStackStyle(index)}
369
387
  />
370
388
  )
389
+ return collapsed && !showCollapsedStack ? (
390
+ <Tooltip key={setlist.id} content={setlist.label} side="right">
391
+ <div className={styles.tooltipTriggerWrapper}>
392
+ {setlistItem}
393
+ </div>
394
+ </Tooltip>
395
+ ) : (
396
+ <Fragment key={setlist.id}>{setlistItem}</Fragment>
397
+ )
371
398
  })}
372
399
  </Flex>
373
400
  </div>
@@ -1,3 +1,8 @@
1
+ .tooltipTriggerWrapper {
2
+ display: block;
3
+ width: 100%;
4
+ }
5
+
1
6
  .newSetlistItem {
2
7
  cursor: pointer;
3
8
  margin: 0 12px;
@@ -85,6 +90,30 @@
85
90
  z-index: 1;
86
91
  }
87
92
 
93
+ .iconGrid {
94
+ display: grid;
95
+ grid-template-columns: 1fr 1fr;
96
+ grid-template-rows: 1fr 1fr;
97
+ width: 100%;
98
+ height: 100%;
99
+ border-radius: 4px;
100
+ overflow: hidden;
101
+ background: var(--neutral-2);
102
+ }
103
+
104
+ .iconGridCell {
105
+ position: relative;
106
+ overflow: hidden;
107
+ }
108
+
109
+ .iconGridCell img {
110
+ position: absolute;
111
+ inset: 0;
112
+ width: 100%;
113
+ height: 100%;
114
+ object-fit: cover;
115
+ }
116
+
88
117
  .textNewSetlist {
89
118
  max-width: 115px;
90
119
  opacity: 1;
@@ -196,9 +225,6 @@
196
225
  gap: 0;
197
226
  }
198
227
 
199
- .collapsedStackCentered {
200
- align-items: center;
201
- }
202
228
  .collapsedStackItem {
203
229
  position: relative;
204
230
  /* justify-content: flex-start; */
@@ -209,7 +235,13 @@
209
235
  overflow: hidden;
210
236
  padding-top: 2px;
211
237
  display: flex;
212
-
238
+ max-height: 80vh;
239
+ transition: max-height 260ms ease-in-out;
240
+ }
241
+
242
+ .collapsedMask.collapsedStack,
243
+ .collapsedMask.collapsedShrinking {
244
+ max-height: 120px;
213
245
  }
214
246
 
215
247
  /* .collapsedStack.collapsedMask {
@@ -217,7 +249,7 @@
217
249
  } */
218
250
 
219
251
  .collapsedTransition {
220
- transition: margin-top 360ms ease-in-out;
252
+ transition: margin-top 260ms ease-in-out;
221
253
  }
222
254
 
223
255
  .contentInner {
@@ -1,6 +1,164 @@
1
1
  import { useState } from 'react'
2
2
  import { SetlistList } from './SetlistList'
3
- import { MusicNoteIcon } from '../../icons'
3
+
4
+ const defaultSetlists = [
5
+ {
6
+ id: 'b55ae387-6f5e-44e9-af9f-c05e0438db69',
7
+ label: 'Jam Sessions with Charlie Puth',
8
+ description:
9
+ 'Submit your cover or remix of "Beat Yourself Up" for a chance to win your share of $100,000* in prizes, including cash, music gear, and an all-expenses-paid NYC trip to meet Charlie backstage at Madison Square Garden.',
10
+ subtitle: 'By Moises',
11
+ moises: true,
12
+ icon: 'https://storage.googleapis.com/moises-stage-api-assets/setlists/jam-sessions-charlie-puth-setlist-title/logo-1770055262172-charlie-puth-cover-132x132-3x-png',
13
+ group: false,
14
+ dropdownMenuOptions: [
15
+ {
16
+ type: 'item',
17
+ key: 'leave-setlist',
18
+ label: 'Leave setlist',
19
+ color: 'red',
20
+ },
21
+ ],
22
+ },
23
+ {
24
+ id: '00fc53a1-da6f-4b9c-afc0-64e40892d32a',
25
+ label: 'track, master and voice',
26
+ description: '',
27
+ subtitle: 'By testeqajp265',
28
+ moises: false,
29
+ icon: [
30
+ 'https://d2-stage.moises.ai/v3/download/moises-stage--tasks/operations/FILEMETADATA_A/bf262dfd-8fe5-4e2a-8baa-ca8971690f62/cover.jpeg',
31
+ 'https://d2-stage.moises.ai/v3/download/moises-stage--tasks/operations/FILEMETADATA_A/62b4b14d-b81f-4ef4-b681-bf6c863f9db8/cover.jpeg',
32
+ 'https://d2-stage.moises.ai/v3/download/moises-stage--tasks/operations/FILEMETADATA_A/5db13e39-a0ac-4251-a4b5-4edb35f6b4a2/cover.jpeg',
33
+ 'https://d3-stage.moises.ai/v3/download/moises-stage--tasks/operations/FILEMETADATA_A/9315c6df-aa12-4265-bb6d-594f638d8a63/cover.jpeg',
34
+ ],
35
+ group: true,
36
+ dropdownMenuOptions: [
37
+ {
38
+ type: 'item',
39
+ key: 'leave-setlist',
40
+ label: 'Leave setlist',
41
+ color: 'red',
42
+ },
43
+ ],
44
+ },
45
+ {
46
+ id: '15c1b18e-a0b8-439a-9480-78660562173b',
47
+ label: 'Minha setlist #532',
48
+ description: '',
49
+ subtitle: 'By Swanny Kathllen',
50
+ moises: false,
51
+ icon: [
52
+ 'https://d2-stage.moises.ai/v3/download/moises-stage--tasks-us-east1/operations/FILEMETADATA_A/8e5ec2e6-61f4-4721-9ed0-c31fda5904ab/cover.jpeg',
53
+ 'https://d2-stage.moises.ai/v3/download/moises-stage--tasks/operations/FILEMETADATA_A/b4112cfb-3f37-4551-bd42-8ce9fcaec2b0/cover.jpeg',
54
+ 'https://d1-stage.moises.ai/v3/download/moises-stage--tasks/operations/FILEMETADATA_A/2e6d2a5f-d9c8-4750-bf9e-12f5d0b43f1d/cover.jpeg',
55
+ ],
56
+ group: false,
57
+ dropdownMenuOptions: [
58
+ { type: 'item', key: 'edit', label: 'Edit' },
59
+ { type: 'separator', key: 'separator-edit' },
60
+ { type: 'item', key: 'delete', label: 'Delete', color: 'red' },
61
+ ],
62
+ },
63
+ {
64
+ id: '85aabd11-4bfe-4f5e-814a-c450fa3695fd',
65
+ label: 'My Setlist #4',
66
+ description: '',
67
+ subtitle: 'By Swanny Kathllen',
68
+ moises: false,
69
+ icon: [],
70
+ group: true,
71
+ dropdownMenuOptions: [
72
+ {
73
+ type: 'item',
74
+ key: 'leave-setlist',
75
+ label: 'Leave setlist',
76
+ color: 'red',
77
+ },
78
+ ],
79
+ },
80
+ {
81
+ id: 'd234cc21-b271-4f11-af53-73fdc685e83b',
82
+ label: 'My Setlist #3',
83
+ description: '',
84
+ subtitle: 'By Swanny Kathllen',
85
+ moises: false,
86
+ group: false,
87
+ dropdownMenuOptions: [
88
+ { type: 'item', key: 'edit', label: 'Edit' },
89
+ { type: 'separator', key: 'separator-edit' },
90
+ { type: 'item', key: 'delete', label: 'Delete', color: 'red' },
91
+ ],
92
+ },
93
+ {
94
+ id: 'd7fe60bb-261e-4f55-89c3-80cffcb018d1',
95
+ label: 'playlist de outro user [não editável]',
96
+ description: '',
97
+ subtitle: 'By Swanny Kathllen',
98
+ moises: false,
99
+ group: true,
100
+ dropdownMenuOptions: [
101
+ {
102
+ type: 'item',
103
+ key: 'leave-setlist',
104
+ label: 'Leave setlist',
105
+ color: 'red',
106
+ },
107
+ ],
108
+ },
109
+ {
110
+ id: '0a3668ac-24f0-4363-a018-5ca9b1d22cfe',
111
+ label: 'Playlist de outro usuário [editável]',
112
+ description: '',
113
+ subtitle: 'By Swanny Kathllen',
114
+ moises: false,
115
+ group: true,
116
+ dropdownMenuOptions: [
117
+ {
118
+ type: 'item',
119
+ key: 'leave-setlist',
120
+ label: 'Leave setlist',
121
+ color: 'red',
122
+ },
123
+ ],
124
+ },
125
+ {
126
+ id: 'df74975a-fbfe-4000-9ce1-d02d998ecd7f',
127
+ label: 'Guitar Exercises',
128
+ description:
129
+ 'Improve your chops with these Berklee Online guitar exercises, pulled from actual courses.',
130
+ subtitle: 'By Berklee Online',
131
+ moises: true,
132
+ icon: 'https://storage.googleapis.com/moises-api-assets/setlists/berklee2.png',
133
+ group: false,
134
+ dropdownMenuOptions: [
135
+ {
136
+ type: 'item',
137
+ key: 'leave-setlist',
138
+ label: 'Leave setlist',
139
+ color: 'red',
140
+ },
141
+ ],
142
+ },
143
+ {
144
+ id: '66d16b7e-2dc0-46e9-8706-56b7d4133104',
145
+ label: 'Moises Collection',
146
+ description:
147
+ 'Songs written and recorded by the Moises community. For demo purposes only.',
148
+ subtitle: 'By Moises',
149
+ moises: true,
150
+ icon: 'https://storage.googleapis.com/moises-api-assets/moises-collection-cover.png',
151
+ group: true,
152
+ dropdownMenuOptions: [
153
+ {
154
+ type: 'item',
155
+ key: 'leave-setlist',
156
+ label: 'Leave setlist',
157
+ color: 'red',
158
+ },
159
+ ],
160
+ },
161
+ ]
4
162
 
5
163
  export default {
6
164
  title: 'Components/SetlistList',
@@ -14,12 +172,14 @@ export default {
14
172
 
15
173
  export const Default = {
16
174
  render: (args) => {
17
- const [selectedSetlistId, setSelectedSetlistId] =
18
- useState('piano-exercises')
175
+ const [selectedSetlistId, setSelectedSetlistId] = useState(
176
+ defaultSetlists[0].id
177
+ )
19
178
 
20
179
  return (
21
180
  <SetlistList
22
181
  {...args}
182
+ setlists={defaultSetlists}
23
183
  selectedSetlistId={selectedSetlistId}
24
184
  onSetlistClick={(id) => {
25
185
  setSelectedSetlistId(id)
@@ -32,63 +192,7 @@ export const Default = {
32
192
  )
33
193
  },
34
194
  args: {
35
- setlists: [
36
- {
37
- id: 'piano-exercises',
38
- label: 'Piano Exercises',
39
- subtitle: 'By Berklee',
40
- moises: true,
41
- icon: 'https://storage.googleapis.com/moises-api-assets/setlists/jamsession_cory/cover.png',
42
- dropdownMenuOptions: [
43
- {
44
- type: 'item',
45
- key: 'edit',
46
- label: 'Edit',
47
- onClick: () => console.log('Edit clicked'),
48
- },
49
- ],
50
- },
51
- {
52
- id: 'bossa-nova',
53
- label: 'Bossa Nova Essentials',
54
- dropdownMenuOptions: [
55
- {
56
- type: 'item',
57
- key: 'edit',
58
- label: 'Edit',
59
- onClick: () => console.log('Edit clicked'),
60
- },
61
- ],
62
- },
63
- {
64
- id: 'band-rehearsal',
65
- label: 'Band Rehearsal',
66
- subtitle: 'By Nickyz dsdasdas',
67
- moises: true,
68
- group: true,
69
- dropdownMenuOptions: [
70
- {
71
- type: 'item',
72
- key: 'edit',
73
- label: 'Edit',
74
- onClick: () => console.log('Edit clicked'),
75
- },
76
- ],
77
- },
78
- {
79
- id: 'gig-dec-21',
80
- label: 'Gig Dec 21th',
81
- icon: <MusicNoteIcon width={16} height={16} />,
82
- dropdownMenuOptions: [
83
- {
84
- type: 'item',
85
- key: 'edit',
86
- label: 'Edit',
87
- onClick: () => console.log('Edit clicked'),
88
- },
89
- ],
90
- },
91
- ],
195
+ setlists: defaultSetlists,
92
196
  collapsed: false,
93
197
  isMobile: false,
94
198
  },
@@ -0,0 +1,67 @@
1
+ import * as TooltipPrimitive from '@radix-ui/react-tooltip'
2
+ import styles from './Tooltip.module.css'
3
+ import classNames from 'classnames'
4
+
5
+ export const Tooltip = ({
6
+ children,
7
+ content,
8
+ shortcut,
9
+ color = 'gray',
10
+ delayDuration = 200,
11
+ skipDelayDuration,
12
+ disableHoverableContent,
13
+ ...props
14
+ }) => {
15
+
16
+ const {
17
+ defaultOpen,
18
+ open,
19
+ onOpenChange,
20
+ ...restContentProps
21
+ } = props
22
+
23
+ const providerProps = {
24
+ delayDuration,
25
+ skipDelayDuration,
26
+ disableHoverableContent,
27
+ }
28
+
29
+ const rootProps = {
30
+ defaultOpen,
31
+ open,
32
+ onOpenChange,
33
+ }
34
+
35
+ return (
36
+ <TooltipPrimitive.Provider {...providerProps}>
37
+ <TooltipPrimitive.Root {...rootProps}>
38
+ <TooltipPrimitive.Trigger asChild>
39
+ {children}
40
+ </TooltipPrimitive.Trigger>
41
+ <TooltipPrimitive.Portal>
42
+ <TooltipPrimitive.Content
43
+ className={classNames(
44
+ styles.TooltipContent,
45
+ styles[`TooltipContent--${color}`]
46
+ )}
47
+ sideOffset={5}
48
+ {...restContentProps}
49
+ >
50
+ <span className={styles.TooltipText}>{content}</span>
51
+ {shortcut && (
52
+ <span className={classNames(
53
+ styles.TooltipShortcut,
54
+ styles[`TooltipShortcut--${color}`]
55
+ )}>
56
+ {shortcut}
57
+ </span>
58
+ )}
59
+ <TooltipPrimitive.Arrow className={styles.TooltipArrow} />
60
+ </TooltipPrimitive.Content>
61
+ </TooltipPrimitive.Portal>
62
+ </TooltipPrimitive.Root>
63
+ </TooltipPrimitive.Provider>
64
+ )
65
+ }
66
+
67
+ Tooltip.displayName = 'Tooltip'