@sentropic/design-system-svelte 0.34.50 → 0.34.52
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/README.md +62 -0
- package/dist/AnomalySwimLaneChart.svelte +10 -1
- package/dist/AnomalySwimLaneChart.svelte.d.ts +2 -0
- package/dist/AnomalySwimLaneChart.svelte.d.ts.map +1 -1
- package/dist/CalendarHeatmapChart.svelte +11 -1
- package/dist/CalendarHeatmapChart.svelte.d.ts +2 -0
- package/dist/CalendarHeatmapChart.svelte.d.ts.map +1 -1
- package/dist/Combobox.svelte +5 -1
- package/dist/Combobox.svelte.d.ts.map +1 -1
- package/dist/ContourChart.svelte +76 -13
- package/dist/ContourChart.svelte.d.ts +3 -1
- package/dist/ContourChart.svelte.d.ts.map +1 -1
- package/dist/CopyButton.svelte +9 -3
- package/dist/CopyButton.svelte.d.ts +1 -0
- package/dist/CopyButton.svelte.d.ts.map +1 -1
- package/dist/Dashboard.svelte +155 -0
- package/dist/Dashboard.svelte.d.ts +21 -0
- package/dist/Dashboard.svelte.d.ts.map +1 -0
- package/dist/DashboardGrid.svelte +237 -0
- package/dist/DashboardGrid.svelte.d.ts +24 -0
- package/dist/DashboardGrid.svelte.d.ts.map +1 -0
- package/dist/DataTable.svelte +3 -1
- package/dist/DataTable.svelte.d.ts +1 -0
- package/dist/DataTable.svelte.d.ts.map +1 -1
- package/dist/DatePicker.svelte +33 -28
- package/dist/DatePicker.svelte.d.ts.map +1 -1
- package/dist/Density2DChart.svelte +10 -1
- package/dist/Density2DChart.svelte.d.ts +2 -0
- package/dist/Density2DChart.svelte.d.ts.map +1 -1
- package/dist/Dropdown.svelte +40 -11
- package/dist/Dropdown.svelte.d.ts +1 -0
- package/dist/Dropdown.svelte.d.ts.map +1 -1
- package/dist/EventFeedPanel.svelte +3 -3
- package/dist/EventFeedPanel.svelte.d.ts +1 -1
- package/dist/EventFeedPanel.svelte.d.ts.map +1 -1
- package/dist/FileUploader.svelte +7 -3
- package/dist/Footer.svelte +75 -11
- package/dist/Footer.svelte.d.ts +16 -6
- package/dist/Footer.svelte.d.ts.map +1 -1
- package/dist/ForceGraph.svelte +9 -3
- package/dist/ForceGraph.svelte.d.ts +4 -0
- package/dist/ForceGraph.svelte.d.ts.map +1 -1
- package/dist/HeatmapChart.svelte +39 -3
- package/dist/HeatmapChart.svelte.d.ts +4 -1
- package/dist/HeatmapChart.svelte.d.ts.map +1 -1
- package/dist/KanbanBoard.svelte +144 -0
- package/dist/KanbanBoard.svelte.d.ts +23 -0
- package/dist/KanbanBoard.svelte.d.ts.map +1 -0
- package/dist/ListReportPage.svelte +184 -0
- package/dist/ListReportPage.svelte.d.ts +46 -0
- package/dist/ListReportPage.svelte.d.ts.map +1 -0
- package/dist/MasterDetail.svelte +267 -0
- package/dist/MasterDetail.svelte.d.ts +35 -0
- package/dist/MasterDetail.svelte.d.ts.map +1 -0
- package/dist/MultiSelect.svelte +20 -11
- package/dist/MultiSelect.svelte.d.ts +1 -0
- package/dist/MultiSelect.svelte.d.ts.map +1 -1
- package/dist/NavItem.svelte +6 -6
- package/dist/ObjectPage.svelte +222 -0
- package/dist/ObjectPage.svelte.d.ts +46 -0
- package/dist/ObjectPage.svelte.d.ts.map +1 -0
- package/dist/OrderedList.svelte +7 -12
- package/dist/OrderedList.svelte.d.ts.map +1 -1
- package/dist/PaginationNav.svelte +10 -4
- package/dist/PaginationNav.svelte.d.ts +1 -0
- package/dist/PaginationNav.svelte.d.ts.map +1 -1
- package/dist/PointAndFigureChart.svelte +18 -11
- package/dist/PointAndFigureChart.svelte.d.ts +1 -1
- package/dist/PointAndFigureChart.svelte.d.ts.map +1 -1
- package/dist/RenkoChart.svelte +40 -13
- package/dist/RenkoChart.svelte.d.ts +1 -1
- package/dist/RenkoChart.svelte.d.ts.map +1 -1
- package/dist/VectorFieldChart.svelte +5 -5
- package/dist/VectorFieldChart.svelte.d.ts +1 -1
- package/dist/VectorFieldChart.svelte.d.ts.map +1 -1
- package/dist/WindBarbChart.svelte +5 -5
- package/dist/WindBarbChart.svelte.d.ts +1 -1
- package/dist/WindBarbChart.svelte.d.ts.map +1 -1
- package/dist/Wizard.svelte +125 -0
- package/dist/Wizard.svelte.d.ts +25 -0
- package/dist/Wizard.svelte.d.ts.map +1 -0
- package/dist/index.d.ts +24 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -0
- package/package.json +1 -1
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
import type { SideNavItem } from "./SideNav.svelte";
|
|
3
|
+
|
|
4
|
+
export interface ListReportPageNavItem extends SideNavItem {}
|
|
5
|
+
|
|
6
|
+
export interface ListReportPageColumn {
|
|
7
|
+
key: string;
|
|
8
|
+
label: string;
|
|
9
|
+
sortable?: boolean;
|
|
10
|
+
align?: "start" | "center" | "end";
|
|
11
|
+
width?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface ListReportPageRow {
|
|
15
|
+
id: string;
|
|
16
|
+
[key: string]: unknown;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface ListReportPageFilter {
|
|
20
|
+
field: string;
|
|
21
|
+
value: string;
|
|
22
|
+
operator?: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface ListReportPageAction {
|
|
26
|
+
value: string;
|
|
27
|
+
label: string;
|
|
28
|
+
danger?: boolean;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export type ListReportPageProps = {
|
|
32
|
+
appTitle: string;
|
|
33
|
+
navItems?: ListReportPageNavItem[];
|
|
34
|
+
pageTitle: string;
|
|
35
|
+
primaryAction?: string;
|
|
36
|
+
secondaryAction?: string;
|
|
37
|
+
searchLabel?: string;
|
|
38
|
+
searchPlaceholder?: string;
|
|
39
|
+
filterBarLabel?: string;
|
|
40
|
+
filters?: ListReportPageFilter[];
|
|
41
|
+
columns: ListReportPageColumn[];
|
|
42
|
+
rows: ListReportPageRow[];
|
|
43
|
+
pageSize?: number;
|
|
44
|
+
rowActions?: ListReportPageAction[];
|
|
45
|
+
rowActionsLabel?: string;
|
|
46
|
+
onprimaryaction?: () => void;
|
|
47
|
+
onsearch?: (q: string) => void;
|
|
48
|
+
};
|
|
49
|
+
</script>
|
|
50
|
+
|
|
51
|
+
<script lang="ts">
|
|
52
|
+
import SideNav from "./SideNav.svelte";
|
|
53
|
+
import DataTable from "./DataTable.svelte";
|
|
54
|
+
import Search from "./Search.svelte";
|
|
55
|
+
import FilterBar from "./FilterBar.svelte";
|
|
56
|
+
import FilterPill from "./FilterPill.svelte";
|
|
57
|
+
import Button from "./Button.svelte";
|
|
58
|
+
|
|
59
|
+
let {
|
|
60
|
+
appTitle,
|
|
61
|
+
navItems = [],
|
|
62
|
+
pageTitle,
|
|
63
|
+
primaryAction,
|
|
64
|
+
secondaryAction,
|
|
65
|
+
searchLabel,
|
|
66
|
+
searchPlaceholder = "Rechercher…",
|
|
67
|
+
filterBarLabel = "Filtres actifs",
|
|
68
|
+
filters = [],
|
|
69
|
+
columns,
|
|
70
|
+
rows,
|
|
71
|
+
pageSize = 10,
|
|
72
|
+
rowActions = [],
|
|
73
|
+
rowActionsLabel,
|
|
74
|
+
onprimaryaction,
|
|
75
|
+
onsearch,
|
|
76
|
+
}: ListReportPageProps = $props();
|
|
77
|
+
|
|
78
|
+
let searchValue = $state("");
|
|
79
|
+
|
|
80
|
+
function handleSearch(q: string) {
|
|
81
|
+
searchValue = q;
|
|
82
|
+
onsearch?.(q);
|
|
83
|
+
}
|
|
84
|
+
</script>
|
|
85
|
+
|
|
86
|
+
<div class="st-lrp">
|
|
87
|
+
<header class="st-lrp__header">
|
|
88
|
+
<span class="st-lrp__appTitle">{appTitle}</span>
|
|
89
|
+
</header>
|
|
90
|
+
<div class="st-lrp__body">
|
|
91
|
+
{#if navItems.length > 0}
|
|
92
|
+
<aside class="st-lrp__aside">
|
|
93
|
+
<SideNav items={navItems} />
|
|
94
|
+
</aside>
|
|
95
|
+
{/if}
|
|
96
|
+
<main class="st-lrp__main">
|
|
97
|
+
<div class="st-lrp__titlebar">
|
|
98
|
+
<h1 class="st-lrp__pageTitle">{pageTitle}</h1>
|
|
99
|
+
<div class="st-lrp__titleActions">
|
|
100
|
+
{#if secondaryAction}
|
|
101
|
+
<Button variant="secondary">{secondaryAction}</Button>
|
|
102
|
+
{/if}
|
|
103
|
+
{#if primaryAction}
|
|
104
|
+
<Button variant="primary" onclick={onprimaryaction}>{primaryAction}</Button>
|
|
105
|
+
{/if}
|
|
106
|
+
</div>
|
|
107
|
+
</div>
|
|
108
|
+
<div class="st-lrp__toolbar">
|
|
109
|
+
<Search
|
|
110
|
+
label={searchLabel}
|
|
111
|
+
placeholder={searchPlaceholder}
|
|
112
|
+
value={searchValue}
|
|
113
|
+
fluid
|
|
114
|
+
oninput={(e) => handleSearch((e.target as HTMLInputElement).value)}
|
|
115
|
+
/>
|
|
116
|
+
{#if filters.length > 0}
|
|
117
|
+
<FilterBar label={filterBarLabel}>
|
|
118
|
+
{#each filters as f}
|
|
119
|
+
<FilterPill field={f.field} value={f.value} operator={f.operator} />
|
|
120
|
+
{/each}
|
|
121
|
+
</FilterBar>
|
|
122
|
+
{/if}
|
|
123
|
+
</div>
|
|
124
|
+
<DataTable {columns} {rows} />
|
|
125
|
+
</main>
|
|
126
|
+
</div>
|
|
127
|
+
</div>
|
|
128
|
+
|
|
129
|
+
<style>
|
|
130
|
+
.st-lrp {
|
|
131
|
+
display: grid;
|
|
132
|
+
grid-template-rows: auto 1fr;
|
|
133
|
+
min-block-size: 100vh;
|
|
134
|
+
background: var(--st-semantic-surface-default);
|
|
135
|
+
color: var(--st-semantic-text-primary);
|
|
136
|
+
}
|
|
137
|
+
.st-lrp__header {
|
|
138
|
+
border-block-end: 1px solid var(--st-semantic-border-subtle);
|
|
139
|
+
padding: var(--st-spacing-3, 0.75rem) var(--st-spacing-6, 1.5rem);
|
|
140
|
+
display: flex;
|
|
141
|
+
align-items: center;
|
|
142
|
+
}
|
|
143
|
+
.st-lrp__appTitle {
|
|
144
|
+
font-weight: 700;
|
|
145
|
+
font-size: 1rem;
|
|
146
|
+
}
|
|
147
|
+
.st-lrp__body {
|
|
148
|
+
display: grid;
|
|
149
|
+
grid-template-columns: 220px 1fr;
|
|
150
|
+
}
|
|
151
|
+
.st-lrp__aside {
|
|
152
|
+
border-inline-end: 1px solid var(--st-semantic-border-subtle);
|
|
153
|
+
background: var(--st-semantic-surface-raised);
|
|
154
|
+
overflow-y: auto;
|
|
155
|
+
}
|
|
156
|
+
.st-lrp__main {
|
|
157
|
+
display: flex;
|
|
158
|
+
flex-direction: column;
|
|
159
|
+
gap: var(--st-spacing-4, 1rem);
|
|
160
|
+
padding: var(--st-spacing-6, 1.5rem);
|
|
161
|
+
overflow: auto;
|
|
162
|
+
}
|
|
163
|
+
.st-lrp__titlebar {
|
|
164
|
+
display: flex;
|
|
165
|
+
align-items: center;
|
|
166
|
+
justify-content: space-between;
|
|
167
|
+
gap: var(--st-spacing-3, 0.75rem);
|
|
168
|
+
}
|
|
169
|
+
.st-lrp__pageTitle {
|
|
170
|
+
font-size: 1.5rem;
|
|
171
|
+
font-weight: 700;
|
|
172
|
+
margin: 0;
|
|
173
|
+
color: var(--st-semantic-text-primary);
|
|
174
|
+
}
|
|
175
|
+
.st-lrp__titleActions {
|
|
176
|
+
display: flex;
|
|
177
|
+
gap: var(--st-spacing-2, 0.5rem);
|
|
178
|
+
}
|
|
179
|
+
.st-lrp__toolbar {
|
|
180
|
+
display: flex;
|
|
181
|
+
flex-direction: column;
|
|
182
|
+
gap: var(--st-spacing-2, 0.5rem);
|
|
183
|
+
}
|
|
184
|
+
</style>
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { SideNavItem } from "./SideNav.svelte";
|
|
2
|
+
export interface ListReportPageNavItem extends SideNavItem {
|
|
3
|
+
}
|
|
4
|
+
export interface ListReportPageColumn {
|
|
5
|
+
key: string;
|
|
6
|
+
label: string;
|
|
7
|
+
sortable?: boolean;
|
|
8
|
+
align?: "start" | "center" | "end";
|
|
9
|
+
width?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface ListReportPageRow {
|
|
12
|
+
id: string;
|
|
13
|
+
[key: string]: unknown;
|
|
14
|
+
}
|
|
15
|
+
export interface ListReportPageFilter {
|
|
16
|
+
field: string;
|
|
17
|
+
value: string;
|
|
18
|
+
operator?: string;
|
|
19
|
+
}
|
|
20
|
+
export interface ListReportPageAction {
|
|
21
|
+
value: string;
|
|
22
|
+
label: string;
|
|
23
|
+
danger?: boolean;
|
|
24
|
+
}
|
|
25
|
+
export type ListReportPageProps = {
|
|
26
|
+
appTitle: string;
|
|
27
|
+
navItems?: ListReportPageNavItem[];
|
|
28
|
+
pageTitle: string;
|
|
29
|
+
primaryAction?: string;
|
|
30
|
+
secondaryAction?: string;
|
|
31
|
+
searchLabel?: string;
|
|
32
|
+
searchPlaceholder?: string;
|
|
33
|
+
filterBarLabel?: string;
|
|
34
|
+
filters?: ListReportPageFilter[];
|
|
35
|
+
columns: ListReportPageColumn[];
|
|
36
|
+
rows: ListReportPageRow[];
|
|
37
|
+
pageSize?: number;
|
|
38
|
+
rowActions?: ListReportPageAction[];
|
|
39
|
+
rowActionsLabel?: string;
|
|
40
|
+
onprimaryaction?: () => void;
|
|
41
|
+
onsearch?: (q: string) => void;
|
|
42
|
+
};
|
|
43
|
+
declare const ListReportPage: import("svelte").Component<ListReportPageProps, {}, "">;
|
|
44
|
+
type ListReportPage = ReturnType<typeof ListReportPage>;
|
|
45
|
+
export default ListReportPage;
|
|
46
|
+
//# sourceMappingURL=ListReportPage.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ListReportPage.svelte.d.ts","sourceRoot":"","sources":["../src/lib/ListReportPage.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD,MAAM,WAAW,qBAAsB,SAAQ,WAAW;CAAG;AAE7D,MAAM,WAAW,oBAAoB;IACnC,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,KAAK,CAAC;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,MAAM,mBAAmB,GAAG;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,qBAAqB,EAAE,CAAC;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,OAAO,CAAC,EAAE,oBAAoB,EAAE,CAAC;IACjC,OAAO,EAAE,oBAAoB,EAAE,CAAC;IAChC,IAAI,EAAE,iBAAiB,EAAE,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,oBAAoB,EAAE,CAAC;IACpC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,eAAe,CAAC,EAAE,MAAM,IAAI,CAAC;IAC7B,QAAQ,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CAChC,CAAC;AAwFJ,QAAA,MAAM,cAAc,yDAAwC,CAAC;AAC7D,KAAK,cAAc,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AACxD,eAAe,cAAc,CAAC"}
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
import type { SideNavItem } from "./SideNav.svelte";
|
|
3
|
+
|
|
4
|
+
export interface MasterDetailNavItem extends SideNavItem {}
|
|
5
|
+
|
|
6
|
+
export type MasterDetailStatusTone = "neutral" | "info" | "success" | "warning" | "error";
|
|
7
|
+
|
|
8
|
+
export interface MasterDetailItem {
|
|
9
|
+
id: string;
|
|
10
|
+
primary: string;
|
|
11
|
+
secondary?: string;
|
|
12
|
+
statusLabel?: string;
|
|
13
|
+
statusTone?: MasterDetailStatusTone;
|
|
14
|
+
active?: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface MasterDetailField {
|
|
18
|
+
key: string;
|
|
19
|
+
value: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export type MasterDetailProps = {
|
|
23
|
+
appTitle?: string;
|
|
24
|
+
navItems?: MasterDetailNavItem[];
|
|
25
|
+
listTitle: string;
|
|
26
|
+
searchPlaceholder?: string;
|
|
27
|
+
listItems: MasterDetailItem[];
|
|
28
|
+
detailTitle: string;
|
|
29
|
+
detailStatus?: { label: string; tone: MasterDetailStatusTone };
|
|
30
|
+
detailActions?: string[];
|
|
31
|
+
detailFields: MasterDetailField[];
|
|
32
|
+
onlistitemclick?: (id: string) => void;
|
|
33
|
+
};
|
|
34
|
+
</script>
|
|
35
|
+
|
|
36
|
+
<script lang="ts">
|
|
37
|
+
import SideNav from "./SideNav.svelte";
|
|
38
|
+
import Badge from "./Badge.svelte";
|
|
39
|
+
import Button from "./Button.svelte";
|
|
40
|
+
import Search from "./Search.svelte";
|
|
41
|
+
|
|
42
|
+
let {
|
|
43
|
+
appTitle,
|
|
44
|
+
navItems = [],
|
|
45
|
+
listTitle,
|
|
46
|
+
searchPlaceholder = "Rechercher…",
|
|
47
|
+
listItems,
|
|
48
|
+
detailTitle,
|
|
49
|
+
detailStatus,
|
|
50
|
+
detailActions = [],
|
|
51
|
+
detailFields,
|
|
52
|
+
onlistitemclick,
|
|
53
|
+
}: MasterDetailProps = $props();
|
|
54
|
+
|
|
55
|
+
let searchValue = $state("");
|
|
56
|
+
</script>
|
|
57
|
+
|
|
58
|
+
<div class="st-md">
|
|
59
|
+
{#if appTitle}
|
|
60
|
+
<header class="st-md__header">
|
|
61
|
+
<span class="st-md__appTitle">{appTitle}</span>
|
|
62
|
+
</header>
|
|
63
|
+
{/if}
|
|
64
|
+
<div class="st-md__body">
|
|
65
|
+
{#if navItems.length > 0}
|
|
66
|
+
<aside class="st-md__aside">
|
|
67
|
+
<SideNav items={navItems} />
|
|
68
|
+
</aside>
|
|
69
|
+
{/if}
|
|
70
|
+
<div class="st-md__split">
|
|
71
|
+
<aside class="st-md__list" aria-label={listTitle}>
|
|
72
|
+
<div class="st-md__listHeader">
|
|
73
|
+
<h2 class="st-md__listTitle">{listTitle}</h2>
|
|
74
|
+
<Search
|
|
75
|
+
placeholder={searchPlaceholder}
|
|
76
|
+
value={searchValue}
|
|
77
|
+
fluid
|
|
78
|
+
oninput={(e) => (searchValue = (e.target as HTMLInputElement).value)}
|
|
79
|
+
/>
|
|
80
|
+
</div>
|
|
81
|
+
<ul class="st-md__listItems">
|
|
82
|
+
{#each listItems as item (item.id)}
|
|
83
|
+
<li>
|
|
84
|
+
<button
|
|
85
|
+
class="st-md__listItem"
|
|
86
|
+
class:st-md__listItem--active={item.active}
|
|
87
|
+
onclick={() => onlistitemclick?.(item.id)}
|
|
88
|
+
type="button"
|
|
89
|
+
>
|
|
90
|
+
<span class="st-md__itemPrimary">{item.primary}</span>
|
|
91
|
+
{#if item.secondary}
|
|
92
|
+
<span class="st-md__itemSecondary">{item.secondary}</span>
|
|
93
|
+
{/if}
|
|
94
|
+
{#if item.statusLabel}
|
|
95
|
+
<Badge tone={item.statusTone ?? "neutral"}>{item.statusLabel}</Badge>
|
|
96
|
+
{/if}
|
|
97
|
+
</button>
|
|
98
|
+
</li>
|
|
99
|
+
{/each}
|
|
100
|
+
</ul>
|
|
101
|
+
</aside>
|
|
102
|
+
<main class="st-md__detail">
|
|
103
|
+
<div class="st-md__detailHeader">
|
|
104
|
+
<div class="st-md__detailTitleRow">
|
|
105
|
+
<h1 class="st-md__detailTitle">{detailTitle}</h1>
|
|
106
|
+
{#if detailStatus}
|
|
107
|
+
<Badge tone={detailStatus.tone}>{detailStatus.label}</Badge>
|
|
108
|
+
{/if}
|
|
109
|
+
</div>
|
|
110
|
+
{#if detailActions.length > 0}
|
|
111
|
+
<div class="st-md__detailActions">
|
|
112
|
+
{#each detailActions as action, i}
|
|
113
|
+
<Button variant={i === 0 ? "primary" : "secondary"}>{action}</Button>
|
|
114
|
+
{/each}
|
|
115
|
+
</div>
|
|
116
|
+
{/if}
|
|
117
|
+
</div>
|
|
118
|
+
<dl class="st-md__fields">
|
|
119
|
+
{#each detailFields as field}
|
|
120
|
+
<div class="st-md__fieldRow">
|
|
121
|
+
<dt class="st-md__fieldKey">{field.key}</dt>
|
|
122
|
+
<dd class="st-md__fieldValue">{field.value}</dd>
|
|
123
|
+
</div>
|
|
124
|
+
{/each}
|
|
125
|
+
</dl>
|
|
126
|
+
</main>
|
|
127
|
+
</div>
|
|
128
|
+
</div>
|
|
129
|
+
</div>
|
|
130
|
+
|
|
131
|
+
<style>
|
|
132
|
+
.st-md {
|
|
133
|
+
display: grid;
|
|
134
|
+
grid-template-rows: auto 1fr;
|
|
135
|
+
min-block-size: 100vh;
|
|
136
|
+
background: var(--st-semantic-surface-default);
|
|
137
|
+
color: var(--st-semantic-text-primary);
|
|
138
|
+
}
|
|
139
|
+
.st-md__header {
|
|
140
|
+
border-block-end: 1px solid var(--st-semantic-border-subtle);
|
|
141
|
+
padding: var(--st-spacing-3, 0.75rem) var(--st-spacing-6, 1.5rem);
|
|
142
|
+
display: flex;
|
|
143
|
+
align-items: center;
|
|
144
|
+
}
|
|
145
|
+
.st-md__appTitle {
|
|
146
|
+
font-weight: 700;
|
|
147
|
+
font-size: 1rem;
|
|
148
|
+
}
|
|
149
|
+
.st-md__body {
|
|
150
|
+
display: grid;
|
|
151
|
+
grid-template-columns: 220px 1fr;
|
|
152
|
+
overflow: hidden;
|
|
153
|
+
}
|
|
154
|
+
.st-md__aside {
|
|
155
|
+
border-inline-end: 1px solid var(--st-semantic-border-subtle);
|
|
156
|
+
background: var(--st-semantic-surface-raised);
|
|
157
|
+
overflow-y: auto;
|
|
158
|
+
}
|
|
159
|
+
.st-md__split {
|
|
160
|
+
display: grid;
|
|
161
|
+
grid-template-columns: 320px 1fr;
|
|
162
|
+
overflow: hidden;
|
|
163
|
+
}
|
|
164
|
+
.st-md__list {
|
|
165
|
+
border-inline-end: 1px solid var(--st-semantic-border-subtle);
|
|
166
|
+
display: flex;
|
|
167
|
+
flex-direction: column;
|
|
168
|
+
overflow: hidden;
|
|
169
|
+
}
|
|
170
|
+
.st-md__listHeader {
|
|
171
|
+
padding: var(--st-spacing-4, 1rem);
|
|
172
|
+
display: flex;
|
|
173
|
+
flex-direction: column;
|
|
174
|
+
gap: var(--st-spacing-2, 0.5rem);
|
|
175
|
+
border-block-end: 1px solid var(--st-semantic-border-subtle);
|
|
176
|
+
}
|
|
177
|
+
.st-md__listTitle {
|
|
178
|
+
font-size: 1rem;
|
|
179
|
+
font-weight: 650;
|
|
180
|
+
margin: 0;
|
|
181
|
+
}
|
|
182
|
+
.st-md__listItems {
|
|
183
|
+
list-style: none;
|
|
184
|
+
margin: 0;
|
|
185
|
+
padding: var(--st-spacing-2, 0.5rem);
|
|
186
|
+
overflow-y: auto;
|
|
187
|
+
flex: 1;
|
|
188
|
+
}
|
|
189
|
+
.st-md__listItem {
|
|
190
|
+
width: 100%;
|
|
191
|
+
text-align: start;
|
|
192
|
+
background: transparent;
|
|
193
|
+
border: none;
|
|
194
|
+
border-radius: var(--st-radius-md, 0.375rem);
|
|
195
|
+
padding: var(--st-spacing-3, 0.75rem);
|
|
196
|
+
cursor: pointer;
|
|
197
|
+
display: flex;
|
|
198
|
+
flex-direction: column;
|
|
199
|
+
gap: var(--st-spacing-1, 0.25rem);
|
|
200
|
+
color: var(--st-semantic-text-primary);
|
|
201
|
+
}
|
|
202
|
+
.st-md__listItem:hover {
|
|
203
|
+
background: var(--st-semantic-surface-subtle, #f1f5f9);
|
|
204
|
+
}
|
|
205
|
+
.st-md__listItem--active {
|
|
206
|
+
background: var(--st-semantic-surface-selected, #eff6ff);
|
|
207
|
+
}
|
|
208
|
+
.st-md__itemPrimary {
|
|
209
|
+
font-size: 0.875rem;
|
|
210
|
+
font-weight: 600;
|
|
211
|
+
}
|
|
212
|
+
.st-md__itemSecondary {
|
|
213
|
+
font-size: 0.75rem;
|
|
214
|
+
color: var(--st-semantic-text-secondary);
|
|
215
|
+
}
|
|
216
|
+
.st-md__detail {
|
|
217
|
+
display: flex;
|
|
218
|
+
flex-direction: column;
|
|
219
|
+
gap: var(--st-spacing-6, 1.5rem);
|
|
220
|
+
padding: var(--st-spacing-6, 1.5rem);
|
|
221
|
+
overflow-y: auto;
|
|
222
|
+
}
|
|
223
|
+
.st-md__detailHeader {
|
|
224
|
+
display: flex;
|
|
225
|
+
align-items: flex-start;
|
|
226
|
+
justify-content: space-between;
|
|
227
|
+
gap: var(--st-spacing-4, 1rem);
|
|
228
|
+
flex-wrap: wrap;
|
|
229
|
+
}
|
|
230
|
+
.st-md__detailTitleRow {
|
|
231
|
+
display: flex;
|
|
232
|
+
align-items: center;
|
|
233
|
+
gap: var(--st-spacing-3, 0.75rem);
|
|
234
|
+
flex-wrap: wrap;
|
|
235
|
+
}
|
|
236
|
+
.st-md__detailTitle {
|
|
237
|
+
font-size: 1.5rem;
|
|
238
|
+
font-weight: 700;
|
|
239
|
+
margin: 0;
|
|
240
|
+
}
|
|
241
|
+
.st-md__detailActions {
|
|
242
|
+
display: flex;
|
|
243
|
+
gap: var(--st-spacing-2, 0.5rem);
|
|
244
|
+
flex-shrink: 0;
|
|
245
|
+
}
|
|
246
|
+
.st-md__fields {
|
|
247
|
+
margin: 0;
|
|
248
|
+
display: grid;
|
|
249
|
+
grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr));
|
|
250
|
+
gap: var(--st-spacing-3, 0.75rem) var(--st-spacing-6, 1.5rem);
|
|
251
|
+
}
|
|
252
|
+
.st-md__fieldRow {
|
|
253
|
+
display: flex;
|
|
254
|
+
flex-direction: column;
|
|
255
|
+
gap: 0.2rem;
|
|
256
|
+
}
|
|
257
|
+
.st-md__fieldKey {
|
|
258
|
+
font-size: 0.75rem;
|
|
259
|
+
color: var(--st-semantic-text-secondary);
|
|
260
|
+
font-weight: 500;
|
|
261
|
+
}
|
|
262
|
+
.st-md__fieldValue {
|
|
263
|
+
margin: 0;
|
|
264
|
+
font-size: 0.875rem;
|
|
265
|
+
color: var(--st-semantic-text-primary);
|
|
266
|
+
}
|
|
267
|
+
</style>
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { SideNavItem } from "./SideNav.svelte";
|
|
2
|
+
export interface MasterDetailNavItem extends SideNavItem {
|
|
3
|
+
}
|
|
4
|
+
export type MasterDetailStatusTone = "neutral" | "info" | "success" | "warning" | "error";
|
|
5
|
+
export interface MasterDetailItem {
|
|
6
|
+
id: string;
|
|
7
|
+
primary: string;
|
|
8
|
+
secondary?: string;
|
|
9
|
+
statusLabel?: string;
|
|
10
|
+
statusTone?: MasterDetailStatusTone;
|
|
11
|
+
active?: boolean;
|
|
12
|
+
}
|
|
13
|
+
export interface MasterDetailField {
|
|
14
|
+
key: string;
|
|
15
|
+
value: string;
|
|
16
|
+
}
|
|
17
|
+
export type MasterDetailProps = {
|
|
18
|
+
appTitle?: string;
|
|
19
|
+
navItems?: MasterDetailNavItem[];
|
|
20
|
+
listTitle: string;
|
|
21
|
+
searchPlaceholder?: string;
|
|
22
|
+
listItems: MasterDetailItem[];
|
|
23
|
+
detailTitle: string;
|
|
24
|
+
detailStatus?: {
|
|
25
|
+
label: string;
|
|
26
|
+
tone: MasterDetailStatusTone;
|
|
27
|
+
};
|
|
28
|
+
detailActions?: string[];
|
|
29
|
+
detailFields: MasterDetailField[];
|
|
30
|
+
onlistitemclick?: (id: string) => void;
|
|
31
|
+
};
|
|
32
|
+
declare const MasterDetail: import("svelte").Component<MasterDetailProps, {}, "">;
|
|
33
|
+
type MasterDetail = ReturnType<typeof MasterDetail>;
|
|
34
|
+
export default MasterDetail;
|
|
35
|
+
//# sourceMappingURL=MasterDetail.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MasterDetail.svelte.d.ts","sourceRoot":"","sources":["../src/lib/MasterDetail.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD,MAAM,WAAW,mBAAoB,SAAQ,WAAW;CAAG;AAE3D,MAAM,MAAM,sBAAsB,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC;AAE1F,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,sBAAsB,CAAC;IACpC,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,mBAAmB,EAAE,CAAC;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,SAAS,EAAE,gBAAgB,EAAE,CAAC;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,sBAAsB,CAAA;KAAE,CAAC;IAC/D,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,YAAY,EAAE,iBAAiB,EAAE,CAAC;IAClC,eAAe,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;CACxC,CAAC;AAmGJ,QAAA,MAAM,YAAY,uDAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}
|
package/dist/MultiSelect.svelte
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
size?: "sm" | "md" | "lg";
|
|
19
19
|
options: MultiSelectOption[];
|
|
20
20
|
selected?: string[];
|
|
21
|
+
locale?: string;
|
|
21
22
|
placeholder?: string;
|
|
22
23
|
searchPlaceholder?: string;
|
|
23
24
|
noResultsLabel?: string;
|
|
@@ -37,11 +38,12 @@
|
|
|
37
38
|
size = "md",
|
|
38
39
|
options,
|
|
39
40
|
selected = $bindable([]),
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
41
|
+
locale = "fr-FR",
|
|
42
|
+
placeholder,
|
|
43
|
+
searchPlaceholder,
|
|
44
|
+
noResultsLabel,
|
|
45
|
+
toggleLabel,
|
|
46
|
+
removeLabel,
|
|
45
47
|
listLabel,
|
|
46
48
|
disabled = false,
|
|
47
49
|
class: className,
|
|
@@ -49,6 +51,13 @@
|
|
|
49
51
|
...rest
|
|
50
52
|
}: MultiSelectProps = $props();
|
|
51
53
|
|
|
54
|
+
const isFr = $derived(locale.toLowerCase().startsWith("fr"));
|
|
55
|
+
const resolvedPlaceholder = $derived(placeholder ?? (isFr ? "Sélectionner des éléments" : "Select items"));
|
|
56
|
+
const resolvedSearchPlaceholder = $derived(searchPlaceholder ?? (isFr ? "Filtrer" : "Filter"));
|
|
57
|
+
const resolvedNoResultsLabel = $derived(noResultsLabel ?? (isFr ? "Aucun résultat" : "No results"));
|
|
58
|
+
const resolvedToggleLabel = $derived(toggleLabel ?? (isFr ? "Afficher les options" : "Toggle options"));
|
|
59
|
+
const resolvedRemoveLabel = $derived(removeLabel ?? (isFr ? "Supprimer" : "Remove"));
|
|
60
|
+
|
|
52
61
|
let expanded = $state(false);
|
|
53
62
|
let query = $state("");
|
|
54
63
|
|
|
@@ -112,7 +121,7 @@
|
|
|
112
121
|
<button
|
|
113
122
|
type="button"
|
|
114
123
|
class="st-multiSelect__tagRemove"
|
|
115
|
-
aria-label={`${
|
|
124
|
+
aria-label={`${resolvedRemoveLabel} ${option.label}`}
|
|
116
125
|
{disabled}
|
|
117
126
|
onclick={() => removeOption(option.value)}
|
|
118
127
|
>
|
|
@@ -132,7 +141,7 @@
|
|
|
132
141
|
onclick={toggleOpen}
|
|
133
142
|
>
|
|
134
143
|
{#if selectedOptions.length === 0}
|
|
135
|
-
<span class="st-multiSelect__placeholder">{
|
|
144
|
+
<span class="st-multiSelect__placeholder">{resolvedPlaceholder}</span>
|
|
136
145
|
{:else}
|
|
137
146
|
<span class="st-multiSelect__count">{selectedOptions.length} selected</span>
|
|
138
147
|
{/if}
|
|
@@ -143,7 +152,7 @@
|
|
|
143
152
|
strokeWidth={2.25}
|
|
144
153
|
/>
|
|
145
154
|
</span>
|
|
146
|
-
<span class="st-visually-hidden">{
|
|
155
|
+
<span class="st-visually-hidden">{resolvedToggleLabel}</span>
|
|
147
156
|
</button>
|
|
148
157
|
</span>
|
|
149
158
|
{#if expanded}
|
|
@@ -151,13 +160,13 @@
|
|
|
151
160
|
<input
|
|
152
161
|
type="search"
|
|
153
162
|
class="st-multiSelect__search"
|
|
154
|
-
placeholder={
|
|
163
|
+
placeholder={resolvedSearchPlaceholder}
|
|
155
164
|
bind:value={query}
|
|
156
|
-
aria-label={
|
|
165
|
+
aria-label={resolvedSearchPlaceholder}
|
|
157
166
|
/>
|
|
158
167
|
<div class="st-multiSelect__list" role="listbox" aria-label={listLabel ?? label ?? "Options"} aria-multiselectable="true">
|
|
159
168
|
{#if filtered.length === 0}
|
|
160
|
-
<div class="st-multiSelect__empty">{
|
|
169
|
+
<div class="st-multiSelect__empty">{resolvedNoResultsLabel}</div>
|
|
161
170
|
{:else}
|
|
162
171
|
{#each filtered as option (option.value)}
|
|
163
172
|
{@const isSelected = selected.includes(option.value)}
|
|
@@ -12,6 +12,7 @@ type MultiSelectProps = Omit<HTMLAttributes<HTMLDivElement>, "class" | "onchange
|
|
|
12
12
|
size?: "sm" | "md" | "lg";
|
|
13
13
|
options: MultiSelectOption[];
|
|
14
14
|
selected?: string[];
|
|
15
|
+
locale?: string;
|
|
15
16
|
placeholder?: string;
|
|
16
17
|
searchPlaceholder?: string;
|
|
17
18
|
noResultsLabel?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MultiSelect.svelte.d.ts","sourceRoot":"","sources":["../src/lib/MultiSelect.svelte.ts"],"names":[],"mappings":"AAGE,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAIH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAGpD,KAAK,gBAAgB,GAAG,IAAI,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,OAAO,GAAG,UAAU,CAAC,GAAG;IACnF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IAC1B,OAAO,EAAE,iBAAiB,EAAE,CAAC;IAC7B,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;CACzC,CAAC;
|
|
1
|
+
{"version":3,"file":"MultiSelect.svelte.d.ts","sourceRoot":"","sources":["../src/lib/MultiSelect.svelte.ts"],"names":[],"mappings":"AAGE,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAIH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAGpD,KAAK,gBAAgB,GAAG,IAAI,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,OAAO,GAAG,UAAU,CAAC,GAAG;IACnF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IAC1B,OAAO,EAAE,iBAAiB,EAAE,CAAC;IAC7B,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;CACzC,CAAC;AA6IJ,QAAA,MAAM,WAAW,8DAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
|
package/dist/NavItem.svelte
CHANGED
|
@@ -87,8 +87,8 @@
|
|
|
87
87
|
selected = $bindable(false),
|
|
88
88
|
disabled = false,
|
|
89
89
|
href,
|
|
90
|
-
leading,
|
|
91
|
-
trailing,
|
|
90
|
+
leading: leadingProp,
|
|
91
|
+
trailing: trailingProp,
|
|
92
92
|
divider = false,
|
|
93
93
|
class: className
|
|
94
94
|
}: NavItemProps = $props();
|
|
@@ -150,8 +150,8 @@
|
|
|
150
150
|
caption={caption ? captionSnippet : undefined}
|
|
151
151
|
>
|
|
152
152
|
{#snippet leading()}
|
|
153
|
-
{#if
|
|
154
|
-
{@render
|
|
153
|
+
{#if leadingProp}
|
|
154
|
+
{@render leadingProp()}
|
|
155
155
|
{:else if swatch}
|
|
156
156
|
<!-- Tête : ColorSwatch pour une couleur arbitraire, sinon StatusDot pour
|
|
157
157
|
un ton sémantique. Décoratif → aria géré par la primitive. -->
|
|
@@ -168,8 +168,8 @@
|
|
|
168
168
|
{/snippet}
|
|
169
169
|
|
|
170
170
|
{#snippet trailing()}
|
|
171
|
-
{#if
|
|
172
|
-
{@render
|
|
171
|
+
{#if trailingProp}
|
|
172
|
+
{@render trailingProp()}
|
|
173
173
|
{:else if count != null}
|
|
174
174
|
<!-- Queue : bulle de compte. Badge shape="circle" size="sm" (tabular-nums),
|
|
175
175
|
ton sémantique aligné sur le `status` de la rangée. aria-label explicite. -->
|