@askrjs/themes 0.0.19 → 0.0.20
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 +5 -2
- package/THEMING.md +8 -1
- package/dist/components/_internal/block-layout.js +1 -1
- package/dist/components/_internal/block-layout.js.map +1 -1
- package/dist/components/_internal/style.js +9 -21
- package/dist/components/_internal/style.js.map +1 -1
- package/dist/components/grid/grid.js +5 -42
- package/dist/components/grid/grid.js.map +1 -1
- package/dist/components/jsx-types.d.ts +0 -3
- package/dist/components/theme/theme.js +11 -12
- package/dist/components/theme/theme.js.map +1 -1
- package/dist/ssr.js +23 -11
- package/dist/ssr.js.map +1 -1
- package/dist/themes/default/index.css +1 -1
- package/package.json +6 -6
- package/src/components/_internal/block-layout.ts +2 -2
- package/src/components/_internal/style.ts +8 -26
- package/src/components/grid/grid.tsx +6 -57
- package/src/components/jsx-types.ts +0 -5
- package/src/components/theme/theme.tsx +22 -22
- package/src/ssr.ts +45 -20
- package/src/themes/default/tokens.css +1 -1
- package/templates/theme/tokens.css +1 -1
package/README.md
CHANGED
|
@@ -81,8 +81,11 @@ export const staticConfig = {
|
|
|
81
81
|
};
|
|
82
82
|
```
|
|
83
83
|
|
|
84
|
-
The wrapper also applies `context.cspNonce` to the emitted style registry
|
|
85
|
-
|
|
84
|
+
The wrapper also applies `context.cspNonce` to the emitted style registry and
|
|
85
|
+
requires the request-local style registrations provided by Askr 0.0.85 or
|
|
86
|
+
newer. It fails clearly if generated classes and their registered rules ever
|
|
87
|
+
diverge instead of emitting unstyled markup. Use the same wrapper for an SSR
|
|
88
|
+
`document` callback.
|
|
86
89
|
|
|
87
90
|
## What To Import
|
|
88
91
|
|
package/THEMING.md
CHANGED
|
@@ -29,6 +29,10 @@ Token override:
|
|
|
29
29
|
}
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
+
The shipped light and dark token sets are contrast-tested. Consumer token
|
|
33
|
+
overrides are ordinary CSS and cannot be validated by the runtime, so recheck
|
|
34
|
+
WCAG contrast for every overridden foreground/background pair.
|
|
35
|
+
|
|
32
36
|
Style override:
|
|
33
37
|
|
|
34
38
|
```css
|
|
@@ -132,6 +136,9 @@ wrapped in `data-slot="theme-toggle-content"` so icon and text compositions can
|
|
|
132
136
|
be styled consistently across themes. If you use icon children, the direct child
|
|
133
137
|
icon is sized from `var(--ak-theme-toggle-icon-size, var(--ak-icon-size, 1em))`,
|
|
134
138
|
so apps can override `--ak-theme-toggle-icon-size` to fit custom icon dimensions.
|
|
139
|
+
`ThemeName` accepts application-defined strings intentionally. Register custom
|
|
140
|
+
names in the scope's theme options and provide a matching `[data-theme="..."]`
|
|
141
|
+
token block; misspelled names otherwise remain valid custom identifiers.
|
|
135
142
|
The common wrapper components also emit familiar alias classes such as
|
|
136
143
|
`alert`, `btn-group`, `btn-close`, `input-group`, `tabs`, and `pills` so
|
|
137
144
|
app-level CSS can stay close to familiar HTML authoring without
|
|
@@ -755,7 +762,7 @@ These are recommended implementation defaults for component authors:
|
|
|
755
762
|
--ak-layout-content-max-width: 75rem;
|
|
756
763
|
|
|
757
764
|
/* stacking */
|
|
758
|
-
--ak-z-dropdown:
|
|
765
|
+
--ak-z-dropdown: 1500;
|
|
759
766
|
--ak-z-sticky: 1100;
|
|
760
767
|
--ak-z-fixed: 1200;
|
|
761
768
|
--ak-z-modal-backdrop: 1300;
|
|
@@ -208,4 +208,4 @@ function mergeLayoutStyles(layout, user) {
|
|
|
208
208
|
return mergedString || void 0;
|
|
209
209
|
}
|
|
210
210
|
//#endregion
|
|
211
|
-
export { applyBlockLayoutStyles, mergeLayoutStyles, splitBlockLayoutProps };
|
|
211
|
+
export { applyBlockLayoutStyles, mergeLayoutStyles, resolveSpaceValue, setResponsiveVar, splitBlockLayoutProps };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"block-layout.js","names":[],"sources":["../../../src/components/_internal/block-layout.ts"],"sourcesContent":["import { serializeCssDeclarations } from \"./style\";\n\nexport type LayoutBreakpoint = \"base\" | \"sm\" | \"md\" | \"lg\" | \"xl\";\nexport type ResponsiveValue<T> = T | Partial<Record<LayoutBreakpoint, T>>;\n\nexport type BlockSpace = \"0\" | \"xs\" | \"sm\" | \"md\" | \"lg\" | \"xl\" | \"2xl\" | \"3xl\" | \"page\";\nexport type BlockMargin = BlockSpace | \"auto\";\nexport type BlockSize =\n | \"auto\"\n | \"full\"\n | \"screen\"\n | \"content\"\n | \"sidebar\"\n | \"page\"\n | \"sm\"\n | \"md\"\n | \"lg\"\n | \"xl\";\nexport type BlockDirection = \"row\" | \"column\" | \"row-reverse\" | \"column-reverse\";\nexport type BlockRowFrom = Exclude<LayoutBreakpoint, \"base\">;\nexport type BlockAlign = \"start\" | \"end\" | \"center\" | \"stretch\" | \"baseline\";\nexport type BlockJustify = \"start\" | \"end\" | \"center\" | \"between\";\nexport type BlockBackground =\n | \"surface\"\n | \"muted\"\n | \"selected\"\n | \"canvas\"\n | \"raised\"\n | \"overlay\"\n | \"transparent\";\nexport type BlockRadius = true | \"sm\" | \"md\" | \"lg\" | \"xl\" | \"round\";\nexport type BlockShadow = true | \"sm\" | \"md\" | \"lg\";\nexport type BlockZIndex =\n | \"header\"\n | \"sticky\"\n | \"fixed\"\n | \"dropdown\"\n | \"modal\"\n | \"popover\"\n | \"toast\"\n | \"tooltip\";\n\nexport type BlockLayoutProps = {\n padding?: ResponsiveValue<BlockSpace>;\n paddingX?: ResponsiveValue<BlockSpace>;\n paddingY?: ResponsiveValue<BlockSpace>;\n margin?: ResponsiveValue<BlockMargin>;\n marginX?: ResponsiveValue<BlockMargin>;\n marginY?: ResponsiveValue<BlockMargin>;\n width?: ResponsiveValue<BlockSize>;\n minWidth?: ResponsiveValue<BlockSize>;\n maxWidth?: ResponsiveValue<BlockSize>;\n height?: ResponsiveValue<BlockSize>;\n minHeight?: ResponsiveValue<BlockSize>;\n maxHeight?: ResponsiveValue<BlockSize>;\n rowFrom?: BlockRowFrom;\n direction?: ResponsiveValue<BlockDirection>;\n align?: ResponsiveValue<BlockAlign>;\n justify?: ResponsiveValue<BlockJustify>;\n gap?: ResponsiveValue<BlockSpace>;\n grow?: ResponsiveValue<boolean | number>;\n shrink?: ResponsiveValue<boolean | number>;\n center?: ResponsiveValue<boolean>;\n sticky?: boolean;\n top?: ResponsiveValue<BlockSpace>;\n zIndex?: ResponsiveValue<BlockZIndex>;\n background?: ResponsiveValue<BlockBackground>;\n border?: ResponsiveValue<boolean>;\n borderTop?: ResponsiveValue<boolean>;\n borderBottom?: ResponsiveValue<boolean>;\n borderRight?: ResponsiveValue<boolean>;\n radius?: ResponsiveValue<BlockRadius>;\n shadow?: ResponsiveValue<BlockShadow>;\n hide?: ResponsiveValue<boolean>;\n};\n\nconst BREAKPOINTS: readonly LayoutBreakpoint[] = [\"base\", \"sm\", \"md\", \"lg\", \"xl\"];\n\nconst SPACE_TOKEN_MAP: Record<string, string> = {\n \"0\": \"0\",\n xs: \"var(--ak-space-xs)\",\n sm: \"var(--ak-space-sm)\",\n md: \"var(--ak-space-md)\",\n lg: \"var(--ak-space-lg)\",\n xl: \"var(--ak-space-xl)\",\n \"2xl\": \"var(--ak-space-2xl)\",\n \"3xl\": \"var(--ak-space-3xl)\",\n page: \"var(--ak-layout-page-gutter)\",\n};\n\nconst SIZE_TOKEN_MAP: Record<string, string> = {\n auto: \"auto\",\n full: \"100%\",\n screen: \"100dvh\",\n content: \"min(60vh, 36rem)\",\n sidebar: \"var(--ak-layout-sidebar-width)\",\n page: \"var(--ak-layout-content-max-width)\",\n sm: \"var(--ak-container-1)\",\n md: \"var(--ak-container-2)\",\n lg: \"var(--ak-container-3)\",\n xl: \"var(--ak-container-4)\",\n};\n\nconst BACKGROUND_TOKEN_MAP: Record<string, string> = {\n canvas: \"var(--ak-color-bg)\",\n surface: \"var(--ak-color-surface)\",\n muted: \"var(--ak-color-surface-muted)\",\n raised: \"var(--ak-color-surface-raised)\",\n overlay: \"var(--ak-color-surface-overlay)\",\n selected: \"var(--ak-color-selected)\",\n transparent: \"transparent\",\n};\n\nconst RADIUS_TOKEN_MAP: Record<string, string> = {\n sm: \"var(--ak-radius-sm)\",\n md: \"var(--ak-radius-md)\",\n lg: \"var(--ak-radius-lg)\",\n xl: \"var(--ak-radius-xl)\",\n round: \"var(--ak-radius-round)\",\n};\n\nconst SHADOW_TOKEN_MAP: Record<string, string> = {\n sm: \"var(--ak-shadow-sm)\",\n md: \"var(--ak-shadow-md)\",\n lg: \"var(--ak-shadow-lg)\",\n};\n\nconst Z_INDEX_TOKEN_MAP: Record<string, string> = {\n header: \"var(--ak-z-sticky)\",\n sticky: \"var(--ak-z-sticky)\",\n fixed: \"var(--ak-z-fixed)\",\n dropdown: \"var(--ak-z-dropdown)\",\n modal: \"var(--ak-z-modal)\",\n popover: \"var(--ak-z-popover)\",\n toast: \"var(--ak-z-toast)\",\n tooltip: \"var(--ak-z-tooltip)\",\n};\n\nconst BORDER_VALUE = \"1px solid var(--ak-color-border-subtle)\";\n\nconst BLOCK_LAYOUT_KEYS = new Set<keyof BlockLayoutProps>([\n \"padding\",\n \"paddingX\",\n \"paddingY\",\n \"margin\",\n \"marginX\",\n \"marginY\",\n \"width\",\n \"minWidth\",\n \"maxWidth\",\n \"height\",\n \"minHeight\",\n \"maxHeight\",\n \"rowFrom\",\n \"direction\",\n \"align\",\n \"justify\",\n \"gap\",\n \"grow\",\n \"shrink\",\n \"center\",\n \"sticky\",\n \"top\",\n \"zIndex\",\n \"background\",\n \"border\",\n \"borderTop\",\n \"borderBottom\",\n \"borderRight\",\n \"radius\",\n \"shadow\",\n \"hide\",\n]);\n\nfunction isResponsiveValue<T>(\n value: ResponsiveValue<T> | undefined,\n): value is Partial<Record<LayoutBreakpoint, T>> {\n return Boolean(\n value &&\n typeof value === \"object\" &&\n !Array.isArray(value) &&\n BREAKPOINTS.some((breakpoint) => breakpoint in value),\n );\n}\n\nfunction normalizeResponsiveValue<T>(\n value: ResponsiveValue<T> | undefined,\n): Partial<Record<LayoutBreakpoint, T>> | undefined {\n if (value === undefined) return undefined;\n if (isResponsiveValue(value)) return value;\n return { base: value };\n}\n\nfunction resolveTokenValue(\n value: string | number,\n tokens: Record<string, string>,\n): string | number {\n if (typeof value === \"number\") return value;\n const trimmed = value.trim();\n return tokens[trimmed] ?? trimmed;\n}\n\nfunction resolveSpaceValue(value: string | number): string | number {\n return resolveTokenValue(value, SPACE_TOKEN_MAP);\n}\n\nfunction resolveSizeValue(value: string, axis: \"inline\" | \"block\"): string {\n if (value === \"screen\") return axis === \"inline\" ? \"100dvw\" : \"100dvh\";\n return String(resolveTokenValue(value, SIZE_TOKEN_MAP));\n}\n\nfunction resolveAlignValue(value: BlockAlign): string {\n if (value === \"start\") return \"flex-start\";\n if (value === \"end\") return \"flex-end\";\n return value;\n}\n\nfunction resolveJustifyValue(value: BlockJustify): string {\n if (value === \"start\") return \"flex-start\";\n if (value === \"end\") return \"flex-end\";\n if (value === \"between\") return \"space-between\";\n return value;\n}\n\nfunction resolveFlexFlag(value: boolean | number): string | number {\n if (value === true) return 1;\n if (value === false) return 0;\n return value;\n}\n\nfunction resolveBorderValue(value: boolean): string | undefined {\n if (value === false) return \"0\";\n return BORDER_VALUE;\n}\n\nfunction resolveRadiusValue(value: BlockRadius): string | undefined {\n if (value === true) return RADIUS_TOKEN_MAP.md;\n return String(resolveTokenValue(value, RADIUS_TOKEN_MAP));\n}\n\nfunction resolveShadowValue(value: BlockShadow): string | undefined {\n if (value === true) return SHADOW_TOKEN_MAP.sm;\n return String(resolveTokenValue(value, SHADOW_TOKEN_MAP));\n}\n\nfunction setResponsiveVar<T>(\n styles: Record<string, string | number>,\n property: string,\n value: ResponsiveValue<T> | undefined,\n resolve: (value: T) => string | number | undefined,\n) {\n const normalized = normalizeResponsiveValue(value);\n if (!normalized) return;\n\n for (const breakpoint of BREAKPOINTS) {\n const breakpointValue = normalized[breakpoint];\n if (breakpointValue === undefined) continue;\n const resolved = resolve(breakpointValue);\n if (resolved === undefined || resolved === \"\") continue;\n styles[`--ak-${property}-${breakpoint}`] = resolved;\n }\n}\n\nexport function splitBlockLayoutProps<T extends Record<string, unknown>>(\n props: T,\n): {\n blockProps: Partial<BlockLayoutProps>;\n rest: Omit<T, keyof BlockLayoutProps>;\n} {\n const blockProps: Partial<BlockLayoutProps> = {};\n const rest: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(props)) {\n if (BLOCK_LAYOUT_KEYS.has(key as keyof BlockLayoutProps)) {\n (blockProps as Record<string, unknown>)[key] = value;\n } else {\n rest[key] = value;\n }\n }\n\n return { blockProps, rest: rest as Omit<T, keyof BlockLayoutProps> };\n}\n\nexport function applyBlockLayoutStyles(\n styles: Record<string, string | number>,\n props: Partial<BlockLayoutProps>,\n) {\n setResponsiveVar(styles, \"p\", props.padding, resolveSpaceValue);\n setResponsiveVar(styles, \"px\", props.paddingX, resolveSpaceValue);\n setResponsiveVar(styles, \"py\", props.paddingY, resolveSpaceValue);\n setResponsiveVar(styles, \"m\", props.margin, resolveSpaceValue);\n setResponsiveVar(styles, \"mx\", props.marginX, resolveSpaceValue);\n setResponsiveVar(styles, \"my\", props.marginY, resolveSpaceValue);\n\n setResponsiveVar(styles, \"width\", props.width, (value) => resolveSizeValue(value, \"inline\"));\n setResponsiveVar(styles, \"min-width\", props.minWidth, (value) =>\n resolveSizeValue(value, \"inline\"),\n );\n setResponsiveVar(styles, \"max-width\", props.maxWidth, (value) =>\n resolveSizeValue(value, \"inline\"),\n );\n setResponsiveVar(styles, \"height\", props.height, (value) => resolveSizeValue(value, \"block\"));\n setResponsiveVar(styles, \"min-height\", props.minHeight, (value) =>\n resolveSizeValue(value, \"block\"),\n );\n setResponsiveVar(styles, \"max-height\", props.maxHeight, (value) =>\n resolveSizeValue(value, \"block\"),\n );\n\n if (props.rowFrom !== undefined) {\n styles[`--ak-flex-direction-${props.rowFrom}`] = \"row\";\n }\n if (props.center !== undefined) {\n setResponsiveVar(styles, \"align-items\", props.center, (value) =>\n value ? \"center\" : \"stretch\",\n );\n setResponsiveVar(styles, \"justify-content\", props.center, (value) =>\n value ? \"center\" : \"flex-start\",\n );\n }\n setResponsiveVar(styles, \"flex-direction\", props.direction, (value) => value);\n setResponsiveVar(styles, \"align-items\", props.align, resolveAlignValue);\n setResponsiveVar(styles, \"justify-content\", props.justify, resolveJustifyValue);\n setResponsiveVar(styles, \"gap\", props.gap, resolveSpaceValue);\n setResponsiveVar(styles, \"flex-grow\", props.grow, resolveFlexFlag);\n setResponsiveVar(styles, \"flex-shrink\", props.shrink, resolveFlexFlag);\n\n if (props.sticky) {\n styles[\"--ak-position-base\"] = \"sticky\";\n }\n setResponsiveVar(styles, \"top\", props.top, resolveSpaceValue);\n setResponsiveVar(styles, \"z-index\", props.zIndex, (value) =>\n String(resolveTokenValue(value, Z_INDEX_TOKEN_MAP)),\n );\n\n setResponsiveVar(styles, \"background\", props.background, (value) =>\n String(resolveTokenValue(value, BACKGROUND_TOKEN_MAP)),\n );\n setResponsiveVar(styles, \"border\", props.border, resolveBorderValue);\n setResponsiveVar(styles, \"border-top\", props.borderTop, resolveBorderValue);\n setResponsiveVar(styles, \"border-bottom\", props.borderBottom, resolveBorderValue);\n setResponsiveVar(styles, \"border-right\", props.borderRight, resolveBorderValue);\n setResponsiveVar(styles, \"border-radius\", props.radius, resolveRadiusValue);\n setResponsiveVar(styles, \"box-shadow\", props.shadow, resolveShadowValue);\n setResponsiveVar(styles, \"display\", props.hide, (value) => (value ? \"none\" : \"flex\"));\n}\n\nexport function mergeLayoutStyles(\n layout: Record<string, string | number>,\n user: unknown,\n): string | undefined {\n const merged: Record<string, unknown> = { ...layout };\n\n if (user !== null && user !== undefined && typeof user === \"object\") {\n Object.assign(merged, user as Record<string, unknown>);\n }\n\n const mergedString = serializeCssDeclarations(merged);\n\n if (typeof user === \"string\" && user.trim()) {\n return mergedString ? `${mergedString};${user.trim()}` : user.trim();\n }\n\n return mergedString || undefined;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"block-layout.js","names":[],"sources":["../../../src/components/_internal/block-layout.ts"],"sourcesContent":["import { serializeCssDeclarations } from \"./style\";\n\nexport type LayoutBreakpoint = \"base\" | \"sm\" | \"md\" | \"lg\" | \"xl\";\nexport type ResponsiveValue<T> = T | Partial<Record<LayoutBreakpoint, T>>;\n\nexport type BlockSpace = \"0\" | \"xs\" | \"sm\" | \"md\" | \"lg\" | \"xl\" | \"2xl\" | \"3xl\" | \"page\";\nexport type BlockMargin = BlockSpace | \"auto\";\nexport type BlockSize =\n | \"auto\"\n | \"full\"\n | \"screen\"\n | \"content\"\n | \"sidebar\"\n | \"page\"\n | \"sm\"\n | \"md\"\n | \"lg\"\n | \"xl\";\nexport type BlockDirection = \"row\" | \"column\" | \"row-reverse\" | \"column-reverse\";\nexport type BlockRowFrom = Exclude<LayoutBreakpoint, \"base\">;\nexport type BlockAlign = \"start\" | \"end\" | \"center\" | \"stretch\" | \"baseline\";\nexport type BlockJustify = \"start\" | \"end\" | \"center\" | \"between\";\nexport type BlockBackground =\n | \"surface\"\n | \"muted\"\n | \"selected\"\n | \"canvas\"\n | \"raised\"\n | \"overlay\"\n | \"transparent\";\nexport type BlockRadius = true | \"sm\" | \"md\" | \"lg\" | \"xl\" | \"round\";\nexport type BlockShadow = true | \"sm\" | \"md\" | \"lg\";\nexport type BlockZIndex =\n | \"header\"\n | \"sticky\"\n | \"fixed\"\n | \"dropdown\"\n | \"modal\"\n | \"popover\"\n | \"toast\"\n | \"tooltip\";\n\nexport type BlockLayoutProps = {\n padding?: ResponsiveValue<BlockSpace>;\n paddingX?: ResponsiveValue<BlockSpace>;\n paddingY?: ResponsiveValue<BlockSpace>;\n margin?: ResponsiveValue<BlockMargin>;\n marginX?: ResponsiveValue<BlockMargin>;\n marginY?: ResponsiveValue<BlockMargin>;\n width?: ResponsiveValue<BlockSize>;\n minWidth?: ResponsiveValue<BlockSize>;\n maxWidth?: ResponsiveValue<BlockSize>;\n height?: ResponsiveValue<BlockSize>;\n minHeight?: ResponsiveValue<BlockSize>;\n maxHeight?: ResponsiveValue<BlockSize>;\n rowFrom?: BlockRowFrom;\n direction?: ResponsiveValue<BlockDirection>;\n align?: ResponsiveValue<BlockAlign>;\n justify?: ResponsiveValue<BlockJustify>;\n gap?: ResponsiveValue<BlockSpace>;\n grow?: ResponsiveValue<boolean | number>;\n shrink?: ResponsiveValue<boolean | number>;\n center?: ResponsiveValue<boolean>;\n sticky?: boolean;\n top?: ResponsiveValue<BlockSpace>;\n zIndex?: ResponsiveValue<BlockZIndex>;\n background?: ResponsiveValue<BlockBackground>;\n border?: ResponsiveValue<boolean>;\n borderTop?: ResponsiveValue<boolean>;\n borderBottom?: ResponsiveValue<boolean>;\n borderRight?: ResponsiveValue<boolean>;\n radius?: ResponsiveValue<BlockRadius>;\n shadow?: ResponsiveValue<BlockShadow>;\n hide?: ResponsiveValue<boolean>;\n};\n\nconst BREAKPOINTS: readonly LayoutBreakpoint[] = [\"base\", \"sm\", \"md\", \"lg\", \"xl\"];\n\nconst SPACE_TOKEN_MAP: Record<string, string> = {\n \"0\": \"0\",\n xs: \"var(--ak-space-xs)\",\n sm: \"var(--ak-space-sm)\",\n md: \"var(--ak-space-md)\",\n lg: \"var(--ak-space-lg)\",\n xl: \"var(--ak-space-xl)\",\n \"2xl\": \"var(--ak-space-2xl)\",\n \"3xl\": \"var(--ak-space-3xl)\",\n page: \"var(--ak-layout-page-gutter)\",\n};\n\nconst SIZE_TOKEN_MAP: Record<string, string> = {\n auto: \"auto\",\n full: \"100%\",\n screen: \"100dvh\",\n content: \"min(60vh, 36rem)\",\n sidebar: \"var(--ak-layout-sidebar-width)\",\n page: \"var(--ak-layout-content-max-width)\",\n sm: \"var(--ak-container-1)\",\n md: \"var(--ak-container-2)\",\n lg: \"var(--ak-container-3)\",\n xl: \"var(--ak-container-4)\",\n};\n\nconst BACKGROUND_TOKEN_MAP: Record<string, string> = {\n canvas: \"var(--ak-color-bg)\",\n surface: \"var(--ak-color-surface)\",\n muted: \"var(--ak-color-surface-muted)\",\n raised: \"var(--ak-color-surface-raised)\",\n overlay: \"var(--ak-color-surface-overlay)\",\n selected: \"var(--ak-color-selected)\",\n transparent: \"transparent\",\n};\n\nconst RADIUS_TOKEN_MAP: Record<string, string> = {\n sm: \"var(--ak-radius-sm)\",\n md: \"var(--ak-radius-md)\",\n lg: \"var(--ak-radius-lg)\",\n xl: \"var(--ak-radius-xl)\",\n round: \"var(--ak-radius-round)\",\n};\n\nconst SHADOW_TOKEN_MAP: Record<string, string> = {\n sm: \"var(--ak-shadow-sm)\",\n md: \"var(--ak-shadow-md)\",\n lg: \"var(--ak-shadow-lg)\",\n};\n\nconst Z_INDEX_TOKEN_MAP: Record<string, string> = {\n header: \"var(--ak-z-sticky)\",\n sticky: \"var(--ak-z-sticky)\",\n fixed: \"var(--ak-z-fixed)\",\n dropdown: \"var(--ak-z-dropdown)\",\n modal: \"var(--ak-z-modal)\",\n popover: \"var(--ak-z-popover)\",\n toast: \"var(--ak-z-toast)\",\n tooltip: \"var(--ak-z-tooltip)\",\n};\n\nconst BORDER_VALUE = \"1px solid var(--ak-color-border-subtle)\";\n\nconst BLOCK_LAYOUT_KEYS = new Set<keyof BlockLayoutProps>([\n \"padding\",\n \"paddingX\",\n \"paddingY\",\n \"margin\",\n \"marginX\",\n \"marginY\",\n \"width\",\n \"minWidth\",\n \"maxWidth\",\n \"height\",\n \"minHeight\",\n \"maxHeight\",\n \"rowFrom\",\n \"direction\",\n \"align\",\n \"justify\",\n \"gap\",\n \"grow\",\n \"shrink\",\n \"center\",\n \"sticky\",\n \"top\",\n \"zIndex\",\n \"background\",\n \"border\",\n \"borderTop\",\n \"borderBottom\",\n \"borderRight\",\n \"radius\",\n \"shadow\",\n \"hide\",\n]);\n\nfunction isResponsiveValue<T>(\n value: ResponsiveValue<T> | undefined,\n): value is Partial<Record<LayoutBreakpoint, T>> {\n return Boolean(\n value &&\n typeof value === \"object\" &&\n !Array.isArray(value) &&\n BREAKPOINTS.some((breakpoint) => breakpoint in value),\n );\n}\n\nfunction normalizeResponsiveValue<T>(\n value: ResponsiveValue<T> | undefined,\n): Partial<Record<LayoutBreakpoint, T>> | undefined {\n if (value === undefined) return undefined;\n if (isResponsiveValue(value)) return value;\n return { base: value };\n}\n\nfunction resolveTokenValue(\n value: string | number,\n tokens: Record<string, string>,\n): string | number {\n if (typeof value === \"number\") return value;\n const trimmed = value.trim();\n return tokens[trimmed] ?? trimmed;\n}\n\nexport function resolveSpaceValue(value: string | number): string | number {\n return resolveTokenValue(value, SPACE_TOKEN_MAP);\n}\n\nfunction resolveSizeValue(value: string, axis: \"inline\" | \"block\"): string {\n if (value === \"screen\") return axis === \"inline\" ? \"100dvw\" : \"100dvh\";\n return String(resolveTokenValue(value, SIZE_TOKEN_MAP));\n}\n\nfunction resolveAlignValue(value: BlockAlign): string {\n if (value === \"start\") return \"flex-start\";\n if (value === \"end\") return \"flex-end\";\n return value;\n}\n\nfunction resolveJustifyValue(value: BlockJustify): string {\n if (value === \"start\") return \"flex-start\";\n if (value === \"end\") return \"flex-end\";\n if (value === \"between\") return \"space-between\";\n return value;\n}\n\nfunction resolveFlexFlag(value: boolean | number): string | number {\n if (value === true) return 1;\n if (value === false) return 0;\n return value;\n}\n\nfunction resolveBorderValue(value: boolean): string | undefined {\n if (value === false) return \"0\";\n return BORDER_VALUE;\n}\n\nfunction resolveRadiusValue(value: BlockRadius): string | undefined {\n if (value === true) return RADIUS_TOKEN_MAP.md;\n return String(resolveTokenValue(value, RADIUS_TOKEN_MAP));\n}\n\nfunction resolveShadowValue(value: BlockShadow): string | undefined {\n if (value === true) return SHADOW_TOKEN_MAP.sm;\n return String(resolveTokenValue(value, SHADOW_TOKEN_MAP));\n}\n\nexport function setResponsiveVar<T>(\n styles: Record<string, string | number>,\n property: string,\n value: ResponsiveValue<T> | undefined,\n resolve: (value: T) => string | number | undefined,\n) {\n const normalized = normalizeResponsiveValue(value);\n if (!normalized) return;\n\n for (const breakpoint of BREAKPOINTS) {\n const breakpointValue = normalized[breakpoint];\n if (breakpointValue === undefined) continue;\n const resolved = resolve(breakpointValue);\n if (resolved === undefined || resolved === \"\") continue;\n styles[`--ak-${property}-${breakpoint}`] = resolved;\n }\n}\n\nexport function splitBlockLayoutProps<T extends Record<string, unknown>>(\n props: T,\n): {\n blockProps: Partial<BlockLayoutProps>;\n rest: Omit<T, keyof BlockLayoutProps>;\n} {\n const blockProps: Partial<BlockLayoutProps> = {};\n const rest: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(props)) {\n if (BLOCK_LAYOUT_KEYS.has(key as keyof BlockLayoutProps)) {\n (blockProps as Record<string, unknown>)[key] = value;\n } else {\n rest[key] = value;\n }\n }\n\n return { blockProps, rest: rest as Omit<T, keyof BlockLayoutProps> };\n}\n\nexport function applyBlockLayoutStyles(\n styles: Record<string, string | number>,\n props: Partial<BlockLayoutProps>,\n) {\n setResponsiveVar(styles, \"p\", props.padding, resolveSpaceValue);\n setResponsiveVar(styles, \"px\", props.paddingX, resolveSpaceValue);\n setResponsiveVar(styles, \"py\", props.paddingY, resolveSpaceValue);\n setResponsiveVar(styles, \"m\", props.margin, resolveSpaceValue);\n setResponsiveVar(styles, \"mx\", props.marginX, resolveSpaceValue);\n setResponsiveVar(styles, \"my\", props.marginY, resolveSpaceValue);\n\n setResponsiveVar(styles, \"width\", props.width, (value) => resolveSizeValue(value, \"inline\"));\n setResponsiveVar(styles, \"min-width\", props.minWidth, (value) =>\n resolveSizeValue(value, \"inline\"),\n );\n setResponsiveVar(styles, \"max-width\", props.maxWidth, (value) =>\n resolveSizeValue(value, \"inline\"),\n );\n setResponsiveVar(styles, \"height\", props.height, (value) => resolveSizeValue(value, \"block\"));\n setResponsiveVar(styles, \"min-height\", props.minHeight, (value) =>\n resolveSizeValue(value, \"block\"),\n );\n setResponsiveVar(styles, \"max-height\", props.maxHeight, (value) =>\n resolveSizeValue(value, \"block\"),\n );\n\n if (props.rowFrom !== undefined) {\n styles[`--ak-flex-direction-${props.rowFrom}`] = \"row\";\n }\n if (props.center !== undefined) {\n setResponsiveVar(styles, \"align-items\", props.center, (value) =>\n value ? \"center\" : \"stretch\",\n );\n setResponsiveVar(styles, \"justify-content\", props.center, (value) =>\n value ? \"center\" : \"flex-start\",\n );\n }\n setResponsiveVar(styles, \"flex-direction\", props.direction, (value) => value);\n setResponsiveVar(styles, \"align-items\", props.align, resolveAlignValue);\n setResponsiveVar(styles, \"justify-content\", props.justify, resolveJustifyValue);\n setResponsiveVar(styles, \"gap\", props.gap, resolveSpaceValue);\n setResponsiveVar(styles, \"flex-grow\", props.grow, resolveFlexFlag);\n setResponsiveVar(styles, \"flex-shrink\", props.shrink, resolveFlexFlag);\n\n if (props.sticky) {\n styles[\"--ak-position-base\"] = \"sticky\";\n }\n setResponsiveVar(styles, \"top\", props.top, resolveSpaceValue);\n setResponsiveVar(styles, \"z-index\", props.zIndex, (value) =>\n String(resolveTokenValue(value, Z_INDEX_TOKEN_MAP)),\n );\n\n setResponsiveVar(styles, \"background\", props.background, (value) =>\n String(resolveTokenValue(value, BACKGROUND_TOKEN_MAP)),\n );\n setResponsiveVar(styles, \"border\", props.border, resolveBorderValue);\n setResponsiveVar(styles, \"border-top\", props.borderTop, resolveBorderValue);\n setResponsiveVar(styles, \"border-bottom\", props.borderBottom, resolveBorderValue);\n setResponsiveVar(styles, \"border-right\", props.borderRight, resolveBorderValue);\n setResponsiveVar(styles, \"border-radius\", props.radius, resolveRadiusValue);\n setResponsiveVar(styles, \"box-shadow\", props.shadow, resolveShadowValue);\n setResponsiveVar(styles, \"display\", props.hide, (value) => (value ? \"none\" : \"flex\"));\n}\n\nexport function mergeLayoutStyles(\n layout: Record<string, string | number>,\n user: unknown,\n): string | undefined {\n const merged: Record<string, unknown> = { ...layout };\n\n if (user !== null && user !== undefined && typeof user === \"object\") {\n Object.assign(merged, user as Record<string, unknown>);\n }\n\n const mergedString = serializeCssDeclarations(merged);\n\n if (typeof user === \"string\" && user.trim()) {\n return mergedString ? `${mergedString};${user.trim()}` : user.trim();\n }\n\n return mergedString || undefined;\n}\n"],"mappings":"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"}
|
|
@@ -125,8 +125,8 @@ function serializeCssDeclarations(styles) {
|
|
|
125
125
|
const STYLE_REGISTRY_ATTR = "data-askr-style-registry";
|
|
126
126
|
const STYLE_CLASS_PREFIX = "ak-style-";
|
|
127
127
|
const MAX_STYLE_RULES = 512;
|
|
128
|
-
const
|
|
129
|
-
const
|
|
128
|
+
const MAX_COLLISION_CACHE = 4096;
|
|
129
|
+
const styleCollisionCache = /* @__PURE__ */ new Map();
|
|
130
130
|
const registries = /* @__PURE__ */ new WeakMap();
|
|
131
131
|
function countRegisteredRules(value) {
|
|
132
132
|
return value?.match(/\.ak-style-[a-z0-9]+\{/g)?.length ?? 0;
|
|
@@ -148,7 +148,7 @@ function escapeStyleRawText(value) {
|
|
|
148
148
|
}
|
|
149
149
|
function styleRuleFor(declarations) {
|
|
150
150
|
const className = styleClassName(declarations);
|
|
151
|
-
const existing =
|
|
151
|
+
const existing = styleCollisionCache.get(className);
|
|
152
152
|
if (existing) {
|
|
153
153
|
if (existing.declarations !== declarations) throw new RangeError("Theme style class collision detected.");
|
|
154
154
|
return existing;
|
|
@@ -160,12 +160,12 @@ function styleRuleFor(declarations) {
|
|
|
160
160
|
};
|
|
161
161
|
}
|
|
162
162
|
function rememberStyleRule(entry) {
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
while (
|
|
166
|
-
const oldest =
|
|
163
|
+
styleCollisionCache.delete(entry.className);
|
|
164
|
+
styleCollisionCache.set(entry.className, entry);
|
|
165
|
+
while (styleCollisionCache.size > MAX_COLLISION_CACHE) {
|
|
166
|
+
const oldest = styleCollisionCache.keys().next().value;
|
|
167
167
|
if (oldest === void 0) break;
|
|
168
|
-
|
|
168
|
+
styleCollisionCache.delete(oldest);
|
|
169
169
|
}
|
|
170
170
|
}
|
|
171
171
|
function registerSSRStyle(entry) {
|
|
@@ -225,17 +225,5 @@ function styleDeclarationsToClass(declarations) {
|
|
|
225
225
|
registerSSRStyle(entry);
|
|
226
226
|
return entry.className;
|
|
227
227
|
}
|
|
228
|
-
function styleRulesForHtml(html) {
|
|
229
|
-
const rules = /* @__PURE__ */ new Map();
|
|
230
|
-
for (const attribute of html.matchAll(/\sclass=(?:"([^"]*)"|'([^']*)')/g)) {
|
|
231
|
-
const value = attribute[1] ?? attribute[2] ?? "";
|
|
232
|
-
for (const className of value.split(/\s+/)) {
|
|
233
|
-
const entry = styleRulesByClass.get(className);
|
|
234
|
-
if (entry) rules.set(className, entry.rule);
|
|
235
|
-
if (rules.size > MAX_STYLE_RULES) throw new RangeError("Theme style registry capacity exceeded.");
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
return Array.from(rules.values());
|
|
239
|
-
}
|
|
240
228
|
//#endregion
|
|
241
|
-
export { serializeCssDeclarations, styleDeclarationsToClass
|
|
229
|
+
export { serializeCssDeclarations, styleDeclarationsToClass };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"style.js","names":[],"sources":["../../../src/components/_internal/style.ts"],"sourcesContent":["import * as Askr from \"@askrjs/askr\";\n\nconst cssPropertyNameCache = new Map<string, string>();\nconst MAX_PROPERTY_CACHE = 256;\n\nconst CSS_UNSAFE_RE = /[{}<>\\\\]/;\nconst CSS_URI_SCHEME_RE = /(?:^|[\\s(,])([a-z][a-z0-9+.-]*):/i;\nconst CSS_FUNCTION_NAME_RE = /([a-z-][a-z0-9-]*)\\s*\\(/gi;\nconst CSS_ALLOWED_FUNCTIONS = new Set([\n \"var\",\n \"calc\",\n \"min\",\n \"max\",\n \"clamp\",\n \"minmax\",\n \"repeat\",\n \"rgb\",\n \"rgba\",\n \"hsl\",\n \"hsla\",\n \"lab\",\n \"lch\",\n \"oklab\",\n \"oklch\",\n \"color\",\n \"color-mix\",\n \"translate\",\n \"translatex\",\n \"translatey\",\n \"translatez\",\n \"scale\",\n \"scalex\",\n \"scaley\",\n \"scalez\",\n \"rotate\",\n \"rotatex\",\n \"rotatey\",\n \"rotatez\",\n \"skew\",\n \"skewx\",\n \"skewy\",\n \"matrix\",\n \"matrix3d\",\n \"linear-gradient\",\n \"radial-gradient\",\n \"conic-gradient\",\n \"repeating-linear-gradient\",\n \"repeating-radial-gradient\",\n \"repeating-conic-gradient\",\n \"cubic-bezier\",\n \"steps\",\n]);\n\nfunction isSafeCssPropertyName(name: string): boolean {\n if (name.startsWith(\"--\")) return /^--[a-zA-Z0-9_-]+$/.test(name);\n return /^[a-zA-Z_][a-zA-Z0-9_-]*$/.test(name);\n}\n\nfunction isSafeCssValue(value: string): boolean {\n if (CSS_UNSAFE_RE.test(value) || CSS_URI_SCHEME_RE.test(value)) return false;\n\n for (const match of value.matchAll(CSS_FUNCTION_NAME_RE)) {\n if (!CSS_ALLOWED_FUNCTIONS.has(match[1]!.toLowerCase())) return false;\n }\n\n return true;\n}\n\nfunction splitCssDeclarations(value: string): string[] {\n const declarations: string[] = [];\n let start = 0;\n let depth = 0;\n let quote: string | undefined;\n\n for (let index = 0; index < value.length; index += 1) {\n const char = value[index]!;\n if (quote) {\n if (char === quote && value[index - 1] !== \"\\\\\") quote = undefined;\n continue;\n }\n if (char === '\"' || char === \"'\") {\n quote = char;\n continue;\n }\n if (char === \"(\") depth += 1;\n if (char === \")\" && depth > 0) depth -= 1;\n if (char === \";\" && depth === 0) {\n declarations.push(value.slice(start, index));\n start = index + 1;\n }\n }\n\n declarations.push(value.slice(start));\n return declarations;\n}\n\nfunction serializeCssString(value: string): string {\n const entries: Array<[string, string]> = [];\n for (const candidate of splitCssDeclarations(value)) {\n const separator = candidate.indexOf(\":\");\n if (separator <= 0) continue;\n const key = candidate.slice(0, separator).trim();\n const cssValue = candidate.slice(separator + 1).trim();\n if (key && cssValue) entries.push([key, cssValue]);\n }\n return serializeCssDeclarations(Object.fromEntries(entries));\n}\n\nfunction cssPropertyName(name: string): string {\n let cached = cssPropertyNameCache.get(name);\n if (cached !== undefined) {\n return cached;\n }\n\n let result = \"\";\n\n for (let index = 0; index < name.length; index += 1) {\n const code = name.charCodeAt(index);\n\n if (code >= 65 && code <= 90) {\n result += `-${String.fromCharCode(code + 32)}`;\n } else {\n result += name[index];\n }\n }\n\n if (cssPropertyNameCache.size < MAX_PROPERTY_CACHE) {\n cssPropertyNameCache.set(name, result);\n }\n return result;\n}\n\nexport function serializeCssDeclarations(styles: Record<string, unknown>): string {\n const keys = Object.keys(styles);\n let result = \"\";\n\n for (const key of keys) {\n const value = styles[key];\n if (value === undefined || value === null) {\n continue;\n }\n\n const rawProperty = key.trim();\n if (!isSafeCssPropertyName(rawProperty)) {\n continue;\n }\n const property = cssPropertyName(rawProperty).trim();\n const cssValue = String(value).trim();\n if (!cssValue || !isSafeCssValue(cssValue)) {\n continue;\n }\n\n const declaration = `${property}:${cssValue}`;\n result = result ? `${result};${declaration}` : declaration;\n }\n\n return result;\n}\n\nexport function mergeCssVar(style: unknown, name: string, value: string): string {\n const decl = `${name}:${value}`;\n\n if (typeof style === \"string\") {\n const trimmed = style.trim();\n return trimmed ? `${trimmed};${decl}` : decl;\n }\n\n if (style && typeof style === \"object\") {\n const entries = serializeCssDeclarations(style as Record<string, unknown>);\n return entries ? `${entries};${decl}` : decl;\n }\n\n return decl;\n}\n\nconst STYLE_REGISTRY_ATTR = \"data-askr-style-registry\";\nconst STYLE_CLASS_PREFIX = \"ak-style-\";\nconst MAX_STYLE_RULES = 512;\nconst MAX_SSR_STYLE_CACHE = 4096;\n\ntype StyleRule = {\n className: string;\n declarations: string;\n rule: string;\n};\n\nconst styleRulesByClass = new Map<string, StyleRule>();\ntype StyleRegistry = {\n element: HTMLStyleElement;\n ruleCount: number;\n rules: Map<string, StyleRule>;\n};\nconst registries = new WeakMap<Document, Map<string, StyleRegistry>>();\nfunction countRegisteredRules(value: string | null): number {\n return value?.match(/\\.ak-style-[a-z0-9]+\\{/g)?.length ?? 0;\n}\n\nfunction styleClassName(declarations: string): string {\n let first = 0xdeadbeef ^ declarations.length;\n let second = 0x41c6ce57 ^ declarations.length;\n\n for (let index = 0; index < declarations.length; index += 1) {\n const code = declarations.charCodeAt(index);\n first = Math.imul(first ^ code, 2_654_435_761);\n second = Math.imul(second ^ code, 1_597_334_677);\n }\n\n first =\n Math.imul(first ^ (first >>> 16), 2_246_822_507) ^\n Math.imul(second ^ (second >>> 13), 3_266_489_909);\n second =\n Math.imul(second ^ (second >>> 16), 2_246_822_507) ^\n Math.imul(first ^ (first >>> 13), 3_266_489_909);\n\n return `${STYLE_CLASS_PREFIX}${(second >>> 0).toString(36)}${(first >>> 0).toString(36)}`;\n}\n\nfunction escapeStyleRawText(value: string): string {\n return value.replace(/<\\//g, \"<\\\\/\");\n}\n\nfunction styleRuleFor(declarations: string): StyleRule {\n const className = styleClassName(declarations);\n const existing = styleRulesByClass.get(className);\n if (existing) {\n if (existing.declarations !== declarations) {\n throw new RangeError(\"Theme style class collision detected.\");\n }\n return existing;\n }\n\n const entry = {\n className,\n declarations,\n rule: `.${className}{${escapeStyleRawText(declarations)}}`,\n };\n return entry;\n}\n\nfunction rememberStyleRule(entry: StyleRule): void {\n styleRulesByClass.delete(entry.className);\n styleRulesByClass.set(entry.className, entry);\n while (styleRulesByClass.size > MAX_SSR_STYLE_CACHE) {\n const oldest = styleRulesByClass.keys().next().value;\n if (oldest === undefined) break;\n styleRulesByClass.delete(oldest);\n }\n}\n\nfunction registerSSRStyle(entry: StyleRule): void {\n const register = (\n Askr as typeof Askr & {\n registerSSRStyle?: (id: string, cssText: string) => void;\n }\n ).registerSSRStyle;\n register?.(entry.className, entry.rule);\n}\n\nfunction ensureStyleRegistry(nonce: string | undefined): StyleRegistry | null {\n if (typeof document === \"undefined\") return null;\n const key = nonce ?? \"\";\n let documentRegistries = registries.get(document);\n if (!documentRegistries) {\n documentRegistries = new Map();\n registries.set(document, documentRegistries);\n }\n const current = documentRegistries.get(key);\n if (current?.element.isConnected) return current;\n\n const existingStyleElements = Array.from(\n document.querySelectorAll<HTMLStyleElement>(`style[${STYLE_REGISTRY_ATTR}]`),\n );\n const styleElement =\n existingStyleElements.find((element) => (element.nonce || undefined) === nonce) ??\n (nonce === undefined && existingStyleElements.length === 1\n ? existingStyleElements[0]\n : undefined) ??\n document.createElement(\"style\");\n if (!styleElement.isConnected) {\n styleElement.setAttribute(STYLE_REGISTRY_ATTR, \"true\");\n if (nonce !== undefined) styleElement.nonce = nonce;\n (document.head ?? document.documentElement).append(styleElement);\n }\n const registry: StyleRegistry = {\n element: styleElement,\n ruleCount: countRegisteredRules(styleElement.textContent),\n rules: new Map(),\n };\n documentRegistries.set(key, registry);\n return registry;\n}\n\nfunction normalizeDeclarations(declarations: string): string {\n return serializeCssString(declarations);\n}\n\nexport function styleDeclarationsToClass(declarations: string | undefined): string | undefined {\n if (typeof declarations !== \"string\") return undefined;\n\n const normalized = normalizeDeclarations(declarations);\n if (!normalized) return undefined;\n\n const entry = styleRuleFor(normalized);\n const nonce = Askr.cspNonce();\n const registry = ensureStyleRegistry(nonce);\n const registered = registry?.rules.get(normalized);\n if (registered) {\n registerSSRStyle(registered);\n return registered.className;\n }\n\n if (registry) {\n if (!registry.rules.has(normalized) && !registry.element.textContent?.includes(entry.rule)) {\n if (registry.ruleCount >= MAX_STYLE_RULES) {\n throw new RangeError(\"Theme style registry capacity exceeded.\");\n }\n }\n if (!registry.element.textContent?.includes(entry.rule)) {\n registry.element.append(entry.rule, \"\\n\");\n registry.ruleCount += 1;\n }\n registry.rules.set(normalized, entry);\n }\n\n rememberStyleRule(entry);\n registerSSRStyle(entry);\n\n return entry.className;\n}\n\nexport function styleRulesForHtml(html: string): string[] {\n const rules = new Map<string, string>();\n const classAttributePattern = /\\sclass=(?:\"([^\"]*)\"|'([^']*)')/g;\n\n for (const attribute of html.matchAll(classAttributePattern)) {\n const value = attribute[1] ?? attribute[2] ?? \"\";\n for (const className of value.split(/\\s+/)) {\n const entry = styleRulesByClass.get(className);\n if (entry) rules.set(className, entry.rule);\n if (rules.size > MAX_STYLE_RULES) {\n throw new RangeError(\"Theme style registry capacity exceeded.\");\n }\n }\n }\n\n return Array.from(rules.values());\n}\n"],"mappings":";;AAEA,MAAM,uCAAuB,IAAI,IAAoB;AACrD,MAAM,qBAAqB;AAE3B,MAAM,gBAAgB;AACtB,MAAM,oBAAoB;AAC1B,MAAM,uBAAuB;AAC7B,MAAM,wCAAwB,IAAI,IAAI;CACpC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;AAED,SAAS,sBAAsB,MAAuB;CACpD,IAAI,KAAK,WAAW,IAAI,GAAG,OAAO,qBAAqB,KAAK,IAAI;CAChE,OAAO,4BAA4B,KAAK,IAAI;AAC9C;AAEA,SAAS,eAAe,OAAwB;CAC9C,IAAI,cAAc,KAAK,KAAK,KAAK,kBAAkB,KAAK,KAAK,GAAG,OAAO;CAEvE,KAAK,MAAM,SAAS,MAAM,SAAS,oBAAoB,GACrD,IAAI,CAAC,sBAAsB,IAAI,MAAM,EAAE,CAAE,YAAY,CAAC,GAAG,OAAO;CAGlE,OAAO;AACT;AAEA,SAAS,qBAAqB,OAAyB;CACrD,MAAM,eAAyB,CAAC;CAChC,IAAI,QAAQ;CACZ,IAAI,QAAQ;CACZ,IAAI;CAEJ,KAAK,IAAI,QAAQ,GAAG,QAAQ,MAAM,QAAQ,SAAS,GAAG;EACpD,MAAM,OAAO,MAAM;EACnB,IAAI,OAAO;GACT,IAAI,SAAS,SAAS,MAAM,QAAQ,OAAO,MAAM,QAAQ,KAAA;GACzD;EACF;EACA,IAAI,SAAS,QAAO,SAAS,KAAK;GAChC,QAAQ;GACR;EACF;EACA,IAAI,SAAS,KAAK,SAAS;EAC3B,IAAI,SAAS,OAAO,QAAQ,GAAG,SAAS;EACxC,IAAI,SAAS,OAAO,UAAU,GAAG;GAC/B,aAAa,KAAK,MAAM,MAAM,OAAO,KAAK,CAAC;GAC3C,QAAQ,QAAQ;EAClB;CACF;CAEA,aAAa,KAAK,MAAM,MAAM,KAAK,CAAC;CACpC,OAAO;AACT;AAEA,SAAS,mBAAmB,OAAuB;CACjD,MAAM,UAAmC,CAAC;CAC1C,KAAK,MAAM,aAAa,qBAAqB,KAAK,GAAG;EACnD,MAAM,YAAY,UAAU,QAAQ,GAAG;EACvC,IAAI,aAAa,GAAG;EACpB,MAAM,MAAM,UAAU,MAAM,GAAG,SAAS,CAAC,CAAC,KAAK;EAC/C,MAAM,WAAW,UAAU,MAAM,YAAY,CAAC,CAAC,CAAC,KAAK;EACrD,IAAI,OAAO,UAAU,QAAQ,KAAK,CAAC,KAAK,QAAQ,CAAC;CACnD;CACA,OAAO,yBAAyB,OAAO,YAAY,OAAO,CAAC;AAC7D;AAEA,SAAS,gBAAgB,MAAsB;CAC7C,IAAI,SAAS,qBAAqB,IAAI,IAAI;CAC1C,IAAI,WAAW,KAAA,GACb,OAAO;CAGT,IAAI,SAAS;CAEb,KAAK,IAAI,QAAQ,GAAG,QAAQ,KAAK,QAAQ,SAAS,GAAG;EACnD,MAAM,OAAO,KAAK,WAAW,KAAK;EAElC,IAAI,QAAQ,MAAM,QAAQ,IACxB,UAAU,IAAI,OAAO,aAAa,OAAO,EAAE;OAE3C,UAAU,KAAK;CAEnB;CAEA,IAAI,qBAAqB,OAAO,oBAC9B,qBAAqB,IAAI,MAAM,MAAM;CAEvC,OAAO;AACT;AAEA,SAAgB,yBAAyB,QAAyC;CAChF,MAAM,OAAO,OAAO,KAAK,MAAM;CAC/B,IAAI,SAAS;CAEb,KAAK,MAAM,OAAO,MAAM;EACtB,MAAM,QAAQ,OAAO;EACrB,IAAI,UAAU,KAAA,KAAa,UAAU,MACnC;EAGF,MAAM,cAAc,IAAI,KAAK;EAC7B,IAAI,CAAC,sBAAsB,WAAW,GACpC;EAEF,MAAM,WAAW,gBAAgB,WAAW,CAAC,CAAC,KAAK;EACnD,MAAM,WAAW,OAAO,KAAK,CAAC,CAAC,KAAK;EACpC,IAAI,CAAC,YAAY,CAAC,eAAe,QAAQ,GACvC;EAGF,MAAM,cAAc,GAAG,SAAS,GAAG;EACnC,SAAS,SAAS,GAAG,OAAO,GAAG,gBAAgB;CACjD;CAEA,OAAO;AACT;AAkBA,MAAM,sBAAsB;AAC5B,MAAM,qBAAqB;AAC3B,MAAM,kBAAkB;AACxB,MAAM,sBAAsB;AAQ5B,MAAM,oCAAoB,IAAI,IAAuB;AAMrD,MAAM,6BAAa,IAAI,QAA8C;AACrE,SAAS,qBAAqB,OAA8B;CAC1D,OAAO,OAAO,MAAM,yBAAyB,CAAC,EAAE,UAAU;AAC5D;AAEA,SAAS,eAAe,cAA8B;CACpD,IAAI,QAAQ,aAAa,aAAa;CACtC,IAAI,SAAS,aAAa,aAAa;CAEvC,KAAK,IAAI,QAAQ,GAAG,QAAQ,aAAa,QAAQ,SAAS,GAAG;EAC3D,MAAM,OAAO,aAAa,WAAW,KAAK;EAC1C,QAAQ,KAAK,KAAK,QAAQ,MAAM,UAAa;EAC7C,SAAS,KAAK,KAAK,SAAS,MAAM,UAAa;CACjD;CAEA,QACE,KAAK,KAAK,QAAS,UAAU,IAAK,UAAa,IAC/C,KAAK,KAAK,SAAU,WAAW,IAAK,UAAa;CACnD,SACE,KAAK,KAAK,SAAU,WAAW,IAAK,UAAa,IACjD,KAAK,KAAK,QAAS,UAAU,IAAK,UAAa;CAEjD,OAAO,GAAG,sBAAsB,WAAW,EAAA,CAAG,SAAS,EAAE,KAAK,UAAU,EAAA,CAAG,SAAS,EAAE;AACxF;AAEA,SAAS,mBAAmB,OAAuB;CACjD,OAAO,MAAM,QAAQ,QAAQ,MAAM;AACrC;AAEA,SAAS,aAAa,cAAiC;CACrD,MAAM,YAAY,eAAe,YAAY;CAC7C,MAAM,WAAW,kBAAkB,IAAI,SAAS;CAChD,IAAI,UAAU;EACZ,IAAI,SAAS,iBAAiB,cAC5B,MAAM,IAAI,WAAW,uCAAuC;EAE9D,OAAO;CACT;CAOA,OAAO;EAJL;EACA;EACA,MAAM,IAAI,UAAU,GAAG,mBAAmB,YAAY,EAAE;CAE/C;AACb;AAEA,SAAS,kBAAkB,OAAwB;CACjD,kBAAkB,OAAO,MAAM,SAAS;CACxC,kBAAkB,IAAI,MAAM,WAAW,KAAK;CAC5C,OAAO,kBAAkB,OAAO,qBAAqB;EACnD,MAAM,SAAS,kBAAkB,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC;EAC/C,IAAI,WAAW,KAAA,GAAW;EAC1B,kBAAkB,OAAO,MAAM;CACjC;AACF;AAEA,SAAS,iBAAiB,OAAwB;CAChD,MAAM,WACJ,KAGA;CACF,WAAW,MAAM,WAAW,MAAM,IAAI;AACxC;AAEA,SAAS,oBAAoB,OAAiD;CAC5E,IAAI,OAAO,aAAa,aAAa,OAAO;CAC5C,MAAM,MAAM,SAAS;CACrB,IAAI,qBAAqB,WAAW,IAAI,QAAQ;CAChD,IAAI,CAAC,oBAAoB;EACvB,qCAAqB,IAAI,IAAI;EAC7B,WAAW,IAAI,UAAU,kBAAkB;CAC7C;CACA,MAAM,UAAU,mBAAmB,IAAI,GAAG;CAC1C,IAAI,SAAS,QAAQ,aAAa,OAAO;CAEzC,MAAM,wBAAwB,MAAM,KAClC,SAAS,iBAAmC,SAAS,oBAAoB,EAAE,CAC7E;CACA,MAAM,eACJ,sBAAsB,MAAM,aAAa,QAAQ,SAAS,KAAA,OAAe,KAAK,MAC7E,UAAU,KAAA,KAAa,sBAAsB,WAAW,IACrD,sBAAsB,KACtB,KAAA,MACJ,SAAS,cAAc,OAAO;CAChC,IAAI,CAAC,aAAa,aAAa;EAC7B,aAAa,aAAa,qBAAqB,MAAM;EACrD,IAAI,UAAU,KAAA,GAAW,aAAa,QAAQ;EAC9C,CAAC,SAAS,QAAQ,SAAS,gBAAA,CAAiB,OAAO,YAAY;CACjE;CACA,MAAM,WAA0B;EAC9B,SAAS;EACT,WAAW,qBAAqB,aAAa,WAAW;EACxD,uBAAO,IAAI,IAAI;CACjB;CACA,mBAAmB,IAAI,KAAK,QAAQ;CACpC,OAAO;AACT;AAEA,SAAS,sBAAsB,cAA8B;CAC3D,OAAO,mBAAmB,YAAY;AACxC;AAEA,SAAgB,yBAAyB,cAAsD;CAC7F,IAAI,OAAO,iBAAiB,UAAU,OAAO,KAAA;CAE7C,MAAM,aAAa,sBAAsB,YAAY;CACrD,IAAI,CAAC,YAAY,OAAO,KAAA;CAExB,MAAM,QAAQ,aAAa,UAAU;CAErC,MAAM,WAAW,oBADH,KAAK,SACsB,CAAC;CAC1C,MAAM,aAAa,UAAU,MAAM,IAAI,UAAU;CACjD,IAAI,YAAY;EACd,iBAAiB,UAAU;EAC3B,OAAO,WAAW;CACpB;CAEA,IAAI,UAAU;EACZ,IAAI,CAAC,SAAS,MAAM,IAAI,UAAU,KAAK,CAAC,SAAS,QAAQ,aAAa,SAAS,MAAM,IAAI;OACnF,SAAS,aAAa,iBACxB,MAAM,IAAI,WAAW,yCAAyC;EAAA;EAGlE,IAAI,CAAC,SAAS,QAAQ,aAAa,SAAS,MAAM,IAAI,GAAG;GACvD,SAAS,QAAQ,OAAO,MAAM,MAAM,IAAI;GACxC,SAAS,aAAa;EACxB;EACA,SAAS,MAAM,IAAI,YAAY,KAAK;CACtC;CAEA,kBAAkB,KAAK;CACvB,iBAAiB,KAAK;CAEtB,OAAO,MAAM;AACf;AAEA,SAAgB,kBAAkB,MAAwB;CACxD,MAAM,wBAAQ,IAAI,IAAoB;CAGtC,KAAK,MAAM,aAAa,KAAK,SAAS,kCAAqB,GAAG;EAC5D,MAAM,QAAQ,UAAU,MAAM,UAAU,MAAM;EAC9C,KAAK,MAAM,aAAa,MAAM,MAAM,KAAK,GAAG;GAC1C,MAAM,QAAQ,kBAAkB,IAAI,SAAS;GAC7C,IAAI,OAAO,MAAM,IAAI,WAAW,MAAM,IAAI;GAC1C,IAAI,MAAM,OAAO,iBACf,MAAM,IAAI,WAAW,yCAAyC;EAElE;CACF;CAEA,OAAO,MAAM,KAAK,MAAM,OAAO,CAAC;AAClC"}
|
|
1
|
+
{"version":3,"file":"style.js","names":[],"sources":["../../../src/components/_internal/style.ts"],"sourcesContent":["import * as Askr from \"@askrjs/askr\";\n\nconst cssPropertyNameCache = new Map<string, string>();\nconst MAX_PROPERTY_CACHE = 256;\n\nconst CSS_UNSAFE_RE = /[{}<>\\\\]/;\nconst CSS_URI_SCHEME_RE = /(?:^|[\\s(,])([a-z][a-z0-9+.-]*):/i;\nconst CSS_FUNCTION_NAME_RE = /([a-z-][a-z0-9-]*)\\s*\\(/gi;\nconst CSS_ALLOWED_FUNCTIONS = new Set([\n \"var\",\n \"calc\",\n \"min\",\n \"max\",\n \"clamp\",\n \"minmax\",\n \"repeat\",\n \"rgb\",\n \"rgba\",\n \"hsl\",\n \"hsla\",\n \"lab\",\n \"lch\",\n \"oklab\",\n \"oklch\",\n \"color\",\n \"color-mix\",\n \"translate\",\n \"translatex\",\n \"translatey\",\n \"translatez\",\n \"scale\",\n \"scalex\",\n \"scaley\",\n \"scalez\",\n \"rotate\",\n \"rotatex\",\n \"rotatey\",\n \"rotatez\",\n \"skew\",\n \"skewx\",\n \"skewy\",\n \"matrix\",\n \"matrix3d\",\n \"linear-gradient\",\n \"radial-gradient\",\n \"conic-gradient\",\n \"repeating-linear-gradient\",\n \"repeating-radial-gradient\",\n \"repeating-conic-gradient\",\n \"cubic-bezier\",\n \"steps\",\n]);\n\nfunction isSafeCssPropertyName(name: string): boolean {\n if (name.startsWith(\"--\")) return /^--[a-zA-Z0-9_-]+$/.test(name);\n return /^[a-zA-Z_][a-zA-Z0-9_-]*$/.test(name);\n}\n\nfunction isSafeCssValue(value: string): boolean {\n if (CSS_UNSAFE_RE.test(value) || CSS_URI_SCHEME_RE.test(value)) return false;\n\n for (const match of value.matchAll(CSS_FUNCTION_NAME_RE)) {\n if (!CSS_ALLOWED_FUNCTIONS.has(match[1]!.toLowerCase())) return false;\n }\n\n return true;\n}\n\nfunction splitCssDeclarations(value: string): string[] {\n const declarations: string[] = [];\n let start = 0;\n let depth = 0;\n let quote: string | undefined;\n\n for (let index = 0; index < value.length; index += 1) {\n const char = value[index]!;\n if (quote) {\n if (char === quote && value[index - 1] !== \"\\\\\") quote = undefined;\n continue;\n }\n if (char === '\"' || char === \"'\") {\n quote = char;\n continue;\n }\n if (char === \"(\") depth += 1;\n if (char === \")\" && depth > 0) depth -= 1;\n if (char === \";\" && depth === 0) {\n declarations.push(value.slice(start, index));\n start = index + 1;\n }\n }\n\n declarations.push(value.slice(start));\n return declarations;\n}\n\nfunction serializeCssString(value: string): string {\n const entries: Array<[string, string]> = [];\n for (const candidate of splitCssDeclarations(value)) {\n const separator = candidate.indexOf(\":\");\n if (separator <= 0) continue;\n const key = candidate.slice(0, separator).trim();\n const cssValue = candidate.slice(separator + 1).trim();\n if (key && cssValue) entries.push([key, cssValue]);\n }\n return serializeCssDeclarations(Object.fromEntries(entries));\n}\n\nfunction cssPropertyName(name: string): string {\n let cached = cssPropertyNameCache.get(name);\n if (cached !== undefined) {\n return cached;\n }\n\n let result = \"\";\n\n for (let index = 0; index < name.length; index += 1) {\n const code = name.charCodeAt(index);\n\n if (code >= 65 && code <= 90) {\n result += `-${String.fromCharCode(code + 32)}`;\n } else {\n result += name[index];\n }\n }\n\n if (cssPropertyNameCache.size < MAX_PROPERTY_CACHE) {\n cssPropertyNameCache.set(name, result);\n }\n return result;\n}\n\nexport function serializeCssDeclarations(styles: Record<string, unknown>): string {\n const keys = Object.keys(styles);\n let result = \"\";\n\n for (const key of keys) {\n const value = styles[key];\n if (value === undefined || value === null) {\n continue;\n }\n\n const rawProperty = key.trim();\n if (!isSafeCssPropertyName(rawProperty)) {\n continue;\n }\n const property = cssPropertyName(rawProperty).trim();\n const cssValue = String(value).trim();\n if (!cssValue || !isSafeCssValue(cssValue)) {\n continue;\n }\n\n const declaration = `${property}:${cssValue}`;\n result = result ? `${result};${declaration}` : declaration;\n }\n\n return result;\n}\n\nexport function mergeCssVar(style: unknown, name: string, value: string): string {\n const decl = `${name}:${value}`;\n\n if (typeof style === \"string\") {\n const trimmed = style.trim();\n return trimmed ? `${trimmed};${decl}` : decl;\n }\n\n if (style && typeof style === \"object\") {\n const entries = serializeCssDeclarations(style as Record<string, unknown>);\n return entries ? `${entries};${decl}` : decl;\n }\n\n return decl;\n}\n\nconst STYLE_REGISTRY_ATTR = \"data-askr-style-registry\";\nconst STYLE_CLASS_PREFIX = \"ak-style-\";\nconst MAX_STYLE_RULES = 512;\nconst MAX_COLLISION_CACHE = 4096;\n\ntype StyleRule = {\n className: string;\n declarations: string;\n rule: string;\n};\n\nconst styleCollisionCache = new Map<string, StyleRule>();\ntype StyleRegistry = {\n element: HTMLStyleElement;\n ruleCount: number;\n rules: Map<string, StyleRule>;\n};\nconst registries = new WeakMap<Document, Map<string, StyleRegistry>>();\nfunction countRegisteredRules(value: string | null): number {\n return value?.match(/\\.ak-style-[a-z0-9]+\\{/g)?.length ?? 0;\n}\n\nfunction styleClassName(declarations: string): string {\n let first = 0xdeadbeef ^ declarations.length;\n let second = 0x41c6ce57 ^ declarations.length;\n\n for (let index = 0; index < declarations.length; index += 1) {\n const code = declarations.charCodeAt(index);\n first = Math.imul(first ^ code, 2_654_435_761);\n second = Math.imul(second ^ code, 1_597_334_677);\n }\n\n first =\n Math.imul(first ^ (first >>> 16), 2_246_822_507) ^\n Math.imul(second ^ (second >>> 13), 3_266_489_909);\n second =\n Math.imul(second ^ (second >>> 16), 2_246_822_507) ^\n Math.imul(first ^ (first >>> 13), 3_266_489_909);\n\n return `${STYLE_CLASS_PREFIX}${(second >>> 0).toString(36)}${(first >>> 0).toString(36)}`;\n}\n\nfunction escapeStyleRawText(value: string): string {\n return value.replace(/<\\//g, \"<\\\\/\");\n}\n\nfunction styleRuleFor(declarations: string): StyleRule {\n const className = styleClassName(declarations);\n const existing = styleCollisionCache.get(className);\n if (existing) {\n if (existing.declarations !== declarations) {\n throw new RangeError(\"Theme style class collision detected.\");\n }\n return existing;\n }\n\n const entry = {\n className,\n declarations,\n rule: `.${className}{${escapeStyleRawText(declarations)}}`,\n };\n return entry;\n}\n\nfunction rememberStyleRule(entry: StyleRule): void {\n styleCollisionCache.delete(entry.className);\n styleCollisionCache.set(entry.className, entry);\n while (styleCollisionCache.size > MAX_COLLISION_CACHE) {\n const oldest = styleCollisionCache.keys().next().value;\n if (oldest === undefined) break;\n styleCollisionCache.delete(oldest);\n }\n}\n\nfunction registerSSRStyle(entry: StyleRule): void {\n const register = (\n Askr as typeof Askr & {\n registerSSRStyle?: (id: string, cssText: string) => void;\n }\n ).registerSSRStyle;\n register?.(entry.className, entry.rule);\n}\n\nfunction ensureStyleRegistry(nonce: string | undefined): StyleRegistry | null {\n if (typeof document === \"undefined\") return null;\n const key = nonce ?? \"\";\n let documentRegistries = registries.get(document);\n if (!documentRegistries) {\n documentRegistries = new Map();\n registries.set(document, documentRegistries);\n }\n const current = documentRegistries.get(key);\n if (current?.element.isConnected) return current;\n\n const existingStyleElements = Array.from(\n document.querySelectorAll<HTMLStyleElement>(`style[${STYLE_REGISTRY_ATTR}]`),\n );\n const styleElement =\n existingStyleElements.find((element) => (element.nonce || undefined) === nonce) ??\n (nonce === undefined && existingStyleElements.length === 1\n ? existingStyleElements[0]\n : undefined) ??\n document.createElement(\"style\");\n if (!styleElement.isConnected) {\n styleElement.setAttribute(STYLE_REGISTRY_ATTR, \"true\");\n if (nonce !== undefined) styleElement.nonce = nonce;\n (document.head ?? document.documentElement).append(styleElement);\n }\n const registry: StyleRegistry = {\n element: styleElement,\n ruleCount: countRegisteredRules(styleElement.textContent),\n rules: new Map(),\n };\n documentRegistries.set(key, registry);\n return registry;\n}\n\nfunction normalizeDeclarations(declarations: string): string {\n return serializeCssString(declarations);\n}\n\nexport function styleDeclarationsToClass(declarations: string | undefined): string | undefined {\n if (typeof declarations !== \"string\") return undefined;\n\n const normalized = normalizeDeclarations(declarations);\n if (!normalized) return undefined;\n\n const entry = styleRuleFor(normalized);\n const nonce = Askr.cspNonce();\n const registry = ensureStyleRegistry(nonce);\n const registered = registry?.rules.get(normalized);\n if (registered) {\n registerSSRStyle(registered);\n return registered.className;\n }\n\n if (registry) {\n if (!registry.rules.has(normalized) && !registry.element.textContent?.includes(entry.rule)) {\n if (registry.ruleCount >= MAX_STYLE_RULES) {\n throw new RangeError(\"Theme style registry capacity exceeded.\");\n }\n }\n if (!registry.element.textContent?.includes(entry.rule)) {\n registry.element.append(entry.rule, \"\\n\");\n registry.ruleCount += 1;\n }\n registry.rules.set(normalized, entry);\n }\n\n rememberStyleRule(entry);\n registerSSRStyle(entry);\n\n return entry.className;\n}\n"],"mappings":";;AAEA,MAAM,uCAAuB,IAAI,IAAoB;AACrD,MAAM,qBAAqB;AAE3B,MAAM,gBAAgB;AACtB,MAAM,oBAAoB;AAC1B,MAAM,uBAAuB;AAC7B,MAAM,wCAAwB,IAAI,IAAI;CACpC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;AAED,SAAS,sBAAsB,MAAuB;CACpD,IAAI,KAAK,WAAW,IAAI,GAAG,OAAO,qBAAqB,KAAK,IAAI;CAChE,OAAO,4BAA4B,KAAK,IAAI;AAC9C;AAEA,SAAS,eAAe,OAAwB;CAC9C,IAAI,cAAc,KAAK,KAAK,KAAK,kBAAkB,KAAK,KAAK,GAAG,OAAO;CAEvE,KAAK,MAAM,SAAS,MAAM,SAAS,oBAAoB,GACrD,IAAI,CAAC,sBAAsB,IAAI,MAAM,EAAE,CAAE,YAAY,CAAC,GAAG,OAAO;CAGlE,OAAO;AACT;AAEA,SAAS,qBAAqB,OAAyB;CACrD,MAAM,eAAyB,CAAC;CAChC,IAAI,QAAQ;CACZ,IAAI,QAAQ;CACZ,IAAI;CAEJ,KAAK,IAAI,QAAQ,GAAG,QAAQ,MAAM,QAAQ,SAAS,GAAG;EACpD,MAAM,OAAO,MAAM;EACnB,IAAI,OAAO;GACT,IAAI,SAAS,SAAS,MAAM,QAAQ,OAAO,MAAM,QAAQ,KAAA;GACzD;EACF;EACA,IAAI,SAAS,QAAO,SAAS,KAAK;GAChC,QAAQ;GACR;EACF;EACA,IAAI,SAAS,KAAK,SAAS;EAC3B,IAAI,SAAS,OAAO,QAAQ,GAAG,SAAS;EACxC,IAAI,SAAS,OAAO,UAAU,GAAG;GAC/B,aAAa,KAAK,MAAM,MAAM,OAAO,KAAK,CAAC;GAC3C,QAAQ,QAAQ;EAClB;CACF;CAEA,aAAa,KAAK,MAAM,MAAM,KAAK,CAAC;CACpC,OAAO;AACT;AAEA,SAAS,mBAAmB,OAAuB;CACjD,MAAM,UAAmC,CAAC;CAC1C,KAAK,MAAM,aAAa,qBAAqB,KAAK,GAAG;EACnD,MAAM,YAAY,UAAU,QAAQ,GAAG;EACvC,IAAI,aAAa,GAAG;EACpB,MAAM,MAAM,UAAU,MAAM,GAAG,SAAS,CAAC,CAAC,KAAK;EAC/C,MAAM,WAAW,UAAU,MAAM,YAAY,CAAC,CAAC,CAAC,KAAK;EACrD,IAAI,OAAO,UAAU,QAAQ,KAAK,CAAC,KAAK,QAAQ,CAAC;CACnD;CACA,OAAO,yBAAyB,OAAO,YAAY,OAAO,CAAC;AAC7D;AAEA,SAAS,gBAAgB,MAAsB;CAC7C,IAAI,SAAS,qBAAqB,IAAI,IAAI;CAC1C,IAAI,WAAW,KAAA,GACb,OAAO;CAGT,IAAI,SAAS;CAEb,KAAK,IAAI,QAAQ,GAAG,QAAQ,KAAK,QAAQ,SAAS,GAAG;EACnD,MAAM,OAAO,KAAK,WAAW,KAAK;EAElC,IAAI,QAAQ,MAAM,QAAQ,IACxB,UAAU,IAAI,OAAO,aAAa,OAAO,EAAE;OAE3C,UAAU,KAAK;CAEnB;CAEA,IAAI,qBAAqB,OAAO,oBAC9B,qBAAqB,IAAI,MAAM,MAAM;CAEvC,OAAO;AACT;AAEA,SAAgB,yBAAyB,QAAyC;CAChF,MAAM,OAAO,OAAO,KAAK,MAAM;CAC/B,IAAI,SAAS;CAEb,KAAK,MAAM,OAAO,MAAM;EACtB,MAAM,QAAQ,OAAO;EACrB,IAAI,UAAU,KAAA,KAAa,UAAU,MACnC;EAGF,MAAM,cAAc,IAAI,KAAK;EAC7B,IAAI,CAAC,sBAAsB,WAAW,GACpC;EAEF,MAAM,WAAW,gBAAgB,WAAW,CAAC,CAAC,KAAK;EACnD,MAAM,WAAW,OAAO,KAAK,CAAC,CAAC,KAAK;EACpC,IAAI,CAAC,YAAY,CAAC,eAAe,QAAQ,GACvC;EAGF,MAAM,cAAc,GAAG,SAAS,GAAG;EACnC,SAAS,SAAS,GAAG,OAAO,GAAG,gBAAgB;CACjD;CAEA,OAAO;AACT;AAkBA,MAAM,sBAAsB;AAC5B,MAAM,qBAAqB;AAC3B,MAAM,kBAAkB;AACxB,MAAM,sBAAsB;AAQ5B,MAAM,sCAAsB,IAAI,IAAuB;AAMvD,MAAM,6BAAa,IAAI,QAA8C;AACrE,SAAS,qBAAqB,OAA8B;CAC1D,OAAO,OAAO,MAAM,yBAAyB,CAAC,EAAE,UAAU;AAC5D;AAEA,SAAS,eAAe,cAA8B;CACpD,IAAI,QAAQ,aAAa,aAAa;CACtC,IAAI,SAAS,aAAa,aAAa;CAEvC,KAAK,IAAI,QAAQ,GAAG,QAAQ,aAAa,QAAQ,SAAS,GAAG;EAC3D,MAAM,OAAO,aAAa,WAAW,KAAK;EAC1C,QAAQ,KAAK,KAAK,QAAQ,MAAM,UAAa;EAC7C,SAAS,KAAK,KAAK,SAAS,MAAM,UAAa;CACjD;CAEA,QACE,KAAK,KAAK,QAAS,UAAU,IAAK,UAAa,IAC/C,KAAK,KAAK,SAAU,WAAW,IAAK,UAAa;CACnD,SACE,KAAK,KAAK,SAAU,WAAW,IAAK,UAAa,IACjD,KAAK,KAAK,QAAS,UAAU,IAAK,UAAa;CAEjD,OAAO,GAAG,sBAAsB,WAAW,EAAA,CAAG,SAAS,EAAE,KAAK,UAAU,EAAA,CAAG,SAAS,EAAE;AACxF;AAEA,SAAS,mBAAmB,OAAuB;CACjD,OAAO,MAAM,QAAQ,QAAQ,MAAM;AACrC;AAEA,SAAS,aAAa,cAAiC;CACrD,MAAM,YAAY,eAAe,YAAY;CAC7C,MAAM,WAAW,oBAAoB,IAAI,SAAS;CAClD,IAAI,UAAU;EACZ,IAAI,SAAS,iBAAiB,cAC5B,MAAM,IAAI,WAAW,uCAAuC;EAE9D,OAAO;CACT;CAOA,OAAO;EAJL;EACA;EACA,MAAM,IAAI,UAAU,GAAG,mBAAmB,YAAY,EAAE;CAE/C;AACb;AAEA,SAAS,kBAAkB,OAAwB;CACjD,oBAAoB,OAAO,MAAM,SAAS;CAC1C,oBAAoB,IAAI,MAAM,WAAW,KAAK;CAC9C,OAAO,oBAAoB,OAAO,qBAAqB;EACrD,MAAM,SAAS,oBAAoB,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC;EACjD,IAAI,WAAW,KAAA,GAAW;EAC1B,oBAAoB,OAAO,MAAM;CACnC;AACF;AAEA,SAAS,iBAAiB,OAAwB;CAChD,MAAM,WACJ,KAGA;CACF,WAAW,MAAM,WAAW,MAAM,IAAI;AACxC;AAEA,SAAS,oBAAoB,OAAiD;CAC5E,IAAI,OAAO,aAAa,aAAa,OAAO;CAC5C,MAAM,MAAM,SAAS;CACrB,IAAI,qBAAqB,WAAW,IAAI,QAAQ;CAChD,IAAI,CAAC,oBAAoB;EACvB,qCAAqB,IAAI,IAAI;EAC7B,WAAW,IAAI,UAAU,kBAAkB;CAC7C;CACA,MAAM,UAAU,mBAAmB,IAAI,GAAG;CAC1C,IAAI,SAAS,QAAQ,aAAa,OAAO;CAEzC,MAAM,wBAAwB,MAAM,KAClC,SAAS,iBAAmC,SAAS,oBAAoB,EAAE,CAC7E;CACA,MAAM,eACJ,sBAAsB,MAAM,aAAa,QAAQ,SAAS,KAAA,OAAe,KAAK,MAC7E,UAAU,KAAA,KAAa,sBAAsB,WAAW,IACrD,sBAAsB,KACtB,KAAA,MACJ,SAAS,cAAc,OAAO;CAChC,IAAI,CAAC,aAAa,aAAa;EAC7B,aAAa,aAAa,qBAAqB,MAAM;EACrD,IAAI,UAAU,KAAA,GAAW,aAAa,QAAQ;EAC9C,CAAC,SAAS,QAAQ,SAAS,gBAAA,CAAiB,OAAO,YAAY;CACjE;CACA,MAAM,WAA0B;EAC9B,SAAS;EACT,WAAW,qBAAqB,aAAa,WAAW;EACxD,uBAAO,IAAI,IAAI;CACjB;CACA,mBAAmB,IAAI,KAAK,QAAQ;CACpC,OAAO;AACT;AAEA,SAAS,sBAAsB,cAA8B;CAC3D,OAAO,mBAAmB,YAAY;AACxC;AAEA,SAAgB,yBAAyB,cAAsD;CAC7F,IAAI,OAAO,iBAAiB,UAAU,OAAO,KAAA;CAE7C,MAAM,aAAa,sBAAsB,YAAY;CACrD,IAAI,CAAC,YAAY,OAAO,KAAA;CAExB,MAAM,QAAQ,aAAa,UAAU;CAErC,MAAM,WAAW,oBADH,KAAK,SACsB,CAAC;CAC1C,MAAM,aAAa,UAAU,MAAM,IAAI,UAAU;CACjD,IAAI,YAAY;EACd,iBAAiB,UAAU;EAC3B,OAAO,WAAW;CACpB;CAEA,IAAI,UAAU;EACZ,IAAI,CAAC,SAAS,MAAM,IAAI,UAAU,KAAK,CAAC,SAAS,QAAQ,aAAa,SAAS,MAAM,IAAI;OACnF,SAAS,aAAa,iBACxB,MAAM,IAAI,WAAW,yCAAyC;EAAA;EAGlE,IAAI,CAAC,SAAS,QAAQ,aAAa,SAAS,MAAM,IAAI,GAAG;GACvD,SAAS,QAAQ,OAAO,MAAM,MAAM,IAAI;GACxC,SAAS,aAAa;EACxB;EACA,SAAS,MAAM,IAAI,YAAY,KAAK;CACtC;CAEA,kBAAkB,KAAK;CACvB,iBAAiB,KAAK;CAEtB,OAAO,MAAM;AACf"}
|
|
@@ -1,65 +1,28 @@
|
|
|
1
1
|
import { classes } from "../_internal/classes.js";
|
|
2
2
|
import { mergeProps } from "../_internal/merge-props.js";
|
|
3
3
|
import { styleDeclarationsToClass } from "../_internal/style.js";
|
|
4
|
-
import { mergeLayoutStyles } from "../_internal/block-layout.js";
|
|
4
|
+
import { mergeLayoutStyles, resolveSpaceValue, setResponsiveVar } from "../_internal/block-layout.js";
|
|
5
5
|
import { intrinsicElement } from "../_internal/jsx.js";
|
|
6
6
|
//#region src/components/grid/grid.tsx
|
|
7
7
|
const DEFAULT_ELEMENT = "div";
|
|
8
|
-
const BREAKPOINTS = [
|
|
9
|
-
"base",
|
|
10
|
-
"sm",
|
|
11
|
-
"md",
|
|
12
|
-
"lg",
|
|
13
|
-
"xl"
|
|
14
|
-
];
|
|
15
|
-
const SPACE_TOKEN_MAP = {
|
|
16
|
-
"0": "0",
|
|
17
|
-
xs: "var(--ak-space-xs)",
|
|
18
|
-
sm: "var(--ak-space-sm)",
|
|
19
|
-
md: "var(--ak-space-md)",
|
|
20
|
-
lg: "var(--ak-space-lg)",
|
|
21
|
-
xl: "var(--ak-space-xl)",
|
|
22
|
-
"2xl": "var(--ak-space-2xl)",
|
|
23
|
-
"3xl": "var(--ak-space-3xl)",
|
|
24
|
-
page: "var(--ak-layout-page-gutter)"
|
|
25
|
-
};
|
|
26
|
-
function isResponsiveValue(value) {
|
|
27
|
-
return Boolean(value && typeof value === "object" && !Array.isArray(value) && BREAKPOINTS.some((breakpoint) => breakpoint in value));
|
|
28
|
-
}
|
|
29
|
-
function normalizeResponsiveValue(value) {
|
|
30
|
-
if (value === void 0) return void 0;
|
|
31
|
-
if (isResponsiveValue(value)) return value;
|
|
32
|
-
return { base: value };
|
|
33
|
-
}
|
|
34
8
|
function resolveColumns(value) {
|
|
35
9
|
if (typeof value === "number") return `repeat(${value}, minmax(0, 1fr))`;
|
|
36
10
|
return value.trim() || "minmax(0, 1fr)";
|
|
37
11
|
}
|
|
38
12
|
function resolveGap(value) {
|
|
39
|
-
return
|
|
13
|
+
return String(resolveSpaceValue(value));
|
|
40
14
|
}
|
|
41
15
|
function resolveAlign(value) {
|
|
42
16
|
if (value === "start") return "start";
|
|
43
17
|
if (value === "end") return "end";
|
|
44
18
|
return value;
|
|
45
19
|
}
|
|
46
|
-
function setResponsiveVar(styles, property, value, resolve) {
|
|
47
|
-
const normalized = normalizeResponsiveValue(value);
|
|
48
|
-
if (!normalized) return;
|
|
49
|
-
for (const breakpoint of BREAKPOINTS) {
|
|
50
|
-
const breakpointValue = normalized[breakpoint];
|
|
51
|
-
if (breakpointValue === void 0) continue;
|
|
52
|
-
const resolved = resolve(breakpointValue);
|
|
53
|
-
if (resolved === void 0 || resolved === "") continue;
|
|
54
|
-
styles[`--ak-grid-${property}-${breakpoint}`] = resolved;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
20
|
function Grid(props) {
|
|
58
21
|
const { as, columns, gap, align, children, class: classProp, className, ref, style: userStyle, ...rest } = props;
|
|
59
22
|
const styles = {};
|
|
60
|
-
setResponsiveVar(styles, "columns", columns, resolveColumns);
|
|
61
|
-
setResponsiveVar(styles, "gap", gap, resolveGap);
|
|
62
|
-
setResponsiveVar(styles, "align", align, resolveAlign);
|
|
23
|
+
setResponsiveVar(styles, "grid-columns", columns, resolveColumns);
|
|
24
|
+
setResponsiveVar(styles, "grid-gap", gap, resolveGap);
|
|
25
|
+
setResponsiveVar(styles, "grid-align", align, resolveAlign);
|
|
63
26
|
const finalProps = mergeProps(rest, {
|
|
64
27
|
ref,
|
|
65
28
|
class: classes("grid", classProp, className, styleDeclarationsToClass(mergeLayoutStyles(styles, userStyle))),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"grid.js","names":[],"sources":["../../../src/components/grid/grid.tsx"],"sourcesContent":["import type { JSX } from \"@askrjs/askr/jsx-runtime\";\nimport { classes } from \"../_internal/classes\";\nimport { mergeLayoutStyles } from \"../_internal/block-layout\";\nimport { mergeProps } from \"../_internal/merge-props\";\nimport { intrinsicElement } from \"../_internal/jsx\";\nimport { styleDeclarationsToClass } from \"../_internal/style\";\nimport type { BlockSpace
|
|
1
|
+
{"version":3,"file":"grid.js","names":[],"sources":["../../../src/components/grid/grid.tsx"],"sourcesContent":["import type { JSX } from \"@askrjs/askr/jsx-runtime\";\nimport { classes } from \"../_internal/classes\";\nimport { mergeLayoutStyles, resolveSpaceValue, setResponsiveVar } from \"../_internal/block-layout\";\nimport { mergeProps } from \"../_internal/merge-props\";\nimport { intrinsicElement } from \"../_internal/jsx\";\nimport { styleDeclarationsToClass } from \"../_internal/style\";\nimport type { BlockSpace } from \"../_internal/block-layout\";\nimport type { GridAlign, GridColumns, GridElement, GridProps } from \"./grid.types\";\n\nconst DEFAULT_ELEMENT = \"div\";\nfunction resolveColumns(value: GridColumns): string {\n if (typeof value === \"number\") {\n return `repeat(${value}, minmax(0, 1fr))`;\n }\n\n const trimmed = value.trim();\n return trimmed || \"minmax(0, 1fr)\";\n}\n\nfunction resolveGap(value: BlockSpace): string {\n return String(resolveSpaceValue(value));\n}\n\nfunction resolveAlign(value: GridAlign): string {\n if (value === \"start\") return \"start\";\n if (value === \"end\") return \"end\";\n return value;\n}\n\nexport function Grid<TElement extends GridElement = \"div\">(\n props: GridProps<TElement>,\n): JSX.Element {\n const {\n as,\n columns,\n gap,\n align,\n children,\n class: classProp,\n className,\n ref,\n style: userStyle,\n ...rest\n } = props as GridProps<GridElement> & {\n class?: unknown;\n className?: unknown;\n style?: unknown;\n };\n\n const styles: Record<string, string | number> = {};\n setResponsiveVar(styles, \"grid-columns\", columns, resolveColumns);\n setResponsiveVar(styles, \"grid-gap\", gap, resolveGap);\n setResponsiveVar(styles, \"grid-align\", align, resolveAlign);\n\n const generatedClass = styleDeclarationsToClass(mergeLayoutStyles(styles, userStyle));\n const finalProps = mergeProps(rest, {\n ref,\n class: classes(\"grid\", classProp, className, generatedClass),\n \"data-slot\": \"grid\",\n });\n\n return intrinsicElement(as ?? DEFAULT_ELEMENT, finalProps, children);\n}\n"],"mappings":";;;;;;AASA,MAAM,kBAAkB;AACxB,SAAS,eAAe,OAA4B;CAClD,IAAI,OAAO,UAAU,UACnB,OAAO,UAAU,MAAM;CAIzB,OADgB,MAAM,KACT,KAAK;AACpB;AAEA,SAAS,WAAW,OAA2B;CAC7C,OAAO,OAAO,kBAAkB,KAAK,CAAC;AACxC;AAEA,SAAS,aAAa,OAA0B;CAC9C,IAAI,UAAU,SAAS,OAAO;CAC9B,IAAI,UAAU,OAAO,OAAO;CAC5B,OAAO;AACT;AAEA,SAAgB,KACd,OACa;CACb,MAAM,EACJ,IACA,SACA,KACA,OACA,UACA,OAAO,WACP,WACA,KACA,OAAO,WACP,GAAG,SACD;CAMJ,MAAM,SAA0C,CAAC;CACjD,iBAAiB,QAAQ,gBAAgB,SAAS,cAAc;CAChE,iBAAiB,QAAQ,YAAY,KAAK,UAAU;CACpD,iBAAiB,QAAQ,cAAc,OAAO,YAAY;CAG1D,MAAM,aAAa,WAAW,MAAM;EAClC;EACA,OAAO,QAAQ,QAAQ,WAAW,WAHb,yBAAyB,kBAAkB,QAAQ,SAAS,CAGvB,CAAC;EAC3D,aAAa;CACf,CAAC;CAED,OAAO,iBAAiB,MAAM,iBAAiB,YAAY,QAAQ;AACrE"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Button } from "@askrjs/ui";
|
|
2
2
|
import { jsx } from "@askrjs/askr/jsx-runtime";
|
|
3
3
|
import { defineScope, getSignal, readScope, state } from "@askrjs/askr";
|
|
4
|
+
import { cloneElement, isElement } from "@askrjs/askr/foundations/structures";
|
|
4
5
|
//#region src/components/theme/theme.tsx
|
|
5
6
|
const CAT_THEME_NAMES = [
|
|
6
7
|
"tabby",
|
|
@@ -47,7 +48,6 @@ const CAT_THEME_OPTIONS = [
|
|
|
47
48
|
];
|
|
48
49
|
const DEFAULT_STORAGE_KEY = "askr-theme";
|
|
49
50
|
const STATIC_CHILDREN = Symbol.for("askr.static-children");
|
|
50
|
-
const STATIC_CHILD_SLOTS_CACHE = Symbol.for("__askrStaticChildSlots");
|
|
51
51
|
const documentThemeCoordinators = /* @__PURE__ */ new WeakMap();
|
|
52
52
|
const ThemeScopeContext = defineScope({
|
|
53
53
|
theme: () => "system",
|
|
@@ -131,6 +131,11 @@ function getDefaultThemeCoordinator() {
|
|
|
131
131
|
documentThemeCoordinators.set(document, coordinator);
|
|
132
132
|
return coordinator;
|
|
133
133
|
}
|
|
134
|
+
function removeEmptyGeneratedStyleRegistries(root) {
|
|
135
|
+
const ownerDocument = root?.nodeType === 9 ? root : root?.ownerDocument ?? (typeof document === "undefined" ? null : document);
|
|
136
|
+
if (!ownerDocument) return;
|
|
137
|
+
for (const registry of ownerDocument.querySelectorAll("style[data-askr-style-registry]")) if (!registry.textContent?.trim()) registry.remove();
|
|
138
|
+
}
|
|
134
139
|
function createThemeCoordinator() {
|
|
135
140
|
const scopes = /* @__PURE__ */ new Map();
|
|
136
141
|
let nextSequence = 0;
|
|
@@ -154,6 +159,7 @@ function createThemeCoordinator() {
|
|
|
154
159
|
setTimeout(() => {
|
|
155
160
|
scheduled = false;
|
|
156
161
|
syncActive();
|
|
162
|
+
if (scopes.size === 0) removeEmptyGeneratedStyleRegistries(root);
|
|
157
163
|
}, 0);
|
|
158
164
|
};
|
|
159
165
|
return Object.freeze({
|
|
@@ -296,9 +302,6 @@ function renderThemeToggleIconSlots(theme, icons) {
|
|
|
296
302
|
children: cloneThemeToggleIcon(icon, slotTheme)
|
|
297
303
|
}, slotTheme));
|
|
298
304
|
}
|
|
299
|
-
function isJSXElement(value) {
|
|
300
|
-
return typeof value === "object" && value !== null && "$$typeof" in value && "type" in value && "props" in value;
|
|
301
|
-
}
|
|
302
305
|
function cloneThemeToggleIcon(icon, key) {
|
|
303
306
|
if (Array.isArray(icon)) {
|
|
304
307
|
const clonedChildren = icon.map((child) => cloneThemeToggleIcon(child));
|
|
@@ -308,18 +311,14 @@ function cloneThemeToggleIcon(icon, key) {
|
|
|
308
311
|
});
|
|
309
312
|
return clonedChildren;
|
|
310
313
|
}
|
|
311
|
-
if (!
|
|
314
|
+
if (!isElement(icon)) return icon;
|
|
312
315
|
const props = icon.props;
|
|
313
316
|
const clonedProps = props ? { ...props } : {};
|
|
314
317
|
if ("children" in clonedProps) clonedProps.children = cloneThemeToggleIcon(clonedProps.children);
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
key: iconKey,
|
|
319
|
-
props: clonedProps
|
|
318
|
+
return {
|
|
319
|
+
...cloneElement(icon, clonedProps),
|
|
320
|
+
key: icon.key ?? key ?? null
|
|
320
321
|
};
|
|
321
|
-
delete clonedIcon[STATIC_CHILD_SLOTS_CACHE];
|
|
322
|
-
return clonedIcon;
|
|
323
322
|
}
|
|
324
323
|
function syncThemeTarget(html, themeChoice) {
|
|
325
324
|
if (!html) return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"theme.js","names":[],"sources":["../../../src/components/theme/theme.tsx"],"sourcesContent":["import type { JSX } from \"@askrjs/askr/jsx-runtime\";\nimport { defineScope, getSignal, readScope, state } from \"@askrjs/askr\";\nimport type { JSXElement } from \"@askrjs/askr/foundations/structures\";\nimport { Button } from \"@askrjs/ui\";\nimport type { ButtonNativeProps, PressEvent } from \"@askrjs/ui\";\n\nexport const CAT_THEME_NAMES = [\"tabby\", \"ginger\", \"tuxedo\", \"calico\", \"torty\"] as const;\n\nexport type CatThemeName = (typeof CAT_THEME_NAMES)[number];\nexport type ThemeName = \"light\" | \"dark\" | \"system\" | CatThemeName | (string & {});\n\nexport type ThemeOption = {\n value: ThemeName;\n label: string;\n};\n\nexport type ThemeScopeValue = {\n theme: () => ThemeName;\n resolvedSystemTheme: () => \"light\" | \"dark\";\n setTheme: (theme: ThemeName) => void;\n themes: readonly ThemeOption[];\n storageKey: string;\n};\n\nexport type ThemeScopeProps = {\n children?: unknown;\n defaultTheme?: ThemeName;\n themes?: readonly ThemeOption[];\n storageKey?: string;\n};\n\nexport type ThemePickerProps = Omit<\n JSX.IntrinsicElements[\"select\"],\n \"children\" | \"value\" | \"defaultValue\" | \"onChange\"\n> & {\n themes?: readonly ThemeOption[];\n label?: string;\n};\n\nexport type ThemeToggleRenderContext = {\n theme: ThemeName;\n nextTheme: ThemeName;\n};\n\nexport type ThemeToggleProps = Omit<ButtonNativeProps, \"children\" | \"onPress\"> & {\n children?: unknown | ((context: ThemeToggleRenderContext) => unknown);\n lightIcon?: unknown;\n darkIcon?: unknown;\n systemIcon?: unknown;\n themes?: readonly ThemeName[];\n onPress?: (event: PressEvent) => void;\n};\n\nexport const DEFAULT_THEME_OPTIONS: readonly ThemeOption[] = [\n { value: \"system\", label: \"System\" },\n { value: \"light\", label: \"Light\" },\n { value: \"dark\", label: \"Dark\" },\n];\n\nexport const CAT_THEME_OPTIONS: readonly ThemeOption[] = [\n { value: \"tabby\", label: \"Tabby\" },\n { value: \"ginger\", label: \"Ginger\" },\n { value: \"tuxedo\", label: \"Tuxedo\" },\n { value: \"calico\", label: \"Calico\" },\n { value: \"torty\", label: \"Torty\" },\n];\n\nconst DEFAULT_STORAGE_KEY = \"askr-theme\";\nconst STATIC_CHILDREN = Symbol.for(\"askr.static-children\");\nconst STATIC_CHILD_SLOTS_CACHE = Symbol.for(\"__askrStaticChildSlots\");\nconst documentThemeCoordinators = new WeakMap<Document, ThemeCoordinator>();\ntype ThemeCoordinator = ReturnType<typeof createThemeCoordinator>;\ntype InternalThemeScopeValue = ThemeScopeValue & {\n readonly coordinator: ThemeCoordinator | null;\n readonly depth: number;\n};\n\nconst ThemeScopeContext = defineScope<InternalThemeScopeValue>({\n theme: () => \"system\",\n resolvedSystemTheme: () => \"light\",\n setTheme: () => undefined,\n themes: DEFAULT_THEME_OPTIONS,\n storageKey: DEFAULT_STORAGE_KEY,\n coordinator: null,\n depth: -1,\n});\n\nexport function theme(): ThemeScopeValue {\n return readScope(ThemeScopeContext);\n}\n\nexport function ThemeScope(props: ThemeScopeProps): JSX.Element {\n const {\n children,\n defaultTheme = \"system\",\n themes = DEFAULT_THEME_OPTIONS,\n storageKey = DEFAULT_STORAGE_KEY,\n } = props;\n\n const scopeId = state<symbol>(Symbol(\"ThemeScope\"))();\n const scopeSignal = getSignal();\n // The first render must be identical on the server and in the browser.\n // Browser persistence is adopted from the committed root ref, after Askr's\n // hydration verifier has accepted the server markup.\n const themeState = state<ThemeName>(defaultTheme);\n const localResolvedSystemTheme = state<\"light\" | \"dark\">(\"light\");\n const persistenceAdoption = state({ complete: false })();\n const currentTheme = themeState();\n const parentScope = readScope(ThemeScopeContext);\n const ownedCoordinator = state<ThemeCoordinator>(getDefaultThemeCoordinator())();\n const coordinator = parentScope.coordinator ?? ownedCoordinator;\n const scopeDepth = parentScope.depth + 1;\n coordinator.register(scopeId, scopeDepth, currentTheme, scopeSignal, (nextTheme) => {\n if (themeState() !== nextTheme) themeState.set(nextTheme);\n });\n\n const setTheme = (nextTheme: ThemeName) => {\n themeState.set(nextTheme);\n writeStoredTheme(storageKey, nextTheme);\n coordinator.activate(scopeId, nextTheme);\n };\n\n const resolvedSystemTheme = parentScope.coordinator\n ? parentScope.resolvedSystemTheme\n : localResolvedSystemTheme;\n const value: InternalThemeScopeValue = {\n theme: themeState,\n resolvedSystemTheme,\n setTheme,\n themes,\n storageKey,\n coordinator,\n depth: scopeDepth,\n };\n\n return (\n <ThemeScopeContext value={value}>\n <div\n data-slot=\"theme-scope\"\n ref={\n parentScope.coordinator === null\n ? (element: HTMLElement | null) => {\n coordinator.attach(\n element,\n (nextTheme) => localResolvedSystemTheme.set(nextTheme),\n scopeSignal,\n );\n if (element && typeof window !== \"undefined\") {\n const onStorage = (event: StorageEvent) => {\n let storageMatches = event.storageArea == null;\n try {\n storageMatches ||= event.storageArea === window.localStorage;\n } catch {\n // Locked-down/private contexts may deny access to localStorage.\n }\n if (!storageMatches || event.key !== storageKey) {\n return;\n }\n const nextTheme = event.newValue as ThemeName | null;\n if (!nextTheme) return;\n themeState.set(nextTheme);\n coordinator.activate(scopeId, nextTheme);\n };\n window.addEventListener(\"storage\", onStorage);\n scopeSignal.addEventListener(\n \"abort\",\n () => window.removeEventListener(\"storage\", onStorage),\n { once: true },\n );\n }\n if (!element || persistenceAdoption.complete) return;\n persistenceAdoption.complete = true;\n const storedTheme = readStoredTheme(storageKey);\n if (storedTheme && storedTheme !== themeState()) {\n themeState.set(storedTheme);\n coordinator.activate(scopeId, storedTheme);\n }\n }\n : undefined\n }\n >\n {children}\n </div>\n </ThemeScopeContext>\n );\n}\n\nfunction getDefaultThemeCoordinator(): ThemeCoordinator {\n if (typeof document === \"undefined\") {\n return createThemeCoordinator();\n }\n\n const existing = documentThemeCoordinators.get(document);\n if (existing) return existing;\n\n const coordinator = createThemeCoordinator();\n documentThemeCoordinators.set(document, coordinator);\n return coordinator;\n}\n\nfunction createThemeCoordinator() {\n const scopes = new Map<\n symbol,\n {\n depth: number;\n sequence: number;\n theme: ThemeName;\n signal: AbortSignal;\n onThemeChange: (themeName: ThemeName) => void;\n }\n >();\n let nextSequence = 0;\n let explicitOwner: symbol | undefined;\n let root: Node | null = null;\n let scheduled = false;\n\n const target = (): HTMLElement | null => {\n if (root?.nodeType === 9) return (root as Document).documentElement;\n if (root && \"host\" in root) return (root as ShadowRoot).host as HTMLElement;\n return typeof document === \"undefined\" ? null : document.documentElement;\n };\n const syncActive = (): void => {\n if (explicitOwner !== undefined) return;\n let candidate: { depth: number; sequence: number; theme: ThemeName } | undefined;\n for (const scope of scopes.values()) {\n if (\n !candidate ||\n scope.depth > candidate.depth ||\n (scope.depth === candidate.depth && scope.sequence > candidate.sequence)\n ) {\n candidate = scope;\n }\n }\n if (candidate) syncThemeTarget(target(), candidate.theme);\n };\n const schedule = (): void => {\n if (typeof document === \"undefined\" || scheduled) return;\n scheduled = true;\n setTimeout(() => {\n scheduled = false;\n syncActive();\n }, 0);\n };\n\n return Object.freeze({\n attach(\n element: HTMLElement | null,\n onResolvedSystemTheme: (themeName: \"light\" | \"dark\") => void,\n signal: AbortSignal,\n ) {\n if (element) root = element.getRootNode();\n if (element && typeof window !== \"undefined\" && typeof window.matchMedia === \"function\") {\n const media = window.matchMedia(\"(prefers-color-scheme: dark)\");\n const update = () => onResolvedSystemTheme(media.matches ? \"dark\" : \"light\");\n update();\n media.addEventListener?.(\"change\", update);\n signal.addEventListener(\"abort\", () => media.removeEventListener?.(\"change\", update), {\n once: true,\n });\n }\n schedule();\n },\n register(\n id: symbol,\n depth: number,\n themeName: ThemeName,\n signal: AbortSignal,\n onThemeChange: (themeName: ThemeName) => void,\n ) {\n const existing = scopes.get(id);\n scopes.set(id, {\n depth,\n sequence: existing?.sequence ?? nextSequence++,\n theme: themeName,\n signal,\n onThemeChange,\n });\n if (!existing) {\n signal.addEventListener(\n \"abort\",\n () => {\n scopes.delete(id);\n if (explicitOwner === id) explicitOwner = undefined;\n schedule();\n },\n { once: true },\n );\n }\n schedule();\n },\n activate(id: symbol, themeName: ThemeName) {\n const scope = scopes.get(id);\n if (scope) scope.theme = themeName;\n explicitOwner = id;\n const ownerDepth = scope?.depth;\n for (const [scopeId, registered] of scopes) {\n if (scopeId !== id && registered.depth === ownerDepth) {\n registered.theme = themeName;\n registered.onThemeChange(themeName);\n }\n }\n syncThemeTarget(target(), themeName);\n },\n });\n}\n\nexport function ThemePicker(props: ThemePickerProps): JSX.Element {\n const activeTheme = theme();\n const { themes = activeTheme.themes, label = \"Theme\", ...rest } = props;\n const currentTheme = activeTheme.theme();\n\n return (\n <select\n {...rest}\n aria-label={rest[\"aria-label\"] ?? label}\n data-slot=\"theme-picker\"\n value={currentTheme}\n onChange={(event: Event) => {\n const target = getThemePickerTarget(event);\n if (target) {\n activeTheme.setTheme(target.value as ThemeName);\n }\n }}\n >\n {themes.map((option) => (\n <option key={option.value} value={option.value} selected={option.value === currentTheme}>\n {option.label}\n </option>\n ))}\n </select>\n );\n}\n\nfunction getThemePickerTarget(event: Event): HTMLSelectElement | null {\n if (typeof HTMLSelectElement === \"undefined\") {\n return null;\n }\n\n const path = typeof event.composedPath === \"function\" ? event.composedPath() : [];\n const candidates = [event.target, event.currentTarget, ...path];\n\n for (const candidate of candidates) {\n if (candidate instanceof HTMLSelectElement) {\n return candidate;\n }\n }\n\n return null;\n}\n\nexport function ThemeToggle(props: ThemeToggleProps): JSX.Element {\n const activeTheme = theme();\n const {\n children,\n lightIcon,\n darkIcon,\n systemIcon,\n themes = [\"light\", \"dark\"],\n onPress,\n ...rest\n } = props;\n\n const currentTheme = activeTheme.theme();\n const nextTheme = getNextTheme(currentTheme, themes, activeTheme.resolvedSystemTheme());\n const renderContext = { theme: currentTheme, nextTheme };\n const ariaLabel = (rest as Record<string, unknown>)[\"aria-label\"];\n const themedIcon = resolveThemeToggleIcon(currentTheme, nextTheme, {\n lightIcon,\n darkIcon,\n systemIcon,\n });\n const renderedIcon = cloneThemeToggleIcon(themedIcon, currentTheme);\n const renderedIconSlots =\n renderThemeToggleIconSlots(currentTheme, {\n lightIcon,\n darkIcon,\n systemIcon,\n }) ?? renderedIcon;\n const content =\n typeof children === \"function\" ? children(renderContext) : (children ?? renderedIconSlots);\n\n return (\n <Button\n {...(rest as ButtonNativeProps)}\n aria-label={typeof ariaLabel === \"string\" ? ariaLabel : `Switch to ${nextTheme} theme`}\n data-theme-control=\"toggle\"\n data-theme-choice={currentTheme}\n data-next-theme={nextTheme}\n onPress={(event) => {\n onPress?.(event);\n if (!event.defaultPrevented && !Object.is(nextTheme, currentTheme)) {\n activeTheme.setTheme(nextTheme);\n }\n }}\n >\n <span data-slot=\"theme-toggle-content\">{content}</span>\n </Button>\n );\n}\n\nfunction getNextTheme(\n currentTheme: ThemeName,\n themes: readonly ThemeName[],\n resolvedSystemTheme: \"light\" | \"dark\" = \"light\",\n): ThemeName {\n if (themes.length === 0) return currentTheme;\n const index = themes.indexOf(currentTheme);\n if (index < 0 && currentTheme === \"system\") {\n if (themes.includes(\"light\") && themes.includes(\"dark\")) {\n return resolvedSystemTheme === \"dark\" ? \"light\" : \"dark\";\n }\n }\n return themes[index >= 0 && index < themes.length - 1 ? index + 1 : 0]!;\n}\n\nfunction getThemeIcon(\n theme: ThemeName,\n icons: Pick<ThemeToggleProps, \"lightIcon\" | \"darkIcon\" | \"systemIcon\">,\n): unknown {\n if (theme === \"light\") return icons.lightIcon;\n if (theme === \"dark\") return icons.darkIcon;\n if (theme === \"system\") return icons.systemIcon;\n return undefined;\n}\n\nexport function resolveThemeToggleIcon(\n theme: ThemeName,\n nextTheme: ThemeName,\n icons: Pick<ThemeToggleProps, \"lightIcon\" | \"darkIcon\" | \"systemIcon\">,\n): unknown {\n return getThemeIcon(theme, icons) ?? getThemeIcon(nextTheme, icons);\n}\n\nfunction renderThemeToggleIconSlots(\n theme: ThemeName,\n icons: Pick<ThemeToggleProps, \"lightIcon\" | \"darkIcon\" | \"systemIcon\">,\n): unknown {\n if (getThemeIcon(theme, icons) === undefined) {\n return undefined;\n }\n\n const slots = [\n [\"light\", icons.lightIcon],\n [\"dark\", icons.darkIcon],\n [\"system\", icons.systemIcon],\n ] as const;\n const availableSlots = slots.filter(([, icon]) => icon !== undefined && icon !== null);\n\n if (availableSlots.length <= 1) {\n return undefined;\n }\n\n return availableSlots.map(([slotTheme, icon]) => (\n <span\n key={slotTheme}\n data-slot=\"theme-toggle-icon\"\n data-theme-toggle-icon={slotTheme}\n hidden={slotTheme === theme ? undefined : true}\n >\n {cloneThemeToggleIcon(icon, slotTheme)}\n </span>\n ));\n}\n\nfunction isJSXElement(value: unknown): value is JSXElement {\n return (\n typeof value === \"object\" &&\n value !== null &&\n \"$$typeof\" in value &&\n \"type\" in value &&\n \"props\" in value\n );\n}\n\nfunction cloneThemeToggleIcon(icon: unknown, key?: string): unknown {\n if (Array.isArray(icon)) {\n const clonedChildren = icon.map((child) => cloneThemeToggleIcon(child));\n if ((icon as unknown as Record<symbol, unknown>)[STATIC_CHILDREN] === true) {\n Object.defineProperty(clonedChildren, STATIC_CHILDREN, {\n value: true,\n configurable: true,\n });\n }\n return clonedChildren;\n }\n\n if (!isJSXElement(icon)) return icon;\n\n const props = icon.props as Record<string, unknown> | undefined;\n const clonedProps = props ? { ...props } : {};\n\n if (\"children\" in clonedProps) {\n clonedProps.children = cloneThemeToggleIcon(clonedProps.children);\n }\n\n const iconKey = (icon.key ?? key ?? null) as string | number | null;\n const clonedIcon = {\n ...icon,\n key: iconKey,\n props: clonedProps,\n };\n\n delete (clonedIcon as Record<symbol, unknown>)[STATIC_CHILD_SLOTS_CACHE];\n return clonedIcon;\n}\n\nfunction syncThemeTarget(\n html: HTMLElement | null,\n themeChoice: ThemeName | null | undefined,\n): void {\n if (!html) return;\n\n if (themeChoice == null) {\n html.removeAttribute(\"data-theme\");\n html.removeAttribute(\"data-theme-choice\");\n return;\n }\n\n html.setAttribute(\"data-theme-choice\", themeChoice);\n\n if (themeChoice === \"system\") {\n html.removeAttribute(\"data-theme\");\n } else {\n html.setAttribute(\"data-theme\", themeChoice);\n }\n}\n\nfunction readStoredTheme(storageKey: string): ThemeName | undefined {\n if (typeof window === \"undefined\") return undefined;\n try {\n const storedTheme = window.localStorage.getItem(storageKey);\n return storedTheme ? (storedTheme as ThemeName) : undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction writeStoredTheme(storageKey: string, theme: ThemeName): void {\n if (typeof window === \"undefined\") return;\n try {\n window.localStorage.setItem(storageKey, theme);\n } catch {\n // Storage can be unavailable in private or locked-down browser contexts.\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"theme.js","names":[],"sources":["../../../src/components/theme/theme.tsx"],"sourcesContent":["import type { JSX } from \"@askrjs/askr/jsx-runtime\";\nimport { defineScope, getSignal, readScope, state } from \"@askrjs/askr\";\nimport { cloneElement, isElement, type JSXElement } from \"@askrjs/askr/foundations/structures\";\nimport { Button } from \"@askrjs/ui\";\nimport type { ButtonNativeProps, PressEvent } from \"@askrjs/ui\";\n\nexport const CAT_THEME_NAMES = [\"tabby\", \"ginger\", \"tuxedo\", \"calico\", \"torty\"] as const;\n\nexport type CatThemeName = (typeof CAT_THEME_NAMES)[number];\nexport type ThemeName = \"light\" | \"dark\" | \"system\" | CatThemeName | (string & {});\n\nexport type ThemeOption = {\n value: ThemeName;\n label: string;\n};\n\nexport type ThemeScopeValue = {\n theme: () => ThemeName;\n resolvedSystemTheme: () => \"light\" | \"dark\";\n setTheme: (theme: ThemeName) => void;\n themes: readonly ThemeOption[];\n storageKey: string;\n};\n\nexport type ThemeScopeProps = {\n children?: unknown;\n defaultTheme?: ThemeName;\n themes?: readonly ThemeOption[];\n storageKey?: string;\n};\n\nexport type ThemePickerProps = Omit<\n JSX.IntrinsicElements[\"select\"],\n \"children\" | \"value\" | \"defaultValue\" | \"onChange\"\n> & {\n themes?: readonly ThemeOption[];\n label?: string;\n};\n\nexport type ThemeToggleRenderContext = {\n theme: ThemeName;\n nextTheme: ThemeName;\n};\n\nexport type ThemeToggleProps = Omit<ButtonNativeProps, \"children\" | \"onPress\"> & {\n children?: unknown | ((context: ThemeToggleRenderContext) => unknown);\n lightIcon?: unknown;\n darkIcon?: unknown;\n systemIcon?: unknown;\n themes?: readonly ThemeName[];\n onPress?: (event: PressEvent) => void;\n};\n\nexport const DEFAULT_THEME_OPTIONS: readonly ThemeOption[] = [\n { value: \"system\", label: \"System\" },\n { value: \"light\", label: \"Light\" },\n { value: \"dark\", label: \"Dark\" },\n];\n\nexport const CAT_THEME_OPTIONS: readonly ThemeOption[] = [\n { value: \"tabby\", label: \"Tabby\" },\n { value: \"ginger\", label: \"Ginger\" },\n { value: \"tuxedo\", label: \"Tuxedo\" },\n { value: \"calico\", label: \"Calico\" },\n { value: \"torty\", label: \"Torty\" },\n];\n\nconst DEFAULT_STORAGE_KEY = \"askr-theme\";\nconst STATIC_CHILDREN = Symbol.for(\"askr.static-children\");\nconst documentThemeCoordinators = new WeakMap<Document, ThemeCoordinator>();\ntype ThemeCoordinator = ReturnType<typeof createThemeCoordinator>;\ntype InternalThemeScopeValue = ThemeScopeValue & {\n readonly coordinator: ThemeCoordinator | null;\n readonly depth: number;\n};\n\nconst ThemeScopeContext = defineScope<InternalThemeScopeValue>({\n theme: () => \"system\",\n resolvedSystemTheme: () => \"light\",\n setTheme: () => undefined,\n themes: DEFAULT_THEME_OPTIONS,\n storageKey: DEFAULT_STORAGE_KEY,\n coordinator: null,\n depth: -1,\n});\n\nexport function theme(): ThemeScopeValue {\n return readScope(ThemeScopeContext);\n}\n\nexport function ThemeScope(props: ThemeScopeProps): JSX.Element {\n const {\n children,\n defaultTheme = \"system\",\n themes = DEFAULT_THEME_OPTIONS,\n storageKey = DEFAULT_STORAGE_KEY,\n } = props;\n\n const scopeId = state<symbol>(Symbol(\"ThemeScope\"))();\n const scopeSignal = getSignal();\n // The first render must be identical on the server and in the browser.\n // Browser persistence is adopted from the committed root ref, after Askr's\n // hydration verifier has accepted the server markup.\n const themeState = state<ThemeName>(defaultTheme);\n const localResolvedSystemTheme = state<\"light\" | \"dark\">(\"light\");\n const persistenceAdoption = state({ complete: false })();\n const currentTheme = themeState();\n const parentScope = readScope(ThemeScopeContext);\n const ownedCoordinator = state<ThemeCoordinator>(getDefaultThemeCoordinator())();\n const coordinator = parentScope.coordinator ?? ownedCoordinator;\n const scopeDepth = parentScope.depth + 1;\n coordinator.register(scopeId, scopeDepth, currentTheme, scopeSignal, (nextTheme) => {\n if (themeState() !== nextTheme) themeState.set(nextTheme);\n });\n\n const setTheme = (nextTheme: ThemeName) => {\n themeState.set(nextTheme);\n writeStoredTheme(storageKey, nextTheme);\n coordinator.activate(scopeId, nextTheme);\n };\n\n const resolvedSystemTheme = parentScope.coordinator\n ? parentScope.resolvedSystemTheme\n : localResolvedSystemTheme;\n const value: InternalThemeScopeValue = {\n theme: themeState,\n resolvedSystemTheme,\n setTheme,\n themes,\n storageKey,\n coordinator,\n depth: scopeDepth,\n };\n\n return (\n <ThemeScopeContext value={value}>\n <div\n data-slot=\"theme-scope\"\n ref={\n parentScope.coordinator === null\n ? (element: HTMLElement | null) => {\n coordinator.attach(\n element,\n (nextTheme) => localResolvedSystemTheme.set(nextTheme),\n scopeSignal,\n );\n if (element && typeof window !== \"undefined\") {\n const onStorage = (event: StorageEvent) => {\n let storageMatches = event.storageArea == null;\n try {\n storageMatches ||= event.storageArea === window.localStorage;\n } catch {\n // Locked-down/private contexts may deny access to localStorage.\n }\n if (!storageMatches || event.key !== storageKey) {\n return;\n }\n const nextTheme = event.newValue as ThemeName | null;\n if (!nextTheme) return;\n themeState.set(nextTheme);\n coordinator.activate(scopeId, nextTheme);\n };\n window.addEventListener(\"storage\", onStorage);\n scopeSignal.addEventListener(\n \"abort\",\n () => window.removeEventListener(\"storage\", onStorage),\n { once: true },\n );\n }\n if (!element || persistenceAdoption.complete) return;\n persistenceAdoption.complete = true;\n const storedTheme = readStoredTheme(storageKey);\n if (storedTheme && storedTheme !== themeState()) {\n themeState.set(storedTheme);\n coordinator.activate(scopeId, storedTheme);\n }\n }\n : undefined\n }\n >\n {children}\n </div>\n </ThemeScopeContext>\n );\n}\n\nfunction getDefaultThemeCoordinator(): ThemeCoordinator {\n if (typeof document === \"undefined\") {\n return createThemeCoordinator();\n }\n\n const existing = documentThemeCoordinators.get(document);\n if (existing) return existing;\n\n const coordinator = createThemeCoordinator();\n documentThemeCoordinators.set(document, coordinator);\n return coordinator;\n}\n\nfunction removeEmptyGeneratedStyleRegistries(root: Node | null): void {\n const ownerDocument =\n root?.nodeType === 9\n ? (root as Document)\n : (root?.ownerDocument ?? (typeof document === \"undefined\" ? null : document));\n if (!ownerDocument) return;\n\n for (const registry of ownerDocument.querySelectorAll<HTMLStyleElement>(\n \"style[data-askr-style-registry]\",\n )) {\n if (!registry.textContent?.trim()) registry.remove();\n }\n}\n\nfunction createThemeCoordinator() {\n const scopes = new Map<\n symbol,\n {\n depth: number;\n sequence: number;\n theme: ThemeName;\n signal: AbortSignal;\n onThemeChange: (themeName: ThemeName) => void;\n }\n >();\n let nextSequence = 0;\n let explicitOwner: symbol | undefined;\n let root: Node | null = null;\n let scheduled = false;\n\n const target = (): HTMLElement | null => {\n if (root?.nodeType === 9) return (root as Document).documentElement;\n if (root && \"host\" in root) return (root as ShadowRoot).host as HTMLElement;\n return typeof document === \"undefined\" ? null : document.documentElement;\n };\n const syncActive = (): void => {\n if (explicitOwner !== undefined) return;\n let candidate: { depth: number; sequence: number; theme: ThemeName } | undefined;\n for (const scope of scopes.values()) {\n if (\n !candidate ||\n scope.depth > candidate.depth ||\n (scope.depth === candidate.depth && scope.sequence > candidate.sequence)\n ) {\n candidate = scope;\n }\n }\n if (candidate) syncThemeTarget(target(), candidate.theme);\n };\n const schedule = (): void => {\n if (typeof document === \"undefined\" || scheduled) return;\n scheduled = true;\n setTimeout(() => {\n scheduled = false;\n syncActive();\n if (scopes.size === 0) removeEmptyGeneratedStyleRegistries(root);\n }, 0);\n };\n\n return Object.freeze({\n attach(\n element: HTMLElement | null,\n onResolvedSystemTheme: (themeName: \"light\" | \"dark\") => void,\n signal: AbortSignal,\n ) {\n if (element) root = element.getRootNode();\n if (element && typeof window !== \"undefined\" && typeof window.matchMedia === \"function\") {\n const media = window.matchMedia(\"(prefers-color-scheme: dark)\");\n const update = () => onResolvedSystemTheme(media.matches ? \"dark\" : \"light\");\n update();\n media.addEventListener?.(\"change\", update);\n signal.addEventListener(\"abort\", () => media.removeEventListener?.(\"change\", update), {\n once: true,\n });\n }\n schedule();\n },\n register(\n id: symbol,\n depth: number,\n themeName: ThemeName,\n signal: AbortSignal,\n onThemeChange: (themeName: ThemeName) => void,\n ) {\n const existing = scopes.get(id);\n scopes.set(id, {\n depth,\n sequence: existing?.sequence ?? nextSequence++,\n theme: themeName,\n signal,\n onThemeChange,\n });\n if (!existing) {\n signal.addEventListener(\n \"abort\",\n () => {\n scopes.delete(id);\n if (explicitOwner === id) explicitOwner = undefined;\n schedule();\n },\n { once: true },\n );\n }\n schedule();\n },\n activate(id: symbol, themeName: ThemeName) {\n const scope = scopes.get(id);\n if (scope) scope.theme = themeName;\n explicitOwner = id;\n const ownerDepth = scope?.depth;\n for (const [scopeId, registered] of scopes) {\n if (scopeId !== id && registered.depth === ownerDepth) {\n registered.theme = themeName;\n registered.onThemeChange(themeName);\n }\n }\n syncThemeTarget(target(), themeName);\n },\n });\n}\n\nexport function ThemePicker(props: ThemePickerProps): JSX.Element {\n const activeTheme = theme();\n const { themes = activeTheme.themes, label = \"Theme\", ...rest } = props;\n const currentTheme = activeTheme.theme();\n\n return (\n <select\n {...rest}\n aria-label={rest[\"aria-label\"] ?? label}\n data-slot=\"theme-picker\"\n value={currentTheme}\n onChange={(event: Event) => {\n const target = getThemePickerTarget(event);\n if (target) {\n activeTheme.setTheme(target.value as ThemeName);\n }\n }}\n >\n {themes.map((option) => (\n <option key={option.value} value={option.value} selected={option.value === currentTheme}>\n {option.label}\n </option>\n ))}\n </select>\n );\n}\n\nfunction getThemePickerTarget(event: Event): HTMLSelectElement | null {\n if (typeof HTMLSelectElement === \"undefined\") {\n return null;\n }\n\n const path = typeof event.composedPath === \"function\" ? event.composedPath() : [];\n const candidates = [event.target, event.currentTarget, ...path];\n\n for (const candidate of candidates) {\n if (candidate instanceof HTMLSelectElement) {\n return candidate;\n }\n }\n\n return null;\n}\n\nexport function ThemeToggle(props: ThemeToggleProps): JSX.Element {\n const activeTheme = theme();\n const {\n children,\n lightIcon,\n darkIcon,\n systemIcon,\n themes = [\"light\", \"dark\"],\n onPress,\n ...rest\n } = props;\n\n const currentTheme = activeTheme.theme();\n const nextTheme = getNextTheme(currentTheme, themes, activeTheme.resolvedSystemTheme());\n const renderContext = { theme: currentTheme, nextTheme };\n const ariaLabel = (rest as Record<string, unknown>)[\"aria-label\"];\n const themedIcon = resolveThemeToggleIcon(currentTheme, nextTheme, {\n lightIcon,\n darkIcon,\n systemIcon,\n });\n const renderedIcon = cloneThemeToggleIcon(themedIcon, currentTheme);\n const renderedIconSlots =\n renderThemeToggleIconSlots(currentTheme, {\n lightIcon,\n darkIcon,\n systemIcon,\n }) ?? renderedIcon;\n const content =\n typeof children === \"function\" ? children(renderContext) : (children ?? renderedIconSlots);\n\n return (\n <Button\n {...(rest as ButtonNativeProps)}\n aria-label={typeof ariaLabel === \"string\" ? ariaLabel : `Switch to ${nextTheme} theme`}\n data-theme-control=\"toggle\"\n data-theme-choice={currentTheme}\n data-next-theme={nextTheme}\n onPress={(event) => {\n onPress?.(event);\n if (!event.defaultPrevented && !Object.is(nextTheme, currentTheme)) {\n activeTheme.setTheme(nextTheme);\n }\n }}\n >\n <span data-slot=\"theme-toggle-content\">{content}</span>\n </Button>\n );\n}\n\nfunction getNextTheme(\n currentTheme: ThemeName,\n themes: readonly ThemeName[],\n resolvedSystemTheme: \"light\" | \"dark\" = \"light\",\n): ThemeName {\n if (themes.length === 0) return currentTheme;\n const index = themes.indexOf(currentTheme);\n if (index < 0 && currentTheme === \"system\") {\n if (themes.includes(\"light\") && themes.includes(\"dark\")) {\n return resolvedSystemTheme === \"dark\" ? \"light\" : \"dark\";\n }\n }\n return themes[index >= 0 && index < themes.length - 1 ? index + 1 : 0]!;\n}\n\nfunction getThemeIcon(\n theme: ThemeName,\n icons: Pick<ThemeToggleProps, \"lightIcon\" | \"darkIcon\" | \"systemIcon\">,\n): unknown {\n if (theme === \"light\") return icons.lightIcon;\n if (theme === \"dark\") return icons.darkIcon;\n if (theme === \"system\") return icons.systemIcon;\n return undefined;\n}\n\nexport function resolveThemeToggleIcon(\n theme: ThemeName,\n nextTheme: ThemeName,\n icons: Pick<ThemeToggleProps, \"lightIcon\" | \"darkIcon\" | \"systemIcon\">,\n): unknown {\n return getThemeIcon(theme, icons) ?? getThemeIcon(nextTheme, icons);\n}\n\nfunction renderThemeToggleIconSlots(\n theme: ThemeName,\n icons: Pick<ThemeToggleProps, \"lightIcon\" | \"darkIcon\" | \"systemIcon\">,\n): unknown {\n if (getThemeIcon(theme, icons) === undefined) {\n return undefined;\n }\n\n const slots = [\n [\"light\", icons.lightIcon],\n [\"dark\", icons.darkIcon],\n [\"system\", icons.systemIcon],\n ] as const;\n const availableSlots = slots.filter(([, icon]) => icon !== undefined && icon !== null);\n\n if (availableSlots.length <= 1) {\n return undefined;\n }\n\n return availableSlots.map(([slotTheme, icon]) => (\n <span\n key={slotTheme}\n data-slot=\"theme-toggle-icon\"\n data-theme-toggle-icon={slotTheme}\n hidden={slotTheme === theme ? undefined : true}\n >\n {cloneThemeToggleIcon(icon, slotTheme)}\n </span>\n ));\n}\n\nfunction cloneThemeToggleIcon(icon: unknown, key?: string): unknown {\n if (Array.isArray(icon)) {\n const clonedChildren = icon.map((child) => cloneThemeToggleIcon(child));\n if ((icon as unknown as Record<symbol, unknown>)[STATIC_CHILDREN] === true) {\n Object.defineProperty(clonedChildren, STATIC_CHILDREN, {\n value: true,\n configurable: true,\n });\n }\n return clonedChildren;\n }\n\n if (!isElement(icon)) return icon;\n\n const props = icon.props as Record<string, unknown> | undefined;\n const clonedProps = props ? { ...props } : {};\n\n if (\"children\" in clonedProps) {\n clonedProps.children = cloneThemeToggleIcon(clonedProps.children);\n }\n\n const clonedIcon = cloneElement(icon, clonedProps);\n return {\n ...clonedIcon,\n key: icon.key ?? key ?? null,\n } satisfies JSXElement;\n}\n\nfunction syncThemeTarget(\n html: HTMLElement | null,\n themeChoice: ThemeName | null | undefined,\n): void {\n if (!html) return;\n\n if (themeChoice == null) {\n html.removeAttribute(\"data-theme\");\n html.removeAttribute(\"data-theme-choice\");\n return;\n }\n\n html.setAttribute(\"data-theme-choice\", themeChoice);\n\n if (themeChoice === \"system\") {\n html.removeAttribute(\"data-theme\");\n } else {\n html.setAttribute(\"data-theme\", themeChoice);\n }\n}\n\nfunction readStoredTheme(storageKey: string): ThemeName | undefined {\n if (typeof window === \"undefined\") return undefined;\n try {\n const storedTheme = window.localStorage.getItem(storageKey);\n return storedTheme ? (storedTheme as ThemeName) : undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction writeStoredTheme(storageKey: string, theme: ThemeName): void {\n if (typeof window === \"undefined\") return;\n try {\n window.localStorage.setItem(storageKey, theme);\n } catch {\n // Storage can be unavailable in private or locked-down browser contexts.\n }\n}\n"],"mappings":"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"}
|
package/dist/ssr.js
CHANGED
|
@@ -1,12 +1,19 @@
|
|
|
1
|
-
import { styleRulesForHtml } from "./components/_internal/style.js";
|
|
2
1
|
//#region src/ssr.ts
|
|
3
2
|
const STYLE_REGISTRY_ATTR = "data-askr-style-registry";
|
|
3
|
+
const STYLE_CLASS_PREFIX = "ak-style-";
|
|
4
|
+
const CLASS_ATTRIBUTE_PATTERN = /\sclass=(?:"([^"]*)"|'([^']*)')/g;
|
|
5
|
+
const MAX_STYLE_RULES = 512;
|
|
4
6
|
function escapeHtmlAttribute(value) {
|
|
5
7
|
return value.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
6
8
|
}
|
|
7
9
|
function escapeStyleRawText(value) {
|
|
8
10
|
return value.replace(/<\/style/gi, "<\\/style");
|
|
9
11
|
}
|
|
12
|
+
function generatedStyleClasses(html) {
|
|
13
|
+
const classes = /* @__PURE__ */ new Set();
|
|
14
|
+
for (const attribute of html.matchAll(CLASS_ATTRIBUTE_PATTERN)) for (const className of (attribute[1] ?? attribute[2] ?? "").split(/\s+/)) if (className.startsWith(STYLE_CLASS_PREFIX)) classes.add(className);
|
|
15
|
+
return classes;
|
|
16
|
+
}
|
|
10
17
|
function findHeadEnd(html) {
|
|
11
18
|
let index = 0;
|
|
12
19
|
while (index < html.length) {
|
|
@@ -44,16 +51,21 @@ function withThemeStyles(documentRenderer) {
|
|
|
44
51
|
return (args) => {
|
|
45
52
|
const documentHtml = documentRenderer(args);
|
|
46
53
|
const registeredStyles = args.context.styles;
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
54
|
+
const generatedClasses = generatedStyleClasses(documentHtml);
|
|
55
|
+
if (registeredStyles === void 0) {
|
|
56
|
+
if (generatedClasses.size > 0) throw new Error("Generated theme classes require request-local SSR style registrations from @askrjs/askr.");
|
|
57
|
+
return documentHtml;
|
|
58
|
+
}
|
|
59
|
+
const styles = /* @__PURE__ */ new Map();
|
|
60
|
+
for (const style of registeredStyles) {
|
|
61
|
+
if (!style.id || !style.cssText) throw new TypeError("Invalid SSR theme style registration.");
|
|
62
|
+
const existing = styles.get(style.id);
|
|
63
|
+
if (existing !== void 0 && existing !== style.cssText) throw new RangeError(`SSR style registration collision for ${JSON.stringify(style.id)}.`);
|
|
64
|
+
styles.set(style.id, style.cssText);
|
|
65
|
+
if (styles.size > MAX_STYLE_RULES) throw new RangeError("Theme style registry capacity exceeded.");
|
|
66
|
+
}
|
|
67
|
+
for (const className of generatedClasses) if (!styles.has(className)) throw new Error(`Missing request-local SSR style registration for ${JSON.stringify(className)}.`);
|
|
68
|
+
const rules = Array.from(styles.values());
|
|
57
69
|
if (rules.length === 0) return documentHtml;
|
|
58
70
|
const nonce = args.context.cspNonce === void 0 ? "" : ` nonce="${escapeHtmlAttribute(args.context.cspNonce)}"`;
|
|
59
71
|
return injectIntoHead(documentHtml, `<style ${STYLE_REGISTRY_ATTR}="true"${nonce}>${escapeStyleRawText(rules.join("\n"))}\n</style>`);
|
package/dist/ssr.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ssr.js","names":[],"sources":["../src/ssr.ts"],"sourcesContent":["
|
|
1
|
+
{"version":3,"file":"ssr.js","names":[],"sources":["../src/ssr.ts"],"sourcesContent":["const STYLE_REGISTRY_ATTR = \"data-askr-style-registry\";\nconst STYLE_CLASS_PREFIX = \"ak-style-\";\nconst CLASS_ATTRIBUTE_PATTERN = /\\sclass=(?:\"([^\"]*)\"|'([^']*)')/g;\nconst MAX_STYLE_RULES = 512;\n\ntype DocumentRenderArgsLike = {\n appHtml: string;\n context: {\n cspNonce?: string;\n styles?: readonly { id: string; cssText: string }[];\n };\n};\n\nfunction escapeHtmlAttribute(value: string): string {\n return value\n .replace(/&/g, \"&\")\n .replace(/\"/g, \""\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\");\n}\n\nfunction escapeStyleRawText(value: string): string {\n return value.replace(/<\\/style/gi, \"<\\\\/style\");\n}\n\nfunction generatedStyleClasses(html: string): Set<string> {\n const classes = new Set<string>();\n for (const attribute of html.matchAll(CLASS_ATTRIBUTE_PATTERN)) {\n for (const className of (attribute[1] ?? attribute[2] ?? \"\").split(/\\s+/)) {\n if (className.startsWith(STYLE_CLASS_PREFIX)) classes.add(className);\n }\n }\n return classes;\n}\n\nfunction findHeadEnd(html: string): number {\n let index = 0;\n while (index < html.length) {\n if (html.startsWith(\"<!--\", index)) {\n const commentEnd = html.indexOf(\"-->\", index + 4);\n index = commentEnd < 0 ? html.length : commentEnd + 3;\n continue;\n }\n\n const rawTextStart = html.slice(index).match(/^<\\s*(script|style|title|textarea)(?:\\s|>)/i);\n if (rawTextStart) {\n const rawTextEndPattern = new RegExp(`</\\\\s*${rawTextStart[1]}\\\\s*>`, \"ig\");\n rawTextEndPattern.lastIndex = index + rawTextStart[0].length;\n const rawTextEnd = rawTextEndPattern.exec(html);\n if (!rawTextEnd) return -1;\n index = rawTextEnd.index + rawTextEnd[0].length;\n continue;\n }\n\n const headEnd = html.slice(index).match(/^<\\/\\s*head\\s*>/i);\n if (headEnd) return index;\n index += 1;\n }\n return -1;\n}\n\nfunction injectIntoHead(html: string, content: string): string {\n const headEnd = findHeadEnd(html);\n if (headEnd >= 0) {\n return `${html.slice(0, headEnd)}${content}${html.slice(headEnd)}`;\n }\n\n const bodyStart = html.search(/<body(?:\\s[^>]*)?>/i);\n if (bodyStart >= 0) {\n return `${html.slice(0, bodyStart)}<head>${content}</head>${html.slice(bodyStart)}`;\n }\n\n return `${content}${html}`;\n}\n\n/**\n * Wrap an Askr SSR/SSG document renderer so generated theme rules used by the\n * rendered app are available before hydration.\n */\nexport function withThemeStyles<TArgs extends DocumentRenderArgsLike>(\n documentRenderer: (args: TArgs) => string,\n): (args: TArgs) => string {\n return (args) => {\n const documentHtml = documentRenderer(args);\n const registeredStyles = args.context.styles;\n const generatedClasses = generatedStyleClasses(documentHtml);\n if (registeredStyles === undefined) {\n if (generatedClasses.size > 0) {\n throw new Error(\n \"Generated theme classes require request-local SSR style registrations from @askrjs/askr.\",\n );\n }\n return documentHtml;\n }\n\n const styles = new Map<string, string>();\n for (const style of registeredStyles) {\n if (!style.id || !style.cssText) {\n throw new TypeError(\"Invalid SSR theme style registration.\");\n }\n const existing = styles.get(style.id);\n if (existing !== undefined && existing !== style.cssText) {\n throw new RangeError(`SSR style registration collision for ${JSON.stringify(style.id)}.`);\n }\n styles.set(style.id, style.cssText);\n if (styles.size > MAX_STYLE_RULES) {\n throw new RangeError(\"Theme style registry capacity exceeded.\");\n }\n }\n for (const className of generatedClasses) {\n if (!styles.has(className)) {\n throw new Error(\n `Missing request-local SSR style registration for ${JSON.stringify(className)}.`,\n );\n }\n }\n const rules = Array.from(styles.values());\n if (rules.length === 0) return documentHtml;\n\n const nonce =\n args.context.cspNonce === undefined\n ? \"\"\n : ` nonce=\"${escapeHtmlAttribute(args.context.cspNonce)}\"`;\n const registry = `<style ${STYLE_REGISTRY_ATTR}=\"true\"${nonce}>${escapeStyleRawText(rules.join(\"\\n\"))}\\n</style>`;\n return injectIntoHead(documentHtml, registry);\n };\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@askrjs/themes",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.20",
|
|
4
4
|
"description": "Default theme tokens, styles, and component presets for Askr apps.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"askr",
|
|
@@ -332,8 +332,8 @@
|
|
|
332
332
|
"test:checks": "vp test run -c vitest.test.checks.config.ts"
|
|
333
333
|
},
|
|
334
334
|
"devDependencies": {
|
|
335
|
-
"@askrjs/askr": ">=0.0.
|
|
336
|
-
"@askrjs/ui": ">=0.0.
|
|
335
|
+
"@askrjs/askr": ">=0.0.85 <0.1.0",
|
|
336
|
+
"@askrjs/ui": ">=0.0.24 <0.1.0",
|
|
337
337
|
"@askrjs/vite": ">=0.0.5 <0.1.0",
|
|
338
338
|
"@tsdown/css": "0.22.8",
|
|
339
339
|
"@types/node": "^26.0.0",
|
|
@@ -346,11 +346,11 @@
|
|
|
346
346
|
"vite-plus": "^0.2.4"
|
|
347
347
|
},
|
|
348
348
|
"peerDependencies": {
|
|
349
|
-
"@askrjs/askr": ">=0.0.
|
|
350
|
-
"@askrjs/ui": ">=0.0.
|
|
349
|
+
"@askrjs/askr": ">=0.0.85 <0.1.0",
|
|
350
|
+
"@askrjs/ui": ">=0.0.24 <0.1.0"
|
|
351
351
|
},
|
|
352
352
|
"engines": {
|
|
353
|
-
"node": ">=
|
|
353
|
+
"node": ">=24.15.0"
|
|
354
354
|
},
|
|
355
355
|
"packageManager": "npm@11.17.0"
|
|
356
356
|
}
|
|
@@ -200,7 +200,7 @@ function resolveTokenValue(
|
|
|
200
200
|
return tokens[trimmed] ?? trimmed;
|
|
201
201
|
}
|
|
202
202
|
|
|
203
|
-
function resolveSpaceValue(value: string | number): string | number {
|
|
203
|
+
export function resolveSpaceValue(value: string | number): string | number {
|
|
204
204
|
return resolveTokenValue(value, SPACE_TOKEN_MAP);
|
|
205
205
|
}
|
|
206
206
|
|
|
@@ -243,7 +243,7 @@ function resolveShadowValue(value: BlockShadow): string | undefined {
|
|
|
243
243
|
return String(resolveTokenValue(value, SHADOW_TOKEN_MAP));
|
|
244
244
|
}
|
|
245
245
|
|
|
246
|
-
function setResponsiveVar<T>(
|
|
246
|
+
export function setResponsiveVar<T>(
|
|
247
247
|
styles: Record<string, string | number>,
|
|
248
248
|
property: string,
|
|
249
249
|
value: ResponsiveValue<T> | undefined,
|
|
@@ -176,7 +176,7 @@ export function mergeCssVar(style: unknown, name: string, value: string): string
|
|
|
176
176
|
const STYLE_REGISTRY_ATTR = "data-askr-style-registry";
|
|
177
177
|
const STYLE_CLASS_PREFIX = "ak-style-";
|
|
178
178
|
const MAX_STYLE_RULES = 512;
|
|
179
|
-
const
|
|
179
|
+
const MAX_COLLISION_CACHE = 4096;
|
|
180
180
|
|
|
181
181
|
type StyleRule = {
|
|
182
182
|
className: string;
|
|
@@ -184,7 +184,7 @@ type StyleRule = {
|
|
|
184
184
|
rule: string;
|
|
185
185
|
};
|
|
186
186
|
|
|
187
|
-
const
|
|
187
|
+
const styleCollisionCache = new Map<string, StyleRule>();
|
|
188
188
|
type StyleRegistry = {
|
|
189
189
|
element: HTMLStyleElement;
|
|
190
190
|
ruleCount: number;
|
|
@@ -221,7 +221,7 @@ function escapeStyleRawText(value: string): string {
|
|
|
221
221
|
|
|
222
222
|
function styleRuleFor(declarations: string): StyleRule {
|
|
223
223
|
const className = styleClassName(declarations);
|
|
224
|
-
const existing =
|
|
224
|
+
const existing = styleCollisionCache.get(className);
|
|
225
225
|
if (existing) {
|
|
226
226
|
if (existing.declarations !== declarations) {
|
|
227
227
|
throw new RangeError("Theme style class collision detected.");
|
|
@@ -238,12 +238,12 @@ function styleRuleFor(declarations: string): StyleRule {
|
|
|
238
238
|
}
|
|
239
239
|
|
|
240
240
|
function rememberStyleRule(entry: StyleRule): void {
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
while (
|
|
244
|
-
const oldest =
|
|
241
|
+
styleCollisionCache.delete(entry.className);
|
|
242
|
+
styleCollisionCache.set(entry.className, entry);
|
|
243
|
+
while (styleCollisionCache.size > MAX_COLLISION_CACHE) {
|
|
244
|
+
const oldest = styleCollisionCache.keys().next().value;
|
|
245
245
|
if (oldest === undefined) break;
|
|
246
|
-
|
|
246
|
+
styleCollisionCache.delete(oldest);
|
|
247
247
|
}
|
|
248
248
|
}
|
|
249
249
|
|
|
@@ -327,21 +327,3 @@ export function styleDeclarationsToClass(declarations: string | undefined): stri
|
|
|
327
327
|
|
|
328
328
|
return entry.className;
|
|
329
329
|
}
|
|
330
|
-
|
|
331
|
-
export function styleRulesForHtml(html: string): string[] {
|
|
332
|
-
const rules = new Map<string, string>();
|
|
333
|
-
const classAttributePattern = /\sclass=(?:"([^"]*)"|'([^']*)')/g;
|
|
334
|
-
|
|
335
|
-
for (const attribute of html.matchAll(classAttributePattern)) {
|
|
336
|
-
const value = attribute[1] ?? attribute[2] ?? "";
|
|
337
|
-
for (const className of value.split(/\s+/)) {
|
|
338
|
-
const entry = styleRulesByClass.get(className);
|
|
339
|
-
if (entry) rules.set(className, entry.rule);
|
|
340
|
-
if (rules.size > MAX_STYLE_RULES) {
|
|
341
|
-
throw new RangeError("Theme style registry capacity exceeded.");
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
return Array.from(rules.values());
|
|
347
|
-
}
|
|
@@ -1,46 +1,13 @@
|
|
|
1
1
|
import type { JSX } from "@askrjs/askr/jsx-runtime";
|
|
2
2
|
import { classes } from "../_internal/classes";
|
|
3
|
-
import { mergeLayoutStyles } from "../_internal/block-layout";
|
|
3
|
+
import { mergeLayoutStyles, resolveSpaceValue, setResponsiveVar } from "../_internal/block-layout";
|
|
4
4
|
import { mergeProps } from "../_internal/merge-props";
|
|
5
5
|
import { intrinsicElement } from "../_internal/jsx";
|
|
6
6
|
import { styleDeclarationsToClass } from "../_internal/style";
|
|
7
|
-
import type { BlockSpace
|
|
7
|
+
import type { BlockSpace } from "../_internal/block-layout";
|
|
8
8
|
import type { GridAlign, GridColumns, GridElement, GridProps } from "./grid.types";
|
|
9
9
|
|
|
10
10
|
const DEFAULT_ELEMENT = "div";
|
|
11
|
-
const BREAKPOINTS: readonly LayoutBreakpoint[] = ["base", "sm", "md", "lg", "xl"];
|
|
12
|
-
|
|
13
|
-
const SPACE_TOKEN_MAP: Record<BlockSpace, string> = {
|
|
14
|
-
"0": "0",
|
|
15
|
-
xs: "var(--ak-space-xs)",
|
|
16
|
-
sm: "var(--ak-space-sm)",
|
|
17
|
-
md: "var(--ak-space-md)",
|
|
18
|
-
lg: "var(--ak-space-lg)",
|
|
19
|
-
xl: "var(--ak-space-xl)",
|
|
20
|
-
"2xl": "var(--ak-space-2xl)",
|
|
21
|
-
"3xl": "var(--ak-space-3xl)",
|
|
22
|
-
page: "var(--ak-layout-page-gutter)",
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
function isResponsiveValue<T>(
|
|
26
|
-
value: ResponsiveValue<T> | undefined,
|
|
27
|
-
): value is Partial<Record<LayoutBreakpoint, T>> {
|
|
28
|
-
return Boolean(
|
|
29
|
-
value &&
|
|
30
|
-
typeof value === "object" &&
|
|
31
|
-
!Array.isArray(value) &&
|
|
32
|
-
BREAKPOINTS.some((breakpoint) => breakpoint in value),
|
|
33
|
-
);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function normalizeResponsiveValue<T>(
|
|
37
|
-
value: ResponsiveValue<T> | undefined,
|
|
38
|
-
): Partial<Record<LayoutBreakpoint, T>> | undefined {
|
|
39
|
-
if (value === undefined) return undefined;
|
|
40
|
-
if (isResponsiveValue(value)) return value;
|
|
41
|
-
return { base: value };
|
|
42
|
-
}
|
|
43
|
-
|
|
44
11
|
function resolveColumns(value: GridColumns): string {
|
|
45
12
|
if (typeof value === "number") {
|
|
46
13
|
return `repeat(${value}, minmax(0, 1fr))`;
|
|
@@ -51,7 +18,7 @@ function resolveColumns(value: GridColumns): string {
|
|
|
51
18
|
}
|
|
52
19
|
|
|
53
20
|
function resolveGap(value: BlockSpace): string {
|
|
54
|
-
return
|
|
21
|
+
return String(resolveSpaceValue(value));
|
|
55
22
|
}
|
|
56
23
|
|
|
57
24
|
function resolveAlign(value: GridAlign): string {
|
|
@@ -60,24 +27,6 @@ function resolveAlign(value: GridAlign): string {
|
|
|
60
27
|
return value;
|
|
61
28
|
}
|
|
62
29
|
|
|
63
|
-
function setResponsiveVar<T>(
|
|
64
|
-
styles: Record<string, string | number>,
|
|
65
|
-
property: string,
|
|
66
|
-
value: ResponsiveValue<T> | undefined,
|
|
67
|
-
resolve: (value: T) => string | number | undefined,
|
|
68
|
-
) {
|
|
69
|
-
const normalized = normalizeResponsiveValue(value);
|
|
70
|
-
if (!normalized) return;
|
|
71
|
-
|
|
72
|
-
for (const breakpoint of BREAKPOINTS) {
|
|
73
|
-
const breakpointValue = normalized[breakpoint];
|
|
74
|
-
if (breakpointValue === undefined) continue;
|
|
75
|
-
const resolved = resolve(breakpointValue);
|
|
76
|
-
if (resolved === undefined || resolved === "") continue;
|
|
77
|
-
styles[`--ak-grid-${property}-${breakpoint}`] = resolved;
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
30
|
export function Grid<TElement extends GridElement = "div">(
|
|
82
31
|
props: GridProps<TElement>,
|
|
83
32
|
): JSX.Element {
|
|
@@ -99,9 +48,9 @@ export function Grid<TElement extends GridElement = "div">(
|
|
|
99
48
|
};
|
|
100
49
|
|
|
101
50
|
const styles: Record<string, string | number> = {};
|
|
102
|
-
setResponsiveVar(styles, "columns", columns, resolveColumns);
|
|
103
|
-
setResponsiveVar(styles, "gap", gap, resolveGap);
|
|
104
|
-
setResponsiveVar(styles, "align", align, resolveAlign);
|
|
51
|
+
setResponsiveVar(styles, "grid-columns", columns, resolveColumns);
|
|
52
|
+
setResponsiveVar(styles, "grid-gap", gap, resolveGap);
|
|
53
|
+
setResponsiveVar(styles, "grid-align", align, resolveAlign);
|
|
105
54
|
|
|
106
55
|
const generatedClass = styleDeclarationsToClass(mergeLayoutStyles(styles, userStyle));
|
|
107
56
|
const finalProps = mergeProps(rest, {
|
|
@@ -5,11 +5,6 @@ declare global {
|
|
|
5
5
|
interface Element extends JSXElement {
|
|
6
6
|
readonly __askrThemesJsxElementBrand?: never;
|
|
7
7
|
}
|
|
8
|
-
|
|
9
|
-
interface IntrinsicElements {
|
|
10
|
-
// @ts-ignore The Askr source types already provide a compatible fallback index.
|
|
11
|
-
[element: string]: Record<string, unknown>;
|
|
12
|
-
}
|
|
13
8
|
}
|
|
14
9
|
}
|
|
15
10
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { JSX } from "@askrjs/askr/jsx-runtime";
|
|
2
2
|
import { defineScope, getSignal, readScope, state } from "@askrjs/askr";
|
|
3
|
-
import type
|
|
3
|
+
import { cloneElement, isElement, type JSXElement } from "@askrjs/askr/foundations/structures";
|
|
4
4
|
import { Button } from "@askrjs/ui";
|
|
5
5
|
import type { ButtonNativeProps, PressEvent } from "@askrjs/ui";
|
|
6
6
|
|
|
@@ -67,7 +67,6 @@ export const CAT_THEME_OPTIONS: readonly ThemeOption[] = [
|
|
|
67
67
|
|
|
68
68
|
const DEFAULT_STORAGE_KEY = "askr-theme";
|
|
69
69
|
const STATIC_CHILDREN = Symbol.for("askr.static-children");
|
|
70
|
-
const STATIC_CHILD_SLOTS_CACHE = Symbol.for("__askrStaticChildSlots");
|
|
71
70
|
const documentThemeCoordinators = new WeakMap<Document, ThemeCoordinator>();
|
|
72
71
|
type ThemeCoordinator = ReturnType<typeof createThemeCoordinator>;
|
|
73
72
|
type InternalThemeScopeValue = ThemeScopeValue & {
|
|
@@ -198,6 +197,20 @@ function getDefaultThemeCoordinator(): ThemeCoordinator {
|
|
|
198
197
|
return coordinator;
|
|
199
198
|
}
|
|
200
199
|
|
|
200
|
+
function removeEmptyGeneratedStyleRegistries(root: Node | null): void {
|
|
201
|
+
const ownerDocument =
|
|
202
|
+
root?.nodeType === 9
|
|
203
|
+
? (root as Document)
|
|
204
|
+
: (root?.ownerDocument ?? (typeof document === "undefined" ? null : document));
|
|
205
|
+
if (!ownerDocument) return;
|
|
206
|
+
|
|
207
|
+
for (const registry of ownerDocument.querySelectorAll<HTMLStyleElement>(
|
|
208
|
+
"style[data-askr-style-registry]",
|
|
209
|
+
)) {
|
|
210
|
+
if (!registry.textContent?.trim()) registry.remove();
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
201
214
|
function createThemeCoordinator() {
|
|
202
215
|
const scopes = new Map<
|
|
203
216
|
symbol,
|
|
@@ -239,6 +252,7 @@ function createThemeCoordinator() {
|
|
|
239
252
|
setTimeout(() => {
|
|
240
253
|
scheduled = false;
|
|
241
254
|
syncActive();
|
|
255
|
+
if (scopes.size === 0) removeEmptyGeneratedStyleRegistries(root);
|
|
242
256
|
}, 0);
|
|
243
257
|
};
|
|
244
258
|
|
|
@@ -462,16 +476,6 @@ function renderThemeToggleIconSlots(
|
|
|
462
476
|
));
|
|
463
477
|
}
|
|
464
478
|
|
|
465
|
-
function isJSXElement(value: unknown): value is JSXElement {
|
|
466
|
-
return (
|
|
467
|
-
typeof value === "object" &&
|
|
468
|
-
value !== null &&
|
|
469
|
-
"$$typeof" in value &&
|
|
470
|
-
"type" in value &&
|
|
471
|
-
"props" in value
|
|
472
|
-
);
|
|
473
|
-
}
|
|
474
|
-
|
|
475
479
|
function cloneThemeToggleIcon(icon: unknown, key?: string): unknown {
|
|
476
480
|
if (Array.isArray(icon)) {
|
|
477
481
|
const clonedChildren = icon.map((child) => cloneThemeToggleIcon(child));
|
|
@@ -484,7 +488,7 @@ function cloneThemeToggleIcon(icon: unknown, key?: string): unknown {
|
|
|
484
488
|
return clonedChildren;
|
|
485
489
|
}
|
|
486
490
|
|
|
487
|
-
if (!
|
|
491
|
+
if (!isElement(icon)) return icon;
|
|
488
492
|
|
|
489
493
|
const props = icon.props as Record<string, unknown> | undefined;
|
|
490
494
|
const clonedProps = props ? { ...props } : {};
|
|
@@ -493,15 +497,11 @@ function cloneThemeToggleIcon(icon: unknown, key?: string): unknown {
|
|
|
493
497
|
clonedProps.children = cloneThemeToggleIcon(clonedProps.children);
|
|
494
498
|
}
|
|
495
499
|
|
|
496
|
-
const
|
|
497
|
-
|
|
498
|
-
...
|
|
499
|
-
key:
|
|
500
|
-
|
|
501
|
-
};
|
|
502
|
-
|
|
503
|
-
delete (clonedIcon as Record<symbol, unknown>)[STATIC_CHILD_SLOTS_CACHE];
|
|
504
|
-
return clonedIcon;
|
|
500
|
+
const clonedIcon = cloneElement(icon, clonedProps);
|
|
501
|
+
return {
|
|
502
|
+
...clonedIcon,
|
|
503
|
+
key: icon.key ?? key ?? null,
|
|
504
|
+
} satisfies JSXElement;
|
|
505
505
|
}
|
|
506
506
|
|
|
507
507
|
function syncThemeTarget(
|
package/src/ssr.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { styleRulesForHtml } from "./components/_internal/style";
|
|
2
|
-
|
|
3
1
|
const STYLE_REGISTRY_ATTR = "data-askr-style-registry";
|
|
2
|
+
const STYLE_CLASS_PREFIX = "ak-style-";
|
|
3
|
+
const CLASS_ATTRIBUTE_PATTERN = /\sclass=(?:"([^"]*)"|'([^']*)')/g;
|
|
4
|
+
const MAX_STYLE_RULES = 512;
|
|
4
5
|
|
|
5
6
|
type DocumentRenderArgsLike = {
|
|
6
7
|
appHtml: string;
|
|
@@ -22,6 +23,16 @@ function escapeStyleRawText(value: string): string {
|
|
|
22
23
|
return value.replace(/<\/style/gi, "<\\/style");
|
|
23
24
|
}
|
|
24
25
|
|
|
26
|
+
function generatedStyleClasses(html: string): Set<string> {
|
|
27
|
+
const classes = new Set<string>();
|
|
28
|
+
for (const attribute of html.matchAll(CLASS_ATTRIBUTE_PATTERN)) {
|
|
29
|
+
for (const className of (attribute[1] ?? attribute[2] ?? "").split(/\s+/)) {
|
|
30
|
+
if (className.startsWith(STYLE_CLASS_PREFIX)) classes.add(className);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return classes;
|
|
34
|
+
}
|
|
35
|
+
|
|
25
36
|
function findHeadEnd(html: string): number {
|
|
26
37
|
let index = 0;
|
|
27
38
|
while (index < html.length) {
|
|
@@ -72,24 +83,38 @@ export function withThemeStyles<TArgs extends DocumentRenderArgsLike>(
|
|
|
72
83
|
return (args) => {
|
|
73
84
|
const documentHtml = documentRenderer(args);
|
|
74
85
|
const registeredStyles = args.context.styles;
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
86
|
+
const generatedClasses = generatedStyleClasses(documentHtml);
|
|
87
|
+
if (registeredStyles === undefined) {
|
|
88
|
+
if (generatedClasses.size > 0) {
|
|
89
|
+
throw new Error(
|
|
90
|
+
"Generated theme classes require request-local SSR style registrations from @askrjs/askr.",
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
return documentHtml;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const styles = new Map<string, string>();
|
|
97
|
+
for (const style of registeredStyles) {
|
|
98
|
+
if (!style.id || !style.cssText) {
|
|
99
|
+
throw new TypeError("Invalid SSR theme style registration.");
|
|
100
|
+
}
|
|
101
|
+
const existing = styles.get(style.id);
|
|
102
|
+
if (existing !== undefined && existing !== style.cssText) {
|
|
103
|
+
throw new RangeError(`SSR style registration collision for ${JSON.stringify(style.id)}.`);
|
|
104
|
+
}
|
|
105
|
+
styles.set(style.id, style.cssText);
|
|
106
|
+
if (styles.size > MAX_STYLE_RULES) {
|
|
107
|
+
throw new RangeError("Theme style registry capacity exceeded.");
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
for (const className of generatedClasses) {
|
|
111
|
+
if (!styles.has(className)) {
|
|
112
|
+
throw new Error(
|
|
113
|
+
`Missing request-local SSR style registration for ${JSON.stringify(className)}.`,
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
const rules = Array.from(styles.values());
|
|
93
118
|
if (rules.length === 0) return documentHtml;
|
|
94
119
|
|
|
95
120
|
const nonce =
|