chroma-panel 0.1.4 → 1.0.0

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 (105) hide show
  1. package/README.md +47 -9
  2. package/dist/a11y/contrast.cjs +94 -0
  3. package/dist/a11y/contrast.d.cts +8 -0
  4. package/dist/a11y/contrast.d.ts +8 -0
  5. package/dist/a11y/contrast.js +93 -2
  6. package/dist/color/css4.cjs +397 -0
  7. package/dist/color/css4.d.cts +10 -0
  8. package/dist/color/css4.d.ts +10 -0
  9. package/dist/color/css4.js +392 -0
  10. package/dist/color/parse.cjs +3 -1
  11. package/dist/color/parse.js +3 -1
  12. package/dist/color/types.d.cts +16 -0
  13. package/dist/color/types.d.ts +16 -0
  14. package/dist/color/value.cjs +11 -0
  15. package/dist/color/value.d.cts +4 -0
  16. package/dist/color/value.d.ts +4 -0
  17. package/dist/color/value.js +12 -0
  18. package/dist/color.cjs +43 -0
  19. package/dist/color.d.cts +9 -0
  20. package/dist/color.d.ts +9 -0
  21. package/dist/color.js +8 -0
  22. package/dist/components/ChromaPanel.cjs +21 -7
  23. package/dist/components/ChromaPanel.d.cts +5 -2
  24. package/dist/components/ChromaPanel.d.ts +5 -2
  25. package/dist/components/ChromaPanel.js +21 -7
  26. package/dist/components/ColorInput.cjs +1 -1
  27. package/dist/components/ColorInput.js +1 -1
  28. package/dist/components/PanelFooter.cjs +37 -4
  29. package/dist/components/PanelFooter.js +39 -6
  30. package/dist/contrast.cjs +3 -0
  31. package/dist/contrast.d.cts +2 -2
  32. package/dist/contrast.d.ts +2 -2
  33. package/dist/contrast.js +2 -2
  34. package/dist/core/context.d.cts +3 -0
  35. package/dist/core/context.d.ts +3 -0
  36. package/dist/core/store.cjs +17 -9
  37. package/dist/core/store.d.cts +6 -6
  38. package/dist/core/store.d.ts +6 -6
  39. package/dist/core/store.js +17 -9
  40. package/dist/core.cjs +10 -0
  41. package/dist/core.d.cts +4 -2
  42. package/dist/core.d.ts +4 -2
  43. package/dist/core.js +3 -1
  44. package/dist/export/tokens.cjs +39 -0
  45. package/dist/export/tokens.d.cts +16 -0
  46. package/dist/export/tokens.d.ts +16 -0
  47. package/dist/export/tokens.js +37 -0
  48. package/dist/export.cjs +6 -0
  49. package/dist/export.d.cts +2 -0
  50. package/dist/export.d.ts +2 -0
  51. package/dist/export.js +2 -0
  52. package/dist/gradient/GradientEditor.cjs +196 -0
  53. package/dist/gradient/GradientEditor.d.cts +17 -0
  54. package/dist/gradient/GradientEditor.d.ts +17 -0
  55. package/dist/gradient/GradientEditor.js +195 -0
  56. package/dist/gradient/model.cjs +63 -0
  57. package/dist/gradient/model.d.cts +23 -0
  58. package/dist/gradient/model.d.ts +23 -0
  59. package/dist/gradient/model.js +60 -0
  60. package/dist/gradient.cjs +9 -0
  61. package/dist/gradient.d.cts +3 -0
  62. package/dist/gradient.d.ts +3 -0
  63. package/dist/gradient.js +3 -0
  64. package/dist/image/extract.cjs +23 -6
  65. package/dist/image/extract.d.cts +2 -0
  66. package/dist/image/extract.d.ts +2 -0
  67. package/dist/image/extract.js +23 -6
  68. package/dist/index.cjs +13 -0
  69. package/dist/index.d.cts +5 -3
  70. package/dist/index.d.ts +5 -3
  71. package/dist/index.js +4 -2
  72. package/dist/modes/image/index.cjs +155 -14
  73. package/dist/modes/image/index.d.cts +1 -0
  74. package/dist/modes/image/index.d.ts +1 -0
  75. package/dist/modes/image/index.js +155 -14
  76. package/dist/modes/registry.d.cts +1 -1
  77. package/dist/modes/registry.d.ts +1 -1
  78. package/dist/modes.cjs +30 -0
  79. package/dist/modes.d.cts +10 -0
  80. package/dist/modes.d.ts +10 -0
  81. package/dist/modes.js +17 -0
  82. package/dist/panel.cjs +10 -0
  83. package/dist/panel.d.cts +4 -2
  84. package/dist/panel.d.ts +4 -2
  85. package/dist/panel.js +3 -1
  86. package/dist/primitives/ChannelSlider.cjs +4 -4
  87. package/dist/primitives/ChannelSlider.js +4 -4
  88. package/dist/primitives/ColorArea.cjs +6 -6
  89. package/dist/primitives/ColorArea.js +6 -6
  90. package/dist/primitives/ColorDisc.cjs +6 -6
  91. package/dist/primitives/ColorDisc.js +6 -6
  92. package/dist/primitives/ColorField.cjs +2 -2
  93. package/dist/primitives/ColorField.js +2 -2
  94. package/dist/primitives/NumberField.cjs +2 -2
  95. package/dist/primitives/NumberField.js +2 -2
  96. package/dist/primitives/SwatchGrid.cjs +2 -2
  97. package/dist/primitives/SwatchGrid.js +2 -2
  98. package/dist/primitives/icons.cjs +8 -0
  99. package/dist/primitives/icons.d.cts +1 -1
  100. package/dist/primitives/icons.d.ts +1 -1
  101. package/dist/primitives/icons.js +8 -0
  102. package/dist/style.css +1 -1
  103. package/dist/styles/css.cjs +1 -1
  104. package/dist/styles/css.js +1 -1
  105. package/package.json +46 -2
@@ -1,4 +1,4 @@
1
- import { ColorChangeResult, ColorFormat, Hsva } from "../color/types.js";
1
+ import { ColorChangeMeta, ColorChangeResult, ColorFormat, Hsva } from "../color/types.js";
2
2
  import { ColorStore } from "../core/store.js";
3
3
  import { ExtractOptions } from "../image/extract.js";
4
4
  import { ModeId, PickerMode } from "../modes/registry.js";
@@ -11,13 +11,16 @@ export interface ChromaPanelProps {
11
11
  defaultValue?: string | Hsva;
12
12
  onChange?: (color: ColorChangeResult) => void;
13
13
  onChangeComplete?: (color: ColorChangeResult) => void;
14
- modes?: (ModeId | string | PickerMode)[];
14
+ onValueChange?: (color: ColorChangeResult, meta: ColorChangeMeta) => void;
15
+ onValueCommit?: (color: ColorChangeResult, meta: ColorChangeMeta) => void;
16
+ modes?: readonly (ModeId | string | PickerMode)[];
15
17
  mode?: string;
16
18
  defaultMode?: string;
17
19
  onModeChange?: (mode: string) => void;
18
20
  format?: ColorFormat;
19
21
  showAlpha?: boolean;
20
22
  showEyedropper?: boolean;
23
+ showCopyButton?: boolean;
21
24
  showRecentColors?: boolean;
22
25
  recentColors?: string[];
23
26
  defaultRecentColors?: string[];
@@ -24,13 +24,13 @@ const FALLBACK = {
24
24
  a: 1
25
25
  };
26
26
  function ChromaPanel(props) {
27
- const { value, defaultValue = DEFAULT_COLOR, onChange, onChangeComplete, modes = [
27
+ const { value, defaultValue = DEFAULT_COLOR, onChange, onChangeComplete, onValueChange, onValueCommit, modes = [
28
28
  "wheel",
29
29
  "sliders",
30
30
  "palettes",
31
31
  "image",
32
32
  "pencils"
33
- ], mode, defaultMode, onModeChange, format = "hex", showAlpha = true, showEyedropper = true, showRecentColors = true, recentColors, defaultRecentColors, onRecentColorsChange, palettes, pencils, modeProps, imageOptions, disabled = false, theme, showTitleBar = true, title = "Colors", onClose, collapsed, defaultCollapsed = false, onCollapsedChange, size, defaultSize = "default", onSizeChange, injectStyles: shouldInject = true, className, classNames = {}, style, store: externalStore } = props;
33
+ ], mode, defaultMode, onModeChange, format = "hex", showAlpha = true, showEyedropper = true, showCopyButton = true, showRecentColors = true, recentColors, defaultRecentColors, onRecentColorsChange, palettes, pencils, modeProps, imageOptions, disabled = false, theme, showTitleBar = true, title = "Colors", onClose, collapsed, defaultCollapsed = false, onCollapsedChange, size, defaultSize = "default", onSizeChange, injectStyles: shouldInject = true, className, classNames = {}, style, store: externalStore } = props;
34
34
  const rootRef = React.useRef(null);
35
35
  const panelHostRef = React.useRef(null);
36
36
  const idPrefix = useStableId("cp");
@@ -62,13 +62,15 @@ function ChromaPanel(props) {
62
62
  return;
63
63
  }
64
64
  if (sameRendered(parsed, store.get())) return;
65
- store.ingest(parsed);
65
+ store.ingest(parsed, "programmatic");
66
66
  }, [value, store]);
67
67
  const [internalRecents, setInternalRecents] = React.useState(() => defaultRecentColors ?? []);
68
68
  const activeRecents = recentColors ?? internalRecents;
69
69
  const callbacks = React.useRef({
70
70
  onChange,
71
71
  onChangeComplete,
72
+ onValueChange,
73
+ onValueCommit,
72
74
  format,
73
75
  activeRecents,
74
76
  onRecentColorsChange,
@@ -78,16 +80,24 @@ function ChromaPanel(props) {
78
80
  callbacks.current = {
79
81
  onChange,
80
82
  onChangeComplete,
83
+ onValueChange,
84
+ onValueCommit,
81
85
  format,
82
86
  activeRecents,
83
87
  onRecentColorsChange,
84
88
  controlled: recentColors !== void 0
85
89
  };
86
90
  });
87
- React.useEffect(() => store.subscribe((c) => callbacks.current.onChange?.(toResult(c, callbacks.current.format))), [store]);
88
- React.useEffect(() => store.subscribeCommit((c) => {
89
- const { onChangeComplete: done, format: fmt, activeRecents: recents, onRecentColorsChange: notify, controlled } = callbacks.current;
90
- done?.(toResult(c, fmt));
91
+ React.useEffect(() => store.subscribe((c, meta) => {
92
+ const result = toResult(c, callbacks.current.format);
93
+ callbacks.current.onChange?.(result);
94
+ callbacks.current.onValueChange?.(result, meta);
95
+ }), [store]);
96
+ React.useEffect(() => store.subscribeCommit((c, meta) => {
97
+ const { onChangeComplete: done, format: fmt, activeRecents: recents, onValueCommit: commit, onRecentColorsChange: notify, controlled } = callbacks.current;
98
+ const result = toResult(c, fmt);
99
+ done?.(result);
100
+ commit?.(result, meta);
91
101
  const next = pushRecent(recents, toHex(c));
92
102
  if (!controlled) setInternalRecents(next);
93
103
  notify?.(next);
@@ -129,7 +139,9 @@ function ChromaPanel(props) {
129
139
  pencils,
130
140
  showAlpha,
131
141
  showEyedropper,
142
+ showCopyButton,
132
143
  showRecentColors,
144
+ format,
133
145
  recentColors: activeRecents,
134
146
  onRecentColorsChange
135
147
  }), [
@@ -139,6 +151,8 @@ function ChromaPanel(props) {
139
151
  imageOptions,
140
152
  showAlpha,
141
153
  showEyedropper,
154
+ showCopyButton,
155
+ format,
142
156
  showRecentColors,
143
157
  activeRecents,
144
158
  onRecentColorsChange
@@ -58,7 +58,7 @@ function ColorInput(props) {
58
58
  });
59
59
  require_useFormControl.useFormReset(hiddenRef, () => {
60
60
  const parsed = typeof defaultValue === "string" ? require_parse.parse(defaultValue) : defaultValue;
61
- if (parsed !== null && parsed !== void 0) store.ingest(parsed);
61
+ if (parsed !== null && parsed !== void 0) store.ingest(parsed, "programmatic");
62
62
  });
63
63
  require_useFormControl.useConstraintValidation(hiddenRef, {
64
64
  behavior: validationBehavior,
@@ -56,7 +56,7 @@ function ColorInput(props) {
56
56
  });
57
57
  useFormReset(hiddenRef, () => {
58
58
  const parsed = typeof defaultValue === "string" ? parse(defaultValue) : defaultValue;
59
- if (parsed !== null && parsed !== void 0) store.ingest(parsed);
59
+ if (parsed !== null && parsed !== void 0) store.ingest(parsed, "programmatic");
60
60
  });
61
61
  useConstraintValidation(hiddenRef, {
62
62
  behavior: validationBehavior,
@@ -13,13 +13,28 @@ let react_jsx_runtime = require("react/jsx-runtime");
13
13
  function PanelFooter() {
14
14
  const { store, classNames, options, disabled } = require_context.usePanel();
15
15
  const { supported, pick } = require_useEyedropper.useEyedropper();
16
+ const [copyStatus, setCopyStatus] = react.useState("idle");
17
+ const copyTimer = react.useRef(null);
18
+ react.useEffect(() => () => {
19
+ if (copyTimer.current !== null) clearTimeout(copyTimer.current);
20
+ }, []);
21
+ const handleCopy = async () => {
22
+ try {
23
+ await navigator.clipboard.writeText(require_serialize.toFormat(store.get(), options.format));
24
+ setCopyStatus("copied");
25
+ } catch {
26
+ setCopyStatus("error");
27
+ }
28
+ if (copyTimer.current !== null) clearTimeout(copyTimer.current);
29
+ copyTimer.current = setTimeout(() => setCopyStatus("idle"), 1600);
30
+ };
16
31
  const handlePick = async () => {
17
32
  const hex = await pick();
18
33
  if (hex === null) return;
19
34
  const parsed = require_parse.parse(hex);
20
35
  if (parsed === null) return;
21
- store.ingest(parsed);
22
- store.commit();
36
+ store.ingest(parsed, "eyedropper");
37
+ store.commit("eyedropper");
23
38
  };
24
39
  const recentsRef = react.useRef(null);
25
40
  const [current, setCurrent] = react.useState(null);
@@ -51,8 +66,8 @@ function PanelFooter() {
51
66
  onClick: () => {
52
67
  const parsed = require_parse.parse(color);
53
68
  if (parsed === null) return;
54
- store.ingest(parsed);
55
- store.commit();
69
+ store.ingest(parsed, "recent");
70
+ store.commit("recent");
56
71
  }
57
72
  }) }, `${color}-${index}`))
58
73
  }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "cp-footer-spacer" }),
@@ -66,6 +81,24 @@ function PanelFooter() {
66
81
  handlePick();
67
82
  },
68
83
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_icons.EyedropperIcon, {})
84
+ }),
85
+ options.showCopyButton && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
86
+ type: "button",
87
+ className: "cp-icon-button",
88
+ "aria-label": copyStatus === "copied" ? "Color copied" : "Copy color",
89
+ title: copyStatus === "copied" ? "Copied" : copyStatus === "error" ? "Could not copy" : "Copy color",
90
+ disabled,
91
+ "data-cp-copy-status": copyStatus,
92
+ onClick: () => {
93
+ handleCopy();
94
+ },
95
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_icons.Icon, { name: "copy" })
96
+ }),
97
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
98
+ className: "cp-visually-hidden",
99
+ role: "status",
100
+ "aria-live": "polite",
101
+ children: [copyStatus === "copied" ? "Color copied to clipboard" : "", copyStatus === "error" ? "Could not copy color" : ""]
69
102
  })
70
103
  ]
71
104
  });
@@ -1,8 +1,8 @@
1
1
  "use client";
2
2
  import { cx, usePanel } from "../core/context.js";
3
3
  import { parse } from "../color/parse.js";
4
- import { toHex } from "../color/serialize.js";
5
- import { EyedropperIcon } from "../primitives/icons.js";
4
+ import { toFormat, toHex } from "../color/serialize.js";
5
+ import { EyedropperIcon, Icon } from "../primitives/icons.js";
6
6
  import { useScrollFade } from "../core/useScrollFade.js";
7
7
  import { useEyedropper } from "../primitives/useEyedropper.js";
8
8
  import * as React from "react";
@@ -11,13 +11,28 @@ import { jsx, jsxs } from "react/jsx-runtime";
11
11
  function PanelFooter() {
12
12
  const { store, classNames, options, disabled } = usePanel();
13
13
  const { supported, pick } = useEyedropper();
14
+ const [copyStatus, setCopyStatus] = React.useState("idle");
15
+ const copyTimer = React.useRef(null);
16
+ React.useEffect(() => () => {
17
+ if (copyTimer.current !== null) clearTimeout(copyTimer.current);
18
+ }, []);
19
+ const handleCopy = async () => {
20
+ try {
21
+ await navigator.clipboard.writeText(toFormat(store.get(), options.format));
22
+ setCopyStatus("copied");
23
+ } catch {
24
+ setCopyStatus("error");
25
+ }
26
+ if (copyTimer.current !== null) clearTimeout(copyTimer.current);
27
+ copyTimer.current = setTimeout(() => setCopyStatus("idle"), 1600);
28
+ };
14
29
  const handlePick = async () => {
15
30
  const hex = await pick();
16
31
  if (hex === null) return;
17
32
  const parsed = parse(hex);
18
33
  if (parsed === null) return;
19
- store.ingest(parsed);
20
- store.commit();
34
+ store.ingest(parsed, "eyedropper");
35
+ store.commit("eyedropper");
21
36
  };
22
37
  const recentsRef = React.useRef(null);
23
38
  const [current, setCurrent] = React.useState(null);
@@ -49,8 +64,8 @@ function PanelFooter() {
49
64
  onClick: () => {
50
65
  const parsed = parse(color);
51
66
  if (parsed === null) return;
52
- store.ingest(parsed);
53
- store.commit();
67
+ store.ingest(parsed, "recent");
68
+ store.commit("recent");
54
69
  }
55
70
  }) }, `${color}-${index}`))
56
71
  }) : /* @__PURE__ */ jsx("div", { className: "cp-footer-spacer" }),
@@ -64,6 +79,24 @@ function PanelFooter() {
64
79
  handlePick();
65
80
  },
66
81
  children: /* @__PURE__ */ jsx(EyedropperIcon, {})
82
+ }),
83
+ options.showCopyButton && /* @__PURE__ */ jsx("button", {
84
+ type: "button",
85
+ className: "cp-icon-button",
86
+ "aria-label": copyStatus === "copied" ? "Color copied" : "Copy color",
87
+ title: copyStatus === "copied" ? "Copied" : copyStatus === "error" ? "Could not copy" : "Copy color",
88
+ disabled,
89
+ "data-cp-copy-status": copyStatus,
90
+ onClick: () => {
91
+ handleCopy();
92
+ },
93
+ children: /* @__PURE__ */ jsx(Icon, { name: "copy" })
94
+ }),
95
+ /* @__PURE__ */ jsxs("span", {
96
+ className: "cp-visually-hidden",
97
+ role: "status",
98
+ "aria-live": "polite",
99
+ children: [copyStatus === "copied" ? "Color copied to clipboard" : "", copyStatus === "error" ? "Could not copy color" : ""]
67
100
  })
68
101
  ]
69
102
  });
package/dist/contrast.cjs CHANGED
@@ -1,10 +1,13 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_contrast = require("./a11y/contrast.cjs");
3
3
  exports.apcaContrast = require_contrast.apcaContrast;
4
+ exports.compositeColor = require_contrast.compositeColor;
4
5
  exports.contrastRatio = require_contrast.contrastRatio;
6
+ exports.contrastRatioWithAlpha = require_contrast.contrastRatioWithAlpha;
5
7
  exports.contrastReport = require_contrast.contrastReport;
6
8
  exports.meetsContrast = require_contrast.meetsContrast;
7
9
  exports.meetsNonTextContrast = require_contrast.meetsNonTextContrast;
8
10
  exports.readableTextColor = require_contrast.readableTextColor;
9
11
  exports.relativeLuminance = require_contrast.relativeLuminance;
12
+ exports.suggestAccessibleColor = require_contrast.suggestAccessibleColor;
10
13
  exports.wcagLevel = require_contrast.wcagLevel;
@@ -1,2 +1,2 @@
1
- import { ContrastOptions, ContrastReport, TextSize, WcagLevel, apcaContrast, contrastRatio, contrastReport, meetsContrast, meetsNonTextContrast, readableTextColor, relativeLuminance, wcagLevel } from "./a11y/contrast.cjs";
2
- export { type ContrastOptions, type ContrastReport, type TextSize, type WcagLevel, apcaContrast, contrastRatio, contrastReport, meetsContrast, meetsNonTextContrast, readableTextColor, relativeLuminance, wcagLevel };
1
+ import { AccessibleColorSuggestion, ContrastOptions, ContrastReport, TextSize, WcagLevel, apcaContrast, compositeColor, contrastRatio, contrastRatioWithAlpha, contrastReport, meetsContrast, meetsNonTextContrast, readableTextColor, relativeLuminance, suggestAccessibleColor, wcagLevel } from "./a11y/contrast.cjs";
2
+ export { type AccessibleColorSuggestion, type ContrastOptions, type ContrastReport, type TextSize, type WcagLevel, apcaContrast, compositeColor, contrastRatio, contrastRatioWithAlpha, contrastReport, meetsContrast, meetsNonTextContrast, readableTextColor, relativeLuminance, suggestAccessibleColor, wcagLevel };
@@ -1,2 +1,2 @@
1
- import { ContrastOptions, ContrastReport, TextSize, WcagLevel, apcaContrast, contrastRatio, contrastReport, meetsContrast, meetsNonTextContrast, readableTextColor, relativeLuminance, wcagLevel } from "./a11y/contrast.js";
2
- export { type ContrastOptions, type ContrastReport, type TextSize, type WcagLevel, apcaContrast, contrastRatio, contrastReport, meetsContrast, meetsNonTextContrast, readableTextColor, relativeLuminance, wcagLevel };
1
+ import { AccessibleColorSuggestion, ContrastOptions, ContrastReport, TextSize, WcagLevel, apcaContrast, compositeColor, contrastRatio, contrastRatioWithAlpha, contrastReport, meetsContrast, meetsNonTextContrast, readableTextColor, relativeLuminance, suggestAccessibleColor, wcagLevel } from "./a11y/contrast.js";
2
+ export { type AccessibleColorSuggestion, type ContrastOptions, type ContrastReport, type TextSize, type WcagLevel, apcaContrast, compositeColor, contrastRatio, contrastRatioWithAlpha, contrastReport, meetsContrast, meetsNonTextContrast, readableTextColor, relativeLuminance, suggestAccessibleColor, wcagLevel };
package/dist/contrast.js CHANGED
@@ -1,2 +1,2 @@
1
- import { apcaContrast, contrastRatio, contrastReport, meetsContrast, meetsNonTextContrast, readableTextColor, relativeLuminance, wcagLevel } from "./a11y/contrast.js";
2
- export { apcaContrast, contrastRatio, contrastReport, meetsContrast, meetsNonTextContrast, readableTextColor, relativeLuminance, wcagLevel };
1
+ import { apcaContrast, compositeColor, contrastRatio, contrastRatioWithAlpha, contrastReport, meetsContrast, meetsNonTextContrast, readableTextColor, relativeLuminance, suggestAccessibleColor, wcagLevel } from "./a11y/contrast.js";
2
+ export { apcaContrast, compositeColor, contrastRatio, contrastRatioWithAlpha, contrastReport, meetsContrast, meetsNonTextContrast, readableTextColor, relativeLuminance, suggestAccessibleColor, wcagLevel };
@@ -1,3 +1,4 @@
1
+ import { ColorFormat } from "../color/types.cjs";
1
2
  import { ColorStore } from "./store.cjs";
2
3
  import "react";
3
4
  //#region src/core/context.d.ts
@@ -28,7 +29,9 @@ export interface PanelOptions {
28
29
  pencils: string[] | undefined;
29
30
  showAlpha: boolean;
30
31
  showEyedropper: boolean;
32
+ showCopyButton: boolean;
31
33
  showRecentColors: boolean;
34
+ format: ColorFormat;
32
35
  recentColors: string[];
33
36
  onRecentColorsChange?: (colors: string[]) => void;
34
37
  }
@@ -1,3 +1,4 @@
1
+ import { ColorFormat } from "../color/types.js";
1
2
  import { ColorStore } from "./store.js";
2
3
  import "react";
3
4
  //#region src/core/context.d.ts
@@ -28,7 +29,9 @@ export interface PanelOptions {
28
29
  pencils: string[] | undefined;
29
30
  showAlpha: boolean;
30
31
  showEyedropper: boolean;
32
+ showCopyButton: boolean;
31
33
  showRecentColors: boolean;
34
+ format: ColorFormat;
32
35
  recentColors: string[];
33
36
  onRecentColorsChange?: (colors: string[]) => void;
34
37
  }
@@ -2,26 +2,34 @@ const require_sticky = require("../color/sticky.cjs");
2
2
  //#region src/core/store.ts
3
3
  function createColorStore(initial) {
4
4
  let state = initial;
5
+ let lastSource = "unknown";
5
6
  const listeners = /* @__PURE__ */ new Set();
6
7
  const commitListeners = /* @__PURE__ */ new Set();
7
- function emit() {
8
- for (const listener of Array.from(listeners)) listener(state);
8
+ function emit(source) {
9
+ for (const listener of Array.from(listeners)) listener(state, {
10
+ phase: "change",
11
+ source
12
+ });
9
13
  }
10
- function set(next) {
14
+ function set(next, source = "unknown") {
11
15
  if (require_sticky.sameHsva(state, next)) return;
12
16
  state = next;
13
- emit();
17
+ lastSource = source;
18
+ emit(source);
14
19
  }
15
20
  return {
16
21
  get: () => state,
17
22
  set,
18
- patch: (partial) => set({
23
+ patch: (partial, source) => set({
19
24
  ...state,
20
25
  ...partial
21
- }),
22
- ingest: (next) => set(require_sticky.ingest(next, state)),
23
- commit: () => {
24
- for (const listener of Array.from(commitListeners)) listener(state);
26
+ }, source),
27
+ ingest: (next, source) => set(require_sticky.ingest(next, state), source),
28
+ commit: (source = lastSource) => {
29
+ for (const listener of Array.from(commitListeners)) listener(state, {
30
+ phase: "commit",
31
+ source
32
+ });
25
33
  },
26
34
  subscribe: (listener) => {
27
35
  listeners.add(listener);
@@ -1,13 +1,13 @@
1
- import { Hsva } from "../color/types.cjs";
1
+ import { ColorChangeMeta, ColorChangeSource, Hsva } from "../color/types.cjs";
2
2
  //#region src/core/store.d.ts
3
- export type ColorListener = (color: Hsva) => void;
3
+ export type ColorListener = (color: Hsva, meta: ColorChangeMeta) => void;
4
4
  export type Unsubscribe = () => void;
5
5
  export interface ColorStore {
6
6
  get(): Hsva;
7
- set(next: Hsva): void;
8
- patch(partial: Partial<Hsva>): void;
9
- ingest(next: Hsva): void;
10
- commit(): void;
7
+ set(next: Hsva, source?: ColorChangeSource): void;
8
+ patch(partial: Partial<Hsva>, source?: ColorChangeSource): void;
9
+ ingest(next: Hsva, source?: ColorChangeSource): void;
10
+ commit(source?: ColorChangeSource): void;
11
11
  subscribe(listener: ColorListener): Unsubscribe;
12
12
  subscribeCommit(listener: ColorListener): Unsubscribe;
13
13
  getSnapshot(): Hsva;
@@ -1,13 +1,13 @@
1
- import { Hsva } from "../color/types.js";
1
+ import { ColorChangeMeta, ColorChangeSource, Hsva } from "../color/types.js";
2
2
  //#region src/core/store.d.ts
3
- export type ColorListener = (color: Hsva) => void;
3
+ export type ColorListener = (color: Hsva, meta: ColorChangeMeta) => void;
4
4
  export type Unsubscribe = () => void;
5
5
  export interface ColorStore {
6
6
  get(): Hsva;
7
- set(next: Hsva): void;
8
- patch(partial: Partial<Hsva>): void;
9
- ingest(next: Hsva): void;
10
- commit(): void;
7
+ set(next: Hsva, source?: ColorChangeSource): void;
8
+ patch(partial: Partial<Hsva>, source?: ColorChangeSource): void;
9
+ ingest(next: Hsva, source?: ColorChangeSource): void;
10
+ commit(source?: ColorChangeSource): void;
11
11
  subscribe(listener: ColorListener): Unsubscribe;
12
12
  subscribeCommit(listener: ColorListener): Unsubscribe;
13
13
  getSnapshot(): Hsva;
@@ -2,26 +2,34 @@ import { ingest, sameHsva } from "../color/sticky.js";
2
2
  //#region src/core/store.ts
3
3
  function createColorStore(initial) {
4
4
  let state = initial;
5
+ let lastSource = "unknown";
5
6
  const listeners = /* @__PURE__ */ new Set();
6
7
  const commitListeners = /* @__PURE__ */ new Set();
7
- function emit() {
8
- for (const listener of Array.from(listeners)) listener(state);
8
+ function emit(source) {
9
+ for (const listener of Array.from(listeners)) listener(state, {
10
+ phase: "change",
11
+ source
12
+ });
9
13
  }
10
- function set(next) {
14
+ function set(next, source = "unknown") {
11
15
  if (sameHsva(state, next)) return;
12
16
  state = next;
13
- emit();
17
+ lastSource = source;
18
+ emit(source);
14
19
  }
15
20
  return {
16
21
  get: () => state,
17
22
  set,
18
- patch: (partial) => set({
23
+ patch: (partial, source) => set({
19
24
  ...state,
20
25
  ...partial
21
- }),
22
- ingest: (next) => set(ingest(next, state)),
23
- commit: () => {
24
- for (const listener of Array.from(commitListeners)) listener(state);
26
+ }, source),
27
+ ingest: (next, source) => set(ingest(next, state), source),
28
+ commit: (source = lastSource) => {
29
+ for (const listener of Array.from(commitListeners)) listener(state, {
30
+ phase: "commit",
31
+ source
32
+ });
25
33
  },
26
34
  subscribe: (listener) => {
27
35
  listeners.add(listener);
package/dist/core.cjs CHANGED
@@ -1,13 +1,18 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_convert = require("./color/convert.cjs");
3
+ const require_css4 = require("./color/css4.cjs");
3
4
  const require_parse = require("./color/parse.cjs");
4
5
  const require_serialize = require("./color/serialize.cjs");
5
6
  const require_sticky = require("./color/sticky.cjs");
6
7
  const require_store = require("./core/store.cjs");
8
+ const require_value = require("./color/value.cjs");
7
9
  exports.clamp = require_convert.clamp;
8
10
  exports.clearNamedColors = require_parse.clearNamedColors;
11
+ exports.colorValueToHsva = require_css4.colorValueToHsva;
12
+ exports.convertColor = require_css4.convertColor;
9
13
  exports.createColorStore = require_store.createColorStore;
10
14
  exports.hslaToHsva = require_convert.hslaToHsva;
15
+ exports.hsvaToColorValue = require_css4.hsvaToColorValue;
11
16
  exports.hsvaToHsl = require_convert.hsvaToHsl;
12
17
  exports.hsvaToHsla = require_convert.hsvaToHsla;
13
18
  exports.hsvaToHwba = require_convert.hsvaToHwba;
@@ -15,14 +20,19 @@ exports.hsvaToRgb = require_convert.hsvaToRgb;
15
20
  exports.hsvaToRgba = require_convert.hsvaToRgba;
16
21
  exports.hwbaToHsva = require_convert.hwbaToHsva;
17
22
  exports.ingest = require_sticky.ingest;
23
+ exports.isInGamut = require_css4.isInGamut;
18
24
  exports.isValidColor = require_parse.isValidColor;
25
+ exports.mapToGamut = require_css4.mapToGamut;
19
26
  exports.normalizeHue = require_convert.normalizeHue;
20
27
  exports.parse = require_parse.parse;
28
+ exports.parseColor = require_value.parseColor;
29
+ exports.parseCss4Color = require_css4.parseCss4Color;
21
30
  exports.registerNamedColors = require_parse.registerNamedColors;
22
31
  exports.rgbaToHsva = require_convert.rgbaToHsva;
23
32
  exports.roundRgba = require_convert.roundRgba;
24
33
  exports.sameHsva = require_sticky.sameHsva;
25
34
  exports.sameRendered = require_sticky.sameRendered;
35
+ exports.serializeColor = require_css4.serializeColor;
26
36
  exports.toFormat = require_serialize.toFormat;
27
37
  exports.toHex = require_serialize.toHex;
28
38
  exports.toHexa = require_serialize.toHexa;
package/dist/core.d.cts CHANGED
@@ -1,7 +1,9 @@
1
- import { ColorChangeResult, ColorFormat, Hsl, Hsla, Hsva, Hwb, Hwba, Rgb, Rgba } from "./color/types.cjs";
1
+ import { ColorChangeMeta, ColorChangeResult, ColorChangeSource, ColorFormat, ColorValue, Hsl, Hsla, Hsva, Hwb, Hwba, ModernColorSpace, Rgb, Rgba, SerializeColorOptions } from "./color/types.cjs";
2
2
  import { clamp, hslaToHsva, hsvaToHsl, hsvaToHsla, hsvaToHwba, hsvaToRgb, hsvaToRgba, hwbaToHsva, normalizeHue, rgbaToHsva, roundRgba } from "./color/convert.cjs";
3
3
  import { clearNamedColors, isValidColor, parse, registerNamedColors } from "./color/parse.cjs";
4
+ import { parseColor } from "./color/value.cjs";
5
+ import { colorValueToHsva, convertColor, hsvaToColorValue, isInGamut, mapToGamut, parseCss4Color, serializeColor } from "./color/css4.cjs";
4
6
  import { toFormat, toHex, toHexa, toHslString, toHslaString, toResult, toRgbString, toRgbaString } from "./color/serialize.cjs";
5
7
  import { ingest, sameHsva, sameRendered } from "./color/sticky.cjs";
6
8
  import { ColorListener, ColorStore, Unsubscribe, createColorStore } from "./core/store.cjs";
7
- export { ColorChangeResult, ColorFormat, type ColorListener, type ColorStore, Hsl, Hsla, Hsva, Hwb, Hwba, Rgb, Rgba, type Unsubscribe, clamp, clearNamedColors, createColorStore, hslaToHsva, hsvaToHsl, hsvaToHsla, hsvaToHwba, hsvaToRgb, hsvaToRgba, hwbaToHsva, ingest, isValidColor, normalizeHue, parse, registerNamedColors, rgbaToHsva, roundRgba, sameHsva, sameRendered, toFormat, toHex, toHexa, toHslString, toHslaString, toResult, toRgbString, toRgbaString };
9
+ export { ColorChangeMeta, ColorChangeResult, ColorChangeSource, ColorFormat, type ColorListener, type ColorStore, ColorValue, Hsl, Hsla, Hsva, Hwb, Hwba, ModernColorSpace, Rgb, Rgba, SerializeColorOptions, type Unsubscribe, clamp, clearNamedColors, colorValueToHsva, convertColor, createColorStore, hslaToHsva, hsvaToColorValue, hsvaToHsl, hsvaToHsla, hsvaToHwba, hsvaToRgb, hsvaToRgba, hwbaToHsva, ingest, isInGamut, isValidColor, mapToGamut, normalizeHue, parse, parseColor, parseCss4Color, registerNamedColors, rgbaToHsva, roundRgba, sameHsva, sameRendered, serializeColor, toFormat, toHex, toHexa, toHslString, toHslaString, toResult, toRgbString, toRgbaString };
package/dist/core.d.ts CHANGED
@@ -1,7 +1,9 @@
1
- import { ColorChangeResult, ColorFormat, Hsl, Hsla, Hsva, Hwb, Hwba, Rgb, Rgba } from "./color/types.js";
1
+ import { ColorChangeMeta, ColorChangeResult, ColorChangeSource, ColorFormat, ColorValue, Hsl, Hsla, Hsva, Hwb, Hwba, ModernColorSpace, Rgb, Rgba, SerializeColorOptions } from "./color/types.js";
2
2
  import { clamp, hslaToHsva, hsvaToHsl, hsvaToHsla, hsvaToHwba, hsvaToRgb, hsvaToRgba, hwbaToHsva, normalizeHue, rgbaToHsva, roundRgba } from "./color/convert.js";
3
3
  import { clearNamedColors, isValidColor, parse, registerNamedColors } from "./color/parse.js";
4
+ import { parseColor } from "./color/value.js";
5
+ import { colorValueToHsva, convertColor, hsvaToColorValue, isInGamut, mapToGamut, parseCss4Color, serializeColor } from "./color/css4.js";
4
6
  import { toFormat, toHex, toHexa, toHslString, toHslaString, toResult, toRgbString, toRgbaString } from "./color/serialize.js";
5
7
  import { ingest, sameHsva, sameRendered } from "./color/sticky.js";
6
8
  import { ColorListener, ColorStore, Unsubscribe, createColorStore } from "./core/store.js";
7
- export { ColorChangeResult, ColorFormat, type ColorListener, type ColorStore, Hsl, Hsla, Hsva, Hwb, Hwba, Rgb, Rgba, type Unsubscribe, clamp, clearNamedColors, createColorStore, hslaToHsva, hsvaToHsl, hsvaToHsla, hsvaToHwba, hsvaToRgb, hsvaToRgba, hwbaToHsva, ingest, isValidColor, normalizeHue, parse, registerNamedColors, rgbaToHsva, roundRgba, sameHsva, sameRendered, toFormat, toHex, toHexa, toHslString, toHslaString, toResult, toRgbString, toRgbaString };
9
+ export { ColorChangeMeta, ColorChangeResult, ColorChangeSource, ColorFormat, type ColorListener, type ColorStore, ColorValue, Hsl, Hsla, Hsva, Hwb, Hwba, ModernColorSpace, Rgb, Rgba, SerializeColorOptions, type Unsubscribe, clamp, clearNamedColors, colorValueToHsva, convertColor, createColorStore, hslaToHsva, hsvaToColorValue, hsvaToHsl, hsvaToHsla, hsvaToHwba, hsvaToRgb, hsvaToRgba, hwbaToHsva, ingest, isInGamut, isValidColor, mapToGamut, normalizeHue, parse, parseColor, parseCss4Color, registerNamedColors, rgbaToHsva, roundRgba, sameHsva, sameRendered, serializeColor, toFormat, toHex, toHexa, toHslString, toHslaString, toResult, toRgbString, toRgbaString };
package/dist/core.js CHANGED
@@ -1,6 +1,8 @@
1
1
  import { clamp, hslaToHsva, hsvaToHsl, hsvaToHsla, hsvaToHwba, hsvaToRgb, hsvaToRgba, hwbaToHsva, normalizeHue, rgbaToHsva, roundRgba } from "./color/convert.js";
2
+ import { colorValueToHsva, convertColor, hsvaToColorValue, isInGamut, mapToGamut, parseCss4Color, serializeColor } from "./color/css4.js";
2
3
  import { clearNamedColors, isValidColor, parse, registerNamedColors } from "./color/parse.js";
3
4
  import { toFormat, toHex, toHexa, toHslString, toHslaString, toResult, toRgbString, toRgbaString } from "./color/serialize.js";
4
5
  import { ingest, sameHsva, sameRendered } from "./color/sticky.js";
5
6
  import { createColorStore } from "./core/store.js";
6
- export { clamp, clearNamedColors, createColorStore, hslaToHsva, hsvaToHsl, hsvaToHsla, hsvaToHwba, hsvaToRgb, hsvaToRgba, hwbaToHsva, ingest, isValidColor, normalizeHue, parse, registerNamedColors, rgbaToHsva, roundRgba, sameHsva, sameRendered, toFormat, toHex, toHexa, toHslString, toHslaString, toResult, toRgbString, toRgbaString };
7
+ import { parseColor } from "./color/value.js";
8
+ export { clamp, clearNamedColors, colorValueToHsva, convertColor, createColorStore, hslaToHsva, hsvaToColorValue, hsvaToHsl, hsvaToHsla, hsvaToHwba, hsvaToRgb, hsvaToRgba, hwbaToHsva, ingest, isInGamut, isValidColor, mapToGamut, normalizeHue, parse, parseColor, parseCss4Color, registerNamedColors, rgbaToHsva, roundRgba, sameHsva, sameRendered, serializeColor, toFormat, toHex, toHexa, toHslString, toHslaString, toResult, toRgbString, toRgbaString };
@@ -0,0 +1,39 @@
1
+ const require_css4 = require("../color/css4.cjs");
2
+ const require_value = require("../color/value.cjs");
3
+ //#region src/export/tokens.ts
4
+ function safeName(name) {
5
+ return name.trim().toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "") || "color";
6
+ }
7
+ function formatColor(color, format) {
8
+ if (format === "preserve") return color;
9
+ const parsed = require_value.parseColor(color);
10
+ return parsed === null ? color : require_css4.serializeColor(parsed, {
11
+ format,
12
+ gamut: format === "display-p3" ? "display-p3" : "preserve"
13
+ });
14
+ }
15
+ function toCssVariables(tokens, options = {}) {
16
+ const prefix = safeName(options.prefix ?? "color");
17
+ const format = options.format ?? "preserve";
18
+ const lines = Object.entries(tokens).map(([name, color]) => ` --${prefix}-${safeName(name)}: ${formatColor(color, format)};`);
19
+ return `${options.selector ?? ":root"} {\n${lines.join("\n")}\n}`;
20
+ }
21
+ function toScssVariables(tokens, options = {}) {
22
+ const prefix = safeName(options.prefix ?? "color");
23
+ const format = options.format ?? "preserve";
24
+ return Object.entries(tokens).map(([name, color]) => `$${prefix}-${safeName(name)}: ${formatColor(color, format)};`).join("\n");
25
+ }
26
+ function toTailwindColors(tokens) {
27
+ return Object.fromEntries(Object.entries(tokens).map(([name, color]) => [safeName(name), color]));
28
+ }
29
+ function toDesignTokens(tokens) {
30
+ return Object.fromEntries(Object.entries(tokens).map(([name, color]) => [safeName(name), {
31
+ $type: "color",
32
+ $value: color
33
+ }]));
34
+ }
35
+ //#endregion
36
+ exports.toCssVariables = toCssVariables;
37
+ exports.toDesignTokens = toDesignTokens;
38
+ exports.toScssVariables = toScssVariables;
39
+ exports.toTailwindColors = toTailwindColors;
@@ -0,0 +1,16 @@
1
+ import { ModernColorSpace } from "../color/types.cjs";
2
+ //#region src/export/tokens.d.ts
3
+ export type ColorTokenMap = Record<string, string>;
4
+ export interface TokenExportOptions {
5
+ prefix?: string;
6
+ format?: ModernColorSpace | "preserve";
7
+ selector?: string;
8
+ }
9
+ export declare function toCssVariables(tokens: ColorTokenMap, options?: TokenExportOptions): string;
10
+ export declare function toScssVariables(tokens: ColorTokenMap, options?: Omit<TokenExportOptions, "selector">): string;
11
+ export declare function toTailwindColors(tokens: ColorTokenMap): Record<string, string>;
12
+ export declare function toDesignTokens(tokens: ColorTokenMap): Record<string, {
13
+ $type: "color";
14
+ $value: string;
15
+ }>;
16
+ //#endregion
@@ -0,0 +1,16 @@
1
+ import { ModernColorSpace } from "../color/types.js";
2
+ //#region src/export/tokens.d.ts
3
+ export type ColorTokenMap = Record<string, string>;
4
+ export interface TokenExportOptions {
5
+ prefix?: string;
6
+ format?: ModernColorSpace | "preserve";
7
+ selector?: string;
8
+ }
9
+ export declare function toCssVariables(tokens: ColorTokenMap, options?: TokenExportOptions): string;
10
+ export declare function toScssVariables(tokens: ColorTokenMap, options?: Omit<TokenExportOptions, "selector">): string;
11
+ export declare function toTailwindColors(tokens: ColorTokenMap): Record<string, string>;
12
+ export declare function toDesignTokens(tokens: ColorTokenMap): Record<string, {
13
+ $type: "color";
14
+ $value: string;
15
+ }>;
16
+ //#endregion