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