@neptune.fintech/web-ui 2.0.0 → 2.2.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.
Files changed (73) hide show
  1. package/README.md +24 -2
  2. package/dist/components/actions.d.ts +43 -0
  3. package/dist/components/actions.d.ts.map +1 -0
  4. package/dist/components/actions.js +279 -0
  5. package/dist/components/actions.js.map +1 -0
  6. package/dist/components/button.d.ts +1 -1
  7. package/dist/components/button.d.ts.map +1 -1
  8. package/dist/components/button.js +9 -1
  9. package/dist/components/button.js.map +1 -1
  10. package/dist/components/cards.d.ts +64 -0
  11. package/dist/components/cards.d.ts.map +1 -0
  12. package/dist/components/cards.js +498 -0
  13. package/dist/components/cards.js.map +1 -0
  14. package/dist/components/containers.d.ts +54 -0
  15. package/dist/components/containers.d.ts.map +1 -0
  16. package/dist/components/containers.js +298 -0
  17. package/dist/components/containers.js.map +1 -0
  18. package/dist/components/corporate.d.ts +84 -0
  19. package/dist/components/corporate.d.ts.map +1 -0
  20. package/dist/components/corporate.js +782 -0
  21. package/dist/components/corporate.js.map +1 -0
  22. package/dist/components/data-viz.d.ts +69 -0
  23. package/dist/components/data-viz.d.ts.map +1 -0
  24. package/dist/components/data-viz.js +526 -0
  25. package/dist/components/data-viz.js.map +1 -0
  26. package/dist/components/feedback-status.d.ts +80 -0
  27. package/dist/components/feedback-status.d.ts.map +1 -0
  28. package/dist/components/feedback-status.js +537 -0
  29. package/dist/components/feedback-status.js.map +1 -0
  30. package/dist/components/feedback.d.ts +42 -0
  31. package/dist/components/feedback.d.ts.map +1 -0
  32. package/dist/components/feedback.js +241 -0
  33. package/dist/components/feedback.js.map +1 -0
  34. package/dist/components/inputs.d.ts +8 -1
  35. package/dist/components/inputs.d.ts.map +1 -1
  36. package/dist/components/inputs.js +57 -3
  37. package/dist/components/inputs.js.map +1 -1
  38. package/dist/components/layout.d.ts +68 -0
  39. package/dist/components/layout.d.ts.map +1 -0
  40. package/dist/components/layout.js +359 -0
  41. package/dist/components/layout.js.map +1 -0
  42. package/dist/components/money-inputs.d.ts +105 -0
  43. package/dist/components/money-inputs.d.ts.map +1 -0
  44. package/dist/components/money-inputs.js +766 -0
  45. package/dist/components/money-inputs.js.map +1 -0
  46. package/dist/components/money-movement.d.ts +79 -0
  47. package/dist/components/money-movement.d.ts.map +1 -0
  48. package/dist/components/money-movement.js +740 -0
  49. package/dist/components/money-movement.js.map +1 -0
  50. package/dist/components/nav-rail.d.ts +22 -0
  51. package/dist/components/nav-rail.d.ts.map +1 -0
  52. package/dist/components/nav-rail.js +120 -0
  53. package/dist/components/nav-rail.js.map +1 -0
  54. package/dist/components/selection.d.ts +51 -0
  55. package/dist/components/selection.d.ts.map +1 -0
  56. package/dist/components/selection.js +377 -0
  57. package/dist/components/selection.js.map +1 -0
  58. package/dist/components/shell-layout.d.ts +103 -0
  59. package/dist/components/shell-layout.d.ts.map +1 -0
  60. package/dist/components/shell-layout.js +582 -0
  61. package/dist/components/shell-layout.js.map +1 -0
  62. package/dist/components/wallet-pay.d.ts +85 -0
  63. package/dist/components/wallet-pay.d.ts.map +1 -0
  64. package/dist/components/wallet-pay.js +633 -0
  65. package/dist/components/wallet-pay.js.map +1 -0
  66. package/dist/index.d.ts +15 -1
  67. package/dist/index.d.ts.map +1 -1
  68. package/dist/index.js +15 -1
  69. package/dist/index.js.map +1 -1
  70. package/dist/register.d.ts.map +1 -1
  71. package/dist/register.js +103 -0
  72. package/dist/register.js.map +1 -1
  73. package/package.json +1 -1
@@ -0,0 +1,740 @@
1
+ // © 2026 Neptune.Fintech (neptune.ly) · Neptune Odyssey Community License v1.0
2
+ // Neptune Odyssey — money-movement flow
3
+ // <npt-stepper>, <npt-step>, <npt-transfer-review>, <npt-success>,
4
+ // <npt-receipt>, <npt-beneficiary-tile>, <npt-method-row>.
5
+ // Custom-property driven only; logical layout → mirrors in RTL.
6
+ import { NptElement, css, html, A11Y, define } from "./base.js";
7
+ const esc = (v) => (v ?? "").replace(/[&<>"]/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;" })[c]);
8
+ /** A single declarative step label, consumed by <npt-stepper>. Renders nothing on its own. */
9
+ export class NptStep extends NptElement {
10
+ styles() {
11
+ return css `
12
+ :host {
13
+ display: none;
14
+ }
15
+ `;
16
+ }
17
+ render() {
18
+ return html ``;
19
+ }
20
+ }
21
+ /**
22
+ * <npt-stepper active="1" steps="Amount,Review,Done"></npt-stepper>
23
+ * or with light-DOM <npt-step>Amount</npt-step> children.
24
+ * Horizontal progress indicator with numbered nodes + connectors.
25
+ */
26
+ export class NptStepper extends NptElement {
27
+ attributeChangedCallback() {
28
+ if (this.isConnected)
29
+ this.update();
30
+ }
31
+ labels() {
32
+ const attr = this.getAttribute("steps");
33
+ if (attr && attr.trim()) {
34
+ return attr
35
+ .split(",")
36
+ .map((s) => s.trim())
37
+ .filter((s) => s.length > 0);
38
+ }
39
+ return Array.from(this.querySelectorAll("npt-step")).map((s) => (s.textContent ?? "").trim());
40
+ }
41
+ styles() {
42
+ return css `
43
+ ${A11Y}
44
+ :host {
45
+ display: block;
46
+ }
47
+ .stepper {
48
+ display: flex;
49
+ align-items: flex-start;
50
+ gap: var(--npt-space-1, 4px);
51
+ font-family: var(--npt-font-text);
52
+ }
53
+ .step {
54
+ display: flex;
55
+ flex-direction: column;
56
+ align-items: center;
57
+ gap: var(--npt-space-2, 8px);
58
+ flex: 0 0 auto;
59
+ min-inline-size: 56px;
60
+ }
61
+ .node {
62
+ inline-size: 32px;
63
+ block-size: 32px;
64
+ border-radius: var(--npt-corner-full, 999px);
65
+ display: grid;
66
+ place-items: center;
67
+ font-family: var(--npt-font-num);
68
+ font-variant-numeric: tabular-nums;
69
+ font-size: var(--npt-text-label, 14px);
70
+ font-weight: 600;
71
+ background: var(--md-sys-color-surface-container-highest);
72
+ color: var(--md-sys-color-on-surface-variant);
73
+ border: 2px solid var(--md-sys-color-outline-variant);
74
+ box-sizing: border-box;
75
+ transition: background-color var(--npt-dur-fast, 200ms) var(--npt-ease-standard, ease),
76
+ border-color var(--npt-dur-fast, 200ms) var(--npt-ease-standard, ease),
77
+ color var(--npt-dur-fast, 200ms) var(--npt-ease-standard, ease);
78
+ }
79
+ .step[data-state="active"] .node {
80
+ background: var(--md-sys-color-primary);
81
+ color: var(--md-sys-color-on-primary);
82
+ border-color: var(--md-sys-color-primary);
83
+ }
84
+ .step[data-state="done"] .node {
85
+ background: var(--md-sys-color-success);
86
+ color: var(--md-sys-color-on-success);
87
+ border-color: var(--md-sys-color-success);
88
+ }
89
+ .label {
90
+ font-size: var(--npt-text-caption, 12px);
91
+ color: var(--md-sys-color-on-surface-variant);
92
+ text-align: center;
93
+ max-inline-size: 80px;
94
+ }
95
+ .step[data-state="active"] .label {
96
+ color: var(--md-sys-color-on-surface);
97
+ font-weight: 600;
98
+ }
99
+ .connector {
100
+ flex: 1 1 auto;
101
+ block-size: 2px;
102
+ margin-block-start: 15px;
103
+ margin-inline: var(--npt-space-1, 4px);
104
+ min-inline-size: var(--npt-space-4, 16px);
105
+ background: var(--md-sys-color-outline-variant);
106
+ border-radius: var(--npt-corner-full, 999px);
107
+ transition: background-color var(--npt-dur-fast, 200ms) var(--npt-ease-standard, ease);
108
+ }
109
+ .connector[data-done="true"] {
110
+ background: var(--md-sys-color-success);
111
+ }
112
+ `;
113
+ }
114
+ render() {
115
+ const labels = this.labels();
116
+ const active = Math.max(0, Number(this.getAttribute("active") ?? 0));
117
+ const total = labels.length;
118
+ const nodes = labels
119
+ .map((label, i) => {
120
+ const state = i < active ? "done" : i === active ? "active" : "upcoming";
121
+ const current = state === "active" ? html `aria-current="step"` : "";
122
+ const mark = state === "done" ? "✓" : String(i + 1);
123
+ const step = html `<li
124
+ class="step"
125
+ part="step"
126
+ data-state="${state}"
127
+ role="listitem"
128
+ ${current}
129
+ >
130
+ <span class="node" aria-hidden="true">${mark}</span>
131
+ <span class="label">${esc(label)}</span>
132
+ </li>`;
133
+ if (i < total - 1) {
134
+ const done = i < active ? "true" : "false";
135
+ return html `${step}<span class="connector" part="connector" data-done="${done}" aria-hidden="true"></span>`;
136
+ }
137
+ return step;
138
+ })
139
+ .join("");
140
+ const label = `Step ${Math.min(active + 1, Math.max(total, 1))} of ${total}`;
141
+ return html `<ol class="stepper" part="stepper" role="list" aria-label="${esc(label)}">${nodes}</ol>`;
142
+ }
143
+ }
144
+ NptStepper.observedAttributes = ["steps", "active"];
145
+ /**
146
+ * <npt-transfer-review rows='[{"label":"To","value":"Mona K"}]'
147
+ * total="1,250.00" currency="LYD"></npt-transfer-review>
148
+ * Light-DOM rows are also supported: place elements with [slot="rows"].
149
+ * Key/value summary with a highlighted total footer.
150
+ */
151
+ export class NptTransferReview extends NptElement {
152
+ attributeChangedCallback() {
153
+ if (this.isConnected)
154
+ this.update();
155
+ }
156
+ rows() {
157
+ const raw = this.getAttribute("rows");
158
+ if (!raw)
159
+ return [];
160
+ try {
161
+ const parsed = JSON.parse(raw);
162
+ if (!Array.isArray(parsed))
163
+ return [];
164
+ return parsed
165
+ .filter((r) => typeof r === "object" && r !== null)
166
+ .map((r) => ({ label: String(r["label"] ?? ""), value: String(r["value"] ?? "") }));
167
+ }
168
+ catch {
169
+ return [];
170
+ }
171
+ }
172
+ styles() {
173
+ return css `
174
+ ${A11Y}
175
+ :host {
176
+ display: block;
177
+ }
178
+ .review {
179
+ background: var(--md-sys-color-surface-container-low);
180
+ color: var(--md-sys-color-on-surface);
181
+ border-radius: var(--npt-corner-lg, 24px);
182
+ padding: var(--npt-space-5, 20px);
183
+ box-sizing: border-box;
184
+ }
185
+ dl {
186
+ margin: 0;
187
+ display: grid;
188
+ gap: var(--npt-space-3, 12px);
189
+ }
190
+ .row {
191
+ display: flex;
192
+ align-items: baseline;
193
+ justify-content: space-between;
194
+ gap: var(--npt-space-4, 16px);
195
+ }
196
+ dt {
197
+ font-family: var(--npt-font-text);
198
+ font-size: var(--npt-text-body, 14px);
199
+ color: var(--md-sys-color-on-surface-variant);
200
+ margin: 0;
201
+ }
202
+ dd {
203
+ font-family: var(--npt-font-text);
204
+ font-size: var(--npt-text-body-lg, 16px);
205
+ color: var(--md-sys-color-on-surface);
206
+ margin: 0;
207
+ text-align: end;
208
+ min-inline-size: 0;
209
+ overflow-wrap: anywhere;
210
+ }
211
+ .total {
212
+ margin-block-start: var(--npt-space-4, 16px);
213
+ padding-block-start: var(--npt-space-4, 16px);
214
+ border-block-start: 1px solid var(--md-sys-color-outline-variant);
215
+ display: flex;
216
+ align-items: baseline;
217
+ justify-content: space-between;
218
+ gap: var(--npt-space-4, 16px);
219
+ }
220
+ .total-label {
221
+ font-family: var(--npt-font-text);
222
+ font-size: var(--npt-text-label, 14px);
223
+ font-weight: 600;
224
+ color: var(--md-sys-color-on-surface);
225
+ }
226
+ .total-value {
227
+ font-family: var(--npt-font-num);
228
+ font-feature-settings: "tnum" 1;
229
+ font-variant-numeric: tabular-nums;
230
+ font-size: var(--npt-text-title-lg, 22px);
231
+ font-weight: 700;
232
+ color: var(--md-sys-color-primary);
233
+ white-space: nowrap;
234
+ }
235
+ .total-currency {
236
+ font-size: var(--npt-text-body, 14px);
237
+ opacity: 0.85;
238
+ margin-inline-start: var(--npt-space-1, 4px);
239
+ }
240
+ ::slotted([slot="rows"]) {
241
+ display: block;
242
+ }
243
+ `;
244
+ }
245
+ render() {
246
+ const total = esc(this.getAttribute("total"));
247
+ const currency = esc(this.getAttribute("currency"));
248
+ const totalLabel = esc(this.getAttribute("total-label")) || "Total";
249
+ const rows = this.rows()
250
+ .map((r) => html `<div class="row" part="row">
251
+ <dt>${esc(r.label)}</dt>
252
+ <dd>${esc(r.value)}</dd>
253
+ </div>`)
254
+ .join("");
255
+ const totalBlock = total
256
+ ? html `<div class="total" part="total">
257
+ <span class="total-label">${totalLabel}</span>
258
+ <span class="total-value"
259
+ >${total}${currency ? html `<span class="total-currency">${currency}</span>` : ""}</span
260
+ >
261
+ </div>`
262
+ : "";
263
+ return html `<section
264
+ class="review"
265
+ part="review"
266
+ role="group"
267
+ aria-label="${totalLabel} ${total} ${currency}"
268
+ >
269
+ <dl>${rows}</dl>
270
+ <slot name="rows"></slot>
271
+ ${totalBlock}
272
+ </section>`;
273
+ }
274
+ }
275
+ NptTransferReview.observedAttributes = ["rows", "total", "currency", "total-label"];
276
+ /**
277
+ * <npt-success title="Transfer sent" message="Your money is on the way.">
278
+ * <npt-button slot="actions">Done</npt-button>
279
+ * </npt-success>
280
+ * Success hero with a spring-in check; honours reduced motion.
281
+ */
282
+ export class NptSuccess extends NptElement {
283
+ attributeChangedCallback() {
284
+ if (this.isConnected)
285
+ this.update();
286
+ }
287
+ styles() {
288
+ return css `
289
+ ${A11Y}
290
+ :host {
291
+ display: block;
292
+ }
293
+ .hero {
294
+ display: flex;
295
+ flex-direction: column;
296
+ align-items: center;
297
+ text-align: center;
298
+ gap: var(--npt-space-4, 16px);
299
+ padding: var(--npt-space-6, 24px);
300
+ box-sizing: border-box;
301
+ }
302
+ .ring {
303
+ inline-size: 96px;
304
+ block-size: 96px;
305
+ border-radius: var(--npt-corner-full, 999px);
306
+ display: grid;
307
+ place-items: center;
308
+ background: var(--md-sys-color-success-container, var(--md-sys-color-secondary-container));
309
+ color: var(--md-sys-color-success);
310
+ animation: spring-in var(--npt-dur-slow, 400ms) var(--npt-ease-spring, ease) both;
311
+ }
312
+ .check {
313
+ font-size: var(--npt-text-display-md, 45px);
314
+ line-height: 1;
315
+ animation: check-pop var(--npt-dur-standard, 300ms) var(--npt-ease-spring, ease) both;
316
+ animation-delay: var(--npt-dur-fast, 200ms);
317
+ }
318
+ .title {
319
+ font-family: var(--npt-font-display);
320
+ font-size: var(--npt-text-headline, 24px);
321
+ font-weight: 700;
322
+ color: var(--md-sys-color-on-surface);
323
+ margin: 0;
324
+ }
325
+ .message {
326
+ font-family: var(--npt-font-text);
327
+ font-size: var(--npt-text-body-lg, 16px);
328
+ color: var(--md-sys-color-on-surface-variant);
329
+ margin: 0;
330
+ max-inline-size: 40ch;
331
+ }
332
+ .actions {
333
+ display: flex;
334
+ flex-wrap: wrap;
335
+ justify-content: center;
336
+ gap: var(--npt-space-3, 12px);
337
+ margin-block-start: var(--npt-space-2, 8px);
338
+ }
339
+ @keyframes spring-in {
340
+ 0% {
341
+ transform: scale(0.6);
342
+ opacity: 0;
343
+ }
344
+ 60% {
345
+ transform: scale(1.08);
346
+ opacity: 1;
347
+ }
348
+ 100% {
349
+ transform: scale(1);
350
+ }
351
+ }
352
+ @keyframes check-pop {
353
+ 0% {
354
+ transform: scale(0);
355
+ }
356
+ 100% {
357
+ transform: scale(1);
358
+ }
359
+ }
360
+ `;
361
+ }
362
+ render() {
363
+ const title = esc(this.getAttribute("title"));
364
+ const message = esc(this.getAttribute("message"));
365
+ return html `<div class="hero" part="hero" role="status" aria-live="polite">
366
+ <span class="ring" part="ring" aria-hidden="true"><span class="check">✓</span></span>
367
+ ${title ? html `<h2 class="title" part="title">${title}</h2>` : ""}
368
+ ${message ? html `<p class="message" part="message">${message}</p>` : ""}
369
+ <div class="actions" part="actions"><slot name="actions"></slot></div>
370
+ </div>`;
371
+ }
372
+ }
373
+ NptSuccess.observedAttributes = ["title", "message"];
374
+ /**
375
+ * <npt-receipt merchant="Acme" amount="42.00" currency="LYD"
376
+ * date="27 Jun 2026" status="Completed" reference="TX-9931"></npt-receipt>
377
+ * Receipt card with a dashed tear divider; extra rows via the default slot.
378
+ */
379
+ export class NptReceipt extends NptElement {
380
+ attributeChangedCallback() {
381
+ if (this.isConnected)
382
+ this.update();
383
+ }
384
+ styles() {
385
+ return css `
386
+ ${A11Y}
387
+ :host {
388
+ display: block;
389
+ }
390
+ .receipt {
391
+ background: var(--md-sys-color-surface-container-lowest);
392
+ color: var(--md-sys-color-on-surface);
393
+ border: 1px solid var(--md-sys-color-outline-variant);
394
+ border-radius: var(--npt-corner-lg, 24px);
395
+ padding: var(--npt-space-5, 20px);
396
+ box-sizing: border-box;
397
+ }
398
+ .merchant {
399
+ font-family: var(--npt-font-display);
400
+ font-size: var(--npt-text-title, 18px);
401
+ font-weight: 700;
402
+ margin: 0;
403
+ }
404
+ .amount {
405
+ font-family: var(--npt-font-num);
406
+ font-feature-settings: "tnum" 1;
407
+ font-variant-numeric: tabular-nums;
408
+ font-size: var(--npt-text-display-md, 45px);
409
+ line-height: var(--npt-leading-display-md, 52px);
410
+ font-weight: 700;
411
+ letter-spacing: -0.02em;
412
+ margin: var(--npt-space-2, 8px) 0 0;
413
+ display: flex;
414
+ align-items: baseline;
415
+ gap: var(--npt-space-2, 8px);
416
+ }
417
+ .amount .currency {
418
+ font-size: var(--npt-text-title, 18px);
419
+ opacity: 0.85;
420
+ }
421
+ .divider {
422
+ block-size: 0;
423
+ border: none;
424
+ border-block-start: 2px dashed var(--md-sys-color-outline-variant);
425
+ margin-block: var(--npt-space-4, 16px);
426
+ }
427
+ dl {
428
+ margin: 0;
429
+ display: grid;
430
+ gap: var(--npt-space-3, 12px);
431
+ }
432
+ .row {
433
+ display: flex;
434
+ align-items: baseline;
435
+ justify-content: space-between;
436
+ gap: var(--npt-space-4, 16px);
437
+ }
438
+ dt {
439
+ font-family: var(--npt-font-text);
440
+ font-size: var(--npt-text-body, 14px);
441
+ color: var(--md-sys-color-on-surface-variant);
442
+ margin: 0;
443
+ }
444
+ dd {
445
+ font-family: var(--npt-font-text);
446
+ font-size: var(--npt-text-body-lg, 16px);
447
+ color: var(--md-sys-color-on-surface);
448
+ margin: 0;
449
+ text-align: end;
450
+ overflow-wrap: anywhere;
451
+ }
452
+ dd.num {
453
+ font-family: var(--npt-font-num);
454
+ font-variant-numeric: tabular-nums;
455
+ }
456
+ .status {
457
+ display: inline-flex;
458
+ align-items: center;
459
+ gap: var(--npt-space-2, 8px);
460
+ font-weight: 600;
461
+ color: var(--md-sys-color-success);
462
+ }
463
+ .status::before {
464
+ content: "";
465
+ inline-size: 8px;
466
+ block-size: 8px;
467
+ border-radius: var(--npt-corner-full, 999px);
468
+ background: currentColor;
469
+ }
470
+ ::slotted(*) {
471
+ margin-block-start: var(--npt-space-3, 12px);
472
+ }
473
+ `;
474
+ }
475
+ render() {
476
+ const merchant = esc(this.getAttribute("merchant"));
477
+ const amount = esc(this.getAttribute("amount"));
478
+ const currency = esc(this.getAttribute("currency"));
479
+ const date = esc(this.getAttribute("date"));
480
+ const status = esc(this.getAttribute("status"));
481
+ const reference = esc(this.getAttribute("reference"));
482
+ return html `<article
483
+ class="receipt"
484
+ part="receipt"
485
+ role="group"
486
+ aria-label="Receipt ${merchant} ${amount} ${currency}"
487
+ >
488
+ ${merchant ? html `<p class="merchant" part="merchant">${merchant}</p>` : ""}
489
+ ${amount
490
+ ? html `<p class="amount" part="amount">
491
+ ${currency ? html `<span class="currency">${currency}</span>` : ""}${amount}
492
+ </p>`
493
+ : ""}
494
+ <hr class="divider" part="divider" aria-hidden="true" />
495
+ <dl>
496
+ ${date ? html `<div class="row"><dt>Date</dt><dd>${date}</dd></div>` : ""}
497
+ ${reference
498
+ ? html `<div class="row"><dt>Reference</dt><dd class="num">${reference}</dd></div>`
499
+ : ""}
500
+ ${status
501
+ ? html `<div class="row">
502
+ <dt>Status</dt>
503
+ <dd><span class="status">${status}</span></dd>
504
+ </div>`
505
+ : ""}
506
+ </dl>
507
+ <slot></slot>
508
+ </article>`;
509
+ }
510
+ }
511
+ NptReceipt.observedAttributes = ["merchant", "amount", "currency", "date", "status", "reference"];
512
+ /**
513
+ * <npt-beneficiary-tile name="Mona Kamel" account="•••• 4821" [favorite]>
514
+ * </npt-beneficiary-tile>
515
+ * Avatar/initials + name + masked account + trailing chevron.
516
+ */
517
+ export class NptBeneficiaryTile extends NptElement {
518
+ attributeChangedCallback() {
519
+ if (this.isConnected)
520
+ this.update();
521
+ }
522
+ initials(name) {
523
+ const parts = name.trim().split(/\s+/).filter(Boolean);
524
+ if (parts.length === 0)
525
+ return "•";
526
+ const first = parts[0]?.charAt(0) ?? "";
527
+ const last = parts.length > 1 ? (parts[parts.length - 1]?.charAt(0) ?? "") : "";
528
+ return (first + last).toUpperCase() || "•";
529
+ }
530
+ styles() {
531
+ return css `
532
+ ${A11Y}
533
+ :host {
534
+ display: block;
535
+ }
536
+ .tile {
537
+ inline-size: 100%;
538
+ display: flex;
539
+ align-items: center;
540
+ gap: var(--npt-space-4, 16px);
541
+ min-height: 56px;
542
+ padding-inline: var(--npt-space-3, 12px);
543
+ padding-block: var(--npt-space-3, 12px);
544
+ border: none;
545
+ background: transparent;
546
+ color: inherit;
547
+ text-align: start;
548
+ font-family: var(--npt-font-text);
549
+ cursor: pointer;
550
+ border-radius: var(--npt-corner-md, 16px);
551
+ box-sizing: border-box;
552
+ transition: background-color var(--npt-dur-fast, 200ms) var(--npt-ease-standard, ease);
553
+ }
554
+ .tile:hover {
555
+ background: var(--md-sys-color-surface-container-high);
556
+ }
557
+ .avatar {
558
+ position: relative;
559
+ inline-size: 44px;
560
+ block-size: 44px;
561
+ flex: 0 0 auto;
562
+ border-radius: var(--npt-corner-full, 999px);
563
+ display: grid;
564
+ place-items: center;
565
+ background: var(--md-sys-color-primary-container);
566
+ color: var(--md-sys-color-on-primary-container);
567
+ font-family: var(--npt-font-display);
568
+ font-weight: 600;
569
+ font-size: var(--npt-text-label, 14px);
570
+ }
571
+ .star {
572
+ position: absolute;
573
+ inset-block-end: -2px;
574
+ inset-inline-end: -2px;
575
+ inline-size: 18px;
576
+ block-size: 18px;
577
+ border-radius: var(--npt-corner-full, 999px);
578
+ display: grid;
579
+ place-items: center;
580
+ font-size: var(--npt-text-caption, 12px);
581
+ background: var(--md-sys-color-tertiary);
582
+ color: var(--md-sys-color-on-tertiary);
583
+ }
584
+ .body {
585
+ flex: 1 1 auto;
586
+ min-inline-size: 0;
587
+ }
588
+ .name {
589
+ font-size: var(--npt-text-body-lg, 16px);
590
+ color: var(--md-sys-color-on-surface);
591
+ margin: 0;
592
+ white-space: nowrap;
593
+ overflow: hidden;
594
+ text-overflow: ellipsis;
595
+ }
596
+ .account {
597
+ font-family: var(--npt-font-num);
598
+ font-variant-numeric: tabular-nums;
599
+ font-size: var(--npt-text-body, 14px);
600
+ color: var(--md-sys-color-on-surface-variant);
601
+ margin: 2px 0 0;
602
+ }
603
+ .chevron {
604
+ flex: 0 0 auto;
605
+ color: var(--md-sys-color-on-surface-variant);
606
+ font-size: var(--npt-text-title, 18px);
607
+ line-height: 1;
608
+ }
609
+ `;
610
+ }
611
+ render() {
612
+ const name = esc(this.getAttribute("name"));
613
+ const account = esc(this.getAttribute("account"));
614
+ const favorite = this.hasAttribute("favorite");
615
+ const initials = this.initials(name);
616
+ const label = favorite ? `${name}, favourite${account ? `, ${account}` : ""}` : `${name}${account ? `, ${account}` : ""}`;
617
+ return html `<button class="tile" part="tile" type="button" aria-label="${esc(label)}">
618
+ <span class="avatar" aria-hidden="true">
619
+ ${initials}${favorite ? html `<span class="star">★</span>` : ""}
620
+ </span>
621
+ <span class="body">
622
+ <span class="name">${name}</span>
623
+ ${account ? html `<span class="account">${account}</span>` : ""}
624
+ </span>
625
+ <span class="chevron" aria-hidden="true">›</span>
626
+ </button>`;
627
+ }
628
+ }
629
+ NptBeneficiaryTile.observedAttributes = ["name", "account", "favorite"];
630
+ /**
631
+ * <npt-method-row title="Bank transfer" subtitle="1–2 business days" [recommended]>
632
+ * <span slot="icon">🏦</span>
633
+ * </npt-method-row>
634
+ * Transfer-method row: leading icon slot, title/subtitle, trailing chevron, badge.
635
+ */
636
+ export class NptMethodRow extends NptElement {
637
+ attributeChangedCallback() {
638
+ if (this.isConnected)
639
+ this.update();
640
+ }
641
+ styles() {
642
+ return css `
643
+ ${A11Y}
644
+ :host {
645
+ display: block;
646
+ }
647
+ .row {
648
+ inline-size: 100%;
649
+ display: flex;
650
+ align-items: center;
651
+ gap: var(--npt-space-4, 16px);
652
+ min-height: 64px;
653
+ padding-inline: var(--npt-space-4, 16px);
654
+ padding-block: var(--npt-space-3, 12px);
655
+ border: 1px solid var(--md-sys-color-outline-variant);
656
+ border-radius: var(--npt-corner-md, 16px);
657
+ background: var(--md-sys-color-surface-container-lowest);
658
+ color: inherit;
659
+ text-align: start;
660
+ font-family: var(--npt-font-text);
661
+ cursor: pointer;
662
+ box-sizing: border-box;
663
+ transition: border-color var(--npt-dur-fast, 200ms) var(--npt-ease-standard, ease),
664
+ background-color var(--npt-dur-fast, 200ms) var(--npt-ease-standard, ease);
665
+ }
666
+ .row:hover {
667
+ border-color: var(--md-sys-color-primary);
668
+ background: var(--md-sys-color-surface-container);
669
+ }
670
+ .icon {
671
+ inline-size: 40px;
672
+ block-size: 40px;
673
+ flex: 0 0 auto;
674
+ border-radius: var(--npt-corner-sm, 12px);
675
+ display: grid;
676
+ place-items: center;
677
+ background: var(--md-sys-color-secondary-container);
678
+ color: var(--md-sys-color-on-secondary-container);
679
+ font-size: var(--npt-text-title, 18px);
680
+ }
681
+ .body {
682
+ flex: 1 1 auto;
683
+ min-inline-size: 0;
684
+ }
685
+ .titlebar {
686
+ display: flex;
687
+ align-items: center;
688
+ gap: var(--npt-space-2, 8px);
689
+ flex-wrap: wrap;
690
+ }
691
+ .title {
692
+ font-size: var(--npt-text-body-lg, 16px);
693
+ font-weight: 600;
694
+ color: var(--md-sys-color-on-surface);
695
+ margin: 0;
696
+ }
697
+ .subtitle {
698
+ font-size: var(--npt-text-body, 14px);
699
+ color: var(--md-sys-color-on-surface-variant);
700
+ margin: 2px 0 0;
701
+ }
702
+ .badge {
703
+ font-size: var(--npt-text-caption, 12px);
704
+ font-weight: 600;
705
+ line-height: 1;
706
+ padding-inline: var(--npt-space-2, 8px);
707
+ padding-block: var(--npt-space-1, 4px);
708
+ border-radius: var(--npt-corner-full, 999px);
709
+ background: var(--md-sys-color-success-container, var(--md-sys-color-tertiary-container));
710
+ color: var(--md-sys-color-on-success-container, var(--md-sys-color-on-tertiary-container));
711
+ }
712
+ .chevron {
713
+ flex: 0 0 auto;
714
+ color: var(--md-sys-color-on-surface-variant);
715
+ font-size: var(--npt-text-title, 18px);
716
+ line-height: 1;
717
+ }
718
+ `;
719
+ }
720
+ render() {
721
+ const title = esc(this.getAttribute("title"));
722
+ const subtitle = esc(this.getAttribute("subtitle"));
723
+ const recommended = this.hasAttribute("recommended");
724
+ const label = recommended ? `${title}, recommended` : title;
725
+ return html `<button class="row" part="row" type="button" aria-label="${esc(label)}">
726
+ <span class="icon" aria-hidden="true"><slot name="icon"></slot></span>
727
+ <span class="body">
728
+ <span class="titlebar">
729
+ <span class="title">${title}</span>
730
+ ${recommended ? html `<span class="badge" part="badge">Recommended</span>` : ""}
731
+ </span>
732
+ ${subtitle ? html `<span class="subtitle">${subtitle}</span>` : ""}
733
+ </span>
734
+ <span class="chevron" aria-hidden="true">›</span>
735
+ </button>`;
736
+ }
737
+ }
738
+ NptMethodRow.observedAttributes = ["title", "subtitle", "recommended"];
739
+ void define;
740
+ //# sourceMappingURL=money-movement.js.map