@fab1o978/react-ui 0.1.5 → 0.1.8

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.
@@ -12,10 +12,6 @@ var ColorPicker_module_default = {
12
12
  discWrapper: "ColorPicker_module_discWrapper2",
13
13
  disc: "ColorPicker_module_disc2",
14
14
  dot: "ColorPicker_module_dot2",
15
- lSlider: "ColorPicker_module_lSlider2",
16
- lThumb: "ColorPicker_module_lThumb2",
17
- alphaSlider: "ColorPicker_module_alphaSlider2",
18
- alphaThumb: "ColorPicker_module_alphaThumb2",
19
15
  previewSwatch: "ColorPicker_module_previewSwatch2",
20
16
  eyedropperBtn: "ColorPicker_module_eyedropperBtn2",
21
17
  inputSection: "ColorPicker_module_inputSection2",
@@ -32,6 +28,21 @@ var ColorPicker_module_default = {
32
28
  recentSwatch: "ColorPicker_module_recentSwatch2"
33
29
  };
34
30
 
31
+ // src/components/SliderControl/SliderControl.module.scss
32
+ var SliderControl_module_default = {
33
+ wrapper: "SliderControl_module_wrapper2",
34
+ disabled: "SliderControl_module_disabled2",
35
+ icon: "SliderControl_module_icon2",
36
+ trackArea: "SliderControl_module_trackArea2",
37
+ groove: "SliderControl_module_groove2",
38
+ rail: "SliderControl_module_rail2",
39
+ fill: "SliderControl_module_fill2",
40
+ thumb: "SliderControl_module_thumb2",
41
+ thumbDragging: "SliderControl_module_thumbDragging2",
42
+ tooltip: "SliderControl_module_tooltip2",
43
+ tooltipVisible: "SliderControl_module_tooltipVisible2"
44
+ };
45
+
35
46
  // src/utils/color.ts
36
47
  function hexToHsl(hex) {
37
48
  const r = parseInt(hex.slice(1, 3), 16) / 255;
@@ -147,6 +158,177 @@ function accentToCssVars(tokens, prefix) {
147
158
  [`--${prefix}-accent-light`]: tokens.light
148
159
  };
149
160
  }
161
+ function clamp(v, min, max) {
162
+ return Math.min(Math.max(v, min), max);
163
+ }
164
+ function snapToStep(v, min, step) {
165
+ return Math.round((v - min) / step) * step + min;
166
+ }
167
+ var SliderControl = ({
168
+ value: controlledValue,
169
+ defaultValue = 50,
170
+ min = 0,
171
+ max = 100,
172
+ step = 1,
173
+ onChange,
174
+ showTooltip = "drag",
175
+ showPercentage = false,
176
+ leftIcon,
177
+ rightIcon,
178
+ railBackground,
179
+ accent,
180
+ className,
181
+ disabled = false
182
+ }) => {
183
+ const isControlled = controlledValue !== void 0;
184
+ const [internalValue, setInternalValue] = react.useState(defaultValue);
185
+ const [isDragging, setIsDragging] = react.useState(false);
186
+ const [isFocused, setIsFocused] = react.useState(false);
187
+ const trackRef = react.useRef(null);
188
+ const thumbRef = react.useRef(null);
189
+ const value = isControlled ? controlledValue : internalValue;
190
+ const pct = (clamp(value, min, max) - min) / (max - min);
191
+ const accentVars = accent ? accentToCssVars(deriveAccent(accent), "slider") : {};
192
+ const setValue = react.useCallback(
193
+ (next) => {
194
+ const snapped = clamp(snapToStep(next, min, step), min, max);
195
+ if (!isControlled) setInternalValue(snapped);
196
+ onChange?.(snapped);
197
+ },
198
+ [isControlled, min, max, step, onChange]
199
+ );
200
+ const valueFromPointer = react.useCallback(
201
+ (e) => {
202
+ const rect = trackRef.current.getBoundingClientRect();
203
+ const ratio = clamp((e.clientX - rect.left) / rect.width, 0, 1);
204
+ return min + ratio * (max - min);
205
+ },
206
+ [min, max]
207
+ );
208
+ const handlePointerDown = react.useCallback(
209
+ (e) => {
210
+ if (disabled) return;
211
+ e.currentTarget.setPointerCapture(e.pointerId);
212
+ setIsDragging(true);
213
+ thumbRef.current?.focus();
214
+ setValue(valueFromPointer(e.nativeEvent));
215
+ },
216
+ [disabled, setValue, valueFromPointer]
217
+ );
218
+ const handlePointerMove = react.useCallback(
219
+ (e) => {
220
+ if (!isDragging || disabled) return;
221
+ setValue(valueFromPointer(e.nativeEvent));
222
+ },
223
+ [isDragging, disabled, setValue, valueFromPointer]
224
+ );
225
+ const handlePointerUp = react.useCallback(() => {
226
+ setIsDragging(false);
227
+ thumbRef.current?.blur();
228
+ }, []);
229
+ react.useEffect(() => {
230
+ const el = trackRef.current;
231
+ if (!el) return;
232
+ const onWheel = (e) => {
233
+ if (disabled) return;
234
+ e.preventDefault();
235
+ setValue(value + (e.deltaY < 0 ? step : -step));
236
+ };
237
+ el.addEventListener("wheel", onWheel, { passive: false });
238
+ return () => el.removeEventListener("wheel", onWheel);
239
+ }, [disabled, value, step, setValue]);
240
+ const handleKeyDown = react.useCallback(
241
+ (e) => {
242
+ if (disabled) return;
243
+ const map = {
244
+ ArrowRight: step,
245
+ ArrowUp: step,
246
+ ArrowLeft: -step,
247
+ ArrowDown: -step,
248
+ PageUp: step * 10,
249
+ PageDown: -step * 10
250
+ };
251
+ if (e.key === "Home") {
252
+ setValue(min);
253
+ e.preventDefault();
254
+ return;
255
+ }
256
+ if (e.key === "End") {
257
+ setValue(max);
258
+ e.preventDefault();
259
+ return;
260
+ }
261
+ if (map[e.key] !== void 0) {
262
+ setValue(value + map[e.key]);
263
+ e.preventDefault();
264
+ }
265
+ },
266
+ [disabled, step, min, max, value, setValue]
267
+ );
268
+ const tooltipVisible = showTooltip === "always" || showTooltip === "drag" && (isDragging || isFocused);
269
+ const decimals = step < 1 ? Math.ceil(-Math.log10(step)) : 0;
270
+ const displayValue = showPercentage ? `${Math.round((value - min) / (max - min) * 100)}%` : value.toFixed(decimals);
271
+ const tooltipOffset = `calc(${pct * 100}% + ${(0.5 - pct) * 20}px)`;
272
+ return /* @__PURE__ */ jsxRuntime.jsxs(
273
+ "div",
274
+ {
275
+ className: [SliderControl_module_default.wrapper, disabled ? SliderControl_module_default.disabled : "", className ?? ""].filter(Boolean).join(" "),
276
+ style: accentVars,
277
+ children: [
278
+ leftIcon !== null && leftIcon !== void 0 && /* @__PURE__ */ jsxRuntime.jsx("span", { className: SliderControl_module_default.icon, children: leftIcon }),
279
+ /* @__PURE__ */ jsxRuntime.jsxs(
280
+ "div",
281
+ {
282
+ ref: trackRef,
283
+ className: SliderControl_module_default.trackArea,
284
+ onPointerDown: handlePointerDown,
285
+ onPointerMove: handlePointerMove,
286
+ onPointerUp: handlePointerUp,
287
+ onPointerCancel: handlePointerUp,
288
+ children: [
289
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: SliderControl_module_default.groove, children: /* @__PURE__ */ jsxRuntime.jsx(
290
+ "div",
291
+ {
292
+ className: SliderControl_module_default.rail,
293
+ style: railBackground ? { background: railBackground } : void 0,
294
+ children: !railBackground && /* @__PURE__ */ jsxRuntime.jsx("div", { className: SliderControl_module_default.fill, style: { width: `${pct * 100}%` } })
295
+ }
296
+ ) }),
297
+ /* @__PURE__ */ jsxRuntime.jsx(
298
+ "div",
299
+ {
300
+ ref: thumbRef,
301
+ role: "slider",
302
+ tabIndex: disabled ? -1 : 0,
303
+ "aria-valuenow": Math.round(value),
304
+ "aria-valuemin": min,
305
+ "aria-valuemax": max,
306
+ "aria-disabled": disabled,
307
+ className: [SliderControl_module_default.thumb, isDragging ? SliderControl_module_default.thumbDragging : ""].filter(Boolean).join(" "),
308
+ style: { left: `calc(${pct * 100}% + ${(0.5 - pct) * 24}px)` },
309
+ onKeyDown: handleKeyDown,
310
+ onFocus: () => setIsFocused(true),
311
+ onBlur: () => setIsFocused(false)
312
+ }
313
+ ),
314
+ /* @__PURE__ */ jsxRuntime.jsx(
315
+ "div",
316
+ {
317
+ className: [SliderControl_module_default.tooltip, tooltipVisible ? SliderControl_module_default.tooltipVisible : ""].filter(Boolean).join(" "),
318
+ style: { left: tooltipOffset },
319
+ "aria-hidden": "true",
320
+ children: displayValue
321
+ }
322
+ )
323
+ ]
324
+ }
325
+ ),
326
+ rightIcon !== null && rightIcon !== void 0 && /* @__PURE__ */ jsxRuntime.jsx("span", { className: SliderControl_module_default.icon, children: rightIcon })
327
+ ]
328
+ }
329
+ );
330
+ };
331
+ SliderControl.displayName = "SliderControl";
150
332
  var DISC = 200;
151
333
  var CopyIcon = () => /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", children: [
152
334
  /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "5", y: "5", width: "7", height: "7", rx: "1.5", stroke: "currentColor", strokeWidth: "1.4" }),
@@ -316,50 +498,21 @@ var ColorPicker = ({
316
498
  },
317
499
  [updateDisc]
318
500
  );
319
- const updateL = react.useCallback(
320
- (clientX, rect) => {
321
- const nl = Math.max(0, Math.min(100, (clientX - rect.left) / rect.width * 100));
501
+ const handleLChange = react.useCallback(
502
+ (nl) => {
322
503
  setL(nl);
323
504
  notify(h, s, nl, a);
324
505
  },
325
506
  [h, s, a, notify]
326
507
  );
327
- const handleLPointerDown = react.useCallback(
328
- (e) => {
329
- e.currentTarget.setPointerCapture(e.pointerId);
330
- updateL(e.clientX, e.currentTarget.getBoundingClientRect());
331
- },
332
- [updateL]
333
- );
334
- const handleLPointerMove = react.useCallback(
335
- (e) => {
336
- if (e.buttons === 0) return;
337
- updateL(e.clientX, e.currentTarget.getBoundingClientRect());
338
- },
339
- [updateL]
340
- );
341
- const updateA = react.useCallback(
342
- (clientX, rect) => {
343
- const na = Math.max(0, Math.min(1, (clientX - rect.left) / rect.width));
508
+ const handleAChange = react.useCallback(
509
+ (val) => {
510
+ const na = val / 100;
344
511
  setA(na);
345
512
  notify(h, s, l, na);
346
513
  },
347
514
  [h, s, l, notify]
348
515
  );
349
- const handleAPointerDown = react.useCallback(
350
- (e) => {
351
- e.currentTarget.setPointerCapture(e.pointerId);
352
- updateA(e.clientX, e.currentTarget.getBoundingClientRect());
353
- },
354
- [updateA]
355
- );
356
- const handleAPointerMove = react.useCallback(
357
- (e) => {
358
- if (e.buttons === 0) return;
359
- updateA(e.clientX, e.currentTarget.getBoundingClientRect());
360
- },
361
- [updateA]
362
- );
363
516
  const { r: rr, g: gg, b: bb } = hslToRgb(h, s, l);
364
517
  const luminance = (0.299 * rr + 0.587 * gg + 0.114 * bb) / 255;
365
518
  const swatchFg = luminance > 0.6 ? "rgba(0,0,0,0.5)" : "rgba(255,255,255,0.9)";
@@ -372,7 +525,6 @@ var ColorPicker = ({
372
525
  hsl(${h}, ${s}%, 5%),
373
526
  hsl(${h}, ${s}%, 50%),
374
527
  hsl(${h}, ${s}%, 95%))`;
375
- const alphaGradient = `linear-gradient(to right, transparent, ${rgbStr})`;
376
528
  const handleEyeDropper = async () => {
377
529
  if (!window.EyeDropper) return;
378
530
  try {
@@ -433,35 +585,27 @@ var ColorPicker = ({
433
585
  )
434
586
  ] }) }),
435
587
  /* @__PURE__ */ jsxRuntime.jsx(
436
- "div",
588
+ SliderControl,
437
589
  {
438
- className: ColorPicker_module_default.lSlider,
439
- style: { backgroundImage: lGradient },
440
- onPointerDown: handleLPointerDown,
441
- onPointerMove: handleLPointerMove,
442
- children: /* @__PURE__ */ jsxRuntime.jsx(
443
- "div",
444
- {
445
- className: ColorPicker_module_default.lThumb,
446
- style: { left: `${l}%` }
447
- }
448
- )
590
+ value: l,
591
+ onChange: handleLChange,
592
+ min: 0,
593
+ max: 100,
594
+ step: 1,
595
+ showTooltip: "never",
596
+ railBackground: lGradient
449
597
  }
450
598
  ),
451
599
  showAlpha && /* @__PURE__ */ jsxRuntime.jsx(
452
- "div",
600
+ SliderControl,
453
601
  {
454
- className: ColorPicker_module_default.alphaSlider,
455
- style: { backgroundImage: alphaGradient },
456
- onPointerDown: handleAPointerDown,
457
- onPointerMove: handleAPointerMove,
458
- children: /* @__PURE__ */ jsxRuntime.jsx(
459
- "div",
460
- {
461
- className: ColorPicker_module_default.alphaThumb,
462
- style: { left: `${a * 100}%` }
463
- }
464
- )
602
+ value: Math.round(a * 100),
603
+ onChange: handleAChange,
604
+ min: 0,
605
+ max: 100,
606
+ step: 1,
607
+ showTooltip: "never",
608
+ railBackground: `linear-gradient(to right, transparent, ${rgbStr}), repeating-conic-gradient(#ccc 0% 25%, #fff 0% 50%) 0 / 10px 10px`
465
609
  }
466
610
  ),
467
611
  /* @__PURE__ */ jsxRuntime.jsx(