Skip to content

Commit af62892

Browse files
authored
[C-2675][C-2692] Add multi track navigation sidebar and form controls (#3847)
1 parent ee38400 commit af62892

18 files changed

+437
-66
lines changed

packages/stems/src/components/HarmonyButton/HarmonyButton.module.css

+11
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,17 @@
212212
--text-color: var(--background-white);
213213
}
214214

215+
/* Plain */
216+
.plain {
217+
--button-color: transparent;
218+
--text-color: var(--neutral);
219+
background: transparent;
220+
border: none;
221+
border-radius: 0;
222+
padding: 0;
223+
height: 100%;
224+
}
225+
215226
.secondary.disabled,
216227
.tertiary.disabled,
217228
.destructive.disabled,

packages/stems/src/components/HarmonyButton/HarmonyButton.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ const TYPE_STYLE_MAP: { [k in HarmonyButtonType]: string } = {
3636
[HarmonyButtonType.SECONDARY]: styles.secondary,
3737
[HarmonyButtonType.TERTIARY]: styles.tertiary,
3838
[HarmonyButtonType.DESTRUCTIVE]: styles.destructive,
39-
[HarmonyButtonType.GHOST]: styles.ghost
39+
[HarmonyButtonType.GHOST]: styles.ghost,
40+
[HarmonyButtonType.PLAIN]: styles.plain
4041
}
4142

4243
/**

packages/stems/src/components/HarmonyButton/types.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ export enum HarmonyButtonType {
99
SECONDARY = 'secondary',
1010
TERTIARY = 'tertiary',
1111
DESTRUCTIVE = 'destructive',
12-
GHOST = 'ghost'
12+
GHOST = 'ghost',
13+
PLAIN = 'plain'
1314
}
1415

1516
export enum HarmonyButtonSize {
Loading

packages/web/src/components/Icon/Icon.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import cn from 'classnames'
66
import styles from './Icon.module.css'
77

88
type IconSize =
9+
| 'xxSmall' // 12
910
| 'xSmall' // 14
1011
| 'small' // 16
1112
| 'medium' // 20

packages/web/src/components/layout/layout.module.css

+6-3
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,18 @@
1212
.gap1 {
1313
gap: var(--unit-1);
1414
}
15-
1615
.gap2 {
1716
gap: var(--unit-2);
1817
}
19-
18+
.gap3 {
19+
gap: var(--unit-3);
20+
}
2021
.gap4 {
2122
gap: var(--unit-4);
2223
}
23-
24+
.gap5 {
25+
gap: var(--unit-5);
26+
}
2427
.gap6 {
2528
gap: var(--unit-6);
2629
}

packages/web/src/pages/upload-page/components/EditPageNew.module.css

+25-3
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
1-
.editForm {
1+
.formContainer {
22
width: 100%;
33
background-color: var(--white);
44

55
display: flex;
66
flex-direction: column;
7-
padding: var(--unit-4);
87
border-radius: 8px;
9-
box-shadow: 0px 0px 4px rgba(0, 0, 0, 0.04), 0px 8px 16px rgba(0, 0, 0, 0.08);
8+
box-shadow: var(--shadow-far);
9+
}
10+
11+
.trackEditForm {
12+
padding: var(--unit-4);
1013
}
1114

1215
.continue {
@@ -18,3 +21,22 @@
1821
font-weight: var(--font-bold);
1922
font-size: var(--font-l);
2023
}
24+
25+
.multiTrackHeader {
26+
padding: var(--unit-4);
27+
border-radius: 8px 8px 0px 0px;
28+
border-bottom: 1px solid var(--border-strong);
29+
background: var(--background-surface-1);
30+
}
31+
32+
.multiTrackFooter {
33+
padding: var(--unit-4);
34+
border-radius: 0px 0px 8px 8px;
35+
border-top: 1px solid var(--border-strong);
36+
background: var(--background-surface-1);
37+
justify-content: space-between;
38+
}
39+
40+
.multiTrackFooter .disabled {
41+
opacity: 0.5;
42+
}

packages/web/src/pages/upload-page/components/EditPageNew.tsx

+98-26
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,19 @@
11
import { useCallback, useMemo } from 'react'
22

3-
import { HarmonyButton, HarmonyButtonType, IconArrow } from '@audius/stems'
3+
import {
4+
HarmonyButton,
5+
HarmonyButtonType,
6+
IconArrow,
7+
IconCaretRight
8+
} from '@audius/stems'
49
import cn from 'classnames'
5-
import { Form, Formik } from 'formik'
10+
import { Form, Formik, FormikProps, useField } from 'formik'
611
import moment from 'moment'
712
import * as Yup from 'yup'
813

14+
import { ReactComponent as IconCaretLeft } from 'assets/img/iconCaretLeft.svg'
915
import layoutStyles from 'components/layout/layout.module.css'
16+
import { Text } from 'components/typography'
1017
import PreviewButton from 'components/upload/PreviewButton'
1118

1219
import { MultiTrackSidebar } from '../fields/MultiTrackSidebar'
@@ -21,7 +28,11 @@ import { TrackForUpload } from './types'
2128
const messages = {
2229
titleError: 'Your track must have a name',
2330
artworkError: 'Artwork is required',
24-
genreError: 'Genre is required'
31+
genreError: 'Genre is required',
32+
multiTrackCount: (index: number, total: number) =>
33+
`TRACK ${index} of ${total}`,
34+
prev: 'Prev',
35+
next: 'Next Track'
2536
}
2637

2738
type EditPageProps = {
@@ -78,35 +89,96 @@ export const EditPageNew = (props: EditPageProps) => {
7889
[onContinue, setTracks, tracks]
7990
)
8091

81-
const isMultiTrack = tracks.length > 1
82-
8392
return (
8493
<Formik<TrackEditFormValues>
8594
initialValues={initialValues}
8695
onSubmit={onSubmit}
8796
validationSchema={EditTrackSchema}
8897
>
89-
{() => (
90-
<Form>
91-
<div className={cn(layoutStyles.row, layoutStyles.gap2)}>
92-
<div className={styles.editForm}>
93-
<TrackMetadataFields playing={false} />
94-
<TrackModalArray />
95-
<PreviewButton playing={false} onClick={() => {}} />
96-
</div>
97-
{isMultiTrack ? <MultiTrackSidebar tracks={tracks} /> : null}
98-
</div>
99-
<div className={styles.continue}>
100-
<HarmonyButton
101-
variant={HarmonyButtonType.PRIMARY}
102-
text='Continue'
103-
name='continue'
104-
iconRight={IconArrow}
105-
className={styles.continueButton}
106-
/>
107-
</div>
108-
</Form>
109-
)}
98+
{TrackEditForm}
11099
</Formik>
111100
)
112101
}
102+
103+
const TrackEditForm = (props: FormikProps<TrackEditFormValues>) => {
104+
const { values } = props
105+
const isMultiTrack = values.trackMetadatas.length > 1
106+
107+
return (
108+
<Form>
109+
<div className={cn(layoutStyles.row, layoutStyles.gap2)}>
110+
<div className={styles.formContainer}>
111+
{isMultiTrack ? <MultiTrackHeader /> : null}
112+
<div className={styles.trackEditForm}>
113+
<TrackMetadataFields playing={false} />
114+
<TrackModalArray />
115+
<PreviewButton playing={false} onClick={() => {}} />
116+
</div>
117+
{isMultiTrack ? <MultiTrackFooter /> : null}
118+
</div>
119+
{isMultiTrack ? <MultiTrackSidebar /> : null}
120+
</div>
121+
{!isMultiTrack ? (
122+
<div className={styles.continue}>
123+
<HarmonyButton
124+
variant={HarmonyButtonType.PRIMARY}
125+
text='Continue'
126+
name='continue'
127+
iconRight={IconArrow}
128+
className={styles.continueButton}
129+
/>
130+
</div>
131+
) : null}
132+
</Form>
133+
)
134+
}
135+
136+
const MultiTrackHeader = () => {
137+
const [{ value: index }] = useField('trackMetadatasIndex')
138+
const [{ value: trackMetadatas }] = useField('trackMetadatas')
139+
140+
return (
141+
<div className={styles.multiTrackHeader}>
142+
<Text variant='title' size='xSmall'>
143+
{messages.multiTrackCount(index + 1, trackMetadatas.length)}
144+
</Text>
145+
</div>
146+
)
147+
}
148+
149+
const MultiTrackFooter = () => {
150+
const [{ value: index }, , { setValue: setIndex }] = useField(
151+
'trackMetadatasIndex'
152+
)
153+
const [{ value: trackMetadatas }] = useField('trackMetadatas')
154+
155+
const goPrev = useCallback(() => {
156+
setIndex(Math.max(index - 1, 0))
157+
}, [index, setIndex])
158+
const goNext = useCallback(() => {
159+
setIndex(Math.min(index + 1, trackMetadatas.length - 1))
160+
}, [index, setIndex, trackMetadatas.length])
161+
162+
const prevDisabled = index === 0
163+
const nextDisabled = index === trackMetadatas.length - 1
164+
return (
165+
<div className={cn(styles.multiTrackFooter, layoutStyles.row)}>
166+
<HarmonyButton
167+
className={cn({ [styles.disabled]: prevDisabled })}
168+
variant={HarmonyButtonType.PLAIN}
169+
text={messages.prev}
170+
iconLeft={IconCaretLeft}
171+
onClick={goPrev}
172+
disabled={prevDisabled}
173+
/>
174+
<HarmonyButton
175+
className={cn({ [styles.disabled]: nextDisabled })}
176+
variant={HarmonyButtonType.PLAIN}
177+
text={messages.next}
178+
iconRight={IconCaretRight}
179+
onClick={goNext}
180+
disabled={nextDisabled}
181+
/>
182+
</div>
183+
)
184+
}

packages/web/src/pages/upload-page/fields/ModalField.module.css

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
padding: var(--unit-4) var(--unit-6);
88
gap: var(--unit-4);
99

10-
border: 1px solid var(--neutral-light-8);
10+
border: 1px solid var(--border-strong);
1111
border-radius: 8px;
1212
}
1313

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
.root {
2+
position: sticky;
3+
top: 152px;
4+
align-self: flex-start;
5+
border-radius: 8px;
6+
7+
flex-shrink: 0;
8+
width: 320px;
9+
background: var(--background-surface-1);
10+
box-shadow: var(--shadow-far);
11+
}
12+
13+
.title {
14+
padding: var(--unit-4);
15+
border-radius: 8px 8px 0px 0px;
16+
border-bottom: 1px solid var(--border-strong);
17+
}
18+
19+
.body {
20+
padding: var(--unit-4);
21+
border-radius: 0px 0px 8px 8px;
22+
background: var(--white);
23+
}
24+
25+
.iconError {
26+
height: 12px;
27+
width: 12px;
28+
}
29+
30+
/* Track */
31+
.tracks {
32+
margin: 0 calc(-1 * var(--unit-4));
33+
}
34+
35+
.track {
36+
justify-content: space-between;
37+
}
38+
39+
.trackRoot {
40+
cursor: pointer;
41+
position: relative;
42+
border-top: 1px solid transparent;
43+
border-bottom: 1px solid transparent;
44+
}
45+
.trackRoot:hover {
46+
background: var(--neutral-light-9);
47+
border-top: 1px solid var(--border-strong);
48+
border-bottom: 1px solid var(--border-strong);
49+
}
50+
51+
.trackInfo {
52+
width: 100%;
53+
padding: var(--unit-2) var(--unit-4);
54+
}
55+
.trackInfo.selected {
56+
color: var(--secondary);
57+
}
58+
.trackInfo.error {
59+
color: var(--accent-red);
60+
}
61+
62+
.trackTitleContainer {
63+
min-width: 0;
64+
flex-grow: 1;
65+
}
66+
.trackTitleContainer > * {
67+
text-overflow: ellipsis;
68+
overflow: hidden;
69+
white-space: nowrap;
70+
}
71+
72+
.selectedIndicator {
73+
width: 4px;
74+
height: 42px;
75+
position: absolute;
76+
top: 6px;
77+
border-radius: 0px 8px 8px 0px;
78+
background: var(--secondary);
79+
}
80+
81+
.trackIndex {
82+
padding: var(--unit-2);
83+
}
84+
85+
.artwork {
86+
width: 40px;
87+
height: 40px;
88+
border-radius: 4px;
89+
border: 1px solid var(--border-strong);
90+
background: lightgray 50% / cover no-repeat;
91+
border: 1px solid var(--border-strong);
92+
transition: border ease-in-out 0.1s;
93+
}
94+
95+
.trackInfo .iconError {
96+
height: 16px;
97+
width: 16px;
98+
margin: var(--unit-1);
99+
}
100+
101+
.iconRemove {
102+
padding-right: 8px;
103+
display: none;
104+
}
105+
.trackRoot:hover .iconRemove {
106+
display: flex;
107+
}

0 commit comments

Comments
 (0)