@bhsd/codemirror-css-color-picker 7.1.1 → 8.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.
package/dist/color.d.ts CHANGED
@@ -1,10 +1,9 @@
1
- import 'color-space/hsl.js';
2
- import type { WidgetOptions, RGB, ColorData } from './types';
1
+ import type { WidgetOptions, ColorData } from './types';
3
2
  /**
4
3
  * Parses a CSS color function call expression (including `rgb()`, `rgba()`, `hsl()`, `hsla()`)
5
4
  * @param callExp the full text of the call expression, including function name and parentheses
6
5
  */
7
- export declare const parseCallExpression: (callExp: string) => ColorData | false | undefined;
6
+ export declare const parseCallExpression: (callExp: string) => ColorData | false;
8
7
  /**
9
8
  * Parses a hex color literal (e.g. `#ff0000`, `#f00`, `#ff000080`, `#f008`)
10
9
  * @param colorLiteral the hex color literal text
@@ -13,9 +12,8 @@ export declare const parseColorLiteral: (colorLiteral: string) => ColorData | fa
13
12
  /**
14
13
  * Parses a named color (e.g. `red`, `blue`, `rebeccapurple`)
15
14
  * @param colorName the named color text
16
- * @param colors an object mapping color names to RGB values
17
15
  */
18
- export declare const parseNamedColor: (colorName: string, colors?: Record<string, RGB>) => ColorData | false;
16
+ export declare const parseNamedColor: (colorName: string) => ColorData | false;
19
17
  export declare const getDelimiter: (legacy: boolean, spaced: boolean) => string;
20
18
  export declare const alphaToString: (alpha: number, legacy: boolean, spaced: boolean) => string;
21
- export declare const colorToString: ({ color, alpha, colorType, legacy, spaced }: WidgetOptions, value: string, colors?: Record<string, RGB>) => string | false;
19
+ export declare const colorToString: ({ color, alpha, colorType, legacy, spaced }: WidgetOptions, value: string) => string | false;
package/dist/color.js ADDED
@@ -0,0 +1,84 @@
1
+ import { intToHex, numToHex, rgba, hsla, colorsNamed } from '@bhsd/common';
2
+ const parse = (color) => {
3
+ const [r, g, b, alpha] = rgba(color);
4
+ return alpha !== undefined && {
5
+ color: [r, g, b],
6
+ alpha,
7
+ };
8
+ };
9
+ /**
10
+ * Parses a CSS color function call expression (including `rgb()`, `rgba()`, `hsl()`, `hsla()`)
11
+ * @param callExp the full text of the call expression, including function name and parentheses
12
+ */
13
+ export const parseCallExpression = (callExp) => {
14
+ const color = parse(callExp);
15
+ return color && {
16
+ ...color,
17
+ colorType: callExp.split('(', 1)[0].toLowerCase(),
18
+ legacy: callExp.includes(','),
19
+ spaced: /\s/u.test(callExp),
20
+ };
21
+ };
22
+ /**
23
+ * Parses a hex color literal (e.g. `#ff0000`, `#f00`, `#ff000080`, `#f008`)
24
+ * @param colorLiteral the hex color literal text
25
+ */
26
+ export const parseColorLiteral = (colorLiteral) => {
27
+ const color = parse(colorLiteral), { length } = colorLiteral;
28
+ return color && {
29
+ ...color,
30
+ colorType: 'hex',
31
+ legacy: length === 4 || length === 7,
32
+ spaced: false,
33
+ };
34
+ };
35
+ /**
36
+ * Parses a named color (e.g. `red`, `blue`, `rebeccapurple`)
37
+ * @param colorName the named color text
38
+ */
39
+ export const parseNamedColor = (colorName) => {
40
+ const color = parse(colorName);
41
+ return color && {
42
+ ...color,
43
+ colorType: 'named',
44
+ legacy: true,
45
+ spaced: false,
46
+ };
47
+ };
48
+ export const getDelimiter = (legacy, spaced) => legacy ? `,${spaced ? ' ' : ''}` : ' ';
49
+ export const alphaToString = (alpha, legacy, spaced) => alpha === 1
50
+ ? ''
51
+ : (legacy ? `,${spaced ? ' ' : ''}` : ' / ')
52
+ + String(alpha === 0 ? alpha : Number(alpha.toFixed(2)));
53
+ const hexToName = new Map(Object.entries(colorsNamed).map(([key, val]) => [`#${intToHex(val, 6)}`, key]));
54
+ export const colorToString = ({ color, alpha, colorType, legacy, spaced }, value) => {
55
+ const currentColor = rgba(value).slice(0, 3);
56
+ if (currentColor.every((c, i) => c === Math.round(color[i]))) {
57
+ return false;
58
+ }
59
+ const delimiter = getDelimiter(legacy, spaced);
60
+ switch (colorType) {
61
+ case 'rgba':
62
+ case 'rgb':
63
+ return `${colorType}(${currentColor.join(delimiter)}${alphaToString(alpha, legacy, spaced)})`;
64
+ case 'hsla':
65
+ case 'hsl': {
66
+ const [h, s, l] = hsla(value);
67
+ return `${colorType}(${[h, `${s}%`, `${l}%`].join(delimiter)}${alphaToString(alpha, legacy, spaced)})`;
68
+ }
69
+ case 'named':
70
+ if (alpha === 1) {
71
+ // If the color is an exact match for another named color, prefer retaining name
72
+ const colorName = hexToName.get(value);
73
+ if (colorName) {
74
+ return colorName;
75
+ }
76
+ }
77
+ // fall through
78
+ case 'hex':
79
+ // hex color literal
80
+ return value + (alpha === 1 ? '' : numToHex(alpha));
81
+ default:
82
+ throw new Error('Unknown color type');
83
+ }
84
+ };
package/dist/css.js ADDED
@@ -0,0 +1,45 @@
1
+ import { NodeProp } from '@lezer/common';
2
+ /**
3
+ * Discovers colors in CSS code
4
+ * @implements
5
+ */
6
+ export const discoverColorsInCSS = (tree, { from, to, name: typeName }, doc) => {
7
+ switch (typeName) {
8
+ case 'UnquotedAttributeValue':
9
+ case 'AttributeValue': {
10
+ // CSS nested in an HTML attribute value
11
+ const overlayTree = tree.resolveInner(from, 0).tree?.prop(NodeProp.mounted)?.tree;
12
+ if (overlayTree?.type.name !== 'Styles') {
13
+ // Skip if the attribute value is not a style attribute, or if there is no mounted tree
14
+ return false;
15
+ }
16
+ const ret = [],
17
+ // Account for the quotation mark in AttributeValue
18
+ offset = from + (typeName === 'AttributeValue' ? 1 : 0);
19
+ overlayTree.iterate({
20
+ from: 0,
21
+ to: overlayTree.length,
22
+ enter({ name, from: overlayFrom, to: overlayTo }) {
23
+ const widgetOptions = discoverColorsInCSS(tree, {
24
+ from: offset + overlayFrom,
25
+ to: offset + overlayTo,
26
+ name,
27
+ }, doc);
28
+ if (widgetOptions) {
29
+ if (Array.isArray(widgetOptions)) {
30
+ throw new Error('Unexpected nested overlays');
31
+ }
32
+ ret.push(widgetOptions);
33
+ }
34
+ },
35
+ });
36
+ return ret;
37
+ }
38
+ case 'CallExpression':
39
+ case 'ColorLiteral':
40
+ case 'ValueName':
41
+ return { from, to };
42
+ default:
43
+ return undefined;
44
+ }
45
+ };
package/dist/index.d.ts CHANGED
@@ -1,14 +1,11 @@
1
- import namedColors from 'color-name';
2
1
  import type { Extension } from '@codemirror/state';
3
- import type { DiscoverColors, WidgetOptions, RGB } from './types';
4
- export { namedColors };
2
+ import type { DiscoverColors, WidgetOptions } from './types';
5
3
  export type { DiscoverColors, WidgetOptions };
6
4
  export declare const wrapperClassName = "cm-css-color-picker-wrapper";
7
5
  /**
8
6
  * Factory function to create a color picker plugin with the given options
9
7
  * @param discoverColors the function to discover colors in a syntax node; return `false` to skip children
10
- * @param colors an optional object of color names mapping to RGB values
11
8
  */
12
- export declare const makeColorPicker: (discoverColors: DiscoverColors, colors?: Record<string, RGB>) => Extension;
9
+ export declare const makeColorPicker: (discoverColors: DiscoverColors) => Extension;
13
10
  /** Default color picker plugin for CSS and HTML */
14
11
  export declare const colorPicker: Extension;
package/dist/index.js CHANGED
@@ -1,410 +1,183 @@
1
- import { EditorView, WidgetType, ViewPlugin, Decoration } from "@codemirror/view";
2
- import { syntaxTree } from "@codemirror/language";
3
- import namedColors from "color-name";
4
- import { intToHex } from "@bhsd/common";
5
-
6
- import { NodeProp } from "@lezer/common";
7
- var discoverColorsInCSS = (tree, { from, to, name: typeName }, doc) => {
8
- switch (typeName) {
9
- case "UnquotedAttributeValue":
10
- case "AttributeValue": {
11
- const overlayTree = tree.resolveInner(from, 0).tree?.prop(NodeProp.mounted)?.tree;
12
- if (overlayTree?.type.name !== "Styles") {
13
- return false;
14
- }
15
- const ret = [], offset = from + (typeName === "AttributeValue" ? 1 : 0);
16
- overlayTree.iterate({
17
- from: 0,
18
- to: overlayTree.length,
19
- enter({ name, from: overlayFrom, to: overlayTo }) {
20
- const widgetOptions = discoverColorsInCSS(
21
- tree,
22
- {
23
- from: offset + overlayFrom,
24
- to: offset + overlayTo,
25
- name
26
- },
27
- doc
28
- );
29
- if (widgetOptions) {
30
- if (Array.isArray(widgetOptions)) {
31
- throw new Error("Unexpected nested overlays");
32
- }
33
- ret.push(widgetOptions);
34
- }
1
+ import { EditorView, WidgetType, ViewPlugin, Decoration } from '@codemirror/view';
2
+ import { syntaxTree } from '@codemirror/language';
3
+ import { intToHex } from '@bhsd/common';
4
+ import { discoverColorsInCSS } from './css.js';
5
+ import { colorToString, parseCallExpression, parseColorLiteral, parseNamedColor } from './color.js';
6
+ export const wrapperClassName = 'cm-css-color-picker-wrapper';
7
+ const pickerState = new WeakMap();
8
+ class ColorPickerWidget extends WidgetType {
9
+ /** @class */
10
+ constructor(state, readOnly) {
11
+ super();
12
+ this.state = state;
13
+ this.readonly = readOnly;
14
+ }
15
+ /** @override */
16
+ eq(other) {
17
+ return other.readonly === this.readonly
18
+ && other.state.from === this.state.from
19
+ && other.state.to === this.state.to
20
+ && other.state.colorType === this.state.colorType
21
+ && other.state.color === this.state.color
22
+ && other.state.alpha === this.state.alpha
23
+ && other.state.legacy === this.state.legacy
24
+ && other.state.spaced === this.state.spaced;
25
+ }
26
+ /** @override */
27
+ toDOM() {
28
+ const picker = document.createElement('input');
29
+ picker.type = 'color';
30
+ picker.value = `#${this.state.color.map(c => intToHex(c)).join('')}`;
31
+ picker.style.opacity = String(this.state.alpha);
32
+ if (this.readonly || this.state.colorType === 'unknown') {
33
+ picker.disabled = true;
35
34
  }
36
- });
37
- return ret;
35
+ pickerState.set(picker, this.state);
36
+ const wrapper = document.createElement('span');
37
+ wrapper.className = wrapperClassName;
38
+ wrapper.append(picker);
39
+ return wrapper;
38
40
  }
39
- case "CallExpression":
40
- case "ColorLiteral":
41
- case "ValueName":
42
- return { from, to };
43
- default:
44
- return void 0;
45
- }
46
- };
47
-
48
- import rgb2 from "color-space/rgb.js";
49
- import "color-space/hsl.js";
50
-
51
- var names = {};
52
- var color_parse_default = parse;
53
- var baseHues = {
54
- red: 0,
55
- orange: 60,
56
- yellow: 120,
57
- green: 180,
58
- blue: 240,
59
- purple: 300
60
- };
61
- function parse(cstr) {
62
- var m, parts = [], alpha = 1, space;
63
- if (typeof cstr === "number") {
64
- return { space: "rgb", values: [cstr >>> 16, (cstr & 65280) >>> 8, cstr & 255], alpha: 1 };
65
- }
66
- if (typeof cstr === "number") return { space: "rgb", values: [cstr >>> 16, (cstr & 65280) >>> 8, cstr & 255], alpha: 1 };
67
- cstr = String(cstr).toLowerCase();
68
- if (names[cstr]) {
69
- parts = names[cstr].slice();
70
- space = "rgb";
71
- } else if (cstr === "transparent") {
72
- alpha = 0;
73
- space = "rgb";
74
- parts = [0, 0, 0];
75
- } else if (cstr[0] === "#") {
76
- var base = cstr.slice(1);
77
- var size = base.length;
78
- var isShort = size <= 4;
79
- alpha = 1;
80
- if (isShort) {
81
- parts = [
82
- parseInt(base[0] + base[0], 16),
83
- parseInt(base[1] + base[1], 16),
84
- parseInt(base[2] + base[2], 16)
85
- ];
86
- if (size === 4) {
87
- alpha = parseInt(base[3] + base[3], 16) / 255;
88
- }
89
- } else {
90
- parts = [
91
- parseInt(base[0] + base[1], 16),
92
- parseInt(base[2] + base[3], 16),
93
- parseInt(base[4] + base[5], 16)
94
- ];
95
- if (size === 8) {
96
- alpha = parseInt(base[6] + base[7], 16) / 255;
97
- }
41
+ /** @override */
42
+ ignoreEvent(e) {
43
+ return e.type !== 'change';
98
44
  }
99
- if (!parts[0]) parts[0] = 0;
100
- if (!parts[1]) parts[1] = 0;
101
- if (!parts[2]) parts[2] = 0;
102
- space = "rgb";
103
- } else if (m = /^((?:rgba?|hs[lvb]a?|hwba?|cmyk?|xy[zy]|gray|lab|lchu?v?|[ly]uv|lms|oklch|oklab|color))\s*\(([^\)]*)\)/.exec(cstr)) {
104
- var name = m[1];
105
- space = name.replace(/a$/, "");
106
- var dims = space === "cmyk" ? 4 : space === "gray" ? 1 : 3;
107
- parts = m[2].trim().split(/\s*[,\/]\s*|\s+/);
108
- if (space === "color") space = parts.shift();
109
- parts = parts.map(function(x, i) {
110
- if (x[x.length - 1] === "%") {
111
- x = parseFloat(x) / 100;
112
- if (i === 3) return x;
113
- if (space === "rgb") return x * 255;
114
- if (space[0] === "h") return x * 100;
115
- if (space[0] === "l" && !i) return x * 100;
116
- if (space === "lab") return x * 125;
117
- if (space === "lch") return i < 2 ? x * 150 : x * 360;
118
- if (space[0] === "o" && !i) return x;
119
- if (space === "oklab") return x * 0.4;
120
- if (space === "oklch") return i < 2 ? x * 0.4 : x * 360;
121
- return x;
122
- }
123
- if (space[i] === "h" || i === 2 && space[space.length - 1] === "h") {
124
- if (baseHues[x] !== void 0) return baseHues[x];
125
- if (x.endsWith("deg")) return parseFloat(x);
126
- if (x.endsWith("turn")) return parseFloat(x) * 360;
127
- if (x.endsWith("grad")) return parseFloat(x) * 360 / 400;
128
- if (x.endsWith("rad")) return parseFloat(x) * 180 / Math.PI;
129
- }
130
- if (x === "none") return 0;
131
- return parseFloat(x);
132
- });
133
- alpha = parts.length > dims ? parts.pop() : 1;
134
- } else if (/[0-9](?:\s|\/|,)/.test(cstr)) {
135
- parts = cstr.match(/([0-9]+)/g).map(function(value) {
136
- return parseFloat(value);
137
- });
138
- space = cstr.match(/([a-z])/ig)?.join("")?.toLowerCase() || "rgb";
139
- }
140
- return {
141
- space,
142
- values: parts,
143
- alpha
144
- };
145
45
  }
146
-
147
- import rgb from "color-space/rgb.js";
148
- import hsl from "color-space/hsl.js";
149
- function rgba(color) {
150
- if (Array.isArray(color) && color.raw) color = String.raw(...arguments);
151
- if (color instanceof Number) color = +color;
152
- var values, i, l;
153
- var parsed = color_parse_default(color);
154
- if (!parsed.space) return [];
155
- const min = parsed.space[0] === "h" ? hsl.min : rgb.min;
156
- const max = parsed.space[0] === "h" ? hsl.max : rgb.max;
157
- values = Array(3);
158
- values[0] = Math.min(Math.max(parsed.values[0], min[0]), max[0]);
159
- values[1] = Math.min(Math.max(parsed.values[1], min[1]), max[1]);
160
- values[2] = Math.min(Math.max(parsed.values[2], min[2]), max[2]);
161
- if (parsed.space[0] === "h") {
162
- values = hsl.rgb(values);
163
- }
164
- values.push(Math.min(Math.max(parsed.alpha, 0), 1));
165
- return values;
166
- }
167
-
168
- import { numToHex } from "@bhsd/common";
169
- var rgbCallExpRegex = /^rgba?\(\s*(?:\d*\.)?\d+%?(?:\s|\s*,)\s*(?:\d*\.)?\d+%?(?:\s|\s*,)\s*(?:\d*\.)?\d+%?\s*(?:[,/]\s*(?:\d*\.)?\d+%?\s*)?\)$/iu;
170
- var hslCallExpRegex = /^hsla?\(\s*(?:\d*\.)?\d+(?:deg|g?rad|turn)?(?:\s|\s*,)\s*(?:\d*\.)?\d+%?(?:\s|\s*,)\s*(?:\d*\.)?\d+%?\s*(?:[,/]\s*(?:\d*\.)?\d+%?\s*)?\)$/iu;
171
- var hexRegex = /^#(?:[\da-f]{3,4}|(?:[\da-f]{2}){3,4})$/iu;
172
- var parseCallExpression = (callExp) => {
173
- const fn = callExp.split("(", 1)[0].toLowerCase();
174
- switch (fn) {
175
- case "rgba":
176
- case "rgb":
177
- if (!rgbCallExpRegex.test(callExp)) {
178
- return void 0;
179
- }
180
- break;
181
- case "hsla":
182
- case "hsl":
183
- if (!hslCallExpRegex.test(callExp)) {
184
- return void 0;
185
- }
186
- break;
187
- default:
188
- return void 0;
189
- }
190
- const [r, g, b, alpha] = rgba(callExp);
191
- return alpha !== void 0 && {
192
- colorType: fn,
193
- color: [r, g, b].map(Math.round),
194
- alpha,
195
- legacy: callExp.includes(","),
196
- spaced: /\s/u.test(callExp)
197
- };
198
- };
199
- var parseColorLiteral = (colorLiteral) => {
200
- if (!hexRegex.test(colorLiteral)) {
201
- return false;
202
- }
203
- const [r, g, b, alpha] = rgba(colorLiteral), { length } = colorLiteral;
204
- return alpha !== void 0 && {
205
- colorType: "hex",
206
- color: [r, g, b],
207
- alpha,
208
- legacy: length === 4 || length === 7,
209
- spaced: false
210
- };
211
- };
212
- var parseNamedColor = (colorName, colors) => {
213
- const lcName = colorName.toLowerCase();
214
- if (!colors || !Object.hasOwn(colors, lcName)) {
215
- return false;
216
- }
217
- const color = colors[lcName];
218
- return {
219
- colorType: "named",
220
- color,
221
- alpha: 1,
222
- legacy: true,
223
- spaced: false
224
- };
225
- };
226
- var getDelimiter = (legacy, spaced) => legacy ? `,${spaced ? " " : ""}` : " ";
227
- var alphaToString = (alpha, legacy, spaced) => alpha === 1 ? "" : (legacy ? `,${spaced ? " " : ""}` : " / ") + String(alpha === 0 ? alpha : Number(alpha.toFixed(2)));
228
- var colorToString = ({ color, alpha, colorType, legacy, spaced }, value, colors) => {
229
- const currentColor = rgba(value).slice(0, 3);
230
- if (currentColor.every((c, i) => c === Math.round(color[i]))) {
231
- return false;
232
- }
233
- const delimiter = getDelimiter(legacy, spaced);
234
- switch (colorType) {
235
- case "rgba":
236
- case "rgb":
237
- return `${colorType}(${currentColor.join(delimiter)}${alphaToString(alpha, legacy, spaced)})`;
238
- case "hsla":
239
- case "hsl": {
240
- const [h, s, l] = rgb2.hsl(currentColor.slice(0, 3));
241
- return `${colorType}(${[Math.round(h), `${Math.round(s)}%`, `${Math.round(l)}%`].join(delimiter)}${alphaToString(alpha, legacy, spaced)})`;
242
- }
243
- case "named":
244
- if (colors && alpha === 1) {
245
- const colorName = Object.entries(colors).find(([, colorValues]) => colorValues.every((c, i) => c === currentColor[i]))?.[0];
246
- if (colorName) {
247
- return colorName;
248
- }
249
- }
250
- // fall through
251
- case "hex":
252
- return value + (alpha === 1 ? "" : numToHex(alpha));
253
- default:
254
- throw new Error("Unknown color type");
255
- }
256
- };
257
-
258
- var wrapperClassName = "cm-css-color-picker-wrapper";
259
- var pickerState = /* @__PURE__ */ new WeakMap();
260
- var ColorPickerWidget = class extends WidgetType {
261
- /** @class */
262
- constructor(state, readOnly) {
263
- super();
264
- this.state = state;
265
- this.readonly = readOnly;
266
- }
267
- /** @override */
268
- eq(other) {
269
- return other.readonly === this.readonly && other.state.from === this.state.from && other.state.to === this.state.to && other.state.colorType === this.state.colorType && other.state.color === this.state.color && other.state.alpha === this.state.alpha && other.state.legacy === this.state.legacy && other.state.spaced === this.state.spaced;
270
- }
271
- /** @override */
272
- toDOM() {
273
- const picker = document.createElement("input");
274
- picker.type = "color";
275
- picker.value = `#${this.state.color.map((c) => intToHex(c)).join("")}`;
276
- if (this.readonly || this.state.colorType === "unknown") {
277
- picker.disabled = true;
46
+ /**
47
+ * Compute color picker decorations
48
+ * @param view the editor view for which to compute decorations
49
+ * @param discoverColors the function to discover colors in a syntax node; return `false` to skip children
50
+ */
51
+ const colorPickersDecorations = (view, discoverColors) => {
52
+ const widgets = [], { state, visibleRanges } = view, tree = syntaxTree(state);
53
+ for (const { from, to } of visibleRanges) {
54
+ tree.iterate({
55
+ from,
56
+ to,
57
+ enter(node) {
58
+ const widgetOptions = discoverColors(tree, node, state.doc);
59
+ if (!widgetOptions) {
60
+ return widgetOptions;
61
+ }
62
+ for (const wo of Array.isArray(widgetOptions) ? widgetOptions : [widgetOptions]) {
63
+ if (wo.colorType !== 'unknown') {
64
+ const value = state.sliceDoc(wo.from, wo.to);
65
+ let data;
66
+ if (value.includes('(')) {
67
+ data = parseCallExpression(value);
68
+ }
69
+ else if (value.startsWith('#')) {
70
+ data = parseColorLiteral(value);
71
+ }
72
+ else {
73
+ data = parseNamedColor(value);
74
+ }
75
+ if (!data) {
76
+ continue;
77
+ }
78
+ Object.assign(wo, data);
79
+ }
80
+ widgets.push(Decoration.widget({
81
+ widget: new ColorPickerWidget(wo, state.readOnly),
82
+ side: 1,
83
+ }).range(wo.from));
84
+ }
85
+ return undefined;
86
+ },
87
+ });
278
88
  }
279
- pickerState.set(picker, this.state);
280
- const wrapper = document.createElement("span");
281
- wrapper.className = wrapperClassName;
282
- wrapper.append(picker);
283
- return wrapper;
284
- }
285
- /** @override */
286
- ignoreEvent(e) {
287
- return e.type !== "change";
288
- }
289
- };
290
- var colorPickersDecorations = (view, discoverColors, colors) => {
291
- const widgets = [], { state, visibleRanges } = view, tree = syntaxTree(state);
292
- for (const { from, to } of visibleRanges) {
293
- tree.iterate({
294
- from,
295
- to,
296
- enter(node) {
297
- const widgetOptions = discoverColors(tree, node, state.doc);
298
- if (!widgetOptions) {
299
- return widgetOptions;
300
- }
301
- for (const wo of Array.isArray(widgetOptions) ? widgetOptions : [widgetOptions]) {
302
- if (wo.colorType !== "unknown") {
303
- const value = state.sliceDoc(wo.from, wo.to);
304
- let data;
305
- if (value.includes("(")) {
306
- data = parseCallExpression(value);
307
- } else if (value.startsWith("#")) {
308
- data = parseColorLiteral(value);
309
- } else {
310
- data = parseNamedColor(value, colors);
311
- }
312
- if (!data) {
313
- continue;
314
- }
315
- Object.assign(wo, data);
316
- }
317
- widgets.push(
318
- Decoration.widget({
319
- widget: new ColorPickerWidget(wo, state.readOnly),
320
- side: 1
321
- }).range(wo.from)
322
- );
323
- }
324
- return void 0;
325
- }
326
- });
327
- }
328
- return Decoration.set(widgets);
89
+ return Decoration.set(widgets);
329
90
  };
330
- var colorPickerTheme = EditorView.baseTheme({
331
- [`.${wrapperClassName}`]: {
332
- display: "inline-block",
333
- marginLeft: "0.6ch",
334
- marginRight: "0.6ch",
335
- height: "1em",
336
- width: "1em",
337
- transform: "translateY(0.1em)",
338
- '&>input[type="color"]': {
339
- height: "100%",
340
- width: "100%",
341
- padding: 0,
342
- appearance: "none",
343
- border: "none",
344
- borderRadius: 0,
345
- outline: "1px solid #ddd",
346
- "&:enabled": {
347
- cursor: "pointer"
348
- },
349
- "&::-webkit-color-swatch-wrapper": {
350
- padding: 0
351
- },
352
- "&::-webkit-color-swatch": {
353
- border: "none",
354
- borderRadius: 0
355
- },
356
- "&::-moz-color-swatch": {
357
- border: "none",
358
- borderRadius: 0
359
- }
360
- }
361
- }
91
+ const getBgImage = (fg, bg) => `linear-gradient(45deg, ${fg} 25%, transparent 25%, transparent 75%, ${fg} 75%),
92
+ linear-gradient(45deg, ${fg} 25%, ${bg} 25%, ${bg} 75%, ${fg} 75%)`;
93
+ const colorPickerTheme = EditorView.baseTheme({
94
+ [`.${wrapperClassName}`]: {
95
+ display: 'inline-block',
96
+ marginLeft: '0.6ch',
97
+ marginRight: '0.6ch',
98
+ height: '1em',
99
+ width: '1em',
100
+ outline: '1px solid #ddd',
101
+ position: 'relative',
102
+ transform: 'translateY(0.1em)',
103
+ backgroundSize: '0.4em 0.4em',
104
+ backgroundPosition: '0 0, 0.2em 0.2em',
105
+ '&>input[type="color"]': {
106
+ height: '100%',
107
+ width: '100%',
108
+ padding: 0,
109
+ appearance: 'none',
110
+ border: 'none',
111
+ borderRadius: 0,
112
+ position: 'absolute',
113
+ top: 0,
114
+ left: 0,
115
+ '&:enabled': {
116
+ cursor: 'pointer',
117
+ },
118
+ '&::-webkit-color-swatch-wrapper': {
119
+ padding: 0,
120
+ },
121
+ '&::-webkit-color-swatch': {
122
+ border: 'none',
123
+ borderRadius: 0,
124
+ },
125
+ '&::-moz-color-swatch': {
126
+ border: 'none',
127
+ borderRadius: 0,
128
+ },
129
+ },
130
+ },
131
+ [`&light .${wrapperClassName}`]: {
132
+ backgroundImage: getBgImage('#aaa', '#fff'),
133
+ },
134
+ [`&dark .${wrapperClassName}`]: {
135
+ backgroundImage: getBgImage('#888', '#000'),
136
+ },
362
137
  });
363
- var makeColorPicker = (discoverColors, colors) => [
364
- ViewPlugin.fromClass(
365
- class ColorPickerViewPlugin {
366
- /** @class */
367
- constructor(view) {
368
- this.readOnly = view.state.readOnly;
369
- this.decorations = colorPickersDecorations(view, discoverColors, colors);
370
- }
371
- /** @implements */
372
- update({ docChanged, viewportChanged, view }) {
373
- const { readOnly } = view.state;
374
- if (docChanged || viewportChanged || readOnly !== this.readOnly) {
375
- this.readOnly = readOnly;
376
- this.decorations = colorPickersDecorations(view, discoverColors, colors);
138
+ /**
139
+ * Factory function to create a color picker plugin with the given options
140
+ * @param discoverColors the function to discover colors in a syntax node; return `false` to skip children
141
+ */
142
+ export const makeColorPicker = (discoverColors) => [
143
+ ViewPlugin.fromClass(class ColorPickerViewPlugin {
144
+ /** @class */
145
+ constructor(view) {
146
+ this.readOnly = view.state.readOnly;
147
+ this.decorations = colorPickersDecorations(view, discoverColors);
377
148
  }
378
- }
379
- },
380
- {
381
- decorations(v) {
382
- return v.decorations;
383
- },
384
- eventHandlers: {
385
- change(e, view) {
386
- const target = e.target;
387
- if (target.nodeName !== "INPUT" || target.type !== "color" || !target.parentElement?.classList.contains(wrapperClassName)) {
388
- return false;
389
- }
390
- const state = pickerState.get(target), insert = colorToString(state, target.value, colors);
391
- if (insert) {
392
- const { from, to } = state;
393
- view.dispatch({
394
- changes: { from, to, insert }
395
- });
396
- }
397
- return true;
149
+ /** @implements */
150
+ update({ docChanged, viewportChanged, view }) {
151
+ const { readOnly } = view.state;
152
+ if (docChanged || viewportChanged || readOnly !== this.readOnly) {
153
+ this.readOnly = readOnly;
154
+ this.decorations = colorPickersDecorations(view, discoverColors);
155
+ }
398
156
  }
399
- }
400
- }
401
- ),
402
- colorPickerTheme
157
+ }, {
158
+ decorations(v) {
159
+ return v.decorations;
160
+ },
161
+ eventHandlers: {
162
+ change(e, view) {
163
+ const target = e.target;
164
+ if (target.nodeName !== 'INPUT'
165
+ || target.type !== 'color'
166
+ || !target.parentElement?.classList.contains(wrapperClassName)) {
167
+ return false;
168
+ }
169
+ const state = pickerState.get(target), insert = colorToString(state, target.value);
170
+ if (insert) {
171
+ const { from, to } = state;
172
+ view.dispatch({
173
+ changes: { from, to, insert },
174
+ });
175
+ }
176
+ return true;
177
+ },
178
+ },
179
+ }),
180
+ colorPickerTheme,
403
181
  ];
404
- var colorPicker = /* @__PURE__ */ makeColorPicker(discoverColorsInCSS, namedColors);
405
- export {
406
- colorPicker,
407
- makeColorPicker,
408
- namedColors,
409
- wrapperClassName
410
- };
182
+ /** Default color picker plugin for CSS and HTML */
183
+ export const colorPicker = /* #__PURE__ */ makeColorPicker(discoverColorsInCSS);
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bhsd/codemirror-css-color-picker",
3
- "version": "7.1.1",
3
+ "version": "8.0.0",
4
4
  "description": "Enables a color picker input next to CSS colors",
5
5
  "homepage": "https://github.com/bhsd-harry/Codemirror-CSS-color-picker#readme",
6
6
  "bugs": {
@@ -17,8 +17,7 @@
17
17
  },
18
18
  "type": "module",
19
19
  "files": [
20
- "/dist/index.js",
21
- "/dist/*.d.ts"
20
+ "/dist/"
22
21
  ],
23
22
  "main": "dist/index.js",
24
23
  "types": "dist/index.d.ts",
@@ -29,32 +28,29 @@
29
28
  "lint:ts": "tsc --noEmit && eslint --cache .",
30
29
  "lint:md": "markdownlint-cli2 '**/*.md'",
31
30
  "lint": "npm run lint:ts && npm run lint:md",
32
- "build": "tsc && node build.js && gsed '/^\\/\\/ /d' build/index.js > dist/index.js && eslint --no-config-lookup -c eslint.dist.mjs dist/index.js",
31
+ "build": "tsc && eslint --no-config-lookup -c eslint.dist.mjs dist/*.js",
33
32
  "build:test": "tsc --project test/tsconfig.json && npm test",
34
33
  "test": "mocha"
35
34
  },
35
+ "peerDependencies": {
36
+ "@codemirror/language": "^6.0.0",
37
+ "@codemirror/view": "^6.0.0"
38
+ },
36
39
  "dependencies": {
37
- "@bhsd/common": "^2.2.1",
38
- "@codemirror/language": "^6.12.3",
39
- "@codemirror/state": "^6.6.0",
40
- "@codemirror/view": "^6.42.1",
41
- "color-name": "~2.0.2",
42
- "color-space": "^2.3.2"
40
+ "@bhsd/common": "^3.0.0"
43
41
  },
44
42
  "devDependencies": {
45
- "@bhsd/code-standard": "^2.5.0",
43
+ "@bhsd/code-standard": "^2.7.0",
44
+ "@codemirror/language": "^6.12.3",
46
45
  "@codemirror/lang-css": "^6.3.1",
47
46
  "@codemirror/lang-html": "^6.4.11",
48
- "@types/color-name": "^2.0.0",
49
- "@types/color-rgba": "^2.1.3",
47
+ "@codemirror/state": "^6.6.0",
48
+ "@codemirror/view": "^6.42.1",
50
49
  "@types/mocha": "^10.0.10",
51
- "@types/node": "^25.9.0",
52
- "@typescript-eslint/parser": "^8.59.4",
53
- "color-rgba": "^3.0.0",
54
50
  "esbuild": "^0.28.0",
55
- "eslint": "^10.4.0",
51
+ "eslint": "^10.4.1",
56
52
  "markdownlint-cli2": "^0.22.1",
57
- "mocha": "^11.7.5",
53
+ "mocha": "^11.7.6",
58
54
  "typescript": "^6.0.3"
59
55
  }
60
56
  }