@webstudio-is/css-data 0.74.0 → 0.76.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 (43) hide show
  1. package/lib/__generated__/properties.js +2 -2
  2. package/lib/__generated__/property-value-descriptions.js +2733 -2930
  3. package/lib/cjs/__generated__/properties.js +2 -2
  4. package/lib/cjs/__generated__/property-value-descriptions.js +2733 -2930
  5. package/lib/cjs/property-parsers/box-shadow.js +128 -0
  6. package/lib/cjs/property-parsers/index.js +1 -0
  7. package/lib/cjs/property-parsers/parsers.js +3 -1
  8. package/lib/cjs/schema.js +17 -12
  9. package/lib/property-parsers/box-shadow.js +98 -0
  10. package/lib/property-parsers/index.js +1 -0
  11. package/lib/property-parsers/parsers.js +3 -1
  12. package/lib/schema.js +17 -12
  13. package/lib/types/{src/__generated__ → __generated__}/properties.d.ts +2 -2
  14. package/lib/types/__generated__/property-value-descriptions.d.ts +5446 -0
  15. package/lib/types/{src/index.d.ts → index.d.ts} +2 -2
  16. package/lib/types/property-parsers/box-shadow.d.ts +3 -0
  17. package/lib/types/{src/property-parsers → property-parsers}/index.d.ts +1 -0
  18. package/lib/types/{src/property-parsers → property-parsers}/parsers.d.ts +1 -0
  19. package/lib/types/{src/schema.d.ts → schema.d.ts} +671 -0
  20. package/package.json +5 -7
  21. package/src/__generated__/properties.ts +2 -2
  22. package/src/__generated__/property-value-descriptions.ts +3020 -3590
  23. package/src/property-parsers/background.ts +0 -1
  24. package/src/property-parsers/box-shadow.test.ts +294 -0
  25. package/src/property-parsers/box-shadow.ts +134 -0
  26. package/src/property-parsers/index.ts +1 -0
  27. package/src/property-parsers/parsers.ts +1 -0
  28. package/src/schema.ts +18 -11
  29. package/lib/types/bin/property-value-descriptions.d.ts +0 -1
  30. package/lib/types/src/__generated__/property-value-descriptions.d.ts +0 -2933
  31. /package/lib/types/{src/__generated__ → __generated__}/keyword-values.d.ts +0 -0
  32. /package/lib/types/{src/__generated__ → __generated__}/units.d.ts +0 -0
  33. /package/lib/types/{src/html-check.d.ts → html-check.d.ts} +0 -0
  34. /package/lib/types/{src/html.d.ts → html.d.ts} +0 -0
  35. /package/lib/types/{src/parse-css-value.d.ts → parse-css-value.d.ts} +0 -0
  36. /package/lib/types/{src/parse-css-value.test.d.ts → parse-css-value.test.d.ts} +0 -0
  37. /package/lib/types/{src/parse-css.d.ts → parse-css.d.ts} +0 -0
  38. /package/lib/types/{src/parse-css.test.d.ts → parse-css.test.d.ts} +0 -0
  39. /package/lib/types/{src/popularity-index.d.ts → popularity-index.d.ts} +0 -0
  40. /package/lib/types/{src/property-parsers → property-parsers}/background.d.ts +0 -0
  41. /package/lib/types/{src/property-parsers → property-parsers}/background.test.d.ts +0 -0
  42. /package/lib/types/{bin/mdn-data.d.ts → property-parsers/box-shadow.test.d.ts} +0 -0
  43. /package/lib/types/{src/property-parsers → property-parsers}/to-longhand.d.ts +0 -0
@@ -54,7 +54,6 @@ export const backgroundToLonghand = (
54
54
  backgroundColor: string | undefined;
55
55
  } => {
56
56
  const layers: string[] = [];
57
-
58
57
  let tokenStream = background.trim();
59
58
 
60
59
  tokenStream = tokenStream.endsWith(";")
@@ -0,0 +1,294 @@
1
+ import { describe, expect, test } from "@jest/globals";
2
+
3
+ import { parseBoxShadow } from "./box-shadow";
4
+
5
+ describe("parseBoxShadow", () => {
6
+ test("parses value and returns invalid when used a invalid color", () => {
7
+ expect(parseBoxShadow(`10px 10px 5px foo`)).toMatchInlineSnapshot(`
8
+ {
9
+ "type": "invalid",
10
+ "value": "10px 10px 5px foo",
11
+ }
12
+ `);
13
+ });
14
+
15
+ test("throws error when passed a value without a unit", () => {
16
+ expect(parseBoxShadow(`10 10px 5px red`)).toMatchInlineSnapshot(`
17
+ {
18
+ "type": "invalid",
19
+ "value": "10 10px 5px red",
20
+ }
21
+ `);
22
+ });
23
+
24
+ test("inset and color values can be interchanged", () => {
25
+ expect(parseBoxShadow(`inset 10px 10px 5px black;`)).toMatchInlineSnapshot(`
26
+ {
27
+ "type": "layers",
28
+ "value": [
29
+ {
30
+ "type": "tuple",
31
+ "value": [
32
+ {
33
+ "type": "keyword",
34
+ "value": "inset",
35
+ },
36
+ {
37
+ "type": "unit",
38
+ "unit": "px",
39
+ "value": 10,
40
+ },
41
+ {
42
+ "type": "unit",
43
+ "unit": "px",
44
+ "value": 10,
45
+ },
46
+ {
47
+ "type": "unit",
48
+ "unit": "px",
49
+ "value": 5,
50
+ },
51
+ {
52
+ "type": "keyword",
53
+ "value": "black",
54
+ },
55
+ ],
56
+ },
57
+ ],
58
+ }
59
+ `);
60
+ });
61
+
62
+ test("parses value when inset is used but missing blur-radius", () => {
63
+ expect(parseBoxShadow(`box-shadow: inset 5em 1em gold`))
64
+ .toMatchInlineSnapshot(`
65
+ {
66
+ "type": "layers",
67
+ "value": [
68
+ {
69
+ "type": "tuple",
70
+ "value": [
71
+ {
72
+ "type": "keyword",
73
+ "value": "inset",
74
+ },
75
+ {
76
+ "type": "unit",
77
+ "unit": "em",
78
+ "value": 5,
79
+ },
80
+ {
81
+ "type": "unit",
82
+ "unit": "em",
83
+ "value": 1,
84
+ },
85
+ {
86
+ "type": "keyword",
87
+ "value": "gold",
88
+ },
89
+ ],
90
+ },
91
+ ],
92
+ }
93
+ `);
94
+ });
95
+
96
+ test("parses value when offsetX and offsetY are used", () => {
97
+ expect(parseBoxShadow(`box-shadow: 60px -16px teal;`))
98
+ .toMatchInlineSnapshot(`
99
+ {
100
+ "type": "layers",
101
+ "value": [
102
+ {
103
+ "type": "tuple",
104
+ "value": [
105
+ {
106
+ "type": "unit",
107
+ "unit": "px",
108
+ "value": 60,
109
+ },
110
+ {
111
+ "type": "unit",
112
+ "unit": "px",
113
+ "value": -16,
114
+ },
115
+ {
116
+ "type": "keyword",
117
+ "value": "teal",
118
+ },
119
+ ],
120
+ },
121
+ ],
122
+ }
123
+ `);
124
+ });
125
+
126
+ test("parses value from figma", () => {
127
+ expect(
128
+ parseBoxShadow(
129
+ "box-shadow: 0 60px 80px rgba(0,0,0,0.60), 0 45px 26px rgba(0,0,0,0.14);"
130
+ )
131
+ ).toMatchInlineSnapshot(`
132
+ {
133
+ "type": "layers",
134
+ "value": [
135
+ {
136
+ "type": "tuple",
137
+ "value": [
138
+ {
139
+ "type": "unit",
140
+ "unit": "number",
141
+ "value": 0,
142
+ },
143
+ {
144
+ "type": "unit",
145
+ "unit": "px",
146
+ "value": 60,
147
+ },
148
+ {
149
+ "type": "unit",
150
+ "unit": "px",
151
+ "value": 80,
152
+ },
153
+ {
154
+ "alpha": 0.6,
155
+ "b": 0,
156
+ "g": 0,
157
+ "r": 0,
158
+ "type": "rgb",
159
+ },
160
+ ],
161
+ },
162
+ {
163
+ "type": "tuple",
164
+ "value": [
165
+ {
166
+ "type": "unit",
167
+ "unit": "number",
168
+ "value": 0,
169
+ },
170
+ {
171
+ "type": "unit",
172
+ "unit": "px",
173
+ "value": 45,
174
+ },
175
+ {
176
+ "type": "unit",
177
+ "unit": "px",
178
+ "value": 26,
179
+ },
180
+ {
181
+ "alpha": 0.14,
182
+ "b": 0,
183
+ "g": 0,
184
+ "r": 0,
185
+ "type": "rgb",
186
+ },
187
+ ],
188
+ },
189
+ ],
190
+ }
191
+ `);
192
+ });
193
+
194
+ test(`parses multiple layers of box-shadow property`, () => {
195
+ expect(
196
+ parseBoxShadow(`box-shadow:
197
+ 0 0 5px rgba(0, 0, 0, 0.2),
198
+ inset 0 0 10px rgba(0, 0, 0, 0.3),
199
+ 0 0 15px rgba(0, 0, 0, 0.4);
200
+ `)
201
+ ).toMatchInlineSnapshot(`
202
+ {
203
+ "type": "layers",
204
+ "value": [
205
+ {
206
+ "type": "tuple",
207
+ "value": [
208
+ {
209
+ "type": "unit",
210
+ "unit": "number",
211
+ "value": 0,
212
+ },
213
+ {
214
+ "type": "unit",
215
+ "unit": "number",
216
+ "value": 0,
217
+ },
218
+ {
219
+ "type": "unit",
220
+ "unit": "px",
221
+ "value": 5,
222
+ },
223
+ {
224
+ "alpha": 0.2,
225
+ "b": 0,
226
+ "g": 0,
227
+ "r": 0,
228
+ "type": "rgb",
229
+ },
230
+ ],
231
+ },
232
+ {
233
+ "type": "tuple",
234
+ "value": [
235
+ {
236
+ "type": "keyword",
237
+ "value": "inset",
238
+ },
239
+ {
240
+ "type": "unit",
241
+ "unit": "number",
242
+ "value": 0,
243
+ },
244
+ {
245
+ "type": "unit",
246
+ "unit": "number",
247
+ "value": 0,
248
+ },
249
+ {
250
+ "type": "unit",
251
+ "unit": "px",
252
+ "value": 10,
253
+ },
254
+ {
255
+ "alpha": 0.3,
256
+ "b": 0,
257
+ "g": 0,
258
+ "r": 0,
259
+ "type": "rgb",
260
+ },
261
+ ],
262
+ },
263
+ {
264
+ "type": "tuple",
265
+ "value": [
266
+ {
267
+ "type": "unit",
268
+ "unit": "number",
269
+ "value": 0,
270
+ },
271
+ {
272
+ "type": "unit",
273
+ "unit": "number",
274
+ "value": 0,
275
+ },
276
+ {
277
+ "type": "unit",
278
+ "unit": "px",
279
+ "value": 15,
280
+ },
281
+ {
282
+ "alpha": 0.4,
283
+ "b": 0,
284
+ "g": 0,
285
+ "r": 0,
286
+ "type": "rgb",
287
+ },
288
+ ],
289
+ },
290
+ ],
291
+ }
292
+ `);
293
+ });
294
+ });
@@ -0,0 +1,134 @@
1
+ import * as csstree from "css-tree";
2
+ import { LayersValue, TupleValue } from "@webstudio-is/css-data";
3
+ import type {
4
+ InvalidValue,
5
+ TupleValueItem,
6
+ Unit,
7
+ } from "@webstudio-is/css-data";
8
+ import { colord } from "colord";
9
+
10
+ const cssTreeTryParseValue = (input: string) => {
11
+ try {
12
+ const ast = csstree.parse(input, { context: "value" });
13
+ return ast;
14
+ } catch {
15
+ return undefined;
16
+ }
17
+ };
18
+
19
+ export const parseBoxShadow = (
20
+ boxShadow: string
21
+ ): LayersValue | InvalidValue => {
22
+ let tokenStream = boxShadow.trim();
23
+ tokenStream = tokenStream.endsWith(";")
24
+ ? tokenStream.slice(0, -1)
25
+ : tokenStream;
26
+
27
+ const cleanupKeywords = ["box-shadow:"];
28
+
29
+ for (const cleanupKeyword of cleanupKeywords) {
30
+ tokenStream = tokenStream.startsWith(cleanupKeyword)
31
+ ? tokenStream.slice(cleanupKeyword.length).trim()
32
+ : tokenStream;
33
+ }
34
+
35
+ const cssAst = cssTreeTryParseValue(tokenStream);
36
+
37
+ if (cssAst === undefined) {
38
+ return {
39
+ type: "invalid",
40
+ value: boxShadow,
41
+ };
42
+ }
43
+
44
+ const parsed = csstree.lexer.matchProperty("box-shadow", cssAst);
45
+ if (parsed.error) {
46
+ return {
47
+ type: "invalid",
48
+ value: boxShadow,
49
+ };
50
+ }
51
+
52
+ const layers: TupleValue[] = [];
53
+
54
+ csstree.walk(cssAst, (node) => {
55
+ if (node.type === "Value") {
56
+ const children = node.children;
57
+ let layer: csstree.CssNode[] = [];
58
+
59
+ for (const child of children) {
60
+ if (children.last === child) {
61
+ layer.push(child);
62
+ }
63
+
64
+ if (child.type === "Operator" || children.last === child) {
65
+ const shadow: TupleValueItem[] = [];
66
+
67
+ /**
68
+ * https://developer.mozilla.org/en-US/docs/Web/CSS/box-shadow#syntax
69
+ * `inset` and color can be at the start or end and their sequence can be anywhere.
70
+ * The rest need to foolow the sequence all the time
71
+ */
72
+
73
+ for (let index = 0; index < layer.length; index++) {
74
+ const item = layer[index];
75
+
76
+ if (item.type === "Identifier") {
77
+ shadow.push({
78
+ type: "keyword",
79
+ value: item.name,
80
+ });
81
+ }
82
+
83
+ if (item.type === "Function" || item.type === "Hash") {
84
+ const colorValue = colord(csstree.generate(item));
85
+ if (!colorValue.isValid()) {
86
+ return;
87
+ }
88
+ const rgb = colorValue.toRgb();
89
+ shadow.push({
90
+ type: "rgb",
91
+ alpha: rgb.a,
92
+ r: rgb.r,
93
+ g: rgb.g,
94
+ b: rgb.b,
95
+ });
96
+ }
97
+
98
+ if (item.type === "Dimension") {
99
+ shadow.push({
100
+ type: "unit",
101
+ value: Number(item.value),
102
+ unit: item.unit as Unit,
103
+ });
104
+ }
105
+
106
+ if (item.type === "Number") {
107
+ shadow.push({
108
+ type: "unit",
109
+ value: Number(item.value),
110
+ unit: "number",
111
+ });
112
+ }
113
+ }
114
+
115
+ layers.push({
116
+ type: "tuple",
117
+ value: shadow,
118
+ });
119
+ layer = [];
120
+ continue;
121
+ }
122
+
123
+ layer.push(child);
124
+ }
125
+ }
126
+ });
127
+
128
+ return layers.length > 0
129
+ ? {
130
+ type: "layers",
131
+ value: layers,
132
+ }
133
+ : { type: "invalid", value: boxShadow };
134
+ };
@@ -1 +1,2 @@
1
1
  export * from "./background";
2
+ export * from "./box-shadow";
@@ -2,3 +2,4 @@
2
2
  Longhand properties parsers. Each named export should match a property name.
3
3
  */
4
4
  export { parseBackgroundImage as backgroundImage } from "./background";
5
+ export { parseBoxShadow as boxShadow } from "./box-shadow";
package/src/schema.ts CHANGED
@@ -99,31 +99,38 @@ const UnsetValue = z.object({
99
99
  });
100
100
  export type UnsetValue = z.infer<typeof UnsetValue>;
101
101
 
102
- export const TupleValueItem = z.union([UnitValue, KeywordValue, UnparsedValue]);
102
+ export const TupleValueItem = z.union([
103
+ UnitValue,
104
+ KeywordValue,
105
+ UnparsedValue,
106
+ RgbValue,
107
+ ]);
103
108
  export type TupleValueItem = z.infer<typeof TupleValueItem>;
104
109
 
105
110
  export const TupleValue = z.object({
106
111
  type: z.literal("tuple"),
107
112
  value: z.array(TupleValueItem),
113
+ hidden: z.boolean().optional(),
108
114
  });
109
115
 
110
116
  export type TupleValue = z.infer<typeof TupleValue>;
111
117
 
118
+ const LayerValueItem = z.union([
119
+ UnitValue,
120
+ KeywordValue,
121
+ UnparsedValue,
122
+ ImageValue,
123
+ TupleValue,
124
+ InvalidValue,
125
+ ]);
126
+
127
+ export type LayerValueItem = z.infer<typeof LayerValueItem>;
112
128
  // To support background layers https://developer.mozilla.org/en-US/docs/Web/CSS/background
113
129
  // and similar comma separated css properties
114
130
  // InvalidValue used in case of asset not found
115
131
  export const LayersValue = z.object({
116
132
  type: z.literal("layers"),
117
- value: z.array(
118
- z.union([
119
- UnitValue,
120
- KeywordValue,
121
- UnparsedValue,
122
- ImageValue,
123
- TupleValue,
124
- InvalidValue,
125
- ])
126
- ),
133
+ value: z.array(LayerValueItem),
127
134
  });
128
135
 
129
136
  export type LayersValue = z.infer<typeof LayersValue>;
@@ -1 +0,0 @@
1
- export {};