@electrovir/color 1.0.0 → 1.2.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.
@@ -28,16 +28,6 @@ export type ColorCoordinateDefinition = {
28
28
  factor?: number | undefined;
29
29
  suffix?: string;
30
30
  }>;
31
- /**
32
- * A single color format definition.
33
- *
34
- * @category Internal
35
- */
36
- export type ColorFormatDefinition<ColorSpace extends string = string> = {
37
- coords: Record<string, ColorCoordinateDefinition>;
38
- /** This name for this color to be used in `Colorjs.to()`. Defaults to the color format key. */
39
- colorSpace: ColorSpace;
40
- };
41
31
  /**
42
32
  * All raw supported color formats.
43
33
  *
@@ -184,15 +174,35 @@ export declare const rawColorFormats: {
184
174
  };
185
175
  };
186
176
  /**
187
- * All supported color format names. This can be used as an enum.
177
+ * All supported color format names.
178
+ *
179
+ * @category Internal
180
+ * @enum
181
+ */
182
+ export declare const ColorFormatName: { [Key in ColorFormatName]: Key; };
183
+ /**
184
+ * {@link ColorFormatName} combined with hex and named CSS color representation.
188
185
  *
189
186
  * @category Internal
187
+ * @enum
190
188
  */
191
- export declare const ColorFormatName: { [FormatName in ColorFormatName]: FormatName; };
189
+ export declare const ColorSyntaxName: {
190
+ readonly hex: "hex";
191
+ readonly name: "name";
192
+ readonly rgb: "rgb";
193
+ readonly hsl: "hsl";
194
+ readonly hwb: "hwb";
195
+ readonly lab: "lab";
196
+ readonly lch: "lch";
197
+ readonly oklab: "oklab";
198
+ readonly oklch: "oklch";
199
+ };
200
+ export type ColorSyntaxName = Values<typeof ColorSyntaxName>;
192
201
  /**
193
- * All supported color format names. This can be used as an enum.
202
+ * All supported color format names.
194
203
  *
195
204
  * @category Internal
205
+ * @enum
196
206
  */
197
207
  export type ColorFormatName = keyof typeof rawColorFormats;
198
208
  /**
@@ -210,9 +220,9 @@ export type ColorCoordsByFormat = {
210
220
  * @category Internal
211
221
  */
212
222
  export type ColorFormats = Readonly<{
213
- [FormatName in ColorFormatName]: ColorFormatDefinition<'colorSpace' extends keyof (typeof rawColorFormats)[FormatName] ? Extract<(typeof rawColorFormats)[FormatName], {
223
+ [FormatName in ColorFormatName]: ColorFormatDefinition<Extract<(typeof rawColorFormats)[FormatName], {
214
224
  colorSpace: any;
215
- }>['colorSpace'] : FormatName>;
225
+ }>['colorSpace'], FormatName>;
216
226
  }>;
217
227
  /**
218
228
  * All supported color formats.
@@ -221,17 +231,25 @@ export type ColorFormats = Readonly<{
221
231
  */
222
232
  export declare const colorFormats: ColorFormats;
223
233
  /**
224
- * All available color space names.
234
+ * All supported color space names.
225
235
  *
226
236
  * @category Internal
237
+ * @enum
227
238
  */
228
239
  export type ColorSpaceName = Values<typeof rawColorFormats>['colorSpace'];
240
+ /**
241
+ * All supported color space names.
242
+ *
243
+ * @category Internal
244
+ * @enum
245
+ */
246
+ export declare const ColorSpaceName: { [Key in ColorSpaceName]: Key; };
229
247
  /**
230
248
  * All value types for all supported color formats.
231
249
  *
232
250
  * @category Internal
233
251
  */
234
- export type ColorValues = {
252
+ export type ColorValue = {
235
253
  [FormatName in ColorFormatName]: Record<keyof (typeof rawColorFormats)[FormatName]['coords'], number>;
236
254
  };
237
255
  /**
@@ -239,16 +257,28 @@ export type ColorValues = {
239
257
  *
240
258
  * @category Color Format
241
259
  */
242
- export declare const colorSpaces: Record<"rgb" | "lab" | "oklab", Record<"rgb" | "hsl" | "hwb" | "lab" | "lch" | "oklab" | "oklch", ColorFormatDefinition<string>>>;
260
+ export declare const colorFormatsBySpace: { [ColorSpace in ColorSpaceName]: { [ColorFormat in ColorFormatName]: ColorFormatDefinition<ColorSpace, ColorFormat>; }; };
243
261
  /**
244
- * All color format names in an array.
262
+ * Determines the color syntax / format in use based on the given CSS color string.
245
263
  *
246
- * @category Internal
264
+ * @category Util
247
265
  */
248
- export declare const colorFormatNames: ColorFormatName[];
266
+ export declare function getColorSyntaxFromCssString(cssString: string): ColorSyntaxName;
249
267
  /**
250
268
  * All possible coordinate names for all supported color formats in a union.
251
269
  *
252
270
  * @category Internal
253
271
  */
254
272
  export type ColorCoordinateName = keyof UnionToIntersection<Values<typeof rawColorFormats>['coords']>;
273
+ /**
274
+ * A single color format definition.
275
+ *
276
+ * @category Internal
277
+ */
278
+ export type ColorFormatDefinition<ColorSpace extends ColorSpaceName = any, ColorFormat extends ColorFormatName = any> = {
279
+ /** Which exact color coordinates exist in here depends on the set color space. */
280
+ coords: Record<ColorCoordsByFormat[ColorFormat], ColorCoordinateDefinition>;
281
+ /** This name for this color to be used in `Colorjs.to()`. Defaults to the color format key. */
282
+ colorSpace: ColorSpace;
283
+ colorFormat: ColorFormat;
284
+ };
@@ -1,4 +1,4 @@
1
- import { getObjectTypedEntries, getObjectTypedKeys, getOrSet, mapObjectValues, } from '@augment-vir/common';
1
+ import { arrayToObject, getObjectTypedEntries, getObjectTypedValues, getOrSet, mapObjectValues, } from '@augment-vir/common';
2
2
  /**
3
3
  * All raw supported color formats.
4
4
  *
@@ -145,31 +145,90 @@ export const rawColorFormats = {
145
145
  },
146
146
  };
147
147
  /**
148
- * All supported color format names. This can be used as an enum.
148
+ * All supported color format names.
149
149
  *
150
150
  * @category Internal
151
+ * @enum
151
152
  */
152
153
  export const ColorFormatName = mapObjectValues(rawColorFormats, (colorName) => colorName);
154
+ /**
155
+ * {@link ColorFormatName} combined with hex and named CSS color representation.
156
+ *
157
+ * @category Internal
158
+ * @enum
159
+ */
160
+ export const ColorSyntaxName = {
161
+ ...ColorFormatName,
162
+ hex: 'hex',
163
+ name: 'name',
164
+ };
153
165
  /**
154
166
  * All supported color formats.
155
167
  *
156
168
  * @category Color Format
157
169
  */
158
- export const colorFormats = rawColorFormats;
170
+ export const colorFormats = mapObjectValues(rawColorFormats, (colorFormatName, colorFormatValue) => {
171
+ return {
172
+ ...colorFormatValue,
173
+ colorFormat: colorFormatName,
174
+ };
175
+ });
176
+ /**
177
+ * All supported color space names.
178
+ *
179
+ * @category Internal
180
+ * @enum
181
+ */
182
+ export const ColorSpaceName = arrayToObject(getObjectTypedValues(rawColorFormats), (format) => {
183
+ return {
184
+ key: format.colorSpace,
185
+ value: format.colorSpace,
186
+ };
187
+ }, {
188
+ useRequired: true,
189
+ });
159
190
  /**
160
191
  * All color formats grouped by their color space.
161
192
  *
162
193
  * @category Color Format
163
194
  */
164
- export const colorSpaces = getObjectTypedEntries(colorFormats).reduce((accum, [colorFormatName, colorFormatDefinition,]) => {
195
+ export const colorFormatsBySpace = getObjectTypedEntries(colorFormats).reduce((accum, [colorFormatName, colorFormatDefinition,]) => {
165
196
  getOrSet(accum, colorFormatDefinition.colorSpace, () => {
166
197
  return {};
167
198
  })[colorFormatName] = colorFormatDefinition;
168
199
  return accum;
169
200
  }, {});
170
201
  /**
171
- * All color format names in an array.
202
+ * Determines the color syntax / format in use based on the given CSS color string.
172
203
  *
173
- * @category Internal
204
+ * @category Util
174
205
  */
175
- export const colorFormatNames = getObjectTypedKeys(colorFormats);
206
+ export function getColorSyntaxFromCssString(cssString) {
207
+ if (cssString.startsWith('rgb')) {
208
+ return ColorSyntaxName.rgb;
209
+ }
210
+ else if (cssString.startsWith('hsl')) {
211
+ return ColorSyntaxName.hsl;
212
+ }
213
+ else if (cssString.startsWith('hwb')) {
214
+ return ColorSyntaxName.hwb;
215
+ }
216
+ else if (cssString.startsWith('oklab')) {
217
+ return ColorSyntaxName.oklab;
218
+ }
219
+ else if (cssString.startsWith('oklch')) {
220
+ return ColorSyntaxName.oklch;
221
+ }
222
+ else if (cssString.startsWith('lab')) {
223
+ return ColorSyntaxName.lab;
224
+ }
225
+ else if (cssString.startsWith('lch')) {
226
+ return ColorSyntaxName.lch;
227
+ }
228
+ else if (cssString.startsWith('#')) {
229
+ return ColorSyntaxName.hex;
230
+ }
231
+ else {
232
+ return ColorSyntaxName.name;
233
+ }
234
+ }
@@ -1,6 +1,6 @@
1
1
  import { type PartialWithUndefined } from '@augment-vir/common';
2
2
  import { type RequireExactlyOne } from 'type-fest';
3
- import { type ColorCoordsByFormat, type ColorFormatName, type ColorValues, type HexColor } from './color-formats.js';
3
+ import { ColorFormatName, ColorSyntaxName, type ColorCoordsByFormat, type ColorValue, type HexColor } from './color-formats.js';
4
4
  /**
5
5
  * An update to an existing {@link Color} instance. Used in {@link Color.set}.
6
6
  *
@@ -9,8 +9,8 @@ import { type ColorCoordsByFormat, type ColorFormatName, type ColorValues, type
9
9
  export type ColorUpdate = RequireExactlyOne<{
10
10
  [FormatName in ColorFormatName]: PartialWithUndefined<Record<ColorCoordsByFormat[FormatName], number>>;
11
11
  } & {
12
- hex: HexColor;
13
- name: string;
12
+ [ColorSyntaxName.hex]: HexColor;
13
+ [ColorSyntaxName.name]: string;
14
14
  }>;
15
15
  /**
16
16
  * The values of all supported color formats for a given {@link Color} instance. Accessed via
@@ -18,8 +18,9 @@ export type ColorUpdate = RequireExactlyOne<{
18
18
  *
19
19
  * @category Internal
20
20
  */
21
- export type AllColorsValues = ColorValues & {
22
- hex: HexColor;
21
+ export type AllColorsValues = ColorValue & {
22
+ [ColorSyntaxName.hex]: HexColor;
23
+ [ColorSyntaxName.name]: string;
23
24
  names: string[];
24
25
  };
25
26
  /**
@@ -46,6 +47,7 @@ export declare class Color {
46
47
  /** All current color values. These are updated whenever {@link Color.set} is called. */
47
48
  protected readonly _allColors: {
48
49
  names: string[];
50
+ name: string;
49
51
  hex: HexColor;
50
52
  rgb: {
51
53
  r: number;
@@ -112,18 +114,27 @@ export declare class Color {
112
114
  /**
113
115
  * Converts the values for each supported color format into a padded string for easy display
114
116
  * purposes.
117
+ *
118
+ * @see `.toCss()`
115
119
  */
116
- toFormattedStrings(): Record<ColorFormatName | 'hex' | 'names', string>;
120
+ toFormattedStrings(): Record<ColorSyntaxName | 'names', string>;
117
121
  /**
118
122
  * Converts the values for each supported color format in a CSS string that can be directly used
119
123
  * in any modern CSS code.
124
+ *
125
+ * @see `.toFormattedStrings()`
120
126
  */
121
- toCss(): Record<ColorFormatName | 'hex' | 'name', string>;
127
+ toCss(): Record<ColorSyntaxName, string>;
122
128
  /**
123
129
  * The current color expressed as hardcoded CSS color keywords. If no CSS color keywords match
124
130
  * the current color, this array will be empty.
125
131
  */
126
132
  get names(): string[];
133
+ /**
134
+ * The current color expressed as a single CSS color name string. If there is no color name that
135
+ * matches the current color, this will be an empty string.
136
+ */
137
+ get name(): string;
127
138
  /** The current color expressed as an RGB hex string. */
128
139
  get hex(): HexColor;
129
140
  /** The current color expressed as its RGB coordinate values. */
@@ -1,8 +1,8 @@
1
1
  import { assert, assertWrap, check } from '@augment-vir/assert';
2
- import { copyThroughJson, filterMap, getObjectTypedEntries, joinWithFinalConjunction, mapObjectValues, round, } from '@augment-vir/common';
2
+ import { copyThroughJson, filterMap, getEnumValues, getObjectTypedEntries, getObjectTypedKeys, joinWithFinalConjunction, mapObjectValues, round, } from '@augment-vir/common';
3
3
  import colorNames from 'color-name';
4
4
  import { clampGamut, converter, formatHex, parse } from 'culori';
5
- import { colorFormatNames, colorFormats, } from './color-formats.js';
5
+ import { ColorFormatName, colorFormats, ColorSyntaxName, } from './color-formats.js';
6
6
  import { maxColorNameLength } from './color-name-length.js';
7
7
  /**
8
8
  * A `Color` class with state and the following features:
@@ -37,38 +37,39 @@ export class Color {
37
37
  /** All current color values. These are updated whenever {@link Color.set} is called. */
38
38
  _allColors = {
39
39
  names: ['black'],
40
- hex: '#000000',
41
- rgb: {
40
+ [ColorSyntaxName.name]: 'black',
41
+ [ColorSyntaxName.hex]: '#000000',
42
+ [ColorSyntaxName.rgb]: {
42
43
  r: 0,
43
44
  g: 0,
44
45
  b: 0,
45
46
  },
46
- hsl: {
47
+ [ColorSyntaxName.hsl]: {
47
48
  h: 0,
48
49
  s: 0,
49
50
  l: 0,
50
51
  },
51
- hwb: {
52
+ [ColorSyntaxName.hwb]: {
52
53
  h: 0,
53
54
  w: 0,
54
55
  b: 0,
55
56
  },
56
- lab: {
57
+ [ColorSyntaxName.lab]: {
57
58
  l: 0,
58
59
  a: 0,
59
60
  b: 0,
60
61
  },
61
- lch: {
62
+ [ColorSyntaxName.lch]: {
62
63
  l: 0,
63
64
  c: 0,
64
65
  h: 0,
65
66
  },
66
- oklab: {
67
+ [ColorSyntaxName.oklab]: {
67
68
  l: 0,
68
69
  a: 0,
69
70
  b: 0,
70
71
  },
71
- oklch: {
72
+ [ColorSyntaxName.oklch]: {
72
73
  l: 0,
73
74
  c: 0,
74
75
  h: 0,
@@ -109,7 +110,7 @@ export class Color {
109
110
  const colorFormatDefinition = colorFormats[colorFormatName];
110
111
  const orderedColorCoords = Object.values(mapObjectValues(colorFormatDefinition.coords, (coordName) => {
111
112
  const coordValue = colorValues[coordName];
112
- const coordDefinition = assertWrap.isDefined(colorFormatDefinition.coords[coordName]);
113
+ const coordDefinition = colorFormatDefinition.coords[assertWrap.isKeyOf(coordName, colorFormatDefinition.coords)];
113
114
  const rawCoordValue = coordValue != undefined &&
114
115
  coordValue >= coordDefinition.min &&
115
116
  coordValue <= coordDefinition.max
@@ -125,7 +126,7 @@ export class Color {
125
126
  * internal color object.
126
127
  */
127
128
  pullFromInternalColor() {
128
- colorFormatNames.forEach((colorFormatName) => {
129
+ getEnumValues(ColorFormatName).forEach((colorFormatName) => {
129
130
  const colorFormatDefinition = colorFormats[colorFormatName];
130
131
  const originalColorDefinition = check.isKeyOf(this.#internalColor.mode, colorFormats)
131
132
  ? colorFormats[this.#internalColor.mode]
@@ -137,17 +138,19 @@ export class Color {
137
138
  if (!converted) {
138
139
  assert.never(`Failed to convert color '${JSON.stringify(this.#internalColor)}' to '${colorFormatName}'.`);
139
140
  }
140
- Object.keys(this[colorFormatName]).forEach((coordName) => {
141
+ getObjectTypedKeys(this[colorFormatName]).forEach((coordName) => {
141
142
  const coordValue = converted[coordName];
143
+ const coordinateDefinition = colorFormatDefinition.coords[assertWrap.isKeyOf(coordName, colorFormatDefinition.coords)];
142
144
  if (coordValue != undefined) {
143
- this._allColors[colorFormatName][coordName] = round((coordValue || 0) * (colorFormatDefinition.coords[coordName]?.factor || 1), {
144
- digits: colorFormatDefinition.coords[coordName]?.digits || 0,
145
+ this._allColors[colorFormatName][coordName] = round((coordValue || 0) * (coordinateDefinition.factor || 1), {
146
+ digits: coordinateDefinition.digits || 0,
145
147
  });
146
148
  }
147
149
  });
148
150
  });
149
- this._allColors.hex = formatHex(this.#internalColor);
151
+ this._allColors[ColorSyntaxName.hex] = formatHex(this.#internalColor);
150
152
  this._allColors.names = findMatchingColorNames(this.rgb);
153
+ this._allColors[ColorSyntaxName.name] = this._allColors.names[0] || '';
151
154
  }
152
155
  /**
153
156
  * Create a string that can be serialized into a new {@link Color} instance which will exactly
@@ -163,6 +166,8 @@ export class Color {
163
166
  /**
164
167
  * Converts the values for each supported color format into a padded string for easy display
165
168
  * purposes.
169
+ *
170
+ * @see `.toCss()`
166
171
  */
167
172
  toFormattedStrings() {
168
173
  const colorFormatStrings = mapObjectValues(colorFormats, (colorFormatName) => {
@@ -170,14 +175,17 @@ export class Color {
170
175
  return coordValues.map((coordValue) => String(coordValue).padStart(6, ' ')).join(' ');
171
176
  });
172
177
  return {
173
- hex: this.hex,
178
+ [ColorSyntaxName.hex]: this.hex,
174
179
  ...colorFormatStrings,
175
180
  names: this.names.join(', ').padEnd(maxColorNameLength, ' '),
181
+ name: (this.names[0] || '').padEnd(maxColorNameLength, ' '),
176
182
  };
177
183
  }
178
184
  /**
179
185
  * Converts the values for each supported color format in a CSS string that can be directly used
180
186
  * in any modern CSS code.
187
+ *
188
+ * @see `.toFormattedStrings()`
181
189
  */
182
190
  toCss() {
183
191
  const colorFormatStrings = mapObjectValues(colorFormats, (colorFormatName) => {
@@ -185,7 +193,7 @@ export class Color {
185
193
  return `${colorFormatName}(${coordValues.join(' ')})`;
186
194
  });
187
195
  return {
188
- hex: this.hex,
196
+ [ColorSyntaxName.hex]: this.hex,
189
197
  ...colorFormatStrings,
190
198
  name: this.names[0] || '',
191
199
  };
@@ -197,37 +205,44 @@ export class Color {
197
205
  get names() {
198
206
  return copyThroughJson(this._allColors.names);
199
207
  }
208
+ /**
209
+ * The current color expressed as a single CSS color name string. If there is no color name that
210
+ * matches the current color, this will be an empty string.
211
+ */
212
+ get name() {
213
+ return this._allColors.names[0] || '';
214
+ }
200
215
  /** The current color expressed as an RGB hex string. */
201
216
  get hex() {
202
- return copyThroughJson(this._allColors.hex);
217
+ return copyThroughJson(this._allColors[ColorSyntaxName.hex]);
203
218
  }
204
219
  /** The current color expressed as its RGB coordinate values. */
205
220
  get rgb() {
206
- return copyThroughJson(this._allColors.rgb);
221
+ return copyThroughJson(this._allColors[ColorSyntaxName.rgb]);
207
222
  }
208
223
  /** The current color expressed as its HSL coordinate values. */
209
224
  get hsl() {
210
- return copyThroughJson(this._allColors.hsl);
225
+ return copyThroughJson(this._allColors[ColorSyntaxName.hsl]);
211
226
  }
212
227
  /** The current color expressed as its HWB coordinate values. */
213
228
  get hwb() {
214
- return copyThroughJson(this._allColors.hwb);
229
+ return copyThroughJson(this._allColors[ColorSyntaxName.hwb]);
215
230
  }
216
231
  /** The current color expressed as its LAB coordinate values. */
217
232
  get lab() {
218
- return copyThroughJson(this._allColors.lab);
233
+ return copyThroughJson(this._allColors[ColorSyntaxName.lab]);
219
234
  }
220
235
  /** The current color expressed as its LCH coordinate values. */
221
236
  get lch() {
222
- return copyThroughJson(this._allColors.lch);
237
+ return copyThroughJson(this._allColors[ColorSyntaxName.lch]);
223
238
  }
224
239
  /** The current color expressed as its Oklab coordinate values. */
225
240
  get oklab() {
226
- return copyThroughJson(this._allColors.oklab);
241
+ return copyThroughJson(this._allColors[ColorSyntaxName.oklab]);
227
242
  }
228
243
  /** The current color expressed as its Oklch coordinate values. */
229
244
  get oklch() {
230
- return copyThroughJson(this._allColors.oklch);
245
+ return copyThroughJson(this._allColors[ColorSyntaxName.oklch]);
231
246
  }
232
247
  }
233
248
  function findMatchingColorNames(rgb) {
@@ -0,0 +1,11 @@
1
+ import { type Color } from '../color-class/color.js';
2
+ /**
3
+ * Color sliders for all color spaces.
4
+ *
5
+ * @category Elements
6
+ */
7
+ export declare const VirAllColorSpaceSliders: import("element-vir").DeclarativeElementDefinition<"vir-all-color-space-sliders", {
8
+ color: Readonly<Color>;
9
+ }, {}, {
10
+ colorChange: import("element-vir").DefineEvent<string>;
11
+ }, "vir-all-color-space-sliders-", "vir-all-color-space-sliders-", readonly [], readonly []>;
@@ -0,0 +1,52 @@
1
+ /* node:coverage disable */
2
+ import { getObjectTypedKeys, getObjectTypedValues } from '@augment-vir/common';
3
+ import { css, defineElement, defineElementEvent, html, listen } from 'element-vir';
4
+ import { colorFormatsBySpace } from '../color-class/color-formats.js';
5
+ import { VirColorFormatSliders } from './vir-color-format-sliders.element.js';
6
+ /**
7
+ * Color sliders for all color spaces.
8
+ *
9
+ * @category Elements
10
+ */
11
+ export const VirAllColorSpaceSliders = defineElement()({
12
+ tagName: 'vir-all-color-space-sliders',
13
+ styles: css `
14
+ :host {
15
+ display: flex;
16
+ flex-direction: column;
17
+ gap: 16px;
18
+ }
19
+
20
+ .color-space {
21
+ display: flex;
22
+ flex-wrap: wrap;
23
+ column-gap: 32px;
24
+ row-gap: 8px;
25
+ }
26
+ `,
27
+ events: {
28
+ colorChange: defineElementEvent(),
29
+ },
30
+ render({ inputs, dispatch, events }) {
31
+ const colorSpaceTemplates = getObjectTypedValues(colorFormatsBySpace).map((colorSpaceFormats) => {
32
+ const formatTemplates = getObjectTypedKeys(colorSpaceFormats).map((colorFormatName) => {
33
+ return html `
34
+ <${VirColorFormatSliders.assign({
35
+ color: inputs.color,
36
+ colorFormatName,
37
+ })}
38
+ ${listen(VirColorFormatSliders.events.colorChange, (event) => {
39
+ dispatch(new events.colorChange(event.detail));
40
+ })}
41
+ ></${VirColorFormatSliders}>
42
+ `;
43
+ });
44
+ return html `
45
+ <section class="color-space">${formatTemplates}</section>
46
+ `;
47
+ });
48
+ return html `
49
+ ${colorSpaceTemplates}
50
+ `;
51
+ },
52
+ });
@@ -0,0 +1,13 @@
1
+ import { type ColorFormatName } from '../color-class/color-formats.js';
2
+ import { type Color } from '../color-class/color.js';
3
+ /**
4
+ * Color sliders for all coordinates within a specific color format.
5
+ *
6
+ * @category Elements
7
+ */
8
+ export declare const VirColorFormatSliders: import("element-vir").DeclarativeElementDefinition<"vir-color-format-sliders", {
9
+ color: Readonly<Color>;
10
+ colorFormatName: ColorFormatName;
11
+ }, {}, {
12
+ colorChange: import("element-vir").DefineEvent<string>;
13
+ }, "vir-color-format-sliders-", "vir-color-format-sliders-", readonly [], readonly []>;
@@ -0,0 +1,54 @@
1
+ /* node:coverage disable */
2
+ import { getObjectTypedKeys } from '@augment-vir/common';
3
+ import { css, defineElement, defineElementEvent, html, listen } from 'element-vir';
4
+ import { noNativeSpacing } from 'vira';
5
+ import { colorFormats, } from '../color-class/color-formats.js';
6
+ import { VirColorSlider } from './vir-color-slider.element.js';
7
+ /**
8
+ * Color sliders for all coordinates within a specific color format.
9
+ *
10
+ * @category Elements
11
+ */
12
+ export const VirColorFormatSliders = defineElement()({
13
+ tagName: 'vir-color-format-sliders',
14
+ styles: css `
15
+ :host {
16
+ display: flex;
17
+ flex-direction: column;
18
+ }
19
+
20
+ h3 {
21
+ ${noNativeSpacing};
22
+ }
23
+ `,
24
+ events: {
25
+ colorChange: defineElementEvent(),
26
+ },
27
+ render({ inputs, dispatch, events }) {
28
+ const colorFormat = colorFormats[inputs.colorFormatName];
29
+ const coordinateTemplates = getObjectTypedKeys(colorFormat.coords).map((colorCoordinate) => {
30
+ return html `
31
+ <${VirColorSlider.assign({
32
+ color: inputs.color,
33
+ colorCoordinateName: colorCoordinate,
34
+ colorFormatName: inputs.colorFormatName,
35
+ })}
36
+ ${listen(VirColorSlider.events.valueChange, (event) => {
37
+ const newColor = inputs.color.clone();
38
+ newColor.set({
39
+ [inputs.colorFormatName]: {
40
+ [colorCoordinate]: event.detail,
41
+ },
42
+ });
43
+ const newValue = newColor.toCss()[inputs.colorFormatName];
44
+ dispatch(new events.colorChange(newValue));
45
+ })}
46
+ ></${VirColorSlider}>
47
+ `;
48
+ });
49
+ return html `
50
+ <h3>${inputs.colorFormatName}</h3>
51
+ ${coordinateTemplates}
52
+ `;
53
+ },
54
+ });
@@ -0,0 +1,14 @@
1
+ import { type ColorCoordinateName, type ColorFormatName } from '../color-class/color-formats.js';
2
+ import { Color } from '../color-class/color.js';
3
+ /**
4
+ * A slider for a specific color coordinate in a specific color space in a specific color.
5
+ *
6
+ * @category Elements
7
+ */
8
+ export declare const VirColorSlider: import("element-vir").DeclarativeElementDefinition<"vir-color-slider", {
9
+ color: Readonly<Color>;
10
+ colorFormatName: ColorFormatName;
11
+ colorCoordinateName: ColorCoordinateName;
12
+ }, {}, {
13
+ valueChange: import("element-vir").DefineEvent<number>;
14
+ }, "vir-color-slider-", "vir-color-slider-gradient", readonly [], readonly []>;
@@ -0,0 +1,101 @@
1
+ /* node:coverage disable */
2
+ import { assertWrap } from '@augment-vir/assert';
3
+ import { createArray } from '@augment-vir/common';
4
+ import { extractEventTarget } from '@augment-vir/web';
5
+ import { css, defineElement, defineElementEvent, html, listen, unsafeCSS } from 'element-vir';
6
+ import { viraFontCssVars, ViraInput } from 'vira';
7
+ import { colorFormats, } from '../color-class/color-formats.js';
8
+ import { Color } from '../color-class/color.js';
9
+ /**
10
+ * A slider for a specific color coordinate in a specific color space in a specific color.
11
+ *
12
+ * @category Elements
13
+ */
14
+ export const VirColorSlider = defineElement()({
15
+ tagName: 'vir-color-slider',
16
+ cssVars: {
17
+ 'vir-color-slider-gradient': 'black',
18
+ },
19
+ styles: ({ cssVars }) => css `
20
+ :host {
21
+ display: flex;
22
+ align-items: center;
23
+ font-family: ${viraFontCssVars['vira-monospace'].value};
24
+ gap: 2px;
25
+ }
26
+
27
+ input[type='range'] {
28
+ flex-grow: 1;
29
+ appearance: none;
30
+ background: ${cssVars['vir-color-slider-gradient'].value};
31
+ height: 9px;
32
+ border-radius: 4px;
33
+ cursor: pointer;
34
+ }
35
+
36
+ ${ViraInput} {
37
+ width: 76px;
38
+ }
39
+
40
+ .coordinate {
41
+ font-size: 18px;
42
+ margin-top: -4px;
43
+ }
44
+ `,
45
+ events: {
46
+ valueChange: defineElementEvent(),
47
+ },
48
+ render({ inputs, events, dispatch, cssVars }) {
49
+ const formatDefinition = colorFormats[inputs.colorFormatName];
50
+ const coordinateDefinition = formatDefinition.coords[inputs.colorCoordinateName];
51
+ if (!coordinateDefinition) {
52
+ throw new Error(`Invalid color coordinate '${inputs.colorCoordinateName}' for color format '${inputs.colorFormatName}'`);
53
+ }
54
+ const totalStops = 10;
55
+ const colorStops = createArray(totalStops, (index) => {
56
+ const value = coordinateDefinition.min +
57
+ (coordinateDefinition.max - coordinateDefinition.min) * (index / totalStops);
58
+ const stopColor = new Color({
59
+ [inputs.colorFormatName]: {
60
+ ...inputs.color[inputs.colorFormatName],
61
+ [inputs.colorCoordinateName]: value,
62
+ },
63
+ });
64
+ return stopColor.toCss()[inputs.colorFormatName];
65
+ });
66
+ const gradient = css `linear-gradient(to right, ${unsafeCSS(colorStops.join(','))})`;
67
+ const coordinateValue = assertWrap.isNumber(inputs.color[inputs.colorFormatName][inputs.colorCoordinateName]);
68
+ return html `
69
+ <span class="coordinate">${inputs.colorCoordinateName.toUpperCase()}</span>
70
+ <input
71
+ type="range"
72
+ style=${css `
73
+ ${cssVars['vir-color-slider-gradient'].name}: ${gradient};
74
+ `}
75
+ min=${coordinateDefinition.min}
76
+ max=${coordinateDefinition.max}
77
+ .value=${String(coordinateValue)}
78
+ step=${Math.pow(10, coordinateDefinition.digits ? -coordinateDefinition.digits : 0)}
79
+ ${listen('input', (event) => {
80
+ const element = extractEventTarget(event, HTMLInputElement);
81
+ const newValue = Number(element.value);
82
+ if (isNaN(newValue)) {
83
+ return;
84
+ }
85
+ dispatch(new events.valueChange(newValue));
86
+ })}
87
+ />
88
+ <${ViraInput.assign({
89
+ value: String(coordinateValue),
90
+ })}
91
+ ${listen(ViraInput.events.valueChange, (event) => {
92
+ const newValue = Number(event.detail);
93
+ if (isNaN(newValue)) {
94
+ return;
95
+ }
96
+ dispatch(new events.valueChange(newValue));
97
+ })}
98
+ ></${ViraInput}>
99
+ `;
100
+ },
101
+ });
package/dist/index.d.ts CHANGED
@@ -2,5 +2,8 @@ export * from './color-class/color-formats.js';
2
2
  export * from './color-class/color-name-length.js';
3
3
  export * from './color-class/color.js';
4
4
  export * from './contrast/contrast.js';
5
+ export * from './elements/vir-all-color-space-sliders.element.js';
6
+ export * from './elements/vir-color-format-sliders.element.js';
5
7
  export * from './elements/vir-color-pair.element.js';
8
+ export * from './elements/vir-color-slider.element.js';
6
9
  export * from './elements/vir-contrast-indicator.element.js';
package/dist/index.js CHANGED
@@ -2,5 +2,8 @@ export * from './color-class/color-formats.js';
2
2
  export * from './color-class/color-name-length.js';
3
3
  export * from './color-class/color.js';
4
4
  export * from './contrast/contrast.js';
5
+ export * from './elements/vir-all-color-space-sliders.element.js';
6
+ export * from './elements/vir-color-format-sliders.element.js';
5
7
  export * from './elements/vir-color-pair.element.js';
8
+ export * from './elements/vir-color-slider.element.js';
6
9
  export * from './elements/vir-contrast-indicator.element.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@electrovir/color",
3
- "version": "1.0.0",
3
+ "version": "1.2.0",
4
4
  "description": "A wrapper for culori with an extremely simple API.",
5
5
  "keywords": [
6
6
  "color",
@@ -52,6 +52,7 @@
52
52
  "dependencies": {
53
53
  "@augment-vir/assert": "^31.57.3",
54
54
  "@augment-vir/common": "^31.57.3",
55
+ "@augment-vir/web": "^31.57.3",
55
56
  "apca-w3": "^0.1.9",
56
57
  "color-name": "^2.1.0",
57
58
  "culori": "^4.0.2",
@@ -101,7 +102,7 @@
101
102
  "typedoc": "^0.28.15",
102
103
  "typescript": "^5.9.3",
103
104
  "typescript-eslint": "^8.51.0",
104
- "vira": "^28.15.0",
105
+ "vira": "^28.16.0",
105
106
  "virmator": "^14.4.0",
106
107
  "vite": "^7.3.0"
107
108
  },