@moises.ai/design-system 3.6.3 → 3.6.5

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.3",
3
+ "version": "3.6.5",
4
4
  "description": "Design System package based on @radix-ui/themes with custom defaults",
5
5
  "private": false,
6
6
  "type": "module",
@@ -1,12 +1,15 @@
1
1
  import { Flex, Text } from '@radix-ui/themes'
2
2
  import styles from './AdditionalItems.module.css'
3
3
  import classNames from 'classnames'
4
+ import { useMobileDrawerContext } from '../../contexts/MobileDrawerContext'
4
5
 
5
6
  export const AdditionalItems = ({
6
7
  additionalItems,
7
8
  collapsed = false,
8
9
  selectedAdditionalItemId,
9
10
  }) => {
11
+ const { close, isMobile } = useMobileDrawerContext()
12
+
10
13
  if (!additionalItems) return null
11
14
  return (
12
15
  <Flex direction="column">
@@ -15,6 +18,10 @@ export const AdditionalItems = ({
15
18
  selectedAdditionalItemId !== undefined
16
19
  ? selectedAdditionalItemId === item.id
17
20
  : item.selected
21
+ const handleClick = () => {
22
+ item.onClick?.()
23
+ if (isMobile) close()
24
+ }
18
25
  return (
19
26
  <Flex
20
27
  key={item.id || index}
@@ -25,11 +32,11 @@ export const AdditionalItems = ({
25
32
  [styles.upgradeItemCollapsed]: collapsed,
26
33
  [styles.upgradeItemSelected]: isSelected,
27
34
  })}
28
- onClick={item.onClick}
35
+ onClick={handleClick}
29
36
  onKeyDown={(e) => {
30
37
  if ((e.key === 'Enter' || e.key === ' ') && item.onClick) {
31
38
  e.preventDefault()
32
- item.onClick()
39
+ handleClick()
33
40
  }
34
41
  }}
35
42
  >
@@ -1,6 +1,7 @@
1
1
  import { Flex, Text, Badge } from '@radix-ui/themes'
2
2
  import styles from './ProductsList.module.css'
3
3
  import classNames from 'classnames'
4
+ import { useMobileDrawerContext } from '../../contexts/MobileDrawerContext'
4
5
 
5
6
  export const ProductsList = ({
6
7
  products = [],
@@ -8,6 +9,13 @@ export const ProductsList = ({
8
9
  onProductClick,
9
10
  collapsed,
10
11
  }) => {
12
+ const { close, isMobile } = useMobileDrawerContext()
13
+
14
+ const handleProductClick = (product) => {
15
+ onProductClick?.(product.id, product)
16
+ if (isMobile) close()
17
+ }
18
+
11
19
  return (
12
20
  <Flex direction="column">
13
21
  {products.map((product) => {
@@ -24,11 +32,11 @@ export const ProductsList = ({
24
32
  [styles.navItemSelected]: isSelected,
25
33
  [styles.collapsed]: collapsed,
26
34
  })}
27
- onClick={() => onProductClick?.(product.id, product)}
35
+ onClick={() => handleProductClick(product)}
28
36
  onKeyDown={(e) => {
29
37
  if (e.key === 'Enter' || e.key === ' ') {
30
38
  e.preventDefault()
31
- onProductClick?.(product.id, product)
39
+ handleProductClick(product)
32
40
  }
33
41
  }}
34
42
  >
@@ -5,24 +5,32 @@ import { useIsMobileViewport } from '../../utils/useIsMobileViewport'
5
5
  import { DropdownMenu } from '../DropdownMenu/DropdownMenu'
6
6
  import { MenuTrigger } from './MenuTrigger'
7
7
  import styles from './ProfileMenu.module.css'
8
+ import { useMobileDrawerContext } from '../../contexts/MobileDrawerContext'
8
9
 
9
10
  export const ProfileMenu = ({
10
11
  className,
11
12
  user,
12
13
  menuOptions = [],
13
14
  collapsed,
15
+ onSelectionChange,
14
16
  ...props
15
17
  }) => {
16
- const isMobile = useIsMobileViewport()
18
+ const isMobileViewport = useIsMobileViewport()
19
+ const { close, isMobile } = useMobileDrawerContext()
17
20
 
21
+ const handleSelectionChange = (option) => {
22
+ onSelectionChange?.(option)
23
+ if (isMobile) close()
24
+ }
18
25
 
19
26
  return (
20
27
  <DropdownMenu
21
28
  className={classNames(styles.profileMenu, className)}
22
29
  trigger={<MenuTrigger user={user} collapsed={collapsed} />}
23
30
  options={menuOptions}
24
- side={isMobile ? 'left' : 'right'}
31
+ side={isMobileViewport ? 'top' : 'right'}
25
32
  {...props}
33
+ onSelectionChange={handleSelectionChange}
26
34
  />
27
35
  )
28
36
  }
@@ -12,13 +12,53 @@ import { DropdownMenu } from '../DropdownMenu/DropdownMenu'
12
12
  import { Tooltip } from '../Tooltip/Tooltip'
13
13
  import { useState, useEffect, useRef, useMemo, Fragment } from 'react'
14
14
  import { MoreButton } from '../MoreButton/MoreButton'
15
+ import { useMobileDrawerContext } from '../../contexts/MobileDrawerContext'
16
+
17
+ const SetlistIconFallback = () => <SetlistIcon width={16} height={16} />
18
+
19
+ function renderSetlistAvatar(icon) {
20
+ if (!icon) return <SetlistIconFallback />
21
+ if (typeof icon === 'string') {
22
+ return (
23
+ <Avatar
24
+ src={icon}
25
+ fallback={<SetlistIconFallback />}
26
+ size="3"
27
+ radius="small"
28
+ />
29
+ )
30
+ }
31
+ if (Array.isArray(icon)) {
32
+ if (icon.length === 0) return <SetlistIconFallback />
33
+ if (icon.length >= 4) {
34
+ const urls = icon.slice(0, 4)
35
+ return (
36
+ <div className={styles.iconGrid}>
37
+ {urls.map((src, i) => (
38
+ <div key={i} className={styles.iconGridCell}>
39
+ <img src={src} alt="" />
40
+ </div>
41
+ ))}
42
+ </div>
43
+ )
44
+ }
45
+ return (
46
+ <Avatar
47
+ src={icon[0]}
48
+ fallback={<SetlistIconFallback />}
49
+ size="3"
50
+ radius="small"
51
+ />
52
+ )
53
+ }
54
+ return icon
55
+ }
15
56
 
16
57
  export const SetlistItem = ({
17
58
  isSelected,
18
59
  onClick,
19
60
  className,
20
61
  style,
21
- avatarStyle,
22
62
  avatar,
23
63
  text,
24
64
  subtitle,
@@ -29,6 +69,7 @@ export const SetlistItem = ({
29
69
  moises = false,
30
70
  collapsed = false,
31
71
  isMobile = false,
72
+ onAvatarClick,
32
73
  }) => {
33
74
  const [isHovering, setIsHovering] = useState(false)
34
75
 
@@ -78,14 +119,38 @@ export const SetlistItem = ({
78
119
  })}
79
120
  >
80
121
  {avatar && (
81
- <div className={styles.avatarSetlist} style={avatarStyle}>
122
+ <div
123
+ className={classNames(styles.avatarSetlist, {
124
+ [styles.avatarClickable]: onAvatarClick,
125
+ })}
126
+ onClick={
127
+ onAvatarClick
128
+ ? (e) => {
129
+ e.stopPropagation()
130
+ onAvatarClick(e)
131
+ }
132
+ : undefined
133
+ }
134
+ role={onAvatarClick ? 'button' : undefined}
135
+ tabIndex={onAvatarClick ? 0 : undefined}
136
+ onKeyDown={
137
+ onAvatarClick
138
+ ? (e) => {
139
+ if (e.key === 'Enter' || e.key === ' ') {
140
+ e.preventDefault()
141
+ e.stopPropagation()
142
+ onAvatarClick(e)
143
+ }
144
+ }
145
+ : undefined
146
+ }
147
+ >
82
148
  {typeof avatar === 'string' ? (
83
149
  <Avatar
84
150
  src={avatar}
85
151
  fallback={<SetlistIcon width={16} height={16} />}
86
- // size="3"
152
+ size="3"
87
153
  radius="small"
88
- style={avatarStyle}
89
154
  />
90
155
  ) : (
91
156
  avatar
@@ -150,14 +215,26 @@ export const SetlistList = ({
150
215
  collapsed = false,
151
216
  isMobile = false,
152
217
  }) => {
218
+ const { close, isMobile: isMobileDrawer } = useMobileDrawerContext()
153
219
  const [openSetlistMenuId, setOpenSetlistMenuId] = useState(null)
220
+
221
+ const handleSetlistClick = (setlistId) => {
222
+ onSetlistClick?.(setlistId)
223
+ if (isMobileDrawer) close()
224
+ }
225
+
226
+ const handleNewSetlistClick = () => {
227
+ onNewSetlistClick?.()
228
+ if (isMobileDrawer) close()
229
+ }
154
230
  const [isHoveredWhenCollapsed, setIsHoveredWhenCollapsed] = useState(false)
155
- const [isFullyCollapsed, setIsFullyCollapsed] = useState(false)
231
+ const [isShrinking, setIsShrinking] = useState(false)
156
232
  const hoverTimeoutRef = useRef(null)
157
- const collapseCenterTimeoutRef = useRef(null)
233
+ const shrinkTimeoutRef = useRef(null)
158
234
  const hoverRegionRef = useRef(null)
235
+ const shrinkDuration = 260
159
236
  const collapsedItemHeight = 44
160
- const collapsedVisibleOffset = 8
237
+ const collapsedVisibleOffset = 4
161
238
  const maxCollapsedItems = 4
162
239
  const collapsedNewItemOffset = collapsed && onNewSetlistClick ? 1 : 0
163
240
  const showCollapsedStack = collapsed && !isHoveredWhenCollapsed
@@ -165,23 +242,47 @@ export const SetlistList = ({
165
242
  ? setlists.slice(0, maxCollapsedItems)
166
243
  : setlists
167
244
 
245
+ const leaveCollapseDelay = 2750
246
+
168
247
  const handleMouseEnter = () => {
169
248
  if (!collapsed) return
170
249
  if (hoverTimeoutRef.current) {
171
250
  clearTimeout(hoverTimeoutRef.current)
172
251
  hoverTimeoutRef.current = null
173
252
  }
174
- setIsHoveredWhenCollapsed(true)
253
+ if (shrinkTimeoutRef.current) {
254
+ clearTimeout(shrinkTimeoutRef.current)
255
+ shrinkTimeoutRef.current = null
256
+ }
257
+ setIsShrinking(false)
175
258
  }
259
+
176
260
  const handleMouseLeave = () => {
177
261
  if (!collapsed) return
262
+ if (!isHoveredWhenCollapsed) return
178
263
  if (hoverTimeoutRef.current) {
179
264
  clearTimeout(hoverTimeoutRef.current)
180
265
  }
181
266
  hoverTimeoutRef.current = setTimeout(() => {
182
- setIsHoveredWhenCollapsed(false)
183
267
  hoverTimeoutRef.current = null
184
- }, 180)
268
+ setIsShrinking(true)
269
+ shrinkTimeoutRef.current = setTimeout(() => {
270
+ shrinkTimeoutRef.current = null
271
+ setIsHoveredWhenCollapsed(false)
272
+ setIsShrinking(false)
273
+ }, shrinkDuration)
274
+ }, leaveCollapseDelay)
275
+ }
276
+
277
+ const handleSetlistIconClick = (e) => {
278
+ if (!collapsed) return
279
+ e.stopPropagation()
280
+ if (hoverTimeoutRef.current) {
281
+ clearTimeout(hoverTimeoutRef.current)
282
+ hoverTimeoutRef.current = null
283
+ }
284
+ setIsShrinking(false)
285
+ setIsHoveredWhenCollapsed(true)
185
286
  }
186
287
 
187
288
  const handleFocus = () => {
@@ -199,33 +300,10 @@ export const SetlistList = ({
199
300
  }, 0)
200
301
  }
201
302
 
202
- // Só centraliza os avatares depois que a sidebar terminou de colapsar (width 280ms + delay 100ms)
203
- const collapseAnimationDuration = 400
204
- useEffect(() => {
205
- if (showCollapsedStack) {
206
- collapseCenterTimeoutRef.current = setTimeout(() => {
207
- setIsFullyCollapsed(true)
208
- collapseCenterTimeoutRef.current = null
209
- }, collapseAnimationDuration)
210
- } else {
211
- setIsFullyCollapsed(false)
212
- if (collapseCenterTimeoutRef.current) {
213
- clearTimeout(collapseCenterTimeoutRef.current)
214
- collapseCenterTimeoutRef.current = null
215
- }
216
- }
217
- return () => {
218
- if (collapseCenterTimeoutRef.current) {
219
- clearTimeout(collapseCenterTimeoutRef.current)
220
- }
221
- }
222
- }, [showCollapsedStack])
223
-
224
303
  useEffect(() => {
225
304
  return () => {
226
- if (hoverTimeoutRef.current) {
227
- clearTimeout(hoverTimeoutRef.current)
228
- }
305
+ if (hoverTimeoutRef.current) clearTimeout(hoverTimeoutRef.current)
306
+ if (shrinkTimeoutRef.current) clearTimeout(shrinkTimeoutRef.current)
229
307
  }
230
308
  }, [])
231
309
 
@@ -251,9 +329,7 @@ export const SetlistList = ({
251
329
  </div>
252
330
  )
253
331
 
254
- const collapsedStackAvatarSizes = ['38px', '37px', '36px', '35px']
255
-
256
- const getCollapsedStackStyle = (index, setlist) => {
332
+ const getCollapsedStackStyle = (index) => {
257
333
  if (!collapsed) return undefined
258
334
  if (!showCollapsedStack) {
259
335
  return {
@@ -304,15 +380,16 @@ export const SetlistList = ({
304
380
  direction="column"
305
381
  className={classNames(styles.setlistsContent, {
306
382
  [styles.collapsedStack]: showCollapsedStack,
307
- [styles.collapsedStackCentered]: isFullyCollapsed,
308
383
  [styles.collapsedMask]: collapsed,
384
+ [styles.collapsedShrinking]: isShrinking,
309
385
  })}
310
386
  >
311
387
  {onNewSetlistClick &&
312
388
  (showCollapsedStack ? (
313
389
  <SetlistItem
314
390
  isSelected={false}
315
- onClick={onNewSetlistClick}
391
+ onClick={handleNewSetlistClick}
392
+ onAvatarClick={handleSetlistIconClick}
316
393
  avatar={renderNewSetlistAvatar()}
317
394
  className={classNames(
318
395
  styles.collapsedStackItem,
@@ -328,7 +405,7 @@ export const SetlistList = ({
328
405
  ) : (
329
406
  <SetlistItem
330
407
  isSelected={false}
331
- onClick={onNewSetlistClick}
408
+ onClick={handleNewSetlistClick}
332
409
  avatar={renderNewSetlistAvatar()}
333
410
  text="New Setlist"
334
411
  className={classNames(styles.newSetlistItemButton, {
@@ -342,20 +419,12 @@ export const SetlistList = ({
342
419
  const setlistItem = (
343
420
  <SetlistItem
344
421
  isSelected={isSelected}
345
- onClick={() => onSetlistClick?.(setlist.id)}
422
+ onClick={() => handleSetlistClick(setlist.id)}
346
423
  dropdownMenuOpen={openSetlistMenuId === setlist.id}
347
424
  onDropdownMenuOpenChange={(nextOpen) => {
348
425
  setOpenSetlistMenuId(nextOpen ? setlist.id : null)
349
426
  }}
350
- avatar={setlist.icon || <SetlistIcon width={16} height={16} />}
351
- avatarStyle={
352
- showCollapsedStack
353
- ? {
354
- width: collapsedStackAvatarSizes[index],
355
- height: collapsedStackAvatarSizes[index],
356
- }
357
- : undefined
358
- }
427
+ avatar={renderSetlistAvatar(setlist.icon)}
359
428
  group={setlist.group}
360
429
  moises={setlist.moises}
361
430
  text={setlist.label}
@@ -367,7 +436,7 @@ export const SetlistList = ({
367
436
  [styles.collapsedStackItem]: showCollapsedStack,
368
437
  [styles.collapsedTransition]: collapsed,
369
438
  })}
370
- style={getCollapsedStackStyle(index, setlist)}
439
+ style={getCollapsedStackStyle(index)}
371
440
  />
372
441
  )
373
442
  return collapsed && !showCollapsedStack ? (
@@ -21,7 +21,8 @@
21
21
 
22
22
  &:hover,
23
23
  &.menuOpen {
24
- background: var(--neutral-alpha-2);
24
+ background: #212225 !important;
25
+
25
26
  .avatarSetlist {
26
27
  background: var(--neutral-alpha-3);
27
28
  }
@@ -90,6 +91,34 @@
90
91
  z-index: 1;
91
92
  }
92
93
 
94
+ .avatarClickable {
95
+ cursor: pointer;
96
+ }
97
+
98
+ .iconGrid {
99
+ display: grid;
100
+ grid-template-columns: 1fr 1fr;
101
+ grid-template-rows: 1fr 1fr;
102
+ width: 100%;
103
+ height: 100%;
104
+ border-radius: 4px;
105
+ overflow: hidden;
106
+ background: var(--neutral-2);
107
+ }
108
+
109
+ .iconGridCell {
110
+ position: relative;
111
+ overflow: hidden;
112
+ }
113
+
114
+ .iconGridCell img {
115
+ position: absolute;
116
+ inset: 0;
117
+ width: 100%;
118
+ height: 100%;
119
+ object-fit: cover;
120
+ }
121
+
93
122
  .textNewSetlist {
94
123
  max-width: 115px;
95
124
  opacity: 1;
@@ -123,12 +152,13 @@
123
152
  opacity: 0;
124
153
  }
125
154
 
155
+
126
156
  .navItemSelected {
127
157
  border-radius: var(--Radius-3-max, 6px);
128
158
  background: var(--neutral-alpha-3);
129
159
 
130
160
  &.collapsed {
131
- background: transparent;
161
+ /* background: transparent; */
132
162
 
133
163
  .avatarSetlist {
134
164
  background: var(--neutral-2);
@@ -201,12 +231,12 @@
201
231
  gap: 0;
202
232
  }
203
233
 
204
- .collapsedStackCentered {
205
- align-items: center;
206
- }
207
234
  .collapsedStackItem {
208
235
  position: relative;
209
236
  /* justify-content: flex-start; */
237
+ /* :hover {
238
+ background: #212225 !important;
239
+ } */
210
240
  }
211
241
 
212
242
  .collapsedMask {
@@ -214,7 +244,13 @@
214
244
  overflow: hidden;
215
245
  padding-top: 2px;
216
246
  display: flex;
217
-
247
+ max-height: 80vh;
248
+ transition: max-height 260ms ease-in-out;
249
+ }
250
+
251
+ .collapsedMask.collapsedStack,
252
+ .collapsedMask.collapsedShrinking {
253
+ max-height: 120px;
218
254
  }
219
255
 
220
256
  /* .collapsedStack.collapsedMask {
@@ -222,7 +258,7 @@
222
258
  } */
223
259
 
224
260
  .collapsedTransition {
225
- transition: margin-top 360ms ease-in-out;
261
+ transition: margin-top 260ms ease-in-out;
226
262
  }
227
263
 
228
264
  .contentInner {