@seekalfred/primitives 0.4.0 → 0.5.1
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/accordion/accordion.d.ts +5 -5
- package/dist/accordion/accordion.js +13 -8
- package/dist/accordion/accordion.js.map +1 -1
- package/dist/alert/alert.d.ts +2 -2
- package/dist/alert/alert.js +4 -2
- package/dist/alert/alert.js.map +1 -1
- package/dist/alert-dialog/alert-dialog.d.ts +1 -1
- package/dist/alert-dialog/alert-dialog.js +4 -2
- package/dist/alert-dialog/alert-dialog.js.map +1 -1
- package/dist/avatar/avatar.d.ts +1 -1
- package/dist/avatar/avatar.js +6 -4
- package/dist/avatar/avatar.js.map +1 -1
- package/dist/badge/badge.d.ts +2 -2
- package/dist/badge/badge.js +4 -2
- package/dist/badge/badge.js.map +1 -1
- package/dist/breadcrumb/breadcrumb.d.ts +3 -3
- package/dist/breadcrumb/breadcrumb.js +7 -4
- package/dist/breadcrumb/breadcrumb.js.map +1 -1
- package/dist/button/button.d.ts +3 -3
- package/dist/button/button.js +4 -2
- package/dist/button/button.js.map +1 -1
- package/dist/calendar/calendar.d.ts +2 -2
- package/dist/calendar/calendar.js +7 -5
- package/dist/calendar/calendar.js.map +1 -1
- package/dist/card/card.d.ts +6 -6
- package/dist/card/card.js +19 -12
- package/dist/card/card.js.map +1 -1
- package/dist/checkbox/checkbox.d.ts +2 -2
- package/dist/checkbox/checkbox.js +4 -2
- package/dist/checkbox/checkbox.js.map +1 -1
- package/dist/dialog/dialog.d.ts +3 -3
- package/dist/dialog/dialog.js +7 -4
- package/dist/dialog/dialog.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/input/input.d.ts +2 -2
- package/dist/input/input.js +4 -2
- package/dist/input/input.js.map +1 -1
- package/dist/label/label.d.ts +1 -1
- package/dist/label/label.js +4 -2
- package/dist/label/label.js.map +1 -1
- package/dist/lib/cn.d.ts +55 -5
- package/dist/lib/cn.js +64 -6
- package/dist/lib/cn.js.map +1 -1
- package/dist/lib/merge-refs.js +22 -0
- package/dist/lib/merge-refs.js.map +1 -0
- package/dist/menu/menu.d.ts +5 -5
- package/dist/menu/menu.js +10 -6
- package/dist/menu/menu.js.map +1 -1
- package/dist/popover/popover.d.ts +2 -2
- package/dist/popover/popover.js +4 -2
- package/dist/popover/popover.js.map +1 -1
- package/dist/progress/progress.d.ts +2 -2
- package/dist/progress/progress.js +7 -5
- package/dist/progress/progress.js.map +1 -1
- package/dist/radio-group/radio-group.d.ts +3 -3
- package/dist/radio-group/radio-group.js +9 -6
- package/dist/radio-group/radio-group.js.map +1 -1
- package/dist/scroll-area/scroll-area.d.ts +7 -6
- package/dist/scroll-area/scroll-area.js +6 -4
- package/dist/scroll-area/scroll-area.js.map +1 -1
- package/dist/select/select.d.ts +3 -3
- package/dist/select/select.js +9 -6
- package/dist/select/select.js.map +1 -1
- package/dist/separator/separator.d.ts +1 -1
- package/dist/separator/separator.js +4 -2
- package/dist/separator/separator.js.map +1 -1
- package/dist/sheet/sheet.d.ts +1 -1
- package/dist/sheet/sheet.js +7 -4
- package/dist/sheet/sheet.js.map +1 -1
- package/dist/skeleton/skeleton.d.ts +1 -1
- package/dist/skeleton/skeleton.js +4 -2
- package/dist/skeleton/skeleton.js.map +1 -1
- package/dist/slider/slider.d.ts +2 -2
- package/dist/slider/slider.js +6 -4
- package/dist/slider/slider.js.map +1 -1
- package/dist/spinner/spinner.d.ts +1 -1
- package/dist/spinner/spinner.js +4 -2
- package/dist/spinner/spinner.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/switch/switch.d.ts +2 -2
- package/dist/switch/switch.js +4 -2
- package/dist/switch/switch.js.map +1 -1
- package/dist/table/table.d.ts +4 -4
- package/dist/table/table.js +18 -17
- package/dist/table/table.js.map +1 -1
- package/dist/tabs/tabs.d.ts +4 -4
- package/dist/tabs/tabs.js +13 -8
- package/dist/tabs/tabs.js.map +1 -1
- package/dist/textarea/textarea.d.ts +1 -1
- package/dist/textarea/textarea.js +4 -2
- package/dist/textarea/textarea.js.map +1 -1
- package/dist/toast/toast.d.ts +2 -2
- package/dist/toast/toast.js +4 -2
- package/dist/toast/toast.js.map +1 -1
- package/dist/tooltip/tooltip.d.ts +2 -2
- package/dist/tooltip/tooltip.js +4 -2
- package/dist/tooltip/tooltip.js.map +1 -1
- package/package.json +3 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"checkbox.js","names":["Checkbox","AriaCheckbox"],"sources":["../../src/checkbox/checkbox.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva } from \"class-variance-authority\";\nimport type
|
|
1
|
+
{"version":3,"file":"checkbox.js","names":["Checkbox","AriaCheckbox"],"sources":["../../src/checkbox/checkbox.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva } from \"class-variance-authority\";\nimport { forwardRef, type ReactNode } from \"react\";\nimport {\n Checkbox as AriaCheckbox,\n type CheckboxProps as AriaCheckboxProps,\n} from \"react-aria-components\";\n\nimport { cn } from \"../lib/cn.js\";\nimport { selectionLabelVariants, selectionRingVariants } from \"../lib/selection-control.js\";\n\n/**\n * Checkbox, rebuilt on React Aria.\n *\n * THE CENSUS FOUND ZERO CALL SITES. It is ported anyway because forms and\n * table row selection are unbuildable without it, but that fact changes what\n * this is: with no usage to be faithful to and no call site to break, it is a\n * rewrite rather than a restyling. Four things the legacy component got\n * wrong are therefore simply fixed rather than carried:\n *\n * - **No accessible name.** It rendered a bare `<input type=\"checkbox\">` with\n * no label, no `aria-label` and no `id`, so every use was an axe `label`\n * violation. Here the label is `children`, and React Aria wraps the input\n * in it.\n * - **The fill never showed.** `checked:bg-primary` on a native input with no\n * `appearance-none` is painted over by the user-agent checkbox.\n * - **`border-primary` cannot be a boundary.** Brand orange is 2.44:1 on\n * white, under even the 3:1 non-text floor (WCAG 1.4.11).\n * - **`focus-visible:ring-2` is a box-shadow**, which forced-colors discards,\n * taking the only focus indication with it.\n *\n * React Aria renders a `<label>` around a visually-hidden real `<input>` and\n * NOTHING else -- there is no visual box in the box. Drawing one, and keeping\n * it in step with `data-selected` / `data-indeterminate`, is what this\n * component owns.\n *\n * Every string in the cva blocks below is a STATIC literal. Tailwind scans\n * source text, so a class assembled at runtime is invisible to it and would\n * simply never be emitted.\n */\n/**\n * The label geometry now lives in lib/selection-control.ts, shared with\n * Radio and Switch. Three controls with the same 24px target and the same\n * typography is the Rule of Three, and this migration has twice found a\n * legacy pair that styled one surface twice and silently drifted.\n */\nexport const checkboxVariants = cva([selectionLabelVariants()]);\n\nexport const checkboxBoxVariants = cva([\n \"inline-flex size-4 shrink-0 items-center justify-center\",\n // rounded-2xs, not rounded-sm. --radius-sm is 8px, which on a 16px box\n // is exactly half its width -- a circle, indistinguishable from a radio\n // button. A screenshot caught that; no assertion in this suite did, which\n // is why NotACircle now exists. --radius-xs (6px) is still 75% of the way\n // there, so the scale gained a 4px step rather than this reaching for a\n // bracket value that check:hardcoding would rightly reject.\n \"rounded-2xs border-(length:--border-width)\",\n\n // box-border: content-box would add the border to the 16px box and make it\n // 18, which is the same opt-in-reset exposure as everywhere else.\n \"box-border\",\n\n // Unselected: a neutral boundary, NOT the legacy border-primary. Brand\n // orange is 2.44:1 on white and cannot legally be the only thing describing\n // a control's edge.\n \"bg-background border-border\",\n\n \"motion-safe:transition-colors motion-safe:duration-(--duration-fast) motion-safe:ease-standard\",\n\n // Selected and indeterminate paint the same: both mean \"this control has a\n // value\", and the glyph inside says which.\n //\n // primary-strong on the border, not primary: a filled orange box is 2.44:1\n // against the page, so the FILL cannot satisfy 1.4.11 and the boundary has\n // to. Same reasoning, and the same token, as the Button's outline variant.\n \"group-data-[selected]:bg-primary group-data-[selected]:border-primary-strong\",\n \"group-data-[selected]:text-primary-foreground\",\n \"group-data-[indeterminate]:bg-primary group-data-[indeterminate]:border-primary-strong\",\n \"group-data-[indeterminate]:text-primary-foreground\",\n\n // Shared with Radio and Switch. The ring goes on the BOX rather than the\n // input, because React Aria's input is visually hidden.\n selectionRingVariants(),\n]);\n\n/**\n * A checkbox.\n *\n * @example\n * <Checkbox defaultSelected>Email me about new courses</Checkbox>\n * <Checkbox aria-label=\"Select row\" />\n */\nexport interface CheckboxProps\n extends Omit<AriaCheckboxProps, \"className\" | \"style\" | \"children\"> {\n /**\n * The label, and the control's accessible name.\n *\n * Omit it only for a genuinely label-less checkbox -- a table row selector\n * -- and pass `aria-label` instead. A checkbox with neither has no\n * accessible name, which is what every legacy call site would have been.\n */\n children?: ReactNode;\n /** Extra classes for the label. Merged last, so they win conflicts. */\n className?: string;\n}\n\nexport const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>(function Checkbox(\n { className, children, ...props },\n ref,\n) {\n return (\n <AriaCheckbox\n {...props}\n // inputRef, not ref: AriaCheckbox's own ref targets the label it\n // renders around a visually-hidden native input, and a label cannot\n // be focused -- ref.current.focus() would silently no-op. inputRef is\n // React Aria's own escape hatch to the real control.\n inputRef={ref}\n data-slot=\"checkbox\"\n // `group` is load-bearing: React Aria puts data-selected,\n // data-indeterminate and data-focus-visible on the LABEL, and CSS\n // cannot look upward from the box to them.\n className={cn(checkboxVariants(), className)}\n >\n {({ isSelected, isIndeterminate }) => (\n <>\n {/*\n aria-hidden: the real <input> already carries the state. A\n decorative box announced as well would say everything twice.\n */}\n <span aria-hidden=\"true\" data-slot=\"checkbox-box\" className={checkboxBoxVariants()}>\n {/*\n Chosen in JS rather than toggled with CSS. Two rules that both\n apply -- show-when-selected and hide-when-indeterminate -- have\n equal specificity, so which wins comes down to the order\n Tailwind happened to emit them in. Rendering one glyph makes\n the precedence explicit and puts it in the DOM, where a test\n can read it.\n\n Indeterminate wins: both states can be true at once (a \"select\n all\" box that has been partially cleared), and a tick there\n would say the opposite of the aria-checked=\"mixed\" beside it.\n */}\n {isIndeterminate ? (\n <svg\n data-slot=\"checkbox-indeterminate\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2.5\"\n strokeLinecap=\"round\"\n className=\"size-3\"\n >\n <path d=\"M4 8h8\" />\n </svg>\n ) : isSelected ? (\n <svg\n data-slot=\"checkbox-check\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className=\"size-3\"\n >\n <path d=\"m3.5 8.5 3 3 6-6\" />\n </svg>\n ) : null}\n </span>\n {children == null ? null : <span data-slot=\"checkbox-label\">{children}</span>}\n </>\n )}\n </AriaCheckbox>\n );\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+CA,MAAa,mBAAmB,IAAI,CAAC,uBAAuB,CAAC,CAAC;AAE9D,MAAa,sBAAsB,IAAI;CACrC;CAOA;CAIA;CAKA;CAEA;CAQA;CACA;CACA;CACA;CAIA,sBAAsB;AACxB,CAAC;AAuBD,MAAaA,aAAW,WAA4C,SAASA,WAC3E,EAAE,WAAW,UAAU,GAAG,SAC1B,KACA;CACA,OACE,oBAACC,UAAD;EACE,GAAI;EAKJ,UAAU;EACV,aAAU;EAIV,WAAW,GAAG,iBAAiB,GAAG,SAAS;EAEzC,WAAA,EAAE,YAAY,sBACd,qBAAA,UAAA,EAAA,UAAA,CAKE,oBAAC,QAAD;GAAM,eAAY;GAAO,aAAU;GAAe,WAAW,oBAAoB;GAa9E,UAAA,kBACC,oBAAC,OAAD;IACE,aAAU;IACV,SAAQ;IACR,MAAK;IACL,QAAO;IACP,aAAY;IACZ,eAAc;IACd,WAAU;IAEV,UAAA,oBAAC,QAAD,EAAM,GAAE,SAAU,CAAA;GACf,CAAA,IACH,aACF,oBAAC,OAAD;IACE,aAAU;IACV,SAAQ;IACR,MAAK;IACL,QAAO;IACP,aAAY;IACZ,eAAc;IACd,gBAAe;IACf,WAAU;IAEV,UAAA,oBAAC,QAAD,EAAM,GAAE,mBAAoB,CAAA;GACzB,CAAA,IACH;EACA,CAAA,GACL,YAAY,OAAO,OAAO,oBAAC,QAAD;GAAM,aAAU;GAAkB;EAAe,CAAA,CAC5E,EAAA,CAAA;CAEQ,CAAA;AAElB,CAAC"}
|
package/dist/dialog/dialog.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { DialogTrigger, ModalOverlayProps } from "react-aria-components";
|
|
2
1
|
import { ReactNode } from "react";
|
|
2
|
+
import { DialogTrigger, ModalOverlayProps } from "react-aria-components";
|
|
3
3
|
//#region src/dialog/dialog.d.ts
|
|
4
4
|
/**
|
|
5
5
|
* Dialog, ported from the legacy shadcn/Radix component onto React Aria's
|
|
@@ -120,7 +120,7 @@ export interface DialogProps extends Omit<ModalOverlayProps, "className" | "styl
|
|
|
120
120
|
*/
|
|
121
121
|
isDismissable?: boolean;
|
|
122
122
|
}
|
|
123
|
-
declare
|
|
123
|
+
declare const Dialog$1: import("react").ForwardRefExoticComponent<DialogProps & import("react").RefAttributes<HTMLElement>>;
|
|
124
124
|
/**
|
|
125
125
|
* Actions at the foot of a dialog.
|
|
126
126
|
*
|
|
@@ -134,7 +134,7 @@ export interface DialogFooterProps {
|
|
|
134
134
|
children?: ReactNode;
|
|
135
135
|
className?: string;
|
|
136
136
|
}
|
|
137
|
-
export declare
|
|
137
|
+
export declare const DialogFooter: import("react").ForwardRefExoticComponent<DialogFooterProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
138
138
|
//#endregion
|
|
139
139
|
export { Dialog$1 as Dialog, DialogTrigger };
|
|
140
140
|
//# sourceMappingURL=dialog.d.ts.map
|
package/dist/dialog/dialog.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { cn } from "../lib/cn.js";
|
|
3
3
|
import { cva } from "class-variance-authority";
|
|
4
|
+
import { forwardRef } from "react";
|
|
4
5
|
import { Button, Dialog, DialogTrigger, Heading, Modal, ModalOverlay, Text } from "react-aria-components";
|
|
5
6
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
7
|
//#region src/dialog/dialog.tsx
|
|
@@ -68,7 +69,7 @@ const dialogCloseVariants = cva([
|
|
|
68
69
|
"data-[focus-visible]:outline-(--color-ring)",
|
|
69
70
|
"[&_svg]:pointer-events-none [&_svg]:size-4"
|
|
70
71
|
]);
|
|
71
|
-
|
|
72
|
+
const Dialog$1 = forwardRef(function Dialog$2({ title, beforeTitle, description, children, className, closeButton = true, closeLabel = "Close", role = "dialog", isDismissable = true, overlayClassName, ...props }, ref) {
|
|
72
73
|
return /* @__PURE__ */ jsx(ModalOverlay, {
|
|
73
74
|
...props,
|
|
74
75
|
isDismissable,
|
|
@@ -77,6 +78,7 @@ function Dialog$1({ title, beforeTitle, description, children, className, closeB
|
|
|
77
78
|
children: /* @__PURE__ */ jsx(Modal, {
|
|
78
79
|
className: "contents",
|
|
79
80
|
children: /* @__PURE__ */ jsxs(Dialog, {
|
|
81
|
+
ref,
|
|
80
82
|
role,
|
|
81
83
|
"data-slot": "dialog",
|
|
82
84
|
className: cn(dialogPanelVariants(), className),
|
|
@@ -113,14 +115,15 @@ function Dialog$1({ title, beforeTitle, description, children, className, closeB
|
|
|
113
115
|
})
|
|
114
116
|
})
|
|
115
117
|
});
|
|
116
|
-
}
|
|
117
|
-
function DialogFooter({ children, className }) {
|
|
118
|
+
});
|
|
119
|
+
const DialogFooter = forwardRef(function DialogFooter({ children, className }, ref) {
|
|
118
120
|
return /* @__PURE__ */ jsx("div", {
|
|
121
|
+
ref,
|
|
119
122
|
"data-slot": "dialog-footer",
|
|
120
123
|
className: cn(dialogFooterVariants(), className),
|
|
121
124
|
children
|
|
122
125
|
});
|
|
123
|
-
}
|
|
126
|
+
});
|
|
124
127
|
//#endregion
|
|
125
128
|
export { Dialog$1 as Dialog, DialogFooter, DialogTrigger, dialogDescriptionVariants, dialogFooterVariants, dialogOverlayVariants, dialogPanelVariants, dialogTitleVariants };
|
|
126
129
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dialog.js","names":["Dialog","AriaModalOverlay","AriaModal","AriaDialog","AriaHeading","AriaText","AriaButton"],"sources":["../../src/dialog/dialog.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva } from \"class-variance-authority\";\nimport type { ReactNode } from \"react\";\nimport {\n Button as AriaButton,\n Dialog as AriaDialog,\n Heading as AriaHeading,\n Modal as AriaModal,\n ModalOverlay as AriaModalOverlay,\n Text as AriaText,\n type ModalOverlayProps as AriaModalOverlayProps,\n} from \"react-aria-components\";\n\nimport { cn } from \"../lib/cn.js\";\n\n/**\n * Dialog, ported from the legacy shadcn/Radix component onto React Aria's\n * Modal primitives.\n *\n * FOUR LEGACY PARTS COLLAPSE INTO TWO PROPS. The census (9 importing files,\n * but only **7** that render anything -- header.tsx and PollQuizCreator.tsx\n * import the parts and use none) reads: DialogContent 9, DialogTitle 8,\n * DialogHeader 7, DialogDescription 2. Those four always appear in the same\n * shape, so `title` and `description` are props of the dialog itself.\n *\n * That is not tidying. `title` is REQUIRED, which makes an unnamed dialog\n * impossible to write -- and the legacy shipped one: LiveQAOverlay.tsx\n * renders a DialogContent with no DialogTitle, so its `aria-labelledby`\n * points at an id that matches no element and the dialog is announced with\n * no name.\n *\n * It also fixes the dangling-description defect for free. Radix set\n * `aria-describedby` unconditionally from a `useId`, so seven of the nine\n * legacy dialogs referenced an id that did not exist. React Aria wires it\n * only when a description slot is actually rendered.\n *\n * `DialogPortal` and `DialogOverlay` are not ported: zero external call\n * sites, and React Aria's ModalOverlay does both jobs.\n *\n * NOT PORTED, and deliberately: the enter/exit animation. The legacy leans\n * on tailwindcss-animate's `animate-in` / `zoom-in-95` utilities, which this\n * repo does not ship, and the motion tokens are a separate task. React Aria\n * exposes `data-entering` / `data-exiting` for whoever adds them.\n *\n * Every string in the cva blocks below is a STATIC literal. Tailwind scans\n * source text, so a class assembled at runtime is invisible to it and would\n * simply never be emitted.\n */\nexport const dialogOverlayVariants = cva([\n // Centring by flex, not by `top-1/2 left-1/2 -translate-1/2`. The legacy's\n // translate pair is a bracket value that check:hardcoding rejects, and\n // flex centring needs no transform to undo.\n \"fixed inset-0 z-50 flex items-center justify-center\",\n // The inset that keeps a full-width panel off the viewport edge. The\n // legacy did this with max-w-[calc(100%-2rem)] -- also a bracket value.\n \"p-4\",\n \"bg-scrim\",\n]);\n\nexport const dialogPanelVariants = cva([\n // max-w-128 (32rem), NOT max-w-lg. `--container-lg` does not exist in this\n // repo -- the container scale holds only page-shell widths (max 75rem,\n // wide 90rem) -- so `max-w-lg` compiles to NOTHING and the panel spans the\n // viewport. It emitted no rule at all, tsc and ESLint were both blind to\n // it, and a screenshot is what caught it. 128 spacing units is 32rem,\n // exactly the legacy sm:max-w-lg.\n \"relative w-full max-w-128\",\n \"rounded-xl border-(length:--border-width) border-border p-6\",\n // bg-card, not bg-background: a dialog is a raised surface, and card is\n // the role that already steps up the ink ramp on dark, where a shadow made\n // of ink is invisible. Its contrast pairs are in the contract already.\n \"bg-card text-card-foreground shadow-modal\",\n \"flex flex-col gap-4\",\n // The reset in @seekalfred/styles is OPT-IN, so a consumer may load none:\n // content-box would add the 24px padding to max-w-lg, and an unstyled\n // document renders text in a serif. The Accordion shipped its panel that\n // way and the Alert overflowed by 46px.\n \"box-border font-sans\",\n // React Aria gives the dialog tabindex=-1 and focuses it when nothing\n // inside is autofocused, so it can receive a visible focus ring it does\n // not want.\n \"outline-none\",\n // A dialog taller than the viewport has to scroll, or its footer is\n // unreachable. max-h is on the panel rather than the overlay so the\n // padding above stays visible.\n \"max-h-full overflow-y-auto\",\n]);\n\nexport const dialogTitleVariants = cva([\n // leading-* AFTER text-*: tailwind-merge treats a later text-* as owning\n // line-height and silently strips a leading-* that precedes it.\n \"text-lg leading-(--leading-tight) font-semibold\",\n // The user-agent <h2> ships a 1em block margin. The legacy was inside a\n // Preflight'd app and never met it.\n \"m-0\",\n]);\n\nexport const dialogDescriptionVariants = cva([\n \"text-sm leading-(--leading-normal) text-muted-foreground\",\n // Same: the user-agent <p> block margin.\n \"m-0\",\n]);\n\nexport const dialogFooterVariants = cva([\n // NOT flex-col-reverse. The legacy reversed the footer below `sm`, so a\n // DOM order of Cancel-then-Confirm rendered Confirm-then-Cancel on a\n // phone -- WCAG 1.3.2 Meaningful Sequence, and both alert-dialog call\n // sites hit it. Wrapping keeps narrow viewports working without lying\n // about the order.\n \"flex flex-wrap items-center justify-end gap-2\",\n]);\n\nconst dialogCloseVariants = cva([\n \"absolute end-4 top-4 inline-flex size-6 items-center justify-center\",\n \"rounded-2xs box-border cursor-pointer\",\n \"border-(length:--border-width) border-transparent bg-transparent\",\n \"text-muted-foreground\",\n \"motion-safe:transition-colors motion-safe:duration-(--duration-fast) motion-safe:ease-standard\",\n // The legacy sat at opacity-70 until hover, dimming the only close\n // affordance below its already-marginal contrast. A token that is legible\n // at rest does the same job without that.\n \"data-[hovered]:text-foreground\",\n // outline-solid is load-bearing: outline-none sets --tw-outline-style:none,\n // and the width utility resolves its style from that variable, so a focus\n // rule can apply a width and a colour and paint nothing. Button shipped\n // exactly that.\n \"outline-none\",\n \"data-[focus-visible]:outline-solid\",\n \"data-[focus-visible]:outline-(length:--focus-ring-width)\",\n \"data-[focus-visible]:outline-offset-(--focus-ring-offset)\",\n \"data-[focus-visible]:outline-(--color-ring)\",\n \"[&_svg]:pointer-events-none [&_svg]:size-4\",\n]);\n\n/**\n * A modal dialog.\n *\n * @example\n * <Dialog isOpen={open} onOpenChange={setOpen} title=\"Delete user\"\n * description=\"This cannot be undone.\">\n * <DialogFooter>\n * <Button slot=\"close\" variant=\"ghost\">Cancel</Button>\n * <Button variant=\"destructive\" onPress={remove}>Delete</Button>\n * </DialogFooter>\n * </Dialog>\n */\nexport interface DialogProps\n extends Omit<AriaModalOverlayProps, \"className\" | \"style\" | \"children\"> {\n /**\n * The dialog's heading, and its accessible name.\n *\n * **Required.** The legacy allowed a dialog with no title and one call\n * site shipped that, producing an `aria-labelledby` pointing at nothing.\n * Making it a required prop turns that into something you cannot express.\n */\n title: ReactNode;\n /**\n * Rendered ABOVE the heading, inside the panel.\n *\n * Exists for Sheet's back control. A panel that replaces its own contents\n * rather than stacking a second modal needs a way back, and that control\n * cannot live in `title`: React Aria wires aria-labelledby to the heading,\n * so a button inside it becomes part of the dialog's accessible name --\n * \"Back Campaign details\" instead of \"Campaign details\".\n */\n beforeTitle?: ReactNode;\n /** Optional supporting text, wired to `aria-describedby` when present. */\n description?: ReactNode;\n /** The dialog body. */\n children?: ReactNode;\n /** Extra classes for the panel. Merged last, so they win conflicts. */\n className?: string;\n /**\n * Extra classes for the scrim, merged last.\n *\n * A centred modal never needs this. A Sheet does: it has to replace the\n * overlay's centring with an edge alignment, and it is the only way to\n * do that without a second copy of the whole overlay.\n */\n overlayClassName?: string;\n /**\n * Whether to render the corner close button.\n *\n * `true` by default. An `alertdialog` sets it `false`: a confirmation must\n * be answered, not dismissed silently.\n */\n closeButton?: boolean;\n /**\n * Accessible name for the close button.\n *\n * A prop because the legacy hard-coded the English string `Close` in an\n * `sr-only` span with no way to override it, which makes the component\n * unlocalisable.\n *\n * @default \"Close\"\n */\n closeLabel?: string;\n /** `alertdialog` for a destructive confirmation. @default \"dialog\" */\n role?: \"dialog\" | \"alertdialog\";\n /**\n * Whether clicking outside closes the dialog.\n *\n * `true`, which is NOT React Aria's default. React Aria ships\n * `isDismissable = false` (useOverlay.mjs:26); Radix, which the legacy\n * component wraps, closes on outside click unless told otherwise. Taking\n * the headless default here would have silently changed the behaviour of\n * all seven legacy call sites, and nothing in the suite would have\n * noticed.\n *\n * An `alertdialog` should set this `false`: a confirmation must be\n * answered, not clicked away.\n *\n * @default true\n */\n isDismissable?: boolean;\n}\n\nexport function Dialog({\n title,\n beforeTitle,\n description,\n children,\n className,\n closeButton = true,\n closeLabel = \"Close\",\n role = \"dialog\",\n isDismissable = true,\n overlayClassName,\n ...props\n}: DialogProps) {\n return (\n // ModalOverlay explicitly, not a bare <Modal>. A bare Modal still renders\n // an overlay div, but className lands on the INNER element -- so the\n // scrim would be unstyleable and would paint nothing at all.\n <AriaModalOverlay\n {...props}\n isDismissable={isDismissable}\n data-slot=\"dialog-overlay\"\n className={cn(dialogOverlayVariants(), overlayClassName)}\n >\n <AriaModal className=\"contents\">\n <AriaDialog\n role={role}\n data-slot=\"dialog\"\n className={cn(dialogPanelVariants(), className)}\n >\n {/*\n Above the heading, not inside it. Sheet's back control lives here:\n folding it into `title` would put a button inside the <h2> React\n Aria wires aria-labelledby to, so the dialog's accessible name\n would become \"Back Campaign details\".\n */}\n {beforeTitle}\n {/*\n slot=\"title\" is what React Aria wires aria-labelledby to. A plain\n <h2> here would render identically and leave the dialog unnamed.\n */}\n <AriaHeading slot=\"title\" data-slot=\"dialog-title\" className={dialogTitleVariants()}>\n {title}\n </AriaHeading>\n {description == null ? null : (\n <AriaText\n slot=\"description\"\n data-slot=\"dialog-description\"\n className={dialogDescriptionVariants()}\n >\n {description}\n </AriaText>\n )}\n {children}\n {closeButton ? (\n // slot=\"close\" is React Aria's own dismiss wiring; an onPress\n // that called onOpenChange by hand would not work inside an\n // uncontrolled DialogTrigger.\n <AriaButton slot=\"close\" aria-label={closeLabel} className={dialogCloseVariants()}>\n <svg\n aria-hidden=\"true\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.75\"\n strokeLinecap=\"round\"\n >\n <path d=\"m4 4 8 8M12 4l-8 8\" />\n </svg>\n </AriaButton>\n ) : null}\n </AriaDialog>\n </AriaModal>\n </AriaModalOverlay>\n );\n}\n\n/**\n * Actions at the foot of a dialog.\n *\n * @example\n * <DialogFooter>\n * <Button slot=\"close\" variant=\"ghost\">Cancel</Button>\n * <Button variant=\"destructive\" onPress={remove}>Delete</Button>\n * </DialogFooter>\n */\nexport interface DialogFooterProps {\n children?: ReactNode;\n className?: string;\n}\n\nexport function DialogFooter({ children, className }: DialogFooterProps) {\n return (\n <div data-slot=\"dialog-footer\" className={cn(dialogFooterVariants(), className)}>\n {children}\n </div>\n );\n}\n\n/**\n * Pairs a trigger with a dialog and owns the open state.\n *\n * Re-exported from React Aria unchanged. Only 3 of the 9 legacy call sites\n * use a trigger at all -- the rest drive `isOpen` themselves -- so wrapping\n * it would add a layer with nothing to do.\n */\nexport { DialogTrigger } from \"react-aria-components\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiDA,MAAa,wBAAwB,IAAI;CAIvC;CAGA;CACA;AACF,CAAC;AAED,MAAa,sBAAsB,IAAI;CAOrC;CACA;CAIA;CACA;CAKA;CAIA;CAIA;AACF,CAAC;AAED,MAAa,sBAAsB,IAAI,CAGrC,mDAGA,KACF,CAAC;AAED,MAAa,4BAA4B,IAAI,CAC3C,4DAEA,KACF,CAAC;AAED,MAAa,uBAAuB,IAAI,CAMtC,+CACF,CAAC;AAED,MAAM,sBAAsB,IAAI;CAC9B;CACA;CACA;CACA;CACA;CAIA;CAKA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;AAqFD,SAAgBA,SAAO,EACrB,OACA,aACA,aACA,UACA,WACA,cAAc,MACd,aAAa,SACb,OAAO,UACP,gBAAgB,MAChB,kBACA,GAAG,SACW;CACd,OAIE,oBAACC,cAAD;EACE,GAAI;EACW;EACf,aAAU;EACV,WAAW,GAAG,sBAAsB,GAAG,gBAAgB;EAEvD,UAAA,oBAACC,OAAD;GAAW,WAAU;GACnB,UAAA,qBAACC,QAAD;IACQ;IACN,aAAU;IACV,WAAW,GAAG,oBAAoB,GAAG,SAAS;IAHhD,UAAA;KAWG;KAKD,oBAACC,SAAD;MAAa,MAAK;MAAQ,aAAU;MAAe,WAAW,oBAAoB;MAC/E,UAAA;KACU,CAAA;KACZ,eAAe,OAAO,OACrB,oBAACC,MAAD;MACE,MAAK;MACL,aAAU;MACV,WAAW,0BAA0B;MAEpC,UAAA;KACO,CAAA;KAEX;KACA,cAIC,oBAACC,QAAD;MAAY,MAAK;MAAQ,cAAY;MAAY,WAAW,oBAAoB;MAC9E,UAAA,oBAAC,OAAD;OACE,eAAY;OACZ,SAAQ;OACR,MAAK;OACL,QAAO;OACP,aAAY;OACZ,eAAc;OAEd,UAAA,oBAAC,QAAD,EAAM,GAAE,qBAAsB,CAAA;MAC3B,CAAA;KACK,CAAA,IACV;IACM;;EACH,CAAA;CACK,CAAA;AAEtB;AAgBA,SAAgB,aAAa,EAAE,UAAU,aAAgC;CACvE,OACE,oBAAC,OAAD;EAAK,aAAU;EAAgB,WAAW,GAAG,qBAAqB,GAAG,SAAS;EAC3E;CACE,CAAA;AAET"}
|
|
1
|
+
{"version":3,"file":"dialog.js","names":["Dialog","AriaModalOverlay","AriaModal","AriaDialog","AriaHeading","AriaText","AriaButton"],"sources":["../../src/dialog/dialog.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva } from \"class-variance-authority\";\nimport { forwardRef, type ReactNode } from \"react\";\nimport {\n Button as AriaButton,\n Dialog as AriaDialog,\n Heading as AriaHeading,\n Modal as AriaModal,\n ModalOverlay as AriaModalOverlay,\n Text as AriaText,\n type ModalOverlayProps as AriaModalOverlayProps,\n} from \"react-aria-components\";\n\nimport { cn } from \"../lib/cn.js\";\n\n/**\n * Dialog, ported from the legacy shadcn/Radix component onto React Aria's\n * Modal primitives.\n *\n * FOUR LEGACY PARTS COLLAPSE INTO TWO PROPS. The census (9 importing files,\n * but only **7** that render anything -- header.tsx and PollQuizCreator.tsx\n * import the parts and use none) reads: DialogContent 9, DialogTitle 8,\n * DialogHeader 7, DialogDescription 2. Those four always appear in the same\n * shape, so `title` and `description` are props of the dialog itself.\n *\n * That is not tidying. `title` is REQUIRED, which makes an unnamed dialog\n * impossible to write -- and the legacy shipped one: LiveQAOverlay.tsx\n * renders a DialogContent with no DialogTitle, so its `aria-labelledby`\n * points at an id that matches no element and the dialog is announced with\n * no name.\n *\n * It also fixes the dangling-description defect for free. Radix set\n * `aria-describedby` unconditionally from a `useId`, so seven of the nine\n * legacy dialogs referenced an id that did not exist. React Aria wires it\n * only when a description slot is actually rendered.\n *\n * `DialogPortal` and `DialogOverlay` are not ported: zero external call\n * sites, and React Aria's ModalOverlay does both jobs.\n *\n * NOT PORTED, and deliberately: the enter/exit animation. The legacy leans\n * on tailwindcss-animate's `animate-in` / `zoom-in-95` utilities, which this\n * repo does not ship, and the motion tokens are a separate task. React Aria\n * exposes `data-entering` / `data-exiting` for whoever adds them.\n *\n * Every string in the cva blocks below is a STATIC literal. Tailwind scans\n * source text, so a class assembled at runtime is invisible to it and would\n * simply never be emitted.\n */\nexport const dialogOverlayVariants = cva([\n // Centring by flex, not by `top-1/2 left-1/2 -translate-1/2`. The legacy's\n // translate pair is a bracket value that check:hardcoding rejects, and\n // flex centring needs no transform to undo.\n \"fixed inset-0 z-50 flex items-center justify-center\",\n // The inset that keeps a full-width panel off the viewport edge. The\n // legacy did this with max-w-[calc(100%-2rem)] -- also a bracket value.\n \"p-4\",\n \"bg-scrim\",\n]);\n\nexport const dialogPanelVariants = cva([\n // max-w-128 (32rem), NOT max-w-lg. `--container-lg` does not exist in this\n // repo -- the container scale holds only page-shell widths (max 75rem,\n // wide 90rem) -- so `max-w-lg` compiles to NOTHING and the panel spans the\n // viewport. It emitted no rule at all, tsc and ESLint were both blind to\n // it, and a screenshot is what caught it. 128 spacing units is 32rem,\n // exactly the legacy sm:max-w-lg.\n \"relative w-full max-w-128\",\n \"rounded-xl border-(length:--border-width) border-border p-6\",\n // bg-card, not bg-background: a dialog is a raised surface, and card is\n // the role that already steps up the ink ramp on dark, where a shadow made\n // of ink is invisible. Its contrast pairs are in the contract already.\n \"bg-card text-card-foreground shadow-modal\",\n \"flex flex-col gap-4\",\n // The reset in @seekalfred/styles is OPT-IN, so a consumer may load none:\n // content-box would add the 24px padding to max-w-lg, and an unstyled\n // document renders text in a serif. The Accordion shipped its panel that\n // way and the Alert overflowed by 46px.\n \"box-border font-sans\",\n // React Aria gives the dialog tabindex=-1 and focuses it when nothing\n // inside is autofocused, so it can receive a visible focus ring it does\n // not want.\n \"outline-none\",\n // A dialog taller than the viewport has to scroll, or its footer is\n // unreachable. max-h is on the panel rather than the overlay so the\n // padding above stays visible.\n \"max-h-full overflow-y-auto\",\n]);\n\nexport const dialogTitleVariants = cva([\n // leading-* AFTER text-*: tailwind-merge treats a later text-* as owning\n // line-height and silently strips a leading-* that precedes it.\n \"text-lg leading-(--leading-tight) font-semibold\",\n // The user-agent <h2> ships a 1em block margin. The legacy was inside a\n // Preflight'd app and never met it.\n \"m-0\",\n]);\n\nexport const dialogDescriptionVariants = cva([\n \"text-sm leading-(--leading-normal) text-muted-foreground\",\n // Same: the user-agent <p> block margin.\n \"m-0\",\n]);\n\nexport const dialogFooterVariants = cva([\n // NOT flex-col-reverse. The legacy reversed the footer below `sm`, so a\n // DOM order of Cancel-then-Confirm rendered Confirm-then-Cancel on a\n // phone -- WCAG 1.3.2 Meaningful Sequence, and both alert-dialog call\n // sites hit it. Wrapping keeps narrow viewports working without lying\n // about the order.\n \"flex flex-wrap items-center justify-end gap-2\",\n]);\n\nconst dialogCloseVariants = cva([\n \"absolute end-4 top-4 inline-flex size-6 items-center justify-center\",\n \"rounded-2xs box-border cursor-pointer\",\n \"border-(length:--border-width) border-transparent bg-transparent\",\n \"text-muted-foreground\",\n \"motion-safe:transition-colors motion-safe:duration-(--duration-fast) motion-safe:ease-standard\",\n // The legacy sat at opacity-70 until hover, dimming the only close\n // affordance below its already-marginal contrast. A token that is legible\n // at rest does the same job without that.\n \"data-[hovered]:text-foreground\",\n // outline-solid is load-bearing: outline-none sets --tw-outline-style:none,\n // and the width utility resolves its style from that variable, so a focus\n // rule can apply a width and a colour and paint nothing. Button shipped\n // exactly that.\n \"outline-none\",\n \"data-[focus-visible]:outline-solid\",\n \"data-[focus-visible]:outline-(length:--focus-ring-width)\",\n \"data-[focus-visible]:outline-offset-(--focus-ring-offset)\",\n \"data-[focus-visible]:outline-(--color-ring)\",\n \"[&_svg]:pointer-events-none [&_svg]:size-4\",\n]);\n\n/**\n * A modal dialog.\n *\n * @example\n * <Dialog isOpen={open} onOpenChange={setOpen} title=\"Delete user\"\n * description=\"This cannot be undone.\">\n * <DialogFooter>\n * <Button slot=\"close\" variant=\"ghost\">Cancel</Button>\n * <Button variant=\"destructive\" onPress={remove}>Delete</Button>\n * </DialogFooter>\n * </Dialog>\n */\nexport interface DialogProps\n extends Omit<AriaModalOverlayProps, \"className\" | \"style\" | \"children\"> {\n /**\n * The dialog's heading, and its accessible name.\n *\n * **Required.** The legacy allowed a dialog with no title and one call\n * site shipped that, producing an `aria-labelledby` pointing at nothing.\n * Making it a required prop turns that into something you cannot express.\n */\n title: ReactNode;\n /**\n * Rendered ABOVE the heading, inside the panel.\n *\n * Exists for Sheet's back control. A panel that replaces its own contents\n * rather than stacking a second modal needs a way back, and that control\n * cannot live in `title`: React Aria wires aria-labelledby to the heading,\n * so a button inside it becomes part of the dialog's accessible name --\n * \"Back Campaign details\" instead of \"Campaign details\".\n */\n beforeTitle?: ReactNode;\n /** Optional supporting text, wired to `aria-describedby` when present. */\n description?: ReactNode;\n /** The dialog body. */\n children?: ReactNode;\n /** Extra classes for the panel. Merged last, so they win conflicts. */\n className?: string;\n /**\n * Extra classes for the scrim, merged last.\n *\n * A centred modal never needs this. A Sheet does: it has to replace the\n * overlay's centring with an edge alignment, and it is the only way to\n * do that without a second copy of the whole overlay.\n */\n overlayClassName?: string;\n /**\n * Whether to render the corner close button.\n *\n * `true` by default. An `alertdialog` sets it `false`: a confirmation must\n * be answered, not dismissed silently.\n */\n closeButton?: boolean;\n /**\n * Accessible name for the close button.\n *\n * A prop because the legacy hard-coded the English string `Close` in an\n * `sr-only` span with no way to override it, which makes the component\n * unlocalisable.\n *\n * @default \"Close\"\n */\n closeLabel?: string;\n /** `alertdialog` for a destructive confirmation. @default \"dialog\" */\n role?: \"dialog\" | \"alertdialog\";\n /**\n * Whether clicking outside closes the dialog.\n *\n * `true`, which is NOT React Aria's default. React Aria ships\n * `isDismissable = false` (useOverlay.mjs:26); Radix, which the legacy\n * component wraps, closes on outside click unless told otherwise. Taking\n * the headless default here would have silently changed the behaviour of\n * all seven legacy call sites, and nothing in the suite would have\n * noticed.\n *\n * An `alertdialog` should set this `false`: a confirmation must be\n * answered, not clicked away.\n *\n * @default true\n */\n isDismissable?: boolean;\n}\n\nexport const Dialog = forwardRef<HTMLElement, DialogProps>(function Dialog(\n {\n title,\n beforeTitle,\n description,\n children,\n className,\n closeButton = true,\n closeLabel = \"Close\",\n role = \"dialog\",\n isDismissable = true,\n overlayClassName,\n ...props\n },\n ref,\n) {\n return (\n // ModalOverlay explicitly, not a bare <Modal>. A bare Modal still renders\n // an overlay div, but className lands on the INNER element -- so the\n // scrim would be unstyleable and would paint nothing at all.\n <AriaModalOverlay\n {...props}\n isDismissable={isDismissable}\n data-slot=\"dialog-overlay\"\n className={cn(dialogOverlayVariants(), overlayClassName)}\n >\n <AriaModal className=\"contents\">\n <AriaDialog\n // Modal is a transparent pass-through (className=\"contents\", no\n // box of its own) and ModalOverlay is the scrim -- AriaDialog is\n // the only one of the three that is the visible panel, so the ref\n // goes here and not on either ancestor.\n ref={ref}\n role={role}\n data-slot=\"dialog\"\n className={cn(dialogPanelVariants(), className)}\n >\n {/*\n Above the heading, not inside it. Sheet's back control lives here:\n folding it into `title` would put a button inside the <h2> React\n Aria wires aria-labelledby to, so the dialog's accessible name\n would become \"Back Campaign details\".\n */}\n {beforeTitle}\n {/*\n slot=\"title\" is what React Aria wires aria-labelledby to. A plain\n <h2> here would render identically and leave the dialog unnamed.\n */}\n <AriaHeading slot=\"title\" data-slot=\"dialog-title\" className={dialogTitleVariants()}>\n {title}\n </AriaHeading>\n {description == null ? null : (\n <AriaText\n slot=\"description\"\n data-slot=\"dialog-description\"\n className={dialogDescriptionVariants()}\n >\n {description}\n </AriaText>\n )}\n {children}\n {closeButton ? (\n // slot=\"close\" is React Aria's own dismiss wiring; an onPress\n // that called onOpenChange by hand would not work inside an\n // uncontrolled DialogTrigger.\n <AriaButton slot=\"close\" aria-label={closeLabel} className={dialogCloseVariants()}>\n <svg\n aria-hidden=\"true\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.75\"\n strokeLinecap=\"round\"\n >\n <path d=\"m4 4 8 8M12 4l-8 8\" />\n </svg>\n </AriaButton>\n ) : null}\n </AriaDialog>\n </AriaModal>\n </AriaModalOverlay>\n );\n});\n\n/**\n * Actions at the foot of a dialog.\n *\n * @example\n * <DialogFooter>\n * <Button slot=\"close\" variant=\"ghost\">Cancel</Button>\n * <Button variant=\"destructive\" onPress={remove}>Delete</Button>\n * </DialogFooter>\n */\nexport interface DialogFooterProps {\n children?: ReactNode;\n className?: string;\n}\n\nexport const DialogFooter = forwardRef<HTMLDivElement, DialogFooterProps>(function DialogFooter(\n { children, className },\n ref,\n) {\n return (\n <div ref={ref} data-slot=\"dialog-footer\" className={cn(dialogFooterVariants(), className)}>\n {children}\n </div>\n );\n});\n\n/**\n * Pairs a trigger with a dialog and owns the open state.\n *\n * Re-exported from React Aria unchanged. Only 3 of the 9 legacy call sites\n * use a trigger at all -- the rest drive `isOpen` themselves -- so wrapping\n * it would add a layer with nothing to do.\n */\nexport { DialogTrigger } from \"react-aria-components\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiDA,MAAa,wBAAwB,IAAI;CAIvC;CAGA;CACA;AACF,CAAC;AAED,MAAa,sBAAsB,IAAI;CAOrC;CACA;CAIA;CACA;CAKA;CAIA;CAIA;AACF,CAAC;AAED,MAAa,sBAAsB,IAAI,CAGrC,mDAGA,KACF,CAAC;AAED,MAAa,4BAA4B,IAAI,CAC3C,4DAEA,KACF,CAAC;AAED,MAAa,uBAAuB,IAAI,CAMtC,+CACF,CAAC;AAED,MAAM,sBAAsB,IAAI;CAC9B;CACA;CACA;CACA;CACA;CAIA;CAKA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;AAqFD,MAAaA,WAAS,WAAqC,SAASA,SAClE,EACE,OACA,aACA,aACA,UACA,WACA,cAAc,MACd,aAAa,SACb,OAAO,UACP,gBAAgB,MAChB,kBACA,GAAG,SAEL,KACA;CACA,OAIE,oBAACC,cAAD;EACE,GAAI;EACW;EACf,aAAU;EACV,WAAW,GAAG,sBAAsB,GAAG,gBAAgB;EAEvD,UAAA,oBAACC,OAAD;GAAW,WAAU;GACnB,UAAA,qBAACC,QAAD;IAKO;IACC;IACN,aAAU;IACV,WAAW,GAAG,oBAAoB,GAAG,SAAS;IARhD,UAAA;KAgBG;KAKD,oBAACC,SAAD;MAAa,MAAK;MAAQ,aAAU;MAAe,WAAW,oBAAoB;MAC/E,UAAA;KACU,CAAA;KACZ,eAAe,OAAO,OACrB,oBAACC,MAAD;MACE,MAAK;MACL,aAAU;MACV,WAAW,0BAA0B;MAEpC,UAAA;KACO,CAAA;KAEX;KACA,cAIC,oBAACC,QAAD;MAAY,MAAK;MAAQ,cAAY;MAAY,WAAW,oBAAoB;MAC9E,UAAA,oBAAC,OAAD;OACE,eAAY;OACZ,SAAQ;OACR,MAAK;OACL,QAAO;OACP,aAAY;OACZ,eAAc;OAEd,UAAA,oBAAC,QAAD,EAAM,GAAE,qBAAsB,CAAA;MAC3B,CAAA;KACK,CAAA,IACV;IACM;;EACH,CAAA;CACK,CAAA;AAEtB,CAAC;AAgBD,MAAa,eAAe,WAA8C,SAAS,aACjF,EAAE,UAAU,aACZ,KACA;CACA,OACE,oBAAC,OAAD;EAAU;EAAK,aAAU;EAAgB,WAAW,GAAG,qBAAqB,GAAG,SAAS;EACrF;CACE,CAAA;AAET,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ import { Card, CardContent, CardContentProps, CardDescription, CardDescriptionPr
|
|
|
11
11
|
import { Checkbox, CheckboxProps, checkboxBoxVariants, checkboxVariants } from "./checkbox/checkbox.js";
|
|
12
12
|
import { Input, InputProps, inputVariants } from "./input/input.js";
|
|
13
13
|
import { Label, LabelProps, labelVariants } from "./label/label.js";
|
|
14
|
-
import { cn } from "./lib/cn.js";
|
|
14
|
+
import { cn, cnAll } from "./lib/cn.js";
|
|
15
15
|
import { Menu, MenuItem, MenuItemProps, MenuProps, MenuSeparator, MenuTrigger, menuItemVariants, menuPopoverVariants, menuSeparatorVariants, menuVariants } from "./menu/menu.js";
|
|
16
16
|
import { Popover, PopoverProps, PopoverTrigger, popoverVariants } from "./popover/popover.js";
|
|
17
17
|
import { Progress, ProgressProps, progressFillVariants, progressTrackVariants, progressVariants } from "./progress/progress.js";
|
|
@@ -29,4 +29,4 @@ import { Tab, TabList, TabListProps, TabPanel, TabPanelProps, TabProps, Tabs, Ta
|
|
|
29
29
|
import { Textarea, TextareaProps, textareaVariants } from "./textarea/textarea.js";
|
|
30
30
|
import { ToastContent, ToastRegion, ToastRegionProps, toast, toastCloseVariants, toastQueue, toastRegionVariants, toastVariants } from "./toast/toast.js";
|
|
31
31
|
import { Tooltip, TooltipProps, TooltipTrigger, TooltipTriggerProps, tooltipVariants } from "./tooltip/tooltip.js";
|
|
32
|
-
export { Accordion, AccordionItem, type AccordionItemProps, AccordionPanel, type AccordionPanelProps, type AccordionProps, AccordionTrigger, type AccordionTriggerProps, Alert, AlertDialog, type AlertDialogProps, type AlertProps, Avatar, type AvatarProps, Badge, type BadgeProps, BreadcrumbItem, type BreadcrumbItemProps, Breadcrumbs, type BreadcrumbsProps, Button, type ButtonProps, Calendar, type CalendarProps, CalendarRange, type CalendarRangeProps, Card, CardContent, type CardContentProps, CardDescription, type CardDescriptionProps, CardFooter, type CardFooterProps, CardHeader, type CardHeaderProps, type CardProps, CardTitle, type CardTitleProps, Checkbox, type CheckboxProps, type Column, Dialog, DialogFooter, type DialogFooterProps, type DialogProps, DialogTrigger, Input, type InputProps, Label, type LabelProps, Menu, MenuItem, type MenuItemProps, type MenuProps, MenuSeparator, MenuTrigger, Popover, type PopoverProps, PopoverTrigger, Progress, type ProgressProps, Radio, RadioGroup, type RadioGroupProps, type RadioProps, type RowKey, ScrollArea, type ScrollAreaProps, Select, SelectItem, type SelectItemProps, type SelectProps, Separator, type SeparatorProps, Sheet, type SheetProps, Skeleton, type SkeletonProps, Slider, type SliderProps, Spinner, type SpinnerProps, Switch, type SwitchProps, Tab, TabList, type TabListProps, TabPanel, type TabPanelProps, type TabProps, Table, type TableProps, Tabs, type TabsProps, Textarea, type TextareaProps, type ToastContent, ToastRegion, type ToastRegionProps, Tooltip, type TooltipProps, TooltipTrigger, type TooltipTriggerProps, alertVariants, avatarVariants, badgeVariants, breadcrumbItemVariants, breadcrumbLinkVariants, breadcrumbsVariants, buttonVariants, calendarCellVariants, calendarClearVariants, calendarGridVariants, calendarHeaderCellVariants, calendarHeaderVariants, calendarHeadingVariants, calendarLabelVariants, calendarMonthsVariants, calendarNavVariants, calendarVariants, cardContentVariants, cardDescriptionVariants, cardFooterVariants, cardHeaderVariants, cardTitleVariants, cardVariants, checkboxBoxVariants, checkboxVariants, cn, createColumns, dialogDescriptionVariants, dialogFooterVariants, dialogOverlayVariants, dialogPanelVariants, dialogTitleVariants, inputVariants, labelVariants, menuItemVariants, menuPopoverVariants, menuSeparatorVariants, menuVariants, popoverVariants, progressFillVariants, progressTrackVariants, progressVariants, radioGroupVariants, radioVariants, scrollAreaVariants, selectItemVariants, selectPopoverVariants, selectTriggerVariants, selectVariants, separatorVariants, sheetOverlayVariants, sheetPanelVariants, skeletonVariants, sliderBarVariants, sliderFillVariants, sliderThumbVariants, sliderTrackVariants, sliderVariants, spinnerVariants, switchThumbVariants, switchTrackVariants, tabListVariants, tabPanelVariants, tabVariants, tableCardVariants, tableCellVariants, tableColumnVariants, tableFooterVariants, tableHeaderVariants, tablePaginationVariants, tableRowVariants, tableScrollVariants, tableVariants, tabsVariants, textareaVariants, toast, toastCloseVariants, toastQueue, toastRegionVariants, toastVariants, tooltipVariants };
|
|
32
|
+
export { Accordion, AccordionItem, type AccordionItemProps, AccordionPanel, type AccordionPanelProps, type AccordionProps, AccordionTrigger, type AccordionTriggerProps, Alert, AlertDialog, type AlertDialogProps, type AlertProps, Avatar, type AvatarProps, Badge, type BadgeProps, BreadcrumbItem, type BreadcrumbItemProps, Breadcrumbs, type BreadcrumbsProps, Button, type ButtonProps, Calendar, type CalendarProps, CalendarRange, type CalendarRangeProps, Card, CardContent, type CardContentProps, CardDescription, type CardDescriptionProps, CardFooter, type CardFooterProps, CardHeader, type CardHeaderProps, type CardProps, CardTitle, type CardTitleProps, Checkbox, type CheckboxProps, type Column, Dialog, DialogFooter, type DialogFooterProps, type DialogProps, DialogTrigger, Input, type InputProps, Label, type LabelProps, Menu, MenuItem, type MenuItemProps, type MenuProps, MenuSeparator, MenuTrigger, Popover, type PopoverProps, PopoverTrigger, Progress, type ProgressProps, Radio, RadioGroup, type RadioGroupProps, type RadioProps, type RowKey, ScrollArea, type ScrollAreaProps, Select, SelectItem, type SelectItemProps, type SelectProps, Separator, type SeparatorProps, Sheet, type SheetProps, Skeleton, type SkeletonProps, Slider, type SliderProps, Spinner, type SpinnerProps, Switch, type SwitchProps, Tab, TabList, type TabListProps, TabPanel, type TabPanelProps, type TabProps, Table, type TableProps, Tabs, type TabsProps, Textarea, type TextareaProps, type ToastContent, ToastRegion, type ToastRegionProps, Tooltip, type TooltipProps, TooltipTrigger, type TooltipTriggerProps, alertVariants, avatarVariants, badgeVariants, breadcrumbItemVariants, breadcrumbLinkVariants, breadcrumbsVariants, buttonVariants, calendarCellVariants, calendarClearVariants, calendarGridVariants, calendarHeaderCellVariants, calendarHeaderVariants, calendarHeadingVariants, calendarLabelVariants, calendarMonthsVariants, calendarNavVariants, calendarVariants, cardContentVariants, cardDescriptionVariants, cardFooterVariants, cardHeaderVariants, cardTitleVariants, cardVariants, checkboxBoxVariants, checkboxVariants, cn, cnAll, createColumns, dialogDescriptionVariants, dialogFooterVariants, dialogOverlayVariants, dialogPanelVariants, dialogTitleVariants, inputVariants, labelVariants, menuItemVariants, menuPopoverVariants, menuSeparatorVariants, menuVariants, popoverVariants, progressFillVariants, progressTrackVariants, progressVariants, radioGroupVariants, radioVariants, scrollAreaVariants, selectItemVariants, selectPopoverVariants, selectTriggerVariants, selectVariants, separatorVariants, sheetOverlayVariants, sheetPanelVariants, skeletonVariants, sliderBarVariants, sliderFillVariants, sliderThumbVariants, sliderTrackVariants, sliderVariants, spinnerVariants, switchThumbVariants, switchTrackVariants, tabListVariants, tabPanelVariants, tabVariants, tableCardVariants, tableCellVariants, tableColumnVariants, tableFooterVariants, tableHeaderVariants, tablePaginationVariants, tableRowVariants, tableScrollVariants, tableVariants, tabsVariants, textareaVariants, toast, toastCloseVariants, toastQueue, toastRegionVariants, toastVariants, tooltipVariants };
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { cn } from "./lib/cn.js";
|
|
1
|
+
import { cn, cnAll } from "./lib/cn.js";
|
|
2
2
|
import { Accordion, AccordionItem, AccordionPanel, AccordionTrigger } from "./accordion/accordion.js";
|
|
3
3
|
import { Dialog, DialogFooter, DialogTrigger, dialogDescriptionVariants, dialogFooterVariants, dialogOverlayVariants, dialogPanelVariants, dialogTitleVariants } from "./dialog/dialog.js";
|
|
4
4
|
import { AlertDialog } from "./alert-dialog/alert-dialog.js";
|
|
@@ -29,4 +29,4 @@ import { Tab, TabList, TabPanel, Tabs, tabListVariants, tabPanelVariants, tabVar
|
|
|
29
29
|
import { Textarea, textareaVariants } from "./textarea/textarea.js";
|
|
30
30
|
import { ToastRegion, toast, toastCloseVariants, toastQueue, toastRegionVariants, toastVariants } from "./toast/toast.js";
|
|
31
31
|
import { Tooltip, TooltipTrigger, tooltipVariants } from "./tooltip/tooltip.js";
|
|
32
|
-
export { Accordion, AccordionItem, AccordionPanel, AccordionTrigger, Alert, AlertDialog, Avatar, Badge, BreadcrumbItem, Breadcrumbs, Button, Calendar, CalendarRange, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Checkbox, Dialog, DialogFooter, DialogTrigger, Input, Label, Menu, MenuItem, MenuSeparator, MenuTrigger, Popover, PopoverTrigger, Progress, Radio, RadioGroup, ScrollArea, Select, SelectItem, Separator, Sheet, Skeleton, Slider, Spinner, Switch, Tab, TabList, TabPanel, Table, Tabs, Textarea, ToastRegion, Tooltip, TooltipTrigger, alertVariants, avatarVariants, badgeVariants, breadcrumbItemVariants, breadcrumbLinkVariants, breadcrumbsVariants, buttonVariants, calendarCellVariants, calendarClearVariants, calendarGridVariants, calendarHeaderCellVariants, calendarHeaderVariants, calendarHeadingVariants, calendarLabelVariants, calendarMonthsVariants, calendarNavVariants, calendarVariants, cardContentVariants, cardDescriptionVariants, cardFooterVariants, cardHeaderVariants, cardTitleVariants, cardVariants, checkboxBoxVariants, checkboxVariants, cn, createColumns, dialogDescriptionVariants, dialogFooterVariants, dialogOverlayVariants, dialogPanelVariants, dialogTitleVariants, inputVariants, labelVariants, menuItemVariants, menuPopoverVariants, menuSeparatorVariants, menuVariants, popoverVariants, progressFillVariants, progressTrackVariants, progressVariants, radioGroupVariants, radioVariants, scrollAreaVariants, selectItemVariants, selectPopoverVariants, selectTriggerVariants, selectVariants, separatorVariants, sheetOverlayVariants, sheetPanelVariants, skeletonVariants, sliderBarVariants, sliderFillVariants, sliderThumbVariants, sliderTrackVariants, sliderVariants, spinnerVariants, switchThumbVariants, switchTrackVariants, tabListVariants, tabPanelVariants, tabVariants, tableCardVariants, tableCellVariants, tableColumnVariants, tableFooterVariants, tableHeaderVariants, tablePaginationVariants, tableRowVariants, tableScrollVariants, tableVariants, tabsVariants, textareaVariants, toast, toastCloseVariants, toastQueue, toastRegionVariants, toastVariants, tooltipVariants };
|
|
32
|
+
export { Accordion, AccordionItem, AccordionPanel, AccordionTrigger, Alert, AlertDialog, Avatar, Badge, BreadcrumbItem, Breadcrumbs, Button, Calendar, CalendarRange, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Checkbox, Dialog, DialogFooter, DialogTrigger, Input, Label, Menu, MenuItem, MenuSeparator, MenuTrigger, Popover, PopoverTrigger, Progress, Radio, RadioGroup, ScrollArea, Select, SelectItem, Separator, Sheet, Skeleton, Slider, Spinner, Switch, Tab, TabList, TabPanel, Table, Tabs, Textarea, ToastRegion, Tooltip, TooltipTrigger, alertVariants, avatarVariants, badgeVariants, breadcrumbItemVariants, breadcrumbLinkVariants, breadcrumbsVariants, buttonVariants, calendarCellVariants, calendarClearVariants, calendarGridVariants, calendarHeaderCellVariants, calendarHeaderVariants, calendarHeadingVariants, calendarLabelVariants, calendarMonthsVariants, calendarNavVariants, calendarVariants, cardContentVariants, cardDescriptionVariants, cardFooterVariants, cardHeaderVariants, cardTitleVariants, cardVariants, checkboxBoxVariants, checkboxVariants, cn, cnAll, createColumns, dialogDescriptionVariants, dialogFooterVariants, dialogOverlayVariants, dialogPanelVariants, dialogTitleVariants, inputVariants, labelVariants, menuItemVariants, menuPopoverVariants, menuSeparatorVariants, menuVariants, popoverVariants, progressFillVariants, progressTrackVariants, progressVariants, radioGroupVariants, radioVariants, scrollAreaVariants, selectItemVariants, selectPopoverVariants, selectTriggerVariants, selectVariants, separatorVariants, sheetOverlayVariants, sheetPanelVariants, skeletonVariants, sliderBarVariants, sliderFillVariants, sliderThumbVariants, sliderTrackVariants, sliderVariants, spinnerVariants, switchThumbVariants, switchTrackVariants, tabListVariants, tabPanelVariants, tabVariants, tableCardVariants, tableCellVariants, tableColumnVariants, tableFooterVariants, tableHeaderVariants, tablePaginationVariants, tableRowVariants, tableScrollVariants, tableVariants, tabsVariants, textareaVariants, toast, toastCloseVariants, toastQueue, toastRegionVariants, toastVariants, tooltipVariants };
|
package/dist/input/input.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { InputProps } from "react-aria-components";
|
|
2
1
|
import { ReactNode } from "react";
|
|
2
|
+
import { InputProps } from "react-aria-components";
|
|
3
3
|
//#region src/input/input.d.ts
|
|
4
4
|
/**
|
|
5
5
|
* Input, ported from the legacy shadcn component onto React Aria's Input.
|
|
@@ -74,7 +74,7 @@ interface InputProps$1 extends Omit<InputProps, "className" | "style" | "size">
|
|
|
74
74
|
/** Decorative glyph at the trailing edge. Same rules as {@link InputProps.startIcon}. */
|
|
75
75
|
endIcon?: ReactNode;
|
|
76
76
|
}
|
|
77
|
-
declare
|
|
77
|
+
declare const Input$1: import("react").ForwardRefExoticComponent<InputProps$1 & import("react").RefAttributes<HTMLInputElement>>;
|
|
78
78
|
//#endregion
|
|
79
79
|
export { Input$1 as Input, InputProps$1 as InputProps };
|
|
80
80
|
//# sourceMappingURL=input.d.ts.map
|
package/dist/input/input.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { cn } from "../lib/cn.js";
|
|
3
3
|
import { fieldSurfaceVariants } from "../lib/field-surface.js";
|
|
4
4
|
import { cva } from "class-variance-authority";
|
|
5
|
+
import { forwardRef } from "react";
|
|
5
6
|
import { Input } from "react-aria-components";
|
|
6
7
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
8
|
//#region src/input/input.tsx
|
|
@@ -154,8 +155,9 @@ const affixVariants = cva([
|
|
|
154
155
|
],
|
|
155
156
|
defaultVariants: { size: "md" }
|
|
156
157
|
});
|
|
157
|
-
|
|
158
|
+
const Input$1 = forwardRef(function Input$2({ className, size = "md", startIcon, endIcon, ...props }, ref) {
|
|
158
159
|
const input = /* @__PURE__ */ jsx(Input, {
|
|
160
|
+
ref,
|
|
159
161
|
...props,
|
|
160
162
|
"data-slot": "input",
|
|
161
163
|
className: cn(inputVariants({
|
|
@@ -188,7 +190,7 @@ function Input$1({ className, size = "md", startIcon, endIcon, ...props }) {
|
|
|
188
190
|
})
|
|
189
191
|
]
|
|
190
192
|
});
|
|
191
|
-
}
|
|
193
|
+
});
|
|
192
194
|
//#endregion
|
|
193
195
|
export { Input$1 as Input, inputVariants };
|
|
194
196
|
|
package/dist/input/input.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"input.js","names":["Input","AriaInput"],"sources":["../../src/input/input.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva } from \"class-variance-authority\";\nimport type { ReactNode } from \"react\";\nimport { Input as AriaInput, type InputProps as AriaInputProps } from \"react-aria-components\";\n\nimport { cn } from \"../lib/cn.js\";\nimport { fieldSurfaceVariants } from \"../lib/field-surface.js\";\n\n/**\n * Input, ported from the legacy shadcn component onto React Aria's Input.\n *\n * Census: 25 files, 51 uses -- the busiest control in the app after Button.\n * Props are `onChange` 36, `value` 35, `placeholder` 33, `id` 23, `type` 21.\n *\n * EVERY ONE OF THOSE 51 CALL SITES MIGRATES UNCHANGED. React Aria's `Input`\n * takes NATIVE props -- `disabled`, `aria-invalid`, `onChange` with a real\n * event -- not the `isDisabled`/`onChange(value)` shape the rest of React\n * Aria uses. It reads `InputContext` when one is present, so the same\n * component is a bare styled input today and composes inside a `TextField`\n * the moment one is wrapped around it. There is no rename to schedule.\n *\n * That is also why this is not `TextField`. Wrapping 51 call sites in a\n * field they do not have, to get a `onChange(value: string)` they did not\n * ask for, would be the migration doing work on the app's behalf.\n *\n * `border-input` becomes `border-border`: in the legacy theme `--input` is\n * character-for-character `--border` on light (`oklch(0.922 0 0)`), and the\n * dark values differ only in alpha. A fourth token role that turns out to\n * be an existing one, after `popover`, `accent` and the scrim.\n */\nexport const inputVariants = cva(\n [\n fieldSurfaceVariants(),\n\n // text-base always, not the legacy `text-base md:text-sm`. 16px is the\n // threshold below which iOS Safari zooms the viewport on focus, and the\n // legacy dropped to 14px at exactly the widths where that stops being a\n // phone -- which reads as a deliberate fix but leaves the field a\n // different size on either side of a breakpoint for no visual reason.\n \"text-base\",\n\n // type=\"file\" has one call site, which is not zero. Enough to keep it\n // from rendering as an unstyled user-agent button.\n \"file:inline-flex file:border-0 file:bg-transparent file:text-sm file:font-medium\",\n \"file:text-foreground\",\n ],\n {\n variants: {\n /**\n * Height and inline padding from the control scale, so `data-density`\n * moves a whole form at once. Same three names and the same tokens as\n * Button, because they sit in the same row: Button's `sm` is the app's\n * second most common at 167 call sites, and while the field was locked\n * to `md` the pair was 10px out of alignment with no supported fix.\n *\n * Type does NOT scale, which is the one place this axis deliberately\n * departs from Button's. iOS Safari zooms the viewport when a focused\n * field's text is under 16px, so `text-base` stays pinned in the base\n * at every rung -- see the note there. Shrinking the box is a layout\n * choice; shrinking the type is a bug on a phone.\n */\n size: {\n sm: \"h-(--density-control-h-sm) px-(--density-control-pad-sm)\",\n md: \"h-(--density-control-h-md) px-(--density-control-pad-md)\",\n lg: \"h-(--density-control-h-lg) px-(--density-control-pad-lg)\",\n },\n // Declared empty and filled by the compound variants below: the affix\n // inset depends on BOTH whether a slot is used and which rung it is on.\n hasStartIcon: { true: \"\" },\n hasEndIcon: { true: \"\" },\n },\n compoundVariants: [\n // The affix slots float ABOVE the field rather than sitting beside it\n // in a flex row, so the only thing keeping the value from running\n // underneath a glyph is this padding. It is the same three tokens that\n // place the glyph -- inline padding, icon box, control gap -- so the\n // text and the glyph move together across density AND size. A\n // hand-tuned `pl-9` at the call site can do neither, and the product\n // app has eighteen of those.\n {\n size: \"sm\",\n hasStartIcon: true,\n class:\n \"ps-[calc(var(--density-control-pad-sm)_+_var(--icon-sm)_+_var(--density-control-gap))]\",\n },\n {\n size: \"md\",\n hasStartIcon: true,\n class:\n \"ps-[calc(var(--density-control-pad-md)_+_var(--icon-sm)_+_var(--density-control-gap))]\",\n },\n {\n size: \"lg\",\n hasStartIcon: true,\n class:\n \"ps-[calc(var(--density-control-pad-lg)_+_var(--icon-sm)_+_var(--density-control-gap))]\",\n },\n {\n size: \"sm\",\n hasEndIcon: true,\n class:\n \"pe-[calc(var(--density-control-pad-sm)_+_var(--icon-sm)_+_var(--density-control-gap))]\",\n },\n {\n size: \"md\",\n hasEndIcon: true,\n class:\n \"pe-[calc(var(--density-control-pad-md)_+_var(--icon-sm)_+_var(--density-control-gap))]\",\n },\n {\n size: \"lg\",\n hasEndIcon: true,\n class:\n \"pe-[calc(var(--density-control-pad-lg)_+_var(--icon-sm)_+_var(--density-control-gap))]\",\n },\n ],\n defaultVariants: { size: \"md\" },\n },\n);\n\n/**\n * An affix slot.\n *\n * `pointer-events-none` is load-bearing, not cosmetic: the slot is painted on\n * top of the field, so without it the glyph swallows the click that was meant\n * to focus the input. Which also fixes the slot's scope -- these are\n * DECORATIVE marks. An interactive trailing control (a password reveal, a\n * clear button) needs its own pointer events and its own accessible name, and\n * the census found no call site for one, so it is not built here.\n *\n * Logical properties throughout. The product app's eighteen hand-rolled\n * adornments are all `absolute left-*`, with `absolute start-*` at zero\n * occurrences, so every one of them puts the glyph on the wrong side under\n * RTL and lets the text run over it.\n */\nconst affixVariants = cva(\n [\n \"pointer-events-none absolute inset-y-0 flex items-center\",\n \"text-muted-foreground\",\n // Sized like Button's icon slots, and skipped when the caller sized the\n // glyph themselves. An svg with neither a width attribute nor a resolved\n // class renders at 0x0 and every position assertion still passes.\n \"[&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-(--icon-sm)\",\n ],\n {\n variants: {\n side: { start: \"\", end: \"\" },\n size: { sm: \"\", md: \"\", lg: \"\" },\n },\n // The glyph's inset is the field's own inline padding, so it lines up with\n // where the value would have started. That makes it a pair of axes, not\n // one: a small field at the medium inset puts the glyph under the text.\n compoundVariants: [\n { side: \"start\", size: \"sm\", class: \"start-(--density-control-pad-sm)\" },\n { side: \"start\", size: \"md\", class: \"start-(--density-control-pad-md)\" },\n { side: \"start\", size: \"lg\", class: \"start-(--density-control-pad-lg)\" },\n { side: \"end\", size: \"sm\", class: \"end-(--density-control-pad-sm)\" },\n { side: \"end\", size: \"md\", class: \"end-(--density-control-pad-md)\" },\n { side: \"end\", size: \"lg\", class: \"end-(--density-control-pad-lg)\" },\n ],\n defaultVariants: { size: \"md\" },\n },\n);\n\n/**\n * A single-line text field.\n *\n * @example\n * <Input type=\"email\" placeholder=\"you@example.com\" value={v} onChange={e => set(e.target.value)} />\n * <Input aria-label=\"Search\" startIcon={<SearchIcon aria-hidden />} />\n */\nexport interface InputProps extends Omit<AriaInputProps, \"className\" | \"style\" | \"size\"> {\n /** Extra classes. Merged last, so they win conflicts with ours. */\n className?: string;\n /**\n * Control scale. Height and inline padding come from the same\n * `--density-control-*` tokens as Button, so a field and a button on one\n * rung are the same height.\n *\n * Type does not scale with it: the field stays at 16px everywhere, because\n * iOS Safari zooms the viewport when a focused input's text is smaller.\n *\n * This REPLACES the native `size` attribute, which React Aria inherits from\n * `InputHTMLAttributes` as a character count. No call site in the census\n * used it; set a width with `className` instead.\n *\n * @default \"md\"\n */\n size?: \"sm\" | \"md\" | \"lg\";\n /**\n * Decorative glyph at the leading edge of the field.\n *\n * Mark it `aria-hidden` -- the field's label carries the accessible name,\n * and a second name source only makes it announce twice. It is sized at\n * `--icon-sm` unless it carries a `size-*` class of its own, positioned\n * from `--density-control-pad-md`, and the field's leading padding grows to\n * match so the value cannot run underneath it.\n *\n * The slot is `pointer-events: none`, so a click on the glyph focuses the\n * field. That makes it decorative by construction: an interactive\n * adornment -- a password reveal, a clear button -- is NOT this prop.\n *\n * Two named slots rather than one `icon` plus a position prop, matching\n * Button: a search field can legitimately carry a leading glyph AND a\n * trailing unit, which one slot cannot express.\n */\n startIcon?: ReactNode;\n /** Decorative glyph at the trailing edge. Same rules as {@link InputProps.startIcon}. */\n endIcon?: ReactNode;\n}\n\nexport function Input({ className, size = \"md\", startIcon, endIcon, ...props }: InputProps) {\n const input = (\n <AriaInput\n {...props}\n data-slot=\"input\"\n className={cn(\n inputVariants({\n size,\n hasStartIcon: startIcon !== undefined,\n hasEndIcon: endIcon !== undefined,\n }),\n className,\n )}\n />\n );\n\n // With neither slot used there is no wrapper AT ALL, so the DOM is\n // byte-identical to what shipped before this prop existed. That is what\n // keeps this additive for the 97 call sites that pass no glyph, and\n // NoIconsLeaveDomUnchanged is the assertion holding it true.\n if (startIcon === undefined && endIcon === undefined) return input;\n\n // An overlay, not a flex row. Moving the border onto this wrapper would\n // take the painted surface off [data-slot=\"input\"] -- the selector the\n // escape hatch is built on -- and would force the focus ring onto\n // :focus-within and the invalid border onto :has(), when React Aria\n // already puts data-focus-visible and data-invalid on the input itself.\n return (\n <span data-slot=\"input-wrapper\" className=\"relative block w-full\">\n {input}\n {startIcon === undefined ? null : (\n <span data-slot=\"input-start-icon\" className={affixVariants({ side: \"start\", size })}>\n {startIcon}\n </span>\n )}\n {endIcon === undefined ? null : (\n <span data-slot=\"input-end-icon\" className={affixVariants({ side: \"end\", size })}>\n {endIcon}\n </span>\n )}\n </span>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+BA,MAAa,gBAAgB,IAC3B;CACE,qBAAqB;CAOrB;CAIA;CACA;AACF,GACA;CACE,UAAU;;;;;;;;;;;;;;EAcR,MAAM;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;EACN;EAGA,cAAc,EAAE,MAAM,GAAG;EACzB,YAAY,EAAE,MAAM,GAAG;CACzB;CACA,kBAAkB;EAQhB;GACE,MAAM;GACN,cAAc;GACd,OACE;EACJ;EACA;GACE,MAAM;GACN,cAAc;GACd,OACE;EACJ;EACA;GACE,MAAM;GACN,cAAc;GACd,OACE;EACJ;EACA;GACE,MAAM;GACN,YAAY;GACZ,OACE;EACJ;EACA;GACE,MAAM;GACN,YAAY;GACZ,OACE;EACJ;EACA;GACE,MAAM;GACN,YAAY;GACZ,OACE;EACJ;CACF;CACA,iBAAiB,EAAE,MAAM,KAAK;AAChC,CACF;;;;;;;;;;;;;;;;AAiBA,MAAM,gBAAgB,IACpB;CACE;CACA;CAIA;AACF,GACA;CACE,UAAU;EACR,MAAM;GAAE,OAAO;GAAI,KAAK;EAAG;EAC3B,MAAM;GAAE,IAAI;GAAI,IAAI;GAAI,IAAI;EAAG;CACjC;CAIA,kBAAkB;EAChB;GAAE,MAAM;GAAS,MAAM;GAAM,OAAO;EAAmC;EACvE;GAAE,MAAM;GAAS,MAAM;GAAM,OAAO;EAAmC;EACvE;GAAE,MAAM;GAAS,MAAM;GAAM,OAAO;EAAmC;EACvE;GAAE,MAAM;GAAO,MAAM;GAAM,OAAO;EAAiC;EACnE;GAAE,MAAM;GAAO,MAAM;GAAM,OAAO;EAAiC;EACnE;GAAE,MAAM;GAAO,MAAM;GAAM,OAAO;EAAiC;CACrE;CACA,iBAAiB,EAAE,MAAM,KAAK;AAChC,CACF;AAiDA,SAAgBA,QAAM,EAAE,WAAW,OAAO,MAAM,WAAW,SAAS,GAAG,SAAqB;CAC1F,MAAM,QACJ,oBAACC,OAAD;EACE,GAAI;EACJ,aAAU;EACV,WAAW,GACT,cAAc;GACZ;GACA,cAAc,cAAc,KAAA;GAC5B,YAAY,YAAY,KAAA;EAC1B,CAAC,GACD,SACF;CACD,CAAA;CAOH,IAAI,cAAc,KAAA,KAAa,YAAY,KAAA,GAAW,OAAO;CAO7D,OACE,qBAAC,QAAD;EAAM,aAAU;EAAgB,WAAU;EAA1C,UAAA;GACG;GACA,cAAc,KAAA,IAAY,OACzB,oBAAC,QAAD;IAAM,aAAU;IAAmB,WAAW,cAAc;KAAE,MAAM;KAAS;IAAK,CAAC;IAChF,UAAA;GACG,CAAA;GAEP,YAAY,KAAA,IAAY,OACvB,oBAAC,QAAD;IAAM,aAAU;IAAiB,WAAW,cAAc;KAAE,MAAM;KAAO;IAAK,CAAC;IAC5E,UAAA;GACG,CAAA;EAEJ;;AAEV"}
|
|
1
|
+
{"version":3,"file":"input.js","names":["Input","AriaInput"],"sources":["../../src/input/input.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva } from \"class-variance-authority\";\nimport { forwardRef, type ReactNode } from \"react\";\nimport { Input as AriaInput, type InputProps as AriaInputProps } from \"react-aria-components\";\n\nimport { cn } from \"../lib/cn.js\";\nimport { fieldSurfaceVariants } from \"../lib/field-surface.js\";\n\n/**\n * Input, ported from the legacy shadcn component onto React Aria's Input.\n *\n * Census: 25 files, 51 uses -- the busiest control in the app after Button.\n * Props are `onChange` 36, `value` 35, `placeholder` 33, `id` 23, `type` 21.\n *\n * EVERY ONE OF THOSE 51 CALL SITES MIGRATES UNCHANGED. React Aria's `Input`\n * takes NATIVE props -- `disabled`, `aria-invalid`, `onChange` with a real\n * event -- not the `isDisabled`/`onChange(value)` shape the rest of React\n * Aria uses. It reads `InputContext` when one is present, so the same\n * component is a bare styled input today and composes inside a `TextField`\n * the moment one is wrapped around it. There is no rename to schedule.\n *\n * That is also why this is not `TextField`. Wrapping 51 call sites in a\n * field they do not have, to get a `onChange(value: string)` they did not\n * ask for, would be the migration doing work on the app's behalf.\n *\n * `border-input` becomes `border-border`: in the legacy theme `--input` is\n * character-for-character `--border` on light (`oklch(0.922 0 0)`), and the\n * dark values differ only in alpha. A fourth token role that turns out to\n * be an existing one, after `popover`, `accent` and the scrim.\n */\nexport const inputVariants = cva(\n [\n fieldSurfaceVariants(),\n\n // text-base always, not the legacy `text-base md:text-sm`. 16px is the\n // threshold below which iOS Safari zooms the viewport on focus, and the\n // legacy dropped to 14px at exactly the widths where that stops being a\n // phone -- which reads as a deliberate fix but leaves the field a\n // different size on either side of a breakpoint for no visual reason.\n \"text-base\",\n\n // type=\"file\" has one call site, which is not zero. Enough to keep it\n // from rendering as an unstyled user-agent button.\n \"file:inline-flex file:border-0 file:bg-transparent file:text-sm file:font-medium\",\n \"file:text-foreground\",\n ],\n {\n variants: {\n /**\n * Height and inline padding from the control scale, so `data-density`\n * moves a whole form at once. Same three names and the same tokens as\n * Button, because they sit in the same row: Button's `sm` is the app's\n * second most common at 167 call sites, and while the field was locked\n * to `md` the pair was 10px out of alignment with no supported fix.\n *\n * Type does NOT scale, which is the one place this axis deliberately\n * departs from Button's. iOS Safari zooms the viewport when a focused\n * field's text is under 16px, so `text-base` stays pinned in the base\n * at every rung -- see the note there. Shrinking the box is a layout\n * choice; shrinking the type is a bug on a phone.\n */\n size: {\n sm: \"h-(--density-control-h-sm) px-(--density-control-pad-sm)\",\n md: \"h-(--density-control-h-md) px-(--density-control-pad-md)\",\n lg: \"h-(--density-control-h-lg) px-(--density-control-pad-lg)\",\n },\n // Declared empty and filled by the compound variants below: the affix\n // inset depends on BOTH whether a slot is used and which rung it is on.\n hasStartIcon: { true: \"\" },\n hasEndIcon: { true: \"\" },\n },\n compoundVariants: [\n // The affix slots float ABOVE the field rather than sitting beside it\n // in a flex row, so the only thing keeping the value from running\n // underneath a glyph is this padding. It is the same three tokens that\n // place the glyph -- inline padding, icon box, control gap -- so the\n // text and the glyph move together across density AND size. A\n // hand-tuned `pl-9` at the call site can do neither, and the product\n // app has eighteen of those.\n {\n size: \"sm\",\n hasStartIcon: true,\n class:\n \"ps-[calc(var(--density-control-pad-sm)_+_var(--icon-sm)_+_var(--density-control-gap))]\",\n },\n {\n size: \"md\",\n hasStartIcon: true,\n class:\n \"ps-[calc(var(--density-control-pad-md)_+_var(--icon-sm)_+_var(--density-control-gap))]\",\n },\n {\n size: \"lg\",\n hasStartIcon: true,\n class:\n \"ps-[calc(var(--density-control-pad-lg)_+_var(--icon-sm)_+_var(--density-control-gap))]\",\n },\n {\n size: \"sm\",\n hasEndIcon: true,\n class:\n \"pe-[calc(var(--density-control-pad-sm)_+_var(--icon-sm)_+_var(--density-control-gap))]\",\n },\n {\n size: \"md\",\n hasEndIcon: true,\n class:\n \"pe-[calc(var(--density-control-pad-md)_+_var(--icon-sm)_+_var(--density-control-gap))]\",\n },\n {\n size: \"lg\",\n hasEndIcon: true,\n class:\n \"pe-[calc(var(--density-control-pad-lg)_+_var(--icon-sm)_+_var(--density-control-gap))]\",\n },\n ],\n defaultVariants: { size: \"md\" },\n },\n);\n\n/**\n * An affix slot.\n *\n * `pointer-events-none` is load-bearing, not cosmetic: the slot is painted on\n * top of the field, so without it the glyph swallows the click that was meant\n * to focus the input. Which also fixes the slot's scope -- these are\n * DECORATIVE marks. An interactive trailing control (a password reveal, a\n * clear button) needs its own pointer events and its own accessible name, and\n * the census found no call site for one, so it is not built here.\n *\n * Logical properties throughout. The product app's eighteen hand-rolled\n * adornments are all `absolute left-*`, with `absolute start-*` at zero\n * occurrences, so every one of them puts the glyph on the wrong side under\n * RTL and lets the text run over it.\n */\nconst affixVariants = cva(\n [\n \"pointer-events-none absolute inset-y-0 flex items-center\",\n \"text-muted-foreground\",\n // Sized like Button's icon slots, and skipped when the caller sized the\n // glyph themselves. An svg with neither a width attribute nor a resolved\n // class renders at 0x0 and every position assertion still passes.\n \"[&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-(--icon-sm)\",\n ],\n {\n variants: {\n side: { start: \"\", end: \"\" },\n size: { sm: \"\", md: \"\", lg: \"\" },\n },\n // The glyph's inset is the field's own inline padding, so it lines up with\n // where the value would have started. That makes it a pair of axes, not\n // one: a small field at the medium inset puts the glyph under the text.\n compoundVariants: [\n { side: \"start\", size: \"sm\", class: \"start-(--density-control-pad-sm)\" },\n { side: \"start\", size: \"md\", class: \"start-(--density-control-pad-md)\" },\n { side: \"start\", size: \"lg\", class: \"start-(--density-control-pad-lg)\" },\n { side: \"end\", size: \"sm\", class: \"end-(--density-control-pad-sm)\" },\n { side: \"end\", size: \"md\", class: \"end-(--density-control-pad-md)\" },\n { side: \"end\", size: \"lg\", class: \"end-(--density-control-pad-lg)\" },\n ],\n defaultVariants: { size: \"md\" },\n },\n);\n\n/**\n * A single-line text field.\n *\n * @example\n * <Input type=\"email\" placeholder=\"you@example.com\" value={v} onChange={e => set(e.target.value)} />\n * <Input aria-label=\"Search\" startIcon={<SearchIcon aria-hidden />} />\n */\nexport interface InputProps extends Omit<AriaInputProps, \"className\" | \"style\" | \"size\"> {\n /** Extra classes. Merged last, so they win conflicts with ours. */\n className?: string;\n /**\n * Control scale. Height and inline padding come from the same\n * `--density-control-*` tokens as Button, so a field and a button on one\n * rung are the same height.\n *\n * Type does not scale with it: the field stays at 16px everywhere, because\n * iOS Safari zooms the viewport when a focused input's text is smaller.\n *\n * This REPLACES the native `size` attribute, which React Aria inherits from\n * `InputHTMLAttributes` as a character count. No call site in the census\n * used it; set a width with `className` instead.\n *\n * @default \"md\"\n */\n size?: \"sm\" | \"md\" | \"lg\";\n /**\n * Decorative glyph at the leading edge of the field.\n *\n * Mark it `aria-hidden` -- the field's label carries the accessible name,\n * and a second name source only makes it announce twice. It is sized at\n * `--icon-sm` unless it carries a `size-*` class of its own, positioned\n * from `--density-control-pad-md`, and the field's leading padding grows to\n * match so the value cannot run underneath it.\n *\n * The slot is `pointer-events: none`, so a click on the glyph focuses the\n * field. That makes it decorative by construction: an interactive\n * adornment -- a password reveal, a clear button -- is NOT this prop.\n *\n * Two named slots rather than one `icon` plus a position prop, matching\n * Button: a search field can legitimately carry a leading glyph AND a\n * trailing unit, which one slot cannot express.\n */\n startIcon?: ReactNode;\n /** Decorative glyph at the trailing edge. Same rules as {@link InputProps.startIcon}. */\n endIcon?: ReactNode;\n}\n\nexport const Input = forwardRef<HTMLInputElement, InputProps>(function Input(\n { className, size = \"md\", startIcon, endIcon, ...props },\n ref,\n) {\n const input = (\n <AriaInput\n // The ref goes on the INPUT, never on the wrapper span below. A ref\n // pointing at the wrapper is still non-null, so it satisfies the obvious\n // assertion while breaking `.focus()`, `.select()` and every\n // react-hook-form site -- but only at the call sites that pass an icon.\n // RefLandsOnTheInputNotTheWrapper is the guard.\n ref={ref}\n {...props}\n data-slot=\"input\"\n className={cn(\n inputVariants({\n size,\n hasStartIcon: startIcon !== undefined,\n hasEndIcon: endIcon !== undefined,\n }),\n className,\n )}\n />\n );\n\n // With neither slot used there is no wrapper AT ALL, so the DOM is\n // byte-identical to what shipped before this prop existed. That is what\n // keeps this additive for the 97 call sites that pass no glyph, and\n // NoIconsLeaveDomUnchanged is the assertion holding it true.\n if (startIcon === undefined && endIcon === undefined) return input;\n\n // An overlay, not a flex row. Moving the border onto this wrapper would\n // take the painted surface off [data-slot=\"input\"] -- the selector the\n // escape hatch is built on -- and would force the focus ring onto\n // :focus-within and the invalid border onto :has(), when React Aria\n // already puts data-focus-visible and data-invalid on the input itself.\n return (\n <span data-slot=\"input-wrapper\" className=\"relative block w-full\">\n {input}\n {startIcon === undefined ? null : (\n <span data-slot=\"input-start-icon\" className={affixVariants({ side: \"start\", size })}>\n {startIcon}\n </span>\n )}\n {endIcon === undefined ? null : (\n <span data-slot=\"input-end-icon\" className={affixVariants({ side: \"end\", size })}>\n {endIcon}\n </span>\n )}\n </span>\n );\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+BA,MAAa,gBAAgB,IAC3B;CACE,qBAAqB;CAOrB;CAIA;CACA;AACF,GACA;CACE,UAAU;;;;;;;;;;;;;;EAcR,MAAM;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;EACN;EAGA,cAAc,EAAE,MAAM,GAAG;EACzB,YAAY,EAAE,MAAM,GAAG;CACzB;CACA,kBAAkB;EAQhB;GACE,MAAM;GACN,cAAc;GACd,OACE;EACJ;EACA;GACE,MAAM;GACN,cAAc;GACd,OACE;EACJ;EACA;GACE,MAAM;GACN,cAAc;GACd,OACE;EACJ;EACA;GACE,MAAM;GACN,YAAY;GACZ,OACE;EACJ;EACA;GACE,MAAM;GACN,YAAY;GACZ,OACE;EACJ;EACA;GACE,MAAM;GACN,YAAY;GACZ,OACE;EACJ;CACF;CACA,iBAAiB,EAAE,MAAM,KAAK;AAChC,CACF;;;;;;;;;;;;;;;;AAiBA,MAAM,gBAAgB,IACpB;CACE;CACA;CAIA;AACF,GACA;CACE,UAAU;EACR,MAAM;GAAE,OAAO;GAAI,KAAK;EAAG;EAC3B,MAAM;GAAE,IAAI;GAAI,IAAI;GAAI,IAAI;EAAG;CACjC;CAIA,kBAAkB;EAChB;GAAE,MAAM;GAAS,MAAM;GAAM,OAAO;EAAmC;EACvE;GAAE,MAAM;GAAS,MAAM;GAAM,OAAO;EAAmC;EACvE;GAAE,MAAM;GAAS,MAAM;GAAM,OAAO;EAAmC;EACvE;GAAE,MAAM;GAAO,MAAM;GAAM,OAAO;EAAiC;EACnE;GAAE,MAAM;GAAO,MAAM;GAAM,OAAO;EAAiC;EACnE;GAAE,MAAM;GAAO,MAAM;GAAM,OAAO;EAAiC;CACrE;CACA,iBAAiB,EAAE,MAAM,KAAK;AAChC,CACF;AAiDA,MAAaA,UAAQ,WAAyC,SAASA,QACrE,EAAE,WAAW,OAAO,MAAM,WAAW,SAAS,GAAG,SACjD,KACA;CACA,MAAM,QACJ,oBAACC,OAAD;EAMO;EACL,GAAI;EACJ,aAAU;EACV,WAAW,GACT,cAAc;GACZ;GACA,cAAc,cAAc,KAAA;GAC5B,YAAY,YAAY,KAAA;EAC1B,CAAC,GACD,SACF;CACD,CAAA;CAOH,IAAI,cAAc,KAAA,KAAa,YAAY,KAAA,GAAW,OAAO;CAO7D,OACE,qBAAC,QAAD;EAAM,aAAU;EAAgB,WAAU;EAA1C,UAAA;GACG;GACA,cAAc,KAAA,IAAY,OACzB,oBAAC,QAAD;IAAM,aAAU;IAAmB,WAAW,cAAc;KAAE,MAAM;KAAS;IAAK,CAAC;IAChF,UAAA;GACG,CAAA;GAEP,YAAY,KAAA,IAAY,OACvB,oBAAC,QAAD;IAAM,aAAU;IAAiB,WAAW,cAAc;KAAE,MAAM;KAAO;IAAK,CAAC;IAC5E,UAAA;GACG,CAAA;EAEJ;;AAEV,CAAC"}
|
package/dist/label/label.d.ts
CHANGED
|
@@ -35,7 +35,7 @@ interface LabelProps$1 extends Omit<LabelProps, "className" | "style"> {
|
|
|
35
35
|
/** Extra classes. Merged last, so they win conflicts with ours. */
|
|
36
36
|
className?: string;
|
|
37
37
|
}
|
|
38
|
-
declare
|
|
38
|
+
declare const Label$1: import("react").ForwardRefExoticComponent<LabelProps$1 & import("react").RefAttributes<HTMLLabelElement>>;
|
|
39
39
|
//#endregion
|
|
40
40
|
export { Label$1 as Label, LabelProps$1 as LabelProps };
|
|
41
41
|
//# sourceMappingURL=label.d.ts.map
|
package/dist/label/label.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { cn } from "../lib/cn.js";
|
|
3
3
|
import { cva } from "class-variance-authority";
|
|
4
|
+
import { forwardRef } from "react";
|
|
4
5
|
import { Label } from "react-aria-components";
|
|
5
6
|
import { jsx } from "react/jsx-runtime";
|
|
6
7
|
//#region src/label/label.tsx
|
|
@@ -31,13 +32,14 @@ const labelVariants = cva([
|
|
|
31
32
|
"select-none",
|
|
32
33
|
"box-border"
|
|
33
34
|
]);
|
|
34
|
-
|
|
35
|
+
const Label$1 = forwardRef(function Label$2({ className, ...props }, ref) {
|
|
35
36
|
return /* @__PURE__ */ jsx(Label, {
|
|
37
|
+
ref,
|
|
36
38
|
...props,
|
|
37
39
|
"data-slot": "label",
|
|
38
40
|
className: cn(labelVariants(), className)
|
|
39
41
|
});
|
|
40
|
-
}
|
|
42
|
+
});
|
|
41
43
|
//#endregion
|
|
42
44
|
export { Label$1 as Label, labelVariants };
|
|
43
45
|
|
package/dist/label/label.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"label.js","names":["Label","AriaLabel"],"sources":["../../src/label/label.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva } from \"class-variance-authority\";\nimport { Label as AriaLabel, type LabelProps as AriaLabelProps } from \"react-aria-components\";\n\nimport { cn } from \"../lib/cn.js\";\n\n/**\n * Label, ported from the legacy Radix Label onto React Aria's.\n *\n * Census: 11 files, 39 uses. 26 pass `htmlFor`, 19 pass `className`, and\n * nothing else recurs -- so the surface really is \"a styled <label>\".\n *\n * Two legacy rules are NOT ported, and both for the same reason: they\n * depend on markup this component cannot see.\n *\n * - `peer-disabled:cursor-not-allowed peer-disabled:opacity-50` requires the\n * consumer to have put `peer` on a sibling input. Nothing in this library\n * does, and a rule that silently does nothing unless the caller happens to\n * add a class is worse than no rule.\n * - `group-data-[disabled=true]:*` requires an ancestor marked `group` with\n * `data-disabled=\"true\"`. React Aria fields set `data-disabled` on their\n * own wrapper and supply it through LabelContext, so when TextField lands\n * the disabled treatment comes from the field rather than from a class\n * the caller remembered.\n *\n * Every string in the cva block below is a STATIC literal.\n */\nexport const labelVariants = cva([\n \"inline-flex items-center gap-2\",\n // leading-* AFTER text-*: tailwind-merge treats a later text-* as owning\n // line-height and silently strips a leading-* that precedes it.\n \"font-sans text-sm leading-(--leading-none) font-medium text-foreground\",\n \"select-none\",\n // The reset in @seekalfred/styles is OPT-IN, so a consumer may load none.\n // A bare <label> inherits the host page's serif; the Accordion shipped its\n // panel exactly that way.\n \"box-border\",\n]);\n\n/**\n * A form label.\n *\n * Inside a React Aria field (`TextField`, `Checkbox`, ...) the association\n * is automatic through context. Standalone, pass `htmlFor`.\n *\n * @example\n * <Label htmlFor=\"email\">Email</Label>\n */\nexport interface LabelProps extends Omit<AriaLabelProps, \"className\" | \"style\"> {\n /** Extra classes. Merged last, so they win conflicts with ours. */\n className?: string;\n}\n\nexport function Label({ className, ...props }
|
|
1
|
+
{"version":3,"file":"label.js","names":["Label","AriaLabel"],"sources":["../../src/label/label.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva } from \"class-variance-authority\";\nimport { forwardRef } from \"react\";\nimport { Label as AriaLabel, type LabelProps as AriaLabelProps } from \"react-aria-components\";\n\nimport { cn } from \"../lib/cn.js\";\n\n/**\n * Label, ported from the legacy Radix Label onto React Aria's.\n *\n * Census: 11 files, 39 uses. 26 pass `htmlFor`, 19 pass `className`, and\n * nothing else recurs -- so the surface really is \"a styled <label>\".\n *\n * Two legacy rules are NOT ported, and both for the same reason: they\n * depend on markup this component cannot see.\n *\n * - `peer-disabled:cursor-not-allowed peer-disabled:opacity-50` requires the\n * consumer to have put `peer` on a sibling input. Nothing in this library\n * does, and a rule that silently does nothing unless the caller happens to\n * add a class is worse than no rule.\n * - `group-data-[disabled=true]:*` requires an ancestor marked `group` with\n * `data-disabled=\"true\"`. React Aria fields set `data-disabled` on their\n * own wrapper and supply it through LabelContext, so when TextField lands\n * the disabled treatment comes from the field rather than from a class\n * the caller remembered.\n *\n * Every string in the cva block below is a STATIC literal.\n */\nexport const labelVariants = cva([\n \"inline-flex items-center gap-2\",\n // leading-* AFTER text-*: tailwind-merge treats a later text-* as owning\n // line-height and silently strips a leading-* that precedes it.\n \"font-sans text-sm leading-(--leading-none) font-medium text-foreground\",\n \"select-none\",\n // The reset in @seekalfred/styles is OPT-IN, so a consumer may load none.\n // A bare <label> inherits the host page's serif; the Accordion shipped its\n // panel exactly that way.\n \"box-border\",\n]);\n\n/**\n * A form label.\n *\n * Inside a React Aria field (`TextField`, `Checkbox`, ...) the association\n * is automatic through context. Standalone, pass `htmlFor`.\n *\n * @example\n * <Label htmlFor=\"email\">Email</Label>\n */\nexport interface LabelProps extends Omit<AriaLabelProps, \"className\" | \"style\"> {\n /** Extra classes. Merged last, so they win conflicts with ours. */\n className?: string;\n}\n\nexport const Label = forwardRef<HTMLLabelElement, LabelProps>(function Label(\n { className, ...props },\n ref,\n) {\n return (\n <AriaLabel ref={ref} {...props} data-slot=\"label\" className={cn(labelVariants(), className)} />\n );\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BA,MAAa,gBAAgB,IAAI;CAC/B;CAGA;CACA;CAIA;AACF,CAAC;AAgBD,MAAaA,UAAQ,WAAyC,SAASA,QACrE,EAAE,WAAW,GAAG,SAChB,KACA;CACA,OACE,oBAACC,OAAD;EAAgB;EAAK,GAAI;EAAO,aAAU;EAAQ,WAAW,GAAG,cAAc,GAAG,SAAS;CAAI,CAAA;AAElG,CAAC"}
|
package/dist/lib/cn.d.ts
CHANGED
|
@@ -1,12 +1,62 @@
|
|
|
1
1
|
import { ClassValue } from "clsx";
|
|
2
2
|
//#region src/lib/cn.d.ts
|
|
3
3
|
/**
|
|
4
|
-
* Merge
|
|
4
|
+
* Merge OUR classes, then append the consumer's UNTOUCHED.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
* The consumer's `className` deliberately does not go through tailwind-merge.
|
|
7
|
+
*
|
|
8
|
+
* WHY. tailwind-merge resolves conflicts by class GROUP, and several of its
|
|
9
|
+
* groups are matched by validators that accept any token -- `text-color` takes
|
|
10
|
+
* anything shaped `text-<word>-<number>`, which is also the shape of a
|
|
11
|
+
* consuming app's own utilities. It cannot tell `text-red-500` from a custom
|
|
12
|
+
* `text-semibold-14`, so it treats them as conflicting and deletes one. There
|
|
13
|
+
* is no error, no warning and no type error; the element simply renders without
|
|
14
|
+
* the class the author wrote.
|
|
15
|
+
*
|
|
16
|
+
* Measured in the consuming app before this changed: 38 call sites passed a
|
|
17
|
+
* custom type-scale class and 14 lost it. Two of those flipped at runtime
|
|
18
|
+
* depending on a data value, which is why it went unnoticed for so long. A
|
|
19
|
+
* second, unrelated utility (`bg-sidebar-bg`) was being eaten the same way, so
|
|
20
|
+
* the defect is structural rather than specific to typography.
|
|
21
|
+
*
|
|
22
|
+
* The loss happens INSIDE the consumer's own string, not between theirs and
|
|
23
|
+
* ours -- a lone consumer class survives, because it comes last and
|
|
24
|
+
* tailwind-merge keeps the last of a group. It is a pair like
|
|
25
|
+
* `"text-semibold-14 text-white"` that loses its first half. So no amount of
|
|
26
|
+
* ordering fixes it: their string has to stay out of the merge entirely.
|
|
27
|
+
*
|
|
28
|
+
* WHY THAT IS SAFE. "The consumer always wins" is delivered twice over. This
|
|
29
|
+
* package ships entirely inside `@layer alfred`, and unlayered CSS beats every
|
|
30
|
+
* layered rule regardless of specificity (ARCHITECTURE.md section 5;
|
|
31
|
+
* PHASE-0.md). `apps/smoke` asserts exactly that on every build. tailwind-merge
|
|
32
|
+
* was belt-and-braces for something the cascade already handles -- and the
|
|
33
|
+
* braces were cutting.
|
|
34
|
+
*
|
|
35
|
+
* WHAT THIS IS NOT. It is not "stop merging". A consumer class that
|
|
36
|
+
* tailwind-merge genuinely understands still wins outright -- `max-w-96` over
|
|
37
|
+
* our `max-w-128` collapses to one class, exactly as before. Only the tokens
|
|
38
|
+
* the merge DISCARDED from their string come back, which is precisely the set
|
|
39
|
+
* it had no business discarding.
|
|
40
|
+
*
|
|
41
|
+
* The one behaviour change to know about: if a consumer passes two conflicting
|
|
42
|
+
* classes of their own (`className="p-2 p-4"`), both now reach the element and
|
|
43
|
+
* CSS picks by stylesheet order rather than by token order. Their string, their
|
|
44
|
+
* conflict -- and the alternative is deleting custom utilities, which is worse.
|
|
45
|
+
*
|
|
46
|
+
* @param ours classes this library authored -- merged against each other
|
|
47
|
+
* @param theirs the consumer's `className` -- merged, then anything the merge
|
|
48
|
+
* discarded from it is restored
|
|
49
|
+
*/
|
|
50
|
+
export declare function cn(ours: ClassValue, theirs?: ClassValue): string;
|
|
51
|
+
/**
|
|
52
|
+
* Merge classes that are ALL ours.
|
|
53
|
+
*
|
|
54
|
+
* The same tailwind-merge behaviour `cn` used to have, for the call sites that
|
|
55
|
+
* combine several of this library's own class strings and never touch a
|
|
56
|
+
* consumer's. Keeping them on a separate function is what lets `cn` treat its
|
|
57
|
+
* second argument as foreign without guessing from argument position -- several
|
|
58
|
+
* internal call sites legitimately end with a library-owned conditional.
|
|
9
59
|
*/
|
|
10
|
-
export declare function
|
|
60
|
+
export declare function cnAll(...inputs: ClassValue[]): string;
|
|
11
61
|
//#endregion
|
|
12
62
|
//# sourceMappingURL=cn.d.ts.map
|
package/dist/lib/cn.js
CHANGED
|
@@ -2,16 +2,74 @@ import { clsx } from "clsx";
|
|
|
2
2
|
import { twMerge } from "tailwind-merge";
|
|
3
3
|
//#region src/lib/cn.ts
|
|
4
4
|
/**
|
|
5
|
-
* Merge
|
|
5
|
+
* Merge OUR classes, then append the consumer's UNTOUCHED.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* The consumer's `className` deliberately does not go through tailwind-merge.
|
|
8
|
+
*
|
|
9
|
+
* WHY. tailwind-merge resolves conflicts by class GROUP, and several of its
|
|
10
|
+
* groups are matched by validators that accept any token -- `text-color` takes
|
|
11
|
+
* anything shaped `text-<word>-<number>`, which is also the shape of a
|
|
12
|
+
* consuming app's own utilities. It cannot tell `text-red-500` from a custom
|
|
13
|
+
* `text-semibold-14`, so it treats them as conflicting and deletes one. There
|
|
14
|
+
* is no error, no warning and no type error; the element simply renders without
|
|
15
|
+
* the class the author wrote.
|
|
16
|
+
*
|
|
17
|
+
* Measured in the consuming app before this changed: 38 call sites passed a
|
|
18
|
+
* custom type-scale class and 14 lost it. Two of those flipped at runtime
|
|
19
|
+
* depending on a data value, which is why it went unnoticed for so long. A
|
|
20
|
+
* second, unrelated utility (`bg-sidebar-bg`) was being eaten the same way, so
|
|
21
|
+
* the defect is structural rather than specific to typography.
|
|
22
|
+
*
|
|
23
|
+
* The loss happens INSIDE the consumer's own string, not between theirs and
|
|
24
|
+
* ours -- a lone consumer class survives, because it comes last and
|
|
25
|
+
* tailwind-merge keeps the last of a group. It is a pair like
|
|
26
|
+
* `"text-semibold-14 text-white"` that loses its first half. So no amount of
|
|
27
|
+
* ordering fixes it: their string has to stay out of the merge entirely.
|
|
28
|
+
*
|
|
29
|
+
* WHY THAT IS SAFE. "The consumer always wins" is delivered twice over. This
|
|
30
|
+
* package ships entirely inside `@layer alfred`, and unlayered CSS beats every
|
|
31
|
+
* layered rule regardless of specificity (ARCHITECTURE.md section 5;
|
|
32
|
+
* PHASE-0.md). `apps/smoke` asserts exactly that on every build. tailwind-merge
|
|
33
|
+
* was belt-and-braces for something the cascade already handles -- and the
|
|
34
|
+
* braces were cutting.
|
|
35
|
+
*
|
|
36
|
+
* WHAT THIS IS NOT. It is not "stop merging". A consumer class that
|
|
37
|
+
* tailwind-merge genuinely understands still wins outright -- `max-w-96` over
|
|
38
|
+
* our `max-w-128` collapses to one class, exactly as before. Only the tokens
|
|
39
|
+
* the merge DISCARDED from their string come back, which is precisely the set
|
|
40
|
+
* it had no business discarding.
|
|
41
|
+
*
|
|
42
|
+
* The one behaviour change to know about: if a consumer passes two conflicting
|
|
43
|
+
* classes of their own (`className="p-2 p-4"`), both now reach the element and
|
|
44
|
+
* CSS picks by stylesheet order rather than by token order. Their string, their
|
|
45
|
+
* conflict -- and the alternative is deleting custom utilities, which is worse.
|
|
46
|
+
*
|
|
47
|
+
* @param ours classes this library authored -- merged against each other
|
|
48
|
+
* @param theirs the consumer's `className` -- merged, then anything the merge
|
|
49
|
+
* discarded from it is restored
|
|
50
|
+
*/
|
|
51
|
+
function cn(ours, theirs) {
|
|
52
|
+
const mine = clsx(ours);
|
|
53
|
+
const yours = clsx(theirs);
|
|
54
|
+
if (!yours) return twMerge(mine);
|
|
55
|
+
const merged = twMerge(`${mine} ${yours}`);
|
|
56
|
+
const kept = new Set(merged.split(" "));
|
|
57
|
+
const rescued = yours.split(" ").filter((token) => token && !kept.has(token));
|
|
58
|
+
return rescued.length === 0 ? merged : `${merged} ${rescued.join(" ")}`;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Merge classes that are ALL ours.
|
|
62
|
+
*
|
|
63
|
+
* The same tailwind-merge behaviour `cn` used to have, for the call sites that
|
|
64
|
+
* combine several of this library's own class strings and never touch a
|
|
65
|
+
* consumer's. Keeping them on a separate function is what lets `cn` treat its
|
|
66
|
+
* second argument as foreign without guessing from argument position -- several
|
|
67
|
+
* internal call sites legitimately end with a library-owned conditional.
|
|
10
68
|
*/
|
|
11
|
-
function
|
|
69
|
+
function cnAll(...inputs) {
|
|
12
70
|
return twMerge(clsx(inputs));
|
|
13
71
|
}
|
|
14
72
|
//#endregion
|
|
15
|
-
export { cn };
|
|
73
|
+
export { cn, cnAll };
|
|
16
74
|
|
|
17
75
|
//# sourceMappingURL=cn.js.map
|
package/dist/lib/cn.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cn.js","names":[],"sources":["../../src/lib/cn.ts"],"sourcesContent":["import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\n/**\n * Merge
|
|
1
|
+
{"version":3,"file":"cn.js","names":[],"sources":["../../src/lib/cn.ts"],"sourcesContent":["import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\n/**\n * Merge OUR classes, then append the consumer's UNTOUCHED.\n *\n * The consumer's `className` deliberately does not go through tailwind-merge.\n *\n * WHY. tailwind-merge resolves conflicts by class GROUP, and several of its\n * groups are matched by validators that accept any token -- `text-color` takes\n * anything shaped `text-<word>-<number>`, which is also the shape of a\n * consuming app's own utilities. It cannot tell `text-red-500` from a custom\n * `text-semibold-14`, so it treats them as conflicting and deletes one. There\n * is no error, no warning and no type error; the element simply renders without\n * the class the author wrote.\n *\n * Measured in the consuming app before this changed: 38 call sites passed a\n * custom type-scale class and 14 lost it. Two of those flipped at runtime\n * depending on a data value, which is why it went unnoticed for so long. A\n * second, unrelated utility (`bg-sidebar-bg`) was being eaten the same way, so\n * the defect is structural rather than specific to typography.\n *\n * The loss happens INSIDE the consumer's own string, not between theirs and\n * ours -- a lone consumer class survives, because it comes last and\n * tailwind-merge keeps the last of a group. It is a pair like\n * `\"text-semibold-14 text-white\"` that loses its first half. So no amount of\n * ordering fixes it: their string has to stay out of the merge entirely.\n *\n * WHY THAT IS SAFE. \"The consumer always wins\" is delivered twice over. This\n * package ships entirely inside `@layer alfred`, and unlayered CSS beats every\n * layered rule regardless of specificity (ARCHITECTURE.md section 5;\n * PHASE-0.md). `apps/smoke` asserts exactly that on every build. tailwind-merge\n * was belt-and-braces for something the cascade already handles -- and the\n * braces were cutting.\n *\n * WHAT THIS IS NOT. It is not \"stop merging\". A consumer class that\n * tailwind-merge genuinely understands still wins outright -- `max-w-96` over\n * our `max-w-128` collapses to one class, exactly as before. Only the tokens\n * the merge DISCARDED from their string come back, which is precisely the set\n * it had no business discarding.\n *\n * The one behaviour change to know about: if a consumer passes two conflicting\n * classes of their own (`className=\"p-2 p-4\"`), both now reach the element and\n * CSS picks by stylesheet order rather than by token order. Their string, their\n * conflict -- and the alternative is deleting custom utilities, which is worse.\n *\n * @param ours classes this library authored -- merged against each other\n * @param theirs the consumer's `className` -- merged, then anything the merge\n * discarded from it is restored\n */\nexport function cn(ours: ClassValue, theirs?: ClassValue): string {\n const mine = clsx(ours);\n const yours = clsx(theirs);\n if (!yours) return twMerge(mine);\n\n // Merge normally first, so a consumer class that tailwind-merge DOES\n // understand still beats ours -- `max-w-96` over our `max-w-128` resolves to\n // one class, as it always did.\n const merged = twMerge(`${mine} ${yours}`);\n\n // Then put back anything of theirs the merge ate. Only their tokens are\n // restored, never ours, so this cannot resurrect a class the consumer\n // deliberately overrode.\n const kept = new Set(merged.split(\" \"));\n const rescued = yours.split(\" \").filter((token) => token && !kept.has(token));\n\n return rescued.length === 0 ? merged : `${merged} ${rescued.join(\" \")}`;\n}\n\n/**\n * Merge classes that are ALL ours.\n *\n * The same tailwind-merge behaviour `cn` used to have, for the call sites that\n * combine several of this library's own class strings and never touch a\n * consumer's. Keeping them on a separate function is what lets `cn` treat its\n * second argument as foreign without guessing from argument position -- several\n * internal call sites legitimately end with a library-owned conditional.\n */\nexport function cnAll(...inputs: ClassValue[]): string {\n return twMerge(clsx(inputs));\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkDA,SAAgB,GAAG,MAAkB,QAA6B;CAChE,MAAM,OAAO,KAAK,IAAI;CACtB,MAAM,QAAQ,KAAK,MAAM;CACzB,IAAI,CAAC,OAAO,OAAO,QAAQ,IAAI;CAK/B,MAAM,SAAS,QAAQ,GAAG,KAAK,GAAG,OAAO;CAKzC,MAAM,OAAO,IAAI,IAAI,OAAO,MAAM,GAAG,CAAC;CACtC,MAAM,UAAU,MAAM,MAAM,GAAG,CAAC,CAAC,QAAQ,UAAU,SAAS,CAAC,KAAK,IAAI,KAAK,CAAC;CAE5E,OAAO,QAAQ,WAAW,IAAI,SAAS,GAAG,OAAO,GAAG,QAAQ,KAAK,GAAG;AACtE;;;;;;;;;;AAWA,SAAgB,MAAM,GAAG,QAA8B;CACrD,OAAO,QAAQ,KAAK,MAAM,CAAC;AAC7B"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
//#region src/lib/merge-refs.ts
|
|
2
|
+
/**
|
|
3
|
+
* Combines a caller-supplied ref with a ref this component already needs for
|
|
4
|
+
* its own purposes, so both receive the same node.
|
|
5
|
+
*
|
|
6
|
+
* Only two call sites need this -- Avatar's `measure` (image decode-state)
|
|
7
|
+
* and ScrollArea's `region` (scrollTop reset) -- both already attached to the
|
|
8
|
+
* same node the newly forwarded ref now also targets. A shared five-line
|
|
9
|
+
* helper for two call sites is the Rule of Two; anything more general (a
|
|
10
|
+
* `useMergedRef` hook, React 19 ref-cleanup support) is speculative until a
|
|
11
|
+
* third caller needs it.
|
|
12
|
+
*/
|
|
13
|
+
function mergeRefs(...refs) {
|
|
14
|
+
return (node) => {
|
|
15
|
+
for (const ref of refs) if (typeof ref === "function") ref(node);
|
|
16
|
+
else if (ref) ref.current = node;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
//#endregion
|
|
20
|
+
export { mergeRefs };
|
|
21
|
+
|
|
22
|
+
//# sourceMappingURL=merge-refs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"merge-refs.js","names":[],"sources":["../../src/lib/merge-refs.ts"],"sourcesContent":["import type { Ref, RefCallback } from \"react\";\n\n/**\n * Combines a caller-supplied ref with a ref this component already needs for\n * its own purposes, so both receive the same node.\n *\n * Only two call sites need this -- Avatar's `measure` (image decode-state)\n * and ScrollArea's `region` (scrollTop reset) -- both already attached to the\n * same node the newly forwarded ref now also targets. A shared five-line\n * helper for two call sites is the Rule of Two; anything more general (a\n * `useMergedRef` hook, React 19 ref-cleanup support) is speculative until a\n * third caller needs it.\n */\nexport function mergeRefs<T>(...refs: (Ref<T> | undefined)[]): RefCallback<T> {\n return (node) => {\n for (const ref of refs) {\n if (typeof ref === \"function\") ref(node);\n else if (ref) (ref as { current: T | null }).current = node;\n }\n };\n}\n"],"mappings":";;;;;;;;;;;;AAaA,SAAgB,UAAa,GAAG,MAA8C;CAC5E,QAAQ,SAAS;EACf,KAAK,MAAM,OAAO,MAChB,IAAI,OAAO,QAAQ,YAAY,IAAI,IAAI;OAClC,IAAI,KAAK,IAA+B,UAAU;CAE3D;AACF"}
|