@vantageos/mosaic 0.4.0 → 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 +39 -15
- package/README.md +114 -5
- 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 +186 -2
- package/dist/artifacts.cjs.map +1 -1
- package/dist/artifacts.js +186 -2
- package/dist/artifacts.js.map +1 -1
- package/dist/confirmation.cjs +724 -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 +722 -4
- package/dist/confirmation.js.map +1 -1
- package/dist/display.cjs +1551 -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 +1535 -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 +2198 -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 +2163 -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 +186 -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 +186 -2
- package/dist/media.js.map +1 -1
- package/dist/preact/artifacts.js +186 -2
- package/dist/preact/artifacts.js.map +1 -1
- package/dist/preact/confirmation.d.ts +321 -1
- package/dist/preact/confirmation.js +625 -4
- package/dist/preact/confirmation.js.map +1 -1
- package/dist/preact/display.d.ts +544 -47
- package/dist/preact/display.js +1190 -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 +465 -3
- package/dist/preact/progress.js.map +1 -1
- package/dist/progress.cjs +521 -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 +518 -3
- package/dist/progress.js.map +1 -1
- package/dist/react/artifacts.cjs +186 -2
- package/dist/react/artifacts.cjs.map +1 -1
- package/dist/react/artifacts.js +186 -2
- package/dist/react/artifacts.js.map +1 -1
- package/dist/react/confirmation.cjs +626 -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 +625 -4
- package/dist/react/confirmation.js.map +1 -1
- package/dist/react/display.cjs +1076 -123
- package/dist/react/display.cjs.map +1 -1
- package/dist/react/display.d.cts +13 -27
- package/dist/react/display.d.ts +13 -27
- package/dist/react/display.js +1064 -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 +467 -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 +465 -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/package.json +250 -233
- package/registry.yaml +44 -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/react/display.cjs
CHANGED
|
@@ -116,7 +116,99 @@ var EN = {
|
|
|
116
116
|
"Select.search.placeholder": "Search\u2026",
|
|
117
117
|
"Select.empty": "No options available",
|
|
118
118
|
"Select.error.required": "Select an option",
|
|
119
|
-
"Select.error.invalidProps": "Select: invalid props"
|
|
119
|
+
"Select.error.invalidProps": "Select: invalid props",
|
|
120
|
+
"ModelContext.selection.one": "1 row selected",
|
|
121
|
+
"ModelContext.selection.many": "{n} rows selected",
|
|
122
|
+
"ModelContext.selection.none": "No rows selected",
|
|
123
|
+
"TableView.sort.by": "Sort by",
|
|
124
|
+
"TableView.select.row": "Select row",
|
|
125
|
+
"TableView.select.all": "Select all rows",
|
|
126
|
+
"TableView.loading": "Loading table",
|
|
127
|
+
"StatCard.title": "Statistic",
|
|
128
|
+
"StatCard.trend.up": "Up",
|
|
129
|
+
"StatCard.trend.down": "Down",
|
|
130
|
+
"StatCard.trend.flat": "Flat",
|
|
131
|
+
"StatCard.empty.message": "No statistic to display",
|
|
132
|
+
"StatCard.loading": "Loading statistic",
|
|
133
|
+
"StatCard.error.invalidProps": "StatCard: invalid props",
|
|
134
|
+
"BalanceCard.title": "Balance",
|
|
135
|
+
"BalanceCard.network": "Network",
|
|
136
|
+
"BalanceCard.testnet": "Testnet",
|
|
137
|
+
"BalanceCard.address": "Address",
|
|
138
|
+
"BalanceCard.contract": "Token contract",
|
|
139
|
+
"BalanceCard.value": "Value",
|
|
140
|
+
"BalanceCard.copy": "Copy address",
|
|
141
|
+
"BalanceCard.copied": "Address copied",
|
|
142
|
+
"BalanceCard.empty.message": "No balance to display",
|
|
143
|
+
"BalanceCard.loading": "Loading balance",
|
|
144
|
+
"BalanceCard.error.invalidProps": "BalanceCard: invalid props",
|
|
145
|
+
"Timeline.title": "Timeline",
|
|
146
|
+
"Timeline.status.done": "Done",
|
|
147
|
+
"Timeline.status.current": "In progress",
|
|
148
|
+
"Timeline.status.pending": "Pending",
|
|
149
|
+
"Timeline.status.blocked": "Blocked",
|
|
150
|
+
"Timeline.completed": "{n} completed",
|
|
151
|
+
"Timeline.more": "{n} more",
|
|
152
|
+
"Timeline.empty.message": "No steps to display",
|
|
153
|
+
"Timeline.loading": "Loading timeline",
|
|
154
|
+
"Timeline.error.invalidProps": "Timeline: invalid props",
|
|
155
|
+
"MessageFeed.title": "Messages",
|
|
156
|
+
"MessageFeed.unread": "Unread",
|
|
157
|
+
"MessageFeed.attachment": "Attachment",
|
|
158
|
+
"MessageFeed.showing": "Showing the latest {n} of {total}",
|
|
159
|
+
"MessageFeed.empty.message": "No messages",
|
|
160
|
+
"MessageFeed.loading": "Loading messages",
|
|
161
|
+
"MessageFeed.error.invalidProps": "MessageFeed: invalid props",
|
|
162
|
+
"PipelineBoard.title": "Pipeline",
|
|
163
|
+
"PipelineBoard.deals.one": "1 deal",
|
|
164
|
+
"PipelineBoard.deals.many": "{n} deals",
|
|
165
|
+
"PipelineBoard.total": "Total",
|
|
166
|
+
"PipelineBoard.stage.empty": "No deals",
|
|
167
|
+
"PipelineBoard.filter.label": "Show stages",
|
|
168
|
+
"PipelineBoard.summary.open": "Open full board",
|
|
169
|
+
"PipelineBoard.more": "{n} more stages",
|
|
170
|
+
"PipelineBoard.empty.message": "No pipeline to display",
|
|
171
|
+
"PipelineBoard.loading": "Loading pipeline",
|
|
172
|
+
"PipelineBoard.error.invalidProps": "PipelineBoard: invalid props",
|
|
173
|
+
"PipelineBoard.col.stage": "Stage",
|
|
174
|
+
"PipelineBoard.col.deals": "Deals",
|
|
175
|
+
"TransactionPreview.title": "Transaction preview",
|
|
176
|
+
"TransactionPreview.notice": "NOT SIGNED - preparation only",
|
|
177
|
+
"TransactionPreview.kind.transfer": "Transfer",
|
|
178
|
+
"TransactionPreview.kind.swap": "Swap",
|
|
179
|
+
"TransactionPreview.testnet": "Testnet",
|
|
180
|
+
"TransactionPreview.field.network": "Network",
|
|
181
|
+
"TransactionPreview.field.from": "From",
|
|
182
|
+
"TransactionPreview.field.to": "To",
|
|
183
|
+
"TransactionPreview.field.amount": "Amount",
|
|
184
|
+
"TransactionPreview.field.fee": "Network fee",
|
|
185
|
+
"TransactionPreview.field.sell": "Sell",
|
|
186
|
+
"TransactionPreview.field.buy": "Buy",
|
|
187
|
+
"TransactionPreview.field.minReceived": "Minimum received",
|
|
188
|
+
"TransactionPreview.field.route": "Route",
|
|
189
|
+
"TransactionPreview.field.priceImpact": "Price impact",
|
|
190
|
+
"TransactionPreview.field.expires": "Quote expires",
|
|
191
|
+
"TransactionPreview.priceImpact.high": "High price impact",
|
|
192
|
+
"TransactionPreview.simulation.success": "Simulation succeeded",
|
|
193
|
+
"TransactionPreview.simulation.failed": "Simulation failed",
|
|
194
|
+
"TransactionPreview.simulation.unavailable": "Simulation unavailable",
|
|
195
|
+
"TransactionPreview.warnings": "Warnings",
|
|
196
|
+
"TransactionPreview.handoff": "Open in wallet",
|
|
197
|
+
"TransactionPreview.empty.message": "No transaction to preview",
|
|
198
|
+
"TransactionPreview.loading": "Loading preview",
|
|
199
|
+
"TransactionPreview.error.invalidProps": "TransactionPreview: invalid props",
|
|
200
|
+
"ProgressBar.title": "Progress",
|
|
201
|
+
"ConfirmDialog.title": "Confirmation",
|
|
202
|
+
"ConfirmDialog.options": "Answers",
|
|
203
|
+
"TableView.title": "Data table",
|
|
204
|
+
"TableView.markdown.more": "{n} more rows",
|
|
205
|
+
"MarkdownRenderer.title": "Markdown content",
|
|
206
|
+
"TokenDisplayOnceModal.title": "Token (shown once)",
|
|
207
|
+
"StatusBadge.title": "Status",
|
|
208
|
+
"TokenDisplayOnceModal.notice.once": "The value is shown once, in the app view only.",
|
|
209
|
+
"TokenDisplayOnceModal.field.scope": "Scope",
|
|
210
|
+
"TokenDisplayOnceModal.field.expires": "Expires",
|
|
211
|
+
"TokenDisplayOnceModal.field.fingerprint": "Fingerprint"
|
|
120
212
|
};
|
|
121
213
|
var FR = {
|
|
122
214
|
// ─── Non-forms components ──────────────────────────────────────────────────
|
|
@@ -210,7 +302,99 @@ var FR = {
|
|
|
210
302
|
"Select.search.placeholder": "Rechercher\u2026",
|
|
211
303
|
"Select.empty": "Aucune option disponible",
|
|
212
304
|
"Select.error.required": "S\xE9lectionnez une option",
|
|
213
|
-
"Select.error.invalidProps": "Select : propri\xE9t\xE9s invalides"
|
|
305
|
+
"Select.error.invalidProps": "Select : propri\xE9t\xE9s invalides",
|
|
306
|
+
"ModelContext.selection.one": "1 ligne s\xE9lectionn\xE9e",
|
|
307
|
+
"ModelContext.selection.many": "{n} lignes s\xE9lectionn\xE9es",
|
|
308
|
+
"ModelContext.selection.none": "Aucune ligne s\xE9lectionn\xE9e",
|
|
309
|
+
"TableView.sort.by": "Trier par",
|
|
310
|
+
"TableView.select.row": "S\xE9lectionner la ligne",
|
|
311
|
+
"TableView.select.all": "Tout s\xE9lectionner",
|
|
312
|
+
"TableView.loading": "Chargement du tableau",
|
|
313
|
+
"StatCard.title": "Statistique",
|
|
314
|
+
"StatCard.trend.up": "En hausse",
|
|
315
|
+
"StatCard.trend.down": "En baisse",
|
|
316
|
+
"StatCard.trend.flat": "Stable",
|
|
317
|
+
"StatCard.empty.message": "Aucune statistique \xE0 afficher",
|
|
318
|
+
"StatCard.loading": "Chargement de la statistique",
|
|
319
|
+
"StatCard.error.invalidProps": "StatCard : propri\xE9t\xE9s invalides",
|
|
320
|
+
"BalanceCard.title": "Solde",
|
|
321
|
+
"BalanceCard.network": "R\xE9seau",
|
|
322
|
+
"BalanceCard.testnet": "R\xE9seau de test",
|
|
323
|
+
"BalanceCard.address": "Adresse",
|
|
324
|
+
"BalanceCard.contract": "Contrat du jeton",
|
|
325
|
+
"BalanceCard.value": "Valeur",
|
|
326
|
+
"BalanceCard.copy": "Copier l'adresse",
|
|
327
|
+
"BalanceCard.copied": "Adresse copi\xE9e",
|
|
328
|
+
"BalanceCard.empty.message": "Aucun solde \xE0 afficher",
|
|
329
|
+
"BalanceCard.loading": "Chargement du solde",
|
|
330
|
+
"BalanceCard.error.invalidProps": "BalanceCard : propri\xE9t\xE9s invalides",
|
|
331
|
+
"Timeline.title": "Chronologie",
|
|
332
|
+
"Timeline.status.done": "Termin\xE9",
|
|
333
|
+
"Timeline.status.current": "En cours",
|
|
334
|
+
"Timeline.status.pending": "\xC0 venir",
|
|
335
|
+
"Timeline.status.blocked": "Bloqu\xE9",
|
|
336
|
+
"Timeline.completed": "{n} termin\xE9(s)",
|
|
337
|
+
"Timeline.more": "{n} de plus",
|
|
338
|
+
"Timeline.empty.message": "Aucune \xE9tape \xE0 afficher",
|
|
339
|
+
"Timeline.loading": "Chargement de la chronologie",
|
|
340
|
+
"Timeline.error.invalidProps": "Timeline : propri\xE9t\xE9s invalides",
|
|
341
|
+
"MessageFeed.title": "Messages",
|
|
342
|
+
"MessageFeed.unread": "Non lu",
|
|
343
|
+
"MessageFeed.attachment": "Pi\xE8ce jointe",
|
|
344
|
+
"MessageFeed.showing": "Affichage des {n} derniers sur {total}",
|
|
345
|
+
"MessageFeed.empty.message": "Aucun message",
|
|
346
|
+
"MessageFeed.loading": "Chargement des messages",
|
|
347
|
+
"MessageFeed.error.invalidProps": "MessageFeed : propri\xE9t\xE9s invalides",
|
|
348
|
+
"PipelineBoard.title": "Pipeline",
|
|
349
|
+
"PipelineBoard.deals.one": "1 affaire",
|
|
350
|
+
"PipelineBoard.deals.many": "{n} affaires",
|
|
351
|
+
"PipelineBoard.total": "Total",
|
|
352
|
+
"PipelineBoard.stage.empty": "Aucune affaire",
|
|
353
|
+
"PipelineBoard.filter.label": "Afficher les \xE9tapes",
|
|
354
|
+
"PipelineBoard.summary.open": "Ouvrir le tableau complet",
|
|
355
|
+
"PipelineBoard.more": "{n} \xE9tapes de plus",
|
|
356
|
+
"PipelineBoard.empty.message": "Aucun pipeline \xE0 afficher",
|
|
357
|
+
"PipelineBoard.loading": "Chargement du pipeline",
|
|
358
|
+
"PipelineBoard.error.invalidProps": "PipelineBoard : propri\xE9t\xE9s invalides",
|
|
359
|
+
"PipelineBoard.col.stage": "\xC9tape",
|
|
360
|
+
"PipelineBoard.col.deals": "Affaires",
|
|
361
|
+
"TransactionPreview.title": "Aper\xE7u de transaction",
|
|
362
|
+
"TransactionPreview.notice": "NON SIGN\xC9 - pr\xE9paration uniquement",
|
|
363
|
+
"TransactionPreview.kind.transfer": "Transfert",
|
|
364
|
+
"TransactionPreview.kind.swap": "\xC9change",
|
|
365
|
+
"TransactionPreview.testnet": "R\xE9seau de test",
|
|
366
|
+
"TransactionPreview.field.network": "R\xE9seau",
|
|
367
|
+
"TransactionPreview.field.from": "De",
|
|
368
|
+
"TransactionPreview.field.to": "\xC0",
|
|
369
|
+
"TransactionPreview.field.amount": "Montant",
|
|
370
|
+
"TransactionPreview.field.fee": "Frais de r\xE9seau",
|
|
371
|
+
"TransactionPreview.field.sell": "Vendre",
|
|
372
|
+
"TransactionPreview.field.buy": "Acheter",
|
|
373
|
+
"TransactionPreview.field.minReceived": "Minimum re\xE7u",
|
|
374
|
+
"TransactionPreview.field.route": "Itin\xE9raire",
|
|
375
|
+
"TransactionPreview.field.priceImpact": "Impact sur le prix",
|
|
376
|
+
"TransactionPreview.field.expires": "Expiration du devis",
|
|
377
|
+
"TransactionPreview.priceImpact.high": "Impact sur le prix \xE9lev\xE9",
|
|
378
|
+
"TransactionPreview.simulation.success": "Simulation r\xE9ussie",
|
|
379
|
+
"TransactionPreview.simulation.failed": "Simulation \xE9chou\xE9e",
|
|
380
|
+
"TransactionPreview.simulation.unavailable": "Simulation indisponible",
|
|
381
|
+
"TransactionPreview.warnings": "Avertissements",
|
|
382
|
+
"TransactionPreview.handoff": "Ouvrir dans le portefeuille",
|
|
383
|
+
"TransactionPreview.empty.message": "Aucune transaction \xE0 pr\xE9visualiser",
|
|
384
|
+
"TransactionPreview.loading": "Chargement de l'aper\xE7u",
|
|
385
|
+
"TransactionPreview.error.invalidProps": "TransactionPreview : propri\xE9t\xE9s invalides",
|
|
386
|
+
"ProgressBar.title": "Progression",
|
|
387
|
+
"ConfirmDialog.title": "Confirmation",
|
|
388
|
+
"ConfirmDialog.options": "R\xE9ponses",
|
|
389
|
+
"TableView.title": "Tableau de donn\xE9es",
|
|
390
|
+
"TableView.markdown.more": "{n} lignes de plus",
|
|
391
|
+
"MarkdownRenderer.title": "Contenu Markdown",
|
|
392
|
+
"TokenDisplayOnceModal.title": "Jeton (affich\xE9 une fois)",
|
|
393
|
+
"StatusBadge.title": "Statut",
|
|
394
|
+
"TokenDisplayOnceModal.notice.once": "La valeur n'est affich\xE9e qu'une fois, dans la vue de l'application uniquement.",
|
|
395
|
+
"TokenDisplayOnceModal.field.scope": "Port\xE9e",
|
|
396
|
+
"TokenDisplayOnceModal.field.expires": "Expire le",
|
|
397
|
+
"TokenDisplayOnceModal.field.fingerprint": "Empreinte"
|
|
214
398
|
};
|
|
215
399
|
var TABLE = {
|
|
216
400
|
en: EN,
|
|
@@ -580,92 +764,172 @@ function Badge(raw) {
|
|
|
580
764
|
return /* @__PURE__ */ jsxRuntime.jsx("span", { role: "alert", children: t("Badge.error.invalidProps", locale) });
|
|
581
765
|
}
|
|
582
766
|
}
|
|
767
|
+
|
|
768
|
+
// src/components/display/TableView.logic.ts
|
|
769
|
+
function isMissing(v) {
|
|
770
|
+
return v === null || v === void 0 || v === "";
|
|
771
|
+
}
|
|
772
|
+
var DECIMAL = /^(-)?(\d+)(?:\.(\d+))?$/;
|
|
773
|
+
function parseDecimal(s) {
|
|
774
|
+
const m = DECIMAL.exec(s.trim());
|
|
775
|
+
if (!m) return void 0;
|
|
776
|
+
const int = (m[2] ?? "0").replace(/^0+(?=\d)/, "");
|
|
777
|
+
const frac = (m[3] ?? "").replace(/0+$/, "");
|
|
778
|
+
const zero = int === "0" && frac === "";
|
|
779
|
+
return { neg: m[1] === "-" && !zero, int, frac };
|
|
780
|
+
}
|
|
781
|
+
function compareDecimalStrings(a, b) {
|
|
782
|
+
const x = parseDecimal(a);
|
|
783
|
+
const y = parseDecimal(b);
|
|
784
|
+
if (!x || !y) return 0;
|
|
785
|
+
if (x.neg !== y.neg) return x.neg ? -1 : 1;
|
|
786
|
+
const sign = x.neg ? -1 : 1;
|
|
787
|
+
if (x.int.length !== y.int.length) return sign * (x.int.length < y.int.length ? -1 : 1);
|
|
788
|
+
if (x.int !== y.int) return sign * (x.int < y.int ? -1 : 1);
|
|
789
|
+
const len = Math.max(x.frac.length, y.frac.length);
|
|
790
|
+
const fx = x.frac.padEnd(len, "0");
|
|
791
|
+
const fy = y.frac.padEnd(len, "0");
|
|
792
|
+
if (fx === fy) return 0;
|
|
793
|
+
return sign * (fx < fy ? -1 : 1);
|
|
794
|
+
}
|
|
795
|
+
var collators = /* @__PURE__ */ new Map();
|
|
796
|
+
function collator(locale) {
|
|
797
|
+
let c = collators.get(locale);
|
|
798
|
+
if (!c) {
|
|
799
|
+
c = new Intl.Collator(locale, { numeric: true, sensitivity: "base" });
|
|
800
|
+
collators.set(locale, c);
|
|
801
|
+
}
|
|
802
|
+
return c;
|
|
803
|
+
}
|
|
804
|
+
function compareCells(a, b, kind, locale) {
|
|
805
|
+
if (kind === "amount") return compareDecimalStrings(String(a), String(b));
|
|
806
|
+
if (kind === "number") {
|
|
807
|
+
const x = Number(a);
|
|
808
|
+
const y = Number(b);
|
|
809
|
+
if (Number.isNaN(x) || Number.isNaN(y)) return collator(locale).compare(String(a), String(b));
|
|
810
|
+
return x === y ? 0 : x < y ? -1 : 1;
|
|
811
|
+
}
|
|
812
|
+
return collator(locale).compare(String(a), String(b));
|
|
813
|
+
}
|
|
814
|
+
function sortItems(items, getValue, kind, direction, locale) {
|
|
815
|
+
const sign = direction === "asc" ? 1 : -1;
|
|
816
|
+
return items.map((item, index) => ({ item, index, value: getValue(item) })).sort((p, q) => {
|
|
817
|
+
const pm = isMissing(p.value);
|
|
818
|
+
const qm = isMissing(q.value);
|
|
819
|
+
if (pm || qm) return pm && qm ? p.index - q.index : pm ? 1 : -1;
|
|
820
|
+
const c = compareCells(p.value, q.value, kind, locale);
|
|
821
|
+
return c !== 0 ? sign * c : p.index - q.index;
|
|
822
|
+
}).map((e) => e.item);
|
|
823
|
+
}
|
|
824
|
+
function nextSort(current, key) {
|
|
825
|
+
if (!current || current.key !== key) return { key, direction: "asc" };
|
|
826
|
+
return current.direction === "asc" ? { key, direction: "desc" } : void 0;
|
|
827
|
+
}
|
|
828
|
+
function toggleSelection(current, key, mode) {
|
|
829
|
+
if (mode === "none") return new Set(current);
|
|
830
|
+
if (mode === "single") return current.has(key) ? /* @__PURE__ */ new Set() : /* @__PURE__ */ new Set([key]);
|
|
831
|
+
const next = new Set(current);
|
|
832
|
+
if (!next.delete(key)) next.add(key);
|
|
833
|
+
return next;
|
|
834
|
+
}
|
|
835
|
+
function toggleAll(current, allKeys) {
|
|
836
|
+
return allKeys.length > 0 && allKeys.every((k) => current.has(k)) ? /* @__PURE__ */ new Set() : new Set(allKeys);
|
|
837
|
+
}
|
|
838
|
+
var ColumnKindSchema = zod.z.enum(["text", "number", "amount", "icon"]);
|
|
839
|
+
var AlignSchema = zod.z.enum(["start", "center", "end"]);
|
|
583
840
|
var ColumnDefSchema = zod.z.object({
|
|
584
841
|
key: zod.z.string().min(1),
|
|
585
|
-
header: zod.z.string().min(1)
|
|
842
|
+
header: zod.z.string().min(1),
|
|
843
|
+
/** Cell kind: drives comparison and rendering. `amount` = decimal STRING, never a float. */
|
|
844
|
+
type: ColumnKindSchema.default("text"),
|
|
845
|
+
/** Defaults to `end` for `number` / `amount` columns, `start` otherwise. */
|
|
846
|
+
align: AlignSchema.optional(),
|
|
847
|
+
/** CSS width: a number is pixels. */
|
|
848
|
+
width: zod.z.union([zod.z.string().min(1), zod.z.number().positive()]).optional(),
|
|
849
|
+
sortable: zod.z.boolean().default(false),
|
|
850
|
+
/**
|
|
851
|
+
* Values of this column are masked in the A-1 markdown fallback and kept out of the HTML
|
|
852
|
+
* props of a tool result (delivered via the result `_meta`), never written to `content[]`.
|
|
853
|
+
*/
|
|
854
|
+
sensitive: zod.z.boolean().default(false)
|
|
586
855
|
// render is a runtime function — excluded from Zod, typed separately
|
|
587
856
|
});
|
|
857
|
+
var SortSchema = zod.z.object({ key: zod.z.string().min(1), direction: zod.z.enum(["asc", "desc"]) });
|
|
588
858
|
var TableViewPropsSchema = zod.z.object({
|
|
589
859
|
columns: zod.z.array(ColumnDefSchema).min(1, "TableView requires at least one column"),
|
|
860
|
+
/**
|
|
861
|
+
* Serialisable rows, for views built through `createMosaicResource` (the React props carry
|
|
862
|
+
* `rows` separately).
|
|
863
|
+
*/
|
|
864
|
+
rows: zod.z.array(zod.z.record(zod.z.unknown())).default([]),
|
|
590
865
|
virtualizeThreshold: zod.z.number().int().positive().default(100),
|
|
591
866
|
ariaLabel: zod.z.string().min(1),
|
|
592
|
-
locale: zod.z.enum(["en", "fr"]).default("en")
|
|
867
|
+
locale: zod.z.enum(["en", "fr"]).default("en"),
|
|
868
|
+
selectionMode: zod.z.enum(["none", "single", "multiple"]).default("none"),
|
|
869
|
+
/** Initial sort. */
|
|
870
|
+
sort: SortSchema.optional(),
|
|
871
|
+
stickyHeader: zod.z.boolean().default(false),
|
|
872
|
+
/** Scroll region height when `stickyHeader` is on (pixels). */
|
|
873
|
+
maxHeight: zod.z.number().int().positive().default(400),
|
|
874
|
+
density: zod.z.enum(["comfortable", "compact"]).default("comfortable"),
|
|
875
|
+
loading: zod.z.boolean().default(false)
|
|
593
876
|
});
|
|
594
877
|
function validateTableViewProps(raw) {
|
|
595
878
|
return TableViewPropsSchema.parse(raw);
|
|
596
879
|
}
|
|
597
880
|
var OVERSCAN = 5;
|
|
598
|
-
|
|
599
|
-
|
|
881
|
+
var SKELETON_ROWS = ["head", "r1", "r2", "r3", "r4", "r5"];
|
|
882
|
+
function resolveLocale(l) {
|
|
883
|
+
return l === "fr" ? "fr" : "en";
|
|
884
|
+
}
|
|
885
|
+
function validate(props) {
|
|
600
886
|
try {
|
|
601
887
|
const validated = validateTableViewProps({
|
|
602
|
-
columns: props.columns.map(({ key, header }) => ({
|
|
888
|
+
columns: props.columns.map(({ key, header, type, align, width, sortable }) => ({
|
|
889
|
+
key,
|
|
890
|
+
header,
|
|
891
|
+
type,
|
|
892
|
+
align,
|
|
893
|
+
width,
|
|
894
|
+
sortable
|
|
895
|
+
})),
|
|
603
896
|
virtualizeThreshold: props.virtualizeThreshold,
|
|
604
897
|
ariaLabel: props.ariaLabel,
|
|
605
|
-
locale: props.locale
|
|
898
|
+
locale: props.locale,
|
|
899
|
+
selectionMode: props.selectionMode,
|
|
900
|
+
density: props.density
|
|
606
901
|
});
|
|
607
|
-
|
|
902
|
+
return validated.virtualizeThreshold;
|
|
608
903
|
} catch {
|
|
609
|
-
|
|
610
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { role: "alert", children: t("TableView.error.invalidProps", locale) });
|
|
904
|
+
return void 0;
|
|
611
905
|
}
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
{
|
|
618
|
-
rows: props.rows,
|
|
619
|
-
columns: props.columns,
|
|
620
|
-
ariaLabel: props.ariaLabel,
|
|
621
|
-
locale: resolvedLocale,
|
|
622
|
-
onRowClick: props.onRowClick
|
|
623
|
-
}
|
|
624
|
-
);
|
|
906
|
+
}
|
|
907
|
+
function TableView(props) {
|
|
908
|
+
const threshold = validate(props);
|
|
909
|
+
if (threshold === void 0) {
|
|
910
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { role: "alert", children: t("TableView.error.invalidProps", resolveLocale(props.locale)) });
|
|
625
911
|
}
|
|
626
912
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
627
|
-
|
|
913
|
+
TableCore,
|
|
628
914
|
{
|
|
915
|
+
...props,
|
|
629
916
|
rows: props.rows,
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
locale: resolvedLocale,
|
|
633
|
-
onRowClick: props.onRowClick
|
|
917
|
+
virtualizeThreshold: threshold,
|
|
918
|
+
locale: resolveLocale(props.locale)
|
|
634
919
|
}
|
|
635
920
|
);
|
|
636
921
|
}
|
|
637
922
|
function StreamingTableView(props) {
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
columns: props.columns.map(({ key, header }) => ({ key, header })),
|
|
642
|
-
virtualizeThreshold: props.virtualizeThreshold,
|
|
643
|
-
ariaLabel: props.ariaLabel,
|
|
644
|
-
locale: props.locale
|
|
645
|
-
});
|
|
646
|
-
validatedThreshold = validated.virtualizeThreshold;
|
|
647
|
-
} catch {
|
|
648
|
-
const locale = props.locale === "fr" ? "fr" : "en";
|
|
649
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { role: "alert", children: t("TableView.error.invalidProps", locale) });
|
|
923
|
+
const threshold = validate(props);
|
|
924
|
+
if (threshold === void 0) {
|
|
925
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { role: "alert", children: t("TableView.error.invalidProps", resolveLocale(props.locale)) });
|
|
650
926
|
}
|
|
651
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
652
|
-
StreamingTableViewInner,
|
|
653
|
-
{
|
|
654
|
-
...props,
|
|
655
|
-
virtualizeThreshold: validatedThreshold,
|
|
656
|
-
onRowClick: props.onRowClick
|
|
657
|
-
}
|
|
658
|
-
);
|
|
927
|
+
return /* @__PURE__ */ jsxRuntime.jsx(StreamingTableViewInner, { ...props, virtualizeThreshold: threshold });
|
|
659
928
|
}
|
|
660
929
|
function StreamingTableViewInner({
|
|
661
|
-
columns,
|
|
662
930
|
rows$,
|
|
663
|
-
|
|
664
|
-
ariaLabel,
|
|
665
|
-
locale,
|
|
666
|
-
onRowClick
|
|
931
|
+
...rest
|
|
667
932
|
}) {
|
|
668
|
-
const resolvedLocale = locale === "fr" ? "fr" : "en";
|
|
669
933
|
const [rows, setRows] = react.useState([]);
|
|
670
934
|
react.useEffect(() => {
|
|
671
935
|
setRows([]);
|
|
@@ -676,87 +940,282 @@ function StreamingTableViewInner({
|
|
|
676
940
|
sub.unsubscribe();
|
|
677
941
|
};
|
|
678
942
|
}, [rows$]);
|
|
679
|
-
|
|
680
|
-
|
|
943
|
+
return /* @__PURE__ */ jsxRuntime.jsx(TableCore, { ...rest, rows, locale: resolveLocale(rest.locale) });
|
|
944
|
+
}
|
|
945
|
+
function kindOf(col) {
|
|
946
|
+
return col.type ?? "text";
|
|
947
|
+
}
|
|
948
|
+
function alignOf(col) {
|
|
949
|
+
return col.align ?? (col.type === "amount" || col.type === "number" ? "end" : "start");
|
|
950
|
+
}
|
|
951
|
+
function cellText(row, key) {
|
|
952
|
+
return String(row[key] ?? "");
|
|
953
|
+
}
|
|
954
|
+
function safeIconSrc(v) {
|
|
955
|
+
if (typeof v !== "string") return void 0;
|
|
956
|
+
return /^(https?:\/\/|data:image\/)/i.test(v) ? v : void 0;
|
|
957
|
+
}
|
|
958
|
+
function renderCell(col, row) {
|
|
959
|
+
if (col.render) {
|
|
960
|
+
return col.render(row);
|
|
961
|
+
}
|
|
962
|
+
const value = row[col.key];
|
|
963
|
+
if (col.type === "icon") {
|
|
964
|
+
const src = safeIconSrc(value);
|
|
965
|
+
return src ? /* @__PURE__ */ jsxRuntime.jsx("img", { src, alt: "", width: 20, height: 20, style: { verticalAlign: "middle" } }) : null;
|
|
966
|
+
}
|
|
967
|
+
if (col.type === "amount" || col.type === "number") {
|
|
968
|
+
return /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontVariantNumeric: "tabular-nums" }, children: String(value ?? "") });
|
|
969
|
+
}
|
|
970
|
+
return String(value ?? "");
|
|
971
|
+
}
|
|
972
|
+
function TableCore(props) {
|
|
973
|
+
const {
|
|
974
|
+
columns,
|
|
975
|
+
rows,
|
|
976
|
+
ariaLabel,
|
|
977
|
+
locale,
|
|
978
|
+
selectionMode = "none",
|
|
979
|
+
stickyHeader = false,
|
|
980
|
+
density = "comfortable"
|
|
981
|
+
} = props;
|
|
982
|
+
const [innerSort, setInnerSort] = react.useState(props.defaultSort);
|
|
983
|
+
const sort = props.sort === void 0 ? innerSort : props.sort ?? void 0;
|
|
984
|
+
const onSort = (key) => {
|
|
985
|
+
const next = nextSort(sort, key);
|
|
986
|
+
if (props.sort === void 0) setInnerSort(next);
|
|
987
|
+
props.onSortChange?.(next);
|
|
988
|
+
};
|
|
989
|
+
const items = react.useMemo(
|
|
990
|
+
() => rows.map((row, i) => {
|
|
991
|
+
const id = row.id;
|
|
992
|
+
return { row, key: id != null ? String(id) : `row-${i}` };
|
|
993
|
+
}),
|
|
994
|
+
[rows]
|
|
995
|
+
);
|
|
996
|
+
const sorted = react.useMemo(() => {
|
|
997
|
+
const col = sort ? columns.find((c) => c.key === sort.key) : void 0;
|
|
998
|
+
if (!sort || !col) return items;
|
|
999
|
+
return sortItems(
|
|
1000
|
+
items,
|
|
1001
|
+
(it) => it.row[col.key],
|
|
1002
|
+
kindOf(col),
|
|
1003
|
+
sort.direction,
|
|
1004
|
+
locale
|
|
1005
|
+
);
|
|
1006
|
+
}, [items, sort, columns, locale]);
|
|
1007
|
+
const [innerSelected, setInnerSelected] = react.useState(
|
|
1008
|
+
() => new Set(props.defaultSelectedKeys ?? [])
|
|
1009
|
+
);
|
|
1010
|
+
const selected = props.selectedKeys ? new Set(props.selectedKeys) : innerSelected;
|
|
1011
|
+
const commit = (next) => {
|
|
1012
|
+
if (!props.selectedKeys) setInnerSelected(next);
|
|
1013
|
+
const chosen = sorted.filter((it) => next.has(it.key));
|
|
1014
|
+
props.onSelectionChange?.(
|
|
1015
|
+
chosen.map((it) => it.row),
|
|
1016
|
+
chosen.map((it) => it.key)
|
|
1017
|
+
);
|
|
1018
|
+
};
|
|
1019
|
+
const selection = selectionMode === "none" ? void 0 : {
|
|
1020
|
+
mode: selectionMode,
|
|
1021
|
+
selected,
|
|
1022
|
+
toggle: (key) => commit(toggleSelection(selected, key, selectionMode)),
|
|
1023
|
+
toggleAll: () => commit(
|
|
1024
|
+
toggleAll(
|
|
1025
|
+
selected,
|
|
1026
|
+
sorted.map((it) => it.key)
|
|
1027
|
+
)
|
|
1028
|
+
)
|
|
1029
|
+
};
|
|
1030
|
+
if (props.loading) {
|
|
1031
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { "aria-busy": "true", "aria-label": t("TableView.loading", locale), lang: locale, children: SKELETON_ROWS.map((_, i) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
1032
|
+
Skeleton,
|
|
1033
|
+
{
|
|
1034
|
+
variant: "rect",
|
|
1035
|
+
height: i === 0 ? 24 : 36,
|
|
1036
|
+
locale
|
|
1037
|
+
},
|
|
1038
|
+
SKELETON_ROWS[i]
|
|
1039
|
+
)) });
|
|
1040
|
+
}
|
|
1041
|
+
const content = {
|
|
1042
|
+
items: sorted,
|
|
1043
|
+
columns,
|
|
1044
|
+
ariaLabel,
|
|
1045
|
+
locale,
|
|
1046
|
+
onRowClick: props.onRowClick,
|
|
1047
|
+
sort,
|
|
1048
|
+
onSort,
|
|
1049
|
+
selection,
|
|
1050
|
+
sticky: stickyHeader,
|
|
1051
|
+
density
|
|
1052
|
+
};
|
|
1053
|
+
const isVirtual = rows.length > props.virtualizeThreshold;
|
|
1054
|
+
if (isVirtual) return /* @__PURE__ */ jsxRuntime.jsx(VirtualTable, { ...content });
|
|
1055
|
+
if (stickyHeader) {
|
|
681
1056
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
682
|
-
|
|
1057
|
+
"div",
|
|
683
1058
|
{
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
ariaLabel,
|
|
687
|
-
|
|
688
|
-
|
|
1059
|
+
tabIndex: 0,
|
|
1060
|
+
role: "region",
|
|
1061
|
+
"aria-label": ariaLabel,
|
|
1062
|
+
style: { maxHeight: props.maxHeight ?? 400, overflow: "auto" },
|
|
1063
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(StaticTable, { ...content })
|
|
689
1064
|
}
|
|
690
1065
|
);
|
|
691
1066
|
}
|
|
1067
|
+
return /* @__PURE__ */ jsxRuntime.jsx(StaticTable, { ...content });
|
|
1068
|
+
}
|
|
1069
|
+
var CELL_PADDING = { comfortable: void 0, compact: "2px 6px" };
|
|
1070
|
+
function HeaderRow({
|
|
1071
|
+
columns,
|
|
1072
|
+
locale,
|
|
1073
|
+
sort,
|
|
1074
|
+
onSort,
|
|
1075
|
+
selection,
|
|
1076
|
+
sticky,
|
|
1077
|
+
density,
|
|
1078
|
+
items
|
|
1079
|
+
}) {
|
|
1080
|
+
const padding = CELL_PADDING[density];
|
|
1081
|
+
const stickyStyle = sticky ? { position: "sticky", top: 0, background: "var(--mosaic-color-background, Canvas)" } : {};
|
|
1082
|
+
const allSelected = items.length > 0 && items.every((it) => selection?.selected.has(it.key));
|
|
1083
|
+
const someSelected = items.some((it) => selection?.selected.has(it.key));
|
|
1084
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
|
|
1085
|
+
selection ? /* @__PURE__ */ jsxRuntime.jsx("th", { scope: "col", style: { ...stickyStyle, padding, width: 32 }, children: selection.mode === "multiple" ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
1086
|
+
"input",
|
|
1087
|
+
{
|
|
1088
|
+
type: "checkbox",
|
|
1089
|
+
"aria-label": t("TableView.select.all", locale),
|
|
1090
|
+
checked: allSelected,
|
|
1091
|
+
ref: (el) => {
|
|
1092
|
+
if (el) el.indeterminate = someSelected && !allSelected;
|
|
1093
|
+
},
|
|
1094
|
+
onChange: selection.toggleAll
|
|
1095
|
+
}
|
|
1096
|
+
) : null }) : null,
|
|
1097
|
+
columns.map((col) => {
|
|
1098
|
+
const dir = sort?.key === col.key ? sort.direction : void 0;
|
|
1099
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1100
|
+
"th",
|
|
1101
|
+
{
|
|
1102
|
+
scope: "col",
|
|
1103
|
+
"aria-sort": col.sortable ? dir === "asc" ? "ascending" : dir === "desc" ? "descending" : "none" : void 0,
|
|
1104
|
+
style: {
|
|
1105
|
+
...stickyStyle,
|
|
1106
|
+
padding,
|
|
1107
|
+
textAlign: alignOf(col),
|
|
1108
|
+
...col.width !== void 0 ? { width: col.width } : {}
|
|
1109
|
+
},
|
|
1110
|
+
children: col.sortable ? /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1111
|
+
"button",
|
|
1112
|
+
{
|
|
1113
|
+
type: "button",
|
|
1114
|
+
"aria-label": `${t("TableView.sort.by", locale)} ${col.header}`,
|
|
1115
|
+
onClick: () => onSort(col.key),
|
|
1116
|
+
style: { all: "inherit", cursor: "pointer", padding: 0 },
|
|
1117
|
+
children: [
|
|
1118
|
+
col.header,
|
|
1119
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", children: dir === "asc" ? " \u25B2" : dir === "desc" ? " \u25BC" : "" })
|
|
1120
|
+
]
|
|
1121
|
+
}
|
|
1122
|
+
) : col.header
|
|
1123
|
+
},
|
|
1124
|
+
col.key
|
|
1125
|
+
);
|
|
1126
|
+
})
|
|
1127
|
+
] });
|
|
1128
|
+
}
|
|
1129
|
+
function SelectCell({
|
|
1130
|
+
item,
|
|
1131
|
+
columns,
|
|
1132
|
+
selection,
|
|
1133
|
+
locale,
|
|
1134
|
+
padding
|
|
1135
|
+
}) {
|
|
1136
|
+
const first = columns[0];
|
|
1137
|
+
const label = `${t("TableView.select.row", locale)}: ${first ? cellText(item.row, first.key) : item.key}`;
|
|
692
1138
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
693
|
-
|
|
1139
|
+
"td",
|
|
694
1140
|
{
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
1141
|
+
style: { padding, width: 32 },
|
|
1142
|
+
onClick: (e) => e.stopPropagation(),
|
|
1143
|
+
onKeyDown: (e) => e.stopPropagation(),
|
|
1144
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1145
|
+
"input",
|
|
1146
|
+
{
|
|
1147
|
+
type: selection.mode === "multiple" ? "checkbox" : "radio",
|
|
1148
|
+
name: selection.mode === "single" ? "mosaic-table-selection" : void 0,
|
|
1149
|
+
"aria-label": label,
|
|
1150
|
+
checked: selection.selected.has(item.key),
|
|
1151
|
+
onChange: () => selection.toggle(item.key),
|
|
1152
|
+
onKeyDown: (e) => e.stopPropagation()
|
|
1153
|
+
}
|
|
1154
|
+
)
|
|
700
1155
|
}
|
|
701
1156
|
);
|
|
702
1157
|
}
|
|
703
|
-
function
|
|
704
|
-
if (
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
1158
|
+
function rowActivation(onRowClick, row, index) {
|
|
1159
|
+
if (!onRowClick) return {};
|
|
1160
|
+
return {
|
|
1161
|
+
role: "button",
|
|
1162
|
+
tabIndex: 0,
|
|
1163
|
+
onClick: () => onRowClick(row, index),
|
|
1164
|
+
onKeyDown: (e) => {
|
|
1165
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
1166
|
+
e.preventDefault();
|
|
1167
|
+
onRowClick(row, index);
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
};
|
|
708
1171
|
}
|
|
709
|
-
function StaticTable({
|
|
710
|
-
|
|
711
|
-
columns,
|
|
712
|
-
ariaLabel,
|
|
713
|
-
locale,
|
|
714
|
-
onRowClick
|
|
715
|
-
}) {
|
|
1172
|
+
function StaticTable(props) {
|
|
1173
|
+
const { items, columns, ariaLabel, locale, onRowClick, selection, density } = props;
|
|
716
1174
|
const emptyMsg = t("TableView.empty.message", locale);
|
|
1175
|
+
const padding = CELL_PADDING[density];
|
|
1176
|
+
const colSpan = columns.length + (selection ? 1 : 0);
|
|
717
1177
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
718
1178
|
"table",
|
|
719
1179
|
{
|
|
720
1180
|
"aria-label": ariaLabel,
|
|
721
|
-
"aria-rowcount":
|
|
1181
|
+
"aria-rowcount": items.length,
|
|
1182
|
+
"data-density": density,
|
|
722
1183
|
style: { width: "100%", borderCollapse: "collapse" },
|
|
723
1184
|
children: [
|
|
724
|
-
/* @__PURE__ */ jsxRuntime.jsx("thead", { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
725
|
-
/* @__PURE__ */ jsxRuntime.jsx("tbody", { children:
|
|
1185
|
+
/* @__PURE__ */ jsxRuntime.jsx("thead", { children: /* @__PURE__ */ jsxRuntime.jsx(HeaderRow, { ...props }) }),
|
|
1186
|
+
/* @__PURE__ */ jsxRuntime.jsx("tbody", { children: items.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("tr", { children: /* @__PURE__ */ jsxRuntime.jsx("td", { colSpan, "aria-label": emptyMsg, children: emptyMsg }) }) : items.map((item, rowIndex) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
726
1187
|
"tr",
|
|
727
1188
|
{
|
|
728
1189
|
"aria-rowindex": rowIndex + 2,
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
1190
|
+
"data-selected": selection?.selected.has(item.key) ? "true" : void 0,
|
|
1191
|
+
...onRowClick ? { style: { cursor: "pointer" } } : {},
|
|
1192
|
+
...rowActivation(onRowClick, item.row, rowIndex),
|
|
1193
|
+
children: [
|
|
1194
|
+
selection ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
1195
|
+
SelectCell,
|
|
1196
|
+
{
|
|
1197
|
+
item,
|
|
1198
|
+
columns,
|
|
1199
|
+
selection,
|
|
1200
|
+
locale,
|
|
1201
|
+
padding
|
|
738
1202
|
}
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
1203
|
+
) : null,
|
|
1204
|
+
columns.map((col) => /* @__PURE__ */ jsxRuntime.jsx("td", { style: { padding, textAlign: alignOf(col) }, children: renderCell(col, item.row) }, col.key))
|
|
1205
|
+
]
|
|
742
1206
|
},
|
|
743
|
-
|
|
1207
|
+
item.key
|
|
744
1208
|
)) })
|
|
745
1209
|
]
|
|
746
1210
|
}
|
|
747
1211
|
);
|
|
748
1212
|
}
|
|
749
|
-
function VirtualTable({
|
|
750
|
-
|
|
751
|
-
columns,
|
|
752
|
-
ariaLabel,
|
|
753
|
-
// locale reserved for future virtual-mode empty/pagination strings
|
|
754
|
-
locale: _locale,
|
|
755
|
-
onRowClick
|
|
756
|
-
}) {
|
|
1213
|
+
function VirtualTable(props) {
|
|
1214
|
+
const { items, columns, ariaLabel, locale, onRowClick, selection, density } = props;
|
|
757
1215
|
const scrollRef = react.useRef(null);
|
|
1216
|
+
const padding = CELL_PADDING[density];
|
|
758
1217
|
const rowVirtualizer = reactVirtual.useVirtualizer({
|
|
759
|
-
count:
|
|
1218
|
+
count: items.length,
|
|
760
1219
|
/* v8 ignore next 1 */
|
|
761
1220
|
getScrollElement: () => scrollRef.current,
|
|
762
1221
|
estimateSize: react.useCallback(() => 40, []),
|
|
@@ -769,18 +1228,19 @@ function VirtualTable({
|
|
|
769
1228
|
{
|
|
770
1229
|
"data-virtual": "true",
|
|
771
1230
|
ref: scrollRef,
|
|
772
|
-
tabIndex: 0,
|
|
773
1231
|
role: "region",
|
|
1232
|
+
tabIndex: 0,
|
|
774
1233
|
"aria-label": ariaLabel,
|
|
775
1234
|
style: { height: 400, overflow: "auto", position: "relative" },
|
|
776
1235
|
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
777
1236
|
"table",
|
|
778
1237
|
{
|
|
779
1238
|
"aria-label": ariaLabel,
|
|
780
|
-
"aria-rowcount":
|
|
1239
|
+
"aria-rowcount": items.length,
|
|
1240
|
+
"data-density": density,
|
|
781
1241
|
style: { width: "100%", borderCollapse: "collapse" },
|
|
782
1242
|
children: [
|
|
783
|
-
/* @__PURE__ */ jsxRuntime.jsx("thead", { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1243
|
+
/* @__PURE__ */ jsxRuntime.jsx("thead", { children: /* @__PURE__ */ jsxRuntime.jsx(HeaderRow, { ...props, sticky: true }) }),
|
|
784
1244
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
785
1245
|
"tbody",
|
|
786
1246
|
{
|
|
@@ -790,11 +1250,12 @@ function VirtualTable({
|
|
|
790
1250
|
display: "block"
|
|
791
1251
|
},
|
|
792
1252
|
children: virtualItems.map((virtualRow) => {
|
|
793
|
-
const
|
|
794
|
-
return /* @__PURE__ */ jsxRuntime.
|
|
1253
|
+
const item = items[virtualRow.index] ?? { row: {}, key: "" };
|
|
1254
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
795
1255
|
"tr",
|
|
796
1256
|
{
|
|
797
1257
|
"aria-rowindex": virtualRow.index + 2,
|
|
1258
|
+
"data-selected": selection?.selected.has(item.key) ? "true" : void 0,
|
|
798
1259
|
style: {
|
|
799
1260
|
position: "absolute",
|
|
800
1261
|
top: 0,
|
|
@@ -804,20 +1265,22 @@ function VirtualTable({
|
|
|
804
1265
|
display: "flex",
|
|
805
1266
|
...onRowClick ? { cursor: "pointer" } : {}
|
|
806
1267
|
},
|
|
807
|
-
...onRowClick
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
1268
|
+
...rowActivation(onRowClick, item.row, virtualRow.index),
|
|
1269
|
+
children: [
|
|
1270
|
+
selection ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
1271
|
+
SelectCell,
|
|
1272
|
+
{
|
|
1273
|
+
item,
|
|
1274
|
+
columns,
|
|
1275
|
+
selection,
|
|
1276
|
+
locale,
|
|
1277
|
+
padding
|
|
815
1278
|
}
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
1279
|
+
) : null,
|
|
1280
|
+
columns.map((col) => /* @__PURE__ */ jsxRuntime.jsx("td", { style: { flex: 1, padding, textAlign: alignOf(col) }, children: renderCell(col, item.row) }, col.key))
|
|
1281
|
+
]
|
|
819
1282
|
},
|
|
820
|
-
|
|
1283
|
+
item.key || `vrow-${virtualRow.index}`
|
|
821
1284
|
);
|
|
822
1285
|
})
|
|
823
1286
|
}
|
|
@@ -828,13 +1291,496 @@ function VirtualTable({
|
|
|
828
1291
|
}
|
|
829
1292
|
);
|
|
830
1293
|
}
|
|
1294
|
+
var StatCardPropsSchema = zod.z.object({
|
|
1295
|
+
label: zod.z.string().default(""),
|
|
1296
|
+
value: zod.z.string().default(""),
|
|
1297
|
+
unit: zod.z.string().optional(),
|
|
1298
|
+
trend: zod.z.enum(["up", "down", "flat"]).optional(),
|
|
1299
|
+
/** Pre-formatted change, e.g. "+4.2%". */
|
|
1300
|
+
change: zod.z.string().optional(),
|
|
1301
|
+
tone: zod.z.enum(["neutral", "success", "warning", "danger"]).default("neutral"),
|
|
1302
|
+
description: zod.z.string().optional(),
|
|
1303
|
+
loading: zod.z.boolean().default(false),
|
|
1304
|
+
locale: zod.z.enum(["en", "fr"]).default("en")
|
|
1305
|
+
});
|
|
1306
|
+
function validateStatCardProps(raw) {
|
|
1307
|
+
return StatCardPropsSchema.parse(raw);
|
|
1308
|
+
}
|
|
1309
|
+
var TONE_CLASSES = {
|
|
1310
|
+
neutral: "text-slate-900",
|
|
1311
|
+
success: "text-green-700",
|
|
1312
|
+
warning: "text-amber-700",
|
|
1313
|
+
danger: "text-red-700"
|
|
1314
|
+
};
|
|
1315
|
+
var TREND_ARROW = { up: "\u25B2", down: "\u25BC", flat: "\u25AC" };
|
|
1316
|
+
function StatCard(raw) {
|
|
1317
|
+
const parsed = StatCardPropsSchema.safeParse(raw);
|
|
1318
|
+
const locale = raw?.locale === "fr" ? "fr" : "en";
|
|
1319
|
+
if (!parsed.success) {
|
|
1320
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { role: "alert", children: t("StatCard.error.invalidProps", locale) });
|
|
1321
|
+
}
|
|
1322
|
+
const p = parsed.data;
|
|
1323
|
+
if (p.loading) {
|
|
1324
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { "aria-busy": "true", "aria-label": t("StatCard.loading", p.locale), children: /* @__PURE__ */ jsxRuntime.jsx(Skeleton, { variant: "rect", height: 72, locale: p.locale }) });
|
|
1325
|
+
}
|
|
1326
|
+
if (p.label === "" && p.value === "") {
|
|
1327
|
+
return /* @__PURE__ */ jsxRuntime.jsx(EmptyState, { title: t("StatCard.empty.message", p.locale), locale: p.locale });
|
|
1328
|
+
}
|
|
1329
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1330
|
+
"section",
|
|
1331
|
+
{
|
|
1332
|
+
"aria-label": `${t("StatCard.title", p.locale)}: ${p.label}`,
|
|
1333
|
+
lang: p.locale,
|
|
1334
|
+
className: "flex flex-col gap-1 rounded-lg border border-slate-200 bg-white p-4",
|
|
1335
|
+
children: [
|
|
1336
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-slate-500", children: p.label }),
|
|
1337
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: `text-2xl font-semibold tabular-nums ${TONE_CLASSES[p.tone]}`, children: [
|
|
1338
|
+
p.value,
|
|
1339
|
+
p.unit ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-1 text-base font-normal text-slate-500", children: p.unit }) : null
|
|
1340
|
+
] }),
|
|
1341
|
+
p.trend || p.change ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex items-center gap-1 text-sm text-slate-600", children: [
|
|
1342
|
+
p.trend ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1343
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", children: TREND_ARROW[p.trend] }),
|
|
1344
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: t(`StatCard.trend.${p.trend}`, p.locale) })
|
|
1345
|
+
] }) : null,
|
|
1346
|
+
p.change ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "tabular-nums", children: p.change }) : null
|
|
1347
|
+
] }) : null,
|
|
1348
|
+
p.description ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-slate-500", children: p.description }) : null
|
|
1349
|
+
]
|
|
1350
|
+
}
|
|
1351
|
+
);
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1354
|
+
// src/components/shared/decimal.ts
|
|
1355
|
+
var DECIMAL2 = /^(-)?(\d+)(?:\.(\d+))?$/;
|
|
1356
|
+
function sumDecimalStrings(values) {
|
|
1357
|
+
let scale = 0;
|
|
1358
|
+
const parsed = [];
|
|
1359
|
+
for (const v of values) {
|
|
1360
|
+
const m = DECIMAL2.exec(v);
|
|
1361
|
+
if (!m) return void 0;
|
|
1362
|
+
const frac2 = m[3] ?? "";
|
|
1363
|
+
scale = Math.max(scale, frac2.length);
|
|
1364
|
+
parsed.push({ neg: m[1] === "-", int: m[2] ?? "0", frac: frac2 });
|
|
1365
|
+
}
|
|
1366
|
+
let total = 0n;
|
|
1367
|
+
for (const p of parsed) {
|
|
1368
|
+
const n = BigInt(p.int + p.frac.padEnd(scale, "0"));
|
|
1369
|
+
total += p.neg ? -n : n;
|
|
1370
|
+
}
|
|
1371
|
+
const neg = total < 0n;
|
|
1372
|
+
const digits = (neg ? -total : total).toString().padStart(scale + 1, "0");
|
|
1373
|
+
const int = digits.slice(0, digits.length - scale);
|
|
1374
|
+
const frac = digits.slice(digits.length - scale).replace(/0+$/, "");
|
|
1375
|
+
return `${neg ? "-" : ""}${int}${frac ? `.${frac}` : ""}`;
|
|
1376
|
+
}
|
|
1377
|
+
function formatDecimalString(value, locale, currency) {
|
|
1378
|
+
if (!DECIMAL2.test(value)) return value;
|
|
1379
|
+
try {
|
|
1380
|
+
const nf = new Intl.NumberFormat(
|
|
1381
|
+
locale,
|
|
1382
|
+
currency ? { style: "currency", currency } : { maximumFractionDigits: 20 }
|
|
1383
|
+
);
|
|
1384
|
+
return nf.format(value);
|
|
1385
|
+
} catch {
|
|
1386
|
+
return value;
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
function truncateAddress(address, head = 6, tail = 4) {
|
|
1390
|
+
return address.length <= head + tail + 1 ? address : `${address.slice(0, head)}\u2026${address.slice(address.length - tail)}`;
|
|
1391
|
+
}
|
|
1392
|
+
var BalanceCardPropsSchema = zod.z.object({
|
|
1393
|
+
token: zod.z.string().default(""),
|
|
1394
|
+
balance: zod.z.string().default(""),
|
|
1395
|
+
address: zod.z.string().default(""),
|
|
1396
|
+
tokenAddress: zod.z.string().optional(),
|
|
1397
|
+
decimals: zod.z.number().int().nonnegative().optional(),
|
|
1398
|
+
network: zod.z.string().optional(),
|
|
1399
|
+
testnet: zod.z.boolean().default(false),
|
|
1400
|
+
/** Decimal string in `currency`. */
|
|
1401
|
+
usdValue: zod.z.string().optional(),
|
|
1402
|
+
currency: zod.z.string().default("USD"),
|
|
1403
|
+
loading: zod.z.boolean().default(false),
|
|
1404
|
+
locale: zod.z.enum(["en", "fr"]).default("en")
|
|
1405
|
+
});
|
|
1406
|
+
function validateBalanceCardProps(raw) {
|
|
1407
|
+
return BalanceCardPropsSchema.parse(raw);
|
|
1408
|
+
}
|
|
1409
|
+
function BalanceCard(raw) {
|
|
1410
|
+
const parsed = BalanceCardPropsSchema.safeParse(raw);
|
|
1411
|
+
const locale = raw?.locale === "fr" ? "fr" : "en";
|
|
1412
|
+
const [copied, setCopied] = react.useState(false);
|
|
1413
|
+
if (!parsed.success) {
|
|
1414
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { role: "alert", children: t("BalanceCard.error.invalidProps", locale) });
|
|
1415
|
+
}
|
|
1416
|
+
const p = parsed.data;
|
|
1417
|
+
if (p.loading) {
|
|
1418
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { "aria-busy": "true", "aria-label": t("BalanceCard.loading", p.locale), children: /* @__PURE__ */ jsxRuntime.jsx(Skeleton, { variant: "rect", height: 120, locale: p.locale }) });
|
|
1419
|
+
}
|
|
1420
|
+
if (p.token === "" && p.balance === "") {
|
|
1421
|
+
return /* @__PURE__ */ jsxRuntime.jsx(EmptyState, { title: t("BalanceCard.empty.message", p.locale), locale: p.locale });
|
|
1422
|
+
}
|
|
1423
|
+
const canCopy = typeof navigator !== "undefined" && typeof navigator.clipboard?.writeText === "function";
|
|
1424
|
+
const copy = async () => {
|
|
1425
|
+
try {
|
|
1426
|
+
await navigator.clipboard.writeText(p.address);
|
|
1427
|
+
setCopied(true);
|
|
1428
|
+
} catch {
|
|
1429
|
+
}
|
|
1430
|
+
};
|
|
1431
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1432
|
+
"section",
|
|
1433
|
+
{
|
|
1434
|
+
"aria-label": `${t("BalanceCard.title", p.locale)}: ${p.token}`,
|
|
1435
|
+
lang: p.locale,
|
|
1436
|
+
className: "flex flex-col gap-2",
|
|
1437
|
+
children: [
|
|
1438
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1439
|
+
StatCard,
|
|
1440
|
+
{
|
|
1441
|
+
label: t("BalanceCard.title", p.locale),
|
|
1442
|
+
value: p.balance,
|
|
1443
|
+
unit: p.token,
|
|
1444
|
+
locale: p.locale
|
|
1445
|
+
}
|
|
1446
|
+
),
|
|
1447
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
1448
|
+
p.network ? /* @__PURE__ */ jsxRuntime.jsx(Badge, { label: p.network, variant: "info", locale: p.locale }) : null,
|
|
1449
|
+
p.testnet ? /* @__PURE__ */ jsxRuntime.jsx(Badge, { label: t("BalanceCard.testnet", p.locale), variant: "warning", locale: p.locale }) : null
|
|
1450
|
+
] }),
|
|
1451
|
+
p.usdValue ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm text-slate-600", children: [
|
|
1452
|
+
t("BalanceCard.value", p.locale),
|
|
1453
|
+
":",
|
|
1454
|
+
" ",
|
|
1455
|
+
formatDecimalString(p.usdValue, p.locale, p.currency)
|
|
1456
|
+
] }) : null,
|
|
1457
|
+
p.address ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-sm text-slate-600", children: [
|
|
1458
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-mono", title: p.address, children: truncateAddress(p.address) }),
|
|
1459
|
+
canCopy ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
1460
|
+
"button",
|
|
1461
|
+
{
|
|
1462
|
+
type: "button",
|
|
1463
|
+
onClick: copy,
|
|
1464
|
+
className: "rounded border border-slate-300 px-2 py-0.5 text-xs hover:bg-slate-50",
|
|
1465
|
+
children: t("BalanceCard.copy", p.locale)
|
|
1466
|
+
}
|
|
1467
|
+
) : null,
|
|
1468
|
+
copied ? /* @__PURE__ */ jsxRuntime.jsx("output", { children: t("BalanceCard.copied", p.locale) }) : null
|
|
1469
|
+
] }) : null
|
|
1470
|
+
]
|
|
1471
|
+
}
|
|
1472
|
+
);
|
|
1473
|
+
}
|
|
1474
|
+
|
|
1475
|
+
// src/components/shared/datetime.ts
|
|
1476
|
+
function formatTimestamp(iso, locale, timeZone) {
|
|
1477
|
+
const d = new Date(iso);
|
|
1478
|
+
if (Number.isNaN(d.getTime())) return iso;
|
|
1479
|
+
try {
|
|
1480
|
+
return new Intl.DateTimeFormat(locale, {
|
|
1481
|
+
dateStyle: "medium",
|
|
1482
|
+
timeStyle: "short",
|
|
1483
|
+
...timeZone ? { timeZone } : {}
|
|
1484
|
+
}).format(d);
|
|
1485
|
+
} catch {
|
|
1486
|
+
return iso;
|
|
1487
|
+
}
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1490
|
+
// src/components/shared/url.ts
|
|
1491
|
+
function normalizeHttpUrl(value) {
|
|
1492
|
+
if (typeof value !== "string") return void 0;
|
|
1493
|
+
try {
|
|
1494
|
+
const u = new URL(value);
|
|
1495
|
+
return u.protocol === "https:" || u.protocol === "http:" ? u.href : void 0;
|
|
1496
|
+
} catch {
|
|
1497
|
+
return void 0;
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1500
|
+
var FeedMessageSchema = zod.z.object({
|
|
1501
|
+
id: zod.z.string().optional(),
|
|
1502
|
+
sender: zod.z.string().min(1),
|
|
1503
|
+
channel: zod.z.string().optional(),
|
|
1504
|
+
/** Plain text. Rendered as text, never as HTML. */
|
|
1505
|
+
content: zod.z.string(),
|
|
1506
|
+
/** ISO 8601. */
|
|
1507
|
+
timestamp: zod.z.string(),
|
|
1508
|
+
attachmentUrl: zod.z.string().optional(),
|
|
1509
|
+
attachmentLabel: zod.z.string().optional(),
|
|
1510
|
+
unread: zod.z.boolean().optional()
|
|
1511
|
+
});
|
|
1512
|
+
var MessageFeedPropsSchema = zod.z.object({
|
|
1513
|
+
messages: zod.z.array(FeedMessageSchema).default([]),
|
|
1514
|
+
maxItems: zod.z.number().int().positive().optional(),
|
|
1515
|
+
/** IANA timezone for timestamps (e.g. `hostContext.timeZone`). */
|
|
1516
|
+
timeZone: zod.z.string().optional(),
|
|
1517
|
+
/** Above this many shown messages the feed is virtualised through VirtualList. */
|
|
1518
|
+
virtualizeThreshold: zod.z.number().int().positive().default(50),
|
|
1519
|
+
loading: zod.z.boolean().default(false),
|
|
1520
|
+
locale: zod.z.enum(["en", "fr"]).default("en")
|
|
1521
|
+
});
|
|
1522
|
+
function validateMessageFeedProps(raw) {
|
|
1523
|
+
return MessageFeedPropsSchema.parse(raw);
|
|
1524
|
+
}
|
|
1525
|
+
function latestMessages(messages, maxItems) {
|
|
1526
|
+
const shown = maxItems === void 0 ? [...messages] : messages.slice(-maxItems);
|
|
1527
|
+
return { shown, total: messages.length };
|
|
1528
|
+
}
|
|
1529
|
+
function Attachment({
|
|
1530
|
+
m,
|
|
1531
|
+
locale,
|
|
1532
|
+
onOpenLink
|
|
1533
|
+
}) {
|
|
1534
|
+
if (!m.attachmentUrl) return null;
|
|
1535
|
+
const label = m.attachmentLabel ?? t("MessageFeed.attachment", locale);
|
|
1536
|
+
const url = normalizeHttpUrl(m.attachmentUrl);
|
|
1537
|
+
if (!url) return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-slate-500", children: label });
|
|
1538
|
+
return onOpenLink ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
1539
|
+
"button",
|
|
1540
|
+
{
|
|
1541
|
+
type: "button",
|
|
1542
|
+
onClick: () => onOpenLink(url),
|
|
1543
|
+
className: "text-left text-sm text-blue-700 underline",
|
|
1544
|
+
children: label
|
|
1545
|
+
}
|
|
1546
|
+
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
1547
|
+
"a",
|
|
1548
|
+
{
|
|
1549
|
+
href: url,
|
|
1550
|
+
rel: "noopener noreferrer",
|
|
1551
|
+
target: "_blank",
|
|
1552
|
+
className: "text-sm text-blue-700 underline",
|
|
1553
|
+
children: label
|
|
1554
|
+
}
|
|
1555
|
+
);
|
|
1556
|
+
}
|
|
1557
|
+
function Message({
|
|
1558
|
+
m,
|
|
1559
|
+
locale,
|
|
1560
|
+
timeZone,
|
|
1561
|
+
onOpenLink
|
|
1562
|
+
}) {
|
|
1563
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("article", { className: "flex flex-col gap-1 border-b border-slate-100 py-2", children: [
|
|
1564
|
+
/* @__PURE__ */ jsxRuntime.jsxs("header", { className: "flex flex-wrap items-center gap-2 text-sm", children: [
|
|
1565
|
+
/* @__PURE__ */ jsxRuntime.jsx("strong", { className: "text-slate-900", children: m.sender }),
|
|
1566
|
+
m.channel ? /* @__PURE__ */ jsxRuntime.jsx(Badge, { label: m.channel, variant: "neutral", locale }) : null,
|
|
1567
|
+
/* @__PURE__ */ jsxRuntime.jsx("time", { dateTime: m.timestamp, className: "text-xs text-slate-500", children: formatTimestamp(m.timestamp, locale, timeZone) }),
|
|
1568
|
+
m.unread ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-semibold text-blue-700", children: t("MessageFeed.unread", locale) }) : null
|
|
1569
|
+
] }),
|
|
1570
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "m-0 whitespace-pre-wrap text-sm text-slate-800", children: m.content }),
|
|
1571
|
+
/* @__PURE__ */ jsxRuntime.jsx(Attachment, { m, locale, onOpenLink })
|
|
1572
|
+
] });
|
|
1573
|
+
}
|
|
1574
|
+
function MessageFeed(raw = {}) {
|
|
1575
|
+
const parsed = MessageFeedPropsSchema.safeParse(raw);
|
|
1576
|
+
const locale = raw?.locale === "fr" ? "fr" : "en";
|
|
1577
|
+
if (!parsed.success) {
|
|
1578
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { role: "alert", children: t("MessageFeed.error.invalidProps", locale) });
|
|
1579
|
+
}
|
|
1580
|
+
const p = parsed.data;
|
|
1581
|
+
if (p.loading) {
|
|
1582
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { "aria-busy": "true", "aria-label": t("MessageFeed.loading", p.locale), children: /* @__PURE__ */ jsxRuntime.jsx(Skeleton, { variant: "text", count: 5, locale: p.locale }) });
|
|
1583
|
+
}
|
|
1584
|
+
if (p.messages.length === 0) {
|
|
1585
|
+
return /* @__PURE__ */ jsxRuntime.jsx(EmptyState, { title: t("MessageFeed.empty.message", p.locale), locale: p.locale });
|
|
1586
|
+
}
|
|
1587
|
+
const { shown, total } = latestMessages(p.messages, p.maxItems);
|
|
1588
|
+
const virtual = shown.length > p.virtualizeThreshold;
|
|
1589
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("section", { "aria-label": t("MessageFeed.title", p.locale), lang: p.locale, children: [
|
|
1590
|
+
shown.length < total ? /* @__PURE__ */ jsxRuntime.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,
|
|
1591
|
+
virtual ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
1592
|
+
VirtualList,
|
|
1593
|
+
{
|
|
1594
|
+
items: shown,
|
|
1595
|
+
itemHeight: 96,
|
|
1596
|
+
locale: p.locale,
|
|
1597
|
+
renderItem: (m) => /* @__PURE__ */ jsxRuntime.jsx(Message, { m, locale: p.locale, timeZone: p.timeZone, onOpenLink: raw.onOpenLink })
|
|
1598
|
+
}
|
|
1599
|
+
) : /* @__PURE__ */ jsxRuntime.jsx("ol", { className: "m-0 list-none p-0", children: shown.map((m, i) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsx(Message, { m, locale: p.locale, timeZone: p.timeZone, onOpenLink: raw.onOpenLink }) }, m.id ?? `msg-${i}`)) })
|
|
1600
|
+
] });
|
|
1601
|
+
}
|
|
1602
|
+
|
|
1603
|
+
// src/components/forms/RadioGroup.logic.ts
|
|
1604
|
+
function getOptionLabelClasses(appearance, isSelected) {
|
|
1605
|
+
const base = "mosaic-forms-radiogroup-option-label inline-flex items-center px-3 py-1 text-sm";
|
|
1606
|
+
const shape = "rounded-full border" ;
|
|
1607
|
+
const tone = isSelected ? "bg-slate-900 text-white border-slate-900" : "bg-white text-slate-700 border-slate-300 hover:bg-slate-50";
|
|
1608
|
+
return [base, shape, tone].filter(Boolean).join(" ");
|
|
1609
|
+
}
|
|
1610
|
+
|
|
1611
|
+
// src/components/display/PipelineBoard.logic.ts
|
|
1612
|
+
function stageSummary(deals) {
|
|
1613
|
+
const amounts = deals.flatMap((d) => d.amount === void 0 ? [] : [d.amount]);
|
|
1614
|
+
return { count: deals.length, total: sumDecimalStrings(amounts) };
|
|
1615
|
+
}
|
|
1616
|
+
var SUMMARY_STAGE_LIMIT = 5;
|
|
1617
|
+
var PipelineDealSchema = zod.z.object({
|
|
1618
|
+
id: zod.z.string().min(1),
|
|
1619
|
+
title: zod.z.string().min(1),
|
|
1620
|
+
/** Decimal STRING (never a float), in the board currency. */
|
|
1621
|
+
amount: zod.z.string().optional(),
|
|
1622
|
+
contact: zod.z.string().optional(),
|
|
1623
|
+
url: zod.z.string().optional()
|
|
1624
|
+
});
|
|
1625
|
+
var PipelineStageSchema = zod.z.object({
|
|
1626
|
+
id: zod.z.string().min(1),
|
|
1627
|
+
name: zod.z.string().min(1),
|
|
1628
|
+
deals: zod.z.array(PipelineDealSchema).default([])
|
|
1629
|
+
});
|
|
1630
|
+
var PipelineBoardPropsSchema = zod.z.object({
|
|
1631
|
+
stages: zod.z.array(PipelineStageSchema).default([]),
|
|
1632
|
+
/** ISO 4217 code used to format amounts. */
|
|
1633
|
+
currency: zod.z.string().default("EUR"),
|
|
1634
|
+
layout: zod.z.enum(["board", "summary"]).default("board"),
|
|
1635
|
+
loading: zod.z.boolean().default(false),
|
|
1636
|
+
locale: zod.z.enum(["en", "fr"]).default("en")
|
|
1637
|
+
});
|
|
1638
|
+
function validatePipelineBoardProps(raw) {
|
|
1639
|
+
return PipelineBoardPropsSchema.parse(raw);
|
|
1640
|
+
}
|
|
1641
|
+
function dealsLabel(n, locale) {
|
|
1642
|
+
return n === 1 ? t("PipelineBoard.deals.one", locale) : t("PipelineBoard.deals.many", locale).replace("{n}", String(n));
|
|
1643
|
+
}
|
|
1644
|
+
function Totals({
|
|
1645
|
+
stage,
|
|
1646
|
+
currency,
|
|
1647
|
+
locale
|
|
1648
|
+
}) {
|
|
1649
|
+
const { count, total } = stageSummary(stage.deals);
|
|
1650
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex flex-col text-sm text-slate-600", children: [
|
|
1651
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: dealsLabel(count, locale) }),
|
|
1652
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "tabular-nums", children: total === void 0 ? "\u2014" : formatDecimalString(total, locale, currency) })
|
|
1653
|
+
] });
|
|
1654
|
+
}
|
|
1655
|
+
function PipelineBoard(raw = {}) {
|
|
1656
|
+
const parsed = PipelineBoardPropsSchema.safeParse(raw);
|
|
1657
|
+
const locale = raw?.locale === "fr" ? "fr" : "en";
|
|
1658
|
+
const [hidden, setHidden] = react.useState(/* @__PURE__ */ new Set());
|
|
1659
|
+
if (!parsed.success) {
|
|
1660
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { role: "alert", children: t("PipelineBoard.error.invalidProps", locale) });
|
|
1661
|
+
}
|
|
1662
|
+
const p = parsed.data;
|
|
1663
|
+
if (p.loading) {
|
|
1664
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { "aria-busy": "true", "aria-label": t("PipelineBoard.loading", p.locale), children: /* @__PURE__ */ jsxRuntime.jsx(Skeleton, { variant: "rect", height: 160, locale: p.locale }) });
|
|
1665
|
+
}
|
|
1666
|
+
if (p.stages.length === 0) {
|
|
1667
|
+
return /* @__PURE__ */ jsxRuntime.jsx(EmptyState, { title: t("PipelineBoard.empty.message", p.locale), locale: p.locale });
|
|
1668
|
+
}
|
|
1669
|
+
if (p.layout === "summary") {
|
|
1670
|
+
const shown = p.stages.slice(0, SUMMARY_STAGE_LIMIT);
|
|
1671
|
+
const rest = p.stages.length - shown.length;
|
|
1672
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1673
|
+
"section",
|
|
1674
|
+
{
|
|
1675
|
+
"aria-label": t("PipelineBoard.title", p.locale),
|
|
1676
|
+
lang: p.locale,
|
|
1677
|
+
className: "flex flex-col gap-2",
|
|
1678
|
+
children: [
|
|
1679
|
+
/* @__PURE__ */ jsxRuntime.jsx("ul", { className: "m-0 flex list-none flex-col gap-2 p-0", children: shown.map((s) => /* @__PURE__ */ jsxRuntime.jsxs("li", { className: "flex items-center justify-between gap-2", children: [
|
|
1680
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-slate-900", children: s.name }),
|
|
1681
|
+
/* @__PURE__ */ jsxRuntime.jsx(Totals, { stage: s, currency: p.currency, locale: p.locale })
|
|
1682
|
+
] }, s.id)) }),
|
|
1683
|
+
rest > 0 ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-slate-500", children: t("PipelineBoard.more", p.locale).replace("{n}", String(rest)) }) : null,
|
|
1684
|
+
raw.onRequestFullscreen ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
1685
|
+
"button",
|
|
1686
|
+
{
|
|
1687
|
+
type: "button",
|
|
1688
|
+
onClick: raw.onRequestFullscreen,
|
|
1689
|
+
className: "self-start rounded border border-slate-300 px-3 py-1 text-sm hover:bg-slate-50",
|
|
1690
|
+
children: t("PipelineBoard.summary.open", p.locale)
|
|
1691
|
+
}
|
|
1692
|
+
) : null
|
|
1693
|
+
]
|
|
1694
|
+
}
|
|
1695
|
+
);
|
|
1696
|
+
}
|
|
1697
|
+
const visible = p.stages.filter((s) => !hidden.has(s.id));
|
|
1698
|
+
const toggle = (id) => {
|
|
1699
|
+
const next = new Set(hidden);
|
|
1700
|
+
if (next.has(id)) next.delete(id);
|
|
1701
|
+
else if (visible.length > 1) next.add(id);
|
|
1702
|
+
setHidden(next);
|
|
1703
|
+
};
|
|
1704
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1705
|
+
"section",
|
|
1706
|
+
{
|
|
1707
|
+
"aria-label": t("PipelineBoard.title", p.locale),
|
|
1708
|
+
lang: p.locale,
|
|
1709
|
+
className: "flex flex-col gap-3",
|
|
1710
|
+
children: [
|
|
1711
|
+
/* @__PURE__ */ jsxRuntime.jsxs("fieldset", { className: "m-0 flex min-w-0 flex-wrap gap-2 border-0 p-0", children: [
|
|
1712
|
+
/* @__PURE__ */ jsxRuntime.jsx("legend", { className: "sr-only", children: t("PipelineBoard.filter.label", p.locale) }),
|
|
1713
|
+
p.stages.map((s) => {
|
|
1714
|
+
const on = !hidden.has(s.id);
|
|
1715
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1716
|
+
"button",
|
|
1717
|
+
{
|
|
1718
|
+
type: "button",
|
|
1719
|
+
"aria-pressed": on,
|
|
1720
|
+
onClick: () => toggle(s.id),
|
|
1721
|
+
className: getOptionLabelClasses("chips", on),
|
|
1722
|
+
children: s.name
|
|
1723
|
+
},
|
|
1724
|
+
s.id
|
|
1725
|
+
);
|
|
1726
|
+
})
|
|
1727
|
+
] }),
|
|
1728
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1729
|
+
"div",
|
|
1730
|
+
{
|
|
1731
|
+
className: "flex flex-row gap-3 overflow-x-auto",
|
|
1732
|
+
style: {
|
|
1733
|
+
scrollPaddingLeft: "var(--mosaic-safe-area-left, 0px)",
|
|
1734
|
+
scrollPaddingRight: "var(--mosaic-safe-area-right, 0px)"
|
|
1735
|
+
},
|
|
1736
|
+
children: visible.map((s) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1737
|
+
"section",
|
|
1738
|
+
{
|
|
1739
|
+
"aria-label": s.name,
|
|
1740
|
+
className: "flex w-64 shrink-0 flex-col gap-2 rounded-lg border border-slate-200 bg-slate-50 p-3",
|
|
1741
|
+
children: [
|
|
1742
|
+
/* @__PURE__ */ jsxRuntime.jsxs("header", { className: "flex items-start justify-between gap-2", children: [
|
|
1743
|
+
/* @__PURE__ */ jsxRuntime.jsx("strong", { className: "text-slate-900", children: s.name }),
|
|
1744
|
+
/* @__PURE__ */ jsxRuntime.jsx(Totals, { stage: s, currency: p.currency, locale: p.locale })
|
|
1745
|
+
] }),
|
|
1746
|
+
s.deals.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-slate-500", children: t("PipelineBoard.stage.empty", p.locale) }) : /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "m-0 flex list-none flex-col gap-2 p-0", children: s.deals.map((d) => /* @__PURE__ */ jsxRuntime.jsxs("li", { className: "rounded border border-slate-200 bg-white p-2", children: [
|
|
1747
|
+
raw.onOpenLink && normalizeHttpUrl(d.url) ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
1748
|
+
"button",
|
|
1749
|
+
{
|
|
1750
|
+
type: "button",
|
|
1751
|
+
onClick: () => raw.onOpenLink?.(normalizeHttpUrl(d.url)),
|
|
1752
|
+
className: "text-left font-medium text-blue-700 underline",
|
|
1753
|
+
children: d.title
|
|
1754
|
+
}
|
|
1755
|
+
) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-slate-900", children: d.title }),
|
|
1756
|
+
d.amount !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "block text-sm tabular-nums text-slate-700", children: formatDecimalString(d.amount, p.locale, p.currency) }) : null,
|
|
1757
|
+
d.contact ? /* @__PURE__ */ jsxRuntime.jsx(Badge, { label: d.contact, variant: "neutral", locale: p.locale }) : null
|
|
1758
|
+
] }, d.id)) })
|
|
1759
|
+
]
|
|
1760
|
+
},
|
|
1761
|
+
s.id
|
|
1762
|
+
))
|
|
1763
|
+
}
|
|
1764
|
+
)
|
|
1765
|
+
]
|
|
1766
|
+
}
|
|
1767
|
+
);
|
|
1768
|
+
}
|
|
831
1769
|
|
|
832
1770
|
exports.Badge = Badge;
|
|
833
1771
|
exports.BadgePropsSchema = BadgePropsSchema;
|
|
1772
|
+
exports.BalanceCard = BalanceCard;
|
|
1773
|
+
exports.BalanceCardPropsSchema = BalanceCardPropsSchema;
|
|
834
1774
|
exports.EmptyState = EmptyState;
|
|
835
1775
|
exports.EmptyStatePropsSchema = EmptyStatePropsSchema;
|
|
1776
|
+
exports.MessageFeed = MessageFeed;
|
|
1777
|
+
exports.MessageFeedPropsSchema = MessageFeedPropsSchema;
|
|
1778
|
+
exports.PipelineBoard = PipelineBoard;
|
|
1779
|
+
exports.PipelineBoardPropsSchema = PipelineBoardPropsSchema;
|
|
836
1780
|
exports.Skeleton = Skeleton;
|
|
837
1781
|
exports.SkeletonPropsSchema = SkeletonPropsSchema;
|
|
1782
|
+
exports.StatCard = StatCard;
|
|
1783
|
+
exports.StatCardPropsSchema = StatCardPropsSchema;
|
|
838
1784
|
exports.StatusBadge = StatusBadge;
|
|
839
1785
|
exports.StatusBadgePropsSchema = StatusBadgePropsSchema;
|
|
840
1786
|
exports.StreamingTableView = StreamingTableView;
|
|
@@ -842,10 +1788,17 @@ exports.TableView = TableView;
|
|
|
842
1788
|
exports.TableViewPropsSchema = TableViewPropsSchema;
|
|
843
1789
|
exports.VirtualList = VirtualList;
|
|
844
1790
|
exports.VirtualListPropsSchema = VirtualListPropsSchema;
|
|
1791
|
+
exports.compareDecimalStrings = compareDecimalStrings;
|
|
1792
|
+
exports.nextSort = nextSort;
|
|
845
1793
|
exports.resolveSkeletonDimensions = resolveSkeletonDimensions;
|
|
1794
|
+
exports.sortItems = sortItems;
|
|
846
1795
|
exports.validateBadgeProps = validateBadgeProps;
|
|
1796
|
+
exports.validateBalanceCardProps = validateBalanceCardProps;
|
|
847
1797
|
exports.validateEmptyStateProps = validateEmptyStateProps;
|
|
1798
|
+
exports.validateMessageFeedProps = validateMessageFeedProps;
|
|
1799
|
+
exports.validatePipelineBoardProps = validatePipelineBoardProps;
|
|
848
1800
|
exports.validateSkeletonProps = validateSkeletonProps;
|
|
1801
|
+
exports.validateStatCardProps = validateStatCardProps;
|
|
849
1802
|
exports.validateStatusBadgeProps = validateProps;
|
|
850
1803
|
exports.validateTableViewProps = validateTableViewProps;
|
|
851
1804
|
exports.validateVirtualListProps = validateVirtualListProps;
|