@selvajs/ui 6.1.0 → 6.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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';
@@ -389,7 +389,7 @@
389
389
  <img
390
390
  src={logoUrl}
391
391
  alt=""
392
- class="h-8 max-w-32 drop-shadow-sm sm:h-10 w-auto object-contain opacity-80"
392
+ class="h-10 max-w-40 drop-shadow-sm sm:h-14 sm:max-w-56 w-auto object-contain opacity-80"
393
393
  />
394
394
  </div>
395
395
  {/if}
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@selvajs/ui",
3
- "version": "6.1.0",
3
+ "version": "6.1.1",
4
4
  "description": "Shared UI components and utilities for Selva applications",
5
5
  "license": "MIT",
6
6
  "author": "VektorNode",
7
- "homepage": "https://github.com/VektorNode/selva#readme",
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.1",
62
+ "@selvajs/compute": "^4.0.2",
63
63
  "@selvajs/schemas": "^5.0.0",
64
- "@selvajs/solve": "^1.0.2",
65
- "@selvajs/visualization": "^1.0.0"
64
+ "@selvajs/solve": "^1.0.3",
65
+ "@selvajs/visualization": "^1.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.1.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.2.0",
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.8",
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/compute": "4.0.1"
94
+ "@selvajs/schemas": "5.0.0",
95
+ "@selvajs/compute": "4.0.2",
96
+ "@selvajs/solve": "1.0.3",
97
+ "@selvajs/visualization": "1.0.1"
98
98
  },
99
99
  "scripts": {
100
100
  "predev": "node ../../scripts/sync-shared-assets.js",
@@ -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';
@@ -389,7 +389,7 @@
389
389
  <img
390
390
  src={logoUrl}
391
391
  alt=""
392
- class="h-8 max-w-32 drop-shadow-sm sm:h-10 w-auto object-contain opacity-80"
392
+ class="h-10 max-w-40 drop-shadow-sm sm:h-14 sm:max-w-56 w-auto object-contain opacity-80"
393
393
  />
394
394
  </div>
395
395
  {/if}