@marianmeres/stuic 3.162.0 → 3.164.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/dist/components/DataTable/DataTable.svelte +98 -70
- package/dist/components/DataTable/DataTable.svelte.d.ts +17 -3
- package/dist/components/DataTable/README.md +106 -34
- package/dist/components/DataTable/i18n-sk.d.ts +17 -0
- package/dist/components/DataTable/i18n-sk.js +29 -0
- package/dist/components/DataTable/i18n.d.ts +43 -0
- package/dist/components/DataTable/i18n.js +51 -0
- package/dist/components/DataTable/index.css +39 -17
- package/dist/components/DataTable/index.d.ts +2 -0
- package/dist/components/DataTable/index.js +2 -0
- package/dist/components/FieldsBuilder/_internal/LocalizedTextInput.svelte +7 -8
- package/package.json +3 -3
|
@@ -4,35 +4,8 @@
|
|
|
4
4
|
import type { PagingCalcResult } from "@marianmeres/paging-store";
|
|
5
5
|
import type { THC } from "../Thc/index.js";
|
|
6
6
|
import type { TranslateFn } from "../../types.js";
|
|
7
|
-
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
// i18n ready
|
|
11
|
-
function t_default(
|
|
12
|
-
k: string,
|
|
13
|
-
values: false | null | undefined | Record<string, string | number> = null,
|
|
14
|
-
fallback: string | boolean = "",
|
|
15
|
-
_i18nSpanWrap: boolean = true
|
|
16
|
-
) {
|
|
17
|
-
const m: Record<string, string> = {
|
|
18
|
-
previous_page: "Prev",
|
|
19
|
-
next_page: "Next",
|
|
20
|
-
page_x_of_y: "Page {page} of {pageCount}",
|
|
21
|
-
no_data: "No data",
|
|
22
|
-
select_all_rows: "Select all rows on this page",
|
|
23
|
-
select_row: "Select row",
|
|
24
|
-
select_all_on_page_x: "All {count} on this page selected.",
|
|
25
|
-
select_all_results: "Select all {totalCount} results",
|
|
26
|
-
all_results_selected: "All {totalCount} results selected.",
|
|
27
|
-
clear_selection: "Clear selection",
|
|
28
|
-
};
|
|
29
|
-
let out = m[k] ?? fallback ?? k;
|
|
30
|
-
return isPlainObject(values)
|
|
31
|
-
? replaceMap(out, values as any, {
|
|
32
|
-
preSearchKeyTransform: (k) => `{${k}}`,
|
|
33
|
-
})
|
|
34
|
-
: out;
|
|
35
|
-
}
|
|
7
|
+
// i18n ready -- see ./i18n.ts (english, built-in) and ./i18n-sk.ts (opt-in slovak)
|
|
8
|
+
import { DATA_TABLE_MESSAGES_EN, t_default } from "./i18n.js";
|
|
36
9
|
|
|
37
10
|
export interface DataTableColumn<T = Record<string, any>> {
|
|
38
11
|
/** Property key to extract from row data (supports dot-notation: "data.name") */
|
|
@@ -95,6 +68,20 @@
|
|
|
95
68
|
/** Set of row IDs explicitly deselected while in all-pages mode (bindable) */
|
|
96
69
|
excluded?: Set<string | number>;
|
|
97
70
|
|
|
71
|
+
/**
|
|
72
|
+
* Keep the selection bar -- the strip above the table holding the idle status,
|
|
73
|
+
* `batchActions`, and the select-all-across-pages offer -- in the layout at all
|
|
74
|
+
* times, so checking the first row does not shove the table down. While nothing
|
|
75
|
+
* is selected it reads `t("no_rows_selected")`.
|
|
76
|
+
*
|
|
77
|
+
* Only meaningful when `selectable` is on AND there is something to reserve room
|
|
78
|
+
* for (a `batchActions` snippet or `allowSelectAllPages`); with neither, no bar
|
|
79
|
+
* is rendered at all.
|
|
80
|
+
*
|
|
81
|
+
* Set `false` for the pop-in behaviour: the bar appears only once it has content.
|
|
82
|
+
*/
|
|
83
|
+
reserveBatchBar?: boolean;
|
|
84
|
+
|
|
98
85
|
/** Callback when a row is clicked */
|
|
99
86
|
onRowClick?: (row: T, index: number) => void;
|
|
100
87
|
|
|
@@ -173,7 +160,8 @@
|
|
|
173
160
|
]
|
|
174
161
|
>;
|
|
175
162
|
/**
|
|
176
|
-
*
|
|
163
|
+
* Content of the selection bar while something is selected. Replaces the default
|
|
164
|
+
* "{count} selected" status, so render your own count alongside the buttons.
|
|
177
165
|
*
|
|
178
166
|
* Note: in all-pages mode (`selectedAll === true`) `selectedRows` only contains
|
|
179
167
|
* rows from the current page that aren't excluded. Off-page rows are not
|
|
@@ -195,8 +183,8 @@
|
|
|
195
183
|
]
|
|
196
184
|
>;
|
|
197
185
|
/**
|
|
198
|
-
* Custom "select all results across pages"
|
|
199
|
-
*
|
|
186
|
+
* Custom "select all results across pages" offer, rendered on the trailing edge
|
|
187
|
+
* of the selection bar. When omitted, a default one is rendered.
|
|
200
188
|
*/
|
|
201
189
|
selectAllBanner?: Snippet<
|
|
202
190
|
[
|
|
@@ -250,6 +238,7 @@
|
|
|
250
238
|
allowSelectAllPages = false,
|
|
251
239
|
selectedAll = $bindable(false),
|
|
252
240
|
excluded = $bindable(new Set()),
|
|
241
|
+
reserveBatchBar = true,
|
|
253
242
|
onRowClick,
|
|
254
243
|
rowHref,
|
|
255
244
|
rowHrefColumn,
|
|
@@ -395,6 +384,30 @@
|
|
|
395
384
|
return allOnPageSelected;
|
|
396
385
|
});
|
|
397
386
|
|
|
387
|
+
// The default selection status, shown when there is no `batchActions` snippet to take
|
|
388
|
+
// its place. Both keys post-date the component, so a consumer `t` written against an
|
|
389
|
+
// older version does not know them -- and one that answers an unknown key with ""
|
|
390
|
+
// would leave the reserved bar visibly blank. Hence the explicit english fallbacks
|
|
391
|
+
// (pre-interpolated, since a `t` may hand a fallback straight back untouched), which
|
|
392
|
+
// every other, older key can do without.
|
|
393
|
+
let statusText = $derived(
|
|
394
|
+
effectiveCount > 0
|
|
395
|
+
? t(
|
|
396
|
+
"x_rows_selected",
|
|
397
|
+
{ count: effectiveCount },
|
|
398
|
+
DATA_TABLE_MESSAGES_EN.x_rows_selected.replace("{count}", `${effectiveCount}`)
|
|
399
|
+
)
|
|
400
|
+
: t("no_rows_selected", null, DATA_TABLE_MESSAGES_EN.no_rows_selected)
|
|
401
|
+
);
|
|
402
|
+
|
|
403
|
+
// The bar only exists if something can appear in it; `reserveBatchBar` then decides
|
|
404
|
+
// whether it holds its place while that something is absent.
|
|
405
|
+
let hasBatchBar = $derived(selectable && (!!batchActions || allowSelectAllPages));
|
|
406
|
+
let showBatchBar = $derived(
|
|
407
|
+
hasBatchBar &&
|
|
408
|
+
(reserveBatchBar || showSelectAllBanner || (effectiveCount > 0 && !!batchActions))
|
|
409
|
+
);
|
|
410
|
+
|
|
398
411
|
// --- Row click / activation ---
|
|
399
412
|
|
|
400
413
|
// A row is a big target that legitimately contains its own controls. Anything that
|
|
@@ -489,47 +502,62 @@
|
|
|
489
502
|
{/if}
|
|
490
503
|
{/snippet}
|
|
491
504
|
|
|
492
|
-
<!--
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
{
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
clearSelection: clearAllSelection,
|
|
517
|
-
})}
|
|
518
|
-
{:else}
|
|
519
|
-
<div class={!unstyled ? "stuic-data-table-select-all-banner" : undefined}>
|
|
520
|
-
{#if selectedAll}
|
|
521
|
-
<span>{t("all_results_selected", { totalCount: paging.total })}</span>
|
|
522
|
-
<Button variant="ghost" size="sm" onclick={clearAllSelection}>
|
|
523
|
-
{t("clear_selection")}
|
|
524
|
-
</Button>
|
|
505
|
+
<!--
|
|
506
|
+
Selection bar -- one strip carrying the selection status, the consumer's
|
|
507
|
+
`batchActions`, and the select-all-across-pages offer. Deliberately a single element
|
|
508
|
+
rather than a stack of independent conditionals: each of those appearing on its own
|
|
509
|
+
reflows everything below it, which is exactly the jump `reserveBatchBar` exists to
|
|
510
|
+
prevent. Sits outside the root container so the loading overlay doesn't cover it.
|
|
511
|
+
-->
|
|
512
|
+
{#if showBatchBar}
|
|
513
|
+
<div
|
|
514
|
+
class={!unstyled ? "stuic-data-table-batch" : undefined}
|
|
515
|
+
data-idle={!unstyled && effectiveCount === 0 ? "true" : undefined}
|
|
516
|
+
data-select-all={!unstyled && showSelectAllBanner ? "true" : undefined}
|
|
517
|
+
>
|
|
518
|
+
<div class={!unstyled ? "stuic-data-table-batch-main" : undefined}>
|
|
519
|
+
{#if effectiveCount > 0 && batchActions}
|
|
520
|
+
{@render batchActions({
|
|
521
|
+
selected,
|
|
522
|
+
selectedRows,
|
|
523
|
+
selectedAll,
|
|
524
|
+
excluded,
|
|
525
|
+
effectiveCount,
|
|
526
|
+
totalCount,
|
|
527
|
+
clearSelection: clearAllSelection,
|
|
528
|
+
})}
|
|
525
529
|
{:else}
|
|
526
|
-
<span
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
</Button>
|
|
530
|
+
<span class={!unstyled ? "stuic-data-table-batch-status" : undefined}>
|
|
531
|
+
{statusText}
|
|
532
|
+
</span>
|
|
530
533
|
{/if}
|
|
531
534
|
</div>
|
|
532
|
-
|
|
535
|
+
|
|
536
|
+
{#if showSelectAllBanner && paging}
|
|
537
|
+
<div class={!unstyled ? "stuic-data-table-batch-select-all" : undefined}>
|
|
538
|
+
{#if selectAllBanner}
|
|
539
|
+
{@render selectAllBanner({
|
|
540
|
+
selectedAll,
|
|
541
|
+
effectiveCount,
|
|
542
|
+
totalCount: paging.total,
|
|
543
|
+
pageCount: data.length,
|
|
544
|
+
selectAll: enterSelectAll,
|
|
545
|
+
clearSelection: clearAllSelection,
|
|
546
|
+
})}
|
|
547
|
+
{:else if selectedAll}
|
|
548
|
+
<span>{t("all_results_selected", { totalCount: paging.total })}</span>
|
|
549
|
+
<Button variant="ghost" size="sm" onclick={clearAllSelection}>
|
|
550
|
+
{t("clear_selection")}
|
|
551
|
+
</Button>
|
|
552
|
+
{:else}
|
|
553
|
+
<span>{t("select_all_on_page_x", { count: data.length })}</span>
|
|
554
|
+
<Button variant="ghost" size="sm" onclick={enterSelectAll}>
|
|
555
|
+
{t("select_all_results", { totalCount: paging.total })}
|
|
556
|
+
</Button>
|
|
557
|
+
{/if}
|
|
558
|
+
</div>
|
|
559
|
+
{/if}
|
|
560
|
+
</div>
|
|
533
561
|
{/if}
|
|
534
562
|
|
|
535
563
|
<!-- Root container -->
|
|
@@ -56,6 +56,19 @@ export interface Props<T = Record<string, any>> extends Omit<HTMLAttributes<HTML
|
|
|
56
56
|
selectedAll?: boolean;
|
|
57
57
|
/** Set of row IDs explicitly deselected while in all-pages mode (bindable) */
|
|
58
58
|
excluded?: Set<string | number>;
|
|
59
|
+
/**
|
|
60
|
+
* Keep the selection bar -- the strip above the table holding the idle status,
|
|
61
|
+
* `batchActions`, and the select-all-across-pages offer -- in the layout at all
|
|
62
|
+
* times, so checking the first row does not shove the table down. While nothing
|
|
63
|
+
* is selected it reads `t("no_rows_selected")`.
|
|
64
|
+
*
|
|
65
|
+
* Only meaningful when `selectable` is on AND there is something to reserve room
|
|
66
|
+
* for (a `batchActions` snippet or `allowSelectAllPages`); with neither, no bar
|
|
67
|
+
* is rendered at all.
|
|
68
|
+
*
|
|
69
|
+
* Set `false` for the pop-in behaviour: the bar appears only once it has content.
|
|
70
|
+
*/
|
|
71
|
+
reserveBatchBar?: boolean;
|
|
59
72
|
/** Callback when a row is clicked */
|
|
60
73
|
onRowClick?: (row: T, index: number) => void;
|
|
61
74
|
/**
|
|
@@ -126,7 +139,8 @@ export interface Props<T = Record<string, any>> extends Omit<HTMLAttributes<HTML
|
|
|
126
139
|
}
|
|
127
140
|
]>;
|
|
128
141
|
/**
|
|
129
|
-
*
|
|
142
|
+
* Content of the selection bar while something is selected. Replaces the default
|
|
143
|
+
* "{count} selected" status, so render your own count alongside the buttons.
|
|
130
144
|
*
|
|
131
145
|
* Note: in all-pages mode (`selectedAll === true`) `selectedRows` only contains
|
|
132
146
|
* rows from the current page that aren't excluded. Off-page rows are not
|
|
@@ -146,8 +160,8 @@ export interface Props<T = Record<string, any>> extends Omit<HTMLAttributes<HTML
|
|
|
146
160
|
}
|
|
147
161
|
]>;
|
|
148
162
|
/**
|
|
149
|
-
* Custom "select all results across pages"
|
|
150
|
-
*
|
|
163
|
+
* Custom "select all results across pages" offer, rendered on the trailing edge
|
|
164
|
+
* of the selection bar. When omitted, a default one is rendered.
|
|
151
165
|
*/
|
|
152
166
|
selectAllBanner?: Snippet<[
|
|
153
167
|
{
|
|
@@ -64,6 +64,30 @@ DataTable integrates with [`@marianmeres/paging-store`](https://github.com/maria
|
|
|
64
64
|
|
|
65
65
|
Shift+click a checkbox to toggle a range of rows from the last clicked checkbox to the current one. Disabled rows in the range are skipped. The anchor resets when `data` changes (e.g. on page navigation).
|
|
66
66
|
|
|
67
|
+
#### The selection bar holds its place
|
|
68
|
+
|
|
69
|
+
Everything selection-related lives in one strip above the table: the status while nothing
|
|
70
|
+
is selected, your `batchActions` while something is, and the select-all-across-pages offer
|
|
71
|
+
on its trailing edge. That strip is rendered from the start and keeps a constant height
|
|
72
|
+
(`reserveBatchBar`, on by default), so checking the first row swaps its contents instead
|
|
73
|
+
of pushing the table down.
|
|
74
|
+
|
|
75
|
+
- The height comes from `--stuic-data-table-batch-min-height`, sized to a `size="sm"`
|
|
76
|
+
Button. Batch actions built from bigger controls need that token raised, or the bar
|
|
77
|
+
grows when they appear — which is the jump all over again.
|
|
78
|
+
- Idle, the bar is transparent (`--stuic-data-table-batch-bg-idle`) so a permanently
|
|
79
|
+
reserved strip doesn't shout; it takes on `--stuic-data-table-batch-bg` once something
|
|
80
|
+
is selected and `--stuic-data-table-select-all-bg` while the select-all offer is up.
|
|
81
|
+
- The bar only exists when there is something to reserve room for — `selectable` plus
|
|
82
|
+
either a `batchActions` snippet or `allowSelectAllPages`. `selectable` on its own
|
|
83
|
+
renders no bar.
|
|
84
|
+
- `reserveBatchBar={false}` restores the old behaviour: the bar appears only once it has
|
|
85
|
+
content.
|
|
86
|
+
|
|
87
|
+
Without a `batchActions` snippet the bar still reports the count itself
|
|
88
|
+
(`t("x_rows_selected")` / `t("no_rows_selected")`). With one, the snippet replaces that
|
|
89
|
+
status entirely — render your own count next to the buttons.
|
|
90
|
+
|
|
67
91
|
### Select All Across Pages
|
|
68
92
|
|
|
69
93
|
When your data is paged, DataTable can offer a "select all results" affordance that expands selection beyond the current page. Enable it with `allowSelectAllPages`. When the user opts in, selection flips to **all-pages mode**: every row is implicitly selected, and `excluded` holds IDs the user has explicitly deselected.
|
|
@@ -115,7 +139,7 @@ function deleteSelection({ selectedAll, excluded }) {
|
|
|
115
139
|
|
|
116
140
|
New records inserted while all-pages mode is active are implicitly selected (they aren't in `excluded`). This matches the conventional intent — "delete everything matching X" should include matches that arrive before the operation runs. If you need snapshot-at-click semantics, capture a timestamp or ID list in your consumer.
|
|
117
141
|
|
|
118
|
-
**Customising the
|
|
142
|
+
**Customising the offer:** the default offer renders on the trailing edge of the selection bar and uses the built-in `t()` keys `select_all_on_page_x`, `select_all_results`, `all_results_selected`, and `clear_selection`. Override its markup entirely with the `selectAllBanner` snippet (which renders in the same slot — it is no longer a standalone block below the batch bar):
|
|
119
143
|
|
|
120
144
|
```svelte
|
|
121
145
|
<DataTable
|
|
@@ -301,6 +325,7 @@ Replace the entire `<tr>` on desktop. When this snippet is provided, DataTable d
|
|
|
301
325
|
| `allowSelectAllPages` | `boolean` | `false` | Show a banner offering "select all results" across paged data |
|
|
302
326
|
| `selectedAll` | `boolean` | `false` | All-pages mode flag (bindable). In this mode `excluded` drives selection |
|
|
303
327
|
| `excluded` | `Set<string \| number>` | `new Set()` | Deselected row IDs while in all-pages mode (bindable) |
|
|
328
|
+
| `reserveBatchBar` | `boolean` | `true` | Keep the selection bar in the layout while nothing is selected |
|
|
304
329
|
| `onRowClick` | `(row, index) => void` | - | Row click callback |
|
|
305
330
|
| `rowHref` | `(row, index) => string \| undefined` | - | Wrap the lead cell's content in an `<a href>` (keyboard/⌘-click reachable) |
|
|
306
331
|
| `rowHrefColumn` | `string` | first column | Which column is the lead cell for `rowHref` |
|
|
@@ -313,8 +338,8 @@ Replace the entire `<tr>` on desktop. When this snippet is provided, DataTable d
|
|
|
313
338
|
| `cell` | `Snippet` | - | Custom cell renderer (desktop + mobile) |
|
|
314
339
|
| `row` | `Snippet` | - | Custom desktop `<tr>` renderer (overrides default row) |
|
|
315
340
|
| `mobileRow` | `Snippet` | - | Custom mobile card renderer |
|
|
316
|
-
| `batchActions` | `Snippet` | - |
|
|
317
|
-
| `selectAllBanner` | `Snippet` | - | Override default "select all across pages"
|
|
341
|
+
| `batchActions` | `Snippet` | - | Selection bar content while something is selected |
|
|
342
|
+
| `selectAllBanner` | `Snippet` | - | Override default "select all across pages" offer |
|
|
318
343
|
| `empty` | `Snippet` | - | Custom empty state |
|
|
319
344
|
| `unstyled` | `boolean` | `false` | Skip default styling |
|
|
320
345
|
| `class` | `string` | - | Additional CSS classes |
|
|
@@ -348,36 +373,83 @@ Replace the entire `<tr>` on desktop. When this snippet is provided, DataTable d
|
|
|
348
373
|
| `hideOnMobile` | `boolean` | `false` | Hide in mobile view |
|
|
349
374
|
| `renderValue` | `(value, row) => string` | - | Value formatter |
|
|
350
375
|
|
|
376
|
+
## i18n
|
|
377
|
+
|
|
378
|
+
All built-in texts go through the `t` prop. English is the built-in default; Slovak ships
|
|
379
|
+
bundled and opt-in (importing it is what pulls it into your bundle — English-only
|
|
380
|
+
consumers pay nothing).
|
|
381
|
+
|
|
382
|
+
```svelte
|
|
383
|
+
<script>
|
|
384
|
+
import {
|
|
385
|
+
DataTable,
|
|
386
|
+
createDataTableT,
|
|
387
|
+
DATA_TABLE_MESSAGES_SK,
|
|
388
|
+
} from "@marianmeres/stuic";
|
|
389
|
+
|
|
390
|
+
const t = createDataTableT(DATA_TABLE_MESSAGES_SK);
|
|
391
|
+
</script>
|
|
392
|
+
|
|
393
|
+
<DataTable {columns} {data} {t} />
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
`createDataTableT(messages, fallbackMessages?)` falls back to `DATA_TABLE_MESSAGES_EN` for
|
|
397
|
+
any key the catalog does not define, so a partial catalog is fine and a raw key is never
|
|
398
|
+
rendered — pass your own object to translate into a language that is not bundled, or to
|
|
399
|
+
override individual bundled texts:
|
|
400
|
+
|
|
401
|
+
```ts
|
|
402
|
+
const t = createDataTableT({ ...DATA_TABLE_MESSAGES_SK, no_data: "Nič tu nie je" });
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
| Key | English | Used by |
|
|
406
|
+
| ---------------------- | ---------------------------------- | ------------------------------------ |
|
|
407
|
+
| `previous_page` | Prev | Paging |
|
|
408
|
+
| `next_page` | Next | Paging |
|
|
409
|
+
| `page_x_of_y` | Page {page} of {pageCount} | Paging |
|
|
410
|
+
| `no_data` | No data | Empty state (no `empty` snippet) |
|
|
411
|
+
| `select_all_rows` | Select all rows on this page | Header checkbox `aria-label` |
|
|
412
|
+
| `select_row` | Select row | Row checkbox `aria-label` |
|
|
413
|
+
| `no_rows_selected` | No rows selected | Idle selection bar |
|
|
414
|
+
| `x_rows_selected` | {count} selected | Selection bar without `batchActions` |
|
|
415
|
+
| `select_all_on_page_x` | All {count} on this page selected. | Select-all offer |
|
|
416
|
+
| `select_all_results` | Select all {totalCount} results | Select-all offer |
|
|
417
|
+
| `all_results_selected` | All {totalCount} results selected. | Select-all offer, all-pages mode |
|
|
418
|
+
| `clear_selection` | Clear selection | Select-all offer, all-pages mode |
|
|
419
|
+
|
|
351
420
|
## CSS Variables
|
|
352
421
|
|
|
353
|
-
| Variable | Default | Description
|
|
354
|
-
| ---------------------------------------------- | ------------------------------------- |
|
|
355
|
-
| `--stuic-data-table-radius` | `var(--radius-md)` | Border radius
|
|
356
|
-
| `--stuic-data-table-border-color` | `var(--stuic-color-border)` | Border color
|
|
357
|
-
| `--stuic-data-table-header-bg` | `var(--stuic-color-muted)` | Header background
|
|
358
|
-
| `--stuic-data-table-header-color` | `var(--stuic-color-muted-foreground)` | Header text
|
|
359
|
-
| `--stuic-data-table-header-font-size` | `0.875rem` | Header font size
|
|
360
|
-
| `--stuic-data-table-header-font-weight` | `var(--font-weight-semibold)` | Header font weight
|
|
361
|
-
| `--stuic-data-table-header-padding-x` | `0.75rem` | Header horizontal padding
|
|
362
|
-
| `--stuic-data-table-header-padding-y` | `0.5rem` | Header vertical padding
|
|
363
|
-
| `--stuic-data-table-row-bg` | `transparent` | Row background
|
|
364
|
-
| `--stuic-data-table-row-bg-hover` | `var(--stuic-color-muted)` | Row hover background
|
|
365
|
-
| `--stuic-data-table-row-bg-selected` | `color-mix(primary 10%)` | Selected row background
|
|
366
|
-
| `--stuic-data-table-row-border-color` | `var(--stuic-color-border)` | Row border color
|
|
367
|
-
| `--stuic-data-table-row-link-color` | `inherit` | `rowHref` anchor color
|
|
368
|
-
| `--stuic-data-table-row-link-decoration` | `none` | `rowHref` anchor decoration
|
|
369
|
-
| `--stuic-data-table-row-link-decoration-hover` | `underline` | …on hover
|
|
370
|
-
| `--stuic-data-table-row-ring-width` | `3px` | Activatable row focus ring
|
|
371
|
-
| `--stuic-data-table-row-ring-color` | `var(--stuic-color-ring)` | Activatable row ring color
|
|
372
|
-
| `--stuic-data-table-cell-padding-x` | `0.75rem` | Cell horizontal padding
|
|
373
|
-
| `--stuic-data-table-cell-padding-y` | `0.75rem` | Cell vertical padding
|
|
374
|
-
| `--stuic-data-table-cell-font-size` | `0.875rem` | Cell font size
|
|
375
|
-
| `--stuic-data-table-loading-opacity` | `0.5` | Loading state opacity
|
|
376
|
-
| `--stuic-data-table-card-bg` | `var(--stuic-color-background)` | Mobile card background
|
|
377
|
-
| `--stuic-data-table-card-border-color` | `var(--stuic-color-border)` | Mobile card border
|
|
378
|
-
| `--stuic-data-table-card-radius` | `var(--radius-md)` | Mobile card radius
|
|
379
|
-
| `--stuic-data-table-card-padding` | `0.75rem` | Mobile card padding
|
|
380
|
-
| `--stuic-data-table-card-gap` | `0.5rem` | Gap between mobile cards
|
|
381
|
-
| `--stuic-data-table-
|
|
382
|
-
| `--stuic-data-table-
|
|
383
|
-
| `--stuic-data-table-
|
|
422
|
+
| Variable | Default | Description |
|
|
423
|
+
| ---------------------------------------------- | ------------------------------------- | --------------------------------- |
|
|
424
|
+
| `--stuic-data-table-radius` | `var(--radius-md)` | Border radius |
|
|
425
|
+
| `--stuic-data-table-border-color` | `var(--stuic-color-border)` | Border color |
|
|
426
|
+
| `--stuic-data-table-header-bg` | `var(--stuic-color-muted)` | Header background |
|
|
427
|
+
| `--stuic-data-table-header-color` | `var(--stuic-color-muted-foreground)` | Header text |
|
|
428
|
+
| `--stuic-data-table-header-font-size` | `0.875rem` | Header font size |
|
|
429
|
+
| `--stuic-data-table-header-font-weight` | `var(--font-weight-semibold)` | Header font weight |
|
|
430
|
+
| `--stuic-data-table-header-padding-x` | `0.75rem` | Header horizontal padding |
|
|
431
|
+
| `--stuic-data-table-header-padding-y` | `0.5rem` | Header vertical padding |
|
|
432
|
+
| `--stuic-data-table-row-bg` | `transparent` | Row background |
|
|
433
|
+
| `--stuic-data-table-row-bg-hover` | `var(--stuic-color-muted)` | Row hover background |
|
|
434
|
+
| `--stuic-data-table-row-bg-selected` | `color-mix(primary 10%)` | Selected row background |
|
|
435
|
+
| `--stuic-data-table-row-border-color` | `var(--stuic-color-border)` | Row border color |
|
|
436
|
+
| `--stuic-data-table-row-link-color` | `inherit` | `rowHref` anchor color |
|
|
437
|
+
| `--stuic-data-table-row-link-decoration` | `none` | `rowHref` anchor decoration |
|
|
438
|
+
| `--stuic-data-table-row-link-decoration-hover` | `underline` | …on hover |
|
|
439
|
+
| `--stuic-data-table-row-ring-width` | `3px` | Activatable row focus ring |
|
|
440
|
+
| `--stuic-data-table-row-ring-color` | `var(--stuic-color-ring)` | Activatable row ring color |
|
|
441
|
+
| `--stuic-data-table-cell-padding-x` | `0.75rem` | Cell horizontal padding |
|
|
442
|
+
| `--stuic-data-table-cell-padding-y` | `0.75rem` | Cell vertical padding |
|
|
443
|
+
| `--stuic-data-table-cell-font-size` | `0.875rem` | Cell font size |
|
|
444
|
+
| `--stuic-data-table-loading-opacity` | `0.5` | Loading state opacity |
|
|
445
|
+
| `--stuic-data-table-card-bg` | `var(--stuic-color-background)` | Mobile card background |
|
|
446
|
+
| `--stuic-data-table-card-border-color` | `var(--stuic-color-border)` | Mobile card border |
|
|
447
|
+
| `--stuic-data-table-card-radius` | `var(--radius-md)` | Mobile card radius |
|
|
448
|
+
| `--stuic-data-table-card-padding` | `0.75rem` | Mobile card padding |
|
|
449
|
+
| `--stuic-data-table-card-gap` | `0.5rem` | Gap between mobile cards |
|
|
450
|
+
| `--stuic-data-table-batch-bg` | `var(--stuic-color-muted)` | Selection bar background |
|
|
451
|
+
| `--stuic-data-table-batch-bg-idle` | `transparent` | …while nothing is selected |
|
|
452
|
+
| `--stuic-data-table-batch-padding-x` | `0.75rem` | Selection bar h. padding |
|
|
453
|
+
| `--stuic-data-table-batch-padding-y` | `0.5rem` | Selection bar v. padding |
|
|
454
|
+
| `--stuic-data-table-batch-min-height` | `sm Button + padding` | Reserved selection bar height |
|
|
455
|
+
| `--stuic-data-table-select-all-bg` | `color-mix(primary 10%)` | …while the select-all offer is up |
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { DataTableMessages } from "./i18n.js";
|
|
2
|
+
/**
|
|
3
|
+
* Slovak message catalog for `DataTable`. 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 { DataTable, createDataTableT, DATA_TABLE_MESSAGES_SK } from "@marianmeres/stuic";
|
|
11
|
+
* const t = createDataTableT(DATA_TABLE_MESSAGES_SK);
|
|
12
|
+
* </script>
|
|
13
|
+
*
|
|
14
|
+
* <DataTable {columns} {data} {t} />
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
export declare const DATA_TABLE_MESSAGES_SK: DataTableMessages;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slovak message catalog for `DataTable`. 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 { DataTable, createDataTableT, DATA_TABLE_MESSAGES_SK } from "@marianmeres/stuic";
|
|
10
|
+
* const t = createDataTableT(DATA_TABLE_MESSAGES_SK);
|
|
11
|
+
* </script>
|
|
12
|
+
*
|
|
13
|
+
* <DataTable {columns} {data} {t} />
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
export const DATA_TABLE_MESSAGES_SK = {
|
|
17
|
+
previous_page: "Späť",
|
|
18
|
+
next_page: "Ďalej",
|
|
19
|
+
page_x_of_y: "Strana {page} z {pageCount}",
|
|
20
|
+
no_data: "Žiadne údaje",
|
|
21
|
+
select_all_rows: "Vybrať všetky riadky na tejto strane",
|
|
22
|
+
select_row: "Vybrať riadok",
|
|
23
|
+
select_all_on_page_x: "Vybrané všetky na tejto strane ({count}).",
|
|
24
|
+
select_all_results: "Vybrať všetky výsledky ({totalCount})",
|
|
25
|
+
all_results_selected: "Vybrané všetky výsledky ({totalCount}).",
|
|
26
|
+
clear_selection: "Zrušiť výber",
|
|
27
|
+
no_rows_selected: "Nič nie je vybrané",
|
|
28
|
+
x_rows_selected: "Vybrané: {count}",
|
|
29
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { TranslateFn } from "../../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* The built-in (English) message catalog of `DataTable`. Also the fallback of every
|
|
4
|
+
* other bundled locale, so a locale missing a key still renders text.
|
|
5
|
+
*
|
|
6
|
+
* Placeholders are single-brace (`{page}`, `{totalCount}`, ...).
|
|
7
|
+
*/
|
|
8
|
+
export declare const DATA_TABLE_MESSAGES_EN: {
|
|
9
|
+
previous_page: string;
|
|
10
|
+
next_page: string;
|
|
11
|
+
page_x_of_y: string;
|
|
12
|
+
no_data: string;
|
|
13
|
+
select_all_rows: string;
|
|
14
|
+
select_row: string;
|
|
15
|
+
select_all_on_page_x: string;
|
|
16
|
+
select_all_results: string;
|
|
17
|
+
all_results_selected: string;
|
|
18
|
+
clear_selection: string;
|
|
19
|
+
no_rows_selected: string;
|
|
20
|
+
x_rows_selected: string;
|
|
21
|
+
};
|
|
22
|
+
/** Every message key `DataTable` may look up. */
|
|
23
|
+
export type DataTableMessageKey = keyof typeof DATA_TABLE_MESSAGES_EN;
|
|
24
|
+
/** A (possibly partial) catalog for one locale. */
|
|
25
|
+
export type DataTableMessages = Record<DataTableMessageKey, string>;
|
|
26
|
+
/**
|
|
27
|
+
* Builds the `t` prop of `DataTable` from a message catalog. Unknown or untranslated
|
|
28
|
+
* keys fall back to `fallbackMessages` (English by default), so a catalog may safely be
|
|
29
|
+
* partial and never renders a raw key.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```svelte
|
|
33
|
+
* <script>
|
|
34
|
+
* import { DataTable, createDataTableT, DATA_TABLE_MESSAGES_SK } from "@marianmeres/stuic";
|
|
35
|
+
* const t = createDataTableT(DATA_TABLE_MESSAGES_SK);
|
|
36
|
+
* </script>
|
|
37
|
+
*
|
|
38
|
+
* <DataTable {columns} {data} {t} />
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
export declare function createDataTableT(messages: Partial<DataTableMessages> | Record<string, string>, fallbackMessages?: Partial<DataTableMessages> | Record<string, string>): TranslateFn;
|
|
42
|
+
/** The component's built-in English `t`. */
|
|
43
|
+
export declare const t_default: TranslateFn;
|
|
@@ -0,0 +1,51 @@
|
|
|
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 `DataTable`. Also the fallback of every
|
|
5
|
+
* other bundled locale, so a locale missing a key still renders text.
|
|
6
|
+
*
|
|
7
|
+
* Placeholders are single-brace (`{page}`, `{totalCount}`, ...).
|
|
8
|
+
*/
|
|
9
|
+
export const DATA_TABLE_MESSAGES_EN = {
|
|
10
|
+
previous_page: "Prev",
|
|
11
|
+
next_page: "Next",
|
|
12
|
+
page_x_of_y: "Page {page} of {pageCount}",
|
|
13
|
+
no_data: "No data",
|
|
14
|
+
select_all_rows: "Select all rows on this page",
|
|
15
|
+
select_row: "Select row",
|
|
16
|
+
select_all_on_page_x: "All {count} on this page selected.",
|
|
17
|
+
select_all_results: "Select all {totalCount} results",
|
|
18
|
+
all_results_selected: "All {totalCount} results selected.",
|
|
19
|
+
clear_selection: "Clear selection",
|
|
20
|
+
no_rows_selected: "No rows selected",
|
|
21
|
+
x_rows_selected: "{count} selected",
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Builds the `t` prop of `DataTable` from a message catalog. Unknown or untranslated
|
|
25
|
+
* keys fall back to `fallbackMessages` (English by default), so a catalog may safely be
|
|
26
|
+
* partial and never renders a raw key.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```svelte
|
|
30
|
+
* <script>
|
|
31
|
+
* import { DataTable, createDataTableT, DATA_TABLE_MESSAGES_SK } from "@marianmeres/stuic";
|
|
32
|
+
* const t = createDataTableT(DATA_TABLE_MESSAGES_SK);
|
|
33
|
+
* </script>
|
|
34
|
+
*
|
|
35
|
+
* <DataTable {columns} {data} {t} />
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
export function createDataTableT(messages, fallbackMessages = DATA_TABLE_MESSAGES_EN) {
|
|
39
|
+
return (k, values = null, fallback = "") => {
|
|
40
|
+
const out = messages[k] ??
|
|
41
|
+
fallbackMessages[k] ??
|
|
42
|
+
(typeof fallback === "string" ? fallback : k);
|
|
43
|
+
return isPlainObject(values)
|
|
44
|
+
? replaceMap(out, values, {
|
|
45
|
+
preSearchKeyTransform: (k) => `{${k}}`,
|
|
46
|
+
})
|
|
47
|
+
: out;
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
/** The component's built-in English `t`. */
|
|
51
|
+
export const t_default = createDataTableT(DATA_TABLE_MESSAGES_EN);
|
|
@@ -42,15 +42,19 @@
|
|
|
42
42
|
--stuic-data-table-paging-gap: 0.5rem;
|
|
43
43
|
--stuic-data-table-paging-padding-y: 0.75rem;
|
|
44
44
|
|
|
45
|
-
/*
|
|
45
|
+
/* Selection (batch action) bar */
|
|
46
46
|
--stuic-data-table-batch-bg: var(--stuic-color-muted);
|
|
47
|
+
/* Nothing selected -- the bar is only holding its place, so it stays quiet. */
|
|
48
|
+
--stuic-data-table-batch-bg-idle: var(--stuic-color-muted);
|
|
47
49
|
--stuic-data-table-batch-padding-x: 0.75rem;
|
|
48
|
-
--stuic-data-table-batch-padding-y: 0.
|
|
50
|
+
--stuic-data-table-batch-padding-y: 0.25rem;
|
|
51
|
+
/* Sized to a `size="sm"` Button so the idle and active states are the same height. */
|
|
52
|
+
--stuic-data-table-batch-min-height: calc(var(--stuic-button-min-height-sm, 2.75rem) + var(--stuic-data-table-batch-padding-y) * 2);
|
|
53
|
+
/* */
|
|
54
|
+
--stuic-data-table-batch-margin: 0 0 0.125rem 0;
|
|
49
55
|
|
|
50
|
-
/* Select-all-across-pages
|
|
56
|
+
/* Select-all-across-pages offer (rendered inside the selection bar) */
|
|
51
57
|
--stuic-data-table-select-all-bg: color-mix(in srgb, var(--stuic-color-primary) 10%, var(--stuic-color-background));
|
|
52
|
-
--stuic-data-table-select-all-padding-x: 0.75rem;
|
|
53
|
-
--stuic-data-table-select-all-padding-y: 0.5rem;
|
|
54
58
|
|
|
55
59
|
/* Checkbox column */
|
|
56
60
|
--stuic-data-table-checkbox-width: 3rem;
|
|
@@ -238,34 +242,52 @@
|
|
|
238
242
|
BATCH ACTION BAR
|
|
239
243
|
============================================================================ */
|
|
240
244
|
|
|
245
|
+
/*
|
|
246
|
+
The min-height is the whole point of the reserved bar: an idle line of text is
|
|
247
|
+
shorter than a row of buttons, so without it the table would still shift -- just
|
|
248
|
+
by less. Keep it in sync with whatever button size the batch actions use.
|
|
249
|
+
*/
|
|
241
250
|
.stuic-data-table-batch {
|
|
242
251
|
display: flex;
|
|
243
252
|
align-items: center;
|
|
253
|
+
justify-content: space-between;
|
|
254
|
+
flex-wrap: wrap;
|
|
244
255
|
gap: var(--stuic-data-table-paging-gap);
|
|
256
|
+
min-height: var(--stuic-data-table-batch-min-height);
|
|
245
257
|
padding: var(--stuic-data-table-batch-padding-y)
|
|
246
258
|
var(--stuic-data-table-batch-padding-x);
|
|
247
259
|
background: var(--stuic-data-table-batch-bg);
|
|
248
260
|
border-radius: var(--stuic-data-table-radius, var(--stuic-radius));
|
|
249
|
-
margin
|
|
261
|
+
margin: var(--stuic-data-table-batch-margin, 0);
|
|
262
|
+
transition: background var(--stuic-data-table-transition, var(--stuic-transition));
|
|
250
263
|
}
|
|
251
264
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
265
|
+
.stuic-data-table-batch[data-idle="true"] {
|
|
266
|
+
background: var(--stuic-data-table-batch-bg-idle);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/* The select-all offer is the one state worth actively drawing attention to. */
|
|
270
|
+
.stuic-data-table-batch[data-select-all="true"] {
|
|
271
|
+
background: var(--stuic-data-table-select-all-bg);
|
|
272
|
+
}
|
|
255
273
|
|
|
256
|
-
.stuic-data-table-
|
|
274
|
+
.stuic-data-table-batch-main,
|
|
275
|
+
.stuic-data-table-batch-select-all {
|
|
257
276
|
display: flex;
|
|
258
277
|
align-items: center;
|
|
259
|
-
|
|
278
|
+
flex-wrap: wrap;
|
|
260
279
|
gap: var(--stuic-data-table-paging-gap);
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
.stuic-data-table-batch-select-all {
|
|
283
|
+
margin-left: auto;
|
|
284
|
+
font-size: var(--stuic-data-table-cell-font-size);
|
|
285
|
+
color: var(--stuic-data-table-header-color);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
.stuic-data-table-batch-status {
|
|
266
289
|
font-size: var(--stuic-data-table-cell-font-size);
|
|
267
290
|
color: var(--stuic-data-table-header-color);
|
|
268
|
-
text-align: center;
|
|
269
291
|
}
|
|
270
292
|
|
|
271
293
|
/* ============================================================================
|
|
@@ -1 +1,3 @@
|
|
|
1
1
|
export { default as DataTable, type Props as DataTableProps, type DataTableColumn, } from "./DataTable.svelte";
|
|
2
|
+
export { createDataTableT, DATA_TABLE_MESSAGES_EN, type DataTableMessageKey, type DataTableMessages, } from "./i18n.js";
|
|
3
|
+
export { DATA_TABLE_MESSAGES_SK } from "./i18n-sk.js";
|
|
@@ -180,14 +180,13 @@
|
|
|
180
180
|
{#if !expanded || !_hasMultiple}
|
|
181
181
|
{@render langInput(_defaultLanguage, true)}
|
|
182
182
|
{:else}
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
>
|
|
183
|
+
<!-- no `.expanded-wrap`/`.entry-divider` chrome here (unlike
|
|
184
|
+
FieldInputLocalized): these inputs carry their own border, so a
|
|
185
|
+
divider line and the wrap's right edge would only add noise —
|
|
186
|
+
plain spacing separates the rows instead -->
|
|
187
|
+
<div class="flex flex-col gap-1.5">
|
|
188
|
+
{#each sortedLanguages as lang (lang)}
|
|
189
|
+
<div class="flex-1 flex gap-2 items-center pl-2">
|
|
191
190
|
<div
|
|
192
191
|
class={twMerge(
|
|
193
192
|
"lang-label",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@marianmeres/stuic",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.164.0",
|
|
4
4
|
"packageManager": "pnpm@11.5.0",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"dev": "vite dev",
|
|
@@ -19,8 +19,8 @@
|
|
|
19
19
|
"test:ui": "vitest --ui",
|
|
20
20
|
"svelte-check": "svelte-check",
|
|
21
21
|
"svelte-package": "svelte-package",
|
|
22
|
-
"rp": "pnpm run build && deno run -A jsr:@marianmeres/release patch --no-push && npm publish --access public && git push --follow-tags",
|
|
23
|
-
"rpm": "pnpm run build && deno run -A jsr:@marianmeres/release minor --no-push && npm publish --access public && git push --follow-tags",
|
|
22
|
+
"rp": "pnpm run build && deno run -A jsr:@marianmeres/release patch --yes --no-push && npm publish --access public && git push --follow-tags",
|
|
23
|
+
"rpm": "pnpm run build && deno run -A jsr:@marianmeres/release minor --yes --no-push && npm publish --access public && git push --follow-tags",
|
|
24
24
|
"release": "deno run -A jsr:@marianmeres/release"
|
|
25
25
|
},
|
|
26
26
|
"files": [
|