@seekalfred/primitives 0.7.1 → 0.7.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +195 -4
- package/dist/alert/alert.d.ts +49 -4
- package/dist/alert/alert.js +66 -38
- package/dist/alert/alert.js.map +1 -1
- package/dist/alert/index.d.ts +2 -2
- package/dist/alert/index.js +2 -2
- package/dist/alert-dialog/alert-dialog.d.ts +17 -4
- package/dist/alert-dialog/alert-dialog.js +26 -2
- package/dist/alert-dialog/alert-dialog.js.map +1 -1
- package/dist/badge/badge.d.ts +1 -1
- package/dist/button/button.d.ts +2 -2
- package/dist/calendar/calendar.d.ts +1 -1
- package/dist/card/card.d.ts +22 -1
- package/dist/card/card.js +60 -7
- package/dist/card/card.js.map +1 -1
- package/dist/carousel/carousel.d.ts +21 -0
- package/dist/carousel/carousel.js +34 -8
- package/dist/carousel/carousel.js.map +1 -1
- package/dist/copy-button/copy-button.d.ts +18 -2
- package/dist/copy-button/copy-button.js +29 -8
- package/dist/copy-button/copy-button.js.map +1 -1
- package/dist/donut-chart/donut-chart.d.ts +22 -0
- package/dist/donut-chart/donut-chart.js +35 -18
- package/dist/donut-chart/donut-chart.js.map +1 -1
- package/dist/donut-chart/index.d.ts +2 -2
- package/dist/empty-state/empty-state.d.ts +21 -0
- package/dist/empty-state/empty-state.js +16 -4
- package/dist/empty-state/empty-state.js.map +1 -1
- package/dist/empty-state/index.d.ts +2 -2
- package/dist/empty-state/index.js +2 -2
- package/dist/filter-bar/filter-bar.d.ts +4 -1
- package/dist/filter-bar/filter-bar.js +53 -9
- package/dist/filter-bar/filter-bar.js.map +1 -1
- package/dist/index.d.ts +10 -10
- package/dist/index.js +7 -7
- package/dist/input/input.js +4 -4
- package/dist/input/input.js.map +1 -1
- package/dist/lib/field-shell.js +10 -3
- package/dist/lib/field-shell.js.map +1 -1
- package/dist/lib/field-surface.js +22 -1
- package/dist/lib/field-surface.js.map +1 -1
- package/dist/lib/use-copy-feedback.js +17 -3
- package/dist/lib/use-copy-feedback.js.map +1 -1
- package/dist/line-chart/index.d.ts +2 -2
- package/dist/line-chart/line-chart.d.ts +42 -2
- package/dist/line-chart/line-chart.js +143 -14
- package/dist/line-chart/line-chart.js.map +1 -1
- package/dist/money-input/money-input.d.ts +22 -1
- package/dist/money-input/money-input.js +13 -7
- package/dist/money-input/money-input.js.map +1 -1
- package/dist/morph-select/morph-select.d.ts +19 -1
- package/dist/morph-select/morph-select.js +21 -2
- package/dist/morph-select/morph-select.js.map +1 -1
- package/dist/multi-select/index.d.ts +2 -2
- package/dist/multi-select/index.js +2 -2
- package/dist/multi-select/multi-select.d.ts +43 -2
- package/dist/multi-select/multi-select.js +93 -36
- package/dist/multi-select/multi-select.js.map +1 -1
- package/dist/password-field/password-field.d.ts +16 -0
- package/dist/password-field/password-field.js +37 -8
- package/dist/password-field/password-field.js.map +1 -1
- package/dist/popover/index.d.ts +2 -2
- package/dist/popover/index.js +2 -2
- package/dist/popover/popover.d.ts +39 -2
- package/dist/popover/popover.js +29 -2
- package/dist/popover/popover.js.map +1 -1
- package/dist/progress/progress.d.ts +16 -2
- package/dist/progress/progress.js +15 -8
- package/dist/progress/progress.js.map +1 -1
- package/dist/radio-group/index.d.ts +2 -2
- package/dist/radio-group/index.js +2 -2
- package/dist/radio-group/radio-group.d.ts +33 -3
- package/dist/radio-group/radio-group.js +109 -9
- package/dist/radio-group/radio-group.js.map +1 -1
- package/dist/scroll-area/scroll-area.js +4 -3
- package/dist/scroll-area/scroll-area.js.map +1 -1
- package/dist/search-field/search-field.d.ts +22 -1
- package/dist/search-field/search-field.js +22 -7
- package/dist/search-field/search-field.js.map +1 -1
- package/dist/select/select.d.ts +1 -1
- package/dist/select/select.js +8 -6
- package/dist/select/select.js.map +1 -1
- package/dist/separator/separator.d.ts +21 -9
- package/dist/separator/separator.js +33 -11
- package/dist/separator/separator.js.map +1 -1
- package/dist/stat-card/index.d.ts +2 -2
- package/dist/stat-card/index.js +2 -2
- package/dist/stat-card/stat-card.d.ts +34 -1
- package/dist/stat-card/stat-card.js +51 -21
- package/dist/stat-card/stat-card.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/table/table.d.ts +1 -1
- package/dist/time-picker/time-picker.d.ts +36 -1
- package/dist/time-picker/time-picker.js +33 -5
- package/dist/time-picker/time-picker.js.map +1 -1
- package/dist/toast/toast.d.ts +2 -2
- package/dist/usage-meter/index.d.ts +2 -2
- package/dist/usage-meter/usage-meter.d.ts +30 -0
- package/dist/usage-meter/usage-meter.js +56 -38
- package/dist/usage-meter/usage-meter.js.map +1 -1
- package/package.json +3 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"multi-select.js","names":["AriaButton","AriaSelect","AriaLabel","AriaSelectValue","AriaFieldError","AriaPopover","AriaListBox"],"sources":["../../src/multi-select/multi-select.tsx"],"sourcesContent":["\"use client\";\n\nimport { forwardRef, useContext, useRef, useState, type ReactNode } from \"react\";\nimport {\n Button as AriaButton,\n ButtonContext,\n FieldError as AriaFieldError,\n Label as AriaLabel,\n ListBox as AriaListBox,\n Popover as AriaPopover,\n Select as AriaSelect,\n SelectStateContext,\n SelectValue as AriaSelectValue,\n type Key,\n type SelectProps as AriaSelectProps,\n} from \"react-aria-components\";\n\nimport { labelVariants } from \"../label/label.js\";\nimport { cn, cnAll } from \"../lib/cn.js\";\nimport { useFlip } from \"../lib/flip.js\";\nimport { SwapText } from \"../lib/swap-text.js\";\nimport { selectPopoverVariants, selectTriggerVariants, selectVariants } from \"../select/select.js\";\n\n/**\n * MultiSelect: pick several options from a list. A Select in multiple mode\n * (React Aria), so the keyboard, typeahead, focus and screen-reader behaviour\n * are Select's own; the list stays open while picking.\n *\n * The trigger shows the first picks as chips and the rest as \"+N\". A new chip\n * fades up from a blur and grows in on the snappy spring while the chips beside\n * it glide over; the count rolls up or down as it changes. A clear button scales\n * in beside the chevron when anything is picked. Options are `SelectItem`s, whose\n * tick pops in as an option is picked.\n */\nexport interface MultiSelectProps extends Omit<\n AriaSelectProps<object, \"multiple\">,\n \"className\" | \"style\" | \"children\" | \"items\" | \"selectionMode\"\n> {\n /** `SelectItem` children. */\n children?: ReactNode;\n /** What is being chosen, and the control's accessible name. */\n label: ReactNode;\n labelHidden?: boolean;\n /** Shown while nothing is picked. @default \"Select options\" */\n placeholder?: string;\n /** Why the selection is wrong. Rendered under the control. */\n errorMessage?: ReactNode;\n /** Chips shown in the trigger before the rest become \"+N\". @default 2 */\n maxVisible?: number;\n /** Accessible name of the clear button. @default \"Clear selections\" */\n clearLabel?: string;\n /** @default \"md\" */\n size?: \"sm\" | \"md\" | \"lg\";\n /** Extra classes for the wrapper. Merged last. */\n className?: string;\n}\n\n/** The picked options as chips, then \"+N\", reading Select's own state. */\nfunction Chips({ placeholder, maxVisible }: { placeholder: string; maxVisible: number }) {\n const state = useContext(SelectStateContext);\n const keys = (state?.value as Key[] | undefined) ?? [];\n const labelOf = (key: Key) => state?.collection.getItem(key)?.textValue ?? String(key);\n const shown = keys.slice(0, maxVisible);\n const rest = keys.length - shown.length;\n const row = useRef<HTMLSpanElement>(null);\n // The chips beside a new or removed one glide to their new place.\n useFlip(row, keys.join(\"|\"), \"var(--duration-smooth)\", \"var(--ease-smooth)\");\n // Chips there at first paint never animate; later ones grow in.\n const [initial] = useState(() => new Set(keys));\n const [count, setCount] = useState({ rest, dir: 1 });\n if (count.rest !== rest) setCount({ rest, dir: rest > count.rest ? 1 : -1 });\n return (\n <span\n ref={row}\n aria-hidden=\"true\"\n className=\"relative flex min-h-6.5 min-w-0 flex-1 items-center gap-1.5 overflow-hidden\"\n >\n {keys.length === 0 ? (\n <span\n data-slot=\"multi-select-placeholder\"\n className=\"truncate text-muted-foreground motion-safe:animate-fade-in\"\n >\n {placeholder}\n </span>\n ) : null}\n {shown.map((key) => (\n <span\n key={String(key)}\n data-flip=\"\"\n data-slot=\"multi-select-chip\"\n className={cnAll(\n \"multi-select-chip\",\n !initial.has(key) && \"motion-safe:animate-chip-reveal\",\n )}\n >\n {labelOf(key)}\n </span>\n ))}\n {rest > 0 ? (\n <span\n data-flip=\"\"\n data-slot=\"multi-select-more\"\n className=\"multi-select-chip text-text-secondary tabular-nums\"\n >\n +\n <SwapText swapKey={String(rest)} dir={count.dir} motion=\"rise\">\n {rest}\n </SwapText>\n </span>\n ) : null}\n </span>\n );\n}\n\n/** Clears every pick. It sits outside the trigger, so it is its own tab stop. */\nfunction Clear({ label, disabled }: { label: string; disabled: boolean }) {\n const state = useContext(SelectStateContext);\n const keys = (state?.value as Key[] | undefined) ?? [];\n if (!state || keys.length === 0 || disabled) return null;\n // Select hands its trigger props to every Button inside it; clear that, or this\n // button would become a second trigger named by the label.\n return (\n <ButtonContext.Provider value={null}>\n <AriaButton\n aria-label={label}\n data-slot=\"multi-select-clear\"\n onPress={() => {\n state.setValue([]);\n state.close();\n }}\n className=\"multi-select-clear motion-safe:animate-field-clear-in motion-reduce:animate-fade-in\"\n >\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 className=\"size-3.5\"\n >\n <path d=\"m4 4 8 8M12 4l-8 8\" />\n </svg>\n </AriaButton>\n </ButtonContext.Provider>\n );\n}\n\nexport const MultiSelect = forwardRef<HTMLButtonElement, MultiSelectProps>(function MultiSelect(\n {\n children,\n label,\n labelHidden,\n placeholder = \"Select options\",\n errorMessage,\n maxVisible = 2,\n clearLabel = \"Clear selections\",\n size = \"md\",\n className,\n ...props\n },\n ref,\n) {\n return (\n <AriaSelect<object, \"multiple\">\n {...props}\n selectionMode=\"multiple\"\n data-slot=\"multi-select\"\n className={cn(selectVariants(), className)}\n >\n <AriaLabel\n data-slot=\"multi-select-label\"\n className={cnAll(labelVariants(), labelHidden && \"sr-only\")}\n >\n {label}\n </AriaLabel>\n <div className=\"relative\">\n <AriaButton\n ref={ref}\n data-slot=\"multi-select-trigger\"\n className={cnAll(selectTriggerVariants({ size }), \"pe-16 multi-select-trigger\")}\n >\n <AriaSelectValue data-slot=\"multi-select-value\" className=\"sr-only\">\n {({ isPlaceholder, selectedText }) => (isPlaceholder ? placeholder : selectedText)}\n </AriaSelectValue>\n <Chips placeholder={placeholder} maxVisible={maxVisible} />\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 strokeLinejoin=\"round\"\n className=\"absolute end-(--density-control-pad-md) text-muted-foreground motion-safe:transition-[rotate] motion-safe:duration-(--duration-gentle) motion-safe:ease-gentle group-aria-expanded/trigger:rotate-180\"\n >\n <path d=\"m4 6 4 4 4-4\" />\n </svg>\n </AriaButton>\n <Clear label={clearLabel} disabled={props.isDisabled ?? false} />\n </div>\n {errorMessage == null ? null : (\n <AriaFieldError\n data-slot=\"multi-select-error\"\n className=\"text-sm leading-(--leading-normal) text-destructive-strong\"\n >\n {errorMessage}\n </AriaFieldError>\n )}\n <AriaPopover data-slot=\"multi-select-popover\" className={selectPopoverVariants()}>\n <AriaListBox data-slot=\"multi-select-list\" className=\"outline-none\">\n {children}\n </AriaListBox>\n </AriaPopover>\n </AriaSelect>\n );\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"multi-select.js","names":["TooltipTrigger","Tooltip","AriaButton","AriaSelectValue","AriaSelect","AriaLabel","AriaFieldError","AriaPopover","AriaListBox","AriaListBoxSection","AriaHeader"],"sources":["../../src/multi-select/multi-select.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva } from \"class-variance-authority\";\nimport { forwardRef, useContext, useRef, useState, type ReactNode, type RefObject } from \"react\";\nimport {\n Button as AriaButton,\n ButtonContext,\n FieldError as AriaFieldError,\n Header as AriaHeader,\n Label as AriaLabel,\n ListBox as AriaListBox,\n ListBoxSection as AriaListBoxSection,\n Popover as AriaPopover,\n Select as AriaSelect,\n SelectStateContext,\n SelectValue as AriaSelectValue,\n type Key,\n type SelectProps as AriaSelectProps,\n} from \"react-aria-components\";\n\nimport { labelVariants } from \"../label/label.js\";\nimport { cn, cnAll } from \"../lib/cn.js\";\nimport { useFlip } from \"../lib/flip.js\";\nimport { SwapText } from \"../lib/swap-text.js\";\nimport { selectPopoverVariants, selectTriggerVariants, selectVariants } from \"../select/select.js\";\nimport { Tooltip, TooltipTrigger } from \"../tooltip/tooltip.js\";\n\n/**\n * MultiSelect: pick several options from a list. A Select in multiple mode\n * (React Aria), so the keyboard, typeahead, focus and screen-reader behaviour\n * are Select's own; the list stays open while picking.\n *\n * The trigger shows the first picks as chips and the rest as \"+N\". A new chip\n * fades up from a blur and grows in on the snappy spring while the chips beside\n * it glide over; the count rolls up or down as it changes. A clear button scales\n * in beside the chevron when anything is picked. Options are `SelectItem`s, whose\n * tick pops in as an option is picked; `MultiSelectGroup` gathers them under a\n * heading.\n */\nexport interface MultiSelectProps extends Omit<\n AriaSelectProps<object, \"multiple\">,\n \"className\" | \"style\" | \"children\" | \"items\" | \"selectionMode\"\n> {\n /** `SelectItem` children. */\n children?: ReactNode;\n /** What is being chosen, and the control's accessible name. */\n label: ReactNode;\n labelHidden?: boolean;\n /** Shown while nothing is picked. @default \"Select options\" */\n placeholder?: string;\n /** Why the selection is wrong. Rendered under the control. */\n errorMessage?: ReactNode;\n /** Chips shown in the trigger before the rest become \"+N\". @default 2 */\n maxVisible?: number;\n /**\n * Words after the overflow count, so `\"more\"` reads \"+2 more\". Off by default:\n * the count alone, \"+2\".\n */\n moreLabel?: string;\n /**\n * While some picks are folded into \"+N\", hovering or focusing the trigger shows\n * a tooltip listing them. Off by default.\n *\n * @default false\n */\n overflowTooltip?: boolean;\n /** Accessible name of the clear button. @default \"Clear selections\" */\n clearLabel?: string;\n /** @default \"md\" */\n size?: \"sm\" | \"md\" | \"lg\";\n /** Extra classes for the wrapper. Merged last. */\n className?: string;\n}\n\n/** The picked options as chips, then \"+N\", reading Select's own state. */\nfunction Chips({\n placeholder,\n maxVisible,\n moreLabel,\n moreRef,\n}: {\n placeholder: string;\n maxVisible: number;\n moreLabel: string | undefined;\n moreRef: RefObject<HTMLSpanElement | null>;\n}) {\n const state = useContext(SelectStateContext);\n const keys = (state?.value as Key[] | undefined) ?? [];\n const labelOf = (key: Key) => state?.collection.getItem(key)?.textValue ?? String(key);\n const shown = keys.slice(0, maxVisible);\n const rest = keys.length - shown.length;\n const row = useRef<HTMLSpanElement>(null);\n // The chips beside a new or removed one glide to their new place.\n useFlip(row, keys.join(\"|\"), \"var(--duration-smooth)\", \"var(--ease-smooth)\");\n // Chips there at first paint never animate; later ones grow in.\n const [initial] = useState(() => new Set(keys));\n const [count, setCount] = useState({ rest, dir: 1 });\n if (count.rest !== rest) setCount({ rest, dir: rest > count.rest ? 1 : -1 });\n return (\n <span\n ref={row}\n aria-hidden=\"true\"\n className=\"relative flex min-h-6.5 min-w-0 flex-1 items-center gap-1.5 overflow-hidden\"\n >\n {keys.length === 0 ? (\n <span\n data-slot=\"multi-select-placeholder\"\n className=\"truncate text-muted-foreground motion-safe:animate-fade-in\"\n >\n {placeholder}\n </span>\n ) : null}\n {shown.map((key) => (\n <span\n key={String(key)}\n data-flip=\"\"\n data-slot=\"multi-select-chip\"\n className={cnAll(\n \"multi-select-chip\",\n !initial.has(key) && \"motion-safe:animate-chip-reveal\",\n )}\n >\n {labelOf(key)}\n </span>\n ))}\n {rest > 0 ? (\n <span\n ref={moreRef}\n data-flip=\"\"\n data-slot=\"multi-select-more\"\n className=\"multi-select-chip text-text-secondary tabular-nums\"\n >\n +\n <SwapText swapKey={String(rest)} dir={count.dir} motion=\"rise\">\n {rest}\n </SwapText>\n {/* The chip is a flex box, which drops a leading space in a bare text\n node; whitespace-pre keeps \"+2 more\" from reading \"+2more\". */}\n {moreLabel ? (\n <span data-slot=\"multi-select-more-label\" className=\"whitespace-pre\">\n {` ${moreLabel}`}\n </span>\n ) : null}\n </span>\n ) : null}\n </span>\n );\n}\n\n/** The labels of the picks folded into \"+N\", in the order they were picked. */\nfunction useHiddenLabels(maxVisible: number) {\n const state = useContext(SelectStateContext);\n const keys = (state?.value as Key[] | undefined) ?? [];\n return keys\n .slice(maxVisible)\n .map((key) => state?.collection.getItem(key)?.textValue ?? String(key));\n}\n\n/**\n * Wraps the trigger in a tooltip naming the picks hidden behind \"+N\", placed\n * against the +N chip rather than the whole field. Always rendered, so turning\n * `overflowTooltip` on or off never swaps the trigger element (and drops its\n * focus); disabled while off or while nothing is hidden, so it never opens empty.\n */\nfunction OverflowTooltip({\n enabled,\n maxVisible,\n anchorRef,\n children,\n}: {\n enabled: boolean;\n maxVisible: number;\n anchorRef: RefObject<HTMLSpanElement | null>;\n children: ReactNode;\n}) {\n const hidden = useHiddenLabels(maxVisible);\n return (\n <TooltipTrigger isDisabled={!enabled || hidden.length === 0}>\n {children}\n <Tooltip triggerRef={anchorRef} data-slot=\"multi-select-overflow-tooltip\">\n {hidden.join(\", \")}\n </Tooltip>\n </TooltipTrigger>\n );\n}\n\n/** Clears every pick. It sits outside the trigger, so it is its own tab stop. */\nfunction Clear({ label, disabled }: { label: string; disabled: boolean }) {\n const state = useContext(SelectStateContext);\n const keys = (state?.value as Key[] | undefined) ?? [];\n if (!state || keys.length === 0 || disabled) return null;\n // Select hands its trigger props to every Button inside it; clear that, or this\n // button would become a second trigger named by the label.\n return (\n <ButtonContext.Provider value={null}>\n <AriaButton\n aria-label={label}\n data-slot=\"multi-select-clear\"\n onPress={() => {\n state.setValue([]);\n state.close();\n }}\n className=\"multi-select-clear motion-safe:animate-field-clear-in motion-reduce:animate-fade-in\"\n >\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 className=\"size-3.5\"\n >\n <path d=\"m4 4 8 8M12 4l-8 8\" />\n </svg>\n </AriaButton>\n </ButtonContext.Provider>\n );\n}\n\nexport const MultiSelect = forwardRef<HTMLButtonElement, MultiSelectProps>(function MultiSelect(\n {\n children,\n label,\n labelHidden,\n placeholder = \"Select options\",\n errorMessage,\n maxVisible = 2,\n moreLabel,\n overflowTooltip = false,\n clearLabel = \"Clear selections\",\n size = \"md\",\n className,\n ...props\n },\n ref,\n) {\n const moreRef = useRef<HTMLSpanElement>(null);\n const trigger = (\n <AriaButton\n ref={ref}\n data-slot=\"multi-select-trigger\"\n className={cnAll(selectTriggerVariants({ size }), \"pe-16 multi-select-trigger\")}\n >\n <AriaSelectValue data-slot=\"multi-select-value\" className=\"sr-only\">\n {({ isPlaceholder, selectedText }) => (isPlaceholder ? placeholder : selectedText)}\n </AriaSelectValue>\n <Chips\n placeholder={placeholder}\n maxVisible={maxVisible}\n moreLabel={moreLabel}\n moreRef={moreRef}\n />\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 strokeLinejoin=\"round\"\n className=\"absolute end-(--density-control-pad-md) text-muted-foreground motion-safe:transition-[rotate] motion-safe:duration-(--duration-gentle) motion-safe:ease-gentle group-aria-expanded/trigger:rotate-180\"\n >\n <path d=\"m4 6 4 4 4-4\" />\n </svg>\n </AriaButton>\n );\n return (\n <AriaSelect<object, \"multiple\">\n {...props}\n selectionMode=\"multiple\"\n data-slot=\"multi-select\"\n className={cn(selectVariants(), className)}\n >\n <AriaLabel\n data-slot=\"multi-select-label\"\n className={cnAll(labelVariants(), labelHidden && \"sr-only\")}\n >\n {label}\n </AriaLabel>\n <div className=\"relative\">\n <OverflowTooltip enabled={overflowTooltip} maxVisible={maxVisible} anchorRef={moreRef}>\n {trigger}\n </OverflowTooltip>\n <Clear label={clearLabel} disabled={props.isDisabled ?? false} />\n </div>\n {errorMessage == null ? null : (\n <AriaFieldError\n data-slot=\"multi-select-error\"\n className=\"text-sm leading-(--leading-normal) text-destructive-strong\"\n >\n {errorMessage}\n </AriaFieldError>\n )}\n <AriaPopover data-slot=\"multi-select-popover\" className={selectPopoverVariants()}>\n <AriaListBox data-slot=\"multi-select-list\" className=\"outline-none\">\n {children}\n </AriaListBox>\n </AriaPopover>\n </AriaSelect>\n );\n});\n\nexport const multiSelectGroupVariants = cva([\"flex flex-col not-first:mt-1\"]);\n\nexport const multiSelectGroupHeaderVariants = cva([\n \"px-2 pt-1.5 pb-1 text-xs leading-(--leading-normal) font-semibold text-muted-foreground\",\n]);\n\n/**\n * A run of options under a heading, such as \"Countries\" and \"Regions\". React\n * Aria's ListBoxSection, so the heading names the group for assistive tech and\n * the arrow keys move straight through from one group to the next.\n *\n * @example\n * <MultiSelect label=\"Markets\">\n * <MultiSelectGroup title=\"Countries\">\n * <SelectItem id=\"IN\">India</SelectItem>\n * </MultiSelectGroup>\n * <MultiSelectGroup title=\"Regions\">\n * <SelectItem id=\"europe\">Europe</SelectItem>\n * </MultiSelectGroup>\n * </MultiSelect>\n */\nexport interface MultiSelectGroupProps {\n /** The heading drawn above the group, and the group's accessible name. */\n title: ReactNode;\n /** `SelectItem` children. */\n children?: ReactNode;\n /** Identifies the group in the collection. Defaults to a generated id. */\n id?: Key;\n /** Extra classes for the group. Merged last. */\n className?: string;\n}\n\nexport const MultiSelectGroup = forwardRef<HTMLElement, MultiSelectGroupProps>(\n function MultiSelectGroup({ title, children, id, className }, ref) {\n return (\n <AriaListBoxSection\n ref={ref}\n id={id}\n data-slot=\"multi-select-group\"\n className={cn(multiSelectGroupVariants(), className)}\n >\n <AriaHeader\n data-slot=\"multi-select-group-header\"\n className={multiSelectGroupHeaderVariants()}\n >\n {title}\n </AriaHeader>\n {children}\n </AriaListBoxSection>\n );\n },\n);\n"],"mappings":"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"}
|
|
@@ -17,6 +17,21 @@ export interface PasswordFieldProps extends Omit<InputProps, "className" | "styl
|
|
|
17
17
|
* after mount and changed words rise in. Nothing animates on first render.
|
|
18
18
|
*/
|
|
19
19
|
description?: string;
|
|
20
|
+
/**
|
|
21
|
+
* Error text under the field. Sets `aria-invalid`, so the box takes its
|
|
22
|
+
* invalid border, and is announced as an alert when it appears. Opens and
|
|
23
|
+
* closes like `description`; an empty or `undefined` value closes the row.
|
|
24
|
+
* The same prop as Input's. Inside a React Aria `TextField` (which supplies
|
|
25
|
+
* the label and description) the error row is still rendered, after the
|
|
26
|
+
* control, and named in the input's `aria-describedby`.
|
|
27
|
+
*/
|
|
28
|
+
error?: string;
|
|
29
|
+
/**
|
|
30
|
+
* The field must be filled: the input is `required` (announced by screen
|
|
31
|
+
* readers, and checked by native form validation) and the label shows the
|
|
32
|
+
* required marker.
|
|
33
|
+
*/
|
|
34
|
+
isRequired?: boolean;
|
|
20
35
|
/** Translated or reworded toggle names. */
|
|
21
36
|
messages?: Partial<PasswordFieldMessages>;
|
|
22
37
|
}
|
|
@@ -31,6 +46,7 @@ export interface PasswordFieldProps extends Omit<InputProps, "className" | "styl
|
|
|
31
46
|
*
|
|
32
47
|
* @example
|
|
33
48
|
* <PasswordField label="Password" description="At least eight characters." />
|
|
49
|
+
* <PasswordField label="API secret" isRequired error={errors.secret?.message} {...register("secret")} />
|
|
34
50
|
*/
|
|
35
51
|
export declare const PasswordField: import("react").ForwardRefExoticComponent<PasswordFieldProps & import("react").RefAttributes<HTMLInputElement>>;
|
|
36
52
|
//#endregion
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { cn } from "../lib/cn.js";
|
|
3
3
|
import { fieldSurfaceVariants } from "../lib/field-surface.js";
|
|
4
|
+
import { FieldMessage } from "../lib/field-message.js";
|
|
4
5
|
import { FieldShell, useFieldWiring } from "../lib/field-shell.js";
|
|
5
6
|
import { EyeMorph } from "../lib/eye-morph.js";
|
|
6
|
-
import { forwardRef, useState } from "react";
|
|
7
|
-
import { Button, Group, Input } from "react-aria-components";
|
|
8
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
+
import { forwardRef, useId, useState } from "react";
|
|
8
|
+
import { Button, Group, Input, InputContext, useSlottedContext } from "react-aria-components";
|
|
9
|
+
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
9
10
|
//#region src/password-field/password-field.tsx
|
|
10
11
|
const defaultPasswordFieldMessages = {
|
|
11
12
|
showPassword: "Show password",
|
|
@@ -37,8 +38,9 @@ const toggleClass = "field-toggle-button grid size-7.5 flex-none cursor-pointer
|
|
|
37
38
|
*
|
|
38
39
|
* @example
|
|
39
40
|
* <PasswordField label="Password" description="At least eight characters." />
|
|
41
|
+
* <PasswordField label="API secret" isRequired error={errors.secret?.message} {...register("secret")} />
|
|
40
42
|
*/
|
|
41
|
-
const PasswordField = forwardRef(function PasswordField({ label, description, messages: messagesProp, className, ...props }, ref) {
|
|
43
|
+
const PasswordField = forwardRef(function PasswordField({ label, description, error, isRequired, messages: messagesProp, className, ...props }, ref) {
|
|
42
44
|
const messages = {
|
|
43
45
|
...defaultPasswordFieldMessages,
|
|
44
46
|
...messagesProp
|
|
@@ -51,20 +53,35 @@ const PasswordField = forwardRef(function PasswordField({ label, description, me
|
|
|
51
53
|
describedBy: props["aria-describedby"],
|
|
52
54
|
invalid: props["aria-invalid"],
|
|
53
55
|
description,
|
|
54
|
-
error
|
|
56
|
+
error
|
|
55
57
|
});
|
|
58
|
+
const inputContext = useSlottedContext(InputContext);
|
|
59
|
+
const generatedId = useId();
|
|
60
|
+
const insideErrorId = field.inside && error ? `${generatedId}-error` : void 0;
|
|
61
|
+
const [errorRow, setErrorRow] = useState(Boolean(insideErrorId));
|
|
62
|
+
if (insideErrorId && !errorRow) setErrorRow(true);
|
|
63
|
+
const insideProps = insideErrorId ? {
|
|
64
|
+
"aria-invalid": true,
|
|
65
|
+
"aria-describedby": [
|
|
66
|
+
inputContext?.["aria-describedby"],
|
|
67
|
+
props["aria-describedby"],
|
|
68
|
+
insideErrorId
|
|
69
|
+
].filter(Boolean).join(" ")
|
|
70
|
+
} : {};
|
|
56
71
|
const disabled = Boolean(props.disabled);
|
|
57
72
|
const ariaInvalid = props["aria-invalid"];
|
|
58
73
|
const control = /* @__PURE__ */ jsxs(Group, {
|
|
59
74
|
role: "presentation",
|
|
60
75
|
isDisabled: disabled,
|
|
61
|
-
isInvalid: ariaInvalid !== void 0 && ariaInvalid !== false && ariaInvalid !== "false",
|
|
76
|
+
isInvalid: Boolean(error) || ariaInvalid !== void 0 && ariaInvalid !== false && ariaInvalid !== "false",
|
|
62
77
|
"data-slot": "password-field-shell",
|
|
63
78
|
className: shellClass,
|
|
64
79
|
children: [/* @__PURE__ */ jsx(Input, {
|
|
65
80
|
ref,
|
|
66
81
|
...props,
|
|
82
|
+
...isRequired ? { required: true } : {},
|
|
67
83
|
...field.controlProps,
|
|
84
|
+
...insideProps,
|
|
68
85
|
type: visible ? "text" : "password",
|
|
69
86
|
"data-slot": "password-field",
|
|
70
87
|
"data-reveal": toggled ? visible ? "shown" : "hidden" : void 0,
|
|
@@ -82,14 +99,26 @@ const PasswordField = forwardRef(function PasswordField({ label, description, me
|
|
|
82
99
|
children: /* @__PURE__ */ jsx(EyeMorph, { slashed: visible })
|
|
83
100
|
})]
|
|
84
101
|
});
|
|
102
|
+
if (field.inside) return /* @__PURE__ */ jsxs(Fragment$1, { children: [control, errorRow ? /* @__PURE__ */ jsx("div", {
|
|
103
|
+
"data-slot": "password-field-error-row",
|
|
104
|
+
className: "grid min-w-0 font-sans",
|
|
105
|
+
children: /* @__PURE__ */ jsx(FieldMessage, {
|
|
106
|
+
id: insideErrorId,
|
|
107
|
+
text: error,
|
|
108
|
+
tone: "error",
|
|
109
|
+
wordExit: "sharp",
|
|
110
|
+
numbers: false
|
|
111
|
+
})
|
|
112
|
+
}) : null] });
|
|
85
113
|
if (!field.active) return control;
|
|
86
114
|
return /* @__PURE__ */ jsx(FieldShell, {
|
|
87
115
|
label,
|
|
88
116
|
controlId: field.controlId,
|
|
89
117
|
description,
|
|
90
118
|
hintId: field.hintId,
|
|
91
|
-
error
|
|
92
|
-
errorId:
|
|
119
|
+
error,
|
|
120
|
+
errorId: field.errorId,
|
|
121
|
+
required: isRequired,
|
|
93
122
|
wordExit: "sharp",
|
|
94
123
|
numbers: false,
|
|
95
124
|
children: control
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"password-field.js","names":["AriaGroup","AriaInput","AriaButton"],"sources":["../../src/password-field/password-field.tsx"],"sourcesContent":["\"use client\";\n\nimport { forwardRef, useState } from \"react\";\nimport {\n Button as AriaButton,\n Group as AriaGroup,\n Input as AriaInput,\n type InputProps as AriaInputProps,\n} from \"react-aria-components\";\n\nimport { cn } from \"../lib/cn.js\";\nimport { EyeMorph } from \"../lib/eye-morph.js\";\nimport { FieldShell, useFieldWiring } from \"../lib/field-shell.js\";\nimport { fieldSurfaceVariants } from \"../lib/field-surface.js\";\n\n/** The toggle's accessible names. Both have English defaults; pass the ones you translate. */\nexport interface PasswordFieldMessages {\n showPassword: string;\n hidePassword: string;\n}\n\nexport const defaultPasswordFieldMessages: PasswordFieldMessages = {\n showPassword: \"Show password\",\n hidePassword: \"Hide password\",\n};\n\n/**\n * The field's box. A React Aria `Group` rather than the input, because the\n * toggle sits inside the same border: it carries the shared field surface (the\n * 160ms border transition, the darker border on hover and focus, the muted\n * disabled look, the invalid border) and the input inside is borderless.\n *\n * `data-focus-within` darkens the border too. A Group reports focus-visible\n * from the input modality, so a mouse click into the input, or onto the toggle,\n * would otherwise leave the border pale; a plain Input is different because\n * React Aria treats a text input as always keyboard-focused.\n */\nconst shellClass = cn(\n fieldSurfaceVariants(),\n \"flex items-center h-(--density-control-h-md) ps-(--density-control-pad-md) field-toggle-shell [&:where([data-focus-within])]:border-border-strong\",\n);\n\n/** The input inside the box: no border, no fill, no outline of its own. */\nconst inputClass =\n \"min-w-0 flex-1 border-0 bg-transparent p-0 font-sans text-base text-foreground outline-none placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground data-[disabled]:cursor-not-allowed motion-safe:data-[reveal=shown]:animate-field-reveal-shown motion-safe:data-[reveal=hidden]:animate-field-reveal-hidden\";\n\nconst toggleClass =\n \"field-toggle-button grid size-7.5 flex-none cursor-pointer place-items-center rounded-control border-0 bg-transparent p-0 text-muted-foreground outline-none data-[hovered]:bg-muted data-[hovered]:text-foreground aria-pressed:text-foreground data-[focus-visible]:outline-solid data-[focus-visible]:outline-(length:--focus-ring-width) data-[focus-visible]:outline-offset-(--focus-ring-offset) data-[focus-visible]:outline-(--color-ring) data-[disabled]:cursor-not-allowed\";\n\nexport interface PasswordFieldProps extends Omit<\n AriaInputProps,\n \"className\" | \"style\" | \"size\" | \"type\"\n> {\n /** Extra classes for the input. Merged last, so they win conflicts with ours. */\n className?: string;\n /** The visible label, tied to the input with `htmlFor`. */\n label: string;\n /**\n * Helper text under the field, named by `aria-describedby`. A string, because\n * the copy is animated word by word: it opens on a spring when it appears\n * after mount and changed words rise in. Nothing animates on first render.\n */\n description?: string;\n /** Translated or reworded toggle names. */\n messages?: Partial<PasswordFieldMessages>;\n}\n\n/**\n * A password input with a show and hide toggle. Switching resolves the text in\n * place from a soft blur, and the eye is one outline that a slash draws across.\n * Native input props apply to the input; `disabled` also disables the toggle,\n * and `aria-invalid` gives the box its invalid border.\n *\n * The toggle's name changes with the state and it is also `aria-pressed`, so a\n * screen reader may announce both (\"Hide password, pressed\").\n *\n * @example\n * <PasswordField label=\"Password\" description=\"At least eight characters.\" />\n */\nexport const PasswordField = forwardRef<HTMLInputElement, PasswordFieldProps>(\n function PasswordField({ label, description, messages: messagesProp, className, ...props }, ref) {\n const messages = { ...defaultPasswordFieldMessages, ...messagesProp };\n const [visible, setVisible] = useState(false);\n // data-reveal only appears after the first toggle, so the animation runs on each change but never on mount.\n const [toggled, setToggled] = useState(false);\n const field = useFieldWiring({\n enabled: true,\n id: props.id,\n describedBy: props[\"aria-describedby\"],\n invalid: props[\"aria-invalid\"],\n description,\n error: undefined,\n });\n const disabled = Boolean(props.disabled);\n const ariaInvalid = props[\"aria-invalid\"];\n const invalid = ariaInvalid !== undefined && ariaInvalid !== false && ariaInvalid !== \"false\";\n\n const control = (\n <AriaGroup\n role=\"presentation\"\n isDisabled={disabled}\n isInvalid={invalid}\n data-slot=\"password-field-shell\"\n className={shellClass}\n >\n <AriaInput\n ref={ref}\n {...props}\n {...field.controlProps}\n type={visible ? \"text\" : \"password\"}\n data-slot=\"password-field\"\n data-reveal={toggled ? (visible ? \"shown\" : \"hidden\") : undefined}\n className={cn(inputClass, className)}\n />\n <AriaButton\n type=\"button\"\n isDisabled={disabled}\n aria-label={visible ? messages.hidePassword : messages.showPassword}\n aria-pressed={visible}\n onPress={() => {\n setVisible((current) => !current);\n setToggled(true);\n }}\n className={toggleClass}\n >\n <EyeMorph slashed={visible} />\n </AriaButton>\n </AriaGroup>\n );\n\n if (!field.active) return control;\n return (\n <FieldShell\n label={label}\n controlId={field.controlId}\n description={description}\n hintId={field.hintId}\n error={undefined}\n errorId={undefined}\n wordExit=\"sharp\"\n numbers={false}\n >\n {control}\n </FieldShell>\n );\n },\n);\n"],"mappings":";;;;;;;;;AAqBA,MAAa,+BAAsD;CACjE,cAAc;CACd,cAAc;AAChB;;;;;;;;;;;;AAaA,MAAM,aAAa,GACjB,qBAAqB,GACrB,mJACF;;AAGA,MAAM,aACJ;AAEF,MAAM,cACJ;;;;;;;;;;;;;AAgCF,MAAa,gBAAgB,WAC3B,SAAS,cAAc,EAAE,OAAO,aAAa,UAAU,cAAc,WAAW,GAAG,SAAS,KAAK;CAC/F,MAAM,WAAW;EAAE,GAAG;EAA8B,GAAG;CAAa;CACpE,MAAM,CAAC,SAAS,cAAc,SAAS,KAAK;CAE5C,MAAM,CAAC,SAAS,cAAc,SAAS,KAAK;CAC5C,MAAM,QAAQ,eAAe;EAC3B,SAAS;EACT,IAAI,MAAM;EACV,aAAa,MAAM;EACnB,SAAS,MAAM;EACf;EACA,OAAO,KAAA;CACT,CAAC;CACD,MAAM,WAAW,QAAQ,MAAM,QAAQ;CACvC,MAAM,cAAc,MAAM;CAG1B,MAAM,UACJ,qBAACA,OAAD;EACE,MAAK;EACL,YAAY;EACZ,WANY,gBAAgB,KAAA,KAAa,gBAAgB,SAAS,gBAAgB;EAOlF,aAAU;EACV,WAAW;EALb,UAAA,CAOE,oBAACC,OAAD;GACO;GACL,GAAI;GACJ,GAAI,MAAM;GACV,MAAM,UAAU,SAAS;GACzB,aAAU;GACV,eAAa,UAAW,UAAU,UAAU,WAAY,KAAA;GACxD,WAAW,GAAG,YAAY,SAAS;EACpC,CAAA,GACD,oBAACC,QAAD;GACE,MAAK;GACL,YAAY;GACZ,cAAY,UAAU,SAAS,eAAe,SAAS;GACvD,gBAAc;GACd,eAAe;IACb,YAAY,YAAY,CAAC,OAAO;IAChC,WAAW,IAAI;GACjB;GACA,WAAW;GAEX,UAAA,oBAAC,UAAD,EAAU,SAAS,QAAU,CAAA;EACnB,CAAA,CACH;;CAGb,IAAI,CAAC,MAAM,QAAQ,OAAO;CAC1B,OACE,oBAAC,YAAD;EACS;EACP,WAAW,MAAM;EACJ;EACb,QAAQ,MAAM;EACd,OAAO,KAAA;EACP,SAAS,KAAA;EACT,UAAS;EACT,SAAS;EAER,UAAA;CACS,CAAA;AAEhB,CACF"}
|
|
1
|
+
{"version":3,"file":"password-field.js","names":["AriaGroup","AriaInput","AriaButton"],"sources":["../../src/password-field/password-field.tsx"],"sourcesContent":["\"use client\";\n\nimport { forwardRef, useId, useState } from \"react\";\nimport {\n Button as AriaButton,\n Group as AriaGroup,\n Input as AriaInput,\n InputContext,\n useSlottedContext,\n type InputProps as AriaInputProps,\n} from \"react-aria-components\";\n\nimport { cn } from \"../lib/cn.js\";\nimport { EyeMorph } from \"../lib/eye-morph.js\";\nimport { FieldMessage } from \"../lib/field-message.js\";\nimport { FieldShell, useFieldWiring } from \"../lib/field-shell.js\";\nimport { fieldSurfaceVariants } from \"../lib/field-surface.js\";\n\n/** The toggle's accessible names. Both have English defaults; pass the ones you translate. */\nexport interface PasswordFieldMessages {\n showPassword: string;\n hidePassword: string;\n}\n\nexport const defaultPasswordFieldMessages: PasswordFieldMessages = {\n showPassword: \"Show password\",\n hidePassword: \"Hide password\",\n};\n\n/**\n * The field's box. A React Aria `Group` rather than the input, because the\n * toggle sits inside the same border: it carries the shared field surface (the\n * 160ms border transition, the darker border on hover and focus, the muted\n * disabled look, the invalid border) and the input inside is borderless.\n *\n * `data-focus-within` darkens the border too. A Group reports focus-visible\n * from the input modality, so a mouse click into the input, or onto the toggle,\n * would otherwise leave the border pale; a plain Input is different because\n * React Aria treats a text input as always keyboard-focused.\n */\nconst shellClass = cn(\n fieldSurfaceVariants(),\n \"flex items-center h-(--density-control-h-md) ps-(--density-control-pad-md) field-toggle-shell [&:where([data-focus-within])]:border-border-strong\",\n);\n\n/** The input inside the box: no border, no fill, no outline of its own. */\nconst inputClass =\n \"min-w-0 flex-1 border-0 bg-transparent p-0 font-sans text-base text-foreground outline-none placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground data-[disabled]:cursor-not-allowed motion-safe:data-[reveal=shown]:animate-field-reveal-shown motion-safe:data-[reveal=hidden]:animate-field-reveal-hidden\";\n\nconst toggleClass =\n \"field-toggle-button grid size-7.5 flex-none cursor-pointer place-items-center rounded-control border-0 bg-transparent p-0 text-muted-foreground outline-none data-[hovered]:bg-muted data-[hovered]:text-foreground aria-pressed:text-foreground data-[focus-visible]:outline-solid data-[focus-visible]:outline-(length:--focus-ring-width) data-[focus-visible]:outline-offset-(--focus-ring-offset) data-[focus-visible]:outline-(--color-ring) data-[disabled]:cursor-not-allowed\";\n\nexport interface PasswordFieldProps extends Omit<\n AriaInputProps,\n \"className\" | \"style\" | \"size\" | \"type\"\n> {\n /** Extra classes for the input. Merged last, so they win conflicts with ours. */\n className?: string;\n /** The visible label, tied to the input with `htmlFor`. */\n label: string;\n /**\n * Helper text under the field, named by `aria-describedby`. A string, because\n * the copy is animated word by word: it opens on a spring when it appears\n * after mount and changed words rise in. Nothing animates on first render.\n */\n description?: string;\n /**\n * Error text under the field. Sets `aria-invalid`, so the box takes its\n * invalid border, and is announced as an alert when it appears. Opens and\n * closes like `description`; an empty or `undefined` value closes the row.\n * The same prop as Input's. Inside a React Aria `TextField` (which supplies\n * the label and description) the error row is still rendered, after the\n * control, and named in the input's `aria-describedby`.\n */\n error?: string;\n /**\n * The field must be filled: the input is `required` (announced by screen\n * readers, and checked by native form validation) and the label shows the\n * required marker.\n */\n isRequired?: boolean;\n /** Translated or reworded toggle names. */\n messages?: Partial<PasswordFieldMessages>;\n}\n\n/**\n * A password input with a show and hide toggle. Switching resolves the text in\n * place from a soft blur, and the eye is one outline that a slash draws across.\n * Native input props apply to the input; `disabled` also disables the toggle,\n * and `aria-invalid` gives the box its invalid border.\n *\n * The toggle's name changes with the state and it is also `aria-pressed`, so a\n * screen reader may announce both (\"Hide password, pressed\").\n *\n * @example\n * <PasswordField label=\"Password\" description=\"At least eight characters.\" />\n * <PasswordField label=\"API secret\" isRequired error={errors.secret?.message} {...register(\"secret\")} />\n */\nexport const PasswordField = forwardRef<HTMLInputElement, PasswordFieldProps>(\n function PasswordField(\n { label, description, error, isRequired, messages: messagesProp, className, ...props },\n ref,\n ) {\n const messages = { ...defaultPasswordFieldMessages, ...messagesProp };\n const [visible, setVisible] = useState(false);\n // data-reveal only appears after the first toggle, so the animation runs on each change but never on mount.\n const [toggled, setToggled] = useState(false);\n const field = useFieldWiring({\n enabled: true,\n id: props.id,\n describedBy: props[\"aria-describedby\"],\n invalid: props[\"aria-invalid\"],\n description,\n error,\n });\n // Inside a React Aria TextField the shell (and its error row) is skipped, since the\n // field supplies the label and description. The error is ours, not the field's,\n // so it is rendered after the control here and added to the input's\n // aria-describedby, after the field's own ids (an explicit prop replaces the\n // context's, so they are carried over). Without this the box turned red with no text.\n const inputContext = useSlottedContext(InputContext) as {\n \"aria-describedby\"?: string;\n } | null;\n const generatedId = useId();\n const insideErrorId = field.inside && error ? `${generatedId}-error` : undefined;\n // The row's wrapper (it carries the font the shell would) is added the first time an\n // error arrives and then kept, so the row can animate closed; without one, nothing is added.\n const [errorRow, setErrorRow] = useState(Boolean(insideErrorId));\n if (insideErrorId && !errorRow) setErrorRow(true);\n const insideProps = insideErrorId\n ? {\n \"aria-invalid\": true,\n \"aria-describedby\": [\n inputContext?.[\"aria-describedby\"],\n props[\"aria-describedby\"],\n insideErrorId,\n ]\n .filter(Boolean)\n .join(\" \"),\n }\n : {};\n const disabled = Boolean(props.disabled);\n const ariaInvalid = props[\"aria-invalid\"];\n const invalid =\n Boolean(error) ||\n (ariaInvalid !== undefined && ariaInvalid !== false && ariaInvalid !== \"false\");\n\n const control = (\n <AriaGroup\n role=\"presentation\"\n isDisabled={disabled}\n isInvalid={invalid}\n data-slot=\"password-field-shell\"\n className={shellClass}\n >\n <AriaInput\n ref={ref}\n {...props}\n {...(isRequired ? { required: true } : {})}\n {...field.controlProps}\n {...insideProps}\n type={visible ? \"text\" : \"password\"}\n data-slot=\"password-field\"\n data-reveal={toggled ? (visible ? \"shown\" : \"hidden\") : undefined}\n className={cn(inputClass, className)}\n />\n <AriaButton\n type=\"button\"\n isDisabled={disabled}\n aria-label={visible ? messages.hidePassword : messages.showPassword}\n aria-pressed={visible}\n onPress={() => {\n setVisible((current) => !current);\n setToggled(true);\n }}\n className={toggleClass}\n >\n <EyeMorph slashed={visible} />\n </AriaButton>\n </AriaGroup>\n );\n\n if (field.inside) {\n // Always this shape inside a field, so an error arriving later does not remount the input.\n return (\n <>\n {control}\n {errorRow ? (\n <div data-slot=\"password-field-error-row\" className=\"grid min-w-0 font-sans\">\n <FieldMessage\n id={insideErrorId}\n text={error}\n tone=\"error\"\n wordExit=\"sharp\"\n numbers={false}\n />\n </div>\n ) : null}\n </>\n );\n }\n if (!field.active) return control;\n return (\n <FieldShell\n label={label}\n controlId={field.controlId}\n description={description}\n hintId={field.hintId}\n error={error}\n errorId={field.errorId}\n required={isRequired}\n wordExit=\"sharp\"\n numbers={false}\n >\n {control}\n </FieldShell>\n );\n },\n);\n"],"mappings":";;;;;;;;;;AAwBA,MAAa,+BAAsD;CACjE,cAAc;CACd,cAAc;AAChB;;;;;;;;;;;;AAaA,MAAM,aAAa,GACjB,qBAAqB,GACrB,mJACF;;AAGA,MAAM,aACJ;AAEF,MAAM,cACJ;;;;;;;;;;;;;;AAgDF,MAAa,gBAAgB,WAC3B,SAAS,cACP,EAAE,OAAO,aAAa,OAAO,YAAY,UAAU,cAAc,WAAW,GAAG,SAC/E,KACA;CACA,MAAM,WAAW;EAAE,GAAG;EAA8B,GAAG;CAAa;CACpE,MAAM,CAAC,SAAS,cAAc,SAAS,KAAK;CAE5C,MAAM,CAAC,SAAS,cAAc,SAAS,KAAK;CAC5C,MAAM,QAAQ,eAAe;EAC3B,SAAS;EACT,IAAI,MAAM;EACV,aAAa,MAAM;EACnB,SAAS,MAAM;EACf;EACA;CACF,CAAC;CAMD,MAAM,eAAe,kBAAkB,YAAY;CAGnD,MAAM,cAAc,MAAM;CAC1B,MAAM,gBAAgB,MAAM,UAAU,QAAQ,GAAG,YAAY,UAAU,KAAA;CAGvE,MAAM,CAAC,UAAU,eAAe,SAAS,QAAQ,aAAa,CAAC;CAC/D,IAAI,iBAAiB,CAAC,UAAU,YAAY,IAAI;CAChD,MAAM,cAAc,gBAChB;EACE,gBAAgB;EAChB,oBAAoB;GAClB,eAAe;GACf,MAAM;GACN;EACF,CAAC,CACE,OAAO,OAAO,CAAC,CACf,KAAK,GAAG;CACb,IACA,CAAC;CACL,MAAM,WAAW,QAAQ,MAAM,QAAQ;CACvC,MAAM,cAAc,MAAM;CAK1B,MAAM,UACJ,qBAACA,OAAD;EACE,MAAK;EACL,YAAY;EACZ,WAPF,QAAQ,KAAK,KACZ,gBAAgB,KAAA,KAAa,gBAAgB,SAAS,gBAAgB;EAOrE,aAAU;EACV,WAAW;EALb,UAAA,CAOE,oBAACC,OAAD;GACO;GACL,GAAI;GACJ,GAAK,aAAa,EAAE,UAAU,KAAK,IAAI,CAAC;GACxC,GAAI,MAAM;GACV,GAAI;GACJ,MAAM,UAAU,SAAS;GACzB,aAAU;GACV,eAAa,UAAW,UAAU,UAAU,WAAY,KAAA;GACxD,WAAW,GAAG,YAAY,SAAS;EACpC,CAAA,GACD,oBAACC,QAAD;GACE,MAAK;GACL,YAAY;GACZ,cAAY,UAAU,SAAS,eAAe,SAAS;GACvD,gBAAc;GACd,eAAe;IACb,YAAY,YAAY,CAAC,OAAO;IAChC,WAAW,IAAI;GACjB;GACA,WAAW;GAEX,UAAA,oBAAC,UAAD,EAAU,SAAS,QAAU,CAAA;EACnB,CAAA,CACH;;CAGb,IAAI,MAAM,QAER,OACE,qBAAA,YAAA,EAAA,UAAA,CACG,SACA,WACC,oBAAC,OAAD;EAAK,aAAU;EAA2B,WAAU;EAClD,UAAA,oBAAC,cAAD;GACE,IAAI;GACJ,MAAM;GACN,MAAK;GACL,UAAS;GACT,SAAS;EACV,CAAA;CACE,CAAA,IACH,IACJ,EAAA,CAAA;CAGN,IAAI,CAAC,MAAM,QAAQ,OAAO;CAC1B,OACE,oBAAC,YAAD;EACS;EACP,WAAW,MAAM;EACJ;EACb,QAAQ,MAAM;EACP;EACP,SAAS,MAAM;EACf,UAAU;EACV,UAAS;EACT,SAAS;EAER,UAAA;CACS,CAAA;AAEhB,CACF"}
|
package/dist/popover/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { Popover, PopoverProps, PopoverTrigger, popoverVariants } from "./popover.js";
|
|
2
|
-
export { Popover, type PopoverProps, PopoverTrigger, popoverVariants };
|
|
1
|
+
import { Popover, PopoverHoverTrigger, PopoverHoverTriggerProps, PopoverProps, PopoverTrigger, popoverVariants } from "./popover.js";
|
|
2
|
+
export { Popover, PopoverHoverTrigger, type PopoverHoverTriggerProps, type PopoverProps, PopoverTrigger, popoverVariants };
|
package/dist/popover/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { Popover, PopoverTrigger, popoverVariants } from "./popover.js";
|
|
2
|
-
export { Popover, PopoverTrigger, popoverVariants };
|
|
1
|
+
import { Popover, PopoverHoverTrigger, PopoverTrigger, popoverVariants } from "./popover.js";
|
|
2
|
+
export { Popover, PopoverHoverTrigger, PopoverTrigger, popoverVariants };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { ReactNode } from "react";
|
|
2
|
-
import { DialogTrigger as PopoverTrigger, PopoverProps } from "react-aria-components";
|
|
1
|
+
import { JSX, ReactNode } from "react";
|
|
2
|
+
import { DialogTrigger as PopoverTrigger, PopoverProps, PreviewTriggerProps } from "react-aria-components";
|
|
3
3
|
//#region src/popover/popover.d.ts
|
|
4
4
|
/**
|
|
5
5
|
* Popover, ported from the legacy shadcn/Radix component.
|
|
@@ -65,6 +65,43 @@ interface PopoverProps$1 extends Omit<PopoverProps, "className" | "style" | "chi
|
|
|
65
65
|
onAction?: () => void;
|
|
66
66
|
}
|
|
67
67
|
declare const Popover$1: import("react").ForwardRefExoticComponent<PopoverProps$1 & import("react").RefAttributes<HTMLElement>>;
|
|
68
|
+
export interface PopoverHoverTriggerProps extends Omit<PreviewTriggerProps, "delay" | "closeDelay"> {
|
|
69
|
+
/** The trigger, then the `Popover`. The trigger must be focusable (a `Button` or `Link`). */
|
|
70
|
+
children: ReactNode;
|
|
71
|
+
/**
|
|
72
|
+
* Milliseconds the pointer rests on the trigger, or keyboard focus sits on
|
|
73
|
+
* it, before the card opens.
|
|
74
|
+
* @default 600
|
|
75
|
+
*/
|
|
76
|
+
delay?: number;
|
|
77
|
+
/**
|
|
78
|
+
* Milliseconds after the pointer leaves the trigger and the card, or focus
|
|
79
|
+
* leaves both, before it closes. Moving onto the card keeps it open.
|
|
80
|
+
* @default 200
|
|
81
|
+
*/
|
|
82
|
+
closeDelay?: number;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Opens a popover on hover instead of press: a hover card.
|
|
86
|
+
*
|
|
87
|
+
* React Aria's `PreviewTrigger`. The card opens after `delay` when the pointer
|
|
88
|
+
* rests on the trigger or keyboard focus reaches it, and on a long press on
|
|
89
|
+
* touch. It stays open while the pointer travels onto it (a safe area covers
|
|
90
|
+
* the gap) and closes `closeDelay` after the pointer or focus leaves both.
|
|
91
|
+
* Escape closes it; Tab from the trigger moves into it. It is non-modal: no
|
|
92
|
+
* focus trap and no underlay, and the page behind stays usable.
|
|
93
|
+
*
|
|
94
|
+
* The trigger keeps its own press action, so a Link still navigates and a
|
|
95
|
+
* Button still runs `onPress`. Content anyone needs must not live only in the
|
|
96
|
+
* card: touch without a long press never sees it.
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* <PopoverHoverTrigger delay={300}>
|
|
100
|
+
* <Button variant="ghost" aria-label="3 more sources">+3</Button>
|
|
101
|
+
* <Popover placement="bottom start">...</Popover>
|
|
102
|
+
* </PopoverHoverTrigger>
|
|
103
|
+
*/
|
|
104
|
+
export declare function PopoverHoverTrigger({ delay, closeDelay, ...props }: PopoverHoverTriggerProps): JSX.Element;
|
|
68
105
|
//#endregion
|
|
69
106
|
export { Popover$1 as Popover, PopoverProps$1 as PopoverProps, PopoverTrigger };
|
|
70
107
|
//# sourceMappingURL=popover.d.ts.map
|
package/dist/popover/popover.js
CHANGED
|
@@ -3,7 +3,7 @@ import { cnAll } from "../lib/cn.js";
|
|
|
3
3
|
import { Button as Button$1 } from "../button/button.js";
|
|
4
4
|
import { cva } from "class-variance-authority";
|
|
5
5
|
import { forwardRef, useContext } from "react";
|
|
6
|
-
import { DialogTrigger as PopoverTrigger, OverlayTriggerStateContext, Popover } from "react-aria-components";
|
|
6
|
+
import { DialogTrigger as PopoverTrigger, OverlayTriggerStateContext, Popover, PreviewTrigger } from "react-aria-components";
|
|
7
7
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
8
|
//#region src/popover/popover.tsx
|
|
9
9
|
/**
|
|
@@ -66,7 +66,34 @@ const Popover$1 = forwardRef(function Popover$2({ children, className, placement
|
|
|
66
66
|
]
|
|
67
67
|
});
|
|
68
68
|
});
|
|
69
|
+
/**
|
|
70
|
+
* Opens a popover on hover instead of press: a hover card.
|
|
71
|
+
*
|
|
72
|
+
* React Aria's `PreviewTrigger`. The card opens after `delay` when the pointer
|
|
73
|
+
* rests on the trigger or keyboard focus reaches it, and on a long press on
|
|
74
|
+
* touch. It stays open while the pointer travels onto it (a safe area covers
|
|
75
|
+
* the gap) and closes `closeDelay` after the pointer or focus leaves both.
|
|
76
|
+
* Escape closes it; Tab from the trigger moves into it. It is non-modal: no
|
|
77
|
+
* focus trap and no underlay, and the page behind stays usable.
|
|
78
|
+
*
|
|
79
|
+
* The trigger keeps its own press action, so a Link still navigates and a
|
|
80
|
+
* Button still runs `onPress`. Content anyone needs must not live only in the
|
|
81
|
+
* card: touch without a long press never sees it.
|
|
82
|
+
*
|
|
83
|
+
* @example
|
|
84
|
+
* <PopoverHoverTrigger delay={300}>
|
|
85
|
+
* <Button variant="ghost" aria-label="3 more sources">+3</Button>
|
|
86
|
+
* <Popover placement="bottom start">...</Popover>
|
|
87
|
+
* </PopoverHoverTrigger>
|
|
88
|
+
*/
|
|
89
|
+
function PopoverHoverTrigger({ delay = 600, closeDelay = 200, ...props }) {
|
|
90
|
+
return /* @__PURE__ */ jsx(PreviewTrigger, {
|
|
91
|
+
...props,
|
|
92
|
+
delay,
|
|
93
|
+
closeDelay
|
|
94
|
+
});
|
|
95
|
+
}
|
|
69
96
|
//#endregion
|
|
70
|
-
export { Popover$1 as Popover, PopoverTrigger, popoverVariants };
|
|
97
|
+
export { Popover$1 as Popover, PopoverHoverTrigger, PopoverTrigger, popoverVariants };
|
|
71
98
|
|
|
72
99
|
//# sourceMappingURL=popover.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"popover.js","names":["Popover","AriaPopover","Button"],"sources":["../../src/popover/popover.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva } from \"class-variance-authority\";\nimport { forwardRef, useContext, type ReactNode } from \"react\";\nimport {\n OverlayTriggerStateContext,\n Popover as AriaPopover,\n type PopoverProps as AriaPopoverProps,\n} from \"react-aria-components\";\n\nimport { Button } from \"../button/button.js\";\nimport { cnAll } from \"../lib/cn.js\";\n\n/**\n * Popover, ported from the legacy shadcn/Radix component.\n *\n * Census: 2 files, 2 uses. `asChild` on the trigger twice, `className`\n * twice, `align=\"start\"` once. `PopoverAnchor` is exported by the legacy\n * and imported by nobody, so it is not ported; neither are `side`,\n * `sideOffset`, `alignOffset`, `open` / `onOpenChange`, `modal`,\n * `collisionPadding` or `avoidCollisions` -- zero call sites each. Both\n * existing uses are uncontrolled.\n *\n * IT GAINS AN ACCESSIBLE NAME. The legacy renders `role=\"dialog\"` with no\n * `aria-label` and no `aria-labelledby`, so both call sites announce an\n * unnamed dialog. React Aria labels the popover from its trigger\n * automatically once the two are paired, which is the only way to build\n * one here.\n *\n * The surface is `card`: in the legacy theme `--popover` is byte-identical\n * to `--card` in both themes, so the role the migration plan proposed\n * already exists under another name.\n */\nexport const popoverVariants = cva([\n // w-72 (288px) is the legacy default width, as a spacing-scale step.\n \"w-72 rounded-panel border-(length:--border-width) border-border p-4\",\n \"bg-card text-card-foreground shadow-floating\",\n // The reset in @seekalfred/styles is OPT-IN, so a consumer may load\n // none: an unstyled document renders text in a serif, and content-box\n // would add the 16px padding on top of the width.\n \"box-border font-sans text-sm leading-(--leading-normal)\",\n // React Aria gives the popover tabindex=-1 and focuses it on open, so it\n // can take a focus ring it does not want.\n \"outline-none\",\n]);\n\n/**\n * Content anchored to a trigger.\n *\n * @example\n * <PopoverTrigger>\n * <Button>Pick a date</Button>\n * <Popover placement=\"bottom start\">...</Popover>\n * </PopoverTrigger>\n */\nexport interface PopoverProps\n extends Omit<AriaPopoverProps, \"className\" | \"style\" | \"children\"> {\n children?: ReactNode;\n /** Extra classes. Merged last, so they win conflicts with ours. */\n className?: string;\n /**\n * Where it opens, as React Aria's single `placement` string. The legacy\n * split this across `side` and `align`; its one non-default call site is\n * `align=\"start\"`, which is `\"bottom start\"` here.\n *\n * @default \"bottom\"\n */\n placement?: AriaPopoverProps[\"placement\"];\n /**\n * How it comes in: `rise` grows out of the trigger (offset toward it, a little\n * small, then settles on a spring) and leaves quicker; `none` leaves the entrance\n * to the caller, as a picker that animates its own popover does.\n *\n * @default \"rise\"\n */\n entrance?: \"rise\" | \"none\";\n /**\n * A heading at the top of the panel. Together with `description` and\n * `actionLabel` it makes the message popover: a dark title, grey body text\n * and a full-width button, with `children` rendered below them.\n */\n title?: ReactNode;\n /** Grey body text under the title. */\n description?: ReactNode;\n /** The label of the full-width button at the bottom. Pressing it runs `onAction` and closes the popover. */\n actionLabel?: ReactNode;\n /** Runs when the action button is pressed, before the popover closes. */\n onAction?: () => void;\n}\n\nexport const Popover = forwardRef<HTMLElement, PopoverProps>(function Popover(\n {\n children,\n className,\n placement = \"bottom\",\n offset = 4,\n entrance = \"rise\",\n title,\n description,\n actionLabel,\n onAction,\n ...props\n },\n ref,\n) {\n // The open state the enclosing PopoverTrigger owns, so the action button can close it.\n const state = useContext(OverlayTriggerStateContext);\n return (\n <AriaPopover\n ref={ref}\n {...props}\n placement={placement}\n offset={offset}\n data-slot=\"popover\"\n className={cnAll(popoverVariants(), entrance === \"rise\" && \"popover-motion\", className)}\n >\n {/*\n No inner Dialog. Wrapping the content in one is the common React\n Aria idiom and it was in the first draft here -- but deleting it\n broke no assertion, so it was doing nothing: a Popover under a\n DialogTrigger already gets role=\"dialog\", a focus scope, and\n aria-labelledby pointing at the trigger. The wrapper was one more\n element and one more className to explain.\n */}\n {title == null ? null : (\n <h3\n data-slot=\"popover-title\"\n className=\"m-0 text-sm leading-(--leading-tight) font-semibold text-foreground\"\n >\n {title}\n </h3>\n )}\n {description == null ? null : (\n <p\n data-slot=\"popover-description\"\n className=\"m-0 mt-1 text-sm leading-(--leading-normal) text-text-secondary\"\n >\n {description}\n </p>\n )}\n {actionLabel == null ? null : (\n <Button\n variant=\"secondary\"\n size=\"sm\"\n className=\"mt-4 w-full\"\n onPress={() => {\n onAction?.();\n state?.close();\n }}\n >\n {actionLabel}\n </Button>\n )}\n {children}\n </AriaPopover>\n );\n});\n\n/**\n * Pairs a trigger with a popover and owns the open state.\n *\n * React Aria's `DialogTrigger`, renamed for the migration: it takes the\n * trigger element directly as its first child, which is what `asChild` was\n * doing on both legacy call sites.\n */\nexport { DialogTrigger as PopoverTrigger } from \"react-aria-components\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"popover.js","names":["Popover","AriaPopover","Button"],"sources":["../../src/popover/popover.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva } from \"class-variance-authority\";\nimport { forwardRef, useContext, type JSX, type ReactNode } from \"react\";\nimport {\n OverlayTriggerStateContext,\n Popover as AriaPopover,\n PreviewTrigger,\n type PopoverProps as AriaPopoverProps,\n type PreviewTriggerProps,\n} from \"react-aria-components\";\n\nimport { Button } from \"../button/button.js\";\nimport { cnAll } from \"../lib/cn.js\";\n\n/**\n * Popover, ported from the legacy shadcn/Radix component.\n *\n * Census: 2 files, 2 uses. `asChild` on the trigger twice, `className`\n * twice, `align=\"start\"` once. `PopoverAnchor` is exported by the legacy\n * and imported by nobody, so it is not ported; neither are `side`,\n * `sideOffset`, `alignOffset`, `open` / `onOpenChange`, `modal`,\n * `collisionPadding` or `avoidCollisions` -- zero call sites each. Both\n * existing uses are uncontrolled.\n *\n * IT GAINS AN ACCESSIBLE NAME. The legacy renders `role=\"dialog\"` with no\n * `aria-label` and no `aria-labelledby`, so both call sites announce an\n * unnamed dialog. React Aria labels the popover from its trigger\n * automatically once the two are paired, which is the only way to build\n * one here.\n *\n * The surface is `card`: in the legacy theme `--popover` is byte-identical\n * to `--card` in both themes, so the role the migration plan proposed\n * already exists under another name.\n */\nexport const popoverVariants = cva([\n // w-72 (288px) is the legacy default width, as a spacing-scale step.\n \"w-72 rounded-panel border-(length:--border-width) border-border p-4\",\n \"bg-card text-card-foreground shadow-floating\",\n // The reset in @seekalfred/styles is OPT-IN, so a consumer may load\n // none: an unstyled document renders text in a serif, and content-box\n // would add the 16px padding on top of the width.\n \"box-border font-sans text-sm leading-(--leading-normal)\",\n // React Aria gives the popover tabindex=-1 and focuses it on open, so it\n // can take a focus ring it does not want.\n \"outline-none\",\n]);\n\n/**\n * Content anchored to a trigger.\n *\n * @example\n * <PopoverTrigger>\n * <Button>Pick a date</Button>\n * <Popover placement=\"bottom start\">...</Popover>\n * </PopoverTrigger>\n */\nexport interface PopoverProps\n extends Omit<AriaPopoverProps, \"className\" | \"style\" | \"children\"> {\n children?: ReactNode;\n /** Extra classes. Merged last, so they win conflicts with ours. */\n className?: string;\n /**\n * Where it opens, as React Aria's single `placement` string. The legacy\n * split this across `side` and `align`; its one non-default call site is\n * `align=\"start\"`, which is `\"bottom start\"` here.\n *\n * @default \"bottom\"\n */\n placement?: AriaPopoverProps[\"placement\"];\n /**\n * How it comes in: `rise` grows out of the trigger (offset toward it, a little\n * small, then settles on a spring) and leaves quicker; `none` leaves the entrance\n * to the caller, as a picker that animates its own popover does.\n *\n * @default \"rise\"\n */\n entrance?: \"rise\" | \"none\";\n /**\n * A heading at the top of the panel. Together with `description` and\n * `actionLabel` it makes the message popover: a dark title, grey body text\n * and a full-width button, with `children` rendered below them.\n */\n title?: ReactNode;\n /** Grey body text under the title. */\n description?: ReactNode;\n /** The label of the full-width button at the bottom. Pressing it runs `onAction` and closes the popover. */\n actionLabel?: ReactNode;\n /** Runs when the action button is pressed, before the popover closes. */\n onAction?: () => void;\n}\n\nexport const Popover = forwardRef<HTMLElement, PopoverProps>(function Popover(\n {\n children,\n className,\n placement = \"bottom\",\n offset = 4,\n entrance = \"rise\",\n title,\n description,\n actionLabel,\n onAction,\n ...props\n },\n ref,\n) {\n // The open state the enclosing PopoverTrigger owns, so the action button can close it.\n const state = useContext(OverlayTriggerStateContext);\n return (\n <AriaPopover\n ref={ref}\n {...props}\n placement={placement}\n offset={offset}\n data-slot=\"popover\"\n className={cnAll(popoverVariants(), entrance === \"rise\" && \"popover-motion\", className)}\n >\n {/*\n No inner Dialog. Wrapping the content in one is the common React\n Aria idiom and it was in the first draft here -- but deleting it\n broke no assertion, so it was doing nothing: a Popover under a\n DialogTrigger already gets role=\"dialog\", a focus scope, and\n aria-labelledby pointing at the trigger. The wrapper was one more\n element and one more className to explain.\n */}\n {title == null ? null : (\n <h3\n data-slot=\"popover-title\"\n className=\"m-0 text-sm leading-(--leading-tight) font-semibold text-foreground\"\n >\n {title}\n </h3>\n )}\n {description == null ? null : (\n <p\n data-slot=\"popover-description\"\n className=\"m-0 mt-1 text-sm leading-(--leading-normal) text-text-secondary\"\n >\n {description}\n </p>\n )}\n {actionLabel == null ? null : (\n <Button\n variant=\"secondary\"\n size=\"sm\"\n className=\"mt-4 w-full\"\n onPress={() => {\n onAction?.();\n state?.close();\n }}\n >\n {actionLabel}\n </Button>\n )}\n {children}\n </AriaPopover>\n );\n});\n\n/**\n * Pairs a trigger with a popover and owns the open state.\n *\n * React Aria's `DialogTrigger`, renamed for the migration: it takes the\n * trigger element directly as its first child, which is what `asChild` was\n * doing on both legacy call sites.\n */\nexport { DialogTrigger as PopoverTrigger } from \"react-aria-components\";\n\nexport interface PopoverHoverTriggerProps extends Omit<PreviewTriggerProps, \"delay\" | \"closeDelay\"> {\n /** The trigger, then the `Popover`. The trigger must be focusable (a `Button` or `Link`). */\n children: ReactNode;\n /**\n * Milliseconds the pointer rests on the trigger, or keyboard focus sits on\n * it, before the card opens.\n * @default 600\n */\n delay?: number;\n /**\n * Milliseconds after the pointer leaves the trigger and the card, or focus\n * leaves both, before it closes. Moving onto the card keeps it open.\n * @default 200\n */\n closeDelay?: number;\n}\n\n/**\n * Opens a popover on hover instead of press: a hover card.\n *\n * React Aria's `PreviewTrigger`. The card opens after `delay` when the pointer\n * rests on the trigger or keyboard focus reaches it, and on a long press on\n * touch. It stays open while the pointer travels onto it (a safe area covers\n * the gap) and closes `closeDelay` after the pointer or focus leaves both.\n * Escape closes it; Tab from the trigger moves into it. It is non-modal: no\n * focus trap and no underlay, and the page behind stays usable.\n *\n * The trigger keeps its own press action, so a Link still navigates and a\n * Button still runs `onPress`. Content anyone needs must not live only in the\n * card: touch without a long press never sees it.\n *\n * @example\n * <PopoverHoverTrigger delay={300}>\n * <Button variant=\"ghost\" aria-label=\"3 more sources\">+3</Button>\n * <Popover placement=\"bottom start\">...</Popover>\n * </PopoverHoverTrigger>\n */\nexport function PopoverHoverTrigger({\n delay = 600,\n closeDelay = 200,\n ...props\n}: PopoverHoverTriggerProps): JSX.Element {\n return <PreviewTrigger {...props} delay={delay} closeDelay={closeDelay} />;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmCA,MAAa,kBAAkB,IAAI;CAEjC;CACA;CAIA;CAGA;AACF,CAAC;AA8CD,MAAaA,YAAU,WAAsC,SAASA,UACpE,EACE,UACA,WACA,YAAY,UACZ,SAAS,GACT,WAAW,QACX,OACA,aACA,aACA,UACA,GAAG,SAEL,KACA;CAEA,MAAM,QAAQ,WAAW,0BAA0B;CACnD,OACE,qBAACC,SAAD;EACO;EACL,GAAI;EACO;EACH;EACR,aAAU;EACV,WAAW,MAAM,gBAAgB,GAAG,aAAa,UAAU,kBAAkB,SAAS;EANxF,UAAA;GAgBG,SAAS,OAAO,OACf,oBAAC,MAAD;IACE,aAAU;IACV,WAAU;IAET,UAAA;GACC,CAAA;GAEL,eAAe,OAAO,OACrB,oBAAC,KAAD;IACE,aAAU;IACV,WAAU;IAET,UAAA;GACA,CAAA;GAEJ,eAAe,OAAO,OACrB,oBAACC,UAAD;IACE,SAAQ;IACR,MAAK;IACL,WAAU;IACV,eAAe;KACb,WAAW;KACX,OAAO,MAAM;IACf;IAEC,UAAA;GACK,CAAA;GAET;EACU;;AAEjB,CAAC;;;;;;;;;;;;;;;;;;;;;AAgDD,SAAgB,oBAAoB,EAClC,QAAQ,KACR,aAAa,KACb,GAAG,SACqC;CACxC,OAAO,oBAAC,gBAAD;EAAgB,GAAI;EAAc;EAAmB;CAAa,CAAA;AAC3E"}
|
|
@@ -32,10 +32,12 @@ import { ProgressBarProps } from "react-aria-components";
|
|
|
32
32
|
*
|
|
33
33
|
* Both are in the build-failing contrast contract.
|
|
34
34
|
*/
|
|
35
|
-
export declare const progressVariants: (props?:
|
|
35
|
+
export declare const progressVariants: (props?: ({
|
|
36
|
+
headerEmpty?: boolean | null | undefined;
|
|
37
|
+
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
36
38
|
export declare const progressTrackVariants: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
|
|
37
39
|
export declare const progressFillVariants: (props?: ({
|
|
38
|
-
tone?: "
|
|
40
|
+
tone?: "success" | "warning" | "danger" | "neutral" | "info" | null | undefined;
|
|
39
41
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
40
42
|
/**
|
|
41
43
|
* A determinate or indeterminate progress bar.
|
|
@@ -70,6 +72,18 @@ export interface ProgressProps extends Omit<ProgressBarProps, "className" | "sty
|
|
|
70
72
|
* announced its value to assistive tech and showed a sighted reader nothing.
|
|
71
73
|
*/
|
|
72
74
|
showValue?: boolean;
|
|
75
|
+
/**
|
|
76
|
+
* Mark a full bar as done: a default-toned fill eases to the success
|
|
77
|
+
* colour at 100% and, with `showValue`, a check lands beside the count.
|
|
78
|
+
*
|
|
79
|
+
* Pass `false` for a bar that can reach 100% before the work is finished
|
|
80
|
+
* -- a timer-driven estimate -- so it does not say "done" early. The fill
|
|
81
|
+
* keeps its tone and no check is shown. An explicit `tone` is never
|
|
82
|
+
* overridden either way.
|
|
83
|
+
*
|
|
84
|
+
* @default true
|
|
85
|
+
*/
|
|
86
|
+
showCompletion?: boolean;
|
|
73
87
|
/** Extra classes for the track. */
|
|
74
88
|
trackClassName?: string;
|
|
75
89
|
/** Extra classes for the fill -- a per-call-site colour the tone axis cannot express. */
|
|
@@ -37,7 +37,13 @@ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
|
37
37
|
*
|
|
38
38
|
* Both are in the build-failing contrast contract.
|
|
39
39
|
*/
|
|
40
|
-
const progressVariants = cva(["flex w-full flex-col
|
|
40
|
+
const progressVariants = cva(["flex w-full flex-col box-border font-sans"], {
|
|
41
|
+
variants: { headerEmpty: {
|
|
42
|
+
true: "gap-0",
|
|
43
|
+
false: "gap-1.5"
|
|
44
|
+
} },
|
|
45
|
+
defaultVariants: { headerEmpty: false }
|
|
46
|
+
});
|
|
41
47
|
const progressTrackVariants = cva(["relative h-2 w-full overflow-hidden rounded-pill box-border", "bg-muted border-(length:--border-width) border-border"]);
|
|
42
48
|
const progressFillVariants = cva(["h-full rounded-pill", "progress-fill"], {
|
|
43
49
|
variants: { tone: {
|
|
@@ -54,10 +60,10 @@ const progressFillVariants = cva(["h-full rounded-pill", "progress-fill"], {
|
|
|
54
60
|
* fill follows, and a check lands beside the final count once the bar is full.
|
|
55
61
|
* Any other text (a price, a step count) is shown as it is.
|
|
56
62
|
*/
|
|
57
|
-
function ProgressValue({ percentage, valueText }) {
|
|
63
|
+
function ProgressValue({ percentage, valueText, showCompletion }) {
|
|
58
64
|
const percent = Math.round(percentage);
|
|
59
65
|
const counted = useCountTo(percent);
|
|
60
|
-
const done = useAfter(percent >= 100, 240);
|
|
66
|
+
const done = useAfter(showCompletion && percent >= 100, 240);
|
|
61
67
|
const isPercent = valueText === `${percent}%`;
|
|
62
68
|
return /* @__PURE__ */ jsxs(Fragment$1, { children: [isPercent ? `${counted}%` : valueText, isPercent ? /* @__PURE__ */ jsx("span", {
|
|
63
69
|
"data-slot": "progress-done",
|
|
@@ -80,12 +86,12 @@ function ProgressValue({ percentage, valueText }) {
|
|
|
80
86
|
})
|
|
81
87
|
}) : null] });
|
|
82
88
|
}
|
|
83
|
-
const Progress = forwardRef(function Progress({ label, labelHidden, showValue, tone, trackClassName, fillClassName, className, ...props }, ref) {
|
|
89
|
+
const Progress = forwardRef(function Progress({ label, labelHidden, showValue, showCompletion = true, tone, trackClassName, fillClassName, className, ...props }, ref) {
|
|
84
90
|
return /* @__PURE__ */ jsx(ProgressBar, {
|
|
85
91
|
ref,
|
|
86
92
|
...props,
|
|
87
93
|
"data-slot": "progress",
|
|
88
|
-
className: cn(progressVariants(), className),
|
|
94
|
+
className: cn(progressVariants({ headerEmpty: Boolean(labelHidden && !showValue) }), className),
|
|
89
95
|
children: ({ percentage, isIndeterminate, valueText }) => /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("div", {
|
|
90
96
|
"data-slot": "progress-header",
|
|
91
97
|
className: cnAll(showValue && "flex items-baseline justify-between gap-2"),
|
|
@@ -103,7 +109,8 @@ const Progress = forwardRef(function Progress({ label, labelHidden, showValue, t
|
|
|
103
109
|
className: "text-sm leading-(--leading-normal) text-muted-foreground tabular-nums",
|
|
104
110
|
children: /* @__PURE__ */ jsx(ProgressValue, {
|
|
105
111
|
percentage: percentage ?? 0,
|
|
106
|
-
valueText
|
|
112
|
+
valueText,
|
|
113
|
+
showCompletion
|
|
107
114
|
})
|
|
108
115
|
}) : null]
|
|
109
116
|
}), /* @__PURE__ */ jsx("div", {
|
|
@@ -112,9 +119,9 @@ const Progress = forwardRef(function Progress({ label, labelHidden, showValue, t
|
|
|
112
119
|
children: /* @__PURE__ */ jsx("div", {
|
|
113
120
|
"data-slot": "progress-fill",
|
|
114
121
|
"data-indeterminate": isIndeterminate || void 0,
|
|
115
|
-
"data-complete": (!tone || tone === "neutral") && !isIndeterminate && (percentage ?? 0) >= 100 ? "" : void 0,
|
|
122
|
+
"data-complete": showCompletion && (!tone || tone === "neutral") && !isIndeterminate && (percentage ?? 0) >= 100 ? "" : void 0,
|
|
116
123
|
className: cn(progressFillVariants({ tone }), fillClassName),
|
|
117
|
-
style:
|
|
124
|
+
style: isIndeterminate ? void 0 : { width: `${percentage ?? 0}%` }
|
|
118
125
|
})
|
|
119
126
|
})] })
|
|
120
127
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"progress.js","names":["AriaProgressBar","AriaLabel"],"sources":["../../src/progress/progress.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { forwardRef, type ReactNode } from \"react\";\nimport {\n Label as AriaLabel,\n ProgressBar as AriaProgressBar,\n type ProgressBarProps as AriaProgressBarProps,\n} from \"react-aria-components\";\n\nimport { cn, cnAll } from \"../lib/cn.js\";\nimport { useAfter, useCountTo } from \"../lib/count-to.js\";\nimport { SwapText } from \"../lib/swap-text.js\";\n\n/**\n * Progress, ported from the legacy shadcn/Radix component.\n *\n * Census: seven files import it and only TWO render anything --\n * `ResponseOption.tsx` and `LiveQA.tsx`. Both pass `value` and\n * `className` and nothing else.\n *\n * `label` IS REQUIRED, and that is the point. Neither live call site\n * passes `aria-label`, `aria-labelledby` or an `id` a label targets, and\n * `role=\"progressbar\"` does not take its name from content -- so both\n * rendered progress bars are announced as an unnamed progressbar. React\n * Aria supplies the plumbing for three naming routes but no name of its\n * own: `<ProgressBar value={60}/>` is just as nameless there. Making the\n * prop required is what turns it into something you cannot forget.\n *\n * The migration plan said this component \"adds the missing\n * aria-valuenow\". That is wrong -- Radix already supplies `aria-valuenow`,\n * `aria-valuemin`, `aria-valuemax` and `aria-valuetext`. The missing thing\n * was always the NAME.\n *\n * THE COLOURS ARE NOT THE LEGACY'S. Its track is `bg-primary/20`, an alpha\n * fill that composites against unknown page content and therefore has no\n * fixed contrast pair at all -- over white it is roughly 1.05:1, an\n * invisible boundary. Its indicator is `bg-primary`, 2.44:1 on the page.\n * Here the track is `muted` with a `border` boundary and the fill is\n * `primary-strong`, which puts the fill against the track at 4.61:1 and\n * the track against the page at 5.25:1.\n *\n * Both are in the build-failing contrast contract.\n */\nexport const progressVariants = cva([\"flex w-full flex-col gap-1.5 box-border font-sans\"]);\n\nexport const progressTrackVariants = cva([\n \"relative h-2 w-full overflow-hidden rounded-pill box-border\",\n // muted, NOT `border` as the Switch uses. A switch has no fill; a\n // progress bar does, and primary-strong on a `border` track is 1.01:1 --\n // the two halves are the same grey and the value disappears.\n \"bg-muted border-(length:--border-width) border-border\",\n]);\n\nexport const progressFillVariants = cva(\n [\n \"h-full rounded-pill\",\n // The fill follows the value on the smooth spring and turns success-coloured when it completes (see progress-fill).\n \"progress-fill\",\n ],\n {\n variants: {\n /*\n * What the value MEANS, which for a progress bar is usually a score\n * rather than a completion. The product's bar carried exactly this axis\n * (`default`/`success`/`warning`/`error`/`info`) and reached for it to\n * colour performance metrics.\n *\n * `-strong` throughout: the fill sits on the `muted` track and has to\n * clear the 3:1 non-text floor of WCAG 1.4.11 against it, which is the\n * same reason the track is muted rather than `border` -- primary-strong\n * on a `border` track measures 1.01:1 and the value disappears.\n */\n tone: {\n neutral: \"bg-primary-strong\",\n success: \"bg-success-strong\",\n warning: \"bg-warning-strong\",\n danger: \"bg-destructive-strong\",\n info: \"bg-info-strong\",\n },\n },\n defaultVariants: { tone: \"neutral\" },\n },\n);\n\n/**\n * A determinate or indeterminate progress bar.\n *\n * @example\n * <Progress label=\"Upload\" value={62} />\n * <Progress label=\"Loading results\" />\n */\nexport interface ProgressProps\n extends Omit<AriaProgressBarProps, \"className\" | \"style\" | \"children\">,\n VariantProps<typeof progressFillVariants> {\n /**\n * What is progressing, and the bar's accessible name.\n *\n * **Required.** Both legacy call sites render an unnamed progressbar.\n * Pass `labelHidden` when the surrounding UI already says it.\n */\n label: ReactNode;\n /**\n * Render the label for screen readers only.\n *\n * Both legacy call sites sit beside visible text that was never\n * associated with the bar -- this is how that text becomes the name\n * without printing it twice.\n */\n labelHidden?: boolean;\n /**\n * Print the value beside the label.\n *\n * The text itself comes from React Aria, which already formats it from\n * `value`/`minValue`/`maxValue` and honours `formatOptions` and\n * `valueLabel` -- so a percentage, a currency or \"3 of 7\" are all reachable\n * without a second prop here. It was never RENDERED before: the bar\n * announced its value to assistive tech and showed a sighted reader nothing.\n */\n showValue?: boolean;\n /** Extra classes for the track. */\n trackClassName?: string;\n /** Extra classes for the fill -- a per-call-site colour the tone axis cannot express. */\n fillClassName?: string;\n /** Extra classes. Merged last, so they win conflicts with ours. */\n className?: string;\n}\n\n/**\n * The printed value. A percentage counts to its new number on the same spring the\n * fill follows, and a check lands beside the final count once the bar is full.\n * Any other text (a price, a step count) is shown as it is.\n */\nfunction ProgressValue({ percentage, valueText }: { percentage: number; valueText: string | undefined }) {\n const percent = Math.round(percentage);\n const counted = useCountTo(percent);\n const done = useAfter(percent >= 100, 240);\n const isPercent = valueText === `${percent}%`;\n return (\n <>\n {isPercent ? `${counted}%` : valueText}\n {isPercent ? (\n <span data-slot=\"progress-done\" className=\"ms-1 inline-flex align-middle text-success-strong\">\n <SwapText swapKey={done ? \"done\" : \"\"} motion=\"glyph\" className=\"inline-flex\">\n <svg\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className=\"size-3.5\"\n aria-hidden=\"true\"\n >\n <path d=\"m3.5 8.5 3 3 6-6\" />\n </svg>\n </SwapText>\n </span>\n ) : null}\n </>\n );\n}\n\nexport const Progress = forwardRef<HTMLDivElement, ProgressProps>(function Progress(\n { label, labelHidden, showValue, tone, trackClassName, fillClassName, className, ...props },\n ref,\n) {\n return (\n <AriaProgressBar\n ref={ref}\n {...props}\n data-slot=\"progress\"\n className={cn(progressVariants(), className)}\n >\n {({ percentage, isIndeterminate, valueText }) => (\n <>\n {/*\n The row exists only when the value is printed. Wrapping the label\n in a flex row unconditionally would change the label's box for\n every existing consumer to no purpose.\n */}\n <div\n data-slot=\"progress-header\"\n className={cnAll(showValue && \"flex items-baseline justify-between gap-2\")}\n >\n <AriaLabel\n data-slot=\"progress-label\"\n className={cnAll(\n \"text-sm leading-(--leading-normal) text-foreground\",\n labelHidden && \"sr-only\",\n )}\n >\n {typeof label === \"string\" ? (\n <SwapText swapKey={label} className=\"block\">\n {label}\n </SwapText>\n ) : (\n label\n )}\n </AriaLabel>\n {showValue ? (\n <span\n data-slot=\"progress-value\"\n // aria-hidden: React Aria already announces the value on the\n // progressbar itself. Without this a screen reader reads it\n // twice, once as the widget's value and once as loose text.\n aria-hidden=\"true\"\n className=\"text-sm leading-(--leading-normal) text-muted-foreground tabular-nums\"\n >\n <ProgressValue percentage={percentage ?? 0} valueText={valueText} />\n </span>\n ) : null}\n </div>\n <div\n data-slot=\"progress-track\"\n className={cn(progressTrackVariants(), trackClassName)}\n >\n {/*\n width, not the legacy translateX(-(100 - value)%). That\n overflowed for value > 100 -- a positive translate slid the\n indicator off to the right, while the announced value stayed\n whatever was passed. React Aria clamps the value, so the two\n can no longer disagree.\n\n An indeterminate bar renders no fill rather than the\n legacy's silent 0%: `value={undefined}` became `(value || 0)`\n there, so a sighted user saw \"0% done\" while a screen reader\n said \"indeterminate\".\n */}\n <div\n data-slot=\"progress-fill\"\n data-indeterminate={isIndeterminate || undefined}\n // A default-toned bar turns success-coloured when it completes; an explicit tone is never overridden.\n data-complete={(!tone || tone === \"neutral\") && !isIndeterminate && (percentage ?? 0) >= 100 ? \"\" : undefined}\n className={cn(progressFillVariants({ tone }), fillClassName)}\n style={{ width: isIndeterminate ? \"0%\" : `${percentage ?? 0}%` }}\n />\n </div>\n </>\n )}\n </AriaProgressBar>\n );\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4CA,MAAa,mBAAmB,IAAI,CAAC,mDAAmD,CAAC;AAEzF,MAAa,wBAAwB,IAAI,CACvC,+DAIA,uDACF,CAAC;AAED,MAAa,uBAAuB,IAClC,CACE,uBAEA,eACF,GACA;CACE,UAAU,EAYR,MAAM;EACJ,SAAS;EACT,SAAS;EACT,SAAS;EACT,QAAQ;EACR,MAAM;CACR,EACF;CACA,iBAAiB,EAAE,MAAM,UAAU;AACrC,CACF;;;;;;AAkDA,SAAS,cAAc,EAAE,YAAY,aAAoE;CACvG,MAAM,UAAU,KAAK,MAAM,UAAU;CACrC,MAAM,UAAU,WAAW,OAAO;CAClC,MAAM,OAAO,SAAS,WAAW,KAAK,GAAG;CACzC,MAAM,YAAY,cAAc,GAAG,QAAQ;CAC3C,OACE,qBAAA,YAAA,EAAA,UAAA,CACG,YAAY,GAAG,QAAQ,KAAK,WAC5B,YACC,oBAAC,QAAD;EAAM,aAAU;EAAgB,WAAU;EACxC,UAAA,oBAAC,UAAD;GAAU,SAAS,OAAO,SAAS;GAAI,QAAO;GAAQ,WAAU;GAC9D,UAAA,oBAAC,OAAD;IACE,SAAQ;IACR,MAAK;IACL,QAAO;IACP,aAAY;IACZ,eAAc;IACd,gBAAe;IACf,WAAU;IACV,eAAY;IAEZ,UAAA,oBAAC,QAAD,EAAM,GAAE,mBAAoB,CAAA;GACzB,CAAA;EACG,CAAA;CACN,CAAA,IACJ,IACJ,EAAA,CAAA;AAEN;AAEA,MAAa,WAAW,WAA0C,SAAS,SACzE,EAAE,OAAO,aAAa,WAAW,MAAM,gBAAgB,eAAe,WAAW,GAAG,SACpF,KACA;CACA,OACE,oBAACA,aAAD;EACO;EACL,GAAI;EACJ,aAAU;EACV,WAAW,GAAG,iBAAiB,GAAG,SAAS;EAEzC,WAAA,EAAE,YAAY,iBAAiB,gBAC/B,qBAAA,YAAA,EAAA,UAAA,CAME,qBAAC,OAAD;GACE,aAAU;GACV,WAAW,MAAM,aAAa,2CAA2C;GAF3E,UAAA,CAIE,oBAACC,OAAD;IACE,aAAU;IACV,WAAW,MACT,sDACA,eAAe,SACjB;IAEC,UAAA,OAAO,UAAU,WAChB,oBAAC,UAAD;KAAU,SAAS;KAAO,WAAU;KACjC,UAAA;IACO,CAAA,IAEV;GAEO,CAAA,GACV,YACC,oBAAC,QAAD;IACE,aAAU;IAIV,eAAY;IACZ,WAAU;IAEV,UAAA,oBAAC,eAAD;KAAe,YAAY,cAAc;KAAc;IAAY,CAAA;GAC/D,CAAA,IACJ,IACD;EACL,CAAA,GAAA,oBAAC,OAAD;GACE,aAAU;GACV,WAAW,GAAG,sBAAsB,GAAG,cAAc;GAcrD,UAAA,oBAAC,OAAD;IACE,aAAU;IACV,sBAAoB,mBAAmB,KAAA;IAEvC,kBAAgB,CAAC,QAAQ,SAAS,cAAc,CAAC,oBAAoB,cAAc,MAAM,MAAM,KAAK,KAAA;IACpG,WAAW,GAAG,qBAAqB,EAAE,KAAK,CAAC,GAAG,aAAa;IAC3D,OAAO,EAAE,OAAO,kBAAkB,OAAO,GAAG,cAAc,EAAE,GAAG;GAChE,CAAA;EACE,CAAA,CACL,EAAA,CAAA;CAEW,CAAA;AAErB,CAAC"}
|
|
1
|
+
{"version":3,"file":"progress.js","names":["AriaProgressBar","AriaLabel"],"sources":["../../src/progress/progress.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { forwardRef, type ReactNode } from \"react\";\nimport {\n Label as AriaLabel,\n ProgressBar as AriaProgressBar,\n type ProgressBarProps as AriaProgressBarProps,\n} from \"react-aria-components\";\n\nimport { cn, cnAll } from \"../lib/cn.js\";\nimport { useAfter, useCountTo } from \"../lib/count-to.js\";\nimport { SwapText } from \"../lib/swap-text.js\";\n\n/**\n * Progress, ported from the legacy shadcn/Radix component.\n *\n * Census: seven files import it and only TWO render anything --\n * `ResponseOption.tsx` and `LiveQA.tsx`. Both pass `value` and\n * `className` and nothing else.\n *\n * `label` IS REQUIRED, and that is the point. Neither live call site\n * passes `aria-label`, `aria-labelledby` or an `id` a label targets, and\n * `role=\"progressbar\"` does not take its name from content -- so both\n * rendered progress bars are announced as an unnamed progressbar. React\n * Aria supplies the plumbing for three naming routes but no name of its\n * own: `<ProgressBar value={60}/>` is just as nameless there. Making the\n * prop required is what turns it into something you cannot forget.\n *\n * The migration plan said this component \"adds the missing\n * aria-valuenow\". That is wrong -- Radix already supplies `aria-valuenow`,\n * `aria-valuemin`, `aria-valuemax` and `aria-valuetext`. The missing thing\n * was always the NAME.\n *\n * THE COLOURS ARE NOT THE LEGACY'S. Its track is `bg-primary/20`, an alpha\n * fill that composites against unknown page content and therefore has no\n * fixed contrast pair at all -- over white it is roughly 1.05:1, an\n * invisible boundary. Its indicator is `bg-primary`, 2.44:1 on the page.\n * Here the track is `muted` with a `border` boundary and the fill is\n * `primary-strong`, which puts the fill against the track at 4.61:1 and\n * the track against the page at 5.25:1.\n *\n * Both are in the build-failing contrast contract.\n */\nexport const progressVariants = cva([\"flex w-full flex-col box-border font-sans\"], {\n variants: {\n /*\n * True when nothing is printed above the track (`labelHidden` and no\n * `showValue`). The header is then an empty row -- its sr-only label is\n * out of flow -- and the column gap would leave 6px of nothing above the\n * bar. The gap lives here and not in the base, so the raw\n * `progressVariants()` output never carries both `gap-1.5` and `gap-0`.\n */\n headerEmpty: { true: \"gap-0\", false: \"gap-1.5\" },\n },\n defaultVariants: { headerEmpty: false },\n});\n\nexport const progressTrackVariants = cva([\n \"relative h-2 w-full overflow-hidden rounded-pill box-border\",\n // muted, NOT `border` as the Switch uses. A switch has no fill; a\n // progress bar does, and primary-strong on a `border` track is 1.01:1 --\n // the two halves are the same grey and the value disappears.\n \"bg-muted border-(length:--border-width) border-border\",\n]);\n\nexport const progressFillVariants = cva(\n [\n \"h-full rounded-pill\",\n // The fill follows the value on the smooth spring and turns success-coloured when it completes; indeterminate, it sweeps (see progress-fill).\n \"progress-fill\",\n ],\n {\n variants: {\n /*\n * What the value MEANS, which for a progress bar is usually a score\n * rather than a completion. The product's bar carried exactly this axis\n * (`default`/`success`/`warning`/`error`/`info`) and reached for it to\n * colour performance metrics.\n *\n * `-strong` throughout: the fill sits on the `muted` track and has to\n * clear the 3:1 non-text floor of WCAG 1.4.11 against it, which is the\n * same reason the track is muted rather than `border` -- primary-strong\n * on a `border` track measures 1.01:1 and the value disappears.\n */\n tone: {\n neutral: \"bg-primary-strong\",\n success: \"bg-success-strong\",\n warning: \"bg-warning-strong\",\n danger: \"bg-destructive-strong\",\n info: \"bg-info-strong\",\n },\n },\n defaultVariants: { tone: \"neutral\" },\n },\n);\n\n/**\n * A determinate or indeterminate progress bar.\n *\n * @example\n * <Progress label=\"Upload\" value={62} />\n * <Progress label=\"Loading results\" />\n */\nexport interface ProgressProps\n extends Omit<AriaProgressBarProps, \"className\" | \"style\" | \"children\">,\n VariantProps<typeof progressFillVariants> {\n /**\n * What is progressing, and the bar's accessible name.\n *\n * **Required.** Both legacy call sites render an unnamed progressbar.\n * Pass `labelHidden` when the surrounding UI already says it.\n */\n label: ReactNode;\n /**\n * Render the label for screen readers only.\n *\n * Both legacy call sites sit beside visible text that was never\n * associated with the bar -- this is how that text becomes the name\n * without printing it twice.\n */\n labelHidden?: boolean;\n /**\n * Print the value beside the label.\n *\n * The text itself comes from React Aria, which already formats it from\n * `value`/`minValue`/`maxValue` and honours `formatOptions` and\n * `valueLabel` -- so a percentage, a currency or \"3 of 7\" are all reachable\n * without a second prop here. It was never RENDERED before: the bar\n * announced its value to assistive tech and showed a sighted reader nothing.\n */\n showValue?: boolean;\n /**\n * Mark a full bar as done: a default-toned fill eases to the success\n * colour at 100% and, with `showValue`, a check lands beside the count.\n *\n * Pass `false` for a bar that can reach 100% before the work is finished\n * -- a timer-driven estimate -- so it does not say \"done\" early. The fill\n * keeps its tone and no check is shown. An explicit `tone` is never\n * overridden either way.\n *\n * @default true\n */\n showCompletion?: boolean;\n /** Extra classes for the track. */\n trackClassName?: string;\n /** Extra classes for the fill -- a per-call-site colour the tone axis cannot express. */\n fillClassName?: string;\n /** Extra classes. Merged last, so they win conflicts with ours. */\n className?: string;\n}\n\n/**\n * The printed value. A percentage counts to its new number on the same spring the\n * fill follows, and a check lands beside the final count once the bar is full.\n * Any other text (a price, a step count) is shown as it is.\n */\nfunction ProgressValue({\n percentage,\n valueText,\n showCompletion,\n}: {\n percentage: number;\n valueText: string | undefined;\n showCompletion: boolean;\n}) {\n const percent = Math.round(percentage);\n const counted = useCountTo(percent);\n const done = useAfter(showCompletion && percent >= 100, 240);\n const isPercent = valueText === `${percent}%`;\n return (\n <>\n {isPercent ? `${counted}%` : valueText}\n {isPercent ? (\n <span data-slot=\"progress-done\" className=\"ms-1 inline-flex align-middle text-success-strong\">\n <SwapText swapKey={done ? \"done\" : \"\"} motion=\"glyph\" className=\"inline-flex\">\n <svg\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className=\"size-3.5\"\n aria-hidden=\"true\"\n >\n <path d=\"m3.5 8.5 3 3 6-6\" />\n </svg>\n </SwapText>\n </span>\n ) : null}\n </>\n );\n}\n\nexport const Progress = forwardRef<HTMLDivElement, ProgressProps>(function Progress(\n {\n label,\n labelHidden,\n showValue,\n showCompletion = true,\n tone,\n trackClassName,\n fillClassName,\n className,\n ...props\n },\n ref,\n) {\n return (\n <AriaProgressBar\n ref={ref}\n {...props}\n data-slot=\"progress\"\n className={cn(progressVariants({ headerEmpty: Boolean(labelHidden && !showValue) }), className)}\n >\n {({ percentage, isIndeterminate, valueText }) => (\n <>\n {/*\n The row exists only when the value is printed. Wrapping the label\n in a flex row unconditionally would change the label's box for\n every existing consumer to no purpose.\n */}\n <div\n data-slot=\"progress-header\"\n className={cnAll(showValue && \"flex items-baseline justify-between gap-2\")}\n >\n <AriaLabel\n data-slot=\"progress-label\"\n className={cnAll(\n \"text-sm leading-(--leading-normal) text-foreground\",\n labelHidden && \"sr-only\",\n )}\n >\n {typeof label === \"string\" ? (\n <SwapText swapKey={label} className=\"block\">\n {label}\n </SwapText>\n ) : (\n label\n )}\n </AriaLabel>\n {showValue ? (\n <span\n data-slot=\"progress-value\"\n // aria-hidden: React Aria already announces the value on the\n // progressbar itself. Without this a screen reader reads it\n // twice, once as the widget's value and once as loose text.\n aria-hidden=\"true\"\n className=\"text-sm leading-(--leading-normal) text-muted-foreground tabular-nums\"\n >\n <ProgressValue\n percentage={percentage ?? 0}\n valueText={valueText}\n showCompletion={showCompletion}\n />\n </span>\n ) : null}\n </div>\n <div\n data-slot=\"progress-track\"\n className={cn(progressTrackVariants(), trackClassName)}\n >\n {/*\n width, not the legacy translateX(-(100 - value)%). That\n overflowed for value > 100 -- a positive translate slid the\n indicator off to the right, while the announced value stayed\n whatever was passed. React Aria clamps the value, so the two\n can no longer disagree.\n\n An indeterminate bar is not the legacy's silent 0%:\n `value={undefined}` became `(value || 0)` there, so a sighted\n user saw \"0% done\" while a screen reader said \"indeterminate\".\n Its fill is a segment that sweeps the track on a loop (a slow\n pulse of the whole track under reduced motion), sized and moved\n by progress-fill, so it carries no inline width.\n */}\n <div\n data-slot=\"progress-fill\"\n data-indeterminate={isIndeterminate || undefined}\n // A default-toned bar turns success-coloured when it completes, unless showCompletion is off; an explicit tone is never overridden.\n data-complete={\n showCompletion && (!tone || tone === \"neutral\") && !isIndeterminate && (percentage ?? 0) >= 100\n ? \"\"\n : undefined\n }\n className={cn(progressFillVariants({ tone }), fillClassName)}\n style={isIndeterminate ? undefined : { width: `${percentage ?? 0}%` }}\n />\n </div>\n </>\n )}\n </AriaProgressBar>\n );\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4CA,MAAa,mBAAmB,IAAI,CAAC,2CAA2C,GAAG;CACjF,UAAU,EAQR,aAAa;EAAE,MAAM;EAAS,OAAO;CAAU,EACjD;CACA,iBAAiB,EAAE,aAAa,MAAM;AACxC,CAAC;AAED,MAAa,wBAAwB,IAAI,CACvC,+DAIA,uDACF,CAAC;AAED,MAAa,uBAAuB,IAClC,CACE,uBAEA,eACF,GACA;CACE,UAAU,EAYR,MAAM;EACJ,SAAS;EACT,SAAS;EACT,SAAS;EACT,QAAQ;EACR,MAAM;CACR,EACF;CACA,iBAAiB,EAAE,MAAM,UAAU;AACrC,CACF;;;;;;AA8DA,SAAS,cAAc,EACrB,YACA,WACA,kBAKC;CACD,MAAM,UAAU,KAAK,MAAM,UAAU;CACrC,MAAM,UAAU,WAAW,OAAO;CAClC,MAAM,OAAO,SAAS,kBAAkB,WAAW,KAAK,GAAG;CAC3D,MAAM,YAAY,cAAc,GAAG,QAAQ;CAC3C,OACE,qBAAA,YAAA,EAAA,UAAA,CACG,YAAY,GAAG,QAAQ,KAAK,WAC5B,YACC,oBAAC,QAAD;EAAM,aAAU;EAAgB,WAAU;EACxC,UAAA,oBAAC,UAAD;GAAU,SAAS,OAAO,SAAS;GAAI,QAAO;GAAQ,WAAU;GAC9D,UAAA,oBAAC,OAAD;IACE,SAAQ;IACR,MAAK;IACL,QAAO;IACP,aAAY;IACZ,eAAc;IACd,gBAAe;IACf,WAAU;IACV,eAAY;IAEZ,UAAA,oBAAC,QAAD,EAAM,GAAE,mBAAoB,CAAA;GACzB,CAAA;EACG,CAAA;CACN,CAAA,IACJ,IACJ,EAAA,CAAA;AAEN;AAEA,MAAa,WAAW,WAA0C,SAAS,SACzE,EACE,OACA,aACA,WACA,iBAAiB,MACjB,MACA,gBACA,eACA,WACA,GAAG,SAEL,KACA;CACA,OACE,oBAACA,aAAD;EACO;EACL,GAAI;EACJ,aAAU;EACV,WAAW,GAAG,iBAAiB,EAAE,aAAa,QAAQ,eAAe,CAAC,SAAS,EAAE,CAAC,GAAG,SAAS;EAE5F,WAAA,EAAE,YAAY,iBAAiB,gBAC/B,qBAAA,YAAA,EAAA,UAAA,CAME,qBAAC,OAAD;GACE,aAAU;GACV,WAAW,MAAM,aAAa,2CAA2C;GAF3E,UAAA,CAIE,oBAACC,OAAD;IACE,aAAU;IACV,WAAW,MACT,sDACA,eAAe,SACjB;IAEC,UAAA,OAAO,UAAU,WAChB,oBAAC,UAAD;KAAU,SAAS;KAAO,WAAU;KACjC,UAAA;IACO,CAAA,IAEV;GAEO,CAAA,GACV,YACC,oBAAC,QAAD;IACE,aAAU;IAIV,eAAY;IACZ,WAAU;IAEV,UAAA,oBAAC,eAAD;KACE,YAAY,cAAc;KACf;KACK;IACjB,CAAA;GACG,CAAA,IACJ,IACD;EACL,CAAA,GAAA,oBAAC,OAAD;GACE,aAAU;GACV,WAAW,GAAG,sBAAsB,GAAG,cAAc;GAgBrD,UAAA,oBAAC,OAAD;IACE,aAAU;IACV,sBAAoB,mBAAmB,KAAA;IAEvC,iBACE,mBAAmB,CAAC,QAAQ,SAAS,cAAc,CAAC,oBAAoB,cAAc,MAAM,MACxF,KACA,KAAA;IAEN,WAAW,GAAG,qBAAqB,EAAE,KAAK,CAAC,GAAG,aAAa;IAC3D,OAAO,kBAAkB,KAAA,IAAY,EAAE,OAAO,GAAG,cAAc,EAAE,GAAG;GACrE,CAAA;EACE,CAAA,CACL,EAAA,CAAA;CAEW,CAAA;AAErB,CAAC"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { Radio, RadioGroup, RadioGroupProps, RadioProps, radioGroupVariants, radioVariants } from "./radio-group.js";
|
|
2
|
-
export { Radio, RadioGroup, type RadioGroupProps, type RadioProps, radioGroupVariants, radioVariants };
|
|
1
|
+
import { Radio, RadioGroup, RadioGroupProps, RadioGroupVariant, RadioProps, radioCardVariants, radioGroupVariants, radioVariants } from "./radio-group.js";
|
|
2
|
+
export { Radio, RadioGroup, type RadioGroupProps, type RadioGroupVariant, type RadioProps, radioCardVariants, radioGroupVariants, radioVariants };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { Radio, RadioGroup, radioGroupVariants, radioVariants } from "./radio-group.js";
|
|
2
|
-
export { Radio, RadioGroup, radioGroupVariants, radioVariants };
|
|
1
|
+
import { Radio, RadioGroup, radioCardVariants, radioGroupVariants, radioVariants } from "./radio-group.js";
|
|
2
|
+
export { Radio, RadioGroup, radioCardVariants, radioGroupVariants, radioVariants };
|