@moises.ai/design-system 3.7.1 → 3.7.3

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.7.1",
3
+ "version": "3.7.3",
4
4
  "description": "Design System package based on @radix-ui/themes with custom defaults",
5
5
  "private": false,
6
6
  "type": "module",
@@ -1,7 +1,6 @@
1
1
  import React, { memo } from 'react'
2
2
  import { ContextMenu as ContextMenuRadix } from 'radix-ui'
3
3
  import classNames from 'classnames'
4
-
5
4
  import { createRenderItem, styles } from '../../lib/menu'
6
5
 
7
6
  const renderOption = createRenderItem(ContextMenuRadix)
@@ -1,4 +1,4 @@
1
- import { Flex } from '../../index'
1
+ import { Flex, Text } from '../../index'
2
2
  import { ProfileMenu } from './ProfileMenu'
3
3
 
4
4
  export default {
@@ -15,6 +15,34 @@ export const Default = {
15
15
  plan: 'Premium',
16
16
  },
17
17
  menuOptions: [
18
+ {
19
+ type: 'custom',
20
+ key: 'credits-header',
21
+ customRender: () => <div key="credits-header">
22
+ <div style={{ display: 'flex', alignItems: 'center', gap: 8 }} className="showActiveTrigger">
23
+ <div>
24
+ <Text style={{ fontSize: 14, fontWeight: 'normal', lineHeight: '20px' }}>Credits:</Text>{` `}
25
+ <Text style={{ color: '#fff', fontSize: 14, fontWeight: 400 }}>45</Text>
26
+ <Text style={{ color: '#fff', fontSize: 14, fontWeight: 400, marginLeft: 4 }}>/60</Text >
27
+ </div>
28
+ <button
29
+ type="button"
30
+ aria-label="Upgrade"
31
+ title="Upgrade"
32
+ style={{ border: 'none', cursor: 'pointer', backgroundColor: '#00dae8', color: '#061415', fontSize: 12, fontWeight: 500, padding: '4px 8px', borderRadius: 4 }}
33
+ onClick={() => {
34
+ alert('Upgrade clicked')
35
+ }}
36
+ >
37
+ Upgrade
38
+ </button>
39
+ </div>
40
+ </div>
41
+ },
42
+ {
43
+ type: 'separator',
44
+ key: 'separator-credits',
45
+ },
18
46
  {
19
47
  type: 'item',
20
48
  key: 'account-settings',
@@ -0,0 +1,136 @@
1
+ import { Flex, Text, Separator, Spinner, Button } from '@radix-ui/themes'
2
+ import { useState, useContext, useCallback, useMemo } from 'react'
3
+ import { ArrowLeftIcon, NoMusicIcon, ChevronDownIcon, ChevronUpIcon, InfoIcon } from '../../../icons'
4
+ import { ProjectsListContext } from '../context'
5
+ import { ProductsBrandPattern } from '../../ProductsBrandPattern/ProductsBrandPattern'
6
+ import styles from './ProjectItem.module.css'
7
+
8
+ const ProjectItem = ({
9
+ id,
10
+ title,
11
+ artist,
12
+ cover,
13
+ type,
14
+ tracks: allTracks = [],
15
+ loading = false,
16
+ failed = false,
17
+ children,
18
+ ...props
19
+ }) => {
20
+ const {
21
+ projects,
22
+ onInsertAllTracks,
23
+ onProjectClick,
24
+ openedItem,
25
+ setOpenedItem,
26
+ } = useContext(ProjectsListContext)
27
+ const [showAllTracks, setShowAllTracks] = useState(false)
28
+
29
+ const tracks = useMemo(() => {
30
+ return showAllTracks ? allTracks : allTracks.slice(0, 6)
31
+ }, [allTracks, showAllTracks])
32
+
33
+ const handleClickProject = useCallback(() => {
34
+ if (failed) return
35
+ setShowAllTracks(false)
36
+ const project = projects.find((p) => p.id === id)
37
+
38
+ if (openedItem === id) {
39
+ setOpenedItem(null)
40
+ onProjectClick?.(project, false)
41
+ } else {
42
+ setOpenedItem(id)
43
+ onProjectClick?.(project, true)
44
+ }
45
+ }, [id, openedItem, setOpenedItem, onProjectClick, projects, failed])
46
+
47
+ const handleClickShowAllTracks = useCallback((e) => {
48
+ e.preventDefault()
49
+ e.stopPropagation()
50
+ setShowAllTracks(!showAllTracks)
51
+ }, [showAllTracks])
52
+
53
+ const handleInsertAllTracks = useCallback((e) => {
54
+ e.stopPropagation()
55
+ onInsertAllTracks?.(allTracks)
56
+ }, [onInsertAllTracks, allTracks])
57
+
58
+ return (
59
+ <Flex
60
+ direction="column"
61
+ gap="2"
62
+ p="1"
63
+ className={`${styles.item} ${openedItem === id ? styles.opened : ''} ${failed ? styles.failed : ''}`}
64
+ onClick={handleClickProject}
65
+ {...props}
66
+ >
67
+ <Flex direction="row" gap="3">
68
+ <Flex className={styles.projectCover}>
69
+ <ProductsBrandPattern type={type} cover={cover} size="48px" />
70
+ </Flex>
71
+
72
+ <Flex direction="column" gap="1" align="start" justify="center">
73
+ <Text size="2" className={styles.projectTitle}>
74
+ {title}
75
+ </Text>
76
+
77
+ {failed ? (
78
+ <Flex size="1" gap="1" className={styles.projectFailed}>
79
+ <InfoIcon width={16} height={16} />
80
+ <Text size="1">Failed</Text>
81
+ </Flex>
82
+ ) : artist && (
83
+ <Text size="1" className={styles.projectArtist}>
84
+ {artist}
85
+ </Text>
86
+ )}
87
+ </Flex>
88
+ </Flex>
89
+
90
+ {openedItem === id && (
91
+ <Flex direction="column">
92
+ {allTracks.length === 0 && !loading && (
93
+ <Flex direction="row" gap="3" p="5" pt="2" align="center" justify="center">
94
+ <NoMusicIcon width={16} height={16} style={{ color: 'var(--neutral-alpha-10)' }} />
95
+ <Text size="2" style={{ color: 'var(--neutral-alpha-10)' }}>This file has no tracks</Text>
96
+ </Flex>
97
+ )}
98
+
99
+ {loading && (
100
+ <Flex direction="row" gap="3" p="5" align="center" justify="center">
101
+ <Spinner width={16} height={16} style={{ color: 'var(--neutral-alpha-10)' }} />
102
+ <Text size="2" style={{ color: 'var(--neutral-alpha-10)' }}>Loading tracks...</Text>
103
+ </Flex>
104
+ )}
105
+
106
+ {allTracks.length > 0 && (
107
+ <Flex direction="column" gap="1">
108
+ {typeof children === 'function' ? children(tracks) : children}
109
+
110
+ <Flex direction="row" gap="3" p="2" align="center" justify="center">
111
+ <Button variant="ghost" color="gray" style={{ flex: '1' }}
112
+ onClick={handleInsertAllTracks}
113
+ >
114
+ <ArrowLeftIcon width={16} height={16} /> Insert all
115
+ </Button>
116
+
117
+ {allTracks.length > 6 && (
118
+ <>
119
+ <Separator orientation="vertical" />
120
+
121
+ <Button variant="ghost" color="gray" style={{ flex: '1' }} onClick={handleClickShowAllTracks}>
122
+ {showAllTracks ? 'Show less' : 'Show more'}
123
+ {showAllTracks ? <ChevronUpIcon width={16} height={16} /> : <ChevronDownIcon width={16} height={16} />}
124
+ </Button>
125
+ </>
126
+ )}
127
+ </Flex>
128
+ </Flex>
129
+ )}
130
+ </Flex>
131
+ )}
132
+ </Flex>
133
+ )
134
+ }
135
+
136
+ export default ProjectItem
@@ -1,11 +1,16 @@
1
1
  .projectTitle {
2
2
  color: var(--neutral-12);
3
+ padding-right: var(--space-2);
3
4
  }
4
5
 
5
6
  .projectArtist {
6
7
  color: var(--neutral-alpha-9);
7
8
  }
8
9
 
10
+ .projectFailed {
11
+ color: var(--error-alpha-11);
12
+ }
13
+
9
14
  .projectCover {
10
15
  width: 48px;
11
16
  height: 48px;
@@ -21,7 +26,7 @@
21
26
  pointer-events: none;
22
27
  }
23
28
 
24
- .item {
29
+ .item:not(.failed) {
25
30
  cursor: pointer;
26
31
  border-radius: var(--radius-4);
27
32
 
@@ -34,39 +39,3 @@
34
39
  background: rgba(216, 244, 246, 0.04);
35
40
  }
36
41
  }
37
-
38
- .itemTrack {
39
- cursor: pointer;
40
- border-radius: var(--radius-3);
41
-
42
- .iconArrowLeft {
43
- display: none;
44
- }
45
-
46
- &:hover {
47
- background: var(--neutral-alpha-2);
48
- box-shadow: 0 24px 24px -16px rgba(0, 0, 0, 0.40);
49
-
50
- .iconArrowLeft {
51
- display: block;
52
- }
53
-
54
- .iconTrack {
55
- display: none;
56
- }
57
- }
58
- }
59
-
60
- .trackTitle {
61
- color: var(--neutral-alpha-12);
62
- }
63
-
64
- .icon {
65
- width: 48px;
66
- height: 48px;
67
- display: flex;
68
- align-items: center;
69
- justify-content: center;
70
- background: var(--neutral-alpha-2);
71
- border-radius: var(--radius-3);
72
- }
@@ -0,0 +1,44 @@
1
+ import { Flex, Text } from '@radix-ui/themes'
2
+ import { useCallback, useContext } from 'react'
3
+ import { ArrowLeftIcon } from '../../../icons'
4
+ import { ProjectsListContext } from '../context'
5
+ import { getIconFromTrackId } from '../utils'
6
+ import styles from './ProjectTrack.module.css'
7
+
8
+ const ProjectTrack = ({
9
+ ...props
10
+ }) => {
11
+ const { onClickTrack } = useContext(ProjectsListContext)
12
+ const { id, title } = props
13
+
14
+ const handleClickTrack = useCallback((e) => {
15
+ e.stopPropagation()
16
+ onClickTrack?.(props)
17
+ }, [onClickTrack, props])
18
+
19
+ return (
20
+ <Flex direction="column" gap="1">
21
+ <Flex
22
+ direction="row"
23
+ gap="3"
24
+ className={styles.itemTrack}
25
+ onClick={handleClickTrack}
26
+ >
27
+ <div className={styles.icon}>
28
+ <Flex className={styles.iconTrack}>
29
+ {getIconFromTrackId(id)}
30
+ </Flex>
31
+ <ArrowLeftIcon width={16} height={16} className={styles.iconArrowLeft} />
32
+ </div>
33
+
34
+ <Flex direction="column" gap="1" align="start" justify="center">
35
+ <Text size="2" className={styles.trackTitle}>
36
+ {title}
37
+ </Text>
38
+ </Flex>
39
+ </Flex>
40
+ </Flex>
41
+ )
42
+ }
43
+
44
+ export default ProjectTrack
@@ -0,0 +1,35 @@
1
+ .itemTrack {
2
+ cursor: pointer;
3
+ border-radius: var(--radius-3);
4
+
5
+ .iconArrowLeft {
6
+ display: none;
7
+ }
8
+
9
+ &:hover {
10
+ background: var(--neutral-alpha-2);
11
+ box-shadow: 0 24px 24px -16px rgba(0, 0, 0, 0.40);
12
+
13
+ .iconArrowLeft {
14
+ display: block;
15
+ }
16
+
17
+ .iconTrack {
18
+ display: none;
19
+ }
20
+ }
21
+ }
22
+
23
+ .trackTitle {
24
+ color: var(--neutral-alpha-12);
25
+ }
26
+
27
+ .icon {
28
+ width: 48px;
29
+ height: 48px;
30
+ display: flex;
31
+ align-items: center;
32
+ justify-content: center;
33
+ background: var(--neutral-alpha-2);
34
+ border-radius: var(--radius-3);
35
+ }
@@ -1,19 +1,8 @@
1
- import { Flex, Text, Separator, Spinner } from '@radix-ui/themes'
2
- import styles from './ProjectsList.module.css'
3
- import { useState, createContext, useContext, useCallback } from 'react'
4
- import { ArrowLeftIcon, NoMusicIcon, ChevronDownIcon, ChevronUpIcon } from '../../icons'
5
- import { getIconFromTrackId } from './utils'
6
- import { Button } from '../Button/Button'
7
- import { ProductsBrandPattern } from '../ProductsBrandPattern/ProductsBrandPattern'
8
-
9
- const ProjectsListContext = createContext({
10
- projects: [],
11
- onClickTrack: null,
12
- onInsertAllTracks: null,
13
- onProjectClick: null,
14
- openedItem: null,
15
- setOpenedItem: () => { },
16
- })
1
+ import { Flex } from '@radix-ui/themes'
2
+ import { useState } from 'react'
3
+ import { ProjectsListContext } from './context'
4
+ import ProjectTrack from './ProjectTrack/ProjectTrack'
5
+ import ProjectItem from './ProjectItem/ProjectItem'
17
6
 
18
7
  export const ProjectsList = ({
19
8
  children,
@@ -24,8 +13,6 @@ export const ProjectsList = ({
24
13
  }) => {
25
14
  const [openedItem, setOpenedItem] = useState(null)
26
15
 
27
- const content = typeof children === 'function' ? children(projects) : children
28
-
29
16
  return (
30
17
  <ProjectsListContext.Provider
31
18
  value={{
@@ -38,143 +25,14 @@ export const ProjectsList = ({
38
25
  }}
39
26
  >
40
27
  <Flex direction="column" width="100%">
41
- {content}
28
+ {projects.map((project) => (
29
+ <ProjectItem key={project.id} {...project}>
30
+ {children}
31
+ </ProjectItem>
32
+ ))}
42
33
  </Flex>
43
34
  </ProjectsListContext.Provider>
44
35
  )
45
36
  }
46
37
 
47
- const ProjectsListItem = ({
48
- id,
49
- title,
50
- artist,
51
- cover,
52
- type,
53
- tracks = [],
54
- isLoading = false,
55
- ...props
56
- }) => {
57
- const { projects, onClickTrack, onInsertAllTracks, onProjectClick, openedItem, setOpenedItem } = useContext(ProjectsListContext)
58
- const [showAllTracks, setShowAllTracks] = useState(false)
59
-
60
- const handleClickProject = useCallback(() => {
61
- setShowAllTracks(false)
62
- const project = projects.find((p) => p.id === id)
63
-
64
- if (openedItem === id) {
65
- setOpenedItem(null)
66
- onProjectClick?.(project, false)
67
- } else {
68
- setOpenedItem(id)
69
- onProjectClick?.(project, true)
70
- }
71
- }, [id, openedItem, setOpenedItem, onProjectClick, projects])
72
-
73
- const handleClickShowAllTracks = useCallback((e) => {
74
- e.preventDefault()
75
- e.stopPropagation()
76
- setShowAllTracks(!showAllTracks)
77
- }, [showAllTracks])
78
-
79
-
80
- return (
81
- <Flex
82
- direction="column"
83
- gap="2"
84
- p="1"
85
- className={`${styles.item} ${openedItem === id ? styles.opened : ''}`}
86
- onClick={handleClickProject}
87
- {...props}
88
- >
89
- <Flex direction="row" gap="3">
90
- <Flex className={styles.projectCover}>
91
- <ProductsBrandPattern type={type} cover={cover} size="48px" />
92
- </Flex>
93
-
94
- <Flex direction="column" gap="1" align="start" justify="center">
95
- <Text size="2" className={styles.projectTitle}>
96
- {title}
97
- </Text>
98
-
99
- {artist && (
100
- <Text size="1" className={styles.projectArtist}>
101
- {artist}
102
- </Text>
103
- )}
104
- </Flex>
105
- </Flex>
106
-
107
- {openedItem === id && (
108
- <Flex direction="column">
109
- {tracks.length === 0 && !isLoading && (
110
- <Flex direction="row" gap="3" p="5" pt="2" align="center" justify="center">
111
- <NoMusicIcon width={16} height={16} style={{ color: 'var(--neutral-alpha-10)' }} />
112
- <Text size="2" style={{ color: 'var(--neutral-alpha-10)' }}>This file has no tracks</Text>
113
- </Flex>
114
- )}
115
-
116
- {isLoading && (
117
- <Flex direction="row" gap="3" p="5" align="center" justify="center">
118
- <Spinner width={16} height={16} style={{ color: 'var(--neutral-alpha-10)' }} />
119
- <Text size="2" style={{ color: 'var(--neutral-alpha-10)' }}>Loading tracks...</Text>
120
- </Flex>
121
- )}
122
-
123
- {tracks.length > 0 && (
124
- <Flex direction="column" gap="1">
125
- {tracks.slice(0, showAllTracks ? tracks.length : 6).map((track) => (
126
- <Flex
127
- key={track.id}
128
- direction="row"
129
- gap="3"
130
- className={styles.itemTrack}
131
- onClick={(e) => {
132
- e.stopPropagation()
133
- onClickTrack?.(track)
134
- }}
135
- >
136
- <div className={styles.icon}>
137
- <Flex className={styles.iconTrack}>
138
- {getIconFromTrackId(track.id)}
139
- </Flex>
140
- <ArrowLeftIcon width={16} height={16} className={styles.iconArrowLeft} />
141
- </div>
142
-
143
- <Flex direction="column" gap="1" align="start" justify="center">
144
- <Text size="2" className={styles.trackTitle}>
145
- {track.title}
146
- </Text>
147
- </Flex>
148
- </Flex>
149
- ))}
150
-
151
- <Flex direction="row" gap="3" p="2" align="center" justify="center">
152
- <Button variant="ghost" color="neutral" style={{ flex: '1' }}
153
- onClick={(e) => {
154
- e.stopPropagation()
155
- onInsertAllTracks?.(tracks)
156
- }}
157
- >
158
- <ArrowLeftIcon width={16} height={16} /> Insert all
159
- </Button>
160
-
161
- {tracks.length > 6 && (
162
- <>
163
- <Separator orientation="vertical" />
164
-
165
- <Button variant="ghost" color="gray" style={{ flex: '1' }} onClick={handleClickShowAllTracks}>
166
- {showAllTracks ? 'Show less' : 'Show more'}
167
- {showAllTracks ? <ChevronUpIcon width={16} height={16} /> : <ChevronDownIcon width={16} height={16} />}
168
- </Button>
169
- </>
170
- )}
171
- </Flex>
172
- </Flex>
173
- )}
174
- </Flex>
175
- )}
176
- </Flex>
177
- )
178
- }
179
-
180
- ProjectsList.Item = ProjectsListItem
38
+ ProjectsList.Track = ProjectTrack
@@ -34,9 +34,9 @@ export const Default = {
34
34
  onInsertAllTracks={args.onInsertAllTracks}
35
35
  onProjectClick={args.onProjectClick}
36
36
  >
37
- {(projects) =>
38
- projects.map((project) => (
39
- <ProjectsList.Item key={project.id} {...project} />
37
+ {(tracks) =>
38
+ tracks.map((track) => (
39
+ <ProjectsList.Track key={track.id} {...track} />
40
40
  ))
41
41
  }
42
42
  </ProjectsList>
@@ -80,9 +80,9 @@ export const WithEmptyProjects = {
80
80
  onInsertAllTracks={args.onInsertAllTracks}
81
81
  onProjectClick={args.onProjectClick}
82
82
  >
83
- {(projects) =>
84
- projects.map((project) => (
85
- <ProjectsList.Item key={project.id} {...project} />
83
+ {(tracks) =>
84
+ tracks.map((track) => (
85
+ <ProjectsList.Track key={track.id} {...track} />
86
86
  ))
87
87
  }
88
88
  </ProjectsList>
@@ -99,7 +99,7 @@ export const WithProjectLoading = {
99
99
  cover: null,
100
100
  duration: 0,
101
101
  type: 'studio',
102
- isLoading: true,
102
+ loading: true,
103
103
  }, {
104
104
  id: '456',
105
105
  title: 'Empty Project 2',
@@ -127,9 +127,56 @@ export const WithProjectLoading = {
127
127
  onInsertAllTracks={args.onInsertAllTracks}
128
128
  onProjectClick={args.onProjectClick}
129
129
  >
130
- {(projects) =>
131
- projects.map((project) => (
132
- <ProjectsList.Item key={project.id} {...project} />
130
+ {(tracks) =>
131
+ tracks.map((track) => (
132
+ <ProjectsList.Track key={track.id} {...track} />
133
+ ))
134
+ }
135
+ </ProjectsList>
136
+ )
137
+ },
138
+ }
139
+
140
+ export const WithProjectFailed = {
141
+ args: {
142
+ projects: [{
143
+ id: '123',
144
+ title: 'Project Failed',
145
+ artist: null,
146
+ cover: null,
147
+ duration: 0,
148
+ type: 'studio',
149
+ failed: true,
150
+ }, {
151
+ id: '456',
152
+ title: 'Empty Project 2',
153
+ artist: null,
154
+ cover: null,
155
+ duration: 0,
156
+ type: 'master',
157
+ }],
158
+ onProjectClick: (project, opened) => {
159
+ console.log('project', project)
160
+ console.log('opened', opened)
161
+ },
162
+ onClickTrack: (track) => {
163
+ console.log('track', track)
164
+ },
165
+ onInsertAllTracks: (tracks) => {
166
+ console.log('insert-all-tracks', tracks)
167
+ },
168
+ },
169
+ render: (args) => {
170
+ return (
171
+ <ProjectsList
172
+ projects={args.projects}
173
+ onClickTrack={args.onClickTrack}
174
+ onInsertAllTracks={args.onInsertAllTracks}
175
+ onProjectClick={args.onProjectClick}
176
+ >
177
+ {(tracks) =>
178
+ tracks.map((track) => (
179
+ <ProjectsList.Track key={track.id} {...track} />
133
180
  ))
134
181
  }
135
182
  </ProjectsList>
@@ -0,0 +1,10 @@
1
+ import { createContext } from 'react'
2
+
3
+ export const ProjectsListContext = createContext({
4
+ projects: [],
5
+ onClickTrack: null,
6
+ onInsertAllTracks: null,
7
+ onProjectClick: null,
8
+ openedItem: null,
9
+ setOpenedItem: () => { },
10
+ })
@@ -5,7 +5,8 @@
5
5
  min-width: 151px;
6
6
  background-color: var(--panel-solid) !important;
7
7
  padding: 4px;
8
- box-shadow: 0 12px 32px -16px rgba(0, 0, 0, 0.30), 0 12px 60px 0 rgba(0, 0, 0, 0.15);
8
+ box-shadow: 0 12px 32px -16px rgba(0, 0, 0, 0.3),
9
+ 0 12px 60px 0 rgba(0, 0, 0, 0.15);
9
10
  z-index: 9999;
10
11
  border: 1px solid var(--neutral-alpha-3);
11
12
  animation-duration: 400ms;
@@ -84,8 +85,9 @@
84
85
  .menuPortal {
85
86
  border-radius: 8px;
86
87
  border: 1px solid var(--neutral-alpha-3);
87
- background: #18191B;
88
- box-shadow: 0 12px 32px -16px rgba(0, 0, 0, 0.30), 0 12px 60px 0 rgba(0, 0, 0, 0.15);
88
+ background: #18191b;
89
+ box-shadow: 0 12px 32px -16px rgba(0, 0, 0, 0.3),
90
+ 0 12px 60px 0 rgba(0, 0, 0, 0.15);
89
91
  }
90
92
 
91
93
  .menuItem {
@@ -271,6 +273,13 @@
271
273
  }
272
274
  }
273
275
 
276
+ .menuCustomRender,
277
+ .menuSubTrigger[data-highlighted]:is(.menuCustomRender),
278
+ .menuItem[data-highlighted]:is(.menuCustomRender) {
279
+ background-color: transparent;
280
+ cursor: default;
281
+ }
282
+
274
283
  .menuItem[data-state='open'],
275
284
  .menuTrigger[data-state='open']:not(.showActiveTrigger),
276
285
  .menuTrigger[data-highlighted]:not(.showActiveTrigger) {
@@ -409,4 +418,3 @@
409
418
  color: var(--aqua-dark-alpha-11);
410
419
  }
411
420
  }
412
-