@moises.ai/design-system 3.6.4 → 3.6.6

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.4",
3
+ "version": "3.6.6",
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
  >
@@ -155,6 +155,7 @@ export const DataTable = ({
155
155
  onSelectAll,
156
156
  onSelectRow,
157
157
  stickyOffset = 0,
158
+ stickyBodyOffset = 0,
158
159
  ...props
159
160
  }) => {
160
161
  const [isHeaderHovering, setIsHeaderHovering] = useState(false)
@@ -279,12 +280,16 @@ export const DataTable = ({
279
280
  const stickyOffsetValue =
280
281
  typeof stickyOffset === 'number' ? `${stickyOffset}px` : stickyOffset
281
282
 
283
+ const stickyBodyOffsetValue =
284
+ typeof stickyBodyOffset === 'number' ? `${stickyBodyOffset}px` : stickyBodyOffset
285
+
282
286
  return (
283
287
  <Table.Root
284
288
  className={classNames(styles.DataTable, className)}
285
289
  size="1"
286
290
  style={{
287
291
  '--datatable-sticky-offset': stickyOffsetValue,
292
+ '--datatable-sticky-body-offset': stickyBodyOffsetValue,
288
293
  ...style,
289
294
  }}
290
295
  {...restProps}
@@ -360,7 +365,7 @@ export const DataTable = ({
360
365
  </Table.Row>
361
366
  </Table.Header>
362
367
 
363
- <Table.Body>
368
+ <Table.Body >
364
369
  {data.map((row, index) => (
365
370
  <DataRow
366
371
  key={row.id}
@@ -54,6 +54,12 @@
54
54
  background-color: var(--neutral-1) !important;
55
55
  }
56
56
 
57
+ .DataTable :global(.rt-TableBody) {
58
+ position: relative;
59
+ top: var(--datatable-sticky-body-offset, 0px) !important;
60
+ }
61
+
62
+
57
63
  .DataTable :global(.rt-TableHeader) :global(.rt-TableRow) {
58
64
  background-color: var(--neutral-1) !important;
59
65
  }
@@ -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
  }
@@ -2,19 +2,66 @@ import { Flex, Text, Avatar, ScrollArea } from '@radix-ui/themes'
2
2
  import styles from './SetlistList.module.css'
3
3
  import classNames from 'classnames'
4
4
  import {
5
- DotsVerticalIcon,
6
5
  SetlistIcon,
7
- UserGroupIcon,
8
- GlobeIcon,
9
6
  PlusIcon,
10
7
  } from '../../icons'
11
8
  import { DropdownMenu } from '../DropdownMenu/DropdownMenu'
12
9
  import { Tooltip } from '../Tooltip/Tooltip'
13
10
  import { useState, useEffect, useRef, useMemo, Fragment } from 'react'
14
11
  import { MoreButton } from '../MoreButton/MoreButton'
12
+ import { useMobileDrawerContext } from '../../contexts/MobileDrawerContext'
15
13
 
16
14
  const SetlistIconFallback = () => <SetlistIcon width={16} height={16} />
17
15
 
16
+ function SetlistExpandButton({ onExpand, className, style, collapsed = false }) {
17
+ return (
18
+ <button
19
+ type="button"
20
+ className={classNames(
21
+ styles.newSetlistItem,
22
+ styles.collapsedStackItem,
23
+ styles.collapsedTransition,
24
+ styles.setlistExpandButton,
25
+ { [styles.collapsed]: collapsed },
26
+ className,
27
+ )}
28
+ style={style}
29
+ onClick={(e) => {
30
+ e.stopPropagation()
31
+ onExpand?.(e)
32
+ }}
33
+ aria-label="Expand setlist"
34
+ >
35
+ <Flex gap="3" align="center" className={styles.collapsedInner}>
36
+ <div className={styles.avatarSetlist}>
37
+ <SetlistIcon width={16} height={16} />
38
+ </div>
39
+ </Flex>
40
+ </button>
41
+ )
42
+ }
43
+
44
+ function NewSetlistButton({ onClick, className, collapsed = false }) {
45
+ return (
46
+ <SetlistItem
47
+ isSelected={false}
48
+ onClick={onClick}
49
+ avatar={
50
+ <div className={styles.iconSwap}>
51
+ <PlusIcon
52
+ width={16}
53
+ height={16}
54
+ className={classNames(styles.iconSwapLayer, styles.iconVisible, styles.plusIcon)}
55
+ />
56
+ </div>
57
+ }
58
+ text="New Setlist"
59
+ className={classNames(styles.newSetlistItemButton, styles.collapsedTransition, className)}
60
+ collapsed={collapsed}
61
+ />
62
+ )
63
+ }
64
+
18
65
  function renderSetlistAvatar(icon) {
19
66
  if (!icon) return <SetlistIconFallback />
20
67
  if (typeof icon === 'string') {
@@ -91,8 +138,8 @@ export const SetlistItem = ({
91
138
  <Flex
92
139
  gap="3"
93
140
  align="center"
94
- role="button"
95
- tabIndex={0}
141
+ role={onClick ? 'button' : undefined}
142
+ tabIndex={onClick ? 0 : undefined}
96
143
  onClick={onClick}
97
144
  className={classNames(styles.newSetlistItem, className, {
98
145
  [styles.navItemSelected]: isSelected,
@@ -101,12 +148,16 @@ export const SetlistItem = ({
101
148
  })}
102
149
  onMouseEnter={handleMouseEnter}
103
150
  onMouseLeave={handleMouseLeave}
104
- onKeyDown={(e) => {
105
- if (e.key === 'Enter' || e.key === ' ') {
106
- e.preventDefault()
107
- onClick?.()
108
- }
109
- }}
151
+ onKeyDown={
152
+ onClick
153
+ ? (e) => {
154
+ if (e.key === 'Enter' || e.key === ' ') {
155
+ e.preventDefault()
156
+ onClick?.()
157
+ }
158
+ }
159
+ : undefined
160
+ }
110
161
  style={style}
111
162
  >
112
163
  <Flex
@@ -188,7 +239,18 @@ export const SetlistList = ({
188
239
  collapsed = false,
189
240
  isMobile = false,
190
241
  }) => {
242
+ const { close, isMobile: isMobileDrawer } = useMobileDrawerContext()
191
243
  const [openSetlistMenuId, setOpenSetlistMenuId] = useState(null)
244
+
245
+ const handleSetlistClick = (setlistId) => {
246
+ onSetlistClick?.(setlistId)
247
+ if (isMobileDrawer) close()
248
+ }
249
+
250
+ const handleNewSetlistClick = () => {
251
+ onNewSetlistClick?.()
252
+ if (isMobileDrawer) close()
253
+ }
192
254
  const [isHoveredWhenCollapsed, setIsHoveredWhenCollapsed] = useState(false)
193
255
  const [isShrinking, setIsShrinking] = useState(false)
194
256
  const hoverTimeoutRef = useRef(null)
@@ -204,6 +266,8 @@ export const SetlistList = ({
204
266
  ? setlists.slice(0, maxCollapsedItems)
205
267
  : setlists
206
268
 
269
+ const leaveCollapseDelay = 2750
270
+
207
271
  const handleMouseEnter = () => {
208
272
  if (!collapsed) return
209
273
  if (hoverTimeoutRef.current) {
@@ -215,10 +279,11 @@ export const SetlistList = ({
215
279
  shrinkTimeoutRef.current = null
216
280
  }
217
281
  setIsShrinking(false)
218
- setIsHoveredWhenCollapsed(true)
219
282
  }
283
+
220
284
  const handleMouseLeave = () => {
221
285
  if (!collapsed) return
286
+ if (!isHoveredWhenCollapsed) return
222
287
  if (hoverTimeoutRef.current) {
223
288
  clearTimeout(hoverTimeoutRef.current)
224
289
  }
@@ -230,7 +295,18 @@ export const SetlistList = ({
230
295
  setIsHoveredWhenCollapsed(false)
231
296
  setIsShrinking(false)
232
297
  }, shrinkDuration)
233
- }, 180)
298
+ }, leaveCollapseDelay)
299
+ }
300
+
301
+ const handleSetlistIconClick = (e) => {
302
+ if (!collapsed) return
303
+ e.stopPropagation()
304
+ if (hoverTimeoutRef.current) {
305
+ clearTimeout(hoverTimeoutRef.current)
306
+ hoverTimeoutRef.current = null
307
+ }
308
+ setIsShrinking(false)
309
+ setIsHoveredWhenCollapsed(true)
234
310
  }
235
311
 
236
312
  const handleFocus = () => {
@@ -255,28 +331,6 @@ export const SetlistList = ({
255
331
  }
256
332
  }, [])
257
333
 
258
- const renderNewSetlistAvatar = () => (
259
- <div className={styles.iconSwap}>
260
- <SetlistIcon
261
- width={16}
262
- height={16}
263
- className={classNames(styles.iconSwapLayer, {
264
- [styles.iconVisible]: showCollapsedStack,
265
- [styles.iconHidden]: !showCollapsedStack,
266
- })}
267
- />
268
- <PlusIcon
269
- width={16}
270
- height={16}
271
- className={classNames(
272
- styles.iconSwapLayer,
273
- styles.plusIcon,
274
- showCollapsedStack ? styles.iconHidden : styles.iconVisible,
275
- )}
276
- />
277
- </div>
278
- )
279
-
280
334
  const getCollapsedStackStyle = (index) => {
281
335
  if (!collapsed) return undefined
282
336
  if (!showCollapsedStack) {
@@ -334,15 +388,10 @@ export const SetlistList = ({
334
388
  >
335
389
  {onNewSetlistClick &&
336
390
  (showCollapsedStack ? (
337
- <SetlistItem
338
- isSelected={false}
339
- onClick={onNewSetlistClick}
340
- avatar={renderNewSetlistAvatar()}
341
- className={classNames(
342
- styles.collapsedStackItem,
343
- styles.collapsedTransition,
344
- )}
391
+ <SetlistExpandButton
392
+ onExpand={handleSetlistIconClick}
345
393
  collapsed={collapsed}
394
+ className={styles.collapsedStackItem}
346
395
  style={{
347
396
  position: 'relative',
348
397
  marginTop: 0,
@@ -350,14 +399,9 @@ export const SetlistList = ({
350
399
  }}
351
400
  />
352
401
  ) : (
353
- <SetlistItem
354
- isSelected={false}
355
- onClick={onNewSetlistClick}
356
- avatar={renderNewSetlistAvatar()}
357
- text="New Setlist"
358
- className={classNames(styles.newSetlistItemButton, {
359
- [styles.collapsedTransition]: collapsed,
360
- })}
402
+ <NewSetlistButton
403
+ onClick={handleNewSetlistClick}
404
+ className={styles.collapsedTransition}
361
405
  collapsed={collapsed}
362
406
  />
363
407
  ))}
@@ -366,7 +410,7 @@ export const SetlistList = ({
366
410
  const setlistItem = (
367
411
  <SetlistItem
368
412
  isSelected={isSelected}
369
- onClick={() => onSetlistClick?.(setlist.id)}
413
+ onClick={() => handleSetlistClick(setlist.id)}
370
414
  dropdownMenuOpen={openSetlistMenuId === setlist.id}
371
415
  onDropdownMenuOpenChange={(nextOpen) => {
372
416
  setOpenSetlistMenuId(nextOpen ? setlist.id : null)
@@ -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,10 @@
90
91
  z-index: 1;
91
92
  }
92
93
 
94
+ .avatarClickable {
95
+ cursor: pointer;
96
+ }
97
+
93
98
  .iconGrid {
94
99
  display: grid;
95
100
  grid-template-columns: 1fr 1fr;
@@ -147,12 +152,13 @@
147
152
  opacity: 0;
148
153
  }
149
154
 
155
+
150
156
  .navItemSelected {
151
157
  border-radius: var(--Radius-3-max, 6px);
152
158
  background: var(--neutral-alpha-3);
153
159
 
154
160
  &.collapsed {
155
- background: transparent;
161
+ /* background: transparent; */
156
162
 
157
163
  .avatarSetlist {
158
164
  background: var(--neutral-2);
@@ -227,7 +233,33 @@
227
233
 
228
234
  .collapsedStackItem {
229
235
  position: relative;
230
- /* justify-content: flex-start; */
236
+ }
237
+
238
+ .setlistExpandButton {
239
+ display: flex;
240
+ align-items: center;
241
+ width: fit-content;
242
+ margin: 0 10px;
243
+ padding: 2px;
244
+ border: none;
245
+ border-radius: 6px;
246
+ background: transparent;
247
+ cursor: pointer;
248
+ font: inherit;
249
+ color: inherit;
250
+ }
251
+
252
+ .setlistExpandButton:hover {
253
+ background: var(--neutral-alpha-2);
254
+ }
255
+
256
+ .setlistExpandButton:hover .avatarSetlist {
257
+ background: var(--neutral-alpha-3);
258
+ }
259
+
260
+ .setlistExpandButton:focus-visible {
261
+ outline: 2px solid var(--neutral-alpha-8);
262
+ outline-offset: -1px;
231
263
  }
232
264
 
233
265
  .collapsedMask {
@@ -288,6 +320,11 @@
288
320
  pointer-events: none;
289
321
  }
290
322
 
323
+ .iconSwap.iconClickable {
324
+ pointer-events: auto;
325
+ cursor: pointer;
326
+ }
327
+
291
328
  .iconSwapLayer {
292
329
  position: absolute;
293
330
  inset: 0;
@@ -296,6 +333,12 @@
296
333
  justify-content: center;
297
334
  opacity: 0;
298
335
  transition: opacity 160ms ease-in-out;
336
+ pointer-events: none;
337
+ }
338
+
339
+ .iconSwapLayer.iconClickable {
340
+ pointer-events: auto;
341
+ cursor: pointer;
299
342
  }
300
343
 
301
344
  .iconVisible {
@@ -11,6 +11,7 @@ import {
11
11
  CloseIcon,
12
12
  } from '../../icons'
13
13
  import { useMobileDrawer } from '../../utils/useMobileDrawer'
14
+ import { MobileDrawerProvider } from '../../contexts/MobileDrawerContext'
14
15
 
15
16
  export const Sidebar = ({
16
17
  className,
@@ -50,12 +51,13 @@ export const Sidebar = ({
50
51
  }
51
52
 
52
53
  return (
53
- <div
54
- className={classNames(styles.root, {
55
- [styles.rootCollapsed]: effectiveCollapsed,
56
- })}
57
- {...props}
58
- >
54
+ <MobileDrawerProvider value={{ close, isMobile }}>
55
+ <div
56
+ className={classNames(styles.root, {
57
+ [styles.rootCollapsed]: effectiveCollapsed,
58
+ })}
59
+ {...props}
60
+ >
59
61
  <div className={styles.mobileTopBarWrapper}>
60
62
  <Flex
61
63
  align="center"
@@ -170,6 +172,7 @@ export const Sidebar = ({
170
172
  </Flex>
171
173
  </Flex>
172
174
  </div>
175
+ </MobileDrawerProvider>
173
176
  )
174
177
  }
175
178
 
@@ -0,0 +1,10 @@
1
+ import { createContext, useContext } from 'react'
2
+
3
+ const MobileDrawerContext = createContext({
4
+ close: () => {},
5
+ isMobile: false,
6
+ })
7
+
8
+ export const useMobileDrawerContext = () => useContext(MobileDrawerContext)
9
+
10
+ export const MobileDrawerProvider = MobileDrawerContext.Provider