@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.
- package/CHANGELOG.md +24 -0
- package/LICENSE +21 -0
- package/README.md +598 -2
- package/dist/cli.mjs +2293 -0
- package/dist/dev/cli/schema.d.ts +73 -0
- package/dist/dev/index.d.ts +6 -0
- package/dist/dev/index.js +5 -0
- package/dist/dev/locales/ar.d.ts +141 -0
- package/dist/dev/locales/ar.js +128 -0
- package/dist/dev/locales/ar.js.map +1 -0
- package/dist/dev/locales/en.d.ts +141 -0
- package/dist/dev/locales/en.js +128 -0
- package/dist/dev/locales/en.js.map +1 -0
- package/dist/dev/registry/utils/utils.d.ts +29 -0
- package/dist/dev/registry/utils/utils.js +27 -0
- package/dist/dev/registry/utils/utils.js.map +1 -0
- package/dist/dev/theme/theme-logic.d.ts +187 -0
- package/dist/dev/theme/theme-logic.js +184 -0
- package/dist/dev/theme/theme-logic.js.map +1 -0
- package/dist/dev/theme/theme-provider.d.ts +90 -0
- package/dist/dev/theme/theme-provider.js +150 -0
- package/dist/dev/theme/theme-provider.js.map +1 -0
- package/dist/prod/index.js +1 -0
- package/dist/prod/locales/ar.js +2 -0
- package/dist/prod/locales/ar.js.map +1 -0
- package/dist/prod/locales/en.js +2 -0
- package/dist/prod/locales/en.js.map +1 -0
- package/dist/prod/registry/utils/utils.js +2 -0
- package/dist/prod/registry/utils/utils.js.map +1 -0
- package/dist/prod/theme/theme-logic.js +2 -0
- package/dist/prod/theme/theme-logic.js.map +1 -0
- package/dist/prod/theme/theme-provider.js +2 -0
- package/dist/prod/theme/theme-provider.js.map +1 -0
- package/package.json +102 -4
- package/registry/accordion/accordion.tsx +210 -0
- package/registry/accordion/item.json +4 -0
- package/registry/alert/alert.tsx +200 -0
- package/registry/alert/item.json +4 -0
- package/registry/alert-dialog/alert-dialog.tsx +530 -0
- package/registry/alert-dialog/item.json +4 -0
- package/registry/aspect-ratio/aspect-ratio.tsx +71 -0
- package/registry/aspect-ratio/item.json +4 -0
- package/registry/attachment/attachment.tsx +459 -0
- package/registry/attachment/item.json +4 -0
- package/registry/avatar/avatar.tsx +385 -0
- package/registry/avatar/item.json +4 -0
- package/registry/badge/badge.tsx +108 -0
- package/registry/badge/item.json +4 -0
- package/registry/breadcrumb/breadcrumb.tsx +354 -0
- package/registry/breadcrumb/item.json +4 -0
- package/registry/bubble/bubble.tsx +260 -0
- package/registry/bubble/item.json +4 -0
- package/registry/button/button.tsx +125 -0
- package/registry/button/item.json +4 -0
- package/registry/button-group/button-group.tsx +189 -0
- package/registry/button-group/item.json +4 -0
- package/registry/calendar/calendar.tsx +348 -0
- package/registry/calendar/item.json +4 -0
- package/registry/card/card.tsx +311 -0
- package/registry/card/item.json +4 -0
- package/registry/carousel/carousel.tsx +851 -0
- package/registry/carousel/item.json +4 -0
- package/registry/chart/chart.tsx +1956 -0
- package/registry/chart/item.json +4 -0
- package/registry/chart-scale/chart-scale.ts +670 -0
- package/registry/chart-scale/item.json +5 -0
- package/registry/checkbox/checkbox.tsx +79 -0
- package/registry/checkbox/item.json +4 -0
- package/registry/code-block/code-block.tsx +470 -0
- package/registry/code-block/item.json +4 -0
- package/registry/collapsible/collapsible.tsx +117 -0
- package/registry/collapsible/item.json +4 -0
- package/registry/combobox/combobox.tsx +977 -0
- package/registry/combobox/item.json +4 -0
- package/registry/command/command.tsx +757 -0
- package/registry/command/item.json +4 -0
- package/registry/context-menu/context-menu.tsx +697 -0
- package/registry/context-menu/item.json +4 -0
- package/registry/data-table/data-table.tsx +437 -0
- package/registry/data-table/item.json +4 -0
- package/registry/date-picker/date-picker.tsx +349 -0
- package/registry/date-picker/item.json +4 -0
- package/registry/dialog/dialog.tsx +412 -0
- package/registry/dialog/item.json +4 -0
- package/registry/direction/direction.tsx +88 -0
- package/registry/direction/item.json +4 -0
- package/registry/drawer/drawer.tsx +787 -0
- package/registry/drawer/item.json +4 -0
- package/registry/dropdown-menu/dropdown-menu.tsx +692 -0
- package/registry/dropdown-menu/item.json +4 -0
- package/registry/empty/empty.tsx +240 -0
- package/registry/empty/item.json +4 -0
- package/registry/field/field.tsx +624 -0
- package/registry/field/item.json +4 -0
- package/registry/form/form.tsx +1112 -0
- package/registry/form/item.json +4 -0
- package/registry/hover-card/hover-card.tsx +144 -0
- package/registry/hover-card/item.json +4 -0
- package/registry/icons/icons.tsx +1691 -0
- package/registry/icons/item.json +4 -0
- package/registry/input/input.tsx +43 -0
- package/registry/input/item.json +4 -0
- package/registry/input-group/input-group.tsx +315 -0
- package/registry/input-group/item.json +4 -0
- package/registry/input-otp/input-otp.tsx +329 -0
- package/registry/input-otp/item.json +4 -0
- package/registry/item/item.json +4 -0
- package/registry/item/item.tsx +439 -0
- package/registry/kbd/item.json +4 -0
- package/registry/kbd/kbd.tsx +65 -0
- package/registry/label/item.json +4 -0
- package/registry/label/label.tsx +60 -0
- package/registry/marker/item.json +4 -0
- package/registry/marker/marker.tsx +175 -0
- package/registry/menubar/item.json +4 -0
- package/registry/menubar/menubar.tsx +694 -0
- package/registry/message/item.json +4 -0
- package/registry/message/message.tsx +280 -0
- package/registry/message-scroller/item.json +4 -0
- package/registry/message-scroller/message-scroller.tsx +262 -0
- package/registry/native-select/item.json +4 -0
- package/registry/native-select/native-select.tsx +113 -0
- package/registry/navigation-menu/item.json +4 -0
- package/registry/navigation-menu/navigation-menu.tsx +362 -0
- package/registry/pagination/item.json +4 -0
- package/registry/pagination/pagination.tsx +460 -0
- package/registry/password-input/item.json +5 -0
- package/registry/password-input/password-input.tsx +494 -0
- package/registry/popover/item.json +4 -0
- package/registry/popover/popover.tsx +284 -0
- package/registry/progress/item.json +4 -0
- package/registry/progress/progress.tsx +190 -0
- package/registry/questionnaire/item.json +4 -0
- package/registry/questionnaire/questionnaire.tsx +569 -0
- package/registry/radio-group/item.json +4 -0
- package/registry/radio-group/radio-group.tsx +101 -0
- package/registry/resizable/item.json +4 -0
- package/registry/resizable/resizable.tsx +455 -0
- package/registry/scroll-area/item.json +4 -0
- package/registry/scroll-area/scroll-area.tsx +149 -0
- package/registry/select/item.json +4 -0
- package/registry/select/select.tsx +571 -0
- package/registry/separator/item.json +4 -0
- package/registry/separator/separator.tsx +52 -0
- package/registry/sheet/item.json +4 -0
- package/registry/sheet/sheet.tsx +476 -0
- package/registry/sidebar/item.json +4 -0
- package/registry/sidebar/sidebar.tsx +1870 -0
- package/registry/skeleton/item.json +4 -0
- package/registry/skeleton/skeleton.tsx +47 -0
- package/registry/slider/item.json +4 -0
- package/registry/slider/slider.tsx +149 -0
- package/registry/spinner/item.json +4 -0
- package/registry/spinner/spinner.tsx +51 -0
- package/registry/switch/item.json +4 -0
- package/registry/switch/switch.tsx +93 -0
- package/registry/table/item.json +4 -0
- package/registry/table/table.tsx +373 -0
- package/registry/tabs/item.json +4 -0
- package/registry/tabs/tabs.tsx +209 -0
- package/registry/textarea/item.json +4 -0
- package/registry/textarea/textarea.tsx +51 -0
- package/registry/toast/item.json +4 -0
- package/registry/toast/toast.tsx +949 -0
- package/registry/toggle/item.json +4 -0
- package/registry/toggle/toggle.tsx +110 -0
- package/registry/toggle-group/item.json +4 -0
- package/registry/toggle-group/toggle-group.tsx +189 -0
- package/registry/tooltip/item.json +4 -0
- package/registry/tooltip/tooltip.tsx +169 -0
- package/registry/trace/item.json +4 -0
- package/registry/trace/trace.tsx +356 -0
- package/registry/ui-strings/item.json +5 -0
- package/registry/ui-strings/ui-strings.tsx +454 -0
- package/registry/use-highlighted-code/item.json +5 -0
- package/registry/use-highlighted-code/use-highlighted-code.tsx +145 -0
- package/registry/use-mobile/item.json +5 -0
- package/registry/use-mobile/use-mobile.ts +46 -0
- package/registry/utils/item.json +5 -0
- package/registry/utils/utils.ts +38 -0
- package/registry.json +3274 -0
- package/styles/animations.css +321 -0
- package/styles/theme-core.css +55 -0
- package/styles/theme.css +12 -0
- package/styles/themes/gray.css +69 -0
- package/styles/themes/neutral.css +69 -0
- package/styles/themes/slate.css +69 -0
- package/styles/themes/stone.css +69 -0
- package/styles/themes/zinc.css +69 -0
- 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
|
+
};
|