@selvajs/ui 6.1.0 → 6.1.2
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/dist/components/layout/Pagination.svelte +65 -0
- package/dist/components/layout/Pagination.svelte.d.ts +12 -0
- package/dist/components/layout/index.d.ts +1 -0
- package/dist/components/layout/index.js +1 -0
- package/dist/components/primitives/dialog/dialog-content.svelte +1 -1
- package/dist/components/viewer/Viewer.svelte +1 -1
- package/package.json +15 -14
- package/src/lib/components/layout/Pagination.svelte +65 -0
- package/src/lib/components/layout/index.ts +1 -0
- package/src/lib/components/primitives/dialog/dialog-content.svelte +1 -1
- package/src/lib/components/viewer/Viewer.svelte +1 -1
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { Button } from '../primitives';
|
|
3
|
+
import { ChevronLeft, ChevronRight } from '@lucide/svelte';
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
/** Current 1-based page. Bindable. */
|
|
7
|
+
page: number;
|
|
8
|
+
total: number;
|
|
9
|
+
perPage?: number;
|
|
10
|
+
/** Plural noun for the range readout: "1–25 of 122 members". */
|
|
11
|
+
label?: string;
|
|
12
|
+
class?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
let {
|
|
16
|
+
page = $bindable(1),
|
|
17
|
+
total,
|
|
18
|
+
perPage = 25,
|
|
19
|
+
label = 'items',
|
|
20
|
+
class: className = ''
|
|
21
|
+
}: Props = $props();
|
|
22
|
+
|
|
23
|
+
const pageCount = $derived(Math.max(1, Math.ceil(total / perPage)));
|
|
24
|
+
|
|
25
|
+
// A filter can shrink `total` under the current page — clamp on read so the
|
|
26
|
+
// list never renders an empty slice while the control still says "page 4".
|
|
27
|
+
$effect(() => {
|
|
28
|
+
if (page > pageCount) page = pageCount;
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const first = $derived(total === 0 ? 0 : (page - 1) * perPage + 1);
|
|
32
|
+
const last = $derived(Math.min(page * perPage, total));
|
|
33
|
+
</script>
|
|
34
|
+
|
|
35
|
+
{#if pageCount > 1}
|
|
36
|
+
<div class={`gap-4 flex items-center justify-between ${className}`}>
|
|
37
|
+
<p class="text-xs text-muted-foreground">
|
|
38
|
+
{first}–{last} of {total}
|
|
39
|
+
{label}
|
|
40
|
+
</p>
|
|
41
|
+
<div class="gap-1 flex items-center">
|
|
42
|
+
<Button
|
|
43
|
+
size="sm"
|
|
44
|
+
variant="outline"
|
|
45
|
+
disabled={page <= 1}
|
|
46
|
+
onclick={() => (page -= 1)}
|
|
47
|
+
aria-label="Previous page"
|
|
48
|
+
class="h-8 w-8 p-0"
|
|
49
|
+
>
|
|
50
|
+
<ChevronLeft class="h-4 w-4" />
|
|
51
|
+
</Button>
|
|
52
|
+
<span class="px-2 font-mono text-xs text-muted-foreground">{page} / {pageCount}</span>
|
|
53
|
+
<Button
|
|
54
|
+
size="sm"
|
|
55
|
+
variant="outline"
|
|
56
|
+
disabled={page >= pageCount}
|
|
57
|
+
onclick={() => (page += 1)}
|
|
58
|
+
aria-label="Next page"
|
|
59
|
+
class="h-8 w-8 p-0"
|
|
60
|
+
>
|
|
61
|
+
<ChevronRight class="h-4 w-4" />
|
|
62
|
+
</Button>
|
|
63
|
+
</div>
|
|
64
|
+
</div>
|
|
65
|
+
{/if}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
interface Props {
|
|
2
|
+
/** Current 1-based page. Bindable. */
|
|
3
|
+
page: number;
|
|
4
|
+
total: number;
|
|
5
|
+
perPage?: number;
|
|
6
|
+
/** Plural noun for the range readout: "1–25 of 122 members". */
|
|
7
|
+
label?: string;
|
|
8
|
+
class?: string;
|
|
9
|
+
}
|
|
10
|
+
declare const Pagination: import("svelte").Component<Props, {}, "page">;
|
|
11
|
+
type Pagination = ReturnType<typeof Pagination>;
|
|
12
|
+
export default Pagination;
|
|
@@ -8,3 +8,4 @@ export { default as SideNav } from './SideNav.svelte';
|
|
|
8
8
|
export type { SideNavItem } from './SideNav.svelte';
|
|
9
9
|
export { default as SectionHeader } from './SectionHeader.svelte';
|
|
10
10
|
export { default as EmptyState } from './EmptyState.svelte';
|
|
11
|
+
export { default as Pagination } from './Pagination.svelte';
|
|
@@ -6,3 +6,4 @@ export { default as SubNav } from './SubNav.svelte';
|
|
|
6
6
|
export { default as SideNav } from './SideNav.svelte';
|
|
7
7
|
export { default as SectionHeader } from './SectionHeader.svelte';
|
|
8
8
|
export { default as EmptyState } from './EmptyState.svelte';
|
|
9
|
+
export { default as Pagination } from './Pagination.svelte';
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
bind:ref
|
|
26
26
|
data-slot="dialog-content"
|
|
27
27
|
class={cn(
|
|
28
|
-
'gap-4 p-6 shadow-lg data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 sm:max-w-lg fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] rounded-lg border bg-background duration-200',
|
|
28
|
+
'gap-4 p-6 shadow-lg data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 sm:max-w-lg fixed top-[50%] left-[50%] z-50 grid max-h-[85dvh] w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] overflow-y-auto rounded-lg border bg-background duration-200',
|
|
29
29
|
className
|
|
30
30
|
)}
|
|
31
31
|
{...restProps}
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@selvajs/ui",
|
|
3
|
-
"version": "6.1.
|
|
3
|
+
"version": "6.1.2",
|
|
4
4
|
"description": "Shared UI components and utilities for Selva applications",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "VektorNode",
|
|
7
|
-
"homepage": "https://
|
|
7
|
+
"homepage": "https://selva.dev",
|
|
8
8
|
"bugs": "https://github.com/VektorNode/selva/issues",
|
|
9
9
|
"keywords": [
|
|
10
10
|
"svelte",
|
|
@@ -59,10 +59,10 @@
|
|
|
59
59
|
"svelte": "^5",
|
|
60
60
|
"tailwind-variants": "^3.3.1",
|
|
61
61
|
"three": "^0.185.1",
|
|
62
|
-
"@selvajs/compute": "^4.0.
|
|
63
|
-
"@selvajs/
|
|
64
|
-
"@selvajs/
|
|
65
|
-
"@selvajs/
|
|
62
|
+
"@selvajs/compute": "^4.0.2",
|
|
63
|
+
"@selvajs/solve": "^1.0.4",
|
|
64
|
+
"@selvajs/visualization": "^1.0.1",
|
|
65
|
+
"@selvajs/schemas": "^5.0.1"
|
|
66
66
|
},
|
|
67
67
|
"peerDependenciesMeta": {
|
|
68
68
|
"three": {
|
|
@@ -76,25 +76,25 @@
|
|
|
76
76
|
"clsx": "^2.1.1",
|
|
77
77
|
"mode-watcher": "^1.1.0",
|
|
78
78
|
"paneforge": "^1.0.2",
|
|
79
|
-
"svelte-sonner": "^1.
|
|
79
|
+
"svelte-sonner": "^1.2.1",
|
|
80
80
|
"tailwind-merge": "^3.5.0",
|
|
81
81
|
"tw-animate-css": "^1.4.0"
|
|
82
82
|
},
|
|
83
83
|
"devDependencies": {
|
|
84
84
|
"@internationalized/date": "^3.12.3",
|
|
85
85
|
"@sveltejs/kit": "2.70.2",
|
|
86
|
-
"@sveltejs/vite-plugin-svelte": "^7.
|
|
86
|
+
"@sveltejs/vite-plugin-svelte": "^7.3.0",
|
|
87
87
|
"@types/three": "^0.185.4",
|
|
88
88
|
"bits-ui": "^2.18.0",
|
|
89
89
|
"rimraf": "^6.1.3",
|
|
90
|
-
"svelte": "5.56.
|
|
90
|
+
"svelte": "5.56.9",
|
|
91
91
|
"tailwind-variants": "^3.3.1",
|
|
92
92
|
"vitest": "^4.1.10",
|
|
93
|
-
"@selvajs/visualization": "1.0.0",
|
|
94
|
-
"@selvajs/schemas": "5.0.0",
|
|
95
|
-
"@selvajs/solve": "1.0.2",
|
|
96
93
|
"@selvajs/config": "0.0.4",
|
|
97
|
-
"@selvajs/
|
|
94
|
+
"@selvajs/schemas": "5.0.1",
|
|
95
|
+
"@selvajs/visualization": "1.0.1",
|
|
96
|
+
"@selvajs/solve": "1.0.4",
|
|
97
|
+
"@selvajs/compute": "4.0.2"
|
|
98
98
|
},
|
|
99
99
|
"scripts": {
|
|
100
100
|
"predev": "node ../../scripts/sync-shared-assets.js",
|
|
@@ -109,6 +109,7 @@
|
|
|
109
109
|
"test": "vitest run",
|
|
110
110
|
"test:watch": "vitest",
|
|
111
111
|
"format": "prettier --write .",
|
|
112
|
-
"lint": "prettier --check . && eslint ."
|
|
112
|
+
"lint": "prettier --check . && eslint .",
|
|
113
|
+
"lint:types": "eslint . --config eslint.typed.config.js"
|
|
113
114
|
}
|
|
114
115
|
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { Button } from '../primitives';
|
|
3
|
+
import { ChevronLeft, ChevronRight } from '@lucide/svelte';
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
/** Current 1-based page. Bindable. */
|
|
7
|
+
page: number;
|
|
8
|
+
total: number;
|
|
9
|
+
perPage?: number;
|
|
10
|
+
/** Plural noun for the range readout: "1–25 of 122 members". */
|
|
11
|
+
label?: string;
|
|
12
|
+
class?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
let {
|
|
16
|
+
page = $bindable(1),
|
|
17
|
+
total,
|
|
18
|
+
perPage = 25,
|
|
19
|
+
label = 'items',
|
|
20
|
+
class: className = ''
|
|
21
|
+
}: Props = $props();
|
|
22
|
+
|
|
23
|
+
const pageCount = $derived(Math.max(1, Math.ceil(total / perPage)));
|
|
24
|
+
|
|
25
|
+
// A filter can shrink `total` under the current page — clamp on read so the
|
|
26
|
+
// list never renders an empty slice while the control still says "page 4".
|
|
27
|
+
$effect(() => {
|
|
28
|
+
if (page > pageCount) page = pageCount;
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const first = $derived(total === 0 ? 0 : (page - 1) * perPage + 1);
|
|
32
|
+
const last = $derived(Math.min(page * perPage, total));
|
|
33
|
+
</script>
|
|
34
|
+
|
|
35
|
+
{#if pageCount > 1}
|
|
36
|
+
<div class={`gap-4 flex items-center justify-between ${className}`}>
|
|
37
|
+
<p class="text-xs text-muted-foreground">
|
|
38
|
+
{first}–{last} of {total}
|
|
39
|
+
{label}
|
|
40
|
+
</p>
|
|
41
|
+
<div class="gap-1 flex items-center">
|
|
42
|
+
<Button
|
|
43
|
+
size="sm"
|
|
44
|
+
variant="outline"
|
|
45
|
+
disabled={page <= 1}
|
|
46
|
+
onclick={() => (page -= 1)}
|
|
47
|
+
aria-label="Previous page"
|
|
48
|
+
class="h-8 w-8 p-0"
|
|
49
|
+
>
|
|
50
|
+
<ChevronLeft class="h-4 w-4" />
|
|
51
|
+
</Button>
|
|
52
|
+
<span class="px-2 font-mono text-xs text-muted-foreground">{page} / {pageCount}</span>
|
|
53
|
+
<Button
|
|
54
|
+
size="sm"
|
|
55
|
+
variant="outline"
|
|
56
|
+
disabled={page >= pageCount}
|
|
57
|
+
onclick={() => (page += 1)}
|
|
58
|
+
aria-label="Next page"
|
|
59
|
+
class="h-8 w-8 p-0"
|
|
60
|
+
>
|
|
61
|
+
<ChevronRight class="h-4 w-4" />
|
|
62
|
+
</Button>
|
|
63
|
+
</div>
|
|
64
|
+
</div>
|
|
65
|
+
{/if}
|
|
@@ -8,3 +8,4 @@ export { default as SideNav } from './SideNav.svelte';
|
|
|
8
8
|
export type { SideNavItem } from './SideNav.svelte';
|
|
9
9
|
export { default as SectionHeader } from './SectionHeader.svelte';
|
|
10
10
|
export { default as EmptyState } from './EmptyState.svelte';
|
|
11
|
+
export { default as Pagination } from './Pagination.svelte';
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
bind:ref
|
|
26
26
|
data-slot="dialog-content"
|
|
27
27
|
class={cn(
|
|
28
|
-
'gap-4 p-6 shadow-lg data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 sm:max-w-lg fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] rounded-lg border bg-background duration-200',
|
|
28
|
+
'gap-4 p-6 shadow-lg data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 sm:max-w-lg fixed top-[50%] left-[50%] z-50 grid max-h-[85dvh] w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] overflow-y-auto rounded-lg border bg-background duration-200',
|
|
29
29
|
className
|
|
30
30
|
)}
|
|
31
31
|
{...restProps}
|