colibri-ui 0.8.96 → 0.8.97

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.
Files changed (84) hide show
  1. package/ColorPicker/ColorPicker.d.ts +3 -0
  2. package/ColorPicker/ColorPicker.js +116 -0
  3. package/ColorPicker/ColorPicker.module.css +22 -0
  4. package/ColorPicker/assets/none.png +0 -0
  5. package/ColorPicker/components/ColorPreview/ColorPreview.module.css +23 -0
  6. package/ColorPicker/components/ColorPreview/index.d.ts +10 -0
  7. package/ColorPicker/components/ColorPreview/index.js +29 -0
  8. package/ColorPicker/components/CustomColorModal/CustomColorModal.module.css +18 -0
  9. package/ColorPicker/components/CustomColorModal/index.d.ts +15 -0
  10. package/ColorPicker/components/CustomColorModal/index.js +107 -0
  11. package/ColorPicker/components/HueSlider/HueSlider.module.css +31 -0
  12. package/ColorPicker/components/HueSlider/index.d.ts +10 -0
  13. package/ColorPicker/components/HueSlider/index.js +73 -0
  14. package/ColorPicker/components/Palette/Palette.module.css +12 -0
  15. package/ColorPicker/components/Palette/index.d.ts +19 -0
  16. package/ColorPicker/components/Palette/index.js +99 -0
  17. package/ColorPicker/components/SaturationPanel/SaturationPanel.module.css +26 -0
  18. package/ColorPicker/components/SaturationPanel/index.d.ts +13 -0
  19. package/ColorPicker/components/SaturationPanel/index.js +86 -0
  20. package/ColorPicker/components/Swatch/Swatch.module.css +36 -0
  21. package/ColorPicker/components/Swatch/index.d.ts +14 -0
  22. package/ColorPicker/components/Swatch/index.js +47 -0
  23. package/ColorPicker/hooks/useEyeDropper.d.ts +8 -0
  24. package/ColorPicker/hooks/useEyeDropper.js +38 -0
  25. package/ColorPicker/hooks/useHexDraft.d.ts +19 -0
  26. package/ColorPicker/hooks/useHexDraft.js +49 -0
  27. package/ColorPicker/hooks/usePointerDrag.d.ts +20 -0
  28. package/ColorPicker/hooks/usePointerDrag.js +55 -0
  29. package/ColorPicker/index.d.ts +3 -0
  30. package/ColorPicker/index.js +9 -0
  31. package/ColorPicker/index.props.d.ts +43 -0
  32. package/ColorPicker/package.json +9 -0
  33. package/ColorPicker/presets.d.ts +3 -0
  34. package/ColorPicker/presets.js +92 -0
  35. package/ColorPicker/utils/color.d.ts +25 -0
  36. package/ColorPicker/utils/color.js +162 -0
  37. package/Icons/Pipette.d.ts +3 -0
  38. package/Icons/Pipette.js +35 -0
  39. package/Icons/index.d.ts +1 -0
  40. package/Icons/index.js +2 -0
  41. package/esm/ColorPicker/ColorPicker.d.ts +3 -0
  42. package/esm/ColorPicker/ColorPicker.js +108 -0
  43. package/esm/ColorPicker/ColorPicker.module.css +22 -0
  44. package/esm/ColorPicker/assets/none.png +0 -0
  45. package/esm/ColorPicker/components/ColorPreview/ColorPreview.module.css +23 -0
  46. package/esm/ColorPicker/components/ColorPreview/index.d.ts +10 -0
  47. package/esm/ColorPicker/components/ColorPreview/index.js +21 -0
  48. package/esm/ColorPicker/components/CustomColorModal/CustomColorModal.module.css +18 -0
  49. package/esm/ColorPicker/components/CustomColorModal/index.d.ts +15 -0
  50. package/esm/ColorPicker/components/CustomColorModal/index.js +100 -0
  51. package/esm/ColorPicker/components/HueSlider/HueSlider.module.css +31 -0
  52. package/esm/ColorPicker/components/HueSlider/index.d.ts +10 -0
  53. package/esm/ColorPicker/components/HueSlider/index.js +66 -0
  54. package/esm/ColorPicker/components/Palette/Palette.module.css +12 -0
  55. package/esm/ColorPicker/components/Palette/index.d.ts +19 -0
  56. package/esm/ColorPicker/components/Palette/index.js +92 -0
  57. package/esm/ColorPicker/components/SaturationPanel/SaturationPanel.module.css +26 -0
  58. package/esm/ColorPicker/components/SaturationPanel/index.d.ts +13 -0
  59. package/esm/ColorPicker/components/SaturationPanel/index.js +79 -0
  60. package/esm/ColorPicker/components/Swatch/Swatch.module.css +36 -0
  61. package/esm/ColorPicker/components/Swatch/index.d.ts +14 -0
  62. package/esm/ColorPicker/components/Swatch/index.js +38 -0
  63. package/esm/ColorPicker/hooks/useEyeDropper.d.ts +8 -0
  64. package/esm/ColorPicker/hooks/useEyeDropper.js +36 -0
  65. package/esm/ColorPicker/hooks/useHexDraft.d.ts +19 -0
  66. package/esm/ColorPicker/hooks/useHexDraft.js +47 -0
  67. package/esm/ColorPicker/hooks/usePointerDrag.d.ts +20 -0
  68. package/esm/ColorPicker/hooks/usePointerDrag.js +53 -0
  69. package/esm/ColorPicker/index.d.ts +3 -0
  70. package/esm/ColorPicker/index.js +2 -0
  71. package/esm/ColorPicker/index.props.d.ts +43 -0
  72. package/esm/ColorPicker/presets.d.ts +3 -0
  73. package/esm/ColorPicker/presets.js +89 -0
  74. package/esm/ColorPicker/utils/color.d.ts +25 -0
  75. package/esm/ColorPicker/utils/color.js +152 -0
  76. package/esm/Icons/Pipette.d.ts +3 -0
  77. package/esm/Icons/Pipette.js +29 -0
  78. package/esm/Icons/index.d.ts +1 -0
  79. package/esm/Icons/index.js +1 -0
  80. package/esm/index.d.ts +2 -0
  81. package/esm/index.js +3 -0
  82. package/index.d.ts +2 -0
  83. package/index.js +7 -0
  84. package/package.json +1 -1
@@ -0,0 +1,19 @@
1
+ import React from "react";
2
+ import type { ColorPickerLabels, ColorPickerPreset } from "../../index.props";
3
+ interface PaletteProps {
4
+ presets: ColorPickerPreset[];
5
+ /** Normalized `#rrggbb` of the current color. */
6
+ value: string | null;
7
+ columns: number;
8
+ labels: ColorPickerLabels;
9
+ onSelect: (hex: string) => void;
10
+ onAddClick: () => void;
11
+ /** Tab or Shift+Tab leaves the palette. */
12
+ onTabOut: () => void;
13
+ }
14
+ /**
15
+ * Preset grid with the "+" button. A current color missing from the presets
16
+ * is shown as an extra swatch before "+".
17
+ */
18
+ export declare const Palette: ({ presets, value, columns, labels, onSelect, onAddClick, onTabOut, }: PaletteProps) => React.JSX.Element;
19
+ export {};
@@ -0,0 +1,92 @@
1
+ import React, { useRef, useEffect } from 'react';
2
+ import { Button } from '../../../Button/index.js';
3
+ import 'tslib';
4
+ import { Plus } from '../../../Icons/Plus.js';
5
+ import { Swatch } from '../Swatch/index.js';
6
+ import styles from './Palette.module.css';
7
+
8
+ /**
9
+ * Preset grid with the "+" button. A current color missing from the presets
10
+ * is shown as an extra swatch before "+".
11
+ */
12
+ const Palette = ({
13
+ presets,
14
+ value,
15
+ columns,
16
+ labels,
17
+ onSelect,
18
+ onAddClick,
19
+ onTabOut
20
+ }) => {
21
+ const rootRef = useRef(null);
22
+ const hasPreset = presets.some(preset => preset.value === value);
23
+ const colors = value && !hasPreset ? [...presets, {
24
+ value,
25
+ label: value
26
+ }] : presets;
27
+ // The dropdown is portaled to the end of the document: move focus into it,
28
+ // to the selected swatch or the first item ("+" when there are no colors).
29
+ useEffect(() => {
30
+ var _a, _b;
31
+ const root = rootRef.current;
32
+ (_b = (_a = root === null || root === void 0 ? void 0 : root.querySelector('[aria-checked="true"]')) !== null && _a !== void 0 ? _a : root === null || root === void 0 ? void 0 : root.querySelector("button")) === null || _b === void 0 ? void 0 : _b.focus();
33
+ }, []);
34
+ // Arrow keys move focus across the grid, Home/End go to the first and the
35
+ // last item. Tab leaves the portaled palette through the trigger, so focus
36
+ // continues from it, also inside a parent focus trap.
37
+ const handleKeyDown = event => {
38
+ var _a;
39
+ if (event.key === "Tab") {
40
+ onTabOut();
41
+ return;
42
+ }
43
+ const items = Array.from(event.currentTarget.querySelectorAll("button"));
44
+ const index = items.indexOf(event.target);
45
+ const offsets = {
46
+ ArrowLeft: -1,
47
+ ArrowRight: 1,
48
+ ArrowUp: -columns,
49
+ ArrowDown: columns,
50
+ Home: -index,
51
+ End: items.length - 1 - index
52
+ };
53
+ if (index === -1 || !(event.key in offsets)) return;
54
+ event.preventDefault();
55
+ (_a = items[index + offsets[event.key]]) === null || _a === void 0 ? void 0 : _a.focus();
56
+ };
57
+ return (
58
+ /*#__PURE__*/
59
+ // eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions -- keys come from the focused items
60
+ React.createElement("div", {
61
+ ref: rootRef,
62
+ role: "group",
63
+ "aria-label": labels.palette,
64
+ className: styles.root,
65
+ onKeyDown: handleKeyDown,
66
+ style: {
67
+ gridTemplateColumns: `repeat(${columns}, auto)`
68
+ }
69
+ }, colors.map(color => (/*#__PURE__*/React.createElement(Swatch, {
70
+ key: color.value,
71
+ color: color.value,
72
+ label: color.label,
73
+ selected: color.value === value,
74
+ onClick: () => onSelect(color.value)
75
+ }))), /*#__PURE__*/React.createElement(Button, {
76
+ className: styles.add,
77
+ variant: "secondary",
78
+ size: "xs",
79
+ icon: /*#__PURE__*/React.createElement(Plus, {
80
+ width: 12,
81
+ height: 12,
82
+ "aria-hidden": "true"
83
+ }),
84
+ role: "menuitem",
85
+ "aria-label": labels.addCustom,
86
+ title: labels.addCustom,
87
+ onClick: onAddClick
88
+ }))
89
+ );
90
+ };
91
+
92
+ export { Palette };
@@ -0,0 +1,26 @@
1
+ .root {
2
+ background-image: linear-gradient(to top, black, transparent), linear-gradient(to right, white, transparent);
3
+ border-radius: var(--radius-sm);
4
+ cursor: crosshair;
5
+ height: var(--space-32);
6
+ outline: none;
7
+ position: relative;
8
+ touch-action: none;
9
+ -webkit-user-select: none;
10
+ user-select: none;
11
+ width: 100%;
12
+ }
13
+ .root:focus-visible {
14
+ box-shadow: 0 0 0 var(--focus-ring-width) var(--color-focus-ring);
15
+ }
16
+
17
+ .thumb {
18
+ border: var(--focus-ring-width) solid var(--color-neutral-0);
19
+ border-radius: var(--radius-full);
20
+ box-shadow: var(--shadow-raised);
21
+ height: var(--space-5);
22
+ pointer-events: none;
23
+ position: absolute;
24
+ transform: translate(-50%, -50%);
25
+ width: var(--space-5);
26
+ }
@@ -0,0 +1,13 @@
1
+ import React from "react";
2
+ import type { HSV } from "../../utils/color";
3
+ interface SaturationPanelProps {
4
+ value: HSV;
5
+ onChange: (value: HSV) => void;
6
+ "aria-label": string;
7
+ }
8
+ /**
9
+ * Saturation (horizontal) and brightness (vertical) area. Arrow keys change
10
+ * by 1, with Shift by 10.
11
+ */
12
+ export declare const SaturationPanel: ({ value, onChange, "aria-label": ariaLabel, }: SaturationPanelProps) => React.JSX.Element;
13
+ export {};
@@ -0,0 +1,79 @@
1
+ import React from 'react';
2
+ import { usePointerDrag } from '../../hooks/usePointerDrag.js';
3
+ import { hsv2css, clamp } from '../../utils/color.js';
4
+ import styles from './SaturationPanel.module.css';
5
+
6
+ const STEP = 1;
7
+ const BIG_STEP = 10;
8
+ /**
9
+ * Saturation (horizontal) and brightness (vertical) area. Arrow keys change
10
+ * by 1, with Shift by 10.
11
+ */
12
+ const SaturationPanel = ({
13
+ value,
14
+ onChange,
15
+ "aria-label": ariaLabel
16
+ }) => {
17
+ const {
18
+ h,
19
+ s,
20
+ v
21
+ } = value;
22
+ const {
23
+ ref,
24
+ handlers
25
+ } = usePointerDrag(({
26
+ x,
27
+ y
28
+ }) => onChange({
29
+ h,
30
+ s: x * 100,
31
+ v: (1 - y) * 100
32
+ }));
33
+ const handleKeyDown = event => {
34
+ const step = event.shiftKey ? BIG_STEP : STEP;
35
+ const deltas = {
36
+ ArrowLeft: [-step, 0],
37
+ ArrowRight: [step, 0],
38
+ ArrowUp: [0, step],
39
+ ArrowDown: [0, -step]
40
+ };
41
+ const delta = deltas[event.key];
42
+ if (!delta) return;
43
+ event.preventDefault();
44
+ onChange({
45
+ h,
46
+ s: clamp(s + delta[0], 0, 100),
47
+ v: clamp(v + delta[1], 0, 100)
48
+ });
49
+ };
50
+ return /*#__PURE__*/React.createElement("div", Object.assign({
51
+ ref: ref
52
+ }, handlers, {
53
+ role: "slider",
54
+ tabIndex: 0,
55
+ "aria-label": ariaLabel,
56
+ "aria-valuemin": 0,
57
+ "aria-valuemax": 100,
58
+ "aria-valuenow": Math.round(s),
59
+ "aria-valuetext": `${Math.round(s)}%, ${Math.round(v)}%`,
60
+ onKeyDown: handleKeyDown,
61
+ className: styles.root,
62
+ style: {
63
+ backgroundColor: hsv2css({
64
+ h,
65
+ s: 100,
66
+ v: 100
67
+ })
68
+ }
69
+ }), /*#__PURE__*/React.createElement("div", {
70
+ className: styles.thumb,
71
+ style: {
72
+ left: `${s}%`,
73
+ top: `${100 - v}%`,
74
+ backgroundColor: hsv2css(value)
75
+ }
76
+ }));
77
+ };
78
+
79
+ export { SaturationPanel };
@@ -0,0 +1,36 @@
1
+ .root {
2
+ align-items: center;
3
+ border-radius: var(--radius-full);
4
+ cursor: pointer;
5
+ display: inline-flex;
6
+ flex-shrink: 0;
7
+ height: var(--space-6);
8
+ justify-content: center;
9
+ padding: 0;
10
+ transition: transform 0.1s ease;
11
+ width: var(--space-6);
12
+ }
13
+ .root:hover {
14
+ transform: scale(1.1);
15
+ }
16
+ .root:focus-visible {
17
+ outline: var(--focus-ring-width) solid var(--color-focus-ring);
18
+ outline-offset: var(--focus-ring-width);
19
+ }
20
+
21
+ .check_light {
22
+ color: var(--color-neutral-0);
23
+ }
24
+
25
+ .check_dark {
26
+ color: var(--color-neutral-950);
27
+ }
28
+
29
+ .outlined {
30
+ box-shadow: inset 0 0 0 var(--border-width-hairline) var(--color-border-default);
31
+ }
32
+
33
+ .check {
34
+ height: var(--space-4);
35
+ width: var(--space-4);
36
+ }
@@ -0,0 +1,14 @@
1
+ import React from "react";
2
+ import type { ButtonHTMLAttributes } from "react";
3
+ interface SwatchProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, "color" | "type"> {
4
+ /** `#rrggbb` */
5
+ color: string;
6
+ label: string;
7
+ selected: boolean;
8
+ }
9
+ /**
10
+ * Color circle button. The check mark adapts to the color, light colors get
11
+ * an outline.
12
+ */
13
+ export declare const Swatch: ({ color, label, selected, className, ...props }: SwatchProps) => React.JSX.Element;
14
+ export {};
@@ -0,0 +1,38 @@
1
+ import { __rest } from 'tslib';
2
+ import React from 'react';
3
+ import clsx from 'clsx';
4
+ import { Check } from '../../../Icons/Check.js';
5
+ import { prefersLightText, isLightColor } from '../../utils/color.js';
6
+ import styles from './Swatch.module.css';
7
+
8
+ /**
9
+ * Color circle button. The check mark adapts to the color, light colors get
10
+ * an outline.
11
+ */
12
+ const Swatch = _a => {
13
+ var {
14
+ color,
15
+ label,
16
+ selected,
17
+ className
18
+ } = _a,
19
+ props = __rest(_a, ["color", "label", "selected", "className"]);
20
+ return /*#__PURE__*/React.createElement("button", Object.assign({}, props, {
21
+ type: "button",
22
+ role: "menuitemradio",
23
+ "aria-checked": selected,
24
+ "aria-label": label,
25
+ title: label,
26
+ className: clsx(styles.root, prefersLightText(color) ? styles.check_light : styles.check_dark, {
27
+ [styles.outlined]: isLightColor(color)
28
+ }, className),
29
+ style: {
30
+ backgroundColor: color
31
+ }
32
+ }), selected && /*#__PURE__*/React.createElement(Check, {
33
+ className: styles.check,
34
+ "aria-hidden": "true"
35
+ }));
36
+ };
37
+
38
+ export { Swatch };
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Native browser eye dropper (EyeDropper API). Chromium only: in Safari and
3
+ * Firefox `isSupported` is `false`.
4
+ */
5
+ export declare const useEyeDropper: () => {
6
+ isSupported: boolean;
7
+ pick: () => Promise<string | null>;
8
+ };
@@ -0,0 +1,36 @@
1
+ import { useSyncExternalStore, useCallback } from 'react';
2
+ import { toHex } from '../utils/color.js';
3
+
4
+ const subscribe = () => () => {};
5
+ const getSnapshot = () => "EyeDropper" in window;
6
+ // No API on the server, so hydration does not mismatch.
7
+ const getServerSnapshot = () => false;
8
+ /**
9
+ * Native browser eye dropper (EyeDropper API). Chromium only: in Safari and
10
+ * Firefox `isSupported` is `false`.
11
+ */
12
+ const useEyeDropper = () => {
13
+ const isSupported = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
14
+ /** `#rrggbb`, or `null` when the user cancels with Escape. */
15
+ const pick = useCallback(async () => {
16
+ const {
17
+ EyeDropper
18
+ } = window;
19
+ if (!EyeDropper) return null;
20
+ try {
21
+ // eslint-disable-next-line compat/compat -- guarded by the feature check above
22
+ const {
23
+ sRGBHex
24
+ } = await new EyeDropper().open();
25
+ return toHex(sRGBHex);
26
+ } catch (_a) {
27
+ return null;
28
+ }
29
+ }, []);
30
+ return {
31
+ isSupported,
32
+ pick
33
+ };
34
+ };
35
+
36
+ export { useEyeDropper };
@@ -0,0 +1,19 @@
1
+ import type { ChangeEvent, KeyboardEvent } from "react";
2
+ /**
3
+ * HEX field logic for `Input`:
4
+ * - while focused, the field keeps a draft, so it can be cleared and retyped;
5
+ * - a full `#rrggbb` applies while typing;
6
+ * - a short `#abc` applies on blur or Enter (focus stays), so typing
7
+ * `#abcdef` does not flash `#aabbcc`;
8
+ * - invalid input reverts to the current value on blur.
9
+ */
10
+ export declare const useHexDraft: (value: string, onChange: (hex: string) => void) => {
11
+ invalid: boolean;
12
+ inputProps: {
13
+ value: string;
14
+ onFocus: () => void;
15
+ onChange: (event: ChangeEvent<HTMLInputElement>) => void;
16
+ onBlur: () => void;
17
+ onKeyDown: (event: KeyboardEvent<HTMLInputElement>) => void;
18
+ };
19
+ };
@@ -0,0 +1,47 @@
1
+ import { useState } from 'react';
2
+ import { normalizeHex } from '../utils/color.js';
3
+
4
+ const FULL_HEX_RE = /^#?[0-9a-f]{6}$/i;
5
+ /**
6
+ * HEX field logic for `Input`:
7
+ * - while focused, the field keeps a draft, so it can be cleared and retyped;
8
+ * - a full `#rrggbb` applies while typing;
9
+ * - a short `#abc` applies on blur or Enter (focus stays), so typing
10
+ * `#abcdef` does not flash `#aabbcc`;
11
+ * - invalid input reverts to the current value on blur.
12
+ */
13
+ const useHexDraft = (value, onChange) => {
14
+ // null: not editing, show `value`.
15
+ const [draft, setDraft] = useState(null);
16
+ const commit = text => {
17
+ const hex = normalizeHex(text);
18
+ if (hex && hex !== value) onChange(hex);
19
+ };
20
+ return {
21
+ invalid: draft !== null && normalizeHex(draft) === null,
22
+ inputProps: {
23
+ value: draft !== null && draft !== void 0 ? draft : value,
24
+ onFocus: () => setDraft(value),
25
+ onChange: event => {
26
+ const text = event.target.value;
27
+ setDraft(text);
28
+ if (FULL_HEX_RE.test(text.trim())) commit(text);
29
+ },
30
+ onBlur: () => {
31
+ if (draft !== null) commit(draft);
32
+ setDraft(null);
33
+ },
34
+ // Applies a short HEX and keeps focus in the field.
35
+ onKeyDown: event => {
36
+ if (event.key !== "Enter" || draft === null) return;
37
+ const hex = normalizeHex(draft);
38
+ if (hex) {
39
+ commit(draft);
40
+ setDraft(hex);
41
+ }
42
+ }
43
+ }
44
+ };
45
+ };
46
+
47
+ export { useHexDraft };
@@ -0,0 +1,20 @@
1
+ import type { PointerEvent as ReactPointerEvent } from "react";
2
+ /** Pointer position inside the element, 0–1 on each axis. */
3
+ export interface DragPosition {
4
+ x: number;
5
+ y: number;
6
+ }
7
+ /**
8
+ * Dragging inside an element with Pointer Events (mouse, touch, pen).
9
+ * Pointer capture keeps tracking outside the element without document
10
+ * listeners. The element needs `touch-action: none`.
11
+ */
12
+ export declare const usePointerDrag: <T extends HTMLElement>(onMove: (position: DragPosition) => void) => {
13
+ ref: import("react").RefObject<T | null>;
14
+ handlers: {
15
+ onPointerDown: (event: ReactPointerEvent<T>) => void;
16
+ onPointerMove: (event: ReactPointerEvent<T>) => void;
17
+ onPointerUp: (event: ReactPointerEvent<T>) => void;
18
+ onPointerCancel: (event: ReactPointerEvent<T>) => void;
19
+ };
20
+ };
@@ -0,0 +1,53 @@
1
+ import { useRef, useEffect, useCallback } from 'react';
2
+ import { clamp } from '../utils/color.js';
3
+
4
+ /**
5
+ * Dragging inside an element with Pointer Events (mouse, touch, pen).
6
+ * Pointer capture keeps tracking outside the element without document
7
+ * listeners. The element needs `touch-action: none`.
8
+ */
9
+ const usePointerDrag = onMove => {
10
+ const ref = useRef(null);
11
+ const onMoveRef = useRef(onMove);
12
+ useEffect(() => {
13
+ onMoveRef.current = onMove;
14
+ });
15
+ const move = useCallback(event => {
16
+ const element = ref.current;
17
+ if (!element) return;
18
+ const rect = element.getBoundingClientRect();
19
+ onMoveRef.current({
20
+ x: clamp((event.clientX - rect.left) / rect.width),
21
+ y: clamp((event.clientY - rect.top) / rect.height)
22
+ });
23
+ }, []);
24
+ const onPointerDown = useCallback(event => {
25
+ if (event.button !== 0 || !ref.current) return;
26
+ // preventDefault stops text selection but also focus, so focus manually.
27
+ event.preventDefault();
28
+ ref.current.setPointerCapture(event.pointerId);
29
+ ref.current.focus();
30
+ move(event);
31
+ }, [move]);
32
+ const onPointerMove = useCallback(event => {
33
+ var _a;
34
+ if ((_a = ref.current) === null || _a === void 0 ? void 0 : _a.hasPointerCapture(event.pointerId)) move(event);
35
+ }, [move]);
36
+ const onPointerUp = useCallback(event => {
37
+ var _a;
38
+ if ((_a = ref.current) === null || _a === void 0 ? void 0 : _a.hasPointerCapture(event.pointerId)) {
39
+ ref.current.releasePointerCapture(event.pointerId);
40
+ }
41
+ }, []);
42
+ return {
43
+ ref,
44
+ handlers: {
45
+ onPointerDown,
46
+ onPointerMove,
47
+ onPointerUp,
48
+ onPointerCancel: onPointerUp
49
+ }
50
+ };
51
+ };
52
+
53
+ export { usePointerDrag };
@@ -0,0 +1,3 @@
1
+ export { ColorPicker } from "./ColorPicker";
2
+ export { COLOR_PICKER_PRESETS } from "./presets";
3
+ export type { ColorPickerLabels, ColorPickerPreset, ColorPickerProps, } from "./index.props";
@@ -0,0 +1,2 @@
1
+ export { ColorPicker } from './ColorPicker.js';
2
+ export { COLOR_PICKER_PRESETS } from './presets.js';
@@ -0,0 +1,43 @@
1
+ import type { ReactElement } from "react";
2
+ import type { Placement } from "@floating-ui/react";
3
+ export interface ColorPickerPreset {
4
+ /** HEX color, for example `#d81b60`. */
5
+ value: string;
6
+ /** Accessible name and tooltip of the swatch. */
7
+ label: string;
8
+ }
9
+ export interface ColorPickerLabels {
10
+ /** Accessible name of the default trigger. */
11
+ trigger: string;
12
+ /** Accessible name of the preset grid. */
13
+ palette: string;
14
+ /** "+" button that opens the custom color dialog. */
15
+ addCustom: string;
16
+ customTitle: string;
17
+ customDescription: string;
18
+ /** Sample text in the preview circle. */
19
+ preview: string;
20
+ hex: string;
21
+ eyeDropper: string;
22
+ saturation: string;
23
+ hue: string;
24
+ cancel: string;
25
+ save: string;
26
+ }
27
+ export interface ColorPickerProps {
28
+ /** Current color in HEX. A color missing from `presets` is shown as an extra swatch. */
29
+ value?: string;
30
+ /** Called with the chosen color as lowercase `#rrggbb`. */
31
+ onChange: (color: string) => void;
32
+ /** Preset colors. Defaults to `COLOR_PICKER_PRESETS`. */
33
+ presets?: ColorPickerPreset[];
34
+ /** Number of grid columns. */
35
+ columns?: number;
36
+ disabled?: boolean;
37
+ placement?: Placement;
38
+ zIndex?: number;
39
+ /** Texts and accessible names. Missing keys use English defaults. */
40
+ labels?: Partial<ColorPickerLabels>;
41
+ /** Custom trigger element. Defaults to a round button with the current color. */
42
+ children?: ReactElement;
43
+ }
@@ -0,0 +1,3 @@
1
+ import type { ColorPickerLabels, ColorPickerPreset } from "./index.props";
2
+ export declare const COLOR_PICKER_PRESETS: ColorPickerPreset[];
3
+ export declare const DEFAULT_LABELS: ColorPickerLabels;
@@ -0,0 +1,89 @@
1
+ const COLOR_PICKER_PRESETS = [{
2
+ value: "#ad1457",
3
+ label: "Radicchio"
4
+ }, {
5
+ value: "#f4511e",
6
+ label: "Tangerine"
7
+ }, {
8
+ value: "#e4c441",
9
+ label: "Citron"
10
+ }, {
11
+ value: "#0b8043",
12
+ label: "Basil"
13
+ }, {
14
+ value: "#3f51b5",
15
+ label: "Blueberry"
16
+ }, {
17
+ value: "#8e24aa",
18
+ label: "Grape"
19
+ }, {
20
+ value: "#d81b60",
21
+ label: "Cherry blossom"
22
+ }, {
23
+ value: "#ef6c00",
24
+ label: "Pumpkin"
25
+ }, {
26
+ value: "#c0ca33",
27
+ label: "Avocado"
28
+ }, {
29
+ value: "#009688",
30
+ label: "Eucalyptus"
31
+ }, {
32
+ value: "#7986cb",
33
+ label: "Lavender"
34
+ }, {
35
+ value: "#795548",
36
+ label: "Cocoa"
37
+ }, {
38
+ value: "#d50000",
39
+ label: "Tomato"
40
+ }, {
41
+ value: "#f09300",
42
+ label: "Mango"
43
+ }, {
44
+ value: "#7cb342",
45
+ label: "Pistachio"
46
+ }, {
47
+ value: "#039be5",
48
+ label: "Peacock"
49
+ }, {
50
+ value: "#b39ddb",
51
+ label: "Wisteria"
52
+ }, {
53
+ value: "#616161",
54
+ label: "Graphite"
55
+ }, {
56
+ value: "#e67c73",
57
+ label: "Flamingo"
58
+ }, {
59
+ value: "#f6bf26",
60
+ label: "Banana"
61
+ }, {
62
+ value: "#33b679",
63
+ label: "Sage"
64
+ }, {
65
+ value: "#4285f4",
66
+ label: "Cobalt"
67
+ }, {
68
+ value: "#9e69af",
69
+ label: "Amethyst"
70
+ }, {
71
+ value: "#a79b8e",
72
+ label: "Birch"
73
+ }];
74
+ const DEFAULT_LABELS = {
75
+ trigger: "Choose color",
76
+ palette: "Colors",
77
+ addCustom: "Add custom color",
78
+ customTitle: "Choose custom color",
79
+ customDescription: "Choose a background color. The text color will be adjusted automatically.",
80
+ preview: "A",
81
+ hex: "Hex",
82
+ eyeDropper: "Pick a color from the screen",
83
+ saturation: "Saturation and brightness",
84
+ hue: "Hue",
85
+ cancel: "Cancel",
86
+ save: "Save"
87
+ };
88
+
89
+ export { COLOR_PICKER_PRESETS, DEFAULT_LABELS };