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,3 @@
1
+ import React from "react";
2
+ import type { ColorPickerProps } from "./index.props";
3
+ export declare const ColorPicker: ({ value, onChange, presets, columns, disabled, placement, zIndex, labels: labelsProp, children, }: ColorPickerProps) => React.JSX.Element;
@@ -0,0 +1,116 @@
1
+ 'use strict';
2
+
3
+ var React = require('react');
4
+ var clsx = require('clsx');
5
+ var index = require('../Button/index.js');
6
+ var index$1 = require('../Dropdown/index.js');
7
+ var index$3 = require('./components/CustomColorModal/index.js');
8
+ var index$2 = require('./components/Palette/index.js');
9
+ var presets = require('./presets.js');
10
+ var color = require('./utils/color.js');
11
+ var styles = require('./ColorPicker.module.css');
12
+
13
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
14
+
15
+ var React__default = /*#__PURE__*/_interopDefault(React);
16
+ var clsx__default = /*#__PURE__*/_interopDefault(clsx);
17
+ var styles__default = /*#__PURE__*/_interopDefault(styles);
18
+
19
+ const normalizePresets = presets => presets.flatMap(preset => {
20
+ const value = color.normalizeHex(preset.value);
21
+ return value ? [Object.assign(Object.assign({}, preset), {
22
+ value
23
+ })] : [];
24
+ });
25
+ const ColorPicker = ({
26
+ value,
27
+ onChange,
28
+ presets: presets$1 = presets.COLOR_PICKER_PRESETS,
29
+ columns = 6,
30
+ disabled,
31
+ placement = "bottom-start",
32
+ zIndex,
33
+ labels: labelsProp,
34
+ children
35
+ }) => {
36
+ var _a, _b;
37
+ const [isOpen, setOpen] = React.useState(false);
38
+ const [isCustomOpen, setCustomOpen] = React.useState(false);
39
+ const rootRef = React.useRef(null);
40
+ const overlayRef = React.useRef(null);
41
+ const labels = Object.assign(Object.assign({}, presets.DEFAULT_LABELS), labelsProp);
42
+ const current = value ? color.normalizeHex(value) : null;
43
+ const normalizedPresets = React.useMemo(() => normalizePresets(presets$1), [presets$1]);
44
+ // Selection, "+" and Escape remove the focused swatch together with the
45
+ // dropdown: return focus to the trigger. The custom color dialog then
46
+ // restores focus to the trigger as well.
47
+ const closeDropdown = () => {
48
+ var _a, _b;
49
+ const trigger = (_a = rootRef.current) === null || _a === void 0 ? void 0 : _a.firstElementChild;
50
+ if (((_b = overlayRef.current) === null || _b === void 0 ? void 0 : _b.contains(document.activeElement)) && trigger instanceof HTMLElement) {
51
+ trigger.focus();
52
+ }
53
+ setOpen(false);
54
+ };
55
+ const handleVisibleChange = visible => {
56
+ if (visible) setOpen(true);else closeDropdown();
57
+ };
58
+ const handleSelect = hex => {
59
+ closeDropdown();
60
+ onChange(hex);
61
+ };
62
+ const handleAddClick = () => {
63
+ closeDropdown();
64
+ setCustomOpen(true);
65
+ };
66
+ const handleSave = hex => {
67
+ setCustomOpen(false);
68
+ onChange(hex);
69
+ };
70
+ const trigger = children !== null && children !== void 0 ? children : (/*#__PURE__*/React__default.default.createElement(index.Button, {
71
+ variant: "outline",
72
+ className: clsx__default.default({
73
+ [styles__default.default.trigger_empty]: !current
74
+ }),
75
+ disabled: disabled,
76
+ "aria-label": labels.trigger,
77
+ icon: /*#__PURE__*/React__default.default.createElement("span", {
78
+ className: clsx__default.default(styles__default.default.dot, {
79
+ [styles__default.default.dot_hidden]: !current
80
+ }),
81
+ style: current ? {
82
+ backgroundColor: current
83
+ } : undefined
84
+ })
85
+ }));
86
+ return /*#__PURE__*/React__default.default.createElement("span", {
87
+ ref: rootRef,
88
+ className: styles__default.default.root
89
+ }, /*#__PURE__*/React__default.default.createElement(index$1.Dropdown, {
90
+ trigger: ["click"],
91
+ visible: isOpen,
92
+ onVisibleChange: handleVisibleChange,
93
+ preventAutoClose: true,
94
+ disabled: disabled,
95
+ placement: placement,
96
+ zIndex: zIndex,
97
+ overlay: /*#__PURE__*/React__default.default.createElement("div", {
98
+ ref: overlayRef
99
+ }, /*#__PURE__*/React__default.default.createElement(index$2.Palette, {
100
+ presets: normalizedPresets,
101
+ value: current,
102
+ columns: columns,
103
+ labels: labels,
104
+ onSelect: handleSelect,
105
+ onAddClick: handleAddClick,
106
+ onTabOut: closeDropdown
107
+ }))
108
+ }, trigger), isCustomOpen && (/*#__PURE__*/React__default.default.createElement(index$3.CustomColorModal, {
109
+ initialColor: (_b = current !== null && current !== void 0 ? current : (_a = normalizedPresets[0]) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : "#000000",
110
+ labels: labels,
111
+ onSave: handleSave,
112
+ onClose: () => setCustomOpen(false)
113
+ })));
114
+ };
115
+
116
+ exports.ColorPicker = ColorPicker;
@@ -0,0 +1,22 @@
1
+ .root {
2
+ display: contents;
3
+ }
4
+
5
+ .dot {
6
+ border-radius: var(--radius-full);
7
+ box-shadow: inset 0 0 0 var(--border-width-hairline) var(--color-border-default);
8
+ display: block;
9
+ height: var(--space-4);
10
+ width: var(--space-4);
11
+ }
12
+
13
+ .trigger_empty {
14
+ background-image: url("./assets/none.png");
15
+ background-position: center;
16
+ background-repeat: no-repeat;
17
+ background-size: cover;
18
+ }
19
+
20
+ .dot_hidden {
21
+ display: none;
22
+ }
Binary file
@@ -0,0 +1,23 @@
1
+ .root {
2
+ align-items: center;
3
+ border-radius: var(--radius-full);
4
+ box-shadow: inset 0 0 0 var(--border-width-hairline) var(--color-border-subtle);
5
+ display: inline-flex;
6
+ flex-shrink: 0;
7
+ font-family: var(--font-family-ui);
8
+ font-size: var(--font-size-heading-md);
9
+ font-weight: var(--font-weight-medium);
10
+ height: var(--space-12);
11
+ justify-content: center;
12
+ line-height: 1;
13
+ transition: background-color 0.1s ease, color 0.1s ease;
14
+ width: var(--space-12);
15
+ }
16
+
17
+ .text_light {
18
+ color: var(--color-neutral-0);
19
+ }
20
+
21
+ .text_dark {
22
+ color: var(--color-neutral-950);
23
+ }
@@ -0,0 +1,10 @@
1
+ import React from "react";
2
+ import type { ReactNode } from "react";
3
+ import type { HSV } from "../../utils/color";
4
+ interface ColorPreviewProps {
5
+ color: HSV;
6
+ children: ReactNode;
7
+ }
8
+ /** Circle with the color and a sample text in the automatically adjusted color. */
9
+ export declare const ColorPreview: ({ color, children }: ColorPreviewProps) => React.JSX.Element;
10
+ export {};
@@ -0,0 +1,29 @@
1
+ 'use strict';
2
+
3
+ var React = require('react');
4
+ var clsx = require('clsx');
5
+ var color = require('../../utils/color.js');
6
+ var styles = require('./ColorPreview.module.css');
7
+
8
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
9
+
10
+ var React__default = /*#__PURE__*/_interopDefault(React);
11
+ var clsx__default = /*#__PURE__*/_interopDefault(clsx);
12
+ var styles__default = /*#__PURE__*/_interopDefault(styles);
13
+
14
+ /** Circle with the color and a sample text in the automatically adjusted color. */
15
+ const ColorPreview = ({
16
+ color: color$1,
17
+ children
18
+ }) => {
19
+ const hex = color.hsv2hex(color$1);
20
+ return /*#__PURE__*/React__default.default.createElement("span", {
21
+ "aria-hidden": "true",
22
+ className: clsx__default.default(styles__default.default.root, color.prefersLightText(hex) ? styles__default.default.text_light : styles__default.default.text_dark),
23
+ style: {
24
+ backgroundColor: hex
25
+ }
26
+ }, children);
27
+ };
28
+
29
+ exports.ColorPreview = ColorPreview;
@@ -0,0 +1,18 @@
1
+ .picker {
2
+ align-items: start;
3
+ display: grid;
4
+ gap: var(--space-5) var(--space-4);
5
+ grid-template-columns: auto 1fr;
6
+ margin-top: var(--space-5);
7
+ }
8
+
9
+ .side {
10
+ align-items: center;
11
+ display: flex;
12
+ flex-direction: column;
13
+ gap: var(--space-4);
14
+ }
15
+
16
+ .full_row {
17
+ grid-column: 1/-1;
18
+ }
@@ -0,0 +1,15 @@
1
+ import React from "react";
2
+ import type { ColorPickerLabels } from "../../index.props";
3
+ interface CustomColorModalProps {
4
+ /** `#rrggbb` the picker starts from. */
5
+ initialColor: string;
6
+ labels: ColorPickerLabels;
7
+ onSave: (hex: string) => void;
8
+ onClose: () => void;
9
+ }
10
+ /**
11
+ * "Choose custom color" dialog. The draft is kept in HSV: HEX loses hue and
12
+ * saturation of grey and black, and the sliders would jump.
13
+ */
14
+ export declare const CustomColorModal: ({ initialColor, labels, onSave, onClose, }: CustomColorModalProps) => React.JSX.Element;
15
+ export {};
@@ -0,0 +1,107 @@
1
+ 'use strict';
2
+
3
+ var React = require('react');
4
+ var index$3 = require('../../../Button/index.js');
5
+ require('../../../Icons/index.js');
6
+ var index$6 = require('../../../Input/index.js');
7
+ var index = require('../../../Modal/index.js');
8
+ var index$1 = require('../../../Typography/index.js');
9
+ var useEyeDropper = require('../../hooks/useEyeDropper.js');
10
+ var useHexDraft = require('../../hooks/useHexDraft.js');
11
+ var color = require('../../utils/color.js');
12
+ var index$2 = require('../ColorPreview/index.js');
13
+ var index$5 = require('../HueSlider/index.js');
14
+ var index$4 = require('../SaturationPanel/index.js');
15
+ var styles = require('./CustomColorModal.module.css');
16
+ var Pipette = require('../../../Icons/Pipette.js');
17
+
18
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
19
+
20
+ var React__default = /*#__PURE__*/_interopDefault(React);
21
+ var styles__default = /*#__PURE__*/_interopDefault(styles);
22
+
23
+ const BLACK = {
24
+ h: 0,
25
+ s: 0,
26
+ v: 0
27
+ };
28
+ /**
29
+ * "Choose custom color" dialog. The draft is kept in HSV: HEX loses hue and
30
+ * saturation of grey and black, and the sliders would jump.
31
+ */
32
+ const CustomColorModal = ({
33
+ initialColor,
34
+ labels,
35
+ onSave,
36
+ onClose
37
+ }) => {
38
+ const [color$1, setColor] = React.useState(() => {
39
+ var _a;
40
+ return (_a = color.hex2hsv(initialColor)) !== null && _a !== void 0 ? _a : BLACK;
41
+ });
42
+ const hex = color.hsv2hex(color$1);
43
+ const {
44
+ isSupported,
45
+ pick
46
+ } = useEyeDropper.useEyeDropper();
47
+ const setHex = next => setColor(prev => {
48
+ var _a;
49
+ return (_a = color.toHsvPreservingHue(next, prev)) !== null && _a !== void 0 ? _a : prev;
50
+ });
51
+ const {
52
+ invalid,
53
+ inputProps
54
+ } = useHexDraft.useHexDraft(hex, setHex);
55
+ const handlePick = async () => {
56
+ const picked = await pick();
57
+ if (picked) setHex(picked);
58
+ };
59
+ return /*#__PURE__*/React__default.default.createElement(index.Modal, {
60
+ onClose: onClose
61
+ }, /*#__PURE__*/React__default.default.createElement(index.Modal.Header, null, /*#__PURE__*/React__default.default.createElement(index.Modal.Title, null, labels.customTitle)), /*#__PURE__*/React__default.default.createElement(index.Modal.Body, null, /*#__PURE__*/React__default.default.createElement(index$1.Typography, {
62
+ tag: "p",
63
+ variant: "secondary",
64
+ size: "text-sm"
65
+ }, labels.customDescription), /*#__PURE__*/React__default.default.createElement("div", {
66
+ className: styles__default.default.picker
67
+ }, /*#__PURE__*/React__default.default.createElement("div", {
68
+ className: styles__default.default.side
69
+ }, /*#__PURE__*/React__default.default.createElement(index$2.ColorPreview, {
70
+ color: color$1
71
+ }, labels.preview), isSupported && (/*#__PURE__*/React__default.default.createElement(index$3.Button, {
72
+ variant: "secondary",
73
+ size: "l",
74
+ icon: /*#__PURE__*/React__default.default.createElement(Pipette.Pipette, {
75
+ width: 20,
76
+ height: 20,
77
+ "aria-hidden": "true"
78
+ }),
79
+ "aria-label": labels.eyeDropper,
80
+ title: labels.eyeDropper,
81
+ onClick: handlePick
82
+ }))), /*#__PURE__*/React__default.default.createElement(index$4.SaturationPanel, {
83
+ value: color$1,
84
+ onChange: setColor,
85
+ "aria-label": labels.saturation
86
+ }), /*#__PURE__*/React__default.default.createElement("div", {
87
+ className: styles__default.default.full_row
88
+ }, /*#__PURE__*/React__default.default.createElement(index$5.HueSlider, {
89
+ value: color$1,
90
+ onChange: setColor,
91
+ "aria-label": labels.hue
92
+ })), /*#__PURE__*/React__default.default.createElement(index$6.Input, Object.assign({
93
+ className: styles__default.default.full_row,
94
+ label: labels.hex,
95
+ hasError: invalid,
96
+ maxLength: 7,
97
+ spellCheck: false,
98
+ autoComplete: "off"
99
+ }, inputProps)))), /*#__PURE__*/React__default.default.createElement(index.Modal.Footer, null, /*#__PURE__*/React__default.default.createElement(index$3.Button, {
100
+ variant: "clear",
101
+ onClick: onClose
102
+ }, labels.cancel), /*#__PURE__*/React__default.default.createElement(index$3.Button, {
103
+ onClick: () => onSave(hex)
104
+ }, labels.save)));
105
+ };
106
+
107
+ exports.CustomColorModal = CustomColorModal;
@@ -0,0 +1,31 @@
1
+ .root {
2
+ background: linear-gradient(to right, hsl(0, 100%, 50%), hsl(60, 100%, 50%), hsl(120, 100%, 50%), hsl(180, 100%, 50%), hsl(240, 100%, 50%), hsl(300, 100%, 50%), hsl(0, 100%, 50%));
3
+ border-radius: var(--radius-full);
4
+ cursor: pointer;
5
+ height: var(--space-3);
6
+ outline: none;
7
+ position: relative;
8
+ touch-action: none;
9
+ -webkit-user-select: none;
10
+ user-select: none;
11
+ }
12
+ .root::before {
13
+ content: "";
14
+ inset: calc(var(--space-2) * -1) 0;
15
+ position: absolute;
16
+ }
17
+ .root:focus-visible {
18
+ box-shadow: 0 0 0 var(--focus-ring-width) var(--color-focus-ring);
19
+ }
20
+
21
+ .thumb {
22
+ border: var(--focus-ring-width) solid var(--color-neutral-0);
23
+ border-radius: var(--radius-full);
24
+ box-shadow: var(--shadow-raised);
25
+ height: var(--space-5);
26
+ pointer-events: none;
27
+ position: absolute;
28
+ top: 50%;
29
+ transform: translate(-50%, -50%);
30
+ width: var(--space-5);
31
+ }
@@ -0,0 +1,10 @@
1
+ import React from "react";
2
+ import type { HSV } from "../../utils/color";
3
+ interface HueSliderProps {
4
+ value: HSV;
5
+ onChange: (value: HSV) => void;
6
+ "aria-label": string;
7
+ }
8
+ /** Hue bar. Arrow keys change by 1°, with Shift by 10°, Home/End go to the ends. */
9
+ export declare const HueSlider: ({ value, onChange, "aria-label": ariaLabel, }: HueSliderProps) => React.JSX.Element;
10
+ export {};
@@ -0,0 +1,73 @@
1
+ 'use strict';
2
+
3
+ var React = require('react');
4
+ var usePointerDrag = require('../../hooks/usePointerDrag.js');
5
+ var color = require('../../utils/color.js');
6
+ var styles = require('./HueSlider.module.css');
7
+
8
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
9
+
10
+ var React__default = /*#__PURE__*/_interopDefault(React);
11
+ var styles__default = /*#__PURE__*/_interopDefault(styles);
12
+
13
+ const STEP = 1;
14
+ const BIG_STEP = 10;
15
+ /** Hue bar. Arrow keys change by 1°, with Shift by 10°, Home/End go to the ends. */
16
+ const HueSlider = ({
17
+ value,
18
+ onChange,
19
+ "aria-label": ariaLabel
20
+ }) => {
21
+ const {
22
+ h
23
+ } = value;
24
+ const {
25
+ ref,
26
+ handlers
27
+ } = usePointerDrag.usePointerDrag(({
28
+ x
29
+ }) => onChange(Object.assign(Object.assign({}, value), {
30
+ h: x * 360
31
+ })));
32
+ const handleKeyDown = event => {
33
+ const step = event.shiftKey ? BIG_STEP : STEP;
34
+ const next = {
35
+ ArrowLeft: h - step,
36
+ ArrowDown: h - step,
37
+ ArrowRight: h + step,
38
+ ArrowUp: h + step,
39
+ Home: 0,
40
+ End: 360
41
+ };
42
+ if (!(event.key in next)) return;
43
+ event.preventDefault();
44
+ onChange(Object.assign(Object.assign({}, value), {
45
+ h: color.clamp(next[event.key], 0, 360)
46
+ }));
47
+ };
48
+ return /*#__PURE__*/React__default.default.createElement("div", Object.assign({
49
+ ref: ref
50
+ }, handlers, {
51
+ role: "slider",
52
+ tabIndex: 0,
53
+ "aria-label": ariaLabel,
54
+ "aria-orientation": "horizontal",
55
+ "aria-valuemin": 0,
56
+ "aria-valuemax": 360,
57
+ "aria-valuenow": Math.round(h),
58
+ onKeyDown: handleKeyDown,
59
+ className: styles__default.default.root
60
+ }), /*#__PURE__*/React__default.default.createElement("div", {
61
+ className: styles__default.default.thumb,
62
+ style: {
63
+ left: `${h / 360 * 100}%`,
64
+ backgroundColor: color.hsv2css({
65
+ h,
66
+ s: 100,
67
+ v: 100
68
+ })
69
+ }
70
+ }));
71
+ };
72
+
73
+ exports.HueSlider = HueSlider;
@@ -0,0 +1,12 @@
1
+ .root {
2
+ display: grid;
3
+ gap: var(--space-2);
4
+ padding: var(--space-3);
5
+ }
6
+
7
+ .add {
8
+ height: var(--space-6);
9
+ min-width: 0;
10
+ padding: 0;
11
+ width: var(--space-6);
12
+ }
@@ -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,99 @@
1
+ 'use strict';
2
+
3
+ var React = require('react');
4
+ var index$1 = require('../../../Button/index.js');
5
+ require('../../../Icons/index.js');
6
+ var index = require('../Swatch/index.js');
7
+ var styles = require('./Palette.module.css');
8
+ var Plus = require('../../../Icons/Plus.js');
9
+
10
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
11
+
12
+ var React__default = /*#__PURE__*/_interopDefault(React);
13
+ var styles__default = /*#__PURE__*/_interopDefault(styles);
14
+
15
+ /**
16
+ * Preset grid with the "+" button. A current color missing from the presets
17
+ * is shown as an extra swatch before "+".
18
+ */
19
+ const Palette = ({
20
+ presets,
21
+ value,
22
+ columns,
23
+ labels,
24
+ onSelect,
25
+ onAddClick,
26
+ onTabOut
27
+ }) => {
28
+ const rootRef = React.useRef(null);
29
+ const hasPreset = presets.some(preset => preset.value === value);
30
+ const colors = value && !hasPreset ? [...presets, {
31
+ value,
32
+ label: value
33
+ }] : presets;
34
+ // The dropdown is portaled to the end of the document: move focus into it,
35
+ // to the selected swatch or the first item ("+" when there are no colors).
36
+ React.useEffect(() => {
37
+ var _a, _b;
38
+ const root = rootRef.current;
39
+ (_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();
40
+ }, []);
41
+ // Arrow keys move focus across the grid, Home/End go to the first and the
42
+ // last item. Tab leaves the portaled palette through the trigger, so focus
43
+ // continues from it, also inside a parent focus trap.
44
+ const handleKeyDown = event => {
45
+ var _a;
46
+ if (event.key === "Tab") {
47
+ onTabOut();
48
+ return;
49
+ }
50
+ const items = Array.from(event.currentTarget.querySelectorAll("button"));
51
+ const index = items.indexOf(event.target);
52
+ const offsets = {
53
+ ArrowLeft: -1,
54
+ ArrowRight: 1,
55
+ ArrowUp: -columns,
56
+ ArrowDown: columns,
57
+ Home: -index,
58
+ End: items.length - 1 - index
59
+ };
60
+ if (index === -1 || !(event.key in offsets)) return;
61
+ event.preventDefault();
62
+ (_a = items[index + offsets[event.key]]) === null || _a === void 0 ? void 0 : _a.focus();
63
+ };
64
+ return (
65
+ /*#__PURE__*/
66
+ // eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions -- keys come from the focused items
67
+ React__default.default.createElement("div", {
68
+ ref: rootRef,
69
+ role: "group",
70
+ "aria-label": labels.palette,
71
+ className: styles__default.default.root,
72
+ onKeyDown: handleKeyDown,
73
+ style: {
74
+ gridTemplateColumns: `repeat(${columns}, auto)`
75
+ }
76
+ }, colors.map(color => (/*#__PURE__*/React__default.default.createElement(index.Swatch, {
77
+ key: color.value,
78
+ color: color.value,
79
+ label: color.label,
80
+ selected: color.value === value,
81
+ onClick: () => onSelect(color.value)
82
+ }))), /*#__PURE__*/React__default.default.createElement(index$1.Button, {
83
+ className: styles__default.default.add,
84
+ variant: "secondary",
85
+ size: "xs",
86
+ icon: /*#__PURE__*/React__default.default.createElement(Plus.Plus, {
87
+ width: 12,
88
+ height: 12,
89
+ "aria-hidden": "true"
90
+ }),
91
+ role: "menuitem",
92
+ "aria-label": labels.addCustom,
93
+ title: labels.addCustom,
94
+ onClick: onAddClick
95
+ }))
96
+ );
97
+ };
98
+
99
+ exports.Palette = 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 {};