@blibliki/ui 0.9.2
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/README.md +91 -0
- package/dist/index.d.ts +542 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/eslint/index.js +18 -0
- package/eslint/ui-governance-plugin.js +252 -0
- package/package.json +59 -0
- package/scripts/check-css-palette.mjs +92 -0
- package/src/UIProvider.tsx +38 -0
- package/src/components/badge.tsx +57 -0
- package/src/components/button.tsx +65 -0
- package/src/components/card.tsx +44 -0
- package/src/components/context-menu.tsx +239 -0
- package/src/components/dialog.tsx +127 -0
- package/src/components/divider.tsx +45 -0
- package/src/components/dropdown-menu.tsx +243 -0
- package/src/components/encoder.tsx +323 -0
- package/src/components/fader.tsx +230 -0
- package/src/components/icon-button.tsx +51 -0
- package/src/components/input.tsx +38 -0
- package/src/components/label.tsx +14 -0
- package/src/components/select.tsx +342 -0
- package/src/components/stack.tsx +90 -0
- package/src/components/surface.tsx +88 -0
- package/src/components/switch.tsx +72 -0
- package/src/components/text.tsx +59 -0
- package/src/components/textarea.tsx +43 -0
- package/src/index.ts +120 -0
- package/src/lib/cn.ts +6 -0
- package/src/semantic.ts +72 -0
- package/src/theme.ts +161 -0
- package/styles.css +2041 -0
- package/tokens.css +90 -0
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { cn } from "@/lib/cn";
|
|
4
|
+
|
|
5
|
+
function MenuCheckIcon(props: React.ComponentProps<"svg">) {
|
|
6
|
+
return (
|
|
7
|
+
<svg viewBox="0 0 16 16" fill="none" aria-hidden {...props}>
|
|
8
|
+
<path
|
|
9
|
+
d="M3.5 8.5 6.5 11.5 12.5 4.5"
|
|
10
|
+
stroke="currentColor"
|
|
11
|
+
strokeWidth="1.75"
|
|
12
|
+
strokeLinecap="round"
|
|
13
|
+
strokeLinejoin="round"
|
|
14
|
+
/>
|
|
15
|
+
</svg>
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function MenuDotIcon(props: React.ComponentProps<"svg">) {
|
|
20
|
+
return (
|
|
21
|
+
<svg viewBox="0 0 16 16" fill="currentColor" aria-hidden {...props}>
|
|
22
|
+
<circle cx="8" cy="8" r="3" />
|
|
23
|
+
</svg>
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function MenuChevronRightIcon(props: React.ComponentProps<"svg">) {
|
|
28
|
+
return (
|
|
29
|
+
<svg viewBox="0 0 16 16" fill="none" aria-hidden {...props}>
|
|
30
|
+
<path
|
|
31
|
+
d="M6 3.5 10.5 8 6 12.5"
|
|
32
|
+
stroke="currentColor"
|
|
33
|
+
strokeWidth="1.5"
|
|
34
|
+
strokeLinecap="round"
|
|
35
|
+
strokeLinejoin="round"
|
|
36
|
+
/>
|
|
37
|
+
</svg>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function DropdownMenu(
|
|
42
|
+
props: React.ComponentProps<typeof DropdownMenuPrimitive.Root>,
|
|
43
|
+
) {
|
|
44
|
+
return <DropdownMenuPrimitive.Root {...props} />;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function DropdownMenuPortal(
|
|
48
|
+
props: React.ComponentProps<typeof DropdownMenuPrimitive.Portal>,
|
|
49
|
+
) {
|
|
50
|
+
return <DropdownMenuPrimitive.Portal {...props} />;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function DropdownMenuTrigger(
|
|
54
|
+
props: React.ComponentProps<typeof DropdownMenuPrimitive.Trigger>,
|
|
55
|
+
) {
|
|
56
|
+
return <DropdownMenuPrimitive.Trigger {...props} />;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function DropdownMenuContent({
|
|
60
|
+
className,
|
|
61
|
+
sideOffset = 6,
|
|
62
|
+
...props
|
|
63
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.Content>) {
|
|
64
|
+
return (
|
|
65
|
+
<DropdownMenuPrimitive.Portal>
|
|
66
|
+
<DropdownMenuPrimitive.Content
|
|
67
|
+
sideOffset={sideOffset}
|
|
68
|
+
className={cn("ui-dropdown-content", className)}
|
|
69
|
+
{...props}
|
|
70
|
+
/>
|
|
71
|
+
</DropdownMenuPrimitive.Portal>
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function DropdownMenuGroup(
|
|
76
|
+
props: React.ComponentProps<typeof DropdownMenuPrimitive.Group>,
|
|
77
|
+
) {
|
|
78
|
+
return <DropdownMenuPrimitive.Group {...props} />;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function DropdownMenuItem({
|
|
82
|
+
className,
|
|
83
|
+
inset = false,
|
|
84
|
+
variant = "default",
|
|
85
|
+
...props
|
|
86
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.Item> & {
|
|
87
|
+
inset?: boolean;
|
|
88
|
+
variant?: "default" | "destructive";
|
|
89
|
+
}) {
|
|
90
|
+
return (
|
|
91
|
+
<DropdownMenuPrimitive.Item
|
|
92
|
+
data-inset={inset ? "true" : undefined}
|
|
93
|
+
data-variant={variant}
|
|
94
|
+
className={cn("ui-dropdown-item", className)}
|
|
95
|
+
{...props}
|
|
96
|
+
/>
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function DropdownMenuCheckboxItem({
|
|
101
|
+
className,
|
|
102
|
+
children,
|
|
103
|
+
checked,
|
|
104
|
+
...props
|
|
105
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.CheckboxItem>) {
|
|
106
|
+
return (
|
|
107
|
+
<DropdownMenuPrimitive.CheckboxItem
|
|
108
|
+
checked={checked}
|
|
109
|
+
className={cn(
|
|
110
|
+
"ui-dropdown-item ui-dropdown-item--with-indicator",
|
|
111
|
+
className,
|
|
112
|
+
)}
|
|
113
|
+
{...props}
|
|
114
|
+
>
|
|
115
|
+
<span className="ui-dropdown-indicator" aria-hidden>
|
|
116
|
+
<DropdownMenuPrimitive.ItemIndicator>
|
|
117
|
+
<MenuCheckIcon className="ui-dropdown-indicator-icon" />
|
|
118
|
+
</DropdownMenuPrimitive.ItemIndicator>
|
|
119
|
+
</span>
|
|
120
|
+
{children}
|
|
121
|
+
</DropdownMenuPrimitive.CheckboxItem>
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function DropdownMenuRadioGroup(
|
|
126
|
+
props: React.ComponentProps<typeof DropdownMenuPrimitive.RadioGroup>,
|
|
127
|
+
) {
|
|
128
|
+
return <DropdownMenuPrimitive.RadioGroup {...props} />;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function DropdownMenuRadioItem({
|
|
132
|
+
className,
|
|
133
|
+
children,
|
|
134
|
+
...props
|
|
135
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.RadioItem>) {
|
|
136
|
+
return (
|
|
137
|
+
<DropdownMenuPrimitive.RadioItem
|
|
138
|
+
className={cn(
|
|
139
|
+
"ui-dropdown-item ui-dropdown-item--with-indicator",
|
|
140
|
+
className,
|
|
141
|
+
)}
|
|
142
|
+
{...props}
|
|
143
|
+
>
|
|
144
|
+
<span className="ui-dropdown-indicator" aria-hidden>
|
|
145
|
+
<DropdownMenuPrimitive.ItemIndicator>
|
|
146
|
+
<MenuDotIcon className="ui-dropdown-indicator-icon" />
|
|
147
|
+
</DropdownMenuPrimitive.ItemIndicator>
|
|
148
|
+
</span>
|
|
149
|
+
{children}
|
|
150
|
+
</DropdownMenuPrimitive.RadioItem>
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function DropdownMenuLabel({
|
|
155
|
+
className,
|
|
156
|
+
inset = false,
|
|
157
|
+
...props
|
|
158
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.Label> & {
|
|
159
|
+
inset?: boolean;
|
|
160
|
+
}) {
|
|
161
|
+
return (
|
|
162
|
+
<DropdownMenuPrimitive.Label
|
|
163
|
+
data-inset={inset ? "true" : undefined}
|
|
164
|
+
className={cn("ui-dropdown-label", className)}
|
|
165
|
+
{...props}
|
|
166
|
+
/>
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function DropdownMenuSeparator({
|
|
171
|
+
className,
|
|
172
|
+
...props
|
|
173
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.Separator>) {
|
|
174
|
+
return (
|
|
175
|
+
<DropdownMenuPrimitive.Separator
|
|
176
|
+
className={cn("ui-dropdown-separator", className)}
|
|
177
|
+
{...props}
|
|
178
|
+
/>
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function DropdownMenuShortcut({
|
|
183
|
+
className,
|
|
184
|
+
...props
|
|
185
|
+
}: React.ComponentProps<"span">) {
|
|
186
|
+
return <span className={cn("ui-dropdown-shortcut", className)} {...props} />;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function DropdownMenuSub(
|
|
190
|
+
props: React.ComponentProps<typeof DropdownMenuPrimitive.Sub>,
|
|
191
|
+
) {
|
|
192
|
+
return <DropdownMenuPrimitive.Sub {...props} />;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function DropdownMenuSubTrigger({
|
|
196
|
+
className,
|
|
197
|
+
inset = false,
|
|
198
|
+
children,
|
|
199
|
+
...props
|
|
200
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.SubTrigger> & {
|
|
201
|
+
inset?: boolean;
|
|
202
|
+
}) {
|
|
203
|
+
return (
|
|
204
|
+
<DropdownMenuPrimitive.SubTrigger
|
|
205
|
+
data-inset={inset ? "true" : undefined}
|
|
206
|
+
className={cn("ui-dropdown-item ui-dropdown-sub-trigger", className)}
|
|
207
|
+
{...props}
|
|
208
|
+
>
|
|
209
|
+
{children}
|
|
210
|
+
<MenuChevronRightIcon className="ui-dropdown-chevron" />
|
|
211
|
+
</DropdownMenuPrimitive.SubTrigger>
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function DropdownMenuSubContent({
|
|
216
|
+
className,
|
|
217
|
+
...props
|
|
218
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.SubContent>) {
|
|
219
|
+
return (
|
|
220
|
+
<DropdownMenuPrimitive.SubContent
|
|
221
|
+
className={cn("ui-dropdown-content ui-dropdown-sub-content", className)}
|
|
222
|
+
{...props}
|
|
223
|
+
/>
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export {
|
|
228
|
+
DropdownMenu,
|
|
229
|
+
DropdownMenuPortal,
|
|
230
|
+
DropdownMenuTrigger,
|
|
231
|
+
DropdownMenuContent,
|
|
232
|
+
DropdownMenuGroup,
|
|
233
|
+
DropdownMenuLabel,
|
|
234
|
+
DropdownMenuItem,
|
|
235
|
+
DropdownMenuCheckboxItem,
|
|
236
|
+
DropdownMenuRadioGroup,
|
|
237
|
+
DropdownMenuRadioItem,
|
|
238
|
+
DropdownMenuSeparator,
|
|
239
|
+
DropdownMenuShortcut,
|
|
240
|
+
DropdownMenuSub,
|
|
241
|
+
DropdownMenuSubTrigger,
|
|
242
|
+
DropdownMenuSubContent,
|
|
243
|
+
};
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
import {
|
|
2
|
+
forwardRef,
|
|
3
|
+
type CSSProperties,
|
|
4
|
+
type HTMLAttributes,
|
|
5
|
+
type KeyboardEvent,
|
|
6
|
+
type PointerEvent,
|
|
7
|
+
type WheelEvent,
|
|
8
|
+
useRef,
|
|
9
|
+
useState,
|
|
10
|
+
} from "react";
|
|
11
|
+
import { cn } from "@/lib/cn";
|
|
12
|
+
|
|
13
|
+
type EncoderSize = "sm" | "md";
|
|
14
|
+
|
|
15
|
+
type DragState = {
|
|
16
|
+
pointerId: number;
|
|
17
|
+
lastY: number;
|
|
18
|
+
sliderValue: number;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const FULL_RANGE_DRAG_DISTANCE_PX = 80;
|
|
22
|
+
const FINE_DRAG_DISTANCE_MULTIPLIER = 8;
|
|
23
|
+
|
|
24
|
+
const clamp = (value: number, min: number, max: number) =>
|
|
25
|
+
Math.min(max, Math.max(min, value));
|
|
26
|
+
|
|
27
|
+
const calcSliderValue = (
|
|
28
|
+
actualValue: number,
|
|
29
|
+
min: number,
|
|
30
|
+
max: number,
|
|
31
|
+
exp?: number,
|
|
32
|
+
) => {
|
|
33
|
+
if (exp === undefined || exp === 1) return actualValue;
|
|
34
|
+
|
|
35
|
+
const range = max - min;
|
|
36
|
+
if (range === 0) return min;
|
|
37
|
+
|
|
38
|
+
const normalizedValue = (actualValue - min) / range;
|
|
39
|
+
const inverseExp = 1 / exp;
|
|
40
|
+
|
|
41
|
+
return min + Math.pow(normalizedValue, inverseExp) * range;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const calcActualValue = (
|
|
45
|
+
sliderValue: number,
|
|
46
|
+
min: number,
|
|
47
|
+
max: number,
|
|
48
|
+
exp?: number,
|
|
49
|
+
) => {
|
|
50
|
+
if (exp === undefined || exp === 1) return sliderValue;
|
|
51
|
+
|
|
52
|
+
const range = max - min;
|
|
53
|
+
if (range === 0) return min;
|
|
54
|
+
|
|
55
|
+
const normalizedSlider = (sliderValue - min) / range;
|
|
56
|
+
|
|
57
|
+
return min + Math.pow(normalizedSlider, exp) * range;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const getStepPrecision = (step: number) => {
|
|
61
|
+
const stepString = `${step}`;
|
|
62
|
+
const decimalIndex = stepString.indexOf(".");
|
|
63
|
+
|
|
64
|
+
return decimalIndex === -1 ? 0 : stepString.length - decimalIndex - 1;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const roundToStep = (value: number, min: number, step: number) => {
|
|
68
|
+
const precision = getStepPrecision(step);
|
|
69
|
+
const nextValue = Math.round((value - min) / step) * step + min;
|
|
70
|
+
|
|
71
|
+
return Number(nextValue.toFixed(precision));
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const formatDisplayValue = (value: number, step: number) => {
|
|
75
|
+
const precision = Math.min(4, getStepPrecision(step));
|
|
76
|
+
|
|
77
|
+
if (precision === 0) return `${Math.round(value)}`;
|
|
78
|
+
|
|
79
|
+
return `${Number(value.toFixed(precision))}`;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
export interface EncoderProps extends Omit<
|
|
83
|
+
HTMLAttributes<HTMLDivElement>,
|
|
84
|
+
"onChange"
|
|
85
|
+
> {
|
|
86
|
+
name: string;
|
|
87
|
+
onChange: (value: number) => void;
|
|
88
|
+
defaultValue?: number;
|
|
89
|
+
value?: number;
|
|
90
|
+
max?: number;
|
|
91
|
+
min?: number;
|
|
92
|
+
step?: number;
|
|
93
|
+
exp?: number;
|
|
94
|
+
size?: EncoderSize;
|
|
95
|
+
disabled?: boolean;
|
|
96
|
+
formatValue?: (value: number) => string;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const Encoder = forwardRef<HTMLDivElement, EncoderProps>(
|
|
100
|
+
(
|
|
101
|
+
{
|
|
102
|
+
className,
|
|
103
|
+
style,
|
|
104
|
+
name,
|
|
105
|
+
onChange,
|
|
106
|
+
value,
|
|
107
|
+
defaultValue,
|
|
108
|
+
max = 1,
|
|
109
|
+
min = 0,
|
|
110
|
+
step = 0.01,
|
|
111
|
+
exp,
|
|
112
|
+
size = "md",
|
|
113
|
+
disabled = false,
|
|
114
|
+
formatValue,
|
|
115
|
+
onKeyDown,
|
|
116
|
+
onPointerDown,
|
|
117
|
+
onPointerMove,
|
|
118
|
+
onPointerUp,
|
|
119
|
+
onPointerCancel,
|
|
120
|
+
onWheel,
|
|
121
|
+
...rest
|
|
122
|
+
},
|
|
123
|
+
ref,
|
|
124
|
+
) => {
|
|
125
|
+
const [uncontrolledValue, setUncontrolledValue] = useState(
|
|
126
|
+
clamp(defaultValue ?? min, min, max),
|
|
127
|
+
);
|
|
128
|
+
const dragStateRef = useRef<DragState | null>(null);
|
|
129
|
+
|
|
130
|
+
const currentValue = clamp(value ?? uncontrolledValue, min, max);
|
|
131
|
+
const currentSliderValue = clamp(
|
|
132
|
+
calcSliderValue(currentValue, min, max, exp),
|
|
133
|
+
min,
|
|
134
|
+
max,
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
const progress = max === min ? 0 : (currentSliderValue - min) / (max - min);
|
|
138
|
+
|
|
139
|
+
const displayValue = formatValue
|
|
140
|
+
? formatValue(currentValue)
|
|
141
|
+
: formatDisplayValue(currentValue, step);
|
|
142
|
+
|
|
143
|
+
const commitValue = (nextRawValue: number) => {
|
|
144
|
+
const nextValue = clamp(roundToStep(nextRawValue, min, step), min, max);
|
|
145
|
+
|
|
146
|
+
if (nextValue === currentValue) return;
|
|
147
|
+
|
|
148
|
+
if (value === undefined) {
|
|
149
|
+
setUncontrolledValue(nextValue);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
onChange(nextValue);
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
const changeBySteps = (stepDelta: number) => {
|
|
156
|
+
commitValue(currentValue + stepDelta * step);
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
const handleKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {
|
|
160
|
+
onKeyDown?.(event);
|
|
161
|
+
|
|
162
|
+
if (event.defaultPrevented || disabled) return;
|
|
163
|
+
|
|
164
|
+
switch (event.key) {
|
|
165
|
+
case "ArrowUp":
|
|
166
|
+
case "ArrowRight":
|
|
167
|
+
event.preventDefault();
|
|
168
|
+
changeBySteps(1);
|
|
169
|
+
return;
|
|
170
|
+
case "ArrowDown":
|
|
171
|
+
case "ArrowLeft":
|
|
172
|
+
event.preventDefault();
|
|
173
|
+
changeBySteps(-1);
|
|
174
|
+
return;
|
|
175
|
+
case "PageUp":
|
|
176
|
+
event.preventDefault();
|
|
177
|
+
changeBySteps(10);
|
|
178
|
+
return;
|
|
179
|
+
case "PageDown":
|
|
180
|
+
event.preventDefault();
|
|
181
|
+
changeBySteps(-10);
|
|
182
|
+
return;
|
|
183
|
+
case "Home":
|
|
184
|
+
event.preventDefault();
|
|
185
|
+
commitValue(min);
|
|
186
|
+
return;
|
|
187
|
+
case "End":
|
|
188
|
+
event.preventDefault();
|
|
189
|
+
commitValue(max);
|
|
190
|
+
return;
|
|
191
|
+
default:
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
const handlePointerDown = (event: PointerEvent<HTMLDivElement>) => {
|
|
197
|
+
onPointerDown?.(event);
|
|
198
|
+
|
|
199
|
+
if (event.defaultPrevented || disabled) return;
|
|
200
|
+
|
|
201
|
+
event.preventDefault();
|
|
202
|
+
event.currentTarget.focus();
|
|
203
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
204
|
+
|
|
205
|
+
dragStateRef.current = {
|
|
206
|
+
pointerId: event.pointerId,
|
|
207
|
+
lastY: event.clientY,
|
|
208
|
+
sliderValue: currentSliderValue,
|
|
209
|
+
};
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
const handlePointerMove = (event: PointerEvent<HTMLDivElement>) => {
|
|
213
|
+
onPointerMove?.(event);
|
|
214
|
+
|
|
215
|
+
if (event.defaultPrevented || disabled) return;
|
|
216
|
+
|
|
217
|
+
const dragState = dragStateRef.current;
|
|
218
|
+
|
|
219
|
+
if (dragState?.pointerId !== event.pointerId) return;
|
|
220
|
+
|
|
221
|
+
event.preventDefault();
|
|
222
|
+
|
|
223
|
+
const dragDistance =
|
|
224
|
+
FULL_RANGE_DRAG_DISTANCE_PX *
|
|
225
|
+
(event.shiftKey ? FINE_DRAG_DISTANCE_MULTIPLIER : 1);
|
|
226
|
+
const deltaSliderValue =
|
|
227
|
+
((dragState.lastY - event.clientY) * (max - min)) / dragDistance;
|
|
228
|
+
const nextSliderValue = clamp(
|
|
229
|
+
dragState.sliderValue + deltaSliderValue,
|
|
230
|
+
min,
|
|
231
|
+
max,
|
|
232
|
+
);
|
|
233
|
+
const nextValue = calcActualValue(nextSliderValue, min, max, exp);
|
|
234
|
+
|
|
235
|
+
dragState.lastY = event.clientY;
|
|
236
|
+
dragState.sliderValue = nextSliderValue;
|
|
237
|
+
|
|
238
|
+
commitValue(nextValue);
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
const clearDragState = (event: PointerEvent<HTMLDivElement>) => {
|
|
242
|
+
const dragState = dragStateRef.current;
|
|
243
|
+
|
|
244
|
+
if (dragState?.pointerId !== event.pointerId) return;
|
|
245
|
+
|
|
246
|
+
if (event.currentTarget.hasPointerCapture(event.pointerId)) {
|
|
247
|
+
event.currentTarget.releasePointerCapture(event.pointerId);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
dragStateRef.current = null;
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
const handlePointerUp = (event: PointerEvent<HTMLDivElement>) => {
|
|
254
|
+
onPointerUp?.(event);
|
|
255
|
+
|
|
256
|
+
if (event.defaultPrevented) return;
|
|
257
|
+
|
|
258
|
+
clearDragState(event);
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
const handlePointerCancel = (event: PointerEvent<HTMLDivElement>) => {
|
|
262
|
+
onPointerCancel?.(event);
|
|
263
|
+
|
|
264
|
+
if (event.defaultPrevented) return;
|
|
265
|
+
|
|
266
|
+
clearDragState(event);
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
const handleWheel = (event: WheelEvent<HTMLDivElement>) => {
|
|
270
|
+
onWheel?.(event);
|
|
271
|
+
|
|
272
|
+
if (event.defaultPrevented || disabled || event.deltaY === 0) return;
|
|
273
|
+
|
|
274
|
+
event.preventDefault();
|
|
275
|
+
changeBySteps(event.deltaY < 0 ? 1 : -1);
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
return (
|
|
279
|
+
<div
|
|
280
|
+
ref={ref}
|
|
281
|
+
role="slider"
|
|
282
|
+
tabIndex={disabled ? -1 : 0}
|
|
283
|
+
aria-label={name}
|
|
284
|
+
aria-disabled={disabled}
|
|
285
|
+
aria-valuemin={min}
|
|
286
|
+
aria-valuemax={max}
|
|
287
|
+
aria-valuenow={currentValue}
|
|
288
|
+
aria-valuetext={displayValue}
|
|
289
|
+
data-size={size}
|
|
290
|
+
className={cn(
|
|
291
|
+
"ui-encoder",
|
|
292
|
+
size === "sm" && "ui-encoder--size-sm",
|
|
293
|
+
size === "md" && "ui-encoder--size-md",
|
|
294
|
+
disabled && "ui-encoder--disabled",
|
|
295
|
+
className,
|
|
296
|
+
)}
|
|
297
|
+
style={
|
|
298
|
+
{
|
|
299
|
+
...style,
|
|
300
|
+
"--ui-encoder-angle": `${-135 + progress * 270}deg`,
|
|
301
|
+
"--ui-encoder-fill": `${progress * 270}deg`,
|
|
302
|
+
} as CSSProperties
|
|
303
|
+
}
|
|
304
|
+
onKeyDown={handleKeyDown}
|
|
305
|
+
onPointerDown={handlePointerDown}
|
|
306
|
+
onPointerMove={handlePointerMove}
|
|
307
|
+
onPointerUp={handlePointerUp}
|
|
308
|
+
onPointerCancel={handlePointerCancel}
|
|
309
|
+
onWheel={handleWheel}
|
|
310
|
+
{...rest}
|
|
311
|
+
>
|
|
312
|
+
<div className="ui-encoder__dial">
|
|
313
|
+
<span className="ui-encoder__indicator" aria-hidden />
|
|
314
|
+
</div>
|
|
315
|
+
<span className="ui-encoder__value">{displayValue}</span>
|
|
316
|
+
</div>
|
|
317
|
+
);
|
|
318
|
+
},
|
|
319
|
+
);
|
|
320
|
+
|
|
321
|
+
Encoder.displayName = "Encoder";
|
|
322
|
+
|
|
323
|
+
export { Encoder };
|