@moises.ai/design-system 4.18.12 → 4.18.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 +1431 -1425
- package/package.json +1 -1
- package/src/components/ProjectsList/ProjectTrackVoice/ProjectTrackVoice.jsx +4 -4
- package/src/components/ProjectsList/ProjectsList.jsx +3 -1
- package/src/components/ProjectsList/ProjectsList.stories.jsx +31 -0
- package/src/components/ProjectsList/context.js +1 -0
- package/src/components/SegmentedControl/SegmentedControl.jsx +25 -14
- package/src/components/SegmentedControl/SegmentedControl.stories.jsx +67 -4
- package/src/components/SegmentedControl/SegmentedControl.styles.css +18 -0
- package/src/components/Select/Select.jsx +9 -12
- package/src/components/Select/Select.module.css +5 -0
- package/src/components/Select/Select.stories.jsx +5 -3
- package/src/components/SetlistList/SetlistList.module.css +1 -1
package/package.json
CHANGED
|
@@ -3,12 +3,12 @@ import { useCallback, useContext, useEffect, useMemo, useState } from 'react'
|
|
|
3
3
|
import { ArrowLeftIcon } from '../../../icons'
|
|
4
4
|
import { ProjectsListContext } from '../context'
|
|
5
5
|
import { NumberPicker } from '../../NumberPicker/NumberPicker'
|
|
6
|
-
import {
|
|
6
|
+
import { UserVoiceIcon } from '../../../icons'
|
|
7
7
|
import { Button } from '../../Button/Button'
|
|
8
8
|
import styles from './ProjectTrackVoice.module.css'
|
|
9
9
|
|
|
10
10
|
const ProjectTrackVoice = ({ ...props }) => {
|
|
11
|
-
const { onClickTrack, onChangeTransfer, projects, labels = {} } = useContext(ProjectsListContext)
|
|
11
|
+
const { onClickTrack, onChangeTransfer, projects, labels = {}, showCover = true } = useContext(ProjectsListContext)
|
|
12
12
|
const { projectId, id, title, cover, transfers, audioUrl, loading: _loading } = props
|
|
13
13
|
|
|
14
14
|
const hasTransfers = useMemo(() => transfers && transfers.length > 0, [transfers])
|
|
@@ -82,9 +82,9 @@ const ProjectTrackVoice = ({ ...props }) => {
|
|
|
82
82
|
>
|
|
83
83
|
<div className={styles.cover}>
|
|
84
84
|
<Flex className={styles.coverImage}>
|
|
85
|
-
{cover
|
|
85
|
+
{cover && showCover
|
|
86
86
|
? <img src={cover} alt={title} width={48} height={48} />
|
|
87
|
-
: <
|
|
87
|
+
: <UserVoiceIcon width={16} height={16} />
|
|
88
88
|
}
|
|
89
89
|
</Flex>
|
|
90
90
|
<ArrowLeftIcon width={16} height={16} className={styles.iconArrowLeft} />
|
|
@@ -12,7 +12,8 @@ export const ProjectsList = ({
|
|
|
12
12
|
onInsertAllTracks,
|
|
13
13
|
onProjectClick,
|
|
14
14
|
onChangeTransfer,
|
|
15
|
-
labels = {}
|
|
15
|
+
labels = {},
|
|
16
|
+
showCover = true
|
|
16
17
|
}) => {
|
|
17
18
|
const [openedItem, setOpenedItem] = useState(null)
|
|
18
19
|
|
|
@@ -27,6 +28,7 @@ export const ProjectsList = ({
|
|
|
27
28
|
openedItem,
|
|
28
29
|
setOpenedItem,
|
|
29
30
|
labels,
|
|
31
|
+
showCover,
|
|
30
32
|
}}
|
|
31
33
|
>
|
|
32
34
|
<Flex direction="column" width="100%">
|
|
@@ -251,6 +251,37 @@ export const WithTrackVoice = {
|
|
|
251
251
|
},
|
|
252
252
|
}
|
|
253
253
|
|
|
254
|
+
export const WithTrackVoiceHiddenCover = {
|
|
255
|
+
args: {
|
|
256
|
+
...WithTrackVoice.args,
|
|
257
|
+
showCover: false,
|
|
258
|
+
},
|
|
259
|
+
render: (args) => {
|
|
260
|
+
return (
|
|
261
|
+
<Flex width="300px">
|
|
262
|
+
<ProjectsList
|
|
263
|
+
projects={args.projects}
|
|
264
|
+
onClickTrack={args.onClickTrack}
|
|
265
|
+
onInsertAllTracks={args.onInsertAllTracks}
|
|
266
|
+
onProjectClick={args.onProjectClick}
|
|
267
|
+
onChangeTransfer={args.onChangeTransfer}
|
|
268
|
+
showCover={args.showCover}
|
|
269
|
+
>
|
|
270
|
+
{({ projectId, tracks }) =>
|
|
271
|
+
tracks.map((track) => (
|
|
272
|
+
<ProjectsList.TrackVoice
|
|
273
|
+
projectId={projectId}
|
|
274
|
+
key={track.id}
|
|
275
|
+
{...track}
|
|
276
|
+
/>
|
|
277
|
+
))
|
|
278
|
+
}
|
|
279
|
+
</ProjectsList>
|
|
280
|
+
</Flex>
|
|
281
|
+
)
|
|
282
|
+
},
|
|
283
|
+
}
|
|
284
|
+
|
|
254
285
|
export const WithTrackVoiceStates = {
|
|
255
286
|
args: {
|
|
256
287
|
projects: [{
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
1
|
import {
|
|
3
2
|
Flex,
|
|
4
3
|
SegmentedControl as SegmentedControlRadix,
|
|
@@ -15,6 +14,9 @@ export const SegmentedControl = ({
|
|
|
15
14
|
iconProps = {},
|
|
16
15
|
...props
|
|
17
16
|
}) => {
|
|
17
|
+
const hasIconWithLabel = items.some((item) => item.icon && item.label)
|
|
18
|
+
const isIconOnly = items.length > 0 && items.every((item) => item.icon && !item.label)
|
|
19
|
+
|
|
18
20
|
return (
|
|
19
21
|
<Flex padding="1" width="100%">
|
|
20
22
|
<SegmentedControlRadix.Root
|
|
@@ -24,22 +26,31 @@ export const SegmentedControl = ({
|
|
|
24
26
|
variant="surface"
|
|
25
27
|
radius="medium"
|
|
26
28
|
className={classNames('customSegmentedControl', className, {
|
|
27
|
-
hasIconSegmentedControl:
|
|
29
|
+
hasIconSegmentedControl: hasIconWithLabel,
|
|
30
|
+
isIconOnlySegmentedControl: isIconOnly,
|
|
28
31
|
})}
|
|
29
32
|
{...props}
|
|
30
33
|
>
|
|
31
|
-
{items.map((item) =>
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
34
|
+
{items.map((item) => {
|
|
35
|
+
const isItemIconOnly = Boolean(item.icon && !item.label)
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<SegmentedControlRadix.Item
|
|
39
|
+
key={item.value}
|
|
40
|
+
value={item.value}
|
|
41
|
+
aria-label={
|
|
42
|
+
isItemIconOnly ? item.ariaLabel || item.value : undefined
|
|
43
|
+
}
|
|
44
|
+
className={classNames('customSegmentedControlItem', {
|
|
45
|
+
hasIcon: item.icon && item.label,
|
|
46
|
+
isIconOnly: isItemIconOnly,
|
|
47
|
+
})}
|
|
48
|
+
>
|
|
49
|
+
{item.icon && <item.icon {...iconProps} />}
|
|
50
|
+
{item.label}
|
|
51
|
+
</SegmentedControlRadix.Item>
|
|
52
|
+
)
|
|
53
|
+
})}
|
|
43
54
|
</SegmentedControlRadix.Root>
|
|
44
55
|
</Flex>
|
|
45
56
|
)
|
|
@@ -2,7 +2,13 @@ import { Flex } from '@radix-ui/themes'
|
|
|
2
2
|
import { SegmentedControl } from './SegmentedControl'
|
|
3
3
|
import { useState } from 'react'
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
AcousticGuitarIcon,
|
|
7
|
+
BassIcon,
|
|
8
|
+
DrumsIcon,
|
|
9
|
+
ElectricGuitarIcon,
|
|
10
|
+
PianoIcon,
|
|
11
|
+
} from '../../icons'
|
|
6
12
|
|
|
7
13
|
export default {
|
|
8
14
|
title: 'Components/SegmentedControl',
|
|
@@ -30,7 +36,7 @@ import { SegmentedControl } from '@moises.ai/design-system'
|
|
|
30
36
|
size="2"
|
|
31
37
|
/>
|
|
32
38
|
|
|
33
|
-
// With icons and
|
|
39
|
+
// With icons and labels
|
|
34
40
|
<SegmentedControl
|
|
35
41
|
items={[
|
|
36
42
|
{ value: 'bass', label: 'Bass', icon: BassIcon },
|
|
@@ -40,6 +46,18 @@ import { SegmentedControl } from '@moises.ai/design-system'
|
|
|
40
46
|
handleTypeChange={handleTypeChange}
|
|
41
47
|
iconProps={{ width: 20, height: 20 }}
|
|
42
48
|
/>
|
|
49
|
+
|
|
50
|
+
// Icon-only (omit label; optionally pass ariaLabel for accessibility)
|
|
51
|
+
<SegmentedControl
|
|
52
|
+
items={[
|
|
53
|
+
{ value: 'guitar', icon: ElectricGuitarIcon, ariaLabel: 'Electric guitar' },
|
|
54
|
+
{ value: 'piano', icon: PianoIcon, ariaLabel: 'Piano' },
|
|
55
|
+
{ value: 'acoustic', icon: AcousticGuitarIcon, ariaLabel: 'Acoustic guitar' },
|
|
56
|
+
]}
|
|
57
|
+
selectedType={selectedType}
|
|
58
|
+
handleTypeChange={handleTypeChange}
|
|
59
|
+
iconProps={{ width: 18, height: 18 }}
|
|
60
|
+
/>
|
|
43
61
|
\`\`\`
|
|
44
62
|
|
|
45
63
|
## Component API
|
|
@@ -49,8 +67,9 @@ import { SegmentedControl } from '@moises.ai/design-system'
|
|
|
49
67
|
items: [
|
|
50
68
|
{
|
|
51
69
|
value: string, // The value of the item
|
|
52
|
-
label
|
|
53
|
-
icon?: React.ComponentType // The icon component (optional)
|
|
70
|
+
label?: string, // The label of the item (omit for icon-only)
|
|
71
|
+
icon?: React.ComponentType, // The icon component (optional)
|
|
72
|
+
ariaLabel?: string // Accessible name when using icon-only (falls back to value)
|
|
54
73
|
}
|
|
55
74
|
],
|
|
56
75
|
selectedType: string, // The value of the selected item
|
|
@@ -60,6 +79,8 @@ import { SegmentedControl } from '@moises.ai/design-system'
|
|
|
60
79
|
iconProps?: object, // Props to pass to all icons (e.g., { width: 20, height: 20 })
|
|
61
80
|
}
|
|
62
81
|
\`\`\`
|
|
82
|
+
|
|
83
|
+
When every item has an \`icon\` and no \`label\`, the control switches to icon-only layout automatically.
|
|
63
84
|
`,
|
|
64
85
|
},
|
|
65
86
|
},
|
|
@@ -131,3 +152,45 @@ export const WithIcons = {
|
|
|
131
152
|
)
|
|
132
153
|
},
|
|
133
154
|
}
|
|
155
|
+
|
|
156
|
+
export const IconOnly = {
|
|
157
|
+
args: {
|
|
158
|
+
items: [
|
|
159
|
+
{
|
|
160
|
+
value: 'guitar',
|
|
161
|
+
icon: ElectricGuitarIcon,
|
|
162
|
+
ariaLabel: 'Electric guitar',
|
|
163
|
+
},
|
|
164
|
+
{ value: 'piano', icon: PianoIcon, ariaLabel: 'Piano' },
|
|
165
|
+
{
|
|
166
|
+
value: 'acoustic',
|
|
167
|
+
icon: AcousticGuitarIcon,
|
|
168
|
+
ariaLabel: 'Acoustic guitar',
|
|
169
|
+
},
|
|
170
|
+
],
|
|
171
|
+
iconProps: { width: 18, height: 18 },
|
|
172
|
+
initialSelectedType: 'guitar',
|
|
173
|
+
},
|
|
174
|
+
parameters: {
|
|
175
|
+
docs: {
|
|
176
|
+
description: {
|
|
177
|
+
story:
|
|
178
|
+
'Omit `label` on every item to enable the icon-only layout. Prefer `ariaLabel` for screen readers.',
|
|
179
|
+
},
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
render: (args) => {
|
|
183
|
+
const [selectedType, setSelectedType] = useState(args.initialSelectedType)
|
|
184
|
+
return (
|
|
185
|
+
<Flex width="200px">
|
|
186
|
+
<SegmentedControl
|
|
187
|
+
{...args}
|
|
188
|
+
selectedType={selectedType}
|
|
189
|
+
handleTypeChange={(value) => {
|
|
190
|
+
setSelectedType(value)
|
|
191
|
+
}}
|
|
192
|
+
/>
|
|
193
|
+
</Flex>
|
|
194
|
+
)
|
|
195
|
+
},
|
|
196
|
+
}
|
|
@@ -12,6 +12,10 @@
|
|
|
12
12
|
height: auto !important;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
.isIconOnlySegmentedControl {
|
|
16
|
+
height: auto !important;
|
|
17
|
+
}
|
|
18
|
+
|
|
15
19
|
.rt-SegmentedControlIndicator {
|
|
16
20
|
&::before {
|
|
17
21
|
border-radius: 4px !important;
|
|
@@ -53,3 +57,17 @@
|
|
|
53
57
|
padding-top: 16px;
|
|
54
58
|
}
|
|
55
59
|
}
|
|
60
|
+
|
|
61
|
+
.isIconOnly {
|
|
62
|
+
.rt-SegmentedControlItemLabel {
|
|
63
|
+
flex-direction: row;
|
|
64
|
+
gap: 0;
|
|
65
|
+
min-width: 0;
|
|
66
|
+
padding: 8px 12px;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
svg {
|
|
70
|
+
display: block;
|
|
71
|
+
flex-shrink: 0;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -16,6 +16,7 @@ export const Select = ({
|
|
|
16
16
|
title,
|
|
17
17
|
onChange,
|
|
18
18
|
defaultValue,
|
|
19
|
+
value,
|
|
19
20
|
items,
|
|
20
21
|
info,
|
|
21
22
|
size = '2',
|
|
@@ -28,6 +29,10 @@ export const Select = ({
|
|
|
28
29
|
placeholder = null,
|
|
29
30
|
...props
|
|
30
31
|
}) => {
|
|
32
|
+
const resolvedValue = value === null ? '' : value
|
|
33
|
+
const resolvedDefaultValue =
|
|
34
|
+
defaultValue === null ? undefined : defaultValue
|
|
35
|
+
|
|
31
36
|
return (
|
|
32
37
|
<Box width="100%">
|
|
33
38
|
{title && (
|
|
@@ -45,7 +50,8 @@ export const Select = ({
|
|
|
45
50
|
<Flex direction="column" maxWidth={maxWidth}>
|
|
46
51
|
<SelectRadix.Root
|
|
47
52
|
onValueChange={onChange}
|
|
48
|
-
defaultValue={
|
|
53
|
+
defaultValue={resolvedDefaultValue}
|
|
54
|
+
value={resolvedValue}
|
|
49
55
|
className={classNames(className)}
|
|
50
56
|
disabled={disabled || readOnly}
|
|
51
57
|
{...props}
|
|
@@ -54,21 +60,12 @@ export const Select = ({
|
|
|
54
60
|
variant={variant}
|
|
55
61
|
color="gray"
|
|
56
62
|
size={size}
|
|
63
|
+
placeholder={placeholder}
|
|
57
64
|
className={classNames(styles.selectTrigger, {
|
|
58
65
|
[styles.ghost]: variant === 'ghost',
|
|
59
66
|
[styles.soft]: variant === 'soft',
|
|
60
67
|
})}
|
|
61
|
-
|
|
62
|
-
{defaultValue === null ? (
|
|
63
|
-
<Text
|
|
64
|
-
size={size}
|
|
65
|
-
ml="1"
|
|
66
|
-
style={{ color: 'var(--neutral-alpha-9)' }}
|
|
67
|
-
>
|
|
68
|
-
{placeholder}
|
|
69
|
-
</Text>
|
|
70
|
-
) : undefined}
|
|
71
|
-
</SelectRadix.Trigger>
|
|
68
|
+
/>
|
|
72
69
|
<SelectRadix.Content
|
|
73
70
|
position="popper"
|
|
74
71
|
className={styles.selectContent}
|
|
@@ -83,6 +83,11 @@
|
|
|
83
83
|
background-color: var(--neutral-alpha-4);
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
+
.selectTrigger.soft[data-placeholder],
|
|
87
|
+
.selectTrigger.ghost[data-placeholder] {
|
|
88
|
+
color: var(--neutral-alpha-9);
|
|
89
|
+
}
|
|
90
|
+
|
|
86
91
|
/* Ghost variant */
|
|
87
92
|
.selectTrigger.ghost {
|
|
88
93
|
background-color: transparent;
|
|
@@ -46,7 +46,9 @@ import { Select } from '@moises.ai/design-system'
|
|
|
46
46
|
}[], // List of items to display in the select
|
|
47
47
|
info?: string, // Optional info text displayed as a tooltip
|
|
48
48
|
onChange: (value: string) => void, // Function called when selection changes
|
|
49
|
-
defaultValue?: string, // Initial selected value
|
|
49
|
+
defaultValue?: string, // Initial selected value (uncontrolled)
|
|
50
|
+
value?: string, // Selected value (controlled). Omit or pass undefined when empty
|
|
51
|
+
placeholder?: string, // Shown when no value is selected
|
|
50
52
|
size?: '1' | '2' | '3', // Size variant of the select (default: '2')
|
|
51
53
|
variant?: 'soft' | 'ghost', // Visual style variant (default: 'soft')
|
|
52
54
|
disabled?: boolean, // Whether the select is disabled (default: false)
|
|
@@ -255,12 +257,12 @@ export const WithAIDetected = {
|
|
|
255
257
|
}
|
|
256
258
|
|
|
257
259
|
const SelectWithPlaceholderModule = () => {
|
|
258
|
-
const [selected, setSelected] = useState(
|
|
260
|
+
const [selected, setSelected] = useState('')
|
|
259
261
|
return (
|
|
260
262
|
<Select
|
|
261
263
|
title="Instrument"
|
|
262
264
|
size="2"
|
|
263
|
-
|
|
265
|
+
value={selected}
|
|
264
266
|
items={[
|
|
265
267
|
{ value: 'bass', label: 'Bass' },
|
|
266
268
|
{ value: 'guitar', label: 'Guitar' },
|