@moises.ai/design-system 3.10.12 → 3.10.14
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/dist/index.js +2519 -2460
- package/package.json +1 -1
- package/src/components/AdditionalItems/AdditionalItems.module.css +1 -1
- package/src/components/DataTable/DataTable.module.css +2 -3
- package/src/components/InstrumentSelector/InstrumentSelector.module.css +1 -1
- package/src/components/ProductsList/ProductsList.module.css +1 -1
- package/src/components/ProfileMenu/ProfileMenu.module.css +1 -1
- package/src/components/SetlistList/SetlistList.module.css +2 -2
- package/src/components/Shell/Shell.jsx +6 -1
- package/src/components/Sidebar/Sidebar.jsx +73 -3
- package/src/components/Sidebar/Sidebar.module.css +2 -3
- package/src/components/Sidebar/Sidebar.stories.jsx +146 -0
- package/src/components/ToastProvider/ToastProvider.jsx +63 -3
package/package.json
CHANGED
|
@@ -318,9 +318,8 @@
|
|
|
318
318
|
}
|
|
319
319
|
|
|
320
320
|
.rowActionButton:focus-visible {
|
|
321
|
-
outline: 2px solid var(--
|
|
322
|
-
outline-offset:
|
|
323
|
-
border-radius: 6px;
|
|
321
|
+
outline: 2px solid var(--neutral-alpha-8);
|
|
322
|
+
outline-offset: 2px;
|
|
324
323
|
}
|
|
325
324
|
|
|
326
325
|
.DataTable :global(.rt-TableRow:hover) .projectCell:not(.dropdownColumn) :not(.artistText) {
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
&:focus-visible {
|
|
18
18
|
outline: 2px solid var(--neutral-alpha-8);
|
|
19
|
-
outline-offset:
|
|
19
|
+
outline-offset: 2px;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
&:hover,
|
|
@@ -289,7 +289,7 @@ sectionTitle {
|
|
|
289
289
|
|
|
290
290
|
.setlistExpandButton:focus-visible {
|
|
291
291
|
outline: 2px solid var(--neutral-alpha-8);
|
|
292
|
-
outline-offset:
|
|
292
|
+
outline-offset: 2px;
|
|
293
293
|
}
|
|
294
294
|
|
|
295
295
|
.collapsedMask {
|
|
@@ -27,14 +27,19 @@ export const Shell = ({
|
|
|
27
27
|
children,
|
|
28
28
|
}) => {
|
|
29
29
|
const [collapsed, setCollapsed] = useState(false)
|
|
30
|
+
const [isTemporarilyExpandedByDrag, setIsTemporarilyExpandedByDrag] =
|
|
31
|
+
useState(false)
|
|
30
32
|
const { isMobile } = useMobileDrawer()
|
|
31
33
|
|
|
32
|
-
const effectiveCollapsed =
|
|
34
|
+
const effectiveCollapsed =
|
|
35
|
+
isMobile ? false : collapsed && !isTemporarilyExpandedByDrag
|
|
33
36
|
|
|
34
37
|
return (
|
|
35
38
|
<>
|
|
36
39
|
<Sidebar
|
|
37
40
|
isCollapsed={collapsed}
|
|
41
|
+
isTemporarilyExpandedByDrag={isTemporarilyExpandedByDrag}
|
|
42
|
+
onTemporarilyExpandedByDragChange={setIsTemporarilyExpandedByDrag}
|
|
38
43
|
onCollapsedChange={setCollapsed}
|
|
39
44
|
tooltip={sidebarTooltip}
|
|
40
45
|
>
|
|
@@ -2,7 +2,7 @@ import { Flex } from '@radix-ui/themes'
|
|
|
2
2
|
import { IconButton } from '../IconButton/IconButton'
|
|
3
3
|
import { Tooltip } from '../Tooltip/Tooltip'
|
|
4
4
|
import styles from './Sidebar.module.css'
|
|
5
|
-
import { useState } from 'react'
|
|
5
|
+
import { useCallback, useEffect, useState } from 'react'
|
|
6
6
|
import classNames from 'classnames'
|
|
7
7
|
import {
|
|
8
8
|
MoisesIcon,
|
|
@@ -18,6 +18,8 @@ export const Sidebar = ({
|
|
|
18
18
|
className,
|
|
19
19
|
children,
|
|
20
20
|
isCollapsed,
|
|
21
|
+
isTemporarilyExpandedByDrag,
|
|
22
|
+
onTemporarilyExpandedByDragChange,
|
|
21
23
|
onCollapsedChange,
|
|
22
24
|
onLogoClick,
|
|
23
25
|
tooltip = 'Expand menu',
|
|
@@ -25,12 +27,29 @@ export const Sidebar = ({
|
|
|
25
27
|
}) => {
|
|
26
28
|
const { isMobile, isOpen: mobileOpen, open, close } = useMobileDrawer()
|
|
27
29
|
const [isHovered, setIsHovered] = useState(false)
|
|
30
|
+
const [internalTemporarilyExpandedByDrag, setInternalTemporarilyExpandedByDrag] =
|
|
31
|
+
useState(false)
|
|
28
32
|
|
|
29
|
-
const
|
|
33
|
+
const dragExpanded =
|
|
34
|
+
isTemporarilyExpandedByDrag ?? internalTemporarilyExpandedByDrag
|
|
35
|
+
|
|
36
|
+
const setDragExpanded = useCallback(
|
|
37
|
+
(value) => {
|
|
38
|
+
if (isTemporarilyExpandedByDrag == null) {
|
|
39
|
+
setInternalTemporarilyExpandedByDrag(value)
|
|
40
|
+
}
|
|
41
|
+
onTemporarilyExpandedByDragChange?.(value)
|
|
42
|
+
},
|
|
43
|
+
[isTemporarilyExpandedByDrag, onTemporarilyExpandedByDragChange],
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
const effectiveCollapsed =
|
|
47
|
+
isMobile ? false : isCollapsed && !dragExpanded
|
|
30
48
|
|
|
31
49
|
const handleToggleCollapse = () => {
|
|
32
50
|
if (isMobile) return
|
|
33
51
|
setIsHovered(false)
|
|
52
|
+
setDragExpanded(false)
|
|
34
53
|
onCollapsedChange((prev) => !prev)
|
|
35
54
|
}
|
|
36
55
|
|
|
@@ -52,9 +71,57 @@ export const Sidebar = ({
|
|
|
52
71
|
}
|
|
53
72
|
}
|
|
54
73
|
|
|
74
|
+
const expandForDragHover = () => {
|
|
75
|
+
if (isMobile || !isCollapsed) return
|
|
76
|
+
setDragExpanded(true)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const collapseAfterDrag = () => {
|
|
80
|
+
setDragExpanded(false)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const handleSidebarPointerEnter = (e) => {
|
|
84
|
+
handleMouseEnter()
|
|
85
|
+
if (e.buttons > 0) {
|
|
86
|
+
expandForDragHover()
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const handleSidebarDragEnter = () => {
|
|
91
|
+
expandForDragHover()
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const handleSidebarDragOver = (e) => {
|
|
95
|
+
if (isMobile || !isCollapsed) return
|
|
96
|
+
e.preventDefault()
|
|
97
|
+
expandForDragHover()
|
|
98
|
+
}
|
|
99
|
+
|
|
55
100
|
const desktopLogoAriaLabel = effectiveCollapsed ? 'Expand menu' : 'Moises'
|
|
56
101
|
const mobileLogoAriaLabel = 'Moises'
|
|
57
102
|
|
|
103
|
+
useEffect(() => {
|
|
104
|
+
if (!dragExpanded) return undefined
|
|
105
|
+
|
|
106
|
+
const handlePointerUp = () => {
|
|
107
|
+
collapseAfterDrag()
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const handleDragEnd = () => {
|
|
111
|
+
collapseAfterDrag()
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
window.addEventListener('pointerup', handlePointerUp)
|
|
115
|
+
window.addEventListener('dragend', handleDragEnd)
|
|
116
|
+
window.addEventListener('drop', handleDragEnd)
|
|
117
|
+
|
|
118
|
+
return () => {
|
|
119
|
+
window.removeEventListener('pointerup', handlePointerUp)
|
|
120
|
+
window.removeEventListener('dragend', handleDragEnd)
|
|
121
|
+
window.removeEventListener('drop', handleDragEnd)
|
|
122
|
+
}
|
|
123
|
+
}, [dragExpanded])
|
|
124
|
+
|
|
58
125
|
const logoContent = (
|
|
59
126
|
<>
|
|
60
127
|
<div
|
|
@@ -156,8 +223,11 @@ export const Sidebar = ({
|
|
|
156
223
|
<Flex
|
|
157
224
|
direction="column"
|
|
158
225
|
className={styles.desktopSidebar}
|
|
159
|
-
onMouseEnter={
|
|
226
|
+
onMouseEnter={handleSidebarPointerEnter}
|
|
160
227
|
onMouseLeave={handleMouseLeave}
|
|
228
|
+
onDragEnter={handleSidebarDragEnter}
|
|
229
|
+
onDragOver={handleSidebarDragOver}
|
|
230
|
+
onDrop={collapseAfterDrag}
|
|
161
231
|
>
|
|
162
232
|
{effectiveCollapsed ? (
|
|
163
233
|
<div className={styles.headerWrapper}>
|
|
@@ -292,9 +292,8 @@
|
|
|
292
292
|
|
|
293
293
|
.logoButton:focus-visible,
|
|
294
294
|
.toggleButton:focus-visible {
|
|
295
|
-
outline: 2px solid var(--
|
|
296
|
-
outline-offset:
|
|
297
|
-
border-radius: 6px;
|
|
295
|
+
outline: 2px solid var(--neutral-alpha-8);
|
|
296
|
+
outline-offset: 2px;
|
|
298
297
|
}
|
|
299
298
|
|
|
300
299
|
.headerCollapsed {
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { useState } from 'react'
|
|
2
2
|
import { Sidebar } from './Sidebar'
|
|
3
|
+
import { SidebarSection } from './SidebarSection/SidebarSection'
|
|
4
|
+
import { ProductsList } from '../ProductsList/ProductsList'
|
|
5
|
+
import { SetlistList } from '../SetlistList/SetlistList'
|
|
6
|
+
import { Flex, Text } from '../../index'
|
|
3
7
|
import {
|
|
4
8
|
MoisesLogoIcon,
|
|
5
9
|
SparkBarsIcon,
|
|
@@ -246,3 +250,145 @@ export const Default = {
|
|
|
246
250
|
)
|
|
247
251
|
},
|
|
248
252
|
}
|
|
253
|
+
|
|
254
|
+
export const DragHoverExpand = {
|
|
255
|
+
render: () => {
|
|
256
|
+
const [isCollapsed, setIsCollapsed] = useState(true)
|
|
257
|
+
const [isTemporarilyExpandedByDrag, setIsTemporarilyExpandedByDrag] =
|
|
258
|
+
useState(false)
|
|
259
|
+
const [selectedProduct, setSelectedProduct] = useState('library')
|
|
260
|
+
const [selectedSetlist, setSelectedSetlist] = useState('setlist-1')
|
|
261
|
+
const [isDraggingFile, setIsDraggingFile] = useState(false)
|
|
262
|
+
const effectiveCollapsed = isCollapsed && !isTemporarilyExpandedByDrag
|
|
263
|
+
|
|
264
|
+
const products = [
|
|
265
|
+
{
|
|
266
|
+
id: 'library',
|
|
267
|
+
label: 'Library',
|
|
268
|
+
icon: <LibraryIcon width={16} height={16} />,
|
|
269
|
+
},
|
|
270
|
+
{
|
|
271
|
+
id: 'ai-studio',
|
|
272
|
+
label: 'AI Studio',
|
|
273
|
+
icon: <SparkBarsIcon width={16} height={16} />,
|
|
274
|
+
},
|
|
275
|
+
{
|
|
276
|
+
id: 'mastering',
|
|
277
|
+
label: 'Mastering',
|
|
278
|
+
icon: <MasteringIcon width={16} height={16} />,
|
|
279
|
+
},
|
|
280
|
+
]
|
|
281
|
+
|
|
282
|
+
const setlists = [
|
|
283
|
+
{
|
|
284
|
+
id: 'setlist-1',
|
|
285
|
+
label: 'Piano Exercises',
|
|
286
|
+
subtitle: 'By Berklee',
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
id: 'setlist-2',
|
|
290
|
+
label: 'Band Rehearsal',
|
|
291
|
+
subtitle: 'By Nickyz',
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
id: 'setlist-3',
|
|
295
|
+
label: 'Gig Dec 21th',
|
|
296
|
+
},
|
|
297
|
+
]
|
|
298
|
+
|
|
299
|
+
return (
|
|
300
|
+
<>
|
|
301
|
+
<style>{`
|
|
302
|
+
.SidebarDragHoverStoryLayout {
|
|
303
|
+
display: flex;
|
|
304
|
+
min-height: 100vh;
|
|
305
|
+
min-height: 100dvh;
|
|
306
|
+
background: var(--neutral-2);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
.SidebarDragHoverStoryMain {
|
|
310
|
+
flex: 1;
|
|
311
|
+
display: flex;
|
|
312
|
+
align-items: center;
|
|
313
|
+
justify-content: center;
|
|
314
|
+
padding: 32px;
|
|
315
|
+
background: linear-gradient(180deg, var(--neutral-2) 0%, var(--neutral-1) 100%);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
.SidebarDragHoverFile {
|
|
319
|
+
display: flex;
|
|
320
|
+
flex-direction: column;
|
|
321
|
+
gap: 8px;
|
|
322
|
+
width: 280px;
|
|
323
|
+
padding: 16px;
|
|
324
|
+
border-radius: 12px;
|
|
325
|
+
border: 1px solid var(--neutral-alpha-4);
|
|
326
|
+
background: var(--neutral-1);
|
|
327
|
+
box-shadow: 0 12px 24px -18px rgba(0, 0, 0, 0.45);
|
|
328
|
+
cursor: grab;
|
|
329
|
+
user-select: none;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
.SidebarDragHoverFile[data-dragging='true'] {
|
|
333
|
+
opacity: 0.6;
|
|
334
|
+
cursor: grabbing;
|
|
335
|
+
}
|
|
336
|
+
`}</style>
|
|
337
|
+
|
|
338
|
+
<div className="SidebarDragHoverStoryLayout">
|
|
339
|
+
<Sidebar
|
|
340
|
+
isCollapsed={isCollapsed}
|
|
341
|
+
isTemporarilyExpandedByDrag={isTemporarilyExpandedByDrag}
|
|
342
|
+
onTemporarilyExpandedByDragChange={setIsTemporarilyExpandedByDrag}
|
|
343
|
+
onCollapsedChange={setIsCollapsed}
|
|
344
|
+
>
|
|
345
|
+
<Flex direction="column">
|
|
346
|
+
<SidebarSection title="PRODUCTS" collapsed={effectiveCollapsed} />
|
|
347
|
+
<ProductsList
|
|
348
|
+
products={products}
|
|
349
|
+
selectedProductId={selectedProduct}
|
|
350
|
+
onProductClick={setSelectedProduct}
|
|
351
|
+
collapsed={effectiveCollapsed}
|
|
352
|
+
/>
|
|
353
|
+
</Flex>
|
|
354
|
+
|
|
355
|
+
<Flex
|
|
356
|
+
direction="column"
|
|
357
|
+
style={{ overflow: 'hidden', flex: 1, minHeight: 0 }}
|
|
358
|
+
>
|
|
359
|
+
<SidebarSection title="SETLISTS" collapsed={effectiveCollapsed} />
|
|
360
|
+
<SetlistList
|
|
361
|
+
setlists={setlists}
|
|
362
|
+
selectedSetlistId={selectedSetlist}
|
|
363
|
+
onSetlistClick={setSelectedSetlist}
|
|
364
|
+
onNewSetlistClick={() => console.log('New setlist clicked')}
|
|
365
|
+
collapsed={effectiveCollapsed}
|
|
366
|
+
/>
|
|
367
|
+
</Flex>
|
|
368
|
+
</Sidebar>
|
|
369
|
+
|
|
370
|
+
<div className="SidebarDragHoverStoryMain">
|
|
371
|
+
<div
|
|
372
|
+
draggable
|
|
373
|
+
data-dragging={isDraggingFile}
|
|
374
|
+
className="SidebarDragHoverFile"
|
|
375
|
+
onDragStart={() => setIsDraggingFile(true)}
|
|
376
|
+
onDragEnd={() => {
|
|
377
|
+
setIsDraggingFile(false)
|
|
378
|
+
setIsTemporarilyExpandedByDrag(false)
|
|
379
|
+
}}
|
|
380
|
+
>
|
|
381
|
+
<Text size="2" weight="bold">
|
|
382
|
+
Drag this file to the sidebar
|
|
383
|
+
</Text>
|
|
384
|
+
<Text size="1" style={{ color: 'var(--neutral-alpha-11)' }}>
|
|
385
|
+
With the sidebar collapsed, drag this card over it. The sidebar should
|
|
386
|
+
expand on hover and collapse again when you release.
|
|
387
|
+
</Text>
|
|
388
|
+
</div>
|
|
389
|
+
</div>
|
|
390
|
+
</div>
|
|
391
|
+
</>
|
|
392
|
+
)
|
|
393
|
+
},
|
|
394
|
+
}
|
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, {
|
|
2
|
+
createContext,
|
|
3
|
+
useCallback,
|
|
4
|
+
useContext,
|
|
5
|
+
useEffect,
|
|
6
|
+
useMemo,
|
|
7
|
+
useRef,
|
|
8
|
+
useState,
|
|
9
|
+
} from 'react'
|
|
2
10
|
import { Toast } from 'radix-ui'
|
|
3
11
|
import { Callout } from '../Callout/Callout'
|
|
4
12
|
import styles from './ToastProvider.module.css'
|
|
@@ -30,9 +38,31 @@ export const ToastProvider = ({
|
|
|
30
38
|
return 'neutral'
|
|
31
39
|
}, [])
|
|
32
40
|
|
|
41
|
+
const getCalloutStyle = useCallback((variant) => {
|
|
42
|
+
if (variant === 'success') {
|
|
43
|
+
return {
|
|
44
|
+
backgroundColor: '#113B29',
|
|
45
|
+
color: '#BBFFD7',
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (variant === 'error') {
|
|
49
|
+
return {
|
|
50
|
+
backgroundColor: '#201314',
|
|
51
|
+
color: '#FF9592',
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return undefined
|
|
55
|
+
}, [])
|
|
56
|
+
|
|
33
57
|
const [toasts, setToasts] = useState([])
|
|
58
|
+
const timeoutIdsRef = useRef(new Map())
|
|
34
59
|
|
|
35
60
|
const dismiss = useCallback((id) => {
|
|
61
|
+
const timeoutId = timeoutIdsRef.current.get(id)
|
|
62
|
+
if (timeoutId) {
|
|
63
|
+
clearTimeout(timeoutId)
|
|
64
|
+
timeoutIdsRef.current.delete(id)
|
|
65
|
+
}
|
|
36
66
|
setToasts((prev) => prev.filter((toastItem) => toastItem.id !== id))
|
|
37
67
|
}, [])
|
|
38
68
|
|
|
@@ -43,10 +73,32 @@ export const ToastProvider = ({
|
|
|
43
73
|
...normalized,
|
|
44
74
|
id: `${Date.now()}-${toastId++}`,
|
|
45
75
|
}
|
|
46
|
-
|
|
76
|
+
|
|
77
|
+
const toastDuration = nextToast.duration ?? duration
|
|
78
|
+
const timeoutId = setTimeout(() => {
|
|
79
|
+
dismiss(nextToast.id)
|
|
80
|
+
}, toastDuration)
|
|
81
|
+
|
|
82
|
+
timeoutIdsRef.current.set(nextToast.id, timeoutId)
|
|
83
|
+
setToasts((prev) => {
|
|
84
|
+
const nextToasts = [...prev.slice(-(maxToasts - 1)), nextToast]
|
|
85
|
+
const nextToastIds = new Set(nextToasts.map((toastItem) => toastItem.id))
|
|
86
|
+
|
|
87
|
+
prev.forEach((toastItem) => {
|
|
88
|
+
if (!nextToastIds.has(toastItem.id)) {
|
|
89
|
+
const staleTimeoutId = timeoutIdsRef.current.get(toastItem.id)
|
|
90
|
+
if (staleTimeoutId) {
|
|
91
|
+
clearTimeout(staleTimeoutId)
|
|
92
|
+
timeoutIdsRef.current.delete(toastItem.id)
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
return nextToasts
|
|
98
|
+
})
|
|
47
99
|
return nextToast.id
|
|
48
100
|
},
|
|
49
|
-
[maxToasts],
|
|
101
|
+
[dismiss, duration, maxToasts],
|
|
50
102
|
)
|
|
51
103
|
|
|
52
104
|
const success = useCallback(
|
|
@@ -73,6 +125,13 @@ export const ToastProvider = ({
|
|
|
73
125
|
[show, success, error, dismiss],
|
|
74
126
|
)
|
|
75
127
|
|
|
128
|
+
useEffect(() => {
|
|
129
|
+
return () => {
|
|
130
|
+
timeoutIdsRef.current.forEach((timeoutId) => clearTimeout(timeoutId))
|
|
131
|
+
timeoutIdsRef.current.clear()
|
|
132
|
+
}
|
|
133
|
+
}, [])
|
|
134
|
+
|
|
76
135
|
return (
|
|
77
136
|
<ToastContext.Provider value={value}>
|
|
78
137
|
<Toast.Provider
|
|
@@ -98,6 +157,7 @@ export const ToastProvider = ({
|
|
|
98
157
|
title={toastItem.title}
|
|
99
158
|
text={toastItem.description}
|
|
100
159
|
className={styles.toastCallout}
|
|
160
|
+
style={getCalloutStyle(toastItem.variant)}
|
|
101
161
|
/>
|
|
102
162
|
</div>
|
|
103
163
|
</Toast.Description>
|