@fab1o978/react-ui 0.1.5 → 0.1.7

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,175 @@ 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
+ leftIcon,
176
+ rightIcon,
177
+ railBackground,
178
+ accent,
179
+ className,
180
+ disabled = false
181
+ }) => {
182
+ const isControlled = controlledValue !== void 0;
183
+ const [internalValue, setInternalValue] = react.useState(defaultValue);
184
+ const [isDragging, setIsDragging] = react.useState(false);
185
+ const [isFocused, setIsFocused] = react.useState(false);
186
+ const trackRef = react.useRef(null);
187
+ const thumbRef = react.useRef(null);
188
+ const value = isControlled ? controlledValue : internalValue;
189
+ const pct = (clamp(value, min, max) - min) / (max - min);
190
+ const accentVars = accent ? accentToCssVars(deriveAccent(accent), "slider") : {};
191
+ const setValue = react.useCallback(
192
+ (next) => {
193
+ const snapped = clamp(snapToStep(next, min, step), min, max);
194
+ if (!isControlled) setInternalValue(snapped);
195
+ onChange?.(snapped);
196
+ },
197
+ [isControlled, min, max, step, onChange]
198
+ );
199
+ const valueFromPointer = react.useCallback(
200
+ (e) => {
201
+ const rect = trackRef.current.getBoundingClientRect();
202
+ const ratio = clamp((e.clientX - rect.left) / rect.width, 0, 1);
203
+ return min + ratio * (max - min);
204
+ },
205
+ [min, max]
206
+ );
207
+ const handlePointerDown = react.useCallback(
208
+ (e) => {
209
+ if (disabled) return;
210
+ e.currentTarget.setPointerCapture(e.pointerId);
211
+ setIsDragging(true);
212
+ thumbRef.current?.focus();
213
+ setValue(valueFromPointer(e.nativeEvent));
214
+ },
215
+ [disabled, setValue, valueFromPointer]
216
+ );
217
+ const handlePointerMove = react.useCallback(
218
+ (e) => {
219
+ if (!isDragging || disabled) return;
220
+ setValue(valueFromPointer(e.nativeEvent));
221
+ },
222
+ [isDragging, disabled, setValue, valueFromPointer]
223
+ );
224
+ const handlePointerUp = react.useCallback(() => {
225
+ setIsDragging(false);
226
+ thumbRef.current?.blur();
227
+ }, []);
228
+ react.useEffect(() => {
229
+ const el = trackRef.current;
230
+ if (!el) return;
231
+ const onWheel = (e) => {
232
+ if (disabled) return;
233
+ e.preventDefault();
234
+ setValue(value + (e.deltaY < 0 ? step : -step));
235
+ };
236
+ el.addEventListener("wheel", onWheel, { passive: false });
237
+ return () => el.removeEventListener("wheel", onWheel);
238
+ }, [disabled, value, step, setValue]);
239
+ const handleKeyDown = react.useCallback(
240
+ (e) => {
241
+ if (disabled) return;
242
+ const map = {
243
+ ArrowRight: step,
244
+ ArrowUp: step,
245
+ ArrowLeft: -step,
246
+ ArrowDown: -step,
247
+ PageUp: step * 10,
248
+ PageDown: -step * 10
249
+ };
250
+ if (e.key === "Home") {
251
+ setValue(min);
252
+ e.preventDefault();
253
+ return;
254
+ }
255
+ if (e.key === "End") {
256
+ setValue(max);
257
+ e.preventDefault();
258
+ return;
259
+ }
260
+ if (map[e.key] !== void 0) {
261
+ setValue(value + map[e.key]);
262
+ e.preventDefault();
263
+ }
264
+ },
265
+ [disabled, step, min, max, value, setValue]
266
+ );
267
+ const tooltipVisible = showTooltip === "always" || showTooltip === "drag" && (isDragging || isFocused);
268
+ const displayValue = `${Math.round(value)}%`;
269
+ const tooltipOffset = `calc(${pct * 100}% + ${(0.5 - pct) * 20}px)`;
270
+ return /* @__PURE__ */ jsxRuntime.jsxs(
271
+ "div",
272
+ {
273
+ className: [SliderControl_module_default.wrapper, disabled ? SliderControl_module_default.disabled : "", className ?? ""].filter(Boolean).join(" "),
274
+ style: accentVars,
275
+ children: [
276
+ leftIcon !== null && leftIcon !== void 0 && /* @__PURE__ */ jsxRuntime.jsx("span", { className: SliderControl_module_default.icon, children: leftIcon }),
277
+ /* @__PURE__ */ jsxRuntime.jsxs(
278
+ "div",
279
+ {
280
+ ref: trackRef,
281
+ className: SliderControl_module_default.trackArea,
282
+ onPointerDown: handlePointerDown,
283
+ onPointerMove: handlePointerMove,
284
+ onPointerUp: handlePointerUp,
285
+ onPointerCancel: handlePointerUp,
286
+ children: [
287
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: SliderControl_module_default.groove, children: /* @__PURE__ */ jsxRuntime.jsx(
288
+ "div",
289
+ {
290
+ className: SliderControl_module_default.rail,
291
+ style: railBackground ? { background: railBackground } : void 0,
292
+ children: !railBackground && /* @__PURE__ */ jsxRuntime.jsx("div", { className: SliderControl_module_default.fill, style: { width: `${pct * 100}%` } })
293
+ }
294
+ ) }),
295
+ /* @__PURE__ */ jsxRuntime.jsx(
296
+ "div",
297
+ {
298
+ ref: thumbRef,
299
+ role: "slider",
300
+ tabIndex: disabled ? -1 : 0,
301
+ "aria-valuenow": Math.round(value),
302
+ "aria-valuemin": min,
303
+ "aria-valuemax": max,
304
+ "aria-disabled": disabled,
305
+ className: [SliderControl_module_default.thumb, isDragging ? SliderControl_module_default.thumbDragging : ""].filter(Boolean).join(" "),
306
+ style: { left: `calc(${pct * 100}% + ${(0.5 - pct) * 24}px)` },
307
+ onKeyDown: handleKeyDown,
308
+ onFocus: () => setIsFocused(true),
309
+ onBlur: () => setIsFocused(false)
310
+ }
311
+ ),
312
+ /* @__PURE__ */ jsxRuntime.jsx(
313
+ "div",
314
+ {
315
+ className: [SliderControl_module_default.tooltip, tooltipVisible ? SliderControl_module_default.tooltipVisible : ""].filter(Boolean).join(" "),
316
+ style: { left: tooltipOffset },
317
+ "aria-hidden": "true",
318
+ children: displayValue
319
+ }
320
+ )
321
+ ]
322
+ }
323
+ ),
324
+ rightIcon !== null && rightIcon !== void 0 && /* @__PURE__ */ jsxRuntime.jsx("span", { className: SliderControl_module_default.icon, children: rightIcon })
325
+ ]
326
+ }
327
+ );
328
+ };
329
+ SliderControl.displayName = "SliderControl";
150
330
  var DISC = 200;
151
331
  var CopyIcon = () => /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", children: [
152
332
  /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "5", y: "5", width: "7", height: "7", rx: "1.5", stroke: "currentColor", strokeWidth: "1.4" }),
@@ -316,50 +496,21 @@ var ColorPicker = ({
316
496
  },
317
497
  [updateDisc]
318
498
  );
319
- const updateL = react.useCallback(
320
- (clientX, rect) => {
321
- const nl = Math.max(0, Math.min(100, (clientX - rect.left) / rect.width * 100));
499
+ const handleLChange = react.useCallback(
500
+ (nl) => {
322
501
  setL(nl);
323
502
  notify(h, s, nl, a);
324
503
  },
325
504
  [h, s, a, notify]
326
505
  );
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));
506
+ const handleAChange = react.useCallback(
507
+ (val) => {
508
+ const na = val / 100;
344
509
  setA(na);
345
510
  notify(h, s, l, na);
346
511
  },
347
512
  [h, s, l, notify]
348
513
  );
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
514
  const { r: rr, g: gg, b: bb } = hslToRgb(h, s, l);
364
515
  const luminance = (0.299 * rr + 0.587 * gg + 0.114 * bb) / 255;
365
516
  const swatchFg = luminance > 0.6 ? "rgba(0,0,0,0.5)" : "rgba(255,255,255,0.9)";
@@ -372,7 +523,6 @@ var ColorPicker = ({
372
523
  hsl(${h}, ${s}%, 5%),
373
524
  hsl(${h}, ${s}%, 50%),
374
525
  hsl(${h}, ${s}%, 95%))`;
375
- const alphaGradient = `linear-gradient(to right, transparent, ${rgbStr})`;
376
526
  const handleEyeDropper = async () => {
377
527
  if (!window.EyeDropper) return;
378
528
  try {
@@ -433,35 +583,27 @@ var ColorPicker = ({
433
583
  )
434
584
  ] }) }),
435
585
  /* @__PURE__ */ jsxRuntime.jsx(
436
- "div",
586
+ SliderControl,
437
587
  {
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
- )
588
+ value: l,
589
+ onChange: handleLChange,
590
+ min: 0,
591
+ max: 100,
592
+ step: 1,
593
+ showTooltip: "never",
594
+ railBackground: lGradient
449
595
  }
450
596
  ),
451
597
  showAlpha && /* @__PURE__ */ jsxRuntime.jsx(
452
- "div",
598
+ SliderControl,
453
599
  {
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
- )
600
+ value: Math.round(a * 100),
601
+ onChange: handleAChange,
602
+ min: 0,
603
+ max: 100,
604
+ step: 1,
605
+ showTooltip: "never",
606
+ railBackground: `linear-gradient(to right, transparent, ${rgbStr}), repeating-conic-gradient(#ccc 0% 25%, #fff 0% 50%) 0 / 10px 10px`
465
607
  }
466
608
  ),
467
609
  /* @__PURE__ */ jsxRuntime.jsx(