@marianmeres/stuic 3.166.0 → 3.168.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/AGENTS.md +6 -6
- package/API.md +14 -13
- package/dist/attachments/index.d.ts +1 -0
- package/dist/attachments/index.js +1 -0
- package/dist/attachments/long-press.d.ts +43 -0
- package/dist/attachments/long-press.js +107 -0
- package/dist/components/Breadcrumbs/Breadcrumbs.svelte +198 -0
- package/dist/components/Breadcrumbs/Breadcrumbs.svelte.d.ts +60 -0
- package/dist/components/Breadcrumbs/README.md +174 -0
- package/dist/components/Breadcrumbs/i18n-sk.d.ts +17 -0
- package/dist/components/Breadcrumbs/i18n-sk.js +19 -0
- package/dist/components/Breadcrumbs/i18n.d.ts +31 -0
- package/dist/components/Breadcrumbs/i18n.js +40 -0
- package/dist/components/Breadcrumbs/index.css +84 -0
- package/dist/components/Breadcrumbs/index.d.ts +4 -0
- package/dist/components/Breadcrumbs/index.js +4 -0
- package/dist/components/Breadcrumbs/json-ld.d.ts +58 -0
- package/dist/components/Breadcrumbs/json-ld.js +56 -0
- package/dist/components/Collapsible/Collapsible.svelte +84 -15
- package/dist/components/Collapsible/Collapsible.svelte.d.ts +29 -2
- package/dist/components/Collapsible/README.md +82 -23
- package/dist/components/Collapsible/index.css +38 -0
- package/dist/components/Collapsible/index.d.ts +1 -1
- package/dist/components/ContextMenu/ContextMenu.svelte +302 -0
- package/dist/components/ContextMenu/ContextMenu.svelte.d.ts +94 -0
- package/dist/components/ContextMenu/README.md +159 -0
- package/dist/components/ContextMenu/i18n-sk.d.ts +17 -0
- package/dist/components/ContextMenu/i18n-sk.js +18 -0
- package/dist/components/ContextMenu/i18n.d.ts +30 -0
- package/dist/components/ContextMenu/i18n.js +38 -0
- package/dist/components/ContextMenu/index.css +26 -0
- package/dist/components/ContextMenu/index.d.ts +3 -0
- package/dist/components/ContextMenu/index.js +3 -0
- package/dist/components/CopyButton/CopyButton.svelte +278 -0
- package/dist/components/CopyButton/CopyButton.svelte.d.ts +76 -0
- package/dist/components/CopyButton/README.md +241 -0
- package/dist/components/CopyButton/i18n-sk.d.ts +17 -0
- package/dist/components/CopyButton/i18n-sk.js +20 -0
- package/dist/components/CopyButton/i18n.d.ts +35 -0
- package/dist/components/CopyButton/i18n.js +43 -0
- package/dist/components/CopyButton/index.css +67 -0
- package/dist/components/CopyButton/index.d.ts +3 -0
- package/dist/components/CopyButton/index.js +3 -0
- package/dist/components/DataTable/DataTable.svelte +58 -36
- package/dist/components/DataTable/DataTable.svelte.d.ts +12 -0
- package/dist/components/DataTable/README.md +108 -33
- package/dist/components/DataTable/i18n-sk.js +1 -0
- package/dist/components/DataTable/i18n.d.ts +1 -0
- package/dist/components/DataTable/i18n.js +3 -0
- package/dist/components/DataTable/index.css +41 -3
- package/dist/components/EmptyState/EmptyState.svelte +121 -0
- package/dist/components/EmptyState/EmptyState.svelte.d.ts +40 -0
- package/dist/components/EmptyState/README.md +188 -0
- package/dist/components/EmptyState/index.css +154 -0
- package/dist/components/EmptyState/index.d.ts +1 -0
- package/dist/components/EmptyState/index.js +1 -0
- package/dist/components/Pagination/Pagination.svelte +250 -0
- package/dist/components/Pagination/Pagination.svelte.d.ts +69 -0
- package/dist/components/Pagination/README.md +152 -0
- package/dist/components/Pagination/i18n-sk.d.ts +17 -0
- package/dist/components/Pagination/i18n-sk.js +24 -0
- package/dist/components/Pagination/i18n.d.ts +41 -0
- package/dist/components/Pagination/i18n.js +49 -0
- package/dist/components/Pagination/index.css +50 -0
- package/dist/components/Pagination/index.d.ts +4 -0
- package/dist/components/Pagination/index.js +4 -0
- package/dist/components/Pagination/pagination-range.d.ts +21 -0
- package/dist/components/Pagination/pagination-range.js +41 -0
- package/dist/components/Stat/README.md +157 -0
- package/dist/components/Stat/Stat.svelte +226 -0
- package/dist/components/Stat/Stat.svelte.d.ts +54 -0
- package/dist/components/Stat/index.css +217 -0
- package/dist/components/Stat/index.d.ts +1 -0
- package/dist/components/Stat/index.js +1 -0
- package/dist/components/Stepper/README.md +181 -0
- package/dist/components/Stepper/Stepper.svelte +240 -0
- package/dist/components/Stepper/Stepper.svelte.d.ts +86 -0
- package/dist/components/Stepper/i18n-sk.d.ts +17 -0
- package/dist/components/Stepper/i18n-sk.js +21 -0
- package/dist/components/Stepper/i18n.d.ts +35 -0
- package/dist/components/Stepper/i18n.js +43 -0
- package/dist/components/Stepper/index.css +302 -0
- package/dist/components/Stepper/index.d.ts +3 -0
- package/dist/components/Stepper/index.js +3 -0
- package/dist/components/TrendChart/README.md +123 -0
- package/dist/components/TrendChart/TrendChart.svelte +114 -0
- package/dist/components/TrendChart/TrendChart.svelte.d.ts +36 -0
- package/dist/components/TrendChart/index.css +51 -0
- package/dist/components/TrendChart/index.d.ts +1 -0
- package/dist/components/TrendChart/index.js +1 -0
- package/dist/icons/index.d.ts +4 -0
- package/dist/icons/index.js +4 -0
- package/dist/index.css +7 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +12 -0
- package/dist/utils/copy-to-clipboard.d.ts +43 -0
- package/dist/utils/copy-to-clipboard.js +102 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +1 -0
- package/docs/architecture.md +1 -1
- package/docs/domains/attachments.md +41 -4
- package/docs/domains/components.md +280 -13
- package/docs/domains/utils.md +8 -6
- package/docs/maybe-todo.md +111 -0
- package/package.json +15 -5
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
3
|
+
import type { Snippet } from "svelte";
|
|
4
|
+
import type { PagingCalcResult } from "@marianmeres/paging-store";
|
|
5
|
+
import type { TranslateFn } from "../../types.js";
|
|
6
|
+
import type { ButtonSize } from "../Button/Button.svelte";
|
|
7
|
+
|
|
8
|
+
export type PaginationVariant = "compact" | "numbers";
|
|
9
|
+
|
|
10
|
+
export interface Props extends Omit<HTMLAttributes<HTMLElement>, "children"> {
|
|
11
|
+
/**
|
|
12
|
+
* Paging calculation result from `@marianmeres/paging-store` — the exact shape
|
|
13
|
+
* `DataTable` consumes, so both can be fed from the same store or `calculatePaging`
|
|
14
|
+
* call. Takes precedence over the `total`/`limit`/`offset` convenience props.
|
|
15
|
+
*/
|
|
16
|
+
paging?: PagingCalcResult;
|
|
17
|
+
/** Convenience alternative to `paging`: total number of items in the dataset. */
|
|
18
|
+
total?: number;
|
|
19
|
+
/** Convenience alternative to `paging`: items per page (default 10). */
|
|
20
|
+
limit?: number;
|
|
21
|
+
/** Convenience alternative to `paging`: current offset (default 0). */
|
|
22
|
+
offset?: number;
|
|
23
|
+
/**
|
|
24
|
+
* Callback when the user navigates to a different page. First argument is the new
|
|
25
|
+
* offset (`DataTable`'s `onPageChange` signature), second is the 1-based target page.
|
|
26
|
+
*/
|
|
27
|
+
onPageChange?: (offset: number, page: number) => void;
|
|
28
|
+
/**
|
|
29
|
+
* `"compact"` (default): prev / "Page X of Y" / next — the `DataTable` pager.
|
|
30
|
+
* `"numbers"`: windowed page-number buttons with ellipsis gaps.
|
|
31
|
+
*/
|
|
32
|
+
variant?: PaginationVariant;
|
|
33
|
+
/** Numbers variant: pages always shown around the current page (default 1). */
|
|
34
|
+
siblingCount?: number;
|
|
35
|
+
/** Numbers variant: pages always shown at the start and at the end (default 1). */
|
|
36
|
+
boundaryCount?: number;
|
|
37
|
+
/** Render first/last jump buttons (default false). */
|
|
38
|
+
showFirstLast?: boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Render the "Page X of Y" info. Defaults per variant: `true` for compact (between
|
|
41
|
+
* prev/next), `false` for numbers (after the buttons when enabled).
|
|
42
|
+
*/
|
|
43
|
+
showInfo?: boolean;
|
|
44
|
+
/**
|
|
45
|
+
* Render nothing when there is at most one page (default true — the `DataTable`
|
|
46
|
+
* rule: one page of results needs no pager). Set `false` to keep it visible.
|
|
47
|
+
*/
|
|
48
|
+
hideSinglePage?: boolean;
|
|
49
|
+
/** Disable all controls (e.g. while a page of data is loading). */
|
|
50
|
+
disabled?: boolean;
|
|
51
|
+
/** Size preset passed to the inner Buttons (default "sm", as in `DataTable`). */
|
|
52
|
+
size?: ButtonSize;
|
|
53
|
+
/** i18n translate function (see `createPaginationT`). */
|
|
54
|
+
t?: TranslateFn;
|
|
55
|
+
/** Override the "Page X of Y" info content. */
|
|
56
|
+
renderInfo?: Snippet<[PagingCalcResult]>;
|
|
57
|
+
/** Skip all default styling (inner Buttons keep their own Button styling) */
|
|
58
|
+
unstyled?: boolean;
|
|
59
|
+
/** Additional CSS classes */
|
|
60
|
+
class?: string;
|
|
61
|
+
/** Class for every inner button */
|
|
62
|
+
classButton?: string;
|
|
63
|
+
/** Extra class for the current-page button (numbers variant) */
|
|
64
|
+
classButtonCurrent?: string;
|
|
65
|
+
/** Class for the "Page X of Y" info */
|
|
66
|
+
classInfo?: string;
|
|
67
|
+
/** Bindable element reference */
|
|
68
|
+
el?: HTMLElement;
|
|
69
|
+
}
|
|
70
|
+
</script>
|
|
71
|
+
|
|
72
|
+
<script lang="ts">
|
|
73
|
+
import { calculatePaging } from "@marianmeres/paging-store";
|
|
74
|
+
import { twMerge } from "../../utils/tw-merge.js";
|
|
75
|
+
import Button from "../Button/Button.svelte";
|
|
76
|
+
import { t_default } from "./i18n.js";
|
|
77
|
+
import { paginationRange } from "./pagination-range.js";
|
|
78
|
+
|
|
79
|
+
let {
|
|
80
|
+
paging,
|
|
81
|
+
total,
|
|
82
|
+
limit,
|
|
83
|
+
offset,
|
|
84
|
+
onPageChange,
|
|
85
|
+
variant = "compact",
|
|
86
|
+
siblingCount = 1,
|
|
87
|
+
boundaryCount = 1,
|
|
88
|
+
showFirstLast = false,
|
|
89
|
+
showInfo,
|
|
90
|
+
hideSinglePage = true,
|
|
91
|
+
disabled = false,
|
|
92
|
+
size = "sm",
|
|
93
|
+
t = t_default,
|
|
94
|
+
renderInfo,
|
|
95
|
+
unstyled = false,
|
|
96
|
+
class: classProp,
|
|
97
|
+
classButton: classButtonProp,
|
|
98
|
+
classButtonCurrent: classButtonCurrentProp,
|
|
99
|
+
classInfo: classInfoProp,
|
|
100
|
+
el = $bindable(),
|
|
101
|
+
...rest
|
|
102
|
+
}: Props = $props();
|
|
103
|
+
|
|
104
|
+
let _paging: PagingCalcResult | undefined = $derived(
|
|
105
|
+
paging ??
|
|
106
|
+
(total != null || limit != null || offset != null
|
|
107
|
+
? calculatePaging({ total, limit, offset })
|
|
108
|
+
: undefined)
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
let _render = $derived(!!_paging && (_paging.pageCount > 1 || !hideSinglePage));
|
|
112
|
+
let _showInfo = $derived(showInfo ?? variant === "compact");
|
|
113
|
+
|
|
114
|
+
let _pages = $derived(
|
|
115
|
+
variant === "numbers" && _paging
|
|
116
|
+
? paginationRange(_paging.currentPage, _paging.pageCount, {
|
|
117
|
+
siblingCount,
|
|
118
|
+
boundaryCount,
|
|
119
|
+
})
|
|
120
|
+
: []
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
let _class = $derived(unstyled ? classProp : twMerge("stuic-pagination", classProp));
|
|
124
|
+
let _classButton = $derived(
|
|
125
|
+
unstyled ? classButtonProp : twMerge("stuic-pagination-button", classButtonProp)
|
|
126
|
+
);
|
|
127
|
+
let _classInfo = $derived(
|
|
128
|
+
unstyled ? classInfoProp : twMerge("stuic-pagination-info", classInfoProp)
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
function goToPage(page: number) {
|
|
132
|
+
if (!_paging || disabled) return;
|
|
133
|
+
if (page < 1 || page > _paging.pageCount || page === _paging.currentPage) return;
|
|
134
|
+
onPageChange?.((page - 1) * _paging.limit, page);
|
|
135
|
+
}
|
|
136
|
+
</script>
|
|
137
|
+
|
|
138
|
+
{#if _paging && _render}
|
|
139
|
+
<nav
|
|
140
|
+
bind:this={el}
|
|
141
|
+
class={_class}
|
|
142
|
+
data-variant={!unstyled ? variant : undefined}
|
|
143
|
+
aria-label={t("pagination", null, "Pagination")}
|
|
144
|
+
{...rest}
|
|
145
|
+
>
|
|
146
|
+
{#if showFirstLast}
|
|
147
|
+
<Button
|
|
148
|
+
variant="ghost"
|
|
149
|
+
{size}
|
|
150
|
+
class={_classButton}
|
|
151
|
+
disabled={disabled || !_paging.hasPrevious}
|
|
152
|
+
onclick={() => goToPage(1)}
|
|
153
|
+
aria-label={t("first_page")}
|
|
154
|
+
>
|
|
155
|
+
«
|
|
156
|
+
</Button>
|
|
157
|
+
{/if}
|
|
158
|
+
|
|
159
|
+
<Button
|
|
160
|
+
variant="ghost"
|
|
161
|
+
{size}
|
|
162
|
+
class={_classButton}
|
|
163
|
+
disabled={disabled || !_paging.hasPrevious}
|
|
164
|
+
onclick={() => goToPage(_paging!.currentPage - 1)}
|
|
165
|
+
aria-label={t("previous_page")}
|
|
166
|
+
>
|
|
167
|
+
{#if variant === "compact"}
|
|
168
|
+
‹ {t("previous_page")}
|
|
169
|
+
{:else}
|
|
170
|
+
‹
|
|
171
|
+
{/if}
|
|
172
|
+
</Button>
|
|
173
|
+
|
|
174
|
+
{#if variant === "numbers"}
|
|
175
|
+
{#each _pages as item, i (item === "ellipsis" ? `ellipsis-${i}` : item)}
|
|
176
|
+
{#if item === "ellipsis"}
|
|
177
|
+
<span
|
|
178
|
+
class={!unstyled ? "stuic-pagination-ellipsis" : undefined}
|
|
179
|
+
aria-hidden="true"
|
|
180
|
+
>
|
|
181
|
+
…
|
|
182
|
+
</span>
|
|
183
|
+
{:else}
|
|
184
|
+
{@const isCurrent = item === _paging.currentPage}
|
|
185
|
+
<Button
|
|
186
|
+
variant={isCurrent ? "solid" : "ghost"}
|
|
187
|
+
intent={isCurrent ? "primary" : undefined}
|
|
188
|
+
{size}
|
|
189
|
+
class={isCurrent
|
|
190
|
+
? twMerge(_classButton, classButtonCurrentProp)
|
|
191
|
+
: _classButton}
|
|
192
|
+
{disabled}
|
|
193
|
+
aria-label={t("go_to_page_x", { page: item })}
|
|
194
|
+
aria-current={isCurrent ? "page" : undefined}
|
|
195
|
+
data-current={isCurrent ? "true" : undefined}
|
|
196
|
+
onclick={() => goToPage(item)}
|
|
197
|
+
>
|
|
198
|
+
{item}
|
|
199
|
+
</Button>
|
|
200
|
+
{/if}
|
|
201
|
+
{/each}
|
|
202
|
+
{:else if _showInfo}
|
|
203
|
+
<span class={_classInfo}>
|
|
204
|
+
{#if renderInfo}
|
|
205
|
+
{@render renderInfo(_paging)}
|
|
206
|
+
{:else}
|
|
207
|
+
{t("page_x_of_y", { page: _paging.currentPage, pageCount: _paging.pageCount })}
|
|
208
|
+
{/if}
|
|
209
|
+
</span>
|
|
210
|
+
{/if}
|
|
211
|
+
|
|
212
|
+
<Button
|
|
213
|
+
variant="ghost"
|
|
214
|
+
{size}
|
|
215
|
+
class={_classButton}
|
|
216
|
+
disabled={disabled || !_paging.hasNext}
|
|
217
|
+
onclick={() => goToPage(_paging!.currentPage + 1)}
|
|
218
|
+
aria-label={t("next_page")}
|
|
219
|
+
>
|
|
220
|
+
{#if variant === "compact"}
|
|
221
|
+
{t("next_page")} ›
|
|
222
|
+
{:else}
|
|
223
|
+
›
|
|
224
|
+
{/if}
|
|
225
|
+
</Button>
|
|
226
|
+
|
|
227
|
+
{#if showFirstLast}
|
|
228
|
+
<Button
|
|
229
|
+
variant="ghost"
|
|
230
|
+
{size}
|
|
231
|
+
class={_classButton}
|
|
232
|
+
disabled={disabled || !_paging.hasNext}
|
|
233
|
+
onclick={() => goToPage(_paging!.pageCount)}
|
|
234
|
+
aria-label={t("last_page")}
|
|
235
|
+
>
|
|
236
|
+
»
|
|
237
|
+
</Button>
|
|
238
|
+
{/if}
|
|
239
|
+
|
|
240
|
+
{#if variant === "numbers" && _showInfo}
|
|
241
|
+
<span class={_classInfo}>
|
|
242
|
+
{#if renderInfo}
|
|
243
|
+
{@render renderInfo(_paging)}
|
|
244
|
+
{:else}
|
|
245
|
+
{t("page_x_of_y", { page: _paging.currentPage, pageCount: _paging.pageCount })}
|
|
246
|
+
{/if}
|
|
247
|
+
</span>
|
|
248
|
+
{/if}
|
|
249
|
+
</nav>
|
|
250
|
+
{/if}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
2
|
+
import type { Snippet } from "svelte";
|
|
3
|
+
import type { PagingCalcResult } from "@marianmeres/paging-store";
|
|
4
|
+
import type { TranslateFn } from "../../types.js";
|
|
5
|
+
import type { ButtonSize } from "../Button/Button.svelte";
|
|
6
|
+
export type PaginationVariant = "compact" | "numbers";
|
|
7
|
+
export interface Props extends Omit<HTMLAttributes<HTMLElement>, "children"> {
|
|
8
|
+
/**
|
|
9
|
+
* Paging calculation result from `@marianmeres/paging-store` — the exact shape
|
|
10
|
+
* `DataTable` consumes, so both can be fed from the same store or `calculatePaging`
|
|
11
|
+
* call. Takes precedence over the `total`/`limit`/`offset` convenience props.
|
|
12
|
+
*/
|
|
13
|
+
paging?: PagingCalcResult;
|
|
14
|
+
/** Convenience alternative to `paging`: total number of items in the dataset. */
|
|
15
|
+
total?: number;
|
|
16
|
+
/** Convenience alternative to `paging`: items per page (default 10). */
|
|
17
|
+
limit?: number;
|
|
18
|
+
/** Convenience alternative to `paging`: current offset (default 0). */
|
|
19
|
+
offset?: number;
|
|
20
|
+
/**
|
|
21
|
+
* Callback when the user navigates to a different page. First argument is the new
|
|
22
|
+
* offset (`DataTable`'s `onPageChange` signature), second is the 1-based target page.
|
|
23
|
+
*/
|
|
24
|
+
onPageChange?: (offset: number, page: number) => void;
|
|
25
|
+
/**
|
|
26
|
+
* `"compact"` (default): prev / "Page X of Y" / next — the `DataTable` pager.
|
|
27
|
+
* `"numbers"`: windowed page-number buttons with ellipsis gaps.
|
|
28
|
+
*/
|
|
29
|
+
variant?: PaginationVariant;
|
|
30
|
+
/** Numbers variant: pages always shown around the current page (default 1). */
|
|
31
|
+
siblingCount?: number;
|
|
32
|
+
/** Numbers variant: pages always shown at the start and at the end (default 1). */
|
|
33
|
+
boundaryCount?: number;
|
|
34
|
+
/** Render first/last jump buttons (default false). */
|
|
35
|
+
showFirstLast?: boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Render the "Page X of Y" info. Defaults per variant: `true` for compact (between
|
|
38
|
+
* prev/next), `false` for numbers (after the buttons when enabled).
|
|
39
|
+
*/
|
|
40
|
+
showInfo?: boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Render nothing when there is at most one page (default true — the `DataTable`
|
|
43
|
+
* rule: one page of results needs no pager). Set `false` to keep it visible.
|
|
44
|
+
*/
|
|
45
|
+
hideSinglePage?: boolean;
|
|
46
|
+
/** Disable all controls (e.g. while a page of data is loading). */
|
|
47
|
+
disabled?: boolean;
|
|
48
|
+
/** Size preset passed to the inner Buttons (default "sm", as in `DataTable`). */
|
|
49
|
+
size?: ButtonSize;
|
|
50
|
+
/** i18n translate function (see `createPaginationT`). */
|
|
51
|
+
t?: TranslateFn;
|
|
52
|
+
/** Override the "Page X of Y" info content. */
|
|
53
|
+
renderInfo?: Snippet<[PagingCalcResult]>;
|
|
54
|
+
/** Skip all default styling (inner Buttons keep their own Button styling) */
|
|
55
|
+
unstyled?: boolean;
|
|
56
|
+
/** Additional CSS classes */
|
|
57
|
+
class?: string;
|
|
58
|
+
/** Class for every inner button */
|
|
59
|
+
classButton?: string;
|
|
60
|
+
/** Extra class for the current-page button (numbers variant) */
|
|
61
|
+
classButtonCurrent?: string;
|
|
62
|
+
/** Class for the "Page X of Y" info */
|
|
63
|
+
classInfo?: string;
|
|
64
|
+
/** Bindable element reference */
|
|
65
|
+
el?: HTMLElement;
|
|
66
|
+
}
|
|
67
|
+
declare const Pagination: import("svelte").Component<Props, {}, "el">;
|
|
68
|
+
type Pagination = ReturnType<typeof Pagination>;
|
|
69
|
+
export default Pagination;
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# Pagination
|
|
2
|
+
|
|
3
|
+
Standalone pagination control for lists, search results, and custom views. Two
|
|
4
|
+
variants: `compact` — prev / "Page X of Y" / next, which _is_ the pager `DataTable`
|
|
5
|
+
renders internally — and `numbers` — windowed page-number buttons with ellipsis gaps
|
|
6
|
+
(the classic `1 … 4 5 6 … 20`).
|
|
7
|
+
|
|
8
|
+
Consumes the same `PagingCalcResult` from
|
|
9
|
+
[`@marianmeres/paging-store`](https://github.com/marianmeres/paging-store) that
|
|
10
|
+
`DataTable` does, so one paging store (or one `calculatePaging` call) can feed both.
|
|
11
|
+
For quick standalone use, `total`/`limit`/`offset` props compute the paging
|
|
12
|
+
internally.
|
|
13
|
+
|
|
14
|
+
## Props
|
|
15
|
+
|
|
16
|
+
| Prop | Type | Default | Description |
|
|
17
|
+
| -------------------- | ------------------------ | ----------- | ------------------------------------------------------------------------------------- |
|
|
18
|
+
| `paging` | `PagingCalcResult` | - | Paging metadata (same shape `DataTable` consumes); wins over `total`/`limit`/`offset` |
|
|
19
|
+
| `total` | `number` | - | Convenience alternative to `paging`: total item count |
|
|
20
|
+
| `limit` | `number` | `10` | Convenience alternative to `paging`: page size |
|
|
21
|
+
| `offset` | `number` | `0` | Convenience alternative to `paging`: current offset |
|
|
22
|
+
| `onPageChange` | `(offset, page) => void` | - | Navigation callback: new offset (`DataTable`-compatible first arg) + 1-based page |
|
|
23
|
+
| `variant` | `"compact" \| "numbers"` | `"compact"` | Prev/info/next, or windowed page-number buttons |
|
|
24
|
+
| `siblingCount` | `number` | `1` | Numbers variant: pages always shown around the current page |
|
|
25
|
+
| `boundaryCount` | `number` | `1` | Numbers variant: pages always shown at the start and end |
|
|
26
|
+
| `showFirstLast` | `boolean` | `false` | Render first/last jump buttons (« ») |
|
|
27
|
+
| `showInfo` | `boolean` | per variant | "Page X of Y" info; defaults `true` for compact, `false` for numbers |
|
|
28
|
+
| `hideSinglePage` | `boolean` | `true` | Render nothing when there is at most one page (the `DataTable` rule) |
|
|
29
|
+
| `disabled` | `boolean` | `false` | Disable all controls (e.g. while loading) |
|
|
30
|
+
| `size` | `ButtonSize` | `"sm"` | Size preset of the inner Buttons |
|
|
31
|
+
| `t` | `TranslateFn` | English | i18n translate function (see below) |
|
|
32
|
+
| `unstyled` | `boolean` | `false` | Skip pagination styling (inner Buttons keep their own Button styling) |
|
|
33
|
+
| `class` | `string` | - | Additional CSS classes (merged via twMerge) |
|
|
34
|
+
| `classButton` | `string` | - | Class for every inner button |
|
|
35
|
+
| `classButtonCurrent` | `string` | - | Extra class for the current-page button (numbers variant) |
|
|
36
|
+
| `classInfo` | `string` | - | Class for the "Page X of Y" info |
|
|
37
|
+
| `el` | `HTMLElement` | - | Element reference (bindable) |
|
|
38
|
+
|
|
39
|
+
## Snippet Props
|
|
40
|
+
|
|
41
|
+
| Snippet | Description |
|
|
42
|
+
| ------------ | ------------------------------------------------------------------------ |
|
|
43
|
+
| `renderInfo` | Override the "Page X of Y" info content; receives the `PagingCalcResult` |
|
|
44
|
+
|
|
45
|
+
## Usage
|
|
46
|
+
|
|
47
|
+
### Basic (compact)
|
|
48
|
+
|
|
49
|
+
```svelte
|
|
50
|
+
<script lang="ts">
|
|
51
|
+
import { Pagination } from "@marianmeres/stuic";
|
|
52
|
+
|
|
53
|
+
let offset = $state(0);
|
|
54
|
+
</script>
|
|
55
|
+
|
|
56
|
+
<Pagination total={137} limit={10} {offset} onPageChange={(o) => (offset = o)} />
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### With a paging store (shared with DataTable)
|
|
60
|
+
|
|
61
|
+
The `paging` prop takes the exact object `DataTable` consumes — hide the table's
|
|
62
|
+
built-in pager and drive both from one store:
|
|
63
|
+
|
|
64
|
+
```svelte
|
|
65
|
+
<script lang="ts">
|
|
66
|
+
import { DataTable, Pagination } from "@marianmeres/stuic";
|
|
67
|
+
import { createPagingStore } from "@marianmeres/paging-store";
|
|
68
|
+
|
|
69
|
+
const paging = createPagingStore({ total: 137, limit: 10, offset: 0 });
|
|
70
|
+
const onPageChange = (offset: number) => paging.update({ offset });
|
|
71
|
+
</script>
|
|
72
|
+
|
|
73
|
+
<DataTable {columns} {data} paging={$paging} showPager={false} />
|
|
74
|
+
<Pagination paging={$paging} {onPageChange} variant="numbers" />
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Numbers variant
|
|
78
|
+
|
|
79
|
+
```svelte
|
|
80
|
+
<Pagination variant="numbers" total={200} {offset} onPageChange={(o) => (offset = o)} />
|
|
81
|
+
|
|
82
|
+
<!-- wider window, first/last jumps -->
|
|
83
|
+
<Pagination
|
|
84
|
+
variant="numbers"
|
|
85
|
+
siblingCount={2}
|
|
86
|
+
boundaryCount={2}
|
|
87
|
+
showFirstLast
|
|
88
|
+
total={200}
|
|
89
|
+
{offset}
|
|
90
|
+
onPageChange={(o) => (offset = o)}
|
|
91
|
+
/>
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### i18n
|
|
95
|
+
|
|
96
|
+
Built-in English; bundled Slovak (`PAGINATION_MESSAGES_SK`) is opt-in. The
|
|
97
|
+
`previous_page`, `next_page` and `page_x_of_y` keys are identical to `DataTable`'s,
|
|
98
|
+
so one merged catalog (and one `t`) can serve both components.
|
|
99
|
+
|
|
100
|
+
```svelte
|
|
101
|
+
<script lang="ts">
|
|
102
|
+
import {
|
|
103
|
+
Pagination,
|
|
104
|
+
createPaginationT,
|
|
105
|
+
PAGINATION_MESSAGES_SK,
|
|
106
|
+
} from "@marianmeres/stuic";
|
|
107
|
+
const t = createPaginationT(PAGINATION_MESSAGES_SK);
|
|
108
|
+
</script>
|
|
109
|
+
|
|
110
|
+
<Pagination {paging} {onPageChange} {t} />
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Custom info
|
|
114
|
+
|
|
115
|
+
```svelte
|
|
116
|
+
<Pagination {paging} {onPageChange}>
|
|
117
|
+
{#snippet renderInfo(p)}
|
|
118
|
+
{p.offset + 1}–{Math.min(p.offset + p.limit, p.total)} of {p.total}
|
|
119
|
+
{/snippet}
|
|
120
|
+
</Pagination>
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Single page
|
|
124
|
+
|
|
125
|
+
By default nothing renders when there is at most one page (same rule as
|
|
126
|
+
`DataTable`'s internal pager). Keep it visible with `hideSinglePage={false}`.
|
|
127
|
+
|
|
128
|
+
## Accessibility
|
|
129
|
+
|
|
130
|
+
- Renders a `<nav aria-label="Pagination">` landmark (label localized via `t`).
|
|
131
|
+
- Every button carries a localized `aria-label`; the current page button (numbers
|
|
132
|
+
variant) is marked `aria-current="page"`.
|
|
133
|
+
|
|
134
|
+
## Exports
|
|
135
|
+
|
|
136
|
+
Besides the component: `paginationRange(currentPage, pageCount, { siblingCount, boundaryCount })`
|
|
137
|
+
— the pure windowing helper behind the numbers variant, usable for fully custom pagers.
|
|
138
|
+
|
|
139
|
+
## CSS Variables
|
|
140
|
+
|
|
141
|
+
| Variable | Default | Description |
|
|
142
|
+
| ------------------------------------- | -------------------------------- | -------------------------------------------------- |
|
|
143
|
+
| `--stuic-pagination-gap` | `0.5rem` | Gap between controls |
|
|
144
|
+
| `--stuic-pagination-button-min-width` | `--stuic-button-min-height-sm` | Min button width (keeps number buttons square-ish) |
|
|
145
|
+
| `--stuic-pagination-info-font-size` | `--text-sm` | Info font size |
|
|
146
|
+
| `--stuic-pagination-info-text` | `--stuic-color-muted-foreground` | Info color |
|
|
147
|
+
| `--stuic-pagination-ellipsis-text` | `--stuic-color-muted-foreground` | Ellipsis color |
|
|
148
|
+
|
|
149
|
+
## Data Attributes
|
|
150
|
+
|
|
151
|
+
- `data-variant` - `"compact" | "numbers"` (on the root `<nav>`)
|
|
152
|
+
- `data-current` - `"true"` on the current-page button (numbers variant)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { PaginationMessages } from "./i18n.js";
|
|
2
|
+
/**
|
|
3
|
+
* Slovak message catalog for `Pagination`. Opt-in — English stays the built-in default,
|
|
4
|
+
* and this module is only pulled into a bundle when it is actually imported (the
|
|
5
|
+
* component itself never references it).
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```svelte
|
|
9
|
+
* <script>
|
|
10
|
+
* import { Pagination, createPaginationT, PAGINATION_MESSAGES_SK } from "@marianmeres/stuic";
|
|
11
|
+
* const t = createPaginationT(PAGINATION_MESSAGES_SK);
|
|
12
|
+
* </script>
|
|
13
|
+
*
|
|
14
|
+
* <Pagination {paging} {onPageChange} {t} />
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
export declare const PAGINATION_MESSAGES_SK: PaginationMessages;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slovak message catalog for `Pagination`. Opt-in — English stays the built-in default,
|
|
3
|
+
* and this module is only pulled into a bundle when it is actually imported (the
|
|
4
|
+
* component itself never references it).
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```svelte
|
|
8
|
+
* <script>
|
|
9
|
+
* import { Pagination, createPaginationT, PAGINATION_MESSAGES_SK } from "@marianmeres/stuic";
|
|
10
|
+
* const t = createPaginationT(PAGINATION_MESSAGES_SK);
|
|
11
|
+
* </script>
|
|
12
|
+
*
|
|
13
|
+
* <Pagination {paging} {onPageChange} {t} />
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
export const PAGINATION_MESSAGES_SK = {
|
|
17
|
+
previous_page: "Späť",
|
|
18
|
+
next_page: "Ďalej",
|
|
19
|
+
first_page: "Prvá strana",
|
|
20
|
+
last_page: "Posledná strana",
|
|
21
|
+
page_x_of_y: "Strana {page} z {pageCount}",
|
|
22
|
+
go_to_page_x: "Prejsť na stranu {page}",
|
|
23
|
+
pagination: "Stránkovanie",
|
|
24
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { TranslateFn } from "../../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* The built-in (English) message catalog of `Pagination`. Also the fallback of every
|
|
4
|
+
* other bundled locale, so a locale missing a key still renders text.
|
|
5
|
+
*
|
|
6
|
+
* The `previous_page`, `next_page` and `page_x_of_y` keys are intentionally identical
|
|
7
|
+
* to `DataTable`'s, so one merged catalog (and one `t`) can serve both components.
|
|
8
|
+
*
|
|
9
|
+
* Placeholders are single-brace (`{page}`, `{pageCount}`, ...).
|
|
10
|
+
*/
|
|
11
|
+
export declare const PAGINATION_MESSAGES_EN: {
|
|
12
|
+
previous_page: string;
|
|
13
|
+
next_page: string;
|
|
14
|
+
first_page: string;
|
|
15
|
+
last_page: string;
|
|
16
|
+
page_x_of_y: string;
|
|
17
|
+
go_to_page_x: string;
|
|
18
|
+
pagination: string;
|
|
19
|
+
};
|
|
20
|
+
/** Every message key `Pagination` may look up. */
|
|
21
|
+
export type PaginationMessageKey = keyof typeof PAGINATION_MESSAGES_EN;
|
|
22
|
+
/** A (possibly partial) catalog for one locale. */
|
|
23
|
+
export type PaginationMessages = Record<PaginationMessageKey, string>;
|
|
24
|
+
/**
|
|
25
|
+
* Builds the `t` prop of `Pagination` from a message catalog. Unknown or untranslated
|
|
26
|
+
* keys fall back to `fallbackMessages` (English by default), so a catalog may safely be
|
|
27
|
+
* partial and never renders a raw key.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```svelte
|
|
31
|
+
* <script>
|
|
32
|
+
* import { Pagination, createPaginationT, PAGINATION_MESSAGES_SK } from "@marianmeres/stuic";
|
|
33
|
+
* const t = createPaginationT(PAGINATION_MESSAGES_SK);
|
|
34
|
+
* </script>
|
|
35
|
+
*
|
|
36
|
+
* <Pagination {paging} {onPageChange} {t} />
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export declare function createPaginationT(messages: Partial<PaginationMessages> | Record<string, string>, fallbackMessages?: Partial<PaginationMessages> | Record<string, string>): TranslateFn;
|
|
40
|
+
/** The component's built-in English `t`. */
|
|
41
|
+
export declare const t_default: TranslateFn;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { isPlainObject } from "../../utils/is-plain-object.js";
|
|
2
|
+
import { replaceMap } from "../../utils/replace-map.js";
|
|
3
|
+
/**
|
|
4
|
+
* The built-in (English) message catalog of `Pagination`. Also the fallback of every
|
|
5
|
+
* other bundled locale, so a locale missing a key still renders text.
|
|
6
|
+
*
|
|
7
|
+
* The `previous_page`, `next_page` and `page_x_of_y` keys are intentionally identical
|
|
8
|
+
* to `DataTable`'s, so one merged catalog (and one `t`) can serve both components.
|
|
9
|
+
*
|
|
10
|
+
* Placeholders are single-brace (`{page}`, `{pageCount}`, ...).
|
|
11
|
+
*/
|
|
12
|
+
export const PAGINATION_MESSAGES_EN = {
|
|
13
|
+
previous_page: "Prev",
|
|
14
|
+
next_page: "Next",
|
|
15
|
+
first_page: "First page",
|
|
16
|
+
last_page: "Last page",
|
|
17
|
+
page_x_of_y: "Page {page} of {pageCount}",
|
|
18
|
+
go_to_page_x: "Go to page {page}",
|
|
19
|
+
pagination: "Pagination",
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Builds the `t` prop of `Pagination` from a message catalog. Unknown or untranslated
|
|
23
|
+
* keys fall back to `fallbackMessages` (English by default), so a catalog may safely be
|
|
24
|
+
* partial and never renders a raw key.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```svelte
|
|
28
|
+
* <script>
|
|
29
|
+
* import { Pagination, createPaginationT, PAGINATION_MESSAGES_SK } from "@marianmeres/stuic";
|
|
30
|
+
* const t = createPaginationT(PAGINATION_MESSAGES_SK);
|
|
31
|
+
* </script>
|
|
32
|
+
*
|
|
33
|
+
* <Pagination {paging} {onPageChange} {t} />
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export function createPaginationT(messages, fallbackMessages = PAGINATION_MESSAGES_EN) {
|
|
37
|
+
return (k, values = null, fallback = "") => {
|
|
38
|
+
const out = messages[k] ??
|
|
39
|
+
fallbackMessages[k] ??
|
|
40
|
+
(typeof fallback === "string" ? fallback : k);
|
|
41
|
+
return isPlainObject(values)
|
|
42
|
+
? replaceMap(out, values, {
|
|
43
|
+
preSearchKeyTransform: (k) => `{${k}}`,
|
|
44
|
+
})
|
|
45
|
+
: out;
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
/** The component's built-in English `t`. */
|
|
49
|
+
export const t_default = createPaginationT(PAGINATION_MESSAGES_EN);
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/* ============================================================================
|
|
2
|
+
PAGINATION COMPONENT TOKENS
|
|
3
|
+
Override globally: :root { --stuic-pagination-gap: 1rem; }
|
|
4
|
+
Override locally: <Pagination style="--stuic-pagination-button-min-width: 2rem;">
|
|
5
|
+
============================================================================ */
|
|
6
|
+
|
|
7
|
+
/* prettier-ignore */
|
|
8
|
+
:root {
|
|
9
|
+
--stuic-pagination-gap: 0.5rem;
|
|
10
|
+
|
|
11
|
+
/* Keeps single-character buttons («, ‹, page numbers) square-ish and aligned.
|
|
12
|
+
Sized to the `size="sm"` Button height. Text buttons (Prev/Next) simply exceed it. */
|
|
13
|
+
--stuic-pagination-button-min-width: var(--stuic-button-min-height-sm, 2.75rem);
|
|
14
|
+
|
|
15
|
+
/* "Page X of Y" info */
|
|
16
|
+
--stuic-pagination-info-font-size: var(--text-sm);
|
|
17
|
+
--stuic-pagination-info-text: var(--stuic-color-muted-foreground);
|
|
18
|
+
|
|
19
|
+
/* Ellipsis gap (numbers variant) */
|
|
20
|
+
--stuic-pagination-ellipsis-text: var(--stuic-color-muted-foreground);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
@layer components {
|
|
24
|
+
.stuic-pagination {
|
|
25
|
+
display: flex;
|
|
26
|
+
align-items: center;
|
|
27
|
+
justify-content: center;
|
|
28
|
+
flex-wrap: wrap;
|
|
29
|
+
gap: var(--stuic-pagination-gap);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.stuic-pagination-button {
|
|
33
|
+
min-width: var(--stuic-pagination-button-min-width);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.stuic-pagination-info {
|
|
37
|
+
font-size: var(--stuic-pagination-info-font-size);
|
|
38
|
+
color: var(--stuic-pagination-info-text);
|
|
39
|
+
white-space: nowrap;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.stuic-pagination-ellipsis {
|
|
43
|
+
display: inline-flex;
|
|
44
|
+
align-items: center;
|
|
45
|
+
justify-content: center;
|
|
46
|
+
min-width: var(--stuic-pagination-button-min-width);
|
|
47
|
+
color: var(--stuic-pagination-ellipsis-text);
|
|
48
|
+
user-select: none;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { default as Pagination, type Props as PaginationProps, type PaginationVariant, } from "./Pagination.svelte";
|
|
2
|
+
export { paginationRange, type PaginationRangeItem, type PaginationRangeOptions, } from "./pagination-range.js";
|
|
3
|
+
export { createPaginationT, PAGINATION_MESSAGES_EN, type PaginationMessageKey, type PaginationMessages, } from "./i18n.js";
|
|
4
|
+
export { PAGINATION_MESSAGES_SK } from "./i18n-sk.js";
|