@gridui/button 1.0.0 → 2.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -29,16 +29,21 @@ Use a real button when the action needs native disabled behavior.
29
29
 
30
30
  ## Appearance and icons
31
31
 
32
- The variants are `primary`, `secondary`, `quiet`, and `text`. Sizes are
32
+ The variants are `primary`, `secondary`, `quiet`, `highlight`, `text`, and `underlined`. Sizes are
33
33
  `standard` (2.75rem minimum height) and `small` (2.25rem). The existing `standard`,
34
- `icon`, `entry`, and `rail` layouts remain available for non-text variants.
34
+ `icon`, `entry`, and `rail` layouts, plus the full-region `cell` layout, are
35
+ available for non-text/non-underlined variants.
35
36
  Small icon controls retain a 2.75rem touch target on coarse pointers.
36
37
 
37
- `text` accepts only `layout="standard"`, which may be omitted. Give it a visible
38
- label in `children`; use a non-text icon layout for an icon-only action. Its
39
- transparent surface has a fixed bottom underline and no inline padding. The
40
- underline does not appear or move on hover. Text uses the surrounding region's
41
- foreground and all variants retain the theme's native keyboard focus treatment.
38
+ `text` and `underlined` accept only `layout="standard"`, which may be omitted.
39
+ Both use a transparent surface, the surrounding foreground, and no inline padding.
40
+ `text` has no underline, including on hover. `underlined` has a fixed bottom
41
+ border that does not appear or move on hover. Give both a visible label in
42
+ `children`; use another variant's icon layout for icon-only actions. All variants
43
+ retain the theme's native keyboard focus treatment.
44
+
45
+ Consumers that previously used `text` for a permanent underline must switch to
46
+ `underlined`. Keep `text` for unlined actions and navigation links.
42
47
  There are no application colors or implicit icon transforms.
43
48
 
44
49
  `startIcon` and `endIcon` accept decorative React nodes before and after the
@@ -53,6 +58,20 @@ omit the border. Secondary and quiet surfaces use `surface-hover` while enabled.
53
58
  Disabled buttons keep their base fill and reduced opacity. Standard and small
54
59
  sizes scale with the consumer's root font size.
55
60
 
61
+ `highlight` uses the neutral `surface` fill and normal foreground. Use it for a
62
+ persistent selected action, with `quiet` for its unselected peers. Choose the
63
+ variant from application state and supply the matching native semantics, such as
64
+ `aria-pressed` for a toggle or `aria-current="page"` for navigation. The variant
65
+ does not create selection behavior, and ARIA attributes do not choose a variant.
66
+
67
+ Appearance and layout are independent. `standard` and `icon` retain standalone
68
+ control geometry. `entry` fills the available width with entry spacing; it does
69
+ not replace the chosen variant's background. `cell` fills both the width and
70
+ height of its containing region, uses square corners and no button border, and
71
+ keeps content padding inside the hit area. Grid or another structural parent
72
+ owns any surrounding seams. Put a cell button in `GridItem padding={0}` to avoid
73
+ an inset selected surface. See the [composition recipe](../../docs/usage/composition.md#full-region-actions).
74
+
56
75
  `buttonClass(variants, className?)` exposes the same appearance and text-layout
57
76
  constraints for caller-owned native elements. `startIcon` and `endIcon` are
58
77
  component props, not styling variants. It uses `cn` for consumer class overrides.
@@ -75,6 +94,7 @@ the browser has had time to consume it. Callers own the Blob contents and name.
75
94
 
76
95
  Package unit tests cover native action/link semantics, decorative icon isolation,
77
96
  busy-state scheduling and failure cleanup. Type fixtures compile with the package
78
- and reject text/icon-layout combinations, missing link destinations, and icon
79
- nodes passed as style variants. Storybook includes text actions and links,
80
- decorative icon slots, long labels, loading, and the existing variants.
97
+ and reject text and underlined/icon-layout combinations, missing link destinations, and icon
98
+ nodes passed as style variants. Storybook includes standalone, icon, full-cell,
99
+ and disabled highlight states, text and underlined actions and links, decorative
100
+ icon slots, long labels, loading, and the existing variants.
package/dist/index.js CHANGED
@@ -72,13 +72,25 @@ var buttonQuietStyles = cva(cn(
72
72
  "text-foreground bg-transparent",
73
73
  "[&:hover:not(:disabled)]:bg-surface-hover"
74
74
  ));
75
+ var buttonHighlightStyles = cva(cn(
76
+ "gridui-button-highlight",
77
+ "text-foreground bg-surface"
78
+ ));
75
79
  var buttonTextStyles = cva(cn(
76
80
  "gridui-button-text",
81
+ "text-foreground bg-transparent border-0 rounded-none"
82
+ ));
83
+ var buttonUnderlinedStyles = cva(cn(
84
+ "gridui-button-underlined",
77
85
  "text-foreground bg-transparent border-0 border-b border-foreground rounded-none"
78
86
  ));
79
87
  var buttonEntryStyles = cva(cn(
80
88
  "gridui-button-entry",
81
- "justify-between w-full py-gridui-16 px-gridui-24 bg-background"
89
+ "justify-between w-full py-gridui-16 px-gridui-24"
90
+ ));
91
+ var buttonCellStyles = cva(cn(
92
+ "gridui-button-cell",
93
+ "w-full h-full justify-start rounded-none border-0 py-gridui-16 px-gridui-24 text-left"
82
94
  ));
83
95
  var buttonRailStyles = cva(cn(
84
96
  "gridui-button-rail",
@@ -97,7 +109,9 @@ var buttonVariants = cva(buttonStyles(), {
97
109
  primary: buttonPrimaryStyles(),
98
110
  secondary: buttonSecondaryStyles(),
99
111
  quiet: buttonQuietStyles(),
100
- text: buttonTextStyles()
112
+ highlight: buttonHighlightStyles(),
113
+ text: buttonTextStyles(),
114
+ underlined: buttonUnderlinedStyles()
101
115
  },
102
116
  size: {
103
117
  small: buttonSmallStyles(),
@@ -107,6 +121,7 @@ var buttonVariants = cva(buttonStyles(), {
107
121
  standard: "",
108
122
  icon: buttonIconStyles(),
109
123
  entry: buttonEntryStyles(),
124
+ cell: buttonCellStyles(),
110
125
  rail: buttonRailStyles()
111
126
  }
112
127
  },
@@ -120,7 +135,7 @@ var buttonClass = (variants = {}, className) => {
120
135
  variant: variants.variant,
121
136
  size: variants.size,
122
137
  layout: variants.layout
123
- }), variants.variant === "text" && "px-0", className);
138
+ }), (variants.variant === "text" || variants.variant === "underlined") && "px-0", className);
124
139
  };
125
140
 
126
141
  // src/button.tsx
@@ -139,7 +154,7 @@ var Button = (props) => {
139
154
  endIcon,
140
155
  ...restProps
141
156
  } = props;
142
- const variants = variant === "text" ? {
157
+ const variants = variant === "text" || variant === "underlined" ? {
143
158
  variant,
144
159
  size,
145
160
  layout: "standard"
@@ -191,7 +206,7 @@ var ButtonLink = (props) => {
191
206
  endIcon,
192
207
  ...restProps
193
208
  } = props;
194
- const variants = variant === "text" ? {
209
+ const variants = variant === "text" || variant === "underlined" ? {
195
210
  variant,
196
211
  size,
197
212
  layout: "standard"
package/dist/index.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/async-action.ts", "../src/style.ts", "../src/button.tsx", "../src/download.ts"],
4
- "sourcesContent": ["/**\n * @author WMXPY\n * @package Button\n * @namespace Root\n * @description Async Action\n */\n\nimport { useRef, useState } from \"react\";\n\ntype Action = () => void | Promise<void>;\n\nexport const performAsyncAction = async ({ action, onStart, onPendingChange }: {\n readonly action: Action;\n readonly onStart?: Action;\n readonly onPendingChange: (pending: boolean) => void;\n}): Promise<void> => {\n onPendingChange(true);\n try {\n await onStart?.();\n await new Promise<void>((resolve) => {\n setTimeout(resolve, 0);\n });\n await action();\n } finally {\n onPendingChange(false);\n }\n};\n\nexport const useAsyncAction = ({ action, onStart, disabled = false }: {\n readonly action: Action;\n readonly onStart?: Action;\n readonly disabled?: boolean;\n}) => {\n const [\n pending,\n setPending,\n ] = useState(false);\n const active = useRef(false);\n return {\n pending,\n async run(): Promise<void> {\n if (disabled || active.current) {\n return;\n }\n active.current = true;\n try {\n await performAsyncAction({\n action,\n ...(onStart ? { onStart } : {}),\n onPendingChange: setPending,\n });\n } finally {\n active.current = false;\n }\n },\n };\n};\n", "/**\n * @author WMXPY\n * @package Button\n * @namespace Root\n * @description Style\n */\n\nimport { cn } from \"@gridui/style-utils\";\nimport type { VariantProps } from \"class-variance-authority\";\nimport { cva } from \"class-variance-authority\";\n\nexport const buttonStyles = cva(cn(\n \"gridui-button\",\n \"inline-flex w-fit max-w-full min-w-0 items-center justify-center gap-gridui-8 border border-solid border-transparent rounded-control type-ui text-center no-underline wrap-anywhere cursor-pointer transition-colors duration-(--gridui-motion-duration-quick) ease-(--gridui-motion-ease-out)\",\n \"[&_svg]:w-gridui-16 [&_svg]:h-gridui-16 [&_svg]:shrink-0 [&_svg]:pointer-events-none\",\n \"disabled:opacity-50 disabled:cursor-not-allowed\",\n \"[&:hover:not(:disabled)]:border-foreground\",\n \"[&:active:not(:disabled)]:opacity-[0.8]\",\n \"motion-reduce:transition-none\",\n));\n\nexport const buttonStandardStyles = cva(cn(\n \"gridui-button-standard\",\n \"min-h-11 py-gridui-8 px-gridui-16\",\n));\n\nexport const buttonSmallStyles = cva(cn(\n \"gridui-button-small\",\n \"min-h-9 py-gridui-4 px-gridui-8\",\n));\n\nexport const buttonPrimaryStyles = cva(cn(\n \"gridui-button-primary\",\n \"text-accent-foreground bg-accent-background border-accent-background\",\n));\n\nexport const buttonSecondaryStyles = cva(cn(\n \"gridui-button-secondary\",\n \"text-foreground bg-transparent border-border\",\n \"[&:hover:not(:disabled)]:bg-surface-hover\",\n));\n\nexport const buttonQuietStyles = cva(cn(\n \"gridui-button-quiet\",\n \"text-foreground bg-transparent\",\n \"[&:hover:not(:disabled)]:bg-surface-hover\",\n));\n\nexport const buttonTextStyles = cva(cn(\n \"gridui-button-text\",\n \"text-foreground bg-transparent border-0 border-b border-foreground rounded-none\",\n));\n\nexport const buttonEntryStyles = cva(cn(\n \"gridui-button-entry\",\n \"justify-between w-full py-gridui-16 px-gridui-24 bg-background\",\n));\n\nexport const buttonRailStyles = cva(cn(\n \"gridui-button-rail\",\n \"w-full min-w-gridui-24 flex-col py-gridui-8 px-0\",\n \"[&_>_span]:[writing-mode:vertical-rl] [&_>_span]:whitespace-nowrap\",\n));\n\nexport const buttonIconStyles = cva(cn(\n \"gridui-button-icon\",\n \"w-11 h-11 shrink-0 p-0\",\n \"[&.gridui-button-small]:w-9 [&.gridui-button-small]:h-9\",\n \"pointer-coarse:[&.gridui-button-small]:w-11 pointer-coarse:[&.gridui-button-small]:h-11\",\n));\n\nexport const buttonVariants = cva(buttonStyles(), {\n variants: {\n variant: {\n primary: buttonPrimaryStyles(),\n secondary: buttonSecondaryStyles(),\n quiet: buttonQuietStyles(),\n text: buttonTextStyles(),\n },\n size: {\n small: buttonSmallStyles(),\n standard: buttonStandardStyles(),\n },\n layout: {\n standard: \"\",\n icon: buttonIconStyles(),\n entry: buttonEntryStyles(),\n rail: buttonRailStyles(),\n },\n },\n defaultVariants: {\n variant: \"primary\",\n size: \"standard\",\n },\n});\n\ntype ButtonStyleVariants = VariantProps<typeof buttonVariants>;\n\nexport type ButtonVariants = Omit<ButtonStyleVariants, \"variant\" | \"layout\"> & (\n | {\n readonly variant: \"text\";\n readonly layout?: \"standard\" | null | undefined;\n }\n | {\n readonly variant?: Exclude<ButtonStyleVariants[\"variant\"], \"text\">;\n readonly layout?: ButtonStyleVariants[\"layout\"];\n }\n);\n\nexport const buttonClass = (variants: ButtonVariants = {}, className?: string): string => {\n return cn(buttonVariants({\n variant: variants.variant,\n size: variants.size,\n layout: variants.layout,\n }), variants.variant === \"text\" && \"px-0\", className);\n};\n", "/**\n * @author WMXPY\n * @package Button\n * @namespace Root\n * @description Button\n */\n\nimport type { ButtonVariants } from \"./style\";\nimport { buttonClass } from \"./style\";\nimport type { ComponentPropsWithRef, FC, ReactNode } from \"react\";\n\nexport type ButtonProps = ComponentPropsWithRef<\"button\"> & ButtonVariants & {\n readonly loading?: boolean;\n readonly startIcon?: ReactNode;\n readonly endIcon?: ReactNode;\n};\n\nexport const Button: FC<ButtonProps> = (props: ButtonProps) => {\n const {\n variant,\n size,\n layout,\n loading = false,\n disabled,\n type = \"button\",\n className,\n children,\n startIcon,\n endIcon,\n ...restProps\n } = props;\n const variants: ButtonVariants = variant === \"text\" ? {\n variant,\n size,\n layout: \"standard\",\n } : {\n variant,\n size,\n layout,\n };\n return (<button\n {...restProps}\n type={type}\n disabled={disabled || loading}\n aria-busy={loading || restProps[\"aria-busy\"]}\n className={buttonClass(variants, className)}\n >\n {startIcon !== undefined && startIcon !== null && typeof startIcon !== \"boolean\" && (<span\n className=\"gridui-button-adornment inline-flex shrink-0 items-center pointer-events-none\"\n aria-hidden=\"true\"\n inert\n >\n {startIcon}\n </span>)}\n {children}\n {endIcon !== undefined && endIcon !== null && typeof endIcon !== \"boolean\" && (<span\n className=\"gridui-button-adornment inline-flex shrink-0 items-center pointer-events-none\"\n aria-hidden=\"true\"\n inert\n >\n {endIcon}\n </span>)}\n </button>);\n};\n\nexport type ButtonLinkProps = ComponentPropsWithRef<\"a\"> & ButtonVariants & {\n readonly href: string;\n readonly startIcon?: ReactNode;\n readonly endIcon?: ReactNode;\n};\n\nexport const ButtonLink: FC<ButtonLinkProps> = (props: ButtonLinkProps) => {\n const {\n variant = \"secondary\",\n size,\n layout,\n className,\n children,\n startIcon,\n endIcon,\n ...restProps\n } = props;\n const variants: ButtonVariants = variant === \"text\" ? {\n variant,\n size,\n layout: \"standard\",\n } : {\n variant,\n size,\n layout,\n };\n return (<a\n {...restProps}\n className={buttonClass(variants, className)}\n >\n {startIcon !== undefined && startIcon !== null && typeof startIcon !== \"boolean\" && (<span\n className=\"gridui-button-adornment inline-flex shrink-0 items-center pointer-events-none\"\n aria-hidden=\"true\"\n inert\n >\n {startIcon}\n </span>)}\n {children}\n {endIcon !== undefined && endIcon !== null && typeof endIcon !== \"boolean\" && (<span\n className=\"gridui-button-adornment inline-flex shrink-0 items-center pointer-events-none\"\n aria-hidden=\"true\"\n inert\n >\n {endIcon}\n </span>)}\n </a>);\n};\n", "/**\n * @author WMXPY\n * @package Button\n * @namespace Root\n * @description Download\n */\n\nexport const downloadBlob = (blob: Blob, filename: string): void => {\n const url = URL.createObjectURL(blob);\n const link = document.createElement(\"a\");\n link.href = url;\n link.download = filename;\n document.body.appendChild(link);\n link.click();\n link.remove();\n window.setTimeout(() => {\n URL.revokeObjectURL(url);\n }, 30000);\n};\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * @author WMXPY\n * @package Button\n * @namespace Root\n * @description Async Action\n */\n\nimport { useRef, useState } from \"react\";\n\ntype Action = () => void | Promise<void>;\n\nexport const performAsyncAction = async ({ action, onStart, onPendingChange }: {\n readonly action: Action;\n readonly onStart?: Action;\n readonly onPendingChange: (pending: boolean) => void;\n}): Promise<void> => {\n onPendingChange(true);\n try {\n await onStart?.();\n await new Promise<void>((resolve) => {\n setTimeout(resolve, 0);\n });\n await action();\n } finally {\n onPendingChange(false);\n }\n};\n\nexport const useAsyncAction = ({ action, onStart, disabled = false }: {\n readonly action: Action;\n readonly onStart?: Action;\n readonly disabled?: boolean;\n}) => {\n const [\n pending,\n setPending,\n ] = useState(false);\n const active = useRef(false);\n return {\n pending,\n async run(): Promise<void> {\n if (disabled || active.current) {\n return;\n }\n active.current = true;\n try {\n await performAsyncAction({\n action,\n ...(onStart ? { onStart } : {}),\n onPendingChange: setPending,\n });\n } finally {\n active.current = false;\n }\n },\n };\n};\n", "/**\n * @author WMXPY\n * @package Button\n * @namespace Root\n * @description Style\n */\n\nimport { cn } from \"@gridui/style-utils\";\nimport type { VariantProps } from \"class-variance-authority\";\nimport { cva } from \"class-variance-authority\";\n\nexport const buttonStyles = cva(cn(\n \"gridui-button\",\n \"inline-flex w-fit max-w-full min-w-0 items-center justify-center gap-gridui-8 border border-solid border-transparent rounded-control type-ui text-center no-underline wrap-anywhere cursor-pointer transition-colors duration-(--gridui-motion-duration-quick) ease-(--gridui-motion-ease-out)\",\n \"[&_svg]:w-gridui-16 [&_svg]:h-gridui-16 [&_svg]:shrink-0 [&_svg]:pointer-events-none\",\n \"disabled:opacity-50 disabled:cursor-not-allowed\",\n \"[&:hover:not(:disabled)]:border-foreground\",\n \"[&:active:not(:disabled)]:opacity-[0.8]\",\n \"motion-reduce:transition-none\",\n));\n\nexport const buttonStandardStyles = cva(cn(\n \"gridui-button-standard\",\n \"min-h-11 py-gridui-8 px-gridui-16\",\n));\n\nexport const buttonSmallStyles = cva(cn(\n \"gridui-button-small\",\n \"min-h-9 py-gridui-4 px-gridui-8\",\n));\n\nexport const buttonPrimaryStyles = cva(cn(\n \"gridui-button-primary\",\n \"text-accent-foreground bg-accent-background border-accent-background\",\n));\n\nexport const buttonSecondaryStyles = cva(cn(\n \"gridui-button-secondary\",\n \"text-foreground bg-transparent border-border\",\n \"[&:hover:not(:disabled)]:bg-surface-hover\",\n));\n\nexport const buttonQuietStyles = cva(cn(\n \"gridui-button-quiet\",\n \"text-foreground bg-transparent\",\n \"[&:hover:not(:disabled)]:bg-surface-hover\",\n));\n\nexport const buttonHighlightStyles = cva(cn(\n \"gridui-button-highlight\",\n \"text-foreground bg-surface\",\n));\n\nexport const buttonTextStyles = cva(cn(\n \"gridui-button-text\",\n \"text-foreground bg-transparent border-0 rounded-none\",\n));\n\nexport const buttonUnderlinedStyles = cva(cn(\n \"gridui-button-underlined\",\n \"text-foreground bg-transparent border-0 border-b border-foreground rounded-none\",\n));\n\nexport const buttonEntryStyles = cva(cn(\n \"gridui-button-entry\",\n \"justify-between w-full py-gridui-16 px-gridui-24\",\n));\n\nexport const buttonCellStyles = cva(cn(\n \"gridui-button-cell\",\n \"w-full h-full justify-start rounded-none border-0 py-gridui-16 px-gridui-24 text-left\",\n));\n\nexport const buttonRailStyles = cva(cn(\n \"gridui-button-rail\",\n \"w-full min-w-gridui-24 flex-col py-gridui-8 px-0\",\n \"[&_>_span]:[writing-mode:vertical-rl] [&_>_span]:whitespace-nowrap\",\n));\n\nexport const buttonIconStyles = cva(cn(\n \"gridui-button-icon\",\n \"w-11 h-11 shrink-0 p-0\",\n \"[&.gridui-button-small]:w-9 [&.gridui-button-small]:h-9\",\n \"pointer-coarse:[&.gridui-button-small]:w-11 pointer-coarse:[&.gridui-button-small]:h-11\",\n));\n\nexport const buttonVariants = cva(buttonStyles(), {\n variants: {\n variant: {\n primary: buttonPrimaryStyles(),\n secondary: buttonSecondaryStyles(),\n quiet: buttonQuietStyles(),\n highlight: buttonHighlightStyles(),\n text: buttonTextStyles(),\n underlined: buttonUnderlinedStyles(),\n },\n size: {\n small: buttonSmallStyles(),\n standard: buttonStandardStyles(),\n },\n layout: {\n standard: \"\",\n icon: buttonIconStyles(),\n entry: buttonEntryStyles(),\n cell: buttonCellStyles(),\n rail: buttonRailStyles(),\n },\n },\n defaultVariants: {\n variant: \"primary\",\n size: \"standard\",\n },\n});\n\ntype ButtonStyleVariants = VariantProps<typeof buttonVariants>;\n\nexport type ButtonVariants = Omit<ButtonStyleVariants, \"variant\" | \"layout\"> & (\n | {\n readonly variant: \"text\" | \"underlined\";\n readonly layout?: \"standard\" | null | undefined;\n }\n | {\n readonly variant?: Exclude<ButtonStyleVariants[\"variant\"], \"text\" | \"underlined\">;\n readonly layout?: ButtonStyleVariants[\"layout\"];\n }\n);\n\nexport const buttonClass = (variants: ButtonVariants = {}, className?: string): string => {\n return cn(buttonVariants({\n variant: variants.variant,\n size: variants.size,\n layout: variants.layout,\n }), (variants.variant === \"text\" || variants.variant === \"underlined\") && \"px-0\", className);\n};\n", "/**\n * @author WMXPY\n * @package Button\n * @namespace Root\n * @description Button\n */\n\nimport type { ButtonVariants } from \"./style\";\nimport { buttonClass } from \"./style\";\nimport type { ComponentPropsWithRef, FC, ReactNode } from \"react\";\n\nexport type ButtonProps = ComponentPropsWithRef<\"button\"> & ButtonVariants & {\n readonly loading?: boolean;\n readonly startIcon?: ReactNode;\n readonly endIcon?: ReactNode;\n};\n\nexport const Button: FC<ButtonProps> = (props: ButtonProps) => {\n const {\n variant,\n size,\n layout,\n loading = false,\n disabled,\n type = \"button\",\n className,\n children,\n startIcon,\n endIcon,\n ...restProps\n } = props;\n const variants: ButtonVariants = (variant === \"text\" || variant === \"underlined\") ? {\n variant,\n size,\n layout: \"standard\",\n } : {\n variant,\n size,\n layout,\n };\n return (<button\n {...restProps}\n type={type}\n disabled={disabled || loading}\n aria-busy={loading || restProps[\"aria-busy\"]}\n className={buttonClass(variants, className)}\n >\n {startIcon !== undefined && startIcon !== null && typeof startIcon !== \"boolean\" && (<span\n className=\"gridui-button-adornment inline-flex shrink-0 items-center pointer-events-none\"\n aria-hidden=\"true\"\n inert\n >\n {startIcon}\n </span>)}\n {children}\n {endIcon !== undefined && endIcon !== null && typeof endIcon !== \"boolean\" && (<span\n className=\"gridui-button-adornment inline-flex shrink-0 items-center pointer-events-none\"\n aria-hidden=\"true\"\n inert\n >\n {endIcon}\n </span>)}\n </button>);\n};\n\nexport type ButtonLinkProps = ComponentPropsWithRef<\"a\"> & ButtonVariants & {\n readonly href: string;\n readonly startIcon?: ReactNode;\n readonly endIcon?: ReactNode;\n};\n\nexport const ButtonLink: FC<ButtonLinkProps> = (props: ButtonLinkProps) => {\n const {\n variant = \"secondary\",\n size,\n layout,\n className,\n children,\n startIcon,\n endIcon,\n ...restProps\n } = props;\n const variants: ButtonVariants = (variant === \"text\" || variant === \"underlined\") ? {\n variant,\n size,\n layout: \"standard\",\n } : {\n variant,\n size,\n layout,\n };\n return (<a\n {...restProps}\n className={buttonClass(variants, className)}\n >\n {startIcon !== undefined && startIcon !== null && typeof startIcon !== \"boolean\" && (<span\n className=\"gridui-button-adornment inline-flex shrink-0 items-center pointer-events-none\"\n aria-hidden=\"true\"\n inert\n >\n {startIcon}\n </span>)}\n {children}\n {endIcon !== undefined && endIcon !== null && typeof endIcon !== \"boolean\" && (<span\n className=\"gridui-button-adornment inline-flex shrink-0 items-center pointer-events-none\"\n aria-hidden=\"true\"\n inert\n >\n {endIcon}\n </span>)}\n </a>);\n};\n", "/**\n * @author WMXPY\n * @package Button\n * @namespace Root\n * @description Download\n */\n\nexport const downloadBlob = (blob: Blob, filename: string): void => {\n const url = URL.createObjectURL(blob);\n const link = document.createElement(\"a\");\n link.href = url;\n link.download = filename;\n document.body.appendChild(link);\n link.click();\n link.remove();\n window.setTimeout(() => {\n URL.revokeObjectURL(url);\n }, 30000);\n};\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
package/dist/style.d.ts CHANGED
@@ -11,21 +11,24 @@ export declare const buttonSmallStyles: (props?: import("class-variance-authorit
11
11
  export declare const buttonPrimaryStyles: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
12
12
  export declare const buttonSecondaryStyles: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
13
13
  export declare const buttonQuietStyles: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
14
+ export declare const buttonHighlightStyles: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
14
15
  export declare const buttonTextStyles: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
16
+ export declare const buttonUnderlinedStyles: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
15
17
  export declare const buttonEntryStyles: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
18
+ export declare const buttonCellStyles: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
16
19
  export declare const buttonRailStyles: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
17
20
  export declare const buttonIconStyles: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
18
21
  export declare const buttonVariants: (props?: ({
19
- variant?: "primary" | "secondary" | "quiet" | "text" | null | undefined;
22
+ variant?: "primary" | "secondary" | "quiet" | "highlight" | "text" | "underlined" | null | undefined;
20
23
  size?: "small" | "standard" | null | undefined;
21
- layout?: "standard" | "icon" | "entry" | "rail" | null | undefined;
24
+ layout?: "standard" | "icon" | "entry" | "cell" | "rail" | null | undefined;
22
25
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
23
26
  type ButtonStyleVariants = VariantProps<typeof buttonVariants>;
24
27
  export type ButtonVariants = Omit<ButtonStyleVariants, "variant" | "layout"> & ({
25
- readonly variant: "text";
28
+ readonly variant: "text" | "underlined";
26
29
  readonly layout?: "standard" | null | undefined;
27
30
  } | {
28
- readonly variant?: Exclude<ButtonStyleVariants["variant"], "text">;
31
+ readonly variant?: Exclude<ButtonStyleVariants["variant"], "text" | "underlined">;
29
32
  readonly layout?: ButtonStyleVariants["layout"];
30
33
  });
31
34
  export declare const buttonClass: (variants?: ButtonVariants, className?: string) => string;
package/dist/styles.css CHANGED
@@ -18,6 +18,9 @@
18
18
  .h-11 {
19
19
  height: calc(var(--spacing) * 11);
20
20
  }
21
+ .h-full {
22
+ height: 100%;
23
+ }
21
24
  .min-h-9 {
22
25
  min-height: calc(var(--spacing) * 9);
23
26
  }
@@ -60,6 +63,9 @@
60
63
  .justify-center {
61
64
  justify-content: center;
62
65
  }
66
+ .justify-start {
67
+ justify-content: flex-start;
68
+ }
63
69
  .gap-gridui-8 {
64
70
  gap: var(--gridui-space-8);
65
71
  }
@@ -100,8 +106,8 @@
100
106
  .bg-accent-background {
101
107
  background-color: var(--gridui-accent-background);
102
108
  }
103
- .bg-background {
104
- background-color: var(--gridui-background);
109
+ .bg-surface {
110
+ background-color: var(--gridui-surface);
105
111
  }
106
112
  .bg-transparent {
107
113
  background-color: transparent;
@@ -133,6 +139,9 @@
133
139
  .text-center {
134
140
  text-align: center;
135
141
  }
142
+ .text-left {
143
+ text-align: left;
144
+ }
136
145
  .type-ui {
137
146
  font: var(--gridui-type-ui);
138
147
  letter-spacing: normal;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gridui/button",
3
- "version": "1.0.0",
3
+ "version": "2.1.1",
4
4
  "description": "GridUI React buttons and action helpers",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",
@@ -27,20 +27,20 @@
27
27
  "access": "public"
28
28
  },
29
29
  "dependencies": {
30
- "@gridui/style-utils": "^2.1.1",
31
- "@gridui/theme": "^2.1.1",
30
+ "@gridui/style-utils": "^2.1.2",
31
+ "@gridui/theme": "^2.1.2",
32
32
  "class-variance-authority": "0.7.1"
33
33
  },
34
34
  "devDependencies": {
35
35
  "@gridui-internal/configuration": "0.0.0",
36
36
  "@jest/globals": "30.5.2",
37
- "@storybook/react-vite": "10.6.3",
37
+ "@storybook/react-vite": "10.6.5",
38
38
  "@types/jest": "30.0.0",
39
39
  "@types/node": "26.6.5",
40
40
  "@types/react": "19.3.0",
41
41
  "@types/react-dom": "19.3.0",
42
42
  "jest": "30.5.2",
43
- "lucide-react": "1.61.0",
43
+ "lucide-react": "1.62.0",
44
44
  "react": "19.3.1",
45
45
  "react-dom": "19.3.1",
46
46
  "typescript": "6.0.3"