@fusion.dev/helix-headless 0.57.0 → 0.58.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.
@@ -18896,169 +18896,11 @@
18896
18896
  }
18897
18897
  ]
18898
18898
  },
18899
- {
18900
- "kind": "javascript-module",
18901
- "path": "src/color-picker/color.ts",
18902
- "declarations": [
18903
- {
18904
- "kind": "function",
18905
- "name": "toRgb",
18906
- "return": {
18907
- "type": {
18908
- "text": "{ blue: number; green: number; red: number }"
18909
- }
18910
- },
18911
- "parameters": [
18912
- {
18913
- "name": "color",
18914
- "type": {
18915
- "text": "Color"
18916
- }
18917
- }
18918
- ],
18919
- "description": "HSB to bytes, which is the road every written syntax goes down."
18920
- },
18921
- {
18922
- "kind": "function",
18923
- "name": "splitColors",
18924
- "return": {
18925
- "type": {
18926
- "text": "string[]"
18927
- }
18928
- },
18929
- "parameters": [
18930
- {
18931
- "name": "value",
18932
- "type": {
18933
- "text": "string"
18934
- }
18935
- }
18936
- ],
18937
- "description": "Splits a comma-separated list of colors, ignoring the commas inside them.\n\nA plain `split(',')` cannot hold the legacy syntaxes:\n`#ef4444, rgb(249 115 22), rgba(34,197,94,.5)` comes apart into six fragments,\nfour of them not colors, and the row quietly renders three swatches instead of\nfour. The modern space-separated forms survive it, which is what makes such a\nlist look like it works.\n\nDepth rather than a regular expression, because the thing that matters is\nwhether a comma is inside a function call and that is exactly what a counter\nanswers. No color syntax nests, so a single counter is enough."
18938
- },
18939
- {
18940
- "kind": "function",
18941
- "name": "parseColor",
18942
- "return": {
18943
- "type": {
18944
- "text": "Color | null"
18945
- }
18946
- },
18947
- "parameters": [
18948
- {
18949
- "name": "value",
18950
- "type": {
18951
- "text": "string"
18952
- }
18953
- }
18954
- ],
18955
- "description": "Any of the syntaxes to the working model, or `null` where it is not a color.\n\nNamed colors are not here. Resolving `rebeccapurple` means either shipping\nthe table of a hundred and forty or asking the browser, and the browser is not\npresent on the server: a field that accepted a name client-side and rejected\nit during a server render would be worse than one that never took it."
18956
- },
18957
- {
18958
- "kind": "function",
18959
- "name": "formatColor",
18960
- "return": {
18961
- "type": {
18962
- "text": "string"
18963
- }
18964
- },
18965
- "parameters": [
18966
- {
18967
- "name": "color",
18968
- "type": {
18969
- "text": "Color"
18970
- }
18971
- },
18972
- {
18973
- "name": "format",
18974
- "type": {
18975
- "text": "ColorFormat"
18976
- }
18977
- },
18978
- {
18979
- "name": "withAlpha",
18980
- "type": {
18981
- "text": "boolean"
18982
- }
18983
- }
18984
- ],
18985
- "description": "The working model back out, in the syntax asked for.\n\nThe alpha is written only where the component carries one. A consumer who did\nnot ask for transparency should not have to strip `ff` off every hex or read\npast a fourth argument they never wanted."
18986
- },
18987
- {
18988
- "kind": "function",
18989
- "name": "toCss",
18990
- "return": {
18991
- "type": {
18992
- "text": "string"
18993
- }
18994
- },
18995
- "parameters": [
18996
- {
18997
- "name": "color",
18998
- "type": {
18999
- "text": "Color"
19000
- }
19001
- }
19002
- ],
19003
- "description": "The color as CSS, for the swatches and the tracks. Always with its alpha."
19004
- }
19005
- ],
19006
- "exports": [
19007
- {
19008
- "kind": "js",
19009
- "name": "toRgb",
19010
- "declaration": {
19011
- "name": "toRgb",
19012
- "module": "src/color-picker/color.ts"
19013
- }
19014
- },
19015
- {
19016
- "kind": "js",
19017
- "name": "splitColors",
19018
- "declaration": {
19019
- "name": "splitColors",
19020
- "module": "src/color-picker/color.ts"
19021
- }
19022
- },
19023
- {
19024
- "kind": "js",
19025
- "name": "parseColor",
19026
- "declaration": {
19027
- "name": "parseColor",
19028
- "module": "src/color-picker/color.ts"
19029
- }
19030
- },
19031
- {
19032
- "kind": "js",
19033
- "name": "formatColor",
19034
- "declaration": {
19035
- "name": "formatColor",
19036
- "module": "src/color-picker/color.ts"
19037
- }
19038
- },
19039
- {
19040
- "kind": "js",
19041
- "name": "toCss",
19042
- "declaration": {
19043
- "name": "toCss",
19044
- "module": "src/color-picker/color.ts"
19045
- }
19046
- }
19047
- ]
19048
- },
19049
18899
  {
19050
18900
  "kind": "javascript-module",
19051
18901
  "path": "src/color-picker/index.ts",
19052
18902
  "declarations": [],
19053
18903
  "exports": [
19054
- {
19055
- "kind": "js",
19056
- "name": "*",
19057
- "declaration": {
19058
- "name": "*",
19059
- "module": "src/color-picker/color.js"
19060
- }
19061
- },
19062
18904
  {
19063
18905
  "kind": "js",
19064
18906
  "name": "*",
@@ -1,7 +1,6 @@
1
- import { AriaForwardController, HelixFormElement } from '@fusion.dev/helix-core';
1
+ import { AriaForwardController, type ColorFormat, HelixFormElement } from '@fusion.dev/helix-core';
2
2
  import { type PropertyValues, type TemplateResult } from 'lit';
3
3
  import '../../color-picker/index.js';
4
- import { type ColorFormat } from '../../color-picker/color.js';
5
4
  declare const HelixHeadlessColorInput_base: (new (...args: any[]) => import("@fusion.dev/helix-core").MixinPickerFieldInterface) & (new (...args: any[]) => import("@fusion.dev/helix-core").MixinControllerOpenInterface) & (new (...args: any[]) => import("@fusion.dev/helix-core").MixinControllerValueInterface) & (new (...args: any[]) => import("@fusion.dev/helix-core").MixinFieldInterface) & typeof HelixFormElement;
6
5
  /**
7
6
  * @summary A color, typed in any syntax or picked from a panel.
@@ -4,12 +4,11 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
4
4
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
- import { AriaForwardController, customElement, HelixFormElement, MixinControllerOpen, MixinControllerValue, MixinField, MixinPickerField, } from '@fusion.dev/helix-core';
7
+ import { AriaForwardController, customElement, HelixFormElement, MixinControllerOpen, MixinControllerValue, MixinField, MixinPickerField, parseColor, toCss, } from '@fusion.dev/helix-core';
8
8
  import { nothing } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
10
  import { html } from 'lit/static-html.js';
11
11
  import '../../color-picker/index.js';
12
- import { parseColor, toCss } from '../../color-picker/color.js';
13
12
  let HelixHeadlessColorInput = class HelixHeadlessColorInput extends MixinPickerField(MixinControllerOpen(MixinControllerValue(MixinField(HelixFormElement)))) {
14
13
  constructor() {
15
14
  super(...arguments);
@@ -1,2 +1 @@
1
- export * from './color.js';
2
1
  export * from './root/color-picker-root.js';
@@ -1,2 +1 @@
1
- export * from './color.js';
2
1
  export * from './root/color-picker-root.js';
@@ -1,9 +1,8 @@
1
- import { HelixFormElement } from '@fusion.dev/helix-core';
1
+ import { type Color, type ColorFormat, HelixFormElement } from '@fusion.dev/helix-core';
2
2
  import { type PropertyValues, type TemplateResult } from 'lit';
3
3
  import '../../button/index.js';
4
4
  import '../../button-group/index.js';
5
5
  import '../../slider/index.js';
6
- import { type Color, type ColorFormat } from '../color.js';
7
6
  declare const HelixHeadlessColorPicker_base: (new (...args: any[]) => import("@fusion.dev/helix-core").MixinControllerValueInterface) & typeof HelixFormElement;
8
7
  /**
9
8
  * @summary Picks a color, from a square and two ramps.
@@ -4,7 +4,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
4
4
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
- import { CopyController, capturePointer, customElement, drawIcon, HelixFormElement, HelixSampleEndEvent, HelixSampleStartEvent, MixinControllerValue, } from '@fusion.dev/helix-core';
7
+ import { CopyController, capturePointer, customElement, drawIcon, formatColor, HelixFormElement, HelixSampleEndEvent, HelixSampleStartEvent, MixinControllerValue, parseColor, splitColors, toCss, } from '@fusion.dev/helix-core';
8
8
  import { eyedropper } from '@fusion.dev/helix-icons';
9
9
  import { nothing } from 'lit';
10
10
  import { property, state } from 'lit/decorators.js';
@@ -12,7 +12,6 @@ import { html } from 'lit/static-html.js';
12
12
  import '../../button/index.js';
13
13
  import '../../button-group/index.js';
14
14
  import '../../slider/index.js';
15
- import { formatColor, parseColor, splitColors, toCss, } from '../color.js';
16
15
  const FORMATS = ['hex', 'rgb', 'hsl'];
17
16
  const START = { alpha: 100, brightness: 100, hue: 0, saturation: 100 };
18
17
  let HelixHeadlessColorPicker = class HelixHeadlessColorPicker extends MixinControllerValue(HelixFormElement) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@fusion.dev/helix-headless",
3
3
  "description": "Headless component tier. Structure, accessibility, state, and form participation. No styling.",
4
- "version": "0.57.0",
4
+ "version": "0.58.0",
5
5
  "license": "MIT",
6
6
  "author": "Ross Sheppard <ross.sheppard@fusion.dev>",
7
7
  "repository": {
@@ -46,14 +46,14 @@
46
46
  },
47
47
  "bugs": "https://github.com/project-fusion/fusion/issues",
48
48
  "dependencies": {
49
- "@fusion.dev/helix-core": "0.57.0",
50
- "@fusion.dev/helix-icons": "0.57.0",
49
+ "@fusion.dev/helix-core": "0.58.0",
50
+ "@fusion.dev/helix-icons": "0.58.0",
51
51
  "uqr": "^0.1.3"
52
52
  },
53
53
  "devDependencies": {
54
54
  "@custom-elements-manifest/analyzer": "^0.11.0",
55
- "@fusion.dev/helix-cem": "0.57.0",
56
- "@fusion.dev/helix-testing": "0.57.0",
55
+ "@fusion.dev/helix-cem": "0.58.0",
56
+ "@fusion.dev/helix-testing": "0.58.0",
57
57
  "@fusion.dev/scripts": "0.0.0-dev",
58
58
  "@happy-dom/global-registrator": "^20.0.0",
59
59
  "@lit-labs/ssr": "^4.1.0",
@@ -1,73 +0,0 @@
1
- /**
2
- * Reading and writing the color syntaxes a field is allowed to hold.
3
- *
4
- * **The working model is HSB**, hue and saturation and brightness, because that
5
- * is what the panel is: a square of saturation against brightness with a hue
6
- * ramp beside it. HSL would put the square's corners somewhere a reader does not
7
- * expect, since full saturation in HSL is a mid-lightness band rather than a
8
- * corner.
9
- *
10
- * **Every syntax here is read; three are written.** A field that refuses
11
- * `rgb(255 0 0)` because it prefers hex is a field arguing with the person using
12
- * it, so the space-separated forms, the comma forms, `rgba` and `hsla` are all
13
- * taken. What is written back is whichever of hex, rgb or hsl the component is
14
- * set to, so a value stays in the shape the consumer asked for however it was
15
- * entered.
16
- *
17
- * **What is written is the comma form.** Both are valid, and the commas are what
18
- * a reader recognizes, what most code around them holds, and what pastes into
19
- * anything without a second thought. A syntax a value is handed *out* in is not
20
- * the place to be early. `rgba` and `hsla` come with them, since a comma form
21
- * with a fourth argument is spelled that way.
22
- *
23
- * Its own module rather than sitting in the element, because it is arithmetic
24
- * with no DOM in it: this is the half worth testing directly.
25
- */
26
- /** A color as the panel holds it. Alpha and the two percentages are 0 to 100. */
27
- export interface Color {
28
- alpha: number;
29
- brightness: number;
30
- hue: number;
31
- saturation: number;
32
- }
33
- /** The syntaxes a value can be written in. */
34
- export type ColorFormat = 'hex' | 'hsl' | 'rgb';
35
- /** HSB to bytes, which is the road every written syntax goes down. */
36
- export declare const toRgb: (color: Color) => {
37
- blue: number;
38
- green: number;
39
- red: number;
40
- };
41
- /**
42
- * Splits a comma-separated list of colors, ignoring the commas inside them.
43
- *
44
- * A plain `split(',')` cannot hold the legacy syntaxes:
45
- * `#ef4444, rgb(249 115 22), rgba(34,197,94,.5)` comes apart into six fragments,
46
- * four of them not colors, and the row quietly renders three swatches instead of
47
- * four. The modern space-separated forms survive it, which is what makes such a
48
- * list look like it works.
49
- *
50
- * Depth rather than a regular expression, because the thing that matters is
51
- * whether a comma is inside a function call and that is exactly what a counter
52
- * answers. No color syntax nests, so a single counter is enough.
53
- */
54
- export declare const splitColors: (value: string) => string[];
55
- /**
56
- * Any of the syntaxes to the working model, or `null` where it is not a color.
57
- *
58
- * Named colors are not here. Resolving `rebeccapurple` means either shipping
59
- * the table of a hundred and forty or asking the browser, and the browser is not
60
- * present on the server: a field that accepted a name client-side and rejected
61
- * it during a server render would be worse than one that never took it.
62
- */
63
- export declare const parseColor: (value: string) => Color | null;
64
- /**
65
- * The working model back out, in the syntax asked for.
66
- *
67
- * The alpha is written only where the component carries one. A consumer who did
68
- * not ask for transparency should not have to strip `ff` off every hex or read
69
- * past a fourth argument they never wanted.
70
- */
71
- export declare const formatColor: (color: Color, format: ColorFormat, withAlpha: boolean) => string;
72
- /** The color as CSS, for the swatches and the tracks. Always with its alpha. */
73
- export declare const toCss: (color: Color) => string;
@@ -1,168 +0,0 @@
1
- const clamp = (value, low, high) => Math.min(high, Math.max(low, value));
2
- const toAlpha = (token) => {
3
- if (token === undefined) {
4
- return 100;
5
- }
6
- const number = Number.parseFloat(token);
7
- if (Number.isNaN(number)) {
8
- return 100;
9
- }
10
- return clamp(token.includes('%') ? number : number * 100, 0, 100);
11
- };
12
- const toByte = (token) => {
13
- const number = Number.parseFloat(token);
14
- return clamp(token.includes('%') ? (number / 100) * 255 : number, 0, 255);
15
- };
16
- const rgbToHsb = (red, green, blue, alpha) => {
17
- const r = red / 255;
18
- const g = green / 255;
19
- const b = blue / 255;
20
- const high = Math.max(r, g, b);
21
- const low = Math.min(r, g, b);
22
- const span = high - low;
23
- let hue = 0;
24
- if (span !== 0) {
25
- if (high === r) {
26
- hue = (g - b) / span + (g < b ? 6 : 0);
27
- }
28
- else if (high === g) {
29
- hue = (b - r) / span + 2;
30
- }
31
- else {
32
- hue = (r - g) / span + 4;
33
- }
34
- hue *= 60;
35
- }
36
- return {
37
- alpha,
38
- brightness: Math.round(high * 100),
39
- hue: Math.round(hue),
40
- saturation: Math.round(high === 0 ? 0 : (span / high) * 100),
41
- };
42
- };
43
- export const toRgb = (color) => {
44
- const hue = (((color.hue % 360) + 360) % 360) / 60;
45
- const saturation = color.saturation / 100;
46
- const brightness = color.brightness / 100;
47
- const chroma = brightness * saturation;
48
- const middle = chroma * (1 - Math.abs((hue % 2) - 1));
49
- const base = brightness - chroma;
50
- const sextants = [
51
- [chroma, middle, 0],
52
- [middle, chroma, 0],
53
- [0, chroma, middle],
54
- [0, middle, chroma],
55
- [middle, 0, chroma],
56
- [chroma, 0, middle],
57
- ];
58
- const [red, green, blue] = sextants[Math.floor(hue) % 6] ?? [0, 0, 0];
59
- return {
60
- blue: Math.round((blue + base) * 255),
61
- green: Math.round((green + base) * 255),
62
- red: Math.round((red + base) * 255),
63
- };
64
- };
65
- const toHsl = (color) => {
66
- const saturation = color.saturation / 100;
67
- const brightness = color.brightness / 100;
68
- const lightness = (brightness * (2 - saturation)) / 2;
69
- return {
70
- hue: ((color.hue % 360) + 360) % 360,
71
- lightness: Math.round(lightness * 100),
72
- saturation: Math.round((lightness === 0 || lightness === 1
73
- ? 0
74
- : (brightness - lightness) / Math.min(lightness, 1 - lightness)) * 100),
75
- };
76
- };
77
- export const splitColors = (value) => {
78
- const found = [];
79
- let depth = 0;
80
- let current = '';
81
- for (const character of value) {
82
- if (character === '(') {
83
- depth += 1;
84
- }
85
- else if (character === ')') {
86
- depth = Math.max(0, depth - 1);
87
- }
88
- if (character === ',' && depth === 0) {
89
- found.push(current.trim());
90
- current = '';
91
- }
92
- else {
93
- current += character;
94
- }
95
- }
96
- found.push(current.trim());
97
- return found.filter(Boolean);
98
- };
99
- export const parseColor = (value) => {
100
- const text = value.trim().toLowerCase();
101
- if (text === '') {
102
- return null;
103
- }
104
- if (text.startsWith('#')) {
105
- const digits = text.slice(1);
106
- const short = digits.length === 3 || digits.length === 4;
107
- const long = digits.length === 6 || digits.length === 8;
108
- if ((!short && !long) || !/^[\da-f]+$/.test(digits)) {
109
- return null;
110
- }
111
- const width = short ? 1 : 2;
112
- const channel = (index) => {
113
- const piece = digits.slice(index * width, index * width + width);
114
- return Number.parseInt(short ? piece + piece : piece, 16);
115
- };
116
- const carriesAlpha = digits.length === 4 || digits.length === 8;
117
- return rgbToHsb(channel(0), channel(1), channel(2), carriesAlpha ? Math.round((channel(3) / 255) * 100) : 100);
118
- }
119
- const call = /^(rgba?|hsla?)\(([^)]*)\)$/.exec(text);
120
- if (!call?.[1] || call[2] === undefined) {
121
- return null;
122
- }
123
- const [first = '', second = '', third = '', fourth] = call[2]
124
- .replace(/[,/]/g, ' ')
125
- .split(/\s+/)
126
- .filter(Boolean);
127
- if (first === '' || second === '' || third === '') {
128
- return null;
129
- }
130
- const alpha = toAlpha(fourth);
131
- if (call[1].startsWith('rgb')) {
132
- return rgbToHsb(toByte(first), toByte(second), toByte(third), alpha);
133
- }
134
- const hue = Number.parseFloat(first);
135
- const saturation = clamp(Number.parseFloat(second), 0, 100) / 100;
136
- const lightness = clamp(Number.parseFloat(third), 0, 100) / 100;
137
- if (Number.isNaN(hue) || Number.isNaN(saturation) || Number.isNaN(lightness)) {
138
- return null;
139
- }
140
- const brightness = lightness + saturation * Math.min(lightness, 1 - lightness);
141
- return {
142
- alpha,
143
- brightness: Math.round(brightness * 100),
144
- hue: Math.round(((hue % 360) + 360) % 360),
145
- saturation: Math.round((brightness === 0 ? 0 : 2 * (1 - lightness / brightness)) * 100),
146
- };
147
- };
148
- export const formatColor = (color, format, withAlpha) => {
149
- const alpha = clamp(color.alpha, 0, 100) / 100;
150
- const opacity = Number(alpha.toFixed(2));
151
- if (format === 'hsl') {
152
- const { hue, lightness, saturation } = toHsl(color);
153
- const body = `${Math.round(hue)}, ${saturation}%, ${lightness}%`;
154
- return withAlpha ? `hsla(${body}, ${opacity})` : `hsl(${body})`;
155
- }
156
- const { blue, green, red } = toRgb(color);
157
- if (format === 'rgb') {
158
- const body = `${red}, ${green}, ${blue}`;
159
- return withAlpha ? `rgba(${body}, ${opacity})` : `rgb(${body})`;
160
- }
161
- const byte = (one) => one.toString(16).padStart(2, '0');
162
- const hex = `#${byte(red)}${byte(green)}${byte(blue)}`;
163
- return withAlpha ? hex + byte(Math.round(alpha * 255)) : hex;
164
- };
165
- export const toCss = (color) => {
166
- const { blue, green, red } = toRgb(color);
167
- return `rgb(${red} ${green} ${blue} / ${clamp(color.alpha, 0, 100)}%)`;
168
- };