@moises.ai/design-system 3.10.18 → 3.11.0

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.10.18",
3
+ "version": "3.11.0",
4
4
  "description": "Design System package based on @radix-ui/themes with custom defaults",
5
5
  "private": false,
6
6
  "type": "module",
@@ -0,0 +1,55 @@
1
+ import styles from './TrackControlsToggle.module.css'
2
+ import classNames from 'classnames'
3
+ import { Text, Tooltip, Flex } from '../../index'
4
+
5
+ export const TrackControlsToggle = ({ children, className, type, selected, onClick, tooltipContent, ...props }) => {
6
+ const toggleType = {
7
+ record: 'R',
8
+ mute: 'M',
9
+ autoMute: 'M',
10
+ solo: 'S',
11
+ }
12
+
13
+ const content = () => {
14
+ return (
15
+ <Flex className={classNames(styles.TrackControlsToggle, className, {
16
+ [styles.selected]: selected,
17
+ [styles.record]: type === 'record',
18
+ [styles.mute]: type === 'mute',
19
+ [styles.autoMute]: type === 'autoMute',
20
+ [styles.solo]: type === 'solo',
21
+ })}
22
+ onClick={onClick}
23
+ role="button"
24
+ tabIndex={0}
25
+ aria-pressed={selected}
26
+ onKeyDown={(e) => {
27
+ if (e.key === 'Enter') {
28
+ e.preventDefault()
29
+ onClick?.()
30
+ }
31
+ }}
32
+ {...props}>
33
+ <Text size="1" weight="regular">{toggleType[type]}</Text>
34
+ </Flex>
35
+ )
36
+
37
+ }
38
+
39
+ return (
40
+ <>
41
+ {tooltipContent && (
42
+ <Tooltip content={tooltipContent?.text} color='gray' shortcut={tooltipContent?.shortcut}>
43
+ {content()}
44
+ </Tooltip>
45
+ )}
46
+ {!tooltipContent && (
47
+ <>
48
+ {content()}
49
+ </>
50
+ )}
51
+ </>
52
+ )
53
+ }
54
+
55
+ TrackControlsToggle.displayName = 'TrackControlsToggle'
@@ -0,0 +1,49 @@
1
+ .TrackControlsToggle {
2
+ border-radius: 4px;
3
+ background: var(--neutral-alpha-2);
4
+ display: inline-flex;
5
+ padding: 2px;
6
+ justify-content: center;
7
+ align-items: center;
8
+ gap: 4px;
9
+
10
+ color: var(--neutral-alpha-10);
11
+
12
+ width: 20px;
13
+ height: 20px;
14
+
15
+ &:focus-visible:not(.disabled){
16
+ outline: 2px solid var(--neutral-alpha-8);
17
+ outline-offset: 2px;
18
+ }
19
+ }
20
+
21
+ .TrackControlsToggle:hover {
22
+ background: var(--neutral-alpha-3);
23
+ cursor: pointer;
24
+ }
25
+
26
+ .selected.record {
27
+ background: rgba(255, 100, 101, 0.92);
28
+ color: var(--neutral-1);
29
+ }
30
+
31
+ .selected.mute {
32
+ background: #FFC53D;
33
+ color: var(--neutral-1);
34
+ }
35
+
36
+ .selected.autoMute {
37
+ background: var(--neutral-alpha-2);
38
+ color: #FFC53D;
39
+ }
40
+
41
+ .selected.autoMute:hover {
42
+ background: var(--neutral-alpha-3);
43
+ cursor: pointer;
44
+ }
45
+
46
+ .selected.solo {
47
+ background: var(--aqua-10);
48
+ color: var(--neutral-1);
49
+ }
@@ -0,0 +1,156 @@
1
+ import { useState } from 'react'
2
+ import { TrackControlsToggle } from './TrackControlsToggle'
3
+
4
+ export default {
5
+ title: 'Components/TrackControlsToggle',
6
+ component: TrackControlsToggle,
7
+ parameters: {
8
+ layout: 'centered',
9
+ docs: {
10
+ description: {
11
+ component: 'Toggle button for track controls (Record, Mute, Auto Mute, and Solo). Use `selected` to represent active state and `tooltipContent` to show context and keyboard shortcut.',
12
+ },
13
+ },
14
+ },
15
+ tags: ['autodocs'],
16
+ argTypes: {
17
+ type: {
18
+ control: 'select',
19
+ options: ['record', 'mute', 'autoMute', 'solo'],
20
+ description: 'Defines the control type and the displayed label (R, M, or S).',
21
+ table: {
22
+ type: { summary: "'record' | 'mute' | 'autoMute' | 'solo'" },
23
+ defaultValue: { summary: "'solo'" },
24
+ },
25
+ },
26
+ selected: {
27
+ control: 'boolean',
28
+ description: 'Controls the visual active/inactive state of the toggle.',
29
+ table: {
30
+ type: { summary: 'boolean' },
31
+ defaultValue: { summary: 'false' },
32
+ },
33
+ },
34
+ onClick: {
35
+ action: 'clicked',
36
+ description: 'Triggered when clicking (or pressing Enter/Space) on the toggle.',
37
+ table: {
38
+ type: { summary: '() => void' },
39
+ },
40
+ },
41
+ tooltipContent: {
42
+ control: 'object',
43
+ description: 'Tooltip data in the `{ text, shortcut }` format.',
44
+ table: {
45
+ type: { summary: '{ text?: string; shortcut?: string }' },
46
+ },
47
+ },
48
+ children: {
49
+ control: false,
50
+ table: {
51
+ disable: true,
52
+ },
53
+ },
54
+ },
55
+ }
56
+
57
+ export const Default = {
58
+ args: {
59
+ type: 'solo',
60
+ selected: true,
61
+ tooltipContent: {
62
+ text: 'Tooltip Content',
63
+ shortcut: '⌘ C',
64
+ },
65
+ },
66
+ }
67
+
68
+ export const Inactive = {
69
+ args: {
70
+ type: 'solo',
71
+ selected: false,
72
+ tooltipContent: {
73
+ text: 'Solo',
74
+ shortcut: 'S',
75
+ },
76
+ },
77
+ }
78
+
79
+ export const RecordSelected = {
80
+ args: {
81
+ type: 'record',
82
+ selected: true,
83
+ tooltipContent: {
84
+ text: 'Record',
85
+ shortcut: 'R',
86
+ },
87
+ },
88
+ }
89
+
90
+ export const MuteSelected = {
91
+ args: {
92
+ type: 'mute',
93
+ selected: true,
94
+ tooltipContent: {
95
+ text: 'Mute',
96
+ shortcut: 'M',
97
+ },
98
+ },
99
+ }
100
+
101
+ export const AllStates = {
102
+ parameters: {
103
+ docs: {
104
+ description: {
105
+ story: 'Shows all states with and without tooltip. Click any toggle to switch between inactive and active.',
106
+ },
107
+ },
108
+ },
109
+ render: (args) => {
110
+ const InteractiveGroup = ({ withTooltip }) => {
111
+ const [states, setStates] = useState({
112
+ record: false,
113
+ mute: false,
114
+ autoMute: false,
115
+ solo: false,
116
+ })
117
+
118
+ const types = ['record', 'mute', 'autoMute', 'solo']
119
+
120
+ const toggleState = (type) => {
121
+ setStates((prev) => ({ ...prev, [type]: !prev[type] }))
122
+ }
123
+
124
+ return (
125
+ <div style={{ display: 'flex', gap: 8 }}>
126
+ {types.map((type) => (
127
+ <TrackControlsToggle
128
+ key={`${withTooltip ? 'with' : 'without'}-${type}`}
129
+ {...args}
130
+ type={type}
131
+ selected={states[type]}
132
+ tooltipContent={withTooltip ? { text: `${type}`, shortcut: type.slice(0, 1).toUpperCase() } : undefined}
133
+ onClick={() => {
134
+ args.onClick?.()
135
+ toggleState(type)
136
+ }}
137
+ />
138
+ ))}
139
+ </div>
140
+ )
141
+ }
142
+
143
+ return (
144
+ <div style={{ display: 'grid', gap: 14 }}>
145
+ <div style={{ display: 'grid', gap: 8 }}>
146
+ <span style={{ color: '#9da3b4', fontSize: 12 }}>With tooltip</span>
147
+ <InteractiveGroup withTooltip />
148
+ </div>
149
+ <div style={{ display: 'grid', gap: 8 }}>
150
+ <span style={{ color: '#9da3b4', fontSize: 12 }}>Without tooltip</span>
151
+ <InteractiveGroup withTooltip={false} />
152
+ </div>
153
+ </div>
154
+ )
155
+ },
156
+ }
@@ -1,27 +1,43 @@
1
- import { DotsVerticalIcon } from '@radix-ui/react-icons'
2
- import { IconButton } from '@radix-ui/themes'
3
1
  import classNames from 'classnames'
4
2
  import { memo, useCallback } from 'react'
5
- import { DropdownMenu, HorizontalVolume, PanControl, Text, Tooltip } from '../../index'
6
- import { TrackControlButton } from '../TrackControlButton'
3
+ import {
4
+ ChevronDownFilledIcon,
5
+ ChevronRightFilledIcon,
6
+ More2Icon,
7
+ } from '../../icons'
8
+ import {
9
+ DropdownMenu,
10
+ Flex,
11
+ HorizontalVolume,
12
+ IconButton,
13
+ PanControl,
14
+ Text,
15
+ Tooltip,
16
+ TrackControlsToggle,
17
+ } from '../../index'
7
18
  import styles from './TrackHeader.module.css'
8
- import { TrackHeaderOptions } from './TrackHeaderOptions'
9
19
 
10
20
  export const TrackHeader = memo(
11
21
  ({
12
22
  // Config
13
23
  title,
14
24
  menuOptions,
25
+ instrumentOptions,
26
+ onInstrumentChange,
27
+ instrumentSelected,
15
28
  showPan = true,
16
29
  showVolumeControls = true,
17
30
  isGrouped = false,
18
31
  height = 81,
19
32
  compact = false,
33
+ isActive = false,
20
34
 
21
35
  // State
22
36
  volume,
23
37
  pan,
24
38
  isMuted,
39
+ isRecord,
40
+ isAutoMuted,
25
41
  isSolo,
26
42
  isOpen,
27
43
 
@@ -29,6 +45,7 @@ export const TrackHeader = memo(
29
45
  onVolumeChange,
30
46
  onPanChange,
31
47
  onMutedChange,
48
+ onRecordChange,
32
49
  onSoloChange,
33
50
  onOpenChange,
34
51
 
@@ -36,10 +53,15 @@ export const TrackHeader = memo(
36
53
  children,
37
54
  }) => {
38
55
  const hasChildren = Boolean(children)
39
- const handleToggleOpen = useCallback(() => onOpenChange?.(!isOpen), [isOpen, onOpenChange])
56
+ const handleToggleOpen = useCallback(
57
+ () => onOpenChange?.(!isOpen),
58
+ [isOpen, onOpenChange],
59
+ )
40
60
 
41
61
  return (
42
- <div className={classNames(styles.container, { [styles.isOpen]: isOpen })}>
62
+ <div
63
+ className={classNames(styles.container, { [styles.isOpen]: isOpen })}
64
+ >
43
65
  <div
44
66
  className={classNames(styles.track, {
45
67
  [styles.isParentTrack]: hasChildren,
@@ -47,46 +69,125 @@ export const TrackHeader = memo(
47
69
  })}
48
70
  style={{ height: `${height}px` }}
49
71
  >
50
- <div className={styles.trackContent}>
72
+ <Flex
73
+ className={classNames(styles.trackContent, {
74
+ [styles.isActive]: isActive,
75
+ })}
76
+ direction="column"
77
+ gap="2"
78
+ p="12px 12px 12px 4px"
79
+ >
51
80
  <div className={styles.line}>
52
- <div className={styles.left}>
53
- <div
54
- className={styles.leftIcon}
81
+ <Flex
82
+ align="center"
83
+ justify="between"
84
+ direction="row"
85
+ gap="3"
86
+ className={styles.headerActions}
87
+ >
88
+ <Flex
89
+ align="center"
90
+ direction="row"
91
+ gap="1"
92
+ flexGrow="1"
93
+ style={{ minWidth: 0, overflow: 'hidden' }}
94
+ >
95
+ <IconButton
96
+ onPointerDown={(e) => e.stopPropagation()}
97
+ onMouseDown={(e) => e.stopPropagation()}
98
+ onTouchStart={(e) => e.stopPropagation()}
99
+ variant="ghost"
100
+ size="1"
101
+ onClick={handleToggleOpen}
102
+ className={styles.toggleOpenButton}
103
+ >
104
+ {isOpen ? (
105
+ <ChevronDownFilledIcon />
106
+ ) : (
107
+ <ChevronRightFilledIcon />
108
+ )}
109
+ </IconButton>
110
+ {instrumentOptions && (
111
+ <DropdownMenu
112
+ showActiveTrigger={true}
113
+ trigger={
114
+ <IconButton
115
+ variant="ghost"
116
+ size="1"
117
+ className={classNames(
118
+ styles.instrumentIconButton,
119
+ styles.menuOptionsTrigger,
120
+ )}
121
+ >
122
+ {instrumentSelected.icon}
123
+ </IconButton>
124
+ }
125
+ options={instrumentOptions}
126
+ onValueChange={onInstrumentChange}
127
+ side="bottom"
128
+ align="start"
129
+ />
130
+ )}
131
+
132
+ <Tooltip content={title}>
133
+ <Text
134
+ size="2"
135
+ className={styles.name}
136
+ onClick={hasChildren ? handleToggleOpen : undefined}
137
+ >
138
+ {title}
139
+ </Text>
140
+ </Tooltip>
141
+ </Flex>
142
+
143
+ <Flex
144
+ direction="row"
145
+ gap="3"
146
+ align="center"
55
147
  onPointerDown={(e) => e.stopPropagation()}
56
148
  onMouseDown={(e) => e.stopPropagation()}
57
149
  onTouchStart={(e) => e.stopPropagation()}
58
150
  >
59
- <TrackHeaderOptions isOpen={isOpen} onToggleOpen={handleToggleOpen} />
60
- </div>
61
-
62
- <Tooltip content={title}>
63
- <Text
64
- size="2"
65
- className={styles.name}
66
- onClick={hasChildren ? handleToggleOpen : undefined}
67
- >
68
- {title}
69
- </Text>
70
- </Tooltip>
71
- </div>
151
+ <Flex gap="2px">
152
+ <TrackControlsToggle
153
+ type="record"
154
+ selected={isRecord}
155
+ onClick={onRecordChange}
156
+ />
157
+ <TrackControlsToggle
158
+ type={
159
+ isMuted ? 'mute' : isAutoMuted ? 'autoMute' : 'mute'
160
+ }
161
+ selected={isMuted}
162
+ onClick={onMutedChange}
163
+ />
164
+ <TrackControlsToggle
165
+ type="solo"
166
+ selected={isSolo}
167
+ onClick={onSoloChange}
168
+ />
169
+ </Flex>
170
+ </Flex>
72
171
 
73
- <div
74
- className={styles.menuOptions}
75
- onPointerDown={(e) => e.stopPropagation()}
76
- onMouseDown={(e) => e.stopPropagation()}
77
- onTouchStart={(e) => e.stopPropagation()}
78
- >
79
172
  {menuOptions && (
80
173
  <DropdownMenu
81
174
  trigger={
82
- <IconButton className={styles.menuOptionsTrigger} variant="ghost" color="gray" size="1">
83
- <DotsVerticalIcon width={16} height={16} />
175
+ <IconButton
176
+ variant="ghost"
177
+ size="1"
178
+ className={styles.menuOptionsTrigger}
179
+ >
180
+ <More2Icon
181
+ width={16}
182
+ height={16}
183
+ className={styles.menuOptionsIcon}
184
+ />
84
185
  </IconButton>
85
186
  }
86
187
  options={menuOptions}
87
188
  />
88
189
  )}
89
- </div>
190
+ </Flex>
90
191
  </div>
91
192
 
92
193
  <div
@@ -101,39 +202,23 @@ export const TrackHeader = memo(
101
202
  volume={volume}
102
203
  onChangeValue={onVolumeChange}
103
204
  />
104
- {showPan && <PanControl className={styles.pan} value={pan} onChange={onPanChange} minValue={-1} maxValue={1}/>}
205
+ {showPan && (
206
+ <PanControl
207
+ className={styles.pan}
208
+ value={pan}
209
+ onChange={onPanChange}
210
+ minValue={-1}
211
+ maxValue={1}
212
+ />
213
+ )}
105
214
  </>
106
215
  )}
107
216
  </div>
108
- </div>
109
-
110
- {showVolumeControls && (
111
- <div
112
- className={styles.right}
113
- onPointerDown={(e) => e.stopPropagation()}
114
- onMouseDown={(e) => e.stopPropagation()}
115
- onTouchStart={(e) => e.stopPropagation()}
116
- >
117
- <TrackControlButton
118
- className={styles.rightButton}
119
- variant="mute"
120
- isActive={isMuted}
121
- onChange={onMutedChange}
122
- />
123
- <TrackControlButton
124
- className={styles.rightButton}
125
- variant="solo"
126
- isActive={isSolo}
127
- onChange={onSoloChange}
128
- />
129
- </div>
130
- )}
217
+ </Flex>
131
218
  </div>
132
219
 
133
220
  {hasChildren && isOpen && (
134
- <div className={styles.group}>
135
- {children}
136
- </div>
221
+ <div className={styles.group}>{children}</div>
137
222
  )}
138
223
  </div>
139
224
  )