@astryxdesign/core 0.1.4-canary.39ffd21 → 0.1.4-canary.3d64ef2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Banner/Banner.d.ts.map +1 -1
- package/dist/Banner/Banner.js +8 -1
- package/dist/Calendar/Calendar.d.ts.map +1 -1
- package/dist/Calendar/Calendar.js +31 -11
- package/dist/Calendar/styles.d.ts +2 -2
- package/dist/Calendar/styles.d.ts.map +1 -1
- package/dist/Calendar/styles.js +2 -2
- package/dist/Card/Card.d.ts +4 -2
- package/dist/Card/Card.d.ts.map +1 -1
- package/dist/Card/Card.js +15 -7
- package/dist/Carousel/Carousel.d.ts.map +1 -1
- package/dist/Carousel/Carousel.js +1 -0
- package/dist/Chat/ChatToolCalls.d.ts.map +1 -1
- package/dist/Chat/ChatToolCalls.js +8 -1
- package/dist/CheckboxInput/CheckboxInput.d.ts +1 -1
- package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
- package/dist/CheckboxInput/CheckboxInput.js +3 -1
- package/dist/ClickableCard/ClickableCard.d.ts.map +1 -1
- package/dist/ClickableCard/ClickableCard.js +26 -3
- package/dist/CodeBlock/CodeBlock.d.ts +10 -1
- package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
- package/dist/CodeBlock/CodeBlock.js +23 -5
- package/dist/Collapsible/Collapsible.d.ts +9 -3
- package/dist/Collapsible/Collapsible.d.ts.map +1 -1
- package/dist/Collapsible/Collapsible.js +101 -13
- package/dist/Collapsible/CollapsibleGroup.d.ts +39 -7
- package/dist/Collapsible/CollapsibleGroup.d.ts.map +1 -1
- package/dist/Collapsible/CollapsibleGroup.js +70 -9
- package/dist/Collapsible/CollapsibleGroupContext.d.ts +31 -0
- package/dist/Collapsible/CollapsibleGroupContext.d.ts.map +1 -1
- package/dist/Collapsible/CollapsibleGroupContext.js +33 -3
- package/dist/Collapsible/index.d.ts +1 -0
- package/dist/Collapsible/index.d.ts.map +1 -1
- package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
- package/dist/ContextMenu/ContextMenu.js +2 -0
- package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
- package/dist/DateTimeInput/DateTimeInput.js +2 -0
- package/dist/Grid/Grid.d.ts +4 -3
- package/dist/Grid/Grid.d.ts.map +1 -1
- package/dist/Grid/Grid.js +26 -9
- package/dist/Icon/Icon.d.ts.map +1 -1
- package/dist/Icon/Icon.js +5 -1
- package/dist/InputGroup/InputGroup.js +2 -2
- package/dist/Item/Item.js +1 -1
- package/dist/Lightbox/Lightbox.d.ts.map +1 -1
- package/dist/Lightbox/Lightbox.js +26 -2
- package/dist/NumberInput/NumberInput.d.ts.map +1 -1
- package/dist/NumberInput/NumberInput.js +32 -16
- package/dist/Pagination/Pagination.d.ts.map +1 -1
- package/dist/Pagination/Pagination.js +87 -36
- package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
- package/dist/PowerSearch/PowerSearch.js +39 -21
- package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
- package/dist/ProgressBar/ProgressBar.js +2 -1
- package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
- package/dist/Resizable/ResizeHandle.js +10 -2
- package/dist/SideNav/SideNav.d.ts.map +1 -1
- package/dist/SideNav/SideNav.js +6 -2
- package/dist/TabList/TabMenu.d.ts.map +1 -1
- package/dist/TabList/TabMenu.js +34 -7
- package/dist/Table/BaseTable.d.ts.map +1 -1
- package/dist/Table/BaseTable.js +14 -3
- package/dist/Table/plugins/columnResize/useTableColumnResize.d.ts.map +1 -1
- package/dist/Table/plugins/columnResize/useTableColumnResize.js +5 -2
- package/dist/Table/plugins/pagination/paginateData.d.ts.map +1 -1
- package/dist/Table/plugins/pagination/paginateData.js +5 -1
- package/dist/Table/plugins/selection/useTableSelectionState.d.ts.map +1 -1
- package/dist/Table/plugins/selection/useTableSelectionState.js +11 -1
- package/dist/Table/plugins/sortable/useTableSortableState.d.ts.map +1 -1
- package/dist/Table/plugins/sortable/useTableSortableState.js +8 -4
- package/dist/Thumbnail/Thumbnail.d.ts +3 -2
- package/dist/Thumbnail/Thumbnail.d.ts.map +1 -1
- package/dist/Thumbnail/Thumbnail.js +1 -0
- package/dist/TimeInput/TimeInput.d.ts.map +1 -1
- package/dist/TimeInput/TimeInput.js +6 -1
- package/dist/Toast/Toast.d.ts.map +1 -1
- package/dist/Toast/Toast.js +11 -3
- package/dist/Toast/ToastViewport.d.ts.map +1 -1
- package/dist/Toast/ToastViewport.js +12 -0
- package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
- package/dist/Typeahead/BaseTypeahead.js +5 -2
- package/dist/astryx.css +7 -0
- package/dist/astryx.umd.js +48 -48
- package/dist/astryx.umd.js.map +4 -4
- package/dist/hooks/focusableSelector.d.ts +1 -1
- package/dist/hooks/focusableSelector.d.ts.map +1 -1
- package/dist/hooks/focusableSelector.js +1 -1
- package/dist/hooks/useFocusTrap.d.ts +3 -0
- package/dist/hooks/useFocusTrap.d.ts.map +1 -1
- package/dist/hooks/useFocusTrap.js +38 -1
- package/dist/theme/defineTheme.d.ts +1 -1
- package/dist/theme/defineTheme.d.ts.map +1 -1
- package/dist/theme/expandColorScale.d.ts.map +1 -1
- package/dist/theme/expandColorScale.js +10 -3
- package/dist/theme/hct.d.ts +0 -15
- package/dist/theme/hct.d.ts.map +1 -1
- package/dist/theme/hct.js +9 -9
- package/dist/theme/tokens.d.ts +10 -2
- package/dist/theme/tokens.d.ts.map +1 -1
- package/dist/theme/tokens.js +220 -4
- package/dist/utils/color.d.ts +42 -0
- package/dist/utils/color.d.ts.map +1 -0
- package/dist/utils/color.js +150 -0
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +2 -1
- package/dist/utils/timeParser.d.ts.map +1 -1
- package/dist/utils/timeParser.js +8 -5
- package/package.json +1 -1
- package/src/Avatar/Avatar.doc.mjs +11 -0
- package/src/Banner/Banner.doc.mjs +8 -0
- package/src/Banner/Banner.test.tsx +38 -0
- package/src/Banner/Banner.tsx +8 -1
- package/src/Calendar/Calendar.doc.mjs +1 -1
- package/src/Calendar/Calendar.test.tsx +112 -4
- package/src/Calendar/Calendar.tsx +26 -5
- package/src/Calendar/styles.ts +12 -2
- package/src/Card/Card.tsx +16 -6
- package/src/Carousel/Carousel.doc.mjs +12 -12
- package/src/Carousel/Carousel.test.tsx +13 -0
- package/src/Carousel/Carousel.tsx +1 -0
- package/src/Chat/Chat.doc.mjs +1 -1
- package/src/Chat/ChatToolCalls.test.tsx +55 -0
- package/src/Chat/ChatToolCalls.tsx +10 -12
- package/src/CheckboxInput/CheckboxInput.doc.mjs +3 -3
- package/src/CheckboxInput/CheckboxInput.test.tsx +71 -4
- package/src/CheckboxInput/CheckboxInput.tsx +2 -0
- package/src/Citation/Citation.doc.mjs +5 -0
- package/src/ClickableCard/ClickableCard.tsx +45 -2
- package/src/CodeBlock/CodeBlock.doc.mjs +7 -2
- package/src/CodeBlock/CodeBlock.test.tsx +87 -3
- package/src/CodeBlock/CodeBlock.tsx +43 -3
- package/src/Collapsible/Collapsible.doc.mjs +8 -4
- package/src/Collapsible/Collapsible.tsx +86 -10
- package/src/Collapsible/CollapsibleGroup.doc.mjs +27 -3
- package/src/Collapsible/CollapsibleGroup.test.tsx +288 -0
- package/src/Collapsible/CollapsibleGroup.tsx +107 -9
- package/src/Collapsible/CollapsibleGroupContext.tsx +42 -2
- package/src/Collapsible/index.ts +4 -0
- package/src/CommandPalette/CommandPaletteEmpty.doc.mjs +5 -0
- package/src/CommandPalette/CommandPaletteGroup.doc.mjs +10 -0
- package/src/CommandPalette/CommandPaletteInput.doc.mjs +6 -0
- package/src/CommandPalette/CommandPaletteItem.doc.mjs +6 -0
- package/src/CommandPalette/CommandPaletteList.doc.mjs +9 -0
- package/src/ContextMenu/ContextMenu.doc.mjs +15 -0
- package/src/ContextMenu/ContextMenu.test.tsx +15 -0
- package/src/ContextMenu/ContextMenu.tsx +2 -0
- package/src/ContextMenu/ContextMenuItem.doc.mjs +6 -0
- package/src/DateTimeInput/DateTimeInput.test.tsx +70 -0
- package/src/DateTimeInput/DateTimeInput.tsx +2 -0
- package/src/Dialog/DialogHeader.doc.mjs +45 -0
- package/src/DropdownMenu/DropdownMenu.doc.mjs +1 -1
- package/src/Field/FieldLabel.test.tsx +2 -2
- package/src/FormLayout/__snapshots__/FormLayout.test.tsx.snap +0 -3
- package/src/Grid/Grid.test.tsx +43 -18
- package/src/Grid/Grid.tsx +26 -9
- package/src/Icon/Icon.test.tsx +41 -29
- package/src/Icon/Icon.tsx +6 -2
- package/src/InputGroup/InputGroup.tsx +2 -2
- package/src/Item/Item.tsx +1 -1
- package/src/Layout/LayoutContent.doc.mjs +12 -0
- package/src/Layout/LayoutPanel.doc.mjs +40 -0
- package/src/Lightbox/Lightbox.doc.mjs +5 -0
- package/src/Lightbox/Lightbox.test.tsx +123 -2
- package/src/Lightbox/Lightbox.tsx +29 -1
- package/src/NavMenu/NavMenu.doc.mjs +15 -0
- package/src/NumberInput/NumberInput.test.tsx +48 -3
- package/src/NumberInput/NumberInput.tsx +32 -15
- package/src/Overlay/Overlay.doc.mjs +6 -0
- package/src/Pagination/Pagination.test.tsx +143 -0
- package/src/Pagination/Pagination.tsx +75 -23
- package/src/Popover/Popover.test.tsx +65 -0
- package/src/PowerSearch/PowerSearch.doc.mjs +2 -2
- package/src/PowerSearch/PowerSearch.test.tsx +109 -1
- package/src/PowerSearch/PowerSearch.tsx +37 -16
- package/src/ProgressBar/ProgressBar.test.tsx +10 -0
- package/src/ProgressBar/ProgressBar.tsx +2 -1
- package/src/Resizable/ResizeHandle.test.tsx +180 -0
- package/src/Resizable/ResizeHandle.tsx +10 -2
- package/src/SegmentedControl/SegmentedControl.doc.mjs +1 -1
- package/src/SelectableCard/SelectableCard.doc.mjs +1 -1
- package/src/SideNav/SideNav.doc.mjs +7 -1
- package/src/SideNav/SideNav.test.tsx +20 -0
- package/src/SideNav/SideNav.tsx +6 -2
- package/src/TabList/TabList.test.tsx +185 -1
- package/src/TabList/TabMenu.tsx +45 -9
- package/src/Table/BaseTable.tsx +13 -2
- package/src/Table/Table.test.tsx +51 -0
- package/src/Table/plugins/columnResize/useTableColumnResize.test.tsx +13 -0
- package/src/Table/plugins/columnResize/useTableColumnResize.tsx +3 -1
- package/src/Table/plugins/pagination/paginateData.ts +5 -1
- package/src/Table/plugins/pagination/useTablePagination.test.tsx +13 -0
- package/src/Table/plugins/selection/useTableSelectionState.test.tsx +23 -0
- package/src/Table/plugins/selection/useTableSelectionState.tsx +11 -6
- package/src/Table/plugins/sortable/useTableSortableState.test.tsx +35 -1
- package/src/Table/plugins/sortable/useTableSortableState.tsx +8 -4
- package/src/TextArea/TextArea.test.tsx +3 -3
- package/src/TextInput/TextInput.test.tsx +3 -3
- package/src/Thumbnail/Thumbnail.test.tsx +36 -2
- package/src/Thumbnail/Thumbnail.tsx +4 -2
- package/src/TimeInput/TimeInput.test.tsx +37 -0
- package/src/TimeInput/TimeInput.tsx +11 -1
- package/src/Timestamp/Timestamp.doc.mjs +1 -1
- package/src/Toast/Toast.tsx +11 -3
- package/src/Toast/ToastViewport.test.tsx +64 -0
- package/src/Toast/ToastViewport.tsx +12 -0
- package/src/ToggleButton/ToggleButton.doc.mjs +1 -0
- package/src/Tokenizer/Tokenizer.doc.mjs +2 -2
- package/src/TopNav/TopNav.doc.mjs +1 -1
- package/src/TreeList/TreeList.doc.mjs +2 -2
- package/src/Typeahead/BaseTypeahead.doc.mjs +19 -0
- package/src/Typeahead/BaseTypeahead.tsx +5 -2
- package/src/Typeahead/Typeahead.doc.mjs +1 -1
- package/src/Typeahead/Typeahead.test.tsx +45 -1
- package/src/__tests__/TestIcon.tsx +26 -0
- package/src/docPropReferences.test.ts +178 -0
- package/src/hooks/focusableSelector.ts +1 -1
- package/src/hooks/useFocusTrap.doc.mjs +4 -2
- package/src/hooks/useFocusTrap.test.tsx +149 -1
- package/src/hooks/useFocusTrap.ts +50 -1
- package/src/theme/SyntaxTheme.doc.mjs +4 -4
- package/src/theme/defineTheme.ts +4 -13
- package/src/theme/derivedVarRegistry.test.ts +2 -2
- package/src/theme/expandColorScale.test.ts +29 -0
- package/src/theme/expandColorScale.ts +11 -6
- package/src/theme/hct.ts +9 -17
- package/src/theme/tokens.test.ts +182 -9
- package/src/theme/tokens.ts +239 -4
- package/src/theme/useTheme.test.tsx +20 -0
- package/src/utils/color.test.ts +133 -0
- package/src/utils/color.ts +148 -0
- package/src/utils/index.ts +3 -0
- package/src/utils/timeParser.test.ts +13 -0
- package/src/utils/timeParser.ts +8 -5
|
@@ -18,5 +18,5 @@
|
|
|
18
18
|
* This is the canonical focusable selector; prefer importing it here over
|
|
19
19
|
* re-declaring the string so behavior stays consistent across hooks.
|
|
20
20
|
*/
|
|
21
|
-
export declare const FOCUSABLE_SELECTOR = "button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"]):not([disabled]), [contenteditable]:not([contenteditable=\"false\"]), audio[controls], video[controls], iframe, details > summary:first-child";
|
|
21
|
+
export declare const FOCUSABLE_SELECTOR = "button:not([disabled]), a[href], area[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"]):not([disabled]), [contenteditable]:not([contenteditable=\"false\"]), audio[controls], video[controls], iframe, details > summary:first-child";
|
|
22
22
|
//# sourceMappingURL=focusableSelector.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"focusableSelector.d.ts","sourceRoot":"","sources":["../../src/hooks/focusableSelector.ts"],"names":[],"mappings":"AAEA;;;;;;;;GAQG;AAEH;;;;;;;;;;GAUG;AACH,eAAO,MAAM,kBAAkB,
|
|
1
|
+
{"version":3,"file":"focusableSelector.d.ts","sourceRoot":"","sources":["../../src/hooks/focusableSelector.ts"],"names":[],"mappings":"AAEA;;;;;;;;GAQG;AAEH;;;;;;;;;;GAUG;AACH,eAAO,MAAM,kBAAkB,ySACqQ,CAAC"}
|
|
@@ -21,4 +21,4 @@
|
|
|
21
21
|
* This is the canonical focusable selector; prefer importing it here over
|
|
22
22
|
* re-declaring the string so behavior stays consistent across hooks.
|
|
23
23
|
*/
|
|
24
|
-
export const FOCUSABLE_SELECTOR = 'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"]):not([disabled]), [contenteditable]:not([contenteditable="false"]), audio[controls], video[controls], iframe, details > summary:first-child';
|
|
24
|
+
export const FOCUSABLE_SELECTOR = 'button:not([disabled]), a[href], area[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"]):not([disabled]), [contenteditable]:not([contenteditable="false"]), audio[controls], video[controls], iframe, details > summary:first-child';
|
|
@@ -49,6 +49,9 @@ export interface UseFocusTrapReturn<T extends HTMLElement = HTMLElement> {
|
|
|
49
49
|
* - Listens to focus events on the document
|
|
50
50
|
* - Redirects focus back into the container if it escapes
|
|
51
51
|
* - Handles both Tab and Shift+Tab navigation
|
|
52
|
+
* - Restores focus to the element that was focused before activation when the
|
|
53
|
+
* trap deactivates or unmounts, unless focus was already moved elsewhere
|
|
54
|
+
* (so consumers that restore focus themselves are unaffected)
|
|
52
55
|
*
|
|
53
56
|
* @example
|
|
54
57
|
* ```
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useFocusTrap.d.ts","sourceRoot":"","sources":["../../src/hooks/useFocusTrap.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useFocusTrap.d.ts","sourceRoot":"","sources":["../../src/hooks/useFocusTrap.ts"],"names":[],"mappings":"AAgDA;;;;;GAKG;AACH,wBAAgB,wBAAwB,IAAI,OAAO,CAElD;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE;IACnC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,GAAG,OAAO,CAEV;AA0ED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC;;OAEG;IACH,QAAQ,EAAE,OAAO,CAAC;IAElB;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW;IACrE;;OAEG;IACH,YAAY,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IAExC;;OAEG;IACH,UAAU,EAAE,MAAM,IAAI,CAAC;CACxB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,YAAY,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW,EAC9D,OAAO,EAAE,mBAAmB,GAC3B,kBAAkB,CAAC,CAAC,CAAC,CAiMvB"}
|
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
/**
|
|
6
6
|
* @file useFocusTrap.ts
|
|
7
7
|
* @input Uses React useCallback, useEffect, useRef
|
|
8
|
-
* @output Exports useFocusTrap hook for trapping focus within a container
|
|
8
|
+
* @output Exports useFocusTrap hook for trapping focus within a container and
|
|
9
|
+
* restoring focus to the previously-focused element on deactivation
|
|
9
10
|
* @position Core hook; used by dialogs, modals, date pickers
|
|
10
11
|
*
|
|
11
12
|
* Based on WAI-ARIA dialog pattern:
|
|
@@ -146,6 +147,9 @@ function focusLastDescendant(container) {
|
|
|
146
147
|
* - Listens to focus events on the document
|
|
147
148
|
* - Redirects focus back into the container if it escapes
|
|
148
149
|
* - Handles both Tab and Shift+Tab navigation
|
|
150
|
+
* - Restores focus to the element that was focused before activation when the
|
|
151
|
+
* trap deactivates or unmounts, unless focus was already moved elsewhere
|
|
152
|
+
* (so consumers that restore focus themselves are unaffected)
|
|
149
153
|
*
|
|
150
154
|
* @example
|
|
151
155
|
* ```
|
|
@@ -185,6 +189,39 @@ export function useFocusTrap(options) {
|
|
|
185
189
|
}
|
|
186
190
|
}, []);
|
|
187
191
|
|
|
192
|
+
/**
|
|
193
|
+
* Capture the element focused before the trap activated, and restore focus to
|
|
194
|
+
* it when the trap deactivates (or the component unmounts). Overlays are
|
|
195
|
+
* opened imperatively (e.g. `showPopover()`), so the browser's declarative
|
|
196
|
+
* popover focus restoration does not apply — without this, closing a Popover
|
|
197
|
+
* via Escape or light dismiss drops keyboard focus to `<body>`.
|
|
198
|
+
*
|
|
199
|
+
* The restore is guarded so it never steals focus a consumer moved on
|
|
200
|
+
* purpose: it only runs when focus would otherwise be lost — i.e. the active
|
|
201
|
+
* element is nothing, the document body/root, or still inside the (possibly
|
|
202
|
+
* now-unmounted) trap container. If focus already moved to some other element
|
|
203
|
+
* outside the trap (the user clicked elsewhere, or a consumer such as
|
|
204
|
+
* DropdownMenu already refocused its trigger), the restore is a no-op.
|
|
205
|
+
*/
|
|
206
|
+
useEffect(() => {
|
|
207
|
+
if (!isActive) {
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
const previouslyFocused = document.activeElement;
|
|
211
|
+
// Snapshot the container now; by cleanup it may be detached or unmounted.
|
|
212
|
+
const container = containerRef.current;
|
|
213
|
+
return () => {
|
|
214
|
+
const active = document.activeElement;
|
|
215
|
+
const focusWasLost = active == null || active === document.body || active === document.documentElement || container != null && container.contains(active);
|
|
216
|
+
if (!focusWasLost) {
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
if (previouslyFocused != null && previouslyFocused.isConnected && typeof previouslyFocused.focus === 'function') {
|
|
220
|
+
previouslyFocused.focus();
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
}, [isActive]);
|
|
224
|
+
|
|
188
225
|
/**
|
|
189
226
|
* Handle focus events - redirect focus back into container if it escapes.
|
|
190
227
|
* Only redirects for keyboard navigation, not mouse clicks.
|
|
@@ -228,7 +228,7 @@ export interface DefineThemeInput {
|
|
|
228
228
|
/**
|
|
229
229
|
* Default syntax highlighting theme for code components.
|
|
230
230
|
* Sets --color-syntax-* tokens at the theme root. Can be overridden
|
|
231
|
-
* per-region
|
|
231
|
+
* per-region (or per-instance) by wrapping in SyntaxTheme.
|
|
232
232
|
*
|
|
233
233
|
* @example
|
|
234
234
|
* ```tsx
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"defineTheme.d.ts","sourceRoot":"","sources":["../../src/theme/defineTheme.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,4BAA4B,CAAC;AAC7D,OAAO,KAAK,EAAC,gBAAgB,EAAa,MAAM,SAAS,CAAC;AAC1D,OAAO,EAEL,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACrB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,aAAa,EACb,eAAe,EACf,YAAY,EACZ,cAAc,EACd,cAAc,EACd,gBAAgB,EAChB,YAAY,EACZ,kBAAkB,EAClB,kBAAkB,EAClB,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,EAClB,MAAM,iBAAiB,CAAC;AAMzB,OAAO,
|
|
1
|
+
{"version":3,"file":"defineTheme.d.ts","sourceRoot":"","sources":["../../src/theme/defineTheme.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"expandColorScale.d.ts","sourceRoot":"","sources":["../../src/theme/expandColorScale.ts"],"names":[],"mappings":"AAuBA;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,gBAAgB;IAC/B,wEAAwE;IACxE,MAAM,EAAE,MAAM,CAAC;IAEf;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IAE3C;;;OAGG;IACH,QAAQ,CAAC,EAAE,UAAU,GAAG,MAAM,CAAC;CAChC;AAED,MAAM,MAAM,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"expandColorScale.d.ts","sourceRoot":"","sources":["../../src/theme/expandColorScale.ts"],"names":[],"mappings":"AAuBA;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,gBAAgB;IAC/B,wEAAwE;IACxE,MAAM,EAAE,MAAM,CAAC;IAEf;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IAE3C;;;OAGG;IACH,QAAQ,CAAC,EAAE,UAAU,GAAG,MAAM,CAAC;CAChC;AAED,MAAM,MAAM,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AA8BtD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,gBAAgB,GACvB,gBAAgB,CAkFlB"}
|
|
@@ -56,6 +56,9 @@ const NEUTRAL_VARIANT_CHROMA = {
|
|
|
56
56
|
function ld(light, dark) {
|
|
57
57
|
return `light-dark(${light}, ${dark})`;
|
|
58
58
|
}
|
|
59
|
+
function accentWithAlpha(alpha) {
|
|
60
|
+
return `color-mix(in srgb, var(--color-accent) ${alpha * 100}%, transparent)`;
|
|
61
|
+
}
|
|
59
62
|
|
|
60
63
|
/**
|
|
61
64
|
* Expand a color scale config into Astryx color token overrides.
|
|
@@ -93,7 +96,11 @@ export function expandColorScale(config) {
|
|
|
93
96
|
return {
|
|
94
97
|
// Core semantic
|
|
95
98
|
'--color-accent': ld(P[40], P[80]),
|
|
96
|
-
|
|
99
|
+
// Derived accent tokens reference --color-accent instead of baking its
|
|
100
|
+
// resolved hex, so a scoped override of the base token re-accents the
|
|
101
|
+
// whole subtree at runtime. --color-on-accent stays baked: it is a
|
|
102
|
+
// contrast computation against the accent, which CSS cannot express.
|
|
103
|
+
'--color-accent-muted': ld(accentWithAlpha(0.2), accentWithAlpha(0.25)),
|
|
97
104
|
'--color-on-accent': ld(P[100], P[20]),
|
|
98
105
|
'--color-neutral': ld(hexWithAlpha(N[10], 0.1), hexWithAlpha(N[90], 0.2)),
|
|
99
106
|
'--color-background-surface': ld(N[99], N[10]),
|
|
@@ -106,9 +113,9 @@ export function expandColorScale(config) {
|
|
|
106
113
|
'--color-text-primary': ld(N[textPrimaryLightTone], N[textPrimaryDarkTone]),
|
|
107
114
|
'--color-text-secondary': ld(NV[textSecondaryLightTone], NV[textSecondaryDarkTone]),
|
|
108
115
|
'--color-text-disabled': ld(NV[60], NV[40]),
|
|
109
|
-
'--color-text-accent':
|
|
116
|
+
'--color-text-accent': 'var(--color-accent)',
|
|
110
117
|
// Icon
|
|
111
|
-
'--color-icon-accent':
|
|
118
|
+
'--color-icon-accent': 'var(--color-accent)',
|
|
112
119
|
'--color-icon-primary': ld(N[textPrimaryLightTone], N[textPrimaryDarkTone]),
|
|
113
120
|
'--color-icon-secondary': ld(NV[textSecondaryLightTone], NV[textSecondaryDarkTone]),
|
|
114
121
|
'--color-icon-disabled': ld(NV[60], NV[40]),
|
package/dist/theme/hct.d.ts
CHANGED
|
@@ -1,18 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file hct.ts
|
|
3
|
-
* @input Hex color string (#RRGGBB)
|
|
4
|
-
* @output HCT color representation (Hue, Chroma, Tone)
|
|
5
|
-
* @position Theme utility; consumed by expandColorScale.ts
|
|
6
|
-
*
|
|
7
|
-
* Minimal HCT (Hue, Chroma, Tone) color space implementation.
|
|
8
|
-
* Ported from Google's material-color-utilities (Apache-2.0).
|
|
9
|
-
* Zero runtime dependencies — all math is self-contained.
|
|
10
|
-
*
|
|
11
|
-
* HCT combines:
|
|
12
|
-
* - Hue from CIELab (perceptually uniform hue)
|
|
13
|
-
* - Chroma approximated via CIELab C*ab (colorfulness)
|
|
14
|
-
* - Tone from L* (CIE Lightness, 0=black, 100=white)
|
|
15
|
-
*/
|
|
16
1
|
export interface HCT {
|
|
17
2
|
hue: number;
|
|
18
3
|
chroma: number;
|
package/dist/theme/hct.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hct.d.ts","sourceRoot":"","sources":["../../src/theme/hct.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"hct.d.ts","sourceRoot":"","sources":["../../src/theme/hct.ts"],"names":[],"mappings":"AAwBA,MAAM,WAAW,GAAG;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACd;AAoFD,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE5C;AAED,wBAAgB,OAAO,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAEzC;AAkBD;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAkBzC;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,GAAG,GAAG,MAAM,CA8BzC;AAkDD;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,GACb,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAMxB;AAMD;;GAEG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAM/D"}
|
package/dist/theme/hct.js
CHANGED
|
@@ -16,6 +16,8 @@
|
|
|
16
16
|
* - Tone from L* (CIE Lightness, 0=black, 100=white)
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
+
import { parseHex, formatHex } from "../utils/color.js";
|
|
20
|
+
|
|
19
21
|
// =============================================================================
|
|
20
22
|
// Types
|
|
21
23
|
// =============================================================================
|
|
@@ -86,13 +88,11 @@ export function yToTone(y) {
|
|
|
86
88
|
// =============================================================================
|
|
87
89
|
|
|
88
90
|
function hexToRgb(hex) {
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
function rgbToHex(r, g, b) {
|
|
95
|
-
return '#' + [r, g, b].map(c => c.toString(16).padStart(2, '0')).join('').toUpperCase();
|
|
91
|
+
const parsed = parseHex(hex);
|
|
92
|
+
if (parsed === null) {
|
|
93
|
+
return [0, 0, 0];
|
|
94
|
+
}
|
|
95
|
+
return [parsed.r, parsed.g, parsed.b];
|
|
96
96
|
}
|
|
97
97
|
|
|
98
98
|
// =============================================================================
|
|
@@ -142,7 +142,7 @@ export function hctToHex(hct) {
|
|
|
142
142
|
}
|
|
143
143
|
if (chroma < 0.5) {
|
|
144
144
|
const gray = toneToGray(tone);
|
|
145
|
-
return
|
|
145
|
+
return formatHex(gray, gray, gray);
|
|
146
146
|
}
|
|
147
147
|
let lo = 0;
|
|
148
148
|
let hi = chroma;
|
|
@@ -183,7 +183,7 @@ function hctComponentToHex(hue, chroma, tone) {
|
|
|
183
183
|
if (r < 0 || r > 255 || g < 0 || g > 255 || bVal < 0 || bVal > 255) {
|
|
184
184
|
return null;
|
|
185
185
|
}
|
|
186
|
-
return
|
|
186
|
+
return formatHex(r, g, bVal);
|
|
187
187
|
}
|
|
188
188
|
|
|
189
189
|
// =============================================================================
|
package/dist/theme/tokens.d.ts
CHANGED
|
@@ -8,6 +8,11 @@
|
|
|
8
8
|
* build-time theme adapters, chart configuration, canvas/SVG rendering, tests,
|
|
9
9
|
* or plain JS theme objects for other styling libraries.
|
|
10
10
|
*
|
|
11
|
+
* Derived tokens can reference other tokens (`var(--color-accent)`) and use CSS
|
|
12
|
+
* color functions (`color-mix`). The resolver follows those references through
|
|
13
|
+
* the theme iteratively and evaluates the supported color functions so callers
|
|
14
|
+
* receive concrete raw values, not CSS expressions.
|
|
15
|
+
*
|
|
11
16
|
* SYNC: When modified, update:
|
|
12
17
|
* - /packages/core/src/theme/useTheme.ts
|
|
13
18
|
* - /packages/core/src/theme/index.ts
|
|
@@ -51,8 +56,11 @@ export declare const tokenVars: Record<TokenName, string>;
|
|
|
51
56
|
* The result starts with `tokenDefaults`, applies `theme.tokens`, then
|
|
52
57
|
* reapplies `theme.__inputTokens` when available so explicit tuple overrides
|
|
53
58
|
* retain their original light/dark sides instead of relying on CSS parsing.
|
|
54
|
-
*
|
|
55
|
-
*
|
|
59
|
+
* A final pass resolves any `var()` references between tokens and evaluates
|
|
60
|
+
* the CSS color functions the theme generator emits (e.g. `color-mix`), so
|
|
61
|
+
* derived tokens like `--color-text-accent` return concrete values rather than
|
|
62
|
+
* `var(--color-accent)`. This mirrors the token resolution used by
|
|
63
|
+
* `useTheme()` but does not need React context or media queries.
|
|
56
64
|
*
|
|
57
65
|
* Pass `theme` as null/undefined to resolve defaults only.
|
|
58
66
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../../src/theme/tokens.ts"],"names":[],"mappings":"AAEA
|
|
1
|
+
{"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../../src/theme/tokens.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAEL,KAAK,YAAY,EACjB,KAAK,SAAS,EAEf,MAAM,eAAe,CAAC;AAGvB,kFAAkF;AAClF,MAAM,MAAM,iBAAiB,GAAG,OAAO,GAAG,MAAM,CAAC;AAEjD,4DAA4D;AAC5D,MAAM,WAAW,yBAAyB;IACxC,kGAAkG;IAClG,IAAI,EAAE,iBAAiB,CAAC;CACzB;AAED,2DAA2D;AAC3D,MAAM,WAAW,wBAAyB,SAAQ,yBAAyB;IACzE,mFAAmF;IACnF,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,SAAS,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,GAAG,MAAM,CAEhE;AAED,sFAAsF;AACtF,eAAO,MAAM,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,MAAM,CAElB,CAAC;AAqT/B;;;;;;;;;;;;;GAaG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,YAAY,GAAG,IAAI,GAAG,SAAS,EACtC,OAAO,EAAE,yBAAyB,GACjC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAyBxB;AAED,2EAA2E;AAC3E,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,YAAY,GAAG,IAAI,GAAG,SAAS,EACtC,IAAI,EAAE,SAAS,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,EAC/B,OAAO,EAAE,wBAAwB,GAChC,MAAM,CAGR"}
|
package/dist/theme/tokens.js
CHANGED
|
@@ -10,12 +10,18 @@
|
|
|
10
10
|
* build-time theme adapters, chart configuration, canvas/SVG rendering, tests,
|
|
11
11
|
* or plain JS theme objects for other styling libraries.
|
|
12
12
|
*
|
|
13
|
+
* Derived tokens can reference other tokens (`var(--color-accent)`) and use CSS
|
|
14
|
+
* color functions (`color-mix`). The resolver follows those references through
|
|
15
|
+
* the theme iteratively and evaluates the supported color functions so callers
|
|
16
|
+
* receive concrete raw values, not CSS expressions.
|
|
17
|
+
*
|
|
13
18
|
* SYNC: When modified, update:
|
|
14
19
|
* - /packages/core/src/theme/useTheme.ts
|
|
15
20
|
* - /packages/core/src/theme/index.ts
|
|
16
21
|
*/
|
|
17
22
|
|
|
18
23
|
import { tokenDefaults } from "./defineTheme.js";
|
|
24
|
+
import { parseColor, formatColor } from "../utils/color.js";
|
|
19
25
|
|
|
20
26
|
/** Resolved color mode used when choosing the side of light/dark token values. */
|
|
21
27
|
|
|
@@ -115,14 +121,224 @@ function resolveXDSTokenValue(value, mode) {
|
|
|
115
121
|
return value;
|
|
116
122
|
}
|
|
117
123
|
|
|
124
|
+
// =============================================================================
|
|
125
|
+
// Reference resolution — turn token expressions into concrete raw values
|
|
126
|
+
// =============================================================================
|
|
127
|
+
//
|
|
128
|
+
// Generated themes emit derived tokens as references to other tokens rather
|
|
129
|
+
// than baked-in values, so a scoped CSS override of a base token re-themes the
|
|
130
|
+
// subtree that references it. The CSS cascade resolves those references, but
|
|
131
|
+
// non-CSS consumers (canvas, SVG, data-viz) need the concrete value. These
|
|
132
|
+
// helpers replay the same resolution in JS: follow `var()` references through
|
|
133
|
+
// the resolved token map (iteratively, guarding against cycles) and evaluate
|
|
134
|
+
// the CSS color functions used by the theme generator against those values.
|
|
135
|
+
|
|
136
|
+
/** Index of the `)` matching the `(` at `openIndex`, or -1 when unbalanced. */
|
|
137
|
+
function findMatchingParen(input, openIndex) {
|
|
138
|
+
let depth = 0;
|
|
139
|
+
for (let i = openIndex; i < input.length; i++) {
|
|
140
|
+
const char = input[i];
|
|
141
|
+
if (char === '(') {
|
|
142
|
+
depth++;
|
|
143
|
+
} else if (char === ')') {
|
|
144
|
+
depth--;
|
|
145
|
+
if (depth === 0) {
|
|
146
|
+
return i;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return -1;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/** Split a color-mix component into its color and optional percentage. */
|
|
154
|
+
function parseMixComponent(part) {
|
|
155
|
+
const trimmed = part.trim();
|
|
156
|
+
const match = trimmed.match(/\s+([\d.]+)%$/);
|
|
157
|
+
if (match) {
|
|
158
|
+
return {
|
|
159
|
+
color: trimmed.slice(0, match.index).trim(),
|
|
160
|
+
percentage: parseFloat(match[1])
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
return {
|
|
164
|
+
color: trimmed,
|
|
165
|
+
percentage: null
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Evaluate `color-mix(in <space>, c1 [p1], c2 [p2])` to a concrete color.
|
|
171
|
+
*
|
|
172
|
+
* Supports the `srgb` color space (the one the theme generator emits) using
|
|
173
|
+
* the CSS Color 5 algorithm: normalize the percentages, interpolate the
|
|
174
|
+
* premultiplied channels, then apply the alpha multiplier when the mix weights
|
|
175
|
+
* sum to less than 100%. Returns null for anything it can't evaluate so the
|
|
176
|
+
* original expression is preserved rather than guessed.
|
|
177
|
+
*/
|
|
178
|
+
function evaluateColorMix(body) {
|
|
179
|
+
const spaceMatch = body.match(/^in\s+([\w-]+)\s*,\s*(.+)$/s);
|
|
180
|
+
if (!spaceMatch) {
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
const [, colorSpace, rest] = spaceMatch;
|
|
184
|
+
if (colorSpace.toLowerCase() !== 'srgb') {
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
const split = splitTopLevelComma(rest);
|
|
188
|
+
if (split === null) {
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
191
|
+
const first = parseMixComponent(split[0]);
|
|
192
|
+
const second = parseMixComponent(split[1]);
|
|
193
|
+
if (first === null || second === null) {
|
|
194
|
+
return null;
|
|
195
|
+
}
|
|
196
|
+
const c1 = parseColor(first.color);
|
|
197
|
+
const c2 = parseColor(second.color);
|
|
198
|
+
if (c1 === null || c2 === null) {
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Fill in omitted percentages, then normalize so they sum to 100%.
|
|
203
|
+
let p1;
|
|
204
|
+
let p2;
|
|
205
|
+
if (first.percentage !== null && second.percentage !== null) {
|
|
206
|
+
p1 = first.percentage;
|
|
207
|
+
p2 = second.percentage;
|
|
208
|
+
} else if (first.percentage !== null) {
|
|
209
|
+
p1 = first.percentage;
|
|
210
|
+
p2 = 100 - p1;
|
|
211
|
+
} else if (second.percentage !== null) {
|
|
212
|
+
p2 = second.percentage;
|
|
213
|
+
p1 = 100 - p2;
|
|
214
|
+
} else {
|
|
215
|
+
p1 = 50;
|
|
216
|
+
p2 = 50;
|
|
217
|
+
}
|
|
218
|
+
const sum = p1 + p2;
|
|
219
|
+
if (sum <= 0) {
|
|
220
|
+
return null;
|
|
221
|
+
}
|
|
222
|
+
const w1 = p1 / sum;
|
|
223
|
+
const w2 = p2 / sum;
|
|
224
|
+
const alphaMultiplier = sum < 100 ? sum / 100 : 1;
|
|
225
|
+
|
|
226
|
+
// Interpolate in premultiplied sRGB, then un-premultiply.
|
|
227
|
+
const mixedA = w1 * c1.a + w2 * c2.a;
|
|
228
|
+
const premix = (k1, k2) => w1 * k1 * c1.a + w2 * k2 * c2.a;
|
|
229
|
+
const rp = premix(c1.r, c2.r);
|
|
230
|
+
const gp = premix(c1.g, c2.g);
|
|
231
|
+
const bp = premix(c1.b, c2.b);
|
|
232
|
+
const rgb = mixedA === 0 ? {
|
|
233
|
+
r: 0,
|
|
234
|
+
g: 0,
|
|
235
|
+
b: 0
|
|
236
|
+
} : {
|
|
237
|
+
r: rp / mixedA,
|
|
238
|
+
g: gp / mixedA,
|
|
239
|
+
b: bp / mixedA
|
|
240
|
+
};
|
|
241
|
+
return formatColor({
|
|
242
|
+
...rgb,
|
|
243
|
+
a: mixedA * alphaMultiplier
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/** Evaluate every supported color function in an expression, innermost first. */
|
|
248
|
+
function evaluateColorFunctions(expr) {
|
|
249
|
+
const idx = expr.indexOf('color-mix(');
|
|
250
|
+
if (idx === -1) {
|
|
251
|
+
return expr;
|
|
252
|
+
}
|
|
253
|
+
const openIndex = idx + 'color-mix'.length;
|
|
254
|
+
const closeIndex = findMatchingParen(expr, openIndex);
|
|
255
|
+
if (closeIndex === -1) {
|
|
256
|
+
return expr;
|
|
257
|
+
}
|
|
258
|
+
const body = evaluateColorFunctions(expr.slice(openIndex + 1, closeIndex));
|
|
259
|
+
const evaluated = evaluateColorMix(body);
|
|
260
|
+
const replacement = evaluated ?? `color-mix(${body})`;
|
|
261
|
+
return expr.slice(0, idx) + replacement + evaluateColorFunctions(expr.slice(closeIndex + 1));
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Substitute `var(--name[, fallback])` references with their resolved values.
|
|
266
|
+
* `seen` tracks the reference chain so cycles resolve to the literal reference
|
|
267
|
+
* instead of recursing forever.
|
|
268
|
+
*/
|
|
269
|
+
function substituteVars(expr, raw, cache, seen) {
|
|
270
|
+
const start = expr.indexOf('var(');
|
|
271
|
+
if (start === -1) {
|
|
272
|
+
return expr;
|
|
273
|
+
}
|
|
274
|
+
const openIndex = start + 'var'.length;
|
|
275
|
+
const closeIndex = findMatchingParen(expr, openIndex);
|
|
276
|
+
if (closeIndex === -1) {
|
|
277
|
+
return expr;
|
|
278
|
+
}
|
|
279
|
+
const inner = expr.slice(openIndex + 1, closeIndex);
|
|
280
|
+
const commaSplit = splitTopLevelComma(inner);
|
|
281
|
+
const name = (commaSplit ? commaSplit[0] : inner).trim();
|
|
282
|
+
const fallback = commaSplit ? commaSplit[1] : null;
|
|
283
|
+
let replacement;
|
|
284
|
+
if (seen.has(name)) {
|
|
285
|
+
replacement = expr.slice(start, closeIndex + 1);
|
|
286
|
+
} else if (name in raw) {
|
|
287
|
+
seen.add(name);
|
|
288
|
+
replacement = resolveReference(name, raw, cache, seen);
|
|
289
|
+
seen.delete(name);
|
|
290
|
+
} else if (fallback !== null) {
|
|
291
|
+
replacement = substituteVars(fallback.trim(), raw, cache, seen);
|
|
292
|
+
} else {
|
|
293
|
+
replacement = expr.slice(start, closeIndex + 1);
|
|
294
|
+
}
|
|
295
|
+
const rest = substituteVars(expr.slice(closeIndex + 1), raw, cache, seen);
|
|
296
|
+
return expr.slice(0, start) + replacement + rest;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/** Fully resolve a single expression: substitute references, then evaluate colors. */
|
|
300
|
+
function resolveExpression(expr, raw, cache, seen) {
|
|
301
|
+
if (!expr.includes('var(') && !expr.includes('color-mix(')) {
|
|
302
|
+
return expr;
|
|
303
|
+
}
|
|
304
|
+
const substituted = substituteVars(expr, raw, cache, seen);
|
|
305
|
+
return evaluateColorFunctions(substituted);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/** Resolve one token by name, memoizing the result in `cache`. */
|
|
309
|
+
function resolveReference(name, raw, cache, seen) {
|
|
310
|
+
if (name in cache) {
|
|
311
|
+
return cache[name];
|
|
312
|
+
}
|
|
313
|
+
const value = raw[name];
|
|
314
|
+
if (value === undefined) {
|
|
315
|
+
return '';
|
|
316
|
+
}
|
|
317
|
+
const resolved = resolveExpression(value, raw, cache, seen);
|
|
318
|
+
cache[name] = resolved;
|
|
319
|
+
return resolved;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/** Resolve every reference/color function in a raw token map to concrete values. */
|
|
323
|
+
function resolveReferences(raw) {
|
|
324
|
+
const cache = {};
|
|
325
|
+
for (const name of Object.keys(raw)) {
|
|
326
|
+
resolveReference(name, raw, cache, new Set());
|
|
327
|
+
}
|
|
328
|
+
return cache;
|
|
329
|
+
}
|
|
330
|
+
|
|
118
331
|
/**
|
|
119
332
|
* Resolve all Astryx token values for a theme and effective color mode.
|
|
120
333
|
*
|
|
121
334
|
* The result starts with `tokenDefaults`, applies `theme.tokens`, then
|
|
122
335
|
* reapplies `theme.__inputTokens` when available so explicit tuple overrides
|
|
123
336
|
* retain their original light/dark sides instead of relying on CSS parsing.
|
|
124
|
-
*
|
|
125
|
-
*
|
|
337
|
+
* A final pass resolves any `var()` references between tokens and evaluates
|
|
338
|
+
* the CSS color functions the theme generator emits (e.g. `color-mix`), so
|
|
339
|
+
* derived tokens like `--color-text-accent` return concrete values rather than
|
|
340
|
+
* `var(--color-accent)`. This mirrors the token resolution used by
|
|
341
|
+
* `useTheme()` but does not need React context or media queries.
|
|
126
342
|
*
|
|
127
343
|
* Pass `theme` as null/undefined to resolve defaults only.
|
|
128
344
|
*/
|
|
@@ -135,7 +351,7 @@ export function resolveThemeTokens(theme, options) {
|
|
|
135
351
|
resolved[key] = resolveXDSTokenValue(value, mode);
|
|
136
352
|
}
|
|
137
353
|
if (theme == null) {
|
|
138
|
-
return resolved;
|
|
354
|
+
return resolveReferences(resolved);
|
|
139
355
|
}
|
|
140
356
|
for (const [key, value] of Object.entries(theme.tokens)) {
|
|
141
357
|
resolved[key] = resolveXDSTokenValue(value, mode);
|
|
@@ -147,7 +363,7 @@ export function resolveThemeTokens(theme, options) {
|
|
|
147
363
|
}
|
|
148
364
|
}
|
|
149
365
|
}
|
|
150
|
-
return resolved;
|
|
366
|
+
return resolveReferences(resolved);
|
|
151
367
|
}
|
|
152
368
|
|
|
153
369
|
/** Resolve one Astryx token value for a theme and effective color mode. */
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file color.ts
|
|
3
|
+
* @input CSS color strings (hex, rgb()/rgba(), a small set of named colors)
|
|
4
|
+
* @output Shared color parsing/formatting primitives used across the design system
|
|
5
|
+
* @position Package utility; backs theme token resolution, HCT color math, and chart rendering
|
|
6
|
+
*
|
|
7
|
+
* A single home for the color parsers that were previously duplicated across
|
|
8
|
+
* the theme layer, charts, and lab. Consumers get one well-tested definition of
|
|
9
|
+
* "parse this color" rather than several subtly different regexes.
|
|
10
|
+
*/
|
|
11
|
+
/** A color decomposed into 0-255 RGB channels and a 0-1 alpha. */
|
|
12
|
+
export interface RGBA {
|
|
13
|
+
r: number;
|
|
14
|
+
g: number;
|
|
15
|
+
b: number;
|
|
16
|
+
a: number;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Parse a hex color into {@link RGBA}. Accepts `#rgb`, `#rgba`, `#rrggbb`, and
|
|
20
|
+
* `#rrggbbaa`, with or without the leading `#`. Returns null for anything else.
|
|
21
|
+
*/
|
|
22
|
+
export declare function parseHex(hex: string): RGBA | null;
|
|
23
|
+
/**
|
|
24
|
+
* Parse an `rgb()`/`rgba()` color into {@link RGBA}. Accepts comma- or
|
|
25
|
+
* space-separated channels, an optional `/ alpha`, and percentage channels.
|
|
26
|
+
*/
|
|
27
|
+
export declare function parseRgb(value: string): RGBA | null;
|
|
28
|
+
/**
|
|
29
|
+
* Parse a concrete CSS color string into {@link RGBA}. Supports hex,
|
|
30
|
+
* `rgb()`/`rgba()`, and the named colors used in token expressions. Returns
|
|
31
|
+
* null for anything it can't evaluate (e.g. `var()`, `oklch()`, unknown names)
|
|
32
|
+
* so callers can preserve the original expression rather than guessing.
|
|
33
|
+
*/
|
|
34
|
+
export declare function parseColor(value: string): RGBA | null;
|
|
35
|
+
/** Format RGB channels (0-255) as an uppercase `#RRGGBB` string. */
|
|
36
|
+
export declare function formatHex(r: number, g: number, b: number): string;
|
|
37
|
+
/**
|
|
38
|
+
* Serialize {@link RGBA} to a compact, JS-usable CSS color: `#RRGGBB` when
|
|
39
|
+
* fully opaque, otherwise `rgba(r, g, b, a)`.
|
|
40
|
+
*/
|
|
41
|
+
export declare function formatColor({ r, g, b, a }: RGBA): string;
|
|
42
|
+
//# sourceMappingURL=color.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"color.d.ts","sourceRoot":"","sources":["../../src/utils/color.ts"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AAEH,kEAAkE;AAClE,MAAM,WAAW,IAAI;IACnB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX;AAoBD;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAqBjD;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAqCnD;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAarD;AAED,oEAAoE;AACpE,wBAAgB,SAAS,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAIjE;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,EAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAC,EAAE,IAAI,GAAG,MAAM,CAMtD"}
|