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
@@ -0,0 +1,3 @@
1
+ import { addGradientStop, gradientToCss, isGradientColorSupported, normalizeGradient, sampleGradient } from "./gradient/model.js";
2
+ import { GradientEditor } from "./gradient/GradientEditor.js";
3
+ export { GradientEditor, addGradientStop, gradientToCss, isGradientColorSupported, normalizeGradient, sampleGradient };
@@ -9,13 +9,27 @@ async function toBlob(source) {
9
9
  if (!response.ok) throw new Error(`chroma-panel: could not load image (${response.status}).`);
10
10
  return await response.blob();
11
11
  }
12
- async function decode(blob, size) {
12
+ const DEFAULT_MAX_FILE_SIZE = 20971520;
13
+ const DEFAULT_MAX_SOURCE_PIXELS = 4e7;
14
+ function validateBlob(blob, maxFileSize) {
15
+ if (blob.size > maxFileSize) {
16
+ const limit = Math.round(maxFileSize / 1048576);
17
+ throw new RangeError(`chroma-panel: image must be ${limit} MB or smaller.`);
18
+ }
19
+ if (blob.type !== "" && !blob.type.toLowerCase().startsWith("image/")) throw new TypeError("chroma-panel: the selected file is not an image.");
20
+ }
21
+ async function decode(blob, size, maxSourcePixels) {
13
22
  if (typeof createImageBitmap !== "function") throw new Error("chroma-panel: this browser cannot decode images off-thread.");
14
23
  const longest = Math.max(8, Math.round(size));
15
- try {
16
- const probe = await createImageBitmap(blob);
17
- const wide = probe.width >= probe.height;
24
+ const probe = await createImageBitmap(blob);
25
+ if (probe.width * probe.height > maxSourcePixels) {
18
26
  probe.close();
27
+ const megapixels = Math.round(maxSourcePixels / 1e6);
28
+ throw new RangeError(`chroma-panel: image dimensions must not exceed ${megapixels} megapixels.`);
29
+ }
30
+ const wide = probe.width >= probe.height;
31
+ probe.close();
32
+ try {
19
33
  return await createImageBitmap(blob, {
20
34
  ...wide ? { resizeWidth: longest } : { resizeHeight: longest },
21
35
  resizeQuality: "medium",
@@ -71,14 +85,17 @@ function runInWorker(worker, data, maxColors, alphaThreshold, signal) {
71
85
  });
72
86
  }
73
87
  async function extractPalette(source, options = {}) {
74
- const { maxColors = 8, size = 100, alphaThreshold = 128, worker, signal } = options;
88
+ const { maxColors = 8, size = 100, alphaThreshold = 128, maxFileSize = DEFAULT_MAX_FILE_SIZE, maxSourcePixels = DEFAULT_MAX_SOURCE_PIXELS, worker, signal } = options;
75
89
  if (!Number.isFinite(maxColors) || maxColors < 2) throw new RangeError("chroma-panel: maxColors must be a number of at least 2.");
76
90
  if (!Number.isFinite(size) || size < 8) throw new RangeError("chroma-panel: size must be a number of at least 8.");
77
91
  if (!Number.isFinite(alphaThreshold) || alphaThreshold < 0 || alphaThreshold > 255) throw new RangeError("chroma-panel: alphaThreshold must be between 0 and 255.");
92
+ if (!Number.isFinite(maxFileSize) || maxFileSize <= 0) throw new RangeError("chroma-panel: maxFileSize must be greater than 0.");
93
+ if (!Number.isFinite(maxSourcePixels) || maxSourcePixels < 64) throw new RangeError("chroma-panel: maxSourcePixels must be at least 64.");
78
94
  if (aborted(signal)) throw new DOMException("Aborted", "AbortError");
79
95
  const blob = await toBlob(source);
96
+ validateBlob(blob, maxFileSize);
80
97
  if (aborted(signal)) throw new DOMException("Aborted", "AbortError");
81
- const bitmap = await decode(blob, size);
98
+ const bitmap = await decode(blob, size, maxSourcePixels);
82
99
  let data;
83
100
  try {
84
101
  if (aborted(signal)) throw new DOMException("Aborted", "AbortError");
@@ -4,6 +4,8 @@ export interface ExtractOptions {
4
4
  maxColors?: number;
5
5
  size?: number;
6
6
  alphaThreshold?: number;
7
+ maxFileSize?: number;
8
+ maxSourcePixels?: number;
7
9
  worker?: Worker | (() => Worker);
8
10
  signal?: AbortSignal;
9
11
  }
@@ -4,6 +4,8 @@ export interface ExtractOptions {
4
4
  maxColors?: number;
5
5
  size?: number;
6
6
  alphaThreshold?: number;
7
+ maxFileSize?: number;
8
+ maxSourcePixels?: number;
7
9
  worker?: Worker | (() => Worker);
8
10
  signal?: AbortSignal;
9
11
  }
@@ -9,13 +9,27 @@ async function toBlob(source) {
9
9
  if (!response.ok) throw new Error(`chroma-panel: could not load image (${response.status}).`);
10
10
  return await response.blob();
11
11
  }
12
- async function decode(blob, size) {
12
+ const DEFAULT_MAX_FILE_SIZE = 20971520;
13
+ const DEFAULT_MAX_SOURCE_PIXELS = 4e7;
14
+ function validateBlob(blob, maxFileSize) {
15
+ if (blob.size > maxFileSize) {
16
+ const limit = Math.round(maxFileSize / 1048576);
17
+ throw new RangeError(`chroma-panel: image must be ${limit} MB or smaller.`);
18
+ }
19
+ if (blob.type !== "" && !blob.type.toLowerCase().startsWith("image/")) throw new TypeError("chroma-panel: the selected file is not an image.");
20
+ }
21
+ async function decode(blob, size, maxSourcePixels) {
13
22
  if (typeof createImageBitmap !== "function") throw new Error("chroma-panel: this browser cannot decode images off-thread.");
14
23
  const longest = Math.max(8, Math.round(size));
15
- try {
16
- const probe = await createImageBitmap(blob);
17
- const wide = probe.width >= probe.height;
24
+ const probe = await createImageBitmap(blob);
25
+ if (probe.width * probe.height > maxSourcePixels) {
18
26
  probe.close();
27
+ const megapixels = Math.round(maxSourcePixels / 1e6);
28
+ throw new RangeError(`chroma-panel: image dimensions must not exceed ${megapixels} megapixels.`);
29
+ }
30
+ const wide = probe.width >= probe.height;
31
+ probe.close();
32
+ try {
19
33
  return await createImageBitmap(blob, {
20
34
  ...wide ? { resizeWidth: longest } : { resizeHeight: longest },
21
35
  resizeQuality: "medium",
@@ -71,14 +85,17 @@ function runInWorker(worker, data, maxColors, alphaThreshold, signal) {
71
85
  });
72
86
  }
73
87
  async function extractPalette(source, options = {}) {
74
- const { maxColors = 8, size = 100, alphaThreshold = 128, worker, signal } = options;
88
+ const { maxColors = 8, size = 100, alphaThreshold = 128, maxFileSize = DEFAULT_MAX_FILE_SIZE, maxSourcePixels = DEFAULT_MAX_SOURCE_PIXELS, worker, signal } = options;
75
89
  if (!Number.isFinite(maxColors) || maxColors < 2) throw new RangeError("chroma-panel: maxColors must be a number of at least 2.");
76
90
  if (!Number.isFinite(size) || size < 8) throw new RangeError("chroma-panel: size must be a number of at least 8.");
77
91
  if (!Number.isFinite(alphaThreshold) || alphaThreshold < 0 || alphaThreshold > 255) throw new RangeError("chroma-panel: alphaThreshold must be between 0 and 255.");
92
+ if (!Number.isFinite(maxFileSize) || maxFileSize <= 0) throw new RangeError("chroma-panel: maxFileSize must be greater than 0.");
93
+ if (!Number.isFinite(maxSourcePixels) || maxSourcePixels < 64) throw new RangeError("chroma-panel: maxSourcePixels must be at least 64.");
78
94
  if (aborted(signal)) throw new DOMException("Aborted", "AbortError");
79
95
  const blob = await toBlob(source);
96
+ validateBlob(blob, maxFileSize);
80
97
  if (aborted(signal)) throw new DOMException("Aborted", "AbortError");
81
- const bitmap = await decode(blob, size);
98
+ const bitmap = await decode(blob, size, maxSourcePixels);
82
99
  let data;
83
100
  try {
84
101
  if (aborted(signal)) throw new DOMException("Aborted", "AbortError");
package/dist/index.cjs CHANGED
@@ -7,6 +7,7 @@ const require_useColorStore = require("./core/useColorStore.cjs");
7
7
  const require_usePointerDrag = require("./core/usePointerDrag.cjs");
8
8
  const require_ChannelSlider = require("./primitives/ChannelSlider.cjs");
9
9
  const require_ColorDisc = require("./primitives/ColorDisc.cjs");
10
+ const require_css4 = require("./color/css4.cjs");
10
11
  const require_parse = require("./color/parse.cjs");
11
12
  const require_serialize = require("./color/serialize.cjs");
12
13
  const require_ColorField = require("./primitives/ColorField.cjs");
@@ -33,6 +34,7 @@ const require_Popover = require("./components/Popover.cjs");
33
34
  const require_ColorInput = require("./components/ColorInput.cjs");
34
35
  const require_ColorArea = require("./primitives/ColorArea.cjs");
35
36
  const require_Swatch = require("./primitives/Swatch.cjs");
37
+ const require_value = require("./color/value.cjs");
36
38
  const require_contrast = require("./a11y/contrast.cjs");
37
39
  //#region src/index.ts
38
40
  require_registry.registerMode(require_index.wheelMode);
@@ -64,14 +66,19 @@ exports.WheelPanel = require_index.WheelPanel;
64
66
  exports.apcaContrast = require_contrast.apcaContrast;
65
67
  exports.clamp = require_convert.clamp;
66
68
  exports.clearNamedColors = require_parse.clearNamedColors;
69
+ exports.colorValueToHsva = require_css4.colorValueToHsva;
70
+ exports.compositeColor = require_contrast.compositeColor;
67
71
  exports.contrastRatio = require_contrast.contrastRatio;
72
+ exports.contrastRatioWithAlpha = require_contrast.contrastRatioWithAlpha;
68
73
  exports.contrastReport = require_contrast.contrastReport;
74
+ exports.convertColor = require_css4.convertColor;
69
75
  exports.createColorStore = require_store.createColorStore;
70
76
  exports.defaultPalettes = require_defaults.defaultPalettes;
71
77
  exports.defaultPencils = require_defaults.defaultPencils;
72
78
  exports.extractPalette = require_extract.extractPalette;
73
79
  exports.getMode = require_registry.getMode;
74
80
  exports.hslaToHsva = require_convert.hslaToHsva;
81
+ exports.hsvaToColorValue = require_css4.hsvaToColorValue;
75
82
  exports.hsvaToHsl = require_convert.hsvaToHsl;
76
83
  exports.hsvaToHsla = require_convert.hsvaToHsla;
77
84
  exports.hsvaToHwba = require_convert.hsvaToHwba;
@@ -81,12 +88,16 @@ exports.hwbaToHsva = require_convert.hwbaToHsva;
81
88
  exports.imageMode = require_index$3.imageMode;
82
89
  exports.ingest = require_sticky.ingest;
83
90
  exports.injectStyles = require_styleInjector.injectStyles;
91
+ exports.isInGamut = require_css4.isInGamut;
84
92
  exports.isValidColor = require_parse.isValidColor;
93
+ exports.mapToGamut = require_css4.mapToGamut;
85
94
  exports.meetsContrast = require_contrast.meetsContrast;
86
95
  exports.meetsNonTextContrast = require_contrast.meetsNonTextContrast;
87
96
  exports.normalizeHue = require_convert.normalizeHue;
88
97
  exports.palettesMode = require_index$2.palettesMode;
89
98
  exports.parse = require_parse.parse;
99
+ exports.parseColor = require_value.parseColor;
100
+ exports.parseCss4Color = require_css4.parseCss4Color;
90
101
  exports.pencilsMode = require_index$4.pencilsMode;
91
102
  exports.pushRecent = require_PanelFooter.pushRecent;
92
103
  exports.quantize = require_mmcq.quantize;
@@ -99,8 +110,10 @@ exports.rgbaToHsva = require_convert.rgbaToHsva;
99
110
  exports.roundRgba = require_convert.roundRgba;
100
111
  exports.sameHsva = require_sticky.sameHsva;
101
112
  exports.sameRendered = require_sticky.sameRendered;
113
+ exports.serializeColor = require_css4.serializeColor;
102
114
  exports.setStyleNonce = require_styleInjector.setStyleNonce;
103
115
  exports.slidersMode = require_index$1.slidersMode;
116
+ exports.suggestAccessibleColor = require_contrast.suggestAccessibleColor;
104
117
  exports.toFormat = require_serialize.toFormat;
105
118
  exports.toHex = require_serialize.toHex;
106
119
  exports.toHexa = require_serialize.toHexa;
package/dist/index.d.cts CHANGED
@@ -1,10 +1,12 @@
1
- import { ColorChangeResult, ColorFormat, Hsl, Hsla, Hsva, Hwb, Hwba, Rgb, Rgba } from "./color/types.cjs";
2
- import { ContrastOptions, ContrastReport, TextSize, WcagLevel, apcaContrast, contrastRatio, contrastReport, meetsContrast, meetsNonTextContrast, readableTextColor, relativeLuminance, wcagLevel } from "./a11y/contrast.cjs";
1
+ import { ColorChangeMeta, ColorChangeResult, ColorChangeSource, ColorFormat, ColorValue, Hsl, Hsla, Hsva, Hwb, Hwba, ModernColorSpace, Rgb, Rgba, SerializeColorOptions } from "./color/types.cjs";
3
2
  import { clamp, hslaToHsva, hsvaToHsl, hsvaToHsla, hsvaToHwba, hsvaToRgb, hsvaToRgba, hwbaToHsva, normalizeHue, rgbaToHsva, roundRgba } from "./color/convert.cjs";
4
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";
5
6
  import { toFormat, toHex, toHexa, toHslString, toHslaString, toResult, toRgbString, toRgbaString } from "./color/serialize.cjs";
6
7
  import { ingest, sameHsva, sameRendered } from "./color/sticky.cjs";
7
8
  import { ColorListener, ColorStore, Unsubscribe, createColorStore } from "./core/store.cjs";
9
+ import { AccessibleColorSuggestion, ContrastOptions, ContrastReport, TextSize, WcagLevel, apcaContrast, compositeColor, contrastRatio, contrastRatioWithAlpha, contrastReport, meetsContrast, meetsNonTextContrast, readableTextColor, relativeLuminance, suggestAccessibleColor, wcagLevel } from "./a11y/contrast.cjs";
8
10
  import { QuantizedSwatch, quantize } from "./image/mmcq.cjs";
9
11
  import { ExtractOptions, ExtractResult, extractPalette } from "./image/extract.cjs";
10
12
  import { ModeId, PickerMode, getMode, registerMode, resolveModes } from "./modes/registry.cjs";
@@ -33,4 +35,4 @@ import { PENCIL_COLUMNS, defaultPalettes, defaultPencils } from "./data/defaults
33
35
  import { useColorValue, useTransientColor } from "./core/useColorStore.cjs";
34
36
  import { useAxisKeyboard, usePointerDrag } from "./core/usePointerDrag.cjs";
35
37
  import { injectStyles, setStyleNonce } from "./core/styleInjector.cjs";
36
- export { ChannelSlider, type ChannelSliderProps, type ChromaClassNames, ChromaPanel, type ChromaPanelProps, ColorArea, type ColorAreaProps, ColorChangeResult, ColorDisc, type ColorDiscProps, ColorField, type ColorFieldProps, ColorFormat, ColorInput, type ColorInputProps, type ColorListener, type ColorPalette, type ColorStore, type ContrastOptions, type ContrastReport, type ExtractOptions, type ExtractResult, type Eyedropper, Hsl, Hsla, Hsva, Hwb, Hwba, Icon, type IconName, type IconProps, ImagePanel, type ModeId, ModeToolbar, type ModeToolbarProps, NumberField, type NumberFieldProps, PENCIL_COLUMNS, PalettesPanel, PanelFooter, type PanelOptions, PencilsPanel, type PickerMode, Popover, type PopoverProps, type QuantizedSwatch, Rgb, Rgba, SegmentedControl, type SegmentedControlProps, type SegmentedItem, SlidersPanel, Swatch, type SwatchEntry, SwatchGrid, type SwatchGridProps, type SwatchProps, type SwatchVariant, type TextSize, type Unsubscribe, type WcagLevel, WheelPanel, apcaContrast, clamp, clearNamedColors, contrastRatio, contrastReport, createColorStore, defaultPalettes, defaultPencils, extractPalette, getMode, hslaToHsva, hsvaToHsl, hsvaToHsla, hsvaToHwba, hsvaToRgb, hsvaToRgba, hwbaToHsva, imageMode, ingest, injectStyles, isValidColor, meetsContrast, meetsNonTextContrast, normalizeHue, palettesMode, parse, pencilsMode, pushRecent, quantize, readableTextColor, registerMode, registerNamedColors, relativeLuminance, resolveModes, rgbaToHsva, roundRgba, sameHsva, sameRendered, setStyleNonce, slidersMode, toFormat, toHex, toHexa, toHslString, toHslaString, toResult, toRgbString, toRgbaString, useAxisKeyboard, useColorValue, useEyedropper, usePanel, usePointerDrag, useTransientColor, wcagLevel, wheelMode };
38
+ export { type AccessibleColorSuggestion, ChannelSlider, type ChannelSliderProps, type ChromaClassNames, ChromaPanel, type ChromaPanelProps, ColorArea, type ColorAreaProps, ColorChangeMeta, ColorChangeResult, ColorChangeSource, ColorDisc, type ColorDiscProps, ColorField, type ColorFieldProps, ColorFormat, ColorInput, type ColorInputProps, type ColorListener, type ColorPalette, type ColorStore, ColorValue, type ContrastOptions, type ContrastReport, type ExtractOptions, type ExtractResult, type Eyedropper, Hsl, Hsla, Hsva, Hwb, Hwba, Icon, type IconName, type IconProps, ImagePanel, type ModeId, ModeToolbar, type ModeToolbarProps, ModernColorSpace, NumberField, type NumberFieldProps, PENCIL_COLUMNS, PalettesPanel, PanelFooter, type PanelOptions, PencilsPanel, type PickerMode, Popover, type PopoverProps, type QuantizedSwatch, Rgb, Rgba, SegmentedControl, type SegmentedControlProps, type SegmentedItem, SerializeColorOptions, SlidersPanel, Swatch, type SwatchEntry, SwatchGrid, type SwatchGridProps, type SwatchProps, type SwatchVariant, type TextSize, type Unsubscribe, type WcagLevel, WheelPanel, apcaContrast, clamp, clearNamedColors, colorValueToHsva, compositeColor, contrastRatio, contrastRatioWithAlpha, contrastReport, convertColor, createColorStore, defaultPalettes, defaultPencils, extractPalette, getMode, hslaToHsva, hsvaToColorValue, hsvaToHsl, hsvaToHsla, hsvaToHwba, hsvaToRgb, hsvaToRgba, hwbaToHsva, imageMode, ingest, injectStyles, isInGamut, isValidColor, mapToGamut, meetsContrast, meetsNonTextContrast, normalizeHue, palettesMode, parse, parseColor, parseCss4Color, pencilsMode, pushRecent, quantize, readableTextColor, registerMode, registerNamedColors, relativeLuminance, resolveModes, rgbaToHsva, roundRgba, sameHsva, sameRendered, serializeColor, setStyleNonce, slidersMode, suggestAccessibleColor, toFormat, toHex, toHexa, toHslString, toHslaString, toResult, toRgbString, toRgbaString, useAxisKeyboard, useColorValue, useEyedropper, usePanel, usePointerDrag, useTransientColor, wcagLevel, wheelMode };
package/dist/index.d.ts CHANGED
@@ -1,10 +1,12 @@
1
- import { ColorChangeResult, ColorFormat, Hsl, Hsla, Hsva, Hwb, Hwba, Rgb, Rgba } from "./color/types.js";
2
- import { ContrastOptions, ContrastReport, TextSize, WcagLevel, apcaContrast, contrastRatio, contrastReport, meetsContrast, meetsNonTextContrast, readableTextColor, relativeLuminance, wcagLevel } from "./a11y/contrast.js";
1
+ import { ColorChangeMeta, ColorChangeResult, ColorChangeSource, ColorFormat, ColorValue, Hsl, Hsla, Hsva, Hwb, Hwba, ModernColorSpace, Rgb, Rgba, SerializeColorOptions } from "./color/types.js";
3
2
  import { clamp, hslaToHsva, hsvaToHsl, hsvaToHsla, hsvaToHwba, hsvaToRgb, hsvaToRgba, hwbaToHsva, normalizeHue, rgbaToHsva, roundRgba } from "./color/convert.js";
4
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";
5
6
  import { toFormat, toHex, toHexa, toHslString, toHslaString, toResult, toRgbString, toRgbaString } from "./color/serialize.js";
6
7
  import { ingest, sameHsva, sameRendered } from "./color/sticky.js";
7
8
  import { ColorListener, ColorStore, Unsubscribe, createColorStore } from "./core/store.js";
9
+ import { AccessibleColorSuggestion, ContrastOptions, ContrastReport, TextSize, WcagLevel, apcaContrast, compositeColor, contrastRatio, contrastRatioWithAlpha, contrastReport, meetsContrast, meetsNonTextContrast, readableTextColor, relativeLuminance, suggestAccessibleColor, wcagLevel } from "./a11y/contrast.js";
8
10
  import { QuantizedSwatch, quantize } from "./image/mmcq.js";
9
11
  import { ExtractOptions, ExtractResult, extractPalette } from "./image/extract.js";
10
12
  import { ModeId, PickerMode, getMode, registerMode, resolveModes } from "./modes/registry.js";
@@ -33,4 +35,4 @@ import { PENCIL_COLUMNS, defaultPalettes, defaultPencils } from "./data/defaults
33
35
  import { useColorValue, useTransientColor } from "./core/useColorStore.js";
34
36
  import { useAxisKeyboard, usePointerDrag } from "./core/usePointerDrag.js";
35
37
  import { injectStyles, setStyleNonce } from "./core/styleInjector.js";
36
- export { ChannelSlider, type ChannelSliderProps, type ChromaClassNames, ChromaPanel, type ChromaPanelProps, ColorArea, type ColorAreaProps, ColorChangeResult, ColorDisc, type ColorDiscProps, ColorField, type ColorFieldProps, ColorFormat, ColorInput, type ColorInputProps, type ColorListener, type ColorPalette, type ColorStore, type ContrastOptions, type ContrastReport, type ExtractOptions, type ExtractResult, type Eyedropper, Hsl, Hsla, Hsva, Hwb, Hwba, Icon, type IconName, type IconProps, ImagePanel, type ModeId, ModeToolbar, type ModeToolbarProps, NumberField, type NumberFieldProps, PENCIL_COLUMNS, PalettesPanel, PanelFooter, type PanelOptions, PencilsPanel, type PickerMode, Popover, type PopoverProps, type QuantizedSwatch, Rgb, Rgba, SegmentedControl, type SegmentedControlProps, type SegmentedItem, SlidersPanel, Swatch, type SwatchEntry, SwatchGrid, type SwatchGridProps, type SwatchProps, type SwatchVariant, type TextSize, type Unsubscribe, type WcagLevel, WheelPanel, apcaContrast, clamp, clearNamedColors, contrastRatio, contrastReport, createColorStore, defaultPalettes, defaultPencils, extractPalette, getMode, hslaToHsva, hsvaToHsl, hsvaToHsla, hsvaToHwba, hsvaToRgb, hsvaToRgba, hwbaToHsva, imageMode, ingest, injectStyles, isValidColor, meetsContrast, meetsNonTextContrast, normalizeHue, palettesMode, parse, pencilsMode, pushRecent, quantize, readableTextColor, registerMode, registerNamedColors, relativeLuminance, resolveModes, rgbaToHsva, roundRgba, sameHsva, sameRendered, setStyleNonce, slidersMode, toFormat, toHex, toHexa, toHslString, toHslaString, toResult, toRgbString, toRgbaString, useAxisKeyboard, useColorValue, useEyedropper, usePanel, usePointerDrag, useTransientColor, wcagLevel, wheelMode };
38
+ export { type AccessibleColorSuggestion, ChannelSlider, type ChannelSliderProps, type ChromaClassNames, ChromaPanel, type ChromaPanelProps, ColorArea, type ColorAreaProps, ColorChangeMeta, ColorChangeResult, ColorChangeSource, ColorDisc, type ColorDiscProps, ColorField, type ColorFieldProps, ColorFormat, ColorInput, type ColorInputProps, type ColorListener, type ColorPalette, type ColorStore, ColorValue, type ContrastOptions, type ContrastReport, type ExtractOptions, type ExtractResult, type Eyedropper, Hsl, Hsla, Hsva, Hwb, Hwba, Icon, type IconName, type IconProps, ImagePanel, type ModeId, ModeToolbar, type ModeToolbarProps, ModernColorSpace, NumberField, type NumberFieldProps, PENCIL_COLUMNS, PalettesPanel, PanelFooter, type PanelOptions, PencilsPanel, type PickerMode, Popover, type PopoverProps, type QuantizedSwatch, Rgb, Rgba, SegmentedControl, type SegmentedControlProps, type SegmentedItem, SerializeColorOptions, SlidersPanel, Swatch, type SwatchEntry, SwatchGrid, type SwatchGridProps, type SwatchProps, type SwatchVariant, type TextSize, type Unsubscribe, type WcagLevel, WheelPanel, apcaContrast, clamp, clearNamedColors, colorValueToHsva, compositeColor, contrastRatio, contrastRatioWithAlpha, contrastReport, convertColor, createColorStore, defaultPalettes, defaultPencils, extractPalette, getMode, hslaToHsva, hsvaToColorValue, hsvaToHsl, hsvaToHsla, hsvaToHwba, hsvaToRgb, hsvaToRgba, hwbaToHsva, imageMode, ingest, injectStyles, isInGamut, isValidColor, mapToGamut, meetsContrast, meetsNonTextContrast, normalizeHue, palettesMode, parse, parseColor, parseCss4Color, pencilsMode, pushRecent, quantize, readableTextColor, registerMode, registerNamedColors, relativeLuminance, resolveModes, rgbaToHsva, roundRgba, sameHsva, sameRendered, serializeColor, setStyleNonce, slidersMode, suggestAccessibleColor, toFormat, toHex, toHexa, toHslString, toHslaString, toResult, toRgbString, toRgbaString, useAxisKeyboard, useColorValue, useEyedropper, usePanel, usePointerDrag, useTransientColor, wcagLevel, wheelMode };
package/dist/index.js CHANGED
@@ -6,6 +6,7 @@ import { useColorValue, useTransientColor } from "./core/useColorStore.js";
6
6
  import { useAxisKeyboard, usePointerDrag } from "./core/usePointerDrag.js";
7
7
  import { ChannelSlider } from "./primitives/ChannelSlider.js";
8
8
  import { ColorDisc } from "./primitives/ColorDisc.js";
9
+ import { colorValueToHsva, convertColor, hsvaToColorValue, isInGamut, mapToGamut, parseCss4Color, serializeColor } from "./color/css4.js";
9
10
  import { clearNamedColors, isValidColor, parse, registerNamedColors } from "./color/parse.js";
10
11
  import { toFormat, toHex, toHexa, toHslString, toHslaString, toResult, toRgbString, toRgbaString } from "./color/serialize.js";
11
12
  import { ColorField } from "./primitives/ColorField.js";
@@ -32,7 +33,8 @@ import { Popover } from "./components/Popover.js";
32
33
  import { ColorInput } from "./components/ColorInput.js";
33
34
  import { ColorArea } from "./primitives/ColorArea.js";
34
35
  import { Swatch } from "./primitives/Swatch.js";
35
- import { apcaContrast, contrastRatio, contrastReport, meetsContrast, meetsNonTextContrast, readableTextColor, relativeLuminance, wcagLevel } from "./a11y/contrast.js";
36
+ import { parseColor } from "./color/value.js";
37
+ import { apcaContrast, compositeColor, contrastRatio, contrastRatioWithAlpha, contrastReport, meetsContrast, meetsNonTextContrast, readableTextColor, relativeLuminance, suggestAccessibleColor, wcagLevel } from "./a11y/contrast.js";
36
38
  //#region src/index.ts
37
39
  registerMode(wheelMode);
38
40
  registerMode(slidersMode);
@@ -40,5 +42,5 @@ registerMode(palettesMode);
40
42
  registerMode(imageMode);
41
43
  registerMode(pencilsMode);
42
44
  //#endregion
43
- export { ChannelSlider, ChromaPanel, ColorArea, ColorDisc, ColorField, ColorInput, Icon, ImagePanel, ModeToolbar, NumberField, PENCIL_COLUMNS, PalettesPanel, PanelFooter, PencilsPanel, Popover, SegmentedControl, SlidersPanel, Swatch, SwatchGrid, WheelPanel, apcaContrast, clamp, clearNamedColors, contrastRatio, contrastReport, createColorStore, defaultPalettes, defaultPencils, extractPalette, getMode, hslaToHsva, hsvaToHsl, hsvaToHsla, hsvaToHwba, hsvaToRgb, hsvaToRgba, hwbaToHsva, imageMode, ingest, injectStyles, isValidColor, meetsContrast, meetsNonTextContrast, normalizeHue, palettesMode, parse, pencilsMode, pushRecent, quantize, readableTextColor, registerMode, registerNamedColors, relativeLuminance, resolveModes, rgbaToHsva, roundRgba, sameHsva, sameRendered, setStyleNonce, slidersMode, toFormat, toHex, toHexa, toHslString, toHslaString, toResult, toRgbString, toRgbaString, useAxisKeyboard, useColorValue, useEyedropper, usePanel, usePointerDrag, useTransientColor, wcagLevel, wheelMode };
45
+ export { ChannelSlider, ChromaPanel, ColorArea, ColorDisc, ColorField, ColorInput, Icon, ImagePanel, ModeToolbar, NumberField, PENCIL_COLUMNS, PalettesPanel, PanelFooter, PencilsPanel, Popover, SegmentedControl, SlidersPanel, Swatch, SwatchGrid, WheelPanel, apcaContrast, clamp, clearNamedColors, colorValueToHsva, compositeColor, contrastRatio, contrastRatioWithAlpha, contrastReport, convertColor, createColorStore, defaultPalettes, defaultPencils, extractPalette, getMode, hslaToHsva, hsvaToColorValue, hsvaToHsl, hsvaToHsla, hsvaToHwba, hsvaToRgb, hsvaToRgba, hwbaToHsva, imageMode, ingest, injectStyles, isInGamut, isValidColor, mapToGamut, meetsContrast, meetsNonTextContrast, normalizeHue, palettesMode, parse, parseColor, parseCss4Color, pencilsMode, pushRecent, quantize, readableTextColor, registerMode, registerNamedColors, relativeLuminance, resolveModes, rgbaToHsva, roundRgba, sameHsva, sameRendered, serializeColor, setStyleNonce, slidersMode, suggestAccessibleColor, toFormat, toHex, toHexa, toHslString, toHslaString, toResult, toRgbString, toRgbaString, useAxisKeyboard, useColorValue, useEyedropper, usePanel, usePointerDrag, useTransientColor, wcagLevel, wheelMode };
44
46
 
@@ -1,6 +1,8 @@
1
1
  "use client";
2
2
  const require_runtime = require("../../_virtual/_rolldown/runtime.cjs");
3
3
  const require_context = require("../../core/context.cjs");
4
+ const require_convert = require("../../color/convert.cjs");
5
+ const require_parse = require("../../color/parse.cjs");
4
6
  const require_icons = require("../../primitives/icons.cjs");
5
7
  const require_SwatchGrid = require("../../primitives/SwatchGrid.cjs");
6
8
  const require_extract = require("../../image/extract.cjs");
@@ -16,7 +18,7 @@ const EMPTY = {
16
18
  preview: null
17
19
  };
18
20
  function ImagePanel(props) {
19
- const { extractOptions } = props;
21
+ const { extractOptions, sortBy = "population" } = props;
20
22
  const { idPrefix, disabled } = require_context.usePanel();
21
23
  const inputId = `${idPrefix}-image-file`;
22
24
  const { store } = require_context.usePanel();
@@ -25,7 +27,13 @@ function ImagePanel(props) {
25
27
  const [message, setMessage] = react.useState(restored.message);
26
28
  const [swatches, setSwatches] = react.useState(restored.swatches);
27
29
  const [preview, setPreview] = react.useState(restored.preview);
30
+ const [point, setPoint] = react.useState(null);
28
31
  const inputRef = react.useRef(null);
32
+ const imageRef = react.useRef(null);
33
+ const [clipboardRead, setClipboardRead] = react.useState(false);
34
+ react.useEffect(() => {
35
+ setClipboardRead(typeof navigator.clipboard?.read === "function");
36
+ }, []);
29
37
  react.useEffect(() => {
30
38
  if (preview === null && status === "idle") kept.delete(store);
31
39
  else kept.set(store, {
@@ -84,6 +92,50 @@ function ImagePanel(props) {
84
92
  const file = files?.[0];
85
93
  if (file !== void 0) run(file);
86
94
  };
95
+ const pasteFile = (items) => {
96
+ for (const item of Array.from(items)) {
97
+ if (item.kind !== "file" || !item.type.startsWith("image/")) continue;
98
+ const file = item.getAsFile();
99
+ if (file !== null) run(file);
100
+ return file !== null;
101
+ }
102
+ return false;
103
+ };
104
+ const readClipboard = async () => {
105
+ try {
106
+ const items = await navigator.clipboard.read();
107
+ for (const item of items) {
108
+ const type = item.types.find((candidate) => candidate.startsWith("image/"));
109
+ if (type === void 0) continue;
110
+ const blob = await item.getType(type);
111
+ await run(new File([blob], "clipboard-image", { type }));
112
+ return;
113
+ }
114
+ setStatus("error");
115
+ setMessage("The clipboard does not contain an image.");
116
+ } catch {
117
+ setStatus("error");
118
+ setMessage("Clipboard access was not allowed. Use paste or choose a file instead.");
119
+ }
120
+ };
121
+ const sortedSwatches = react.useMemo(() => [...swatches].sort((a, b) => {
122
+ if (sortBy === "population") return b.population - a.population;
123
+ if (sortBy === "luminance") {
124
+ const luminance = (rgb) => .2126 * rgb[0] + .7152 * rgb[1] + .0722 * rgb[2];
125
+ return luminance(b.rgb) - luminance(a.rgb);
126
+ }
127
+ return require_convert.rgbaToHsva({
128
+ r: a.rgb[0],
129
+ g: a.rgb[1],
130
+ b: a.rgb[2],
131
+ a: 1
132
+ }).h - require_convert.rgbaToHsva({
133
+ r: b.rgb[0],
134
+ g: b.rgb[1],
135
+ b: b.rgb[2],
136
+ a: 1
137
+ }).h;
138
+ }), [swatches, sortBy]);
87
139
  const clear = () => {
88
140
  controller.current?.abort();
89
141
  releasePreview();
@@ -91,8 +143,47 @@ function ImagePanel(props) {
91
143
  setSwatches([]);
92
144
  setMessage("");
93
145
  setStatus("idle");
146
+ setPoint(null);
94
147
  if (inputRef.current !== null) inputRef.current.value = "";
95
148
  };
149
+ const locate = (image, clientX, clientY) => {
150
+ const rect = image.getBoundingClientRect();
151
+ if (rect.width <= 0 || rect.height <= 0 || image.naturalWidth <= 0 || image.naturalHeight <= 0) return null;
152
+ const scale = Math.min(rect.width / image.naturalWidth, rect.height / image.naturalHeight);
153
+ const width = image.naturalWidth * scale;
154
+ const height = image.naturalHeight * scale;
155
+ const left = rect.left + (rect.width - width) / 2;
156
+ const top = rect.top + (rect.height - height) / 2;
157
+ if (clientX < left || clientX > left + width || clientY < top || clientY > top + height) return null;
158
+ return {
159
+ x: Math.min(1, Math.max(0, (clientX - left) / width)),
160
+ y: Math.min(1, Math.max(0, (clientY - top) / height)),
161
+ left: Math.min(1, Math.max(0, (clientX - rect.left) / rect.width)),
162
+ top: Math.min(1, Math.max(0, (clientY - rect.top) / rect.height))
163
+ };
164
+ };
165
+ const selectPixel = (next) => {
166
+ const image = imageRef.current;
167
+ if (image === null || image.naturalWidth <= 0 || image.naturalHeight <= 0) return;
168
+ const canvas = document.createElement("canvas");
169
+ canvas.width = 1;
170
+ canvas.height = 1;
171
+ const context = canvas.getContext("2d", { willReadFrequently: true });
172
+ if (context === null) return;
173
+ const sx = Math.min(image.naturalWidth - 1, Math.floor(next.x * image.naturalWidth));
174
+ const sy = Math.min(image.naturalHeight - 1, Math.floor(next.y * image.naturalHeight));
175
+ context.drawImage(image, sx, sy, 1, 1, 0, 0, 1, 1);
176
+ const [r = 0, g = 0, b = 0, a = 0] = context.getImageData(0, 0, 1, 1).data;
177
+ if (a === 0) {
178
+ setMessage("That pixel is transparent. Choose another point.");
179
+ return;
180
+ }
181
+ const parsed = require_parse.parse(`rgba(${r}, ${g}, ${b}, ${Math.round(a / 255 * 1e3) / 1e3})`);
182
+ if (parsed === null) return;
183
+ store.ingest(parsed, "image");
184
+ store.commit("image");
185
+ setMessage("Color selected from image.");
186
+ };
96
187
  const dragDepth = react.useRef(0);
97
188
  const [dragging, setDragging] = react.useState(false);
98
189
  const endDrag = () => {
@@ -101,6 +192,9 @@ function ImagePanel(props) {
101
192
  };
102
193
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
103
194
  className: "cp-panel",
195
+ onPaste: (event) => {
196
+ if (!disabled && pasteFile(event.clipboardData.items)) event.preventDefault();
197
+ },
104
198
  onDragEnter: (e) => {
105
199
  e.preventDefault();
106
200
  if (disabled) return;
@@ -145,18 +239,52 @@ function ImagePanel(props) {
145
239
  ]
146
240
  }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
147
241
  className: "cp-image-preview",
148
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
149
- src: preview,
150
- alt: ""
151
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
152
- type: "button",
153
- className: "cp-image-remove",
154
- "aria-label": "Remove image",
155
- title: "Remove image",
156
- disabled,
157
- onClick: clear,
158
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_icons.Icon, { name: "x" })
159
- })]
242
+ children: [
243
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
244
+ ref: imageRef,
245
+ src: preview,
246
+ alt: "Uploaded image. Move over it and click to select a color.",
247
+ role: "button",
248
+ tabIndex: disabled ? -1 : 0,
249
+ "aria-label": "Pick a color from the uploaded image",
250
+ onPointerMove: (event) => setPoint(locate(event.currentTarget, event.clientX, event.clientY)),
251
+ onPointerLeave: () => setPoint(null),
252
+ onClick: (event) => {
253
+ if (disabled) return;
254
+ const next = locate(event.currentTarget, event.clientX, event.clientY);
255
+ if (next !== null) selectPixel(next);
256
+ },
257
+ onKeyDown: (event) => {
258
+ if (disabled || event.key !== "Enter" && event.key !== " ") return;
259
+ event.preventDefault();
260
+ selectPixel(point ?? {
261
+ x: .5,
262
+ y: .5,
263
+ left: .5,
264
+ top: .5
265
+ });
266
+ }
267
+ }),
268
+ point !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
269
+ className: "cp-image-loupe",
270
+ "aria-hidden": "true",
271
+ style: {
272
+ left: `${point.left * 100}%`,
273
+ top: `${point.top * 100}%`,
274
+ backgroundImage: `url("${preview}")`,
275
+ backgroundPosition: `${point.x * 100}% ${point.y * 100}%`
276
+ }
277
+ }),
278
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
279
+ type: "button",
280
+ className: "cp-image-remove",
281
+ "aria-label": "Remove image",
282
+ title: "Remove image",
283
+ disabled,
284
+ onClick: clear,
285
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_icons.Icon, { name: "x" })
286
+ })
287
+ ]
160
288
  }),
161
289
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
162
290
  ref: inputRef,
@@ -169,12 +297,25 @@ function ImagePanel(props) {
169
297
  tabIndex: preview === null ? void 0 : -1,
170
298
  onChange: (e) => onFiles(e.currentTarget.files)
171
299
  }),
300
+ preview === null && clipboardRead && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
301
+ type: "button",
302
+ className: "cp-button",
303
+ disabled,
304
+ onClick: () => {
305
+ readClipboard();
306
+ },
307
+ children: "Paste image"
308
+ }),
172
309
  preview !== null && status === "error" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
173
310
  className: "cp-empty",
174
311
  children: message
175
312
  }),
313
+ preview !== null && status !== "error" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
314
+ className: "cp-image-hint",
315
+ children: "Click the image to pick an exact color."
316
+ }),
176
317
  status === "ready" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_SwatchGrid.SwatchGrid, {
177
- swatches: swatches.map((s) => ({
318
+ swatches: sortedSwatches.map((s) => ({
178
319
  color: s.hex,
179
320
  name: s.hex
180
321
  })),
@@ -4,6 +4,7 @@ import * as React from "react";
4
4
  //#region src/modes/image/index.d.ts
5
5
  export interface ImagePanelProps {
6
6
  extractOptions?: ExtractOptions;
7
+ sortBy?: "population" | "luminance" | "hue";
7
8
  }
8
9
  export declare function ImagePanel(props: ImagePanelProps): React.ReactElement;
9
10
  export declare const imageMode: PickerMode;
@@ -4,6 +4,7 @@ import * as React from "react";
4
4
  //#region src/modes/image/index.d.ts
5
5
  export interface ImagePanelProps {
6
6
  extractOptions?: ExtractOptions;
7
+ sortBy?: "population" | "luminance" | "hue";
7
8
  }
8
9
  export declare function ImagePanel(props: ImagePanelProps): React.ReactElement;
9
10
  export declare const imageMode: PickerMode;