@tenphi/tasty 1.4.1 → 1.5.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/README.md +8 -7
- package/dist/compute-styles.js +6 -28
- package/dist/compute-styles.js.map +1 -1
- package/dist/config.d.ts +3 -2
- package/dist/config.js.map +1 -1
- package/dist/core/index.d.ts +2 -2
- package/dist/core/index.js +2 -2
- package/dist/hooks/useCounterStyle.d.ts +3 -17
- package/dist/hooks/useCounterStyle.js +54 -35
- package/dist/hooks/useCounterStyle.js.map +1 -1
- package/dist/hooks/useFontFace.d.ts +3 -1
- package/dist/hooks/useFontFace.js +21 -24
- package/dist/hooks/useFontFace.js.map +1 -1
- package/dist/hooks/useGlobalStyles.d.ts +18 -2
- package/dist/hooks/useGlobalStyles.js +51 -40
- package/dist/hooks/useGlobalStyles.js.map +1 -1
- package/dist/hooks/useKeyframes.d.ts +4 -2
- package/dist/hooks/useKeyframes.js +41 -50
- package/dist/hooks/useKeyframes.js.map +1 -1
- package/dist/hooks/useProperty.d.ts +4 -2
- package/dist/hooks/useProperty.js +29 -41
- package/dist/hooks/useProperty.js.map +1 -1
- package/dist/hooks/useRawCSS.d.ts +13 -44
- package/dist/hooks/useRawCSS.js +90 -21
- package/dist/hooks/useRawCSS.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/injector/index.d.ts +5 -10
- package/dist/injector/index.js +5 -12
- package/dist/injector/index.js.map +1 -1
- package/dist/injector/injector.d.ts +11 -13
- package/dist/injector/injector.js +50 -73
- package/dist/injector/injector.js.map +1 -1
- package/dist/injector/sheet-manager.js +2 -1
- package/dist/injector/sheet-manager.js.map +1 -1
- package/dist/injector/types.d.ts +21 -28
- package/dist/rsc-cache.js +81 -0
- package/dist/rsc-cache.js.map +1 -0
- package/dist/ssr/astro-client.d.ts +1 -0
- package/dist/ssr/astro-client.js +24 -0
- package/dist/ssr/astro-client.js.map +1 -0
- package/dist/ssr/astro-middleware.d.ts +15 -0
- package/dist/ssr/astro-middleware.js +19 -0
- package/dist/ssr/astro-middleware.js.map +1 -0
- package/dist/ssr/astro.d.ts +85 -8
- package/dist/ssr/astro.js +110 -20
- package/dist/ssr/astro.js.map +1 -1
- package/dist/ssr/collect-auto-properties.js +28 -9
- package/dist/ssr/collect-auto-properties.js.map +1 -1
- package/dist/ssr/index.d.ts +1 -1
- package/dist/tasty.d.ts +1 -1
- package/dist/utils/deps-equal.js +15 -0
- package/dist/utils/deps-equal.js.map +1 -0
- package/dist/utils/filter-base-props.d.ts +1 -1
- package/dist/utils/filter-base-props.js.map +1 -1
- package/dist/utils/hash.js +14 -0
- package/dist/utils/hash.js.map +1 -0
- package/docs/adoption.md +1 -1
- package/docs/comparison.md +1 -1
- package/docs/configuration.md +1 -1
- package/docs/design-system.md +1 -1
- package/docs/dsl.md +21 -6
- package/docs/getting-started.md +1 -1
- package/docs/injector.md +25 -24
- package/docs/methodology.md +1 -1
- package/docs/runtime.md +12 -31
- package/docs/ssr.md +117 -36
- package/docs/tasty-static.md +1 -1
- package/package.json +8 -2
|
@@ -1,42 +1,61 @@
|
|
|
1
1
|
import { formatCounterStyleRule } from "../counter-style/index.js";
|
|
2
2
|
import { getGlobalInjector } from "../config.js";
|
|
3
|
-
import {
|
|
4
|
-
import { useInsertionEffect, useMemo } from "react";
|
|
3
|
+
import { getStyleTarget, pushRSCCSS } from "../rsc-cache.js";
|
|
5
4
|
//#region src/hooks/useCounterStyle.ts
|
|
6
5
|
let clientCounterStyleCounter = 0;
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
name
|
|
38
|
-
|
|
39
|
-
|
|
6
|
+
const clientContentToName = /* @__PURE__ */ new Map();
|
|
7
|
+
/**
|
|
8
|
+
* Inject a CSS @counter-style rule and return the generated name.
|
|
9
|
+
* Permanent — no cleanup on unmount. Deduplicates by name.
|
|
10
|
+
*
|
|
11
|
+
* Works in all environments: client, SSR with collector, and React Server Components.
|
|
12
|
+
*
|
|
13
|
+
* @example Basic usage
|
|
14
|
+
* ```tsx
|
|
15
|
+
* function EmojiList() {
|
|
16
|
+
* const styleName = useCounterStyle({
|
|
17
|
+
* system: 'cyclic',
|
|
18
|
+
* symbols: '"👍"',
|
|
19
|
+
* suffix: '" "',
|
|
20
|
+
* }, { name: 'thumbs' });
|
|
21
|
+
*
|
|
22
|
+
* return (
|
|
23
|
+
* <ol style={{ listStyleType: styleName }}>
|
|
24
|
+
* <li>First</li>
|
|
25
|
+
* <li>Second</li>
|
|
26
|
+
* </ol>
|
|
27
|
+
* );
|
|
28
|
+
* }
|
|
29
|
+
* ```
|
|
30
|
+
*
|
|
31
|
+
*/
|
|
32
|
+
function useCounterStyle(descriptors, options) {
|
|
33
|
+
if (!descriptors || !descriptors.system) return "";
|
|
34
|
+
const target = getStyleTarget();
|
|
35
|
+
if (target.mode === "ssr") {
|
|
36
|
+
const actualName = target.collector.allocateCounterStyleName(options?.name);
|
|
37
|
+
const css = formatCounterStyleRule(actualName, descriptors);
|
|
38
|
+
target.collector.collectCounterStyle(actualName, css);
|
|
39
|
+
return actualName;
|
|
40
|
+
}
|
|
41
|
+
if (target.mode === "rsc") {
|
|
42
|
+
const serializedContent = JSON.stringify(descriptors);
|
|
43
|
+
const key = `__cs:${options?.name ?? ""}:${serializedContent}`;
|
|
44
|
+
const existingName = target.cache.generatedNames.get(key);
|
|
45
|
+
if (existingName) return existingName;
|
|
46
|
+
const actualName = options?.name ?? `cs${target.cache.counterStyleCounter++}`;
|
|
47
|
+
const css = formatCounterStyleRule(actualName, descriptors);
|
|
48
|
+
pushRSCCSS(target.cache, key, css);
|
|
49
|
+
target.cache.generatedNames.set(key, actualName);
|
|
50
|
+
return actualName;
|
|
51
|
+
}
|
|
52
|
+
const serializedContent = JSON.stringify(descriptors);
|
|
53
|
+
const cacheKey = `${options?.name ?? ""}:${serializedContent}`;
|
|
54
|
+
const existingName = clientContentToName.get(cacheKey);
|
|
55
|
+
if (existingName) return existingName;
|
|
56
|
+
const name = options?.name ?? `cs${clientCounterStyleCounter++}`;
|
|
57
|
+
clientContentToName.set(cacheKey, name);
|
|
58
|
+
getGlobalInjector().counterStyle(name, descriptors, { root: options?.root });
|
|
40
59
|
return name;
|
|
41
60
|
}
|
|
42
61
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useCounterStyle.js","names":[],"sources":["../../src/hooks/useCounterStyle.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"useCounterStyle.js","names":[],"sources":["../../src/hooks/useCounterStyle.ts"],"sourcesContent":["import { getGlobalInjector } from '../config';\nimport { formatCounterStyleRule } from '../counter-style';\nimport type { CounterStyleDescriptors } from '../injector/types';\nimport { getStyleTarget, pushRSCCSS } from '../rsc-cache';\n\ninterface UseCounterStyleOptions {\n name?: string;\n root?: Document | ShadowRoot;\n}\n\nlet clientCounterStyleCounter = 0;\n\nconst clientContentToName = new Map<string, string>();\n\n/* @internal — used only for tests */\nexport function _resetCounterStyleCache(): void {\n clientContentToName.clear();\n clientCounterStyleCounter = 0;\n}\n\n/**\n * Inject a CSS @counter-style rule and return the generated name.\n * Permanent — no cleanup on unmount. Deduplicates by name.\n *\n * Works in all environments: client, SSR with collector, and React Server Components.\n *\n * @example Basic usage\n * ```tsx\n * function EmojiList() {\n * const styleName = useCounterStyle({\n * system: 'cyclic',\n * symbols: '\"👍\"',\n * suffix: '\" \"',\n * }, { name: 'thumbs' });\n *\n * return (\n * <ol style={{ listStyleType: styleName }}>\n * <li>First</li>\n * <li>Second</li>\n * </ol>\n * );\n * }\n * ```\n *\n */\nexport function useCounterStyle(\n descriptors: CounterStyleDescriptors,\n options?: UseCounterStyleOptions,\n): string {\n if (!descriptors || !descriptors.system) {\n return '';\n }\n\n const target = getStyleTarget();\n\n if (target.mode === 'ssr') {\n const actualName = target.collector.allocateCounterStyleName(options?.name);\n const css = formatCounterStyleRule(actualName, descriptors);\n target.collector.collectCounterStyle(actualName, css);\n return actualName;\n }\n\n if (target.mode === 'rsc') {\n const serializedContent = JSON.stringify(descriptors);\n const key = `__cs:${options?.name ?? ''}:${serializedContent}`;\n\n const existingName = target.cache.generatedNames.get(key);\n if (existingName) return existingName;\n\n const actualName =\n options?.name ?? `cs${target.cache.counterStyleCounter++}`;\n const css = formatCounterStyleRule(actualName, descriptors);\n pushRSCCSS(target.cache, key, css);\n target.cache.generatedNames.set(key, actualName);\n return actualName;\n }\n\n // Client path: stable name via content-based dedup\n const serializedContent = JSON.stringify(descriptors);\n const cacheKey = `${options?.name ?? ''}:${serializedContent}`;\n\n const existingName = clientContentToName.get(cacheKey);\n if (existingName) {\n return existingName;\n }\n\n const name = options?.name ?? `cs${clientCounterStyleCounter++}`;\n clientContentToName.set(cacheKey, name);\n\n const injector = getGlobalInjector();\n injector.counterStyle(name, descriptors, { root: options?.root });\n\n return name;\n}\n"],"mappings":";;;;AAUA,IAAI,4BAA4B;AAEhC,MAAM,sCAAsB,IAAI,KAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;AAiCrD,SAAgB,gBACd,aACA,SACQ;AACR,KAAI,CAAC,eAAe,CAAC,YAAY,OAC/B,QAAO;CAGT,MAAM,SAAS,gBAAgB;AAE/B,KAAI,OAAO,SAAS,OAAO;EACzB,MAAM,aAAa,OAAO,UAAU,yBAAyB,SAAS,KAAK;EAC3E,MAAM,MAAM,uBAAuB,YAAY,YAAY;AAC3D,SAAO,UAAU,oBAAoB,YAAY,IAAI;AACrD,SAAO;;AAGT,KAAI,OAAO,SAAS,OAAO;EACzB,MAAM,oBAAoB,KAAK,UAAU,YAAY;EACrD,MAAM,MAAM,QAAQ,SAAS,QAAQ,GAAG,GAAG;EAE3C,MAAM,eAAe,OAAO,MAAM,eAAe,IAAI,IAAI;AACzD,MAAI,aAAc,QAAO;EAEzB,MAAM,aACJ,SAAS,QAAQ,KAAK,OAAO,MAAM;EACrC,MAAM,MAAM,uBAAuB,YAAY,YAAY;AAC3D,aAAW,OAAO,OAAO,KAAK,IAAI;AAClC,SAAO,MAAM,eAAe,IAAI,KAAK,WAAW;AAChD,SAAO;;CAIT,MAAM,oBAAoB,KAAK,UAAU,YAAY;CACrD,MAAM,WAAW,GAAG,SAAS,QAAQ,GAAG,GAAG;CAE3C,MAAM,eAAe,oBAAoB,IAAI,SAAS;AACtD,KAAI,aACF,QAAO;CAGT,MAAM,OAAO,SAAS,QAAQ,KAAK;AACnC,qBAAoB,IAAI,UAAU,KAAK;AAEtB,oBAAmB,CAC3B,aAAa,MAAM,aAAa,EAAE,MAAM,SAAS,MAAM,CAAC;AAEjE,QAAO"}
|
|
@@ -5,9 +5,11 @@ interface UseFontFaceOptions {
|
|
|
5
5
|
root?: Document | ShadowRoot;
|
|
6
6
|
}
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
8
|
+
* Inject CSS @font-face rules.
|
|
9
9
|
* Permanent — no cleanup on unmount. Deduplicates by content hash.
|
|
10
10
|
*
|
|
11
|
+
* Works in all environments: client, SSR with collector, and React Server Components.
|
|
12
|
+
*
|
|
11
13
|
* @param family - The font-family name
|
|
12
14
|
* @param input - Single descriptor object or array of descriptors (for multiple weights/styles)
|
|
13
15
|
* @param options - Optional settings (e.g. Shadow DOM root)
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { fontFaceContentHash, formatFontFaceRule } from "../font-face/index.js";
|
|
2
2
|
import { getGlobalInjector } from "../config.js";
|
|
3
|
-
import {
|
|
4
|
-
import { useInsertionEffect, useMemo } from "react";
|
|
3
|
+
import { getStyleTarget, pushRSCCSS } from "../rsc-cache.js";
|
|
5
4
|
//#region src/hooks/useFontFace.ts
|
|
6
5
|
/**
|
|
7
|
-
*
|
|
6
|
+
* Inject CSS @font-face rules.
|
|
8
7
|
* Permanent — no cleanup on unmount. Deduplicates by content hash.
|
|
9
8
|
*
|
|
9
|
+
* Works in all environments: client, SSR with collector, and React Server Components.
|
|
10
|
+
*
|
|
10
11
|
* @param family - The font-family name
|
|
11
12
|
* @param input - Single descriptor object or array of descriptors (for multiple weights/styles)
|
|
12
13
|
* @param options - Optional settings (e.g. Shadow DOM root)
|
|
@@ -37,31 +38,27 @@ import { useInsertionEffect, useMemo } from "react";
|
|
|
37
38
|
* ```
|
|
38
39
|
*/
|
|
39
40
|
function useFontFace(family, input, options) {
|
|
40
|
-
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
const
|
|
41
|
+
if (!family) return;
|
|
42
|
+
const descriptors = Array.isArray(input) ? input : [input];
|
|
43
|
+
const target = getStyleTarget();
|
|
44
|
+
if (target.mode === "ssr") {
|
|
45
|
+
for (const desc of descriptors) {
|
|
46
|
+
const hash = fontFaceContentHash(family, desc);
|
|
47
|
+
const css = formatFontFaceRule(family, desc);
|
|
48
|
+
target.collector.collectFontFace(hash, css);
|
|
49
|
+
}
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if (target.mode === "rsc") {
|
|
45
53
|
for (const desc of descriptors) {
|
|
46
54
|
const hash = fontFaceContentHash(family, desc);
|
|
47
55
|
const css = formatFontFaceRule(family, desc);
|
|
48
|
-
|
|
56
|
+
pushRSCCSS(target.cache, `__ff:${hash}`, css);
|
|
49
57
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
]);
|
|
55
|
-
useInsertionEffect(() => {
|
|
56
|
-
if (!family) return;
|
|
57
|
-
const injector = getGlobalInjector();
|
|
58
|
-
const descriptors = Array.isArray(input) ? input : [input];
|
|
59
|
-
for (const desc of descriptors) injector.fontFace(family, desc, { root: options?.root });
|
|
60
|
-
}, [
|
|
61
|
-
family,
|
|
62
|
-
inputKey,
|
|
63
|
-
options?.root
|
|
64
|
-
]);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const injector = getGlobalInjector();
|
|
61
|
+
for (const desc of descriptors) injector.fontFace(family, desc, { root: options?.root });
|
|
65
62
|
}
|
|
66
63
|
//#endregion
|
|
67
64
|
export { useFontFace };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useFontFace.js","names":[],"sources":["../../src/hooks/useFontFace.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"useFontFace.js","names":[],"sources":["../../src/hooks/useFontFace.ts"],"sourcesContent":["import { getGlobalInjector } from '../config';\nimport { fontFaceContentHash, formatFontFaceRule } from '../font-face';\nimport type { FontFaceDescriptors, FontFaceInput } from '../injector/types';\nimport { getStyleTarget, pushRSCCSS } from '../rsc-cache';\n\ninterface UseFontFaceOptions {\n root?: Document | ShadowRoot;\n}\n\n/**\n * Inject CSS @font-face rules.\n * Permanent — no cleanup on unmount. Deduplicates by content hash.\n *\n * Works in all environments: client, SSR with collector, and React Server Components.\n *\n * @param family - The font-family name\n * @param input - Single descriptor object or array of descriptors (for multiple weights/styles)\n * @param options - Optional settings (e.g. Shadow DOM root)\n *\n * @example Single weight\n * ```tsx\n * function App() {\n * useFontFace('Brand Sans', {\n * src: 'url(\"/fonts/brand-sans.woff2\") format(\"woff2\")',\n * fontWeight: '400 700',\n * fontDisplay: 'swap',\n * });\n *\n * return <div style={{ fontFamily: '\"Brand Sans\", sans-serif' }}>Hello</div>;\n * }\n * ```\n *\n * @example Multiple weights\n * ```tsx\n * function App() {\n * useFontFace('Brand Sans', [\n * { src: 'url(\"/fonts/brand-regular.woff2\") format(\"woff2\")', fontWeight: 400, fontDisplay: 'swap' },\n * { src: 'url(\"/fonts/brand-bold.woff2\") format(\"woff2\")', fontWeight: 700, fontDisplay: 'swap' },\n * ]);\n *\n * return <div style={{ fontFamily: '\"Brand Sans\", sans-serif' }}>Hello</div>;\n * }\n * ```\n */\nexport function useFontFace(\n family: string,\n input: FontFaceInput,\n options?: UseFontFaceOptions,\n): void {\n if (!family) return;\n\n const descriptors: FontFaceDescriptors[] = Array.isArray(input)\n ? input\n : [input];\n\n const target = getStyleTarget();\n\n if (target.mode === 'ssr') {\n for (const desc of descriptors) {\n const hash = fontFaceContentHash(family, desc);\n const css = formatFontFaceRule(family, desc);\n target.collector.collectFontFace(hash, css);\n }\n return;\n }\n\n if (target.mode === 'rsc') {\n for (const desc of descriptors) {\n const hash = fontFaceContentHash(family, desc);\n const css = formatFontFaceRule(family, desc);\n pushRSCCSS(target.cache, `__ff:${hash}`, css);\n }\n return;\n }\n\n const injector = getGlobalInjector();\n for (const desc of descriptors) {\n injector.fontFace(family, desc, { root: options?.root });\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4CA,SAAgB,YACd,QACA,OACA,SACM;AACN,KAAI,CAAC,OAAQ;CAEb,MAAM,cAAqC,MAAM,QAAQ,MAAM,GAC3D,QACA,CAAC,MAAM;CAEX,MAAM,SAAS,gBAAgB;AAE/B,KAAI,OAAO,SAAS,OAAO;AACzB,OAAK,MAAM,QAAQ,aAAa;GAC9B,MAAM,OAAO,oBAAoB,QAAQ,KAAK;GAC9C,MAAM,MAAM,mBAAmB,QAAQ,KAAK;AAC5C,UAAO,UAAU,gBAAgB,MAAM,IAAI;;AAE7C;;AAGF,KAAI,OAAO,SAAS,OAAO;AACzB,OAAK,MAAM,QAAQ,aAAa;GAC9B,MAAM,OAAO,oBAAoB,QAAQ,KAAK;GAC9C,MAAM,MAAM,mBAAmB,QAAQ,KAAK;AAC5C,cAAW,OAAO,OAAO,QAAQ,QAAQ,IAAI;;AAE/C;;CAGF,MAAM,WAAW,mBAAmB;AACpC,MAAK,MAAM,QAAQ,YACjB,UAAS,SAAS,QAAQ,MAAM,EAAE,MAAM,SAAS,MAAM,CAAC"}
|
|
@@ -1,15 +1,31 @@
|
|
|
1
1
|
import { Styles } from "../styles/types.js";
|
|
2
2
|
|
|
3
3
|
//#region src/hooks/useGlobalStyles.d.ts
|
|
4
|
+
interface UseGlobalStylesOptions {
|
|
5
|
+
/**
|
|
6
|
+
* Stable identifier for update tracking (client-only). When provided,
|
|
7
|
+
* changing the styles will dispose the previous injection and inject the
|
|
8
|
+
* new one. Without an id, the selector is used as the slot key.
|
|
9
|
+
* In RSC mode, renders are single-pass so update tracking does not apply.
|
|
10
|
+
*/
|
|
11
|
+
id?: string;
|
|
12
|
+
}
|
|
4
13
|
/**
|
|
5
|
-
*
|
|
14
|
+
* Inject global styles for a given selector.
|
|
6
15
|
* Useful for styling elements by selector without generating classNames.
|
|
7
16
|
*
|
|
8
17
|
* SSR-aware: when a ServerStyleCollector is available, CSS is collected
|
|
9
18
|
* during the render phase instead of being injected into the DOM.
|
|
10
19
|
*
|
|
20
|
+
* Works in all environments: client, SSR with collector, and React Server Components.
|
|
21
|
+
*
|
|
22
|
+
* Injected styles are permanent — they are not cleaned up on component unmount.
|
|
23
|
+
* Use the `id` option for update tracking when styles change over the
|
|
24
|
+
* component lifecycle.
|
|
25
|
+
*
|
|
11
26
|
* @param selector - CSS selector to apply styles to (e.g., '.my-class', ':root', 'body')
|
|
12
27
|
* @param styles - Tasty styles object
|
|
28
|
+
* @param options - Optional settings including `id` for update tracking
|
|
13
29
|
*
|
|
14
30
|
* @example
|
|
15
31
|
* ```tsx
|
|
@@ -24,7 +40,7 @@ import { Styles } from "../styles/types.js";
|
|
|
24
40
|
* }
|
|
25
41
|
* ```
|
|
26
42
|
*/
|
|
27
|
-
declare function useGlobalStyles(selector: string, styles?: Styles): void;
|
|
43
|
+
declare function useGlobalStyles(selector: string, styles?: Styles, options?: UseGlobalStylesOptions): void;
|
|
28
44
|
//#endregion
|
|
29
45
|
export { useGlobalStyles };
|
|
30
46
|
//# sourceMappingURL=useGlobalStyles.d.ts.map
|
|
@@ -1,21 +1,29 @@
|
|
|
1
1
|
import { renderStyles } from "../pipeline/index.js";
|
|
2
2
|
import { getConfig } from "../config.js";
|
|
3
3
|
import { injectGlobal } from "../injector/index.js";
|
|
4
|
-
import {
|
|
4
|
+
import { getStyleTarget, pushRSCCSS } from "../rsc-cache.js";
|
|
5
|
+
import { collectAutoInferredProperties, collectAutoInferredPropertiesRSC } from "../ssr/collect-auto-properties.js";
|
|
5
6
|
import { formatGlobalRules } from "../ssr/format-global-rules.js";
|
|
6
|
-
import { getRegisteredSSRCollector } from "../ssr/ssr-collector-ref.js";
|
|
7
7
|
import { resolveRecipes } from "../utils/resolve-recipes.js";
|
|
8
|
-
import {
|
|
8
|
+
import { hashString } from "../utils/hash.js";
|
|
9
9
|
//#region src/hooks/useGlobalStyles.ts
|
|
10
|
+
const clientGlobalEntries = /* @__PURE__ */ new Map();
|
|
10
11
|
/**
|
|
11
|
-
*
|
|
12
|
+
* Inject global styles for a given selector.
|
|
12
13
|
* Useful for styling elements by selector without generating classNames.
|
|
13
14
|
*
|
|
14
15
|
* SSR-aware: when a ServerStyleCollector is available, CSS is collected
|
|
15
16
|
* during the render phase instead of being injected into the DOM.
|
|
16
17
|
*
|
|
18
|
+
* Works in all environments: client, SSR with collector, and React Server Components.
|
|
19
|
+
*
|
|
20
|
+
* Injected styles are permanent — they are not cleaned up on component unmount.
|
|
21
|
+
* Use the `id` option for update tracking when styles change over the
|
|
22
|
+
* component lifecycle.
|
|
23
|
+
*
|
|
17
24
|
* @param selector - CSS selector to apply styles to (e.g., '.my-class', ':root', 'body')
|
|
18
25
|
* @param styles - Tasty styles object
|
|
26
|
+
* @param options - Optional settings including `id` for update tracking
|
|
19
27
|
*
|
|
20
28
|
* @example
|
|
21
29
|
* ```tsx
|
|
@@ -30,46 +38,49 @@ import { useInsertionEffect, useMemo, useRef } from "react";
|
|
|
30
38
|
* }
|
|
31
39
|
* ```
|
|
32
40
|
*/
|
|
33
|
-
function useGlobalStyles(selector, styles) {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
41
|
+
function useGlobalStyles(selector, styles, options) {
|
|
42
|
+
if (!styles) return;
|
|
43
|
+
if (!selector) {
|
|
44
|
+
console.warn("[Tasty] useGlobalStyles: selector is required and cannot be empty. Styles will not be injected.");
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
const target = getStyleTarget();
|
|
48
|
+
if (target.mode === "client") {
|
|
49
|
+
const slotKey = options?.id ?? selector;
|
|
50
|
+
const stylesKey = JSON.stringify(styles);
|
|
51
|
+
const existing = clientGlobalEntries.get(slotKey);
|
|
52
|
+
if (existing && existing.stylesKey === stylesKey) return;
|
|
53
|
+
}
|
|
54
|
+
const resolvedStyles = resolveRecipes(styles);
|
|
55
|
+
const styleResults = renderStyles(resolvedStyles, selector);
|
|
56
|
+
if (styleResults.length === 0) return;
|
|
57
|
+
if (target.mode === "ssr") {
|
|
58
|
+
target.collector.collectInternals();
|
|
59
|
+
const css = formatGlobalRules(styleResults);
|
|
60
|
+
if (css) {
|
|
61
|
+
const key = options?.id ? `global:${options.id}` : `global:${selector}:${hashString(css)}`;
|
|
62
|
+
target.collector.collectGlobalStyles(key, css);
|
|
45
63
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
ssrCollector.collectInternals();
|
|
64
|
+
if (getConfig().autoPropertyTypes !== false) collectAutoInferredProperties(styleResults, target.collector, resolvedStyles);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
if (target.mode === "rsc") {
|
|
51
68
|
const css = formatGlobalRules(styleResults);
|
|
52
69
|
if (css) {
|
|
53
|
-
const key = `
|
|
54
|
-
|
|
70
|
+
const key = options?.id ? `__global:${options.id}` : `__global:${selector}:${hashString(css)}`;
|
|
71
|
+
pushRSCCSS(target.cache, key, css);
|
|
55
72
|
}
|
|
56
|
-
if (getConfig().autoPropertyTypes !== false)
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
} else disposeRef.current = null;
|
|
68
|
-
return () => {
|
|
69
|
-
disposeRef.current?.();
|
|
70
|
-
disposeRef.current = null;
|
|
71
|
-
};
|
|
72
|
-
}, [styleResults]);
|
|
73
|
+
if (getConfig().autoPropertyTypes !== false) collectAutoInferredPropertiesRSC(styleResults, target.cache, resolvedStyles);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
const slotKey = options?.id ?? selector;
|
|
77
|
+
const existing = clientGlobalEntries.get(slotKey);
|
|
78
|
+
if (existing) existing.dispose();
|
|
79
|
+
const { dispose } = injectGlobal(styleResults);
|
|
80
|
+
clientGlobalEntries.set(slotKey, {
|
|
81
|
+
stylesKey: JSON.stringify(styles),
|
|
82
|
+
dispose
|
|
83
|
+
});
|
|
73
84
|
}
|
|
74
85
|
//#endregion
|
|
75
86
|
export { useGlobalStyles };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useGlobalStyles.js","names":[],"sources":["../../src/hooks/useGlobalStyles.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"useGlobalStyles.js","names":[],"sources":["../../src/hooks/useGlobalStyles.ts"],"sourcesContent":["import { getConfig } from '../config';\nimport { injectGlobal } from '../injector';\nimport type { StyleResult } from '../pipeline';\nimport { renderStyles } from '../pipeline';\nimport { getStyleTarget, pushRSCCSS } from '../rsc-cache';\nimport {\n collectAutoInferredProperties,\n collectAutoInferredPropertiesRSC,\n} from '../ssr/collect-auto-properties';\nimport { formatGlobalRules } from '../ssr/format-global-rules';\nimport type { Styles } from '../styles/types';\nimport { hashString } from '../utils/hash';\nimport { resolveRecipes } from '../utils/resolve-recipes';\n\ninterface UseGlobalStylesOptions {\n /**\n * Stable identifier for update tracking (client-only). When provided,\n * changing the styles will dispose the previous injection and inject the\n * new one. Without an id, the selector is used as the slot key.\n * In RSC mode, renders are single-pass so update tracking does not apply.\n */\n id?: string;\n}\n\ninterface ClientGlobalEntry {\n stylesKey: string;\n dispose: () => void;\n}\n\nconst clientGlobalEntries = new Map<string, ClientGlobalEntry>();\n\n/* @internal — used only for tests */\nexport function _resetGlobalStylesCache(): void {\n clientGlobalEntries.clear();\n}\n\n/**\n * Inject global styles for a given selector.\n * Useful for styling elements by selector without generating classNames.\n *\n * SSR-aware: when a ServerStyleCollector is available, CSS is collected\n * during the render phase instead of being injected into the DOM.\n *\n * Works in all environments: client, SSR with collector, and React Server Components.\n *\n * Injected styles are permanent — they are not cleaned up on component unmount.\n * Use the `id` option for update tracking when styles change over the\n * component lifecycle.\n *\n * @param selector - CSS selector to apply styles to (e.g., '.my-class', ':root', 'body')\n * @param styles - Tasty styles object\n * @param options - Optional settings including `id` for update tracking\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * useGlobalStyles('.card', {\n * padding: '2x',\n * radius: '1r',\n * fill: '#white',\n * });\n *\n * return <div className=\"card\">Content</div>;\n * }\n * ```\n */\nexport function useGlobalStyles(\n selector: string,\n styles?: Styles,\n options?: UseGlobalStylesOptions,\n): void {\n if (!styles) return;\n\n if (!selector) {\n if (process.env.NODE_ENV !== 'production') {\n console.warn(\n '[Tasty] useGlobalStyles: selector is required and cannot be empty. ' +\n 'Styles will not be injected.',\n );\n }\n return;\n }\n\n const target = getStyleTarget();\n\n // Client fast path: skip resolveRecipes/renderStyles if styles haven't changed\n if (target.mode === 'client') {\n const slotKey = options?.id ?? selector;\n const stylesKey = JSON.stringify(styles);\n const existing = clientGlobalEntries.get(slotKey);\n if (existing && existing.stylesKey === stylesKey) return;\n }\n\n const resolvedStyles = resolveRecipes(styles);\n\n const styleResults = renderStyles(resolvedStyles, selector) as StyleResult[];\n\n if (styleResults.length === 0) return;\n\n if (target.mode === 'ssr') {\n target.collector.collectInternals();\n\n const css = formatGlobalRules(styleResults);\n if (css) {\n const key = options?.id\n ? `global:${options.id}`\n : `global:${selector}:${hashString(css)}`;\n target.collector.collectGlobalStyles(key, css);\n }\n\n if (getConfig().autoPropertyTypes !== false) {\n collectAutoInferredProperties(\n styleResults,\n target.collector,\n resolvedStyles,\n );\n }\n return;\n }\n\n if (target.mode === 'rsc') {\n const css = formatGlobalRules(styleResults);\n if (css) {\n const key = options?.id\n ? `__global:${options.id}`\n : `__global:${selector}:${hashString(css)}`;\n pushRSCCSS(target.cache, key, css);\n }\n\n if (getConfig().autoPropertyTypes !== false) {\n collectAutoInferredPropertiesRSC(\n styleResults,\n target.cache,\n resolvedStyles,\n );\n }\n return;\n }\n\n // Client path\n const slotKey = options?.id ?? selector;\n\n const existing = clientGlobalEntries.get(slotKey);\n if (existing) {\n existing.dispose();\n }\n\n const { dispose } = injectGlobal(styleResults);\n clientGlobalEntries.set(slotKey, {\n stylesKey: JSON.stringify(styles),\n dispose,\n });\n}\n"],"mappings":";;;;;;;;;AA6BA,MAAM,sCAAsB,IAAI,KAAgC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqChE,SAAgB,gBACd,UACA,QACA,SACM;AACN,KAAI,CAAC,OAAQ;AAEb,KAAI,CAAC,UAAU;AAEX,UAAQ,KACN,kGAED;AAEH;;CAGF,MAAM,SAAS,gBAAgB;AAG/B,KAAI,OAAO,SAAS,UAAU;EAC5B,MAAM,UAAU,SAAS,MAAM;EAC/B,MAAM,YAAY,KAAK,UAAU,OAAO;EACxC,MAAM,WAAW,oBAAoB,IAAI,QAAQ;AACjD,MAAI,YAAY,SAAS,cAAc,UAAW;;CAGpD,MAAM,iBAAiB,eAAe,OAAO;CAE7C,MAAM,eAAe,aAAa,gBAAgB,SAAS;AAE3D,KAAI,aAAa,WAAW,EAAG;AAE/B,KAAI,OAAO,SAAS,OAAO;AACzB,SAAO,UAAU,kBAAkB;EAEnC,MAAM,MAAM,kBAAkB,aAAa;AAC3C,MAAI,KAAK;GACP,MAAM,MAAM,SAAS,KACjB,UAAU,QAAQ,OAClB,UAAU,SAAS,GAAG,WAAW,IAAI;AACzC,UAAO,UAAU,oBAAoB,KAAK,IAAI;;AAGhD,MAAI,WAAW,CAAC,sBAAsB,MACpC,+BACE,cACA,OAAO,WACP,eACD;AAEH;;AAGF,KAAI,OAAO,SAAS,OAAO;EACzB,MAAM,MAAM,kBAAkB,aAAa;AAC3C,MAAI,KAAK;GACP,MAAM,MAAM,SAAS,KACjB,YAAY,QAAQ,OACpB,YAAY,SAAS,GAAG,WAAW,IAAI;AAC3C,cAAW,OAAO,OAAO,KAAK,IAAI;;AAGpC,MAAI,WAAW,CAAC,sBAAsB,MACpC,kCACE,cACA,OAAO,OACP,eACD;AAEH;;CAIF,MAAM,UAAU,SAAS,MAAM;CAE/B,MAAM,WAAW,oBAAoB,IAAI,QAAQ;AACjD,KAAI,SACF,UAAS,SAAS;CAGpB,MAAM,EAAE,YAAY,aAAa,aAAa;AAC9C,qBAAoB,IAAI,SAAS;EAC/B,WAAW,KAAK,UAAU,OAAO;EACjC;EACD,CAAC"}
|
|
@@ -6,8 +6,10 @@ interface UseKeyframesOptions {
|
|
|
6
6
|
root?: Document | ShadowRoot;
|
|
7
7
|
}
|
|
8
8
|
/**
|
|
9
|
-
*
|
|
10
|
-
*
|
|
9
|
+
* Inject CSS @keyframes and return the generated animation name.
|
|
10
|
+
* Deduplicates by content — identical steps always return the same name.
|
|
11
|
+
*
|
|
12
|
+
* Works in all environments: client, SSR with collector, and React Server Components.
|
|
11
13
|
*
|
|
12
14
|
* @example Basic usage - steps object is the dependency
|
|
13
15
|
* ```tsx
|
|
@@ -1,60 +1,51 @@
|
|
|
1
1
|
import { keyframes } from "../injector/index.js";
|
|
2
|
+
import { getStyleTarget, pushRSCCSS } from "../rsc-cache.js";
|
|
2
3
|
import { formatKeyframesCSS } from "../ssr/format-keyframes.js";
|
|
3
|
-
import {
|
|
4
|
-
import { useInsertionEffect, useMemo, useRef } from "react";
|
|
4
|
+
import { depsEqual } from "../utils/deps-equal.js";
|
|
5
5
|
//#region src/hooks/useKeyframes.ts
|
|
6
|
+
const clientContentToName = /* @__PURE__ */ new Map();
|
|
7
|
+
const factoryDepsCache = /* @__PURE__ */ new Map();
|
|
6
8
|
function useKeyframes(stepsOrFactory, depsOrOptions, options) {
|
|
7
|
-
const ssrCollector = getRegisteredSSRCollector();
|
|
8
9
|
const isFactory = typeof stepsOrFactory === "function";
|
|
9
10
|
const deps = isFactory && Array.isArray(depsOrOptions) ? depsOrOptions : void 0;
|
|
10
11
|
const opts = isFactory ? options : depsOrOptions;
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
return
|
|
15
|
-
}
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
return
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
effectResultRef.current?.dispose();
|
|
49
|
-
effectResultRef.current = null;
|
|
50
|
-
renderResultRef.current?.dispose();
|
|
51
|
-
renderResultRef.current = null;
|
|
52
|
-
};
|
|
53
|
-
}, [
|
|
54
|
-
stepsData,
|
|
55
|
-
opts?.name,
|
|
56
|
-
opts?.root
|
|
57
|
-
]);
|
|
12
|
+
const target = getStyleTarget();
|
|
13
|
+
if (isFactory && deps && opts?.name && target.mode === "client") {
|
|
14
|
+
const cached = factoryDepsCache.get(opts.name);
|
|
15
|
+
if (cached && depsEqual(cached.deps, deps)) return cached.name;
|
|
16
|
+
}
|
|
17
|
+
const steps = isFactory ? stepsOrFactory() : stepsOrFactory;
|
|
18
|
+
if (!steps || Object.keys(steps).length === 0) return "";
|
|
19
|
+
if (target.mode === "ssr") {
|
|
20
|
+
const actualName = target.collector.allocateKeyframeName(opts?.name);
|
|
21
|
+
const css = formatKeyframesCSS(actualName, steps);
|
|
22
|
+
target.collector.collectKeyframes(actualName, css);
|
|
23
|
+
return actualName;
|
|
24
|
+
}
|
|
25
|
+
if (target.mode === "rsc") {
|
|
26
|
+
const serializedContent = JSON.stringify(steps);
|
|
27
|
+
const key = `__kf:${opts?.name ?? ""}:${serializedContent}`;
|
|
28
|
+
const existingName = target.cache.generatedNames.get(key);
|
|
29
|
+
if (existingName) return existingName;
|
|
30
|
+
const actualName = opts?.name ?? `k${target.cache.keyframesCounter++}`;
|
|
31
|
+
const css = formatKeyframesCSS(actualName, steps);
|
|
32
|
+
pushRSCCSS(target.cache, key, css);
|
|
33
|
+
target.cache.generatedNames.set(key, actualName);
|
|
34
|
+
return actualName;
|
|
35
|
+
}
|
|
36
|
+
const serializedContent = JSON.stringify(steps);
|
|
37
|
+
const cacheKey = `${opts?.name ?? ""}:${serializedContent}`;
|
|
38
|
+
const cachedName = clientContentToName.get(cacheKey);
|
|
39
|
+
if (cachedName) return cachedName;
|
|
40
|
+
const name = keyframes(steps, {
|
|
41
|
+
name: opts?.name,
|
|
42
|
+
root: opts?.root
|
|
43
|
+
}).toString();
|
|
44
|
+
clientContentToName.set(cacheKey, name);
|
|
45
|
+
if (deps && opts?.name) factoryDepsCache.set(opts.name, {
|
|
46
|
+
deps,
|
|
47
|
+
name
|
|
48
|
+
});
|
|
58
49
|
return name;
|
|
59
50
|
}
|
|
60
51
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useKeyframes.js","names":[],"sources":["../../src/hooks/useKeyframes.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"useKeyframes.js","names":[],"sources":["../../src/hooks/useKeyframes.ts"],"sourcesContent":["import { keyframes } from '../injector';\nimport type { KeyframesSteps } from '../injector/types';\nimport { getStyleTarget, pushRSCCSS } from '../rsc-cache';\nimport { formatKeyframesCSS } from '../ssr/format-keyframes';\nimport { depsEqual } from '../utils/deps-equal';\n\ninterface UseKeyframesOptions {\n name?: string;\n root?: Document | ShadowRoot;\n}\n\nconst clientContentToName = new Map<string, string>();\n\ninterface FactoryDepsEntry {\n deps: readonly unknown[];\n name: string;\n}\n\nconst factoryDepsCache = new Map<string, FactoryDepsEntry>();\n\n/* @internal — used only for tests */\nexport function _resetKeyframesCache(): void {\n clientContentToName.clear();\n factoryDepsCache.clear();\n}\n\n/**\n * Inject CSS @keyframes and return the generated animation name.\n * Deduplicates by content — identical steps always return the same name.\n *\n * Works in all environments: client, SSR with collector, and React Server Components.\n *\n * @example Basic usage - steps object is the dependency\n * ```tsx\n * function MyComponent() {\n * const bounce = useKeyframes({\n * '0%': { transform: 'scale(1)' },\n * '50%': { transform: 'scale(1.1)' },\n * '100%': { transform: 'scale(1)' },\n * });\n *\n * return <div style={{ animation: `${bounce} 1s infinite` }}>Bouncing</div>;\n * }\n * ```\n *\n * @example With custom name\n * ```tsx\n * function MyComponent() {\n * const fadeIn = useKeyframes(\n * { from: { opacity: 0 }, to: { opacity: 1 } },\n * { name: 'fadeIn' }\n * );\n *\n * return <div style={{ animation: `${fadeIn} 0.3s ease-out` }}>Fading in</div>;\n * }\n * ```\n *\n * @example Factory function with dependencies\n * ```tsx\n * function MyComponent({ scale }: { scale: number }) {\n * const pulse = useKeyframes(\n * () => ({\n * '0%': { transform: 'scale(1)' },\n * '100%': { transform: `scale(${scale})` },\n * }),\n * [scale]\n * );\n *\n * return <div style={{ animation: `${pulse} 1s infinite` }}>Pulsing</div>;\n * }\n * ```\n */\n\n// Overload 1: Static steps object\nexport function useKeyframes(\n steps: KeyframesSteps,\n options?: UseKeyframesOptions,\n): string;\n\n// Overload 2: Factory function with dependencies\nexport function useKeyframes(\n factory: () => KeyframesSteps,\n deps: readonly unknown[],\n options?: UseKeyframesOptions,\n): string;\n\n// Implementation\nexport function useKeyframes(\n stepsOrFactory: KeyframesSteps | (() => KeyframesSteps),\n depsOrOptions?: readonly unknown[] | UseKeyframesOptions,\n options?: UseKeyframesOptions,\n): string {\n const isFactory = typeof stepsOrFactory === 'function';\n\n const deps =\n isFactory && Array.isArray(depsOrOptions) ? depsOrOptions : undefined;\n const opts = isFactory\n ? options\n : (depsOrOptions as UseKeyframesOptions | undefined);\n\n const target = getStyleTarget();\n\n // Client deps cache: skip factory re-evaluation when deps haven't changed\n if (isFactory && deps && opts?.name && target.mode === 'client') {\n const cached = factoryDepsCache.get(opts.name);\n if (cached && depsEqual(cached.deps, deps)) {\n return cached.name;\n }\n }\n\n const steps = isFactory\n ? (stepsOrFactory as () => KeyframesSteps)()\n : (stepsOrFactory as KeyframesSteps);\n\n if (!steps || Object.keys(steps).length === 0) {\n return '';\n }\n\n if (target.mode === 'ssr') {\n const actualName = target.collector.allocateKeyframeName(opts?.name);\n const css = formatKeyframesCSS(actualName, steps);\n target.collector.collectKeyframes(actualName, css);\n return actualName;\n }\n\n if (target.mode === 'rsc') {\n const serializedContent = JSON.stringify(steps);\n const key = `__kf:${opts?.name ?? ''}:${serializedContent}`;\n\n const existingName = target.cache.generatedNames.get(key);\n if (existingName) return existingName;\n\n const actualName = opts?.name ?? `k${target.cache.keyframesCounter++}`;\n const css = formatKeyframesCSS(actualName, steps);\n pushRSCCSS(target.cache, key, css);\n target.cache.generatedNames.set(key, actualName);\n return actualName;\n }\n\n // Client path: stable name via content-based dedup\n const serializedContent = JSON.stringify(steps);\n const cacheKey = `${opts?.name ?? ''}:${serializedContent}`;\n\n const cachedName = clientContentToName.get(cacheKey);\n if (cachedName) {\n return cachedName;\n }\n\n const result = keyframes(steps, {\n name: opts?.name,\n root: opts?.root,\n });\n\n const name = result.toString();\n clientContentToName.set(cacheKey, name);\n\n if (deps && opts?.name) {\n factoryDepsCache.set(opts.name, { deps, name });\n }\n\n return name;\n}\n"],"mappings":";;;;;AAWA,MAAM,sCAAsB,IAAI,KAAqB;AAOrD,MAAM,mCAAmB,IAAI,KAA+B;AAqE5D,SAAgB,aACd,gBACA,eACA,SACQ;CACR,MAAM,YAAY,OAAO,mBAAmB;CAE5C,MAAM,OACJ,aAAa,MAAM,QAAQ,cAAc,GAAG,gBAAgB,KAAA;CAC9D,MAAM,OAAO,YACT,UACC;CAEL,MAAM,SAAS,gBAAgB;AAG/B,KAAI,aAAa,QAAQ,MAAM,QAAQ,OAAO,SAAS,UAAU;EAC/D,MAAM,SAAS,iBAAiB,IAAI,KAAK,KAAK;AAC9C,MAAI,UAAU,UAAU,OAAO,MAAM,KAAK,CACxC,QAAO,OAAO;;CAIlB,MAAM,QAAQ,YACT,gBAAyC,GACzC;AAEL,KAAI,CAAC,SAAS,OAAO,KAAK,MAAM,CAAC,WAAW,EAC1C,QAAO;AAGT,KAAI,OAAO,SAAS,OAAO;EACzB,MAAM,aAAa,OAAO,UAAU,qBAAqB,MAAM,KAAK;EACpE,MAAM,MAAM,mBAAmB,YAAY,MAAM;AACjD,SAAO,UAAU,iBAAiB,YAAY,IAAI;AAClD,SAAO;;AAGT,KAAI,OAAO,SAAS,OAAO;EACzB,MAAM,oBAAoB,KAAK,UAAU,MAAM;EAC/C,MAAM,MAAM,QAAQ,MAAM,QAAQ,GAAG,GAAG;EAExC,MAAM,eAAe,OAAO,MAAM,eAAe,IAAI,IAAI;AACzD,MAAI,aAAc,QAAO;EAEzB,MAAM,aAAa,MAAM,QAAQ,IAAI,OAAO,MAAM;EAClD,MAAM,MAAM,mBAAmB,YAAY,MAAM;AACjD,aAAW,OAAO,OAAO,KAAK,IAAI;AAClC,SAAO,MAAM,eAAe,IAAI,KAAK,WAAW;AAChD,SAAO;;CAIT,MAAM,oBAAoB,KAAK,UAAU,MAAM;CAC/C,MAAM,WAAW,GAAG,MAAM,QAAQ,GAAG,GAAG;CAExC,MAAM,aAAa,oBAAoB,IAAI,SAAS;AACpD,KAAI,WACF,QAAO;CAQT,MAAM,OALS,UAAU,OAAO;EAC9B,MAAM,MAAM;EACZ,MAAM,MAAM;EACb,CAAC,CAEkB,UAAU;AAC9B,qBAAoB,IAAI,UAAU,KAAK;AAEvC,KAAI,QAAQ,MAAM,KAChB,kBAAiB,IAAI,KAAK,MAAM;EAAE;EAAM;EAAM,CAAC;AAGjD,QAAO"}
|
|
@@ -22,17 +22,19 @@ interface UsePropertyOptions {
|
|
|
22
22
|
root?: Document | ShadowRoot;
|
|
23
23
|
}
|
|
24
24
|
/**
|
|
25
|
-
*
|
|
25
|
+
* Register a CSS @property custom property.
|
|
26
26
|
* This enables advanced features like animating custom properties.
|
|
27
27
|
*
|
|
28
28
|
* Note: @property rules are global and persistent once defined.
|
|
29
|
-
* The
|
|
29
|
+
* The function ensures the property is only registered once per root.
|
|
30
30
|
*
|
|
31
31
|
* Accepts tasty token syntax for the property name:
|
|
32
32
|
* - `$name` → defines `--name`
|
|
33
33
|
* - `#name` → defines `--name-color` (auto-sets syntax: '<color>', defaults initialValue: 'transparent')
|
|
34
34
|
* - `--name` → defines `--name` (legacy format)
|
|
35
35
|
*
|
|
36
|
+
* Works in all environments: client, SSR with collector, and React Server Components.
|
|
37
|
+
*
|
|
36
38
|
* @param name - The property token ($name, #name) or CSS property name (--name)
|
|
37
39
|
* @param options - Property configuration
|
|
38
40
|
*
|