@wildo-ai/presets-components 1.1.1 → 1.1.2
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/esm/components/actions/action-button.d.ts +1 -4
- package/dist/esm/components/actions/action-button.d.ts.map +1 -1
- package/dist/esm/components/actions/action-button.js +21 -5
- package/dist/esm/components/actions/action-button.js.map +1 -1
- package/dist/esm/components/ai-elements/artifacts/artifact-browser.d.ts.map +1 -1
- package/dist/esm/components/ai-elements/artifacts/artifact-browser.js +7 -3
- package/dist/esm/components/ai-elements/artifacts/artifact-browser.js.map +1 -1
- package/dist/esm/components/ai-elements/execution/task.d.ts.map +1 -1
- package/dist/esm/components/ai-elements/execution/task.js +10 -2
- package/dist/esm/components/ai-elements/execution/task.js.map +1 -1
- package/dist/esm/components/app/app-layout-main-container.labels.d.ts.map +1 -1
- package/dist/esm/components/app/app-layout-main-container.labels.js +10 -2
- package/dist/esm/components/app/app-layout-main-container.labels.js.map +1 -1
- package/dist/esm/components/app/public-layout-main-container.d.ts.map +1 -1
- package/dist/esm/components/app/public-layout-main-container.js +52 -5
- package/dist/esm/components/app/public-layout-main-container.js.map +1 -1
- package/dist/esm/components/data-display/description-list.d.ts.map +1 -1
- package/dist/esm/components/data-display/description-list.js +10 -7
- package/dist/esm/components/data-display/description-list.js.map +1 -1
- package/dist/esm/components/data-display/formatters/display-field-string.d.ts.map +1 -1
- package/dist/esm/components/data-display/formatters/display-field-string.js +3 -0
- package/dist/esm/components/data-display/formatters/display-field-string.js.map +1 -1
- package/dist/esm/components/form/chromes/form-primitive.labels.js +2 -2
- package/dist/esm/components/form/chromes/form-primitive.labels.js.map +1 -1
- package/dist/esm/components/navigation/app-level/menu-user-org.d.ts.map +1 -1
- package/dist/esm/components/navigation/app-level/menu-user-org.js +8 -2
- package/dist/esm/components/navigation/app-level/menu-user-org.js.map +1 -1
- package/dist/esm/components/navigation/app-level/sidebar.d.ts.map +1 -1
- package/dist/esm/components/navigation/app-level/sidebar.js +32 -15
- package/dist/esm/components/navigation/app-level/sidebar.js.map +1 -1
- package/dist/esm/components/overlays/containers/sheet.d.ts.map +1 -1
- package/dist/esm/components/overlays/containers/sheet.js +2 -2
- package/dist/esm/components/overlays/containers/sheet.js.map +1 -1
- package/dist/esm/components/sections/accordion.d.ts.map +1 -1
- package/dist/esm/components/sections/accordion.js +43 -13
- package/dist/esm/components/sections/accordion.js.map +1 -1
- package/dist/esm/components/sections/collapsible.d.ts.map +1 -1
- package/dist/esm/components/sections/collapsible.js +17 -3
- package/dist/esm/components/sections/collapsible.js.map +1 -1
- package/dist/esm/components/sections/content-section.d.ts.map +1 -1
- package/dist/esm/components/sections/content-section.js +11 -7
- package/dist/esm/components/sections/content-section.js.map +1 -1
- package/dist/esm/components/sections/disclosure-nesting.d.ts +20 -0
- package/dist/esm/components/sections/disclosure-nesting.d.ts.map +1 -0
- package/dist/esm/components/sections/disclosure-nesting.js +20 -0
- package/dist/esm/components/sections/disclosure-nesting.js.map +1 -0
- package/dist/esm/components/sections/tabs.d.ts.map +1 -1
- package/dist/esm/components/sections/tabs.js +141 -10
- package/dist/esm/components/sections/tabs.js.map +1 -1
- package/dist/esm/config/preset-kits.d.ts.map +1 -1
- package/dist/esm/config/preset-kits.js +0 -4
- package/dist/esm/config/preset-kits.js.map +1 -1
- package/dist/esm/config/preset-shell.d.ts.map +1 -1
- package/dist/esm/config/preset-shell.js +0 -4
- package/dist/esm/config/preset-shell.js.map +1 -1
- package/dist/esm/config/presets.d.ts.map +1 -1
- package/dist/esm/config/presets.js +0 -1
- package/dist/esm/config/presets.js.map +1 -1
- package/dist/esm/config/resources/application.resource-presets.d.ts.map +1 -1
- package/dist/esm/config/resources/application.resource-presets.js +6 -6
- package/dist/esm/config/resources/application.resource-presets.js.map +1 -1
- package/dist/esm/config/resources/billing.resource-presets.d.ts.map +1 -1
- package/dist/esm/config/resources/billing.resource-presets.js +8 -8
- package/dist/esm/config/resources/billing.resource-presets.js.map +1 -1
- package/dist/esm/config/resources/guidance.resource-presets.d.ts.map +1 -1
- package/dist/esm/config/resources/guidance.resource-presets.js +7 -7
- package/dist/esm/config/resources/guidance.resource-presets.js.map +1 -1
- package/dist/esm/config/resources/identity.resource-presets.d.ts.map +1 -1
- package/dist/esm/config/resources/identity.resource-presets.js +7 -7
- package/dist/esm/config/resources/identity.resource-presets.js.map +1 -1
- package/dist/esm/config/resources/organizations.resource-presets.d.ts.map +1 -1
- package/dist/esm/config/resources/organizations.resource-presets.js +8 -8
- package/dist/esm/config/resources/organizations.resource-presets.js.map +1 -1
- package/dist/esm/config/resources/security.resource-presets.d.ts.map +1 -1
- package/dist/esm/config/resources/security.resource-presets.js +9 -9
- package/dist/esm/config/resources/security.resource-presets.js.map +1 -1
- package/dist/esm/themes/cozy.theme.d.ts.map +1 -1
- package/dist/esm/themes/cozy.theme.js +8 -3
- package/dist/esm/themes/cozy.theme.js.map +1 -1
- package/dist/esm/themes/dense.theme.d.ts.map +1 -1
- package/dist/esm/themes/dense.theme.js +8 -5
- package/dist/esm/themes/dense.theme.js.map +1 -1
- package/dist/esm/themes/editorial.theme.d.ts.map +1 -1
- package/dist/esm/themes/editorial.theme.js +8 -3
- package/dist/esm/themes/editorial.theme.js.map +1 -1
- package/dist/esm/themes/glass.theme.d.ts.map +1 -1
- package/dist/esm/themes/glass.theme.js +10 -4
- package/dist/esm/themes/glass.theme.js.map +1 -1
- package/dist/esm/themes/neo-brutalist.theme.d.ts.map +1 -1
- package/dist/esm/themes/neo-brutalist.theme.js +8 -3
- package/dist/esm/themes/neo-brutalist.theme.js.map +1 -1
- package/dist/esm/themes/terminal.theme.d.ts.map +1 -1
- package/dist/esm/themes/terminal.theme.js +8 -3
- package/dist/esm/themes/terminal.theme.js.map +1 -1
- package/dist/esm/themes/trading.theme.d.ts.map +1 -1
- package/dist/esm/themes/trading.theme.js +8 -3
- package/dist/esm/themes/trading.theme.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +7 -7
- package/dist/esm/.builder.pid +0 -9
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"collapsible.js","sourceRoot":"","sources":["../../../../../src/components/sections/collapsible.tsx"],"names":[],"mappings":";AACA,OAAO,KAAK,oBAAoB,MAAM,6BAA6B,CAAA;AAEnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAA;
|
|
1
|
+
{"version":3,"file":"collapsible.js","sourceRoot":"","sources":["../../../../../src/components/sections/collapsible.tsx"],"names":[],"mappings":";AACA,OAAO,KAAK,oBAAoB,MAAM,6BAA6B,CAAA;AAEnE,OAAO,EAAE,oBAAoB,EAAE,EAAE,EAAE,MAAM,6BAA6B,CAAA;AAEtE,OAAO,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAA;AAE/D,SAAS,eAAe,CAAC,EACvB,GAAG,KAAK,EAC+C;IACvD,OAAO,KAAC,oBAAoB,CAAC,IAAI,iBAAW,aAAa,KAAK,KAAK,GAAI,CAAA;AACzE,CAAC;AAED,SAAS,kBAAkB,CAAC,EAC1B,GAAG,KAAK,EAC6D;IACrE,OAAO,CACL,KAAC,oBAAoB,CAAC,kBAAkB,iBAC5B,qBAAqB,KAC3B,KAAK,GACT,CACH,CAAA;AACH,CAAC;AAED,SAAS,kBAAkB,CAAC,EAC1B,SAAS,EACT,GAAG,KAAK,EAC6D;IACrE,OAAO;IACL;;;;;;;;;;;OAWG;IACH,KAAC,wBAAwB,CAAC,QAAQ,IAAC,KAAK,EAAE,IAAI,YAiB5C,KAAC,oBAAoB,CAAC,kBAAkB,iBAC5B,qBAAqB,EAC/B,SAAS,EAAE,EAAE,CACX,mGAAmG,EACnG,SAAS,CACV,KACG,KAAK,GACT,GACgC,CACrC,CAAA;AACH,CAAC;AAED,MAAM,CAAC,MAAM,WAAW,GAAG,oBAAoB,CAAsB,eAAe,EAAE,EAAE,OAAO,EAAE,kBAAkB,EAAE,OAAO,EAAE,kBAAkB,EAAE,CAAC,CAAA","sourcesContent":["import * as React from \"react\"\nimport * as CollapsiblePrimitive from \"@radix-ui/react-collapsible\"\nimport type { CollapsibleCompound } from '@wildo-ai/saas-frontend-lib'\nimport { createCompoundPreset, cn } from \"@wildo-ai/saas-frontend-lib\"\nimport { FrontendComponentType } from \"@wildo-ai/presets-components-models\"\nimport { DisclosureNestingContext } from \"./disclosure-nesting\"\n\nfunction CollapsibleRoot({\n ...props\n}: React.ComponentProps<typeof CollapsiblePrimitive.Root>): React.JSX.Element {\n return <CollapsiblePrimitive.Root data-slot=\"collapsible\" {...props} />\n}\n\nfunction CollapsibleTrigger({\n ...props\n}: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleTrigger>): React.JSX.Element {\n return (\n <CollapsiblePrimitive.CollapsibleTrigger\n data-slot=\"collapsible-trigger\"\n {...props}\n />\n )\n}\n\nfunction CollapsibleContent({\n className,\n ...props\n}: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleContent>): React.JSX.Element {\n return (\n /*\n * A collapsible panel is a disclosure panel: anything nesting-aware below it (today, an\n * Accordion) reads this fact and applies the themed nesting guide. Providing the fact is not\n * styling — this component stays headless.\n *\n * The provider wraps OUTSIDE the primitive, deliberately. Wrapping `children` INSIDE it shipped\n * a crash: with `asChild` (AI_PlanContent does exactly this) Radix's Slot merges its props and\n * measuring ref onto the single child element — which had become the Provider, not a DOM\n * element — so the ref resolved to nothing and `ResizeObserver.observe()` threw \"parameter 1 is\n * not of type 'Element'\". Context reaches descendants identically from out here, and the\n * children slot stays exactly what the caller authored.\n */\n <DisclosureNestingContext.Provider value={true}>\n {/*\n * The panel ANIMATES its height open and closed, which it did not until 2026-09-02: the\n * trigger's chevron rotated over 200ms and the content it revealed appeared instantly, so a\n * fold read as a jump and a reader lost track of what had moved.\n *\n * Same idiom as the sibling `AccordionContent`, deliberately — both are disclosure panels\n * and there is no reason for one to move and the other to snap. The keyframes come from\n * `tw-animate-css` (which an application already imports for every overlay transition) and\n * interpolate `--radix-collapsible-content-height`, the variable Radix measures onto the\n * element; `overflow-hidden` is what makes an interpolated height clip rather than spill.\n *\n * `className` is destructured only so the animation can MERGE with a caller's classes\n * (`content-section` passes `space-y-field p-card pt-0` here). It was never dropped — the\n * previous `{...props}` spread forwarded it — so this is not a fix, just what merging\n * requires.\n */}\n <CollapsiblePrimitive.CollapsibleContent\n data-slot=\"collapsible-content\"\n className={cn(\n 'overflow-hidden data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down',\n className,\n )}\n {...props}\n />\n </DisclosureNestingContext.Provider>\n )\n}\n\nexport const Collapsible = createCompoundPreset<CollapsibleCompound>(CollapsibleRoot, { Trigger: CollapsibleTrigger, Content: CollapsibleContent })\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"content-section.d.ts","sourceRoot":"","sources":["../../../../../src/components/sections/content-section.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,EAYL,KAAK,mBAAmB,EACxB,KAAK,wBAAwB,EAC7B,KAAK,yBAAyB,EAC/B,MAAM,6BAA6B,CAAC;AAKrC;;;;GAIG;AACH,iBAAS,oBAAoB,CAAC,EAC5B,SAAS,EACT,OAAkB,EAClB,GAAG,KAAK,EACT,EAAE,yBAAyB,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CAc/C;AAED;;;;;GAKG;AACH,iBAAS,mBAAmB,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,wBAAwB,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CAWjG;AAiBD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,iBAAS,cAAc,CAAC,EACtB,SAAS,EACT,UAAgC,EAChC,MAAM,EACN,WAAW,EACX,WAAkB,EAClB,QAAQ,EACR,KAAa,EACb,IAAI,EACJ,UAAU,EACV,QAAQ,EACR,GAAG,KAAK,EACT,EAAE,mBAAmB,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,
|
|
1
|
+
{"version":3,"file":"content-section.d.ts","sourceRoot":"","sources":["../../../../../src/components/sections/content-section.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,EAYL,KAAK,mBAAmB,EACxB,KAAK,wBAAwB,EAC7B,KAAK,yBAAyB,EAC/B,MAAM,6BAA6B,CAAC;AAKrC;;;;GAIG;AACH,iBAAS,oBAAoB,CAAC,EAC5B,SAAS,EACT,OAAkB,EAClB,GAAG,KAAK,EACT,EAAE,yBAAyB,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CAc/C;AAED;;;;;GAKG;AACH,iBAAS,mBAAmB,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,wBAAwB,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CAWjG;AAiBD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,iBAAS,cAAc,CAAC,EACtB,SAAS,EACT,UAAgC,EAChC,MAAM,EACN,WAAW,EACX,WAAkB,EAClB,QAAQ,EACR,KAAa,EACb,IAAI,EACJ,UAAU,EACV,QAAQ,EACR,GAAG,KAAK,EACT,EAAE,mBAAmB,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CAkRzC;AAED,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,CAAA"}
|
|
@@ -120,14 +120,18 @@ function ContentSection({ className, appearance = SectionMode.DEFAULT, legend, d
|
|
|
120
120
|
? props.style
|
|
121
121
|
: { borderWidth: 0, background: 'transparent', boxShadow: 'none', borderRadius: 0, padding: 0, ...props.style }, children: [hasHeading && (_jsxs("div", { className: isCardEach ? 'p-card border-b' : 'space-y-item-xs', children: [(legend != null || icon != null) && (_jsxs(Heading, { level: headingLevel, className: "text-base font-semibold", children: [icon != null && _jsx("span", { className: "me-element inline-flex items-center", children: icon }), legend] })), description != null && _jsx(Text, { variant: TextVariant.MUTED, size: TextSize.SM, children: description })] })), _jsx("div", { className: isCardEach ? 'p-card' : undefined, children: children })] }) }));
|
|
122
122
|
}
|
|
123
|
-
// COLLAPSIBLE — toggle with the legend as the clickable trigger
|
|
124
|
-
//
|
|
123
|
+
// COLLAPSIBLE — toggle with the legend as the clickable trigger, framed as a CARD
|
|
124
|
+
// (decided 2026-08-31; it was a flat div first). A fold needs a boundary: the closed
|
|
125
|
+
// header must read as an affordance with an extent, and an open one must say where its
|
|
126
|
+
// content ends — a frameless collapsed row was indistinguishable from a plain heading.
|
|
127
|
+
// Item-level content inside stays UNframed (the display pipeline's entry items draw a
|
|
128
|
+
// left accent line, not a card), so the frame stays the SECTION's alone.
|
|
125
129
|
if (appearance === SectionMode.COLLAPSIBLE) {
|
|
126
|
-
return (_jsx(Collapsible, { defaultOpen: defaultOpen, children: _jsxs("div", { className: cn(
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
130
|
+
return (_jsx(Collapsible, { defaultOpen: defaultOpen, children: _jsx(Surface, { asChild: true, emitPolicyVars: false, variant: SurfaceVariant.CARD, policyOverride: { nesting: { framingMode: 'always' } }, children: _jsxs("div", { className: cn(cardRecipe.base, className), ...divPassthrough, children: [structuredLegend && (
|
|
131
|
+
// No `asChild` + raw <button>: Radix's CollapsibleTrigger renders its
|
|
132
|
+
// own <button data-slot="collapsible-trigger"> — the framework element,
|
|
133
|
+
// no bespoke button chrome to add.
|
|
134
|
+
_jsxs(CollapsibleTrigger, { className: "flex w-full items-center justify-between p-card text-left", children: [structuredLegend, _jsx(CollapsibleChevron, {})] })), structuredDescription && _jsx("div", { className: "px-card pb-card-sm", children: structuredDescription }), _jsx(CollapsibleContent, { className: "space-y-field p-card pt-0", children: children })] }) }) }));
|
|
131
135
|
}
|
|
132
136
|
// GHOST — flat div, no fieldset/frame. The form-level <fieldset> (Form.tsx)
|
|
133
137
|
// already owns the disabled cascade, so a flat section loses nothing.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"content-section.js","sourceRoot":"","sources":["../../../../../src/components/sections/content-section.tsx"],"names":[],"mappings":"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","sourcesContent":["import * as React from \"react\"\n\nimport {\n cn,\n useUI,\n useIconRegistry,\n StandardIconReference,\n IconSize,\n Surface,\n Heading,\n Text,\n Collapsible,\n CollapsibleTrigger,\n CollapsibleContent,\n type ContentSectionProps,\n type ContentSectionGroupProps,\n type ContentSectionLegendProps,\n} from '@wildo-ai/saas-frontend-lib';\n\nimport { SectionMode } from \"@wildo-ai/zod-decorators\"\nimport { SurfaceVariant, UIBehavior, TextVariant, TextSize } from \"@wildo-ai/presets-components-models\"\n\n/**\n * ContentSectionLegend preset — section heading. DOM-identical to the legacy\n * FieldLegend preset (`data-slot=\"field-legend\"` + classes) so existing CSS and\n * the `FieldDescription` legend-adjacency rule keep working unchanged.\n */\nfunction ContentSectionLegend({\n className,\n variant = \"legend\",\n ...props\n}: ContentSectionLegendProps): React.JSX.Element {\n return (\n <legend\n data-slot=\"field-legend\"\n data-variant={variant}\n className={cn(\n \"mb-field-sm font-medium\",\n \"data-[variant=legend]:text-base\",\n \"data-[variant=label]:text-sm\",\n className\n )}\n {...props}\n />\n )\n}\n\n/**\n * ContentSectionGroup preset — child-grouping container. DOM-identical to the\n * legacy FieldGroup preset (`data-slot=\"field-group\"`, `@container/field-group`,\n * `group/field-group`) so the responsive `Field` orientation + `FieldSeparator`\n * selectors keyed off those container/group names keep resolving.\n */\nfunction ContentSectionGroup({ className, ...props }: ContentSectionGroupProps): React.JSX.Element {\n return (\n <div\n data-slot=\"field-group\"\n className={cn(\n \"group/field-group @container/field-group flex w-full flex-col gap-section-lg data-[slot=checkbox-group]:gap-field-sm [&>[data-slot=field-group]]:gap-field\",\n className\n )}\n {...props}\n />\n )\n}\n\n/**\n * Collapsible heading chevron — rendered through the icon registry\n * (`StandardIconReference.CHEVRON_DOWN`) instead of a hand-drawn `<svg>`, so it\n * tracks the app's icon set. The `[[data-state=open]_&]` selector rotates it\n * when the ancestor `CollapsibleTrigger` is open (Radix sets `data-state=open`).\n */\nfunction CollapsibleChevron(): React.ReactNode {\n const { renderIcon } = useIconRegistry();\n return renderIcon(StandardIconReference.CHEVRON_DOWN, {\n size: IconSize.SM,\n className:\n \"shrink-0 text-muted-foreground transition-transform duration-200 [[data-state=open]_&]:rotate-180\",\n });\n}\n\n/**\n * ContentSection preset — the section chrome workhorse.\n *\n * DEFAULT / CARD: ported verbatim from the FieldSet preset — a `<Surface>`-wrapped\n * native `<fieldset>` (the `disabled` cascade carrier) with the `when-orphan`\n * frame de-duplication, the inter-field `gap-field` ownership + checkbox/radio +\n * settings-row flush exceptions, and truthful SurfacePolicy publication\n * (`framingMode` = `when-orphan` for framed modes, `never` for flat). DOM is\n * byte-identical to the old FieldSet (`data-slot=\"field-set\"`), so there is no\n * CSS / visual / test regression by construction.\n *\n * GHOST: a flat `<div>` (no fieldset/frame), and COLLAPSIBLE: a `Collapsible`\n * toggle with the legend as the clickable trigger — both ported from the\n * hand-branched solo-mode logic that used to live in `Section.tsx`, so all four\n * solo appearances now flow through this one primitive.\n *\n * `legend` / `description` are structured props (rendered in the mode-appropriate\n * position). When omitted, the imperative composite/record/array sites pass\n * `<ContentSectionLegend>` / `<ContentSectionGroup>` as children instead — both\n * shapes produce the same fieldset DOM for DEFAULT.\n */\nfunction ContentSection({\n className,\n appearance = SectionMode.DEFAULT,\n legend,\n description,\n defaultOpen = true,\n disabled,\n panel = false,\n icon,\n headingTag,\n children,\n ...props\n}: ContentSectionProps): React.JSX.Element {\n const { getRecipe } = useUI();\n // CARD sections paint the active theme's CARD recipe — an elevated\n // `surface-raised` fill + border (or a translucent liquid-glass tile under the\n // glass theme). Resolved once per render.\n const cardRecipe = getRecipe(UIBehavior.SURFACE, SurfaceVariant.CARD);\n // Stable id for the CARD group heading ↔ fieldset `aria-labelledby` wiring.\n // Called unconditionally (hooks rule) even though only the CARD branch uses it.\n const reactId = React.useId();\n\n // CARD body full-bleed detection. A CARD whose body hosts SETTINGS_ROW\n // list-items drops its inline padding so each row's `border-b` divider runs\n // corner-to-corner; free-form content keeps `p-card`. The natural expression\n // is a CSS `has-[>[data-chrome-mode=settings_row]]:p-0` variant, but that is a\n // NET-NEW arbitrary-variant class string, and Tailwind v4 only scans engine\n // `@source` dirs at dev-server STARTUP — so a new variant class does not\n // compile on hot-reload (only on a full restart / production build). To stay\n // reliable on every reload we instead detect the settings-row children at\n // runtime and toggle between ALREADY-EMITTED utilities (`p-0`/`p-card`). The\n // effect runs each commit with a change-guard (no re-render loop); `bodyRef`\n // is null for the non-CARD branches, so this is inert there.\n const cardBodyRef = React.useRef<HTMLDivElement>(null);\n const [cardBodyIsSettingsList, setCardBodyIsSettingsList] = React.useState(false);\n React.useLayoutEffect(() => {\n const el = cardBodyRef.current;\n const next = !!el && !!el.querySelector(':scope > [data-chrome-mode=\"settings_row\"]');\n setCardBodyIsSettingsList((prev) => (prev === next ? prev : next));\n });\n\n // Explicit CARD sections — solo AND container-panel `card-each` — ALWAYS paint\n // their themed card chrome (elevated fill + border). The `when-orphan` frame\n // de-dup is intentionally NOT applied to an authored card: the author chose a\n // card, so it stays a card in every context (dialog, page, nested). DEFAULT /\n // GHOST stay flat; COLLAPSIBLE is its own toggle.\n\n // GHOST / COLLAPSIBLE render a <div>, not a <fieldset>. `props` is typed for a\n // fieldset (ref/form/name); the only passthrough the section render sites use\n // on these modes is data-*/aria-*/className, all valid on a div — so narrow the\n // type for the div branches. (The <fieldset> branch keeps the full fieldset props.)\n const divPassthrough = props as unknown as React.HTMLAttributes<HTMLDivElement>;\n\n const structuredLegend =\n legend != null && legend !== false ? <ContentSectionLegend>{legend}</ContentSectionLegend> : null;\n const structuredDescription =\n description != null && description !== false ? (\n <Text variant={TextVariant.MUTED} size={TextSize.SM}>{description}</Text>\n ) : null;\n\n // CONTAINER-PANEL chrome (ContentSectionContainer's tabs/grid/accordion/… panels).\n // Renders a heading element (not a <legend>) with a header-bar/divider layout.\n // Per appearance: CARD = card-each, which paints the SAME themed card chrome as\n // a solo CARD section (the elevated `surface-raised` fill + border + radius from\n // the active theme's CARD recipe), ALWAYS — so a grid/columns of cards reads as\n // distinct cards in every context; DEFAULT = flat. Stays a <fieldset> so the\n // disabled cascade applies — the consolidation that lets container panels share\n // the section primitive. Heading omitted entirely when there is no title/icon/\n // description (content-only panels, e.g. tab bodies whose heading is the tab).\n if (panel) {\n // Semantic heading level (h3 default; h4 when nested under a collapsible-all\n // <h3>), rendered via the Heading primitive. `text-base` pins the size to the\n // prior inherited size — the heading recipe's default h3 size would be larger.\n const headingLevel = headingTag === 'h4' ? 4 : 3;\n const isCardEach = appearance === SectionMode.CARD;\n const hasHeading = legend != null || icon != null || description != null;\n return (\n <Surface\n asChild\n emitPolicyVars={false}\n variant={isCardEach ? SurfaceVariant.CARD : SurfaceVariant.BASE}\n policyOverride={{\n nesting: { framingMode: isCardEach ? ('always' as const) : ('never' as const) },\n }}\n >\n <fieldset\n data-slot=\"field-set\"\n data-appearance={appearance}\n data-section-frame={isCardEach ? 'rendered' : 'suppressed'}\n disabled={disabled}\n // Non-card panels are FLAT: neutralize the Surface-painted BASE recipe\n // (a bordered card panel in every theme) on the element itself — the\n // wrapper is kept for the policy context only. See the solo DEFAULT\n // branch below for the full account of this trap.\n className={cn(\n isCardEach ? cardRecipe.base : 'space-y-field rounded-none border-0 bg-transparent p-0 shadow-none',\n className,\n )}\n {...props}\n style={\n isCardEach\n ? props.style\n : { borderWidth: 0, background: 'transparent', boxShadow: 'none', borderRadius: 0, padding: 0, ...props.style }\n }\n >\n {hasHeading && (\n <div className={isCardEach ? 'p-card border-b' : 'space-y-item-xs'}>\n {(legend != null || icon != null) && (\n <Heading level={headingLevel} className=\"text-base font-semibold\">\n {icon != null && <span className=\"me-element inline-flex items-center\">{icon}</span>}\n {legend}\n </Heading>\n )}\n {description != null && <Text variant={TextVariant.MUTED} size={TextSize.SM}>{description}</Text>}\n </div>\n )}\n <div className={isCardEach ? 'p-card' : undefined}>{children}</div>\n </fieldset>\n </Surface>\n );\n }\n\n // COLLAPSIBLE — toggle with the legend as the clickable trigger. Plain div (no\n // Surface/frame), matching the prior Section.tsx behavior exactly.\n if (appearance === SectionMode.COLLAPSIBLE) {\n return (\n <Collapsible defaultOpen={defaultOpen}>\n <div className={cn('space-y-field', className)} {...divPassthrough}>\n {structuredLegend && (\n // No `asChild` + raw <button>: Radix's CollapsibleTrigger renders its\n // own <button data-slot=\"collapsible-trigger\"> — the framework element,\n // no bespoke button chrome to add.\n <CollapsibleTrigger className=\"flex w-full items-center justify-between text-left\">\n {structuredLegend}\n <CollapsibleChevron />\n </CollapsibleTrigger>\n )}\n {structuredDescription}\n <CollapsibleContent>{children}</CollapsibleContent>\n </div>\n </Collapsible>\n );\n }\n\n // GHOST — flat div, no fieldset/frame. The form-level <fieldset> (Form.tsx)\n // already owns the disabled cascade, so a flat section loses nothing.\n if (appearance === SectionMode.GHOST) {\n return (\n <div className={cn('space-y-field', className)} {...divPassthrough}>\n {structuredLegend}\n {structuredDescription}\n {children}\n </div>\n );\n }\n\n const isCard = appearance === SectionMode.CARD;\n // ⚠ The settings-row match is `^=settings` (prefix), NOT `=settings_row`:\n // Tailwind converts an unescaped `_` inside an arbitrary variant to a SPACE,\n // so the original `…=settings_row]]:gap-0` spelling emitted the invalid\n // selector `[data-chrome-mode=settings row]` and was silently DEAD CSS\n // (verified against the served stylesheet, 2026-08-19). Only `settings_row`\n // starts with `settings`, so the prefix match is exact.\n const baseStyles =\n \"flex flex-col gap-field has-[>[data-slot=checkbox-group]]:gap-field-sm has-[>[data-slot=radio-group]]:gap-field-sm has-[>[data-chrome-mode^=settings]]:gap-0\";\n\n // DEFAULT — a flat fieldset that owns only inter-field spacing (no frame). The\n // form-level <fieldset> already owns the disabled cascade; the legend renders\n // in place at the top. Truthfully publishes `framingMode: 'never'` to descendants.\n //\n // The Surface wrapper exists for the POLICY context only, but `Surface` always\n // paints its variant recipe (policy and appearance are separate axes), and\n // every theme paints `SurfaceVariant.BASE` as a bordered card panel (it is the\n // anchor card surface). Without the frame neutralizers below, this \"flat\"\n // section rendered a card frame whose native <legend> straddled the top border\n // (the todo-lists READ \"header section glitch\", 2026-08-19). Same idiom as the\n // CARD branch's transparent fieldset: neutralize the paint on the element, keep\n // the policy context. The inline style is the deterministic half: recipe vs\n // neutralizer utility classes race on STYLESHEET order (measured: `shadow-sm`\n // beat `shadow-none`), while an inline declaration always wins; caller-passed\n // style still overrides via the trailing spread.\n if (!isCard) {\n return (\n <Surface asChild emitPolicyVars={false} variant={SurfaceVariant.BASE} policyOverride={{ nesting: { framingMode: 'never' as const } }}>\n <fieldset\n data-slot=\"field-set\"\n data-appearance={appearance}\n data-section-frame=\"suppressed\"\n disabled={disabled}\n className={cn(baseStyles, 'rounded-none border-0 bg-transparent p-0 shadow-none', className)}\n {...props}\n style={{ borderWidth: 0, background: 'transparent', boxShadow: 'none', borderRadius: 0, padding: 0, ...props.style }}\n >\n {structuredLegend}\n {structuredDescription}\n {children}\n </fieldset>\n </Surface>\n );\n }\n\n // CARD — the section title + description sit ABOVE the card as a group heading\n // (the macOS/iOS/Linear \"settings group\" pattern), and the themed card chrome\n // (elevated fill + border + radius from the active theme's CARD recipe) wraps\n // ONLY the body. The <fieldset> stays the outer element so its `disabled`\n // cascade still reaches every control, and it is `aria-labelledby` the heading\n // so the group keeps its accessible name.\n //\n // An explicit CARD ALWAYS paints its themed chrome (the `when-orphan` de-dup\n // does NOT apply — the author chose a card, so it stays a card in every\n // context, truthfully publishing `framingMode: 'always'` to descendants).\n //\n // Body padding is CONTENT-AWARE (see `cardBodyIsSettingsList` above): a body\n // hosting SETTINGS_ROW list-items drops its inline padding and clips, so each\n // row's hairline `border-b` divider runs corner-to-corner (full-bleed) instead\n // of floating inset; the rows then re-inset their own content with `px-card`\n // (owned by the SETTINGS_ROW chrome). Free-form content keeps a comfortable\n // `p-card`.\n const headingId = `${reactId}-legend`;\n const hasHeading = legend != null && legend !== false;\n return (\n <Surface asChild emitPolicyVars={false} variant={SurfaceVariant.CARD} policyOverride={{ nesting: { framingMode: 'always' as const } }}>\n <fieldset\n data-slot=\"field-set\"\n data-appearance={appearance}\n data-section-frame=\"rendered\"\n disabled={disabled}\n aria-labelledby={hasHeading ? headingId : undefined}\n className={cn(\"flex flex-col gap-field-sm border-0 bg-transparent p-0\", className)}\n {...props}\n >\n {hasHeading && (\n <div className=\"space-y-item-xs\">\n {/*\n Group heading ABOVE the card body. Rendered via the Heading primitive\n at semantic level 3; `id` (the fieldset's aria-labelledby target),\n `data-slot=\"field-legend\"` + `data-variant=\"legend\"` (CSS hooks) are\n forwarded, and `text-base font-medium` pins the size (the heading\n recipe's default h3 size would be larger).\n */}\n <Heading\n level={3}\n id={headingId}\n data-slot=\"field-legend\"\n data-variant=\"legend\"\n className=\"text-base font-medium leading-tight\"\n >\n {legend}\n </Heading>\n {structuredDescription}\n </div>\n )}\n <div\n ref={cardBodyRef}\n data-slot=\"section-card-body\"\n data-settings-list={cardBodyIsSettingsList || undefined}\n className={cn(\n cardRecipe.base,\n \"flex flex-col\",\n // Settings-row list → zero body padding + clip so each row's\n // `border-b` runs full-bleed to the rounded card edge (rows re-inset\n // with their own `px-card`, owned by the SETTINGS_ROW chrome).\n // Free-form content → comfortable `p-card`. All utilities here are\n // already-emitted (no new arbitrary-variant strings), so the toggle\n // works on hot-reload — see `cardBodyIsSettingsList` above.\n cardBodyIsSettingsList\n ? \"gap-0 p-0 overflow-hidden\"\n : \"gap-field p-card has-[>[data-slot=checkbox-group]]:gap-field-sm has-[>[data-slot=radio-group]]:gap-field-sm\",\n )}\n >\n {children}\n </div>\n </fieldset>\n </Surface>\n );\n}\n\nexport { ContentSection, ContentSectionGroup, ContentSectionLegend }\n"]}
|
|
1
|
+
{"version":3,"file":"content-section.js","sourceRoot":"","sources":["../../../../../src/components/sections/content-section.tsx"],"names":[],"mappings":"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","sourcesContent":["import * as React from \"react\"\n\nimport {\n cn,\n useUI,\n useIconRegistry,\n StandardIconReference,\n IconSize,\n Surface,\n Heading,\n Text,\n Collapsible,\n CollapsibleTrigger,\n CollapsibleContent,\n type ContentSectionProps,\n type ContentSectionGroupProps,\n type ContentSectionLegendProps,\n} from '@wildo-ai/saas-frontend-lib';\n\nimport { SectionMode } from \"@wildo-ai/zod-decorators\"\nimport { SurfaceVariant, UIBehavior, TextVariant, TextSize } from \"@wildo-ai/presets-components-models\"\n\n/**\n * ContentSectionLegend preset — section heading. DOM-identical to the legacy\n * FieldLegend preset (`data-slot=\"field-legend\"` + classes) so existing CSS and\n * the `FieldDescription` legend-adjacency rule keep working unchanged.\n */\nfunction ContentSectionLegend({\n className,\n variant = \"legend\",\n ...props\n}: ContentSectionLegendProps): React.JSX.Element {\n return (\n <legend\n data-slot=\"field-legend\"\n data-variant={variant}\n className={cn(\n \"mb-field-sm font-medium\",\n \"data-[variant=legend]:text-base\",\n \"data-[variant=label]:text-sm\",\n className\n )}\n {...props}\n />\n )\n}\n\n/**\n * ContentSectionGroup preset — child-grouping container. DOM-identical to the\n * legacy FieldGroup preset (`data-slot=\"field-group\"`, `@container/field-group`,\n * `group/field-group`) so the responsive `Field` orientation + `FieldSeparator`\n * selectors keyed off those container/group names keep resolving.\n */\nfunction ContentSectionGroup({ className, ...props }: ContentSectionGroupProps): React.JSX.Element {\n return (\n <div\n data-slot=\"field-group\"\n className={cn(\n \"group/field-group @container/field-group flex w-full flex-col gap-section-lg data-[slot=checkbox-group]:gap-field-sm [&>[data-slot=field-group]]:gap-field\",\n className\n )}\n {...props}\n />\n )\n}\n\n/**\n * Collapsible heading chevron — rendered through the icon registry\n * (`StandardIconReference.CHEVRON_DOWN`) instead of a hand-drawn `<svg>`, so it\n * tracks the app's icon set. The `[[data-state=open]_&]` selector rotates it\n * when the ancestor `CollapsibleTrigger` is open (Radix sets `data-state=open`).\n */\nfunction CollapsibleChevron(): React.ReactNode {\n const { renderIcon } = useIconRegistry();\n return renderIcon(StandardIconReference.CHEVRON_DOWN, {\n size: IconSize.SM,\n className:\n \"shrink-0 text-muted-foreground transition-transform duration-200 [[data-state=open]_&]:rotate-180\",\n });\n}\n\n/**\n * ContentSection preset — the section chrome workhorse.\n *\n * DEFAULT / CARD: ported verbatim from the FieldSet preset — a `<Surface>`-wrapped\n * native `<fieldset>` (the `disabled` cascade carrier) with the `when-orphan`\n * frame de-duplication, the inter-field `gap-field` ownership + checkbox/radio +\n * settings-row flush exceptions, and truthful SurfacePolicy publication\n * (`framingMode` = `when-orphan` for framed modes, `never` for flat). DOM is\n * byte-identical to the old FieldSet (`data-slot=\"field-set\"`), so there is no\n * CSS / visual / test regression by construction.\n *\n * GHOST: a flat `<div>` (no fieldset/frame), and COLLAPSIBLE: a `Collapsible`\n * toggle with the legend as the clickable trigger — both ported from the\n * hand-branched solo-mode logic that used to live in `Section.tsx`, so all four\n * solo appearances now flow through this one primitive.\n *\n * `legend` / `description` are structured props (rendered in the mode-appropriate\n * position). When omitted, the imperative composite/record/array sites pass\n * `<ContentSectionLegend>` / `<ContentSectionGroup>` as children instead — both\n * shapes produce the same fieldset DOM for DEFAULT.\n */\nfunction ContentSection({\n className,\n appearance = SectionMode.DEFAULT,\n legend,\n description,\n defaultOpen = true,\n disabled,\n panel = false,\n icon,\n headingTag,\n children,\n ...props\n}: ContentSectionProps): React.JSX.Element {\n const { getRecipe } = useUI();\n // CARD sections paint the active theme's CARD recipe — an elevated\n // `surface-raised` fill + border (or a translucent liquid-glass tile under the\n // glass theme). Resolved once per render.\n const cardRecipe = getRecipe(UIBehavior.SURFACE, SurfaceVariant.CARD);\n // Stable id for the CARD group heading ↔ fieldset `aria-labelledby` wiring.\n // Called unconditionally (hooks rule) even though only the CARD branch uses it.\n const reactId = React.useId();\n\n // CARD body full-bleed detection. A CARD whose body hosts SETTINGS_ROW\n // list-items drops its inline padding so each row's `border-b` divider runs\n // corner-to-corner; free-form content keeps `p-card`. The natural expression\n // is a CSS `has-[>[data-chrome-mode=settings_row]]:p-0` variant, but that is a\n // NET-NEW arbitrary-variant class string, and Tailwind v4 only scans engine\n // `@source` dirs at dev-server STARTUP — so a new variant class does not\n // compile on hot-reload (only on a full restart / production build). To stay\n // reliable on every reload we instead detect the settings-row children at\n // runtime and toggle between ALREADY-EMITTED utilities (`p-0`/`p-card`). The\n // effect runs each commit with a change-guard (no re-render loop); `bodyRef`\n // is null for the non-CARD branches, so this is inert there.\n const cardBodyRef = React.useRef<HTMLDivElement>(null);\n const [cardBodyIsSettingsList, setCardBodyIsSettingsList] = React.useState(false);\n React.useLayoutEffect(() => {\n const el = cardBodyRef.current;\n const next = !!el && !!el.querySelector(':scope > [data-chrome-mode=\"settings_row\"]');\n setCardBodyIsSettingsList((prev) => (prev === next ? prev : next));\n });\n\n // Explicit CARD sections — solo AND container-panel `card-each` — ALWAYS paint\n // their themed card chrome (elevated fill + border). The `when-orphan` frame\n // de-dup is intentionally NOT applied to an authored card: the author chose a\n // card, so it stays a card in every context (dialog, page, nested). DEFAULT /\n // GHOST stay flat; COLLAPSIBLE is its own toggle.\n\n // GHOST / COLLAPSIBLE render a <div>, not a <fieldset>. `props` is typed for a\n // fieldset (ref/form/name); the only passthrough the section render sites use\n // on these modes is data-*/aria-*/className, all valid on a div — so narrow the\n // type for the div branches. (The <fieldset> branch keeps the full fieldset props.)\n const divPassthrough = props as unknown as React.HTMLAttributes<HTMLDivElement>;\n\n const structuredLegend =\n legend != null && legend !== false ? <ContentSectionLegend>{legend}</ContentSectionLegend> : null;\n const structuredDescription =\n description != null && description !== false ? (\n <Text variant={TextVariant.MUTED} size={TextSize.SM}>{description}</Text>\n ) : null;\n\n // CONTAINER-PANEL chrome (ContentSectionContainer's tabs/grid/accordion/… panels).\n // Renders a heading element (not a <legend>) with a header-bar/divider layout.\n // Per appearance: CARD = card-each, which paints the SAME themed card chrome as\n // a solo CARD section (the elevated `surface-raised` fill + border + radius from\n // the active theme's CARD recipe), ALWAYS — so a grid/columns of cards reads as\n // distinct cards in every context; DEFAULT = flat. Stays a <fieldset> so the\n // disabled cascade applies — the consolidation that lets container panels share\n // the section primitive. Heading omitted entirely when there is no title/icon/\n // description (content-only panels, e.g. tab bodies whose heading is the tab).\n if (panel) {\n // Semantic heading level (h3 default; h4 when nested under a collapsible-all\n // <h3>), rendered via the Heading primitive. `text-base` pins the size to the\n // prior inherited size — the heading recipe's default h3 size would be larger.\n const headingLevel = headingTag === 'h4' ? 4 : 3;\n const isCardEach = appearance === SectionMode.CARD;\n const hasHeading = legend != null || icon != null || description != null;\n return (\n <Surface\n asChild\n emitPolicyVars={false}\n variant={isCardEach ? SurfaceVariant.CARD : SurfaceVariant.BASE}\n policyOverride={{\n nesting: { framingMode: isCardEach ? ('always' as const) : ('never' as const) },\n }}\n >\n <fieldset\n data-slot=\"field-set\"\n data-appearance={appearance}\n data-section-frame={isCardEach ? 'rendered' : 'suppressed'}\n disabled={disabled}\n // Non-card panels are FLAT: neutralize the Surface-painted BASE recipe\n // (a bordered card panel in every theme) on the element itself — the\n // wrapper is kept for the policy context only. See the solo DEFAULT\n // branch below for the full account of this trap.\n className={cn(\n isCardEach ? cardRecipe.base : 'space-y-field rounded-none border-0 bg-transparent p-0 shadow-none',\n className,\n )}\n {...props}\n style={\n isCardEach\n ? props.style\n : { borderWidth: 0, background: 'transparent', boxShadow: 'none', borderRadius: 0, padding: 0, ...props.style }\n }\n >\n {hasHeading && (\n <div className={isCardEach ? 'p-card border-b' : 'space-y-item-xs'}>\n {(legend != null || icon != null) && (\n <Heading level={headingLevel} className=\"text-base font-semibold\">\n {icon != null && <span className=\"me-element inline-flex items-center\">{icon}</span>}\n {legend}\n </Heading>\n )}\n {description != null && <Text variant={TextVariant.MUTED} size={TextSize.SM}>{description}</Text>}\n </div>\n )}\n <div className={isCardEach ? 'p-card' : undefined}>{children}</div>\n </fieldset>\n </Surface>\n );\n }\n\n // COLLAPSIBLE — toggle with the legend as the clickable trigger, framed as a CARD\n // (decided 2026-08-31; it was a flat div first). A fold needs a boundary: the closed\n // header must read as an affordance with an extent, and an open one must say where its\n // content ends — a frameless collapsed row was indistinguishable from a plain heading.\n // Item-level content inside stays UNframed (the display pipeline's entry items draw a\n // left accent line, not a card), so the frame stays the SECTION's alone.\n if (appearance === SectionMode.COLLAPSIBLE) {\n return (\n <Collapsible defaultOpen={defaultOpen}>\n <Surface\n asChild\n emitPolicyVars={false}\n variant={SurfaceVariant.CARD}\n policyOverride={{ nesting: { framingMode: 'always' as const } }}\n >\n <div className={cn(cardRecipe.base, className)} {...divPassthrough}>\n {structuredLegend && (\n // No `asChild` + raw <button>: Radix's CollapsibleTrigger renders its\n // own <button data-slot=\"collapsible-trigger\"> — the framework element,\n // no bespoke button chrome to add.\n <CollapsibleTrigger className=\"flex w-full items-center justify-between p-card text-left\">\n {structuredLegend}\n <CollapsibleChevron />\n </CollapsibleTrigger>\n )}\n {structuredDescription && <div className=\"px-card pb-card-sm\">{structuredDescription}</div>}\n <CollapsibleContent className=\"space-y-field p-card pt-0\">{children}</CollapsibleContent>\n </div>\n </Surface>\n </Collapsible>\n );\n }\n\n // GHOST — flat div, no fieldset/frame. The form-level <fieldset> (Form.tsx)\n // already owns the disabled cascade, so a flat section loses nothing.\n if (appearance === SectionMode.GHOST) {\n return (\n <div className={cn('space-y-field', className)} {...divPassthrough}>\n {structuredLegend}\n {structuredDescription}\n {children}\n </div>\n );\n }\n\n const isCard = appearance === SectionMode.CARD;\n // ⚠ The settings-row match is `^=settings` (prefix), NOT `=settings_row`:\n // Tailwind converts an unescaped `_` inside an arbitrary variant to a SPACE,\n // so the original `…=settings_row]]:gap-0` spelling emitted the invalid\n // selector `[data-chrome-mode=settings row]` and was silently DEAD CSS\n // (verified against the served stylesheet, 2026-08-19). Only `settings_row`\n // starts with `settings`, so the prefix match is exact.\n const baseStyles =\n \"flex flex-col gap-field has-[>[data-slot=checkbox-group]]:gap-field-sm has-[>[data-slot=radio-group]]:gap-field-sm has-[>[data-chrome-mode^=settings]]:gap-0\";\n\n // DEFAULT — a flat fieldset that owns only inter-field spacing (no frame). The\n // form-level <fieldset> already owns the disabled cascade; the legend renders\n // in place at the top. Truthfully publishes `framingMode: 'never'` to descendants.\n //\n // The Surface wrapper exists for the POLICY context only, but `Surface` always\n // paints its variant recipe (policy and appearance are separate axes), and\n // every theme paints `SurfaceVariant.BASE` as a bordered card panel (it is the\n // anchor card surface). Without the frame neutralizers below, this \"flat\"\n // section rendered a card frame whose native <legend> straddled the top border\n // (the todo-lists READ \"header section glitch\", 2026-08-19). Same idiom as the\n // CARD branch's transparent fieldset: neutralize the paint on the element, keep\n // the policy context. The inline style is the deterministic half: recipe vs\n // neutralizer utility classes race on STYLESHEET order (measured: `shadow-sm`\n // beat `shadow-none`), while an inline declaration always wins; caller-passed\n // style still overrides via the trailing spread.\n if (!isCard) {\n return (\n <Surface asChild emitPolicyVars={false} variant={SurfaceVariant.BASE} policyOverride={{ nesting: { framingMode: 'never' as const } }}>\n <fieldset\n data-slot=\"field-set\"\n data-appearance={appearance}\n data-section-frame=\"suppressed\"\n disabled={disabled}\n className={cn(baseStyles, 'rounded-none border-0 bg-transparent p-0 shadow-none', className)}\n {...props}\n style={{ borderWidth: 0, background: 'transparent', boxShadow: 'none', borderRadius: 0, padding: 0, ...props.style }}\n >\n {structuredLegend}\n {structuredDescription}\n {children}\n </fieldset>\n </Surface>\n );\n }\n\n // CARD — the section title + description sit ABOVE the card as a group heading\n // (the macOS/iOS/Linear \"settings group\" pattern), and the themed card chrome\n // (elevated fill + border + radius from the active theme's CARD recipe) wraps\n // ONLY the body. The <fieldset> stays the outer element so its `disabled`\n // cascade still reaches every control, and it is `aria-labelledby` the heading\n // so the group keeps its accessible name.\n //\n // An explicit CARD ALWAYS paints its themed chrome (the `when-orphan` de-dup\n // does NOT apply — the author chose a card, so it stays a card in every\n // context, truthfully publishing `framingMode: 'always'` to descendants).\n //\n // Body padding is CONTENT-AWARE (see `cardBodyIsSettingsList` above): a body\n // hosting SETTINGS_ROW list-items drops its inline padding and clips, so each\n // row's hairline `border-b` divider runs corner-to-corner (full-bleed) instead\n // of floating inset; the rows then re-inset their own content with `px-card`\n // (owned by the SETTINGS_ROW chrome). Free-form content keeps a comfortable\n // `p-card`.\n const headingId = `${reactId}-legend`;\n const hasHeading = legend != null && legend !== false;\n return (\n <Surface asChild emitPolicyVars={false} variant={SurfaceVariant.CARD} policyOverride={{ nesting: { framingMode: 'always' as const } }}>\n <fieldset\n data-slot=\"field-set\"\n data-appearance={appearance}\n data-section-frame=\"rendered\"\n disabled={disabled}\n aria-labelledby={hasHeading ? headingId : undefined}\n className={cn(\"flex flex-col gap-field-sm border-0 bg-transparent p-0\", className)}\n {...props}\n >\n {hasHeading && (\n <div className=\"space-y-item-xs\">\n {/*\n Group heading ABOVE the card body. Rendered via the Heading primitive\n at semantic level 3; `id` (the fieldset's aria-labelledby target),\n `data-slot=\"field-legend\"` + `data-variant=\"legend\"` (CSS hooks) are\n forwarded, and `text-base font-medium` pins the size (the heading\n recipe's default h3 size would be larger).\n */}\n <Heading\n level={3}\n id={headingId}\n data-slot=\"field-legend\"\n data-variant=\"legend\"\n className=\"text-base font-medium leading-tight\"\n >\n {legend}\n </Heading>\n {structuredDescription}\n </div>\n )}\n <div\n ref={cardBodyRef}\n data-slot=\"section-card-body\"\n data-settings-list={cardBodyIsSettingsList || undefined}\n className={cn(\n cardRecipe.base,\n \"flex flex-col\",\n // Settings-row list → zero body padding + clip so each row's\n // `border-b` runs full-bleed to the rounded card edge (rows re-inset\n // with their own `px-card`, owned by the SETTINGS_ROW chrome).\n // Free-form content → comfortable `p-card`. All utilities here are\n // already-emitted (no new arbitrary-variant strings), so the toggle\n // works on hot-reload — see `cardBodyIsSettingsList` above.\n cardBodyIsSettingsList\n ? \"gap-0 p-0 overflow-hidden\"\n : \"gap-field p-card has-[>[data-slot=checkbox-group]]:gap-field-sm has-[>[data-slot=radio-group]]:gap-field-sm\",\n )}\n >\n {children}\n </div>\n </fieldset>\n </Surface>\n );\n}\n\nexport { ContentSection, ContentSectionGroup, ContentSectionLegend }\n"]}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
/**
|
|
3
|
+
* True below an OPEN disclosure panel — the fact a nesting-aware root reads to know it is nested.
|
|
4
|
+
*
|
|
5
|
+
* "Disclosure panel" is the shared role of `AccordionContent` and `CollapsibleContent`: content
|
|
6
|
+
* revealed by an activating control. Both PROVIDE this context; a root that wants the themed
|
|
7
|
+
* nesting treatment (`UIBehavior.DIVIDER` / `DividerVariant.NESTING_GUIDE`) consumes it.
|
|
8
|
+
*
|
|
9
|
+
* A React context rather than a CSS selector, for one reason: the nested look is a THEME RECIPE — a
|
|
10
|
+
* runtime value — and a selector cannot apply a runtime class string conditionally. The context
|
|
11
|
+
* carries the same property a selector would: nesting is a FACT of the tree, not an authoring
|
|
12
|
+
* decision, so a caller cannot forget it or declare it falsely.
|
|
13
|
+
*
|
|
14
|
+
* Consumption is deliberately opt-in per component. `AccordionRoot` consumes it; `CollapsibleRoot`
|
|
15
|
+
* does not (yet): the ai-element surfaces built on Collapsible (task, tool, queue, approval,
|
|
16
|
+
* execution-stream) hand-compose their own chrome, and an automatic guide on every nested
|
|
17
|
+
* collapsible there is a visual change that must be looked at, not deduced.
|
|
18
|
+
*/
|
|
19
|
+
export declare const DisclosureNestingContext: React.Context<boolean>;
|
|
20
|
+
//# sourceMappingURL=disclosure-nesting.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"disclosure-nesting.d.ts","sourceRoot":"","sources":["../../../../../src/components/sections/disclosure-nesting.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,wBAAwB,wBAA6B,CAAA"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
/**
|
|
3
|
+
* True below an OPEN disclosure panel — the fact a nesting-aware root reads to know it is nested.
|
|
4
|
+
*
|
|
5
|
+
* "Disclosure panel" is the shared role of `AccordionContent` and `CollapsibleContent`: content
|
|
6
|
+
* revealed by an activating control. Both PROVIDE this context; a root that wants the themed
|
|
7
|
+
* nesting treatment (`UIBehavior.DIVIDER` / `DividerVariant.NESTING_GUIDE`) consumes it.
|
|
8
|
+
*
|
|
9
|
+
* A React context rather than a CSS selector, for one reason: the nested look is a THEME RECIPE — a
|
|
10
|
+
* runtime value — and a selector cannot apply a runtime class string conditionally. The context
|
|
11
|
+
* carries the same property a selector would: nesting is a FACT of the tree, not an authoring
|
|
12
|
+
* decision, so a caller cannot forget it or declare it falsely.
|
|
13
|
+
*
|
|
14
|
+
* Consumption is deliberately opt-in per component. `AccordionRoot` consumes it; `CollapsibleRoot`
|
|
15
|
+
* does not (yet): the ai-element surfaces built on Collapsible (task, tool, queue, approval,
|
|
16
|
+
* execution-stream) hand-compose their own chrome, and an automatic guide on every nested
|
|
17
|
+
* collapsible there is a visual change that must be looked at, not deduced.
|
|
18
|
+
*/
|
|
19
|
+
export const DisclosureNestingContext = React.createContext(false);
|
|
20
|
+
//# sourceMappingURL=disclosure-nesting.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"disclosure-nesting.js","sourceRoot":"","sources":["../../../../../src/components/sections/disclosure-nesting.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,KAAK,CAAC,aAAa,CAAC,KAAK,CAAC,CAAA","sourcesContent":["import * as React from \"react\"\n\n/**\n * True below an OPEN disclosure panel — the fact a nesting-aware root reads to know it is nested.\n *\n * \"Disclosure panel\" is the shared role of `AccordionContent` and `CollapsibleContent`: content\n * revealed by an activating control. Both PROVIDE this context; a root that wants the themed\n * nesting treatment (`UIBehavior.DIVIDER` / `DividerVariant.NESTING_GUIDE`) consumes it.\n *\n * A React context rather than a CSS selector, for one reason: the nested look is a THEME RECIPE — a\n * runtime value — and a selector cannot apply a runtime class string conditionally. The context\n * carries the same property a selector would: nesting is a FACT of the tree, not an authoring\n * decision, so a caller cannot forget it or declare it falsely.\n *\n * Consumption is deliberately opt-in per component. `AccordionRoot` consumes it; `CollapsibleRoot`\n * does not (yet): the ai-element surfaces built on Collapsible (task, tool, queue, approval,\n * execution-stream) hand-compose their own chrome, and an automatic guide on every nested\n * collapsible there is a visual change that must be looked at, not deduced.\n */\nexport const DisclosureNestingContext = React.createContext(false)\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tabs.d.ts","sourceRoot":"","sources":["../../../../../src/components/sections/tabs.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"tabs.d.ts","sourceRoot":"","sources":["../../../../../src/components/sections/tabs.tsx"],"names":[],"mappings":"AAMA,OAAO,EACL,YAAY,EAKb,MAAM,6BAA6B,CAAC;AAiRrC,eAAO,MAAM,IAAI,cAA+G,CAAA"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import * as TabsPrimitive from "@radix-ui/react-tabs";
|
|
5
|
+
import { ChevronLeft, ChevronRight } from "lucide-react";
|
|
5
6
|
import { createCompoundPreset, cn, useUI, Surface, } from '@wildo-ai/saas-frontend-lib';
|
|
6
7
|
import { SelectableVariant, SurfaceVariant, UIBehavior } from "@wildo-ai/presets-components-models";
|
|
7
8
|
/**
|
|
@@ -21,15 +22,144 @@ function TabsRoot({ className, orientation = 'horizontal', ...props }) {
|
|
|
21
22
|
const resolved = orientation === 'vertical' ? 'vertical' : 'horizontal';
|
|
22
23
|
return (_jsx(TabsOrientationContext.Provider, { value: resolved, children: _jsx(TabsPrimitive.Root, { "data-slot": "tabs", orientation: orientation, className: cn("flex flex-col gap-element data-[orientation=vertical]:flex-row data-[orientation=vertical]:gap-card", className), ...props }) }));
|
|
23
24
|
}
|
|
25
|
+
/**
|
|
26
|
+
* Overflow state of the horizontal scrollport — which edges have more content.
|
|
27
|
+
*
|
|
28
|
+
* Updated from three signals, because each misses a case the others catch: the
|
|
29
|
+
* `scroll` listener (user scrolled), a `ResizeObserver` on the list (viewport
|
|
30
|
+
* grew/shrank — the common "window resized" case), and a per-render effect
|
|
31
|
+
* (tab COUNT changed — children resizing does not move the list's own box, so
|
|
32
|
+
* the observer cannot see it).
|
|
33
|
+
*/
|
|
34
|
+
function useHorizontalOverflow(ref) {
|
|
35
|
+
const [overflow, setOverflow] = React.useState({ start: false, end: false });
|
|
36
|
+
const update = React.useCallback(() => {
|
|
37
|
+
const el = ref.current;
|
|
38
|
+
if (!el)
|
|
39
|
+
return;
|
|
40
|
+
// 1px slack: fractional layout widths make scrollLeft/scrollWidth disagree by <1.
|
|
41
|
+
const start = el.scrollLeft > 1;
|
|
42
|
+
const end = el.scrollLeft + el.clientWidth < el.scrollWidth - 1;
|
|
43
|
+
setOverflow((prev) => (prev.start === start && prev.end === end ? prev : { start, end }));
|
|
44
|
+
}, [ref]);
|
|
45
|
+
React.useEffect(() => {
|
|
46
|
+
const el = ref.current;
|
|
47
|
+
if (!el)
|
|
48
|
+
return;
|
|
49
|
+
el.addEventListener('scroll', update, { passive: true });
|
|
50
|
+
const observer = new ResizeObserver(update);
|
|
51
|
+
observer.observe(el);
|
|
52
|
+
return () => {
|
|
53
|
+
el.removeEventListener('scroll', update);
|
|
54
|
+
observer.disconnect();
|
|
55
|
+
};
|
|
56
|
+
}, [ref, update]);
|
|
57
|
+
// No dependency array on purpose — re-measures after every render, which is
|
|
58
|
+
// what catches content-only changes (a tab added/removed, a label resolving
|
|
59
|
+
// from i18n). Three numeric reads; cheap enough not to gate.
|
|
60
|
+
React.useEffect(update);
|
|
61
|
+
return overflow;
|
|
62
|
+
}
|
|
63
|
+
/** Width of each fade/affordance zone. One constant so the mask and the button agree. */
|
|
64
|
+
const TABS_FADE_ZONE = '2.5rem';
|
|
65
|
+
/**
|
|
66
|
+
* The edge fade, as a MASK on the strip rather than a gradient painted over it.
|
|
67
|
+
*
|
|
68
|
+
* A painted gradient has to name the colour it fades FROM, and no colour is
|
|
69
|
+
* right: measured on the workbench, the strip sits on `bg-surface-raised`
|
|
70
|
+
* (oklch 0.208) while `--background` is oklch 0.129, so an overlay fading to
|
|
71
|
+
* the background token washed a visibly wrong band across the strip — and it
|
|
72
|
+
* would be wrong differently on every surface a tab strip can sit on (card,
|
|
73
|
+
* dialog, panel) and in every theme.
|
|
74
|
+
*
|
|
75
|
+
* Masking fades the CONTENT to transparency instead, revealing whatever is
|
|
76
|
+
* actually behind it. It needs no colour token, so it is correct on every
|
|
77
|
+
* surface and every theme by construction.
|
|
78
|
+
*/
|
|
79
|
+
function edgeFadeMask(overflow) {
|
|
80
|
+
if (!overflow.start && !overflow.end)
|
|
81
|
+
return undefined;
|
|
82
|
+
const stops = [
|
|
83
|
+
overflow.start ? `transparent 0, black ${TABS_FADE_ZONE}` : 'black 0',
|
|
84
|
+
overflow.end ? `black calc(100% - ${TABS_FADE_ZONE}), transparent 100%` : 'black 100%',
|
|
85
|
+
];
|
|
86
|
+
return `linear-gradient(to right, ${stops.join(', ')})`;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* One edge's slide affordance: the DISCREET control the design asks for — a
|
|
90
|
+
* quiet chevron over the faded zone, with no box and no background of its own
|
|
91
|
+
* (the mask above is what makes room for it). Mouse-only by intent
|
|
92
|
+
* (`aria-hidden`, unfocusable): keyboard users already reach every tab through
|
|
93
|
+
* the tab list's roving arrow-key focus, so exposing this to the tab order
|
|
94
|
+
* would add a stop that does nothing arrows don't.
|
|
95
|
+
*/
|
|
96
|
+
function TabsListSlideAffordance({ side, onSlide }) {
|
|
97
|
+
const Chevron = side === 'start' ? ChevronLeft : ChevronRight;
|
|
98
|
+
return (_jsx("button", { type: "button", tabIndex: -1, "aria-hidden": "true", "data-slot": "tabs-list-slide", "data-side": side, onClick: onSlide, style: { width: TABS_FADE_ZONE }, className: cn("absolute inset-y-0 z-10 flex cursor-pointer items-center border-0 bg-transparent p-0", side === 'start' ? "left-0 justify-start" : "right-0 justify-end"), children: _jsx(Chevron, { className: "size-4 text-muted-foreground opacity-60 transition-opacity hover:opacity-100" }) }));
|
|
99
|
+
}
|
|
24
100
|
function TabsList({ className, ...props }) {
|
|
25
101
|
const isVertical = React.useContext(TabsOrientationContext) === 'vertical';
|
|
26
|
-
|
|
102
|
+
const listRef = React.useRef(null);
|
|
103
|
+
const overflow = useHorizontalOverflow(listRef);
|
|
104
|
+
/*
|
|
105
|
+
* Vertical wheel input slides the strip horizontally — the strip is a
|
|
106
|
+
* one-axis surface, so a wheel over it can only mean "move along it". A
|
|
107
|
+
* NATIVE non-passive listener, not React `onWheel`: React registers wheel
|
|
108
|
+
* passively at the root, so `preventDefault()` there is ignored and every
|
|
109
|
+
* translated tick would also scroll the page behind the strip.
|
|
110
|
+
*/
|
|
111
|
+
React.useEffect(() => {
|
|
112
|
+
const el = listRef.current;
|
|
113
|
+
if (!el || isVertical)
|
|
114
|
+
return;
|
|
115
|
+
const onWheel = (event) => {
|
|
116
|
+
if (el.scrollWidth <= el.clientWidth)
|
|
117
|
+
return;
|
|
118
|
+
const delta = Math.abs(event.deltaX) > Math.abs(event.deltaY) ? event.deltaX : event.deltaY;
|
|
119
|
+
if (delta === 0)
|
|
120
|
+
return;
|
|
121
|
+
el.scrollLeft += delta;
|
|
122
|
+
event.preventDefault();
|
|
123
|
+
};
|
|
124
|
+
el.addEventListener('wheel', onWheel, { passive: false });
|
|
125
|
+
return () => el.removeEventListener('wheel', onWheel);
|
|
126
|
+
}, [isVertical]);
|
|
127
|
+
const fadeMask = edgeFadeMask(overflow);
|
|
128
|
+
const slideBy = React.useCallback((direction) => {
|
|
129
|
+
const el = listRef.current;
|
|
130
|
+
if (!el)
|
|
131
|
+
return;
|
|
132
|
+
el.scrollBy({ left: direction * el.clientWidth * 0.6, behavior: 'smooth' });
|
|
133
|
+
}, []);
|
|
134
|
+
if (isVertical) {
|
|
135
|
+
return (_jsx(TabsPrimitive.List, { "data-slot": "tabs-list",
|
|
27
136
|
// Vertical: clean nav rail (column of items, no boxed container).
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
137
|
+
className: cn("flex flex-col items-stretch justify-start gap-item-xs", className), ...props }));
|
|
138
|
+
}
|
|
139
|
+
/*
|
|
140
|
+
* Horizontal: underline tab bar that SCROLLS when it overflows, instead of
|
|
141
|
+
* widening the page (measured on the workbench artifact list, whose concern
|
|
142
|
+
* tabs outgrew the viewport).
|
|
143
|
+
*
|
|
144
|
+
* ## Why the bottom rule is a wrapper layer, not `border-b` on the list
|
|
145
|
+
*
|
|
146
|
+
* Becoming a scrollport is what forces the restructure: `overflow-x: auto`
|
|
147
|
+
* makes `overflow-y` compute to a clipping value, and the old geometry —
|
|
148
|
+
* `border-b` on the list with `-mb-px` triggers overlapping their 2px
|
|
149
|
+
* indicator onto the 1px rule — needs the trigger to paint one pixel PAST
|
|
150
|
+
* the list's content edge, which a scrollport clips. So the rule is drawn by
|
|
151
|
+
* a non-scrolling absolutely-positioned layer in a wrapper, the triggers
|
|
152
|
+
* drop the overlap trick, and the indicator's bottom pixel lands on the rule
|
|
153
|
+
* because both sit on the wrapper's last pixel row. Same rendered result,
|
|
154
|
+
* scroll-safe.
|
|
155
|
+
*
|
|
156
|
+
* The scrollbar itself is suppressed on every engine (`scrollbar-width` for
|
|
157
|
+
* Firefox, the `::-webkit-scrollbar` pseudo elsewhere): a native horizontal
|
|
158
|
+
* scrollbar under a tab strip — Windows most visibly — reads as a broken
|
|
159
|
+
* layout, and the gradient affordances + wheel translation replace what it
|
|
160
|
+
* afforded.
|
|
161
|
+
*/
|
|
162
|
+
return (_jsxs("div", { "data-slot": "tabs-list-scrollport", className: "relative w-full", children: [_jsx("span", { "aria-hidden": "true", className: "pointer-events-none absolute inset-x-0 bottom-0 h-px bg-border" }), _jsx(TabsPrimitive.List, { ref: listRef, "data-slot": "tabs-list", style: { maskImage: fadeMask, WebkitMaskImage: fadeMask }, className: cn("relative flex w-full items-center gap-card overflow-x-auto overscroll-x-contain [scrollbar-width:none] [&::-webkit-scrollbar]:hidden", className), ...props }), overflow.start && _jsx(TabsListSlideAffordance, { side: "start", onSlide: () => slideBy(-1) }), overflow.end && _jsx(TabsListSlideAffordance, { side: "end", onSlide: () => slideBy(1) })] }));
|
|
33
163
|
}
|
|
34
164
|
function TabsTrigger({ className, ...props }) {
|
|
35
165
|
const isVertical = React.useContext(TabsOrientationContext) === 'vertical';
|
|
@@ -40,9 +170,10 @@ function TabsTrigger({ className, ...props }) {
|
|
|
40
170
|
// Vertical: full-width, left-aligned rail item (FILLED recipe fills it).
|
|
41
171
|
? "w-full justify-start rounded-md px-card-sm py-item"
|
|
42
172
|
// Horizontal: content-width underline tab (UNDERLINE recipe draws the
|
|
43
|
-
// bottom border). `-mb-px`
|
|
44
|
-
//
|
|
45
|
-
|
|
173
|
+
// bottom border). No `-mb-px` overlap here: the list is a scrollport
|
|
174
|
+
// (which would clip it) and the 1px rule is the wrapper's own layer,
|
|
175
|
+
// sharing the wrapper's last pixel row with this indicator.
|
|
176
|
+
: "justify-center px-card-sm py-item",
|
|
46
177
|
// Style from the orientation-appropriate SELECTABLE recipe.
|
|
47
178
|
recipe.base, recipe.states.focus, recipe.states.selected, recipe.states.disabled, recipe.motion.transition, className), ...props }));
|
|
48
179
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tabs.js","sourceRoot":"","sources":["../../../../../src/components/sections/tabs.tsx"],"names":[],"mappings":"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","sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport * as TabsPrimitive from \"@radix-ui/react-tabs\"\n\nimport {\n TabsCompound,\n createCompoundPreset,\n cn,\n useUI,\n Surface,\n} from '@wildo-ai/saas-frontend-lib';\n\nimport { FrontendComponentType, SelectableVariant, SurfaceVariant, UIBehavior } from \"@wildo-ai/presets-components-models\"\n\n/**\n * Orientation shared from TabsRoot down to List/Trigger so each sub-component can\n * choose the right `SELECTABLE` recipe. Radix exposes orientation only as a\n * `data-orientation` DOM attribute, but a recipe's utility strings (which already\n * carry `data-[state=active]:…`) can't be re-scoped per orientation from a class\n * list — so the choice is made in JS here.\n *\n * Horizontal tabs use `UNDERLINE` (a bottom-border indicator on separated items —\n * the canonical modern-SaaS navigation-tab look: \"separated = tabs, connected by\n * a track = segmented control\"). The vertical rail keeps `FILLED` (a soft fill on\n * the selected rail item).\n */\nconst TabsOrientationContext = React.createContext<'horizontal' | 'vertical'>('horizontal')\n\nfunction TabsRoot({\n className,\n orientation = 'horizontal',\n ...props\n}: React.ComponentProps<typeof TabsPrimitive.Root>): React.JSX.Element {\n const resolved = orientation === 'vertical' ? 'vertical' : 'horizontal'\n return (\n <TabsOrientationContext.Provider value={resolved}>\n <TabsPrimitive.Root\n data-slot=\"tabs\"\n orientation={orientation}\n className={cn(\"flex flex-col gap-element data-[orientation=vertical]:flex-row data-[orientation=vertical]:gap-card\", className)}\n {...props}\n />\n </TabsOrientationContext.Provider>\n )\n}\n\nfunction TabsList({\n className,\n ...props\n}: React.ComponentProps<typeof TabsPrimitive.List>): React.JSX.Element {\n const isVertical = React.useContext(TabsOrientationContext) === 'vertical'\n\n return (\n <TabsPrimitive.List\n data-slot=\"tabs-list\"\n className={cn(\n \"flex\",\n isVertical\n // Vertical: clean nav rail (column of items, no boxed container).\n ? \"flex-col items-stretch justify-start gap-item-xs\"\n // Horizontal: underline tab bar — a full-width bottom rule that the\n // active tab's 2px indicator sits on. Separated items, NO connecting\n // tray (a tray would read as a segmented control).\n : \"w-full items-center gap-card border-b\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction TabsTrigger({\n className,\n ...props\n}: React.ComponentProps<typeof TabsPrimitive.Trigger>): React.JSX.Element {\n const isVertical = React.useContext(TabsOrientationContext) === 'vertical'\n const { getRecipe } = useUI()\n // Vertical rail → FILLED (soft fill on select); horizontal → UNDERLINE.\n const recipe = getRecipe(UIBehavior.SELECTABLE, isVertical ? SelectableVariant.FILLED : SelectableVariant.UNDERLINE)\n\n return (\n <TabsPrimitive.Trigger\n data-slot=\"tabs-trigger\"\n className={cn(\n \"inline-flex items-center gap-1.5 text-sm font-medium whitespace-nowrap [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n isVertical\n // Vertical: full-width, left-aligned rail item (FILLED recipe fills it).\n ? \"w-full justify-start rounded-md px-card-sm py-item\"\n // Horizontal: content-width underline tab (UNDERLINE recipe draws the\n // bottom border). `-mb-px` overlaps the tab's 2px indicator onto the\n // list's 1px rule so they read as one line.\n : \"-mb-px justify-center px-card-sm py-item\",\n // Style from the orientation-appropriate SELECTABLE recipe.\n recipe.base,\n recipe.states.focus,\n recipe.states.selected,\n recipe.states.disabled,\n recipe.motion.transition,\n className\n )}\n {...props}\n />\n )\n}\n\nfunction TabsContent({\n className,\n ...props\n}: React.ComponentProps<typeof TabsPrimitive.Content>): React.JSX.Element {\n return (\n <Surface asChild emitPolicyVars={false} variant={SurfaceVariant.TAB_PANEL}>\n <TabsPrimitive.Content\n data-slot=\"tabs-content\"\n className={cn(\"flex-1 outline-none\", className)}\n {...props}\n />\n </Surface>\n )\n}\n\nexport const Tabs = createCompoundPreset<TabsCompound>(TabsRoot, { List: TabsList, Trigger: TabsTrigger, Content: TabsContent })\n"]}
|
|
1
|
+
{"version":3,"file":"tabs.js","sourceRoot":"","sources":["../../../../../src/components/sections/tabs.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAEZ,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,KAAK,aAAa,MAAM,sBAAsB,CAAA;AACrD,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAExD,OAAO,EAEL,oBAAoB,EACpB,EAAE,EACF,KAAK,EACL,OAAO,GACR,MAAM,6BAA6B,CAAC;AAErC,OAAO,EAAyB,iBAAiB,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,qCAAqC,CAAA;AAE1H;;;;;;;;;;;GAWG;AACH,MAAM,sBAAsB,GAAG,KAAK,CAAC,aAAa,CAA4B,YAAY,CAAC,CAAA;AAE3F,SAAS,QAAQ,CAAC,EAChB,SAAS,EACT,WAAW,GAAG,YAAY,EAC1B,GAAG,KAAK,EACwC;IAChD,MAAM,QAAQ,GAAG,WAAW,KAAK,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,YAAY,CAAA;IACvE,OAAO,CACL,KAAC,sBAAsB,CAAC,QAAQ,IAAC,KAAK,EAAE,QAAQ,YAC9C,KAAC,aAAa,CAAC,IAAI,iBACP,MAAM,EAChB,WAAW,EAAE,WAAW,EACxB,SAAS,EAAE,EAAE,CAAC,qGAAqG,EAAE,SAAS,CAAC,KAC3H,KAAK,GACT,GAC8B,CACnC,CAAA;AACH,CAAC;AAED;;;;;;;;GAQG;AACH,SAAS,qBAAqB,CAAC,GAAwC;IACrE,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,CAAA;IAE5E,MAAM,MAAM,GAAG,KAAK,CAAC,WAAW,CAAC,GAAG,EAAE;QACpC,MAAM,EAAE,GAAG,GAAG,CAAC,OAAO,CAAA;QACtB,IAAI,CAAC,EAAE;YAAE,OAAM;QACf,kFAAkF;QAClF,MAAM,KAAK,GAAG,EAAE,CAAC,UAAU,GAAG,CAAC,CAAA;QAC/B,MAAM,GAAG,GAAG,EAAE,CAAC,UAAU,GAAG,EAAE,CAAC,WAAW,GAAG,EAAE,CAAC,WAAW,GAAG,CAAC,CAAA;QAC/D,WAAW,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,KAAK,KAAK,IAAI,IAAI,CAAC,GAAG,KAAK,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC,CAAA;IAC3F,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAA;IAET,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE;QACnB,MAAM,EAAE,GAAG,GAAG,CAAC,OAAO,CAAA;QACtB,IAAI,CAAC,EAAE;YAAE,OAAM;QACf,EAAE,CAAC,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAA;QACxD,MAAM,QAAQ,GAAG,IAAI,cAAc,CAAC,MAAM,CAAC,CAAA;QAC3C,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;QACpB,OAAO,GAAG,EAAE;YACV,EAAE,CAAC,mBAAmB,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAA;YACxC,QAAQ,CAAC,UAAU,EAAE,CAAA;QACvB,CAAC,CAAA;IACH,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAA;IAEjB,4EAA4E;IAC5E,4EAA4E;IAC5E,6DAA6D;IAC7D,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAA;IAEvB,OAAO,QAAQ,CAAA;AACjB,CAAC;AAED,yFAAyF;AACzF,MAAM,cAAc,GAAG,QAAQ,CAAA;AAE/B;;;;;;;;;;;;;GAaG;AACH,SAAS,YAAY,CAAC,QAA0C;IAC9D,IAAI,CAAC,QAAQ,CAAC,KAAK,IAAI,CAAC,QAAQ,CAAC,GAAG;QAAE,OAAO,SAAS,CAAA;IACtD,MAAM,KAAK,GAAG;QACZ,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,wBAAwB,cAAc,EAAE,CAAC,CAAC,CAAC,SAAS;QACrE,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,qBAAqB,cAAc,qBAAqB,CAAC,CAAC,CAAC,YAAY;KACvF,CAAA;IACD,OAAO,6BAA6B,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAA;AACzD,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,uBAAuB,CAAC,EAAE,IAAI,EAAE,OAAO,EAAkD;IAChG,MAAM,OAAO,GAAG,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,YAAY,CAAA;IAC7D,OAAO,CACL,iBACE,IAAI,EAAC,QAAQ,EACb,QAAQ,EAAE,CAAC,CAAC,iBACA,MAAM,eACR,iBAAiB,eAChB,IAAI,EACf,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,EAAE,KAAK,EAAE,cAAc,EAAE,EAChC,SAAS,EAAE,EAAE,CACX,sFAAsF,EACtF,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,qBAAqB,CAClE,YAED,KAAC,OAAO,IAAC,SAAS,EAAC,8EAA8E,GAAG,GAC7F,CACV,CAAA;AACH,CAAC;AAED,SAAS,QAAQ,CAAC,EAChB,SAAS,EACT,GAAG,KAAK,EACwC;IAChD,MAAM,UAAU,GAAG,KAAK,CAAC,UAAU,CAAC,sBAAsB,CAAC,KAAK,UAAU,CAAA;IAC1E,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAiB,IAAI,CAAC,CAAA;IAClD,MAAM,QAAQ,GAAG,qBAAqB,CAAC,OAAO,CAAC,CAAA;IAE/C;;;;;;OAMG;IACH,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE;QACnB,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,CAAA;QAC1B,IAAI,CAAC,EAAE,IAAI,UAAU;YAAE,OAAM;QAC7B,MAAM,OAAO,GAAG,CAAC,KAAiB,EAAE,EAAE;YACpC,IAAI,EAAE,CAAC,WAAW,IAAI,EAAE,CAAC,WAAW;gBAAE,OAAM;YAC5C,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAA;YAC3F,IAAI,KAAK,KAAK,CAAC;gBAAE,OAAM;YACvB,EAAE,CAAC,UAAU,IAAI,KAAK,CAAA;YACtB,KAAK,CAAC,cAAc,EAAE,CAAA;QACxB,CAAC,CAAA;QACD,EAAE,CAAC,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAA;QACzD,OAAO,GAAG,EAAE,CAAC,EAAE,CAAC,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;IACvD,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAA;IAEhB,MAAM,QAAQ,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAA;IAEvC,MAAM,OAAO,GAAG,KAAK,CAAC,WAAW,CAAC,CAAC,SAAiB,EAAE,EAAE;QACtD,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,CAAA;QAC1B,IAAI,CAAC,EAAE;YAAE,OAAM;QACf,EAAE,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,SAAS,GAAG,EAAE,CAAC,WAAW,GAAG,GAAG,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAA;IAC7E,CAAC,EAAE,EAAE,CAAC,CAAA;IAEN,IAAI,UAAU,EAAE,CAAC;QACf,OAAO,CACL,KAAC,aAAa,CAAC,IAAI,iBACP,WAAW;YACrB,kEAAkE;YAClE,SAAS,EAAE,EAAE,CAAC,uDAAuD,EAAE,SAAS,CAAC,KAC7E,KAAK,GACT,CACH,CAAA;IACH,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,OAAO,CACL,4BAAe,sBAAsB,EAAC,SAAS,EAAC,iBAAiB,aAE/D,8BAAkB,MAAM,EAAC,SAAS,EAAC,gEAAgE,GAAG,EACtG,KAAC,aAAa,CAAC,IAAI,IACjB,GAAG,EAAE,OAAO,eACF,WAAW,EACrB,KAAK,EAAE,EAAE,SAAS,EAAE,QAAQ,EAAE,eAAe,EAAE,QAAQ,EAAE,EACzD,SAAS,EAAE,EAAE,CACX,sIAAsI,EACtI,SAAS,CACV,KACG,KAAK,GACT,EACD,QAAQ,CAAC,KAAK,IAAI,KAAC,uBAAuB,IAAC,IAAI,EAAC,OAAO,EAAC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GAAI,EACtF,QAAQ,CAAC,GAAG,IAAI,KAAC,uBAAuB,IAAC,IAAI,EAAC,KAAK,EAAC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,GAAI,IAC9E,CACP,CAAA;AACH,CAAC;AAED,SAAS,WAAW,CAAC,EACnB,SAAS,EACT,GAAG,KAAK,EAC2C;IACnD,MAAM,UAAU,GAAG,KAAK,CAAC,UAAU,CAAC,sBAAsB,CAAC,KAAK,UAAU,CAAA;IAC1E,MAAM,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,CAAA;IAC7B,wEAAwE;IACxE,MAAM,MAAM,GAAG,SAAS,CAAC,UAAU,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC,CAAC,iBAAiB,CAAC,SAAS,CAAC,CAAA;IAEpH,OAAO,CACL,KAAC,aAAa,CAAC,OAAO,iBACV,cAAc,EACxB,SAAS,EAAE,EAAE,CACX,0JAA0J,EAC1J,UAAU;YACR,yEAAyE;YACzE,CAAC,CAAC,oDAAoD;YACtD,sEAAsE;YACtE,qEAAqE;YACrE,qEAAqE;YACrE,4DAA4D;YAC5D,CAAC,CAAC,mCAAmC;QACvC,4DAA4D;QAC5D,MAAM,CAAC,IAAI,EACX,MAAM,CAAC,MAAM,CAAC,KAAK,EACnB,MAAM,CAAC,MAAM,CAAC,QAAQ,EACtB,MAAM,CAAC,MAAM,CAAC,QAAQ,EACtB,MAAM,CAAC,MAAM,CAAC,UAAU,EACxB,SAAS,CACV,KACG,KAAK,GACT,CACH,CAAA;AACH,CAAC;AAED,SAAS,WAAW,CAAC,EACnB,SAAS,EACT,GAAG,KAAK,EAC2C;IACnD,OAAO,CACL,KAAC,OAAO,IAAC,OAAO,QAAC,cAAc,EAAE,KAAK,EAAE,OAAO,EAAE,cAAc,CAAC,SAAS,YACvE,KAAC,aAAa,CAAC,OAAO,iBACV,cAAc,EACxB,SAAS,EAAE,EAAE,CAAC,qBAAqB,EAAE,SAAS,CAAC,KAC3C,KAAK,GACT,GACM,CACX,CAAA;AACH,CAAC;AAED,MAAM,CAAC,MAAM,IAAI,GAAG,oBAAoB,CAAe,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC,CAAA","sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport * as TabsPrimitive from \"@radix-ui/react-tabs\"\nimport { ChevronLeft, ChevronRight } from \"lucide-react\"\n\nimport {\n TabsCompound,\n createCompoundPreset,\n cn,\n useUI,\n Surface,\n} from '@wildo-ai/saas-frontend-lib';\n\nimport { FrontendComponentType, SelectableVariant, SurfaceVariant, UIBehavior } from \"@wildo-ai/presets-components-models\"\n\n/**\n * Orientation shared from TabsRoot down to List/Trigger so each sub-component can\n * choose the right `SELECTABLE` recipe. Radix exposes orientation only as a\n * `data-orientation` DOM attribute, but a recipe's utility strings (which already\n * carry `data-[state=active]:…`) can't be re-scoped per orientation from a class\n * list — so the choice is made in JS here.\n *\n * Horizontal tabs use `UNDERLINE` (a bottom-border indicator on separated items —\n * the canonical modern-SaaS navigation-tab look: \"separated = tabs, connected by\n * a track = segmented control\"). The vertical rail keeps `FILLED` (a soft fill on\n * the selected rail item).\n */\nconst TabsOrientationContext = React.createContext<'horizontal' | 'vertical'>('horizontal')\n\nfunction TabsRoot({\n className,\n orientation = 'horizontal',\n ...props\n}: React.ComponentProps<typeof TabsPrimitive.Root>): React.JSX.Element {\n const resolved = orientation === 'vertical' ? 'vertical' : 'horizontal'\n return (\n <TabsOrientationContext.Provider value={resolved}>\n <TabsPrimitive.Root\n data-slot=\"tabs\"\n orientation={orientation}\n className={cn(\"flex flex-col gap-element data-[orientation=vertical]:flex-row data-[orientation=vertical]:gap-card\", className)}\n {...props}\n />\n </TabsOrientationContext.Provider>\n )\n}\n\n/**\n * Overflow state of the horizontal scrollport — which edges have more content.\n *\n * Updated from three signals, because each misses a case the others catch: the\n * `scroll` listener (user scrolled), a `ResizeObserver` on the list (viewport\n * grew/shrank — the common \"window resized\" case), and a per-render effect\n * (tab COUNT changed — children resizing does not move the list's own box, so\n * the observer cannot see it).\n */\nfunction useHorizontalOverflow(ref: React.RefObject<HTMLElement | null>): { start: boolean; end: boolean } {\n const [overflow, setOverflow] = React.useState({ start: false, end: false })\n\n const update = React.useCallback(() => {\n const el = ref.current\n if (!el) return\n // 1px slack: fractional layout widths make scrollLeft/scrollWidth disagree by <1.\n const start = el.scrollLeft > 1\n const end = el.scrollLeft + el.clientWidth < el.scrollWidth - 1\n setOverflow((prev) => (prev.start === start && prev.end === end ? prev : { start, end }))\n }, [ref])\n\n React.useEffect(() => {\n const el = ref.current\n if (!el) return\n el.addEventListener('scroll', update, { passive: true })\n const observer = new ResizeObserver(update)\n observer.observe(el)\n return () => {\n el.removeEventListener('scroll', update)\n observer.disconnect()\n }\n }, [ref, update])\n\n // No dependency array on purpose — re-measures after every render, which is\n // what catches content-only changes (a tab added/removed, a label resolving\n // from i18n). Three numeric reads; cheap enough not to gate.\n React.useEffect(update)\n\n return overflow\n}\n\n/** Width of each fade/affordance zone. One constant so the mask and the button agree. */\nconst TABS_FADE_ZONE = '2.5rem'\n\n/**\n * The edge fade, as a MASK on the strip rather than a gradient painted over it.\n *\n * A painted gradient has to name the colour it fades FROM, and no colour is\n * right: measured on the workbench, the strip sits on `bg-surface-raised`\n * (oklch 0.208) while `--background` is oklch 0.129, so an overlay fading to\n * the background token washed a visibly wrong band across the strip — and it\n * would be wrong differently on every surface a tab strip can sit on (card,\n * dialog, panel) and in every theme.\n *\n * Masking fades the CONTENT to transparency instead, revealing whatever is\n * actually behind it. It needs no colour token, so it is correct on every\n * surface and every theme by construction.\n */\nfunction edgeFadeMask(overflow: { start: boolean; end: boolean }): string | undefined {\n if (!overflow.start && !overflow.end) return undefined\n const stops = [\n overflow.start ? `transparent 0, black ${TABS_FADE_ZONE}` : 'black 0',\n overflow.end ? `black calc(100% - ${TABS_FADE_ZONE}), transparent 100%` : 'black 100%',\n ]\n return `linear-gradient(to right, ${stops.join(', ')})`\n}\n\n/**\n * One edge's slide affordance: the DISCREET control the design asks for — a\n * quiet chevron over the faded zone, with no box and no background of its own\n * (the mask above is what makes room for it). Mouse-only by intent\n * (`aria-hidden`, unfocusable): keyboard users already reach every tab through\n * the tab list's roving arrow-key focus, so exposing this to the tab order\n * would add a stop that does nothing arrows don't.\n */\nfunction TabsListSlideAffordance({ side, onSlide }: { side: 'start' | 'end'; onSlide: () => void }): React.JSX.Element {\n const Chevron = side === 'start' ? ChevronLeft : ChevronRight\n return (\n <button\n type=\"button\"\n tabIndex={-1}\n aria-hidden=\"true\"\n data-slot=\"tabs-list-slide\"\n data-side={side}\n onClick={onSlide}\n style={{ width: TABS_FADE_ZONE }}\n className={cn(\n \"absolute inset-y-0 z-10 flex cursor-pointer items-center border-0 bg-transparent p-0\",\n side === 'start' ? \"left-0 justify-start\" : \"right-0 justify-end\",\n )}\n >\n <Chevron className=\"size-4 text-muted-foreground opacity-60 transition-opacity hover:opacity-100\" />\n </button>\n )\n}\n\nfunction TabsList({\n className,\n ...props\n}: React.ComponentProps<typeof TabsPrimitive.List>): React.JSX.Element {\n const isVertical = React.useContext(TabsOrientationContext) === 'vertical'\n const listRef = React.useRef<HTMLDivElement>(null)\n const overflow = useHorizontalOverflow(listRef)\n\n /*\n * Vertical wheel input slides the strip horizontally — the strip is a\n * one-axis surface, so a wheel over it can only mean \"move along it\". A\n * NATIVE non-passive listener, not React `onWheel`: React registers wheel\n * passively at the root, so `preventDefault()` there is ignored and every\n * translated tick would also scroll the page behind the strip.\n */\n React.useEffect(() => {\n const el = listRef.current\n if (!el || isVertical) return\n const onWheel = (event: WheelEvent) => {\n if (el.scrollWidth <= el.clientWidth) return\n const delta = Math.abs(event.deltaX) > Math.abs(event.deltaY) ? event.deltaX : event.deltaY\n if (delta === 0) return\n el.scrollLeft += delta\n event.preventDefault()\n }\n el.addEventListener('wheel', onWheel, { passive: false })\n return () => el.removeEventListener('wheel', onWheel)\n }, [isVertical])\n\n const fadeMask = edgeFadeMask(overflow)\n\n const slideBy = React.useCallback((direction: 1 | -1) => {\n const el = listRef.current\n if (!el) return\n el.scrollBy({ left: direction * el.clientWidth * 0.6, behavior: 'smooth' })\n }, [])\n\n if (isVertical) {\n return (\n <TabsPrimitive.List\n data-slot=\"tabs-list\"\n // Vertical: clean nav rail (column of items, no boxed container).\n className={cn(\"flex flex-col items-stretch justify-start gap-item-xs\", className)}\n {...props}\n />\n )\n }\n\n /*\n * Horizontal: underline tab bar that SCROLLS when it overflows, instead of\n * widening the page (measured on the workbench artifact list, whose concern\n * tabs outgrew the viewport).\n *\n * ## Why the bottom rule is a wrapper layer, not `border-b` on the list\n *\n * Becoming a scrollport is what forces the restructure: `overflow-x: auto`\n * makes `overflow-y` compute to a clipping value, and the old geometry —\n * `border-b` on the list with `-mb-px` triggers overlapping their 2px\n * indicator onto the 1px rule — needs the trigger to paint one pixel PAST\n * the list's content edge, which a scrollport clips. So the rule is drawn by\n * a non-scrolling absolutely-positioned layer in a wrapper, the triggers\n * drop the overlap trick, and the indicator's bottom pixel lands on the rule\n * because both sit on the wrapper's last pixel row. Same rendered result,\n * scroll-safe.\n *\n * The scrollbar itself is suppressed on every engine (`scrollbar-width` for\n * Firefox, the `::-webkit-scrollbar` pseudo elsewhere): a native horizontal\n * scrollbar under a tab strip — Windows most visibly — reads as a broken\n * layout, and the gradient affordances + wheel translation replace what it\n * afforded.\n */\n return (\n <div data-slot=\"tabs-list-scrollport\" className=\"relative w-full\">\n {/* The full-width rule lives OUTSIDE the masked strip, so the edge fade never eats it. */}\n <span aria-hidden=\"true\" className=\"pointer-events-none absolute inset-x-0 bottom-0 h-px bg-border\" />\n <TabsPrimitive.List\n ref={listRef}\n data-slot=\"tabs-list\"\n style={{ maskImage: fadeMask, WebkitMaskImage: fadeMask }}\n className={cn(\n \"relative flex w-full items-center gap-card overflow-x-auto overscroll-x-contain [scrollbar-width:none] [&::-webkit-scrollbar]:hidden\",\n className\n )}\n {...props}\n />\n {overflow.start && <TabsListSlideAffordance side=\"start\" onSlide={() => slideBy(-1)} />}\n {overflow.end && <TabsListSlideAffordance side=\"end\" onSlide={() => slideBy(1)} />}\n </div>\n )\n}\n\nfunction TabsTrigger({\n className,\n ...props\n}: React.ComponentProps<typeof TabsPrimitive.Trigger>): React.JSX.Element {\n const isVertical = React.useContext(TabsOrientationContext) === 'vertical'\n const { getRecipe } = useUI()\n // Vertical rail → FILLED (soft fill on select); horizontal → UNDERLINE.\n const recipe = getRecipe(UIBehavior.SELECTABLE, isVertical ? SelectableVariant.FILLED : SelectableVariant.UNDERLINE)\n\n return (\n <TabsPrimitive.Trigger\n data-slot=\"tabs-trigger\"\n className={cn(\n \"inline-flex items-center gap-1.5 text-sm font-medium whitespace-nowrap [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n isVertical\n // Vertical: full-width, left-aligned rail item (FILLED recipe fills it).\n ? \"w-full justify-start rounded-md px-card-sm py-item\"\n // Horizontal: content-width underline tab (UNDERLINE recipe draws the\n // bottom border). No `-mb-px` overlap here: the list is a scrollport\n // (which would clip it) and the 1px rule is the wrapper's own layer,\n // sharing the wrapper's last pixel row with this indicator.\n : \"justify-center px-card-sm py-item\",\n // Style from the orientation-appropriate SELECTABLE recipe.\n recipe.base,\n recipe.states.focus,\n recipe.states.selected,\n recipe.states.disabled,\n recipe.motion.transition,\n className\n )}\n {...props}\n />\n )\n}\n\nfunction TabsContent({\n className,\n ...props\n}: React.ComponentProps<typeof TabsPrimitive.Content>): React.JSX.Element {\n return (\n <Surface asChild emitPolicyVars={false} variant={SurfaceVariant.TAB_PANEL}>\n <TabsPrimitive.Content\n data-slot=\"tabs-content\"\n className={cn(\"flex-1 outline-none\", className)}\n {...props}\n />\n </Surface>\n )\n}\n\nexport const Tabs = createCompoundPreset<TabsCompound>(TabsRoot, { List: TabsList, Trigger: TabsTrigger, Content: TabsContent })\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"preset-kits.d.ts","sourceRoot":"","sources":["../../../../src/config/preset-kits.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAC;AA8B7E;;;;;;;GAOG;AACH,eAAO,MAAM,SAAS,EAAE,OAAO,CAAC,yBAAyB,CAExD,CAAC;AAGF;;;;;;;GAOG;AACH,eAAO,MAAM,UAAU,EAAE,OAAO,CAAC,yBAAyB,CAEzD,CAAC;AAGF;;;;;;;;GAQG;AACH,eAAO,MAAM,eAAe,EAAE,OAAO,CAAC,yBAAyB,CAE9D,CAAC;AAGF;;;;;;;GAOG;AACH,eAAO,MAAM,UAAU,EAAE,OAAO,CAAC,yBAAyB,CAGzD,CAAC;AAGF;;;;GAIG;AACH,eAAO,MAAM,YAAY,EAAE,OAAO,CAAC,yBAAyB,CAY3D,CAAC;AAGF;;;;GAIG;AACH,eAAO,MAAM,aAAa,EAAE,OAAO,CAAC,yBAAyB,
|
|
1
|
+
{"version":3,"file":"preset-kits.d.ts","sourceRoot":"","sources":["../../../../src/config/preset-kits.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAC;AA8B7E;;;;;;;GAOG;AACH,eAAO,MAAM,SAAS,EAAE,OAAO,CAAC,yBAAyB,CAExD,CAAC;AAGF;;;;;;;GAOG;AACH,eAAO,MAAM,UAAU,EAAE,OAAO,CAAC,yBAAyB,CAEzD,CAAC;AAGF;;;;;;;;GAQG;AACH,eAAO,MAAM,eAAe,EAAE,OAAO,CAAC,yBAAyB,CAE9D,CAAC;AAGF;;;;;;;GAOG;AACH,eAAO,MAAM,UAAU,EAAE,OAAO,CAAC,yBAAyB,CAGzD,CAAC;AAGF;;;;GAIG;AACH,eAAO,MAAM,YAAY,EAAE,OAAO,CAAC,yBAAyB,CAY3D,CAAC;AAGF;;;;GAIG;AACH,eAAO,MAAM,aAAa,EAAE,OAAO,CAAC,yBAAyB,CAc5D,CAAC;AAOF;;;;GAIG;AACH,eAAO,MAAM,UAAU,EAAE,OAAO,CAAC,yBAAyB,CAgBzD,CAAC;AAGF;;;;GAIG;AACH,eAAO,MAAM,cAAc,EAAE,OAAO,CAAC,yBAAyB,CAM7D,CAAC;AAOF;;;;;GAKG;AACH,eAAO,MAAM,UAAU,EAAE,OAAO,CAAC,yBAAyB,CAwBzD,CAAC;AAGF;;;;;;;GAOG;AACH,eAAO,MAAM,OAAO,EAAE,OAAO,CAAC,yBAAyB,CAGtD,CAAC;AAGF;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa,EAAE,OAAO,CAAC,yBAAyB,CAM5D,CAAC;AAGF;;;;GAIG;AACH,eAAO,MAAM,eAAe,EAAE,OAAO,CAAC,yBAAyB,CAqB9D,CAAC;AAGF;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc,EAAE,OAAO,CAAC,yBAAyB,CAU7D,CAAC;AAGF;;;;GAIG;AACH,eAAO,MAAM,cAAc,EAAE,OAAO,CAAC,yBAAyB,CAM7D,CAAC;AAGF;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc,EAAE,OAAO,CAAC,yBAAyB,CAmB7D,CAAC"}
|
|
@@ -95,7 +95,6 @@ export const KIT_DRAG_DROP = {
|
|
|
95
95
|
navSection('main', { showLabel: false, collapsible: false, nested: true, maxNestingDepth: 2 }),
|
|
96
96
|
],
|
|
97
97
|
defaultSection: "main",
|
|
98
|
-
showUncategorized: true,
|
|
99
98
|
},
|
|
100
99
|
}),
|
|
101
100
|
};
|
|
@@ -121,7 +120,6 @@ export const KIT_TABLET = {
|
|
|
121
120
|
navSection('tools'),
|
|
122
121
|
],
|
|
123
122
|
defaultSection: "main",
|
|
124
|
-
showUncategorized: true,
|
|
125
123
|
},
|
|
126
124
|
}),
|
|
127
125
|
};
|
|
@@ -162,7 +160,6 @@ export const KIT_COLLAB = {
|
|
|
162
160
|
navSection('settings', { defaultCollapsed: true }),
|
|
163
161
|
],
|
|
164
162
|
defaultSection: "main",
|
|
165
|
-
showUncategorized: true,
|
|
166
163
|
},
|
|
167
164
|
[ApplicationLevelComponentType.STATUS_BAR]: {
|
|
168
165
|
showMemoryUsage: false,
|
|
@@ -283,7 +280,6 @@ export const KIT_AI_COPILOT = {
|
|
|
283
280
|
navSection('settings', { defaultCollapsed: true }),
|
|
284
281
|
],
|
|
285
282
|
defaultSection: "main",
|
|
286
|
-
showUncategorized: true,
|
|
287
283
|
},
|
|
288
284
|
}),
|
|
289
285
|
};
|