@shadow-js/ui 0.0.0-stage → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (190) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/LICENSE +21 -0
  3. package/README.md +598 -2
  4. package/dist/cli.mjs +2293 -0
  5. package/dist/dev/cli/schema.d.ts +73 -0
  6. package/dist/dev/index.d.ts +6 -0
  7. package/dist/dev/index.js +5 -0
  8. package/dist/dev/locales/ar.d.ts +141 -0
  9. package/dist/dev/locales/ar.js +128 -0
  10. package/dist/dev/locales/ar.js.map +1 -0
  11. package/dist/dev/locales/en.d.ts +141 -0
  12. package/dist/dev/locales/en.js +128 -0
  13. package/dist/dev/locales/en.js.map +1 -0
  14. package/dist/dev/registry/utils/utils.d.ts +29 -0
  15. package/dist/dev/registry/utils/utils.js +27 -0
  16. package/dist/dev/registry/utils/utils.js.map +1 -0
  17. package/dist/dev/theme/theme-logic.d.ts +187 -0
  18. package/dist/dev/theme/theme-logic.js +184 -0
  19. package/dist/dev/theme/theme-logic.js.map +1 -0
  20. package/dist/dev/theme/theme-provider.d.ts +90 -0
  21. package/dist/dev/theme/theme-provider.js +150 -0
  22. package/dist/dev/theme/theme-provider.js.map +1 -0
  23. package/dist/prod/index.js +1 -0
  24. package/dist/prod/locales/ar.js +2 -0
  25. package/dist/prod/locales/ar.js.map +1 -0
  26. package/dist/prod/locales/en.js +2 -0
  27. package/dist/prod/locales/en.js.map +1 -0
  28. package/dist/prod/registry/utils/utils.js +2 -0
  29. package/dist/prod/registry/utils/utils.js.map +1 -0
  30. package/dist/prod/theme/theme-logic.js +2 -0
  31. package/dist/prod/theme/theme-logic.js.map +1 -0
  32. package/dist/prod/theme/theme-provider.js +2 -0
  33. package/dist/prod/theme/theme-provider.js.map +1 -0
  34. package/package.json +102 -4
  35. package/registry/accordion/accordion.tsx +210 -0
  36. package/registry/accordion/item.json +4 -0
  37. package/registry/alert/alert.tsx +200 -0
  38. package/registry/alert/item.json +4 -0
  39. package/registry/alert-dialog/alert-dialog.tsx +530 -0
  40. package/registry/alert-dialog/item.json +4 -0
  41. package/registry/aspect-ratio/aspect-ratio.tsx +71 -0
  42. package/registry/aspect-ratio/item.json +4 -0
  43. package/registry/attachment/attachment.tsx +459 -0
  44. package/registry/attachment/item.json +4 -0
  45. package/registry/avatar/avatar.tsx +385 -0
  46. package/registry/avatar/item.json +4 -0
  47. package/registry/badge/badge.tsx +108 -0
  48. package/registry/badge/item.json +4 -0
  49. package/registry/breadcrumb/breadcrumb.tsx +354 -0
  50. package/registry/breadcrumb/item.json +4 -0
  51. package/registry/bubble/bubble.tsx +260 -0
  52. package/registry/bubble/item.json +4 -0
  53. package/registry/button/button.tsx +125 -0
  54. package/registry/button/item.json +4 -0
  55. package/registry/button-group/button-group.tsx +189 -0
  56. package/registry/button-group/item.json +4 -0
  57. package/registry/calendar/calendar.tsx +348 -0
  58. package/registry/calendar/item.json +4 -0
  59. package/registry/card/card.tsx +311 -0
  60. package/registry/card/item.json +4 -0
  61. package/registry/carousel/carousel.tsx +851 -0
  62. package/registry/carousel/item.json +4 -0
  63. package/registry/chart/chart.tsx +1956 -0
  64. package/registry/chart/item.json +4 -0
  65. package/registry/chart-scale/chart-scale.ts +670 -0
  66. package/registry/chart-scale/item.json +5 -0
  67. package/registry/checkbox/checkbox.tsx +79 -0
  68. package/registry/checkbox/item.json +4 -0
  69. package/registry/code-block/code-block.tsx +470 -0
  70. package/registry/code-block/item.json +4 -0
  71. package/registry/collapsible/collapsible.tsx +117 -0
  72. package/registry/collapsible/item.json +4 -0
  73. package/registry/combobox/combobox.tsx +977 -0
  74. package/registry/combobox/item.json +4 -0
  75. package/registry/command/command.tsx +757 -0
  76. package/registry/command/item.json +4 -0
  77. package/registry/context-menu/context-menu.tsx +697 -0
  78. package/registry/context-menu/item.json +4 -0
  79. package/registry/data-table/data-table.tsx +437 -0
  80. package/registry/data-table/item.json +4 -0
  81. package/registry/date-picker/date-picker.tsx +349 -0
  82. package/registry/date-picker/item.json +4 -0
  83. package/registry/dialog/dialog.tsx +412 -0
  84. package/registry/dialog/item.json +4 -0
  85. package/registry/direction/direction.tsx +88 -0
  86. package/registry/direction/item.json +4 -0
  87. package/registry/drawer/drawer.tsx +787 -0
  88. package/registry/drawer/item.json +4 -0
  89. package/registry/dropdown-menu/dropdown-menu.tsx +692 -0
  90. package/registry/dropdown-menu/item.json +4 -0
  91. package/registry/empty/empty.tsx +240 -0
  92. package/registry/empty/item.json +4 -0
  93. package/registry/field/field.tsx +624 -0
  94. package/registry/field/item.json +4 -0
  95. package/registry/form/form.tsx +1112 -0
  96. package/registry/form/item.json +4 -0
  97. package/registry/hover-card/hover-card.tsx +144 -0
  98. package/registry/hover-card/item.json +4 -0
  99. package/registry/icons/icons.tsx +1691 -0
  100. package/registry/icons/item.json +4 -0
  101. package/registry/input/input.tsx +43 -0
  102. package/registry/input/item.json +4 -0
  103. package/registry/input-group/input-group.tsx +315 -0
  104. package/registry/input-group/item.json +4 -0
  105. package/registry/input-otp/input-otp.tsx +329 -0
  106. package/registry/input-otp/item.json +4 -0
  107. package/registry/item/item.json +4 -0
  108. package/registry/item/item.tsx +439 -0
  109. package/registry/kbd/item.json +4 -0
  110. package/registry/kbd/kbd.tsx +65 -0
  111. package/registry/label/item.json +4 -0
  112. package/registry/label/label.tsx +60 -0
  113. package/registry/marker/item.json +4 -0
  114. package/registry/marker/marker.tsx +175 -0
  115. package/registry/menubar/item.json +4 -0
  116. package/registry/menubar/menubar.tsx +694 -0
  117. package/registry/message/item.json +4 -0
  118. package/registry/message/message.tsx +280 -0
  119. package/registry/message-scroller/item.json +4 -0
  120. package/registry/message-scroller/message-scroller.tsx +262 -0
  121. package/registry/native-select/item.json +4 -0
  122. package/registry/native-select/native-select.tsx +113 -0
  123. package/registry/navigation-menu/item.json +4 -0
  124. package/registry/navigation-menu/navigation-menu.tsx +362 -0
  125. package/registry/pagination/item.json +4 -0
  126. package/registry/pagination/pagination.tsx +460 -0
  127. package/registry/password-input/item.json +5 -0
  128. package/registry/password-input/password-input.tsx +494 -0
  129. package/registry/popover/item.json +4 -0
  130. package/registry/popover/popover.tsx +284 -0
  131. package/registry/progress/item.json +4 -0
  132. package/registry/progress/progress.tsx +190 -0
  133. package/registry/questionnaire/item.json +4 -0
  134. package/registry/questionnaire/questionnaire.tsx +569 -0
  135. package/registry/radio-group/item.json +4 -0
  136. package/registry/radio-group/radio-group.tsx +101 -0
  137. package/registry/resizable/item.json +4 -0
  138. package/registry/resizable/resizable.tsx +455 -0
  139. package/registry/scroll-area/item.json +4 -0
  140. package/registry/scroll-area/scroll-area.tsx +149 -0
  141. package/registry/select/item.json +4 -0
  142. package/registry/select/select.tsx +571 -0
  143. package/registry/separator/item.json +4 -0
  144. package/registry/separator/separator.tsx +52 -0
  145. package/registry/sheet/item.json +4 -0
  146. package/registry/sheet/sheet.tsx +476 -0
  147. package/registry/sidebar/item.json +4 -0
  148. package/registry/sidebar/sidebar.tsx +1870 -0
  149. package/registry/skeleton/item.json +4 -0
  150. package/registry/skeleton/skeleton.tsx +47 -0
  151. package/registry/slider/item.json +4 -0
  152. package/registry/slider/slider.tsx +149 -0
  153. package/registry/spinner/item.json +4 -0
  154. package/registry/spinner/spinner.tsx +51 -0
  155. package/registry/switch/item.json +4 -0
  156. package/registry/switch/switch.tsx +93 -0
  157. package/registry/table/item.json +4 -0
  158. package/registry/table/table.tsx +373 -0
  159. package/registry/tabs/item.json +4 -0
  160. package/registry/tabs/tabs.tsx +209 -0
  161. package/registry/textarea/item.json +4 -0
  162. package/registry/textarea/textarea.tsx +51 -0
  163. package/registry/toast/item.json +4 -0
  164. package/registry/toast/toast.tsx +949 -0
  165. package/registry/toggle/item.json +4 -0
  166. package/registry/toggle/toggle.tsx +110 -0
  167. package/registry/toggle-group/item.json +4 -0
  168. package/registry/toggle-group/toggle-group.tsx +189 -0
  169. package/registry/tooltip/item.json +4 -0
  170. package/registry/tooltip/tooltip.tsx +169 -0
  171. package/registry/trace/item.json +4 -0
  172. package/registry/trace/trace.tsx +356 -0
  173. package/registry/ui-strings/item.json +5 -0
  174. package/registry/ui-strings/ui-strings.tsx +454 -0
  175. package/registry/use-highlighted-code/item.json +5 -0
  176. package/registry/use-highlighted-code/use-highlighted-code.tsx +145 -0
  177. package/registry/use-mobile/item.json +5 -0
  178. package/registry/use-mobile/use-mobile.ts +46 -0
  179. package/registry/utils/item.json +5 -0
  180. package/registry/utils/utils.ts +38 -0
  181. package/registry.json +3274 -0
  182. package/styles/animations.css +321 -0
  183. package/styles/theme-core.css +55 -0
  184. package/styles/theme.css +12 -0
  185. package/styles/themes/gray.css +69 -0
  186. package/styles/themes/neutral.css +69 -0
  187. package/styles/themes/slate.css +69 -0
  188. package/styles/themes/stone.css +69 -0
  189. package/styles/themes/zinc.css +69 -0
  190. package/styles/typeset.css +251 -0
@@ -0,0 +1,460 @@
1
+ import { splitProps } from "@shadow-js/core";
2
+ import {
3
+ type PaginationEllipsisProps,
4
+ type PaginationListProps,
5
+ type PaginationNextProps,
6
+ type PaginationPreviousProps,
7
+ Pagination as PaginationPrimitive,
8
+ type PaginationItemProps as PaginationPrimitiveItemProps,
9
+ type PaginationItemsProps as PaginationPrimitiveItemsProps,
10
+ type PaginationProps,
11
+ type PaginationRangeItem,
12
+ Polymorphic,
13
+ type PolymorphicProps,
14
+ type PolymorphicTarget,
15
+ paginationContext,
16
+ } from "@shadow-js/primitives";
17
+ import type { JSX } from "@shadow-js/web";
18
+ import type { VariantProps } from "class-variance-authority";
19
+ import { buttonVariants } from "@/components/ui/button";
20
+ import { ChevronLeftIcon, ChevronRightIcon, EllipsisIcon } from "@/components/ui/icons";
21
+ import { useUiStrings } from "@/lib/ui-strings";
22
+ import { type ClassValue, cn } from "@/lib/utils";
23
+
24
+ /** The sizes of {@link buttonVariants}, which page controls borrow. */
25
+ type ButtonSize = VariantProps<typeof buttonVariants>["size"];
26
+
27
+ /** The props as the implementation sees them. */
28
+ type Loose = {
29
+ readonly as?: PolymorphicTarget;
30
+ readonly class?: ClassValue;
31
+ readonly size?: ButtonSize;
32
+ readonly page?: number;
33
+ readonly isActive?: boolean | undefined;
34
+ readonly itemProps?: Record<string, unknown> | undefined;
35
+ readonly children?: JSX.Element;
36
+ readonly [prop: string]: unknown;
37
+ };
38
+
39
+ /**
40
+ * Page navigation: a `nav` landmark that holds the page list. Controlled with `page` and
41
+ * `onPageChange`, or uncontrolled with `defaultPage`. `pageCount` is the number of pages. The current
42
+ * page is kept within `1..pageCount`, so a shrinking list never leaves the bar on a page that does
43
+ * not exist.
44
+ *
45
+ * Keyboard and ARIA: the items are ordinary tab stops. Enter goes to the page on a link, and Enter or
46
+ * Space on a button. The `nav` is named "pagination" by default; override it with `aria-label` when
47
+ * the page has more than one navigation. The current item has `aria-current="page"`. Previous and
48
+ * Next are named "Go to previous page" and "Go to next page", and carry `aria-disabled` at the ends.
49
+ *
50
+ * Pass `getHref` to make the pages links; without it they are buttons.
51
+ *
52
+ * Styling hooks: every part carries a `data-slot`: `pagination` on the `nav`, `pagination-content`
53
+ * on the list, `pagination-item` on the `li` of each control, `pagination-link` on the page,
54
+ * Previous and Next controls, and `pagination-ellipsis` on the ellipsis.
55
+ *
56
+ * @param props The `nav` attributes plus the primitive's options: `page`, `defaultPage`,
57
+ * `onPageChange`, `pageCount`, `siblingCount`, `boundaryCount`, `disabled`, `dir`, `getItemLabel` and
58
+ * `getHref`. `class` adds to the classes.
59
+ * @returns The `nav` element with its children inside.
60
+ * @example
61
+ * import {
62
+ * Pagination,
63
+ * PaginationContent,
64
+ * PaginationItems,
65
+ * PaginationNext,
66
+ * PaginationPrevious,
67
+ * } from "@/components/ui/pagination";
68
+ *
69
+ * const load = (page: number): void => console.info("page", page);
70
+ *
71
+ * <Pagination pageCount={20} defaultPage={3} onPageChange={load}>
72
+ * <PaginationContent>
73
+ * <PaginationPrevious />
74
+ * <PaginationItems />
75
+ * <PaginationNext />
76
+ * </PaginationContent>
77
+ * </Pagination>
78
+ * @since 1.0.0
79
+ * @see {@link PaginationContent}
80
+ */
81
+ function Pagination(props: PaginationProps): JSX.Element {
82
+ const [local, rest] = splitProps(props, ["class"]);
83
+ const t = useUiStrings();
84
+ return (
85
+ <PaginationPrimitive
86
+ data-slot="pagination"
87
+ aria-label={t("pagination.label", "pagination")}
88
+ getItemLabel={(page: number) => t("pagination.goToPage", "Go to page {page}", { page })}
89
+ class={cn("mx-auto flex w-full justify-center", local.class)}
90
+ {...rest}
91
+ />
92
+ );
93
+ }
94
+
95
+ /**
96
+ * The list (`ul`) of page controls.
97
+ *
98
+ * Keyboard and ARIA: a plain list with no role of its own; the controls inside are the tab stops.
99
+ *
100
+ * @param props The `ul` attributes. `class` adds to the classes.
101
+ * @returns The `ul` element.
102
+ * @throws Error when rendered outside a {@link Pagination}.
103
+ * @example
104
+ * import { Pagination, PaginationContent, PaginationItems } from "@/components/ui/pagination";
105
+ *
106
+ * <Pagination pageCount={5}>
107
+ * <PaginationContent>
108
+ * <PaginationItems />
109
+ * </PaginationContent>
110
+ * </Pagination>
111
+ * @since 1.0.0
112
+ * @see {@link Pagination}
113
+ */
114
+ function PaginationContent(props: PaginationListProps): JSX.Element {
115
+ const [local, rest] = splitProps(props, ["class"]);
116
+ return (
117
+ <PaginationPrimitive.List
118
+ data-slot="pagination-content"
119
+ class={cn("flex flex-row items-center gap-1", local.class)}
120
+ {...rest}
121
+ />
122
+ );
123
+ }
124
+
125
+ /**
126
+ * Own props of {@link PaginationItem}, {@link PaginationLink}, {@link PaginationPrevious} and
127
+ * {@link PaginationNext}: the `size` of the button the control looks like.
128
+ * @since 1.0.0
129
+ * @see {@link PaginationItem}
130
+ */
131
+ export interface PaginationSizeProps {
132
+ /**
133
+ * The size of the button the control looks like: `default`, `sm`, `lg`, `icon`, `icon-sm` or
134
+ * `icon-lg`. {@link PaginationPrevious} and {@link PaginationNext} default to `"default"`.
135
+ * @default "icon"
136
+ */
137
+ readonly size?: ButtonSize;
138
+ }
139
+
140
+ /**
141
+ * Props of {@link PaginationItem}: the primitive's item props (`page`, `itemProps`, and the link
142
+ * attributes) plus `size`.
143
+ * @since 1.0.0
144
+ * @see {@link PaginationItem}
145
+ */
146
+ export type PaginationItemProps<T extends PolymorphicTarget = "a"> =
147
+ PaginationPrimitiveItemProps<T> & PaginationSizeProps;
148
+
149
+ /** `itemProps` for the `li` of a control, with the shadcn slot. */
150
+ function itemShell(itemProps: Record<string, unknown> | undefined): Record<string, unknown> {
151
+ return { "data-slot": "pagination-item", ...itemProps };
152
+ }
153
+
154
+ /**
155
+ * One page of the bar: an `li` holding a link (or a button, without `getHref`) that goes to `page`.
156
+ * The current page looks `outline` and has `aria-current="page"`; the others look `ghost`. `class`
157
+ * styles the link, and `itemProps` the `li`.
158
+ *
159
+ * Keyboard and ARIA: Enter goes to the page on a link; Enter or Space does on a button. The link is
160
+ * named "Go to page N", unless the root's `getItemLabel` says otherwise.
161
+ *
162
+ * shadcn/ui differences: in shadcn, `PaginationItem` is the bare `li` around a `PaginationLink`. Here
163
+ * `PaginationItem` renders both, because the primitive's item owns the link behavior. Use
164
+ * {@link PaginationLink} for hand-written markup.
165
+ *
166
+ * @param props See {@link PaginationItemProps}.
167
+ * @returns The `li` element around the link or button.
168
+ * @throws Error when rendered outside a {@link Pagination}.
169
+ * @example
170
+ * import { Pagination, PaginationContent, PaginationItem } from "@/components/ui/pagination";
171
+ *
172
+ * <Pagination pageCount={5}>
173
+ * <PaginationContent>
174
+ * <PaginationItem page={3}>3</PaginationItem>
175
+ * </PaginationContent>
176
+ * </Pagination>
177
+ * @since 1.0.0
178
+ * @see {@link PaginationItems}
179
+ */
180
+ function PaginationItem<T extends PolymorphicTarget = "a">(
181
+ props: PaginationItemProps<T>,
182
+ ): JSX.Element {
183
+ const context = paginationContext.use("PaginationItem");
184
+ const [local, rest] = splitProps(props as unknown as Loose, [
185
+ "class",
186
+ "size",
187
+ "itemProps",
188
+ "page",
189
+ ]);
190
+ const active = (): boolean => context.page() === local.page;
191
+ return (
192
+ <PaginationPrimitive.Item
193
+ data-slot="pagination-link"
194
+ class={cn(
195
+ buttonVariants({ variant: active() ? "outline" : "ghost", size: local.size ?? "icon" }),
196
+ local.class,
197
+ )}
198
+ itemProps={itemShell(local.itemProps)}
199
+ page={local.page ?? 1}
200
+ {...rest}
201
+ />
202
+ );
203
+ }
204
+
205
+ /**
206
+ * Props of {@link PaginationLink}: the attributes of the chosen element (`a` by default), plus
207
+ * `isActive` and `size`.
208
+ * @since 1.0.0
209
+ * @see {@link PaginationLink}
210
+ */
211
+ export type PaginationLinkProps<T extends PolymorphicTarget = "a"> = PolymorphicProps<
212
+ "a",
213
+ PaginationSizeProps & {
214
+ /**
215
+ * Marks the link as the current page: it looks `outline` and gets `aria-current="page"`.
216
+ * @default false
217
+ */
218
+ readonly isActive?: boolean | undefined;
219
+ },
220
+ T
221
+ >;
222
+
223
+ /**
224
+ * A link styled like a page button, for hand-written pagination markup (your router's link, through
225
+ * `as`). It is not connected to the {@link Pagination} state, so set `isActive` yourself. Wrap it in
226
+ * a plain `li`.
227
+ *
228
+ * Keyboard and ARIA: a native link, so Enter follows it; `as` renders another element in its place.
229
+ * `isActive` sets `aria-current="page"` and `data-active="true"`. Its name is its text.
230
+ *
231
+ * @param props The link's attributes, plus `isActive`, `size` and `as`. `class` adds to the classes.
232
+ * @returns The link element.
233
+ * @example
234
+ * import { PaginationLink } from "@/components/ui/pagination";
235
+ *
236
+ * <li>
237
+ * <PaginationLink href="/page/2" isActive>2</PaginationLink>
238
+ * </li>
239
+ * @since 1.0.0
240
+ * @see {@link PaginationItem}
241
+ */
242
+ function PaginationLink<T extends PolymorphicTarget = "a">(
243
+ props: PaginationLinkProps<T>,
244
+ ): JSX.Element {
245
+ const [local, rest] = splitProps(props as unknown as Loose, ["as", "class", "isActive", "size"]);
246
+ return (
247
+ <Polymorphic
248
+ as={local.as ?? "a"}
249
+ data-slot="pagination-link"
250
+ aria-current={local.isActive === true ? "page" : undefined}
251
+ data-active={local.isActive === true ? "true" : undefined}
252
+ class={cn(
253
+ buttonVariants({
254
+ variant: local.isActive === true ? "outline" : "ghost",
255
+ size: local.size ?? "icon",
256
+ }),
257
+ local.class,
258
+ )}
259
+ {...rest}
260
+ />
261
+ );
262
+ }
263
+
264
+ /**
265
+ * Props of {@link PaginationPrevious}: the primitive's props plus `size`. `children` replaces the
266
+ * "Previous" text.
267
+ * @since 1.0.0
268
+ * @see {@link PaginationPrevious}
269
+ */
270
+ export type PaginationPreviousUiProps<T extends PolymorphicTarget = "a"> =
271
+ PaginationPreviousProps<T> & PaginationSizeProps;
272
+
273
+ /**
274
+ * Goes to the previous page. On the first page it is disabled (`aria-disabled`) but stays in the bar,
275
+ * so the bar does not shift. The text hides on small screens; the accessible name is "Go to previous
276
+ * page".
277
+ *
278
+ * Keyboard and ARIA: a link with `rel="prev"` or a button, depending on the root's `getHref`. Enter
279
+ * (on a link) or Enter and Space (on a button) goes to the previous page.
280
+ *
281
+ * @param props See {@link PaginationPreviousUiProps}. `children` replaces the "Previous" text.
282
+ * @returns The `li` element around the link or button.
283
+ * @throws Error when rendered outside a {@link Pagination}.
284
+ * @example
285
+ * import { Pagination, PaginationContent, PaginationPrevious } from "@/components/ui/pagination";
286
+ *
287
+ * <Pagination pageCount={5}>
288
+ * <PaginationContent>
289
+ * <PaginationPrevious />
290
+ * </PaginationContent>
291
+ * </Pagination>
292
+ * @since 1.0.0
293
+ * @see {@link PaginationNext}
294
+ */
295
+ function PaginationPrevious<T extends PolymorphicTarget = "a">(
296
+ props: PaginationPreviousUiProps<T>,
297
+ ): JSX.Element {
298
+ const [local, rest] = splitProps(props as unknown as Loose, [
299
+ "class",
300
+ "size",
301
+ "itemProps",
302
+ "children",
303
+ ]);
304
+ const t = useUiStrings();
305
+ return (
306
+ <PaginationPrimitive.Previous
307
+ data-slot="pagination-link"
308
+ aria-label={t("pagination.goPrevious", "Go to previous page")}
309
+ class={cn(
310
+ buttonVariants({ variant: "ghost", size: local.size ?? "default" }),
311
+ "gap-1 px-2.5 sm:ps-2.5",
312
+ local.class,
313
+ )}
314
+ itemProps={itemShell(local.itemProps)}
315
+ {...rest}
316
+ >
317
+ <ChevronLeftIcon class="rtl:rotate-180" />
318
+ <span class="hidden sm:block">{local.children ?? t("pagination.previous", "Previous")}</span>
319
+ </PaginationPrimitive.Previous>
320
+ );
321
+ }
322
+
323
+ /**
324
+ * Props of {@link PaginationNext}: the primitive's props plus `size`. `children` replaces the "Next"
325
+ * text.
326
+ * @since 1.0.0
327
+ * @see {@link PaginationNext}
328
+ */
329
+ export type PaginationNextUiProps<T extends PolymorphicTarget = "a"> = PaginationNextProps<T> &
330
+ PaginationSizeProps;
331
+
332
+ /**
333
+ * Goes to the next page. On the last page it is disabled (`aria-disabled`) but stays in the bar, so
334
+ * the bar does not shift. The text hides on small screens; the accessible name is "Go to next page".
335
+ *
336
+ * Keyboard and ARIA: a link with `rel="next"` or a button, depending on the root's `getHref`. Enter
337
+ * (on a link) or Enter and Space (on a button) goes to the next page.
338
+ *
339
+ * @param props See {@link PaginationNextUiProps}. `children` replaces the "Next" text.
340
+ * @returns The `li` element around the link or button.
341
+ * @throws Error when rendered outside a {@link Pagination}.
342
+ * @example
343
+ * import { Pagination, PaginationContent, PaginationNext } from "@/components/ui/pagination";
344
+ *
345
+ * <Pagination pageCount={5}>
346
+ * <PaginationContent>
347
+ * <PaginationNext />
348
+ * </PaginationContent>
349
+ * </Pagination>
350
+ * @since 1.0.0
351
+ * @see {@link PaginationPrevious}
352
+ */
353
+ function PaginationNext<T extends PolymorphicTarget = "a">(
354
+ props: PaginationNextUiProps<T>,
355
+ ): JSX.Element {
356
+ const [local, rest] = splitProps(props as unknown as Loose, [
357
+ "class",
358
+ "size",
359
+ "itemProps",
360
+ "children",
361
+ ]);
362
+ const t = useUiStrings();
363
+ return (
364
+ <PaginationPrimitive.Next
365
+ data-slot="pagination-link"
366
+ aria-label={t("pagination.goNext", "Go to next page")}
367
+ class={cn(
368
+ buttonVariants({ variant: "ghost", size: local.size ?? "default" }),
369
+ "gap-1 px-2.5 sm:pe-2.5",
370
+ local.class,
371
+ )}
372
+ itemProps={itemShell(local.itemProps)}
373
+ {...rest}
374
+ >
375
+ <span class="hidden sm:block">{local.children ?? t("pagination.next", "Next")}</span>
376
+ <ChevronRightIcon class="rtl:rotate-180" />
377
+ </PaginationPrimitive.Next>
378
+ );
379
+ }
380
+
381
+ /**
382
+ * A gap standing for hidden pages, drawn as an ellipsis icon by default. Its `li` is `aria-hidden`,
383
+ * so assistive technology skips it.
384
+ *
385
+ * Keyboard and ARIA: not focusable and not announced.
386
+ *
387
+ * @param props The `span` attributes, plus `itemProps` for the `li`. `children` replaces the icon.
388
+ * @returns The `li` element around the gap.
389
+ * @throws Error when rendered outside a {@link Pagination}.
390
+ * @example
391
+ * import { Pagination, PaginationContent, PaginationEllipsis } from "@/components/ui/pagination";
392
+ *
393
+ * <Pagination pageCount={20}>
394
+ * <PaginationContent>
395
+ * <PaginationEllipsis />
396
+ * </PaginationContent>
397
+ * </Pagination>
398
+ * @since 1.0.0
399
+ * @see {@link PaginationItems}
400
+ */
401
+ function PaginationEllipsis(props: PaginationEllipsisProps): JSX.Element {
402
+ const [local, rest] = splitProps(props as unknown as Loose, ["class", "itemProps", "children"]);
403
+ return (
404
+ <PaginationPrimitive.Ellipsis
405
+ data-slot="pagination-ellipsis"
406
+ class={cn("flex size-9 items-center justify-center", local.class)}
407
+ itemProps={itemShell(local.itemProps)}
408
+ {...rest}
409
+ >
410
+ {local.children ?? <EllipsisIcon class="size-4" />}
411
+ </PaginationPrimitive.Ellipsis>
412
+ );
413
+ }
414
+
415
+ /**
416
+ * Renders the whole bar for the current page: a {@link PaginationItem} for each page and a
417
+ * {@link PaginationEllipsis} for each gap, computed from the root's page count. Pass a function as
418
+ * `children` to draw each slot yourself.
419
+ *
420
+ * Keyboard and ARIA: the items keep their own keys and ARIA, as described on {@link Pagination}. The
421
+ * number of slots stays the same while the user pages through the list.
422
+ *
423
+ * @param props `children`, an optional function that receives each slot's range item (a page number,
424
+ * or the gap) and returns what to render for it.
425
+ * @returns The slots of the bar for the current page.
426
+ * @throws Error when rendered outside a {@link Pagination}.
427
+ * @example
428
+ * import { Pagination, PaginationContent, PaginationItems } from "@/components/ui/pagination";
429
+ *
430
+ * <Pagination pageCount={20}>
431
+ * <PaginationContent>
432
+ * <PaginationItems />
433
+ * </PaginationContent>
434
+ * </Pagination>
435
+ * @since 1.0.0
436
+ * @see {@link PaginationItem}
437
+ */
438
+ function PaginationItems(props: PaginationPrimitiveItemsProps): JSX.Element {
439
+ return (
440
+ <PaginationPrimitive.Items>
441
+ {(item: PaginationRangeItem) => {
442
+ const render = props.children;
443
+ if (render !== undefined) return render(item);
444
+ if (typeof item === "number") return <PaginationItem page={item}>{item}</PaginationItem>;
445
+ return <PaginationEllipsis />;
446
+ }}
447
+ </PaginationPrimitive.Items>
448
+ );
449
+ }
450
+
451
+ export {
452
+ Pagination,
453
+ PaginationContent,
454
+ PaginationEllipsis,
455
+ PaginationItem,
456
+ PaginationItems,
457
+ PaginationLink,
458
+ PaginationNext,
459
+ PaginationPrevious,
460
+ };
@@ -0,0 +1,5 @@
1
+ {
2
+ "title": "Password input",
3
+ "description": "A password field with a show and hide button, a strength meter slot and a Caps Lock hint, built on the PasswordToggleField primitive.",
4
+ "type": "ui"
5
+ }