@puzzle-ui/ui 0.4.0 → 0.5.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.
Files changed (163) hide show
  1. package/README.md +61 -77
  2. package/dist/components/actions/button/button.css +5 -2
  3. package/dist/components/actions/button-group/button-group.css +16 -17
  4. package/dist/components/data-display/badge/badge.css +15 -2
  5. package/dist/components/data-display/badge/index.cjs +13 -2
  6. package/dist/components/data-display/badge/index.cjs.map +1 -1
  7. package/dist/components/data-display/badge/index.d.cts +7 -2
  8. package/dist/components/data-display/badge/index.d.ts +7 -2
  9. package/dist/components/data-display/badge/index.js +13 -2
  10. package/dist/components/data-display/badge/index.js.map +1 -1
  11. package/dist/components/data-display/rating/index.cjs +84 -0
  12. package/dist/components/data-display/rating/index.cjs.map +1 -0
  13. package/dist/components/data-display/rating/index.d.cts +3 -0
  14. package/dist/components/data-display/rating/index.d.ts +3 -0
  15. package/dist/components/data-display/rating/index.js +84 -0
  16. package/dist/components/data-display/rating/index.js.map +1 -0
  17. package/dist/components/data-display/rating/rating.css +72 -0
  18. package/dist/components/data-display/rating/rating.types.d.cts +15 -0
  19. package/dist/components/data-display/rating/rating.types.d.ts +15 -0
  20. package/dist/components/data-display/rating/rating.utils.cjs +27 -0
  21. package/dist/components/data-display/rating/rating.utils.cjs.map +1 -0
  22. package/dist/components/data-display/rating/rating.utils.js +25 -0
  23. package/dist/components/data-display/rating/rating.utils.js.map +1 -0
  24. package/dist/components/forms/checkbox/checkbox.css +2 -10
  25. package/dist/components/forms/input-group/input-group.d.cts +4 -2
  26. package/dist/components/forms/input-group/input-group.d.ts +4 -2
  27. package/dist/components/forms/radio-group/index.d.cts +2 -1
  28. package/dist/components/forms/radio-group/index.d.ts +2 -1
  29. package/dist/components/forms/stepper/index.d.cts +4 -2
  30. package/dist/components/forms/stepper/index.d.ts +4 -2
  31. package/dist/components/forms/time-picker/index.cjs +108 -0
  32. package/dist/components/forms/time-picker/index.cjs.map +1 -0
  33. package/dist/components/forms/time-picker/index.d.cts +3 -0
  34. package/dist/components/forms/time-picker/index.d.ts +3 -0
  35. package/dist/components/forms/time-picker/index.js +108 -0
  36. package/dist/components/forms/time-picker/index.js.map +1 -0
  37. package/dist/components/forms/time-picker/time-picker.css +166 -0
  38. package/dist/components/forms/time-picker/time-picker.types.d.cts +34 -0
  39. package/dist/components/forms/time-picker/time-picker.types.d.ts +34 -0
  40. package/dist/components/forms/time-picker/time-picker.utils.cjs +25 -0
  41. package/dist/components/forms/time-picker/time-picker.utils.cjs.map +1 -0
  42. package/dist/components/forms/time-picker/time-picker.utils.js +24 -0
  43. package/dist/components/forms/time-picker/time-picker.utils.js.map +1 -0
  44. package/dist/components/forms/token-input/index.cjs +189 -0
  45. package/dist/components/forms/token-input/index.cjs.map +1 -0
  46. package/dist/components/forms/token-input/index.d.cts +3 -0
  47. package/dist/components/forms/token-input/index.d.ts +3 -0
  48. package/dist/components/forms/token-input/index.js +189 -0
  49. package/dist/components/forms/token-input/index.js.map +1 -0
  50. package/dist/components/forms/token-input/token-input.css +145 -0
  51. package/dist/components/forms/token-input/token-input.types.d.cts +48 -0
  52. package/dist/components/forms/token-input/token-input.types.d.ts +48 -0
  53. package/dist/components/forms/token-input/token-input.utils.cjs +30 -0
  54. package/dist/components/forms/token-input/token-input.utils.cjs.map +1 -0
  55. package/dist/components/forms/token-input/token-input.utils.js +24 -0
  56. package/dist/components/forms/token-input/token-input.utils.js.map +1 -0
  57. package/dist/components/layout/aspect-ratio/aspect-ratio.css +14 -0
  58. package/dist/components/layout/aspect-ratio/aspect-ratio.utils.cjs +11 -0
  59. package/dist/components/layout/aspect-ratio/aspect-ratio.utils.cjs.map +1 -0
  60. package/dist/components/layout/aspect-ratio/aspect-ratio.utils.js +11 -0
  61. package/dist/components/layout/aspect-ratio/aspect-ratio.utils.js.map +1 -0
  62. package/dist/components/layout/aspect-ratio/index.cjs +16 -0
  63. package/dist/components/layout/aspect-ratio/index.cjs.map +1 -0
  64. package/dist/components/layout/aspect-ratio/index.d.cts +11 -0
  65. package/dist/components/layout/aspect-ratio/index.d.ts +11 -0
  66. package/dist/components/layout/aspect-ratio/index.js +16 -0
  67. package/dist/components/layout/aspect-ratio/index.js.map +1 -0
  68. package/dist/components/layout/container/container.css +1 -0
  69. package/dist/components/layout/page-layout/page-layout.css +1 -0
  70. package/dist/components/layout/scroll-area/index.cjs +27 -0
  71. package/dist/components/layout/scroll-area/index.cjs.map +1 -0
  72. package/dist/components/layout/scroll-area/index.d.cts +15 -0
  73. package/dist/components/layout/scroll-area/index.d.ts +15 -0
  74. package/dist/components/layout/scroll-area/index.js +27 -0
  75. package/dist/components/layout/scroll-area/index.js.map +1 -0
  76. package/dist/components/layout/scroll-area/scroll-area.css +25 -0
  77. package/dist/components/layout/scroll-area/scroll-area.utils.cjs +8 -0
  78. package/dist/components/layout/scroll-area/scroll-area.utils.cjs.map +1 -0
  79. package/dist/components/layout/scroll-area/scroll-area.utils.d.cts +2 -0
  80. package/dist/components/layout/scroll-area/scroll-area.utils.d.ts +2 -0
  81. package/dist/components/layout/scroll-area/scroll-area.utils.js +8 -0
  82. package/dist/components/layout/scroll-area/scroll-area.utils.js.map +1 -0
  83. package/dist/components/layout/section/index.cjs +10 -4
  84. package/dist/components/layout/section/index.cjs.map +1 -1
  85. package/dist/components/layout/section/index.d.cts +1 -1
  86. package/dist/components/layout/section/index.d.ts +1 -1
  87. package/dist/components/layout/section/index.js +11 -5
  88. package/dist/components/layout/section/index.js.map +1 -1
  89. package/dist/components/layout/section/section.css +9 -3
  90. package/dist/components/layout/section/types.d.cts +5 -5
  91. package/dist/components/layout/section/types.d.ts +5 -5
  92. package/dist/components/layout/separator/index.cjs +18 -0
  93. package/dist/components/layout/separator/index.cjs.map +1 -0
  94. package/dist/components/layout/separator/index.d.cts +17 -0
  95. package/dist/components/layout/separator/index.d.ts +17 -0
  96. package/dist/components/layout/separator/index.js +18 -0
  97. package/dist/components/layout/separator/index.js.map +1 -0
  98. package/dist/components/layout/separator/separator.css +15 -0
  99. package/dist/components/layout/separator/separator.utils.cjs +11 -0
  100. package/dist/components/layout/separator/separator.utils.cjs.map +1 -0
  101. package/dist/components/layout/separator/separator.utils.d.cts +2 -0
  102. package/dist/components/layout/separator/separator.utils.d.ts +2 -0
  103. package/dist/components/layout/separator/separator.utils.js +11 -0
  104. package/dist/components/layout/separator/separator.utils.js.map +1 -0
  105. package/dist/components/layout/stack/index.cjs +18 -2
  106. package/dist/components/layout/stack/index.cjs.map +1 -1
  107. package/dist/components/layout/stack/index.d.cts +8 -6
  108. package/dist/components/layout/stack/index.d.ts +8 -6
  109. package/dist/components/layout/stack/index.js +18 -2
  110. package/dist/components/layout/stack/index.js.map +1 -1
  111. package/dist/components/layout/stack/stack.css +118 -138
  112. package/dist/components/navigation/carousel/index.cjs +4 -1
  113. package/dist/components/navigation/carousel/index.cjs.map +1 -1
  114. package/dist/components/navigation/carousel/index.js +4 -1
  115. package/dist/components/navigation/carousel/index.js.map +1 -1
  116. package/dist/components/navigation/tabs/index.d.cts +3 -2
  117. package/dist/components/navigation/tabs/index.d.ts +3 -2
  118. package/dist/components/navigation/tree-view/index.cjs +1 -1
  119. package/dist/components/navigation/tree-view/index.cjs.map +1 -1
  120. package/dist/components/navigation/tree-view/index.d.cts +5 -1
  121. package/dist/components/navigation/tree-view/index.d.ts +5 -1
  122. package/dist/components/navigation/tree-view/index.js +1 -1
  123. package/dist/components/navigation/tree-view/index.js.map +1 -1
  124. package/dist/components/navigation/tree-view/tree-view.css +5 -3
  125. package/dist/components/overlays/context-menu/context-menu.css +151 -0
  126. package/dist/components/overlays/context-menu/context-menu.types.d.cts +69 -0
  127. package/dist/components/overlays/context-menu/context-menu.types.d.ts +69 -0
  128. package/dist/components/overlays/context-menu/context-menu.utils.cjs +46 -0
  129. package/dist/components/overlays/context-menu/context-menu.utils.cjs.map +1 -0
  130. package/dist/components/overlays/context-menu/context-menu.utils.js +40 -0
  131. package/dist/components/overlays/context-menu/context-menu.utils.js.map +1 -0
  132. package/dist/components/overlays/context-menu/index.cjs +830 -0
  133. package/dist/components/overlays/context-menu/index.cjs.map +1 -0
  134. package/dist/components/overlays/context-menu/index.d.cts +42 -0
  135. package/dist/components/overlays/context-menu/index.d.ts +42 -0
  136. package/dist/components/overlays/context-menu/index.js +818 -0
  137. package/dist/components/overlays/context-menu/index.js.map +1 -0
  138. package/dist/components/typography/heading.cjs +6 -2
  139. package/dist/components/typography/heading.cjs.map +1 -1
  140. package/dist/components/typography/heading.d.cts +3 -2
  141. package/dist/components/typography/heading.d.ts +3 -2
  142. package/dist/components/typography/heading.js +6 -2
  143. package/dist/components/typography/heading.js.map +1 -1
  144. package/dist/components/typography/text.cjs +6 -2
  145. package/dist/components/typography/text.cjs.map +1 -1
  146. package/dist/components/typography/text.d.cts +3 -2
  147. package/dist/components/typography/text.d.ts +3 -2
  148. package/dist/components/typography/text.js +6 -2
  149. package/dist/components/typography/text.js.map +1 -1
  150. package/dist/components/utilities/preview/index.cjs +88 -20
  151. package/dist/components/utilities/preview/index.cjs.map +1 -1
  152. package/dist/components/utilities/preview/index.d.cts +6 -2
  153. package/dist/components/utilities/preview/index.d.ts +6 -2
  154. package/dist/components/utilities/preview/index.js +88 -20
  155. package/dist/components/utilities/preview/index.js.map +1 -1
  156. package/dist/components/utilities/preview/preview.css +26 -11
  157. package/dist/components/utilities/transition/index.d.cts +1 -1
  158. package/dist/components/utilities/transition/index.d.ts +1 -1
  159. package/dist/index.cjs +123 -97
  160. package/dist/index.d.cts +15 -2
  161. package/dist/index.d.ts +15 -2
  162. package/dist/index.js +8 -1
  163. package/package.json +11 -6
package/README.md CHANGED
@@ -1,99 +1,83 @@
1
- ---
2
- title: Getting Started
3
- description: Install Puzzle in your React app, build your first interface, and customize the theme.
4
- ---
1
+ # @puzzle-ui/ui
5
2
 
6
- > \[!WARNING\] **Puzzle is not ready for production use.**
7
- >
8
- > This project is under active development. APIs, component props,
9
- > package exports, styling, and internal implementation may change
10
- > without notice. Components may be incomplete, and testing,
11
- > accessibility, documentation, and framework compatibility are not yet
12
- > guaranteed. Use it for experimentation and evaluation only---not for
13
- > production applications.
3
+ Accessible, token-driven React components for building Puzzle interfaces.
14
4
 
15
- Puzzle is a TypeScript and React UI component library built around reusable components and shared design tokens. Its components are composable and router-independent, with styling supplied by `"@puzzle-ui/theme`. The framework is setup to be compatible with `Next.js`, `Tanstack Start` and `react-router`.
5
+ **Documentation:** [https://puzzle-ui.com/docs/ui](https://puzzle-ui.com/docs/ui)
16
6
 
17
- **Puzzle is under active development and is not ready for production use.** APIs, props, exports, and styling may change without notice. Accessibility and framework compatibility are still being tested. Use the project for experimentation and evaluation.
7
+ > [!WARNING]
8
+ > **Puzzle is not ready for production use.** APIs, component props, package
9
+ > exports, and styling may change without notice. Accessibility and framework
10
+ > compatibility are still being tested. Use it for experimentation and
11
+ > evaluation.
18
12
 
19
- ## Prerequisites
13
+ `@puzzle-ui/ui` provides composable, router-independent React components:
14
+ actions, data display, editors, forms, layout, navigation, overlays,
15
+ typography, and utilities. Components use semantic HTML and native browser
16
+ behavior first, and all styling comes from the CSS custom properties supplied
17
+ by [`@puzzle-ui/theme`](https://puzzle-ui.com/docs/theme), so one `Theme`
18
+ provider styles your whole application.
20
19
 
21
- - A package manager: npm, Bun, pnpm, or Yarn.
22
- - An existing React application with a bundler that supports CSS imports, such as Vite.
23
- - Familiarity with React and TypeScript. The UI and theme packages require React and React DOM 19 or later within the supported React 19 range.
24
-
25
- ## Set up a new project
26
-
27
- Simple run our @puzzle-ui/create project template to get started.
28
-
29
- npm:
30
-
31
- ```bash
32
- npx @puzzle-ui/create
33
- ```
34
-
35
- bun:
20
+ ## Install
36
21
 
37
22
  ```bash
38
- bunx @puzzle-ui/create
23
+ npm install @puzzle-ui/ui @puzzle-ui/theme
24
+ # or
25
+ bun add @puzzle-ui/ui @puzzle-ui/theme
26
+ # or
27
+ pnpm add @puzzle-ui/ui @puzzle-ui/theme
28
+ # or
29
+ yarn add @puzzle-ui/ui @puzzle-ui/theme
39
30
  ```
40
31
 
41
- Puzzle can set up a new project for you with the following framework options:
42
-
43
- - Basic React app
44
- - Tanstack start
45
- - Next.js
32
+ React and React DOM 19 are peer dependencies and should already be installed
33
+ in your application.
46
34
 
47
- Furthermore you can choose to install tailwindcss or not.
48
- It directly installs all dependencies and sets up the project for you.
35
+ To start a new project with Puzzle preconfigured, run
36
+ [`npx @puzzle-ui/create`](https://puzzle-ui.com/docs/create).
49
37
 
50
- ## Set up in an existing project:
38
+ ## Usage
51
39
 
52
- Install the UI library and theme package in your application's directory. Choose your package manager below:
53
-
54
- npm:
55
-
56
- ```bash
57
- npm install @puzzle-ui/ui --save
58
- ```
59
-
60
- bun:
61
-
62
- ```bash
63
- bun add @puzzle-ui/create
64
- ```
40
+ Wrap your application in `Theme` and import components from `@puzzle-ui/ui`:
65
41
 
66
- pnpm:
42
+ ```tsx
43
+ import { Theme } from "@puzzle-ui/theme";
44
+ import { Button, Stack, Text } from "@puzzle-ui/ui";
67
45
 
68
- ```bash
69
- pnpm add @puzzle-ui/ui
46
+ export const App = () => (
47
+ <Theme>
48
+ <Stack direction="col" gap="sm">
49
+ <Text>Build your interface with Puzzle.</Text>
50
+ <Button>Get started</Button>
51
+ </Stack>
52
+ </Theme>
53
+ );
70
54
  ```
71
55
 
72
- yarn:
56
+ ## Component categories
73
57
 
74
- ```bash
75
- yarn add @puzzle-ui/ui
76
- ```
58
+ | Category | Components |
59
+ | ------------ | ------------------------------------------------------------------------------------------------------------- |
60
+ | Actions | Button, ButtonGroup, ToggleGroup |
61
+ | Data display | Alert, Avatar, Badge, Card, EmptyState, Image, Paper, Progress, Rating, Skeleton |
62
+ | Editors | TextEditor, TextEditorToolbar, TextRenderer |
63
+ | Forms | Autocomplete, Checkbox, ColorPicker, DatePicker, FileUpload, FormField, inputs, Radio, Select, Slider, Switch |
64
+ | Layout | AppBar, AspectRatio, Container, PageLayout, ResizablePanels, ScrollArea, Section, Separator, Sidebar, Stack |
65
+ | Navigation | Accordion, Breadcrumbs, Carousel, Link, Pagination, Tabs, TreeView |
66
+ | Overlays | Command, ContextMenu, Dialog, Drawer, DropdownMenu, Notification, Popover, Tooltip |
67
+ | Typography | Heading, Text |
68
+ | Utilities | Portal, Preview, ScrollLock, Transition |
77
69
 
78
- These commands install published packages; they do not clone the repository or build its workspaces. Puzzle does not require Bun in your application. React and React DOM are peer dependencies and should already be installed in your app.
70
+ Browse every component with live examples, props, and accessibility guidance
71
+ in the [component documentation](https://puzzle-ui.com/docs/ui).
79
72
 
80
- ## Your first interface
73
+ ## Framework integration
81
74
 
82
- Import components from `"@puzzle-ui/ui` and wrap your application in `Theme` from `"@puzzle-ui/theme`:
75
+ Components do not depend on a router or framework. Use the `components`
76
+ prop of `Theme` to provide your framework's link and image components; see
77
+ [Component Overrides](https://puzzle-ui.com/docs/theme/components).
83
78
 
84
- ```tsx
85
- import { Theme } from "@puzzle-ui/theme";
86
- import { Button } from "@puzzle-ui/ui";
79
+ ## Related packages
87
80
 
88
- export const App = () => (
89
- <Theme>
90
- <main>
91
- <h1>Hello, Puzzle</h1>
92
- <p>Build your interface with reusable components and shared tokens.</p>
93
- <Button onClick={() => window.alert("Welcome to Puzzle!")}>
94
- Get started
95
- </Button>
96
- </main>
97
- </Theme>
98
- );
99
- ```
81
+ - [`@puzzle-ui/theme`](https://puzzle-ui.com/docs/theme) — design tokens and the `Theme` provider.
82
+ - [`@puzzle-ui/icons`](https://puzzle-ui.com/docs/icons) — icon components.
83
+ - [`@puzzle-ui/hooks`](https://puzzle-ui.com/docs/hooks) — reusable React hooks.
@@ -44,7 +44,7 @@
44
44
  }
45
45
 
46
46
  .puzzle-button--primary:hover:not(:disabled):not([aria-disabled="true"]) {
47
- filter: brightness(.93);
47
+ background: color-mix(in srgb, var(--puzzle-color-primary) 88%, var(--puzzle-color-text));
48
48
  }
49
49
 
50
50
  .puzzle-button--secondary {
@@ -53,7 +53,7 @@
53
53
  }
54
54
 
55
55
  .puzzle-button--secondary:hover:not(:disabled):not([aria-disabled="true"]) {
56
- filter: brightness(.96);
56
+ background: color-mix(in srgb, var(--puzzle-color-surface-sunken) 85%, var(--puzzle-color-text));
57
57
  }
58
58
 
59
59
  .puzzle-button--outline {
@@ -160,7 +160,10 @@
160
160
  }
161
161
 
162
162
  .puzzle-button__content {
163
+ gap: var(--puzzle-button-gap, var(--puzzle-space-2, .5rem));
164
+ flex-direction: row;
163
165
  flex: 0 auto;
166
+ display: inline-flex;
164
167
  }
165
168
 
166
169
  .puzzle-button__leading, .puzzle-button__trailing, .puzzle-button__spinner {
@@ -24,41 +24,40 @@
24
24
  }
25
25
 
26
26
  .puzzle-button-group--attached {
27
+ --puzzle-button-group-divider-color: var(--puzzle-color-border);
28
+ isolation: isolate;
27
29
  gap: 0;
28
30
  }
29
31
 
30
- .puzzle-button-group--attached > :not(:first-child) {
31
- margin-inline-start: -1px;
32
- }
33
-
34
- .puzzle-button-group--horizontal .puzzle-button-group--attached {
35
- border-radius: inherit;
32
+ .puzzle-button-group--attached > :is(:hover, :focus-visible) {
33
+ z-index: 1;
34
+ position: relative;
36
35
  }
37
36
 
38
- .puzzle-button-group--attached > :not(:first-child):not(:last-child) {
39
- border-radius: 0;
37
+ .puzzle-button-group--horizontal.puzzle-button-group--attached > :not(:first-child) {
38
+ border-inline-start: 1px solid var(--puzzle-button-group-divider-color);
39
+ border-start-start-radius: 0;
40
+ border-end-start-radius: 0;
41
+ margin-inline-start: -1px;
40
42
  }
41
43
 
42
- .puzzle-button-group--horizontal .puzzle-button-group--attached > :first-child {
44
+ .puzzle-button-group--horizontal.puzzle-button-group--attached > :not(:last-child) {
43
45
  border-start-end-radius: 0;
44
46
  border-end-end-radius: 0;
45
47
  }
46
48
 
47
- .puzzle-button-group--horizontal .puzzle-button-group--attached > :last-child {
49
+ .puzzle-button-group--vertical.puzzle-button-group--attached > :not(:first-child) {
50
+ border-block-start: 1px solid var(--puzzle-button-group-divider-color);
48
51
  border-start-start-radius: 0;
49
- border-end-start-radius: 0;
52
+ border-start-end-radius: 0;
53
+ margin-block-start: -1px;
50
54
  }
51
55
 
52
- .puzzle-button-group--vertical .puzzle-button-group--attached > :first-child {
56
+ .puzzle-button-group--vertical.puzzle-button-group--attached > :not(:last-child) {
53
57
  border-end-end-radius: 0;
54
58
  border-end-start-radius: 0;
55
59
  }
56
60
 
57
- .puzzle-button-group--vertical .puzzle-button-group--attached > :last-child {
58
- border-start-start-radius: 0;
59
- border-start-end-radius: 0;
60
- }
61
-
62
61
  .puzzle-button-group--disabled {
63
62
  cursor: not-allowed;
64
63
  }
@@ -1,22 +1,30 @@
1
1
  .puzzle-badge {
2
+ justify-content: center;
3
+ align-items: center;
2
4
  display: inline-flex;
3
5
  position: relative;
4
6
  }
5
7
 
6
8
  .puzzle-badge > .puzzle-badge-indicator {
7
- aspect-ratio: 1;
9
+ box-sizing: border-box;
10
+ padding: var(--puzzle-space-1) var(--puzzle-space-2);
8
11
  border-radius: var(--puzzle-radius-full, 50%);
9
12
  font-weight: var(--puzzle-font-weight-normal, 400);
10
13
  line-height: var(--puzzle-font-line-height-none, 1);
11
14
  text-align: center;
15
+ white-space: nowrap;
12
16
  vertical-align: middle;
13
17
  justify-content: center;
14
18
  align-items: center;
15
- padding: .25em;
16
19
  display: inline-flex;
17
20
  position: absolute;
18
21
  }
19
22
 
23
+ .puzzle-badge > .puzzle-badge-indicator--single {
24
+ padding-inline: var(--puzzle-space-1);
25
+ aspect-ratio: 1;
26
+ }
27
+
20
28
  .puzzle-badge-primary > .puzzle-badge-indicator {
21
29
  background-color: var(--puzzle-color-primary);
22
30
  color: var(--puzzle-color-primary-foreground);
@@ -89,3 +97,8 @@
89
97
  left: 85.3553%;
90
98
  transform: translate(-50%, -50%);
91
99
  }
100
+
101
+ .puzzle-badge.puzzle-badge-standalone > .puzzle-badge-indicator {
102
+ position: static;
103
+ transform: none;
104
+ }
@@ -6,19 +6,30 @@ let react_jsx_runtime = require("react/jsx-runtime");
6
6
  const Badge = ({ content, size = 20, color = "primary", anchorOrigin = {
7
7
  vertical: "top",
8
8
  horizontal: "right"
9
+ }, anchorOffset = {
10
+ vertical: 0,
11
+ horizontal: 0
9
12
  }, anchorOverlap = "rectangular", className, children }) => {
13
+ if (!Number.isFinite(anchorOffset.vertical) || !Number.isFinite(anchorOffset.horizontal)) throw new TypeError("Badge anchorOffset values must be finite numbers.");
10
14
  const resolvedSize = Math.max(size, 16);
11
15
  const isHex = (0, _puzzle_ui_core.isHexColor)(color);
12
16
  const isDark = isHex ? (0, _puzzle_ui_core.isDarkColor)(color) : false;
13
- const classes = (0, _puzzle_ui_core.cn)("puzzle-badge", anchorOrigin.vertical, anchorOrigin.horizontal, anchorOverlap, !isHex && `puzzle-badge-${color}`, className);
17
+ const textContent = typeof content === "string" || typeof content === "number" ? String(content).trim() : null;
18
+ const isSingleCharacter = textContent != null && Array.from(textContent).length === 1;
19
+ const classes = (0, _puzzle_ui_core.cn)("puzzle-badge", children != null && [
20
+ anchorOrigin.vertical,
21
+ anchorOrigin.horizontal,
22
+ anchorOverlap
23
+ ], children == null && "puzzle-badge-standalone", !isHex && `puzzle-badge-${color}`, className);
14
24
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
15
25
  className: classes,
16
26
  children: [children, /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
17
- className: "puzzle-badge-indicator",
27
+ className: (0, _puzzle_ui_core.cn)("puzzle-badge-indicator", isSingleCharacter && "puzzle-badge-indicator--single"),
18
28
  style: {
19
29
  fontSize: `clamp(0.7rem, calc(${resolvedSize}px - 1em), 4rem)`,
20
30
  minHeight: `${resolvedSize}px`,
21
31
  minWidth: `${resolvedSize}px`,
32
+ translate: children != null ? `${anchorOffset.horizontal * (anchorOrigin.horizontal === "left" ? 1 : -1)}px ${anchorOffset.vertical * (anchorOrigin.vertical === "top" ? 1 : -1)}px` : void 0,
22
33
  backgroundColor: isHex ? color : void 0,
23
34
  color: isHex ? isDark ? "var(--puzzle-color-light-foreground)" : "var(--puzzle-color-dark-foreground)" : void 0
24
35
  },
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["isHexColor","isDarkColor","cn"],"sources":["../../../../src/components/data-display/badge/index.tsx"],"sourcesContent":["import { cn, isDarkColor, isHexColor } from \"@puzzle-ui/core\";\nimport { type ReactNode } from \"react\";\nimport \"./badge.css\";\n\ntype AnchorOrigin = {\n vertical: \"top\" | \"bottom\";\n horizontal: \"left\" | \"right\";\n};\n\ntype AnchorOverlap = \"rectangular\" | \"circular\";\ntype BadgeColor =\n | \"primary\"\n | \"secondary\"\n | \"accent\"\n | \"danger\"\n | \"info\"\n | `#${string}`;\n\ntype BadgeProps = {\n content: ReactNode;\n size?: number;\n color?: BadgeColor;\n anchorOrigin?: AnchorOrigin;\n anchorOverlap?: AnchorOverlap;\n className?: string;\n children?: ReactNode;\n};\n\nconst Badge = ({\n content,\n size = 20,\n color = \"primary\",\n anchorOrigin = { vertical: \"top\", horizontal: \"right\" },\n anchorOverlap = \"rectangular\",\n className,\n children,\n}: BadgeProps) => {\n const resolvedSize = Math.max(size, 16);\n const isHex = isHexColor(color);\n const isDark = isHex ? isDarkColor(color) : false;\n\n const classes = cn(\n \"puzzle-badge\",\n anchorOrigin.vertical,\n anchorOrigin.horizontal,\n anchorOverlap,\n !isHex && `puzzle-badge-${color}`,\n className,\n );\n\n return (\n <div className={classes}>\n {children}\n <span\n className=\"puzzle-badge-indicator\"\n style={{\n fontSize: `clamp(0.7rem, calc(${resolvedSize}px - 1em), 4rem)`,\n minHeight: `${resolvedSize}px`,\n minWidth: `${resolvedSize}px`,\n backgroundColor: isHex ? color : undefined,\n color: isHex\n ? isDark\n ? \"var(--puzzle-color-light-foreground)\"\n : \"var(--puzzle-color-dark-foreground)\"\n : undefined,\n }}\n >\n {content}\n </span>\n </div>\n );\n};\n\nexport {\n Badge,\n type BadgeProps,\n type BadgeColor,\n type AnchorOrigin,\n type AnchorOverlap,\n};\n"],"mappings":";;;;;AA4BA,MAAM,SAAS,EACb,SACA,OAAO,IACP,QAAQ,WACR,eAAe;CAAE,UAAU;CAAO,YAAY;AAAQ,GACtD,gBAAgB,eAChB,WACA,eACgB;CAChB,MAAM,eAAe,KAAK,IAAI,MAAM,EAAE;CACtC,MAAM,SAAA,GAAQA,gBAAAA,WAAAA,CAAW,KAAK;CAC9B,MAAM,SAAS,SAAA,GAAQC,gBAAAA,YAAAA,CAAY,KAAK,IAAI;CAE5C,MAAM,WAAA,GAAUC,gBAAAA,GAAAA,CACd,gBACA,aAAa,UACb,aAAa,YACb,eACA,CAAC,SAAS,gBAAgB,SAC1B,SACF;CAEA,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;EAAK,WAAW;EAAhB,UAAA,CACG,UACD,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;GACE,WAAU;GACV,OAAO;IACL,UAAU,sBAAsB,aAAa;IAC7C,WAAW,GAAG,aAAa;IAC3B,UAAU,GAAG,aAAa;IAC1B,iBAAiB,QAAQ,QAAQ,KAAA;IACjC,OAAO,QACH,SACE,yCACA,wCACF,KAAA;GACN;GAEC,UAAA;EACG,CAAA,CACH;;AAET"}
1
+ {"version":3,"file":"index.cjs","names":["isHexColor","isDarkColor","cn"],"sources":["../../../../src/components/data-display/badge/index.tsx"],"sourcesContent":["import { cn, isDarkColor, isHexColor } from \"@puzzle-ui/core\";\nimport { type ReactNode } from \"react\";\nimport \"./badge.css\";\n\ntype AnchorOrigin = {\n vertical: \"top\" | \"bottom\";\n horizontal: \"left\" | \"right\";\n};\n\ntype AnchorOffset = {\n vertical: number;\n horizontal: number;\n};\n\ntype AnchorOverlap = \"rectangular\" | \"circular\";\ntype BadgeColor =\n | \"primary\"\n | \"secondary\"\n | \"accent\"\n | \"danger\"\n | \"info\"\n | `#${string}`;\n\ntype BadgeProps = {\n content: ReactNode;\n size?: number;\n color?: BadgeColor;\n anchorOrigin?: AnchorOrigin;\n anchorOffset?: AnchorOffset;\n anchorOverlap?: AnchorOverlap;\n className?: string;\n children?: ReactNode;\n};\n\nconst Badge = ({\n content,\n size = 20,\n color = \"primary\",\n anchorOrigin = { vertical: \"top\", horizontal: \"right\" },\n anchorOffset = { vertical: 0, horizontal: 0 },\n anchorOverlap = \"rectangular\",\n className,\n children,\n}: BadgeProps) => {\n if (!Number.isFinite(anchorOffset.vertical) || !Number.isFinite(anchorOffset.horizontal)) {\n throw new TypeError(\"Badge anchorOffset values must be finite numbers.\");\n }\n\n const resolvedSize = Math.max(size, 16);\n const isHex = isHexColor(color);\n const isDark = isHex ? isDarkColor(color) : false;\n\n const textContent =\n typeof content === \"string\" || typeof content === \"number\"\n ? String(content).trim()\n : null;\n const isSingleCharacter =\n textContent != null && Array.from(textContent).length === 1;\n\n const classes = cn(\n \"puzzle-badge\",\n children != null && [\n anchorOrigin.vertical,\n anchorOrigin.horizontal,\n anchorOverlap,\n ],\n children == null && \"puzzle-badge-standalone\",\n !isHex && `puzzle-badge-${color}`,\n className,\n );\n\n return (\n <div className={classes}>\n {children}\n <span\n className={cn(\n \"puzzle-badge-indicator\",\n isSingleCharacter && \"puzzle-badge-indicator--single\",\n )}\n style={{\n fontSize: `clamp(0.7rem, calc(${resolvedSize}px - 1em), 4rem)`,\n minHeight: `${resolvedSize}px`,\n minWidth: `${resolvedSize}px`,\n translate: children != null\n ? `${anchorOffset.horizontal * (anchorOrigin.horizontal === \"left\" ? 1 : -1)}px ${anchorOffset.vertical * (anchorOrigin.vertical === \"top\" ? 1 : -1)}px`\n : undefined,\n backgroundColor: isHex ? color : undefined,\n color: isHex\n ? isDark\n ? \"var(--puzzle-color-light-foreground)\"\n : \"var(--puzzle-color-dark-foreground)\"\n : undefined,\n }}\n >\n {content}\n </span>\n </div>\n );\n};\n\nexport {\n Badge,\n type BadgeProps,\n type BadgeColor,\n type AnchorOrigin,\n type AnchorOffset,\n type AnchorOverlap,\n};\n"],"mappings":";;;;;AAkCA,MAAM,SAAS,EACb,SACA,OAAO,IACP,QAAQ,WACR,eAAe;CAAE,UAAU;CAAO,YAAY;AAAQ,GACtD,eAAe;CAAE,UAAU;CAAG,YAAY;AAAE,GAC5C,gBAAgB,eAChB,WACA,eACgB;CAChB,IAAI,CAAC,OAAO,SAAS,aAAa,QAAQ,KAAK,CAAC,OAAO,SAAS,aAAa,UAAU,GACrF,MAAM,IAAI,UAAU,mDAAmD;CAGzE,MAAM,eAAe,KAAK,IAAI,MAAM,EAAE;CACtC,MAAM,SAAA,GAAQA,gBAAAA,WAAAA,CAAW,KAAK;CAC9B,MAAM,SAAS,SAAA,GAAQC,gBAAAA,YAAAA,CAAY,KAAK,IAAI;CAE5C,MAAM,cACJ,OAAO,YAAY,YAAY,OAAO,YAAY,WAC9C,OAAO,OAAO,CAAC,CAAC,KAAK,IACrB;CACN,MAAM,oBACJ,eAAe,QAAQ,MAAM,KAAK,WAAW,CAAC,CAAC,WAAW;CAE5D,MAAM,WAAA,GAAUC,gBAAAA,GAAAA,CACd,gBACA,YAAY,QAAQ;EAClB,aAAa;EACb,aAAa;EACb;CACF,GACA,YAAY,QAAQ,2BACpB,CAAC,SAAS,gBAAgB,SAC1B,SACF;CAEA,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;EAAK,WAAW;EAAhB,UAAA,CACG,UACD,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;GACE,YAAA,GAAWA,gBAAAA,GAAAA,CACT,0BACA,qBAAqB,gCACvB;GACA,OAAO;IACL,UAAU,sBAAsB,aAAa;IAC7C,WAAW,GAAG,aAAa;IAC3B,UAAU,GAAG,aAAa;IAC1B,WAAW,YAAY,OACnB,GAAG,aAAa,cAAc,aAAa,eAAe,SAAS,IAAI,IAAI,KAAK,aAAa,YAAY,aAAa,aAAa,QAAQ,IAAI,IAAI,MACnJ,KAAA;IACJ,iBAAiB,QAAQ,QAAQ,KAAA;IACjC,OAAO,QACH,SACE,yCACA,wCACF,KAAA;GACN;GAEC,UAAA;EACG,CAAA,CACH;;AAET"}
@@ -3,6 +3,10 @@ type AnchorOrigin = {
3
3
  vertical: "top" | "bottom";
4
4
  horizontal: "left" | "right";
5
5
  };
6
+ type AnchorOffset = {
7
+ vertical: number;
8
+ horizontal: number;
9
+ };
6
10
  type AnchorOverlap = "rectangular" | "circular";
7
11
  type BadgeColor = "primary" | "secondary" | "accent" | "danger" | "info" | `#${string}`;
8
12
  type BadgeProps = {
@@ -10,9 +14,10 @@ type BadgeProps = {
10
14
  size?: number;
11
15
  color?: BadgeColor;
12
16
  anchorOrigin?: AnchorOrigin;
17
+ anchorOffset?: AnchorOffset;
13
18
  anchorOverlap?: AnchorOverlap;
14
19
  className?: string;
15
20
  children?: ReactNode;
16
21
  };
17
- declare const Badge: ({ content, size, color, anchorOrigin, anchorOverlap, className, children }: BadgeProps) => import("react").JSX.Element;
18
- export { type AnchorOrigin, type AnchorOverlap, Badge, type BadgeColor, type BadgeProps };
22
+ declare const Badge: ({ content, size, color, anchorOrigin, anchorOffset, anchorOverlap, className, children }: BadgeProps) => import("react").JSX.Element;
23
+ export { type AnchorOffset, type AnchorOrigin, type AnchorOverlap, Badge, type BadgeColor, type BadgeProps };
@@ -3,6 +3,10 @@ type AnchorOrigin = {
3
3
  vertical: "top" | "bottom";
4
4
  horizontal: "left" | "right";
5
5
  };
6
+ type AnchorOffset = {
7
+ vertical: number;
8
+ horizontal: number;
9
+ };
6
10
  type AnchorOverlap = "rectangular" | "circular";
7
11
  type BadgeColor = "primary" | "secondary" | "accent" | "danger" | "info" | `#${string}`;
8
12
  type BadgeProps = {
@@ -10,9 +14,10 @@ type BadgeProps = {
10
14
  size?: number;
11
15
  color?: BadgeColor;
12
16
  anchorOrigin?: AnchorOrigin;
17
+ anchorOffset?: AnchorOffset;
13
18
  anchorOverlap?: AnchorOverlap;
14
19
  className?: string;
15
20
  children?: ReactNode;
16
21
  };
17
- declare const Badge: ({ content, size, color, anchorOrigin, anchorOverlap, className, children }: BadgeProps) => import("react").JSX.Element;
18
- export { type AnchorOrigin, type AnchorOverlap, Badge, type BadgeColor, type BadgeProps };
22
+ declare const Badge: ({ content, size, color, anchorOrigin, anchorOffset, anchorOverlap, className, children }: BadgeProps) => import("react").JSX.Element;
23
+ export { type AnchorOffset, type AnchorOrigin, type AnchorOverlap, Badge, type BadgeColor, type BadgeProps };
@@ -6,19 +6,30 @@ import { jsx, jsxs } from "react/jsx-runtime";
6
6
  const Badge = ({ content, size = 20, color = "primary", anchorOrigin = {
7
7
  vertical: "top",
8
8
  horizontal: "right"
9
+ }, anchorOffset = {
10
+ vertical: 0,
11
+ horizontal: 0
9
12
  }, anchorOverlap = "rectangular", className, children }) => {
13
+ if (!Number.isFinite(anchorOffset.vertical) || !Number.isFinite(anchorOffset.horizontal)) throw new TypeError("Badge anchorOffset values must be finite numbers.");
10
14
  const resolvedSize = Math.max(size, 16);
11
15
  const isHex = isHexColor(color);
12
16
  const isDark = isHex ? isDarkColor(color) : false;
13
- const classes = cn("puzzle-badge", anchorOrigin.vertical, anchorOrigin.horizontal, anchorOverlap, !isHex && `puzzle-badge-${color}`, className);
17
+ const textContent = typeof content === "string" || typeof content === "number" ? String(content).trim() : null;
18
+ const isSingleCharacter = textContent != null && Array.from(textContent).length === 1;
19
+ const classes = cn("puzzle-badge", children != null && [
20
+ anchorOrigin.vertical,
21
+ anchorOrigin.horizontal,
22
+ anchorOverlap
23
+ ], children == null && "puzzle-badge-standalone", !isHex && `puzzle-badge-${color}`, className);
14
24
  return /* @__PURE__ */ jsxs("div", {
15
25
  className: classes,
16
26
  children: [children, /* @__PURE__ */ jsx("span", {
17
- className: "puzzle-badge-indicator",
27
+ className: cn("puzzle-badge-indicator", isSingleCharacter && "puzzle-badge-indicator--single"),
18
28
  style: {
19
29
  fontSize: `clamp(0.7rem, calc(${resolvedSize}px - 1em), 4rem)`,
20
30
  minHeight: `${resolvedSize}px`,
21
31
  minWidth: `${resolvedSize}px`,
32
+ translate: children != null ? `${anchorOffset.horizontal * (anchorOrigin.horizontal === "left" ? 1 : -1)}px ${anchorOffset.vertical * (anchorOrigin.vertical === "top" ? 1 : -1)}px` : void 0,
22
33
  backgroundColor: isHex ? color : void 0,
23
34
  color: isHex ? isDark ? "var(--puzzle-color-light-foreground)" : "var(--puzzle-color-dark-foreground)" : void 0
24
35
  },
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../../src/components/data-display/badge/index.tsx"],"sourcesContent":["import { cn, isDarkColor, isHexColor } from \"@puzzle-ui/core\";\nimport { type ReactNode } from \"react\";\nimport \"./badge.css\";\n\ntype AnchorOrigin = {\n vertical: \"top\" | \"bottom\";\n horizontal: \"left\" | \"right\";\n};\n\ntype AnchorOverlap = \"rectangular\" | \"circular\";\ntype BadgeColor =\n | \"primary\"\n | \"secondary\"\n | \"accent\"\n | \"danger\"\n | \"info\"\n | `#${string}`;\n\ntype BadgeProps = {\n content: ReactNode;\n size?: number;\n color?: BadgeColor;\n anchorOrigin?: AnchorOrigin;\n anchorOverlap?: AnchorOverlap;\n className?: string;\n children?: ReactNode;\n};\n\nconst Badge = ({\n content,\n size = 20,\n color = \"primary\",\n anchorOrigin = { vertical: \"top\", horizontal: \"right\" },\n anchorOverlap = \"rectangular\",\n className,\n children,\n}: BadgeProps) => {\n const resolvedSize = Math.max(size, 16);\n const isHex = isHexColor(color);\n const isDark = isHex ? isDarkColor(color) : false;\n\n const classes = cn(\n \"puzzle-badge\",\n anchorOrigin.vertical,\n anchorOrigin.horizontal,\n anchorOverlap,\n !isHex && `puzzle-badge-${color}`,\n className,\n );\n\n return (\n <div className={classes}>\n {children}\n <span\n className=\"puzzle-badge-indicator\"\n style={{\n fontSize: `clamp(0.7rem, calc(${resolvedSize}px - 1em), 4rem)`,\n minHeight: `${resolvedSize}px`,\n minWidth: `${resolvedSize}px`,\n backgroundColor: isHex ? color : undefined,\n color: isHex\n ? isDark\n ? \"var(--puzzle-color-light-foreground)\"\n : \"var(--puzzle-color-dark-foreground)\"\n : undefined,\n }}\n >\n {content}\n </span>\n </div>\n );\n};\n\nexport {\n Badge,\n type BadgeProps,\n type BadgeColor,\n type AnchorOrigin,\n type AnchorOverlap,\n};\n"],"mappings":";;;;;AA4BA,MAAM,SAAS,EACb,SACA,OAAO,IACP,QAAQ,WACR,eAAe;CAAE,UAAU;CAAO,YAAY;AAAQ,GACtD,gBAAgB,eAChB,WACA,eACgB;CAChB,MAAM,eAAe,KAAK,IAAI,MAAM,EAAE;CACtC,MAAM,QAAQ,WAAW,KAAK;CAC9B,MAAM,SAAS,QAAQ,YAAY,KAAK,IAAI;CAE5C,MAAM,UAAU,GACd,gBACA,aAAa,UACb,aAAa,YACb,eACA,CAAC,SAAS,gBAAgB,SAC1B,SACF;CAEA,OACE,qBAAC,OAAD;EAAK,WAAW;EAAhB,UAAA,CACG,UACD,oBAAC,QAAD;GACE,WAAU;GACV,OAAO;IACL,UAAU,sBAAsB,aAAa;IAC7C,WAAW,GAAG,aAAa;IAC3B,UAAU,GAAG,aAAa;IAC1B,iBAAiB,QAAQ,QAAQ,KAAA;IACjC,OAAO,QACH,SACE,yCACA,wCACF,KAAA;GACN;GAEC,UAAA;EACG,CAAA,CACH;;AAET"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../../src/components/data-display/badge/index.tsx"],"sourcesContent":["import { cn, isDarkColor, isHexColor } from \"@puzzle-ui/core\";\nimport { type ReactNode } from \"react\";\nimport \"./badge.css\";\n\ntype AnchorOrigin = {\n vertical: \"top\" | \"bottom\";\n horizontal: \"left\" | \"right\";\n};\n\ntype AnchorOffset = {\n vertical: number;\n horizontal: number;\n};\n\ntype AnchorOverlap = \"rectangular\" | \"circular\";\ntype BadgeColor =\n | \"primary\"\n | \"secondary\"\n | \"accent\"\n | \"danger\"\n | \"info\"\n | `#${string}`;\n\ntype BadgeProps = {\n content: ReactNode;\n size?: number;\n color?: BadgeColor;\n anchorOrigin?: AnchorOrigin;\n anchorOffset?: AnchorOffset;\n anchorOverlap?: AnchorOverlap;\n className?: string;\n children?: ReactNode;\n};\n\nconst Badge = ({\n content,\n size = 20,\n color = \"primary\",\n anchorOrigin = { vertical: \"top\", horizontal: \"right\" },\n anchorOffset = { vertical: 0, horizontal: 0 },\n anchorOverlap = \"rectangular\",\n className,\n children,\n}: BadgeProps) => {\n if (!Number.isFinite(anchorOffset.vertical) || !Number.isFinite(anchorOffset.horizontal)) {\n throw new TypeError(\"Badge anchorOffset values must be finite numbers.\");\n }\n\n const resolvedSize = Math.max(size, 16);\n const isHex = isHexColor(color);\n const isDark = isHex ? isDarkColor(color) : false;\n\n const textContent =\n typeof content === \"string\" || typeof content === \"number\"\n ? String(content).trim()\n : null;\n const isSingleCharacter =\n textContent != null && Array.from(textContent).length === 1;\n\n const classes = cn(\n \"puzzle-badge\",\n children != null && [\n anchorOrigin.vertical,\n anchorOrigin.horizontal,\n anchorOverlap,\n ],\n children == null && \"puzzle-badge-standalone\",\n !isHex && `puzzle-badge-${color}`,\n className,\n );\n\n return (\n <div className={classes}>\n {children}\n <span\n className={cn(\n \"puzzle-badge-indicator\",\n isSingleCharacter && \"puzzle-badge-indicator--single\",\n )}\n style={{\n fontSize: `clamp(0.7rem, calc(${resolvedSize}px - 1em), 4rem)`,\n minHeight: `${resolvedSize}px`,\n minWidth: `${resolvedSize}px`,\n translate: children != null\n ? `${anchorOffset.horizontal * (anchorOrigin.horizontal === \"left\" ? 1 : -1)}px ${anchorOffset.vertical * (anchorOrigin.vertical === \"top\" ? 1 : -1)}px`\n : undefined,\n backgroundColor: isHex ? color : undefined,\n color: isHex\n ? isDark\n ? \"var(--puzzle-color-light-foreground)\"\n : \"var(--puzzle-color-dark-foreground)\"\n : undefined,\n }}\n >\n {content}\n </span>\n </div>\n );\n};\n\nexport {\n Badge,\n type BadgeProps,\n type BadgeColor,\n type AnchorOrigin,\n type AnchorOffset,\n type AnchorOverlap,\n};\n"],"mappings":";;;;;AAkCA,MAAM,SAAS,EACb,SACA,OAAO,IACP,QAAQ,WACR,eAAe;CAAE,UAAU;CAAO,YAAY;AAAQ,GACtD,eAAe;CAAE,UAAU;CAAG,YAAY;AAAE,GAC5C,gBAAgB,eAChB,WACA,eACgB;CAChB,IAAI,CAAC,OAAO,SAAS,aAAa,QAAQ,KAAK,CAAC,OAAO,SAAS,aAAa,UAAU,GACrF,MAAM,IAAI,UAAU,mDAAmD;CAGzE,MAAM,eAAe,KAAK,IAAI,MAAM,EAAE;CACtC,MAAM,QAAQ,WAAW,KAAK;CAC9B,MAAM,SAAS,QAAQ,YAAY,KAAK,IAAI;CAE5C,MAAM,cACJ,OAAO,YAAY,YAAY,OAAO,YAAY,WAC9C,OAAO,OAAO,CAAC,CAAC,KAAK,IACrB;CACN,MAAM,oBACJ,eAAe,QAAQ,MAAM,KAAK,WAAW,CAAC,CAAC,WAAW;CAE5D,MAAM,UAAU,GACd,gBACA,YAAY,QAAQ;EAClB,aAAa;EACb,aAAa;EACb;CACF,GACA,YAAY,QAAQ,2BACpB,CAAC,SAAS,gBAAgB,SAC1B,SACF;CAEA,OACE,qBAAC,OAAD;EAAK,WAAW;EAAhB,UAAA,CACG,UACD,oBAAC,QAAD;GACE,WAAW,GACT,0BACA,qBAAqB,gCACvB;GACA,OAAO;IACL,UAAU,sBAAsB,aAAa;IAC7C,WAAW,GAAG,aAAa;IAC3B,UAAU,GAAG,aAAa;IAC1B,WAAW,YAAY,OACnB,GAAG,aAAa,cAAc,aAAa,eAAe,SAAS,IAAI,IAAI,KAAK,aAAa,YAAY,aAAa,aAAa,QAAQ,IAAI,IAAI,MACnJ,KAAA;IACJ,iBAAiB,QAAQ,QAAQ,KAAA;IACjC,OAAO,QACH,SACE,yCACA,wCACF,KAAA;GACN;GAEC,UAAA;EACG,CAAA,CACH;;AAET"}
@@ -0,0 +1,84 @@
1
+ "use client";
2
+ const require_rating_utils = require("./rating.utils.cjs");
3
+ ;/* empty css */
4
+ let _puzzle_ui_core = require("@puzzle-ui/core");
5
+ let react = require("react");
6
+ let react_jsx_runtime = require("react/jsx-runtime");
7
+ let _puzzle_ui_icons = require("@puzzle-ui/icons");
8
+ // src/components/data-display/rating/index.tsx
9
+ const ICONS = {
10
+ full: "ph:star-fill",
11
+ half: "ph:star-half-fill",
12
+ empty: "ph:star"
13
+ };
14
+ const SIZE_MAP = {
15
+ sm: 16,
16
+ md: 20,
17
+ lg: 24
18
+ };
19
+ const Rating = ({ value, defaultValue = 0, onValueChange, max = 5, precision = .5, size = "md", readOnly = false, disabled = false, color = "var(--puzzle-color-warning, #FABC0A)", emptyColor = "var(--puzzle-color-border)", className, style, "aria-label": ariaLabel = "Rating", onKeyDown, ...props }) => {
20
+ if (!Number.isInteger(max) || max < 1 || max > 10) throw new Error("Rating max must be an integer between 1 and 10.");
21
+ const isControlled = value !== void 0;
22
+ const [internalValue, setInternalValue] = (0, react.useState)(() => require_rating_utils.normalizeRating(defaultValue, max, precision));
23
+ const currentValue = require_rating_utils.normalizeRating(isControlled ? value : internalValue, max, precision);
24
+ const commitValue = (nextValue) => {
25
+ if (disabled || readOnly) return;
26
+ const next = require_rating_utils.normalizeRating(nextValue, max, precision);
27
+ if (next === currentValue) return;
28
+ if (!isControlled) setInternalValue(next);
29
+ onValueChange?.(next);
30
+ };
31
+ const handleKeyDown = (event) => {
32
+ onKeyDown?.(event);
33
+ if (event.defaultPrevented || disabled || readOnly) return;
34
+ const nextValue = require_rating_utils.getNextRating(currentValue, event.key, max, precision);
35
+ if (nextValue === void 0) return;
36
+ event.preventDefault();
37
+ commitValue(nextValue);
38
+ };
39
+ const classes = (0, _puzzle_ui_core.cn)("puzzle-rating", disabled && "puzzle-rating-disabled", readOnly && "puzzle-rating-readonly", className);
40
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
41
+ ...props,
42
+ className: classes,
43
+ style: {
44
+ ...style,
45
+ "--puzzle-rating-color": color,
46
+ "--puzzle-rating-empty-color": emptyColor
47
+ },
48
+ role: readOnly ? "img" : "slider",
49
+ tabIndex: disabled || readOnly ? void 0 : 0,
50
+ "aria-label": readOnly ? `${ariaLabel}: ${currentValue} out of ${max} stars` : ariaLabel,
51
+ "aria-valuemin": readOnly ? void 0 : 0,
52
+ "aria-valuemax": readOnly ? void 0 : max,
53
+ "aria-valuenow": readOnly ? void 0 : currentValue,
54
+ "aria-valuetext": readOnly ? void 0 : `${currentValue} out of ${max} stars`,
55
+ "aria-disabled": disabled || void 0,
56
+ "aria-readonly": readOnly || void 0,
57
+ onKeyDown: handleKeyDown,
58
+ "data-size": size,
59
+ "data-disabled": disabled || void 0,
60
+ "data-readonly": readOnly || void 0,
61
+ children: Array.from({ length: max }, (_, index) => {
62
+ const state = require_rating_utils.getRatingStarState(currentValue, index);
63
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
64
+ className: "puzzle-rating__star",
65
+ "data-state": state,
66
+ "aria-hidden": "true",
67
+ onClick: (event) => {
68
+ const rect = event.currentTarget.getBoundingClientRect();
69
+ const isHalf = precision === .5 && event.clientX < rect.left + rect.width / 2;
70
+ commitValue(index + (isHalf ? .5 : 1));
71
+ },
72
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_puzzle_ui_icons.Icon, {
73
+ name: ICONS[state],
74
+ size: SIZE_MAP[size],
75
+ "aria-hidden": "true",
76
+ focusable: "false"
77
+ })
78
+ }, index);
79
+ })
80
+ });
81
+ };
82
+ exports.Rating = Rating;
83
+
84
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","names":["useState","normalizeRating","getNextRating","cn","getRatingStarState","Icon"],"sources":["../../../../src/components/data-display/rating/index.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState, type CSSProperties, type KeyboardEvent } from \"react\";\n\nimport { cn } from \"@puzzle-ui/core\";\nimport { Icon } from \"@puzzle-ui/icons\";\n\nimport type { RatingProps, RatingSize } from \"./rating.types\";\n\nimport {\n getNextRating,\n getRatingStarState,\n normalizeRating,\n} from \"./rating.utils\";\n\nimport \"./rating.css\";\n\nconst ICONS = {\n full: \"ph:star-fill\",\n half: \"ph:star-half-fill\",\n empty: \"ph:star\",\n} as const;\n\nconst SIZE_MAP = {\n sm: 16,\n md: 20,\n lg: 24,\n} as const;\n\nconst Rating = ({\n value,\n defaultValue = 0,\n onValueChange,\n max = 5,\n precision = 0.5,\n size = \"md\",\n readOnly = false,\n disabled = false,\n color = \"var(--puzzle-color-warning, #FABC0A)\",\n emptyColor = \"var(--puzzle-color-border)\",\n className,\n style,\n \"aria-label\": ariaLabel = \"Rating\",\n onKeyDown,\n ...props\n}: RatingProps) => {\n if (!Number.isInteger(max) || max < 1 || max > 10) {\n throw new Error(\"Rating max must be an integer between 1 and 10.\");\n }\n\n const isControlled = value !== undefined;\n\n const [internalValue, setInternalValue] = useState(() =>\n normalizeRating(defaultValue, max, precision),\n );\n\n const currentValue = normalizeRating(\n isControlled ? value : internalValue,\n max,\n precision,\n );\n\n const commitValue = (nextValue: number) => {\n if (disabled || readOnly) {\n return;\n }\n\n const next = normalizeRating(nextValue, max, precision);\n\n if (next === currentValue) {\n return;\n }\n\n if (!isControlled) {\n setInternalValue(next);\n }\n\n onValueChange?.(next);\n };\n\n const handleKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {\n onKeyDown?.(event);\n\n if (event.defaultPrevented || disabled || readOnly) {\n return;\n }\n\n const nextValue = getNextRating(currentValue, event.key, max, precision);\n\n if (nextValue === undefined) {\n return;\n }\n\n event.preventDefault();\n commitValue(nextValue);\n };\n\n const classes = cn(\n \"puzzle-rating\",\n disabled && \"puzzle-rating-disabled\",\n readOnly && \"puzzle-rating-readonly\",\n className,\n );\n\n return (\n <div\n {...props}\n className={classes}\n style={\n {\n ...style,\n \"--puzzle-rating-color\": color,\n \"--puzzle-rating-empty-color\": emptyColor,\n } as CSSProperties\n }\n role={readOnly ? \"img\" : \"slider\"}\n tabIndex={disabled || readOnly ? undefined : 0}\n aria-label={\n readOnly\n ? `${ariaLabel}: ${currentValue} out of ${max} stars`\n : ariaLabel\n }\n aria-valuemin={readOnly ? undefined : 0}\n aria-valuemax={readOnly ? undefined : max}\n aria-valuenow={readOnly ? undefined : currentValue}\n aria-valuetext={\n readOnly ? undefined : `${currentValue} out of ${max} stars`\n }\n aria-disabled={disabled || undefined}\n aria-readonly={readOnly || undefined}\n onKeyDown={handleKeyDown}\n data-size={size}\n data-disabled={disabled || undefined}\n data-readonly={readOnly || undefined}\n >\n {Array.from({ length: max }, (_, index) => {\n const state = getRatingStarState(currentValue, index);\n\n return (\n <span\n key={index}\n className=\"puzzle-rating__star\"\n data-state={state}\n aria-hidden=\"true\"\n onClick={(event) => {\n const rect = event.currentTarget.getBoundingClientRect();\n\n const isHalf =\n precision === 0.5 && event.clientX < rect.left + rect.width / 2;\n\n commitValue(index + (isHalf ? 0.5 : 1));\n }}\n >\n <Icon\n name={ICONS[state]}\n size={SIZE_MAP[size]}\n aria-hidden=\"true\"\n focusable=\"false\"\n />\n </span>\n );\n })}\n </div>\n );\n};\n\nexport { Rating, type RatingProps, type RatingSize };\n"],"mappings":";;;;;;;;AAiBA,MAAM,QAAQ;CACZ,MAAM;CACN,MAAM;CACN,OAAO;AACT;AAEA,MAAM,WAAW;CACf,IAAI;CACJ,IAAI;CACJ,IAAI;AACN;AAEA,MAAM,UAAU,EACd,OACA,eAAe,GACf,eACA,MAAM,GACN,YAAY,IACZ,OAAO,MACP,WAAW,OACX,WAAW,OACX,QAAQ,wCACR,aAAa,8BACb,WACA,OACA,cAAc,YAAY,UAC1B,WACA,GAAG,YACc;CACjB,IAAI,CAAC,OAAO,UAAU,GAAG,KAAK,MAAM,KAAK,MAAM,IAC7C,MAAM,IAAI,MAAM,iDAAiD;CAGnE,MAAM,eAAe,UAAU,KAAA;CAE/B,MAAM,CAAC,eAAe,qBAAA,GAAoBA,MAAAA,SAAAA,OACxCC,qBAAAA,gBAAgB,cAAc,KAAK,SAAS,CAC9C;CAEA,MAAM,eAAeA,qBAAAA,gBACnB,eAAe,QAAQ,eACvB,KACA,SACF;CAEA,MAAM,eAAe,cAAsB;EACzC,IAAI,YAAY,UACd;EAGF,MAAM,OAAOA,qBAAAA,gBAAgB,WAAW,KAAK,SAAS;EAEtD,IAAI,SAAS,cACX;EAGF,IAAI,CAAC,cACH,iBAAiB,IAAI;EAGvB,gBAAgB,IAAI;CACtB;CAEA,MAAM,iBAAiB,UAAyC;EAC9D,YAAY,KAAK;EAEjB,IAAI,MAAM,oBAAoB,YAAY,UACxC;EAGF,MAAM,YAAYC,qBAAAA,cAAc,cAAc,MAAM,KAAK,KAAK,SAAS;EAEvE,IAAI,cAAc,KAAA,GAChB;EAGF,MAAM,eAAe;EACrB,YAAY,SAAS;CACvB;CAEA,MAAM,WAAA,GAAUC,gBAAAA,GAAAA,CACd,iBACA,YAAY,0BACZ,YAAY,0BACZ,SACF;CAEA,OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;EACE,GAAI;EACJ,WAAW;EACX,OACE;GACE,GAAG;GACH,yBAAyB;GACzB,+BAA+B;EACjC;EAEF,MAAM,WAAW,QAAQ;EACzB,UAAU,YAAY,WAAW,KAAA,IAAY;EAC7C,cACE,WACI,GAAG,UAAU,IAAI,aAAa,UAAU,IAAI,UAC5C;EAEN,iBAAe,WAAW,KAAA,IAAY;EACtC,iBAAe,WAAW,KAAA,IAAY;EACtC,iBAAe,WAAW,KAAA,IAAY;EACtC,kBACE,WAAW,KAAA,IAAY,GAAG,aAAa,UAAU,IAAI;EAEvD,iBAAe,YAAY,KAAA;EAC3B,iBAAe,YAAY,KAAA;EAC3B,WAAW;EACX,aAAW;EACX,iBAAe,YAAY,KAAA;EAC3B,iBAAe,YAAY,KAAA;EAE1B,UAAA,MAAM,KAAK,EAAE,QAAQ,IAAI,IAAI,GAAG,UAAU;GACzC,MAAM,QAAQC,qBAAAA,mBAAmB,cAAc,KAAK;GAEpD,OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;IAEE,WAAU;IACV,cAAY;IACZ,eAAY;IACZ,UAAU,UAAU;KAClB,MAAM,OAAO,MAAM,cAAc,sBAAsB;KAEvD,MAAM,SACJ,cAAc,MAAO,MAAM,UAAU,KAAK,OAAO,KAAK,QAAQ;KAEhE,YAAY,SAAS,SAAS,KAAM,EAAE;IACxC;IAEA,UAAA,iBAAA,GAAA,kBAAA,IAAA,CAACC,iBAAAA,MAAD;KACE,MAAM,MAAM;KACZ,MAAM,SAAS;KACf,eAAY;KACZ,WAAU;IACX,CAAA;GACG,GAnBC,KAmBD;EAEV,CAAC;CACE,CAAA;AAET"}
@@ -0,0 +1,3 @@
1
+ import { RatingProps, RatingSize } from "./rating.types.cjs";
2
+ declare const Rating: ({ value, defaultValue, onValueChange, max, precision, size, readOnly, disabled, color, emptyColor, className, style, "aria-label": ariaLabel, onKeyDown, ...props }: RatingProps) => import("react").JSX.Element;
3
+ export { Rating, type RatingProps, type RatingSize };
@@ -0,0 +1,3 @@
1
+ import { RatingProps, RatingSize } from "./rating.types.js";
2
+ declare const Rating: ({ value, defaultValue, onValueChange, max, precision, size, readOnly, disabled, color, emptyColor, className, style, "aria-label": ariaLabel, onKeyDown, ...props }: RatingProps) => import("react").JSX.Element;
3
+ export { Rating, type RatingProps, type RatingSize };
@@ -0,0 +1,84 @@
1
+ "use client";
2
+ import { getNextRating, getRatingStarState, normalizeRating } from "./rating.utils.js";
3
+ import "./rating.css";
4
+ import { cn } from "@puzzle-ui/core";
5
+ import { useState } from "react";
6
+ import { jsx } from "react/jsx-runtime";
7
+ import { Icon } from "@puzzle-ui/icons";
8
+ // src/components/data-display/rating/index.tsx
9
+ const ICONS = {
10
+ full: "ph:star-fill",
11
+ half: "ph:star-half-fill",
12
+ empty: "ph:star"
13
+ };
14
+ const SIZE_MAP = {
15
+ sm: 16,
16
+ md: 20,
17
+ lg: 24
18
+ };
19
+ const Rating = ({ value, defaultValue = 0, onValueChange, max = 5, precision = .5, size = "md", readOnly = false, disabled = false, color = "var(--puzzle-color-warning, #FABC0A)", emptyColor = "var(--puzzle-color-border)", className, style, "aria-label": ariaLabel = "Rating", onKeyDown, ...props }) => {
20
+ if (!Number.isInteger(max) || max < 1 || max > 10) throw new Error("Rating max must be an integer between 1 and 10.");
21
+ const isControlled = value !== void 0;
22
+ const [internalValue, setInternalValue] = useState(() => normalizeRating(defaultValue, max, precision));
23
+ const currentValue = normalizeRating(isControlled ? value : internalValue, max, precision);
24
+ const commitValue = (nextValue) => {
25
+ if (disabled || readOnly) return;
26
+ const next = normalizeRating(nextValue, max, precision);
27
+ if (next === currentValue) return;
28
+ if (!isControlled) setInternalValue(next);
29
+ onValueChange?.(next);
30
+ };
31
+ const handleKeyDown = (event) => {
32
+ onKeyDown?.(event);
33
+ if (event.defaultPrevented || disabled || readOnly) return;
34
+ const nextValue = getNextRating(currentValue, event.key, max, precision);
35
+ if (nextValue === void 0) return;
36
+ event.preventDefault();
37
+ commitValue(nextValue);
38
+ };
39
+ const classes = cn("puzzle-rating", disabled && "puzzle-rating-disabled", readOnly && "puzzle-rating-readonly", className);
40
+ return /* @__PURE__ */ jsx("div", {
41
+ ...props,
42
+ className: classes,
43
+ style: {
44
+ ...style,
45
+ "--puzzle-rating-color": color,
46
+ "--puzzle-rating-empty-color": emptyColor
47
+ },
48
+ role: readOnly ? "img" : "slider",
49
+ tabIndex: disabled || readOnly ? void 0 : 0,
50
+ "aria-label": readOnly ? `${ariaLabel}: ${currentValue} out of ${max} stars` : ariaLabel,
51
+ "aria-valuemin": readOnly ? void 0 : 0,
52
+ "aria-valuemax": readOnly ? void 0 : max,
53
+ "aria-valuenow": readOnly ? void 0 : currentValue,
54
+ "aria-valuetext": readOnly ? void 0 : `${currentValue} out of ${max} stars`,
55
+ "aria-disabled": disabled || void 0,
56
+ "aria-readonly": readOnly || void 0,
57
+ onKeyDown: handleKeyDown,
58
+ "data-size": size,
59
+ "data-disabled": disabled || void 0,
60
+ "data-readonly": readOnly || void 0,
61
+ children: Array.from({ length: max }, (_, index) => {
62
+ const state = getRatingStarState(currentValue, index);
63
+ return /* @__PURE__ */ jsx("span", {
64
+ className: "puzzle-rating__star",
65
+ "data-state": state,
66
+ "aria-hidden": "true",
67
+ onClick: (event) => {
68
+ const rect = event.currentTarget.getBoundingClientRect();
69
+ const isHalf = precision === .5 && event.clientX < rect.left + rect.width / 2;
70
+ commitValue(index + (isHalf ? .5 : 1));
71
+ },
72
+ children: /* @__PURE__ */ jsx(Icon, {
73
+ name: ICONS[state],
74
+ size: SIZE_MAP[size],
75
+ "aria-hidden": "true",
76
+ focusable: "false"
77
+ })
78
+ }, index);
79
+ })
80
+ });
81
+ };
82
+ export { Rating };
83
+
84
+ //# sourceMappingURL=index.js.map