@epilot/zenith-react 0.2.0-next.4 → 0.2.0-next.5
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 +12 -1
- package/codemods/README.md +18 -4
- package/codemods/format.ts +86 -0
- package/codemods/migrate/from-base-elements.ts +8 -2
- package/codemods/migrate/from-volt.ts +8 -2
- package/codemods/radix-to-base-ui.ts +300 -12
- package/dist/components/field/field-combobox.js +190 -181
- package/dist/components/field/field-combobox.js.map +1 -1
- package/dist/components/field/field-select.js +37 -22
- package/dist/components/field/field-select.js.map +1 -1
- package/dist/components/scroll-overflow/scroll-overflow.js +21 -21
- package/dist/components/scroll-overflow/scroll-overflow.js.map +1 -1
- package/dist/components/toggle/toggle.js +29 -26
- package/dist/components/toggle/toggle.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/renderer/served.js +19 -14
- package/dist/renderer/served.js.map +1 -1
- package/dist/tokens.json +1 -1
- package/package.json +4 -3
- package/scripts/migration-diagnostics-core.mjs +284 -0
- package/scripts/migration-sweep.mjs +10 -10
- package/testing/setup.cjs +87 -0
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import { jsx as m } from "react/jsx-runtime";
|
|
2
2
|
import * as i from "react";
|
|
3
3
|
import { Toggle as u } from "../../_virtual/_zenith-runtime-family_toggle.js";
|
|
4
|
-
import { mergeClasses as
|
|
4
|
+
import { mergeClasses as f, SLOT_ATTR as h, shorthands as o } from "@epilot/zenith-runtime/v1";
|
|
5
5
|
import { makeZenithStyles as p } from "../../styles/make-styles.js";
|
|
6
6
|
import { nativeControl as b } from "../../styles/host-reset.js";
|
|
7
|
-
import { errorA4 as
|
|
7
|
+
import { errorA4 as y, errorOnSoft as S, errorA3 as v, errorSoft as C, errorLight as T, accentA4 as k, accentOnSoft as x, accentA3 as R, accentSoft as W, accentLight as A, focusRing as E, fontWeightSemibold as I, radius2xl as w, spacingElement2 as L, radiusXl as O, radiusLg as P, spacingElement1 as D } from "../../tokens/index.js";
|
|
8
8
|
const j = p({
|
|
9
9
|
root: {
|
|
10
10
|
...b,
|
|
11
11
|
display: "inline-flex",
|
|
12
12
|
alignItems: "center",
|
|
13
13
|
justifyContent: "center",
|
|
14
|
-
...
|
|
14
|
+
...o.gap(D),
|
|
15
15
|
flexShrink: 0,
|
|
16
16
|
cursor: "pointer",
|
|
17
17
|
fontWeight: I,
|
|
@@ -21,8 +21,8 @@ const j = p({
|
|
|
21
21
|
"@media (prefers-reduced-motion: reduce)": {
|
|
22
22
|
transitionDuration: "0.01ms"
|
|
23
23
|
},
|
|
24
|
-
...
|
|
25
|
-
...
|
|
24
|
+
...o.borderStyle("none"),
|
|
25
|
+
...o.borderWidth(0),
|
|
26
26
|
":focus-visible": {
|
|
27
27
|
outlineWidth: "2px",
|
|
28
28
|
outlineOffset: "2px",
|
|
@@ -61,19 +61,19 @@ const j = p({
|
|
|
61
61
|
backgroundColor: C
|
|
62
62
|
},
|
|
63
63
|
"&[data-pressed]": {
|
|
64
|
-
backgroundColor:
|
|
65
|
-
color:
|
|
64
|
+
backgroundColor: v,
|
|
65
|
+
color: S
|
|
66
66
|
},
|
|
67
67
|
"&[data-pressed]:hover": {
|
|
68
|
-
backgroundColor:
|
|
68
|
+
backgroundColor: y
|
|
69
69
|
}
|
|
70
70
|
},
|
|
71
71
|
// Sizes
|
|
72
72
|
sm: {
|
|
73
73
|
height: "1.5rem",
|
|
74
74
|
minWidth: "1.5rem",
|
|
75
|
-
...
|
|
76
|
-
...
|
|
75
|
+
...o.borderRadius(P),
|
|
76
|
+
...o.paddingInline("0.375rem"),
|
|
77
77
|
"& svg": {
|
|
78
78
|
width: "0.875rem",
|
|
79
79
|
height: "0.875rem"
|
|
@@ -82,8 +82,8 @@ const j = p({
|
|
|
82
82
|
base: {
|
|
83
83
|
height: "2rem",
|
|
84
84
|
minWidth: "2rem",
|
|
85
|
-
...
|
|
86
|
-
...
|
|
85
|
+
...o.borderRadius(O),
|
|
86
|
+
...o.paddingInline(L),
|
|
87
87
|
"& svg": {
|
|
88
88
|
width: "1rem",
|
|
89
89
|
height: "1rem"
|
|
@@ -92,32 +92,35 @@ const j = p({
|
|
|
92
92
|
lg: {
|
|
93
93
|
height: "2.5rem",
|
|
94
94
|
minWidth: "2.5rem",
|
|
95
|
-
...
|
|
96
|
-
...
|
|
95
|
+
...o.borderRadius(w),
|
|
96
|
+
...o.paddingInline("0.625rem"),
|
|
97
97
|
"& svg": {
|
|
98
98
|
width: "1.25rem",
|
|
99
99
|
height: "1.25rem"
|
|
100
100
|
}
|
|
101
101
|
}
|
|
102
102
|
});
|
|
103
|
-
function B(
|
|
104
|
-
const e = i.Children.toArray(
|
|
103
|
+
function B(t) {
|
|
104
|
+
const e = i.Children.toArray(t).filter(i.isValidElement);
|
|
105
105
|
return e.length === 1 ? e[0] : void 0;
|
|
106
106
|
}
|
|
107
|
-
function z(
|
|
108
|
-
const
|
|
109
|
-
return
|
|
107
|
+
function z(t, e) {
|
|
108
|
+
const r = t ? B(e) : void 0;
|
|
109
|
+
return r ? {
|
|
110
|
+
render: r,
|
|
111
|
+
nativeButton: typeof r.type != "string" || r.type === "button"
|
|
112
|
+
} : { children: e };
|
|
110
113
|
}
|
|
111
|
-
function F(
|
|
114
|
+
function F(t) {
|
|
112
115
|
return (e) => {
|
|
113
|
-
var
|
|
114
|
-
|
|
116
|
+
var r;
|
|
117
|
+
t == null || t(e), e.defaultPrevented && ((r = e.preventBaseUIHandler) == null || r.call(e));
|
|
115
118
|
};
|
|
116
119
|
}
|
|
117
120
|
const _ = i.forwardRef(
|
|
118
121
|
function({
|
|
119
122
|
className: e,
|
|
120
|
-
variant:
|
|
123
|
+
variant: r = "default",
|
|
121
124
|
size: a = "base",
|
|
122
125
|
asChild: s = !1,
|
|
123
126
|
children: d,
|
|
@@ -130,10 +133,10 @@ const _ = i.forwardRef(
|
|
|
130
133
|
{
|
|
131
134
|
ref: c,
|
|
132
135
|
"data-slot": "toggle",
|
|
133
|
-
[
|
|
134
|
-
className:
|
|
136
|
+
[h]: "toggle",
|
|
137
|
+
className: f(
|
|
135
138
|
n.root,
|
|
136
|
-
n[
|
|
139
|
+
n[r],
|
|
137
140
|
n[a],
|
|
138
141
|
e
|
|
139
142
|
),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toggle.js","sources":["../../../src/components/toggle/toggle.tsx"],"sourcesContent":["import * as React from \"react\"\nimport { Toggle as TogglePrimitive } from \"@base-ui/react/toggle\"\nimport { mergeClasses, shorthands } from \"@epilot/zenith-runtime/v1\"\n\nimport { SLOT_ATTR } from \"../../renderer/contract\"\nimport { makeZenithStyles } from \"../../styles/make-styles\"\nimport { nativeControl } from \"../../styles/host-reset\"\nimport * as tokens from \"../../tokens\"\n\nexport type ToggleVariant = \"default\" | \"destructive\"\nexport type ToggleSize = \"sm\" | \"base\" | \"lg\"\n\nexport const useToggleStyles = makeZenithStyles({\n root: {\n ...nativeControl,\n display: \"inline-flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n ...shorthands.gap(tokens.spacingElement1),\n flexShrink: 0,\n cursor: \"pointer\",\n fontWeight: tokens.fontWeightSemibold,\n transitionProperty: \"all\",\n transitionDuration: \"150ms\",\n transitionTimingFunction: \"cubic-bezier(0.4, 0, 0.2, 1)\",\n \"@media (prefers-reduced-motion: reduce)\": {\n transitionDuration: \"0.01ms\",\n },\n ...shorthands.borderStyle(\"none\"),\n ...shorthands.borderWidth(0),\n \":focus-visible\": {\n outlineWidth: \"2px\",\n outlineOffset: \"2px\",\n outlineStyle: \"solid\",\n outlineColor: tokens.focusRing,\n },\n \":disabled, &[data-disabled]\": {\n pointerEvents: \"none\",\n opacity: 0.5,\n },\n \"& svg\": {\n flexShrink: 0,\n pointerEvents: \"none\",\n color: \"currentColor\",\n },\n },\n // Variants\n default: {\n backgroundColor: \"transparent\",\n color: tokens.accentLight,\n \":hover\": {\n backgroundColor: tokens.accentSoft,\n },\n \"&[data-pressed]\": {\n backgroundColor: tokens.accentA3,\n color: tokens.accentOnSoft,\n },\n \"&[data-pressed]:hover\": {\n backgroundColor: tokens.accentA4,\n },\n },\n destructive: {\n backgroundColor: \"transparent\",\n color: tokens.errorLight,\n \":hover\": {\n backgroundColor: tokens.errorSoft,\n },\n \"&[data-pressed]\": {\n backgroundColor: tokens.errorA3,\n color: tokens.errorOnSoft,\n },\n \"&[data-pressed]:hover\": {\n backgroundColor: tokens.errorA4,\n },\n },\n // Sizes\n sm: {\n height: \"1.5rem\",\n minWidth: \"1.5rem\",\n ...shorthands.borderRadius(tokens.radiusLg),\n ...shorthands.paddingInline(\"0.375rem\"),\n \"& svg\": {\n width: \"0.875rem\",\n height: \"0.875rem\",\n },\n },\n base: {\n height: \"2rem\",\n minWidth: \"2rem\",\n ...shorthands.borderRadius(tokens.radiusXl),\n ...shorthands.paddingInline(tokens.spacingElement2),\n \"& svg\": {\n width: \"1rem\",\n height: \"1rem\",\n },\n },\n lg: {\n height: \"2.5rem\",\n minWidth: \"2.5rem\",\n ...shorthands.borderRadius(tokens.radius2xl),\n ...shorthands.paddingInline(\"0.625rem\"),\n \"& svg\": {\n width: \"1.25rem\",\n height: \"1.25rem\",\n },\n },\n})\n\n/**\n * What `onPressedChange` receives as its second argument: Base UI's change details, declared in\n * Zenith so that none of Base UI's internals reach the published declarations, where TypeScript 4\n * cannot parse them.\n */\nexport type ToggleChangeDetails = {\n /** A toggle only changes through a person's click or key press, which Base UI calls `none`. */\n reason: \"none\"\n /** The native event behind the change. */\n event: Event\n /** Keeps the current state. */\n cancel: () => void\n /** Lets the event propagate where the toggle would otherwise stop it. */\n allowPropagation: () => void\n isCanceled: boolean\n isPropagationAllowed: boolean\n /** The element that caused the change, if any. */\n trigger: Element | undefined\n}\n\n/**\n * A single child can arrive wrapped in a one-element array depending on the JSX runtime (MDX call\n * sites do this). Radix's Slot normalised that, so the `asChild` shim has to as well.\n */\nexport function onlySlotChild(children: React.ReactNode) {\n const elements = React.Children.toArray(children).filter(React.isValidElement)\n return elements.length === 1 ? elements[0] : undefined\n}\n\n/**\n * Base UI's `render` in place of Radix's `asChild` on a button part: the single child element\n * becomes the part and keeps its own children. Anything but a `<button>` is told so, since Base UI\n * otherwise warns that a native button was expected.\n */\nexport function buttonSlotProps(asChild: boolean, children: React.ReactNode) {\n const child = asChild ? onlySlotChild(children) : undefined\n return child\n ? { render: child, nativeButton: child.type === \"button\" }\n : { children }\n}\n\n/**\n * Radix skipped its own handling when the caller's `onClick` prevented default; Base UI runs the\n * caller's handler first and then its own unless told not to.\n */\nexport function respectDefaultPrevented<E extends React.MouseEvent>(\n onClick: ((event: E) => void) | undefined\n) {\n return (event: E) => {\n onClick?.(event)\n if (event.defaultPrevented)\n (\n event as E & { preventBaseUIHandler?: () => void }\n ).preventBaseUIHandler?.()\n }\n}\n\n/** The toggle's own props, declared in Zenith rather than taken from Base UI. */\ntype ToggleOwnProps = {\n /** Whether the toggle is pressed, when it is controlled. */\n pressed?: boolean\n /** Whether the toggle is pressed at first, when it is uncontrolled. */\n defaultPressed?: boolean\n /**\n * Called with the new pressed state and Base UI's change details.\n * `eventDetails.cancel()` keeps the current state.\n */\n onPressedChange?: (\n pressed: boolean,\n eventDetails: ToggleChangeDetails\n ) => void\n /** Renders the single child element as the toggle, merging its props and behaviour onto it. */\n asChild?: boolean\n}\n\nexport type ToggleProps = Omit<\n React.ComponentPropsWithRef<\"button\">,\n keyof ToggleOwnProps\n> &\n ToggleOwnProps & {\n variant?: ToggleVariant\n size?: ToggleSize\n }\n\nexport const Toggle = React.forwardRef<HTMLButtonElement, ToggleProps>(\n function Toggle(\n {\n className,\n variant = \"default\",\n size = \"base\",\n asChild = false,\n children,\n onClick,\n ...props\n },\n ref\n ) {\n const styles = useToggleStyles()\n\n return (\n <TogglePrimitive\n ref={ref}\n data-slot=\"toggle\"\n {...{ [SLOT_ATTR]: \"toggle\" }}\n className={mergeClasses(\n styles.root,\n styles[variant],\n styles[size],\n className\n )}\n // Base UI types the handlers with its own event wrapper; the element is the same <button>,\n // so the caller's button-typed handlers receive exactly the events they declare.\n {...(props as TogglePrimitive.Props)}\n onClick={respectDefaultPrevented(onClick)}\n {...buttonSlotProps(asChild, children)}\n />\n )\n }\n)\n"],"names":["useToggleStyles","makeZenithStyles","nativeControl","shorthands","tokens.spacingElement1","tokens.fontWeightSemibold","tokens.focusRing","tokens.accentLight","tokens.accentSoft","tokens.accentA3","tokens.accentOnSoft","tokens.accentA4","tokens.errorLight","tokens.errorSoft","tokens.errorA3","tokens.errorOnSoft","tokens.errorA4","tokens.radiusLg","tokens.radiusXl","tokens.spacingElement2","tokens.radius2xl","onlySlotChild","children","elements","React","buttonSlotProps","asChild","child","respectDefaultPrevented","onClick","event","_a","Toggle","className","variant","size","props","ref","styles","jsx","TogglePrimitive","SLOT_ATTR","mergeClasses"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"toggle.js","sources":["../../../src/components/toggle/toggle.tsx"],"sourcesContent":["import * as React from \"react\"\nimport { Toggle as TogglePrimitive } from \"@base-ui/react/toggle\"\nimport { mergeClasses, shorthands } from \"@epilot/zenith-runtime/v1\"\n\nimport { SLOT_ATTR } from \"../../renderer/contract\"\nimport { makeZenithStyles } from \"../../styles/make-styles\"\nimport { nativeControl } from \"../../styles/host-reset\"\nimport * as tokens from \"../../tokens\"\n\nexport type ToggleVariant = \"default\" | \"destructive\"\nexport type ToggleSize = \"sm\" | \"base\" | \"lg\"\n\nexport const useToggleStyles = makeZenithStyles({\n root: {\n ...nativeControl,\n display: \"inline-flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n ...shorthands.gap(tokens.spacingElement1),\n flexShrink: 0,\n cursor: \"pointer\",\n fontWeight: tokens.fontWeightSemibold,\n transitionProperty: \"all\",\n transitionDuration: \"150ms\",\n transitionTimingFunction: \"cubic-bezier(0.4, 0, 0.2, 1)\",\n \"@media (prefers-reduced-motion: reduce)\": {\n transitionDuration: \"0.01ms\",\n },\n ...shorthands.borderStyle(\"none\"),\n ...shorthands.borderWidth(0),\n \":focus-visible\": {\n outlineWidth: \"2px\",\n outlineOffset: \"2px\",\n outlineStyle: \"solid\",\n outlineColor: tokens.focusRing,\n },\n \":disabled, &[data-disabled]\": {\n pointerEvents: \"none\",\n opacity: 0.5,\n },\n \"& svg\": {\n flexShrink: 0,\n pointerEvents: \"none\",\n color: \"currentColor\",\n },\n },\n // Variants\n default: {\n backgroundColor: \"transparent\",\n color: tokens.accentLight,\n \":hover\": {\n backgroundColor: tokens.accentSoft,\n },\n \"&[data-pressed]\": {\n backgroundColor: tokens.accentA3,\n color: tokens.accentOnSoft,\n },\n \"&[data-pressed]:hover\": {\n backgroundColor: tokens.accentA4,\n },\n },\n destructive: {\n backgroundColor: \"transparent\",\n color: tokens.errorLight,\n \":hover\": {\n backgroundColor: tokens.errorSoft,\n },\n \"&[data-pressed]\": {\n backgroundColor: tokens.errorA3,\n color: tokens.errorOnSoft,\n },\n \"&[data-pressed]:hover\": {\n backgroundColor: tokens.errorA4,\n },\n },\n // Sizes\n sm: {\n height: \"1.5rem\",\n minWidth: \"1.5rem\",\n ...shorthands.borderRadius(tokens.radiusLg),\n ...shorthands.paddingInline(\"0.375rem\"),\n \"& svg\": {\n width: \"0.875rem\",\n height: \"0.875rem\",\n },\n },\n base: {\n height: \"2rem\",\n minWidth: \"2rem\",\n ...shorthands.borderRadius(tokens.radiusXl),\n ...shorthands.paddingInline(tokens.spacingElement2),\n \"& svg\": {\n width: \"1rem\",\n height: \"1rem\",\n },\n },\n lg: {\n height: \"2.5rem\",\n minWidth: \"2.5rem\",\n ...shorthands.borderRadius(tokens.radius2xl),\n ...shorthands.paddingInline(\"0.625rem\"),\n \"& svg\": {\n width: \"1.25rem\",\n height: \"1.25rem\",\n },\n },\n})\n\n/**\n * What `onPressedChange` receives as its second argument: Base UI's change details, declared in\n * Zenith so that none of Base UI's internals reach the published declarations, where TypeScript 4\n * cannot parse them.\n */\nexport type ToggleChangeDetails = {\n /** A toggle only changes through a person's click or key press, which Base UI calls `none`. */\n reason: \"none\"\n /** The native event behind the change. */\n event: Event\n /** Keeps the current state. */\n cancel: () => void\n /** Lets the event propagate where the toggle would otherwise stop it. */\n allowPropagation: () => void\n isCanceled: boolean\n isPropagationAllowed: boolean\n /** The element that caused the change, if any. */\n trigger: Element | undefined\n}\n\n/**\n * A single child can arrive wrapped in a one-element array depending on the JSX runtime (MDX call\n * sites do this). Radix's Slot normalised that, so the `asChild` shim has to as well.\n */\nexport function onlySlotChild(children: React.ReactNode) {\n const elements = React.Children.toArray(children).filter(React.isValidElement)\n return elements.length === 1 ? elements[0] : undefined\n}\n\n/**\n * Base UI's `render` in place of Radix's `asChild` on a button part: the single child element\n * becomes the part and keeps its own children. A component child counts as a native button, like\n * TabsTrigger, so Base UI stamps `type=\"button\"` and `disabled`; only a child that is another\n * intrinsic element is told otherwise, since Base UI would warn that a native button was expected.\n */\nexport function buttonSlotProps(asChild: boolean, children: React.ReactNode) {\n const child = asChild ? onlySlotChild(children) : undefined\n return child\n ? {\n render: child,\n nativeButton: typeof child.type !== \"string\" || child.type === \"button\",\n }\n : { children }\n}\n\n/**\n * Radix skipped its own handling when the caller's `onClick` prevented default; Base UI runs the\n * caller's handler first and then its own unless told not to.\n */\nexport function respectDefaultPrevented<E extends React.MouseEvent>(\n onClick: ((event: E) => void) | undefined\n) {\n return (event: E) => {\n onClick?.(event)\n if (event.defaultPrevented)\n (\n event as E & { preventBaseUIHandler?: () => void }\n ).preventBaseUIHandler?.()\n }\n}\n\n/** The toggle's own props, declared in Zenith rather than taken from Base UI. */\ntype ToggleOwnProps = {\n /** Whether the toggle is pressed, when it is controlled. */\n pressed?: boolean\n /** Whether the toggle is pressed at first, when it is uncontrolled. */\n defaultPressed?: boolean\n /**\n * Called with the new pressed state and Base UI's change details.\n * `eventDetails.cancel()` keeps the current state.\n */\n onPressedChange?: (\n pressed: boolean,\n eventDetails: ToggleChangeDetails\n ) => void\n /** Renders the single child element as the toggle, merging its props and behaviour onto it. */\n asChild?: boolean\n}\n\nexport type ToggleProps = Omit<\n React.ComponentPropsWithRef<\"button\">,\n keyof ToggleOwnProps\n> &\n ToggleOwnProps & {\n variant?: ToggleVariant\n size?: ToggleSize\n }\n\nexport const Toggle = React.forwardRef<HTMLButtonElement, ToggleProps>(\n function Toggle(\n {\n className,\n variant = \"default\",\n size = \"base\",\n asChild = false,\n children,\n onClick,\n ...props\n },\n ref\n ) {\n const styles = useToggleStyles()\n\n return (\n <TogglePrimitive\n ref={ref}\n data-slot=\"toggle\"\n {...{ [SLOT_ATTR]: \"toggle\" }}\n className={mergeClasses(\n styles.root,\n styles[variant],\n styles[size],\n className\n )}\n // Base UI types the handlers with its own event wrapper; the element is the same <button>,\n // so the caller's button-typed handlers receive exactly the events they declare.\n {...(props as TogglePrimitive.Props)}\n onClick={respectDefaultPrevented(onClick)}\n {...buttonSlotProps(asChild, children)}\n />\n )\n }\n)\n"],"names":["useToggleStyles","makeZenithStyles","nativeControl","shorthands","tokens.spacingElement1","tokens.fontWeightSemibold","tokens.focusRing","tokens.accentLight","tokens.accentSoft","tokens.accentA3","tokens.accentOnSoft","tokens.accentA4","tokens.errorLight","tokens.errorSoft","tokens.errorA3","tokens.errorOnSoft","tokens.errorA4","tokens.radiusLg","tokens.radiusXl","tokens.spacingElement2","tokens.radius2xl","onlySlotChild","children","elements","React","buttonSlotProps","asChild","child","respectDefaultPrevented","onClick","event","_a","Toggle","className","variant","size","props","ref","styles","jsx","TogglePrimitive","SLOT_ATTR","mergeClasses"],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -5152,7 +5152,7 @@ type ScrollOverflowProps = Omit<React.ComponentProps<"div">, "dir"> & UseScrollO
|
|
|
5152
5152
|
gradients?: boolean;
|
|
5153
5153
|
/** Gradient width in pixels. Default: 40 */
|
|
5154
5154
|
gradientWidth?: number;
|
|
5155
|
-
/** Gradient base color (CSS value). Should match the parent background. Default:
|
|
5155
|
+
/** Gradient base color (CSS value). Should match the parent background. Default: the Zenith body token. */
|
|
5156
5156
|
gradientColor?: string;
|
|
5157
5157
|
/** Show inset shadow on overflow edges. Default: true */
|
|
5158
5158
|
shadow?: boolean;
|
package/dist/renderer/served.js
CHANGED
|
@@ -1,34 +1,39 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
1
|
+
import * as o from "react";
|
|
2
|
+
import { BASE_UI_FAMILIES as n, getTokenSheet as r, assertCompatibility as s, BASE_UI_VERSION as a } from "@epilot/zenith-runtime/v1";
|
|
3
|
+
import { EXPECTED_TOKEN_SHEETS as h, EXPECTED_PRIMITIVES as m, EXPECTED_CLASS_SALT as p, EXPECTED_ABI_VERSION as E } from "./runtime-contract.generated.js";
|
|
3
4
|
let i = !1;
|
|
4
|
-
function
|
|
5
|
+
function u() {
|
|
5
6
|
if (!i) {
|
|
6
|
-
if (i = !0,
|
|
7
|
+
if (i = !0, n === void 0 || typeof r != "function")
|
|
7
8
|
throw new Error(
|
|
8
9
|
"Incompatible Zenith runtime: @epilot/zenith-react takes its Base UI primitives and core token sheet from @epilot/zenith-runtime/v1, but the runtime on this page serves neither (it predates the shared-primitives runtime). Deploy a runtime release that serves them (root-config) before this application, or bundle the runtime."
|
|
9
10
|
);
|
|
10
|
-
|
|
11
|
+
s({
|
|
11
12
|
abiVersion: E,
|
|
12
13
|
classSalt: p,
|
|
13
|
-
primitives:
|
|
14
|
-
tokenSheets:
|
|
14
|
+
primitives: m,
|
|
15
|
+
tokenSheets: h,
|
|
16
|
+
// The served primitives render inside this package's components, so they must run on this
|
|
17
|
+
// package's React: the page's single copy. A runtime bundled with a React of its own, or with
|
|
18
|
+
// a `react/jsx-runtime` of another React version, is named here with the host rule (#116).
|
|
19
|
+
react: o
|
|
15
20
|
});
|
|
16
21
|
}
|
|
17
22
|
}
|
|
18
|
-
|
|
19
|
-
function
|
|
23
|
+
u();
|
|
24
|
+
function f(e, t) {
|
|
20
25
|
if (t === void 0)
|
|
21
26
|
throw new Error(
|
|
22
|
-
`Incompatible Zenith runtime primitives: this build of @epilot/zenith-react needs Base UI "${e}", which the runtime on this page (Base UI ${
|
|
27
|
+
`Incompatible Zenith runtime primitives: this build of @epilot/zenith-react needs Base UI "${e}", which the runtime on this page (Base UI ${a}) does not serve. Deploy a runtime release that serves it (root-config) before this application.`
|
|
23
28
|
);
|
|
24
29
|
return t;
|
|
25
30
|
}
|
|
26
|
-
function
|
|
31
|
+
function S(e) {
|
|
27
32
|
return r(e);
|
|
28
33
|
}
|
|
29
34
|
export {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
35
|
+
u as ensureServedContract,
|
|
36
|
+
f as servedFamily,
|
|
37
|
+
S as servedTokenSheet
|
|
33
38
|
};
|
|
34
39
|
//# sourceMappingURL=served.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"served.js","sources":["../../src/renderer/served.ts"],"sourcesContent":["import {\n assertCompatibility,\n BASE_UI_FAMILIES,\n BASE_UI_VERSION,\n getTokenSheet,\n type ServedFamily,\n} from \"@epilot/zenith-runtime/v1\"\nimport {\n EXPECTED_ABI_VERSION,\n EXPECTED_CLASS_SALT,\n EXPECTED_PRIMITIVES,\n EXPECTED_TOKEN_SHEETS,\n} from \"./runtime-contract.generated\"\n\n/**\n * The seam where this package meets the runtime that serves its Base UI primitives and its core\n * token sheet (D13). It runs once, at module initialisation, before any served family or sheet is\n * read (`vite.shared-primitives.ts` makes every rewritten module import it first): a runtime that\n * predates the shared primitives,
|
|
1
|
+
{"version":3,"file":"served.js","sources":["../../src/renderer/served.ts"],"sourcesContent":["import * as React from \"react\"\nimport {\n assertCompatibility,\n BASE_UI_FAMILIES,\n BASE_UI_VERSION,\n getTokenSheet,\n type ServedFamily,\n} from \"@epilot/zenith-runtime/v1\"\nimport {\n EXPECTED_ABI_VERSION,\n EXPECTED_CLASS_SALT,\n EXPECTED_PRIMITIVES,\n EXPECTED_TOKEN_SHEETS,\n} from \"./runtime-contract.generated\"\n\n/**\n * The seam where this package meets the runtime that serves its Base UI primitives and its core\n * token sheet (D13). It runs once, at module initialisation, before any served family or sheet is\n * read (`vite.shared-primitives.ts` makes every rewritten module import it first): a runtime that\n * predates the shared primitives, that serves another family set or another sheet, or that runs on\n * a React of its own rather than this package's (#116), fails here with a named cause — never as a\n * TypeError inside a component, never as a blank page (D3).\n *\n * Named imports, not a namespace: a namespace object held in a variable makes a bundler keep every\n * export of `/v1`, and with it every served family, in an application that bundles the runtime.\n * Under SystemJS (the import-map host) an export the runtime lacks arrives as `undefined`, which is\n * what the first check reads; under native ESM the engine names the missing export itself.\n */\nlet asserted = false\n\n/**\n * Validates the contract once. Called here at module initialisation, and from `contract.ts` as a\n * statement: a bare side-effect import of this file would be dropped by a consumer's bundler, since\n * the package declares `sideEffects: false`, while a call is kept.\n */\nexport function ensureServedContract(): void {\n if (asserted) return\n asserted = true\n if (\n (BASE_UI_FAMILIES as unknown) === undefined ||\n typeof (getTokenSheet as unknown) !== \"function\"\n ) {\n throw new Error(\n \"Incompatible Zenith runtime: @epilot/zenith-react takes its Base UI primitives and core token \" +\n \"sheet from @epilot/zenith-runtime/v1, but the runtime on this page serves neither (it predates \" +\n \"the shared-primitives runtime). Deploy a runtime release that serves them (root-config) before \" +\n \"this application, or bundle the runtime.\"\n )\n }\n assertCompatibility({\n abiVersion: EXPECTED_ABI_VERSION,\n classSalt: EXPECTED_CLASS_SALT,\n primitives: EXPECTED_PRIMITIVES,\n tokenSheets: EXPECTED_TOKEN_SHEETS,\n // The served primitives render inside this package's components, so they must run on this\n // package's React: the page's single copy. A runtime bundled with a React of its own, or with\n // a `react/jsx-runtime` of another React version, is named here with the host rule (#116).\n react: React,\n })\n}\n\nensureServedContract()\n\n/** A served family, or the named cause when the runtime on the page does not serve it. */\nexport function servedFamily(\n key: string,\n family: ServedFamily | undefined\n): ServedFamily {\n if (family === undefined) {\n throw new Error(\n `Incompatible Zenith runtime primitives: this build of @epilot/zenith-react needs Base UI \"${key}\", ` +\n `which the runtime on this page (Base UI ${BASE_UI_VERSION}) does not serve. Deploy a ` +\n `runtime release that serves it (root-config) before this application.`\n )\n }\n return family\n}\n\n/** The served sheet for a hash this package was compiled against; the runtime names a missing one. */\nexport function servedTokenSheet(hash: string): string {\n return getTokenSheet(hash)\n}\n"],"names":["asserted","ensureServedContract","BASE_UI_FAMILIES","getTokenSheet","assertCompatibility","EXPECTED_ABI_VERSION","EXPECTED_CLASS_SALT","EXPECTED_PRIMITIVES","EXPECTED_TOKEN_SHEETS","React","servedFamily","key","family","BASE_UI_VERSION","servedTokenSheet","hash"],"mappings":";;;AA4BA,IAAIA,IAAW;AAOR,SAASC,IAA6B;AAC3C,MAAI,CAAAD,GAEJ;AAAA,QADAA,IAAW,IAERE,MAAiC,UAClC,OAAQC,KAA8B;AAEtC,YAAM,IAAI;AAAA,QACR;AAAA,MAAA;AAMJ,IAAAC,EAAoB;AAAA,MAClB,YAAYC;AAAA,MACZ,WAAWC;AAAA,MACX,YAAYC;AAAA,MACZ,aAAaC;AAAA;AAAA;AAAA;AAAA,MAIb,OAAOC;AAAA,IAAA,CACR;AAAA;AACH;AAEAR,EAAA;AAGO,SAASS,EACdC,GACAC,GACc;AACd,MAAIA,MAAW;AACb,UAAM,IAAI;AAAA,MACR,6FAA6FD,CAAG,8CACnDE,CAAe;AAAA,IAAA;AAIhE,SAAOD;AACT;AAGO,SAASE,EAAiBC,GAAsB;AACrD,SAAOZ,EAAcY,CAAI;AAC3B;"}
|
package/dist/tokens.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$comment": "GENERATED. Design token values for consumers that do not render this package's React components. Compile these into your own CSS under your own names at your own build time; do not reference this package's runtime custom properties, whose names are content-hashed and change with their values. `themes.light`/`themes.dark` are mode-resolved; `densities.compact`/`densities.normal`/`densities.comfortable` resolve the component layer's geometry tokens (WP9A-ACCEPTANCE-WP9B1) and carry no mode of their own — do not read a value out of `themes` for a key that only exists under `densities`, or the reverse. This file is copied verbatim to this package's public `tokens.json` export (`package.json`'s `build` script, `cp src/generated/values.generated.json dist/tokens.json`).",
|
|
3
3
|
"package": "@epilot/zenith-react",
|
|
4
|
-
"version": "0.2.0-next.
|
|
4
|
+
"version": "0.2.0-next.5",
|
|
5
5
|
"themes": {
|
|
6
6
|
"light": {
|
|
7
7
|
"accent-1": "#fcfdfe",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@epilot/zenith-react",
|
|
3
|
-
"version": "0.2.0-next.
|
|
3
|
+
"version": "0.2.0-next.5",
|
|
4
4
|
"description": "Zenith React components, styled with Griffel and designed for isolated micro-frontend delivery",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
},
|
|
31
31
|
"./tokens.json": "./dist/tokens.json",
|
|
32
32
|
"./testing/jest-preset.cjs": "./testing/jest-preset.cjs",
|
|
33
|
+
"./testing/setup.cjs": "./testing/setup.cjs",
|
|
33
34
|
"./package.json": "./package.json"
|
|
34
35
|
},
|
|
35
36
|
"files": [
|
|
@@ -92,7 +93,7 @@
|
|
|
92
93
|
"verify:consumer": "bun ../../scripts/verify-packed-consumer.ts"
|
|
93
94
|
},
|
|
94
95
|
"dependencies": {
|
|
95
|
-
"@epilot/zenith-runtime": "0.2.0-next.
|
|
96
|
+
"@epilot/zenith-runtime": "0.2.0-next.3"
|
|
96
97
|
},
|
|
97
98
|
"peerDependencies": {
|
|
98
99
|
"react": ">=18",
|
|
@@ -103,7 +104,7 @@
|
|
|
103
104
|
"@dnd-kit/core": "6.3.1",
|
|
104
105
|
"@dnd-kit/sortable": "10.0.0",
|
|
105
106
|
"@dnd-kit/utilities": "3.2.2",
|
|
106
|
-
"@epilot/zenith-tokens": "0.2.0-next.
|
|
107
|
+
"@epilot/zenith-tokens": "0.2.0-next.5",
|
|
107
108
|
"@tanstack/react-table": "8.21.3",
|
|
108
109
|
"@tanstack/react-virtual": "3.11.2",
|
|
109
110
|
"@testing-library/react": "^16.3.2",
|
|
@@ -29,6 +29,22 @@ export const RULES = [
|
|
|
29
29
|
description:
|
|
30
30
|
'A decorative (non-core) token family is used, with varying confidence that a correctly-scoped install call covers it (high confidence for no local evidence at all, down to low for an unresolved dynamic path). This rule NEVER reports severity "error": local absence of an install call does not prove a bootstrap/provider install does not exist elsewhere — this is single-source analysis with no cross-file tracing, so it cannot assert a definite defect, only flag what needs verification. Core families (accent/gray/error/warning/success/info) are excluded — they load by default. Requires an injected TypeScript module (see requiresTypeScript).',
|
|
31
31
|
},
|
|
32
|
+
{
|
|
33
|
+
code: "radix-data-state-selector",
|
|
34
|
+
title: "Radix-era data-state selector or query",
|
|
35
|
+
defaultSeverity: "warning",
|
|
36
|
+
requiresTypeScript: false,
|
|
37
|
+
description:
|
|
38
|
+
"A `data-state` attribute selector, Tailwind `data-[state=…]` variant, JSX attribute or test query reads a state Radix set (`open`, `closed`, `checked`, `active`, `on`, …). The Base UI parts carry one attribute per state instead (`data-open`, `data-checked`, `data-active`, `data-pressed`, …), so the selector stops matching after the upgrade; the suggestion names the replacement per family (RADIX_RENAMES, the upgrade guide's table). A warning, not an error: Zenith's own List, Table and DataTable keep their `data-state` values, and a selector alone does not say which element it targets.",
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
code: "radix-css-variable",
|
|
42
|
+
title: "Radix-era --radix-* CSS variable",
|
|
43
|
+
defaultSeverity: "error",
|
|
44
|
+
requiresTypeScript: false,
|
|
45
|
+
description:
|
|
46
|
+
"A `--radix-*` custom property (`--radix-popover-content-transform-origin`, `--radix-select-trigger-width`, `--radix-accordion-content-height`, …) is read. Base UI publishes none of them; the positioner and panel variables have new names (`--transform-origin`, `--available-width`, `--anchor-width`, `--accordion-panel-height`, …), which the suggestion gives.",
|
|
47
|
+
},
|
|
32
48
|
]
|
|
33
49
|
|
|
34
50
|
/** ts/tsx/js/jsx — the extensions `unestablished-palette-install` can parse. Exported so the
|
|
@@ -165,9 +181,277 @@ function textualFindings(identity, fileLabel, raw) {
|
|
|
165
181
|
)
|
|
166
182
|
}
|
|
167
183
|
|
|
184
|
+
findings.push(...radixFindings(identity, fileLabel, stripped, lineOf, colOf))
|
|
185
|
+
return findings
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// ---------------------------------------------------------------------------
|
|
189
|
+
// Rule 4 + 5: Radix-era state attributes and CSS variables (textual, comment-stripped)
|
|
190
|
+
// ---------------------------------------------------------------------------
|
|
191
|
+
|
|
192
|
+
/** The Zenith parts an overlay-style `data-state="open|closed"` became `data-open`/`data-closed` on. */
|
|
193
|
+
const OPEN_CLOSED_PARTS = [
|
|
194
|
+
"Dialog",
|
|
195
|
+
"AlertDialog",
|
|
196
|
+
"Drawer",
|
|
197
|
+
"Popover",
|
|
198
|
+
"FieldCombobox",
|
|
199
|
+
"Tooltip",
|
|
200
|
+
"DropdownMenu content and sub content",
|
|
201
|
+
"Select content",
|
|
202
|
+
"Accordion item and panel",
|
|
203
|
+
]
|
|
204
|
+
const POPUP_TRIGGERS = [
|
|
205
|
+
"Dialog trigger",
|
|
206
|
+
"AlertDialog trigger",
|
|
207
|
+
"Drawer trigger",
|
|
208
|
+
"Popover trigger",
|
|
209
|
+
"Tooltip trigger",
|
|
210
|
+
"DropdownMenu trigger and sub trigger",
|
|
211
|
+
"Select trigger",
|
|
212
|
+
]
|
|
213
|
+
const CHECKED_PARTS = [
|
|
214
|
+
"Checkbox",
|
|
215
|
+
"Switch (root and thumb)",
|
|
216
|
+
"Radio",
|
|
217
|
+
"SegmentedControl",
|
|
218
|
+
"DropdownMenu checkbox and radio items",
|
|
219
|
+
]
|
|
220
|
+
|
|
221
|
+
/** The Radix primitive names Zenith's positioned families used in their `--radix-*` variables. */
|
|
222
|
+
export const RADIX_VARIABLE_FAMILIES = [
|
|
223
|
+
{ radix: "popover", zenith: "Popover, FieldCombobox" },
|
|
224
|
+
{ radix: "dropdown-menu", zenith: "DropdownMenu" },
|
|
225
|
+
{ radix: "select", zenith: "Select" },
|
|
226
|
+
{ radix: "tooltip", zenith: "Tooltip" },
|
|
227
|
+
]
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* The attribute and CSS-variable renames of the Base UI port (DECISIONS.md D12), one row per
|
|
231
|
+
* Radix name and Base UI replacement. The rules below match on these, and
|
|
232
|
+
* `renderRadixRenamesMarkdown()` prints the upgrade guide's table from them, so the two cannot
|
|
233
|
+
* drift. Family names are Zenith's; `<family>` in a variable name stands for one of
|
|
234
|
+
* RADIX_VARIABLE_FAMILIES. A `baseUi` of null means the state has no attribute any more: it is
|
|
235
|
+
* the part without the other one (`:not([data-active])`).
|
|
236
|
+
*/
|
|
237
|
+
export const RADIX_RENAMES = {
|
|
238
|
+
attributes: [
|
|
239
|
+
{ value: "open", baseUi: "data-open", parts: OPEN_CLOSED_PARTS },
|
|
240
|
+
{ value: "open", baseUi: "data-popup-open", parts: POPUP_TRIGGERS },
|
|
241
|
+
{ value: "open", baseUi: "data-panel-open", parts: ["Accordion trigger"] },
|
|
242
|
+
{ value: "closed", baseUi: "data-closed", parts: OPEN_CLOSED_PARTS },
|
|
243
|
+
{ value: "delayed-open", baseUi: "data-open", parts: ["Tooltip"] },
|
|
244
|
+
{ value: "instant-open", baseUi: "data-open", parts: ["Tooltip"] },
|
|
245
|
+
{ value: "checked", baseUi: "data-checked", parts: CHECKED_PARTS },
|
|
246
|
+
{ value: "checked", baseUi: "data-selected", parts: ["Select item"] },
|
|
247
|
+
{ value: "unchecked", baseUi: "data-unchecked", parts: CHECKED_PARTS },
|
|
248
|
+
{
|
|
249
|
+
value: "indeterminate",
|
|
250
|
+
baseUi: "data-indeterminate",
|
|
251
|
+
parts: ["Checkbox"],
|
|
252
|
+
},
|
|
253
|
+
{ value: "active", baseUi: "data-active", parts: ["Tabs trigger"] },
|
|
254
|
+
{
|
|
255
|
+
value: "inactive",
|
|
256
|
+
baseUi: null,
|
|
257
|
+
parts: ["Tabs trigger"],
|
|
258
|
+
note: "the trigger without `data-active`",
|
|
259
|
+
},
|
|
260
|
+
{ value: "on", baseUi: "data-pressed", parts: ["Toggle", "OptionCard"] },
|
|
261
|
+
{
|
|
262
|
+
value: "off",
|
|
263
|
+
baseUi: null,
|
|
264
|
+
parts: ["Toggle", "OptionCard"],
|
|
265
|
+
note: "the part without `data-pressed`",
|
|
266
|
+
},
|
|
267
|
+
],
|
|
268
|
+
variables: [
|
|
269
|
+
{
|
|
270
|
+
radix: "--radix-<family>-content-transform-origin",
|
|
271
|
+
baseUi: "--transform-origin",
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
radix: "--radix-<family>-content-available-width",
|
|
275
|
+
baseUi: "--available-width",
|
|
276
|
+
},
|
|
277
|
+
{
|
|
278
|
+
radix: "--radix-<family>-content-available-height",
|
|
279
|
+
baseUi: "--available-height",
|
|
280
|
+
},
|
|
281
|
+
{ radix: "--radix-<family>-trigger-width", baseUi: "--anchor-width" },
|
|
282
|
+
{ radix: "--radix-<family>-trigger-height", baseUi: "--anchor-height" },
|
|
283
|
+
{
|
|
284
|
+
radix: "--radix-accordion-content-height",
|
|
285
|
+
baseUi: "--accordion-panel-height",
|
|
286
|
+
parts: ["Accordion panel"],
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
radix: "--radix-accordion-content-width",
|
|
290
|
+
baseUi: "--accordion-panel-width",
|
|
291
|
+
parts: ["Accordion panel"],
|
|
292
|
+
},
|
|
293
|
+
],
|
|
294
|
+
/** Attributes the port left alone, for the table's footnote. */
|
|
295
|
+
unchanged: [
|
|
296
|
+
"data-highlighted",
|
|
297
|
+
"data-side",
|
|
298
|
+
"data-orientation",
|
|
299
|
+
"data-disabled",
|
|
300
|
+
"data-slot",
|
|
301
|
+
],
|
|
302
|
+
/** New under Base UI: a part entering or leaving carries one of these while it transitions. */
|
|
303
|
+
added: ["data-starting-style", "data-ending-style"],
|
|
304
|
+
/** Zenith's own parts that still set `data-state`, with the values they use. */
|
|
305
|
+
keptDataState: [
|
|
306
|
+
{ part: "List group", values: ["open", "closed"] },
|
|
307
|
+
{ part: "Table row and DataTable row", values: ["selected"] },
|
|
308
|
+
{ part: "DataTable body", values: ["loading", "error", "empty"] },
|
|
309
|
+
],
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
const RADIX_STATE_VALUES = new Set(RADIX_RENAMES.attributes.map((a) => a.value))
|
|
313
|
+
/** `open` and `closed` are also Zenith's List values, so a hit on them is only probably Radix. */
|
|
314
|
+
const SHARED_STATE_VALUES = new Set(
|
|
315
|
+
RADIX_RENAMES.keptDataState
|
|
316
|
+
.flatMap((k) => k.values)
|
|
317
|
+
.filter((v) => RADIX_STATE_VALUES.has(v))
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* `data-state` wherever consumer code spells it: an attribute selector (`[data-state="open"]`,
|
|
322
|
+
* `[data-state=open]`, inside a CSS-in-JS string too), a JSX attribute, a Tailwind variant
|
|
323
|
+
* (`data-[state=open]:…`) and a test query (`toHaveAttribute("data-state", "open")`,
|
|
324
|
+
* `getAttribute("data-state")`). Group 1: the Tailwind value; 3: the selector or attribute
|
|
325
|
+
* value; 4: the query's value argument.
|
|
326
|
+
*/
|
|
327
|
+
const DATA_STATE_RE =
|
|
328
|
+
/data-\[state=([a-z-]+)\]|\bdata-state\b(?![a-z0-9-])(?:\s*[~|^$*]?=\s*(["']?)([a-z-]+)\2|["']\s*,\s*["']([a-z-]+)["'])?/g
|
|
329
|
+
const RADIX_VAR_RE = /--radix-[a-z0-9-]+/g
|
|
330
|
+
|
|
331
|
+
const familyPattern = `(?<family>${RADIX_VARIABLE_FAMILIES.map((f) => f.radix).join("|")})`
|
|
332
|
+
const VARIABLE_MATCHERS = RADIX_RENAMES.variables.map((v) => ({
|
|
333
|
+
...v,
|
|
334
|
+
re: new RegExp(`^${v.radix.replace("<family>", familyPattern)}$`),
|
|
335
|
+
}))
|
|
336
|
+
|
|
337
|
+
const describeParts = (parts) => parts.join(", ")
|
|
338
|
+
|
|
339
|
+
/** What replaces `data-state="<value>"`, per family, or null for a value Radix never set. */
|
|
340
|
+
export function dataStateReplacement(value) {
|
|
341
|
+
if (!value) {
|
|
342
|
+
return "one attribute per state on the Base UI part: data-open/data-closed (overlays, Accordion), data-popup-open (triggers), data-checked/data-unchecked (Checkbox, Switch, Radio, SegmentedControl, menu items), data-selected (Select item), data-active (Tabs), data-pressed (Toggle, OptionCard); see the upgrade guide's rename table."
|
|
343
|
+
}
|
|
344
|
+
const rows = RADIX_RENAMES.attributes.filter((a) => a.value === value)
|
|
345
|
+
if (!rows.length) return null
|
|
346
|
+
return rows
|
|
347
|
+
.map((r) =>
|
|
348
|
+
r.baseUi
|
|
349
|
+
? `${r.baseUi} on ${describeParts(r.parts)}`
|
|
350
|
+
: `no attribute on ${describeParts(r.parts)} (${r.note})`
|
|
351
|
+
)
|
|
352
|
+
.join("; ")
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/** The Base UI variable for a `--radix-*` name, or null when no Zenith family used it. */
|
|
356
|
+
export function radixVariableReplacement(name) {
|
|
357
|
+
for (const m of VARIABLE_MATCHERS) {
|
|
358
|
+
const hit = m.re.exec(name)
|
|
359
|
+
if (!hit) continue
|
|
360
|
+
const family = hit.groups?.family
|
|
361
|
+
const parts = family
|
|
362
|
+
? RADIX_VARIABLE_FAMILIES.find((f) => f.radix === family).zenith
|
|
363
|
+
: describeParts(m.parts)
|
|
364
|
+
return { baseUi: m.baseUi, parts }
|
|
365
|
+
}
|
|
366
|
+
return null
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
function radixFindings(identity, fileLabel, stripped, lineOf, colOf) {
|
|
370
|
+
const findings = []
|
|
371
|
+
for (const m of stripped.matchAll(DATA_STATE_RE)) {
|
|
372
|
+
const value = m[1] ?? m[3] ?? m[4]
|
|
373
|
+
if (value && !RADIX_STATE_VALUES.has(value)) continue // Zenith's own values (selected, loading, …)
|
|
374
|
+
const replacement = dataStateReplacement(value)
|
|
375
|
+
findings.push(
|
|
376
|
+
makeFinding(identity, {
|
|
377
|
+
code: "radix-data-state-selector",
|
|
378
|
+
severity: "warning",
|
|
379
|
+
confidence:
|
|
380
|
+
!value || SHARED_STATE_VALUES.has(value) ? "medium" : "high",
|
|
381
|
+
file: fileLabel,
|
|
382
|
+
line: lineOf(m.index),
|
|
383
|
+
column: colOf(m.index),
|
|
384
|
+
evidence: m[0],
|
|
385
|
+
explanation: value
|
|
386
|
+
? `Radix set data-state="${value}"; the Base UI part carries a dedicated attribute instead, so this selector or query matches nothing after the upgrade.${SHARED_STATE_VALUES.has(value) ? " Zenith's List still uses this value on its groups; leave the selector if it targets one." : ""}`
|
|
387
|
+
: "Radix set data-state on every stateful part; the Base UI parts carry one attribute per state instead. Zenith's List, Table and DataTable keep their own data-state values, so check what this reads.",
|
|
388
|
+
suggestion: `Use ${replacement}.`,
|
|
389
|
+
})
|
|
390
|
+
)
|
|
391
|
+
}
|
|
392
|
+
for (const m of stripped.matchAll(RADIX_VAR_RE)) {
|
|
393
|
+
const replacement = radixVariableReplacement(m[0])
|
|
394
|
+
findings.push(
|
|
395
|
+
makeFinding(identity, {
|
|
396
|
+
code: "radix-css-variable",
|
|
397
|
+
severity: "error",
|
|
398
|
+
confidence: "high",
|
|
399
|
+
file: fileLabel,
|
|
400
|
+
line: lineOf(m.index),
|
|
401
|
+
column: colOf(m.index),
|
|
402
|
+
evidence: m[0],
|
|
403
|
+
explanation:
|
|
404
|
+
"Base UI publishes no --radix-* custom property, so this resolves to nothing after the upgrade.",
|
|
405
|
+
suggestion: replacement
|
|
406
|
+
? `Read ${replacement.baseUi} instead (${replacement.parts}).`
|
|
407
|
+
: "No Zenith family sets a Base UI counterpart for this variable; the declaration that reads it needs another source.",
|
|
408
|
+
})
|
|
409
|
+
)
|
|
410
|
+
}
|
|
168
411
|
return findings
|
|
169
412
|
}
|
|
170
413
|
|
|
414
|
+
/** The upgrade guide's rename table, from RADIX_RENAMES: one Markdown document with two tables. */
|
|
415
|
+
export function renderRadixRenamesMarkdown() {
|
|
416
|
+
const code = (s) => `\`${s}\``
|
|
417
|
+
const attributeRows = RADIX_RENAMES.attributes.map(
|
|
418
|
+
(a) =>
|
|
419
|
+
`| ${code(`data-state="${a.value}"`)} | ${a.baseUi ? code(a.baseUi) : `none (${a.note})`} | ${describeParts(a.parts)} |`
|
|
420
|
+
)
|
|
421
|
+
const variableRows = RADIX_RENAMES.variables.map((v) => {
|
|
422
|
+
const parts = v.parts
|
|
423
|
+
? describeParts(v.parts)
|
|
424
|
+
: RADIX_VARIABLE_FAMILIES.map((f) => f.zenith).join(", ")
|
|
425
|
+
return `| ${code(v.radix)} | ${code(v.baseUi)} | ${parts} |`
|
|
426
|
+
})
|
|
427
|
+
const families = RADIX_VARIABLE_FAMILIES.map(
|
|
428
|
+
(f) => `${code(f.radix)} (${f.zenith})`
|
|
429
|
+
).join(", ")
|
|
430
|
+
const kept = RADIX_RENAMES.keptDataState
|
|
431
|
+
.map((k) => `${k.part} (${k.values.map((v) => code(`"${v}"`)).join(", ")})`)
|
|
432
|
+
.join("; ")
|
|
433
|
+
return [
|
|
434
|
+
"<!-- Generated by packages/react/scripts/migration-renames.mjs from RADIX_RENAMES in migration-diagnostics-core.mjs. Do not edit; run `bun scripts/migration-renames.mjs --write`. -->",
|
|
435
|
+
"",
|
|
436
|
+
"### State attributes",
|
|
437
|
+
"",
|
|
438
|
+
"| Radix | Base UI | Zenith parts |",
|
|
439
|
+
"| --- | --- | --- |",
|
|
440
|
+
...attributeRows,
|
|
441
|
+
"",
|
|
442
|
+
`${RADIX_RENAMES.added.map(code).join(" and ")} are new: a part carries one while it enters or leaves. ${RADIX_RENAMES.unchanged.map(code).join(", ")} and the ${code("aria-*")} attributes are unchanged. Zenith's own parts keep ${code("data-state")}: ${kept}.`,
|
|
443
|
+
"",
|
|
444
|
+
"### CSS variables",
|
|
445
|
+
"",
|
|
446
|
+
"| Radix | Base UI | Zenith parts |",
|
|
447
|
+
"| --- | --- | --- |",
|
|
448
|
+
...variableRows,
|
|
449
|
+
"",
|
|
450
|
+
`${code("<family>")} is ${families}.`,
|
|
451
|
+
"",
|
|
452
|
+
].join("\n")
|
|
453
|
+
}
|
|
454
|
+
|
|
171
455
|
// ---------------------------------------------------------------------------
|
|
172
456
|
// Rule 3: syntax-aware (TypeScript AST) palette-install analysis
|
|
173
457
|
// ---------------------------------------------------------------------------
|
|
@@ -16,6 +16,14 @@ import {
|
|
|
16
16
|
const DEFAULT_PACKAGE_DIR = resolve(import.meta.dirname, "..")
|
|
17
17
|
const KNOWN_FLAGS = new Set(["--json", "--package", "--exclude"])
|
|
18
18
|
|
|
19
|
+
/** One finding: where and what, then the fix on its own line so the text report carries it too. */
|
|
20
|
+
function printFinding(f) {
|
|
21
|
+
console.log(
|
|
22
|
+
` [${f.severity}] ${f.code.padEnd(28)} ${f.file}:${f.line}:${f.column} ${f.evidence}`
|
|
23
|
+
)
|
|
24
|
+
if (f.suggestion) console.log(` → ${f.suggestion}`)
|
|
25
|
+
}
|
|
26
|
+
|
|
19
27
|
function usage() {
|
|
20
28
|
return (
|
|
21
29
|
"usage: node migration-sweep.mjs [--json] [--package <dir>] [--exclude <path>]... <path> [...]\n" +
|
|
@@ -157,11 +165,7 @@ export async function runCli(argv) {
|
|
|
157
165
|
? ` (${r.findings.length} finding(s) from the rules that did run, below)`
|
|
158
166
|
: "")
|
|
159
167
|
)
|
|
160
|
-
for (const f of r.findings)
|
|
161
|
-
console.log(
|
|
162
|
-
` [${f.severity}] ${f.code.padEnd(28)} ${f.file}:${f.line}:${f.column} ${f.evidence}`
|
|
163
|
-
)
|
|
164
|
-
}
|
|
168
|
+
for (const f of r.findings) printFinding(f)
|
|
165
169
|
continue
|
|
166
170
|
}
|
|
167
171
|
if (!r.findings.length) {
|
|
@@ -171,11 +175,7 @@ export async function runCli(argv) {
|
|
|
171
175
|
continue
|
|
172
176
|
}
|
|
173
177
|
console.log(` ✗ ${name} — ${r.findings.length} finding(s)`)
|
|
174
|
-
for (const f of r.findings)
|
|
175
|
-
console.log(
|
|
176
|
-
` [${f.severity}] ${f.code.padEnd(28)} ${f.file}:${f.line}:${f.column} ${f.evidence}`
|
|
177
|
-
)
|
|
178
|
-
}
|
|
178
|
+
for (const f of r.findings) printFinding(f)
|
|
179
179
|
}
|
|
180
180
|
console.log("")
|
|
181
181
|
}
|