@gridui/button 0.0.0-stage → 0.0.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 CHANGED
@@ -1,3 +1,80 @@
1
- # Temporary Holding Version
1
+ # Button
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ `@gridui/button` owns native buttons, button-styled links, shared button classes,
4
+ async action scheduling, and browser download lifecycle. It has no domain,
5
+ route, locale, or Layout dependency. Load `@gridui/button/styles.css` after
6
+ the theme styles.
7
+
8
+ ```tsx
9
+ import { Button, ButtonLink } from "@gridui/button";
10
+ import "@gridui/button/styles.css";
11
+
12
+ <Button type="submit" loading={saving}>Save changes</Button>;
13
+ <ButtonLink href="/guide" variant="text" endIcon={<ArrowUpRight />}>
14
+ Read the guide
15
+ </ButtonLink>;
16
+ ```
17
+
18
+ ## Native behavior
19
+
20
+ `Button` forwards native button props and refs. It defaults to `type="button"`;
21
+ use `type="submit"` for a form action. `loading` disables the native button and
22
+ exposes `aria-busy` without replacing the visible action name. Native `disabled`
23
+ remains available independently of loading.
24
+
25
+ `ButtonLink` forwards native anchor props and refs and requires `href`. It
26
+ defaults to the secondary variant and retains navigation, modified-click,
27
+ download, target, and rel behavior. It has no loading or disabled button API.
28
+ Use a real button when the action needs native disabled behavior.
29
+
30
+ ## Appearance and icons
31
+
32
+ The variants are `primary`, `secondary`, `quiet`, and `text`. Sizes are
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.
35
+ Small icon controls retain a 2.75rem touch target on coarse pointers.
36
+
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.
42
+ There are no application colors or implicit icon transforms.
43
+
44
+ `startIcon` and `endIcon` accept decorative React nodes before and after the
45
+ existing `children`. Their wrappers are hidden from assistive technology and
46
+ inert, so the visible label remains the accessible name. Do not put interactive
47
+ content in these slots. Existing icons passed directly in `children` keep their
48
+ original rendering and accessibility responsibilities.
49
+
50
+ Primary buttons use `accent-background` and `accent-foreground`. Secondary
51
+ buttons use a transparent fill, foreground text, and a border; quiet buttons
52
+ omit the border. Secondary and quiet surfaces use `surface-hover` while enabled.
53
+ Disabled buttons keep their base fill and reduced opacity. Standard and small
54
+ sizes scale with the consumer's root font size.
55
+
56
+ `buttonClass(variants, className?)` exposes the same appearance and text-layout
57
+ constraints for caller-owned native elements. `startIcon` and `endIcon` are
58
+ component props, not styling variants. It uses `cn` for consumer class overrides.
59
+ Callers using the class helper own native semantics, icon accessibility, and
60
+ loading behavior.
61
+
62
+ ## Action helpers
63
+
64
+ `useAsyncAction({ action, onStart?, disabled? })` returns `pending` and `run`.
65
+ It publishes pending state and optional start feedback before yielding to the
66
+ browser event loop, runs the action, and releases pending state on success or
67
+ failure. Disabled actions and repeated activation while pending are ignored.
68
+ Callers own business error handling and generated bytes.
69
+
70
+ `downloadBlob(blob, filename)` creates the download anchor and object URL,
71
+ activates the native download, removes the anchor, and releases the URL after
72
+ the browser has had time to consume it. Callers own the Blob contents and name.
73
+
74
+ ## Verification
75
+
76
+ Package unit tests cover native action/link semantics, decorative icon isolation,
77
+ 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.
@@ -0,0 +1,21 @@
1
+ /**
2
+ * @author WMXPY
3
+ * @package Button
4
+ * @namespace Root
5
+ * @description Async Action
6
+ */
7
+ type Action = () => void | Promise<void>;
8
+ export declare const performAsyncAction: ({ action, onStart, onPendingChange }: {
9
+ readonly action: Action;
10
+ readonly onStart?: Action;
11
+ readonly onPendingChange: (pending: boolean) => void;
12
+ }) => Promise<void>;
13
+ export declare const useAsyncAction: ({ action, onStart, disabled }: {
14
+ readonly action: Action;
15
+ readonly onStart?: Action;
16
+ readonly disabled?: boolean;
17
+ }) => {
18
+ pending: boolean;
19
+ run(): Promise<void>;
20
+ };
21
+ export {};
@@ -0,0 +1,20 @@
1
+ /**
2
+ * @author WMXPY
3
+ * @package Button
4
+ * @namespace Root
5
+ * @description Button
6
+ */
7
+ import type { ButtonVariants } from "./style";
8
+ import type { ComponentPropsWithRef, FC, ReactNode } from "react";
9
+ export type ButtonProps = ComponentPropsWithRef<"button"> & ButtonVariants & {
10
+ readonly loading?: boolean;
11
+ readonly startIcon?: ReactNode;
12
+ readonly endIcon?: ReactNode;
13
+ };
14
+ export declare const Button: FC<ButtonProps>;
15
+ export type ButtonLinkProps = ComponentPropsWithRef<"a"> & ButtonVariants & {
16
+ readonly href: string;
17
+ readonly startIcon?: ReactNode;
18
+ readonly endIcon?: ReactNode;
19
+ };
20
+ export declare const ButtonLink: FC<ButtonLinkProps>;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @author WMXPY
3
+ * @package Button
4
+ * @namespace Root
5
+ * @description Download
6
+ */
7
+ export declare const downloadBlob: (blob: Blob, filename: string) => void;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * @author WMXPY
3
+ * @package Button
4
+ * @namespace Root
5
+ * @description Index
6
+ */
7
+ export { useAsyncAction } from "./async-action";
8
+ export type { ButtonLinkProps, ButtonProps } from "./button";
9
+ export { Button, ButtonLink } from "./button";
10
+ export { downloadBlob } from "./download";
11
+ export type { ButtonVariants } from "./style";
12
+ export { buttonClass } from "./style";
package/dist/index.js ADDED
@@ -0,0 +1,253 @@
1
+ // src/async-action.ts
2
+ import { useRef, useState } from "react";
3
+ var performAsyncAction = async ({ action, onStart, onPendingChange }) => {
4
+ onPendingChange(true);
5
+ try {
6
+ await onStart?.();
7
+ await new Promise((resolve) => {
8
+ setTimeout(resolve, 0);
9
+ });
10
+ await action();
11
+ } finally {
12
+ onPendingChange(false);
13
+ }
14
+ };
15
+ var useAsyncAction = ({ action, onStart, disabled = false }) => {
16
+ const [
17
+ pending,
18
+ setPending
19
+ ] = useState(false);
20
+ const active = useRef(false);
21
+ return {
22
+ pending,
23
+ async run() {
24
+ if (disabled || active.current) {
25
+ return;
26
+ }
27
+ active.current = true;
28
+ try {
29
+ await performAsyncAction({
30
+ action,
31
+ ...onStart ? { onStart } : {},
32
+ onPendingChange: setPending
33
+ });
34
+ } finally {
35
+ active.current = false;
36
+ }
37
+ }
38
+ };
39
+ };
40
+
41
+ // src/style.ts
42
+ import { cn } from "@gridui/style-utils";
43
+ import { cva } from "class-variance-authority";
44
+ var buttonStyles = cva(cn(
45
+ "gridui-button",
46
+ "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)",
47
+ "[&_svg]:w-gridui-16 [&_svg]:h-gridui-16 [&_svg]:shrink-0 [&_svg]:pointer-events-none",
48
+ "disabled:opacity-50 disabled:cursor-not-allowed",
49
+ "[&:hover:not(:disabled)]:border-foreground",
50
+ "[&:active:not(:disabled)]:opacity-[0.8]",
51
+ "motion-reduce:transition-none"
52
+ ));
53
+ var buttonStandardStyles = cva(cn(
54
+ "gridui-button-standard",
55
+ "min-h-11 py-gridui-8 px-gridui-16"
56
+ ));
57
+ var buttonSmallStyles = cva(cn(
58
+ "gridui-button-small",
59
+ "min-h-9 py-gridui-4 px-gridui-8"
60
+ ));
61
+ var buttonPrimaryStyles = cva(cn(
62
+ "gridui-button-primary",
63
+ "text-accent-foreground bg-accent-background border-accent-background"
64
+ ));
65
+ var buttonSecondaryStyles = cva(cn(
66
+ "gridui-button-secondary",
67
+ "text-foreground bg-transparent border-border",
68
+ "[&:hover:not(:disabled)]:bg-surface-hover"
69
+ ));
70
+ var buttonQuietStyles = cva(cn(
71
+ "gridui-button-quiet",
72
+ "text-foreground bg-transparent",
73
+ "[&:hover:not(:disabled)]:bg-surface-hover"
74
+ ));
75
+ var buttonTextStyles = cva(cn(
76
+ "gridui-button-text",
77
+ "text-foreground bg-transparent border-0 border-b border-foreground rounded-none"
78
+ ));
79
+ var buttonEntryStyles = cva(cn(
80
+ "gridui-button-entry",
81
+ "justify-between w-full py-gridui-16 px-gridui-24 bg-background"
82
+ ));
83
+ var buttonRailStyles = cva(cn(
84
+ "gridui-button-rail",
85
+ "w-full min-w-gridui-24 flex-col py-gridui-8 px-0",
86
+ "[&_>_span]:[writing-mode:vertical-rl] [&_>_span]:whitespace-nowrap"
87
+ ));
88
+ var buttonIconStyles = cva(cn(
89
+ "gridui-button-icon",
90
+ "w-11 h-11 shrink-0 p-0",
91
+ "[&.gridui-button-small]:w-9 [&.gridui-button-small]:h-9",
92
+ "pointer-coarse:[&.gridui-button-small]:w-11 pointer-coarse:[&.gridui-button-small]:h-11"
93
+ ));
94
+ var buttonVariants = cva(buttonStyles(), {
95
+ variants: {
96
+ variant: {
97
+ primary: buttonPrimaryStyles(),
98
+ secondary: buttonSecondaryStyles(),
99
+ quiet: buttonQuietStyles(),
100
+ text: buttonTextStyles()
101
+ },
102
+ size: {
103
+ small: buttonSmallStyles(),
104
+ standard: buttonStandardStyles()
105
+ },
106
+ layout: {
107
+ standard: "",
108
+ icon: buttonIconStyles(),
109
+ entry: buttonEntryStyles(),
110
+ rail: buttonRailStyles()
111
+ }
112
+ },
113
+ defaultVariants: {
114
+ variant: "primary",
115
+ size: "standard"
116
+ }
117
+ });
118
+ var buttonClass = (variants = {}, className) => {
119
+ return cn(buttonVariants({
120
+ variant: variants.variant,
121
+ size: variants.size,
122
+ layout: variants.layout
123
+ }), variants.variant === "text" && "px-0", className);
124
+ };
125
+
126
+ // src/button.tsx
127
+ import { jsx, jsxs } from "react/jsx-runtime";
128
+ var Button = (props) => {
129
+ const {
130
+ variant,
131
+ size,
132
+ layout,
133
+ loading = false,
134
+ disabled,
135
+ type = "button",
136
+ className,
137
+ children,
138
+ startIcon,
139
+ endIcon,
140
+ ...restProps
141
+ } = props;
142
+ const variants = variant === "text" ? {
143
+ variant,
144
+ size,
145
+ layout: "standard"
146
+ } : {
147
+ variant,
148
+ size,
149
+ layout
150
+ };
151
+ return /* @__PURE__ */ jsxs(
152
+ "button",
153
+ {
154
+ ...restProps,
155
+ type,
156
+ disabled: disabled || loading,
157
+ "aria-busy": loading || restProps["aria-busy"],
158
+ className: buttonClass(variants, className),
159
+ children: [
160
+ startIcon !== void 0 && startIcon !== null && typeof startIcon !== "boolean" && /* @__PURE__ */ jsx(
161
+ "span",
162
+ {
163
+ className: "gridui-button-adornment inline-flex shrink-0 items-center pointer-events-none",
164
+ "aria-hidden": "true",
165
+ inert: true,
166
+ children: startIcon
167
+ }
168
+ ),
169
+ children,
170
+ endIcon !== void 0 && endIcon !== null && typeof endIcon !== "boolean" && /* @__PURE__ */ jsx(
171
+ "span",
172
+ {
173
+ className: "gridui-button-adornment inline-flex shrink-0 items-center pointer-events-none",
174
+ "aria-hidden": "true",
175
+ inert: true,
176
+ children: endIcon
177
+ }
178
+ )
179
+ ]
180
+ }
181
+ );
182
+ };
183
+ var ButtonLink = (props) => {
184
+ const {
185
+ variant = "secondary",
186
+ size,
187
+ layout,
188
+ className,
189
+ children,
190
+ startIcon,
191
+ endIcon,
192
+ ...restProps
193
+ } = props;
194
+ const variants = variant === "text" ? {
195
+ variant,
196
+ size,
197
+ layout: "standard"
198
+ } : {
199
+ variant,
200
+ size,
201
+ layout
202
+ };
203
+ return /* @__PURE__ */ jsxs(
204
+ "a",
205
+ {
206
+ ...restProps,
207
+ className: buttonClass(variants, className),
208
+ children: [
209
+ startIcon !== void 0 && startIcon !== null && typeof startIcon !== "boolean" && /* @__PURE__ */ jsx(
210
+ "span",
211
+ {
212
+ className: "gridui-button-adornment inline-flex shrink-0 items-center pointer-events-none",
213
+ "aria-hidden": "true",
214
+ inert: true,
215
+ children: startIcon
216
+ }
217
+ ),
218
+ children,
219
+ endIcon !== void 0 && endIcon !== null && typeof endIcon !== "boolean" && /* @__PURE__ */ jsx(
220
+ "span",
221
+ {
222
+ className: "gridui-button-adornment inline-flex shrink-0 items-center pointer-events-none",
223
+ "aria-hidden": "true",
224
+ inert: true,
225
+ children: endIcon
226
+ }
227
+ )
228
+ ]
229
+ }
230
+ );
231
+ };
232
+
233
+ // src/download.ts
234
+ var downloadBlob = (blob, filename) => {
235
+ const url = URL.createObjectURL(blob);
236
+ const link = document.createElement("a");
237
+ link.href = url;
238
+ link.download = filename;
239
+ document.body.appendChild(link);
240
+ link.click();
241
+ link.remove();
242
+ window.setTimeout(() => {
243
+ URL.revokeObjectURL(url);
244
+ }, 3e4);
245
+ };
246
+ export {
247
+ Button,
248
+ ButtonLink,
249
+ buttonClass,
250
+ downloadBlob,
251
+ useAsyncAction
252
+ };
253
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 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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",
6
+ "names": []
7
+ }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * @author WMXPY
3
+ * @package Button
4
+ * @namespace Root
5
+ * @description Style
6
+ */
7
+ import type { VariantProps } from "class-variance-authority";
8
+ export declare const buttonStyles: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
9
+ export declare const buttonStandardStyles: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
10
+ export declare const buttonSmallStyles: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
11
+ export declare const buttonPrimaryStyles: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
12
+ export declare const buttonSecondaryStyles: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
13
+ export declare const buttonQuietStyles: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
14
+ export declare const buttonTextStyles: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
15
+ export declare const buttonEntryStyles: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
16
+ export declare const buttonRailStyles: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
17
+ export declare const buttonIconStyles: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
18
+ export declare const buttonVariants: (props?: ({
19
+ variant?: "primary" | "secondary" | "quiet" | "text" | null | undefined;
20
+ size?: "small" | "standard" | null | undefined;
21
+ layout?: "standard" | "icon" | "entry" | "rail" | null | undefined;
22
+ } & import("class-variance-authority/types").ClassProp) | undefined) => string;
23
+ type ButtonStyleVariants = VariantProps<typeof buttonVariants>;
24
+ export type ButtonVariants = Omit<ButtonStyleVariants, "variant" | "layout"> & ({
25
+ readonly variant: "text";
26
+ readonly layout?: "standard" | null | undefined;
27
+ } | {
28
+ readonly variant?: Exclude<ButtonStyleVariants["variant"], "text">;
29
+ readonly layout?: ButtonStyleVariants["layout"];
30
+ });
31
+ export declare const buttonClass: (variants?: ButtonVariants, className?: string) => string;
32
+ export {};
@@ -0,0 +1,239 @@
1
+ /*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */
2
+ @layer properties;
3
+ @layer theme, base, components, utilities;
4
+ @layer theme {
5
+ :root, :host {
6
+ --spacing: 0.25rem;
7
+ --default-transition-duration: 150ms;
8
+ --default-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
9
+ }
10
+ }
11
+ @layer utilities {
12
+ .pointer-events-none {
13
+ pointer-events: none;
14
+ }
15
+ .inline-flex {
16
+ display: inline-flex;
17
+ }
18
+ .h-11 {
19
+ height: calc(var(--spacing) * 11);
20
+ }
21
+ .min-h-9 {
22
+ min-height: calc(var(--spacing) * 9);
23
+ }
24
+ .min-h-11 {
25
+ min-height: calc(var(--spacing) * 11);
26
+ }
27
+ .w-11 {
28
+ width: calc(var(--spacing) * 11);
29
+ }
30
+ .w-fit {
31
+ width: fit-content;
32
+ }
33
+ .w-full {
34
+ width: 100%;
35
+ }
36
+ .max-w-full {
37
+ max-width: 100%;
38
+ }
39
+ .min-w-0 {
40
+ min-width: 0px;
41
+ }
42
+ .min-w-gridui-24 {
43
+ min-width: var(--gridui-space-24);
44
+ }
45
+ .shrink-0 {
46
+ flex-shrink: 0;
47
+ }
48
+ .cursor-pointer {
49
+ cursor: pointer;
50
+ }
51
+ .flex-col {
52
+ flex-direction: column;
53
+ }
54
+ .items-center {
55
+ align-items: center;
56
+ }
57
+ .justify-between {
58
+ justify-content: space-between;
59
+ }
60
+ .justify-center {
61
+ justify-content: center;
62
+ }
63
+ .gap-gridui-8 {
64
+ gap: var(--gridui-space-8);
65
+ }
66
+ .rounded-control {
67
+ border-radius: var(--gridui-control-radius);
68
+ }
69
+ .rounded-none {
70
+ border-radius: 0;
71
+ }
72
+ .border {
73
+ border-style: var(--tw-border-style);
74
+ border-width: 1px;
75
+ }
76
+ .border-0 {
77
+ border-style: var(--tw-border-style);
78
+ border-width: 0px;
79
+ }
80
+ .border-b {
81
+ border-bottom-style: var(--tw-border-style);
82
+ border-bottom-width: 1px;
83
+ }
84
+ .border-solid {
85
+ --tw-border-style: solid;
86
+ border-style: solid;
87
+ }
88
+ .border-accent-background {
89
+ border-color: var(--gridui-accent-background);
90
+ }
91
+ .border-border {
92
+ border-color: var(--gridui-border);
93
+ }
94
+ .border-foreground {
95
+ border-color: var(--gridui-foreground);
96
+ }
97
+ .border-transparent {
98
+ border-color: transparent;
99
+ }
100
+ .bg-accent-background {
101
+ background-color: var(--gridui-accent-background);
102
+ }
103
+ .bg-background {
104
+ background-color: var(--gridui-background);
105
+ }
106
+ .bg-transparent {
107
+ background-color: transparent;
108
+ }
109
+ .p-0 {
110
+ padding: 0px;
111
+ }
112
+ .px-0 {
113
+ padding-inline: 0px;
114
+ }
115
+ .px-gridui-8 {
116
+ padding-inline: var(--gridui-space-8);
117
+ }
118
+ .px-gridui-16 {
119
+ padding-inline: var(--gridui-space-16);
120
+ }
121
+ .px-gridui-24 {
122
+ padding-inline: var(--gridui-space-24);
123
+ }
124
+ .py-gridui-4 {
125
+ padding-block: var(--gridui-space-4);
126
+ }
127
+ .py-gridui-8 {
128
+ padding-block: var(--gridui-space-8);
129
+ }
130
+ .py-gridui-16 {
131
+ padding-block: var(--gridui-space-16);
132
+ }
133
+ .text-center {
134
+ text-align: center;
135
+ }
136
+ .type-ui {
137
+ font: var(--gridui-type-ui);
138
+ letter-spacing: normal;
139
+ }
140
+ .wrap-anywhere {
141
+ overflow-wrap: anywhere;
142
+ }
143
+ .text-accent-foreground {
144
+ color: var(--gridui-accent-foreground);
145
+ }
146
+ .text-foreground {
147
+ color: var(--gridui-foreground);
148
+ }
149
+ .no-underline {
150
+ text-decoration-line: none;
151
+ }
152
+ .transition-colors {
153
+ transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to;
154
+ transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
155
+ transition-duration: var(--tw-duration, var(--default-transition-duration));
156
+ }
157
+ .duration-\(--gridui-motion-duration-quick\) {
158
+ --tw-duration: var(--gridui-motion-duration-quick);
159
+ transition-duration: var(--gridui-motion-duration-quick);
160
+ }
161
+ .ease-\(--gridui-motion-ease-out\) {
162
+ --tw-ease: var(--gridui-motion-ease-out);
163
+ transition-timing-function: var(--gridui-motion-ease-out);
164
+ }
165
+ .disabled\:cursor-not-allowed:disabled {
166
+ cursor: not-allowed;
167
+ }
168
+ .disabled\:opacity-50:disabled {
169
+ opacity: 50%;
170
+ }
171
+ @media (prefers-reduced-motion: reduce) {
172
+ .motion-reduce\:transition-none {
173
+ transition-property: none;
174
+ }
175
+ }
176
+ .\[\&_\>_span\]\:whitespace-nowrap > span {
177
+ white-space: nowrap;
178
+ }
179
+ .\[\&_\>_span\]\:\[writing-mode\:vertical-rl\] > span {
180
+ writing-mode: vertical-rl;
181
+ }
182
+ .\[\&_svg\]\:pointer-events-none svg {
183
+ pointer-events: none;
184
+ }
185
+ .\[\&_svg\]\:h-gridui-16 svg {
186
+ height: var(--gridui-space-16);
187
+ }
188
+ .\[\&_svg\]\:w-gridui-16 svg {
189
+ width: var(--gridui-space-16);
190
+ }
191
+ .\[\&_svg\]\:shrink-0 svg {
192
+ flex-shrink: 0;
193
+ }
194
+ .\[\&\.gridui-button-small\]\:h-9.gridui-button-small {
195
+ height: calc(var(--spacing) * 9);
196
+ }
197
+ .\[\&\.gridui-button-small\]\:w-9.gridui-button-small {
198
+ width: calc(var(--spacing) * 9);
199
+ }
200
+ @media (pointer: coarse) {
201
+ .pointer-coarse\:\[\&\.gridui-button-small\]\:h-11.gridui-button-small {
202
+ height: calc(var(--spacing) * 11);
203
+ }
204
+ .pointer-coarse\:\[\&\.gridui-button-small\]\:w-11.gridui-button-small {
205
+ width: calc(var(--spacing) * 11);
206
+ }
207
+ }
208
+ .\[\&\:active\:not\(\:disabled\)\]\:opacity-\[0\.8\]:active:not(:disabled) {
209
+ opacity: 0.8;
210
+ }
211
+ .\[\&\:hover\:not\(\:disabled\)\]\:border-foreground:hover:not(:disabled) {
212
+ border-color: var(--gridui-foreground);
213
+ }
214
+ .\[\&\:hover\:not\(\:disabled\)\]\:bg-surface-hover:hover:not(:disabled) {
215
+ background-color: var(--gridui-surface-hover);
216
+ }
217
+ }
218
+ @property --tw-border-style {
219
+ syntax: "*";
220
+ inherits: false;
221
+ initial-value: solid;
222
+ }
223
+ @property --tw-duration {
224
+ syntax: "*";
225
+ inherits: false;
226
+ }
227
+ @property --tw-ease {
228
+ syntax: "*";
229
+ inherits: false;
230
+ }
231
+ @layer properties {
232
+ @supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) {
233
+ *, ::before, ::after, ::backdrop {
234
+ --tw-border-style: solid;
235
+ --tw-duration: initial;
236
+ --tw-ease: initial;
237
+ }
238
+ }
239
+ }
package/package.json CHANGED
@@ -1,6 +1,58 @@
1
1
  {
2
2
  "name": "@gridui/button",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.0.0",
4
+ "description": "GridUI React buttons and action helpers",
5
+ "license": "UNLICENSED",
6
+ "type": "module",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/sudo-ui/GridUI-React-Monorepo.git",
10
+ "directory": "packages/button"
11
+ },
12
+ "files": [
13
+ "dist",
14
+ "README.md"
15
+ ],
16
+ "sideEffects": [
17
+ "**/*.css"
18
+ ],
19
+ "exports": {
20
+ ".": {
21
+ "types": "./dist/index.d.ts",
22
+ "import": "./dist/index.js"
23
+ },
24
+ "./styles.css": "./dist/styles.css"
25
+ },
26
+ "publishConfig": {
27
+ "access": "public"
28
+ },
29
+ "dependencies": {
30
+ "@gridui/style-utils": "^2.1.1",
31
+ "@gridui/theme": "^2.1.1",
32
+ "class-variance-authority": "0.7.1"
33
+ },
34
+ "devDependencies": {
35
+ "@gridui-internal/configuration": "0.0.0",
36
+ "@jest/globals": "30.5.2",
37
+ "@storybook/react-vite": "10.6.3",
38
+ "@types/jest": "30.0.0",
39
+ "@types/node": "26.6.5",
40
+ "@types/react": "19.3.0",
41
+ "@types/react-dom": "19.3.0",
42
+ "jest": "30.5.2",
43
+ "lucide-react": "1.61.0",
44
+ "react": "19.3.1",
45
+ "react-dom": "19.3.1",
46
+ "typescript": "6.0.3"
47
+ },
48
+ "peerDependencies": {
49
+ "react": "^19.2.0",
50
+ "react-dom": "^19.2.0"
51
+ },
52
+ "scripts": {
53
+ "build": "node ../../scripts/compilation/package.ts",
54
+ "compile": "tsc --project tsconfig.json",
55
+ "test": "jest",
56
+ "coverage": "jest --coverage"
57
+ }
6
58
  }