@marianmeres/stuic 3.189.0 → 3.191.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 +2 -2
- package/API.md +37 -0
- package/dist/components/ListGroup/ListGroup.fixture.svelte +91 -0
- package/dist/components/ListGroup/ListGroup.fixture.svelte.d.ts +12 -0
- package/dist/components/ListGroup/ListGroup.svelte +232 -0
- package/dist/components/ListGroup/ListGroup.svelte.d.ts +116 -0
- package/dist/components/ListGroup/README.md +299 -0
- package/dist/components/ListGroup/index.css +279 -0
- package/dist/components/ListGroup/index.d.ts +1 -0
- package/dist/components/ListGroup/index.js +1 -0
- package/dist/components/RangeSlider/README.md +22 -22
- package/dist/components/RangeSlider/RangeSlider.svelte +4 -4
- package/dist/components/RangeSlider/RangeSlider.svelte.d.ts +2 -2
- package/dist/components/RangeSlider/index.css +9 -6
- package/dist/components/Slider/README.md +23 -24
- package/dist/components/Slider/Slider.svelte +4 -4
- package/dist/components/Slider/Slider.svelte.d.ts +2 -2
- package/dist/components/Slider/index.css +6 -3
- package/dist/index.css +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/docs/domains/components.md +55 -1
- package/package.json +1 -1
package/AGENTS.md
CHANGED
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
|
|
24
24
|
```
|
|
25
25
|
src/lib/
|
|
26
|
-
├── components/ #
|
|
26
|
+
├── components/ # 82 component directories
|
|
27
27
|
├── actions/ # 16 Svelte actions (use: directives)
|
|
28
28
|
├── attachments/ # Svelte attachments ({@attach} — preferred for new DOM helpers)
|
|
29
29
|
├── utils/ # 55 utility modules (48 on the barrel)
|
|
@@ -146,7 +146,7 @@ so it is the only confusable pair — do not "fix" one into the other.
|
|
|
146
146
|
|
|
147
147
|
### Domain Docs
|
|
148
148
|
|
|
149
|
-
- [Components](./docs/domains/components.md) —
|
|
149
|
+
- [Components](./docs/domains/components.md) — 82 component directories, Props pattern, snippets
|
|
150
150
|
- [Theming](./docs/domains/theming.md) — CSS tokens, dark mode, themes
|
|
151
151
|
- [CSS presets](./docs/domains/css-presets.md) — ratio-locked frame (letterbox), safe-area, scrollbar
|
|
152
152
|
- [Actions](./docs/domains/actions.md) — 16 Svelte directives
|
package/API.md
CHANGED
|
@@ -1273,6 +1273,43 @@ Horizontal schedule chart — project plans (task per row, progress, milestones)
|
|
|
1273
1273
|
|
|
1274
1274
|
The geometry is exported separately for axis-aligned overlays: `buildGanttAxis`, `placeRange`, `placePoint`, `dayToFraction`, `boundsOf`.
|
|
1275
1275
|
|
|
1276
|
+
#### `ListGroup`
|
|
1277
|
+
|
|
1278
|
+
Bordered, rounded box of rows split by hairlines, with an optional header (title + aside) and footer. Each row is one wrapping flex line of parts; `data-grow` on the part that should absorb the slack. Generic over the item type.
|
|
1279
|
+
|
|
1280
|
+
| Prop | Type | Default | Description |
|
|
1281
|
+
| ------------ | -------------------------------------- | ------- | ---------------------------------------------------------------------- |
|
|
1282
|
+
| `items` | `T[]` | — | The rows (without `renderItem`, each renders as `THC`) |
|
|
1283
|
+
| `renderItem` | `Snippet<[{ item, index }]>` | — | Row content |
|
|
1284
|
+
| `children` | `Snippet` | — | Hand-written `<li>`s instead of `items` (styled identically) |
|
|
1285
|
+
| `getItemId` | `(item, index) => string \| number` | index | Keyed identity |
|
|
1286
|
+
| `itemProps` | `(item, index) => ListGroupItemProps` | — | Attributes (`data-*`, `class`) on each `<li>` |
|
|
1287
|
+
| `itemHref` | `(item, index) => string \| undefined` | — | Makes the whole row a link |
|
|
1288
|
+
| `title` | `THC` | — | Header start side; labels the list |
|
|
1289
|
+
| `titleLevel` | `1…6` | — | Render the title as `<hN>` (semantics only) |
|
|
1290
|
+
| `aside` | `THC` | — | Header end side |
|
|
1291
|
+
| `footer` | `THC` | — | A line under the rows |
|
|
1292
|
+
| `empty` | `THC` | — | Replaces the list when there are no rows; without it, nothing renders |
|
|
1293
|
+
| `listProps` | `ListGroupListProps` | — | Attributes for the `<ul>` (e.g. `aria-label` when there is no `title`) |
|
|
1294
|
+
|
|
1295
|
+
```svelte
|
|
1296
|
+
<ListGroup
|
|
1297
|
+
class="text-sm"
|
|
1298
|
+
items={lines}
|
|
1299
|
+
getItemId={(l) => l.id}
|
|
1300
|
+
itemProps={(l) => ({ "data-line": l.id })}
|
|
1301
|
+
title="Loose items"
|
|
1302
|
+
aside="70 pc"
|
|
1303
|
+
empty="Nothing booked yet."
|
|
1304
|
+
>
|
|
1305
|
+
{#snippet renderItem({ item })}
|
|
1306
|
+
<span class="font-mono">{item.code}</span>
|
|
1307
|
+
<span data-grow>{item.name}</span>
|
|
1308
|
+
<span>×{item.qty}</span>
|
|
1309
|
+
{/snippet}
|
|
1310
|
+
</ListGroup>
|
|
1311
|
+
```
|
|
1312
|
+
|
|
1276
1313
|
#### `ImageCycler`
|
|
1277
1314
|
|
|
1278
1315
|
Auto-cycling image carousel with fade transitions. Preloads next image before displaying. Supports custom title/description snippets.
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
// Layout fixture: rows whose parts are DIRECT children of the <li> (a raw snippet can
|
|
3
|
+
// only render one root element), plus a hand-written `children` form to compare.
|
|
4
|
+
import ListGroup from "./ListGroup.svelte";
|
|
5
|
+
|
|
6
|
+
interface Line {
|
|
7
|
+
id: number;
|
|
8
|
+
code: string;
|
|
9
|
+
name: string;
|
|
10
|
+
qty: number;
|
|
11
|
+
location: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
let {
|
|
15
|
+
width = 1000,
|
|
16
|
+
mode = "items",
|
|
17
|
+
linked = false,
|
|
18
|
+
showRow = false,
|
|
19
|
+
style,
|
|
20
|
+
title = "Loose items",
|
|
21
|
+
}: {
|
|
22
|
+
width?: number;
|
|
23
|
+
mode?: "items" | "children" | "children-empty";
|
|
24
|
+
linked?: boolean;
|
|
25
|
+
showRow?: boolean;
|
|
26
|
+
style?: string;
|
|
27
|
+
title?: string;
|
|
28
|
+
} = $props();
|
|
29
|
+
|
|
30
|
+
const LINES: Line[] = [
|
|
31
|
+
{
|
|
32
|
+
id: 1,
|
|
33
|
+
code: "7QMET2GD",
|
|
34
|
+
name: "Folding chair",
|
|
35
|
+
qty: 40,
|
|
36
|
+
location: "hall-a.floor-a",
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
id: 2,
|
|
40
|
+
code: "89A6KMHY",
|
|
41
|
+
name: "Carpet tape, double-sided",
|
|
42
|
+
qty: 3,
|
|
43
|
+
location: "hall-a-raca.rack-a2.shelf-a2-2.consumables-bin",
|
|
44
|
+
},
|
|
45
|
+
];
|
|
46
|
+
</script>
|
|
47
|
+
|
|
48
|
+
<!-- the theme is not loaded in browser tests: `--stuic-color-border` / `-ring` are undefined, which
|
|
49
|
+
would make the rule and ring declarations invalid at computed-value time -->
|
|
50
|
+
<div
|
|
51
|
+
data-testid="frame"
|
|
52
|
+
style="width: {width}px; --stuic-list-group-rule-color: currentColor; --stuic-list-group-item-ring-color: currentColor;"
|
|
53
|
+
>
|
|
54
|
+
{#if mode === "items"}
|
|
55
|
+
<ListGroup
|
|
56
|
+
items={LINES}
|
|
57
|
+
getItemId={(l) => l.id}
|
|
58
|
+
itemHref={linked ? (l) => `#/line/${l.id}` : undefined}
|
|
59
|
+
{title}
|
|
60
|
+
{style}
|
|
61
|
+
>
|
|
62
|
+
{#snippet renderItem({ item })}
|
|
63
|
+
<span data-part="code">{item.code}</span>
|
|
64
|
+
<span data-part="name" data-grow>{item.name}</span>
|
|
65
|
+
<span data-part="qty">×{item.qty}</span>
|
|
66
|
+
<span data-part="picked">picked {item.qty}/{item.qty}</span>
|
|
67
|
+
<span data-part="location">{item.location}</span>
|
|
68
|
+
{/snippet}
|
|
69
|
+
</ListGroup>
|
|
70
|
+
{:else if mode === "children"}
|
|
71
|
+
<ListGroup {title} {style}>
|
|
72
|
+
<li data-row="plain">
|
|
73
|
+
<span data-part="code">7QMET2GD</span>
|
|
74
|
+
<span data-part="name" data-grow>Folding chair</span>
|
|
75
|
+
</li>
|
|
76
|
+
<li data-row="link">
|
|
77
|
+
<a href="#/search"><span data-part="name" data-grow>Search</span><kbd>/</kbd></a>
|
|
78
|
+
</li>
|
|
79
|
+
<li data-row="button">
|
|
80
|
+
<button type="button"><span>A button row</span></button>
|
|
81
|
+
</li>
|
|
82
|
+
<li data-row="stuic">
|
|
83
|
+
<button type="button" class="stuic-button">Kept</button>
|
|
84
|
+
</li>
|
|
85
|
+
</ListGroup>
|
|
86
|
+
{:else}
|
|
87
|
+
<ListGroup {title} footer="The footer">
|
|
88
|
+
{#if showRow}<li>A row</li>{/if}
|
|
89
|
+
</ListGroup>
|
|
90
|
+
{/if}
|
|
91
|
+
</div>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import ListGroup from "./ListGroup.svelte";
|
|
2
|
+
type $$ComponentProps = {
|
|
3
|
+
width?: number;
|
|
4
|
+
mode?: "items" | "children" | "children-empty";
|
|
5
|
+
linked?: boolean;
|
|
6
|
+
showRow?: boolean;
|
|
7
|
+
style?: string;
|
|
8
|
+
title?: string;
|
|
9
|
+
};
|
|
10
|
+
declare const ListGroup: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
11
|
+
type ListGroup = ReturnType<typeof ListGroup>;
|
|
12
|
+
export default ListGroup;
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
import type { HTMLAttributes, HTMLLiAttributes } from "svelte/elements";
|
|
3
|
+
import type { Snippet } from "svelte";
|
|
4
|
+
import type { THC } from "../Thc/Thc.svelte";
|
|
5
|
+
|
|
6
|
+
/** Renders the title as `<h1>`…`<h6>`. Semantics only — the look never changes. */
|
|
7
|
+
export type ListGroupTitleLevel = 1 | 2 | 3 | 4 | 5 | 6;
|
|
8
|
+
|
|
9
|
+
export interface ListGroupSnippetArg<T = unknown> {
|
|
10
|
+
item: T;
|
|
11
|
+
index: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* What `itemProps` may put on a row `<li>` — typically the `data-*` hooks a consumer's
|
|
16
|
+
* tests select on. `class` is a plain string so it can be merged after `classItem`.
|
|
17
|
+
*/
|
|
18
|
+
export type ListGroupItemProps = Omit<HTMLLiAttributes, "children" | "class"> & {
|
|
19
|
+
class?: string;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/** Attributes for the `<ul>` itself (`class` is `classList`, `role` is fixed) */
|
|
23
|
+
export type ListGroupListProps = Omit<
|
|
24
|
+
HTMLAttributes<HTMLUListElement>,
|
|
25
|
+
"children" | "class" | "role"
|
|
26
|
+
>;
|
|
27
|
+
|
|
28
|
+
export interface Props<T = unknown> extends Omit<
|
|
29
|
+
HTMLAttributes<HTMLDivElement>,
|
|
30
|
+
"children" | "title"
|
|
31
|
+
> {
|
|
32
|
+
/** The rows, in order. Data-driven form. */
|
|
33
|
+
items?: T[];
|
|
34
|
+
/**
|
|
35
|
+
* A row's content, rendered inside the `<li>` (or inside the `<a>` when `itemHref`
|
|
36
|
+
* returns a href). Without it an item is rendered as `THC`, so `items={["a", "b"]}`
|
|
37
|
+
* works as is.
|
|
38
|
+
*/
|
|
39
|
+
renderItem?: Snippet<[ListGroupSnippetArg<T>]>;
|
|
40
|
+
/**
|
|
41
|
+
* Compositional form: rendered inside the `<ul>` *instead of* `items`. Write `<li>`s;
|
|
42
|
+
* the structural CSS styles them exactly like generated rows.
|
|
43
|
+
*/
|
|
44
|
+
children?: Snippet;
|
|
45
|
+
/** Keyed `{#each}` identity. Defaults to the index. */
|
|
46
|
+
getItemId?: (item: T, index: number) => string | number;
|
|
47
|
+
/**
|
|
48
|
+
* Attributes spread onto each `<li>` — the `data-*` hooks consumers test against.
|
|
49
|
+
* A returned `class` is merged after `classItem`. `null`/`undefined` values omit the
|
|
50
|
+
* attribute. Identity comes from `getItemId`, never from here.
|
|
51
|
+
*/
|
|
52
|
+
itemProps?: (item: T, index: number) => ListGroupItemProps | undefined;
|
|
53
|
+
/**
|
|
54
|
+
* When it returns a href, the row's content is wrapped in
|
|
55
|
+
* `<a class="stuic-list-group-item-link">` and the anchor becomes the row box
|
|
56
|
+
* (padding, hover, focus ring). Falsy → a plain row. A linked row must not contain
|
|
57
|
+
* other interactive content.
|
|
58
|
+
*/
|
|
59
|
+
itemHref?: (item: T, index: number) => string | undefined | null;
|
|
60
|
+
/** The header's start side. Labels the list (`aria-labelledby`). */
|
|
61
|
+
title?: THC;
|
|
62
|
+
/** Render the title as `<hN>` instead of a `<div>`. Semantics only. */
|
|
63
|
+
titleLevel?: ListGroupTitleLevel;
|
|
64
|
+
/**
|
|
65
|
+
* The header's end side: a sum, a count, a shortfall, an action. Never an automatic
|
|
66
|
+
* row count — say what the figure is.
|
|
67
|
+
*/
|
|
68
|
+
aside?: THC;
|
|
69
|
+
/** A line inside the box, under the rows, above a rule. */
|
|
70
|
+
footer?: THC;
|
|
71
|
+
/**
|
|
72
|
+
* Rendered in place of the `<ul>` when `items` is empty (or absent) and there are no
|
|
73
|
+
* `children`. Without it, a group with no rows renders nothing at all.
|
|
74
|
+
*/
|
|
75
|
+
empty?: THC;
|
|
76
|
+
/**
|
|
77
|
+
* Attributes for the `<ul>`. The escape hatch for labelling a list that has no
|
|
78
|
+
* `title` (`aria-label`, or `aria-labelledby` pointing at a caption outside the box)
|
|
79
|
+
* — an `aria-label` on the root `<div>` would name nothing.
|
|
80
|
+
*/
|
|
81
|
+
listProps?: ListGroupListProps;
|
|
82
|
+
/** Skip all default styling */
|
|
83
|
+
unstyled?: boolean;
|
|
84
|
+
/** Additional CSS classes for the root */
|
|
85
|
+
class?: string;
|
|
86
|
+
/** Class for the header row */
|
|
87
|
+
classHeader?: string;
|
|
88
|
+
/** Class for the title */
|
|
89
|
+
classTitle?: string;
|
|
90
|
+
/** Class for the aside */
|
|
91
|
+
classAside?: string;
|
|
92
|
+
/** Class for the `<ul>` */
|
|
93
|
+
classList?: string;
|
|
94
|
+
/** Class for every generated `<li>` */
|
|
95
|
+
classItem?: string;
|
|
96
|
+
/** Class for every generated `<a>` (`itemHref`) */
|
|
97
|
+
classItemLink?: string;
|
|
98
|
+
/** Class for the empty state */
|
|
99
|
+
classEmpty?: string;
|
|
100
|
+
/** Class for the footer */
|
|
101
|
+
classFooter?: string;
|
|
102
|
+
/** Bindable root element reference */
|
|
103
|
+
el?: HTMLDivElement;
|
|
104
|
+
}
|
|
105
|
+
</script>
|
|
106
|
+
|
|
107
|
+
<script lang="ts" generics="T = unknown">
|
|
108
|
+
import { twMerge } from "../../utils/tw-merge.js";
|
|
109
|
+
import { getId } from "../../utils/get-id.js";
|
|
110
|
+
import Thc, { isTHCNotEmpty } from "../Thc/Thc.svelte";
|
|
111
|
+
|
|
112
|
+
let {
|
|
113
|
+
items,
|
|
114
|
+
renderItem,
|
|
115
|
+
children,
|
|
116
|
+
getItemId = (_item: T, index: number) => index,
|
|
117
|
+
itemProps,
|
|
118
|
+
itemHref,
|
|
119
|
+
title,
|
|
120
|
+
titleLevel,
|
|
121
|
+
aside,
|
|
122
|
+
footer,
|
|
123
|
+
empty,
|
|
124
|
+
listProps,
|
|
125
|
+
unstyled = false,
|
|
126
|
+
class: classProp,
|
|
127
|
+
classHeader: classHeaderProp,
|
|
128
|
+
classTitle: classTitleProp,
|
|
129
|
+
classAside: classAsideProp,
|
|
130
|
+
classList: classListProp,
|
|
131
|
+
classItem: classItemProp,
|
|
132
|
+
classItemLink: classItemLinkProp,
|
|
133
|
+
classEmpty: classEmptyProp,
|
|
134
|
+
classFooter: classFooterProp,
|
|
135
|
+
el = $bindable(),
|
|
136
|
+
...rest
|
|
137
|
+
}: Props<T> = $props();
|
|
138
|
+
|
|
139
|
+
const titleId = getId("stuic-list-group-title-");
|
|
140
|
+
|
|
141
|
+
let rows = $derived(items ?? []);
|
|
142
|
+
let hasRows = $derived(!!children || rows.length > 0);
|
|
143
|
+
let hasTitle = $derived(isTHCNotEmpty(title));
|
|
144
|
+
let hasAside = $derived(isTHCNotEmpty(aside));
|
|
145
|
+
let hasFooter = $derived(isTHCNotEmpty(footer));
|
|
146
|
+
let hasEmpty = $derived(isTHCNotEmpty(empty));
|
|
147
|
+
|
|
148
|
+
let _titleTag = $derived(
|
|
149
|
+
titleLevel && titleLevel >= 1 && titleLevel <= 6 ? `h${titleLevel}` : "div"
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
// Under `unstyled` a part keeps only the consumer's classes — `undefined` rather than
|
|
153
|
+
// `class=""` when there are none.
|
|
154
|
+
const _cls = (base: string, ...extra: (string | undefined)[]) =>
|
|
155
|
+
(unstyled ? twMerge(...extra) : twMerge(base, ...extra)) || undefined;
|
|
156
|
+
|
|
157
|
+
let _class = $derived(unstyled ? classProp : twMerge("stuic-list-group", classProp));
|
|
158
|
+
let _classHeader = $derived(_cls("stuic-list-group-header", classHeaderProp));
|
|
159
|
+
let _classTitle = $derived(_cls("stuic-list-group-title", classTitleProp));
|
|
160
|
+
let _classAside = $derived(_cls("stuic-list-group-aside", classAsideProp));
|
|
161
|
+
let _classList = $derived(_cls("stuic-list-group-list", classListProp));
|
|
162
|
+
let _classItemLink = $derived(_cls("stuic-list-group-item-link", classItemLinkProp));
|
|
163
|
+
let _classEmpty = $derived(_cls("stuic-list-group-empty", classEmptyProp));
|
|
164
|
+
let _classFooter = $derived(_cls("stuic-list-group-footer", classFooterProp));
|
|
165
|
+
|
|
166
|
+
/** `itemProps` split into the attributes to spread and the class to merge */
|
|
167
|
+
const _itemAttrs = (item: T, index: number) => {
|
|
168
|
+
const { class: itemClass, ...attrs }: ListGroupItemProps =
|
|
169
|
+
itemProps?.(item, index) ?? {};
|
|
170
|
+
return { attrs, class: _cls("stuic-list-group-item", classItemProp, itemClass) };
|
|
171
|
+
};
|
|
172
|
+
</script>
|
|
173
|
+
|
|
174
|
+
{#snippet content(item: T, index: number)}
|
|
175
|
+
{#if renderItem}
|
|
176
|
+
{@render renderItem({ item, index })}
|
|
177
|
+
{:else}
|
|
178
|
+
<Thc thc={item as THC} />
|
|
179
|
+
{/if}
|
|
180
|
+
{/snippet}
|
|
181
|
+
|
|
182
|
+
{#if hasRows || hasEmpty}
|
|
183
|
+
<div bind:this={el} class={_class} {...rest}>
|
|
184
|
+
<!-- A <div>, not a <header>: outside <main>/sectioning content (a drawer, a
|
|
185
|
+
dialog) a <header> is a `banner` landmark, one per group. -->
|
|
186
|
+
{#if hasTitle || hasAside}
|
|
187
|
+
<div class={_classHeader}>
|
|
188
|
+
{#if hasTitle}
|
|
189
|
+
<svelte:element this={_titleTag} id={titleId} class={_classTitle}>
|
|
190
|
+
<Thc thc={title!} />
|
|
191
|
+
</svelte:element>
|
|
192
|
+
{/if}
|
|
193
|
+
{#if hasAside}
|
|
194
|
+
<div class={_classAside}><Thc thc={aside!} /></div>
|
|
195
|
+
{/if}
|
|
196
|
+
</div>
|
|
197
|
+
{/if}
|
|
198
|
+
|
|
199
|
+
{#if hasRows}
|
|
200
|
+
<!-- WebKit drops list semantics from a `list-style: none` <ul> without it -->
|
|
201
|
+
<ul
|
|
202
|
+
role="list"
|
|
203
|
+
aria-labelledby={hasTitle ? titleId : undefined}
|
|
204
|
+
{...listProps}
|
|
205
|
+
class={_classList}
|
|
206
|
+
>
|
|
207
|
+
{#if children}
|
|
208
|
+
{@render children()}
|
|
209
|
+
{:else}
|
|
210
|
+
{#each rows as item, index (getItemId(item, index))}
|
|
211
|
+
{@const li = _itemAttrs(item, index)}
|
|
212
|
+
{@const href = itemHref?.(item, index)}
|
|
213
|
+
<li {...li.attrs} class={li.class}>
|
|
214
|
+
{#if href}
|
|
215
|
+
<a {href} class={_classItemLink}>{@render content(item, index)}</a>
|
|
216
|
+
{:else}
|
|
217
|
+
{@render content(item, index)}
|
|
218
|
+
{/if}
|
|
219
|
+
</li>
|
|
220
|
+
{/each}
|
|
221
|
+
{/if}
|
|
222
|
+
</ul>
|
|
223
|
+
{:else}
|
|
224
|
+
<div class={_classEmpty}><Thc thc={empty!} /></div>
|
|
225
|
+
{/if}
|
|
226
|
+
|
|
227
|
+
<!-- A <div>, not a <footer>: same landmark reason as the header -->
|
|
228
|
+
{#if hasFooter}
|
|
229
|
+
<div class={_classFooter}><Thc thc={footer!} /></div>
|
|
230
|
+
{/if}
|
|
231
|
+
</div>
|
|
232
|
+
{/if}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import type { HTMLAttributes, HTMLLiAttributes } from "svelte/elements";
|
|
2
|
+
import type { Snippet } from "svelte";
|
|
3
|
+
import type { THC } from "../Thc/Thc.svelte";
|
|
4
|
+
/** Renders the title as `<h1>`…`<h6>`. Semantics only — the look never changes. */
|
|
5
|
+
export type ListGroupTitleLevel = 1 | 2 | 3 | 4 | 5 | 6;
|
|
6
|
+
export interface ListGroupSnippetArg<T = unknown> {
|
|
7
|
+
item: T;
|
|
8
|
+
index: number;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* What `itemProps` may put on a row `<li>` — typically the `data-*` hooks a consumer's
|
|
12
|
+
* tests select on. `class` is a plain string so it can be merged after `classItem`.
|
|
13
|
+
*/
|
|
14
|
+
export type ListGroupItemProps = Omit<HTMLLiAttributes, "children" | "class"> & {
|
|
15
|
+
class?: string;
|
|
16
|
+
};
|
|
17
|
+
/** Attributes for the `<ul>` itself (`class` is `classList`, `role` is fixed) */
|
|
18
|
+
export type ListGroupListProps = Omit<HTMLAttributes<HTMLUListElement>, "children" | "class" | "role">;
|
|
19
|
+
export interface Props<T = unknown> extends Omit<HTMLAttributes<HTMLDivElement>, "children" | "title"> {
|
|
20
|
+
/** The rows, in order. Data-driven form. */
|
|
21
|
+
items?: T[];
|
|
22
|
+
/**
|
|
23
|
+
* A row's content, rendered inside the `<li>` (or inside the `<a>` when `itemHref`
|
|
24
|
+
* returns a href). Without it an item is rendered as `THC`, so `items={["a", "b"]}`
|
|
25
|
+
* works as is.
|
|
26
|
+
*/
|
|
27
|
+
renderItem?: Snippet<[ListGroupSnippetArg<T>]>;
|
|
28
|
+
/**
|
|
29
|
+
* Compositional form: rendered inside the `<ul>` *instead of* `items`. Write `<li>`s;
|
|
30
|
+
* the structural CSS styles them exactly like generated rows.
|
|
31
|
+
*/
|
|
32
|
+
children?: Snippet;
|
|
33
|
+
/** Keyed `{#each}` identity. Defaults to the index. */
|
|
34
|
+
getItemId?: (item: T, index: number) => string | number;
|
|
35
|
+
/**
|
|
36
|
+
* Attributes spread onto each `<li>` — the `data-*` hooks consumers test against.
|
|
37
|
+
* A returned `class` is merged after `classItem`. `null`/`undefined` values omit the
|
|
38
|
+
* attribute. Identity comes from `getItemId`, never from here.
|
|
39
|
+
*/
|
|
40
|
+
itemProps?: (item: T, index: number) => ListGroupItemProps | undefined;
|
|
41
|
+
/**
|
|
42
|
+
* When it returns a href, the row's content is wrapped in
|
|
43
|
+
* `<a class="stuic-list-group-item-link">` and the anchor becomes the row box
|
|
44
|
+
* (padding, hover, focus ring). Falsy → a plain row. A linked row must not contain
|
|
45
|
+
* other interactive content.
|
|
46
|
+
*/
|
|
47
|
+
itemHref?: (item: T, index: number) => string | undefined | null;
|
|
48
|
+
/** The header's start side. Labels the list (`aria-labelledby`). */
|
|
49
|
+
title?: THC;
|
|
50
|
+
/** Render the title as `<hN>` instead of a `<div>`. Semantics only. */
|
|
51
|
+
titleLevel?: ListGroupTitleLevel;
|
|
52
|
+
/**
|
|
53
|
+
* The header's end side: a sum, a count, a shortfall, an action. Never an automatic
|
|
54
|
+
* row count — say what the figure is.
|
|
55
|
+
*/
|
|
56
|
+
aside?: THC;
|
|
57
|
+
/** A line inside the box, under the rows, above a rule. */
|
|
58
|
+
footer?: THC;
|
|
59
|
+
/**
|
|
60
|
+
* Rendered in place of the `<ul>` when `items` is empty (or absent) and there are no
|
|
61
|
+
* `children`. Without it, a group with no rows renders nothing at all.
|
|
62
|
+
*/
|
|
63
|
+
empty?: THC;
|
|
64
|
+
/**
|
|
65
|
+
* Attributes for the `<ul>`. The escape hatch for labelling a list that has no
|
|
66
|
+
* `title` (`aria-label`, or `aria-labelledby` pointing at a caption outside the box)
|
|
67
|
+
* — an `aria-label` on the root `<div>` would name nothing.
|
|
68
|
+
*/
|
|
69
|
+
listProps?: ListGroupListProps;
|
|
70
|
+
/** Skip all default styling */
|
|
71
|
+
unstyled?: boolean;
|
|
72
|
+
/** Additional CSS classes for the root */
|
|
73
|
+
class?: string;
|
|
74
|
+
/** Class for the header row */
|
|
75
|
+
classHeader?: string;
|
|
76
|
+
/** Class for the title */
|
|
77
|
+
classTitle?: string;
|
|
78
|
+
/** Class for the aside */
|
|
79
|
+
classAside?: string;
|
|
80
|
+
/** Class for the `<ul>` */
|
|
81
|
+
classList?: string;
|
|
82
|
+
/** Class for every generated `<li>` */
|
|
83
|
+
classItem?: string;
|
|
84
|
+
/** Class for every generated `<a>` (`itemHref`) */
|
|
85
|
+
classItemLink?: string;
|
|
86
|
+
/** Class for the empty state */
|
|
87
|
+
classEmpty?: string;
|
|
88
|
+
/** Class for the footer */
|
|
89
|
+
classFooter?: string;
|
|
90
|
+
/** Bindable root element reference */
|
|
91
|
+
el?: HTMLDivElement;
|
|
92
|
+
}
|
|
93
|
+
declare function $$render<T = unknown>(): {
|
|
94
|
+
props: Props<T>;
|
|
95
|
+
exports: {};
|
|
96
|
+
bindings: "el";
|
|
97
|
+
slots: {};
|
|
98
|
+
events: {};
|
|
99
|
+
};
|
|
100
|
+
declare class __sveltets_Render<T = unknown> {
|
|
101
|
+
props(): ReturnType<typeof $$render<T>>['props'];
|
|
102
|
+
events(): ReturnType<typeof $$render<T>>['events'];
|
|
103
|
+
slots(): ReturnType<typeof $$render<T>>['slots'];
|
|
104
|
+
bindings(): "el";
|
|
105
|
+
exports(): {};
|
|
106
|
+
}
|
|
107
|
+
interface $$IsomorphicComponent {
|
|
108
|
+
new <T = unknown>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<T>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<T>['props']>, ReturnType<__sveltets_Render<T>['events']>, ReturnType<__sveltets_Render<T>['slots']>> & {
|
|
109
|
+
$$bindings?: ReturnType<__sveltets_Render<T>['bindings']>;
|
|
110
|
+
} & ReturnType<__sveltets_Render<T>['exports']>;
|
|
111
|
+
<T = unknown>(internal: unknown, props: ReturnType<__sveltets_Render<T>['props']> & {}): ReturnType<__sveltets_Render<T>['exports']>;
|
|
112
|
+
z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
|
|
113
|
+
}
|
|
114
|
+
declare const ListGroup: $$IsomorphicComponent;
|
|
115
|
+
type ListGroup<T = unknown> = InstanceType<typeof ListGroup<T>>;
|
|
116
|
+
export default ListGroup;
|