tempest-react-sdk 0.72.0 → 0.74.0
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 +6 -4
- package/dist/br/MapMarkers.cjs +1 -1
- package/dist/br/MapMarkers.cjs.map +1 -1
- package/dist/br/MapMarkers.js +19 -22
- package/dist/br/MapMarkers.js.map +1 -1
- package/dist/components/AIChat/AIChatTurn.cjs +1 -1
- package/dist/components/AIChat/AIChatTurn.cjs.map +1 -1
- package/dist/components/AIChat/AIChatTurn.js +1 -1
- package/dist/components/AIChat/AIChatTurn.js.map +1 -1
- package/dist/components/AppShell/AppShell.module.cjs.map +1 -1
- package/dist/components/AppShell/AppShell.module.js.map +1 -1
- package/dist/components/Button/Button.cjs +1 -1
- package/dist/components/Button/Button.cjs.map +1 -1
- package/dist/components/Button/Button.js +41 -36
- package/dist/components/Button/Button.js.map +1 -1
- package/dist/components/Button/Button.module.cjs.map +1 -1
- package/dist/components/Button/Button.module.js.map +1 -1
- package/dist/components/Button/ButtonContent.cjs +2 -0
- package/dist/components/Button/ButtonContent.cjs.map +1 -0
- package/dist/components/Button/ButtonContent.js +34 -0
- package/dist/components/Button/ButtonContent.js.map +1 -0
- package/dist/components/Button/ButtonSlot.cjs +2 -0
- package/dist/components/Button/ButtonSlot.cjs.map +1 -0
- package/dist/components/Button/ButtonSlot.js +36 -0
- package/dist/components/Button/ButtonSlot.js.map +1 -0
- package/dist/components/Button/button-class.cjs +2 -0
- package/dist/components/Button/button-class.cjs.map +1 -0
- package/dist/components/Button/button-class.js +13 -0
- package/dist/components/Button/button-class.js.map +1 -0
- package/dist/components/Button/link-rel.cjs +2 -0
- package/dist/components/Button/link-rel.cjs.map +1 -0
- package/dist/components/Button/link-rel.js +11 -0
- package/dist/components/Button/link-rel.js.map +1 -0
- package/dist/components/Button/slot.cjs +2 -0
- package/dist/components/Button/slot.cjs.map +1 -0
- package/dist/components/Button/slot.js +41 -0
- package/dist/components/Button/slot.js.map +1 -0
- package/dist/components/Collapsible/Collapsible.cjs +1 -1
- package/dist/components/Collapsible/Collapsible.cjs.map +1 -1
- package/dist/components/Collapsible/Collapsible.js +1 -1
- package/dist/components/Collapsible/Collapsible.js.map +1 -1
- package/dist/components/Form/Form.cjs.map +1 -1
- package/dist/components/Form/Form.js.map +1 -1
- package/dist/components/InstallButton/InstallButton.cjs.map +1 -1
- package/dist/components/InstallButton/InstallButton.js.map +1 -1
- package/dist/components/Modal/Modal.cjs +1 -1
- package/dist/components/Modal/Modal.cjs.map +1 -1
- package/dist/components/Modal/Modal.js +1 -1
- package/dist/components/Modal/Modal.js.map +1 -1
- package/dist/components/Modal/Modal.module.cjs +1 -1
- package/dist/components/Modal/Modal.module.cjs.map +1 -1
- package/dist/components/Modal/Modal.module.js +1 -1
- package/dist/components/Modal/Modal.module.js.map +1 -1
- package/dist/components/Page/Page.cjs +1 -1
- package/dist/components/Page/Page.cjs.map +1 -1
- package/dist/components/Page/Page.js +23 -39
- package/dist/components/Page/Page.js.map +1 -1
- package/dist/components/Page/Page.module.cjs +1 -1
- package/dist/components/Page/Page.module.cjs.map +1 -1
- package/dist/components/Page/Page.module.js +5 -11
- package/dist/components/Page/Page.module.js.map +1 -1
- package/dist/components/Reveal/Reveal.cjs +2 -0
- package/dist/components/Reveal/Reveal.cjs.map +1 -0
- package/dist/components/Reveal/Reveal.js +56 -0
- package/dist/components/Reveal/Reveal.js.map +1 -0
- package/dist/components/Reveal/Reveal.module.cjs +3 -0
- package/dist/components/Reveal/Reveal.module.cjs.map +1 -0
- package/dist/components/Reveal/Reveal.module.js +7 -0
- package/dist/components/Reveal/Reveal.module.js.map +1 -0
- package/dist/components/Reveal/reveal-threshold.cjs +2 -0
- package/dist/components/Reveal/reveal-threshold.cjs.map +1 -0
- package/dist/components/Reveal/reveal-threshold.js +13 -0
- package/dist/components/Reveal/reveal-threshold.js.map +1 -0
- package/dist/components/SectionHeader/SectionHeader.cjs +2 -0
- package/dist/components/SectionHeader/SectionHeader.cjs.map +1 -0
- package/dist/components/SectionHeader/SectionHeader.js +58 -0
- package/dist/components/SectionHeader/SectionHeader.js.map +1 -0
- package/dist/components/SectionHeader/SectionHeader.module.cjs +3 -0
- package/dist/components/SectionHeader/SectionHeader.module.cjs.map +1 -0
- package/dist/components/SectionHeader/SectionHeader.module.js +15 -0
- package/dist/components/SectionHeader/SectionHeader.module.js.map +1 -0
- package/dist/components/Timeline/Timeline.cjs +1 -1
- package/dist/components/Timeline/Timeline.cjs.map +1 -1
- package/dist/components/Timeline/Timeline.js +22 -15
- package/dist/components/Timeline/Timeline.js.map +1 -1
- package/dist/components/Timeline/Timeline.module.cjs +1 -1
- package/dist/components/Timeline/Timeline.module.cjs.map +1 -1
- package/dist/components/Timeline/Timeline.module.js +1 -1
- package/dist/components/Timeline/Timeline.module.js.map +1 -1
- package/dist/styles/Reveal.css +2 -0
- package/dist/styles/SectionHeader.css +2 -0
- package/dist/styles/actions.css +24 -22
- package/dist/styles/base.css +3 -1
- package/dist/styles/component/AppShell.css +1 -1
- package/dist/styles/component/Button.css +24 -22
- package/dist/styles/component/Modal.css +2 -2
- package/dist/styles/component/Page.css +0 -6
- package/dist/styles/component/Reveal.css +9 -0
- package/dist/styles/component/SectionHeader.css +9 -0
- package/dist/styles/component/Timeline.css +5 -5
- package/dist/styles/core.css +3 -1
- package/dist/styles/data.css +5 -5
- package/dist/styles/inverse.css +147 -0
- package/dist/styles/layout.css +9 -7
- package/dist/styles/manifest.json +14 -1
- package/dist/styles/overlay.css +2 -2
- package/dist/styles/utility.css +8 -0
- package/dist/styles.css +1 -1
- package/dist/tempest-react-sdk.cjs +1 -1
- package/dist/tempest-react-sdk.d.ts +353 -4
- package/dist/tempest-react-sdk.js +350 -346
- package/dist/theme/create-theme.cjs +3 -3
- package/dist/theme/create-theme.cjs.map +1 -1
- package/dist/theme/create-theme.js +67 -55
- package/dist/theme/create-theme.js.map +1 -1
- package/dist/theme/inverse-render.cjs +3 -0
- package/dist/theme/inverse-render.cjs.map +1 -0
- package/dist/theme/inverse-render.js +10 -0
- package/dist/theme/inverse-render.js.map +1 -0
- package/dist/theme/inverse-surface.cjs +2 -0
- package/dist/theme/inverse-surface.cjs.map +1 -0
- package/dist/theme/inverse-surface.js +258 -0
- package/dist/theme/inverse-surface.js.map +1 -0
- package/dist/theme/status-tokens.cjs +2 -0
- package/dist/theme/status-tokens.cjs.map +1 -0
- package/dist/theme/status-tokens.js +15 -0
- package/dist/theme/status-tokens.js.map +1 -0
- package/dist/vision/core/graph.cjs.map +1 -1
- package/dist/vision/core/graph.js.map +1 -1
- package/dist/vision/core/metadata.cjs +2 -2
- package/dist/vision/core/metadata.cjs.map +1 -1
- package/dist/vision/core/metadata.js +101 -29
- package/dist/vision/core/metadata.js.map +1 -1
- package/dist/vision/core/session.cjs +1 -1
- package/dist/vision/core/session.cjs.map +1 -1
- package/dist/vision/core/session.js +59 -48
- package/dist/vision/core/session.js.map +1 -1
- package/dist/vision/index.cjs +1 -1
- package/dist/vision/index.cjs.map +1 -1
- package/dist/vision/index.js +1 -1
- package/dist/vision/index.js.map +1 -1
- package/dist/vision.d.ts +25 -21
- package/dist/vite/create-vite-config.cjs +1 -1
- package/dist/vite/create-vite-config.cjs.map +1 -1
- package/dist/vite/create-vite-config.js +6 -4
- package/dist/vite/create-vite-config.js.map +1 -1
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Button.js","names":[],"sources":["../../../src/components/Button/Button.tsx"],"sourcesContent":["/**\n * @tempest-limits props-count — this is the design system's button: variant, size,\n * loading, fullWidth, iconOnly, pill, leftIcon and rightIcon are the whole\n * vocabulary every screen composes from.
|
|
1
|
+
{"version":3,"file":"Button.js","names":[],"sources":["../../../src/components/Button/Button.tsx"],"sourcesContent":["/**\n * @tempest-limits props-count — this is the design system's button: variant, size,\n * loading, fullWidth, iconOnly, pill, leftIcon and rightIcon are the whole\n * vocabulary every screen composes from, and href only picks which element carries\n * it. Nine orthogonal knobs on the most-used component is the API working — the\n * count rule is looking for two components fused together, and there is only one\n * here.\n */\nimport { forwardRef } from \"react\";\nimport type {\n AnchorHTMLAttributes,\n ButtonHTMLAttributes,\n ForwardedRef,\n HTMLAttributes,\n ReactElement,\n ReactNode,\n Ref,\n RefAttributes,\n} from \"react\";\nimport { buttonClassName, preventActivation } from \"./button-class\";\nimport { ButtonContent } from \"./ButtonContent\";\nimport { resolveLinkRel } from \"./link-rel\";\nimport type { ButtonBaseProps } from \"./types\";\n\nexport type { ButtonBaseProps, ButtonSize, ButtonVariant } from \"./types\";\n\n/** `Button` rendering a native `<button>` — the default, when `href` is not set. */\nexport interface ButtonAsButtonProps\n extends ButtonBaseProps, ButtonHTMLAttributes<HTMLButtonElement> {\n href?: undefined;\n}\n\n/**\n * `Button` rendering an `<a href>` — a call to action that navigates.\n *\n * `type` is a `<button>` attribute and is rejected here; `target` and `rel` are\n * forwarded, and `target=\"_blank\"` always gets `noopener noreferrer` added.\n */\nexport interface ButtonAsLinkProps\n extends ButtonBaseProps, Omit<AnchorHTMLAttributes<HTMLAnchorElement>, \"href\" | \"type\"> {\n /** Destination. Its presence is what makes the button an `<a>`. */\n href: string;\n type?: never;\n}\n\nexport type ButtonProps = ButtonAsButtonProps | ButtonAsLinkProps;\n\n/**\n * Each mode's props with the ref type of the element it renders, so a `ref` to an\n * `HTMLButtonElement` on a link is a type error.\n */\ntype ButtonPropsWithRef =\n | (ButtonAsButtonProps & RefAttributes<HTMLButtonElement>)\n | (ButtonAsLinkProps & RefAttributes<HTMLAnchorElement>);\n\n/** `Button`'s call signature: one props union, each member with its own ref type. */\ntype ButtonComponent = ((props: ButtonPropsWithRef) => ReactElement) & { displayName?: string };\n\n/** The flattened view the render function works on after the call site was type-checked. */\ntype ButtonRenderProps = ButtonBaseProps &\n HTMLAttributes<HTMLElement> & {\n href?: string;\n target?: string;\n rel?: string;\n };\n\n/**\n * Primary action button with variants, sizes and a loading state that\n * preserves layout via an absolutely-positioned spinner.\n *\n * Variants: `primary` (solid), `secondary` (neutral), `danger`, `success`,\n * `ghost` (transparent), `soft` (tinted), `outline` (bordered), `link`.\n *\n * Sizes are density-aware — they read from `--tempest-control-height-*`\n * tokens which respond to the `data-tempest-density` attribute.\n *\n * With `href` it renders an `<a>` with the same look: a call to action that\n * navigates keeps the middle click, Ctrl+click, \"open in new tab\" and the URL\n * preview, which a `<button>` calling `location.assign` loses. `target=\"_blank\"`\n * gets `noopener noreferrer`. An `<a>` has no `disabled`, so a disabled or loading\n * link gets `aria-disabled=\"true\"`, `role=\"link\"`, `tabIndex={-1}`, no `href`, and\n * its click and middle-click cancelled.\n *\n * To style another element — a react-router `Link` — use {@link ButtonSlot}. It is\n * a separate component, not an `asChild` prop, so an app that never uses it does\n * not ship the prop merge and ref composition it needs.\n *\n * The ref is typed per mode: `HTMLButtonElement`, or `HTMLAnchorElement` with\n * `href`. `forwardRef` takes one ref type for all props, so its result is re-typed\n * to the per-mode union — the runtime is plain `forwardRef`.\n *\n * @example\n * <Button onClick={save}>Salvar</Button>\n *\n * @example\n * <Button href=\"https://wa.me/5586999999999\" target=\"_blank\" rightIcon={<ExternalLink />}>\n * Fale conosco\n * </Button>\n */\nexport const Button = forwardRef<HTMLElement, ButtonProps>(function Button(\n props: ButtonProps,\n ref: ForwardedRef<HTMLElement>,\n): ReactElement {\n const {\n variant,\n size,\n loading = false,\n disabled = false,\n fullWidth,\n iconOnly,\n pill,\n leftIcon,\n rightIcon,\n className,\n children,\n href,\n target,\n rel,\n ...rest\n } = props as ButtonRenderProps;\n const inactive = disabled || loading;\n const classes = buttonClassName(\n { variant, size, loading, fullWidth, iconOnly, pill },\n className,\n );\n const content: ReactNode = (\n <ButtonContent loading={loading} leftIcon={leftIcon} rightIcon={rightIcon}>\n {children}\n </ButtonContent>\n );\n\n if (href !== undefined) {\n return (\n <a\n {...rest}\n ref={ref as Ref<HTMLAnchorElement>}\n className={classes}\n href={inactive ? undefined : href}\n target={target}\n rel={resolveLinkRel(target, rel)}\n role={inactive ? \"link\" : rest.role}\n aria-disabled={inactive || undefined}\n aria-busy={loading || undefined}\n tabIndex={inactive ? -1 : rest.tabIndex}\n onClick={inactive ? preventActivation : rest.onClick}\n onAuxClick={inactive ? preventActivation : rest.onAuxClick}\n >\n {content}\n </a>\n );\n }\n\n return (\n <button\n {...(rest as ButtonHTMLAttributes<HTMLButtonElement>)}\n ref={ref as Ref<HTMLButtonElement>}\n className={classes}\n disabled={inactive}\n aria-busy={loading || undefined}\n >\n {content}\n </button>\n );\n}) as unknown as ButtonComponent;\n"],"mappings":";;;;;;AAmGA,IAAa,IAAS,EAAqC,SACvD,GACA,GACY;CACZ,IAAM,EACF,YACA,SACA,aAAU,IACV,cAAW,IACX,cACA,aACA,SACA,aACA,cACA,cACA,aACA,SACA,WACA,QACA,GAAG,MACH,GACE,IAAW,KAAY,GACvB,IAAU,EACZ;EAAE;EAAS;EAAM;EAAS;EAAW;EAAU;CAAK,GACpD,CACJ,GACM,IACF,kBAAC,GAAD;EAAwB;EAAmB;EAAqB;EAC3D;CACU,CAAA;CAwBnB,OArBI,MAAS,KAAA,IAsBT,kBAAC,UAAD;EACI,GAAK;EACA;EACL,WAAW;EACX,UAAU;EACV,aAAW,KAAW,KAAA;EAErB,UAAA;CACG,CAAA,IA5BJ,kBAAC,KAAD;EACI,GAAI;EACC;EACL,WAAW;EACX,MAAM,IAAW,KAAA,IAAY;EACrB;EACR,KAAK,EAAe,GAAQ,CAAG;EAC/B,MAAM,IAAW,SAAS,EAAK;EAC/B,iBAAe,KAAY,KAAA;EAC3B,aAAW,KAAW,KAAA;EACtB,UAAU,IAAW,KAAK,EAAK;EAC/B,SAAS,IAAW,IAAoB,EAAK;EAC7C,YAAY,IAAW,IAAoB,EAAK;EAE/C,UAAA;CACF,CAAA;AAef,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Button.module.cjs","names":[],"sources":["../../../src/components/Button/Button.module.css"],"sourcesContent":[".button {\n position: relative;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--tempest-control-gap, var(--tempest-space-2));\n border: 1px solid transparent;\n border-radius: var(--tempest-control-radius, var(--tempest-radius-md));\n font-family: var(--tempest-font-sans);\n font-weight: var(--tempest-weight-semibold);\n line-height: var(--tempest-leading-none);\n letter-spacing: var(--tempest-tracking-normal);\n text-decoration: none;\n white-space: nowrap;\n user-select: none;\n transition: var(--tempest-transition-base);\n box-shadow: var(--tempest-shadow-xs);\n}\n\n.button:focus-visible {\n outline: var(--tempest-focus-ring-width) solid var(--tempest-focus-ring-color);\n outline-offset: var(--tempest-focus-ring-offset);\n}\n\n@media (hover: hover) and (pointer: fine) {\n .button:hover:not(:disabled) {\n box-shadow: var(--tempest-shadow-sm);\n }\n}\n\n.button:active:not(:disabled) {\n transform: translateY(1px);\n box-shadow: var(--tempest-shadow-xs);\n}\n\n.button:disabled {\n opacity: 0.55;\n box-shadow: none;\n}\n\n/* ============================================================\n * Variants — solid (default), soft, outline, ghost\n * Status colors: primary, secondary (neutral), danger, success\n * ============================================================ */\n\n/* primary — solid filled */\n.primary {\n background-color: var(--tempest-primary);\n
|
|
1
|
+
{"version":3,"file":"Button.module.cjs","names":[],"sources":["../../../src/components/Button/Button.module.css"],"sourcesContent":[".button {\n position: relative;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--tempest-control-gap, var(--tempest-space-2));\n border: 1px solid transparent;\n border-radius: var(--tempest-control-radius, var(--tempest-radius-md));\n font-family: var(--tempest-font-sans);\n font-weight: var(--tempest-weight-semibold);\n line-height: var(--tempest-leading-none);\n letter-spacing: var(--tempest-tracking-normal);\n color: var(--_tempest-button-fg);\n text-decoration: none;\n white-space: nowrap;\n user-select: none;\n transition: var(--tempest-transition-base);\n box-shadow: var(--tempest-shadow-xs);\n}\n\n.button:focus-visible {\n outline: var(--tempest-focus-ring-width) solid var(--tempest-focus-ring-color);\n outline-offset: var(--tempest-focus-ring-offset);\n}\n\n@media (hover: hover) and (pointer: fine) {\n .button:hover:not(:disabled, [aria-disabled=\"true\"]) {\n box-shadow: var(--tempest-shadow-sm);\n }\n}\n\n.button:active:not(:disabled, [aria-disabled=\"true\"]) {\n transform: translateY(1px);\n box-shadow: var(--tempest-shadow-xs);\n}\n\n.button:disabled,\n.button[aria-disabled=\"true\"] {\n opacity: 0.55;\n box-shadow: none;\n}\n\n.button[aria-disabled=\"true\"] {\n cursor: not-allowed;\n}\n\n/*\n * `<Button href>` and `<ButtonSlot>` around a link render an `<a>`, and an app's own link\n * rules reach it. `.button` is one class (0,1,0); `a:hover { color }` is (0,1,1) and\n * wins — the Vite React template ships exactly that rule, so in a fresh app a\n * primary link-button turned its label `#535bf2` on hover, over the primary fill.\n *\n * Each variant therefore sets its text colour as `--_tempest-button-fg` instead of\n * `color`, and this rule restates it on every link state at (0,2,1), above any\n * single-element link rule. The `link` variant's hover underline is (0,3,0) and\n * still applies.\n */\na.button:is(:link, :visited, :hover, :active, :focus, :not([href])) {\n color: var(--_tempest-button-fg);\n text-decoration: none;\n}\n\n/* ============================================================\n * Variants — solid (default), soft, outline, ghost\n * Status colors: primary, secondary (neutral), danger, success\n * ============================================================ */\n\n/* primary — solid filled */\n.primary {\n background-color: var(--tempest-primary);\n --_tempest-button-fg: var(--tempest-primary-foreground);\n}\n.primary:hover:not(:disabled, [aria-disabled=\"true\"]) {\n background-color: var(--tempest-primary-hover);\n}\n.primary:active:not(:disabled, [aria-disabled=\"true\"]) {\n background-color: var(--tempest-primary-active);\n}\n\n/* secondary — neutral surface */\n.secondary {\n background-color: var(--tempest-surface);\n --_tempest-button-fg: var(--tempest-text);\n border-color: var(--tempest-border);\n}\n.secondary:hover:not(:disabled, [aria-disabled=\"true\"]) {\n background-color: var(--tempest-surface-2);\n border-color: var(--tempest-border-strong);\n}\n\n/* danger — solid red */\n.danger {\n background-color: var(--tempest-danger-solid);\n --_tempest-button-fg: var(--tempest-danger-on-solid);\n}\n.danger:hover:not(:disabled, [aria-disabled=\"true\"]) {\n background-color: var(--tempest-danger-hover);\n}\n\n/* success — solid green */\n.success {\n background-color: var(--tempest-success-solid);\n --_tempest-button-fg: var(--tempest-success-on-solid);\n}\n.success:hover:not(:disabled, [aria-disabled=\"true\"]) {\n filter: brightness(0.92);\n}\n\n/* ghost — transparent, hover surface */\n.ghost {\n background-color: transparent;\n --_tempest-button-fg: var(--tempest-text);\n box-shadow: none;\n}\n.ghost:hover:not(:disabled, [aria-disabled=\"true\"]) {\n background-color: var(--tempest-surface);\n box-shadow: none;\n}\n.ghost:active:not(:disabled, [aria-disabled=\"true\"]) {\n background-color: var(--tempest-surface-2);\n box-shadow: none;\n}\n\n/* soft — tinted background, primary text */\n.soft {\n background-color: var(--tempest-primary-soft);\n --_tempest-button-fg: var(--tempest-primary-on-soft);\n box-shadow: none;\n}\n.soft:hover:not(:disabled, [aria-disabled=\"true\"]) {\n background-color: var(--tempest-primary-soft-hover);\n}\n\n/* outline — bordered, transparent fill */\n.outline {\n background-color: transparent;\n --_tempest-button-fg: var(--tempest-primary);\n border-color: var(--tempest-primary);\n box-shadow: none;\n}\n/*\n * The text colour has to move with the background.\n *\n * `.outline` sets its text to `var(--tempest-primary)` over a transparent background,\n * where it is fine. This rule tints the background to `--tempest-primary-soft` and,\n * without restating the colour, leaves the fill token sitting on the tint: **4.38:1 in\n * the light theme and 4.28:1 in dark**, both under the 4.5:1 floor for text — so the\n * label got harder to read at the exact moment the pointer was on it.\n *\n * `--tempest-primary-on-soft` is the token for this ground (6.18:1 / 8.72:1) and\n * `docs/styles.md` has documented the rule all along. A rule that changes only the\n * background of a coloured element is the shape of this bug; the other eight places in\n * the SDK that tint a background inherit `--tempest-text`, `--tempest-text-muted` or\n * `--tempest-primary-on-soft` and are fine.\n */\n.outline:hover:not(:disabled, [aria-disabled=\"true\"]) {\n background-color: var(--tempest-primary-soft);\n --_tempest-button-fg: var(--tempest-primary-on-soft);\n}\n\n/* link — text-only, underline on hover */\n.link {\n background-color: transparent;\n --_tempest-button-fg: var(--tempest-primary);\n box-shadow: none;\n padding-left: 0;\n padding-right: 0;\n border-radius: var(--tempest-radius-sm);\n}\n.link:hover:not(:disabled, [aria-disabled=\"true\"]) {\n --_tempest-button-fg: var(--tempest-primary-hover);\n text-decoration: underline;\n background: none;\n box-shadow: none;\n}\n\n/* ============================================================\n * Sizes — token-driven, density-aware\n * ============================================================ */\n.xs {\n height: var(--tempest-control-height-xs);\n padding: 0 var(--tempest-control-padding-xs);\n font-size: var(--tempest-control-font-xs);\n}\n.sm {\n height: var(--tempest-control-height-sm);\n padding: 0 var(--tempest-control-padding-sm);\n font-size: var(--tempest-control-font-sm);\n}\n.md {\n height: var(--tempest-control-height-md);\n padding: 0 var(--tempest-control-padding-md);\n font-size: var(--tempest-control-font-md);\n}\n.lg {\n height: var(--tempest-control-height-lg);\n padding: 0 var(--tempest-control-padding-lg);\n font-size: var(--tempest-control-font-lg);\n}\n.xl {\n height: var(--tempest-control-height-xl);\n padding: 0 var(--tempest-control-padding-xl);\n font-size: var(--tempest-control-font-xl);\n}\n\n/* Square (icon-only) — width matches height */\n.iconOnly {\n position: relative;\n}\n.iconOnly.xs {\n width: var(--tempest-control-height-xs);\n padding: 0;\n}\n.iconOnly.sm {\n width: var(--tempest-control-height-sm);\n padding: 0;\n}\n.iconOnly.md {\n width: var(--tempest-control-height-md);\n padding: 0;\n}\n.iconOnly.lg {\n width: var(--tempest-control-height-lg);\n padding: 0;\n}\n.iconOnly.xl {\n width: var(--tempest-control-height-xl);\n padding: 0;\n}\n\n/* Touch hit-slop — extend tap target to a min 44×44 on coarse pointers\n * for the smallest icon-only sizes, without changing visual footprint. */\n@media (pointer: coarse) {\n .iconOnly.xs::after,\n .iconOnly.sm::after {\n content: \"\";\n position: absolute;\n inset: -8px;\n }\n}\n\n/* Pill shape */\n.pill {\n border-radius: var(--tempest-radius-full);\n}\n\n.fullWidth {\n width: 100%;\n}\n\n/*\n * Lay out leftIcon + label + rightIcon as one centered, gapped row. Without\n * this the three are inline inside a single span (no gap, icon baseline-\n * aligned instead of centered), which reads as off-center — most visible on\n * fullWidth buttons where the label has room to drift.\n */\n.content {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--tempest-control-gap, var(--tempest-space-2));\n min-width: 0;\n}\n\n.loading .hiddenText {\n visibility: hidden;\n}\n\n.spinner {\n position: absolute;\n animation: tempest-spin 0.8s linear infinite;\n}\n\n@keyframes tempest-spin {\n from {\n transform: rotate(0deg);\n }\n to {\n transform: rotate(360deg);\n }\n}\n"],"mappings":";"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Button.module.js","names":[],"sources":["../../../src/components/Button/Button.module.css"],"sourcesContent":[".button {\n position: relative;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--tempest-control-gap, var(--tempest-space-2));\n border: 1px solid transparent;\n border-radius: var(--tempest-control-radius, var(--tempest-radius-md));\n font-family: var(--tempest-font-sans);\n font-weight: var(--tempest-weight-semibold);\n line-height: var(--tempest-leading-none);\n letter-spacing: var(--tempest-tracking-normal);\n text-decoration: none;\n white-space: nowrap;\n user-select: none;\n transition: var(--tempest-transition-base);\n box-shadow: var(--tempest-shadow-xs);\n}\n\n.button:focus-visible {\n outline: var(--tempest-focus-ring-width) solid var(--tempest-focus-ring-color);\n outline-offset: var(--tempest-focus-ring-offset);\n}\n\n@media (hover: hover) and (pointer: fine) {\n .button:hover:not(:disabled) {\n box-shadow: var(--tempest-shadow-sm);\n }\n}\n\n.button:active:not(:disabled) {\n transform: translateY(1px);\n box-shadow: var(--tempest-shadow-xs);\n}\n\n.button:disabled {\n opacity: 0.55;\n box-shadow: none;\n}\n\n/* ============================================================\n * Variants — solid (default), soft, outline, ghost\n * Status colors: primary, secondary (neutral), danger, success\n * ============================================================ */\n\n/* primary — solid filled */\n.primary {\n background-color: var(--tempest-primary);\n
|
|
1
|
+
{"version":3,"file":"Button.module.js","names":[],"sources":["../../../src/components/Button/Button.module.css"],"sourcesContent":[".button {\n position: relative;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--tempest-control-gap, var(--tempest-space-2));\n border: 1px solid transparent;\n border-radius: var(--tempest-control-radius, var(--tempest-radius-md));\n font-family: var(--tempest-font-sans);\n font-weight: var(--tempest-weight-semibold);\n line-height: var(--tempest-leading-none);\n letter-spacing: var(--tempest-tracking-normal);\n color: var(--_tempest-button-fg);\n text-decoration: none;\n white-space: nowrap;\n user-select: none;\n transition: var(--tempest-transition-base);\n box-shadow: var(--tempest-shadow-xs);\n}\n\n.button:focus-visible {\n outline: var(--tempest-focus-ring-width) solid var(--tempest-focus-ring-color);\n outline-offset: var(--tempest-focus-ring-offset);\n}\n\n@media (hover: hover) and (pointer: fine) {\n .button:hover:not(:disabled, [aria-disabled=\"true\"]) {\n box-shadow: var(--tempest-shadow-sm);\n }\n}\n\n.button:active:not(:disabled, [aria-disabled=\"true\"]) {\n transform: translateY(1px);\n box-shadow: var(--tempest-shadow-xs);\n}\n\n.button:disabled,\n.button[aria-disabled=\"true\"] {\n opacity: 0.55;\n box-shadow: none;\n}\n\n.button[aria-disabled=\"true\"] {\n cursor: not-allowed;\n}\n\n/*\n * `<Button href>` and `<ButtonSlot>` around a link render an `<a>`, and an app's own link\n * rules reach it. `.button` is one class (0,1,0); `a:hover { color }` is (0,1,1) and\n * wins — the Vite React template ships exactly that rule, so in a fresh app a\n * primary link-button turned its label `#535bf2` on hover, over the primary fill.\n *\n * Each variant therefore sets its text colour as `--_tempest-button-fg` instead of\n * `color`, and this rule restates it on every link state at (0,2,1), above any\n * single-element link rule. The `link` variant's hover underline is (0,3,0) and\n * still applies.\n */\na.button:is(:link, :visited, :hover, :active, :focus, :not([href])) {\n color: var(--_tempest-button-fg);\n text-decoration: none;\n}\n\n/* ============================================================\n * Variants — solid (default), soft, outline, ghost\n * Status colors: primary, secondary (neutral), danger, success\n * ============================================================ */\n\n/* primary — solid filled */\n.primary {\n background-color: var(--tempest-primary);\n --_tempest-button-fg: var(--tempest-primary-foreground);\n}\n.primary:hover:not(:disabled, [aria-disabled=\"true\"]) {\n background-color: var(--tempest-primary-hover);\n}\n.primary:active:not(:disabled, [aria-disabled=\"true\"]) {\n background-color: var(--tempest-primary-active);\n}\n\n/* secondary — neutral surface */\n.secondary {\n background-color: var(--tempest-surface);\n --_tempest-button-fg: var(--tempest-text);\n border-color: var(--tempest-border);\n}\n.secondary:hover:not(:disabled, [aria-disabled=\"true\"]) {\n background-color: var(--tempest-surface-2);\n border-color: var(--tempest-border-strong);\n}\n\n/* danger — solid red */\n.danger {\n background-color: var(--tempest-danger-solid);\n --_tempest-button-fg: var(--tempest-danger-on-solid);\n}\n.danger:hover:not(:disabled, [aria-disabled=\"true\"]) {\n background-color: var(--tempest-danger-hover);\n}\n\n/* success — solid green */\n.success {\n background-color: var(--tempest-success-solid);\n --_tempest-button-fg: var(--tempest-success-on-solid);\n}\n.success:hover:not(:disabled, [aria-disabled=\"true\"]) {\n filter: brightness(0.92);\n}\n\n/* ghost — transparent, hover surface */\n.ghost {\n background-color: transparent;\n --_tempest-button-fg: var(--tempest-text);\n box-shadow: none;\n}\n.ghost:hover:not(:disabled, [aria-disabled=\"true\"]) {\n background-color: var(--tempest-surface);\n box-shadow: none;\n}\n.ghost:active:not(:disabled, [aria-disabled=\"true\"]) {\n background-color: var(--tempest-surface-2);\n box-shadow: none;\n}\n\n/* soft — tinted background, primary text */\n.soft {\n background-color: var(--tempest-primary-soft);\n --_tempest-button-fg: var(--tempest-primary-on-soft);\n box-shadow: none;\n}\n.soft:hover:not(:disabled, [aria-disabled=\"true\"]) {\n background-color: var(--tempest-primary-soft-hover);\n}\n\n/* outline — bordered, transparent fill */\n.outline {\n background-color: transparent;\n --_tempest-button-fg: var(--tempest-primary);\n border-color: var(--tempest-primary);\n box-shadow: none;\n}\n/*\n * The text colour has to move with the background.\n *\n * `.outline` sets its text to `var(--tempest-primary)` over a transparent background,\n * where it is fine. This rule tints the background to `--tempest-primary-soft` and,\n * without restating the colour, leaves the fill token sitting on the tint: **4.38:1 in\n * the light theme and 4.28:1 in dark**, both under the 4.5:1 floor for text — so the\n * label got harder to read at the exact moment the pointer was on it.\n *\n * `--tempest-primary-on-soft` is the token for this ground (6.18:1 / 8.72:1) and\n * `docs/styles.md` has documented the rule all along. A rule that changes only the\n * background of a coloured element is the shape of this bug; the other eight places in\n * the SDK that tint a background inherit `--tempest-text`, `--tempest-text-muted` or\n * `--tempest-primary-on-soft` and are fine.\n */\n.outline:hover:not(:disabled, [aria-disabled=\"true\"]) {\n background-color: var(--tempest-primary-soft);\n --_tempest-button-fg: var(--tempest-primary-on-soft);\n}\n\n/* link — text-only, underline on hover */\n.link {\n background-color: transparent;\n --_tempest-button-fg: var(--tempest-primary);\n box-shadow: none;\n padding-left: 0;\n padding-right: 0;\n border-radius: var(--tempest-radius-sm);\n}\n.link:hover:not(:disabled, [aria-disabled=\"true\"]) {\n --_tempest-button-fg: var(--tempest-primary-hover);\n text-decoration: underline;\n background: none;\n box-shadow: none;\n}\n\n/* ============================================================\n * Sizes — token-driven, density-aware\n * ============================================================ */\n.xs {\n height: var(--tempest-control-height-xs);\n padding: 0 var(--tempest-control-padding-xs);\n font-size: var(--tempest-control-font-xs);\n}\n.sm {\n height: var(--tempest-control-height-sm);\n padding: 0 var(--tempest-control-padding-sm);\n font-size: var(--tempest-control-font-sm);\n}\n.md {\n height: var(--tempest-control-height-md);\n padding: 0 var(--tempest-control-padding-md);\n font-size: var(--tempest-control-font-md);\n}\n.lg {\n height: var(--tempest-control-height-lg);\n padding: 0 var(--tempest-control-padding-lg);\n font-size: var(--tempest-control-font-lg);\n}\n.xl {\n height: var(--tempest-control-height-xl);\n padding: 0 var(--tempest-control-padding-xl);\n font-size: var(--tempest-control-font-xl);\n}\n\n/* Square (icon-only) — width matches height */\n.iconOnly {\n position: relative;\n}\n.iconOnly.xs {\n width: var(--tempest-control-height-xs);\n padding: 0;\n}\n.iconOnly.sm {\n width: var(--tempest-control-height-sm);\n padding: 0;\n}\n.iconOnly.md {\n width: var(--tempest-control-height-md);\n padding: 0;\n}\n.iconOnly.lg {\n width: var(--tempest-control-height-lg);\n padding: 0;\n}\n.iconOnly.xl {\n width: var(--tempest-control-height-xl);\n padding: 0;\n}\n\n/* Touch hit-slop — extend tap target to a min 44×44 on coarse pointers\n * for the smallest icon-only sizes, without changing visual footprint. */\n@media (pointer: coarse) {\n .iconOnly.xs::after,\n .iconOnly.sm::after {\n content: \"\";\n position: absolute;\n inset: -8px;\n }\n}\n\n/* Pill shape */\n.pill {\n border-radius: var(--tempest-radius-full);\n}\n\n.fullWidth {\n width: 100%;\n}\n\n/*\n * Lay out leftIcon + label + rightIcon as one centered, gapped row. Without\n * this the three are inline inside a single span (no gap, icon baseline-\n * aligned instead of centered), which reads as off-center — most visible on\n * fullWidth buttons where the label has room to drift.\n */\n.content {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--tempest-control-gap, var(--tempest-space-2));\n min-width: 0;\n}\n\n.loading .hiddenText {\n visibility: hidden;\n}\n\n.spinner {\n position: absolute;\n animation: tempest-spin 0.8s linear infinite;\n}\n\n@keyframes tempest-spin {\n from {\n transform: rotate(0deg);\n }\n to {\n transform: rotate(360deg);\n }\n}\n"],"mappings":";"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require("../../utils/cn.cjs"),t=require("./Button.module.cjs");let n=require("react/jsx-runtime");function r({loading:r,leftIcon:i,rightIcon:a,children:o}){return(0,n.jsxs)(n.Fragment,{children:[r&&(0,n.jsx)(`span`,{className:t.default.spinner,"aria-hidden":!0,children:(0,n.jsx)(`svg`,{width:`16`,height:`16`,viewBox:`0 0 24 24`,fill:`none`,xmlns:`http://www.w3.org/2000/svg`,children:(0,n.jsx)(`path`,{d:`M21 12a9 9 0 1 1-6.219-8.56`,stroke:`currentColor`,strokeWidth:`2`,strokeLinecap:`round`})})}),(0,n.jsxs)(`span`,{className:e.cn(t.default.content,r&&t.default.hiddenText),children:[i,o,a]})]})}exports.ButtonContent=r;
|
|
2
|
+
//# sourceMappingURL=ButtonContent.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ButtonContent.cjs","names":[],"sources":["../../../src/components/Button/ButtonContent.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Button.module.css\";\n\n/** Props of {@link ButtonContent}. */\nexport interface ButtonContentProps {\n loading: boolean;\n leftIcon?: ReactNode;\n rightIcon?: ReactNode;\n children?: ReactNode;\n}\n\n/**\n * The inside of every button-looking element: the absolutely-positioned spinner\n * plus the icon/label row, shared by `Button` and `ButtonSlot`.\n *\n * @param props - See {@link ButtonContentProps}.\n * @returns The content nodes.\n */\nexport function ButtonContent({ loading, leftIcon, rightIcon, children }: ButtonContentProps) {\n return (\n <>\n {loading && (\n <span className={styles.spinner} aria-hidden>\n <svg\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n d=\"M21 12a9 9 0 1 1-6.219-8.56\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n />\n </svg>\n </span>\n )}\n <span className={cn(styles.content, loading && styles.hiddenText)}>\n {leftIcon}\n {children}\n {rightIcon}\n </span>\n </>\n );\n}\n"],"mappings":"0GAmBA,SAAgB,EAAc,CAAE,UAAS,WAAU,YAAW,YAAgC,CAC1F,OACI,EAAA,EAAA,KAAA,CAAA,EAAA,SAAA,CAAA,SAAA,CACK,IACG,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,EAAA,QAAO,QAAS,cAAA,GAC7B,UAAA,EAAA,EAAA,IAAA,CAAC,MAAD,CACI,MAAM,KACN,OAAO,KACP,QAAQ,YACR,KAAK,OACL,MAAM,6BAEN,UAAA,EAAA,EAAA,IAAA,CAAC,OAAD,CACI,EAAE,8BACF,OAAO,eACP,YAAY,IACZ,cAAc,OACjB,CAAA,CACA,CAAA,CACH,CAAA,GAEV,EAAA,EAAA,KAAA,CAAC,OAAD,CAAM,UAAW,EAAA,GAAG,EAAA,QAAO,QAAS,GAAW,EAAA,QAAO,UAAU,EAAhE,SAAA,CACK,EACA,EACA,CACC,CACR,CAAA,CAAA,CAAA,CAAA,CAEV"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { cn as e } from "../../utils/cn.js";
|
|
2
|
+
import t from "./Button.module.js";
|
|
3
|
+
import { Fragment as n, jsx as r, jsxs as i } from "react/jsx-runtime";
|
|
4
|
+
//#region src/components/Button/ButtonContent.tsx
|
|
5
|
+
function a({ loading: a, leftIcon: o, rightIcon: s, children: c }) {
|
|
6
|
+
return /* @__PURE__ */ i(n, { children: [a && /* @__PURE__ */ r("span", {
|
|
7
|
+
className: t.spinner,
|
|
8
|
+
"aria-hidden": !0,
|
|
9
|
+
children: /* @__PURE__ */ r("svg", {
|
|
10
|
+
width: "16",
|
|
11
|
+
height: "16",
|
|
12
|
+
viewBox: "0 0 24 24",
|
|
13
|
+
fill: "none",
|
|
14
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
15
|
+
children: /* @__PURE__ */ r("path", {
|
|
16
|
+
d: "M21 12a9 9 0 1 1-6.219-8.56",
|
|
17
|
+
stroke: "currentColor",
|
|
18
|
+
strokeWidth: "2",
|
|
19
|
+
strokeLinecap: "round"
|
|
20
|
+
})
|
|
21
|
+
})
|
|
22
|
+
}), /* @__PURE__ */ i("span", {
|
|
23
|
+
className: e(t.content, a && t.hiddenText),
|
|
24
|
+
children: [
|
|
25
|
+
o,
|
|
26
|
+
c,
|
|
27
|
+
s
|
|
28
|
+
]
|
|
29
|
+
})] });
|
|
30
|
+
}
|
|
31
|
+
//#endregion
|
|
32
|
+
export { a as ButtonContent };
|
|
33
|
+
|
|
34
|
+
//# sourceMappingURL=ButtonContent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ButtonContent.js","names":[],"sources":["../../../src/components/Button/ButtonContent.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Button.module.css\";\n\n/** Props of {@link ButtonContent}. */\nexport interface ButtonContentProps {\n loading: boolean;\n leftIcon?: ReactNode;\n rightIcon?: ReactNode;\n children?: ReactNode;\n}\n\n/**\n * The inside of every button-looking element: the absolutely-positioned spinner\n * plus the icon/label row, shared by `Button` and `ButtonSlot`.\n *\n * @param props - See {@link ButtonContentProps}.\n * @returns The content nodes.\n */\nexport function ButtonContent({ loading, leftIcon, rightIcon, children }: ButtonContentProps) {\n return (\n <>\n {loading && (\n <span className={styles.spinner} aria-hidden>\n <svg\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n d=\"M21 12a9 9 0 1 1-6.219-8.56\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n />\n </svg>\n </span>\n )}\n <span className={cn(styles.content, loading && styles.hiddenText)}>\n {leftIcon}\n {children}\n {rightIcon}\n </span>\n </>\n );\n}\n"],"mappings":";;;;AAmBA,SAAgB,EAAc,EAAE,YAAS,aAAU,cAAW,eAAgC;CAC1F,OACI,kBAAA,GAAA,EAAA,UAAA,CACK,KACG,kBAAC,QAAD;EAAM,WAAW,EAAO;EAAS,eAAA;EAC7B,UAAA,kBAAC,OAAD;GACI,OAAM;GACN,QAAO;GACP,SAAQ;GACR,MAAK;GACL,OAAM;GAEN,UAAA,kBAAC,QAAD;IACI,GAAE;IACF,QAAO;IACP,aAAY;IACZ,eAAc;GACjB,CAAA;EACA,CAAA;CACH,CAAA,GAEV,kBAAC,QAAD;EAAM,WAAW,EAAG,EAAO,SAAS,KAAW,EAAO,UAAU;EAAhE,UAAA;GACK;GACA;GACA;EACC;CACR,CAAA,CAAA,EAAA,CAAA;AAEV"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require("./button-class.cjs"),t=require("./ButtonContent.cjs"),n=require("./link-rel.cjs"),r=require("./slot.cjs");let i=require("react"),a=require("react/jsx-runtime");var o=(0,i.forwardRef)(function({variant:o,size:s,loading:c=!1,disabled:l=!1,fullWidth:u,iconOnly:d,pill:f,leftIcon:p,rightIcon:m,className:h,children:g,..._},v){let y=(0,i.isValidElement)(g)?g.props:{},b={..._,className:e.buttonClassName({variant:o,size:s,loading:c,fullWidth:u,iconOnly:d,pill:f},h),"aria-busy":c||void 0};return y.target===`_blank`&&(b.rel=n.resolveLinkRel(`_blank`,typeof y.rel==`string`?y.rel:void 0)),(l||c)&&(b[`aria-disabled`]=!0,b.tabIndex=-1,b.onClick=e.preventActivation,b.onAuxClick=e.preventActivation,y.href!==void 0&&(b.href=void 0,b.role=`link`)),(0,a.jsx)(r.Slot,{child:g,slotProps:b,slotRef:v,content:(0,a.jsx)(t.ButtonContent,{loading:c,leftIcon:p,rightIcon:m,children:y.children})})});exports.ButtonSlot=o;
|
|
2
|
+
//# sourceMappingURL=ButtonSlot.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ButtonSlot.cjs","names":[],"sources":["../../../src/components/Button/ButtonSlot.tsx"],"sourcesContent":["import { forwardRef, isValidElement } from \"react\";\nimport type { ForwardedRef, HTMLAttributes, ReactElement, ReactNode } from \"react\";\nimport { buttonClassName, preventActivation } from \"./button-class\";\nimport { ButtonContent } from \"./ButtonContent\";\nimport { resolveLinkRel } from \"./link-rel\";\nimport { Slot } from \"./slot\";\nimport type { SlotProps } from \"./slot\";\nimport type { ButtonBaseProps } from \"./types\";\n\n/** Props of {@link ButtonSlot}: the `Button` look, applied to the single child. */\nexport interface ButtonSlotProps\n extends ButtonBaseProps, Omit<HTMLAttributes<HTMLElement>, \"children\"> {\n /** The one element that receives the classes, props and ref — e.g. a router `Link`. */\n children: ReactElement;\n}\n\n/**\n * Lends the `Button` look to its single child element instead of rendering one —\n * the `asChild`/Slot pattern. `<ButtonSlot><Link to=\"/x\">Go</Link></ButtonSlot>`\n * styles a react-router `Link`, so the route changes without a reload, and no\n * router is imported here.\n *\n * It is a component of its own rather than an `asChild` prop on `Button` because\n * of what it costs: the prop merge and ref composition are ~0.6 kB brotli, and a\n * prop is a runtime branch no bundler can drop, so every app using `Button` paid\n * for it. As a separate export it is tree-shaken away where unused.\n *\n * Merge rules: classes are joined (a function `className`, as `NavLink` takes,\n * stays a function); `ButtonSlot`'s props win over the child's, because they carry\n * the disabled semantics; handlers run `ButtonSlot`'s first and skip the child's\n * when the event was prevented; both refs receive the node. A child with\n * `target=\"_blank\"` gets `noopener noreferrer`.\n *\n * Disabled or loading: `aria-disabled=\"true\"`, `tabIndex={-1}`, click and\n * middle-click cancelled — a router `Link` then skips its navigation because the\n * event arrives prevented. A plain `<a href>` child also loses the `href` and gets\n * `role=\"link\"`; a `Link` computes its own `href`, which stays in the DOM.\n *\n * @example\n * <ButtonSlot variant=\"outline\">\n * <Link to=\"/planos\">Ver planos</Link>\n * </ButtonSlot>\n *\n * @throws {Error} When `children` is not a single React element.\n */\nexport const ButtonSlot = forwardRef<HTMLElement, ButtonSlotProps>(function ButtonSlot(\n {\n variant,\n size,\n loading = false,\n disabled = false,\n fullWidth,\n iconOnly,\n pill,\n leftIcon,\n rightIcon,\n className,\n children,\n ...rest\n }: ButtonSlotProps,\n ref: ForwardedRef<HTMLElement>,\n): ReactElement {\n const childProps = (isValidElement(children) ? children.props : {}) as {\n target?: unknown;\n rel?: unknown;\n href?: unknown;\n children?: ReactNode;\n };\n const slotProps: SlotProps = {\n ...rest,\n className: buttonClassName(\n { variant, size, loading, fullWidth, iconOnly, pill },\n className,\n ),\n \"aria-busy\": loading || undefined,\n };\n if (childProps.target === \"_blank\") {\n slotProps.rel = resolveLinkRel(\n \"_blank\",\n typeof childProps.rel === \"string\" ? childProps.rel : undefined,\n );\n }\n if (disabled || loading) {\n slotProps[\"aria-disabled\"] = true;\n slotProps.tabIndex = -1;\n slotProps.onClick = preventActivation;\n slotProps.onAuxClick = preventActivation;\n if (childProps.href !== undefined) {\n slotProps.href = undefined;\n slotProps.role = \"link\";\n }\n }\n return (\n <Slot\n child={children}\n slotProps={slotProps}\n slotRef={ref}\n content={\n <ButtonContent loading={loading} leftIcon={leftIcon} rightIcon={rightIcon}>\n {childProps.children}\n </ButtonContent>\n }\n />\n );\n});\n"],"mappings":"iLA6CA,IAAa,GAAA,EAAa,EAAA,WAAA,CAAyC,SAC/D,CACI,UACA,OACA,UAAU,GACV,WAAW,GACX,YACA,WACA,OACA,WACA,YACA,YACA,WACA,GAAG,GAEP,EACY,CACZ,IAAM,GAAA,EAAc,EAAA,eAAA,CAAe,CAAQ,EAAI,EAAS,MAAQ,CAAC,EAM3D,EAAuB,CACzB,GAAG,EACH,UAAW,EAAA,gBACP,CAAE,UAAS,OAAM,UAAS,YAAW,WAAU,MAAK,EACpD,CACJ,EACA,YAAa,GAAW,IAAA,EAC5B,EAiBA,OAhBI,EAAW,SAAW,WACtB,EAAU,IAAM,EAAA,eACZ,SACA,OAAO,EAAW,KAAQ,SAAW,EAAW,IAAM,IAAA,EAC1D,IAEA,GAAY,KACZ,EAAU,iBAAmB,GAC7B,EAAU,SAAW,GACrB,EAAU,QAAU,EAAA,kBACpB,EAAU,WAAa,EAAA,kBACnB,EAAW,OAAS,IAAA,KACpB,EAAU,KAAO,IAAA,GACjB,EAAU,KAAO,UAIrB,EAAA,EAAA,IAAA,CAAC,EAAA,KAAD,CACI,MAAO,EACI,YACX,QAAS,EACT,SACI,EAAA,EAAA,IAAA,CAAC,EAAA,cAAD,CAAwB,UAAmB,WAAqB,YAC3D,SAAA,EAAW,QACD,CAAA,CAEtB,CAAA,CAET,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { buttonClassName as e, preventActivation as t } from "./button-class.js";
|
|
2
|
+
import { ButtonContent as n } from "./ButtonContent.js";
|
|
3
|
+
import { resolveLinkRel as r } from "./link-rel.js";
|
|
4
|
+
import { Slot as i } from "./slot.js";
|
|
5
|
+
import { forwardRef as a, isValidElement as o } from "react";
|
|
6
|
+
import { jsx as s } from "react/jsx-runtime";
|
|
7
|
+
//#region src/components/Button/ButtonSlot.tsx
|
|
8
|
+
var c = a(function({ variant: a, size: c, loading: l = !1, disabled: u = !1, fullWidth: d, iconOnly: f, pill: p, leftIcon: m, rightIcon: h, className: g, children: _, ...v }, y) {
|
|
9
|
+
let b = o(_) ? _.props : {}, x = {
|
|
10
|
+
...v,
|
|
11
|
+
className: e({
|
|
12
|
+
variant: a,
|
|
13
|
+
size: c,
|
|
14
|
+
loading: l,
|
|
15
|
+
fullWidth: d,
|
|
16
|
+
iconOnly: f,
|
|
17
|
+
pill: p
|
|
18
|
+
}, g),
|
|
19
|
+
"aria-busy": l || void 0
|
|
20
|
+
};
|
|
21
|
+
return b.target === "_blank" && (x.rel = r("_blank", typeof b.rel == "string" ? b.rel : void 0)), (u || l) && (x["aria-disabled"] = !0, x.tabIndex = -1, x.onClick = t, x.onAuxClick = t, b.href !== void 0 && (x.href = void 0, x.role = "link")), /* @__PURE__ */ s(i, {
|
|
22
|
+
child: _,
|
|
23
|
+
slotProps: x,
|
|
24
|
+
slotRef: y,
|
|
25
|
+
content: /* @__PURE__ */ s(n, {
|
|
26
|
+
loading: l,
|
|
27
|
+
leftIcon: m,
|
|
28
|
+
rightIcon: h,
|
|
29
|
+
children: b.children
|
|
30
|
+
})
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
//#endregion
|
|
34
|
+
export { c as ButtonSlot };
|
|
35
|
+
|
|
36
|
+
//# sourceMappingURL=ButtonSlot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ButtonSlot.js","names":[],"sources":["../../../src/components/Button/ButtonSlot.tsx"],"sourcesContent":["import { forwardRef, isValidElement } from \"react\";\nimport type { ForwardedRef, HTMLAttributes, ReactElement, ReactNode } from \"react\";\nimport { buttonClassName, preventActivation } from \"./button-class\";\nimport { ButtonContent } from \"./ButtonContent\";\nimport { resolveLinkRel } from \"./link-rel\";\nimport { Slot } from \"./slot\";\nimport type { SlotProps } from \"./slot\";\nimport type { ButtonBaseProps } from \"./types\";\n\n/** Props of {@link ButtonSlot}: the `Button` look, applied to the single child. */\nexport interface ButtonSlotProps\n extends ButtonBaseProps, Omit<HTMLAttributes<HTMLElement>, \"children\"> {\n /** The one element that receives the classes, props and ref — e.g. a router `Link`. */\n children: ReactElement;\n}\n\n/**\n * Lends the `Button` look to its single child element instead of rendering one —\n * the `asChild`/Slot pattern. `<ButtonSlot><Link to=\"/x\">Go</Link></ButtonSlot>`\n * styles a react-router `Link`, so the route changes without a reload, and no\n * router is imported here.\n *\n * It is a component of its own rather than an `asChild` prop on `Button` because\n * of what it costs: the prop merge and ref composition are ~0.6 kB brotli, and a\n * prop is a runtime branch no bundler can drop, so every app using `Button` paid\n * for it. As a separate export it is tree-shaken away where unused.\n *\n * Merge rules: classes are joined (a function `className`, as `NavLink` takes,\n * stays a function); `ButtonSlot`'s props win over the child's, because they carry\n * the disabled semantics; handlers run `ButtonSlot`'s first and skip the child's\n * when the event was prevented; both refs receive the node. A child with\n * `target=\"_blank\"` gets `noopener noreferrer`.\n *\n * Disabled or loading: `aria-disabled=\"true\"`, `tabIndex={-1}`, click and\n * middle-click cancelled — a router `Link` then skips its navigation because the\n * event arrives prevented. A plain `<a href>` child also loses the `href` and gets\n * `role=\"link\"`; a `Link` computes its own `href`, which stays in the DOM.\n *\n * @example\n * <ButtonSlot variant=\"outline\">\n * <Link to=\"/planos\">Ver planos</Link>\n * </ButtonSlot>\n *\n * @throws {Error} When `children` is not a single React element.\n */\nexport const ButtonSlot = forwardRef<HTMLElement, ButtonSlotProps>(function ButtonSlot(\n {\n variant,\n size,\n loading = false,\n disabled = false,\n fullWidth,\n iconOnly,\n pill,\n leftIcon,\n rightIcon,\n className,\n children,\n ...rest\n }: ButtonSlotProps,\n ref: ForwardedRef<HTMLElement>,\n): ReactElement {\n const childProps = (isValidElement(children) ? children.props : {}) as {\n target?: unknown;\n rel?: unknown;\n href?: unknown;\n children?: ReactNode;\n };\n const slotProps: SlotProps = {\n ...rest,\n className: buttonClassName(\n { variant, size, loading, fullWidth, iconOnly, pill },\n className,\n ),\n \"aria-busy\": loading || undefined,\n };\n if (childProps.target === \"_blank\") {\n slotProps.rel = resolveLinkRel(\n \"_blank\",\n typeof childProps.rel === \"string\" ? childProps.rel : undefined,\n );\n }\n if (disabled || loading) {\n slotProps[\"aria-disabled\"] = true;\n slotProps.tabIndex = -1;\n slotProps.onClick = preventActivation;\n slotProps.onAuxClick = preventActivation;\n if (childProps.href !== undefined) {\n slotProps.href = undefined;\n slotProps.role = \"link\";\n }\n }\n return (\n <Slot\n child={children}\n slotProps={slotProps}\n slotRef={ref}\n content={\n <ButtonContent loading={loading} leftIcon={leftIcon} rightIcon={rightIcon}>\n {childProps.children}\n </ButtonContent>\n }\n />\n );\n});\n"],"mappings":";;;;;;;AA6CA,IAAa,IAAa,EAAyC,SAC/D,EACI,YACA,SACA,aAAU,IACV,cAAW,IACX,cACA,aACA,SACA,aACA,cACA,cACA,aACA,GAAG,KAEP,GACY;CACZ,IAAM,IAAc,EAAe,CAAQ,IAAI,EAAS,QAAQ,CAAC,GAM3D,IAAuB;EACzB,GAAG;EACH,WAAW,EACP;GAAE;GAAS;GAAM;GAAS;GAAW;GAAU;EAAK,GACpD,CACJ;EACA,aAAa,KAAW,KAAA;CAC5B;CAiBA,OAhBI,EAAW,WAAW,aACtB,EAAU,MAAM,EACZ,UACA,OAAO,EAAW,OAAQ,WAAW,EAAW,MAAM,KAAA,CAC1D,KAEA,KAAY,OACZ,EAAU,mBAAmB,IAC7B,EAAU,WAAW,IACrB,EAAU,UAAU,GACpB,EAAU,aAAa,GACnB,EAAW,SAAS,KAAA,MACpB,EAAU,OAAO,KAAA,GACjB,EAAU,OAAO,UAIrB,kBAAC,GAAD;EACI,OAAO;EACI;EACX,SAAS;EACT,SACI,kBAAC,GAAD;GAAwB;GAAmB;GAAqB;GAC3D,UAAA,EAAW;EACD,CAAA;CAEtB,CAAA;AAET,CAAC"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require("../../utils/cn.cjs"),t=require("./Button.module.cjs");function n({variant:n=`primary`,size:r=`md`,loading:i=!1,fullWidth:a=!1,iconOnly:o=!1,pill:s=!1},c){return e.cn(t.default.button,t.default[n],t.default[r],o&&t.default.iconOnly,s&&t.default.pill,i&&t.default.loading,a&&t.default.fullWidth,c)}function r(e){e.preventDefault()}exports.buttonClassName=n,exports.preventActivation=r;
|
|
2
|
+
//# sourceMappingURL=button-class.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"button-class.cjs","names":[],"sources":["../../../src/components/Button/button-class.ts"],"sourcesContent":["import type { MouseEvent } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Button.module.css\";\nimport type { ButtonBaseProps } from \"./types\";\n\n/**\n * The class list of a button-looking element, shared by `Button` and `ButtonSlot`\n * so the two can never drift apart.\n *\n * @param props - The look props.\n * @param className - The consumer's own class, appended last.\n * @returns The joined class string.\n */\nexport function buttonClassName(\n {\n variant = \"primary\",\n size = \"md\",\n loading = false,\n fullWidth = false,\n iconOnly = false,\n pill = false,\n }: ButtonBaseProps,\n className: string | undefined,\n): string {\n return cn(\n styles.button,\n styles[variant],\n styles[size],\n iconOnly && styles.iconOnly,\n pill && styles.pill,\n loading && styles.loading,\n fullWidth && styles.fullWidth,\n className,\n );\n}\n\n/**\n * Click handler of a disabled link: cancels the navigation.\n *\n * @param event - The click or middle-click.\n */\nexport function preventActivation(event: MouseEvent<HTMLElement>): void {\n event.preventDefault();\n}\n"],"mappings":"uEAaA,SAAgB,EACZ,CACI,UAAU,UACV,OAAO,KACP,UAAU,GACV,YAAY,GACZ,WAAW,GACX,OAAO,IAEX,EACM,CACN,OAAO,EAAA,GACH,EAAA,QAAO,OACP,EAAA,QAAO,GACP,EAAA,QAAO,GACP,GAAY,EAAA,QAAO,SACnB,GAAQ,EAAA,QAAO,KACf,GAAW,EAAA,QAAO,QAClB,GAAa,EAAA,QAAO,UACpB,CACJ,CACJ,CAOA,SAAgB,EAAkB,EAAsC,CACpE,EAAM,eAAe,CACzB"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { cn as e } from "../../utils/cn.js";
|
|
2
|
+
import t from "./Button.module.js";
|
|
3
|
+
//#region src/components/Button/button-class.ts
|
|
4
|
+
function n({ variant: n = "primary", size: r = "md", loading: i = !1, fullWidth: a = !1, iconOnly: o = !1, pill: s = !1 }, c) {
|
|
5
|
+
return e(t.button, t[n], t[r], o && t.iconOnly, s && t.pill, i && t.loading, a && t.fullWidth, c);
|
|
6
|
+
}
|
|
7
|
+
function r(e) {
|
|
8
|
+
e.preventDefault();
|
|
9
|
+
}
|
|
10
|
+
//#endregion
|
|
11
|
+
export { n as buttonClassName, r as preventActivation };
|
|
12
|
+
|
|
13
|
+
//# sourceMappingURL=button-class.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"button-class.js","names":[],"sources":["../../../src/components/Button/button-class.ts"],"sourcesContent":["import type { MouseEvent } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Button.module.css\";\nimport type { ButtonBaseProps } from \"./types\";\n\n/**\n * The class list of a button-looking element, shared by `Button` and `ButtonSlot`\n * so the two can never drift apart.\n *\n * @param props - The look props.\n * @param className - The consumer's own class, appended last.\n * @returns The joined class string.\n */\nexport function buttonClassName(\n {\n variant = \"primary\",\n size = \"md\",\n loading = false,\n fullWidth = false,\n iconOnly = false,\n pill = false,\n }: ButtonBaseProps,\n className: string | undefined,\n): string {\n return cn(\n styles.button,\n styles[variant],\n styles[size],\n iconOnly && styles.iconOnly,\n pill && styles.pill,\n loading && styles.loading,\n fullWidth && styles.fullWidth,\n className,\n );\n}\n\n/**\n * Click handler of a disabled link: cancels the navigation.\n *\n * @param event - The click or middle-click.\n */\nexport function preventActivation(event: MouseEvent<HTMLElement>): void {\n event.preventDefault();\n}\n"],"mappings":";;;AAaA,SAAgB,EACZ,EACI,aAAU,WACV,UAAO,MACP,aAAU,IACV,eAAY,IACZ,cAAW,IACX,UAAO,MAEX,GACM;CACN,OAAO,EACH,EAAO,QACP,EAAO,IACP,EAAO,IACP,KAAY,EAAO,UACnB,KAAQ,EAAO,MACf,KAAW,EAAO,SAClB,KAAa,EAAO,WACpB,CACJ;AACJ;AAOA,SAAgB,EAAkB,GAAsC;CACpE,EAAM,eAAe;AACzB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"link-rel.cjs","names":[],"sources":["../../../src/components/Button/link-rel.ts"],"sourcesContent":["/** Link relationships that `target=\"_blank\"` must carry. */\nconst BLANK_TARGET_REL: readonly string[] = [\"noopener\", \"noreferrer\"];\n\n/**\n * The `rel` to render for a link, adding `noopener noreferrer` to a new-tab link.\n *\n * A page opened with `target=\"_blank\"` and no `noopener` gets `window.opener` and\n * can redirect the tab that opened it. It is a mechanical rule with one right\n * answer, so it lives here instead of in a warning on the docs page. The tokens\n * the consumer passed are kept, and none is added twice (case-insensitively).\n *\n * @param target - The link's `target`.\n * @param rel - The `rel` the consumer passed, if any.\n * @returns The `rel` to render, or `undefined` when there is none.\n */\nexport function resolveLinkRel(\n target: string | undefined,\n rel: string | undefined,\n): string | undefined {\n if (target !== \"_blank\") return rel;\n const present = ` ${(rel ?? \"\").toLowerCase().replace(/\\s+/g, \" \")} `;\n return [rel?.trim(), ...BLANK_TARGET_REL.filter((token) => !present.includes(` ${token} `))]\n .filter(Boolean)\n .join(\" \");\n}\n"],"mappings":"AACA,IAAM,EAAsC,CAAC,WAAY,YAAY,EAcrE,SAAgB,EACZ,EACA,EACkB,CAClB,GAAI,IAAW,SAAU,OAAO,EAChC,IAAM,EAAU,KAAK,GAAO,GAAA,CAAI,YAAY,CAAC,CAAC,QAAQ,OAAQ,GAAG,EAAE,GACnE,MAAO,CAAC,GAAK,KAAK,EAAG,GAAG,EAAiB,OAAQ,GAAU,CAAC,EAAQ,SAAS,IAAI,EAAM,EAAE,CAAC,CAAC,CAAC,CACvF,OAAO,OAAO,CAAC,CACf,KAAK,GAAG,CACjB"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
//#region src/components/Button/link-rel.ts
|
|
2
|
+
var e = ["noopener", "noreferrer"];
|
|
3
|
+
function t(t, n) {
|
|
4
|
+
if (t !== "_blank") return n;
|
|
5
|
+
let r = ` ${(n ?? "").toLowerCase().replace(/\s+/g, " ")} `;
|
|
6
|
+
return [n?.trim(), ...e.filter((e) => !r.includes(` ${e} `))].filter(Boolean).join(" ");
|
|
7
|
+
}
|
|
8
|
+
//#endregion
|
|
9
|
+
export { t as resolveLinkRel };
|
|
10
|
+
|
|
11
|
+
//# sourceMappingURL=link-rel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"link-rel.js","names":[],"sources":["../../../src/components/Button/link-rel.ts"],"sourcesContent":["/** Link relationships that `target=\"_blank\"` must carry. */\nconst BLANK_TARGET_REL: readonly string[] = [\"noopener\", \"noreferrer\"];\n\n/**\n * The `rel` to render for a link, adding `noopener noreferrer` to a new-tab link.\n *\n * A page opened with `target=\"_blank\"` and no `noopener` gets `window.opener` and\n * can redirect the tab that opened it. It is a mechanical rule with one right\n * answer, so it lives here instead of in a warning on the docs page. The tokens\n * the consumer passed are kept, and none is added twice (case-insensitively).\n *\n * @param target - The link's `target`.\n * @param rel - The `rel` the consumer passed, if any.\n * @returns The `rel` to render, or `undefined` when there is none.\n */\nexport function resolveLinkRel(\n target: string | undefined,\n rel: string | undefined,\n): string | undefined {\n if (target !== \"_blank\") return rel;\n const present = ` ${(rel ?? \"\").toLowerCase().replace(/\\s+/g, \" \")} `;\n return [rel?.trim(), ...BLANK_TARGET_REL.filter((token) => !present.includes(` ${token} `))]\n .filter(Boolean)\n .join(\" \");\n}\n"],"mappings":";AACA,IAAM,IAAsC,CAAC,YAAY,YAAY;AAcrE,SAAgB,EACZ,GACA,GACkB;CAClB,IAAI,MAAW,UAAU,OAAO;CAChC,IAAM,IAAU,KAAK,KAAO,GAAA,CAAI,YAAY,CAAC,CAAC,QAAQ,QAAQ,GAAG,EAAE;CACnE,OAAO,CAAC,GAAK,KAAK,GAAG,GAAG,EAAiB,QAAQ,MAAU,CAAC,EAAQ,SAAS,IAAI,EAAM,EAAE,CAAC,CAAC,CAAC,CACvF,OAAO,OAAO,CAAC,CACf,KAAK,GAAG;AACjB"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require("../../utils/cn.cjs");let t=require("react");var n=Number.parseInt(t.version,10);function r(...e){return t=>{for(let n of e)typeof n==`function`?n(t):n&&(n.current=t)}}function i(e){return n>=19?e.props.ref:e.ref}function a(t,n){return typeof n==`function`?(...r)=>e.cn(t,n(...r)):e.cn(t,n)}function o(e,t){let n={...t,...e};for(let r of Object.keys(e)){let i=e[r],a=t[r];/^on[A-Z]/.test(r)&&typeof i==`function`&&typeof a==`function`&&(n[r]=e=>{i(e),e.defaultPrevented||a(e)})}return n.className=a(e.className,t.className),e.style&&(n.style={...t.style,...e.style}),n}function s({child:e,slotProps:n,slotRef:a,content:s}){let c=(0,t.isValidElement)(e)?i(e):void 0,l=(0,t.useMemo)(()=>r(c,a),[c,a]);if(!(0,t.isValidElement)(e))throw Error(`ButtonSlot expects exactly one React element as its child.`);let u=o(n,e.props);return u.ref=l,(0,t.cloneElement)(e,u,s)}exports.Slot=s,exports.composeRefs=r,exports.getElementRef=i,exports.mergeSlotProps=o;
|
|
2
|
+
//# sourceMappingURL=slot.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slot.cjs","names":[],"sources":["../../../src/components/Button/slot.ts"],"sourcesContent":["import { cloneElement, isValidElement, useMemo, version } from \"react\";\nimport type { CSSProperties, ReactElement, ReactNode, Ref, RefCallback, RefObject } from \"react\";\nimport { cn } from \"@/utils/cn\";\n\n/** Props a slot hands to the element it renders through. */\nexport type SlotProps = Record<string, unknown> & {\n className?: string;\n style?: CSSProperties;\n};\n\n/**\n * Major version of the React running the page.\n *\n * React 19 moved an element's `ref` into `element.props.ref` and turned the old\n * `element.ref` into a getter that logs a deprecation warning; React 18 only has\n * `element.ref`. Reading the version once picks the right field without touching\n * the deprecated one.\n */\nconst REACT_MAJOR: number = Number.parseInt(version, 10);\n\n/**\n * Combine several refs into one callback ref, so a single DOM node reaches all of\n * them — the ref the consumer put on the child and the one forwarded to `Button`.\n *\n * @param refs - Callback refs, ref objects, `null` or `undefined`, in any mix.\n * @returns A callback ref that assigns the node to every ref it was given.\n */\nexport function composeRefs<T>(...refs: Array<Ref<T> | undefined>): RefCallback<T> {\n return (node: T | null): void => {\n for (const ref of refs) {\n if (typeof ref === \"function\") ref(node);\n else if (ref) (ref as RefObject<T | null>).current = node;\n }\n };\n}\n\n/**\n * Read the `ref` a React element was created with, on React 18 and 19 alike.\n *\n * @param element - The element whose ref is wanted.\n * @returns The element's ref, or `undefined` when it has none.\n */\nexport function getElementRef(element: ReactElement): Ref<unknown> | undefined {\n if (REACT_MAJOR >= 19) return (element.props as { ref?: Ref<unknown> }).ref;\n return (element as unknown as { ref?: Ref<unknown> }).ref;\n}\n\n/**\n * Join the slot's class with the child's, which may be a function of state.\n *\n * react-router's `NavLink` takes `className` as `({ isActive }) => string`.\n * Stringifying that would drop the active class, so a function stays a function\n * and gets the slot's class prepended to whatever it returns.\n *\n * @param slotClass - The slot's class string.\n * @param childClass - The child's `className`: a string, a function or absent.\n * @returns A string, or a function of the same shape as the child's.\n */\nfunction mergeClassName(slotClass: string | undefined, childClass: unknown): unknown {\n if (typeof childClass === \"function\") {\n return (...args: unknown[]): string =>\n cn(slotClass, childClass(...args) as string | undefined);\n }\n return cn(slotClass, childClass as string | undefined);\n}\n\n/**\n * Merge the slot's props over the child's own props.\n *\n * The rules, and why each one is that way round:\n *\n * - **Plain props: the slot wins.** The slot is `ButtonSlot`, and the props it adds\n * carry its invariants — `aria-disabled`, `tabIndex={-1}` and the click blocker\n * of a disabled link. A child prop overriding them would make a disabled link\n * navigate, which is the bug the props exist to prevent.\n * - **`className` is joined**, slot first, so the child keeps its own classes.\n * A function `className` (react-router's `NavLink`) stays a function.\n * - **`style` is merged** when the slot sets one, slot keys over child keys, for\n * the same reason as the plain props. Without a slot `style` the child's stays\n * untouched, function or not.\n * - **Event handlers (`on[A-Z]…`) both run**: the slot's first, then the child's,\n * unless the slot's handler called `preventDefault()`. That is what lets a\n * disabled `ButtonSlot` stop a react-router `Link`: the `Link` runs the `onClick` it\n * was given and then skips its own navigation when the event is already\n * prevented.\n *\n * @param slotProps - Props the slot applies.\n * @param childProps - Props the child element was created with.\n * @returns The merged props for `cloneElement`.\n */\nexport function mergeSlotProps(\n slotProps: SlotProps,\n childProps: Record<string, unknown>,\n): Record<string, unknown> {\n const merged: Record<string, unknown> = { ...childProps, ...slotProps };\n\n for (const key of Object.keys(slotProps)) {\n const slotValue = slotProps[key];\n const childValue = childProps[key];\n if (\n /^on[A-Z]/.test(key) &&\n typeof slotValue === \"function\" &&\n typeof childValue === \"function\"\n ) {\n merged[key] = (event: { defaultPrevented?: boolean }): void => {\n slotValue(event);\n if (!event.defaultPrevented) childValue(event);\n };\n }\n }\n\n merged.className = mergeClassName(slotProps.className, childProps.className);\n if (slotProps.style) {\n merged.style = { ...(childProps.style as CSSProperties | undefined), ...slotProps.style };\n }\n return merged;\n}\n\n/** Props of {@link Slot}. */\nexport interface SlotComponentProps {\n /** The single element the consumer passed as `children`. */\n child: ReactNode;\n /** Props to merge over the child's own (see {@link mergeSlotProps}). */\n slotProps: SlotProps;\n /** Ref forwarded to the slot, composed with the child's own ref. */\n slotRef?: Ref<HTMLElement>;\n /** What to render inside the child, in place of its children. */\n content: ReactNode;\n}\n\n/**\n * Render `child` with the slot's props, ref and content — the `asChild` pattern behind `ButtonSlot`.\n *\n * Kept local to `Button` instead of taking `@radix-ui/react-slot`: the whole\n * mechanism is a prop merge and a ref composition, and a dependency for that\n * would put its version bounds on every consumer of the SDK.\n *\n * The composed ref is memoised on the two refs, so a re-render with the same refs\n * does not detach and re-attach them.\n *\n * @param props - See {@link SlotComponentProps}.\n * @returns The cloned element.\n * @throws {Error} When `child` is not a single React element.\n */\nexport function Slot({ child, slotProps, slotRef, content }: SlotComponentProps): ReactElement {\n const childRef = isValidElement(child)\n ? (getElementRef(child) as Ref<HTMLElement> | undefined)\n : undefined;\n const ref = useMemo(() => composeRefs(childRef, slotRef), [childRef, slotRef]);\n if (!isValidElement(child)) {\n throw new Error(\"ButtonSlot expects exactly one React element as its child.\");\n }\n const merged = mergeSlotProps(slotProps, child.props as Record<string, unknown>);\n merged.ref = ref;\n return cloneElement(child, merged, content);\n}\n"],"mappings":"6DAkBA,IAAM,EAAsB,OAAO,SAAS,EAAA,QAAS,EAAE,EASvD,SAAgB,EAAe,GAAG,EAAiD,CAC/E,MAAQ,IAAyB,CAC7B,IAAK,IAAM,KAAO,EACV,OAAO,GAAQ,WAAY,EAAI,CAAI,EAC9B,IAAK,EAA6B,QAAU,EAE7D,CACJ,CAQA,SAAgB,EAAc,EAAiD,CAE3E,OADI,GAAe,GAAY,EAAQ,MAAiC,IAChE,EAA8C,GAC1D,CAaA,SAAS,EAAe,EAA+B,EAA8B,CAKjF,OAJI,OAAO,GAAe,YACd,GAAG,IACP,EAAA,GAAG,EAAW,EAAW,GAAG,CAAI,CAAuB,EAExD,EAAA,GAAG,EAAW,CAAgC,CACzD,CA0BA,SAAgB,EACZ,EACA,EACuB,CACvB,IAAM,EAAkC,CAAE,GAAG,EAAY,GAAG,CAAU,EAEtE,IAAK,IAAM,KAAO,OAAO,KAAK,CAAS,EAAG,CACtC,IAAM,EAAY,EAAU,GACtB,EAAa,EAAW,GAE1B,WAAW,KAAK,CAAG,GACnB,OAAO,GAAc,YACrB,OAAO,GAAe,aAEtB,EAAO,GAAQ,GAAgD,CAC3D,EAAU,CAAK,EACV,EAAM,kBAAkB,EAAW,CAAK,CACjD,EAER,CAMA,MAJA,GAAO,UAAY,EAAe,EAAU,UAAW,EAAW,SAAS,EACvE,EAAU,QACV,EAAO,MAAQ,CAAE,GAAI,EAAW,MAAqC,GAAG,EAAU,KAAM,GAErF,CACX,CA4BA,SAAgB,EAAK,CAAE,QAAO,YAAW,UAAS,WAA6C,CAC3F,IAAM,GAAA,EAAW,EAAA,eAAA,CAAe,CAAK,EAC9B,EAAc,CAAK,EACpB,IAAA,GACA,GAAA,EAAM,EAAA,QAAA,KAAc,EAAY,EAAU,CAAO,EAAG,CAAC,EAAU,CAAO,CAAC,EAC7E,GAAI,EAAA,EAAC,EAAA,eAAA,CAAe,CAAK,EACrB,MAAU,MAAM,4DAA4D,EAEhF,IAAM,EAAS,EAAe,EAAW,EAAM,KAAgC,EAE/E,MADA,GAAO,IAAM,GACb,EAAO,EAAA,aAAA,CAAa,EAAO,EAAQ,CAAO,CAC9C"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { cn as e } from "../../utils/cn.js";
|
|
2
|
+
import { cloneElement as t, isValidElement as n, useMemo as r, version as i } from "react";
|
|
3
|
+
//#region src/components/Button/slot.ts
|
|
4
|
+
var a = Number.parseInt(i, 10);
|
|
5
|
+
function o(...e) {
|
|
6
|
+
return (t) => {
|
|
7
|
+
for (let n of e) typeof n == "function" ? n(t) : n && (n.current = t);
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
function s(e) {
|
|
11
|
+
return a >= 19 ? e.props.ref : e.ref;
|
|
12
|
+
}
|
|
13
|
+
function c(t, n) {
|
|
14
|
+
return typeof n == "function" ? (...r) => e(t, n(...r)) : e(t, n);
|
|
15
|
+
}
|
|
16
|
+
function l(e, t) {
|
|
17
|
+
let n = {
|
|
18
|
+
...t,
|
|
19
|
+
...e
|
|
20
|
+
};
|
|
21
|
+
for (let r of Object.keys(e)) {
|
|
22
|
+
let i = e[r], a = t[r];
|
|
23
|
+
/^on[A-Z]/.test(r) && typeof i == "function" && typeof a == "function" && (n[r] = (e) => {
|
|
24
|
+
i(e), e.defaultPrevented || a(e);
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
return n.className = c(e.className, t.className), e.style && (n.style = {
|
|
28
|
+
...t.style,
|
|
29
|
+
...e.style
|
|
30
|
+
}), n;
|
|
31
|
+
}
|
|
32
|
+
function u({ child: e, slotProps: i, slotRef: a, content: c }) {
|
|
33
|
+
let u = n(e) ? s(e) : void 0, d = r(() => o(u, a), [u, a]);
|
|
34
|
+
if (!n(e)) throw Error("ButtonSlot expects exactly one React element as its child.");
|
|
35
|
+
let f = l(i, e.props);
|
|
36
|
+
return f.ref = d, t(e, f, c);
|
|
37
|
+
}
|
|
38
|
+
//#endregion
|
|
39
|
+
export { u as Slot, o as composeRefs, s as getElementRef, l as mergeSlotProps };
|
|
40
|
+
|
|
41
|
+
//# sourceMappingURL=slot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slot.js","names":[],"sources":["../../../src/components/Button/slot.ts"],"sourcesContent":["import { cloneElement, isValidElement, useMemo, version } from \"react\";\nimport type { CSSProperties, ReactElement, ReactNode, Ref, RefCallback, RefObject } from \"react\";\nimport { cn } from \"@/utils/cn\";\n\n/** Props a slot hands to the element it renders through. */\nexport type SlotProps = Record<string, unknown> & {\n className?: string;\n style?: CSSProperties;\n};\n\n/**\n * Major version of the React running the page.\n *\n * React 19 moved an element's `ref` into `element.props.ref` and turned the old\n * `element.ref` into a getter that logs a deprecation warning; React 18 only has\n * `element.ref`. Reading the version once picks the right field without touching\n * the deprecated one.\n */\nconst REACT_MAJOR: number = Number.parseInt(version, 10);\n\n/**\n * Combine several refs into one callback ref, so a single DOM node reaches all of\n * them — the ref the consumer put on the child and the one forwarded to `Button`.\n *\n * @param refs - Callback refs, ref objects, `null` or `undefined`, in any mix.\n * @returns A callback ref that assigns the node to every ref it was given.\n */\nexport function composeRefs<T>(...refs: Array<Ref<T> | undefined>): RefCallback<T> {\n return (node: T | null): void => {\n for (const ref of refs) {\n if (typeof ref === \"function\") ref(node);\n else if (ref) (ref as RefObject<T | null>).current = node;\n }\n };\n}\n\n/**\n * Read the `ref` a React element was created with, on React 18 and 19 alike.\n *\n * @param element - The element whose ref is wanted.\n * @returns The element's ref, or `undefined` when it has none.\n */\nexport function getElementRef(element: ReactElement): Ref<unknown> | undefined {\n if (REACT_MAJOR >= 19) return (element.props as { ref?: Ref<unknown> }).ref;\n return (element as unknown as { ref?: Ref<unknown> }).ref;\n}\n\n/**\n * Join the slot's class with the child's, which may be a function of state.\n *\n * react-router's `NavLink` takes `className` as `({ isActive }) => string`.\n * Stringifying that would drop the active class, so a function stays a function\n * and gets the slot's class prepended to whatever it returns.\n *\n * @param slotClass - The slot's class string.\n * @param childClass - The child's `className`: a string, a function or absent.\n * @returns A string, or a function of the same shape as the child's.\n */\nfunction mergeClassName(slotClass: string | undefined, childClass: unknown): unknown {\n if (typeof childClass === \"function\") {\n return (...args: unknown[]): string =>\n cn(slotClass, childClass(...args) as string | undefined);\n }\n return cn(slotClass, childClass as string | undefined);\n}\n\n/**\n * Merge the slot's props over the child's own props.\n *\n * The rules, and why each one is that way round:\n *\n * - **Plain props: the slot wins.** The slot is `ButtonSlot`, and the props it adds\n * carry its invariants — `aria-disabled`, `tabIndex={-1}` and the click blocker\n * of a disabled link. A child prop overriding them would make a disabled link\n * navigate, which is the bug the props exist to prevent.\n * - **`className` is joined**, slot first, so the child keeps its own classes.\n * A function `className` (react-router's `NavLink`) stays a function.\n * - **`style` is merged** when the slot sets one, slot keys over child keys, for\n * the same reason as the plain props. Without a slot `style` the child's stays\n * untouched, function or not.\n * - **Event handlers (`on[A-Z]…`) both run**: the slot's first, then the child's,\n * unless the slot's handler called `preventDefault()`. That is what lets a\n * disabled `ButtonSlot` stop a react-router `Link`: the `Link` runs the `onClick` it\n * was given and then skips its own navigation when the event is already\n * prevented.\n *\n * @param slotProps - Props the slot applies.\n * @param childProps - Props the child element was created with.\n * @returns The merged props for `cloneElement`.\n */\nexport function mergeSlotProps(\n slotProps: SlotProps,\n childProps: Record<string, unknown>,\n): Record<string, unknown> {\n const merged: Record<string, unknown> = { ...childProps, ...slotProps };\n\n for (const key of Object.keys(slotProps)) {\n const slotValue = slotProps[key];\n const childValue = childProps[key];\n if (\n /^on[A-Z]/.test(key) &&\n typeof slotValue === \"function\" &&\n typeof childValue === \"function\"\n ) {\n merged[key] = (event: { defaultPrevented?: boolean }): void => {\n slotValue(event);\n if (!event.defaultPrevented) childValue(event);\n };\n }\n }\n\n merged.className = mergeClassName(slotProps.className, childProps.className);\n if (slotProps.style) {\n merged.style = { ...(childProps.style as CSSProperties | undefined), ...slotProps.style };\n }\n return merged;\n}\n\n/** Props of {@link Slot}. */\nexport interface SlotComponentProps {\n /** The single element the consumer passed as `children`. */\n child: ReactNode;\n /** Props to merge over the child's own (see {@link mergeSlotProps}). */\n slotProps: SlotProps;\n /** Ref forwarded to the slot, composed with the child's own ref. */\n slotRef?: Ref<HTMLElement>;\n /** What to render inside the child, in place of its children. */\n content: ReactNode;\n}\n\n/**\n * Render `child` with the slot's props, ref and content — the `asChild` pattern behind `ButtonSlot`.\n *\n * Kept local to `Button` instead of taking `@radix-ui/react-slot`: the whole\n * mechanism is a prop merge and a ref composition, and a dependency for that\n * would put its version bounds on every consumer of the SDK.\n *\n * The composed ref is memoised on the two refs, so a re-render with the same refs\n * does not detach and re-attach them.\n *\n * @param props - See {@link SlotComponentProps}.\n * @returns The cloned element.\n * @throws {Error} When `child` is not a single React element.\n */\nexport function Slot({ child, slotProps, slotRef, content }: SlotComponentProps): ReactElement {\n const childRef = isValidElement(child)\n ? (getElementRef(child) as Ref<HTMLElement> | undefined)\n : undefined;\n const ref = useMemo(() => composeRefs(childRef, slotRef), [childRef, slotRef]);\n if (!isValidElement(child)) {\n throw new Error(\"ButtonSlot expects exactly one React element as its child.\");\n }\n const merged = mergeSlotProps(slotProps, child.props as Record<string, unknown>);\n merged.ref = ref;\n return cloneElement(child, merged, content);\n}\n"],"mappings":";;;AAkBA,IAAM,IAAsB,OAAO,SAAS,GAAS,EAAE;AASvD,SAAgB,EAAe,GAAG,GAAiD;CAC/E,QAAQ,MAAyB;EAC7B,KAAK,IAAM,KAAO,GACd,AAAI,OAAO,KAAQ,aAAY,EAAI,CAAI,IAC9B,MAAK,EAA6B,UAAU;CAE7D;AACJ;AAQA,SAAgB,EAAc,GAAiD;CAE3E,OADI,KAAe,KAAY,EAAQ,MAAiC,MAChE,EAA8C;AAC1D;AAaA,SAAS,EAAe,GAA+B,GAA8B;CAKjF,OAJI,OAAO,KAAe,cACd,GAAG,MACP,EAAG,GAAW,EAAW,GAAG,CAAI,CAAuB,IAExD,EAAG,GAAW,CAAgC;AACzD;AA0BA,SAAgB,EACZ,GACA,GACuB;CACvB,IAAM,IAAkC;EAAE,GAAG;EAAY,GAAG;CAAU;CAEtE,KAAK,IAAM,KAAO,OAAO,KAAK,CAAS,GAAG;EACtC,IAAM,IAAY,EAAU,IACtB,IAAa,EAAW;EAC9B,AACI,WAAW,KAAK,CAAG,KACnB,OAAO,KAAc,cACrB,OAAO,KAAe,eAEtB,EAAO,MAAQ,MAAgD;GAE3D,AADA,EAAU,CAAK,GACV,EAAM,oBAAkB,EAAW,CAAK;EACjD;CAER;CAMA,OAJA,EAAO,YAAY,EAAe,EAAU,WAAW,EAAW,SAAS,GACvE,EAAU,UACV,EAAO,QAAQ;EAAE,GAAI,EAAW;EAAqC,GAAG,EAAU;CAAM,IAErF;AACX;AA4BA,SAAgB,EAAK,EAAE,UAAO,cAAW,YAAS,cAA6C;CAC3F,IAAM,IAAW,EAAe,CAAK,IAC9B,EAAc,CAAK,IACpB,KAAA,GACA,IAAM,QAAc,EAAY,GAAU,CAAO,GAAG,CAAC,GAAU,CAAO,CAAC;CAC7E,IAAI,CAAC,EAAe,CAAK,GACrB,MAAU,MAAM,4DAA4D;CAEhF,IAAM,IAAS,EAAe,GAAW,EAAM,KAAgC;CAE/E,OADA,EAAO,MAAM,GACN,EAAa,GAAO,GAAQ,CAAO;AAC9C"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("../../utils/cn.cjs"),t=require("./Collapsible.module.cjs");let n=require("react"),r=require("react/jsx-runtime");function i({open:i,defaultOpen:a=!1,onOpenChange:o,trigger:s,className:c,children:l,...u}){let d=(0,n.useId)(),f=`${d}-content`,p=`${d}-trigger`,m=i!==void 0,[h,g]=(0,n.useState)(a),_=m?i:h,v=(0,n.useCallback)(()=>{let e=!_;m||g(e),o?.(e)},[_,m,o]);return(0,r.jsxs)(`div`,{className:e.cn(t.default.collapsible,
|
|
1
|
+
const e=require("../../utils/cn.cjs"),t=require("./Collapsible.module.cjs");let n=require("react"),r=require("react/jsx-runtime");function i({open:i,defaultOpen:a=!1,onOpenChange:o,trigger:s,className:c,children:l,...u}){let d=(0,n.useId)(),f=`${d}-content`,p=`${d}-trigger`,m=i!==void 0,[h,g]=(0,n.useState)(a),_=m?i:h,v=(0,n.useCallback)(()=>{let e=!_;m||g(e),o?.(e)},[_,m,o]);return(0,r.jsxs)(`div`,{className:e.cn(t.default.collapsible,c),...u,children:[(0,r.jsx)(`button`,{type:`button`,id:p,className:t.default.trigger,"aria-expanded":_,"aria-controls":f,onClick:v,children:s}),(0,r.jsx)(`div`,{id:f,role:`region`,"aria-labelledby":p,hidden:!_,className:t.default.content,children:l})]})}exports.Collapsible=i;
|
|
2
2
|
//# sourceMappingURL=Collapsible.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Collapsible.cjs","names":[],"sources":["../../../src/components/Collapsible/Collapsible.tsx"],"sourcesContent":["import { useCallback, useId, useState } from \"react\";\nimport type { HTMLAttributes, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Collapsible.module.css\";\n\nexport interface CollapsibleProps extends HTMLAttributes<HTMLDivElement> {\n /** Controlled open state. When provided the component is controlled. */\n open?: boolean;\n /** Initial open state for the uncontrolled variant. Default `false`. */\n defaultOpen?: boolean;\n /** Fired with the next open state whenever the trigger is activated. */\n onOpenChange?: (open: boolean) => void;\n /** Content rendered inside the trigger button. */\n trigger: ReactNode;\n /** Collapsible content, hidden while closed. */\n children: ReactNode;\n}\n\n/**\n * A single expand/collapse region — a lighter alternative to `Accordion` for\n * one toggleable block. The trigger is a `<button aria-expanded aria-controls>`\n * wired to a content region sharing the same id; the region is `hidden` while\n * closed. Works controlled (`open` + `onOpenChange`) or uncontrolled\n * (`defaultOpen`).\n *\n * @param props - {@link CollapsibleProps}.\n * @returns The collapsible container.\n */\nexport function Collapsible({\n open,\n defaultOpen = false,\n onOpenChange,\n trigger,\n className,\n children,\n ...props\n}: CollapsibleProps) {\n const reactId = useId();\n const contentId = `${reactId}-content`;\n const triggerId = `${reactId}-trigger`;\n\n const isControlled = open !== undefined;\n const [internalOpen, setInternalOpen] = useState<boolean>(defaultOpen);\n const isOpen = isControlled ? open : internalOpen;\n\n const handleToggle = useCallback((): void => {\n const next = !isOpen;\n if (!isControlled) setInternalOpen(next);\n onOpenChange?.(next);\n }, [isOpen, isControlled, onOpenChange]);\n\n return (\n <div className={cn(styles.collapsible,
|
|
1
|
+
{"version":3,"file":"Collapsible.cjs","names":[],"sources":["../../../src/components/Collapsible/Collapsible.tsx"],"sourcesContent":["import { useCallback, useId, useState } from \"react\";\nimport type { HTMLAttributes, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Collapsible.module.css\";\n\nexport interface CollapsibleProps extends HTMLAttributes<HTMLDivElement> {\n /** Controlled open state. When provided the component is controlled. */\n open?: boolean;\n /** Initial open state for the uncontrolled variant. Default `false`. */\n defaultOpen?: boolean;\n /** Fired with the next open state whenever the trigger is activated. */\n onOpenChange?: (open: boolean) => void;\n /** Content rendered inside the trigger button. */\n trigger: ReactNode;\n /** Collapsible content, hidden while closed. */\n children: ReactNode;\n}\n\n/**\n * A single expand/collapse region — a lighter alternative to `Accordion` for\n * one toggleable block. The trigger is a `<button aria-expanded aria-controls>`\n * wired to a content region sharing the same id; the region is `hidden` while\n * closed. Works controlled (`open` + `onOpenChange`) or uncontrolled\n * (`defaultOpen`).\n *\n * @param props - {@link CollapsibleProps}.\n * @returns The collapsible container.\n */\nexport function Collapsible({\n open,\n defaultOpen = false,\n onOpenChange,\n trigger,\n className,\n children,\n ...props\n}: CollapsibleProps) {\n const reactId = useId();\n const contentId = `${reactId}-content`;\n const triggerId = `${reactId}-trigger`;\n\n const isControlled = open !== undefined;\n const [internalOpen, setInternalOpen] = useState<boolean>(defaultOpen);\n const isOpen = isControlled ? open : internalOpen;\n\n const handleToggle = useCallback((): void => {\n const next = !isOpen;\n if (!isControlled) setInternalOpen(next);\n onOpenChange?.(next);\n }, [isOpen, isControlled, onOpenChange]);\n\n return (\n <div className={cn(styles.collapsible, className)} {...props}>\n <button\n type=\"button\"\n id={triggerId}\n className={styles.trigger}\n aria-expanded={isOpen}\n aria-controls={contentId}\n onClick={handleToggle}\n >\n {trigger}\n </button>\n <div\n id={contentId}\n role=\"region\"\n aria-labelledby={triggerId}\n hidden={!isOpen}\n className={styles.content}\n >\n {children}\n </div>\n </div>\n );\n}\n"],"mappings":"kIA4BA,SAAgB,EAAY,CACxB,OACA,cAAc,GACd,eACA,UACA,YACA,WACA,GAAG,GACc,CACjB,IAAM,GAAA,EAAU,EAAA,MAAA,CAAM,EAChB,EAAY,GAAG,EAAQ,UACvB,EAAY,GAAG,EAAQ,UAEvB,EAAe,IAAS,IAAA,GACxB,CAAC,EAAc,IAAA,EAAmB,EAAA,SAAA,CAAkB,CAAW,EAC/D,EAAS,EAAe,EAAO,EAE/B,GAAA,EAAe,EAAA,YAAA,KAAwB,CACzC,IAAM,EAAO,CAAC,EACT,GAAc,EAAgB,CAAI,EACvC,IAAe,CAAI,CACvB,EAAG,CAAC,EAAQ,EAAc,CAAY,CAAC,EAEvC,OACI,EAAA,EAAA,KAAA,CAAC,MAAD,CAAK,UAAW,EAAA,GAAG,EAAA,QAAO,YAAa,CAAS,EAAG,GAAI,EAAvD,SAAA,EACI,EAAA,EAAA,IAAA,CAAC,SAAD,CACI,KAAK,SACL,GAAI,EACJ,UAAW,EAAA,QAAO,QAClB,gBAAe,EACf,gBAAe,EACf,QAAS,EAER,SAAA,CACG,CAAA,GACR,EAAA,EAAA,IAAA,CAAC,MAAD,CACI,GAAI,EACJ,KAAK,SACL,kBAAiB,EACjB,OAAQ,CAAC,EACT,UAAW,EAAA,QAAO,QAEjB,UACA,CAAA,CACJ,GAEb"}
|
|
@@ -13,7 +13,7 @@ function s({ open: s, defaultOpen: c = !1, onOpenChange: l, trigger: u, classNam
|
|
|
13
13
|
l
|
|
14
14
|
]);
|
|
15
15
|
return /* @__PURE__ */ o("div", {
|
|
16
|
-
className: e(t.collapsible,
|
|
16
|
+
className: e(t.collapsible, d),
|
|
17
17
|
...p,
|
|
18
18
|
children: [/* @__PURE__ */ a("button", {
|
|
19
19
|
type: "button",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Collapsible.js","names":[],"sources":["../../../src/components/Collapsible/Collapsible.tsx"],"sourcesContent":["import { useCallback, useId, useState } from \"react\";\nimport type { HTMLAttributes, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Collapsible.module.css\";\n\nexport interface CollapsibleProps extends HTMLAttributes<HTMLDivElement> {\n /** Controlled open state. When provided the component is controlled. */\n open?: boolean;\n /** Initial open state for the uncontrolled variant. Default `false`. */\n defaultOpen?: boolean;\n /** Fired with the next open state whenever the trigger is activated. */\n onOpenChange?: (open: boolean) => void;\n /** Content rendered inside the trigger button. */\n trigger: ReactNode;\n /** Collapsible content, hidden while closed. */\n children: ReactNode;\n}\n\n/**\n * A single expand/collapse region — a lighter alternative to `Accordion` for\n * one toggleable block. The trigger is a `<button aria-expanded aria-controls>`\n * wired to a content region sharing the same id; the region is `hidden` while\n * closed. Works controlled (`open` + `onOpenChange`) or uncontrolled\n * (`defaultOpen`).\n *\n * @param props - {@link CollapsibleProps}.\n * @returns The collapsible container.\n */\nexport function Collapsible({\n open,\n defaultOpen = false,\n onOpenChange,\n trigger,\n className,\n children,\n ...props\n}: CollapsibleProps) {\n const reactId = useId();\n const contentId = `${reactId}-content`;\n const triggerId = `${reactId}-trigger`;\n\n const isControlled = open !== undefined;\n const [internalOpen, setInternalOpen] = useState<boolean>(defaultOpen);\n const isOpen = isControlled ? open : internalOpen;\n\n const handleToggle = useCallback((): void => {\n const next = !isOpen;\n if (!isControlled) setInternalOpen(next);\n onOpenChange?.(next);\n }, [isOpen, isControlled, onOpenChange]);\n\n return (\n <div className={cn(styles.collapsible,
|
|
1
|
+
{"version":3,"file":"Collapsible.js","names":[],"sources":["../../../src/components/Collapsible/Collapsible.tsx"],"sourcesContent":["import { useCallback, useId, useState } from \"react\";\nimport type { HTMLAttributes, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Collapsible.module.css\";\n\nexport interface CollapsibleProps extends HTMLAttributes<HTMLDivElement> {\n /** Controlled open state. When provided the component is controlled. */\n open?: boolean;\n /** Initial open state for the uncontrolled variant. Default `false`. */\n defaultOpen?: boolean;\n /** Fired with the next open state whenever the trigger is activated. */\n onOpenChange?: (open: boolean) => void;\n /** Content rendered inside the trigger button. */\n trigger: ReactNode;\n /** Collapsible content, hidden while closed. */\n children: ReactNode;\n}\n\n/**\n * A single expand/collapse region — a lighter alternative to `Accordion` for\n * one toggleable block. The trigger is a `<button aria-expanded aria-controls>`\n * wired to a content region sharing the same id; the region is `hidden` while\n * closed. Works controlled (`open` + `onOpenChange`) or uncontrolled\n * (`defaultOpen`).\n *\n * @param props - {@link CollapsibleProps}.\n * @returns The collapsible container.\n */\nexport function Collapsible({\n open,\n defaultOpen = false,\n onOpenChange,\n trigger,\n className,\n children,\n ...props\n}: CollapsibleProps) {\n const reactId = useId();\n const contentId = `${reactId}-content`;\n const triggerId = `${reactId}-trigger`;\n\n const isControlled = open !== undefined;\n const [internalOpen, setInternalOpen] = useState<boolean>(defaultOpen);\n const isOpen = isControlled ? open : internalOpen;\n\n const handleToggle = useCallback((): void => {\n const next = !isOpen;\n if (!isControlled) setInternalOpen(next);\n onOpenChange?.(next);\n }, [isOpen, isControlled, onOpenChange]);\n\n return (\n <div className={cn(styles.collapsible, className)} {...props}>\n <button\n type=\"button\"\n id={triggerId}\n className={styles.trigger}\n aria-expanded={isOpen}\n aria-controls={contentId}\n onClick={handleToggle}\n >\n {trigger}\n </button>\n <div\n id={contentId}\n role=\"region\"\n aria-labelledby={triggerId}\n hidden={!isOpen}\n className={styles.content}\n >\n {children}\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;AA4BA,SAAgB,EAAY,EACxB,SACA,iBAAc,IACd,iBACA,YACA,cACA,aACA,GAAG,KACc;CACjB,IAAM,IAAU,EAAM,GAChB,IAAY,GAAG,EAAQ,WACvB,IAAY,GAAG,EAAQ,WAEvB,IAAe,MAAS,KAAA,GACxB,CAAC,GAAc,KAAmB,EAAkB,CAAW,GAC/D,IAAS,IAAe,IAAO,GAE/B,IAAe,QAAwB;EACzC,IAAM,IAAO,CAAC;EAEd,AADK,KAAc,EAAgB,CAAI,GACvC,IAAe,CAAI;CACvB,GAAG;EAAC;EAAQ;EAAc;CAAY,CAAC;CAEvC,OACI,kBAAC,OAAD;EAAK,WAAW,EAAG,EAAO,aAAa,CAAS;EAAG,GAAI;EAAvD,UAAA,CACI,kBAAC,UAAD;GACI,MAAK;GACL,IAAI;GACJ,WAAW,EAAO;GAClB,iBAAe;GACf,iBAAe;GACf,SAAS;GAER,UAAA;EACG,CAAA,GACR,kBAAC,OAAD;GACI,IAAI;GACJ,MAAK;GACL,mBAAiB;GACjB,QAAQ,CAAC;GACT,WAAW,EAAO;GAEjB;EACA,CAAA,CACJ;;AAEb"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Form.cjs","names":[],"sources":["../../../src/components/Form/Form.tsx"],"sourcesContent":["import { forwardRef } from \"react\";\nimport type { CSSProperties, FormHTMLAttributes, HTMLAttributes, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Form.module.css\";\n\nexport type FormLayout = \"stack\" | \"inline\" | \"grid\";\nexport type FormActionsAlign = \"start\" | \"center\" | \"end\" | \"between\";\n\ninterface LayoutProps {\n /** Field arrangement. `stack` stacks fields vertically, `inline` lines them up in a wrapping row, `grid` arranges them in `columns` equal-width tracks. Default `stack`. */\n layout?: FormLayout;\n /** Number of columns when `layout=\"grid\"`, or a custom `grid-template-columns` value. Default `2`. */\n columns?: number | string;\n /** Gap between fields. Numbers map to a multiple of the 4px scale (default `4` → 16px). */\n gap?: number | string;\n}\n\nfunction resolveGap(gap: number | string | undefined, fallback: number): string {\n if (gap === undefined) return `${fallback * 4}px`;\n return typeof gap === \"number\" ? `${gap * 4}px` : gap;\n}\n\nfunction resolveColumns(columns: number | string | undefined): string {\n if (columns === undefined) return \"repeat(2, minmax(0, 1fr))\";\n return typeof columns === \"number\" ? `repeat(${columns}, minmax(0, 1fr))` : columns;\n}\n\nexport interface FormProps extends FormHTMLAttributes<HTMLFormElement>, LayoutProps {}\n\n/**\n * Form wrapper with a built-in layout variant. Replaces ad-hoc `<form>` +\n * `<Stack>` / `<Grid>` boilerplate; pair with [[FormRow]], [[FormSection]],\n * and [[FormActions]] for richer layouts.\n *\n * @example\n * <Form layout=\"grid\" columns={2} gap={4} onSubmit={form.handleSubmit(save)}>\n * <Input label=\"Nome\" {...form.register(\"name\")} />\n * <Input label=\"Email\" {...form.register(\"email\")} />\n * <FormActions align=\"end\">\n * <Button type=\"submit\">Salvar</Button>\n * </FormActions>\n * </Form>\n */\nexport const Form = forwardRef<HTMLFormElement, FormProps>(function Form(\n { layout = \"stack\", columns, gap, className, style, children, ...props },\n ref,\n) {\n const finalStyle: CSSProperties = {\n gap: resolveGap(gap, 4),\n ...(layout === \"grid\" ? { gridTemplateColumns: resolveColumns(columns) } : null),\n ...style,\n };\n return (\n <form\n ref={ref}\n className={cn(styles.form, styles[layout], className)}\n style={finalStyle}\n {...props}\n >\n {children}\n </form>\n );\n});\n\nexport interface FormSectionProps extends Omit<HTMLAttributes<HTMLElement>, \"title\">, LayoutProps {\n title?: ReactNode;\n description?: ReactNode;\n}\n\n/**\n * Visually grouped subset of fields with an optional title and description.\n * Has its own `layout`/`columns`/`gap` so subsets can stack while the parent\n * grids (or vice versa).\n *\n * @example\n * <FormSection title=\"Endereço\" description=\"Usado para entrega\" layout=\"grid\" columns={3}>\n * <Input label=\"CEP\" />\n * <Input label=\"Rua\" />\n * <Input label=\"Número\" />\n * </FormSection>\n */\nexport function FormSection({\n title,\n description,\n layout = \"stack\",\n columns,\n gap,\n className,\n style,\n children,\n ...props\n}: FormSectionProps) {\n const bodyStyle: CSSProperties = {\n gap: resolveGap(gap, 4),\n ...(layout === \"grid\" ? { gridTemplateColumns: resolveColumns(columns) } : null),\n };\n return (\n <section className={cn(styles.section, className)} style={style} {...props}>\n {(title || description) && (\n <header className={styles.sectionHeader}>\n {title && <h3 className={styles.sectionTitle}>{title}</h3>}\n {description && <p className={styles.sectionDescription}>{description}</p>}\n </header>\n )}\n <div className={cn(styles.sectionBody, styles[layout])} style={bodyStyle}>\n {children}\n </div>\n </section>\n );\n}\n\nexport interface FormRowProps extends HTMLAttributes<HTMLDivElement> {\n /** Gap between row items. Numbers map to a multiple of the 4px scale (default `3` → 12px). */\n gap?: number | string;\n}\n\n/**\n * Forces a horizontal row regardless of parent layout — useful for grouping\n * two short fields side-by-side inside an otherwise stacked form (e.g. CEP +\n * city, expiry month + year).\n *\n * @example\n * <Form layout=\"stack\">\n * <FormRow>\n * <Input label=\"CEP\" />\n * <Input label=\"Cidade\" />\n * </FormRow>\n * <Input label=\"Endereço completo\" />\n * </Form>\n */\nexport function FormRow({ gap, className, style, children, ...props }: FormRowProps) {\n const finalStyle: CSSProperties = { gap: resolveGap(gap, 3), ...style };\n return (\n <div className={cn(styles.row, className)} style={finalStyle} {...props}>\n {children}\n </div>\n );\n}\n\nexport interface FormActionsProps extends HTMLAttributes<HTMLDivElement> {\n /** Horizontal alignment of buttons. Default `end`. */\n align?: FormActionsAlign;\n /** Gap between buttons. Numbers map to a multiple of the 4px scale (default `2` → 8px). */\n gap?: number | string;\n}\n\n/**\n * Footer button row for a form. Use inside (or after) [[Form]] / [[FormSection]].\n *\n * @example\n * <FormActions align=\"end\">\n * <Button variant=\"ghost\" onClick={onCancel}>Cancelar</Button>\n * <Button type=\"submit\" loading={form.formState.isSubmitting}>Salvar</Button>\n * </FormActions>\n */\nexport function FormActions({\n align = \"end\",\n gap,\n className,\n style,\n children,\n ...props\n}: FormActionsProps) {\n const finalStyle: CSSProperties = { gap: resolveGap(gap, 2), ...style };\n return (\n <div className={cn(styles.actions, styles[align], className)} style={finalStyle} {...props}>\n {children}\n </div>\n );\n}\n"],"mappings":"2HAiBA,SAAS,EAAW,EAAkC,EAA0B,CAE5E,OADI,IAAQ,IAAA,GAAkB,GAAG,EAAW,EAAE,IACvC,OAAO,GAAQ,SAAW,GAAG,EAAM,EAAE,IAAM,CACtD,CAEA,SAAS,EAAe,EAA8C,CAElE,OADI,IAAY,IAAA,GAAkB,4BAC3B,OAAO,GAAY,SAAW,UAAU,EAAQ,mBAAqB,CAChF,CAkBA,IAAa,GAAA,EAAO,EAAA,WAAA,CAAuC,SACvD,CAAE,SAAS,QAAS,UAAS,MAAK,YAAW,QAAO,WAAU,GAAG,GACjE,EACF,CACE,IAAM,EAA4B,CAC9B,IAAK,EAAW,EAAK,CAAC,EACtB,GAAI,IAAW,OAAS,CAAE,oBAAqB,EAAe,CAAO,CAAE,EAAI,KAC3E,GAAG,CACP,EACA,OACI,EAAA,EAAA,IAAA,CAAC,OAAD,CACS,MACL,UAAW,EAAA,GAAG,EAAA,QAAO,KAAM,EAAA,QAAO,GAAS,CAAS,EACpD,MAAO,EACP,GAAI,EAEH,UACC,CAAA,CAEd,CAAC,
|
|
1
|
+
{"version":3,"file":"Form.cjs","names":[],"sources":["../../../src/components/Form/Form.tsx"],"sourcesContent":["import { forwardRef } from \"react\";\nimport type { CSSProperties, FormHTMLAttributes, HTMLAttributes, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Form.module.css\";\n\nexport type FormLayout = \"stack\" | \"inline\" | \"grid\";\nexport type FormActionsAlign = \"start\" | \"center\" | \"end\" | \"between\";\n\ninterface LayoutProps {\n /** Field arrangement. `stack` stacks fields vertically, `inline` lines them up in a wrapping row, `grid` arranges them in `columns` equal-width tracks. Default `stack`. */\n layout?: FormLayout;\n /** Number of columns when `layout=\"grid\"`, or a custom `grid-template-columns` value. Default `2`. */\n columns?: number | string;\n /** Gap between fields. Numbers map to a multiple of the 4px scale (default `4` → 16px). */\n gap?: number | string;\n}\n\nfunction resolveGap(gap: number | string | undefined, fallback: number): string {\n if (gap === undefined) return `${fallback * 4}px`;\n return typeof gap === \"number\" ? `${gap * 4}px` : gap;\n}\n\nfunction resolveColumns(columns: number | string | undefined): string {\n if (columns === undefined) return \"repeat(2, minmax(0, 1fr))\";\n return typeof columns === \"number\" ? `repeat(${columns}, minmax(0, 1fr))` : columns;\n}\n\nexport interface FormProps extends FormHTMLAttributes<HTMLFormElement>, LayoutProps {}\n\n/**\n * Form wrapper with a built-in layout variant. Replaces ad-hoc `<form>` +\n * `<Stack>` / `<Grid>` boilerplate; pair with [[FormRow]], [[FormSection]],\n * and [[FormActions]] for richer layouts.\n *\n * @example\n * <Form layout=\"grid\" columns={2} gap={4} onSubmit={form.handleSubmit(save)}>\n * <Input label=\"Nome\" {...form.register(\"name\")} />\n * <Input label=\"Email\" {...form.register(\"email\")} />\n * <FormActions align=\"end\">\n * <Button type=\"submit\">Salvar</Button>\n * </FormActions>\n * </Form>\n */\nexport const Form = forwardRef<HTMLFormElement, FormProps>(function Form(\n { layout = \"stack\", columns, gap, className, style, children, ...props },\n ref,\n) {\n const finalStyle: CSSProperties = {\n gap: resolveGap(gap, 4),\n ...(layout === \"grid\" ? { gridTemplateColumns: resolveColumns(columns) } : null),\n ...style,\n };\n return (\n <form\n ref={ref}\n className={cn(styles.form, styles[layout], className)}\n style={finalStyle}\n {...props}\n >\n {children}\n </form>\n );\n});\n\nexport interface FormSectionProps extends Omit<HTMLAttributes<HTMLElement>, \"title\">, LayoutProps {\n title?: ReactNode;\n description?: ReactNode;\n}\n\n/**\n * Visually grouped subset of fields with an optional title and description.\n * Has its own `layout`/`columns`/`gap` so subsets can stack while the parent\n * grids (or vice versa).\n *\n * The header is deliberately not a `SectionHeader`: a field group sits one step\n * below a page section, and its title is `text-lg` / semibold / `leading-snug`\n * with `space-3` below, against `text-2xl` / bold / `leading-tight` with\n * `space-5` for `SectionHeader`. Sharing the component would either grow every\n * form group to page-section size or add a size switch to `SectionHeader` that\n * only this caller uses.\n *\n * @example\n * <FormSection title=\"Endereço\" description=\"Usado para entrega\" layout=\"grid\" columns={3}>\n * <Input label=\"CEP\" />\n * <Input label=\"Rua\" />\n * <Input label=\"Número\" />\n * </FormSection>\n */\nexport function FormSection({\n title,\n description,\n layout = \"stack\",\n columns,\n gap,\n className,\n style,\n children,\n ...props\n}: FormSectionProps) {\n const bodyStyle: CSSProperties = {\n gap: resolveGap(gap, 4),\n ...(layout === \"grid\" ? { gridTemplateColumns: resolveColumns(columns) } : null),\n };\n return (\n <section className={cn(styles.section, className)} style={style} {...props}>\n {(title || description) && (\n <header className={styles.sectionHeader}>\n {title && <h3 className={styles.sectionTitle}>{title}</h3>}\n {description && <p className={styles.sectionDescription}>{description}</p>}\n </header>\n )}\n <div className={cn(styles.sectionBody, styles[layout])} style={bodyStyle}>\n {children}\n </div>\n </section>\n );\n}\n\nexport interface FormRowProps extends HTMLAttributes<HTMLDivElement> {\n /** Gap between row items. Numbers map to a multiple of the 4px scale (default `3` → 12px). */\n gap?: number | string;\n}\n\n/**\n * Forces a horizontal row regardless of parent layout — useful for grouping\n * two short fields side-by-side inside an otherwise stacked form (e.g. CEP +\n * city, expiry month + year).\n *\n * @example\n * <Form layout=\"stack\">\n * <FormRow>\n * <Input label=\"CEP\" />\n * <Input label=\"Cidade\" />\n * </FormRow>\n * <Input label=\"Endereço completo\" />\n * </Form>\n */\nexport function FormRow({ gap, className, style, children, ...props }: FormRowProps) {\n const finalStyle: CSSProperties = { gap: resolveGap(gap, 3), ...style };\n return (\n <div className={cn(styles.row, className)} style={finalStyle} {...props}>\n {children}\n </div>\n );\n}\n\nexport interface FormActionsProps extends HTMLAttributes<HTMLDivElement> {\n /** Horizontal alignment of buttons. Default `end`. */\n align?: FormActionsAlign;\n /** Gap between buttons. Numbers map to a multiple of the 4px scale (default `2` → 8px). */\n gap?: number | string;\n}\n\n/**\n * Footer button row for a form. Use inside (or after) [[Form]] / [[FormSection]].\n *\n * @example\n * <FormActions align=\"end\">\n * <Button variant=\"ghost\" onClick={onCancel}>Cancelar</Button>\n * <Button type=\"submit\" loading={form.formState.isSubmitting}>Salvar</Button>\n * </FormActions>\n */\nexport function FormActions({\n align = \"end\",\n gap,\n className,\n style,\n children,\n ...props\n}: FormActionsProps) {\n const finalStyle: CSSProperties = { gap: resolveGap(gap, 2), ...style };\n return (\n <div className={cn(styles.actions, styles[align], className)} style={finalStyle} {...props}>\n {children}\n </div>\n );\n}\n"],"mappings":"2HAiBA,SAAS,EAAW,EAAkC,EAA0B,CAE5E,OADI,IAAQ,IAAA,GAAkB,GAAG,EAAW,EAAE,IACvC,OAAO,GAAQ,SAAW,GAAG,EAAM,EAAE,IAAM,CACtD,CAEA,SAAS,EAAe,EAA8C,CAElE,OADI,IAAY,IAAA,GAAkB,4BAC3B,OAAO,GAAY,SAAW,UAAU,EAAQ,mBAAqB,CAChF,CAkBA,IAAa,GAAA,EAAO,EAAA,WAAA,CAAuC,SACvD,CAAE,SAAS,QAAS,UAAS,MAAK,YAAW,QAAO,WAAU,GAAG,GACjE,EACF,CACE,IAAM,EAA4B,CAC9B,IAAK,EAAW,EAAK,CAAC,EACtB,GAAI,IAAW,OAAS,CAAE,oBAAqB,EAAe,CAAO,CAAE,EAAI,KAC3E,GAAG,CACP,EACA,OACI,EAAA,EAAA,IAAA,CAAC,OAAD,CACS,MACL,UAAW,EAAA,GAAG,EAAA,QAAO,KAAM,EAAA,QAAO,GAAS,CAAS,EACpD,MAAO,EACP,GAAI,EAEH,UACC,CAAA,CAEd,CAAC,EA0BD,SAAgB,EAAY,CACxB,QACA,cACA,SAAS,QACT,UACA,MACA,YACA,QACA,WACA,GAAG,GACc,CACjB,IAAM,EAA2B,CAC7B,IAAK,EAAW,EAAK,CAAC,EACtB,GAAI,IAAW,OAAS,CAAE,oBAAqB,EAAe,CAAO,CAAE,EAAI,IAC/E,EACA,OACI,EAAA,EAAA,KAAA,CAAC,UAAD,CAAS,UAAW,EAAA,GAAG,EAAA,QAAO,QAAS,CAAS,EAAU,QAAO,GAAI,EAArE,SAAA,EACM,GAAS,KACP,EAAA,EAAA,KAAA,CAAC,SAAD,CAAQ,UAAW,EAAA,QAAO,cAA1B,SAAA,CACK,IAAS,EAAA,EAAA,IAAA,CAAC,KAAD,CAAI,UAAW,EAAA,QAAO,aAAe,SAAA,CAAU,CAAA,EACxD,IAAe,EAAA,EAAA,IAAA,CAAC,IAAD,CAAG,UAAW,EAAA,QAAO,mBAAqB,SAAA,CAAe,CAAA,CACrE,CAEZ,CAAA,GAAA,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,UAAW,EAAA,GAAG,EAAA,QAAO,YAAa,EAAA,QAAO,EAAO,EAAG,MAAO,EAC1D,UACA,CAAA,CACA,GAEjB,CAqBA,SAAgB,EAAQ,CAAE,MAAK,YAAW,QAAO,WAAU,GAAG,GAAuB,CACjF,IAAM,EAA4B,CAAE,IAAK,EAAW,EAAK,CAAC,EAAG,GAAG,CAAM,EACtE,OACI,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,UAAW,EAAA,GAAG,EAAA,QAAO,IAAK,CAAS,EAAG,MAAO,EAAY,GAAI,EAC7D,UACA,CAAA,CAEb,CAkBA,SAAgB,EAAY,CACxB,QAAQ,MACR,MACA,YACA,QACA,WACA,GAAG,GACc,CACjB,IAAM,EAA4B,CAAE,IAAK,EAAW,EAAK,CAAC,EAAG,GAAG,CAAM,EACtE,OACI,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,UAAW,EAAA,GAAG,EAAA,QAAO,QAAS,EAAA,QAAO,GAAQ,CAAS,EAAG,MAAO,EAAY,GAAI,EAChF,UACA,CAAA,CAEb"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Form.js","names":[],"sources":["../../../src/components/Form/Form.tsx"],"sourcesContent":["import { forwardRef } from \"react\";\nimport type { CSSProperties, FormHTMLAttributes, HTMLAttributes, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Form.module.css\";\n\nexport type FormLayout = \"stack\" | \"inline\" | \"grid\";\nexport type FormActionsAlign = \"start\" | \"center\" | \"end\" | \"between\";\n\ninterface LayoutProps {\n /** Field arrangement. `stack` stacks fields vertically, `inline` lines them up in a wrapping row, `grid` arranges them in `columns` equal-width tracks. Default `stack`. */\n layout?: FormLayout;\n /** Number of columns when `layout=\"grid\"`, or a custom `grid-template-columns` value. Default `2`. */\n columns?: number | string;\n /** Gap between fields. Numbers map to a multiple of the 4px scale (default `4` → 16px). */\n gap?: number | string;\n}\n\nfunction resolveGap(gap: number | string | undefined, fallback: number): string {\n if (gap === undefined) return `${fallback * 4}px`;\n return typeof gap === \"number\" ? `${gap * 4}px` : gap;\n}\n\nfunction resolveColumns(columns: number | string | undefined): string {\n if (columns === undefined) return \"repeat(2, minmax(0, 1fr))\";\n return typeof columns === \"number\" ? `repeat(${columns}, minmax(0, 1fr))` : columns;\n}\n\nexport interface FormProps extends FormHTMLAttributes<HTMLFormElement>, LayoutProps {}\n\n/**\n * Form wrapper with a built-in layout variant. Replaces ad-hoc `<form>` +\n * `<Stack>` / `<Grid>` boilerplate; pair with [[FormRow]], [[FormSection]],\n * and [[FormActions]] for richer layouts.\n *\n * @example\n * <Form layout=\"grid\" columns={2} gap={4} onSubmit={form.handleSubmit(save)}>\n * <Input label=\"Nome\" {...form.register(\"name\")} />\n * <Input label=\"Email\" {...form.register(\"email\")} />\n * <FormActions align=\"end\">\n * <Button type=\"submit\">Salvar</Button>\n * </FormActions>\n * </Form>\n */\nexport const Form = forwardRef<HTMLFormElement, FormProps>(function Form(\n { layout = \"stack\", columns, gap, className, style, children, ...props },\n ref,\n) {\n const finalStyle: CSSProperties = {\n gap: resolveGap(gap, 4),\n ...(layout === \"grid\" ? { gridTemplateColumns: resolveColumns(columns) } : null),\n ...style,\n };\n return (\n <form\n ref={ref}\n className={cn(styles.form, styles[layout], className)}\n style={finalStyle}\n {...props}\n >\n {children}\n </form>\n );\n});\n\nexport interface FormSectionProps extends Omit<HTMLAttributes<HTMLElement>, \"title\">, LayoutProps {\n title?: ReactNode;\n description?: ReactNode;\n}\n\n/**\n * Visually grouped subset of fields with an optional title and description.\n * Has its own `layout`/`columns`/`gap` so subsets can stack while the parent\n * grids (or vice versa).\n *\n * @example\n * <FormSection title=\"Endereço\" description=\"Usado para entrega\" layout=\"grid\" columns={3}>\n * <Input label=\"CEP\" />\n * <Input label=\"Rua\" />\n * <Input label=\"Número\" />\n * </FormSection>\n */\nexport function FormSection({\n title,\n description,\n layout = \"stack\",\n columns,\n gap,\n className,\n style,\n children,\n ...props\n}: FormSectionProps) {\n const bodyStyle: CSSProperties = {\n gap: resolveGap(gap, 4),\n ...(layout === \"grid\" ? { gridTemplateColumns: resolveColumns(columns) } : null),\n };\n return (\n <section className={cn(styles.section, className)} style={style} {...props}>\n {(title || description) && (\n <header className={styles.sectionHeader}>\n {title && <h3 className={styles.sectionTitle}>{title}</h3>}\n {description && <p className={styles.sectionDescription}>{description}</p>}\n </header>\n )}\n <div className={cn(styles.sectionBody, styles[layout])} style={bodyStyle}>\n {children}\n </div>\n </section>\n );\n}\n\nexport interface FormRowProps extends HTMLAttributes<HTMLDivElement> {\n /** Gap between row items. Numbers map to a multiple of the 4px scale (default `3` → 12px). */\n gap?: number | string;\n}\n\n/**\n * Forces a horizontal row regardless of parent layout — useful for grouping\n * two short fields side-by-side inside an otherwise stacked form (e.g. CEP +\n * city, expiry month + year).\n *\n * @example\n * <Form layout=\"stack\">\n * <FormRow>\n * <Input label=\"CEP\" />\n * <Input label=\"Cidade\" />\n * </FormRow>\n * <Input label=\"Endereço completo\" />\n * </Form>\n */\nexport function FormRow({ gap, className, style, children, ...props }: FormRowProps) {\n const finalStyle: CSSProperties = { gap: resolveGap(gap, 3), ...style };\n return (\n <div className={cn(styles.row, className)} style={finalStyle} {...props}>\n {children}\n </div>\n );\n}\n\nexport interface FormActionsProps extends HTMLAttributes<HTMLDivElement> {\n /** Horizontal alignment of buttons. Default `end`. */\n align?: FormActionsAlign;\n /** Gap between buttons. Numbers map to a multiple of the 4px scale (default `2` → 8px). */\n gap?: number | string;\n}\n\n/**\n * Footer button row for a form. Use inside (or after) [[Form]] / [[FormSection]].\n *\n * @example\n * <FormActions align=\"end\">\n * <Button variant=\"ghost\" onClick={onCancel}>Cancelar</Button>\n * <Button type=\"submit\" loading={form.formState.isSubmitting}>Salvar</Button>\n * </FormActions>\n */\nexport function FormActions({\n align = \"end\",\n gap,\n className,\n style,\n children,\n ...props\n}: FormActionsProps) {\n const finalStyle: CSSProperties = { gap: resolveGap(gap, 2), ...style };\n return (\n <div className={cn(styles.actions, styles[align], className)} style={finalStyle} {...props}>\n {children}\n </div>\n );\n}\n"],"mappings":";;;;;AAiBA,SAAS,EAAW,GAAkC,GAA0B;CAE5E,OADI,MAAQ,KAAA,IAAkB,GAAG,IAAW,EAAE,MACvC,OAAO,KAAQ,WAAW,GAAG,IAAM,EAAE,MAAM;AACtD;AAEA,SAAS,EAAe,GAA8C;CAElE,OADI,MAAY,KAAA,IAAkB,8BAC3B,OAAO,KAAY,WAAW,UAAU,EAAQ,qBAAqB;AAChF;AAkBA,IAAa,IAAO,EAAuC,SACvD,EAAE,YAAS,SAAS,YAAS,QAAK,cAAW,UAAO,aAAU,GAAG,KACjE,GACF;CACE,IAAM,IAA4B;EAC9B,KAAK,EAAW,GAAK,CAAC;EACtB,GAAI,MAAW,SAAS,EAAE,qBAAqB,EAAe,CAAO,EAAE,IAAI;EAC3E,GAAG;CACP;CACA,OACI,kBAAC,QAAD;EACS;EACL,WAAW,EAAG,EAAO,MAAM,EAAO,IAAS,CAAS;EACpD,OAAO;EACP,GAAI;EAEH;CACC,CAAA;AAEd,CAAC;
|
|
1
|
+
{"version":3,"file":"Form.js","names":[],"sources":["../../../src/components/Form/Form.tsx"],"sourcesContent":["import { forwardRef } from \"react\";\nimport type { CSSProperties, FormHTMLAttributes, HTMLAttributes, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Form.module.css\";\n\nexport type FormLayout = \"stack\" | \"inline\" | \"grid\";\nexport type FormActionsAlign = \"start\" | \"center\" | \"end\" | \"between\";\n\ninterface LayoutProps {\n /** Field arrangement. `stack` stacks fields vertically, `inline` lines them up in a wrapping row, `grid` arranges them in `columns` equal-width tracks. Default `stack`. */\n layout?: FormLayout;\n /** Number of columns when `layout=\"grid\"`, or a custom `grid-template-columns` value. Default `2`. */\n columns?: number | string;\n /** Gap between fields. Numbers map to a multiple of the 4px scale (default `4` → 16px). */\n gap?: number | string;\n}\n\nfunction resolveGap(gap: number | string | undefined, fallback: number): string {\n if (gap === undefined) return `${fallback * 4}px`;\n return typeof gap === \"number\" ? `${gap * 4}px` : gap;\n}\n\nfunction resolveColumns(columns: number | string | undefined): string {\n if (columns === undefined) return \"repeat(2, minmax(0, 1fr))\";\n return typeof columns === \"number\" ? `repeat(${columns}, minmax(0, 1fr))` : columns;\n}\n\nexport interface FormProps extends FormHTMLAttributes<HTMLFormElement>, LayoutProps {}\n\n/**\n * Form wrapper with a built-in layout variant. Replaces ad-hoc `<form>` +\n * `<Stack>` / `<Grid>` boilerplate; pair with [[FormRow]], [[FormSection]],\n * and [[FormActions]] for richer layouts.\n *\n * @example\n * <Form layout=\"grid\" columns={2} gap={4} onSubmit={form.handleSubmit(save)}>\n * <Input label=\"Nome\" {...form.register(\"name\")} />\n * <Input label=\"Email\" {...form.register(\"email\")} />\n * <FormActions align=\"end\">\n * <Button type=\"submit\">Salvar</Button>\n * </FormActions>\n * </Form>\n */\nexport const Form = forwardRef<HTMLFormElement, FormProps>(function Form(\n { layout = \"stack\", columns, gap, className, style, children, ...props },\n ref,\n) {\n const finalStyle: CSSProperties = {\n gap: resolveGap(gap, 4),\n ...(layout === \"grid\" ? { gridTemplateColumns: resolveColumns(columns) } : null),\n ...style,\n };\n return (\n <form\n ref={ref}\n className={cn(styles.form, styles[layout], className)}\n style={finalStyle}\n {...props}\n >\n {children}\n </form>\n );\n});\n\nexport interface FormSectionProps extends Omit<HTMLAttributes<HTMLElement>, \"title\">, LayoutProps {\n title?: ReactNode;\n description?: ReactNode;\n}\n\n/**\n * Visually grouped subset of fields with an optional title and description.\n * Has its own `layout`/`columns`/`gap` so subsets can stack while the parent\n * grids (or vice versa).\n *\n * The header is deliberately not a `SectionHeader`: a field group sits one step\n * below a page section, and its title is `text-lg` / semibold / `leading-snug`\n * with `space-3` below, against `text-2xl` / bold / `leading-tight` with\n * `space-5` for `SectionHeader`. Sharing the component would either grow every\n * form group to page-section size or add a size switch to `SectionHeader` that\n * only this caller uses.\n *\n * @example\n * <FormSection title=\"Endereço\" description=\"Usado para entrega\" layout=\"grid\" columns={3}>\n * <Input label=\"CEP\" />\n * <Input label=\"Rua\" />\n * <Input label=\"Número\" />\n * </FormSection>\n */\nexport function FormSection({\n title,\n description,\n layout = \"stack\",\n columns,\n gap,\n className,\n style,\n children,\n ...props\n}: FormSectionProps) {\n const bodyStyle: CSSProperties = {\n gap: resolveGap(gap, 4),\n ...(layout === \"grid\" ? { gridTemplateColumns: resolveColumns(columns) } : null),\n };\n return (\n <section className={cn(styles.section, className)} style={style} {...props}>\n {(title || description) && (\n <header className={styles.sectionHeader}>\n {title && <h3 className={styles.sectionTitle}>{title}</h3>}\n {description && <p className={styles.sectionDescription}>{description}</p>}\n </header>\n )}\n <div className={cn(styles.sectionBody, styles[layout])} style={bodyStyle}>\n {children}\n </div>\n </section>\n );\n}\n\nexport interface FormRowProps extends HTMLAttributes<HTMLDivElement> {\n /** Gap between row items. Numbers map to a multiple of the 4px scale (default `3` → 12px). */\n gap?: number | string;\n}\n\n/**\n * Forces a horizontal row regardless of parent layout — useful for grouping\n * two short fields side-by-side inside an otherwise stacked form (e.g. CEP +\n * city, expiry month + year).\n *\n * @example\n * <Form layout=\"stack\">\n * <FormRow>\n * <Input label=\"CEP\" />\n * <Input label=\"Cidade\" />\n * </FormRow>\n * <Input label=\"Endereço completo\" />\n * </Form>\n */\nexport function FormRow({ gap, className, style, children, ...props }: FormRowProps) {\n const finalStyle: CSSProperties = { gap: resolveGap(gap, 3), ...style };\n return (\n <div className={cn(styles.row, className)} style={finalStyle} {...props}>\n {children}\n </div>\n );\n}\n\nexport interface FormActionsProps extends HTMLAttributes<HTMLDivElement> {\n /** Horizontal alignment of buttons. Default `end`. */\n align?: FormActionsAlign;\n /** Gap between buttons. Numbers map to a multiple of the 4px scale (default `2` → 8px). */\n gap?: number | string;\n}\n\n/**\n * Footer button row for a form. Use inside (or after) [[Form]] / [[FormSection]].\n *\n * @example\n * <FormActions align=\"end\">\n * <Button variant=\"ghost\" onClick={onCancel}>Cancelar</Button>\n * <Button type=\"submit\" loading={form.formState.isSubmitting}>Salvar</Button>\n * </FormActions>\n */\nexport function FormActions({\n align = \"end\",\n gap,\n className,\n style,\n children,\n ...props\n}: FormActionsProps) {\n const finalStyle: CSSProperties = { gap: resolveGap(gap, 2), ...style };\n return (\n <div className={cn(styles.actions, styles[align], className)} style={finalStyle} {...props}>\n {children}\n </div>\n );\n}\n"],"mappings":";;;;;AAiBA,SAAS,EAAW,GAAkC,GAA0B;CAE5E,OADI,MAAQ,KAAA,IAAkB,GAAG,IAAW,EAAE,MACvC,OAAO,KAAQ,WAAW,GAAG,IAAM,EAAE,MAAM;AACtD;AAEA,SAAS,EAAe,GAA8C;CAElE,OADI,MAAY,KAAA,IAAkB,8BAC3B,OAAO,KAAY,WAAW,UAAU,EAAQ,qBAAqB;AAChF;AAkBA,IAAa,IAAO,EAAuC,SACvD,EAAE,YAAS,SAAS,YAAS,QAAK,cAAW,UAAO,aAAU,GAAG,KACjE,GACF;CACE,IAAM,IAA4B;EAC9B,KAAK,EAAW,GAAK,CAAC;EACtB,GAAI,MAAW,SAAS,EAAE,qBAAqB,EAAe,CAAO,EAAE,IAAI;EAC3E,GAAG;CACP;CACA,OACI,kBAAC,QAAD;EACS;EACL,WAAW,EAAG,EAAO,MAAM,EAAO,IAAS,CAAS;EACpD,OAAO;EACP,GAAI;EAEH;CACC,CAAA;AAEd,CAAC;AA0BD,SAAgB,EAAY,EACxB,UACA,gBACA,YAAS,SACT,YACA,QACA,cACA,UACA,aACA,GAAG,KACc;CACjB,IAAM,IAA2B;EAC7B,KAAK,EAAW,GAAK,CAAC;EACtB,GAAI,MAAW,SAAS,EAAE,qBAAqB,EAAe,CAAO,EAAE,IAAI;CAC/E;CACA,OACI,kBAAC,WAAD;EAAS,WAAW,EAAG,EAAO,SAAS,CAAS;EAAU;EAAO,GAAI;EAArE,UAAA,EACM,KAAS,MACP,kBAAC,UAAD;GAAQ,WAAW,EAAO;GAA1B,UAAA,CACK,KAAS,kBAAC,MAAD;IAAI,WAAW,EAAO;IAAe,UAAA;GAAU,CAAA,GACxD,KAAe,kBAAC,KAAD;IAAG,WAAW,EAAO;IAAqB,UAAA;GAAe,CAAA,CACrE;EAEZ,CAAA,GAAA,kBAAC,OAAD;GAAK,WAAW,EAAG,EAAO,aAAa,EAAO,EAAO;GAAG,OAAO;GAC1D;EACA,CAAA,CACA;;AAEjB;AAqBA,SAAgB,EAAQ,EAAE,QAAK,cAAW,UAAO,aAAU,GAAG,KAAuB;CACjF,IAAM,IAA4B;EAAE,KAAK,EAAW,GAAK,CAAC;EAAG,GAAG;CAAM;CACtE,OACI,kBAAC,OAAD;EAAK,WAAW,EAAG,EAAO,KAAK,CAAS;EAAG,OAAO;EAAY,GAAI;EAC7D;CACA,CAAA;AAEb;AAkBA,SAAgB,EAAY,EACxB,WAAQ,OACR,QACA,cACA,UACA,aACA,GAAG,KACc;CACjB,IAAM,IAA4B;EAAE,KAAK,EAAW,GAAK,CAAC;EAAG,GAAG;CAAM;CACtE,OACI,kBAAC,OAAD;EAAK,WAAW,EAAG,EAAO,SAAS,EAAO,IAAQ,CAAS;EAAG,OAAO;EAAY,GAAI;EAChF;CACA,CAAA;AAEb"}
|