@css-hooks/core 1.8.1 → 2.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/esm/index.js CHANGED
@@ -1,268 +1,287 @@
1
- function isHookSpec(x) {
2
- if (!x) {
3
- return false;
4
- }
5
- if (typeof x === "string") {
6
- return (x.startsWith(":") ||
7
- x.startsWith("@media ") ||
8
- x.startsWith("@container ") ||
9
- x.startsWith("@supports ") ||
10
- x.includes("&"));
11
- }
12
- if (typeof x === "object") {
13
- if ("or" in x && x.or instanceof Array) {
14
- return !x.or.some(xx => !isHookSpec(xx));
15
- }
16
- if ("and" in x && x.and instanceof Array) {
17
- return !x.and.some(xx => !isHookSpec(xx));
18
- }
19
- }
20
- return false;
21
- }
22
- /** @internal */
23
- export function genericStringify(_, value) {
24
- if (typeof value === "string") {
25
- return value;
26
- }
27
- if (typeof value === "number") {
28
- return `${value}`;
29
- }
30
- return null;
1
+ // @ts-nocheck
2
+
3
+ const helpers = {
4
+ and: (...and) => ({ and }),
5
+ or: (...or) => ({ or }),
6
+ not: not => ({ not }),
7
+ };
8
+
9
+ function genericStringify(_, value) {
10
+ if (typeof value === "string") {
11
+ return value;
12
+ }
13
+
14
+ if (typeof value === "number") {
15
+ return `${value}`;
16
+ }
17
+
18
+ return null;
31
19
  }
20
+
32
21
  function hash(obj) {
33
- const jsonString = JSON.stringify(obj);
34
- let hashValue = 0;
35
- for (let i = 0; i < jsonString.length; i++) {
36
- const charCode = jsonString.charCodeAt(i);
37
- hashValue = (hashValue << 5) - hashValue + charCode;
38
- hashValue &= 0x7fffffff;
22
+ const jsonString = JSON.stringify(obj);
23
+
24
+ let hashValue = 0;
25
+
26
+ for (let i = 0; i < jsonString.length; i++) {
27
+ const charCode = jsonString.charCodeAt(i);
28
+ hashValue = (hashValue << 5) - hashValue + charCode;
29
+ hashValue &= 0x7fffffff;
30
+ }
31
+
32
+ return hashValue.toString(36);
33
+ }
34
+
35
+ function normalizeCondition(cond) {
36
+ if (!cond) {
37
+ return undefined;
38
+ }
39
+ if (typeof cond === "string") {
40
+ return cond;
41
+ }
42
+ if (typeof cond !== "object") {
43
+ return undefined;
44
+ }
45
+ if ("not" in cond) {
46
+ if (!cond.not) {
47
+ return undefined;
39
48
  }
40
- return hashValue.toString(36);
49
+ if (cond.not.not) {
50
+ return normalizeCondition(cond.not.not);
51
+ }
52
+ const inner = normalizeCondition(cond.not);
53
+ return inner ? { not: inner } : undefined;
54
+ }
55
+ const [operator] = Object.keys(cond);
56
+ const [head, ...tail] = cond[operator].map(normalizeCondition).filter(x => x);
57
+ if (!head) {
58
+ return undefined;
59
+ }
60
+ if (tail.length === 0) {
61
+ return head;
62
+ }
63
+ if (tail.length === 1) {
64
+ return { [operator]: [head, tail[0]] };
65
+ }
66
+ return { [operator]: [head, normalizeCondition({ [operator]: tail })] };
41
67
  }
68
+
42
69
  export function buildHooksSystem(stringify = genericStringify) {
43
- return function createHooks(config, options) {
44
- const fallbackKeyword = (options === null || options === void 0 ? void 0 : options.fallback) || "unset";
45
- const stringifyImpl = (propertyName, value) => {
46
- return typeof value === "string" && value.startsWith("var(")
47
- ? value
48
- : stringify(propertyName, value);
49
- };
50
- const hookId = (options === null || options === void 0 ? void 0 : options.hookNameToId) ||
51
- ((hookName) => {
52
- const specHash = hash(config[hookName]);
53
- return (options === null || options === void 0 ? void 0 : options.debug)
54
- ? `${hookName.replace(/[^A-Za-z0-9-]/g, "_")}-${specHash}`
55
- : specHash;
56
- });
57
- const hooks = Object.entries(config)
58
- .map(([name, definition]) => {
59
- function nest(input) {
60
- if (typeof input === "object") {
61
- if ("and" in input) {
62
- if (input.and.length > 2) {
63
- const [left, ...rest] = input.and;
64
- return { and: [left, nest({ and: rest })] };
65
- }
66
- return {
67
- and: input.and.map(item => typeof item === "string" ? item : nest(item)),
68
- };
69
- }
70
- if ("or" in input) {
71
- if (input.or.length > 2) {
72
- const [left, ...rest] = input.or;
73
- return { or: [left, nest({ or: rest })] };
74
- }
75
- return {
76
- or: input.or.map(item => typeof item === "string" ? item : nest(item)),
77
- };
78
- }
79
- }
80
- return input;
70
+ return function createHooks({
71
+ hooks: hooksConfigUnresolved,
72
+ fallback = "revert-layer",
73
+ debug,
74
+ sort: {
75
+ properties: sortProperties = true,
76
+ conditionalStyles: sortConditionalStyles = true,
77
+ } = {},
78
+ hookNameToId: customHookNameToId,
79
+ }) {
80
+ const hooksConfig =
81
+ typeof hooksConfigUnresolved === "function"
82
+ ? hooksConfigUnresolved(helpers)
83
+ : hooksConfigUnresolved;
84
+
85
+ const [space, newline] = debug ? [" ", "\n"] : ["", ""];
86
+ const indent = `${space}${space}`;
87
+
88
+ const hookNameToId =
89
+ customHookNameToId ||
90
+ (hookName => {
91
+ const specHash = hash(hooksConfig[hookName]);
92
+ return debug
93
+ ? `${hookName.replace(/[^A-Za-z0-9-]/g, "_")}-${specHash}`
94
+ : specHash;
95
+ });
96
+
97
+ function styleSheet() {
98
+ function variablePair({ id, initial, indents }) {
99
+ return [0, 1]
100
+ .map(
101
+ i =>
102
+ `${Array(indents).fill(indent).join("")}--${id}-${i}:${space}${
103
+ initial === i ? "initial" : space ? "" : " "
104
+ };${newline}`,
105
+ )
106
+ .join("");
107
+ }
108
+
109
+ let sheet = `*${space}{${newline}`;
110
+
111
+ const hooks = Object.entries(hooksConfig)
112
+ .map(([hookName, hookCondition]) => [
113
+ hookName,
114
+ normalizeCondition(hookCondition),
115
+ ])
116
+ .filter(([, hookCondition]) => hookCondition);
117
+
118
+ for (const [hookName, hookCondition] of hooks) {
119
+ (function it(id, hookCondition) {
120
+ if (hookCondition && typeof hookCondition === "object") {
121
+ if (hookCondition.not) {
122
+ it(`${id}X`, hookCondition.not);
123
+ sheet += `${indent}--${id}-0:${space}var(--${id}X-1);${newline}`;
124
+ sheet += `${indent}--${id}-1:${space}var(--${id}X-0);${newline}`;
125
+ return;
81
126
  }
82
- if (!isHookSpec(definition) || typeof definition !== "object") {
83
- return [name, definition];
127
+
128
+ if ("and" in hookCondition || "or" in hookCondition) {
129
+ const operator = hookCondition.and ? "and" : "or";
130
+ it(`${id}A`, hookCondition[operator][0]);
131
+ it(`${id}B`, hookCondition[operator][1]);
132
+ if (operator === "and") {
133
+ sheet += `${indent}--${id}-0:${space}var(--${id}A-0)${space}var(--${id}B-0);${newline}`;
134
+ sheet += `${indent}--${id}-1:${space}var(--${id}A-1,${space}var(--${id}B-1));${newline}`;
135
+ } else {
136
+ sheet += `${indent}--${id}-0:${space}var(--${id}A-0,${space}var(--${id}B-0));${newline}`;
137
+ sheet += `${indent}--${id}-1:${space}var(--${id}A-1)${space}var(--${id}B-1);${newline}`;
138
+ }
139
+ return;
84
140
  }
85
- return [name, nest(definition)];
86
- })
87
- .flatMap(([name, definition]) => (function hooksCSS(name, definition) {
88
- if (!isHookSpec(definition)) {
89
- return [];
141
+ }
142
+ sheet += variablePair({ id, initial: 0, indents: 1 });
143
+ })(hookNameToId(hookName), hookCondition);
144
+ }
145
+
146
+ sheet += `}${newline}`;
147
+
148
+ for (const [hookName, hookCondition] of hooks) {
149
+ (function it(id, hookCondition) {
150
+ if (hookCondition && typeof hookCondition === "object") {
151
+ if (hookCondition.not) {
152
+ return it(`${id}X`, hookCondition.not);
90
153
  }
91
- if (typeof definition === "object") {
92
- let a, operator, b, extraHooksCSS = [];
93
- if ("or" in definition) {
94
- operator = "or";
95
- [a, b] = definition.or;
96
- if (!a) {
97
- return [];
98
- }
99
- if (!b) {
100
- return hooksCSS(name, a);
101
- }
102
- extraHooksCSS = [
103
- {
104
- init: (function aorb(x) {
105
- const a = `${x}A`;
106
- const b = `${x}B`;
107
- return [
108
- `--${x}-0:var(--${a}-0,var(--${b}-0));`,
109
- `--${x}-1:var(--${a}-1) var(--${b}-1);`,
110
- ].join("");
111
- })(name),
112
- },
113
- ];
114
- }
115
- else if ("and" in definition) {
116
- operator = "and";
117
- [a, b] = definition.and;
118
- if (!a) {
119
- return [];
120
- }
121
- if (!b) {
122
- return hooksCSS(name, a);
123
- }
124
- extraHooksCSS = [
125
- {
126
- init: (function aandb(x) {
127
- const a = `${x}A`;
128
- const b = `${x}B`;
129
- return [
130
- `--${x}-0:var(--${a}-0) var(--${b}-0);`,
131
- `--${x}-1:var(--${a}-1,var(--${b}-1));`,
132
- ].join("");
133
- })(name),
134
- },
135
- ];
136
- }
137
- if (operator) {
138
- return [
139
- ...hooksCSS(`${name}A`, a),
140
- ...hooksCSS(`${name}B`, b),
141
- ...extraHooksCSS,
142
- ];
143
- }
154
+
155
+ if ("and" in hookCondition || "or" in hookCondition) {
156
+ const operator = hookCondition.and ? "and" : "or";
157
+ it(`${id}A`, hookCondition[operator][0]);
158
+ it(`${id}B`, hookCondition[operator][1]);
159
+ return;
144
160
  }
145
- const init = `--${name}-0:initial;--${name}-1: ;`;
146
- let rule;
147
- if (typeof definition === "string") {
148
- if (definition.includes("&")) {
149
- rule = `${definition.replace(/&/g, "*")}{--${name}-0: ;--${name}-1:initial;}`;
150
- }
151
- else if (definition.startsWith(":")) {
152
- rule = `${definition}{--${name}-0: ;--${name}-1:initial;}`;
153
- }
154
- else if (definition.startsWith("@")) {
155
- rule = `${definition}{*{--${name}-0: ;--${name}-1:initial;}}`;
156
- }
161
+ }
162
+
163
+ if (typeof hookCondition === "string") {
164
+ if (hookCondition[0] === "@") {
165
+ sheet += [
166
+ `${hookCondition}${space}{${newline}`,
167
+ `${indent}*${space}{${newline}`,
168
+ variablePair({
169
+ id,
170
+ initial: 1,
171
+ indents: 2,
172
+ }),
173
+ `${indent}}${newline}`,
174
+ `}${newline}`,
175
+ ].join("");
176
+ } else {
177
+ sheet += [
178
+ `${hookCondition.replace(/&/g, "*")}${space}{${newline}`,
179
+ variablePair({
180
+ id,
181
+ initial: 1,
182
+ indents: 1,
183
+ }),
184
+ `}${newline}`,
185
+ ].join("");
157
186
  }
158
- return rule === undefined ? [] : [{ init, rule }];
159
- })(hookId(name), definition))
160
- .reduce((acc, { init = "", rule = "" }) => ({
161
- init: acc.init + init,
162
- rule: acc.rule + rule,
163
- }), {
164
- init: "",
165
- rule: "",
166
- });
167
- function cssImpl(properties, fallback = propertyName => stringifyImpl(propertyName, properties[propertyName])) {
168
- const sort = options === null || options === void 0 ? void 0 : options.sort;
169
- const keys = sort ? Object.keys(properties) : [];
170
- for (const k in properties) {
171
- const key = k;
172
- const value = properties[key];
173
- if (key in config) {
174
- const hookName = key;
175
- const innerProperties = value;
176
- cssImpl(innerProperties, propertyName => {
177
- let v = stringifyImpl(propertyName, innerProperties[propertyName]);
178
- if (v === null) {
179
- v = fallback(propertyName);
180
- }
181
- if (v === null) {
182
- v = fallbackKeyword;
183
- }
184
- return v;
185
- });
186
- for (const propertyNameStr in innerProperties) {
187
- if (keys.indexOf(propertyNameStr) > keys.indexOf(hookName)) {
188
- continue;
189
- }
190
- const propertyName = propertyNameStr;
191
- const v1 = stringifyImpl(propertyName, innerProperties[propertyName]);
192
- if (v1 !== null) {
193
- let v0 = fallback(propertyName);
194
- if (v0 === null) {
195
- v0 = fallbackKeyword;
196
- }
197
- if (sort) {
198
- delete properties[propertyName];
199
- }
200
- properties[propertyName] = `var(--${hookId(hookName)}-1, ${v1}) var(--${hookId(hookName)}-0, ${v0})`;
201
- }
202
- }
203
- delete properties[hookName];
204
- }
205
- else if (sort) {
206
- delete properties[key];
207
- properties[key] =
208
- value;
209
- }
187
+ }
188
+ })(hookNameToId(hookName), hookCondition);
189
+ }
190
+
191
+ return sheet;
192
+ }
193
+
194
+ function css(...args) {
195
+ const style = {};
196
+ let conditionCount = 0;
197
+ const rules = args
198
+ .filter(rule => rule)
199
+ .reduce(
200
+ ([baseStyles, conditionalStyles], rule) => {
201
+ if (rule.on) {
202
+ baseStyles.push(rule);
203
+ (sortConditionalStyles ? conditionalStyles : baseStyles).push(
204
+ ...(typeof rule.on === "function"
205
+ ? rule.on((condition, styles) => [condition, styles], helpers)
206
+ : rule.on),
207
+ );
208
+ } else {
209
+ baseStyles.push(rule);
210
210
  }
211
- return properties;
211
+ delete rule.on;
212
+ return [baseStyles, conditionalStyles];
213
+ },
214
+ [[], []],
215
+ )
216
+ .reduce((a, b) => {
217
+ return a.concat(b);
218
+ }, []);
219
+ for (const rule of rules) {
220
+ if (!rule || typeof rule !== "object") {
221
+ continue;
212
222
  }
213
- function css(...styles) {
214
- do {
215
- const current = styles[0];
216
- const next = styles[1] || {};
217
- for (const k in next) {
218
- if (options === null || options === void 0 ? void 0 : options.sort) {
219
- if (k in current) {
220
- delete current[k];
221
- }
222
- }
223
- current[k] = next[k];
223
+ if (rule instanceof Array && rule.length === 2) {
224
+ let conditionId = normalizeCondition(rule[0]);
225
+ if (!conditionId) {
226
+ continue;
227
+ }
228
+ if (typeof conditionId === "string") {
229
+ conditionId = hookNameToId(conditionId);
230
+ } else if (typeof conditionId === "object") {
231
+ conditionId = (function it(name, cond) {
232
+ if (typeof cond === "string") {
233
+ return hookNameToId(cond);
234
+ }
235
+ if (cond.not) {
236
+ const inner = it(`${name}X`, cond.not);
237
+ style[`--${name}-0`] = `var(--${inner}-1)`;
238
+ style[`--${name}-1`] = `var(--${inner}-0)`;
239
+ }
240
+ if (cond.and || cond.or) {
241
+ const operator = cond.and ? "and" : "or";
242
+ const a = it(`${name}A`, cond[operator][0]);
243
+ const b = it(`${name}B`, cond[operator][1]);
244
+ if (operator === "and") {
245
+ style[`--${name}-0`] = `var(--${a}-0)${space}var(--${b}-0)`;
246
+ style[`--${name}-1`] = `var(--${a}-1,${space}var(--${b}-1))`;
247
+ } else {
248
+ style[`--${name}-0`] = `var(--${a}-0,${space}var(--${b}-0))`;
249
+ style[`--${name}-1`] = `var(--${a}-1)${space}var(--${b}-1)`;
224
250
  }
225
- styles.splice(1, 1);
226
- } while (styles[1]);
227
- return cssImpl(styles[0]);
251
+ }
252
+ return name;
253
+ })(`cond${conditionCount++}`, conditionId);
254
+ }
255
+ for (const [property, value] of Object.entries(rule[1])) {
256
+ const stringifiedValue = stringify(property, value);
257
+ if (stringifiedValue === null) {
258
+ continue;
259
+ }
260
+ const fallbackValue = (() => {
261
+ if (!(property in style)) {
262
+ return fallback;
263
+ }
264
+ const stringifiedValue = stringify(property, style[property]);
265
+ return stringifiedValue === null ? fallback : stringifiedValue;
266
+ })();
267
+ if (sortProperties) {
268
+ delete style[property];
269
+ }
270
+ style[property] =
271
+ `var(--${conditionId}-1,${space}${stringifiedValue})${space}var(--${conditionId}-0,${space}${fallbackValue})`;
272
+ }
273
+ continue;
228
274
  }
229
- return [`*{${hooks.init}}${hooks.rule}`, css];
230
- };
275
+ for (const [property, value] of Object.entries(rule)) {
276
+ if (sortProperties) {
277
+ delete style[property];
278
+ }
279
+ style[property] = value;
280
+ }
281
+ }
282
+ return style;
283
+ }
284
+
285
+ return { styleSheet, css };
286
+ };
231
287
  }
232
- /**
233
- * A list of hooks offered as a "sensible default" to solve the most common use cases.
234
- *
235
- * @deprecated Use the `@css-hooks/recommended` package instead.
236
- */
237
- export const recommended = {
238
- active: ":active",
239
- autofill: { or: [":autofill", ":-webkit-autofill"] },
240
- checked: ":checked",
241
- default: ":default",
242
- disabled: ":disabled",
243
- empty: ":empty",
244
- enabled: ":enabled",
245
- evenChild: ":nth-child(even)",
246
- firstChild: ":first-child",
247
- firstOfType: ":first-of-type",
248
- focus: ":focus",
249
- focusVisible: ":focus-visible",
250
- focusWithin: ":focus-within",
251
- hover: ":hover",
252
- inRange: ":in-range",
253
- indeterminate: ":indeterminate",
254
- invalid: ":invalid",
255
- lastChild: ":last-child",
256
- lastOfType: ":last-of-type",
257
- oddChild: ":nth-child(odd)",
258
- onlyChild: ":only-child",
259
- onlyOfType: ":only-of-type",
260
- outOfRange: ":out-of-range",
261
- placeholderShown: { or: [":placeholder-shown", ":-moz-placeholder-shown"] },
262
- readOnly: { or: [":read-only", ":-moz-read-only"] },
263
- readWrite: { or: [":read-write", ":-moz-read-write"] },
264
- required: ":required",
265
- target: ":target",
266
- valid: ":valid",
267
- visited: ":visited",
268
- };
package/package.json CHANGED
@@ -1,15 +1,21 @@
1
1
  {
2
2
  "name": "@css-hooks/core",
3
3
  "description": "CSS Hooks core library",
4
- "version": "1.8.1",
4
+ "version": "2.0.0",
5
5
  "author": "Nick Saunders",
6
6
  "devDependencies": {
7
+ "@microsoft/api-extractor": "^7.39.4",
7
8
  "@tsconfig/strictest": "^2.0.1",
8
- "@types/css-tree": "^2.3.2",
9
+ "@types/color": "^3.0.6",
9
10
  "@typescript-eslint/eslint-plugin": "^6.3.0",
10
11
  "@typescript-eslint/parser": "^6.3.0",
11
- "css-tree": "^2.3.1",
12
+ "ascjs": "^6.0.3",
13
+ "color": "^4.2.3",
14
+ "csstype": "^3.1.3",
12
15
  "eslint": "^8.47.0",
16
+ "eslint-plugin-compat": "^4.2.0",
17
+ "lightningcss": "^1.23.0",
18
+ "puppeteer": "^21.7.0",
13
19
  "rimraf": "^5.0.1",
14
20
  "tsc-watch": "^6.0.4",
15
21
  "typescript": "^5.1.6"
@@ -17,7 +23,8 @@
17
23
  "files": [
18
24
  "cjs",
19
25
  "esm",
20
- "types"
26
+ "types",
27
+ "tsdoc-metadata.json"
21
28
  ],
22
29
  "license": "MIT",
23
30
  "main": "cjs",
@@ -30,9 +37,10 @@
30
37
  "directory": "packages/core"
31
38
  },
32
39
  "scripts": {
40
+ "api": "node -e \"var path=require('path').resolve,fs=require('fs'),cp=fs.cpSync;cp(path('src', 'index.d.ts'),path('types','index.d.ts'))\" && api-extractor run",
33
41
  "clean": "rimraf cjs esm out types",
34
42
  "lint": "eslint src .*.*js *.*js",
35
- "prepublishOnly": "tsc -p tsconfig.dist.json --outDir esm --module es6 && tsc -p tsconfig.dist.json --outDir cjs --module commonjs && tsc -p tsconfig.dist.json --outDir types --declaration --emitDeclarationOnly",
43
+ "prepublishOnly": "node -e \"var path=require('path').resolve,fs=require('fs'),cp=fs.cpSync,mkdir=fs.mkdirSync;cp(path('src', 'index.d.ts'),path('types','index.d.ts'));cp(path('src','index.js'),path('esm','index.js'));mkdir(path('cjs'),{recursive:true})\" && ascjs src/index.js cjs/index.js",
36
44
  "test": "tsc && node --test",
37
45
  "test.watch": "tsc-watch --onSuccess 'node --test'"
38
46
  },
@@ -43,5 +51,8 @@
43
51
  "require": "./cjs/index.js",
44
52
  "types": "./types/index.d.ts"
45
53
  }
46
- }
54
+ },
55
+ "browserslist": [
56
+ "supports css-variables and supports object-entries"
57
+ ]
47
58
  }