beacon-ui 3.1.4

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 (59) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/README.md +175 -0
  3. package/dist/components/Avatar.d.ts +20 -0
  4. package/dist/components/Avatar.d.ts.map +1 -0
  5. package/dist/components/Avatar.js +174 -0
  6. package/dist/components/Button.d.ts +21 -0
  7. package/dist/components/Button.d.ts.map +1 -0
  8. package/dist/components/Button.js +227 -0
  9. package/dist/components/Card.d.ts +40 -0
  10. package/dist/components/Card.d.ts.map +1 -0
  11. package/dist/components/Card.js +517 -0
  12. package/dist/components/Checkbox.d.ts +12 -0
  13. package/dist/components/Checkbox.d.ts.map +1 -0
  14. package/dist/components/Checkbox.js +64 -0
  15. package/dist/components/CheckboxPreview.d.ts +13 -0
  16. package/dist/components/CheckboxPreview.d.ts.map +1 -0
  17. package/dist/components/CheckboxPreview.js +155 -0
  18. package/dist/components/Chip.d.ts +15 -0
  19. package/dist/components/Chip.d.ts.map +1 -0
  20. package/dist/components/Chip.js +99 -0
  21. package/dist/components/Input.d.ts +24 -0
  22. package/dist/components/Input.d.ts.map +1 -0
  23. package/dist/components/Input.js +138 -0
  24. package/dist/components/Menu.d.ts +20 -0
  25. package/dist/components/Menu.d.ts.map +1 -0
  26. package/dist/components/Menu.js +252 -0
  27. package/dist/components/RadioButton.d.ts +13 -0
  28. package/dist/components/RadioButton.d.ts.map +1 -0
  29. package/dist/components/RadioButton.js +140 -0
  30. package/dist/components/Switch.d.ts +11 -0
  31. package/dist/components/Switch.d.ts.map +1 -0
  32. package/dist/components/Switch.js +64 -0
  33. package/dist/components/SwitchPreview.d.ts +14 -0
  34. package/dist/components/SwitchPreview.d.ts.map +1 -0
  35. package/dist/components/SwitchPreview.js +281 -0
  36. package/dist/icons/index.d.ts +97 -0
  37. package/dist/icons/index.d.ts.map +1 -0
  38. package/dist/icons/index.js +383 -0
  39. package/dist/index.d.ts +13 -0
  40. package/dist/index.d.ts.map +1 -0
  41. package/dist/index.js +12 -0
  42. package/dist/providers/ThemeProvider.d.ts +23 -0
  43. package/dist/providers/ThemeProvider.d.ts.map +1 -0
  44. package/dist/providers/ThemeProvider.js +79 -0
  45. package/dist/tokens/types.d.ts +14 -0
  46. package/dist/tokens/types.d.ts.map +1 -0
  47. package/dist/tokens/types.js +5 -0
  48. package/dist/utils/patternPaths.d.ts +28 -0
  49. package/dist/utils/patternPaths.d.ts.map +1 -0
  50. package/dist/utils/patternPaths.js +92 -0
  51. package/package.json +51 -0
  52. package/tokens/generated/brand-dark.css +86 -0
  53. package/tokens/generated/brand-light.css +86 -0
  54. package/tokens/generated/effects.css +10 -0
  55. package/tokens/generated/index.css +804 -0
  56. package/tokens/generated/primitives.css +116 -0
  57. package/tokens/generated/responsive.css +235 -0
  58. package/tokens/generated/semantic.css +138 -0
  59. package/tokens/generated/typography.css +124 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,50 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [3.1.4] - 2025-12-29
9
+
10
+ ### Added
11
+ - Initial npm package release as `beacon-ui`
12
+ - README.md with installation and usage instructions
13
+ - CHANGELOG.md for version tracking
14
+ - Comprehensive documentation for all components
15
+ - TypeScript type exports for design tokens
16
+ - 9 production-ready components:
17
+ - Avatar
18
+ - Button
19
+ - Card
20
+ - Checkbox
21
+ - Chip
22
+ - Input
23
+ - Menu
24
+ - Radio Button
25
+ - Switch
26
+ - ThemeProvider for theme and hue management
27
+ - Complete design token system:
28
+ - Primitives (colors, spacing, typography)
29
+ - Semantic tokens (primary, success, warning, critical)
30
+ - Brand tokens (light/dark themes)
31
+ - Responsive tokens (desktop, tablet, mobile)
32
+ - Effect tokens (shadows)
33
+ - Typography tokens
34
+ - TypeScript support with full type definitions
35
+ - Icon component library
36
+ - Support for multiple themes (light/dark)
37
+ - Support for multiple hue variants (chromatic-prime, hue-sky, hue-indigo)
38
+
39
+ ### Features
40
+ - Token-driven architecture
41
+ - WCAG 2.1 AA accessibility compliance
42
+ - Responsive design support
43
+ - Full TypeScript support
44
+ - React 18 and 19 compatibility
45
+
46
+ ---
47
+
48
+ ## Version History
49
+
50
+ - **3.1.4** - Initial release of `beacon-ui` package
package/README.md ADDED
@@ -0,0 +1,175 @@
1
+ # Beacon Design System
2
+
3
+ A comprehensive React design system with 9 production-ready components and design tokens. Built with TypeScript and token-driven architecture for consistency and scalability.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install beacon-ui
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ### 1. Import Tokens CSS
14
+
15
+ Import the design tokens in your main CSS file or at the root of your application:
16
+
17
+ ```tsx
18
+ import 'beacon-ui/tokens';
19
+ ```
20
+
21
+ Or import specific token files:
22
+
23
+ ```tsx
24
+ import 'beacon-ui/tokens/primitives';
25
+ import 'beacon-ui/tokens/semantic';
26
+ import 'beacon-ui/tokens/brand-light';
27
+ import 'beacon-ui/tokens/brand-dark';
28
+ import 'beacon-ui/tokens/responsive';
29
+ import 'beacon-ui/tokens/effects';
30
+ import 'beacon-ui/tokens/typography';
31
+ ```
32
+
33
+ ### 2. Wrap Your App with ThemeProvider
34
+
35
+ ```tsx
36
+ import { ThemeProvider } from 'beacon-ui';
37
+
38
+ function App() {
39
+ return (
40
+ <ThemeProvider defaultTheme="dark" defaultHue="hue-sky">
41
+ {/* Your app content */}
42
+ </ThemeProvider>
43
+ );
44
+ }
45
+ ```
46
+
47
+ ### 3. Use Components
48
+
49
+ ```tsx
50
+ import { Button, Card, Checkbox, Switch, Input, Avatar, Chip, Menu, RadioButton } from 'beacon-ui';
51
+
52
+ function MyComponent() {
53
+ return (
54
+ <>
55
+ <Button
56
+ variant="filled"
57
+ size="md"
58
+ cornerRadius={2}
59
+ hasStartIcon={false}
60
+ hasEndIcon={false}
61
+ fillContainer={false}
62
+ justifyContent="center"
63
+ state="default"
64
+ theme="dark"
65
+ hue="hue-sky"
66
+ />
67
+ <Checkbox checked={true} onChange={(checked) => console.log(checked)} />
68
+ <Switch checked={false} onChange={(checked) => console.log(checked)} />
69
+ </>
70
+ );
71
+ }
72
+ ```
73
+
74
+ ## Components
75
+
76
+ - **Avatar** - User avatars with icon, text, or image support
77
+ - **Button** - Multiple variants (filled, tonal, outline, link) with sizes and states
78
+ - **Card** - Flexible card components (product, experience, info, generic)
79
+ - **Checkbox** - Accessible checkbox with label support
80
+ - **Chip** - Compact labels and tags
81
+ - **Input** - Form inputs with icons, labels, and error states
82
+ - **Menu** - Navigation menus for desktop, tablet, and mobile
83
+ - **Radio Button** - Radio button groups with label support
84
+ - **Switch** - Toggle switches with optional icons
85
+
86
+ ## Design Tokens
87
+
88
+ Beacon Design System uses a comprehensive token system:
89
+
90
+ - **Primitives** - Base color, spacing, and typography values
91
+ - **Semantic** - Context-aware tokens (primary, success, warning, critical)
92
+ - **Brand** - Theme-specific tokens (light/dark)
93
+ - **Responsive** - Breakpoint-aware tokens for desktop, tablet, and mobile
94
+ - **Effects** - Shadows and visual effects
95
+ - **Typography** - Font families, sizes, weights, and line heights
96
+
97
+ ## Theme Support
98
+
99
+ Beacon supports multiple themes and hue variants:
100
+
101
+ ### Themes
102
+ - `light` - Light theme
103
+ - `dark` - Dark theme (default)
104
+
105
+ ### Hue Variants
106
+ - `chromatic-prime` - Default chromatic palette
107
+ - `hue-sky` - Sky blue variant
108
+ - `hue-indigo` - Indigo variant
109
+
110
+ ### Using Theme Context
111
+
112
+ ```tsx
113
+ import { useTheme } from 'beacon-ui';
114
+
115
+ function ThemeToggle() {
116
+ const { theme, hue, setTheme, setHue, toggleTheme } = useTheme();
117
+
118
+ return (
119
+ <button onClick={toggleTheme}>
120
+ Current theme: {theme}
121
+ </button>
122
+ );
123
+ }
124
+ ```
125
+
126
+ ## TypeScript Support
127
+
128
+ Full TypeScript support with exported types:
129
+
130
+ ```tsx
131
+ import type {
132
+ Theme,
133
+ HueVariant,
134
+ ColorPrimitive,
135
+ SemanticColor,
136
+ SpacingToken,
137
+ BackgroundToken,
138
+ ForegroundToken,
139
+ BorderToken
140
+ } from 'beacon-ui';
141
+ ```
142
+
143
+ ## Accessibility
144
+
145
+ All components are built with accessibility in mind:
146
+ - WCAG 2.1 AA compliant
147
+ - Proper ARIA attributes
148
+ - Keyboard navigation support
149
+ - Focus management
150
+ - Screen reader friendly
151
+
152
+ ## Responsive Design
153
+
154
+ Components adapt seamlessly across breakpoints:
155
+ - Desktop (default)
156
+ - Tablet (max-width: 1024px)
157
+ - Mobile (max-width: 768px)
158
+
159
+ ## Documentation
160
+
161
+ For detailed documentation, component APIs, and examples, visit:
162
+ https://beacon.uxraza.com/
163
+
164
+ ## Version
165
+
166
+ Current version: **3.1.4**
167
+
168
+ ## License
169
+
170
+ MIT
171
+
172
+ ## Repository
173
+
174
+ https://github.com/raza-ahmed/beacon
175
+
@@ -0,0 +1,20 @@
1
+ import type { Theme, HueVariant } from "../tokens/types";
2
+ type AvatarSize = "sm" | "md" | "lg" | "xl";
3
+ type AvatarType = "icon" | "text" | "image";
4
+ type AvatarColor = "primary" | "neutral" | "success" | "critical" | "warning";
5
+ type AvatarVariant = "solid" | "faded";
6
+ interface AvatarProps {
7
+ size: AvatarSize;
8
+ type: AvatarType;
9
+ color: AvatarColor;
10
+ variant: AvatarVariant;
11
+ isRound: boolean;
12
+ hasStroke: boolean;
13
+ theme: Theme;
14
+ hue: HueVariant;
15
+ initials?: string;
16
+ imageUrl?: string;
17
+ }
18
+ export declare function Avatar({ size, type, color, variant, isRound, hasStroke, theme, hue, initials, imageUrl, }: AvatarProps): import("react/jsx-runtime").JSX.Element;
19
+ export {};
20
+ //# sourceMappingURL=Avatar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Avatar.d.ts","sourceRoot":"","sources":["../../src/components/Avatar.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAGzD,KAAK,UAAU,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAC5C,KAAK,UAAU,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAC5C,KAAK,WAAW,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,CAAC;AAC9E,KAAK,aAAa,GAAG,OAAO,GAAG,OAAO,CAAC;AAEvC,UAAU,WAAW;IACnB,IAAI,EAAE,UAAU,CAAC;IACjB,IAAI,EAAE,UAAU,CAAC;IACjB,KAAK,EAAE,WAAW,CAAC;IACnB,OAAO,EAAE,aAAa,CAAC;IACvB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,KAAK,CAAC;IACb,GAAG,EAAE,UAAU,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAwBD,wBAAgB,MAAM,CAAC,EACrB,IAAI,EACJ,IAAI,EACJ,KAAK,EACL,OAAO,EACP,OAAO,EACP,SAAS,EACT,KAAK,EACL,GAAG,EACH,QAAe,EACf,QAAQ,GACT,EAAE,WAAW,2CA4Lb"}
@@ -0,0 +1,174 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { useMemo, useState } from "react";
4
+ import { UserPersonIcon } from "../icons";
5
+ // Avatar container sizes
6
+ const CONTAINER_SIZE_CONFIG = {
7
+ sm: { width: "32px", height: "32px" },
8
+ md: { width: "48px", height: "48px" },
9
+ lg: { width: "64px", height: "64px" },
10
+ xl: { width: "124px", height: "124px" },
11
+ };
12
+ // Icon sizes (no xl)
13
+ const ICON_SIZE_CONFIG = {
14
+ sm: { size: 16 },
15
+ md: { size: 24 },
16
+ lg: { size: 40 },
17
+ };
18
+ // Text sizes (no xl)
19
+ const TEXT_SIZE_CONFIG = {
20
+ sm: { fontSize: "var(--body-small-text-size)" },
21
+ md: { fontSize: "var(--body-regular-text-size)" },
22
+ lg: { fontSize: "var(--heading-h5-text-size)" },
23
+ };
24
+ export function Avatar({ size, type, color, variant, isRound, hasStroke, theme, hue, initials = "JD", imageUrl, }) {
25
+ const [imageError, setImageError] = useState(false);
26
+ const containerSize = CONTAINER_SIZE_CONFIG[size];
27
+ const avatarStyles = useMemo(() => {
28
+ // Determine border color for stroke based on variant and color
29
+ let borderColor = "";
30
+ if (hasStroke) {
31
+ if (variant === "solid") {
32
+ // Solid variants use tonal border colors (except warning which uses solid)
33
+ switch (color) {
34
+ case "primary":
35
+ borderColor = "var(--border-primary-tonal)";
36
+ break;
37
+ case "neutral":
38
+ borderColor = "var(--border-strong-100)";
39
+ break;
40
+ case "success":
41
+ borderColor = "var(--border-success-tonal)";
42
+ break;
43
+ case "critical":
44
+ borderColor = "var(--border-critical-tonal)";
45
+ break;
46
+ case "warning":
47
+ borderColor = "var(--border-warning)";
48
+ break;
49
+ }
50
+ }
51
+ else {
52
+ // Faded variants use solid border colors
53
+ switch (color) {
54
+ case "primary":
55
+ borderColor = "var(--border-primary)";
56
+ break;
57
+ case "neutral":
58
+ borderColor = "var(--border-strong-100)";
59
+ break;
60
+ case "success":
61
+ borderColor = "var(--border-success)";
62
+ break;
63
+ case "critical":
64
+ borderColor = "var(--border-critical)";
65
+ break;
66
+ case "warning":
67
+ borderColor = "var(--border-warning)";
68
+ break;
69
+ }
70
+ }
71
+ }
72
+ const baseStyles = {
73
+ width: containerSize.width,
74
+ height: containerSize.height,
75
+ display: "flex",
76
+ alignItems: "center",
77
+ justifyContent: "center",
78
+ flexShrink: 0,
79
+ overflow: "hidden",
80
+ position: "relative",
81
+ border: hasStroke ? `var(--border-width-50) solid ${borderColor}` : "none",
82
+ boxSizing: "border-box",
83
+ };
84
+ // Corner radius: default 8px, or full (50%) if isRound
85
+ if (isRound) {
86
+ baseStyles.borderRadius = "50%";
87
+ }
88
+ else {
89
+ baseStyles.borderRadius = "8px"; // Default 8px corner radius
90
+ }
91
+ // Background color based on color and variant
92
+ let backgroundColor = "";
93
+ let textColor = "var(--fg-on-action)";
94
+ if (variant === "solid") {
95
+ switch (color) {
96
+ case "primary":
97
+ backgroundColor = "var(--bg-primary)";
98
+ break;
99
+ case "neutral":
100
+ backgroundColor = "var(--color-neutral-500)";
101
+ break;
102
+ case "success":
103
+ backgroundColor = "var(--bg-success)";
104
+ break;
105
+ case "critical":
106
+ backgroundColor = "var(--bg-critical)";
107
+ break;
108
+ case "warning":
109
+ backgroundColor = "var(--bg-warning)";
110
+ break;
111
+ }
112
+ }
113
+ else {
114
+ // faded variant
115
+ switch (color) {
116
+ case "primary":
117
+ backgroundColor = "var(--bg-primary-tonal)";
118
+ textColor = "var(--fg-primary-on-tonal)";
119
+ break;
120
+ case "neutral":
121
+ backgroundColor = "var(--color-neutral-200)";
122
+ textColor = "var(--fg-neutral)";
123
+ break;
124
+ case "success":
125
+ backgroundColor = "var(--bg-success-tonal)";
126
+ textColor = "var(--fg-success-on-tonal)";
127
+ break;
128
+ case "critical":
129
+ backgroundColor = "var(--bg-critical-tonal)";
130
+ textColor = "var(--fg-critical-on-tonal)";
131
+ break;
132
+ case "warning":
133
+ backgroundColor = "var(--bg-warning-tonal)";
134
+ textColor = "var(--fg-warning-on-tonal)";
135
+ break;
136
+ }
137
+ }
138
+ baseStyles.backgroundColor = backgroundColor;
139
+ baseStyles.color = textColor;
140
+ return baseStyles;
141
+ }, [containerSize, isRound, hasStroke, color, variant]);
142
+ const handleImageError = () => {
143
+ setImageError(true);
144
+ };
145
+ const renderContent = () => {
146
+ if (type === "image" && imageUrl && !imageError) {
147
+ return (_jsx("img", { src: imageUrl, alt: "Avatar", onError: handleImageError, style: {
148
+ width: "100%",
149
+ height: "100%",
150
+ objectFit: "cover",
151
+ } }));
152
+ }
153
+ if (type === "text" || (type === "image" && imageError)) {
154
+ // Text type: no xl size
155
+ const textSize = size === "xl" ? "lg" : size;
156
+ const textConfig = TEXT_SIZE_CONFIG[textSize];
157
+ return (_jsx("span", { style: {
158
+ fontSize: textConfig.fontSize,
159
+ fontWeight: "var(--font-weight-secondary-medium)",
160
+ lineHeight: "1",
161
+ userSelect: "none",
162
+ }, children: initials }));
163
+ }
164
+ // Icon type: no xl size
165
+ const iconSize = size === "xl" ? "lg" : size;
166
+ const iconConfig = ICON_SIZE_CONFIG[iconSize];
167
+ return (_jsx("div", { style: {
168
+ display: "flex",
169
+ alignItems: "center",
170
+ justifyContent: "center",
171
+ }, children: _jsx(UserPersonIcon, { size: iconConfig.size }) }));
172
+ };
173
+ return (_jsx("div", { className: "ds-avatar-preview-container", children: _jsx("div", { className: "ds-avatar-preview-canvas", children: _jsx("div", { className: "ds-avatar-preview-avatar", style: avatarStyles, children: renderContent() }) }) }));
174
+ }
@@ -0,0 +1,21 @@
1
+ import type { Theme, HueVariant } from "../tokens/types";
2
+ type ButtonVariant = "filled" | "tonal" | "outline" | "link";
3
+ type ButtonSize = "xs" | "sm" | "md" | "lg" | "xl";
4
+ type CornerRadiusStep = 0 | 1 | 2 | 3 | 4 | 5;
5
+ type ButtonState = "default" | "hovered" | "focused" | "pressed" | "disabled" | "loading" | "success" | "critical" | "warning";
6
+ type JustifyContent = "center" | "space-between";
7
+ interface ButtonProps {
8
+ variant: ButtonVariant;
9
+ size: ButtonSize;
10
+ cornerRadius: CornerRadiusStep;
11
+ hasStartIcon: boolean;
12
+ hasEndIcon: boolean;
13
+ fillContainer: boolean;
14
+ justifyContent: JustifyContent;
15
+ state: ButtonState;
16
+ theme: Theme;
17
+ hue: HueVariant;
18
+ }
19
+ export declare function Button({ variant, size, cornerRadius, hasStartIcon, hasEndIcon, fillContainer, justifyContent, state, theme, hue, }: ButtonProps): import("react/jsx-runtime").JSX.Element;
20
+ export {};
21
+ //# sourceMappingURL=Button.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Button.d.ts","sourceRoot":"","sources":["../../src/components/Button.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAGzD,KAAK,aAAa,GAAG,QAAQ,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;AAC7D,KAAK,UAAU,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AACnD,KAAK,gBAAgB,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AAC9C,KAAK,WAAW,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,CAAC;AAC/H,KAAK,cAAc,GAAG,QAAQ,GAAG,eAAe,CAAC;AAEjD,UAAU,WAAW;IACnB,OAAO,EAAE,aAAa,CAAC;IACvB,IAAI,EAAE,UAAU,CAAC;IACjB,YAAY,EAAE,gBAAgB,CAAC;IAC/B,YAAY,EAAE,OAAO,CAAC;IACtB,UAAU,EAAE,OAAO,CAAC;IACpB,aAAa,EAAE,OAAO,CAAC;IACvB,cAAc,EAAE,cAAc,CAAC;IAC/B,KAAK,EAAE,WAAW,CAAC;IACnB,KAAK,EAAE,KAAK,CAAC;IACb,GAAG,EAAE,UAAU,CAAC;CACjB;AAkED,wBAAgB,MAAM,CAAC,EACrB,OAAO,EACP,IAAI,EACJ,YAAY,EACZ,YAAY,EACZ,UAAU,EACV,aAAa,EACb,cAAc,EACd,KAAK,EACL,KAAK,EACL,GAAG,GACJ,EAAE,WAAW,2CAmLb"}
@@ -0,0 +1,227 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { useMemo } from "react";
4
+ import { SearchIcon, ChevronDownIcon, LoaderIcon } from "../icons";
5
+ const CORNER_RADIUS_MAP = {
6
+ 0: "var(--corner-radius-none)",
7
+ 1: "var(--corner-radius-100)",
8
+ 2: "var(--corner-radius-200)",
9
+ 3: "var(--corner-radius-300)",
10
+ 4: "var(--corner-radius-400)",
11
+ 5: "var(--corner-radius-full)",
12
+ };
13
+ const SIZE_CONFIG = {
14
+ xs: {
15
+ height: "28px",
16
+ paddingX: "var(--spacing-200)",
17
+ paddingY: "var(--spacing-50)",
18
+ fontSize: "var(--body-extra-small-text-size)",
19
+ iconSize: "xs",
20
+ },
21
+ sm: {
22
+ height: "36px",
23
+ paddingX: "var(--spacing-300)",
24
+ paddingY: "var(--spacing-100)",
25
+ fontSize: "var(--body-small-text-size)",
26
+ iconSize: "xs",
27
+ },
28
+ md: {
29
+ height: "44px",
30
+ paddingX: "var(--spacing-400)",
31
+ paddingY: "var(--spacing-200)",
32
+ fontSize: "var(--body-regular-text-size)",
33
+ iconSize: "xs",
34
+ },
35
+ lg: {
36
+ height: "56px",
37
+ paddingX: "var(--spacing-500)",
38
+ paddingY: "var(--spacing-300)",
39
+ fontSize: "var(--body-medium-text-size)",
40
+ iconSize: "sm",
41
+ },
42
+ xl: {
43
+ height: "68px",
44
+ paddingX: "var(--spacing-600)",
45
+ paddingY: "var(--spacing-400)",
46
+ fontSize: "var(--body-medium-text-size)",
47
+ iconSize: "sm",
48
+ },
49
+ };
50
+ const LOADER_ICON_SIZE_MAP = {
51
+ xs: 20,
52
+ sm: 24,
53
+ md: 24,
54
+ lg: 32,
55
+ xl: 40,
56
+ };
57
+ export function Button({ variant, size, cornerRadius, hasStartIcon, hasEndIcon, fillContainer, justifyContent, state, theme, hue, }) {
58
+ const sizeConfig = SIZE_CONFIG[size];
59
+ const borderRadius = CORNER_RADIUS_MAP[cornerRadius];
60
+ const buttonStyles = useMemo(() => {
61
+ const baseStyles = {
62
+ display: "inline-flex",
63
+ alignItems: "center",
64
+ justifyContent: justifyContent,
65
+ gap: "var(--spacing-100)",
66
+ fontFamily: "var(--font-secondary)",
67
+ fontSize: sizeConfig.fontSize,
68
+ lineHeight: "1",
69
+ fontWeight: "var(--font-weight-secondary-medium)",
70
+ borderWidth: "var(--border-width-25)",
71
+ borderStyle: "solid",
72
+ borderColor: "transparent",
73
+ borderRadius,
74
+ cursor: state === "disabled" ? "not-allowed" : "pointer",
75
+ transition: "background-color 0.15s ease, border-color 0.15s ease, color 0.15s ease",
76
+ minHeight: sizeConfig.height,
77
+ paddingLeft: sizeConfig.paddingX,
78
+ paddingRight: sizeConfig.paddingX,
79
+ paddingTop: sizeConfig.paddingY,
80
+ paddingBottom: sizeConfig.paddingY,
81
+ width: fillContainer ? "100%" : "auto",
82
+ opacity: state === "disabled" ? 0.6 : 1,
83
+ };
84
+ // Get base variant styles
85
+ let variantStyles = {};
86
+ switch (variant) {
87
+ case "filled":
88
+ variantStyles = {
89
+ backgroundColor: "var(--bg-primary)",
90
+ color: "var(--fg-on-action)",
91
+ borderColor: "var(--bg-primary)",
92
+ };
93
+ break;
94
+ case "tonal":
95
+ variantStyles = {
96
+ backgroundColor: "var(--bg-primary-tonal)",
97
+ color: "var(--fg-primary-on-tonal)",
98
+ borderColor: "var(--border-primary-tonal)",
99
+ };
100
+ break;
101
+ case "outline":
102
+ variantStyles = {
103
+ backgroundColor: "transparent",
104
+ color: "var(--fg-primary)",
105
+ borderColor: "var(--border-primary)",
106
+ };
107
+ break;
108
+ case "link":
109
+ variantStyles = {
110
+ backgroundColor: "transparent",
111
+ color: "var(--fg-primary)",
112
+ borderWidth: 0,
113
+ borderStyle: "none",
114
+ borderColor: "transparent",
115
+ textDecoration: "underline",
116
+ textUnderlineOffset: "2px",
117
+ };
118
+ break;
119
+ }
120
+ // Apply state-specific overrides
121
+ const stateStyles = {};
122
+ if (state === "disabled") {
123
+ if (variant === "filled") {
124
+ stateStyles.backgroundColor = "var(--bg-disabled)";
125
+ stateStyles.color = "var(--fg-on-disabled)";
126
+ stateStyles.borderColor = "var(--border-disabled)";
127
+ }
128
+ else if (variant === "tonal") {
129
+ stateStyles.backgroundColor = "var(--bg-disabled)";
130
+ stateStyles.color = "var(--fg-disabled)";
131
+ stateStyles.borderColor = "var(--border-disabled)";
132
+ }
133
+ else if (variant === "outline") {
134
+ stateStyles.color = "var(--fg-disabled)";
135
+ stateStyles.borderColor = "var(--border-disabled)";
136
+ }
137
+ else if (variant === "link") {
138
+ stateStyles.color = "var(--fg-disabled)";
139
+ }
140
+ }
141
+ else if (state === "hovered") {
142
+ if (variant === "filled") {
143
+ stateStyles.backgroundColor = "var(--bg-primary-on-hover)";
144
+ stateStyles.borderColor = "var(--bg-primary-on-hover)";
145
+ }
146
+ else if (variant === "tonal") {
147
+ stateStyles.backgroundColor = "var(--bg-primary-tonal-on-hover)";
148
+ }
149
+ else if (variant === "outline") {
150
+ stateStyles.borderColor = "var(--border-primary-on-hover)";
151
+ }
152
+ else if (variant === "link") {
153
+ stateStyles.color = "var(--fg-primary-on-hover)";
154
+ }
155
+ }
156
+ else if (state === "pressed") {
157
+ if (variant === "filled") {
158
+ stateStyles.backgroundColor = "var(--bg-primary-pressed)";
159
+ stateStyles.borderColor = "var(--bg-primary-pressed)";
160
+ }
161
+ else if (variant === "tonal") {
162
+ stateStyles.backgroundColor = "var(--bg-primary-tonal-on-hover)";
163
+ }
164
+ }
165
+ else if (state === "focused") {
166
+ if (variant === "filled") {
167
+ stateStyles.backgroundColor = "var(--bg-primary-on-focused)";
168
+ stateStyles.borderColor = "var(--bg-primary-on-focused)";
169
+ }
170
+ else if (variant === "outline") {
171
+ stateStyles.borderColor = "var(--border-primary)";
172
+ }
173
+ stateStyles.outline = "2px solid var(--border-primary)";
174
+ stateStyles.outlineOffset = "2px";
175
+ }
176
+ else if (state === "success") {
177
+ if (variant === "filled") {
178
+ stateStyles.backgroundColor = "var(--bg-success)";
179
+ stateStyles.borderColor = "var(--border-success)";
180
+ }
181
+ else if (variant === "tonal") {
182
+ stateStyles.backgroundColor = "var(--bg-success-tonal)";
183
+ stateStyles.color = "var(--fg-success-on-tonal)";
184
+ stateStyles.borderColor = "var(--border-success-tonal)";
185
+ }
186
+ else if (variant === "outline") {
187
+ stateStyles.color = "var(--fg-success)";
188
+ stateStyles.borderColor = "var(--border-success)";
189
+ }
190
+ }
191
+ else if (state === "critical") {
192
+ if (variant === "filled") {
193
+ stateStyles.backgroundColor = "var(--bg-critical)";
194
+ stateStyles.borderColor = "var(--border-critical)";
195
+ }
196
+ else if (variant === "tonal") {
197
+ stateStyles.backgroundColor = "var(--bg-critical-tonal)";
198
+ stateStyles.color = "var(--fg-critical-on-tonal)";
199
+ stateStyles.borderColor = "var(--border-critical-tonal)";
200
+ }
201
+ else if (variant === "outline") {
202
+ stateStyles.color = "var(--fg-critical)";
203
+ stateStyles.borderColor = "var(--border-critical)";
204
+ }
205
+ }
206
+ else if (state === "warning") {
207
+ if (variant === "filled") {
208
+ stateStyles.backgroundColor = "var(--bg-warning)";
209
+ stateStyles.borderColor = "var(--border-warning)";
210
+ }
211
+ else if (variant === "tonal") {
212
+ stateStyles.backgroundColor = "var(--bg-warning-tonal)";
213
+ stateStyles.color = "var(--fg-warning-on-tonal)";
214
+ stateStyles.borderColor = "var(--border-warning-tonal)";
215
+ }
216
+ else if (variant === "outline") {
217
+ stateStyles.color = "var(--fg-warning)";
218
+ stateStyles.borderColor = "var(--border-warning)";
219
+ }
220
+ }
221
+ return { ...baseStyles, ...variantStyles, ...stateStyles };
222
+ }, [variant, sizeConfig, borderRadius, fillContainer, justifyContent, state]);
223
+ const isLoading = state === "loading";
224
+ const showStartIcon = !isLoading && hasStartIcon;
225
+ const showEndIcon = !isLoading && hasEndIcon;
226
+ return (_jsx("div", { className: "ds-button-preview-container", children: _jsx("div", { className: "ds-button-preview-canvas", children: _jsx("button", { type: "button", className: "ds-button-preview-button", style: buttonStyles, disabled: state === "disabled", children: isLoading ? (_jsx(LoaderIcon, { size: LOADER_ICON_SIZE_MAP[size], className: "ds-button-loading-spinner" })) : (_jsxs(_Fragment, { children: [_jsxs("div", { className: "ds-button-preview-start", children: [showStartIcon && _jsx(SearchIcon, { size: sizeConfig.iconSize }), _jsx("span", { children: "Button" })] }), showEndIcon && _jsx(ChevronDownIcon, { size: sizeConfig.iconSize })] })) }) }) }));
227
+ }