@elmeragroup/fuse 0.1.0-canary.13 → 0.1.0-canary.14
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/components/button/button-variants.d.ts.map +1 -1
- package/components/button/button-variants.js +1 -1
- package/components/button/button-variants.js.map +1 -1
- package/components/button/button.d.ts +19 -4
- package/components/button/button.d.ts.map +1 -1
- package/components/button/button.js +18 -4
- package/components/button/button.js.map +1 -1
- package/components/field/field-frame.js +1 -1
- package/components/radio-group/radio-group.js +1 -1
- package/components/toast/toast.js +1 -1
- package/icons.js +1 -1
- package/package.json +2 -2
- package/styles.css +3 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"button-variants.d.ts","names":[],"sources":["../../../src/components/button/button-variants.ts"],"mappings":";
|
|
1
|
+
{"version":3,"file":"button-variants.d.ts","names":[],"sources":["../../../src/components/button/button-variants.ts"],"mappings":";qBAmCa,4CAAc"}
|
|
@@ -5,7 +5,7 @@ import { controlSize } from "../../styles/control-size.js";
|
|
|
5
5
|
import { tv } from "tailwind-variants";
|
|
6
6
|
//#region src/components/button/button-variants.ts
|
|
7
7
|
const buttonVariants = tv({
|
|
8
|
-
base: cn("group/button font-medium box-border inline-flex shrink-0 cursor-pointer items-center justify-center rounded-(--radius-button) border border-transparent bg-clip-padding p-0 whitespace-nowrap transition-[color,background-color,border-color,box-shadow,translate,opacity] select-none in-data-[slot=button-group]:rounded-md enabled-active:not-aria-[haspopup]:translate-y-px [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", selfFocusRingClass, nativeStateFaceClass, dataStateFaceClass),
|
|
8
|
+
base: cn("group/button font-medium box-border inline-flex shrink-0 cursor-pointer items-center justify-center rounded-(--radius-button) border border-transparent bg-clip-padding p-0 whitespace-nowrap transition-[color,background-color,border-color,box-shadow,translate,opacity] select-none in-data-[slot=button-group]:rounded-md enabled-active:not-aria-[haspopup]:translate-y-px [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 data-[pending-indicator]:[&>svg:not([data-icon=inline-end])]:hidden", selfFocusRingClass, nativeStateFaceClass, dataStateFaceClass),
|
|
9
9
|
variants: {
|
|
10
10
|
variant: {
|
|
11
11
|
default: "bg-primary text-primary-foreground enabled-hover:bg-primary/80",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"button-variants.js","names":[],"sources":["../../../src/components/button/button-variants.ts"],"sourcesContent":["import { tv } from \"tailwind-variants\";\n\nimport { cn } from \"../../styles/cn\";\nimport { controlSize } from \"../../styles/control-size\";\nimport { dataStateFaceClass, nativeStateFaceClass } from \"../../styles/state-face\";\nimport { selfFocusRingClass } from \"../../styles/utils\";\n\n// Runtime-free recipe so other components can borrow it without Button's client graph.\n// The state face composes two targets. `native` covers a native <button> that borrows\n// the recipe, and `data` covers Base UI roots that never match `:disabled`, such as\n// `render={<a />}` or `focusableWhenDisabled`. Both carry the `aria-disabled` arm, which\n// covers `isVisuallyDisabled` and a consumer's own `aria-disabled`. No target drops pointer events, so a Tooltip on a\n// disabled button still opens, and Base UI already cancels clicks on a disabled root.\n// Hover and press styles use the `enabled-hover:` and `enabled-active:` variants from\n// fuse.css, so a disabled root never changes fill, border, text or position under the\n// pointer. `aria-expanded:` follows an open popup, not the pointer, so it has no gate.\n// The text and icon sizes take their box from the control-size recipe: `default` is the md\n// label, `icon*` the squares. The xs glyph size and `icon-inline`, a square as tall as the\n// surrounding line that follows no density, stay local.\n// Every size rounds with the theme's `--radius-button`. The arbitrary value keeps\n// tailwind-merge able to replace it with a consumer `rounded-*` class, which the custom\n// `rounded-button` utility would not be.\n// Inside a ButtonGroup a button keeps the group radius, `rounded-md`, not the button role.\n// The group joins its buttons edge to edge, often with inputs and text, into one bar that\n// rounds like a field, and a pill button would bulge out of that outline.\n// The pointer cursor is the library's policy for an interactive control, as on the\n// Accordion trigger and the selection labels. The state face's `disabled:`,\n// `data-disabled:` and `aria-disabled:` cursors are variants, so they sort after this\n// plain utility and a disabled button keeps `not-allowed`.\nexport const buttonVariants = tv({\n base: cn(\n \"group/button font-medium box-border inline-flex shrink-0 cursor-pointer items-center justify-center rounded-(--radius-button) border border-transparent bg-clip-padding p-0 whitespace-nowrap transition-[color,background-color,border-color,box-shadow,translate,opacity] select-none in-data-[slot=button-group]:rounded-md enabled-active:not-aria-[haspopup]:translate-y-px [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n selfFocusRingClass,\n nativeStateFaceClass,\n dataStateFaceClass\n ),\n variants: {\n variant: {\n default: \"bg-primary text-primary-foreground enabled-hover:bg-primary/80\",\n outline:\n \"shadow-xs border-border bg-background aria-expanded:bg-muted aria-expanded:text-foreground enabled-hover:bg-muted enabled-hover:text-foreground\",\n secondary:\n \"bg-secondary text-secondary-foreground aria-expanded:bg-secondary aria-expanded:text-secondary-foreground enabled-hover:bg-secondary-hover\",\n ghost:\n \"aria-expanded:bg-muted aria-expanded:text-foreground enabled-hover:bg-muted enabled-hover:text-foreground\",\n destructive:\n \"border-error/20 bg-error/10 text-error enabled-hover:border-error enabled-hover:bg-error/20\",\n success:\n \"border-success/20 bg-success/10 text-success enabled-hover:border-success enabled-hover:bg-success/20\",\n link: \"text-primary underline-offset-4 enabled-hover:underline\",\n },\n size: {\n default: controlSize({ size: \"md\", fit: \"label\" }),\n xs: controlSize({ size: \"xs\", fit: \"label\", class: \"[&_svg:not([class*='size-'])]:size-3\" }),\n sm: controlSize({ size: \"sm\", fit: \"label\" }),\n lg: controlSize({ size: \"lg\", fit: \"label\" }),\n icon: controlSize({ size: \"md\", fit: \"square\" }),\n \"icon-xs\": controlSize({ size: \"xs\", fit: \"square\", class: \"[&_svg:not([class*='size-'])]:size-3\" }),\n \"icon-sm\": controlSize({ size: \"sm\", fit: \"square\" }),\n \"icon-inline\": \"hit-area-1 aspect-square h-lh w-auto\",\n \"icon-lg\": controlSize({ size: \"lg\", fit: \"square\" }),\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n});\n"],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"button-variants.js","names":[],"sources":["../../../src/components/button/button-variants.ts"],"sourcesContent":["import { tv } from \"tailwind-variants\";\n\nimport { cn } from \"../../styles/cn\";\nimport { controlSize } from \"../../styles/control-size\";\nimport { dataStateFaceClass, nativeStateFaceClass } from \"../../styles/state-face\";\nimport { selfFocusRingClass } from \"../../styles/utils\";\n\n// Runtime-free recipe so other components can borrow it without Button's client graph.\n// The state face composes two targets. `native` covers a native <button> that borrows\n// the recipe, and `data` covers Base UI roots that never match `:disabled`, such as\n// `render={<a />}` or `focusableWhenDisabled`. Both carry the `aria-disabled` arm, which\n// covers `isVisuallyDisabled` and a consumer's own `aria-disabled`. No target drops pointer events, so a Tooltip on a\n// disabled button still opens, and Base UI already cancels clicks on a disabled root.\n// Hover and press styles use the `enabled-hover:` and `enabled-active:` variants from\n// fuse.css, so a disabled root never changes fill, border, text or position under the\n// pointer. `aria-expanded:` follows an open popup, not the pointer, so it has no gate.\n// The text and icon sizes take their box from the control-size recipe: `default` is the md\n// label, `icon*` the squares. The xs glyph size and `icon-inline`, a square as tall as the\n// surrounding line that follows no density, stay local.\n// Every size rounds with the theme's `--radius-button`. The arbitrary value keeps\n// tailwind-merge able to replace it with a consumer `rounded-*` class, which the custom\n// `rounded-button` utility would not be.\n// Inside a ButtonGroup a button keeps the group radius, `rounded-md`, not the button role.\n// The group joins its buttons edge to edge, often with inputs and text, into one bar that\n// rounds like a field, and a pill button would bulge out of that outline.\n// The pointer cursor is the library's policy for an interactive control, as on the\n// Accordion trigger and the selection labels. The state face's `disabled:`,\n// `data-disabled:` and `aria-disabled:` cursors are variants, so they sort after this\n// plain utility and a disabled button keeps `not-allowed`.\n// While Button renders a pending indicator it stamps `data-pending-indicator`, and the\n// recipe then hides the button's own leading and bare direct SVG children, so the indicator\n// takes a leading icon's place, an icon-only square shows the indicator alone, and a spinner\n// a consumer still places by hand is not doubled. A trailing `data-icon=\"inline-end\"` icon\n// stays, as its marker keeps the end inset tight either way, and text stays. With\n// `pendingIndicator={null}` the attribute is absent and nothing hides.\nexport const buttonVariants = tv({\n base: cn(\n \"group/button font-medium box-border inline-flex shrink-0 cursor-pointer items-center justify-center rounded-(--radius-button) border border-transparent bg-clip-padding p-0 whitespace-nowrap transition-[color,background-color,border-color,box-shadow,translate,opacity] select-none in-data-[slot=button-group]:rounded-md enabled-active:not-aria-[haspopup]:translate-y-px [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 data-[pending-indicator]:[&>svg:not([data-icon=inline-end])]:hidden\",\n selfFocusRingClass,\n nativeStateFaceClass,\n dataStateFaceClass\n ),\n variants: {\n variant: {\n default: \"bg-primary text-primary-foreground enabled-hover:bg-primary/80\",\n outline:\n \"shadow-xs border-border bg-background aria-expanded:bg-muted aria-expanded:text-foreground enabled-hover:bg-muted enabled-hover:text-foreground\",\n secondary:\n \"bg-secondary text-secondary-foreground aria-expanded:bg-secondary aria-expanded:text-secondary-foreground enabled-hover:bg-secondary-hover\",\n ghost:\n \"aria-expanded:bg-muted aria-expanded:text-foreground enabled-hover:bg-muted enabled-hover:text-foreground\",\n destructive:\n \"border-error/20 bg-error/10 text-error enabled-hover:border-error enabled-hover:bg-error/20\",\n success:\n \"border-success/20 bg-success/10 text-success enabled-hover:border-success enabled-hover:bg-success/20\",\n link: \"text-primary underline-offset-4 enabled-hover:underline\",\n },\n size: {\n default: controlSize({ size: \"md\", fit: \"label\" }),\n xs: controlSize({ size: \"xs\", fit: \"label\", class: \"[&_svg:not([class*='size-'])]:size-3\" }),\n sm: controlSize({ size: \"sm\", fit: \"label\" }),\n lg: controlSize({ size: \"lg\", fit: \"label\" }),\n icon: controlSize({ size: \"md\", fit: \"square\" }),\n \"icon-xs\": controlSize({ size: \"xs\", fit: \"square\", class: \"[&_svg:not([class*='size-'])]:size-3\" }),\n \"icon-sm\": controlSize({ size: \"sm\", fit: \"square\" }),\n \"icon-inline\": \"hit-area-1 aspect-square h-lh w-auto\",\n \"icon-lg\": controlSize({ size: \"lg\", fit: \"square\" }),\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n});\n"],"mappings":";;;;;;AAmCA,MAAa,iBAAiB,GAAG;CAC/B,MAAM,GACJ,0gBACA,oBACA,sBACA,kBACF;CACA,UAAU;EACR,SAAS;GACP,SAAS;GACT,SACE;GACF,WACE;GACF,OACE;GACF,aACE;GACF,SACE;GACF,MAAM;EACR;EACA,MAAM;GACJ,SAAS,YAAY;IAAE,MAAM;IAAM,KAAK;GAAQ,CAAC;GACjD,IAAI,YAAY;IAAE,MAAM;IAAM,KAAK;IAAS,OAAO;GAAuC,CAAC;GAC3F,IAAI,YAAY;IAAE,MAAM;IAAM,KAAK;GAAQ,CAAC;GAC5C,IAAI,YAAY;IAAE,MAAM;IAAM,KAAK;GAAQ,CAAC;GAC5C,MAAM,YAAY;IAAE,MAAM;IAAM,KAAK;GAAS,CAAC;GAC/C,WAAW,YAAY;IAAE,MAAM;IAAM,KAAK;IAAU,OAAO;GAAuC,CAAC;GACnG,WAAW,YAAY;IAAE,MAAM;IAAM,KAAK;GAAS,CAAC;GACpD,eAAe;GACf,WAAW,YAAY;IAAE,MAAM;IAAM,KAAK;GAAS,CAAC;EACtD;CACF;CACA,iBAAiB;EACf,SAAS;EACT,MAAM;CACR;AACF,CAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { buttonVariants } from "./button-variants.js";
|
|
2
|
-
import { ComponentProps, ReactElement } from "react";
|
|
2
|
+
import { ComponentProps, ReactElement, ReactNode } from "react";
|
|
3
3
|
import { VariantProps } from "tailwind-variants";
|
|
4
4
|
import { Button as Button$1 } from "@base-ui/react/button";
|
|
5
5
|
//#region src/components/button/button.d.ts
|
|
@@ -24,10 +24,25 @@ type ButtonSharedProps = ButtonPrimitiveProps & Omit<VariantProps<typeof buttonV
|
|
|
24
24
|
*/
|
|
25
25
|
isVisuallyDisabled?: boolean;
|
|
26
26
|
/**
|
|
27
|
-
*
|
|
28
|
-
*
|
|
27
|
+
* The in-flight state of an async action. Stamps `data-pending` and `aria-busy`, blocks
|
|
28
|
+
* activation (effective disabled is `disabled || isPending`) and renders the disabled
|
|
29
|
+
* treatment, but keeps the button in the focus order like `focusableWhenDisabled`, so a
|
|
30
|
+
* keyboard user who activated it is still on it when the result arrives. Shows the
|
|
31
|
+
* `pendingIndicator` in the leading icon position meanwhile. An explicit
|
|
32
|
+
* `focusableWhenDisabled={false}` restores the native `disabled` attribute while pending.
|
|
33
|
+
* `aria-busy` announces nothing by itself: say what is happening through the label
|
|
34
|
+
* ("Saving") or a `role="status"` region outside the button.
|
|
29
35
|
*/
|
|
30
36
|
isPending?: boolean;
|
|
37
|
+
/**
|
|
38
|
+
* What the button shows while pending. Omitted: a spinning `SpinnerGap` in the leading icon
|
|
39
|
+
* position, and the button's own leading and bare SVG children hide so the spinner takes
|
|
40
|
+
* their place (a trailing `data-icon="inline-end"` icon stays). A node: rendered in that same
|
|
41
|
+
* place instead of the spinner, as a decorative `aria-hidden` slot; animate it yourself. `null`
|
|
42
|
+
* or `false`: no indicator and nothing hidden, so the call site owns the pending markup, for
|
|
43
|
+
* example a brand button that keeps its logo and centres its own spinner over it.
|
|
44
|
+
*/
|
|
45
|
+
pendingIndicator?: ReactNode;
|
|
31
46
|
/** Pixels the hit rect is inflated on every side when predicting pointer intent. */
|
|
32
47
|
predictionZoneSize?: number;
|
|
33
48
|
/**
|
|
@@ -60,7 +75,7 @@ export type IconButtonProps = ButtonSharedProps & {
|
|
|
60
75
|
* {@link IconButtonProps} instead, or distribute the omit over the union.
|
|
61
76
|
*/
|
|
62
77
|
export type ButtonProps = LabelButtonProps | IconButtonProps;
|
|
63
|
-
export declare function Button({ className, variant, size, isVisuallyDisabled, disabled, isPending, predictionZoneSize, onIntent, onMouseDown, ref, ...props }: ButtonProps): ReactElement;
|
|
78
|
+
export declare function Button({ className, variant, size, isVisuallyDisabled, disabled, isPending, pendingIndicator, predictionZoneSize, onIntent, onMouseDown, children, ref, ...props }: ButtonProps): ReactElement;
|
|
64
79
|
export declare namespace Button {
|
|
65
80
|
var displayName: string;
|
|
66
81
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"button.d.ts","names":[],"sources":["../../../src/components/button/button.tsx"],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"button.d.ts","names":[],"sources":["../../../src/components/button/button.tsx"],"mappings":";;;;;;YAeY,aAAa,YAAY,oBAAoB;;YAG7C,iBAAiB,QAAQ;;YAGzB,kBAAkB,QAAQ,YAAY;KAE7C,uBAAuB,KAAK,sBAAsB;KAElD,oBAAoB,uBACvB,KAAK,oBAAoB;;EAEvB;;;;;;;;;;EAUA;;;;;;;;;;;EAWA;;;;;;;;;EASA,mBAAmB;;EAEnB;;;;;EAKA;;;;;;;YAQQ,mBAAmB;;EAE7B,OAAO;;;;;;YAOG,kBAAkB;;EAE5B,MAAM;EACN;;;;;;;YAQU,cAAc,mBAAmB;wBAE7B,SACd,WACA,SACA,MACA,oBACA,UACA,WACA,kBACA,oBACA,UACA,aACA,UACA,QACG,SACF,cAAc;yBAdD;MAAA"}
|
|
@@ -2,22 +2,29 @@
|
|
|
2
2
|
import { cn } from "../../styles/cn.js";
|
|
3
3
|
import { useMergedRefs } from "../../hooks/use-merged-refs.js";
|
|
4
4
|
import { usePredictedEvents } from "../../hooks/use-predicted-events.js";
|
|
5
|
+
import { SpinnerGap } from "../../icons/generated/spinner-gap.js";
|
|
5
6
|
import { definedProps } from "../../internal/defined-props.js";
|
|
6
7
|
import { buttonVariants } from "./button-variants.js";
|
|
7
|
-
import { jsx } from "react/jsx-runtime";
|
|
8
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
9
|
import { Button as Button$1 } from "@base-ui/react/button";
|
|
9
10
|
//#region src/components/button/button.tsx
|
|
10
|
-
function Button({ className, variant, size, isVisuallyDisabled = false, disabled = false, isPending = false, predictionZoneSize = 30, onIntent, onMouseDown, ref, ...props }) {
|
|
11
|
+
function Button({ className, variant, size, isVisuallyDisabled = false, disabled = false, isPending = false, pendingIndicator, predictionZoneSize = 30, onIntent, onMouseDown, children, ref, ...props }) {
|
|
11
12
|
const { ref: predictedRef } = usePredictedEvents({
|
|
12
13
|
predictionZoneSize,
|
|
13
14
|
onIntent,
|
|
14
15
|
enabled: !disabled && !isPending && !isVisuallyDisabled && onIntent !== void 0
|
|
15
16
|
});
|
|
16
17
|
const mergedRef = useMergedRefs(ref, onIntent === void 0 ? null : predictedRef);
|
|
17
|
-
|
|
18
|
+
const showsIndicator = isPending && pendingIndicator !== null && pendingIndicator !== false;
|
|
19
|
+
return /* @__PURE__ */ jsxs(Button$1, {
|
|
18
20
|
"data-slot": "button",
|
|
19
21
|
"data-pending": isPending || void 0,
|
|
22
|
+
"data-pending-indicator": showsIndicator || void 0,
|
|
20
23
|
disabled: disabled || isPending,
|
|
24
|
+
...definedProps({
|
|
25
|
+
focusableWhenDisabled: isPending || void 0,
|
|
26
|
+
"aria-busy": isPending || void 0
|
|
27
|
+
}),
|
|
21
28
|
...definedProps({ "aria-disabled": isVisuallyDisabled || void 0 }),
|
|
22
29
|
className: cn(buttonVariants({
|
|
23
30
|
variant,
|
|
@@ -28,7 +35,14 @@ function Button({ className, variant, size, isVisuallyDisabled = false, disabled
|
|
|
28
35
|
onMouseDown?.(event);
|
|
29
36
|
},
|
|
30
37
|
...definedProps(props),
|
|
31
|
-
ref: mergedRef
|
|
38
|
+
ref: mergedRef,
|
|
39
|
+
children: [showsIndicator ? /* @__PURE__ */ jsx("span", {
|
|
40
|
+
"data-slot": "button-pending-indicator",
|
|
41
|
+
"data-icon": "inline-start",
|
|
42
|
+
"aria-hidden": true,
|
|
43
|
+
className: "pointer-events-none inline-flex shrink-0 items-center justify-center",
|
|
44
|
+
children: pendingIndicator ?? /* @__PURE__ */ jsx(SpinnerGap, { className: "animate-spin" })
|
|
45
|
+
}) : null, children]
|
|
32
46
|
});
|
|
33
47
|
}
|
|
34
48
|
Button.displayName = "Button";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"button.js","names":["ButtonPrimitive"],"sources":["../../../src/components/button/button.tsx"],"sourcesContent":["\"use client\";\n\nimport type { ComponentProps, ReactElement } from \"react\";\n\nimport { Button as ButtonPrimitive } from \"@base-ui/react/button\";\nimport type { VariantProps } from \"tailwind-variants\";\n\nimport { useMergedRefs } from \"../../hooks/use-merged-refs\";\nimport { usePredictedEvents } from \"../../hooks/use-predicted-events\";\nimport { definedProps } from \"../../internal/defined-props\";\nimport { cn } from \"../../styles/cn\";\nimport { buttonVariants } from \"./button-variants\";\n\n/** Every `size` the Button recipe knows: the label sizes plus the square `icon*` sizes. */\nexport type ButtonSize = NonNullable<VariantProps<typeof buttonVariants>[\"size\"]>;\n\n/** The square icon-only sizes. A button at one of these requires an `aria-label`. */\nexport type IconButtonSize = Extract<ButtonSize, `icon${string}`>;\n\n/** The label sizes: `xs`, `sm`, `default` and `lg`. */\nexport type LabelButtonSize = Exclude<ButtonSize, IconButtonSize>;\n\ntype ButtonPrimitiveProps = Omit<ComponentProps<typeof ButtonPrimitive>, \"className\">;\n\ntype ButtonSharedProps = ButtonPrimitiveProps &\n Omit<VariantProps<typeof buttonVariants>, \"size\"> & {\n /** Extra classes, merged last through `cn`. */\n className?: string;\n /**\n * Stamps `aria-disabled` (an explicit consumer value wins), which renders the disabled\n * treatment (`opacity-50`, the `not-allowed` cursor, no hover or press paint), and\n * suppresses focus-on-press while the button stays fully interactive — click, keyboard\n * and focus-visible all still work, and a Tooltip on it still opens. For \"looks\n * disabled but explains itself on activation\" flows. The treatment keys off\n * `aria-disabled`, so an explicit `aria-disabled={false}` also turns it off: the button\n * then looks and announces enabled and only the focus-on-press suppression remains.\n */\n isVisuallyDisabled?: boolean;\n /**\n *
|
|
1
|
+
{"version":3,"file":"button.js","names":["ButtonPrimitive"],"sources":["../../../src/components/button/button.tsx"],"sourcesContent":["\"use client\";\n\nimport type { ComponentProps, ReactElement, ReactNode } from \"react\";\n\nimport { Button as ButtonPrimitive } from \"@base-ui/react/button\";\nimport type { VariantProps } from \"tailwind-variants\";\n\nimport { useMergedRefs } from \"../../hooks/use-merged-refs\";\nimport { usePredictedEvents } from \"../../hooks/use-predicted-events\";\nimport { SpinnerGap } from \"../../icons/generated/spinner-gap\";\nimport { definedProps } from \"../../internal/defined-props\";\nimport { cn } from \"../../styles/cn\";\nimport { buttonVariants } from \"./button-variants\";\n\n/** Every `size` the Button recipe knows: the label sizes plus the square `icon*` sizes. */\nexport type ButtonSize = NonNullable<VariantProps<typeof buttonVariants>[\"size\"]>;\n\n/** The square icon-only sizes. A button at one of these requires an `aria-label`. */\nexport type IconButtonSize = Extract<ButtonSize, `icon${string}`>;\n\n/** The label sizes: `xs`, `sm`, `default` and `lg`. */\nexport type LabelButtonSize = Exclude<ButtonSize, IconButtonSize>;\n\ntype ButtonPrimitiveProps = Omit<ComponentProps<typeof ButtonPrimitive>, \"className\">;\n\ntype ButtonSharedProps = ButtonPrimitiveProps &\n Omit<VariantProps<typeof buttonVariants>, \"size\"> & {\n /** Extra classes, merged last through `cn`. */\n className?: string;\n /**\n * Stamps `aria-disabled` (an explicit consumer value wins), which renders the disabled\n * treatment (`opacity-50`, the `not-allowed` cursor, no hover or press paint), and\n * suppresses focus-on-press while the button stays fully interactive — click, keyboard\n * and focus-visible all still work, and a Tooltip on it still opens. For \"looks\n * disabled but explains itself on activation\" flows. The treatment keys off\n * `aria-disabled`, so an explicit `aria-disabled={false}` also turns it off: the button\n * then looks and announces enabled and only the focus-on-press suppression remains.\n */\n isVisuallyDisabled?: boolean;\n /**\n * The in-flight state of an async action. Stamps `data-pending` and `aria-busy`, blocks\n * activation (effective disabled is `disabled || isPending`) and renders the disabled\n * treatment, but keeps the button in the focus order like `focusableWhenDisabled`, so a\n * keyboard user who activated it is still on it when the result arrives. Shows the\n * `pendingIndicator` in the leading icon position meanwhile. An explicit\n * `focusableWhenDisabled={false}` restores the native `disabled` attribute while pending.\n * `aria-busy` announces nothing by itself: say what is happening through the label\n * (\"Saving\") or a `role=\"status\"` region outside the button.\n */\n isPending?: boolean;\n /**\n * What the button shows while pending. Omitted: a spinning `SpinnerGap` in the leading icon\n * position, and the button's own leading and bare SVG children hide so the spinner takes\n * their place (a trailing `data-icon=\"inline-end\"` icon stays). A node: rendered in that same\n * place instead of the spinner, as a decorative `aria-hidden` slot; animate it yourself. `null`\n * or `false`: no indicator and nothing hidden, so the call site owns the pending markup, for\n * example a brand button that keeps its logo and centres its own spinner over it.\n */\n pendingIndicator?: ReactNode;\n /** Pixels the hit rect is inflated on every side when predicting pointer intent. */\n predictionZoneSize?: number;\n /**\n * Predictive-prefetch callback. Fires once when the pointer's predicted trajectory\n * lands inside the inflated hit rect; browsers without the prediction API fail soft.\n */\n onIntent?: () => void;\n };\n\n/**\n * The props of a button with a visible label: every size except the square `icon*` ones, so\n * an accessible name is optional. A wrapper that supplies its own label types its props as\n * `Omit<LabelButtonProps, \"children\">` and keeps the size union in sync with the recipe.\n */\nexport type LabelButtonProps = ButtonSharedProps & {\n /** Recipe size axis. The `icon*` sizes are square and additionally require an `aria-label`. */\n size?: LabelButtonSize;\n};\n\n/**\n * The props of a square icon-only button: one of the `icon*` sizes plus the `aria-label` that\n * names it, since the icon is the whole content.\n */\nexport type IconButtonProps = ButtonSharedProps & {\n /** Recipe size axis. The `icon*` sizes are square and additionally require an `aria-label`. */\n size: IconButtonSize;\n \"aria-label\": string;\n};\n\n/**\n * `LabelButtonProps | IconButtonProps`. `Omit` on this union collapses it and lets an icon\n * size through without its name; omit from {@link LabelButtonProps} or\n * {@link IconButtonProps} instead, or distribute the omit over the union.\n */\nexport type ButtonProps = LabelButtonProps | IconButtonProps;\n\nexport function Button({\n className,\n variant,\n size,\n isVisuallyDisabled = false,\n disabled = false,\n isPending = false,\n pendingIndicator,\n predictionZoneSize = 30,\n onIntent,\n onMouseDown,\n children,\n ref,\n ...props\n}: ButtonProps): ReactElement {\n const { ref: predictedRef } = usePredictedEvents({\n predictionZoneSize,\n onIntent,\n enabled: !disabled && !isPending && !isVisuallyDisabled && onIntent !== undefined,\n });\n const mergedRef = useMergedRefs(ref, onIntent === undefined ? null : predictedRef);\n // `null` and `false` opt out of the indicator and of the icon swap; every other value, `0`\n // included, is a node to show, and `undefined` is the default spinner.\n const showsIndicator = isPending && pendingIndicator !== null && pendingIndicator !== false;\n\n return (\n <ButtonPrimitive\n data-slot=\"button\"\n data-pending={isPending || undefined}\n // Present only while an indicator renders: the recipe's icon swap keys off it, so a\n // `null` indicator hides nothing.\n data-pending-indicator={showsIndicator || undefined}\n disabled={disabled || isPending}\n // A pending button stays in the focus order and announces busy: Base UI then drops the\n // native `disabled`, stamps `aria-disabled` and `data-disabled` and still cancels click\n // and keyboard activation. Both keys are omitted, not undefined, when not pending, so a\n // consumer's own `focusableWhenDisabled` for a plain `disabled` button flows through the\n // later {...definedProps(props)} spread, where an explicit value also wins while pending.\n {...definedProps({\n focusableWhenDisabled: isPending || undefined,\n \"aria-busy\": isPending || undefined,\n })}\n // Announced as unavailable without being disabled: the button still activates so\n // the flow that explains itself can run. An explicit consumer value wins through the\n // later {...definedProps(props)} spread. When not visually disabled the key is omitted,\n // not undefined, so Base UI's own aria-disabled (focusableWhenDisabled, non-native\n // disabled) survives.\n {...definedProps({ \"aria-disabled\": isVisuallyDisabled || undefined })}\n className={cn(buttonVariants({ variant, size }), className)}\n onMouseDown={(event) => {\n if (isVisuallyDisabled) {\n event.preventDefault();\n }\n onMouseDown?.(event);\n }}\n // Filtered so a forwarded undefined cannot erase what Base UI's Button sets itself,\n // such as aria-disabled, type and role.\n {...definedProps(props)}\n ref={mergedRef}>\n {showsIndicator ? (\n // Decorative: the state is announced through aria-busy and the label. The slot is a\n // leading icon, so the icon edge tightens the start inset as for any inline-start child,\n // and the recipe hides the button's own leading and bare SVG children while it shows. A\n // consumer's node rides in the same slot, so it gets the same place and the same\n // aria-hidden; only the default spinner animates. `null` never reaches this branch, so\n // the coalescing only fills in `undefined`.\n <span\n data-slot=\"button-pending-indicator\"\n data-icon=\"inline-start\"\n aria-hidden\n className=\"pointer-events-none inline-flex shrink-0 items-center justify-center\">\n {pendingIndicator ?? <SpinnerGap className=\"animate-spin\" />}\n </span>\n ) : null}\n {children}\n </ButtonPrimitive>\n );\n}\n\nButton.displayName = \"Button\";\n"],"mappings":";;;;;;;;;;AA+FA,SAAgB,OAAO,EACrB,WACA,SACA,MACA,qBAAqB,OACrB,WAAW,OACX,YAAY,OACZ,kBACA,qBAAqB,IACrB,UACA,aACA,UACA,KACA,GAAG,SACyB;CAC5B,MAAM,EAAE,KAAK,iBAAiB,mBAAmB;EAC/C;EACA;EACA,SAAS,CAAC,YAAY,CAAC,aAAa,CAAC,sBAAsB,aAAa,KAAA;CAC1E,CAAC;CACD,MAAM,YAAY,cAAc,KAAK,aAAa,KAAA,IAAY,OAAO,YAAY;CAGjF,MAAM,iBAAiB,aAAa,qBAAqB,QAAQ,qBAAqB;CAEtF,OACE,qBAACA,UAAD;EACE,aAAU;EACV,gBAAc,aAAa,KAAA;EAG3B,0BAAwB,kBAAkB,KAAA;EAC1C,UAAU,YAAY;EAMtB,GAAI,aAAa;GACf,uBAAuB,aAAa,KAAA;GACpC,aAAa,aAAa,KAAA;EAC5B,CAAC;EAMD,GAAI,aAAa,EAAE,iBAAiB,sBAAsB,KAAA,EAAU,CAAC;EACrE,WAAW,GAAG,eAAe;GAAE;GAAS;EAAK,CAAC,GAAG,SAAS;EAC1D,cAAc,UAAU;GACtB,IAAI,oBACF,MAAM,eAAe;GAEvB,cAAc,KAAK;EACrB;EAGA,GAAI,aAAa,KAAK;EACtB,KAAK;EAhCP,UAAA,CAiCG,iBAOC,oBAAC,QAAD;GACE,aAAU;GACV,aAAU;GACV,eAAA;GACA,WAAU;GACT,UAAA,oBAAoB,oBAAC,YAAD,EAAY,WAAU,eAAgB,CAAA;EACvD,CAAA,IACJ,MACH,QACc;;AAErB;AAEA,OAAO,cAAc"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { cn } from "../../styles/cn.js";
|
|
2
2
|
import { iconCrossfadeHidden, iconCrossfadeShown, iconCrossfadeTransition } from "../../styles/utils.js";
|
|
3
|
-
import { Check } from "../../icons/generated/check.js";
|
|
4
3
|
import { SpinnerGap } from "../../icons/generated/spinner-gap.js";
|
|
4
|
+
import { Check } from "../../icons/generated/check.js";
|
|
5
5
|
import { FieldDescription, FieldError, FieldLabel, FieldLegend, FieldRoot, FieldSet } from "./field.js";
|
|
6
6
|
import { tv } from "tailwind-variants";
|
|
7
7
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
import { cn } from "../../styles/cn.js";
|
|
3
3
|
import { dataStateFaceClass } from "../../styles/state-face.js";
|
|
4
4
|
import { selfFocusRingClass } from "../../styles/utils.js";
|
|
5
|
-
import { definedProps } from "../../internal/defined-props.js";
|
|
6
5
|
import { SpinnerGap } from "../../icons/generated/spinner-gap.js";
|
|
6
|
+
import { definedProps } from "../../internal/defined-props.js";
|
|
7
7
|
import { FieldItem } from "../field/field.js";
|
|
8
8
|
import { FieldFrame } from "../field/field-frame.js";
|
|
9
9
|
import { selectionGroupOrientationVariants } from "../selection-item/selection-item-variants.js";
|
|
@@ -3,6 +3,7 @@ import { selfFocusRingClass } from "../../styles/utils.js";
|
|
|
3
3
|
import { useLocalizedStrings } from "../../hooks/use-localized-strings.js";
|
|
4
4
|
import { Info } from "../../icons/generated/info.js";
|
|
5
5
|
import { WarningOctagon } from "../../icons/generated/warning-octagon.js";
|
|
6
|
+
import { SpinnerGap } from "../../icons/generated/spinner-gap.js";
|
|
6
7
|
import { Button } from "../button/button.js";
|
|
7
8
|
import { mergeClassName } from "../../styles/merge-class-name.js";
|
|
8
9
|
import { overlayCloseStrings } from "../overlay/intl/index.js";
|
|
@@ -11,7 +12,6 @@ import { X } from "../../icons/generated/x.js";
|
|
|
11
12
|
import { OverlayPortal } from "../overlay/overlay-portal.js";
|
|
12
13
|
import { CheckCircle } from "../../icons/generated/check-circle.js";
|
|
13
14
|
import { Warning } from "../../icons/generated/warning.js";
|
|
14
|
-
import { SpinnerGap } from "../../icons/generated/spinner-gap.js";
|
|
15
15
|
import { toastVariants } from "./toast-variants.js";
|
|
16
16
|
import { useMemo } from "react";
|
|
17
17
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
package/icons.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { CaretDown } from "./icons/generated/caret-down.js";
|
|
2
2
|
import { Info } from "./icons/generated/info.js";
|
|
3
3
|
import { WarningOctagon } from "./icons/generated/warning-octagon.js";
|
|
4
|
+
import { SpinnerGap } from "./icons/generated/spinner-gap.js";
|
|
4
5
|
import { X } from "./icons/generated/x.js";
|
|
5
6
|
import { CheckCircle } from "./icons/generated/check-circle.js";
|
|
6
7
|
import { Warning } from "./icons/generated/warning.js";
|
|
@@ -9,7 +10,6 @@ import { DotsThree } from "./icons/generated/dots-three.js";
|
|
|
9
10
|
import { Circle } from "./icons/generated/circle.js";
|
|
10
11
|
import { Check } from "./icons/generated/check.js";
|
|
11
12
|
import { Minus } from "./icons/generated/minus.js";
|
|
12
|
-
import { SpinnerGap } from "./icons/generated/spinner-gap.js";
|
|
13
13
|
import { SlidersHorizontal } from "./icons/generated/sliders-horizontal.js";
|
|
14
14
|
import { CaretDoubleLeft } from "./icons/generated/caret-double-left.js";
|
|
15
15
|
import { CaretDoubleRight } from "./icons/generated/caret-double-right.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elmeragroup/fuse",
|
|
3
|
-
"version": "0.1.0-canary.
|
|
3
|
+
"version": "0.1.0-canary.14",
|
|
4
4
|
"description": "Fuse — whitelabel React components for Elmera Group's energy brands. One package, twenty theme permutations, ESM-only.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -359,7 +359,7 @@
|
|
|
359
359
|
"access": "public"
|
|
360
360
|
},
|
|
361
361
|
"elmeraRelease": {
|
|
362
|
-
"commit": "
|
|
362
|
+
"commit": "ac3ad25dce032398a239cb9486b20d169a30fa70",
|
|
363
363
|
"channel": "canary"
|
|
364
364
|
}
|
|
365
365
|
}
|
package/styles.css
CHANGED
|
@@ -4510,6 +4510,9 @@
|
|
|
4510
4510
|
width: calc(0.25rem * 4);
|
|
4511
4511
|
height: calc(0.25rem * 4);
|
|
4512
4512
|
}
|
|
4513
|
+
.data-\[pending-indicator\]\:\[\&\>svg\:not\(\[data-icon\=inline-end\]\)\]\:hidden[data-pending-indicator] > svg:not([data-icon=inline-end]) {
|
|
4514
|
+
display: none;
|
|
4515
|
+
}
|
|
4513
4516
|
.\[\&\>tr\]\:last\:border-b-0 > tr:last-child {
|
|
4514
4517
|
border-bottom-style: var(--tw-border-style);
|
|
4515
4518
|
border-bottom-width: 0px;
|