@labpane/ui 0.0.0-stage → 1.2.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.
Files changed (76) hide show
  1. package/README.md +130 -2
  2. package/dist/LICENSE +21 -0
  3. package/dist/chunk-NR3PLNO7.js +100 -0
  4. package/dist/chunk-NR3PLNO7.js.map +7 -0
  5. package/dist/index.js +7137 -0
  6. package/dist/index.js.map +7 -0
  7. package/dist/styles.css +2 -0
  8. package/dist/styles.d.ts +1 -0
  9. package/dist/terminal.js +369 -0
  10. package/dist/terminal.js.map +7 -0
  11. package/dist/ui/alert.d.ts +13 -0
  12. package/dist/ui/app-menu.d.ts +19 -0
  13. package/dist/ui/avatar.d.ts +11 -0
  14. package/dist/ui/badge.d.ts +17 -0
  15. package/dist/ui/breadcrumb.d.ts +11 -0
  16. package/dist/ui/browser-layout.d.ts +20 -0
  17. package/dist/ui/button.d.ts +27 -0
  18. package/dist/ui/card.d.ts +15 -0
  19. package/dist/ui/chart-frame.d.ts +4 -0
  20. package/dist/ui/checkbox.d.ts +4 -0
  21. package/dist/ui/classes.d.ts +7 -0
  22. package/dist/ui/command.d.ts +82 -0
  23. package/dist/ui/confirm-dialog.d.ts +28 -0
  24. package/dist/ui/copy-button.d.ts +7 -0
  25. package/dist/ui/dialog.d.ts +18 -0
  26. package/dist/ui/discard-dialog.d.ts +6 -0
  27. package/dist/ui/email-thread.d.ts +35 -0
  28. package/dist/ui/empty.d.ts +14 -0
  29. package/dist/ui/field.d.ts +26 -0
  30. package/dist/ui/file-input.d.ts +6 -0
  31. package/dist/ui/flame-graph-model.d.ts +23 -0
  32. package/dist/ui/flame-graph.d.ts +15 -0
  33. package/dist/ui/form-fields.d.ts +21 -0
  34. package/dist/ui/histogram.d.ts +23 -0
  35. package/dist/ui/index.d.ts +57 -0
  36. package/dist/ui/input-group.d.ts +28 -0
  37. package/dist/ui/input.d.ts +3 -0
  38. package/dist/ui/kbd.d.ts +4 -0
  39. package/dist/ui/label.d.ts +3 -0
  40. package/dist/ui/menu.d.ts +60 -0
  41. package/dist/ui/native-select.d.ts +7 -0
  42. package/dist/ui/nav-tabs.d.ts +7 -0
  43. package/dist/ui/overlay.d.ts +1 -0
  44. package/dist/ui/page.d.ts +22 -0
  45. package/dist/ui/rail.d.ts +16 -0
  46. package/dist/ui/search-input.d.ts +6 -0
  47. package/dist/ui/select.d.ts +13 -0
  48. package/dist/ui/separator.d.ts +6 -0
  49. package/dist/ui/settings.d.ts +14 -0
  50. package/dist/ui/sheet.d.ts +20 -0
  51. package/dist/ui/side-panel.d.ts +23 -0
  52. package/dist/ui/sidebar.d.ts +81 -0
  53. package/dist/ui/skeleton.d.ts +3 -0
  54. package/dist/ui/spinner.d.ts +3 -0
  55. package/dist/ui/stat.d.ts +7 -0
  56. package/dist/ui/steps.d.ts +7 -0
  57. package/dist/ui/switch.d.ts +6 -0
  58. package/dist/ui/tab-state.d.ts +8 -0
  59. package/dist/ui/table-layout.d.ts +19 -0
  60. package/dist/ui/table-model.d.ts +42 -0
  61. package/dist/ui/table-screen.d.ts +9 -0
  62. package/dist/ui/table.d.ts +41 -0
  63. package/dist/ui/tabs.d.ts +13 -0
  64. package/dist/ui/terminal.d.ts +57 -0
  65. package/dist/ui/textarea.d.ts +3 -0
  66. package/dist/ui/theme.d.ts +22 -0
  67. package/dist/ui/time-series-chart.d.ts +19 -0
  68. package/dist/ui/time-series-model.d.ts +90 -0
  69. package/dist/ui/timing-tracks.d.ts +27 -0
  70. package/dist/ui/toggle.d.ts +22 -0
  71. package/dist/ui/tooltip.d.ts +7 -0
  72. package/dist/ui/topbar.d.ts +4 -0
  73. package/dist/ui/use-chart-width.d.ts +5 -0
  74. package/dist/ui/use-mobile.d.ts +1 -0
  75. package/package.json +116 -4
  76. package/styles.css +358 -0
package/README.md CHANGED
@@ -1,3 +1,131 @@
1
- # Temporary Holding Version
1
+ # @labpane/ui
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
+ Pages, forms, dialogs, menus, tables, navigation, charts, and an optional terminal for React, built on Radix. It pairs with [`@labpane/os`](../os) but works on its own.
4
+
5
+ ```sh
6
+ npm install @labpane/ui @radix-ui/react-alert-dialog @radix-ui/react-avatar @radix-ui/react-checkbox @radix-ui/react-context-menu @radix-ui/react-dialog @radix-ui/react-dropdown-menu @radix-ui/react-popover @radix-ui/react-select @radix-ui/react-slot @radix-ui/react-switch @radix-ui/react-tabs @radix-ui/react-toggle @radix-ui/react-toggle-group @radix-ui/react-tooltip
7
+ ```
8
+
9
+ ```tsx
10
+ import "@labpane/ui/styles.css";
11
+ import { Button, Page } from "@labpane/ui";
12
+
13
+ export function Settings() {
14
+ return (
15
+ <Page title="Settings" actions={<Button>Save</Button>}>
16
+ …
17
+ </Page>
18
+ );
19
+ }
20
+ ```
21
+
22
+ Radix and React are peer dependencies, so your app keeps one copy of each. The stylesheet is built with Tailwind 4 but needs no Tailwind in your app: every class is prefixed `labui:`, nothing is global, and it builds in Tailwind 3 PostCSS pipelines too.
23
+
24
+ ## Components
25
+
26
+ | | |
27
+ | ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
28
+ | Pages | `Page`, `PageHeader` and its parts, `PageActions`, `Back`, `Topbar`, `Breadcrumb` and its parts, `TableScreen`, `BrowserLayout` and its parts, `SidePanel`, `SettingsSection`, `SettingsRow`, `SettingsFooter` |
29
+ | Navigation | `Sidebar` and its parts, `Rail`, `RailItem`, `NavTabs`, `NavTab`, `Tabs` and its parts, `Steps`, `Step`, `Command` and its parts |
30
+ | Controls | `Button`, `Badge`, `Input`, `Textarea`, `Checkbox`, `Switch`, `Toggle`, `ToggleGroup`, `Select` and its parts, `NativeSelect`, `SearchInput`, `FileInput`, `InputGroup` and its parts, `CopyButton`, `Kbd` |
31
+ | Forms | `Field`, `FieldLabel`, `FieldDescription`, `FieldError`, `FieldSet`, `FieldGroup`, `Label`, `TextField`, `SelectField`, `AddButton` |
32
+ | Display | `Alert`, `Card` and its parts, `Avatar` and its parts, `Stats`, `Stat`, `Empty` and its parts, `Separator`, `Skeleton`, `Spinner`, `Tooltip`, `EmailThread`, `EmailMessage` |
33
+ | Dialogs | `Dialog` and its parts, `Sheet` and its parts, `ConfirmDialog`, `DiscardDialog` |
34
+ | Menus | `DropdownMenu` and its parts, `OptionMenu`, `ActionMenu`, `MenuContent` |
35
+ | Tables | `Table`, `TableStack`, `column`, `mono`, `num` |
36
+ | Charts | `TimeSeriesChart`, `HistogramChart`, `HistogramSparkline`, `FlameGraph`, `TimingTracks`, `ChartFrame` |
37
+ | Workspace | `AppMenu` with `AppMenuSection`; `useTabActive` and `useTabVisible` read the tab state that `@labpane/os` provides |
38
+
39
+ `cn()` merges class names and lets your own Tailwind classes override the `labui:` defaults they conflict with.
40
+
41
+ Tailwind 4 apps can also write their own `labui:` classes on the same theme by importing ui's Tailwind source instead of the built stylesheet:
42
+
43
+ ```css
44
+ @import "@labpane/ui/tailwind.css";
45
+ @source "./src";
46
+ ```
47
+
48
+ ## With @labpane/os
49
+
50
+ Fill the workspace's slots with ui's menus and dialog:
51
+
52
+ ```tsx
53
+ <Workspace
54
+ {...workspaceProps}
55
+ toolbar={(pane) => (
56
+ <OptionMenu
57
+ options={[{ id: "reports", label: "Reports" }]}
58
+ onSelect={openTab}
59
+ trigger={
60
+ <Button size="icon-sm" aria-label="Open a tab">
61
+ +
62
+ </Button>
63
+ }
64
+ />
65
+ )}
66
+ menu={(target, actions, restoreFocus) => (
67
+ <ActionMenu
68
+ actions={actions}
69
+ onCloseAutoFocus={
70
+ restoreFocus &&
71
+ ((event) => {
72
+ event.preventDefault();
73
+ restoreFocus();
74
+ })
75
+ }
76
+ >
77
+ {target}
78
+ </ActionMenu>
79
+ )}
80
+ confirmClose={(tab) => askToDiscard(tab.label)}
81
+ />
82
+ ```
83
+
84
+ `askToDiscard` returns a promise that a `DiscardDialog` resolves with the user's choice. Action menu items can hold `items` for a submenu, or `checked` for a check mark.
85
+
86
+ ## Terminal
87
+
88
+ `@labpane/ui/terminal` is a separate entry point, so apps that don't import it never load the terminal. It needs [`@wterm/dom`](https://wterm.dev):
89
+
90
+ ```sh
91
+ npm install @wterm/dom
92
+ ```
93
+
94
+ ```tsx
95
+ import "@wterm/dom/css";
96
+ import { Terminal } from "@labpane/ui/terminal";
97
+
98
+ <Terminal connect={() => "/pty"} expandable />;
99
+ ```
100
+
101
+ `connect` returns the URL of a WebSocket that streams the terminal: binary frames carry input and output, text frames carry error messages, and resizes arrive as JSON `{ cols, rows }`. Instead of `connect`, `shell` runs an in-browser shell such as `BashShell` from `@wterm/just-bash`. `core` swaps in another terminal engine, such as `GhosttyCore` from `@wterm/ghostty`, and `theme` picks one of `terminalThemes` or `"auto"`.
102
+
103
+ ## Theming
104
+
105
+ Set these on `:root`, or on any ancestor. Add a `dark` class to an ancestor for the dark defaults.
106
+
107
+ ```css
108
+ :root {
109
+ --labui-primary: #05697a;
110
+ --labui-radius: 0.75rem;
111
+ --labui-font-sans: inherit;
112
+ }
113
+ ```
114
+
115
+ A theme can also be an object. `themeCss(theme)` returns the CSS for both packages' tokens and `<ThemeStyle theme={theme} />` renders it; keys left out keep the defaults.
116
+
117
+ ```tsx
118
+ <ThemeStyle
119
+ theme={{
120
+ light: { primary: "#a3242f" },
121
+ dark: { primary: "#f0808c" },
122
+ radius: "0.5rem",
123
+ }}
124
+ />
125
+ ```
126
+
127
+ Colors: `background`, `foreground`, `card`, `popover`, `primary`, `secondary`, `muted`, `accent`, `destructive`, `success`, and `warning`, each with a `-foreground` pair where it applies, plus `link`, `border`, `input`, `input-background`, `ring`, `overlay`, and `destructive-muted`. Fonts: `font-sans`, `font-mono`, and `font-display`. Sizes: `radius`, `radius-dialog`, `control-height`, `control-height-sm`, `control-px`, and `row-py`. Each is a `--labui-` variable.
128
+
129
+ ## License
130
+
131
+ MIT
package/dist/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Authors of Labpane
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,100 @@
1
+ // classes.ts
2
+ import { clsx } from "clsx";
3
+ import { extendTailwindMerge } from "tailwind-merge";
4
+ var twMerge = extendTailwindMerge({
5
+ experimentalParseClassName: ({ className, parseClassName }) => parseClassName(className.replace(/^labui:/, ""))
6
+ });
7
+ function cn(...inputs) {
8
+ return twMerge(clsx(inputs));
9
+ }
10
+ var focusRing = "labui:focus-visible:border-ring labui:focus-visible:ring-[3px] labui:focus-visible:ring-ring/50";
11
+ var invalidRing = "labui:aria-invalid:border-destructive labui:aria-invalid:ring-destructive/20 labui:dark:aria-invalid:ring-destructive/40";
12
+ var overlay = "labui:fixed labui:inset-0 labui:z-50 labui:bg-overlay labui:data-[state=open]:animate-fade-in labui:data-[state=closed]:animate-fade-out";
13
+ var closeButton = [
14
+ "labui:absolute labui:top-4 labui:right-4 labui:inline-flex labui:size-8 labui:items-center labui:justify-center labui:rounded-md",
15
+ "labui:text-muted-foreground labui:transition-colors labui:hover:bg-accent labui:hover:text-foreground",
16
+ "labui:focus-visible:ring-[3px] labui:focus-visible:ring-ring/50 labui:focus-visible:outline-none labui:disabled:pointer-events-none",
17
+ "labui:[&_svg]:pointer-events-none labui:[&_svg]:size-4 labui:[&_svg]:shrink-0"
18
+ ].join(" ");
19
+ var inputSurface = [
20
+ "labui:w-full labui:min-w-0 labui:rounded-md labui:border labui:border-input labui:bg-input-background labui:shadow-xs",
21
+ "labui:text-base labui:md:text-sm labui:placeholder:text-muted-foreground labui:transition-[color,box-shadow] labui:outline-none",
22
+ "labui:disabled:cursor-not-allowed labui:disabled:opacity-50",
23
+ focusRing,
24
+ invalidRing
25
+ ].join(" ");
26
+
27
+ // button.tsx
28
+ import * as React from "react";
29
+ import { Slot } from "@radix-ui/react-slot";
30
+ import { jsx } from "react/jsx-runtime";
31
+ var raisedButtonSurface = [
32
+ "labui:border labui:border-border labui:border-b-input labui:bg-card labui:bg-linear-to-b labui:from-card labui:to-muted labui:text-foreground",
33
+ "labui:shadow-[inset_0_1px_0_#ffffff,0_1px_1px_#00000014,0_3px_4px_#0000000d]",
34
+ "labui:hover:to-background labui:hover:shadow-[inset_0_1px_0_#ffffff,0_2px_3px_#0000001a,0_4px_6px_#0000000d]",
35
+ "labui:active:translate-y-px labui:active:from-muted labui:active:to-card labui:active:shadow-[inset_0_1px_2px_#0000001a,0_1px_0_#ffffff80]",
36
+ "labui:dark:border-foreground/15 labui:dark:border-b-foreground/10 labui:dark:bg-secondary labui:dark:from-[color-mix(in_srgb,var(--labui-secondary)_88%,var(--labui-foreground))] labui:dark:to-secondary labui:dark:text-popover-foreground",
37
+ "labui:dark:shadow-[inset_0_1px_0_#ffffff14,0_1px_2px_#00000026]",
38
+ "labui:dark:hover:border-foreground/25 labui:dark:hover:from-[color-mix(in_srgb,var(--labui-secondary)_82%,var(--labui-foreground))] labui:dark:hover:to-secondary labui:dark:hover:shadow-[inset_0_1px_0_#ffffff1f,0_2px_4px_#00000033]",
39
+ "labui:dark:active:from-secondary labui:dark:active:to-secondary labui:dark:active:shadow-[inset_0_1px_2px_#00000033,0_1px_0_#ffffff0a]",
40
+ "labui:disabled:bg-secondary labui:disabled:bg-none labui:disabled:shadow-none labui:dark:disabled:shadow-none"
41
+ ].join(" ");
42
+ var buttonVariants = {
43
+ default: "labui:border labui:border-transparent labui:bg-primary labui:text-primary-foreground labui:shadow-xs labui:hover:bg-primary/90 labui:active:translate-y-px",
44
+ destructive: "labui:border labui:border-transparent labui:bg-destructive-muted labui:text-destructive labui:hover:border-destructive/20 labui:hover:bg-destructive/15 labui:focus-visible:ring-destructive/20 labui:dark:focus-visible:ring-destructive/40",
45
+ outline: "labui:border labui:bg-background labui:shadow-xs labui:hover:bg-accent labui:hover:text-accent-foreground labui:dark:border-input labui:dark:bg-input/30 labui:dark:hover:bg-input/50",
46
+ secondary: raisedButtonSurface,
47
+ ghost: "labui:hover:bg-accent labui:hover:text-accent-foreground labui:dark:hover:bg-accent/50",
48
+ link: "labui:text-link labui:underline-offset-4 labui:hover:underline"
49
+ };
50
+ var buttonSizes = {
51
+ default: "labui:h-(--labui-control-height) labui:px-4 labui:py-2 labui:has-[>svg]:px-3",
52
+ xs: "labui:h-6 labui:gap-1 labui:rounded-md labui:px-2 labui:text-xs labui:has-[>svg]:px-1.5 labui:[&_svg:not([class*='size-'])]:size-3",
53
+ sm: "labui:h-(--labui-control-height-sm) labui:gap-1.5 labui:rounded-md labui:px-3 labui:has-[>svg]:px-2.5",
54
+ lg: "labui:h-11 labui:rounded-md labui:px-6 labui:has-[>svg]:px-4",
55
+ icon: "labui:size-(--labui-control-height)",
56
+ "icon-xs": "labui:size-6 labui:rounded-md labui:[&_svg:not([class*='size-'])]:size-3",
57
+ "icon-sm": "labui:size-(--labui-control-height-sm)",
58
+ "icon-lg": "labui:size-11"
59
+ };
60
+ var Button = React.forwardRef(function Button2({
61
+ className,
62
+ variant = "default",
63
+ size = "default",
64
+ asChild = false,
65
+ ...props
66
+ }, ref) {
67
+ const Comp = asChild ? Slot : "button";
68
+ return /* @__PURE__ */ jsx(
69
+ Comp,
70
+ {
71
+ ref,
72
+ "data-slot": "button",
73
+ "data-variant": variant,
74
+ "data-size": size,
75
+ className: cn(
76
+ "labui:inline-flex labui:shrink-0 labui:items-center labui:justify-center labui:gap-2 labui:rounded-md",
77
+ "labui:text-sm labui:font-medium labui:whitespace-nowrap labui:transition-all labui:outline-none",
78
+ "labui:disabled:pointer-events-none labui:disabled:opacity-50",
79
+ focusRing,
80
+ invalidRing,
81
+ "labui:[&_svg]:pointer-events-none labui:[&_svg]:shrink-0 labui:[&_svg:not([class*='size-'])]:size-4",
82
+ variant && buttonVariants[variant],
83
+ size && buttonSizes[size],
84
+ className
85
+ ),
86
+ ...props
87
+ }
88
+ );
89
+ });
90
+
91
+ export {
92
+ cn,
93
+ focusRing,
94
+ invalidRing,
95
+ overlay,
96
+ closeButton,
97
+ inputSurface,
98
+ Button
99
+ };
100
+ //# sourceMappingURL=chunk-NR3PLNO7.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../classes.ts", "../button.tsx"],
4
+ "sourcesContent": ["import { type ClassValue, clsx } from \"clsx\";\nimport { extendTailwindMerge } from \"tailwind-merge\";\n\nconst twMerge = extendTailwindMerge({\n experimentalParseClassName: ({ className, parseClassName }) =>\n parseClassName(className.replace(/^labui:/, \"\")),\n});\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n\nexport const focusRing =\n \"labui:focus-visible:border-ring labui:focus-visible:ring-[3px] labui:focus-visible:ring-ring/50\";\n\nexport const invalidRing =\n \"labui:aria-invalid:border-destructive labui:aria-invalid:ring-destructive/20 labui:dark:aria-invalid:ring-destructive/40\";\n\nexport const overlay =\n \"labui:fixed labui:inset-0 labui:z-50 labui:bg-overlay labui:data-[state=open]:animate-fade-in labui:data-[state=closed]:animate-fade-out\";\n\nexport const closeButton = [\n \"labui:absolute labui:top-4 labui:right-4 labui:inline-flex labui:size-8 labui:items-center labui:justify-center labui:rounded-md\",\n \"labui:text-muted-foreground labui:transition-colors labui:hover:bg-accent labui:hover:text-foreground\",\n \"labui:focus-visible:ring-[3px] labui:focus-visible:ring-ring/50 labui:focus-visible:outline-none labui:disabled:pointer-events-none\",\n \"labui:[&_svg]:pointer-events-none labui:[&_svg]:size-4 labui:[&_svg]:shrink-0\",\n].join(\" \");\n\nexport const inputSurface = [\n \"labui:w-full labui:min-w-0 labui:rounded-md labui:border labui:border-input labui:bg-input-background labui:shadow-xs\",\n \"labui:text-base labui:md:text-sm labui:placeholder:text-muted-foreground labui:transition-[color,box-shadow] labui:outline-none\",\n \"labui:disabled:cursor-not-allowed labui:disabled:opacity-50\",\n focusRing,\n invalidRing,\n].join(\" \");\n", "import * as React from \"react\";\nimport { Slot } from \"@radix-ui/react-slot\";\nimport { cn, focusRing, invalidRing } from \"./classes.js\";\n\nconst raisedButtonSurface = [\n \"labui:border labui:border-border labui:border-b-input labui:bg-card labui:bg-linear-to-b labui:from-card labui:to-muted labui:text-foreground\",\n \"labui:shadow-[inset_0_1px_0_#ffffff,0_1px_1px_#00000014,0_3px_4px_#0000000d]\",\n \"labui:hover:to-background labui:hover:shadow-[inset_0_1px_0_#ffffff,0_2px_3px_#0000001a,0_4px_6px_#0000000d]\",\n \"labui:active:translate-y-px labui:active:from-muted labui:active:to-card labui:active:shadow-[inset_0_1px_2px_#0000001a,0_1px_0_#ffffff80]\",\n \"labui:dark:border-foreground/15 labui:dark:border-b-foreground/10 labui:dark:bg-secondary labui:dark:from-[color-mix(in_srgb,var(--labui-secondary)_88%,var(--labui-foreground))] labui:dark:to-secondary labui:dark:text-popover-foreground\",\n \"labui:dark:shadow-[inset_0_1px_0_#ffffff14,0_1px_2px_#00000026]\",\n \"labui:dark:hover:border-foreground/25 labui:dark:hover:from-[color-mix(in_srgb,var(--labui-secondary)_82%,var(--labui-foreground))] labui:dark:hover:to-secondary labui:dark:hover:shadow-[inset_0_1px_0_#ffffff1f,0_2px_4px_#00000033]\",\n \"labui:dark:active:from-secondary labui:dark:active:to-secondary labui:dark:active:shadow-[inset_0_1px_2px_#00000033,0_1px_0_#ffffff0a]\",\n \"labui:disabled:bg-secondary labui:disabled:bg-none labui:disabled:shadow-none labui:dark:disabled:shadow-none\",\n].join(\" \");\n\nconst buttonVariants = {\n default:\n \"labui:border labui:border-transparent labui:bg-primary labui:text-primary-foreground labui:shadow-xs labui:hover:bg-primary/90 labui:active:translate-y-px\",\n destructive:\n \"labui:border labui:border-transparent labui:bg-destructive-muted labui:text-destructive labui:hover:border-destructive/20 labui:hover:bg-destructive/15 labui:focus-visible:ring-destructive/20 labui:dark:focus-visible:ring-destructive/40\",\n outline:\n \"labui:border labui:bg-background labui:shadow-xs labui:hover:bg-accent labui:hover:text-accent-foreground labui:dark:border-input labui:dark:bg-input/30 labui:dark:hover:bg-input/50\",\n secondary: raisedButtonSurface,\n ghost:\n \"labui:hover:bg-accent labui:hover:text-accent-foreground labui:dark:hover:bg-accent/50\",\n link: \"labui:text-link labui:underline-offset-4 labui:hover:underline\",\n};\n\nconst buttonSizes = {\n default:\n \"labui:h-(--labui-control-height) labui:px-4 labui:py-2 labui:has-[>svg]:px-3\",\n xs: \"labui:h-6 labui:gap-1 labui:rounded-md labui:px-2 labui:text-xs labui:has-[>svg]:px-1.5 labui:[&_svg:not([class*='size-'])]:size-3\",\n sm: \"labui:h-(--labui-control-height-sm) labui:gap-1.5 labui:rounded-md labui:px-3 labui:has-[>svg]:px-2.5\",\n lg: \"labui:h-11 labui:rounded-md labui:px-6 labui:has-[>svg]:px-4\",\n icon: \"labui:size-(--labui-control-height)\",\n \"icon-xs\":\n \"labui:size-6 labui:rounded-md labui:[&_svg:not([class*='size-'])]:size-3\",\n \"icon-sm\": \"labui:size-(--labui-control-height-sm)\",\n \"icon-lg\": \"labui:size-11\",\n};\n\nexport type ButtonVariant = keyof typeof buttonVariants;\nexport type ButtonSize = keyof typeof buttonSizes;\n\nconst Button = React.forwardRef<\n HTMLButtonElement,\n React.ComponentPropsWithoutRef<\"button\"> & {\n variant?: ButtonVariant | null;\n size?: ButtonSize | null;\n asChild?: boolean;\n }\n>(function Button(\n {\n className,\n variant = \"default\",\n size = \"default\",\n asChild = false,\n ...props\n },\n ref,\n) {\n const Comp = asChild ? Slot : \"button\";\n\n return (\n <Comp\n ref={ref}\n data-slot=\"button\"\n data-variant={variant}\n data-size={size}\n className={cn(\n \"labui:inline-flex labui:shrink-0 labui:items-center labui:justify-center labui:gap-2 labui:rounded-md\",\n \"labui:text-sm labui:font-medium labui:whitespace-nowrap labui:transition-all labui:outline-none\",\n \"labui:disabled:pointer-events-none labui:disabled:opacity-50\",\n focusRing,\n invalidRing,\n \"labui:[&_svg]:pointer-events-none labui:[&_svg]:shrink-0 labui:[&_svg:not([class*='size-'])]:size-4\",\n variant && buttonVariants[variant],\n size && buttonSizes[size],\n className,\n )}\n {...props}\n />\n );\n});\n\nexport { Button };\n"],
5
+ "mappings": ";AAAA,SAA0B,YAAY;AACtC,SAAS,2BAA2B;AAEpC,IAAM,UAAU,oBAAoB;AAAA,EAClC,4BAA4B,CAAC,EAAE,WAAW,eAAe,MACvD,eAAe,UAAU,QAAQ,WAAW,EAAE,CAAC;AACnD,CAAC;AAEM,SAAS,MAAM,QAAsB;AAC1C,SAAO,QAAQ,KAAK,MAAM,CAAC;AAC7B;AAEO,IAAM,YACX;AAEK,IAAM,cACX;AAEK,IAAM,UACX;AAEK,IAAM,cAAc;AAAA,EACzB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,EAAE,KAAK,GAAG;AAEH,IAAM,eAAe;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,EAAE,KAAK,GAAG;;;AClCV,YAAY,WAAW;AACvB,SAAS,YAAY;AAgEjB;AA7DJ,IAAM,sBAAsB;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,EAAE,KAAK,GAAG;AAEV,IAAM,iBAAiB;AAAA,EACrB,SACE;AAAA,EACF,aACE;AAAA,EACF,SACE;AAAA,EACF,WAAW;AAAA,EACX,OACE;AAAA,EACF,MAAM;AACR;AAEA,IAAM,cAAc;AAAA,EAClB,SACE;AAAA,EACF,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,MAAM;AAAA,EACN,WACE;AAAA,EACF,WAAW;AAAA,EACX,WAAW;AACb;AAKA,IAAM,SAAe,iBAOnB,SAASA,QACT;AAAA,EACE;AAAA,EACA,UAAU;AAAA,EACV,OAAO;AAAA,EACP,UAAU;AAAA,EACV,GAAG;AACL,GACA,KACA;AACA,QAAM,OAAO,UAAU,OAAO;AAE9B,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,aAAU;AAAA,MACV,gBAAc;AAAA,MACd,aAAW;AAAA,MACX,WAAW;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,WAAW,eAAe,OAAO;AAAA,QACjC,QAAQ,YAAY,IAAI;AAAA,QACxB;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ,CAAC;",
6
+ "names": ["Button"]
7
+ }