@marianmeres/stuic 3.167.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/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 +41 -34
- package/dist/components/DataTable/README.md +45 -0
- 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 +11 -1
|
@@ -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";
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/** One rendered slot of the "numbers" variant: a page number or an ellipsis gap. */
|
|
2
|
+
export type PaginationRangeItem = number | "ellipsis";
|
|
3
|
+
export interface PaginationRangeOptions {
|
|
4
|
+
/** How many pages to always show around the current page (default 1). */
|
|
5
|
+
siblingCount?: number;
|
|
6
|
+
/** How many pages to always show at the start and at the end (default 1). */
|
|
7
|
+
boundaryCount?: number;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Computes the windowed page list of the "numbers" variant: boundary pages at both
|
|
11
|
+
* ends, siblings around the current page, and `"ellipsis"` tokens for the collapsed
|
|
12
|
+
* gaps. The window is stable — for a given `pageCount` and options, every
|
|
13
|
+
* `currentPage` yields the same number of slots (no layout jumping while paging).
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```ts
|
|
17
|
+
* paginationRange(5, 10); // [1, "ellipsis", 4, 5, 6, "ellipsis", 10]
|
|
18
|
+
* paginationRange(1, 5); // [1, 2, 3, 4, 5]
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export declare function paginationRange(currentPage: number, pageCount: number, options?: PaginationRangeOptions): PaginationRangeItem[];
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Computes the windowed page list of the "numbers" variant: boundary pages at both
|
|
3
|
+
* ends, siblings around the current page, and `"ellipsis"` tokens for the collapsed
|
|
4
|
+
* gaps. The window is stable — for a given `pageCount` and options, every
|
|
5
|
+
* `currentPage` yields the same number of slots (no layout jumping while paging).
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* paginationRange(5, 10); // [1, "ellipsis", 4, 5, 6, "ellipsis", 10]
|
|
10
|
+
* paginationRange(1, 5); // [1, 2, 3, 4, 5]
|
|
11
|
+
* ```
|
|
12
|
+
*/
|
|
13
|
+
export function paginationRange(currentPage, pageCount, options = {}) {
|
|
14
|
+
const siblingCount = Math.max(0, Math.floor(options.siblingCount ?? 1));
|
|
15
|
+
const boundaryCount = Math.max(0, Math.floor(options.boundaryCount ?? 1));
|
|
16
|
+
if (!Number.isFinite(pageCount) || pageCount <= 0)
|
|
17
|
+
return [];
|
|
18
|
+
const page = Math.min(Math.max(1, Math.floor(currentPage) || 1), pageCount);
|
|
19
|
+
const range = (start, end) => Array.from({ length: end - start + 1 }, (_, i) => start + i);
|
|
20
|
+
const startPages = range(1, Math.min(boundaryCount, pageCount));
|
|
21
|
+
const endPages = range(Math.max(pageCount - boundaryCount + 1, boundaryCount + 1), pageCount);
|
|
22
|
+
const siblingsStart = Math.max(Math.min(page - siblingCount, pageCount - boundaryCount - siblingCount * 2 - 1), boundaryCount + 2);
|
|
23
|
+
const siblingsEnd = Math.min(Math.max(page + siblingCount, boundaryCount + siblingCount * 2 + 2), endPages.length > 0 ? endPages[0] - 2 : pageCount - 1);
|
|
24
|
+
return [
|
|
25
|
+
...startPages,
|
|
26
|
+
// start gap: ellipsis, the single page it would hide, or nothing
|
|
27
|
+
...(siblingsStart > boundaryCount + 2
|
|
28
|
+
? ["ellipsis"]
|
|
29
|
+
: boundaryCount + 1 < pageCount - boundaryCount
|
|
30
|
+
? [boundaryCount + 1]
|
|
31
|
+
: []),
|
|
32
|
+
...range(siblingsStart, siblingsEnd),
|
|
33
|
+
// end gap: ellipsis, the single page it would hide, or nothing
|
|
34
|
+
...(siblingsEnd < pageCount - boundaryCount - 1
|
|
35
|
+
? ["ellipsis"]
|
|
36
|
+
: pageCount - boundaryCount > boundaryCount
|
|
37
|
+
? [pageCount - boundaryCount]
|
|
38
|
+
: []),
|
|
39
|
+
...endPages,
|
|
40
|
+
];
|
|
41
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# Stat
|
|
2
|
+
|
|
3
|
+
A KPI/stat card: a metric label, a prominent value, and an optional delta with trend
|
|
4
|
+
arrow and color-coded semantics (up/down/flat), plus an optional hint, corner icon, and
|
|
5
|
+
footer area (e.g. for a sparkline). Renders as a `<div>`, or as an `<a>`/`<button>` when
|
|
6
|
+
`href`/`onclick` is provided.
|
|
7
|
+
|
|
8
|
+
## Usage
|
|
9
|
+
|
|
10
|
+
```svelte
|
|
11
|
+
<script>
|
|
12
|
+
import { Stat } from "@marianmeres/stuic";
|
|
13
|
+
// optional, for the sparkline footer example below (see TrendChart's README):
|
|
14
|
+
import { TrendChart } from "@marianmeres/stuic/trend-chart";
|
|
15
|
+
</script>
|
|
16
|
+
|
|
17
|
+
<!-- Basic -->
|
|
18
|
+
<Stat
|
|
19
|
+
label="Total Revenue"
|
|
20
|
+
value="$45,231.89"
|
|
21
|
+
delta="+20.1%"
|
|
22
|
+
trend="up"
|
|
23
|
+
hint="vs. last month"
|
|
24
|
+
/>
|
|
25
|
+
|
|
26
|
+
<!-- Trend down (auto-colored destructive) -->
|
|
27
|
+
<Stat
|
|
28
|
+
label="Bounce Rate"
|
|
29
|
+
value="47.3%"
|
|
30
|
+
delta="+2.4%"
|
|
31
|
+
trend="up"
|
|
32
|
+
trendIntent="destructive"
|
|
33
|
+
/>
|
|
34
|
+
|
|
35
|
+
<!-- Down is good here: override the auto color mapping -->
|
|
36
|
+
<Stat label="Churn" value="2.1%" delta="-0.4%" trend="down" trendIntent="success" />
|
|
37
|
+
|
|
38
|
+
<!-- With a corner icon -->
|
|
39
|
+
<Stat label="Active Users" value="2,338" delta="+180" trend="up">
|
|
40
|
+
{#snippet renderIcon()}
|
|
41
|
+
{@html iconUser({ size: 20 })}
|
|
42
|
+
{/snippet}
|
|
43
|
+
</Stat>
|
|
44
|
+
|
|
45
|
+
<!-- Clickable (renders as <a>) -->
|
|
46
|
+
<Stat
|
|
47
|
+
href="/analytics/revenue"
|
|
48
|
+
label="Revenue"
|
|
49
|
+
value="$45,231"
|
|
50
|
+
delta="+20.1%"
|
|
51
|
+
trend="up"
|
|
52
|
+
/>
|
|
53
|
+
|
|
54
|
+
<!-- Footer (sparkline, actions, ...) — first-class pairing with the TrendChart
|
|
55
|
+
wrapper (subpath import; optional @marianmeres/trend-chart peer dep) -->
|
|
56
|
+
<Stat label="Requests" value="1.2M" delta="+8.4%" trend="up">
|
|
57
|
+
{#snippet renderFooter()}
|
|
58
|
+
<TrendChart data={[42, 51, 48, 63, 58, 71, 84]} sparkline />
|
|
59
|
+
{/snippet}
|
|
60
|
+
</Stat>
|
|
61
|
+
|
|
62
|
+
<!-- Full override -->
|
|
63
|
+
<Stat>
|
|
64
|
+
<div class="p-4">Completely custom content</div>
|
|
65
|
+
</Stat>
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Props
|
|
69
|
+
|
|
70
|
+
| Prop | Type | Default | Description |
|
|
71
|
+
| ---------------- | ---------------------------------------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
72
|
+
| `label` | `THC` | - | Small label above the value (metric name) |
|
|
73
|
+
| `value` | `THC \| number` | - | The primary metric value, preformatted (e.g. `"$45,231.89"`) |
|
|
74
|
+
| `delta` | `THC \| number` | - | Delta/change indicator, preformatted (e.g. `"+20.1%"`) |
|
|
75
|
+
| `trend` | `"up" \| "down" \| "flat"` | - | Trend direction — drives the arrow and (via `trendIntent="auto"`) the delta color |
|
|
76
|
+
| `trendIntent` | `"auto" \| "success" \| "destructive" \| "warning" \| "neutral"` | `"auto"` | Delta color semantics. `"auto"` maps up→success, down→destructive, flat→neutral. Set explicitly for metrics where down is good (churn, costs). |
|
|
77
|
+
| `showTrendArrow` | `boolean` | `true` | Show the trend direction arrow next to the delta |
|
|
78
|
+
| `hint` | `THC` | - | Secondary text next to the delta (e.g. "vs. last month") |
|
|
79
|
+
| `href` | `string` | - | Renders stat as `<a>` |
|
|
80
|
+
| `onclick` | `(e: MouseEvent) => void` | - | Renders stat as `<button>` |
|
|
81
|
+
| `disabled` | `boolean` | `false` | Disabled state (reduced opacity, no interaction) |
|
|
82
|
+
| `unstyled` | `boolean` | `false` | Skip all default styling |
|
|
83
|
+
| `class` | `string` | - | Additional CSS classes for the stat shell |
|
|
84
|
+
| `classLabel` | `string` | - | Additional CSS classes for the label |
|
|
85
|
+
| `classValue` | `string` | - | Additional CSS classes for the value |
|
|
86
|
+
| `classDelta` | `string` | - | Additional CSS classes for the delta |
|
|
87
|
+
| `classHint` | `string` | - | Additional CSS classes for the hint |
|
|
88
|
+
| `classFooter` | `string` | - | Additional CSS classes for the footer |
|
|
89
|
+
| `el` | `HTMLElement` | - | Bindable element reference |
|
|
90
|
+
|
|
91
|
+
## Snippets
|
|
92
|
+
|
|
93
|
+
| Snippet | Parameters | Description |
|
|
94
|
+
| -------------- | ---------- | ------------------------------------------------- |
|
|
95
|
+
| `children` | - | Overrides the entire stat body |
|
|
96
|
+
| `renderIcon` | - | Icon area in the top-right corner (next to label) |
|
|
97
|
+
| `renderFooter` | - | Footer area (sparkline, actions, metadata) |
|
|
98
|
+
|
|
99
|
+
## CSS Variables
|
|
100
|
+
|
|
101
|
+
| Variable | Default | Description |
|
|
102
|
+
| ------------------------------------- | --------------------------------------------------------- | ----------------------------------- |
|
|
103
|
+
| `--stuic-stat-bg` | `var(--stuic-color-card, var(--stuic-color-background))` | Background color |
|
|
104
|
+
| `--stuic-stat-bg-hover` | `var(--stuic-color-card-hover, var(--stuic-color-muted))` | Hover background (interactive only) |
|
|
105
|
+
| `--stuic-stat-border-width` | `1px` | Border width |
|
|
106
|
+
| `--stuic-stat-border` | `var(--stuic-color-border)` | Border color |
|
|
107
|
+
| `--stuic-stat-border-hover` | `var(--stuic-color-border-hover)` | Hover border color |
|
|
108
|
+
| `--stuic-stat-radius` | `var(--radius-lg)` | Border radius |
|
|
109
|
+
| `--stuic-stat-shadow` | `var(--shadow-sm)` | Box shadow |
|
|
110
|
+
| `--stuic-stat-shadow-hover` | `var(--shadow-md)` | Hover box shadow |
|
|
111
|
+
| `--stuic-stat-transition` | `150ms` | Transition duration |
|
|
112
|
+
| `--stuic-stat-padding` | `1rem` | Padding |
|
|
113
|
+
| `--stuic-stat-gap` | `0.375rem` | Vertical gap between rows |
|
|
114
|
+
| `--stuic-stat-meta-gap` | `0.5rem` | Gap between delta and hint |
|
|
115
|
+
| `--stuic-stat-delta-gap` | `0.25rem` | Gap between arrow and delta text |
|
|
116
|
+
| `--stuic-stat-label-font-size` | `var(--text-sm)` | Label font size |
|
|
117
|
+
| `--stuic-stat-label-font-weight` | `var(--font-weight-medium)` | Label font weight |
|
|
118
|
+
| `--stuic-stat-label-text` | `var(--stuic-color-muted-foreground)` | Label text color |
|
|
119
|
+
| `--stuic-stat-value-font-size` | `var(--text-2xl)` | Value font size |
|
|
120
|
+
| `--stuic-stat-value-font-weight` | `var(--font-weight-semibold)` | Value font weight |
|
|
121
|
+
| `--stuic-stat-value-line-height` | `1.2` | Value line height |
|
|
122
|
+
| `--stuic-stat-value-text` | `var(--stuic-color-foreground)` | Value text color |
|
|
123
|
+
| `--stuic-stat-delta-font-size` | `var(--text-sm)` | Delta font size |
|
|
124
|
+
| `--stuic-stat-delta-font-weight` | `var(--font-weight-medium)` | Delta font weight |
|
|
125
|
+
| `--stuic-stat-delta-text-success` | `var(--stuic-color-success)` | Delta color (success intent) |
|
|
126
|
+
| `--stuic-stat-delta-text-destructive` | `var(--stuic-color-destructive)` | Delta color (destructive intent) |
|
|
127
|
+
| `--stuic-stat-delta-text-warning` | `var(--stuic-color-warning)` | Delta color (warning intent) |
|
|
128
|
+
| `--stuic-stat-delta-text-neutral` | `var(--stuic-color-muted-foreground)` | Delta color (neutral intent) |
|
|
129
|
+
| `--stuic-stat-hint-font-size` | `var(--text-sm)` | Hint font size |
|
|
130
|
+
| `--stuic-stat-hint-text` | `var(--stuic-color-muted-foreground)` | Hint text color |
|
|
131
|
+
| `--stuic-stat-icon-text` | `var(--stuic-color-muted-foreground)` | Corner icon color |
|
|
132
|
+
| `--stuic-stat-ring-width` | `3px` | Focus ring width |
|
|
133
|
+
| `--stuic-stat-ring-color` | `var(--stuic-color-ring)` | Focus ring color |
|
|
134
|
+
| `--stuic-stat-opacity-disabled` | `0.5` | Disabled opacity |
|
|
135
|
+
|
|
136
|
+
## Notes
|
|
137
|
+
|
|
138
|
+
- The stat fills its parent container (`width: 100%; height: 100%`). Control sizing via
|
|
139
|
+
the parent element (typically a CSS grid of KPI cards).
|
|
140
|
+
- The arrow shows the **direction** (`trend`), the color shows the **sentiment**
|
|
141
|
+
(`trendIntent`). They are deliberately independent: a downward trend can be colored
|
|
142
|
+
as success (churn, costs, error rate) via `trendIntent="success"`.
|
|
143
|
+
- Values and deltas are rendered as-is — format numbers/percentages/currencies yourself
|
|
144
|
+
(e.g. via `Intl.NumberFormat`). Bare `number` values are stringified verbatim.
|
|
145
|
+
- The trend arrow is `aria-hidden`; make sure the delta text itself carries the sign
|
|
146
|
+
(`"+20.1%"`, `"-3%"`) so the direction survives for screen readers.
|
|
147
|
+
- All content props (`label`, `value`, `delta`, `hint`) accept the THC type (plain
|
|
148
|
+
string, HTML, component, or snippet).
|
|
149
|
+
- When using `href` or `onclick`, the entire stat becomes interactive with hover
|
|
150
|
+
effects. Avoid placing interactive elements inside `renderFooter` in this case, as it
|
|
151
|
+
creates invalid nested interactive HTML.
|
|
152
|
+
- Footer sticks to the bottom of the stat via `margin-top: auto` (relevant in
|
|
153
|
+
equal-height grids).
|
|
154
|
+
- For a sparkline footer, the first-class pairing is the `TrendChart` wrapper
|
|
155
|
+
(`import { TrendChart } from "@marianmeres/stuic/trend-chart"`) — a subpath
|
|
156
|
+
export whose `@marianmeres/trend-chart` peer dependency stays opt-in (nothing
|
|
157
|
+
is bundled unless you use it). See `src/lib/components/TrendChart/README.md`.
|