@zylem/ui 0.8.9 → 0.8.11
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/styles.css +1 -1
- package/package.json +12 -10
- package/src/components/Accordion/Accordion.tsx +10 -1
- package/src/components/AddTile/AddTile.css.ts +17 -0
- package/src/components/AddTile/AddTile.tsx +15 -7
- package/src/components/Callout/Callout.css.ts +45 -0
- package/src/components/Callout/Callout.tsx +32 -0
- package/src/components/Card/Card.css.ts +18 -0
- package/src/components/Card/Card.tsx +25 -2
- package/src/components/Dialog/Dialog.css.ts +69 -2
- package/src/components/Dialog/Dialog.tsx +35 -7
- package/src/components/DropdownMenu/DropdownMenu.css.ts +22 -0
- package/src/components/DropdownMenu/DropdownMenu.tsx +67 -20
- package/src/components/EmptyState/EmptyState.css.ts +9 -0
- package/src/components/EmptyState/EmptyState.tsx +3 -1
- package/src/components/FrameStrip/FrameStrip.css.ts +6 -0
- package/src/components/FrameStrip/FrameStrip.tsx +30 -2
- package/src/components/Menubar/Menubar.css.ts +52 -0
- package/src/components/Menubar/Menubar.tsx +77 -0
- package/src/components/Mixer/Knob.tsx +188 -0
- package/src/components/Mixer/Mixer.css.ts +246 -0
- package/src/components/Mixer/MixerStrip.tsx +150 -0
- package/src/components/NumberField/NumberField.tsx +5 -1
- package/src/components/Panel/Panel.tsx +6 -5
- package/src/components/PianoRoll/PianoRoll.css.ts +224 -0
- package/src/components/PianoRoll/PianoRoll.tsx +353 -0
- package/src/components/PianoRoll/piano-roll-math.ts +89 -0
- package/src/components/Property/Property.css.ts +1 -1
- package/src/components/Property/Property.tsx +3 -0
- package/src/components/Sidebar/Sidebar.css.ts +8 -0
- package/src/components/Sidebar/Sidebar.tsx +10 -2
- package/src/components/Slider/Slider.tsx +6 -0
- package/src/components/SpriteSizePicker/SpriteSizePicker.tsx +15 -4
- package/src/components/Tabs/Tabs.css.ts +48 -0
- package/src/components/Tabs/Tabs.tsx +41 -3
- package/src/components/TextField/TextField.tsx +49 -5
- package/src/components/TimelineLanes/TimelineLanes.css.ts +365 -0
- package/src/components/TimelineLanes/TimelineLanes.tsx +509 -0
- package/src/components/TimelineLanes/lanes-math.ts +182 -0
- package/src/components/Toast/Toast.css.ts +48 -0
- package/src/components/Toast/Toast.tsx +37 -0
- package/src/components/TrackHeader/TrackHeader.css.ts +162 -0
- package/src/components/TrackHeader/TrackHeader.tsx +145 -0
- package/src/components/TransportBar/TransportBar.css.ts +65 -0
- package/src/components/TransportBar/TransportBar.tsx +154 -0
- package/src/components/Waveform/Waveform.css.ts +77 -0
- package/src/components/Waveform/Waveform.tsx +195 -0
- package/src/components/Waveform/peaks.ts +49 -0
- package/src/components/index.ts +55 -1
- package/src/global/accordion.css.ts +23 -0
- package/src/styles.ts +11 -0
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { Slider as KSlider } from '@kobalte/core';
|
|
2
|
+
import { Show } from 'solid-js';
|
|
3
|
+
import { clamp } from '../TimelineLanes/lanes-math';
|
|
4
|
+
import { Knob } from './Knob';
|
|
5
|
+
|
|
6
|
+
export interface MixerStripProps {
|
|
7
|
+
name: string;
|
|
8
|
+
color?: string;
|
|
9
|
+
/** Fader position in dB. */
|
|
10
|
+
volume: number;
|
|
11
|
+
onVolumeChange?: (volume: number) => void;
|
|
12
|
+
onVolumeCommit?: (volume: number) => void;
|
|
13
|
+
minVolume?: number;
|
|
14
|
+
maxVolume?: number;
|
|
15
|
+
/** -1 (left) … 1 (right). Omit to hide the pan knob. */
|
|
16
|
+
pan?: number;
|
|
17
|
+
onPanChange?: (pan: number) => void;
|
|
18
|
+
onPanCommit?: (pan: number) => void;
|
|
19
|
+
/** Mute / solo render only when their handler is given. */
|
|
20
|
+
muted?: boolean;
|
|
21
|
+
onToggleMute?: () => void;
|
|
22
|
+
solo?: boolean;
|
|
23
|
+
onToggleSolo?: () => void;
|
|
24
|
+
/** Live output level, 0–1, drawn as a meter beside the fader. */
|
|
25
|
+
level?: number;
|
|
26
|
+
/** Wider strip with a stronger label; for the master bus. */
|
|
27
|
+
master?: boolean;
|
|
28
|
+
selected?: boolean;
|
|
29
|
+
onSelect?: () => void;
|
|
30
|
+
disabled?: boolean;
|
|
31
|
+
class?: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** `-60` (the floor) reads as `-inf`; otherwise one decimal with a sign. */
|
|
35
|
+
export function formatDb(value: number, floor = -60): string {
|
|
36
|
+
if (!Number.isFinite(value) || value <= floor) return '-inf';
|
|
37
|
+
const rounded = Math.round(value * 10) / 10;
|
|
38
|
+
return `${rounded > 0 ? '+' : ''}${rounded.toFixed(1)} dB`;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** `-1` → `L100`, `0` → `C`, `0.5` → `R50`. */
|
|
42
|
+
export function formatPan(value: number): string {
|
|
43
|
+
const percent = Math.round(Math.abs(value) * 100);
|
|
44
|
+
if (percent === 0) return 'C';
|
|
45
|
+
return `${value < 0 ? 'L' : 'R'}${percent}`;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Channel strip: pan knob, vertical fader with a level meter, mute/solo, and
|
|
50
|
+
* the track name with its color. Compose several into a mixer row.
|
|
51
|
+
*/
|
|
52
|
+
export function MixerStrip(props: MixerStripProps) {
|
|
53
|
+
const min = () => props.minVolume ?? -60;
|
|
54
|
+
const max = () => props.maxVolume ?? 6;
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
<div
|
|
58
|
+
class={props.class ? `zylem-mixer-strip ${props.class}` : 'zylem-mixer-strip'}
|
|
59
|
+
style={{ '--zylem-track-color': props.color ?? 'var(--zylem-color-primary)' }}
|
|
60
|
+
data-master={props.master ? '' : undefined}
|
|
61
|
+
data-selected={props.selected ? '' : undefined}
|
|
62
|
+
data-disabled={props.disabled ? '' : undefined}
|
|
63
|
+
data-muted={props.muted ? '' : undefined}
|
|
64
|
+
onPointerDown={() => props.onSelect?.()}
|
|
65
|
+
>
|
|
66
|
+
<Show when={props.pan !== undefined}>
|
|
67
|
+
<Knob
|
|
68
|
+
value={props.pan ?? 0}
|
|
69
|
+
min={-1}
|
|
70
|
+
max={1}
|
|
71
|
+
step={0.01}
|
|
72
|
+
defaultValue={0}
|
|
73
|
+
bipolar
|
|
74
|
+
size={30}
|
|
75
|
+
label="Pan"
|
|
76
|
+
format={formatPan}
|
|
77
|
+
disabled={props.disabled}
|
|
78
|
+
onChange={(value) => props.onPanChange?.(value)}
|
|
79
|
+
onCommit={(value) => props.onPanCommit?.(value)}
|
|
80
|
+
class="zylem-mixer-pan"
|
|
81
|
+
/>
|
|
82
|
+
</Show>
|
|
83
|
+
<div class="zylem-mixer-fader-row">
|
|
84
|
+
<KSlider.Root
|
|
85
|
+
class="zylem-fader"
|
|
86
|
+
orientation="vertical"
|
|
87
|
+
value={[clamp(props.volume, min(), max())]}
|
|
88
|
+
minValue={min()}
|
|
89
|
+
maxValue={max()}
|
|
90
|
+
step={0.1}
|
|
91
|
+
disabled={props.disabled}
|
|
92
|
+
onChange={(values) => props.onVolumeChange?.(values[0] ?? min())}
|
|
93
|
+
onChangeEnd={(values) => props.onVolumeCommit?.(values[0] ?? min())}
|
|
94
|
+
getValueLabel={(params) => formatDb(params.values[0] ?? min(), min())}
|
|
95
|
+
>
|
|
96
|
+
<KSlider.Label class="zylem-fader-sr">{`${props.name} volume`}</KSlider.Label>
|
|
97
|
+
<KSlider.Track class="zylem-fader-track">
|
|
98
|
+
<KSlider.Fill class="zylem-fader-fill" />
|
|
99
|
+
<span class="zylem-fader-zero" aria-hidden="true" />
|
|
100
|
+
<KSlider.Thumb class="zylem-fader-thumb">
|
|
101
|
+
<KSlider.Input class="zylem-fader-sr" />
|
|
102
|
+
</KSlider.Thumb>
|
|
103
|
+
</KSlider.Track>
|
|
104
|
+
</KSlider.Root>
|
|
105
|
+
<div class="zylem-mixer-meter" aria-hidden="true">
|
|
106
|
+
<div
|
|
107
|
+
class="zylem-mixer-meter-fill"
|
|
108
|
+
style={{ height: `${Math.round(clamp(props.level ?? 0, 0, 1) * 100)}%` }}
|
|
109
|
+
/>
|
|
110
|
+
</div>
|
|
111
|
+
</div>
|
|
112
|
+
<span class="zylem-mixer-readout">{formatDb(props.volume, min())}</span>
|
|
113
|
+
<div class="zylem-mixer-toggles">
|
|
114
|
+
<Show when={props.onToggleMute}>
|
|
115
|
+
<button
|
|
116
|
+
type="button"
|
|
117
|
+
class="zylem-track-toggle"
|
|
118
|
+
data-kind="mute"
|
|
119
|
+
aria-pressed={props.muted ? 'true' : 'false'}
|
|
120
|
+
aria-label={props.muted ? 'Unmute' : 'Mute'}
|
|
121
|
+
title={props.muted ? 'Unmute' : 'Mute'}
|
|
122
|
+
disabled={props.disabled}
|
|
123
|
+
onPointerDown={(event) => event.stopPropagation()}
|
|
124
|
+
onClick={() => props.onToggleMute?.()}
|
|
125
|
+
>
|
|
126
|
+
M
|
|
127
|
+
</button>
|
|
128
|
+
</Show>
|
|
129
|
+
<Show when={props.onToggleSolo}>
|
|
130
|
+
<button
|
|
131
|
+
type="button"
|
|
132
|
+
class="zylem-track-toggle"
|
|
133
|
+
data-kind="solo"
|
|
134
|
+
aria-pressed={props.solo ? 'true' : 'false'}
|
|
135
|
+
aria-label={props.solo ? 'Unsolo' : 'Solo'}
|
|
136
|
+
title={props.solo ? 'Unsolo' : 'Solo'}
|
|
137
|
+
disabled={props.disabled}
|
|
138
|
+
onPointerDown={(event) => event.stopPropagation()}
|
|
139
|
+
onClick={() => props.onToggleSolo?.()}
|
|
140
|
+
>
|
|
141
|
+
S
|
|
142
|
+
</button>
|
|
143
|
+
</Show>
|
|
144
|
+
</div>
|
|
145
|
+
<span class="zylem-mixer-name" title={props.name}>
|
|
146
|
+
{props.name}
|
|
147
|
+
</span>
|
|
148
|
+
</div>
|
|
149
|
+
);
|
|
150
|
+
}
|
|
@@ -23,8 +23,12 @@ export function NumberField(props: NumberFieldProps) {
|
|
|
23
23
|
<KNumberField.Root
|
|
24
24
|
class={props.class ? `zylem-field-root ${props.class}` : 'zylem-field-root'}
|
|
25
25
|
rawValue={props.value}
|
|
26
|
+
// Kobalte reports the parsed value on mount and after each format pass;
|
|
27
|
+
// only real edits reach the owner, so a controlled form is not marked
|
|
28
|
+
// dirty just by rendering.
|
|
26
29
|
onRawValueChange={(value) => {
|
|
27
|
-
if (props.onChange
|
|
30
|
+
if (!props.onChange || Number.isNaN(value) || value === props.value) return;
|
|
31
|
+
props.onChange(value);
|
|
28
32
|
}}
|
|
29
33
|
minValue={props.minValue}
|
|
30
34
|
maxValue={props.maxValue}
|
|
@@ -16,6 +16,8 @@ export interface PanelProps
|
|
|
16
16
|
* inside an already-outlined region.
|
|
17
17
|
*/
|
|
18
18
|
borderless?: boolean;
|
|
19
|
+
/** Class for the body element, for layouts the body's children need (a form's column, a grid). */
|
|
20
|
+
bodyClass?: string;
|
|
19
21
|
children?: JSX.Element;
|
|
20
22
|
}
|
|
21
23
|
|
|
@@ -30,6 +32,7 @@ export function Panel(props: PanelProps) {
|
|
|
30
32
|
'window',
|
|
31
33
|
'flush',
|
|
32
34
|
'borderless',
|
|
35
|
+
'bodyClass',
|
|
33
36
|
'children',
|
|
34
37
|
'class',
|
|
35
38
|
]);
|
|
@@ -53,11 +56,9 @@ export function Panel(props: PanelProps) {
|
|
|
53
56
|
</div>
|
|
54
57
|
</Show>
|
|
55
58
|
<div
|
|
56
|
-
class={
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
: 'zylem-panel-body'
|
|
60
|
-
}
|
|
59
|
+
class={['zylem-panel-body', local.flush ? 'zylem-panel-body--flush' : '', local.bodyClass ?? '']
|
|
60
|
+
.filter(Boolean)
|
|
61
|
+
.join(' ')}
|
|
61
62
|
>
|
|
62
63
|
{local.children}
|
|
63
64
|
</div>
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import { globalStyle } from '@vanilla-extract/css';
|
|
2
|
+
import { vars } from '../../theme.css';
|
|
3
|
+
|
|
4
|
+
globalStyle('.zylem-piano-roll', {
|
|
5
|
+
position: 'relative',
|
|
6
|
+
display: 'flex',
|
|
7
|
+
flexDirection: 'column',
|
|
8
|
+
minHeight: 0,
|
|
9
|
+
minWidth: 0,
|
|
10
|
+
fontFamily: vars.typography.fontFamily,
|
|
11
|
+
color: vars.colors.text,
|
|
12
|
+
background: vars.material.fieldGlass,
|
|
13
|
+
border: '1px solid rgba(97, 166, 232, 0.22)',
|
|
14
|
+
borderRadius: vars.radii.control,
|
|
15
|
+
boxShadow: vars.effects.shadowInset,
|
|
16
|
+
outline: 'none',
|
|
17
|
+
userSelect: 'none',
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
globalStyle('.zylem-piano-roll:focus-visible', {
|
|
21
|
+
boxShadow: `${vars.effects.shadowInset}, ${vars.effects.focusRing}`,
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
globalStyle('.zylem-piano-roll[data-disabled]', {
|
|
25
|
+
opacity: 0.6,
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
globalStyle('.zylem-piano-roll-scroll', {
|
|
29
|
+
flex: '1 1 auto',
|
|
30
|
+
minHeight: 0,
|
|
31
|
+
overflow: 'auto',
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
globalStyle('.zylem-piano-roll-content', {
|
|
35
|
+
position: 'relative',
|
|
36
|
+
display: 'flex',
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// Keys: sticky column of white/black rows, labelled at each C.
|
|
40
|
+
globalStyle('.zylem-piano-roll-keys', {
|
|
41
|
+
position: 'sticky',
|
|
42
|
+
left: 0,
|
|
43
|
+
zIndex: 2,
|
|
44
|
+
flex: 'none',
|
|
45
|
+
display: 'flex',
|
|
46
|
+
flexDirection: 'column',
|
|
47
|
+
borderRight: '1px solid rgba(97, 166, 232, 0.3)',
|
|
48
|
+
boxShadow: '2px 0 6px rgba(0,0,0,0.35)',
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
globalStyle('.zylem-piano-roll-key', {
|
|
52
|
+
boxSizing: 'border-box',
|
|
53
|
+
display: 'flex',
|
|
54
|
+
alignItems: 'center',
|
|
55
|
+
justifyContent: 'flex-end',
|
|
56
|
+
padding: '0 4px 0 0',
|
|
57
|
+
margin: 0,
|
|
58
|
+
border: 0,
|
|
59
|
+
borderBottom: '1px solid rgba(0,0,0,0.35)',
|
|
60
|
+
fontFamily: vars.typography.fontFamily,
|
|
61
|
+
fontSize: '9px',
|
|
62
|
+
lineHeight: 1,
|
|
63
|
+
color: '#1c2633',
|
|
64
|
+
background: 'linear-gradient(90deg, #dbe6f2, #f4f8fc)',
|
|
65
|
+
cursor: 'pointer',
|
|
66
|
+
outline: 'none',
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
globalStyle('.zylem-piano-roll-key[data-black]', {
|
|
70
|
+
color: '#cdd8e6',
|
|
71
|
+
background: 'linear-gradient(90deg, #0d151f, #263241)',
|
|
72
|
+
boxShadow: 'inset -6px 0 0 rgba(255,255,255,0.05)',
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
globalStyle('.zylem-piano-roll-key[data-c]', {
|
|
76
|
+
fontWeight: 700,
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
globalStyle('.zylem-piano-roll-key:hover:not(:disabled)', {
|
|
80
|
+
filter: 'brightness(1.1)',
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
globalStyle('.zylem-piano-roll-key:active:not(:disabled)', {
|
|
84
|
+
filter: 'brightness(0.92)',
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// Grid: row stripes plus beat/bar/snap columns from gradients.
|
|
88
|
+
globalStyle('.zylem-piano-roll-grid', {
|
|
89
|
+
position: 'relative',
|
|
90
|
+
flex: 'none',
|
|
91
|
+
backgroundImage: [
|
|
92
|
+
'repeating-linear-gradient(90deg, rgba(205, 232, 255, 0.28) 0 1px, transparent 1px var(--zylem-pr-bar))',
|
|
93
|
+
'repeating-linear-gradient(90deg, rgba(205, 232, 255, 0.12) 0 1px, transparent 1px var(--zylem-pr-ppb))',
|
|
94
|
+
'repeating-linear-gradient(90deg, rgba(205, 232, 255, 0.05) 0 1px, transparent 1px var(--zylem-pr-snap))',
|
|
95
|
+
].join(', '),
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
globalStyle('.zylem-piano-roll-row', {
|
|
99
|
+
position: 'absolute',
|
|
100
|
+
left: 0,
|
|
101
|
+
right: 0,
|
|
102
|
+
boxSizing: 'border-box',
|
|
103
|
+
borderBottom: '1px solid rgba(255,255,255,0.04)',
|
|
104
|
+
pointerEvents: 'none',
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
globalStyle('.zylem-piano-roll-row[data-black]', {
|
|
108
|
+
background: 'rgba(0, 0, 0, 0.22)',
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
globalStyle('.zylem-piano-roll-row[data-c]', {
|
|
112
|
+
borderBottom: '1px solid rgba(205, 232, 255, 0.25)',
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
globalStyle('.zylem-piano-roll-end', {
|
|
116
|
+
position: 'absolute',
|
|
117
|
+
top: 0,
|
|
118
|
+
bottom: 0,
|
|
119
|
+
right: 0,
|
|
120
|
+
background: 'rgba(0, 0, 0, 0.35)',
|
|
121
|
+
pointerEvents: 'none',
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
globalStyle('.zylem-piano-roll-note', {
|
|
125
|
+
position: 'absolute',
|
|
126
|
+
boxSizing: 'border-box',
|
|
127
|
+
display: 'flex',
|
|
128
|
+
alignItems: 'center',
|
|
129
|
+
borderRadius: '3px',
|
|
130
|
+
border: '1px solid rgba(255, 255, 255, 0.55)',
|
|
131
|
+
background:
|
|
132
|
+
'linear-gradient(180deg, rgba(178, 219, 255, 0.98), rgba(48, 137, 220, 0.98))',
|
|
133
|
+
boxShadow: 'inset 0 1px 0 rgba(255,255,255,0.45), 0 1px 2px rgba(0,0,0,0.5)',
|
|
134
|
+
opacity: 'calc(0.45 + 0.55 * var(--zylem-pr-velocity, 0.8))',
|
|
135
|
+
color: '#08131f',
|
|
136
|
+
cursor: 'grab',
|
|
137
|
+
outline: 'none',
|
|
138
|
+
overflow: 'hidden',
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
globalStyle('.zylem-piano-roll-note[data-selected]', {
|
|
142
|
+
background:
|
|
143
|
+
'linear-gradient(180deg, rgba(255, 244, 200, 0.98), rgba(240, 180, 60, 0.98))',
|
|
144
|
+
borderColor: 'white',
|
|
145
|
+
boxShadow: 'inset 0 1px 0 rgba(255,255,255,0.6), 0 0 8px rgba(240, 180, 60, 0.7)',
|
|
146
|
+
opacity: 1,
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
globalStyle('.zylem-piano-roll[data-dragging] .zylem-piano-roll-note', {
|
|
150
|
+
cursor: 'grabbing',
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
globalStyle('.zylem-piano-roll-note-label', {
|
|
154
|
+
padding: '0 4px',
|
|
155
|
+
fontSize: '9px',
|
|
156
|
+
fontWeight: 700,
|
|
157
|
+
lineHeight: 1,
|
|
158
|
+
whiteSpace: 'nowrap',
|
|
159
|
+
pointerEvents: 'none',
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
globalStyle('.zylem-piano-roll-note-handle', {
|
|
163
|
+
position: 'absolute',
|
|
164
|
+
top: 0,
|
|
165
|
+
bottom: 0,
|
|
166
|
+
right: 0,
|
|
167
|
+
width: '6px',
|
|
168
|
+
cursor: 'ew-resize',
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
globalStyle('.zylem-piano-roll-playhead', {
|
|
172
|
+
position: 'absolute',
|
|
173
|
+
top: 0,
|
|
174
|
+
bottom: 0,
|
|
175
|
+
width: '1px',
|
|
176
|
+
background: 'rgba(255, 236, 170, 0.9)',
|
|
177
|
+
boxShadow: '0 0 6px rgba(240, 180, 60, 0.7)',
|
|
178
|
+
pointerEvents: 'none',
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
// Velocity lane under the grid: one bar per note, drag vertically.
|
|
182
|
+
globalStyle('.zylem-piano-roll-velocity', {
|
|
183
|
+
display: 'flex',
|
|
184
|
+
flex: 'none',
|
|
185
|
+
borderTop: '1px solid rgba(97, 166, 232, 0.3)',
|
|
186
|
+
background: 'rgba(8, 19, 31, 0.6)',
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
globalStyle('.zylem-piano-roll-velocity-label', {
|
|
190
|
+
position: 'sticky',
|
|
191
|
+
left: 0,
|
|
192
|
+
zIndex: 2,
|
|
193
|
+
flex: 'none',
|
|
194
|
+
display: 'flex',
|
|
195
|
+
alignItems: 'flex-end',
|
|
196
|
+
justifyContent: 'flex-end',
|
|
197
|
+
padding: '0 4px 4px 0',
|
|
198
|
+
fontSize: '9px',
|
|
199
|
+
letterSpacing: '0.08em',
|
|
200
|
+
textTransform: 'uppercase',
|
|
201
|
+
color: vars.colors.textSecondary,
|
|
202
|
+
background: 'rgba(12, 22, 34, 0.98)',
|
|
203
|
+
borderRight: '1px solid rgba(97, 166, 232, 0.3)',
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
globalStyle('.zylem-piano-roll-velocity-lane', {
|
|
207
|
+
position: 'relative',
|
|
208
|
+
flex: 'none',
|
|
209
|
+
height: '100%',
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
globalStyle('.zylem-piano-roll-velocity-bar', {
|
|
213
|
+
position: 'absolute',
|
|
214
|
+
bottom: 0,
|
|
215
|
+
width: '5px',
|
|
216
|
+
marginLeft: '1px',
|
|
217
|
+
borderRadius: '2px 2px 0 0',
|
|
218
|
+
background: 'linear-gradient(180deg, rgba(178, 219, 255, 0.95), rgba(48, 137, 220, 0.85))',
|
|
219
|
+
cursor: 'ns-resize',
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
globalStyle('.zylem-piano-roll-velocity-bar[data-selected]', {
|
|
223
|
+
background: 'linear-gradient(180deg, rgba(255, 244, 200, 0.98), rgba(240, 180, 60, 0.95))',
|
|
224
|
+
});
|