@vantageos/mosaic 0.3.1 → 0.5.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/CHANGELOG.md +88 -2
- package/LICENSE +110 -0
- package/README.md +193 -21
- package/dist/PipelineBoard-FucyGArR.d.cts +759 -0
- package/dist/PipelineBoard-FucyGArR.d.ts +759 -0
- package/dist/PipelineBoard.markdown-DIcvrS-2.d.cts +16 -0
- package/dist/PipelineBoard.markdown-DIcvrS-2.d.ts +16 -0
- package/dist/{StatusBadge.schema-1tOjB4Ir.d.cts → StatusBadge.schema-BEjSH2_4.d.cts} +2 -2
- package/dist/{StatusBadge.schema-1tOjB4Ir.d.ts → StatusBadge.schema-BEjSH2_4.d.ts} +2 -2
- package/dist/{Tabs.schema-BzcEyeq8.d.cts → Tabs.schema-Dqr6vwV3.d.cts} +2 -2
- package/dist/{Tabs.schema-BzcEyeq8.d.ts → Tabs.schema-Dqr6vwV3.d.ts} +2 -2
- package/dist/Timeline-Coo_3tA_.d.cts +114 -0
- package/dist/Timeline-Coo_3tA_.d.ts +114 -0
- package/dist/Timeline.markdown-C6UPDv1H.d.cts +4 -0
- package/dist/Timeline.markdown-C6UPDv1H.d.ts +4 -0
- package/dist/TransactionPreview-BzWDn8kE.d.cts +391 -0
- package/dist/TransactionPreview-BzWDn8kE.d.ts +391 -0
- package/dist/TransactionPreview.markdown-Dpb0VYgN.d.cts +7 -0
- package/dist/TransactionPreview.markdown-Dpb0VYgN.d.ts +7 -0
- package/dist/artifacts.cjs +190 -2
- package/dist/artifacts.cjs.map +1 -1
- package/dist/artifacts.js +190 -2
- package/dist/artifacts.js.map +1 -1
- package/dist/confirmation.cjs +728 -2
- package/dist/confirmation.cjs.map +1 -1
- package/dist/confirmation.d.cts +2 -1
- package/dist/confirmation.d.ts +2 -1
- package/dist/confirmation.js +726 -4
- package/dist/confirmation.js.map +1 -1
- package/dist/display.cjs +1555 -203
- package/dist/display.cjs.map +1 -1
- package/dist/display.d.cts +6 -229
- package/dist/display.d.ts +6 -229
- package/dist/display.js +1539 -206
- package/dist/display.js.map +1 -1
- package/dist/forms.cjs +139 -46
- package/dist/forms.cjs.map +1 -1
- package/dist/forms.d.cts +2 -2
- package/dist/forms.d.ts +2 -2
- package/dist/forms.js +139 -46
- package/dist/forms.js.map +1 -1
- package/dist/host.cjs +822 -0
- package/dist/host.cjs.map +1 -0
- package/dist/host.d.cts +287 -0
- package/dist/host.d.ts +287 -0
- package/dist/host.js +794 -0
- package/dist/host.js.map +1 -0
- package/dist/{index-CwXVgtGb.d.cts → index-BadRcCbm.d.cts} +28 -11
- package/dist/{index-CwXVgtGb.d.ts → index-BadRcCbm.d.ts} +28 -11
- package/dist/index.cjs +2202 -247
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +42 -8
- package/dist/index.d.ts +42 -8
- package/dist/index.js +2167 -251
- package/dist/index.js.map +1 -1
- package/dist/input.d.cts +2 -2
- package/dist/input.d.ts +2 -2
- package/dist/media.cjs +190 -2
- package/dist/media.cjs.map +1 -1
- package/dist/media.d.cts +1 -1
- package/dist/media.d.ts +1 -1
- package/dist/media.js +190 -2
- package/dist/media.js.map +1 -1
- package/dist/preact/artifacts.js +190 -2
- package/dist/preact/artifacts.js.map +1 -1
- package/dist/preact/confirmation.d.ts +321 -1
- package/dist/preact/confirmation.js +629 -4
- package/dist/preact/confirmation.js.map +1 -1
- package/dist/preact/display.d.ts +570 -31
- package/dist/preact/display.js +1248 -126
- package/dist/preact/display.js.map +1 -1
- package/dist/preact/forms.d.ts +32 -15
- package/dist/preact/forms.js +139 -46
- package/dist/preact/forms.js.map +1 -1
- package/dist/preact/host.d.ts +307 -0
- package/dist/preact/host.js +875 -0
- package/dist/preact/host.js.map +1 -0
- package/dist/preact/media.d.ts +37 -1
- package/dist/preact/media.js +423 -0
- package/dist/preact/media.js.map +1 -1
- package/dist/preact/progress.d.ts +92 -1
- package/dist/preact/progress.js +469 -3
- package/dist/preact/progress.js.map +1 -1
- package/dist/progress.cjs +525 -2
- package/dist/progress.cjs.map +1 -1
- package/dist/progress.d.cts +2 -1
- package/dist/progress.d.ts +2 -1
- package/dist/progress.js +522 -3
- package/dist/progress.js.map +1 -1
- package/dist/react/artifacts.cjs +190 -2
- package/dist/react/artifacts.cjs.map +1 -1
- package/dist/react/artifacts.js +190 -2
- package/dist/react/artifacts.js.map +1 -1
- package/dist/react/confirmation.cjs +630 -2
- package/dist/react/confirmation.cjs.map +1 -1
- package/dist/react/confirmation.d.cts +3 -3
- package/dist/react/confirmation.d.ts +3 -3
- package/dist/react/confirmation.js +629 -4
- package/dist/react/confirmation.js.map +1 -1
- package/dist/react/display.cjs +1137 -123
- package/dist/react/display.cjs.map +1 -1
- package/dist/react/display.d.cts +42 -14
- package/dist/react/display.d.ts +42 -14
- package/dist/react/display.js +1122 -126
- package/dist/react/display.js.map +1 -1
- package/dist/react/forms.cjs +139 -46
- package/dist/react/forms.cjs.map +1 -1
- package/dist/react/forms.d.cts +1 -1
- package/dist/react/forms.d.ts +1 -1
- package/dist/react/forms.js +139 -46
- package/dist/react/forms.js.map +1 -1
- package/dist/react/host.cjs +910 -0
- package/dist/react/host.cjs.map +1 -0
- package/dist/react/host.d.cts +24 -0
- package/dist/react/host.d.ts +24 -0
- package/dist/react/host.js +875 -0
- package/dist/react/host.js.map +1 -0
- package/dist/react/input.d.cts +1 -1
- package/dist/react/input.d.ts +1 -1
- package/dist/react/media.cjs +426 -0
- package/dist/react/media.cjs.map +1 -1
- package/dist/react/media.d.cts +19 -1
- package/dist/react/media.d.ts +19 -1
- package/dist/react/media.js +423 -0
- package/dist/react/media.js.map +1 -1
- package/dist/react/progress.cjs +471 -2
- package/dist/react/progress.cjs.map +1 -1
- package/dist/react/progress.d.cts +1 -1
- package/dist/react/progress.d.ts +1 -1
- package/dist/react/progress.js +469 -3
- package/dist/react/progress.js.map +1 -1
- package/dist/server/resource.cjs +151 -0
- package/dist/server/resource.cjs.map +1 -0
- package/dist/server/resource.d.cts +90 -0
- package/dist/server/resource.d.ts +90 -0
- package/dist/server/resource.js +147 -0
- package/dist/server/resource.js.map +1 -0
- package/dist/server.cjs +1100 -25
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +84 -5
- package/dist/server.d.ts +84 -5
- package/dist/server.js +1087 -27
- package/dist/server.js.map +1 -1
- package/dist/tokens.cjs +73 -22
- package/dist/tokens.cjs.map +1 -1
- package/dist/tokens.d.cts +23 -3
- package/dist/tokens.d.ts +23 -3
- package/dist/tokens.js +73 -22
- package/dist/tokens.js.map +1 -1
- package/package.json +250 -231
- package/registry.yaml +51 -2
- package/dist/Alert.schema-LPJE5gFP.d.cts +0 -71
- package/dist/Alert.schema-LPJE5gFP.d.ts +0 -71
- package/dist/ProgressBar.schema-CdVYx0Wq.d.cts +0 -22
- package/dist/ProgressBar.schema-CdVYx0Wq.d.ts +0 -22
- package/dist/{ConfirmDialog-B3CQQGmJ.d.cts → ConfirmDialog-BCtOcmHe.d.cts} +1 -1
- package/dist/{ConfirmDialog-B3CQQGmJ.d.ts → ConfirmDialog-BCtOcmHe.d.ts} +1 -1
package/dist/display.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useVirtualizer } from '@tanstack/react-virtual';
|
|
2
|
-
import { useState, useEffect, useRef, useCallback } from 'react';
|
|
2
|
+
import { useState, useEffect, useMemo, useRef, useCallback } from 'react';
|
|
3
3
|
import { z } from 'zod';
|
|
4
|
-
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
4
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
5
5
|
|
|
6
6
|
// src/components/display/TableView.tsx
|
|
7
7
|
|
|
@@ -27,6 +27,8 @@ var EN = {
|
|
|
27
27
|
"TokenDisplayOnceModal.copied": "Copied",
|
|
28
28
|
"StatusBadge.aria.status": "Status",
|
|
29
29
|
"StatusBadge.error.invalidProps": "StatusBadge: invalid props",
|
|
30
|
+
"Badge.aria.label": "Badge",
|
|
31
|
+
"Badge.error.invalidProps": "Badge: invalid props",
|
|
30
32
|
"EmptyState.error.invalidProps": "EmptyState: invalid props",
|
|
31
33
|
"Toast.aria.close": "Close notification",
|
|
32
34
|
"Toast.error.invalidProps": "Toast: invalid props",
|
|
@@ -96,7 +98,99 @@ var EN = {
|
|
|
96
98
|
"Select.search.placeholder": "Search\u2026",
|
|
97
99
|
"Select.empty": "No options available",
|
|
98
100
|
"Select.error.required": "Select an option",
|
|
99
|
-
"Select.error.invalidProps": "Select: invalid props"
|
|
101
|
+
"Select.error.invalidProps": "Select: invalid props",
|
|
102
|
+
"ModelContext.selection.one": "1 row selected",
|
|
103
|
+
"ModelContext.selection.many": "{n} rows selected",
|
|
104
|
+
"ModelContext.selection.none": "No rows selected",
|
|
105
|
+
"TableView.sort.by": "Sort by",
|
|
106
|
+
"TableView.select.row": "Select row",
|
|
107
|
+
"TableView.select.all": "Select all rows",
|
|
108
|
+
"TableView.loading": "Loading table",
|
|
109
|
+
"StatCard.title": "Statistic",
|
|
110
|
+
"StatCard.trend.up": "Up",
|
|
111
|
+
"StatCard.trend.down": "Down",
|
|
112
|
+
"StatCard.trend.flat": "Flat",
|
|
113
|
+
"StatCard.empty.message": "No statistic to display",
|
|
114
|
+
"StatCard.loading": "Loading statistic",
|
|
115
|
+
"StatCard.error.invalidProps": "StatCard: invalid props",
|
|
116
|
+
"BalanceCard.title": "Balance",
|
|
117
|
+
"BalanceCard.network": "Network",
|
|
118
|
+
"BalanceCard.testnet": "Testnet",
|
|
119
|
+
"BalanceCard.address": "Address",
|
|
120
|
+
"BalanceCard.contract": "Token contract",
|
|
121
|
+
"BalanceCard.value": "Value",
|
|
122
|
+
"BalanceCard.copy": "Copy address",
|
|
123
|
+
"BalanceCard.copied": "Address copied",
|
|
124
|
+
"BalanceCard.empty.message": "No balance to display",
|
|
125
|
+
"BalanceCard.loading": "Loading balance",
|
|
126
|
+
"BalanceCard.error.invalidProps": "BalanceCard: invalid props",
|
|
127
|
+
"Timeline.title": "Timeline",
|
|
128
|
+
"Timeline.status.done": "Done",
|
|
129
|
+
"Timeline.status.current": "In progress",
|
|
130
|
+
"Timeline.status.pending": "Pending",
|
|
131
|
+
"Timeline.status.blocked": "Blocked",
|
|
132
|
+
"Timeline.completed": "{n} completed",
|
|
133
|
+
"Timeline.more": "{n} more",
|
|
134
|
+
"Timeline.empty.message": "No steps to display",
|
|
135
|
+
"Timeline.loading": "Loading timeline",
|
|
136
|
+
"Timeline.error.invalidProps": "Timeline: invalid props",
|
|
137
|
+
"MessageFeed.title": "Messages",
|
|
138
|
+
"MessageFeed.unread": "Unread",
|
|
139
|
+
"MessageFeed.attachment": "Attachment",
|
|
140
|
+
"MessageFeed.showing": "Showing the latest {n} of {total}",
|
|
141
|
+
"MessageFeed.empty.message": "No messages",
|
|
142
|
+
"MessageFeed.loading": "Loading messages",
|
|
143
|
+
"MessageFeed.error.invalidProps": "MessageFeed: invalid props",
|
|
144
|
+
"PipelineBoard.title": "Pipeline",
|
|
145
|
+
"PipelineBoard.deals.one": "1 deal",
|
|
146
|
+
"PipelineBoard.deals.many": "{n} deals",
|
|
147
|
+
"PipelineBoard.total": "Total",
|
|
148
|
+
"PipelineBoard.stage.empty": "No deals",
|
|
149
|
+
"PipelineBoard.filter.label": "Show stages",
|
|
150
|
+
"PipelineBoard.summary.open": "Open full board",
|
|
151
|
+
"PipelineBoard.more": "{n} more stages",
|
|
152
|
+
"PipelineBoard.empty.message": "No pipeline to display",
|
|
153
|
+
"PipelineBoard.loading": "Loading pipeline",
|
|
154
|
+
"PipelineBoard.error.invalidProps": "PipelineBoard: invalid props",
|
|
155
|
+
"PipelineBoard.col.stage": "Stage",
|
|
156
|
+
"PipelineBoard.col.deals": "Deals",
|
|
157
|
+
"TransactionPreview.title": "Transaction preview",
|
|
158
|
+
"TransactionPreview.notice": "NOT SIGNED - preparation only",
|
|
159
|
+
"TransactionPreview.kind.transfer": "Transfer",
|
|
160
|
+
"TransactionPreview.kind.swap": "Swap",
|
|
161
|
+
"TransactionPreview.testnet": "Testnet",
|
|
162
|
+
"TransactionPreview.field.network": "Network",
|
|
163
|
+
"TransactionPreview.field.from": "From",
|
|
164
|
+
"TransactionPreview.field.to": "To",
|
|
165
|
+
"TransactionPreview.field.amount": "Amount",
|
|
166
|
+
"TransactionPreview.field.fee": "Network fee",
|
|
167
|
+
"TransactionPreview.field.sell": "Sell",
|
|
168
|
+
"TransactionPreview.field.buy": "Buy",
|
|
169
|
+
"TransactionPreview.field.minReceived": "Minimum received",
|
|
170
|
+
"TransactionPreview.field.route": "Route",
|
|
171
|
+
"TransactionPreview.field.priceImpact": "Price impact",
|
|
172
|
+
"TransactionPreview.field.expires": "Quote expires",
|
|
173
|
+
"TransactionPreview.priceImpact.high": "High price impact",
|
|
174
|
+
"TransactionPreview.simulation.success": "Simulation succeeded",
|
|
175
|
+
"TransactionPreview.simulation.failed": "Simulation failed",
|
|
176
|
+
"TransactionPreview.simulation.unavailable": "Simulation unavailable",
|
|
177
|
+
"TransactionPreview.warnings": "Warnings",
|
|
178
|
+
"TransactionPreview.handoff": "Open in wallet",
|
|
179
|
+
"TransactionPreview.empty.message": "No transaction to preview",
|
|
180
|
+
"TransactionPreview.loading": "Loading preview",
|
|
181
|
+
"TransactionPreview.error.invalidProps": "TransactionPreview: invalid props",
|
|
182
|
+
"ProgressBar.title": "Progress",
|
|
183
|
+
"ConfirmDialog.title": "Confirmation",
|
|
184
|
+
"ConfirmDialog.options": "Answers",
|
|
185
|
+
"TableView.title": "Data table",
|
|
186
|
+
"TableView.markdown.more": "{n} more rows",
|
|
187
|
+
"MarkdownRenderer.title": "Markdown content",
|
|
188
|
+
"TokenDisplayOnceModal.title": "Token (shown once)",
|
|
189
|
+
"StatusBadge.title": "Status",
|
|
190
|
+
"TokenDisplayOnceModal.notice.once": "The value is shown once, in the app view only.",
|
|
191
|
+
"TokenDisplayOnceModal.field.scope": "Scope",
|
|
192
|
+
"TokenDisplayOnceModal.field.expires": "Expires",
|
|
193
|
+
"TokenDisplayOnceModal.field.fingerprint": "Fingerprint"
|
|
100
194
|
};
|
|
101
195
|
var FR = {
|
|
102
196
|
// ─── Non-forms components ──────────────────────────────────────────────────
|
|
@@ -119,6 +213,8 @@ var FR = {
|
|
|
119
213
|
"TokenDisplayOnceModal.copied": "Copi\xE9",
|
|
120
214
|
"StatusBadge.aria.status": "Statut",
|
|
121
215
|
"StatusBadge.error.invalidProps": "StatusBadge : propri\xE9t\xE9s invalides",
|
|
216
|
+
"Badge.aria.label": "Badge",
|
|
217
|
+
"Badge.error.invalidProps": "Badge : propri\xE9t\xE9s invalides",
|
|
122
218
|
"EmptyState.error.invalidProps": "EmptyState : propri\xE9t\xE9s invalides",
|
|
123
219
|
"Toast.aria.close": "Fermer la notification",
|
|
124
220
|
"Toast.error.invalidProps": "Toast : propri\xE9t\xE9s invalides",
|
|
@@ -188,7 +284,99 @@ var FR = {
|
|
|
188
284
|
"Select.search.placeholder": "Rechercher\u2026",
|
|
189
285
|
"Select.empty": "Aucune option disponible",
|
|
190
286
|
"Select.error.required": "S\xE9lectionnez une option",
|
|
191
|
-
"Select.error.invalidProps": "Select : propri\xE9t\xE9s invalides"
|
|
287
|
+
"Select.error.invalidProps": "Select : propri\xE9t\xE9s invalides",
|
|
288
|
+
"ModelContext.selection.one": "1 ligne s\xE9lectionn\xE9e",
|
|
289
|
+
"ModelContext.selection.many": "{n} lignes s\xE9lectionn\xE9es",
|
|
290
|
+
"ModelContext.selection.none": "Aucune ligne s\xE9lectionn\xE9e",
|
|
291
|
+
"TableView.sort.by": "Trier par",
|
|
292
|
+
"TableView.select.row": "S\xE9lectionner la ligne",
|
|
293
|
+
"TableView.select.all": "Tout s\xE9lectionner",
|
|
294
|
+
"TableView.loading": "Chargement du tableau",
|
|
295
|
+
"StatCard.title": "Statistique",
|
|
296
|
+
"StatCard.trend.up": "En hausse",
|
|
297
|
+
"StatCard.trend.down": "En baisse",
|
|
298
|
+
"StatCard.trend.flat": "Stable",
|
|
299
|
+
"StatCard.empty.message": "Aucune statistique \xE0 afficher",
|
|
300
|
+
"StatCard.loading": "Chargement de la statistique",
|
|
301
|
+
"StatCard.error.invalidProps": "StatCard : propri\xE9t\xE9s invalides",
|
|
302
|
+
"BalanceCard.title": "Solde",
|
|
303
|
+
"BalanceCard.network": "R\xE9seau",
|
|
304
|
+
"BalanceCard.testnet": "R\xE9seau de test",
|
|
305
|
+
"BalanceCard.address": "Adresse",
|
|
306
|
+
"BalanceCard.contract": "Contrat du jeton",
|
|
307
|
+
"BalanceCard.value": "Valeur",
|
|
308
|
+
"BalanceCard.copy": "Copier l'adresse",
|
|
309
|
+
"BalanceCard.copied": "Adresse copi\xE9e",
|
|
310
|
+
"BalanceCard.empty.message": "Aucun solde \xE0 afficher",
|
|
311
|
+
"BalanceCard.loading": "Chargement du solde",
|
|
312
|
+
"BalanceCard.error.invalidProps": "BalanceCard : propri\xE9t\xE9s invalides",
|
|
313
|
+
"Timeline.title": "Chronologie",
|
|
314
|
+
"Timeline.status.done": "Termin\xE9",
|
|
315
|
+
"Timeline.status.current": "En cours",
|
|
316
|
+
"Timeline.status.pending": "\xC0 venir",
|
|
317
|
+
"Timeline.status.blocked": "Bloqu\xE9",
|
|
318
|
+
"Timeline.completed": "{n} termin\xE9(s)",
|
|
319
|
+
"Timeline.more": "{n} de plus",
|
|
320
|
+
"Timeline.empty.message": "Aucune \xE9tape \xE0 afficher",
|
|
321
|
+
"Timeline.loading": "Chargement de la chronologie",
|
|
322
|
+
"Timeline.error.invalidProps": "Timeline : propri\xE9t\xE9s invalides",
|
|
323
|
+
"MessageFeed.title": "Messages",
|
|
324
|
+
"MessageFeed.unread": "Non lu",
|
|
325
|
+
"MessageFeed.attachment": "Pi\xE8ce jointe",
|
|
326
|
+
"MessageFeed.showing": "Affichage des {n} derniers sur {total}",
|
|
327
|
+
"MessageFeed.empty.message": "Aucun message",
|
|
328
|
+
"MessageFeed.loading": "Chargement des messages",
|
|
329
|
+
"MessageFeed.error.invalidProps": "MessageFeed : propri\xE9t\xE9s invalides",
|
|
330
|
+
"PipelineBoard.title": "Pipeline",
|
|
331
|
+
"PipelineBoard.deals.one": "1 affaire",
|
|
332
|
+
"PipelineBoard.deals.many": "{n} affaires",
|
|
333
|
+
"PipelineBoard.total": "Total",
|
|
334
|
+
"PipelineBoard.stage.empty": "Aucune affaire",
|
|
335
|
+
"PipelineBoard.filter.label": "Afficher les \xE9tapes",
|
|
336
|
+
"PipelineBoard.summary.open": "Ouvrir le tableau complet",
|
|
337
|
+
"PipelineBoard.more": "{n} \xE9tapes de plus",
|
|
338
|
+
"PipelineBoard.empty.message": "Aucun pipeline \xE0 afficher",
|
|
339
|
+
"PipelineBoard.loading": "Chargement du pipeline",
|
|
340
|
+
"PipelineBoard.error.invalidProps": "PipelineBoard : propri\xE9t\xE9s invalides",
|
|
341
|
+
"PipelineBoard.col.stage": "\xC9tape",
|
|
342
|
+
"PipelineBoard.col.deals": "Affaires",
|
|
343
|
+
"TransactionPreview.title": "Aper\xE7u de transaction",
|
|
344
|
+
"TransactionPreview.notice": "NON SIGN\xC9 - pr\xE9paration uniquement",
|
|
345
|
+
"TransactionPreview.kind.transfer": "Transfert",
|
|
346
|
+
"TransactionPreview.kind.swap": "\xC9change",
|
|
347
|
+
"TransactionPreview.testnet": "R\xE9seau de test",
|
|
348
|
+
"TransactionPreview.field.network": "R\xE9seau",
|
|
349
|
+
"TransactionPreview.field.from": "De",
|
|
350
|
+
"TransactionPreview.field.to": "\xC0",
|
|
351
|
+
"TransactionPreview.field.amount": "Montant",
|
|
352
|
+
"TransactionPreview.field.fee": "Frais de r\xE9seau",
|
|
353
|
+
"TransactionPreview.field.sell": "Vendre",
|
|
354
|
+
"TransactionPreview.field.buy": "Acheter",
|
|
355
|
+
"TransactionPreview.field.minReceived": "Minimum re\xE7u",
|
|
356
|
+
"TransactionPreview.field.route": "Itin\xE9raire",
|
|
357
|
+
"TransactionPreview.field.priceImpact": "Impact sur le prix",
|
|
358
|
+
"TransactionPreview.field.expires": "Expiration du devis",
|
|
359
|
+
"TransactionPreview.priceImpact.high": "Impact sur le prix \xE9lev\xE9",
|
|
360
|
+
"TransactionPreview.simulation.success": "Simulation r\xE9ussie",
|
|
361
|
+
"TransactionPreview.simulation.failed": "Simulation \xE9chou\xE9e",
|
|
362
|
+
"TransactionPreview.simulation.unavailable": "Simulation indisponible",
|
|
363
|
+
"TransactionPreview.warnings": "Avertissements",
|
|
364
|
+
"TransactionPreview.handoff": "Ouvrir dans le portefeuille",
|
|
365
|
+
"TransactionPreview.empty.message": "Aucune transaction \xE0 pr\xE9visualiser",
|
|
366
|
+
"TransactionPreview.loading": "Chargement de l'aper\xE7u",
|
|
367
|
+
"TransactionPreview.error.invalidProps": "TransactionPreview : propri\xE9t\xE9s invalides",
|
|
368
|
+
"ProgressBar.title": "Progression",
|
|
369
|
+
"ConfirmDialog.title": "Confirmation",
|
|
370
|
+
"ConfirmDialog.options": "R\xE9ponses",
|
|
371
|
+
"TableView.title": "Tableau de donn\xE9es",
|
|
372
|
+
"TableView.markdown.more": "{n} lignes de plus",
|
|
373
|
+
"MarkdownRenderer.title": "Contenu Markdown",
|
|
374
|
+
"TokenDisplayOnceModal.title": "Jeton (affich\xE9 une fois)",
|
|
375
|
+
"StatusBadge.title": "Statut",
|
|
376
|
+
"TokenDisplayOnceModal.notice.once": "La valeur n'est affich\xE9e qu'une fois, dans la vue de l'application uniquement.",
|
|
377
|
+
"TokenDisplayOnceModal.field.scope": "Port\xE9e",
|
|
378
|
+
"TokenDisplayOnceModal.field.expires": "Expire le",
|
|
379
|
+
"TokenDisplayOnceModal.field.fingerprint": "Empreinte"
|
|
192
380
|
};
|
|
193
381
|
var TABLE = {
|
|
194
382
|
en: EN,
|
|
@@ -197,92 +385,279 @@ var TABLE = {
|
|
|
197
385
|
function t(key, locale = "en") {
|
|
198
386
|
return TABLE[locale]?.[key] ?? TABLE.en[key];
|
|
199
387
|
}
|
|
388
|
+
|
|
389
|
+
// src/components/display/Skeleton.logic.ts
|
|
390
|
+
function getAnimationClass(animation) {
|
|
391
|
+
switch (animation) {
|
|
392
|
+
case "pulse":
|
|
393
|
+
return "animate-pulse";
|
|
394
|
+
case "wave":
|
|
395
|
+
return "skeleton-wave";
|
|
396
|
+
case "none":
|
|
397
|
+
return "";
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
function getVariantClass(variant) {
|
|
401
|
+
switch (variant) {
|
|
402
|
+
case "rect":
|
|
403
|
+
return "rounded-md";
|
|
404
|
+
case "circle":
|
|
405
|
+
return "rounded-full";
|
|
406
|
+
case "text":
|
|
407
|
+
return "rounded";
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
var SKELETON_BG_CLASS = "bg-gray-200 dark:bg-gray-700";
|
|
411
|
+
function textLineKey(index) {
|
|
412
|
+
return `skeleton-line-${index}`;
|
|
413
|
+
}
|
|
414
|
+
function lastLineWidth(index, total, baseWidth) {
|
|
415
|
+
if (total > 1 && index === total - 1) {
|
|
416
|
+
return "75%";
|
|
417
|
+
}
|
|
418
|
+
return baseWidth;
|
|
419
|
+
}
|
|
420
|
+
var SkeletonPropsSchema = z.object({
|
|
421
|
+
variant: z.enum(["rect", "circle", "text"]).default("rect"),
|
|
422
|
+
width: z.union([z.string().min(1), z.number().positive()]).optional(),
|
|
423
|
+
height: z.union([z.string().min(1), z.number().positive()]).optional(),
|
|
424
|
+
count: z.number().int().min(1).max(20).default(1),
|
|
425
|
+
animation: z.enum(["pulse", "wave", "none"]).default("pulse"),
|
|
426
|
+
locale: z.enum(["en", "fr"]).default("en")
|
|
427
|
+
});
|
|
428
|
+
function validateSkeletonProps(raw) {
|
|
429
|
+
return SkeletonPropsSchema.parse(raw);
|
|
430
|
+
}
|
|
431
|
+
function resolveSkeletonDimensions(variant, width, height) {
|
|
432
|
+
const toCss = (v, fallback) => {
|
|
433
|
+
if (v === void 0) return fallback;
|
|
434
|
+
if (typeof v === "number") return `${v}px`;
|
|
435
|
+
return v;
|
|
436
|
+
};
|
|
437
|
+
if (variant === "circle") {
|
|
438
|
+
const size = width !== void 0 ? toCss(width, "40px") : height !== void 0 ? toCss(height, "40px") : "40px";
|
|
439
|
+
return { widthCss: size, heightCss: size };
|
|
440
|
+
}
|
|
441
|
+
if (variant === "text") {
|
|
442
|
+
return {
|
|
443
|
+
widthCss: toCss(width, "100%"),
|
|
444
|
+
heightCss: toCss(height, "16px")
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
return {
|
|
448
|
+
widthCss: toCss(width, "100%"),
|
|
449
|
+
heightCss: toCss(height, "100px")
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
function SkeletonInner({ variant, width, height, count, animation, locale }) {
|
|
453
|
+
const { widthCss, heightCss } = resolveSkeletonDimensions(variant, width, height);
|
|
454
|
+
const animClass = getAnimationClass(animation);
|
|
455
|
+
const variantClass = getVariantClass(variant);
|
|
456
|
+
const ariaLabel = t("Skeleton.aria.label", locale);
|
|
457
|
+
const baseClass = `${SKELETON_BG_CLASS} ${variantClass} ${animClass}`.trim();
|
|
458
|
+
if (variant === "text") {
|
|
459
|
+
return (
|
|
460
|
+
// biome-ignore lint/a11y/useSemanticElements: role="status" is the correct ARIA pattern for loading indicators; <output> conveys form output semantics which is incorrect here
|
|
461
|
+
/* @__PURE__ */ jsx("div", { role: "status", "aria-label": ariaLabel, "aria-busy": "true", className: "flex flex-col gap-2", children: Array.from({ length: count }, (_, i) => /* @__PURE__ */ jsx(
|
|
462
|
+
"span",
|
|
463
|
+
{
|
|
464
|
+
className: `block ${baseClass}`,
|
|
465
|
+
style: {
|
|
466
|
+
width: lastLineWidth(i, count, widthCss),
|
|
467
|
+
height: heightCss
|
|
468
|
+
},
|
|
469
|
+
"aria-hidden": "true"
|
|
470
|
+
},
|
|
471
|
+
textLineKey(i)
|
|
472
|
+
)) })
|
|
473
|
+
);
|
|
474
|
+
}
|
|
475
|
+
return /* @__PURE__ */ jsx(
|
|
476
|
+
"div",
|
|
477
|
+
{
|
|
478
|
+
role: "status",
|
|
479
|
+
"aria-label": ariaLabel,
|
|
480
|
+
"aria-busy": "true",
|
|
481
|
+
className: `block ${baseClass}`,
|
|
482
|
+
style: { width: widthCss, height: heightCss }
|
|
483
|
+
}
|
|
484
|
+
);
|
|
485
|
+
}
|
|
486
|
+
function Skeleton(raw) {
|
|
487
|
+
try {
|
|
488
|
+
const props = validateSkeletonProps(raw);
|
|
489
|
+
return /* @__PURE__ */ jsx(SkeletonInner, { ...props });
|
|
490
|
+
} catch {
|
|
491
|
+
const locale = raw.locale === "fr" ? "fr" : "en";
|
|
492
|
+
return /* @__PURE__ */ jsx("span", { role: "alert", className: "text-red-600 text-sm", children: t("Skeleton.error.invalidProps", locale) });
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
// src/components/display/TableView.logic.ts
|
|
497
|
+
function isMissing(v) {
|
|
498
|
+
return v === null || v === void 0 || v === "";
|
|
499
|
+
}
|
|
500
|
+
var DECIMAL = /^(-)?(\d+)(?:\.(\d+))?$/;
|
|
501
|
+
function parseDecimal(s) {
|
|
502
|
+
const m = DECIMAL.exec(s.trim());
|
|
503
|
+
if (!m) return void 0;
|
|
504
|
+
const int = (m[2] ?? "0").replace(/^0+(?=\d)/, "");
|
|
505
|
+
const frac = (m[3] ?? "").replace(/0+$/, "");
|
|
506
|
+
const zero = int === "0" && frac === "";
|
|
507
|
+
return { neg: m[1] === "-" && !zero, int, frac };
|
|
508
|
+
}
|
|
509
|
+
function compareDecimalStrings(a, b) {
|
|
510
|
+
const x = parseDecimal(a);
|
|
511
|
+
const y = parseDecimal(b);
|
|
512
|
+
if (!x || !y) return 0;
|
|
513
|
+
if (x.neg !== y.neg) return x.neg ? -1 : 1;
|
|
514
|
+
const sign = x.neg ? -1 : 1;
|
|
515
|
+
if (x.int.length !== y.int.length) return sign * (x.int.length < y.int.length ? -1 : 1);
|
|
516
|
+
if (x.int !== y.int) return sign * (x.int < y.int ? -1 : 1);
|
|
517
|
+
const len = Math.max(x.frac.length, y.frac.length);
|
|
518
|
+
const fx = x.frac.padEnd(len, "0");
|
|
519
|
+
const fy = y.frac.padEnd(len, "0");
|
|
520
|
+
if (fx === fy) return 0;
|
|
521
|
+
return sign * (fx < fy ? -1 : 1);
|
|
522
|
+
}
|
|
523
|
+
var collators = /* @__PURE__ */ new Map();
|
|
524
|
+
function collator(locale) {
|
|
525
|
+
let c = collators.get(locale);
|
|
526
|
+
if (!c) {
|
|
527
|
+
c = new Intl.Collator(locale, { numeric: true, sensitivity: "base" });
|
|
528
|
+
collators.set(locale, c);
|
|
529
|
+
}
|
|
530
|
+
return c;
|
|
531
|
+
}
|
|
532
|
+
function compareCells(a, b, kind, locale) {
|
|
533
|
+
if (kind === "amount") return compareDecimalStrings(String(a), String(b));
|
|
534
|
+
if (kind === "number") {
|
|
535
|
+
const x = Number(a);
|
|
536
|
+
const y = Number(b);
|
|
537
|
+
if (Number.isNaN(x) || Number.isNaN(y)) return collator(locale).compare(String(a), String(b));
|
|
538
|
+
return x === y ? 0 : x < y ? -1 : 1;
|
|
539
|
+
}
|
|
540
|
+
return collator(locale).compare(String(a), String(b));
|
|
541
|
+
}
|
|
542
|
+
function sortItems(items, getValue, kind, direction, locale) {
|
|
543
|
+
const sign = direction === "asc" ? 1 : -1;
|
|
544
|
+
return items.map((item, index) => ({ item, index, value: getValue(item) })).sort((p, q) => {
|
|
545
|
+
const pm = isMissing(p.value);
|
|
546
|
+
const qm = isMissing(q.value);
|
|
547
|
+
if (pm || qm) return pm && qm ? p.index - q.index : pm ? 1 : -1;
|
|
548
|
+
const c = compareCells(p.value, q.value, kind, locale);
|
|
549
|
+
return c !== 0 ? sign * c : p.index - q.index;
|
|
550
|
+
}).map((e) => e.item);
|
|
551
|
+
}
|
|
552
|
+
function nextSort(current, key) {
|
|
553
|
+
if (!current || current.key !== key) return { key, direction: "asc" };
|
|
554
|
+
return current.direction === "asc" ? { key, direction: "desc" } : void 0;
|
|
555
|
+
}
|
|
556
|
+
function toggleSelection(current, key, mode) {
|
|
557
|
+
if (mode === "none") return new Set(current);
|
|
558
|
+
if (mode === "single") return current.has(key) ? /* @__PURE__ */ new Set() : /* @__PURE__ */ new Set([key]);
|
|
559
|
+
const next = new Set(current);
|
|
560
|
+
if (!next.delete(key)) next.add(key);
|
|
561
|
+
return next;
|
|
562
|
+
}
|
|
563
|
+
function toggleAll(current, allKeys) {
|
|
564
|
+
return allKeys.length > 0 && allKeys.every((k) => current.has(k)) ? /* @__PURE__ */ new Set() : new Set(allKeys);
|
|
565
|
+
}
|
|
566
|
+
var ColumnKindSchema = z.enum(["text", "number", "amount", "icon"]);
|
|
567
|
+
var AlignSchema = z.enum(["start", "center", "end"]);
|
|
200
568
|
var ColumnDefSchema = z.object({
|
|
201
569
|
key: z.string().min(1),
|
|
202
|
-
header: z.string().min(1)
|
|
570
|
+
header: z.string().min(1),
|
|
571
|
+
/** Cell kind: drives comparison and rendering. `amount` = decimal STRING, never a float. */
|
|
572
|
+
type: ColumnKindSchema.default("text"),
|
|
573
|
+
/** Defaults to `end` for `number` / `amount` columns, `start` otherwise. */
|
|
574
|
+
align: AlignSchema.optional(),
|
|
575
|
+
/** CSS width: a number is pixels. */
|
|
576
|
+
width: z.union([z.string().min(1), z.number().positive()]).optional(),
|
|
577
|
+
sortable: z.boolean().default(false),
|
|
578
|
+
/**
|
|
579
|
+
* Values of this column are masked in the A-1 markdown fallback and kept out of the HTML
|
|
580
|
+
* props of a tool result (delivered via the result `_meta`), never written to `content[]`.
|
|
581
|
+
*/
|
|
582
|
+
sensitive: z.boolean().default(false)
|
|
203
583
|
// render is a runtime function — excluded from Zod, typed separately
|
|
204
584
|
});
|
|
585
|
+
var SortSchema = z.object({ key: z.string().min(1), direction: z.enum(["asc", "desc"]) });
|
|
205
586
|
var TableViewPropsSchema = z.object({
|
|
206
587
|
columns: z.array(ColumnDefSchema).min(1, "TableView requires at least one column"),
|
|
588
|
+
/**
|
|
589
|
+
* Serialisable rows, for views built through `createMosaicResource` (the React props carry
|
|
590
|
+
* `rows` separately).
|
|
591
|
+
*/
|
|
592
|
+
rows: z.array(z.record(z.unknown())).default([]),
|
|
207
593
|
virtualizeThreshold: z.number().int().positive().default(100),
|
|
208
594
|
ariaLabel: z.string().min(1),
|
|
209
|
-
locale: z.enum(["en", "fr"]).default("en")
|
|
595
|
+
locale: z.enum(["en", "fr"]).default("en"),
|
|
596
|
+
selectionMode: z.enum(["none", "single", "multiple"]).default("none"),
|
|
597
|
+
/** Initial sort. */
|
|
598
|
+
sort: SortSchema.optional(),
|
|
599
|
+
stickyHeader: z.boolean().default(false),
|
|
600
|
+
/** Scroll region height when `stickyHeader` is on (pixels). */
|
|
601
|
+
maxHeight: z.number().int().positive().default(400),
|
|
602
|
+
density: z.enum(["comfortable", "compact"]).default("comfortable"),
|
|
603
|
+
loading: z.boolean().default(false)
|
|
210
604
|
});
|
|
211
605
|
function validateTableViewProps(raw) {
|
|
212
606
|
return TableViewPropsSchema.parse(raw);
|
|
213
607
|
}
|
|
214
608
|
var OVERSCAN = 5;
|
|
215
|
-
|
|
216
|
-
|
|
609
|
+
var SKELETON_ROWS = ["head", "r1", "r2", "r3", "r4", "r5"];
|
|
610
|
+
function resolveLocale(l) {
|
|
611
|
+
return l === "fr" ? "fr" : "en";
|
|
612
|
+
}
|
|
613
|
+
function validate(props) {
|
|
217
614
|
try {
|
|
218
615
|
const validated = validateTableViewProps({
|
|
219
|
-
columns: props.columns.map(({ key, header }) => ({
|
|
616
|
+
columns: props.columns.map(({ key, header, type, align, width, sortable }) => ({
|
|
617
|
+
key,
|
|
618
|
+
header,
|
|
619
|
+
type,
|
|
620
|
+
align,
|
|
621
|
+
width,
|
|
622
|
+
sortable
|
|
623
|
+
})),
|
|
220
624
|
virtualizeThreshold: props.virtualizeThreshold,
|
|
221
625
|
ariaLabel: props.ariaLabel,
|
|
222
|
-
locale: props.locale
|
|
626
|
+
locale: props.locale,
|
|
627
|
+
selectionMode: props.selectionMode,
|
|
628
|
+
density: props.density
|
|
223
629
|
});
|
|
224
|
-
|
|
630
|
+
return validated.virtualizeThreshold;
|
|
225
631
|
} catch {
|
|
226
|
-
|
|
227
|
-
return /* @__PURE__ */ jsx("div", { role: "alert", children: t("TableView.error.invalidProps", locale) });
|
|
632
|
+
return void 0;
|
|
228
633
|
}
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
{
|
|
235
|
-
rows: props.rows,
|
|
236
|
-
columns: props.columns,
|
|
237
|
-
ariaLabel: props.ariaLabel,
|
|
238
|
-
locale: resolvedLocale,
|
|
239
|
-
onRowClick: props.onRowClick
|
|
240
|
-
}
|
|
241
|
-
);
|
|
634
|
+
}
|
|
635
|
+
function TableView(props) {
|
|
636
|
+
const threshold = validate(props);
|
|
637
|
+
if (threshold === void 0) {
|
|
638
|
+
return /* @__PURE__ */ jsx("div", { role: "alert", children: t("TableView.error.invalidProps", resolveLocale(props.locale)) });
|
|
242
639
|
}
|
|
243
640
|
return /* @__PURE__ */ jsx(
|
|
244
|
-
|
|
641
|
+
TableCore,
|
|
245
642
|
{
|
|
643
|
+
...props,
|
|
246
644
|
rows: props.rows,
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
locale: resolvedLocale,
|
|
250
|
-
onRowClick: props.onRowClick
|
|
645
|
+
virtualizeThreshold: threshold,
|
|
646
|
+
locale: resolveLocale(props.locale)
|
|
251
647
|
}
|
|
252
648
|
);
|
|
253
649
|
}
|
|
254
650
|
function StreamingTableView(props) {
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
columns: props.columns.map(({ key, header }) => ({ key, header })),
|
|
259
|
-
virtualizeThreshold: props.virtualizeThreshold,
|
|
260
|
-
ariaLabel: props.ariaLabel,
|
|
261
|
-
locale: props.locale
|
|
262
|
-
});
|
|
263
|
-
validatedThreshold = validated.virtualizeThreshold;
|
|
264
|
-
} catch {
|
|
265
|
-
const locale = props.locale === "fr" ? "fr" : "en";
|
|
266
|
-
return /* @__PURE__ */ jsx("div", { role: "alert", children: t("TableView.error.invalidProps", locale) });
|
|
651
|
+
const threshold = validate(props);
|
|
652
|
+
if (threshold === void 0) {
|
|
653
|
+
return /* @__PURE__ */ jsx("div", { role: "alert", children: t("TableView.error.invalidProps", resolveLocale(props.locale)) });
|
|
267
654
|
}
|
|
268
|
-
return /* @__PURE__ */ jsx(
|
|
269
|
-
StreamingTableViewInner,
|
|
270
|
-
{
|
|
271
|
-
...props,
|
|
272
|
-
virtualizeThreshold: validatedThreshold,
|
|
273
|
-
onRowClick: props.onRowClick
|
|
274
|
-
}
|
|
275
|
-
);
|
|
655
|
+
return /* @__PURE__ */ jsx(StreamingTableViewInner, { ...props, virtualizeThreshold: threshold });
|
|
276
656
|
}
|
|
277
657
|
function StreamingTableViewInner({
|
|
278
|
-
columns,
|
|
279
658
|
rows$,
|
|
280
|
-
|
|
281
|
-
ariaLabel,
|
|
282
|
-
locale,
|
|
283
|
-
onRowClick
|
|
659
|
+
...rest
|
|
284
660
|
}) {
|
|
285
|
-
const resolvedLocale = locale === "fr" ? "fr" : "en";
|
|
286
661
|
const [rows, setRows] = useState([]);
|
|
287
662
|
useEffect(() => {
|
|
288
663
|
setRows([]);
|
|
@@ -293,87 +668,282 @@ function StreamingTableViewInner({
|
|
|
293
668
|
sub.unsubscribe();
|
|
294
669
|
};
|
|
295
670
|
}, [rows$]);
|
|
296
|
-
|
|
297
|
-
|
|
671
|
+
return /* @__PURE__ */ jsx(TableCore, { ...rest, rows, locale: resolveLocale(rest.locale) });
|
|
672
|
+
}
|
|
673
|
+
function kindOf(col) {
|
|
674
|
+
return col.type ?? "text";
|
|
675
|
+
}
|
|
676
|
+
function alignOf(col) {
|
|
677
|
+
return col.align ?? (col.type === "amount" || col.type === "number" ? "end" : "start");
|
|
678
|
+
}
|
|
679
|
+
function cellText(row, key) {
|
|
680
|
+
return String(row[key] ?? "");
|
|
681
|
+
}
|
|
682
|
+
function safeIconSrc(v) {
|
|
683
|
+
if (typeof v !== "string") return void 0;
|
|
684
|
+
return /^(https?:\/\/|data:image\/)/i.test(v) ? v : void 0;
|
|
685
|
+
}
|
|
686
|
+
function renderCell(col, row) {
|
|
687
|
+
if (col.render) {
|
|
688
|
+
return col.render(row);
|
|
689
|
+
}
|
|
690
|
+
const value = row[col.key];
|
|
691
|
+
if (col.type === "icon") {
|
|
692
|
+
const src = safeIconSrc(value);
|
|
693
|
+
return src ? /* @__PURE__ */ jsx("img", { src, alt: "", width: 20, height: 20, style: { verticalAlign: "middle" } }) : null;
|
|
694
|
+
}
|
|
695
|
+
if (col.type === "amount" || col.type === "number") {
|
|
696
|
+
return /* @__PURE__ */ jsx("span", { style: { fontVariantNumeric: "tabular-nums" }, children: String(value ?? "") });
|
|
697
|
+
}
|
|
698
|
+
return String(value ?? "");
|
|
699
|
+
}
|
|
700
|
+
function TableCore(props) {
|
|
701
|
+
const {
|
|
702
|
+
columns,
|
|
703
|
+
rows,
|
|
704
|
+
ariaLabel,
|
|
705
|
+
locale,
|
|
706
|
+
selectionMode = "none",
|
|
707
|
+
stickyHeader = false,
|
|
708
|
+
density = "comfortable"
|
|
709
|
+
} = props;
|
|
710
|
+
const [innerSort, setInnerSort] = useState(props.defaultSort);
|
|
711
|
+
const sort = props.sort === void 0 ? innerSort : props.sort ?? void 0;
|
|
712
|
+
const onSort = (key) => {
|
|
713
|
+
const next = nextSort(sort, key);
|
|
714
|
+
if (props.sort === void 0) setInnerSort(next);
|
|
715
|
+
props.onSortChange?.(next);
|
|
716
|
+
};
|
|
717
|
+
const items = useMemo(
|
|
718
|
+
() => rows.map((row, i) => {
|
|
719
|
+
const id = row.id;
|
|
720
|
+
return { row, key: id != null ? String(id) : `row-${i}` };
|
|
721
|
+
}),
|
|
722
|
+
[rows]
|
|
723
|
+
);
|
|
724
|
+
const sorted = useMemo(() => {
|
|
725
|
+
const col = sort ? columns.find((c) => c.key === sort.key) : void 0;
|
|
726
|
+
if (!sort || !col) return items;
|
|
727
|
+
return sortItems(
|
|
728
|
+
items,
|
|
729
|
+
(it) => it.row[col.key],
|
|
730
|
+
kindOf(col),
|
|
731
|
+
sort.direction,
|
|
732
|
+
locale
|
|
733
|
+
);
|
|
734
|
+
}, [items, sort, columns, locale]);
|
|
735
|
+
const [innerSelected, setInnerSelected] = useState(
|
|
736
|
+
() => new Set(props.defaultSelectedKeys ?? [])
|
|
737
|
+
);
|
|
738
|
+
const selected = props.selectedKeys ? new Set(props.selectedKeys) : innerSelected;
|
|
739
|
+
const commit = (next) => {
|
|
740
|
+
if (!props.selectedKeys) setInnerSelected(next);
|
|
741
|
+
const chosen = sorted.filter((it) => next.has(it.key));
|
|
742
|
+
props.onSelectionChange?.(
|
|
743
|
+
chosen.map((it) => it.row),
|
|
744
|
+
chosen.map((it) => it.key)
|
|
745
|
+
);
|
|
746
|
+
};
|
|
747
|
+
const selection = selectionMode === "none" ? void 0 : {
|
|
748
|
+
mode: selectionMode,
|
|
749
|
+
selected,
|
|
750
|
+
toggle: (key) => commit(toggleSelection(selected, key, selectionMode)),
|
|
751
|
+
toggleAll: () => commit(
|
|
752
|
+
toggleAll(
|
|
753
|
+
selected,
|
|
754
|
+
sorted.map((it) => it.key)
|
|
755
|
+
)
|
|
756
|
+
)
|
|
757
|
+
};
|
|
758
|
+
if (props.loading) {
|
|
759
|
+
return /* @__PURE__ */ jsx("div", { "aria-busy": "true", "aria-label": t("TableView.loading", locale), lang: locale, children: SKELETON_ROWS.map((_, i) => /* @__PURE__ */ jsx(
|
|
760
|
+
Skeleton,
|
|
761
|
+
{
|
|
762
|
+
variant: "rect",
|
|
763
|
+
height: i === 0 ? 24 : 36,
|
|
764
|
+
locale
|
|
765
|
+
},
|
|
766
|
+
SKELETON_ROWS[i]
|
|
767
|
+
)) });
|
|
768
|
+
}
|
|
769
|
+
const content = {
|
|
770
|
+
items: sorted,
|
|
771
|
+
columns,
|
|
772
|
+
ariaLabel,
|
|
773
|
+
locale,
|
|
774
|
+
onRowClick: props.onRowClick,
|
|
775
|
+
sort,
|
|
776
|
+
onSort,
|
|
777
|
+
selection,
|
|
778
|
+
sticky: stickyHeader,
|
|
779
|
+
density
|
|
780
|
+
};
|
|
781
|
+
const isVirtual = rows.length > props.virtualizeThreshold;
|
|
782
|
+
if (isVirtual) return /* @__PURE__ */ jsx(VirtualTable, { ...content });
|
|
783
|
+
if (stickyHeader) {
|
|
298
784
|
return /* @__PURE__ */ jsx(
|
|
299
|
-
|
|
785
|
+
"div",
|
|
300
786
|
{
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
ariaLabel,
|
|
304
|
-
|
|
305
|
-
|
|
787
|
+
tabIndex: 0,
|
|
788
|
+
role: "region",
|
|
789
|
+
"aria-label": ariaLabel,
|
|
790
|
+
style: { maxHeight: props.maxHeight ?? 400, overflow: "auto" },
|
|
791
|
+
children: /* @__PURE__ */ jsx(StaticTable, { ...content })
|
|
306
792
|
}
|
|
307
793
|
);
|
|
308
794
|
}
|
|
795
|
+
return /* @__PURE__ */ jsx(StaticTable, { ...content });
|
|
796
|
+
}
|
|
797
|
+
var CELL_PADDING = { comfortable: void 0, compact: "2px 6px" };
|
|
798
|
+
function HeaderRow({
|
|
799
|
+
columns,
|
|
800
|
+
locale,
|
|
801
|
+
sort,
|
|
802
|
+
onSort,
|
|
803
|
+
selection,
|
|
804
|
+
sticky,
|
|
805
|
+
density,
|
|
806
|
+
items
|
|
807
|
+
}) {
|
|
808
|
+
const padding = CELL_PADDING[density];
|
|
809
|
+
const stickyStyle = sticky ? { position: "sticky", top: 0, background: "var(--mosaic-color-background, Canvas)" } : {};
|
|
810
|
+
const allSelected = items.length > 0 && items.every((it) => selection?.selected.has(it.key));
|
|
811
|
+
const someSelected = items.some((it) => selection?.selected.has(it.key));
|
|
812
|
+
return /* @__PURE__ */ jsxs("tr", { children: [
|
|
813
|
+
selection ? /* @__PURE__ */ jsx("th", { scope: "col", style: { ...stickyStyle, padding, width: 32 }, children: selection.mode === "multiple" ? /* @__PURE__ */ jsx(
|
|
814
|
+
"input",
|
|
815
|
+
{
|
|
816
|
+
type: "checkbox",
|
|
817
|
+
"aria-label": t("TableView.select.all", locale),
|
|
818
|
+
checked: allSelected,
|
|
819
|
+
ref: (el) => {
|
|
820
|
+
if (el) el.indeterminate = someSelected && !allSelected;
|
|
821
|
+
},
|
|
822
|
+
onChange: selection.toggleAll
|
|
823
|
+
}
|
|
824
|
+
) : null }) : null,
|
|
825
|
+
columns.map((col) => {
|
|
826
|
+
const dir = sort?.key === col.key ? sort.direction : void 0;
|
|
827
|
+
return /* @__PURE__ */ jsx(
|
|
828
|
+
"th",
|
|
829
|
+
{
|
|
830
|
+
scope: "col",
|
|
831
|
+
"aria-sort": col.sortable ? dir === "asc" ? "ascending" : dir === "desc" ? "descending" : "none" : void 0,
|
|
832
|
+
style: {
|
|
833
|
+
...stickyStyle,
|
|
834
|
+
padding,
|
|
835
|
+
textAlign: alignOf(col),
|
|
836
|
+
...col.width !== void 0 ? { width: col.width } : {}
|
|
837
|
+
},
|
|
838
|
+
children: col.sortable ? /* @__PURE__ */ jsxs(
|
|
839
|
+
"button",
|
|
840
|
+
{
|
|
841
|
+
type: "button",
|
|
842
|
+
"aria-label": `${t("TableView.sort.by", locale)} ${col.header}`,
|
|
843
|
+
onClick: () => onSort(col.key),
|
|
844
|
+
style: { all: "inherit", cursor: "pointer", padding: 0 },
|
|
845
|
+
children: [
|
|
846
|
+
col.header,
|
|
847
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: dir === "asc" ? " \u25B2" : dir === "desc" ? " \u25BC" : "" })
|
|
848
|
+
]
|
|
849
|
+
}
|
|
850
|
+
) : col.header
|
|
851
|
+
},
|
|
852
|
+
col.key
|
|
853
|
+
);
|
|
854
|
+
})
|
|
855
|
+
] });
|
|
856
|
+
}
|
|
857
|
+
function SelectCell({
|
|
858
|
+
item,
|
|
859
|
+
columns,
|
|
860
|
+
selection,
|
|
861
|
+
locale,
|
|
862
|
+
padding
|
|
863
|
+
}) {
|
|
864
|
+
const first = columns[0];
|
|
865
|
+
const label = `${t("TableView.select.row", locale)}: ${first ? cellText(item.row, first.key) : item.key}`;
|
|
309
866
|
return /* @__PURE__ */ jsx(
|
|
310
|
-
|
|
867
|
+
"td",
|
|
311
868
|
{
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
869
|
+
style: { padding, width: 32 },
|
|
870
|
+
onClick: (e) => e.stopPropagation(),
|
|
871
|
+
onKeyDown: (e) => e.stopPropagation(),
|
|
872
|
+
children: /* @__PURE__ */ jsx(
|
|
873
|
+
"input",
|
|
874
|
+
{
|
|
875
|
+
type: selection.mode === "multiple" ? "checkbox" : "radio",
|
|
876
|
+
name: selection.mode === "single" ? "mosaic-table-selection" : void 0,
|
|
877
|
+
"aria-label": label,
|
|
878
|
+
checked: selection.selected.has(item.key),
|
|
879
|
+
onChange: () => selection.toggle(item.key),
|
|
880
|
+
onKeyDown: (e) => e.stopPropagation()
|
|
881
|
+
}
|
|
882
|
+
)
|
|
317
883
|
}
|
|
318
884
|
);
|
|
319
885
|
}
|
|
320
|
-
function
|
|
321
|
-
if (
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
886
|
+
function rowActivation(onRowClick, row, index) {
|
|
887
|
+
if (!onRowClick) return {};
|
|
888
|
+
return {
|
|
889
|
+
role: "button",
|
|
890
|
+
tabIndex: 0,
|
|
891
|
+
onClick: () => onRowClick(row, index),
|
|
892
|
+
onKeyDown: (e) => {
|
|
893
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
894
|
+
e.preventDefault();
|
|
895
|
+
onRowClick(row, index);
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
};
|
|
325
899
|
}
|
|
326
|
-
function StaticTable({
|
|
327
|
-
|
|
328
|
-
columns,
|
|
329
|
-
ariaLabel,
|
|
330
|
-
locale,
|
|
331
|
-
onRowClick
|
|
332
|
-
}) {
|
|
900
|
+
function StaticTable(props) {
|
|
901
|
+
const { items, columns, ariaLabel, locale, onRowClick, selection, density } = props;
|
|
333
902
|
const emptyMsg = t("TableView.empty.message", locale);
|
|
903
|
+
const padding = CELL_PADDING[density];
|
|
904
|
+
const colSpan = columns.length + (selection ? 1 : 0);
|
|
334
905
|
return /* @__PURE__ */ jsxs(
|
|
335
906
|
"table",
|
|
336
907
|
{
|
|
337
908
|
"aria-label": ariaLabel,
|
|
338
|
-
"aria-rowcount":
|
|
909
|
+
"aria-rowcount": items.length,
|
|
910
|
+
"data-density": density,
|
|
339
911
|
style: { width: "100%", borderCollapse: "collapse" },
|
|
340
912
|
children: [
|
|
341
|
-
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx(
|
|
342
|
-
/* @__PURE__ */ jsx("tbody", { children:
|
|
913
|
+
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx(HeaderRow, { ...props }) }),
|
|
914
|
+
/* @__PURE__ */ jsx("tbody", { children: items.length === 0 ? /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx("td", { colSpan, "aria-label": emptyMsg, children: emptyMsg }) }) : items.map((item, rowIndex) => /* @__PURE__ */ jsxs(
|
|
343
915
|
"tr",
|
|
344
916
|
{
|
|
345
917
|
"aria-rowindex": rowIndex + 2,
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
918
|
+
"data-selected": selection?.selected.has(item.key) ? "true" : void 0,
|
|
919
|
+
...onRowClick ? { style: { cursor: "pointer" } } : {},
|
|
920
|
+
...rowActivation(onRowClick, item.row, rowIndex),
|
|
921
|
+
children: [
|
|
922
|
+
selection ? /* @__PURE__ */ jsx(
|
|
923
|
+
SelectCell,
|
|
924
|
+
{
|
|
925
|
+
item,
|
|
926
|
+
columns,
|
|
927
|
+
selection,
|
|
928
|
+
locale,
|
|
929
|
+
padding
|
|
355
930
|
}
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
931
|
+
) : null,
|
|
932
|
+
columns.map((col) => /* @__PURE__ */ jsx("td", { style: { padding, textAlign: alignOf(col) }, children: renderCell(col, item.row) }, col.key))
|
|
933
|
+
]
|
|
359
934
|
},
|
|
360
|
-
|
|
935
|
+
item.key
|
|
361
936
|
)) })
|
|
362
937
|
]
|
|
363
938
|
}
|
|
364
939
|
);
|
|
365
940
|
}
|
|
366
|
-
function VirtualTable({
|
|
367
|
-
|
|
368
|
-
columns,
|
|
369
|
-
ariaLabel,
|
|
370
|
-
// locale reserved for future virtual-mode empty/pagination strings
|
|
371
|
-
locale: _locale,
|
|
372
|
-
onRowClick
|
|
373
|
-
}) {
|
|
941
|
+
function VirtualTable(props) {
|
|
942
|
+
const { items, columns, ariaLabel, locale, onRowClick, selection, density } = props;
|
|
374
943
|
const scrollRef = useRef(null);
|
|
944
|
+
const padding = CELL_PADDING[density];
|
|
375
945
|
const rowVirtualizer = useVirtualizer({
|
|
376
|
-
count:
|
|
946
|
+
count: items.length,
|
|
377
947
|
/* v8 ignore next 1 */
|
|
378
948
|
getScrollElement: () => scrollRef.current,
|
|
379
949
|
estimateSize: useCallback(() => 40, []),
|
|
@@ -386,18 +956,19 @@ function VirtualTable({
|
|
|
386
956
|
{
|
|
387
957
|
"data-virtual": "true",
|
|
388
958
|
ref: scrollRef,
|
|
389
|
-
tabIndex: 0,
|
|
390
959
|
role: "region",
|
|
960
|
+
tabIndex: 0,
|
|
391
961
|
"aria-label": ariaLabel,
|
|
392
962
|
style: { height: 400, overflow: "auto", position: "relative" },
|
|
393
963
|
children: /* @__PURE__ */ jsxs(
|
|
394
964
|
"table",
|
|
395
965
|
{
|
|
396
966
|
"aria-label": ariaLabel,
|
|
397
|
-
"aria-rowcount":
|
|
967
|
+
"aria-rowcount": items.length,
|
|
968
|
+
"data-density": density,
|
|
398
969
|
style: { width: "100%", borderCollapse: "collapse" },
|
|
399
970
|
children: [
|
|
400
|
-
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx(
|
|
971
|
+
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx(HeaderRow, { ...props, sticky: true }) }),
|
|
401
972
|
/* @__PURE__ */ jsx(
|
|
402
973
|
"tbody",
|
|
403
974
|
{
|
|
@@ -407,11 +978,12 @@ function VirtualTable({
|
|
|
407
978
|
display: "block"
|
|
408
979
|
},
|
|
409
980
|
children: virtualItems.map((virtualRow) => {
|
|
410
|
-
const
|
|
411
|
-
return /* @__PURE__ */
|
|
981
|
+
const item = items[virtualRow.index] ?? { row: {}, key: "" };
|
|
982
|
+
return /* @__PURE__ */ jsxs(
|
|
412
983
|
"tr",
|
|
413
984
|
{
|
|
414
985
|
"aria-rowindex": virtualRow.index + 2,
|
|
986
|
+
"data-selected": selection?.selected.has(item.key) ? "true" : void 0,
|
|
415
987
|
style: {
|
|
416
988
|
position: "absolute",
|
|
417
989
|
top: 0,
|
|
@@ -421,20 +993,22 @@ function VirtualTable({
|
|
|
421
993
|
display: "flex",
|
|
422
994
|
...onRowClick ? { cursor: "pointer" } : {}
|
|
423
995
|
},
|
|
424
|
-
...onRowClick
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
996
|
+
...rowActivation(onRowClick, item.row, virtualRow.index),
|
|
997
|
+
children: [
|
|
998
|
+
selection ? /* @__PURE__ */ jsx(
|
|
999
|
+
SelectCell,
|
|
1000
|
+
{
|
|
1001
|
+
item,
|
|
1002
|
+
columns,
|
|
1003
|
+
selection,
|
|
1004
|
+
locale,
|
|
1005
|
+
padding
|
|
432
1006
|
}
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
1007
|
+
) : null,
|
|
1008
|
+
columns.map((col) => /* @__PURE__ */ jsx("td", { style: { flex: 1, padding, textAlign: alignOf(col) }, children: renderCell(col, item.row) }, col.key))
|
|
1009
|
+
]
|
|
436
1010
|
},
|
|
437
|
-
|
|
1011
|
+
item.key || `vrow-${virtualRow.index}`
|
|
438
1012
|
);
|
|
439
1013
|
})
|
|
440
1014
|
}
|
|
@@ -458,113 +1032,872 @@ function validateEmptyStateProps(raw) {
|
|
|
458
1032
|
return EmptyStatePropsSchema.parse(raw);
|
|
459
1033
|
}
|
|
460
1034
|
|
|
461
|
-
// src/components/display/
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
1035
|
+
// src/components/display/EmptyState.logic.ts
|
|
1036
|
+
var ACTION_VARIANT_CLASSES = {
|
|
1037
|
+
primary: "inline-flex items-center justify-center rounded-md bg-slate-900 px-4 py-2 text-sm font-medium text-white shadow-sm transition-colors hover:bg-slate-700 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-slate-900 disabled:pointer-events-none disabled:opacity-50",
|
|
1038
|
+
secondary: "inline-flex items-center justify-center rounded-md border border-slate-300 bg-white px-4 py-2 text-sm font-medium text-slate-700 shadow-sm transition-colors hover:bg-slate-50 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-slate-400 disabled:pointer-events-none disabled:opacity-50"
|
|
1039
|
+
};
|
|
1040
|
+
function EmptyStateInner({
|
|
1041
|
+
icon,
|
|
1042
|
+
title,
|
|
1043
|
+
description,
|
|
1044
|
+
action,
|
|
1045
|
+
locale: _locale
|
|
1046
|
+
}) {
|
|
1047
|
+
const variant = action?.variant ?? "primary";
|
|
1048
|
+
return /* @__PURE__ */ jsxs(
|
|
1049
|
+
"section",
|
|
1050
|
+
{
|
|
1051
|
+
"aria-label": title,
|
|
1052
|
+
className: "flex flex-col items-center justify-center gap-4 px-6 py-12 text-center",
|
|
1053
|
+
children: [
|
|
1054
|
+
icon && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "text-slate-400", children: icon }),
|
|
1055
|
+
/* @__PURE__ */ jsx("h2", { className: "text-base font-semibold text-slate-900", children: title }),
|
|
1056
|
+
description && /* @__PURE__ */ jsx("p", { className: "max-w-sm text-sm leading-relaxed text-slate-500", children: description }),
|
|
1057
|
+
action && /* @__PURE__ */ jsx("button", { type: "button", onClick: action.onClick, className: ACTION_VARIANT_CLASSES[variant], children: action.label })
|
|
1058
|
+
]
|
|
1059
|
+
}
|
|
1060
|
+
);
|
|
1061
|
+
}
|
|
1062
|
+
function EmptyState(raw) {
|
|
1063
|
+
try {
|
|
1064
|
+
const parsed = validateEmptyStateProps(raw);
|
|
1065
|
+
return /* @__PURE__ */ jsx(
|
|
1066
|
+
EmptyStateInner,
|
|
1067
|
+
{
|
|
1068
|
+
...parsed,
|
|
1069
|
+
icon: raw.icon,
|
|
1070
|
+
action: parsed.action && raw.action?.onClick ? { ...parsed.action, onClick: raw.action.onClick } : void 0
|
|
1071
|
+
}
|
|
1072
|
+
);
|
|
1073
|
+
} catch (err) {
|
|
1074
|
+
console.error("[EmptyState] Invalid props \u2014 component will not render.", err);
|
|
1075
|
+
const locale = raw.locale === "fr" ? "fr" : "en";
|
|
1076
|
+
return /* @__PURE__ */ jsx("span", { role: "alert", children: t("EmptyState.error.invalidProps", locale) });
|
|
470
1077
|
}
|
|
471
1078
|
}
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
1079
|
+
var StatCardPropsSchema = z.object({
|
|
1080
|
+
label: z.string().default(""),
|
|
1081
|
+
value: z.string().default(""),
|
|
1082
|
+
unit: z.string().optional(),
|
|
1083
|
+
trend: z.enum(["up", "down", "flat"]).optional(),
|
|
1084
|
+
/** Pre-formatted change, e.g. "+4.2%". */
|
|
1085
|
+
change: z.string().optional(),
|
|
1086
|
+
tone: z.enum(["neutral", "success", "warning", "danger"]).default("neutral"),
|
|
1087
|
+
description: z.string().optional(),
|
|
1088
|
+
loading: z.boolean().default(false),
|
|
1089
|
+
locale: z.enum(["en", "fr"]).default("en")
|
|
1090
|
+
});
|
|
1091
|
+
function validateStatCardProps(raw) {
|
|
1092
|
+
return StatCardPropsSchema.parse(raw);
|
|
1093
|
+
}
|
|
1094
|
+
var TONE_CLASSES = {
|
|
1095
|
+
neutral: "text-slate-900",
|
|
1096
|
+
success: "text-green-700",
|
|
1097
|
+
warning: "text-amber-700",
|
|
1098
|
+
danger: "text-red-700"
|
|
1099
|
+
};
|
|
1100
|
+
var TREND_ARROW = { up: "\u25B2", down: "\u25BC", flat: "\u25AC" };
|
|
1101
|
+
function StatCard(raw) {
|
|
1102
|
+
const parsed = StatCardPropsSchema.safeParse(raw);
|
|
1103
|
+
const locale = raw?.locale === "fr" ? "fr" : "en";
|
|
1104
|
+
if (!parsed.success) {
|
|
1105
|
+
return /* @__PURE__ */ jsx("div", { role: "alert", children: t("StatCard.error.invalidProps", locale) });
|
|
1106
|
+
}
|
|
1107
|
+
const p = parsed.data;
|
|
1108
|
+
if (p.loading) {
|
|
1109
|
+
return /* @__PURE__ */ jsx("div", { "aria-busy": "true", "aria-label": t("StatCard.loading", p.locale), children: /* @__PURE__ */ jsx(Skeleton, { variant: "rect", height: 72, locale: p.locale }) });
|
|
1110
|
+
}
|
|
1111
|
+
if (p.label === "" && p.value === "") {
|
|
1112
|
+
return /* @__PURE__ */ jsx(EmptyState, { title: t("StatCard.empty.message", p.locale), locale: p.locale });
|
|
480
1113
|
}
|
|
1114
|
+
return /* @__PURE__ */ jsxs(
|
|
1115
|
+
"section",
|
|
1116
|
+
{
|
|
1117
|
+
"aria-label": `${t("StatCard.title", p.locale)}: ${p.label}`,
|
|
1118
|
+
lang: p.locale,
|
|
1119
|
+
className: "flex flex-col gap-1 rounded-lg border border-slate-200 bg-white p-4",
|
|
1120
|
+
children: [
|
|
1121
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm text-slate-500", children: p.label }),
|
|
1122
|
+
/* @__PURE__ */ jsxs("span", { className: `text-2xl font-semibold tabular-nums ${TONE_CLASSES[p.tone]}`, children: [
|
|
1123
|
+
p.value,
|
|
1124
|
+
p.unit ? /* @__PURE__ */ jsx("span", { className: "ml-1 text-base font-normal text-slate-500", children: p.unit }) : null
|
|
1125
|
+
] }),
|
|
1126
|
+
p.trend || p.change ? /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1 text-sm text-slate-600", children: [
|
|
1127
|
+
p.trend ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1128
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: TREND_ARROW[p.trend] }),
|
|
1129
|
+
/* @__PURE__ */ jsx("span", { children: t(`StatCard.trend.${p.trend}`, p.locale) })
|
|
1130
|
+
] }) : null,
|
|
1131
|
+
p.change ? /* @__PURE__ */ jsx("span", { className: "tabular-nums", children: p.change }) : null
|
|
1132
|
+
] }) : null,
|
|
1133
|
+
p.description ? /* @__PURE__ */ jsx("span", { className: "text-sm text-slate-500", children: p.description }) : null
|
|
1134
|
+
]
|
|
1135
|
+
}
|
|
1136
|
+
);
|
|
481
1137
|
}
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
1138
|
+
|
|
1139
|
+
// src/components/shared/markdown.ts
|
|
1140
|
+
var SPECIAL = /([\\`*_[\]()<>&!|~])/g;
|
|
1141
|
+
var BREAKS = /* @__PURE__ */ new Set([10, 11, 12, 13, 133, 8232, 8233]);
|
|
1142
|
+
function flatten(text) {
|
|
1143
|
+
let out = "";
|
|
1144
|
+
let gap = false;
|
|
1145
|
+
for (const c of text) {
|
|
1146
|
+
const n = c.codePointAt(0) ?? 0;
|
|
1147
|
+
if (BREAKS.has(n)) {
|
|
1148
|
+
gap = true;
|
|
1149
|
+
continue;
|
|
1150
|
+
}
|
|
1151
|
+
if (n < 32 && n !== 9) continue;
|
|
1152
|
+
if (n === 127) continue;
|
|
1153
|
+
if (gap) out += " ";
|
|
1154
|
+
gap = false;
|
|
1155
|
+
out += c;
|
|
1156
|
+
}
|
|
1157
|
+
return gap ? `${out} ` : out;
|
|
485
1158
|
}
|
|
486
|
-
function
|
|
487
|
-
|
|
488
|
-
|
|
1159
|
+
function mdEscape(text) {
|
|
1160
|
+
const flat = flatten(text).replace(SPECIAL, "\\$1").replace(/\b(https?|ftp):\/\//gi, "$1:\\/\\/").replace(/\bwww\./gi, (m) => `${m.slice(0, 3)}\\.`);
|
|
1161
|
+
return flat.replace(/^(\s*)([#>+-])/, "$1\\$2").replace(/^(\s*\d+)([.)])(?=\s|$)/, "$1\\$2");
|
|
1162
|
+
}
|
|
1163
|
+
function mdUrl(href) {
|
|
1164
|
+
return href.replace(
|
|
1165
|
+
/[()[\]\\<>`\s]/g,
|
|
1166
|
+
(c) => `%${c.charCodeAt(0).toString(16).toUpperCase().padStart(2, "0")}`
|
|
1167
|
+
);
|
|
1168
|
+
}
|
|
1169
|
+
function mdCode(text) {
|
|
1170
|
+
const flat = flatten(text);
|
|
1171
|
+
const longest = Math.max(0, ...(flat.match(/`+/g) ?? []).map((r) => r.length));
|
|
1172
|
+
const fence = "`".repeat(longest + 1);
|
|
1173
|
+
return `${fence} ${flat} ${fence}`;
|
|
1174
|
+
}
|
|
1175
|
+
function mdTable(headers, rows) {
|
|
1176
|
+
const line = (cells) => `| ${cells.join(" | ")} |`;
|
|
1177
|
+
return [line(headers), line(headers.map(() => "---")), ...rows.map(line)].join("\n");
|
|
1178
|
+
}
|
|
1179
|
+
function mdHeading(text, level = 1) {
|
|
1180
|
+
return `${"#".repeat(level)} ${text}`;
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1183
|
+
// src/components/display/StatCard.markdown.ts
|
|
1184
|
+
function statCardToMarkdown(props, locale = "en") {
|
|
1185
|
+
const parsed = StatCardPropsSchema.safeParse(props);
|
|
1186
|
+
const title = mdHeading(t("StatCard.title", locale));
|
|
1187
|
+
if (!parsed.success || parsed.data.label === "" && parsed.data.value === "") {
|
|
1188
|
+
return `${title}
|
|
1189
|
+
|
|
1190
|
+
${t("StatCard.empty.message", locale)}
|
|
1191
|
+
`;
|
|
489
1192
|
}
|
|
490
|
-
|
|
1193
|
+
const { label, value, unit, trend, change, description } = parsed.data;
|
|
1194
|
+
const amount = unit ? `${value} ${unit}` : value;
|
|
1195
|
+
const movement = [trend ? t(`StatCard.trend.${trend}`, locale) : "", change ?? ""].filter(Boolean).join(" ");
|
|
1196
|
+
const line = `- **${mdEscape(label)}**: ${mdEscape(amount)}${movement ? ` (${mdEscape(movement)})` : ""}`;
|
|
1197
|
+
return `${title}
|
|
1198
|
+
|
|
1199
|
+
${line}
|
|
1200
|
+
${description ? `
|
|
1201
|
+
${mdEscape(description)}
|
|
1202
|
+
` : ""}`;
|
|
491
1203
|
}
|
|
492
|
-
var
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
count: z.number().int().
|
|
497
|
-
animation: z.enum(["pulse", "wave", "none"]).default("pulse"),
|
|
1204
|
+
var BadgePropsSchema = z.object({
|
|
1205
|
+
label: z.string(),
|
|
1206
|
+
variant: z.enum(["default", "primary", "success", "warning", "danger", "info", "neutral"]).default("default"),
|
|
1207
|
+
dot: z.boolean().optional(),
|
|
1208
|
+
count: z.number().int().nonnegative().optional(),
|
|
498
1209
|
locale: z.enum(["en", "fr"]).default("en")
|
|
499
1210
|
});
|
|
500
|
-
function
|
|
501
|
-
return
|
|
1211
|
+
function validateBadgeProps(raw) {
|
|
1212
|
+
return BadgePropsSchema.parse(raw);
|
|
502
1213
|
}
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
1214
|
+
var variantClasses = {
|
|
1215
|
+
default: "bg-slate-100 text-slate-700 ring-1 ring-slate-200",
|
|
1216
|
+
primary: "bg-indigo-100 text-indigo-800 ring-1 ring-indigo-300",
|
|
1217
|
+
success: "bg-green-100 text-green-800 ring-1 ring-green-300",
|
|
1218
|
+
warning: "bg-amber-100 text-amber-800 ring-1 ring-amber-300",
|
|
1219
|
+
danger: "bg-red-100 text-red-800 ring-1 ring-red-300",
|
|
1220
|
+
info: "bg-blue-100 text-blue-800 ring-1 ring-blue-300",
|
|
1221
|
+
neutral: "bg-slate-100 text-slate-800 ring-1 ring-slate-300"
|
|
1222
|
+
};
|
|
1223
|
+
var dotClasses = {
|
|
1224
|
+
default: "bg-slate-400",
|
|
1225
|
+
primary: "bg-indigo-500",
|
|
1226
|
+
success: "bg-green-500",
|
|
1227
|
+
warning: "bg-amber-500",
|
|
1228
|
+
danger: "bg-red-500",
|
|
1229
|
+
info: "bg-blue-500",
|
|
1230
|
+
neutral: "bg-slate-400"
|
|
1231
|
+
};
|
|
1232
|
+
function BadgeInner({ label, variant, dot, count, locale }) {
|
|
1233
|
+
const ariaLabel = t("Badge.aria.label", locale);
|
|
1234
|
+
const displayCount = count !== void 0 ? ` (${count})` : "";
|
|
1235
|
+
return /* @__PURE__ */ jsxs(
|
|
1236
|
+
"span",
|
|
1237
|
+
{
|
|
1238
|
+
"aria-label": `${ariaLabel}: ${label}${displayCount}`,
|
|
1239
|
+
lang: locale,
|
|
1240
|
+
className: `inline-flex items-center gap-1 rounded-full px-2.5 py-0.5 text-xs font-medium ${variantClasses[variant]}`,
|
|
1241
|
+
children: [
|
|
1242
|
+
dot && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: `h-1.5 w-1.5 rounded-full ${dotClasses[variant]}` }),
|
|
1243
|
+
label,
|
|
1244
|
+
count !== void 0 && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "font-semibold tabular-nums", children: count })
|
|
1245
|
+
]
|
|
1246
|
+
}
|
|
1247
|
+
);
|
|
1248
|
+
}
|
|
1249
|
+
function Badge(raw) {
|
|
1250
|
+
try {
|
|
1251
|
+
const props = validateBadgeProps(raw);
|
|
1252
|
+
return /* @__PURE__ */ jsx(BadgeInner, { ...props });
|
|
1253
|
+
} catch {
|
|
1254
|
+
const locale = raw.locale === "fr" ? "fr" : "en";
|
|
1255
|
+
return /* @__PURE__ */ jsx("span", { role: "alert", children: t("Badge.error.invalidProps", locale) });
|
|
512
1256
|
}
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
// src/components/shared/decimal.ts
|
|
1260
|
+
var DECIMAL2 = /^(-)?(\d+)(?:\.(\d+))?$/;
|
|
1261
|
+
function sumDecimalStrings(values) {
|
|
1262
|
+
let scale = 0;
|
|
1263
|
+
const parsed = [];
|
|
1264
|
+
for (const v of values) {
|
|
1265
|
+
const m = DECIMAL2.exec(v);
|
|
1266
|
+
if (!m) return void 0;
|
|
1267
|
+
const frac2 = m[3] ?? "";
|
|
1268
|
+
scale = Math.max(scale, frac2.length);
|
|
1269
|
+
parsed.push({ neg: m[1] === "-", int: m[2] ?? "0", frac: frac2 });
|
|
518
1270
|
}
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
1271
|
+
let total = 0n;
|
|
1272
|
+
for (const p of parsed) {
|
|
1273
|
+
const n = BigInt(p.int + p.frac.padEnd(scale, "0"));
|
|
1274
|
+
total += p.neg ? -n : n;
|
|
1275
|
+
}
|
|
1276
|
+
const neg = total < 0n;
|
|
1277
|
+
const digits = (neg ? -total : total).toString().padStart(scale + 1, "0");
|
|
1278
|
+
const int = digits.slice(0, digits.length - scale);
|
|
1279
|
+
const frac = digits.slice(digits.length - scale).replace(/0+$/, "");
|
|
1280
|
+
return `${neg ? "-" : ""}${int}${frac ? `.${frac}` : ""}`;
|
|
1281
|
+
}
|
|
1282
|
+
function formatDecimalString(value, locale, currency) {
|
|
1283
|
+
if (!DECIMAL2.test(value)) return value;
|
|
1284
|
+
try {
|
|
1285
|
+
const nf = new Intl.NumberFormat(
|
|
1286
|
+
locale,
|
|
1287
|
+
currency ? { style: "currency", currency } : { maximumFractionDigits: 20 }
|
|
1288
|
+
);
|
|
1289
|
+
return nf.format(value);
|
|
1290
|
+
} catch {
|
|
1291
|
+
return value;
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
function truncateAddress(address, head = 6, tail = 4) {
|
|
1295
|
+
return address.length <= head + tail + 1 ? address : `${address.slice(0, head)}\u2026${address.slice(address.length - tail)}`;
|
|
1296
|
+
}
|
|
1297
|
+
var BalanceCardPropsSchema = z.object({
|
|
1298
|
+
token: z.string().default(""),
|
|
1299
|
+
balance: z.string().default(""),
|
|
1300
|
+
address: z.string().default(""),
|
|
1301
|
+
tokenAddress: z.string().optional(),
|
|
1302
|
+
decimals: z.number().int().nonnegative().optional(),
|
|
1303
|
+
network: z.string().optional(),
|
|
1304
|
+
testnet: z.boolean().default(false),
|
|
1305
|
+
/** Decimal string in `currency`. */
|
|
1306
|
+
usdValue: z.string().optional(),
|
|
1307
|
+
currency: z.string().default("USD"),
|
|
1308
|
+
loading: z.boolean().default(false),
|
|
1309
|
+
locale: z.enum(["en", "fr"]).default("en")
|
|
1310
|
+
});
|
|
1311
|
+
function validateBalanceCardProps(raw) {
|
|
1312
|
+
return BalanceCardPropsSchema.parse(raw);
|
|
1313
|
+
}
|
|
1314
|
+
function BalanceCard(raw) {
|
|
1315
|
+
const parsed = BalanceCardPropsSchema.safeParse(raw);
|
|
1316
|
+
const locale = raw?.locale === "fr" ? "fr" : "en";
|
|
1317
|
+
const [copied, setCopied] = useState(false);
|
|
1318
|
+
if (!parsed.success) {
|
|
1319
|
+
return /* @__PURE__ */ jsx("div", { role: "alert", children: t("BalanceCard.error.invalidProps", locale) });
|
|
1320
|
+
}
|
|
1321
|
+
const p = parsed.data;
|
|
1322
|
+
if (p.loading) {
|
|
1323
|
+
return /* @__PURE__ */ jsx("div", { "aria-busy": "true", "aria-label": t("BalanceCard.loading", p.locale), children: /* @__PURE__ */ jsx(Skeleton, { variant: "rect", height: 120, locale: p.locale }) });
|
|
1324
|
+
}
|
|
1325
|
+
if (p.token === "" && p.balance === "") {
|
|
1326
|
+
return /* @__PURE__ */ jsx(EmptyState, { title: t("BalanceCard.empty.message", p.locale), locale: p.locale });
|
|
1327
|
+
}
|
|
1328
|
+
const canCopy = typeof navigator !== "undefined" && typeof navigator.clipboard?.writeText === "function";
|
|
1329
|
+
const copy = async () => {
|
|
1330
|
+
try {
|
|
1331
|
+
await navigator.clipboard.writeText(p.address);
|
|
1332
|
+
setCopied(true);
|
|
1333
|
+
} catch {
|
|
1334
|
+
}
|
|
522
1335
|
};
|
|
1336
|
+
return /* @__PURE__ */ jsxs(
|
|
1337
|
+
"section",
|
|
1338
|
+
{
|
|
1339
|
+
"aria-label": `${t("BalanceCard.title", p.locale)}: ${p.token}`,
|
|
1340
|
+
lang: p.locale,
|
|
1341
|
+
className: "flex flex-col gap-2",
|
|
1342
|
+
children: [
|
|
1343
|
+
/* @__PURE__ */ jsx(
|
|
1344
|
+
StatCard,
|
|
1345
|
+
{
|
|
1346
|
+
label: t("BalanceCard.title", p.locale),
|
|
1347
|
+
value: p.balance,
|
|
1348
|
+
unit: p.token,
|
|
1349
|
+
locale: p.locale
|
|
1350
|
+
}
|
|
1351
|
+
),
|
|
1352
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
1353
|
+
p.network ? /* @__PURE__ */ jsx(Badge, { label: p.network, variant: "info", locale: p.locale }) : null,
|
|
1354
|
+
p.testnet ? /* @__PURE__ */ jsx(Badge, { label: t("BalanceCard.testnet", p.locale), variant: "warning", locale: p.locale }) : null
|
|
1355
|
+
] }),
|
|
1356
|
+
p.usdValue ? /* @__PURE__ */ jsxs("span", { className: "text-sm text-slate-600", children: [
|
|
1357
|
+
t("BalanceCard.value", p.locale),
|
|
1358
|
+
":",
|
|
1359
|
+
" ",
|
|
1360
|
+
formatDecimalString(p.usdValue, p.locale, p.currency)
|
|
1361
|
+
] }) : null,
|
|
1362
|
+
p.address ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-sm text-slate-600", children: [
|
|
1363
|
+
/* @__PURE__ */ jsx("span", { className: "font-mono", title: p.address, children: truncateAddress(p.address) }),
|
|
1364
|
+
canCopy ? /* @__PURE__ */ jsx(
|
|
1365
|
+
"button",
|
|
1366
|
+
{
|
|
1367
|
+
type: "button",
|
|
1368
|
+
onClick: copy,
|
|
1369
|
+
className: "rounded border border-slate-300 px-2 py-0.5 text-xs hover:bg-slate-50",
|
|
1370
|
+
children: t("BalanceCard.copy", p.locale)
|
|
1371
|
+
}
|
|
1372
|
+
) : null,
|
|
1373
|
+
copied ? /* @__PURE__ */ jsx("output", { children: t("BalanceCard.copied", p.locale) }) : null
|
|
1374
|
+
] }) : null
|
|
1375
|
+
]
|
|
1376
|
+
}
|
|
1377
|
+
);
|
|
523
1378
|
}
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
const
|
|
528
|
-
const
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
textLineKey(i)
|
|
544
|
-
)) })
|
|
1379
|
+
|
|
1380
|
+
// src/components/display/BalanceCard.markdown.ts
|
|
1381
|
+
function balanceCardToMarkdown(props, locale = "en") {
|
|
1382
|
+
const parsed = BalanceCardPropsSchema.safeParse(props);
|
|
1383
|
+
const title = mdHeading(t("BalanceCard.title", locale));
|
|
1384
|
+
if (!parsed.success || parsed.data.token === "" && parsed.data.balance === "") {
|
|
1385
|
+
return `${title}
|
|
1386
|
+
|
|
1387
|
+
${t("BalanceCard.empty.message", locale)}
|
|
1388
|
+
`;
|
|
1389
|
+
}
|
|
1390
|
+
const p = parsed.data;
|
|
1391
|
+
const where = [p.network, p.testnet ? t("BalanceCard.testnet", locale) : ""].filter(Boolean);
|
|
1392
|
+
const lines = [
|
|
1393
|
+
`- ${mdEscape(p.token)}: ${mdEscape(p.balance)}${where.length ? ` (${mdEscape(where.join(", "))})` : ""}`
|
|
1394
|
+
];
|
|
1395
|
+
if (p.usdValue) {
|
|
1396
|
+
lines.push(
|
|
1397
|
+
`- ${t("BalanceCard.value", locale)}: ${mdEscape(formatDecimalString(p.usdValue, locale, p.currency))}`
|
|
545
1398
|
);
|
|
546
1399
|
}
|
|
1400
|
+
if (p.address) lines.push(`- ${t("BalanceCard.address", locale)}: ${mdCode(p.address)}`);
|
|
1401
|
+
if (p.tokenAddress)
|
|
1402
|
+
lines.push(`- ${t("BalanceCard.contract", locale)}: ${mdCode(p.tokenAddress)}`);
|
|
1403
|
+
return `${title}
|
|
1404
|
+
|
|
1405
|
+
${lines.join("\n")}
|
|
1406
|
+
`;
|
|
1407
|
+
}
|
|
1408
|
+
var VirtualListPropsSchema = z.object({
|
|
1409
|
+
/** Array of items to render — required, can be empty */
|
|
1410
|
+
items: z.array(z.unknown()),
|
|
1411
|
+
/** Fixed row height in px — mutually exclusive with estimateSize */
|
|
1412
|
+
itemHeight: z.number().positive().optional(),
|
|
1413
|
+
/** Variable row height estimator — mutually exclusive with itemHeight */
|
|
1414
|
+
estimateSize: z.function().args(z.number()).returns(z.number()).optional(),
|
|
1415
|
+
/** Row renderer — required (runtime function, excluded from strict Zod parse but validated for presence) */
|
|
1416
|
+
renderItem: z.function().args(z.unknown(), z.number()).returns(z.unknown()).optional(),
|
|
1417
|
+
/** Rows to render outside visible area (default: 5) */
|
|
1418
|
+
overscan: z.number().int().min(0).optional(),
|
|
1419
|
+
/** CSS class applied to the scroll container */
|
|
1420
|
+
className: z.string().optional(),
|
|
1421
|
+
/** Locale for built-in empty state + error messages */
|
|
1422
|
+
locale: z.enum(["en", "fr"]).optional(),
|
|
1423
|
+
/** a11y: when provided, rows become keyboard-activable (role=button + tabIndex=0 + Enter/Space) */
|
|
1424
|
+
onRowClick: z.function().args(z.unknown(), z.number()).returns(z.void()).optional()
|
|
1425
|
+
});
|
|
1426
|
+
function validateVirtualListProps(raw) {
|
|
1427
|
+
return VirtualListPropsSchema.parse(raw);
|
|
1428
|
+
}
|
|
1429
|
+
var DEFAULT_OVERSCAN = 5;
|
|
1430
|
+
var DEFAULT_ITEM_HEIGHT = 40;
|
|
1431
|
+
function VirtualList(props) {
|
|
1432
|
+
try {
|
|
1433
|
+
validateVirtualListProps({
|
|
1434
|
+
items: props.items,
|
|
1435
|
+
itemHeight: props.itemHeight,
|
|
1436
|
+
overscan: props.overscan,
|
|
1437
|
+
className: props.className,
|
|
1438
|
+
locale: props.locale
|
|
1439
|
+
});
|
|
1440
|
+
} catch {
|
|
1441
|
+
const locale2 = props.locale === "fr" ? "fr" : "en";
|
|
1442
|
+
return /* @__PURE__ */ jsx("div", { role: "alert", children: t("VirtualList.error.invalidProps", locale2) });
|
|
1443
|
+
}
|
|
1444
|
+
const locale = props.locale === "fr" ? "fr" : "en";
|
|
1445
|
+
if (props.items.length === 0) {
|
|
1446
|
+
return /* @__PURE__ */ jsx("output", { "aria-live": "polite", children: t("VirtualList.empty.message", locale) });
|
|
1447
|
+
}
|
|
1448
|
+
return /* @__PURE__ */ jsx(
|
|
1449
|
+
VirtualListInner,
|
|
1450
|
+
{
|
|
1451
|
+
items: props.items,
|
|
1452
|
+
itemHeight: props.itemHeight,
|
|
1453
|
+
estimateSize: props.estimateSize,
|
|
1454
|
+
renderItem: props.renderItem,
|
|
1455
|
+
onRowClick: props.onRowClick,
|
|
1456
|
+
overscan: props.overscan ?? DEFAULT_OVERSCAN,
|
|
1457
|
+
className: props.className,
|
|
1458
|
+
locale
|
|
1459
|
+
}
|
|
1460
|
+
);
|
|
1461
|
+
}
|
|
1462
|
+
function VirtualListInner({
|
|
1463
|
+
items,
|
|
1464
|
+
itemHeight,
|
|
1465
|
+
estimateSize,
|
|
1466
|
+
renderItem,
|
|
1467
|
+
onRowClick,
|
|
1468
|
+
overscan,
|
|
1469
|
+
className
|
|
1470
|
+
}) {
|
|
1471
|
+
const scrollRef = useRef(null);
|
|
1472
|
+
const resolvedEstimateSize = useCallback(
|
|
1473
|
+
(index) => {
|
|
1474
|
+
if (estimateSize) return estimateSize(index);
|
|
1475
|
+
return itemHeight ?? DEFAULT_ITEM_HEIGHT;
|
|
1476
|
+
},
|
|
1477
|
+
[estimateSize, itemHeight]
|
|
1478
|
+
);
|
|
1479
|
+
const rowVirtualizer = useVirtualizer({
|
|
1480
|
+
count: items.length,
|
|
1481
|
+
/* v8 ignore next 1 */
|
|
1482
|
+
getScrollElement: () => scrollRef.current,
|
|
1483
|
+
estimateSize: resolvedEstimateSize,
|
|
1484
|
+
overscan
|
|
1485
|
+
});
|
|
1486
|
+
const virtualItems = rowVirtualizer.getVirtualItems();
|
|
1487
|
+
const totalSize = rowVirtualizer.getTotalSize();
|
|
547
1488
|
return /* @__PURE__ */ jsx(
|
|
548
1489
|
"div",
|
|
549
1490
|
{
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
style: {
|
|
1491
|
+
"data-virtual": "true",
|
|
1492
|
+
ref: scrollRef,
|
|
1493
|
+
className,
|
|
1494
|
+
style: { height: 400, overflow: "auto", position: "relative" },
|
|
1495
|
+
children: /* @__PURE__ */ jsx("div", { style: { height: `${totalSize}px`, position: "relative" }, children: virtualItems.map((virtualRow) => {
|
|
1496
|
+
const item = items[virtualRow.index];
|
|
1497
|
+
const index = virtualRow.index;
|
|
1498
|
+
const rowProps = onRowClick ? {
|
|
1499
|
+
role: "button",
|
|
1500
|
+
tabIndex: 0,
|
|
1501
|
+
"data-list-row": true,
|
|
1502
|
+
style: {
|
|
1503
|
+
position: "absolute",
|
|
1504
|
+
top: 0,
|
|
1505
|
+
left: 0,
|
|
1506
|
+
width: "100%",
|
|
1507
|
+
transform: `translateY(${virtualRow.start}px)`,
|
|
1508
|
+
cursor: "pointer"
|
|
1509
|
+
},
|
|
1510
|
+
onClick: () => onRowClick(item, index),
|
|
1511
|
+
onKeyDown: (e) => {
|
|
1512
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
1513
|
+
e.preventDefault();
|
|
1514
|
+
onRowClick(item, index);
|
|
1515
|
+
}
|
|
1516
|
+
}
|
|
1517
|
+
} : {
|
|
1518
|
+
"data-list-row": true,
|
|
1519
|
+
style: {
|
|
1520
|
+
position: "absolute",
|
|
1521
|
+
top: 0,
|
|
1522
|
+
left: 0,
|
|
1523
|
+
width: "100%",
|
|
1524
|
+
transform: `translateY(${virtualRow.start}px)`
|
|
1525
|
+
}
|
|
1526
|
+
};
|
|
1527
|
+
return /* @__PURE__ */ jsx("div", { ...rowProps, children: renderItem(item, index) }, virtualRow.key);
|
|
1528
|
+
}) })
|
|
555
1529
|
}
|
|
556
1530
|
);
|
|
557
1531
|
}
|
|
558
|
-
|
|
1532
|
+
|
|
1533
|
+
// src/components/shared/datetime.ts
|
|
1534
|
+
function formatTimestamp(iso, locale, timeZone) {
|
|
1535
|
+
const d = new Date(iso);
|
|
1536
|
+
if (Number.isNaN(d.getTime())) return iso;
|
|
559
1537
|
try {
|
|
560
|
-
|
|
561
|
-
|
|
1538
|
+
return new Intl.DateTimeFormat(locale, {
|
|
1539
|
+
dateStyle: "medium",
|
|
1540
|
+
timeStyle: "short",
|
|
1541
|
+
...timeZone ? { timeZone } : {}
|
|
1542
|
+
}).format(d);
|
|
562
1543
|
} catch {
|
|
563
|
-
|
|
564
|
-
|
|
1544
|
+
return iso;
|
|
1545
|
+
}
|
|
1546
|
+
}
|
|
1547
|
+
|
|
1548
|
+
// src/components/shared/url.ts
|
|
1549
|
+
function normalizeHttpUrl(value) {
|
|
1550
|
+
if (typeof value !== "string") return void 0;
|
|
1551
|
+
try {
|
|
1552
|
+
const u = new URL(value);
|
|
1553
|
+
return u.protocol === "https:" || u.protocol === "http:" ? u.href : void 0;
|
|
1554
|
+
} catch {
|
|
1555
|
+
return void 0;
|
|
1556
|
+
}
|
|
1557
|
+
}
|
|
1558
|
+
var FeedMessageSchema = z.object({
|
|
1559
|
+
id: z.string().optional(),
|
|
1560
|
+
sender: z.string().min(1),
|
|
1561
|
+
channel: z.string().optional(),
|
|
1562
|
+
/** Plain text. Rendered as text, never as HTML. */
|
|
1563
|
+
content: z.string(),
|
|
1564
|
+
/** ISO 8601. */
|
|
1565
|
+
timestamp: z.string(),
|
|
1566
|
+
attachmentUrl: z.string().optional(),
|
|
1567
|
+
attachmentLabel: z.string().optional(),
|
|
1568
|
+
unread: z.boolean().optional()
|
|
1569
|
+
});
|
|
1570
|
+
var MessageFeedPropsSchema = z.object({
|
|
1571
|
+
messages: z.array(FeedMessageSchema).default([]),
|
|
1572
|
+
maxItems: z.number().int().positive().optional(),
|
|
1573
|
+
/** IANA timezone for timestamps (e.g. `hostContext.timeZone`). */
|
|
1574
|
+
timeZone: z.string().optional(),
|
|
1575
|
+
/** Above this many shown messages the feed is virtualised through VirtualList. */
|
|
1576
|
+
virtualizeThreshold: z.number().int().positive().default(50),
|
|
1577
|
+
loading: z.boolean().default(false),
|
|
1578
|
+
locale: z.enum(["en", "fr"]).default("en")
|
|
1579
|
+
});
|
|
1580
|
+
function validateMessageFeedProps(raw) {
|
|
1581
|
+
return MessageFeedPropsSchema.parse(raw);
|
|
1582
|
+
}
|
|
1583
|
+
function latestMessages(messages, maxItems) {
|
|
1584
|
+
const shown = maxItems === void 0 ? [...messages] : messages.slice(-maxItems);
|
|
1585
|
+
return { shown, total: messages.length };
|
|
1586
|
+
}
|
|
1587
|
+
function Attachment({
|
|
1588
|
+
m,
|
|
1589
|
+
locale,
|
|
1590
|
+
onOpenLink
|
|
1591
|
+
}) {
|
|
1592
|
+
if (!m.attachmentUrl) return null;
|
|
1593
|
+
const label = m.attachmentLabel ?? t("MessageFeed.attachment", locale);
|
|
1594
|
+
const url = normalizeHttpUrl(m.attachmentUrl);
|
|
1595
|
+
if (!url) return /* @__PURE__ */ jsx("span", { className: "text-sm text-slate-500", children: label });
|
|
1596
|
+
return onOpenLink ? /* @__PURE__ */ jsx(
|
|
1597
|
+
"button",
|
|
1598
|
+
{
|
|
1599
|
+
type: "button",
|
|
1600
|
+
onClick: () => onOpenLink(url),
|
|
1601
|
+
className: "text-left text-sm text-blue-700 underline",
|
|
1602
|
+
children: label
|
|
1603
|
+
}
|
|
1604
|
+
) : /* @__PURE__ */ jsx(
|
|
1605
|
+
"a",
|
|
1606
|
+
{
|
|
1607
|
+
href: url,
|
|
1608
|
+
rel: "noopener noreferrer",
|
|
1609
|
+
target: "_blank",
|
|
1610
|
+
className: "text-sm text-blue-700 underline",
|
|
1611
|
+
children: label
|
|
1612
|
+
}
|
|
1613
|
+
);
|
|
1614
|
+
}
|
|
1615
|
+
function Message({
|
|
1616
|
+
m,
|
|
1617
|
+
locale,
|
|
1618
|
+
timeZone,
|
|
1619
|
+
onOpenLink
|
|
1620
|
+
}) {
|
|
1621
|
+
return /* @__PURE__ */ jsxs("article", { className: "flex flex-col gap-1 border-b border-slate-100 py-2", children: [
|
|
1622
|
+
/* @__PURE__ */ jsxs("header", { className: "flex flex-wrap items-center gap-2 text-sm", children: [
|
|
1623
|
+
/* @__PURE__ */ jsx("strong", { className: "text-slate-900", children: m.sender }),
|
|
1624
|
+
m.channel ? /* @__PURE__ */ jsx(Badge, { label: m.channel, variant: "neutral", locale }) : null,
|
|
1625
|
+
/* @__PURE__ */ jsx("time", { dateTime: m.timestamp, className: "text-xs text-slate-500", children: formatTimestamp(m.timestamp, locale, timeZone) }),
|
|
1626
|
+
m.unread ? /* @__PURE__ */ jsx("span", { className: "text-xs font-semibold text-blue-700", children: t("MessageFeed.unread", locale) }) : null
|
|
1627
|
+
] }),
|
|
1628
|
+
/* @__PURE__ */ jsx("p", { className: "m-0 whitespace-pre-wrap text-sm text-slate-800", children: m.content }),
|
|
1629
|
+
/* @__PURE__ */ jsx(Attachment, { m, locale, onOpenLink })
|
|
1630
|
+
] });
|
|
1631
|
+
}
|
|
1632
|
+
function MessageFeed(raw = {}) {
|
|
1633
|
+
const parsed = MessageFeedPropsSchema.safeParse(raw);
|
|
1634
|
+
const locale = raw?.locale === "fr" ? "fr" : "en";
|
|
1635
|
+
if (!parsed.success) {
|
|
1636
|
+
return /* @__PURE__ */ jsx("div", { role: "alert", children: t("MessageFeed.error.invalidProps", locale) });
|
|
1637
|
+
}
|
|
1638
|
+
const p = parsed.data;
|
|
1639
|
+
if (p.loading) {
|
|
1640
|
+
return /* @__PURE__ */ jsx("div", { "aria-busy": "true", "aria-label": t("MessageFeed.loading", p.locale), children: /* @__PURE__ */ jsx(Skeleton, { variant: "text", count: 5, locale: p.locale }) });
|
|
1641
|
+
}
|
|
1642
|
+
if (p.messages.length === 0) {
|
|
1643
|
+
return /* @__PURE__ */ jsx(EmptyState, { title: t("MessageFeed.empty.message", p.locale), locale: p.locale });
|
|
1644
|
+
}
|
|
1645
|
+
const { shown, total } = latestMessages(p.messages, p.maxItems);
|
|
1646
|
+
const virtual = shown.length > p.virtualizeThreshold;
|
|
1647
|
+
return /* @__PURE__ */ jsxs("section", { "aria-label": t("MessageFeed.title", p.locale), lang: p.locale, children: [
|
|
1648
|
+
shown.length < total ? /* @__PURE__ */ jsx("p", { className: "m-0 pb-1 text-xs text-slate-500", children: t("MessageFeed.showing", p.locale).replace("{n}", String(shown.length)).replace("{total}", String(total)) }) : null,
|
|
1649
|
+
virtual ? /* @__PURE__ */ jsx(
|
|
1650
|
+
VirtualList,
|
|
1651
|
+
{
|
|
1652
|
+
items: shown,
|
|
1653
|
+
itemHeight: 96,
|
|
1654
|
+
locale: p.locale,
|
|
1655
|
+
renderItem: (m) => /* @__PURE__ */ jsx(Message, { m, locale: p.locale, timeZone: p.timeZone, onOpenLink: raw.onOpenLink })
|
|
1656
|
+
}
|
|
1657
|
+
) : /* @__PURE__ */ jsx("ol", { className: "m-0 list-none p-0", children: shown.map((m, i) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(Message, { m, locale: p.locale, timeZone: p.timeZone, onOpenLink: raw.onOpenLink }) }, m.id ?? `msg-${i}`)) })
|
|
1658
|
+
] });
|
|
1659
|
+
}
|
|
1660
|
+
|
|
1661
|
+
// src/components/display/MessageFeed.markdown.ts
|
|
1662
|
+
function messageFeedToMarkdown(props, locale = "en") {
|
|
1663
|
+
const parsed = MessageFeedPropsSchema.safeParse(props);
|
|
1664
|
+
const title = mdHeading(t("MessageFeed.title", locale));
|
|
1665
|
+
if (!parsed.success || parsed.data.messages.length === 0) {
|
|
1666
|
+
return `${title}
|
|
1667
|
+
|
|
1668
|
+
${t("MessageFeed.empty.message", locale)}
|
|
1669
|
+
`;
|
|
1670
|
+
}
|
|
1671
|
+
const { messages, maxItems, timeZone } = parsed.data;
|
|
1672
|
+
const { shown, total } = latestMessages(messages, maxItems);
|
|
1673
|
+
const lines = shown.map((m) => {
|
|
1674
|
+
const when = [formatTimestamp(m.timestamp, locale, timeZone), m.channel ?? ""].filter(Boolean).join(", ");
|
|
1675
|
+
const href = normalizeHttpUrl(m.attachmentUrl);
|
|
1676
|
+
const label = mdEscape(m.attachmentLabel ?? t("MessageFeed.attachment", locale));
|
|
1677
|
+
const attachment = m.attachmentUrl ? href ? ` [${label}](${mdUrl(href)})` : ` ${label}` : "";
|
|
1678
|
+
return `> **${mdEscape(m.sender)}** (${mdEscape(when)}): ${mdEscape(m.content)}${attachment}`;
|
|
1679
|
+
});
|
|
1680
|
+
const cut = shown.length < total ? `${t("MessageFeed.showing", locale).replace("{n}", String(shown.length)).replace("{total}", String(total))}
|
|
1681
|
+
|
|
1682
|
+
` : "";
|
|
1683
|
+
return `${title}
|
|
1684
|
+
|
|
1685
|
+
${cut}${lines.join("\n>\n")}
|
|
1686
|
+
`;
|
|
1687
|
+
}
|
|
1688
|
+
|
|
1689
|
+
// src/components/forms/RadioGroup.logic.ts
|
|
1690
|
+
function getOptionLabelClasses(appearance, isSelected) {
|
|
1691
|
+
const base = "mosaic-forms-radiogroup-option-label inline-flex items-center px-3 py-1 text-sm";
|
|
1692
|
+
const shape = "rounded-full border" ;
|
|
1693
|
+
const tone = isSelected ? "bg-slate-900 text-white border-slate-900" : "bg-white text-slate-700 border-slate-300 hover:bg-slate-50";
|
|
1694
|
+
return [base, shape, tone].filter(Boolean).join(" ");
|
|
1695
|
+
}
|
|
1696
|
+
|
|
1697
|
+
// src/components/display/PipelineBoard.logic.ts
|
|
1698
|
+
function stageSummary(deals) {
|
|
1699
|
+
const amounts = deals.flatMap((d) => d.amount === void 0 ? [] : [d.amount]);
|
|
1700
|
+
return { count: deals.length, total: sumDecimalStrings(amounts) };
|
|
1701
|
+
}
|
|
1702
|
+
var SUMMARY_STAGE_LIMIT = 5;
|
|
1703
|
+
var PipelineDealSchema = z.object({
|
|
1704
|
+
id: z.string().min(1),
|
|
1705
|
+
title: z.string().min(1),
|
|
1706
|
+
/** Decimal STRING (never a float), in the board currency. */
|
|
1707
|
+
amount: z.string().optional(),
|
|
1708
|
+
contact: z.string().optional(),
|
|
1709
|
+
url: z.string().optional()
|
|
1710
|
+
});
|
|
1711
|
+
var PipelineStageSchema = z.object({
|
|
1712
|
+
id: z.string().min(1),
|
|
1713
|
+
name: z.string().min(1),
|
|
1714
|
+
deals: z.array(PipelineDealSchema).default([])
|
|
1715
|
+
});
|
|
1716
|
+
var PipelineBoardPropsSchema = z.object({
|
|
1717
|
+
stages: z.array(PipelineStageSchema).default([]),
|
|
1718
|
+
/** ISO 4217 code used to format amounts. */
|
|
1719
|
+
currency: z.string().default("EUR"),
|
|
1720
|
+
layout: z.enum(["board", "summary"]).default("board"),
|
|
1721
|
+
loading: z.boolean().default(false),
|
|
1722
|
+
locale: z.enum(["en", "fr"]).default("en")
|
|
1723
|
+
});
|
|
1724
|
+
function validatePipelineBoardProps(raw) {
|
|
1725
|
+
return PipelineBoardPropsSchema.parse(raw);
|
|
1726
|
+
}
|
|
1727
|
+
function dealsLabel(n, locale) {
|
|
1728
|
+
return n === 1 ? t("PipelineBoard.deals.one", locale) : t("PipelineBoard.deals.many", locale).replace("{n}", String(n));
|
|
1729
|
+
}
|
|
1730
|
+
function Totals({
|
|
1731
|
+
stage,
|
|
1732
|
+
currency,
|
|
1733
|
+
locale
|
|
1734
|
+
}) {
|
|
1735
|
+
const { count, total } = stageSummary(stage.deals);
|
|
1736
|
+
return /* @__PURE__ */ jsxs("span", { className: "flex flex-col text-sm text-slate-600", children: [
|
|
1737
|
+
/* @__PURE__ */ jsx("span", { children: dealsLabel(count, locale) }),
|
|
1738
|
+
/* @__PURE__ */ jsx("span", { className: "tabular-nums", children: total === void 0 ? "\u2014" : formatDecimalString(total, locale, currency) })
|
|
1739
|
+
] });
|
|
1740
|
+
}
|
|
1741
|
+
function PipelineBoard(raw = {}) {
|
|
1742
|
+
const parsed = PipelineBoardPropsSchema.safeParse(raw);
|
|
1743
|
+
const locale = raw?.locale === "fr" ? "fr" : "en";
|
|
1744
|
+
const [hidden, setHidden] = useState(/* @__PURE__ */ new Set());
|
|
1745
|
+
if (!parsed.success) {
|
|
1746
|
+
return /* @__PURE__ */ jsx("div", { role: "alert", children: t("PipelineBoard.error.invalidProps", locale) });
|
|
565
1747
|
}
|
|
1748
|
+
const p = parsed.data;
|
|
1749
|
+
if (p.loading) {
|
|
1750
|
+
return /* @__PURE__ */ jsx("div", { "aria-busy": "true", "aria-label": t("PipelineBoard.loading", p.locale), children: /* @__PURE__ */ jsx(Skeleton, { variant: "rect", height: 160, locale: p.locale }) });
|
|
1751
|
+
}
|
|
1752
|
+
if (p.stages.length === 0) {
|
|
1753
|
+
return /* @__PURE__ */ jsx(EmptyState, { title: t("PipelineBoard.empty.message", p.locale), locale: p.locale });
|
|
1754
|
+
}
|
|
1755
|
+
if (p.layout === "summary") {
|
|
1756
|
+
const shown = p.stages.slice(0, SUMMARY_STAGE_LIMIT);
|
|
1757
|
+
const rest = p.stages.length - shown.length;
|
|
1758
|
+
return /* @__PURE__ */ jsxs(
|
|
1759
|
+
"section",
|
|
1760
|
+
{
|
|
1761
|
+
"aria-label": t("PipelineBoard.title", p.locale),
|
|
1762
|
+
lang: p.locale,
|
|
1763
|
+
className: "flex flex-col gap-2",
|
|
1764
|
+
children: [
|
|
1765
|
+
/* @__PURE__ */ jsx("ul", { className: "m-0 flex list-none flex-col gap-2 p-0", children: shown.map((s) => /* @__PURE__ */ jsxs("li", { className: "flex items-center justify-between gap-2", children: [
|
|
1766
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium text-slate-900", children: s.name }),
|
|
1767
|
+
/* @__PURE__ */ jsx(Totals, { stage: s, currency: p.currency, locale: p.locale })
|
|
1768
|
+
] }, s.id)) }),
|
|
1769
|
+
rest > 0 ? /* @__PURE__ */ jsx("span", { className: "text-sm text-slate-500", children: t("PipelineBoard.more", p.locale).replace("{n}", String(rest)) }) : null,
|
|
1770
|
+
raw.onRequestFullscreen ? /* @__PURE__ */ jsx(
|
|
1771
|
+
"button",
|
|
1772
|
+
{
|
|
1773
|
+
type: "button",
|
|
1774
|
+
onClick: raw.onRequestFullscreen,
|
|
1775
|
+
className: "self-start rounded border border-slate-300 px-3 py-1 text-sm hover:bg-slate-50",
|
|
1776
|
+
children: t("PipelineBoard.summary.open", p.locale)
|
|
1777
|
+
}
|
|
1778
|
+
) : null
|
|
1779
|
+
]
|
|
1780
|
+
}
|
|
1781
|
+
);
|
|
1782
|
+
}
|
|
1783
|
+
const visible = p.stages.filter((s) => !hidden.has(s.id));
|
|
1784
|
+
const toggle = (id) => {
|
|
1785
|
+
const next = new Set(hidden);
|
|
1786
|
+
if (next.has(id)) next.delete(id);
|
|
1787
|
+
else if (visible.length > 1) next.add(id);
|
|
1788
|
+
setHidden(next);
|
|
1789
|
+
};
|
|
1790
|
+
return /* @__PURE__ */ jsxs(
|
|
1791
|
+
"section",
|
|
1792
|
+
{
|
|
1793
|
+
"aria-label": t("PipelineBoard.title", p.locale),
|
|
1794
|
+
lang: p.locale,
|
|
1795
|
+
className: "flex flex-col gap-3",
|
|
1796
|
+
children: [
|
|
1797
|
+
/* @__PURE__ */ jsxs("fieldset", { className: "m-0 flex min-w-0 flex-wrap gap-2 border-0 p-0", children: [
|
|
1798
|
+
/* @__PURE__ */ jsx("legend", { className: "sr-only", children: t("PipelineBoard.filter.label", p.locale) }),
|
|
1799
|
+
p.stages.map((s) => {
|
|
1800
|
+
const on = !hidden.has(s.id);
|
|
1801
|
+
return /* @__PURE__ */ jsx(
|
|
1802
|
+
"button",
|
|
1803
|
+
{
|
|
1804
|
+
type: "button",
|
|
1805
|
+
"aria-pressed": on,
|
|
1806
|
+
onClick: () => toggle(s.id),
|
|
1807
|
+
className: getOptionLabelClasses("chips", on),
|
|
1808
|
+
children: s.name
|
|
1809
|
+
},
|
|
1810
|
+
s.id
|
|
1811
|
+
);
|
|
1812
|
+
})
|
|
1813
|
+
] }),
|
|
1814
|
+
/* @__PURE__ */ jsx(
|
|
1815
|
+
"div",
|
|
1816
|
+
{
|
|
1817
|
+
className: "flex flex-row gap-3 overflow-x-auto",
|
|
1818
|
+
style: {
|
|
1819
|
+
scrollPaddingLeft: "var(--mosaic-safe-area-left, 0px)",
|
|
1820
|
+
scrollPaddingRight: "var(--mosaic-safe-area-right, 0px)"
|
|
1821
|
+
},
|
|
1822
|
+
children: visible.map((s) => /* @__PURE__ */ jsxs(
|
|
1823
|
+
"section",
|
|
1824
|
+
{
|
|
1825
|
+
"aria-label": s.name,
|
|
1826
|
+
className: "flex w-64 shrink-0 flex-col gap-2 rounded-lg border border-slate-200 bg-slate-50 p-3",
|
|
1827
|
+
children: [
|
|
1828
|
+
/* @__PURE__ */ jsxs("header", { className: "flex items-start justify-between gap-2", children: [
|
|
1829
|
+
/* @__PURE__ */ jsx("strong", { className: "text-slate-900", children: s.name }),
|
|
1830
|
+
/* @__PURE__ */ jsx(Totals, { stage: s, currency: p.currency, locale: p.locale })
|
|
1831
|
+
] }),
|
|
1832
|
+
s.deals.length === 0 ? /* @__PURE__ */ jsx("span", { className: "text-sm text-slate-500", children: t("PipelineBoard.stage.empty", p.locale) }) : /* @__PURE__ */ jsx("ul", { className: "m-0 flex list-none flex-col gap-2 p-0", children: s.deals.map((d) => /* @__PURE__ */ jsxs("li", { className: "rounded border border-slate-200 bg-white p-2", children: [
|
|
1833
|
+
raw.onOpenLink && normalizeHttpUrl(d.url) ? /* @__PURE__ */ jsx(
|
|
1834
|
+
"button",
|
|
1835
|
+
{
|
|
1836
|
+
type: "button",
|
|
1837
|
+
onClick: () => raw.onOpenLink?.(normalizeHttpUrl(d.url)),
|
|
1838
|
+
className: "text-left font-medium text-blue-700 underline",
|
|
1839
|
+
children: d.title
|
|
1840
|
+
}
|
|
1841
|
+
) : /* @__PURE__ */ jsx("span", { className: "font-medium text-slate-900", children: d.title }),
|
|
1842
|
+
d.amount !== void 0 ? /* @__PURE__ */ jsx("span", { className: "block text-sm tabular-nums text-slate-700", children: formatDecimalString(d.amount, p.locale, p.currency) }) : null,
|
|
1843
|
+
d.contact ? /* @__PURE__ */ jsx(Badge, { label: d.contact, variant: "neutral", locale: p.locale }) : null
|
|
1844
|
+
] }, d.id)) })
|
|
1845
|
+
]
|
|
1846
|
+
},
|
|
1847
|
+
s.id
|
|
1848
|
+
))
|
|
1849
|
+
}
|
|
1850
|
+
)
|
|
1851
|
+
]
|
|
1852
|
+
}
|
|
1853
|
+
);
|
|
1854
|
+
}
|
|
1855
|
+
|
|
1856
|
+
// src/components/display/PipelineBoard.markdown.ts
|
|
1857
|
+
function pipelineBoardToMarkdown(props, locale = "en") {
|
|
1858
|
+
const parsed = PipelineBoardPropsSchema.safeParse(props);
|
|
1859
|
+
const title = mdHeading(t("PipelineBoard.title", locale));
|
|
1860
|
+
if (!parsed.success || parsed.data.stages.length === 0) {
|
|
1861
|
+
return `${title}
|
|
1862
|
+
|
|
1863
|
+
${t("PipelineBoard.empty.message", locale)}
|
|
1864
|
+
`;
|
|
1865
|
+
}
|
|
1866
|
+
const { stages, currency } = parsed.data;
|
|
1867
|
+
const money = (v) => mdEscape(formatDecimalString(v, locale, currency));
|
|
1868
|
+
const table = mdTable(
|
|
1869
|
+
[
|
|
1870
|
+
t("PipelineBoard.col.stage", locale),
|
|
1871
|
+
t("PipelineBoard.col.deals", locale),
|
|
1872
|
+
t("PipelineBoard.total", locale)
|
|
1873
|
+
],
|
|
1874
|
+
stages.map((s) => {
|
|
1875
|
+
const sum = stageSummary(s.deals);
|
|
1876
|
+
return [
|
|
1877
|
+
mdEscape(s.name),
|
|
1878
|
+
String(sum.count),
|
|
1879
|
+
sum.total === void 0 ? "-" : money(sum.total)
|
|
1880
|
+
];
|
|
1881
|
+
})
|
|
1882
|
+
);
|
|
1883
|
+
const sections = stages.filter((s) => s.deals.length > 0).map((s) => {
|
|
1884
|
+
const lines = s.deals.map((d) => {
|
|
1885
|
+
const amount = d.amount === void 0 ? "" : ` (${money(d.amount)})`;
|
|
1886
|
+
const contact = d.contact ? ` - ${mdEscape(d.contact)}` : "";
|
|
1887
|
+
return `- ${mdEscape(d.title)}${amount}${contact}`;
|
|
1888
|
+
});
|
|
1889
|
+
return `${mdHeading(mdEscape(s.name), 2)}
|
|
1890
|
+
|
|
1891
|
+
${lines.join("\n")}`;
|
|
1892
|
+
});
|
|
1893
|
+
return `${title}
|
|
1894
|
+
|
|
1895
|
+
${table}
|
|
1896
|
+
${sections.length ? `
|
|
1897
|
+
${sections.join("\n\n")}
|
|
1898
|
+
` : ""}`;
|
|
566
1899
|
}
|
|
567
1900
|
|
|
568
|
-
export { EmptyStatePropsSchema, Skeleton, SkeletonPropsSchema, StreamingTableView, TableView, TableViewPropsSchema, resolveSkeletonDimensions, validateEmptyStateProps, validateSkeletonProps, validateTableViewProps };
|
|
1901
|
+
export { BalanceCard, BalanceCardPropsSchema, EmptyStatePropsSchema, MessageFeed, MessageFeedPropsSchema, PipelineBoard, PipelineBoardPropsSchema, Skeleton, SkeletonPropsSchema, StatCard, StatCardPropsSchema, StreamingTableView, TableView, TableViewPropsSchema, balanceCardToMarkdown, compareDecimalStrings, messageFeedToMarkdown, nextSort, pipelineBoardToMarkdown, resolveSkeletonDimensions, sortItems, statCardToMarkdown, validateBalanceCardProps, validateEmptyStateProps, validateMessageFeedProps, validatePipelineBoardProps, validateSkeletonProps, validateStatCardProps, validateTableViewProps };
|
|
569
1902
|
//# sourceMappingURL=display.js.map
|
|
570
1903
|
//# sourceMappingURL=display.js.map
|