@nikala-ui/core 0.10.1 → 0.11.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/package.json +1 -1
- package/registry/bubble.json +18 -0
- package/registry/button-group.json +20 -0
- package/registry/create-chat-scroll.json +13 -0
- package/registry/create-drop-zone.json +13 -0
- package/registry/create-pagination.json +13 -0
- package/registry/dropzone.json +21 -0
- package/registry/footer.json +18 -0
- package/registry/forgot-password-01.json +28 -0
- package/registry/hero-01.json +22 -0
- package/registry/index.json +331 -0
- package/registry/login-01.json +28 -0
- package/registry/marker.json +17 -0
- package/registry/marquee.json +17 -0
- package/registry/message.json +17 -0
- package/registry/navbar.json +19 -0
- package/registry/navigation-menu.json +22 -0
- package/registry/otp-verification-01.json +26 -0
- package/registry/pagination.json +22 -0
- package/registry/rating.json +19 -0
- package/registry/register-01.json +31 -0
- package/registry/review-card.json +23 -0
- package/registry/scroll-area.json +1 -1
- package/registry/sidebar.json +24 -0
- package/registry/spinner.json +1 -1
- package/registry/stat.json +19 -0
- package/registry/table.json +17 -0
- package/registry/timeline.json +18 -0
- package/registry/toggle-group.json +21 -0
- package/src/registry/blocks/forgot-password-01.tsx +141 -0
- package/src/registry/blocks/hero-01.tsx +28 -0
- package/src/registry/blocks/login-01.tsx +231 -0
- package/src/registry/blocks/otp-verification-01.tsx +170 -0
- package/src/registry/blocks/register-01.tsx +318 -0
- package/src/registry/components/ui/bubble.tsx +142 -0
- package/src/registry/components/ui/button-group.tsx +42 -0
- package/src/registry/components/ui/dropzone.tsx +189 -0
- package/src/registry/components/ui/footer.tsx +247 -0
- package/src/registry/components/ui/marker.tsx +102 -0
- package/src/registry/components/ui/marquee.tsx +118 -0
- package/src/registry/components/ui/message.tsx +168 -0
- package/src/registry/components/ui/navbar.tsx +368 -0
- package/src/registry/components/ui/navigation-menu.tsx +358 -0
- package/src/registry/components/ui/pagination.tsx +258 -0
- package/src/registry/components/ui/rating.tsx +185 -0
- package/src/registry/components/ui/review-card.tsx +195 -0
- package/src/registry/components/ui/scroll-area.tsx +2 -2
- package/src/registry/components/ui/sidebar.tsx +692 -0
- package/src/registry/components/ui/spinner.tsx +1 -1
- package/src/registry/components/ui/stat.tsx +245 -0
- package/src/registry/components/ui/table.tsx +160 -0
- package/src/registry/components/ui/timeline.tsx +350 -0
- package/src/registry/components/ui/toggle-group.tsx +203 -0
- package/src/registry/index.ts +3 -3
- package/src/registry/metadata.ts +141 -0
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createContext,
|
|
3
|
+
createSignal,
|
|
4
|
+
useContext,
|
|
5
|
+
splitProps,
|
|
6
|
+
onCleanup,
|
|
7
|
+
type Component,
|
|
8
|
+
type JSX,
|
|
9
|
+
type ParentComponent,
|
|
10
|
+
type Accessor,
|
|
11
|
+
Show,
|
|
12
|
+
} from "solid-js";
|
|
13
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
14
|
+
import { createClickOutside } from "@nikala-ui/hooks";
|
|
15
|
+
import { cn } from "@/lib/cn";
|
|
16
|
+
import { ChevronDown } from "lucide-solid";
|
|
17
|
+
|
|
18
|
+
/* --- Context Definition --- */
|
|
19
|
+
interface NavigationMenuContextValue {
|
|
20
|
+
activeValue: Accessor<string | null>;
|
|
21
|
+
setActiveValue: (value: string | null) => void;
|
|
22
|
+
onItemMouseEnter: (value: string) => void;
|
|
23
|
+
onItemMouseLeave: () => void;
|
|
24
|
+
delayMs: Accessor<number>;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const NavigationMenuContext = createContext<NavigationMenuContextValue>();
|
|
28
|
+
|
|
29
|
+
export function useNavigationMenu() {
|
|
30
|
+
const context = useContext(NavigationMenuContext);
|
|
31
|
+
if (!context) {
|
|
32
|
+
throw new Error("useNavigationMenu must be used within a <NavigationMenu />");
|
|
33
|
+
}
|
|
34
|
+
return context;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/* --- 1. NavigationMenu Root --- */
|
|
38
|
+
export interface NavigationMenuProps extends JSX.HTMLAttributes<HTMLElement> {
|
|
39
|
+
class?: string;
|
|
40
|
+
delayMs?: number;
|
|
41
|
+
value?: string | null;
|
|
42
|
+
onValueChange?: (value: string | null) => void;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export const NavigationMenu: ParentComponent<NavigationMenuProps> = (props) => {
|
|
46
|
+
const [local, rest] = splitProps(props, ["class", "delayMs", "value", "onValueChange", "children"]);
|
|
47
|
+
|
|
48
|
+
const [internalValue, setInternalValue] = createSignal<string | null>(local.value ?? null);
|
|
49
|
+
const activeValue = () => (local.value !== undefined ? local.value : internalValue());
|
|
50
|
+
|
|
51
|
+
const setActiveValue = (val: string | null) => {
|
|
52
|
+
if (local.value === undefined) {
|
|
53
|
+
setInternalValue(val);
|
|
54
|
+
}
|
|
55
|
+
local.onValueChange?.(val);
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
let rootRef!: HTMLElement;
|
|
59
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
60
|
+
const delay = () => local.delayMs ?? 150;
|
|
61
|
+
|
|
62
|
+
const onItemMouseEnter = (val: string) => {
|
|
63
|
+
clearTimeout(timer);
|
|
64
|
+
const ms = delay();
|
|
65
|
+
if (ms <= 0) {
|
|
66
|
+
setActiveValue(val);
|
|
67
|
+
} else {
|
|
68
|
+
timer = setTimeout(() => {
|
|
69
|
+
setActiveValue(val);
|
|
70
|
+
}, ms);
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const onItemMouseLeave = () => {
|
|
75
|
+
clearTimeout(timer);
|
|
76
|
+
const ms = delay();
|
|
77
|
+
timer = setTimeout(() => {
|
|
78
|
+
setActiveValue(null);
|
|
79
|
+
}, Math.max(50, ms));
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
createClickOutside({
|
|
83
|
+
target: () => rootRef,
|
|
84
|
+
onInteractOutside: () => {
|
|
85
|
+
setActiveValue(null);
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
onCleanup(() => {
|
|
90
|
+
clearTimeout(timer);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
const contextValue: NavigationMenuContextValue = {
|
|
94
|
+
activeValue,
|
|
95
|
+
setActiveValue,
|
|
96
|
+
onItemMouseEnter,
|
|
97
|
+
onItemMouseLeave,
|
|
98
|
+
delayMs: delay,
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
return (
|
|
102
|
+
<NavigationMenuContext.Provider value={contextValue}>
|
|
103
|
+
<nav
|
|
104
|
+
ref={rootRef}
|
|
105
|
+
aria-label="Main Navigation"
|
|
106
|
+
class={cn("relative z-10 flex max-w-max flex-1 items-center justify-center", local.class)}
|
|
107
|
+
{...rest}
|
|
108
|
+
>
|
|
109
|
+
{local.children}
|
|
110
|
+
</nav>
|
|
111
|
+
</NavigationMenuContext.Provider>
|
|
112
|
+
);
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
/* --- 2. NavigationMenuList --- */
|
|
116
|
+
export interface NavigationMenuListProps extends JSX.HTMLAttributes<HTMLUListElement> {
|
|
117
|
+
class?: string;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export const NavigationMenuList: ParentComponent<NavigationMenuListProps> = (props) => {
|
|
121
|
+
const [local, rest] = splitProps(props, ["class", "children"]);
|
|
122
|
+
|
|
123
|
+
return (
|
|
124
|
+
<ul
|
|
125
|
+
class={cn(
|
|
126
|
+
"group flex flex-1 list-none items-center justify-center space-x-1 p-1",
|
|
127
|
+
local.class
|
|
128
|
+
)}
|
|
129
|
+
{...rest}
|
|
130
|
+
>
|
|
131
|
+
{local.children}
|
|
132
|
+
</ul>
|
|
133
|
+
);
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
/* --- 3. NavigationMenuItem Context & Component --- */
|
|
137
|
+
interface NavigationMenuItemContextValue {
|
|
138
|
+
value: string;
|
|
139
|
+
isOpen: Accessor<boolean>;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const NavigationMenuItemContext = createContext<NavigationMenuItemContextValue>();
|
|
143
|
+
|
|
144
|
+
function useNavigationMenuItem() {
|
|
145
|
+
const context = useContext(NavigationMenuItemContext);
|
|
146
|
+
if (!context) {
|
|
147
|
+
throw new Error("useNavigationMenuItem must be used within a <NavigationMenuItem />");
|
|
148
|
+
}
|
|
149
|
+
return context;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export interface NavigationMenuItemProps extends JSX.HTMLAttributes<HTMLLIElement> {
|
|
153
|
+
value?: string;
|
|
154
|
+
class?: string;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
let itemCounter = 0;
|
|
158
|
+
|
|
159
|
+
export const NavigationMenuItem: ParentComponent<NavigationMenuItemProps> = (props) => {
|
|
160
|
+
const itemId = props.value ?? `nav-item-${++itemCounter}`;
|
|
161
|
+
const [local, rest] = splitProps(props, ["class", "value", "children"]);
|
|
162
|
+
const rootContext = useNavigationMenu();
|
|
163
|
+
|
|
164
|
+
const isOpen = () => rootContext.activeValue() === itemId;
|
|
165
|
+
|
|
166
|
+
const itemContext: NavigationMenuItemContextValue = {
|
|
167
|
+
value: itemId,
|
|
168
|
+
isOpen,
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
return (
|
|
172
|
+
<NavigationMenuItemContext.Provider value={itemContext}>
|
|
173
|
+
<li
|
|
174
|
+
class={cn("relative", local.class)}
|
|
175
|
+
onMouseEnter={() => rootContext.onItemMouseEnter(itemId)}
|
|
176
|
+
onMouseLeave={() => rootContext.onItemMouseLeave()}
|
|
177
|
+
{...rest}
|
|
178
|
+
>
|
|
179
|
+
{local.children}
|
|
180
|
+
</li>
|
|
181
|
+
</NavigationMenuItemContext.Provider>
|
|
182
|
+
);
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
/* --- 4. Trigger Style Helper --- */
|
|
186
|
+
export const navigationMenuTriggerVariants = cva(
|
|
187
|
+
"group inline-flex h-9 items-center justify-center rounded-md px-3 text-sm font-medium transition-colors focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50 cursor-pointer select-none gap-1",
|
|
188
|
+
{
|
|
189
|
+
variants: {
|
|
190
|
+
variant: {
|
|
191
|
+
default:
|
|
192
|
+
"bg-transparent text-muted-foreground hover:bg-muted hover:text-foreground data-[state=open]:bg-muted data-[state=open]:text-foreground",
|
|
193
|
+
filled:
|
|
194
|
+
"bg-muted/60 text-foreground hover:bg-muted border border-border/40 data-[state=open]:bg-muted",
|
|
195
|
+
ghost:
|
|
196
|
+
"bg-transparent text-foreground hover:bg-accent hover:text-accent-foreground data-[state=open]:bg-accent/60",
|
|
197
|
+
outline:
|
|
198
|
+
"border border-border bg-background hover:bg-accent hover:text-accent-foreground shadow-2xs data-[state=open]:bg-accent",
|
|
199
|
+
},
|
|
200
|
+
size: {
|
|
201
|
+
default: "h-9 px-3 text-sm",
|
|
202
|
+
sm: "h-8 px-2.5 text-xs",
|
|
203
|
+
lg: "h-10 px-4 text-base",
|
|
204
|
+
},
|
|
205
|
+
},
|
|
206
|
+
defaultVariants: {
|
|
207
|
+
variant: "default",
|
|
208
|
+
size: "default",
|
|
209
|
+
},
|
|
210
|
+
}
|
|
211
|
+
);
|
|
212
|
+
|
|
213
|
+
export function navigationMenuTriggerStyle(
|
|
214
|
+
variants?: VariantProps<typeof navigationMenuTriggerVariants>
|
|
215
|
+
) {
|
|
216
|
+
return navigationMenuTriggerVariants(variants);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/* --- 5. NavigationMenuTrigger --- */
|
|
220
|
+
export interface NavigationMenuTriggerProps
|
|
221
|
+
extends JSX.ButtonHTMLAttributes<HTMLButtonElement>,
|
|
222
|
+
VariantProps<typeof navigationMenuTriggerVariants> {
|
|
223
|
+
class?: string;
|
|
224
|
+
hideChevron?: boolean;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export const NavigationMenuTrigger: ParentComponent<NavigationMenuTriggerProps> = (props) => {
|
|
228
|
+
const [local, rest] = splitProps(props, ["class", "variant", "size", "hideChevron", "children"]);
|
|
229
|
+
const rootContext = useNavigationMenu();
|
|
230
|
+
const itemContext = useNavigationMenuItem();
|
|
231
|
+
|
|
232
|
+
const handleClick = (e: MouseEvent) => {
|
|
233
|
+
e.preventDefault();
|
|
234
|
+
if (itemContext.isOpen()) {
|
|
235
|
+
rootContext.setActiveValue(null);
|
|
236
|
+
} else {
|
|
237
|
+
rootContext.setActiveValue(itemContext.value);
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
return (
|
|
242
|
+
<button
|
|
243
|
+
type="button"
|
|
244
|
+
aria-expanded={itemContext.isOpen()}
|
|
245
|
+
data-state={itemContext.isOpen() ? "open" : "closed"}
|
|
246
|
+
onClick={handleClick}
|
|
247
|
+
class={cn(
|
|
248
|
+
navigationMenuTriggerVariants({ variant: local.variant, size: local.size }),
|
|
249
|
+
"gap-1.5",
|
|
250
|
+
local.class
|
|
251
|
+
)}
|
|
252
|
+
{...rest}
|
|
253
|
+
>
|
|
254
|
+
{local.children}
|
|
255
|
+
<Show when={!local.hideChevron}>
|
|
256
|
+
<ChevronDown
|
|
257
|
+
class={cn(
|
|
258
|
+
"size-3.5 text-muted-foreground transition-transform duration-200",
|
|
259
|
+
itemContext.isOpen() && "rotate-180 text-foreground"
|
|
260
|
+
)}
|
|
261
|
+
aria-hidden="true"
|
|
262
|
+
/>
|
|
263
|
+
</Show>
|
|
264
|
+
</button>
|
|
265
|
+
);
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
/* --- 6. NavigationMenuContent (Mega Menu Flyout) --- */
|
|
269
|
+
export interface NavigationMenuContentProps extends JSX.HTMLAttributes<HTMLDivElement> {
|
|
270
|
+
class?: string;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
export const NavigationMenuContent: ParentComponent<NavigationMenuContentProps> = (props) => {
|
|
274
|
+
const [local, rest] = splitProps(props, ["class", "children"]);
|
|
275
|
+
const itemContext = useNavigationMenuItem();
|
|
276
|
+
|
|
277
|
+
return (
|
|
278
|
+
<Show when={itemContext.isOpen()}>
|
|
279
|
+
<div
|
|
280
|
+
data-state={itemContext.isOpen() ? "open" : "closed"}
|
|
281
|
+
class={cn(
|
|
282
|
+
"absolute left-0 top-full mt-1.5 w-auto rounded-lg border border-border bg-popover p-4 text-popover-foreground shadow-lg outline-hidden z-50",
|
|
283
|
+
"animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 duration-200",
|
|
284
|
+
local.class
|
|
285
|
+
)}
|
|
286
|
+
{...rest}
|
|
287
|
+
>
|
|
288
|
+
{local.children}
|
|
289
|
+
</div>
|
|
290
|
+
</Show>
|
|
291
|
+
);
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
/* --- 7. NavigationMenuLink --- */
|
|
295
|
+
export interface NavigationMenuLinkProps extends JSX.AnchorHTMLAttributes<HTMLAnchorElement> {
|
|
296
|
+
class?: string;
|
|
297
|
+
active?: boolean;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
export const NavigationMenuLink: ParentComponent<NavigationMenuLinkProps> = (props) => {
|
|
301
|
+
const [local, rest] = splitProps(props, ["class", "active", "children"]);
|
|
302
|
+
|
|
303
|
+
return (
|
|
304
|
+
<a
|
|
305
|
+
data-active={local.active ? "" : undefined}
|
|
306
|
+
class={cn(
|
|
307
|
+
"block select-none space-y-1 rounded-md p-3 leading-none no-underline outline-hidden transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground",
|
|
308
|
+
local.active && "bg-accent/60 text-accent-foreground",
|
|
309
|
+
local.class
|
|
310
|
+
)}
|
|
311
|
+
{...rest}
|
|
312
|
+
>
|
|
313
|
+
{local.children}
|
|
314
|
+
</a>
|
|
315
|
+
);
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
/* --- 8. NavigationMenuViewport --- */
|
|
319
|
+
export interface NavigationMenuViewportProps extends JSX.HTMLAttributes<HTMLDivElement> {
|
|
320
|
+
class?: string;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
export const NavigationMenuViewport: Component<NavigationMenuViewportProps> = (props) => {
|
|
324
|
+
const [local, rest] = splitProps(props, ["class"]);
|
|
325
|
+
|
|
326
|
+
return (
|
|
327
|
+
<div class="absolute left-0 top-full flex justify-center">
|
|
328
|
+
<div
|
|
329
|
+
class={cn(
|
|
330
|
+
"origin-top-center relative mt-1.5 h-[var(--radix-navigation-menu-viewport-height)] w-full overflow-hidden rounded-md border border-border bg-popover text-popover-foreground shadow-lg transition-all",
|
|
331
|
+
local.class
|
|
332
|
+
)}
|
|
333
|
+
{...rest}
|
|
334
|
+
/>
|
|
335
|
+
</div>
|
|
336
|
+
);
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
/* --- 9. NavigationMenuIndicator --- */
|
|
340
|
+
export interface NavigationMenuIndicatorProps extends JSX.HTMLAttributes<HTMLDivElement> {
|
|
341
|
+
class?: string;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
export const NavigationMenuIndicator: Component<NavigationMenuIndicatorProps> = (props) => {
|
|
345
|
+
const [local, rest] = splitProps(props, ["class"]);
|
|
346
|
+
|
|
347
|
+
return (
|
|
348
|
+
<div
|
|
349
|
+
class={cn(
|
|
350
|
+
"top-full z-1 flex h-1.5 items-end justify-center overflow-hidden transition-[width,transform] duration-200",
|
|
351
|
+
local.class
|
|
352
|
+
)}
|
|
353
|
+
{...rest}
|
|
354
|
+
>
|
|
355
|
+
<div class="relative top-[60%] h-2 w-2 rotate-45 rounded-tl-xs bg-border shadow-md" />
|
|
356
|
+
</div>
|
|
357
|
+
);
|
|
358
|
+
};
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
import { splitProps, type Component, type JSX, type ParentComponent, Show } from "solid-js";
|
|
2
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
3
|
+
import { cn } from "@/lib/cn";
|
|
4
|
+
import { ChevronLeft, ChevronRight, ChevronsLeft, ChevronsRight, MoreHorizontal } from "lucide-solid";
|
|
5
|
+
|
|
6
|
+
/* --- 1. Button Variants for Pagination Links --- */
|
|
7
|
+
export const paginationButtonVariants = cva(
|
|
8
|
+
"inline-flex items-center justify-center whitespace-nowrap rounded-md font-medium transition-colors focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 cursor-pointer select-none",
|
|
9
|
+
{
|
|
10
|
+
variants: {
|
|
11
|
+
variant: {
|
|
12
|
+
default: "hover:bg-accent hover:text-accent-foreground data-[active=true]:bg-primary data-[active=true]:text-primary-foreground data-[active=true]:font-bold data-[active=true]:shadow-2xs",
|
|
13
|
+
outline: "border border-border bg-background hover:bg-accent hover:text-accent-foreground data-[active=true]:border-primary data-[active=true]:bg-primary/10 data-[active=true]:text-primary data-[active=true]:font-bold",
|
|
14
|
+
ghost: "hover:bg-accent/80 hover:text-accent-foreground data-[active=true]:bg-accent data-[active=true]:text-foreground data-[active=true]:font-bold",
|
|
15
|
+
flat: "hover:bg-muted/80 data-[active=true]:bg-muted data-[active=true]:text-foreground data-[active=true]:font-bold",
|
|
16
|
+
},
|
|
17
|
+
size: {
|
|
18
|
+
default: "h-9 min-w-9 px-3 text-sm",
|
|
19
|
+
sm: "h-8 min-w-8 px-2 text-xs",
|
|
20
|
+
lg: "h-10 min-w-10 px-4 text-base",
|
|
21
|
+
icon: "h-9 w-9 p-0 text-sm",
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
defaultVariants: {
|
|
25
|
+
variant: "default",
|
|
26
|
+
size: "default",
|
|
27
|
+
},
|
|
28
|
+
}
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
/* --- 2. Pagination Root --- */
|
|
32
|
+
export interface PaginationProps extends JSX.HTMLAttributes<HTMLElement> {
|
|
33
|
+
class?: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export const Pagination: ParentComponent<PaginationProps> = (props) => {
|
|
37
|
+
const [local, rest] = splitProps(props, ["class", "children"]);
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<nav
|
|
41
|
+
role="navigation"
|
|
42
|
+
aria-label="pagination"
|
|
43
|
+
class={cn("mx-auto flex w-full justify-center", local.class)}
|
|
44
|
+
{...rest}
|
|
45
|
+
>
|
|
46
|
+
{local.children}
|
|
47
|
+
</nav>
|
|
48
|
+
);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
/* --- 3. PaginationContent --- */
|
|
52
|
+
export interface PaginationContentProps extends JSX.HTMLAttributes<HTMLUListElement> {
|
|
53
|
+
class?: string;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export const PaginationContent: ParentComponent<PaginationContentProps> = (props) => {
|
|
57
|
+
const [local, rest] = splitProps(props, ["class", "children"]);
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
<ul
|
|
61
|
+
class={cn("flex flex-row items-center gap-1 flex-wrap", local.class)}
|
|
62
|
+
{...rest}
|
|
63
|
+
>
|
|
64
|
+
{local.children}
|
|
65
|
+
</ul>
|
|
66
|
+
);
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
/* --- 4. PaginationItem --- */
|
|
70
|
+
export interface PaginationItemProps extends JSX.HTMLAttributes<HTMLLIElement> {
|
|
71
|
+
class?: string;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export const PaginationItem: ParentComponent<PaginationItemProps> = (props) => {
|
|
75
|
+
const [local, rest] = splitProps(props, ["class", "children"]);
|
|
76
|
+
|
|
77
|
+
return (
|
|
78
|
+
<li class={cn("", local.class)} {...rest}>
|
|
79
|
+
{local.children}
|
|
80
|
+
</li>
|
|
81
|
+
);
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
/* --- 5. PaginationLink --- */
|
|
85
|
+
export interface PaginationLinkProps
|
|
86
|
+
extends JSX.ButtonHTMLAttributes<HTMLButtonElement>,
|
|
87
|
+
VariantProps<typeof paginationButtonVariants> {
|
|
88
|
+
isActive?: boolean;
|
|
89
|
+
class?: string;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export const PaginationLink: ParentComponent<PaginationLinkProps> = (props) => {
|
|
93
|
+
const [local, rest] = splitProps(props, ["class", "variant", "size", "isActive", "children"]);
|
|
94
|
+
|
|
95
|
+
return (
|
|
96
|
+
<button
|
|
97
|
+
type="button"
|
|
98
|
+
aria-current={local.isActive ? "page" : undefined}
|
|
99
|
+
data-active={local.isActive ? "true" : "false"}
|
|
100
|
+
class={cn(
|
|
101
|
+
paginationButtonVariants({
|
|
102
|
+
variant: local.variant,
|
|
103
|
+
size: local.size,
|
|
104
|
+
}),
|
|
105
|
+
local.class
|
|
106
|
+
)}
|
|
107
|
+
{...rest}
|
|
108
|
+
>
|
|
109
|
+
{local.children}
|
|
110
|
+
</button>
|
|
111
|
+
);
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
/* --- 6. PaginationPrevious --- */
|
|
115
|
+
export interface PaginationPreviousProps
|
|
116
|
+
extends JSX.ButtonHTMLAttributes<HTMLButtonElement>,
|
|
117
|
+
VariantProps<typeof paginationButtonVariants> {
|
|
118
|
+
class?: string;
|
|
119
|
+
hideText?: boolean;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export const PaginationPrevious: Component<PaginationPreviousProps> = (props) => {
|
|
123
|
+
const [local, rest] = splitProps(props, ["class", "variant", "size", "hideText"]);
|
|
124
|
+
|
|
125
|
+
return (
|
|
126
|
+
<PaginationLink
|
|
127
|
+
aria-label="Go to previous page"
|
|
128
|
+
variant={local.variant}
|
|
129
|
+
size={local.size}
|
|
130
|
+
class={cn("gap-1 pl-2.5", local.hideText && "p-0 min-w-8", local.class)}
|
|
131
|
+
{...rest}
|
|
132
|
+
>
|
|
133
|
+
<ChevronLeft class="size-4" />
|
|
134
|
+
<Show when={!local.hideText}>
|
|
135
|
+
<span>Previous</span>
|
|
136
|
+
</Show>
|
|
137
|
+
</PaginationLink>
|
|
138
|
+
);
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
/* --- 7. PaginationNext --- */
|
|
142
|
+
export interface PaginationNextProps
|
|
143
|
+
extends JSX.ButtonHTMLAttributes<HTMLButtonElement>,
|
|
144
|
+
VariantProps<typeof paginationButtonVariants> {
|
|
145
|
+
class?: string;
|
|
146
|
+
hideText?: boolean;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export const PaginationNext: Component<PaginationNextProps> = (props) => {
|
|
150
|
+
const [local, rest] = splitProps(props, ["class", "variant", "size", "hideText"]);
|
|
151
|
+
|
|
152
|
+
return (
|
|
153
|
+
<PaginationLink
|
|
154
|
+
aria-label="Go to next page"
|
|
155
|
+
variant={local.variant}
|
|
156
|
+
size={local.size}
|
|
157
|
+
class={cn("gap-1 pr-2.5", local.hideText && "p-0 min-w-8", local.class)}
|
|
158
|
+
{...rest}
|
|
159
|
+
>
|
|
160
|
+
<Show when={!local.hideText}>
|
|
161
|
+
<span>Next</span>
|
|
162
|
+
</Show>
|
|
163
|
+
<ChevronRight class="size-4" />
|
|
164
|
+
</PaginationLink>
|
|
165
|
+
);
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
/* --- 8. PaginationFirst --- */
|
|
169
|
+
export interface PaginationFirstProps
|
|
170
|
+
extends JSX.ButtonHTMLAttributes<HTMLButtonElement>,
|
|
171
|
+
VariantProps<typeof paginationButtonVariants> {
|
|
172
|
+
class?: string;
|
|
173
|
+
hideText?: boolean;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export const PaginationFirst: Component<PaginationFirstProps> = (props) => {
|
|
177
|
+
const [local, rest] = splitProps(props, ["class", "variant", "size", "hideText"]);
|
|
178
|
+
|
|
179
|
+
return (
|
|
180
|
+
<PaginationLink
|
|
181
|
+
aria-label="Go to first page"
|
|
182
|
+
variant={local.variant}
|
|
183
|
+
size={local.size}
|
|
184
|
+
class={cn("gap-1 p-0 min-w-8", local.class)}
|
|
185
|
+
{...rest}
|
|
186
|
+
>
|
|
187
|
+
<ChevronsLeft class="size-4" />
|
|
188
|
+
<Show when={!local.hideText}>
|
|
189
|
+
<span class="sr-only">First page</span>
|
|
190
|
+
</Show>
|
|
191
|
+
</PaginationLink>
|
|
192
|
+
);
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
/* --- 9. PaginationLast --- */
|
|
196
|
+
export interface PaginationLastProps
|
|
197
|
+
extends JSX.ButtonHTMLAttributes<HTMLButtonElement>,
|
|
198
|
+
VariantProps<typeof paginationButtonVariants> {
|
|
199
|
+
class?: string;
|
|
200
|
+
hideText?: boolean;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export const PaginationLast: Component<PaginationLastProps> = (props) => {
|
|
204
|
+
const [local, rest] = splitProps(props, ["class", "variant", "size", "hideText"]);
|
|
205
|
+
|
|
206
|
+
return (
|
|
207
|
+
<PaginationLink
|
|
208
|
+
aria-label="Go to last page"
|
|
209
|
+
variant={local.variant}
|
|
210
|
+
size={local.size}
|
|
211
|
+
class={cn("gap-1 p-0 min-w-8", local.class)}
|
|
212
|
+
{...rest}
|
|
213
|
+
>
|
|
214
|
+
<ChevronsRight class="size-4" />
|
|
215
|
+
<Show when={!local.hideText}>
|
|
216
|
+
<span class="sr-only">Last page</span>
|
|
217
|
+
</Show>
|
|
218
|
+
</PaginationLink>
|
|
219
|
+
);
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
/* --- 10. PaginationEllipsis --- */
|
|
223
|
+
export interface PaginationEllipsisProps extends JSX.HTMLAttributes<HTMLSpanElement> {
|
|
224
|
+
class?: string;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export const PaginationEllipsis: Component<PaginationEllipsisProps> = (props) => {
|
|
228
|
+
const [local, rest] = splitProps(props, ["class"]);
|
|
229
|
+
|
|
230
|
+
return (
|
|
231
|
+
<span
|
|
232
|
+
aria-hidden="true"
|
|
233
|
+
class={cn("flex h-9 w-9 items-center justify-center text-muted-foreground", local.class)}
|
|
234
|
+
{...rest}
|
|
235
|
+
>
|
|
236
|
+
<MoreHorizontal class="size-4" />
|
|
237
|
+
<span class="sr-only">More pages</span>
|
|
238
|
+
</span>
|
|
239
|
+
);
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
/* --- 11. PaginationSummary --- */
|
|
243
|
+
export interface PaginationSummaryProps extends JSX.HTMLAttributes<HTMLDivElement> {
|
|
244
|
+
class?: string;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
export const PaginationSummary: ParentComponent<PaginationSummaryProps> = (props) => {
|
|
248
|
+
const [local, rest] = splitProps(props, ["class", "children"]);
|
|
249
|
+
|
|
250
|
+
return (
|
|
251
|
+
<div
|
|
252
|
+
class={cn("text-xs text-muted-foreground font-mono select-none", local.class)}
|
|
253
|
+
{...rest}
|
|
254
|
+
>
|
|
255
|
+
{local.children}
|
|
256
|
+
</div>
|
|
257
|
+
);
|
|
258
|
+
};
|