@taybart/corvid 0.2.0 → 0.2.3
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/dom.d.ts +96 -5
- package/dist/dom.js +281 -5
- package/dist/index.js +293 -14
- package/dist/ls.js +269 -4
- package/dist/qr.js +269 -4
- package/dist/style.d.ts +50 -1
- package/dist/style.js +156 -12
- package/dist/style.test.d.ts +1 -0
- package/package.json +1 -1
package/dist/style.d.ts
CHANGED
|
@@ -3,7 +3,56 @@
|
|
|
3
3
|
**************/
|
|
4
4
|
export declare function cssVar(name: string): string;
|
|
5
5
|
export declare function render(style: Object): string;
|
|
6
|
-
|
|
6
|
+
/**
|
|
7
|
+
* A block of css declarations. A value that is itself an object is a nested
|
|
8
|
+
* rule: the key is a selector (`&:focus`, `input`) or an at-rule (`@media ...`)
|
|
9
|
+
* rather than a property.
|
|
10
|
+
*/
|
|
11
|
+
export type declarations = {
|
|
12
|
+
[key: string]: string | number | declarations;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Render a declaration block to css without touching the document.
|
|
16
|
+
*/
|
|
17
|
+
export declare function css(selector: string, style: declarations): string;
|
|
18
|
+
/**
|
|
19
|
+
* Install a stylesheet for `selector`. Nested objects become nested rules, so
|
|
20
|
+
* a component declares its styles in one call:
|
|
21
|
+
*
|
|
22
|
+
* inject('x-search-form', {
|
|
23
|
+
* marginTop: '33vh',
|
|
24
|
+
* input: {
|
|
25
|
+
* border: 'none',
|
|
26
|
+
* '&:focus': { borderBottomColor: 'var(--ring)' },
|
|
27
|
+
* },
|
|
28
|
+
* })
|
|
29
|
+
*
|
|
30
|
+
* Injecting the same key again replaces that sheet rather than adding to it;
|
|
31
|
+
* `key` defaults to the selector.
|
|
32
|
+
* @return the generated css
|
|
33
|
+
*/
|
|
34
|
+
export declare function inject(selector: string, style: declarations, { key }?: {
|
|
35
|
+
key?: string;
|
|
36
|
+
}): string;
|
|
37
|
+
/**
|
|
38
|
+
* Compile `style` for a shadow root, rooted at `:host`.
|
|
39
|
+
*
|
|
40
|
+
* Hands back a constructable sheet where the platform has them — one per key,
|
|
41
|
+
* shared by every instance of the component, so a thousand cards cost one
|
|
42
|
+
* sheet — and the css text otherwise, for the caller to put in a `<style>`
|
|
43
|
+
* inside the root. Re-compiling an existing key replaces that sheet's contents
|
|
44
|
+
* in place, so live instances pick the change up (which is what makes an HMR
|
|
45
|
+
* reload work rather than stack a second sheet).
|
|
46
|
+
*
|
|
47
|
+
* Document styles do not reach into a shadow tree, so this is the only way a
|
|
48
|
+
* shadowed component gets styled at all.
|
|
49
|
+
*/
|
|
50
|
+
export declare function shadowSheet(key: string, style: declarations): CSSStyleSheet | string;
|
|
51
|
+
/**
|
|
52
|
+
* Remove a stylesheet installed by `inject`
|
|
53
|
+
* @param key - the key it was injected under (the selector, unless overridden)
|
|
54
|
+
*/
|
|
55
|
+
export declare function eject(key: string): void;
|
|
7
56
|
/**
|
|
8
57
|
* Check if the current theme is dark
|
|
9
58
|
*/
|
package/dist/style.js
CHANGED
|
@@ -10,17 +10,161 @@ function render(style) {
|
|
|
10
10
|
Object.entries(style).forEach(([k, v])=>s += `${toKebab(k)}:${v};`);
|
|
11
11
|
return s;
|
|
12
12
|
}
|
|
13
|
-
function
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
13
|
+
function splitSelectors(selector) {
|
|
14
|
+
const out = [];
|
|
15
|
+
let depth = 0;
|
|
16
|
+
let quote = '';
|
|
17
|
+
let current = '';
|
|
18
|
+
for (const ch of selector){
|
|
19
|
+
if (quote) {
|
|
20
|
+
if (ch === quote) quote = '';
|
|
21
|
+
} else if ('"' === ch || "'" === ch) quote = ch;
|
|
22
|
+
else if ('(' === ch || '[' === ch) depth++;
|
|
23
|
+
else if (')' === ch || ']' === ch) depth--;
|
|
24
|
+
else if (',' === ch && 0 === depth) {
|
|
25
|
+
if (current.trim()) out.push(current.trim());
|
|
26
|
+
current = '';
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
current += ch;
|
|
30
|
+
}
|
|
31
|
+
if (current.trim()) out.push(current.trim());
|
|
32
|
+
return out;
|
|
33
|
+
}
|
|
34
|
+
function hostCompound(selector) {
|
|
35
|
+
if (!selector.startsWith(':host')) return selector;
|
|
36
|
+
let i = 5;
|
|
37
|
+
let inner = '';
|
|
38
|
+
if ('(' === selector[i]) {
|
|
39
|
+
const open = i;
|
|
40
|
+
let depth = 0;
|
|
41
|
+
let quote = '';
|
|
42
|
+
while(i < selector.length){
|
|
43
|
+
const ch = selector[i];
|
|
44
|
+
if (quote) {
|
|
45
|
+
if (ch === quote) quote = '';
|
|
46
|
+
} else if ('"' === ch || "'" === ch) quote = ch;
|
|
47
|
+
else if ('(' === ch) depth++;
|
|
48
|
+
else if (')' === ch) {
|
|
49
|
+
depth--;
|
|
50
|
+
if (0 === depth) {
|
|
51
|
+
i++;
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
i++;
|
|
56
|
+
}
|
|
57
|
+
inner = selector.slice(open + 1, i - 1);
|
|
58
|
+
}
|
|
59
|
+
const start = i;
|
|
60
|
+
let depth = 0;
|
|
61
|
+
let quote = '';
|
|
62
|
+
while(i < selector.length){
|
|
63
|
+
const ch = selector[i];
|
|
64
|
+
if (quote) {
|
|
65
|
+
if (ch === quote) quote = '';
|
|
66
|
+
} else if ('"' === ch || "'" === ch) quote = ch;
|
|
67
|
+
else if (0 === depth && (' ' === ch || '>' === ch || '+' === ch || '~' === ch)) break;
|
|
68
|
+
else if (0 === depth && ':' === ch && ':' === selector[i + 1]) break;
|
|
69
|
+
else if ('(' === ch || '[' === ch) depth++;
|
|
70
|
+
else if (')' === ch || ']' === ch) depth--;
|
|
71
|
+
i++;
|
|
72
|
+
}
|
|
73
|
+
const compound = selector.slice(start, i);
|
|
74
|
+
if (!compound) return selector;
|
|
75
|
+
return `:host(${inner}${compound})${selector.slice(i)}`;
|
|
76
|
+
}
|
|
77
|
+
function resolve(parents, selector) {
|
|
78
|
+
const out = [];
|
|
79
|
+
for (const parent of parents)for (const part of splitSelectors(selector))out.push(hostCompound(part.includes('&') ? part.replaceAll('&', parent) : `${parent} ${part}`));
|
|
80
|
+
return out;
|
|
81
|
+
}
|
|
82
|
+
function compile(parents, decls, indent = '') {
|
|
83
|
+
const blocks = [];
|
|
84
|
+
const props = [];
|
|
85
|
+
const nested = [];
|
|
86
|
+
for (const [k, v] of Object.entries(decls))if (null !== v && 'object' == typeof v) nested.push([
|
|
87
|
+
k,
|
|
88
|
+
v
|
|
89
|
+
]);
|
|
90
|
+
else props.push(`${indent} ${toKebab(k)}: ${v};`);
|
|
91
|
+
if (props.length) blocks.push(`${indent}${parents.join(', ')} {\n${props.join('\n')}\n${indent}}`);
|
|
92
|
+
for (const [selector, block] of nested){
|
|
93
|
+
if (selector.startsWith('@')) {
|
|
94
|
+
const inner = selector.startsWith('@keyframes') ? Object.entries(block).flatMap(([step, d])=>null !== d && 'object' == typeof d ? compile([
|
|
95
|
+
step
|
|
96
|
+
], d, `${indent} `) : []) : compile(parents, block, `${indent} `);
|
|
97
|
+
if (inner.length) blocks.push(`${indent}${selector} {\n${inner.join('\n')}\n${indent}}`);
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
blocks.push(...compile(resolve(parents, selector), block, indent));
|
|
101
|
+
}
|
|
102
|
+
return blocks;
|
|
103
|
+
}
|
|
104
|
+
function css(selector, style) {
|
|
105
|
+
return compile(splitSelectors(selector), style).join('\n');
|
|
106
|
+
}
|
|
107
|
+
function registry() {
|
|
108
|
+
const doc = document;
|
|
109
|
+
if (!doc.__corvidSheets) doc.__corvidSheets = new Map();
|
|
110
|
+
return doc.__corvidSheets;
|
|
111
|
+
}
|
|
112
|
+
function adoptable() {
|
|
113
|
+
return 'undefined' != typeof CSSStyleSheet && 'replaceSync' in CSSStyleSheet.prototype && 'undefined' != typeof Document && 'adoptedStyleSheets' in Document.prototype;
|
|
114
|
+
}
|
|
115
|
+
function styleTag(key) {
|
|
116
|
+
const tagged = Array.from(document.head.querySelectorAll('style[data-corvid]'));
|
|
117
|
+
for (const node of tagged)if (node.dataset.corvid === key) return node;
|
|
118
|
+
const node = document.createElement('style');
|
|
119
|
+
node.dataset.corvid = key;
|
|
120
|
+
document.head.appendChild(node);
|
|
121
|
+
return node;
|
|
122
|
+
}
|
|
123
|
+
function inject(selector, style, { key = selector } = {}) {
|
|
124
|
+
const rules = css(selector, style);
|
|
125
|
+
const sheets = registry();
|
|
126
|
+
const existing = sheets.get(key);
|
|
127
|
+
if (existing) {
|
|
128
|
+
if ('replaceSync' in existing) existing.replaceSync(rules);
|
|
129
|
+
else existing.textContent = rules;
|
|
130
|
+
return rules;
|
|
131
|
+
}
|
|
132
|
+
if (adoptable()) {
|
|
133
|
+
const sheet = new CSSStyleSheet();
|
|
134
|
+
sheet.replaceSync(rules);
|
|
135
|
+
document.adoptedStyleSheets = [
|
|
136
|
+
...document.adoptedStyleSheets,
|
|
137
|
+
sheet
|
|
138
|
+
];
|
|
139
|
+
sheets.set(key, sheet);
|
|
140
|
+
return rules;
|
|
141
|
+
}
|
|
142
|
+
const node = styleTag(key);
|
|
143
|
+
node.textContent = rules;
|
|
144
|
+
sheets.set(key, node);
|
|
145
|
+
return rules;
|
|
146
|
+
}
|
|
147
|
+
function shadowSheet(key, style) {
|
|
148
|
+
const rules = css(':host', style);
|
|
149
|
+
if ('undefined' == typeof CSSStyleSheet || !('replaceSync' in CSSStyleSheet.prototype) || 'undefined' == typeof ShadowRoot || !('adoptedStyleSheets' in ShadowRoot.prototype)) return rules;
|
|
150
|
+
const sheets = registry();
|
|
151
|
+
const existing = sheets.get(key);
|
|
152
|
+
if (existing && 'replaceSync' in existing) {
|
|
153
|
+
existing.replaceSync(rules);
|
|
154
|
+
return existing;
|
|
155
|
+
}
|
|
156
|
+
const sheet = new CSSStyleSheet();
|
|
157
|
+
sheet.replaceSync(rules);
|
|
158
|
+
sheets.set(key, sheet);
|
|
159
|
+
return sheet;
|
|
160
|
+
}
|
|
161
|
+
function eject(key) {
|
|
162
|
+
const sheets = registry();
|
|
163
|
+
const sheet = sheets.get(key);
|
|
164
|
+
if (!sheet) return;
|
|
165
|
+
if ('replaceSync' in sheet) document.adoptedStyleSheets = document.adoptedStyleSheets.filter((s)=>s !== sheet);
|
|
166
|
+
else sheet.remove();
|
|
167
|
+
sheets.delete(key);
|
|
24
168
|
}
|
|
25
169
|
function isDarkMode() {
|
|
26
170
|
return window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
@@ -46,4 +190,4 @@ function gradient(start, end, value) {
|
|
|
46
190
|
const blue = Math.round(start.blue + (end.blue - start.blue) * value / 100);
|
|
47
191
|
return `rgb(${red}, ${green}, ${blue})`;
|
|
48
192
|
}
|
|
49
|
-
export { cssVar, gradient, handleThemeSwitch, inject, isDarkMode, onDarkMode, render, switchTheme };
|
|
193
|
+
export { css, cssVar, eject, gradient, handleThemeSwitch, inject, isDarkMode, onDarkMode, render, shadowSheet, switchTheme };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|