@eifi1/ui-kit 0.4.1 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +32 -0
- package/README.md +498 -57
- package/dist/chart.d.ts +10 -0
- package/dist/chart.js +10 -0
- package/dist/chart.js.map +1 -0
- package/dist/components/account-settings.d.ts +11 -0
- package/dist/components/account-settings.js +19 -12
- package/dist/components/account-settings.js.map +1 -1
- package/dist/components/alert-banner.d.ts +10 -6
- package/dist/components/alert-banner.js +20 -16
- package/dist/components/alert-banner.js.map +1 -1
- package/dist/components/amount-input.d.ts +30 -4
- package/dist/components/amount-input.js +36 -14
- package/dist/components/amount-input.js.map +1 -1
- package/dist/components/calculator.d.ts +24 -7
- package/dist/components/calculator.js +37 -20
- package/dist/components/calculator.js.map +1 -1
- package/dist/components/chart-zoom.d.ts +156 -0
- package/dist/components/chart-zoom.js +262 -0
- package/dist/components/chart-zoom.js.map +1 -0
- package/dist/components/chart.d.ts +28 -3
- package/dist/components/chart.js +71 -32
- package/dist/components/chart.js.map +1 -1
- package/dist/components/checkbox.d.ts +51 -0
- package/dist/components/checkbox.js +123 -0
- package/dist/components/checkbox.js.map +1 -0
- package/dist/components/chip.d.ts +128 -0
- package/dist/components/chip.js +357 -0
- package/dist/components/chip.js.map +1 -0
- package/dist/components/choice-parts.d.ts +24 -0
- package/dist/components/choice-parts.js +18 -0
- package/dist/components/choice-parts.js.map +1 -0
- package/dist/components/combobox-core.d.ts +52 -9
- package/dist/components/combobox-core.js +89 -19
- package/dist/components/combobox-core.js.map +1 -1
- package/dist/components/combobox.d.ts +43 -33
- package/dist/components/combobox.js +409 -340
- package/dist/components/combobox.js.map +1 -1
- package/dist/components/currency-select.d.ts +48 -8
- package/dist/components/currency-select.js +107 -65
- package/dist/components/currency-select.js.map +1 -1
- package/dist/components/data-table-filter-popover.d.ts +15 -3
- package/dist/components/data-table-filter-popover.js +23 -17
- package/dist/components/data-table-filter-popover.js.map +1 -1
- package/dist/components/data-table-filters.d.ts +1 -1
- package/dist/components/data-table-filters.js +3 -2
- package/dist/components/data-table-filters.js.map +1 -1
- package/dist/components/data-table-labels.d.ts +53 -2
- package/dist/components/data-table-labels.js +31 -2
- package/dist/components/data-table-labels.js.map +1 -1
- package/dist/components/data-table-pagination.d.ts +3 -1
- package/dist/components/data-table-pagination.js +16 -10
- package/dist/components/data-table-pagination.js.map +1 -1
- package/dist/components/data-table-sort.js +1 -0
- package/dist/components/data-table-sort.js.map +1 -1
- package/dist/components/data-table.d.ts +1 -1
- package/dist/components/data-table.js +201 -85
- package/dist/components/data-table.js.map +1 -1
- package/dist/components/date-picker.d.ts +25 -9
- package/dist/components/date-picker.js +232 -94
- package/dist/components/date-picker.js.map +1 -1
- package/dist/components/dropdown.d.ts +74 -19
- package/dist/components/dropdown.js +72 -22
- package/dist/components/dropdown.js.map +1 -1
- package/dist/components/entity-combobox.d.ts +5 -4
- package/dist/components/entity-combobox.js +107 -76
- package/dist/components/entity-combobox.js.map +1 -1
- package/dist/components/facing-pair.d.ts +36 -0
- package/dist/components/facing-pair.js +40 -0
- package/dist/components/facing-pair.js.map +1 -0
- package/dist/components/field-sync.d.ts +164 -0
- package/dist/components/field-sync.js +264 -0
- package/dist/components/field-sync.js.map +1 -0
- package/dist/components/file-dropzone.d.ts +21 -7
- package/dist/components/file-dropzone.js +28 -7
- package/dist/components/file-dropzone.js.map +1 -1
- package/dist/components/full-bleed-dialog.d.ts +37 -26
- package/dist/components/full-bleed-dialog.js +20 -5
- package/dist/components/full-bleed-dialog.js.map +1 -1
- package/dist/components/grouped-picker.d.ts +21 -10
- package/dist/components/grouped-picker.js +66 -52
- package/dist/components/grouped-picker.js.map +1 -1
- package/dist/components/hover-menu.d.ts +20 -4
- package/dist/components/hover-menu.js +15 -4
- package/dist/components/hover-menu.js.map +1 -1
- package/dist/components/mini-calendar.d.ts +102 -14
- package/dist/components/mini-calendar.js +256 -70
- package/dist/components/mini-calendar.js.map +1 -1
- package/dist/components/modal.d.ts +9 -3
- package/dist/components/modal.js +34 -45
- package/dist/components/modal.js.map +1 -1
- package/dist/components/month-picker.d.ts +79 -0
- package/dist/components/month-picker.js +342 -0
- package/dist/components/month-picker.js.map +1 -0
- package/dist/components/multi-entity-combobox.d.ts +7 -6
- package/dist/components/multi-entity-combobox.js +112 -72
- package/dist/components/multi-entity-combobox.js.map +1 -1
- package/dist/components/multi-select.d.ts +13 -11
- package/dist/components/multi-select.js +161 -84
- package/dist/components/multi-select.js.map +1 -1
- package/dist/components/number-field.d.ts +92 -0
- package/dist/components/number-field.js +111 -0
- package/dist/components/number-field.js.map +1 -0
- package/dist/components/number-input.d.ts +21 -2
- package/dist/components/number-input.js +2 -1
- package/dist/components/number-input.js.map +1 -1
- package/dist/components/numpad-sheet.d.ts +25 -4
- package/dist/components/numpad-sheet.js +30 -13
- package/dist/components/numpad-sheet.js.map +1 -1
- package/dist/components/page-contents.d.ts +90 -0
- package/dist/components/page-contents.js +170 -0
- package/dist/components/page-contents.js.map +1 -0
- package/dist/components/password-strength.d.ts +106 -0
- package/dist/components/password-strength.js +132 -0
- package/dist/components/password-strength.js.map +1 -0
- package/dist/components/picker-sheet.d.ts +23 -15
- package/dist/components/picker-sheet.js +34 -14
- package/dist/components/picker-sheet.js.map +1 -1
- package/dist/components/popover.d.ts +62 -4
- package/dist/components/popover.js +52 -2
- package/dist/components/popover.js.map +1 -1
- package/dist/components/search-field.d.ts +25 -7
- package/dist/components/search-field.js +7 -5
- package/dist/components/search-field.js.map +1 -1
- package/dist/components/series-chart-labels.d.ts +14 -0
- package/dist/components/series-chart-labels.js +11 -0
- package/dist/components/series-chart-labels.js.map +1 -0
- package/dist/components/series-chart.d.ts +207 -0
- package/dist/components/series-chart.js +223 -0
- package/dist/components/series-chart.js.map +1 -0
- package/dist/components/settings-fields.d.ts +14 -4
- package/dist/components/settings-fields.js +19 -8
- package/dist/components/settings-fields.js.map +1 -1
- package/dist/components/signature-pad.d.ts +119 -0
- package/dist/components/signature-pad.js +405 -0
- package/dist/components/signature-pad.js.map +1 -0
- package/dist/components/slider.d.ts +80 -0
- package/dist/components/slider.js +155 -0
- package/dist/components/slider.js.map +1 -0
- package/dist/components/sparkline.d.ts +93 -0
- package/dist/components/sparkline.js +205 -0
- package/dist/components/sparkline.js.map +1 -0
- package/dist/components/stat-tile.d.ts +151 -0
- package/dist/components/stat-tile.js +236 -0
- package/dist/components/stat-tile.js.map +1 -0
- package/dist/components/swipeable-row.d.ts +25 -2
- package/dist/components/swipeable-row.js +31 -0
- package/dist/components/swipeable-row.js.map +1 -1
- package/dist/components/switch.d.ts +60 -0
- package/dist/components/switch.js +81 -0
- package/dist/components/switch.js.map +1 -0
- package/dist/components/time-input.d.ts +58 -0
- package/dist/components/time-input.js +50 -0
- package/dist/components/time-input.js.map +1 -0
- package/dist/components/toggle-group.d.ts +18 -3
- package/dist/components/toggle-group.js +10 -6
- package/dist/components/toggle-group.js.map +1 -1
- package/dist/components/toggle-legend.d.ts +115 -0
- package/dist/components/toggle-legend.js +114 -0
- package/dist/components/toggle-legend.js.map +1 -0
- package/dist/components/tooltip.d.ts +40 -11
- package/dist/components/tooltip.js +77 -20
- package/dist/components/tooltip.js.map +1 -1
- package/dist/components/treemap.d.ts +132 -0
- package/dist/components/treemap.js +179 -0
- package/dist/components/treemap.js.map +1 -0
- package/dist/components/ui.d.ts +159 -46
- package/dist/components/ui.js +220 -115
- package/dist/components/ui.js.map +1 -1
- package/dist/components/use-mobile-reveal.js +1 -0
- package/dist/components/use-mobile-reveal.js.map +1 -1
- package/dist/components/use-table-state.d.ts +1 -1
- package/dist/components/use-table-state.js +38 -17
- package/dist/components/use-table-state.js.map +1 -1
- package/dist/components/user-avatar.d.ts +5 -3
- package/dist/components/user-avatar.js +4 -2
- package/dist/components/user-avatar.js.map +1 -1
- package/dist/components/wizard-stepper.js +3 -2
- package/dist/components/wizard-stepper.js.map +1 -1
- package/dist/{data-table-filters-aXyf0Xub.d.ts → data-table-filters-DM1auUfH.d.ts} +27 -5
- package/dist/data-table.d.ts +7 -0
- package/dist/data-table.js +12 -0
- package/dist/data-table.js.map +1 -0
- package/dist/feedback/feedback-attachment.js +12 -11
- package/dist/feedback/feedback-attachment.js.map +1 -1
- package/dist/feedback/feedback-dialog.js +2 -1
- package/dist/feedback/feedback-dialog.js.map +1 -1
- package/dist/feedback/feedback-inbox.d.ts +33 -7
- package/dist/feedback/feedback-inbox.js +49 -37
- package/dist/feedback/feedback-inbox.js.map +1 -1
- package/dist/feedback.d.ts +5 -0
- package/dist/feedback.js +5 -0
- package/dist/feedback.js.map +1 -0
- package/dist/hooks/use-anchored-panel.js +1 -0
- package/dist/hooks/use-anchored-panel.js.map +1 -1
- package/dist/hooks/use-anchored-rect.js +1 -0
- package/dist/hooks/use-anchored-rect.js.map +1 -1
- package/dist/hooks/use-announce.d.ts +46 -0
- package/dist/hooks/use-announce.js +37 -0
- package/dist/hooks/use-announce.js.map +1 -0
- package/dist/hooks/use-body-scroll-lock.js +1 -0
- package/dist/hooks/use-body-scroll-lock.js.map +1 -1
- package/dist/hooks/use-close-transition.js +1 -0
- package/dist/hooks/use-close-transition.js.map +1 -1
- package/dist/hooks/use-dismiss.js +1 -0
- package/dist/hooks/use-dismiss.js.map +1 -1
- package/dist/hooks/use-focus-trap.d.ts +16 -0
- package/dist/hooks/use-focus-trap.js +67 -0
- package/dist/hooks/use-focus-trap.js.map +1 -0
- package/dist/hooks/use-media-query.js +1 -0
- package/dist/hooks/use-media-query.js.map +1 -1
- package/dist/hooks/use-overlay-history.js +27 -1
- package/dist/hooks/use-overlay-history.js.map +1 -1
- package/dist/hooks/use-row-swipe.js +1 -0
- package/dist/hooks/use-row-swipe.js.map +1 -1
- package/dist/i18n/defaults.d.ts +31 -0
- package/dist/i18n/defaults.js +63 -0
- package/dist/i18n/defaults.js.map +1 -0
- package/dist/i18n/kit-labels.d.ts +250 -0
- package/dist/i18n/kit-labels.js +184 -0
- package/dist/i18n/kit-labels.js.map +1 -0
- package/dist/index.d.ts +47 -22
- package/dist/index.js +28 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/calc.js +1 -0
- package/dist/lib/calc.js.map +1 -1
- package/dist/lib/cn.js +1 -0
- package/dist/lib/cn.js.map +1 -1
- package/dist/lib/dates.js +1 -0
- package/dist/lib/dates.js.map +1 -1
- package/dist/lib/logger.js +1 -0
- package/dist/lib/logger.js.map +1 -1
- package/dist/lib/safe-storage.js +1 -0
- package/dist/lib/safe-storage.js.map +1 -1
- package/dist/search/command-palette.d.ts +11 -1
- package/dist/search/command-palette.js +21 -15
- package/dist/search/command-palette.js.map +1 -1
- package/dist/search.d.ts +2 -0
- package/dist/search.js +3 -0
- package/dist/search.js.map +1 -0
- package/dist/shell/app-shell.d.ts +33 -4
- package/dist/shell/app-shell.js +275 -78
- package/dist/shell/app-shell.js.map +1 -1
- package/dist/shell/option-switcher-menu.js +3 -2
- package/dist/shell/option-switcher-menu.js.map +1 -1
- package/dist/shell/role-switcher.d.ts +6 -0
- package/dist/shell/role-switcher.js +7 -4
- package/dist/shell/role-switcher.js.map +1 -1
- package/dist/shell/top-bar.d.ts +15 -7
- package/dist/shell/top-bar.js +3 -5
- package/dist/shell/top-bar.js.map +1 -1
- package/dist/shell/topbar-action-menu.js +3 -2
- package/dist/shell/topbar-action-menu.js.map +1 -1
- package/dist/shell/topbar-controls.d.ts +5 -2
- package/dist/shell/topbar-controls.js +20 -15
- package/dist/shell/topbar-controls.js.map +1 -1
- package/dist/shell.d.ts +13 -0
- package/dist/shell.js +8 -0
- package/dist/shell.js.map +1 -0
- package/dist/theme/chart-palette.d.ts +19 -12
- package/dist/theme/chart-palette.js +24 -30
- package/dist/theme/chart-palette.js.map +1 -1
- package/dist/theme/color.d.ts +78 -0
- package/dist/theme/color.js +148 -0
- package/dist/theme/color.js.map +1 -0
- package/dist/theme/palette-derive.d.ts +140 -0
- package/dist/theme/palette-derive.js +376 -0
- package/dist/theme/palette-derive.js.map +1 -0
- package/dist/theme/palette-presets.d.ts +14 -1
- package/dist/theme/palette-presets.js +231 -3
- package/dist/theme/palette-presets.js.map +1 -1
- package/dist/theme/palette-store.js +1 -0
- package/dist/theme/palette-store.js.map +1 -1
- package/dist/theme/theme-store.js +1 -0
- package/dist/theme/theme-store.js.map +1 -1
- package/dist/tour/tour.d.ts +2 -1
- package/dist/tour/tour.js +15 -9
- package/dist/tour/tour.js.map +1 -1
- package/dist/tour.d.ts +2 -0
- package/dist/tour.js +3 -0
- package/dist/tour.js.map +1 -0
- package/dist/wizard/stepper-nav.js +10 -8
- package/dist/wizard/stepper-nav.js.map +1 -1
- package/dist/wizard/types.d.ts +18 -2
- package/dist/wizard/types.js +4 -1
- package/dist/wizard/types.js.map +1 -1
- package/dist/wizard/use-wizard.js +12 -4
- package/dist/wizard/use-wizard.js.map +1 -1
- package/dist/wizard/validation.js +1 -0
- package/dist/wizard/validation.js.map +1 -1
- package/dist/wizard/wizard-context.js +1 -0
- package/dist/wizard/wizard-context.js.map +1 -1
- package/dist/wizard/wizard-step.js +1 -0
- package/dist/wizard/wizard-step.js.map +1 -1
- package/dist/wizard/wizard-summary.js +5 -3
- package/dist/wizard/wizard-summary.js.map +1 -1
- package/dist/wizard.d.ts +8 -0
- package/dist/wizard.js +9 -0
- package/dist/wizard.js.map +1 -0
- package/package.json +66 -13
- package/src/chart.ts +22 -0
- package/src/components/account-settings.tsx +275 -0
- package/src/components/alert-banner.tsx +75 -0
- package/src/components/amount-input.tsx +471 -0
- package/src/components/calculator.tsx +252 -0
- package/src/components/chart-zoom.tsx +499 -0
- package/src/components/chart.tsx +363 -0
- package/src/components/checkbox.tsx +222 -0
- package/src/components/chip.tsx +570 -0
- package/src/components/choice-parts.ts +33 -0
- package/src/components/combobox-core.tsx +540 -0
- package/src/components/combobox.tsx +1026 -0
- package/src/components/currency-select.tsx +269 -0
- package/src/components/data-table-filter-popover.tsx +252 -0
- package/src/components/data-table-filters.ts +176 -0
- package/src/components/data-table-labels.ts +190 -0
- package/src/components/data-table-pagination.tsx +126 -0
- package/src/components/data-table-sort.ts +70 -0
- package/src/components/data-table.tsx +1740 -0
- package/src/components/date-picker.tsx +631 -0
- package/src/components/dropdown.tsx +341 -0
- package/src/components/entity-combobox.tsx +215 -0
- package/src/components/facing-pair.ts +86 -0
- package/src/components/field-sync.tsx +472 -0
- package/src/components/file-dropzone.tsx +181 -0
- package/src/components/full-bleed-dialog.tsx +179 -0
- package/src/components/grouped-picker.tsx +157 -0
- package/src/components/hover-menu.tsx +183 -0
- package/src/components/mini-calendar.tsx +554 -0
- package/src/components/modal.tsx +287 -0
- package/src/components/month-picker.tsx +549 -0
- package/src/components/multi-entity-combobox.tsx +226 -0
- package/src/components/multi-select.tsx +303 -0
- package/src/components/number-field.tsx +225 -0
- package/src/components/number-input.tsx +211 -0
- package/src/components/numpad-sheet.tsx +264 -0
- package/src/components/page-contents.tsx +289 -0
- package/src/components/password-strength.tsx +258 -0
- package/src/components/picker-sheet.tsx +251 -0
- package/src/components/popover.tsx +195 -0
- package/src/components/search-field.tsx +146 -0
- package/src/components/series-chart-labels.ts +26 -0
- package/src/components/series-chart.tsx +481 -0
- package/src/components/settings-fields.tsx +68 -0
- package/src/components/signature-pad.tsx +630 -0
- package/src/components/slider.tsx +313 -0
- package/src/components/sparkline.tsx +329 -0
- package/src/components/stat-tile.tsx +448 -0
- package/src/components/swipeable-row.tsx +271 -0
- package/src/components/switch.tsx +170 -0
- package/src/components/time-input.tsx +107 -0
- package/src/components/toggle-group.tsx +153 -0
- package/src/components/toggle-legend.tsx +229 -0
- package/src/components/tooltip.tsx +477 -0
- package/src/components/treemap.tsx +390 -0
- package/src/components/ui.tsx +1284 -0
- package/src/components/use-mobile-reveal.ts +54 -0
- package/src/components/use-table-state.ts +295 -0
- package/src/components/user-avatar.tsx +61 -0
- package/src/components/wizard-stepper.tsx +37 -0
- package/src/data-table.ts +19 -0
- package/src/feedback/feedback-attachment.tsx +246 -0
- package/src/feedback/feedback-dialog.tsx +209 -0
- package/src/feedback/feedback-inbox.tsx +615 -0
- package/src/feedback.ts +8 -0
- package/src/hooks/use-anchored-panel.ts +169 -0
- package/src/hooks/use-anchored-rect.ts +78 -0
- package/src/hooks/use-announce.ts +85 -0
- package/src/hooks/use-body-scroll-lock.ts +94 -0
- package/src/hooks/use-close-transition.ts +99 -0
- package/src/hooks/use-dismiss.ts +77 -0
- package/src/hooks/use-focus-trap.ts +160 -0
- package/src/hooks/use-media-query.ts +23 -0
- package/src/hooks/use-overlay-history.ts +345 -0
- package/src/hooks/use-row-swipe.ts +212 -0
- package/src/i18n/defaults.ts +68 -0
- package/src/i18n/kit-labels.tsx +447 -0
- package/src/index.ts +169 -0
- package/src/lib/calc.ts +309 -0
- package/src/lib/cn.ts +5 -0
- package/src/lib/dates.ts +187 -0
- package/src/lib/logger.ts +73 -0
- package/src/lib/safe-storage.ts +37 -0
- package/src/search/command-palette.tsx +342 -0
- package/src/search.ts +3 -0
- package/src/shell/app-shell.tsx +772 -0
- package/src/shell/option-switcher-menu.tsx +72 -0
- package/src/shell/role-switcher.tsx +46 -0
- package/src/shell/top-bar.tsx +39 -0
- package/src/shell/topbar-action-menu.tsx +91 -0
- package/src/shell/topbar-controls.tsx +229 -0
- package/src/shell.ts +10 -0
- package/src/theme/chart-palette.ts +145 -0
- package/src/theme/color.ts +266 -0
- package/src/theme/palette-derive.ts +681 -0
- package/src/theme/palette-presets.ts +760 -0
- package/src/theme/palette-store.ts +94 -0
- package/src/theme/theme-store.ts +145 -0
- package/src/tour/tour.tsx +586 -0
- package/src/tour.ts +3 -0
- package/src/vite-env.d.ts +26 -0
- package/src/wizard/stepper-nav.tsx +221 -0
- package/src/wizard/types.ts +178 -0
- package/src/wizard/use-wizard.ts +345 -0
- package/src/wizard/validation.ts +37 -0
- package/src/wizard/wizard-context.tsx +29 -0
- package/src/wizard/wizard-step.tsx +14 -0
- package/src/wizard/wizard-summary.tsx +69 -0
- package/src/wizard.ts +14 -0
- package/tokens.css +222 -20
package/dist/chart.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { ChartConfig, ChartContainer, ChartLegend, ChartLegendContent, ChartSeriesConfig, ChartTooltip, ChartTooltipContent, useChart } from './components/chart.js';
|
|
2
|
+
export { CHART_COLORS, HEATMAP_HEX, HeatStops, PALETTE_HEX, lerpHex, paletteFor, textOn } from './theme/chart-palette.js';
|
|
3
|
+
export { Treemap, TreemapCell, TreemapCellProps, TreemapNode, TreemapProps, fitLabel } from './components/treemap.js';
|
|
4
|
+
export { AXIS_TICK_WIDTH, AXIS_TITLE_STRIP, SeriesChart, SeriesChartAxis, SeriesChartProps, SeriesChartSeries, SeriesChartSpan, SeriesChartX, SeriesSource, StaticSeriesChart, axisBandWidth, mergeSeries, oneAxis, padBand, paddedDomain, seriesKey, soleSeriesColor } from './components/series-chart.js';
|
|
5
|
+
export { DEFAULT_Y_AXIS, NO_ZOOM, SharedXZoom, ZOOM_MIN_DRAG, ZOOM_SQUARE_ENOUGH, ZoomAxes, ZoomBinding, ZoomDrag, ZoomFitSource, ZoomSelection, ZoomState, ZoomTarget, fitXToY, fitYToX, selectionFromDrag, withChartZoom, zoomAfter, zoomAxesFor, zoomDomains } from './components/chart-zoom.js';
|
|
6
|
+
export { LegendColumn, LegendEntry, LegendGroup, STEP_DASH, STROKE_PATTERNS, ToggleLegend, ToggleLegendProps, strokeDash, toggleHidden } from './components/toggle-legend.js';
|
|
7
|
+
export { FACING_SIDES, FacingSide, facingAxes, facingBand, facingHeadingPad } from './components/facing-pair.js';
|
|
8
|
+
export { DEFAULT_SERIES_CHART_LABELS, SeriesChartLabels } from './components/series-chart-labels.js';
|
|
9
|
+
import 'recharts';
|
|
10
|
+
import 'react';
|
package/dist/chart.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
export * from "./components/chart.js";
|
|
3
|
+
export * from "./theme/chart-palette.js";
|
|
4
|
+
export * from "./components/treemap.js";
|
|
5
|
+
export * from "./components/series-chart.js";
|
|
6
|
+
export * from "./components/chart-zoom.js";
|
|
7
|
+
export * from "./components/toggle-legend.js";
|
|
8
|
+
export * from "./components/facing-pair.js";
|
|
9
|
+
export * from "./components/series-chart-labels.js";
|
|
10
|
+
//# sourceMappingURL=chart.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/chart.ts"],"sourcesContent":["// `@eifi1/ui-kit/chart` — the chart shell and its colour system.\n//\n// A re-slicing of what the main barrel already exports, not a new API. It exists so a\n// consumer can reach the chart kit WITHOUT the barrel: `import(\"@eifi1/ui-kit/chart\")`\n// is a 31KB chunk, where the same lazy import through the barrel pulls 221KB, because\n// a dynamic import of the barrel cannot be tree-shaken down to one of its members.\n//\n// This is also the only entry point in the package that requires `recharts`.\nexport * from \"./components/chart\";\nexport * from \"./theme/chart-palette\";\n\n// The tile chart (treemap) the apps share — moved here from keksdose so every consumer\n// derives it from the package rather than from a copy.\nexport * from \"./components/treemap\";\n\n// lenkbank's zoomable series chart, its synced x-zoom, the toggle legend and the\n// mirrored-pair axis geometry — moved here so the apps derive them from the package.\nexport * from \"./components/series-chart\";\nexport * from \"./components/chart-zoom\";\nexport * from \"./components/toggle-legend\";\nexport * from \"./components/facing-pair\";\nexport * from \"./components/series-chart-labels\";\n"],"mappings":";AAQA,cAAc;AACd,cAAc;AAId,cAAc;AAId,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;","names":[]}
|
|
@@ -53,6 +53,17 @@ interface TwoFactorSettingLabels {
|
|
|
53
53
|
disableSection: string;
|
|
54
54
|
password: string;
|
|
55
55
|
disable: string;
|
|
56
|
+
/**
|
|
57
|
+
* Alt text for the setup QR image, which shipped as a hardcoded `alt="QR"`.
|
|
58
|
+
*
|
|
59
|
+
* OPTIONAL, alone among these keys, and not because it matters less: this
|
|
60
|
+
* interface is annotated at consumer call sites (`const LABELS:
|
|
61
|
+
* TwoFactorSettingLabels = {…}`), so a new REQUIRED key is a compile error in
|
|
62
|
+
* every app on the next `npm update` — the additive-API rule in the README. It
|
|
63
|
+
* falls back to English here the way `AmountInput`'s label keys do. Make it
|
|
64
|
+
* required in the next major, when the three apps can be updated with it.
|
|
65
|
+
*/
|
|
66
|
+
qrAlt?: string;
|
|
56
67
|
}
|
|
57
68
|
declare function TwoFactorSetting({ enabled, setup, onStartSetup, onEnable, onDisable, busy, labels, }: {
|
|
58
69
|
enabled: boolean;
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
"use client";
|
|
1
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
3
|
import { useState } from "react";
|
|
3
4
|
import { Button, Card, FIELD_WRITABLE_LOOK, Input } from "./ui.js";
|
|
4
5
|
import { UserAvatar } from "./user-avatar.js";
|
|
6
|
+
import { DEFAULT_COMMON_LABELS, useKitLabels } from "../i18n/kit-labels.js";
|
|
5
7
|
function ProfileSetting({
|
|
6
8
|
name,
|
|
7
9
|
email,
|
|
@@ -19,13 +21,13 @@ function ProfileSetting({
|
|
|
19
21
|
/* @__PURE__ */ jsx(UserAvatar, { name, email, size: "lg" }),
|
|
20
22
|
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
21
23
|
/* @__PURE__ */ jsx("div", { className: "text-sm font-medium", children: labels.title }),
|
|
22
|
-
/* @__PURE__ */ jsx("div", { className: "truncate font-mono text-xs text-
|
|
24
|
+
/* @__PURE__ */ jsx("div", { className: "truncate font-mono text-xs text-[var(--text-muted)]", children: email ?? "\u2014" })
|
|
23
25
|
] })
|
|
24
26
|
] }),
|
|
25
27
|
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-[max-content_1fr] gap-x-3 gap-y-1 text-sm", children: [
|
|
26
|
-
/* @__PURE__ */ jsx("span", { className: "text-
|
|
28
|
+
/* @__PURE__ */ jsx("span", { className: "text-[var(--text-muted)]", children: labels.role }),
|
|
27
29
|
/* @__PURE__ */ jsx("span", { children: role ?? "\u2014" }),
|
|
28
|
-
/* @__PURE__ */ jsx("span", { className: "text-
|
|
30
|
+
/* @__PURE__ */ jsx("span", { className: "text-[var(--text-muted)]", children: labels.memberSince }),
|
|
29
31
|
/* @__PURE__ */ jsx("span", { children: memberSince ?? "\u2014" })
|
|
30
32
|
] }),
|
|
31
33
|
/* @__PURE__ */ jsx(
|
|
@@ -74,10 +76,11 @@ function PasswordSetting({
|
|
|
74
76
|
/* @__PURE__ */ jsx(Input, { type: "password", autoComplete: "current-password", label: labels.current, value: current, onChange: (e) => setCurrent(e.target.value) }),
|
|
75
77
|
/* @__PURE__ */ jsx(Input, { type: "password", autoComplete: "new-password", label: labels.next, value: next, onChange: (e) => setNext(e.target.value) }),
|
|
76
78
|
/* @__PURE__ */ jsx(Input, { type: "password", autoComplete: "new-password", label: labels.confirm, value: confirm, onChange: (e) => setConfirm(e.target.value) }),
|
|
77
|
-
error && /* @__PURE__ */ jsx("p", { className: "text-xs text-
|
|
79
|
+
error && /* @__PURE__ */ jsx("p", { className: "text-xs text-[var(--danger)]", children: error }),
|
|
78
80
|
/* @__PURE__ */ jsx(Button, { onClick: () => void submit(), disabled: !current || !next || !confirm || pending, children: labels.submit })
|
|
79
81
|
] });
|
|
80
82
|
}
|
|
83
|
+
const DEFAULT_QR_ALT = "QR code";
|
|
81
84
|
function TwoFactorSetting({
|
|
82
85
|
enabled,
|
|
83
86
|
setup,
|
|
@@ -91,16 +94,20 @@ function TwoFactorSetting({
|
|
|
91
94
|
const [password, setPassword] = useState("");
|
|
92
95
|
const [otpReadonly, setOtpReadonly] = useState(true);
|
|
93
96
|
const onOtpFocus = () => setOtpReadonly(false);
|
|
97
|
+
const common = useKitLabels("common", DEFAULT_COMMON_LABELS);
|
|
94
98
|
return /* @__PURE__ */ jsxs(Card, { className: "p-4 space-y-3", children: [
|
|
95
|
-
/* @__PURE__ */
|
|
96
|
-
labels.status,
|
|
97
|
-
": ",
|
|
98
|
-
enabled ? labels.enabledText : labels.disabledText
|
|
99
|
-
] }),
|
|
99
|
+
/* @__PURE__ */ jsx("div", { className: "text-sm", children: common.fieldValue(labels.status, enabled ? labels.enabledText : labels.disabledText) }),
|
|
100
100
|
!enabled && !setup && /* @__PURE__ */ jsx(Button, { onClick: onStartSetup, disabled: busy, children: labels.enable }),
|
|
101
101
|
setup && /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
102
|
-
/* @__PURE__ */ jsx("div", { className: "text-xs text-
|
|
103
|
-
/* @__PURE__ */ jsx("div", { className: "inline-block rounded-md bg-white p-2", children: /* @__PURE__ */ jsx(
|
|
102
|
+
/* @__PURE__ */ jsx("div", { className: "text-xs text-[var(--text-secondary)]", children: labels.scanHint }),
|
|
103
|
+
/* @__PURE__ */ jsx("div", { className: "inline-block rounded-md bg-white p-2", children: /* @__PURE__ */ jsx(
|
|
104
|
+
"img",
|
|
105
|
+
{
|
|
106
|
+
alt: labels.qrAlt ?? DEFAULT_QR_ALT,
|
|
107
|
+
src: `data:image/svg+xml;base64,${setup.qrSvg}`,
|
|
108
|
+
className: "h-48 w-48"
|
|
109
|
+
}
|
|
110
|
+
) }),
|
|
104
111
|
/* @__PURE__ */ jsx("div", { className: "break-all font-mono text-xs", children: setup.secret }),
|
|
105
112
|
/* @__PURE__ */ jsx(
|
|
106
113
|
Input,
|
|
@@ -124,7 +131,7 @@ function TwoFactorSetting({
|
|
|
124
131
|
),
|
|
125
132
|
/* @__PURE__ */ jsx(Button, { onClick: () => onEnable(code), disabled: !code || busy, children: labels.verify })
|
|
126
133
|
] }),
|
|
127
|
-
enabled && /* @__PURE__ */ jsxs("div", { className: "space-y-3 border-t border-
|
|
134
|
+
enabled && /* @__PURE__ */ jsxs("div", { className: "space-y-3 border-t border-[var(--border)] pt-3", children: [
|
|
128
135
|
/* @__PURE__ */ jsx("div", { className: "text-sm font-medium", children: labels.disableSection }),
|
|
129
136
|
/* @__PURE__ */ jsx(Input, { type: "password", autoComplete: "current-password", label: labels.password, value: password, onChange: (e) => setPassword(e.target.value) }),
|
|
130
137
|
/* @__PURE__ */ jsx(
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/account-settings.tsx"],"sourcesContent":["import { useState } from \"react\";\nimport type { KeyboardEvent, ReactNode } from \"react\";\nimport { Button, Card, FIELD_WRITABLE_LOOK, Input } from \"./ui\";\nimport { UserAvatar } from \"./user-avatar\";\n\n/**\n * App-agnostic account-settings SECTIONS (feedback #333). The presentation lives\n * in @hb/ui; each app injects its own auth-API handlers + translated labels and\n * arranges these next to its own app-specific cards (appearance, budgets, …).\n * Every section is a self-contained <Card> owning its transient form state.\n */\n\nexport interface ProfileSettingLabels {\n title: string;\n email: string;\n role: string;\n memberSince: string;\n displayName: string;\n save: string;\n}\n\nexport function ProfileSetting({\n name,\n email,\n role,\n memberSince,\n value,\n onChange,\n onSave,\n saving,\n labels,\n}: {\n name?: string | null;\n email?: string | null;\n role?: ReactNode;\n memberSince?: ReactNode;\n value: string;\n onChange: (value: string) => void;\n onSave: () => void;\n saving?: boolean;\n labels: ProfileSettingLabels;\n}) {\n const dirty = value.trim().length > 0 && value.trim() !== (name ?? \"\");\n return (\n <Card className=\"p-4 space-y-3\">\n <div className=\"flex items-center gap-3\">\n <UserAvatar name={name} email={email} size=\"lg\" />\n <div className=\"min-w-0\">\n <div className=\"text-sm font-medium\">{labels.title}</div>\n <div className=\"truncate font-mono text-xs text-slate-500 dark:text-slate-400\">{email ?? \"—\"}</div>\n </div>\n </div>\n <div className=\"grid grid-cols-[max-content_1fr] gap-x-3 gap-y-1 text-sm\">\n <span className=\"text-slate-500\">{labels.role}</span>\n <span>{role ?? \"—\"}</span>\n <span className=\"text-slate-500\">{labels.memberSince}</span>\n <span>{memberSince ?? \"—\"}</span>\n </div>\n <Input\n id=\"display-name\"\n label={labels.displayName}\n value={value}\n onChange={(e) => onChange(e.target.value)}\n maxLength={120}\n />\n <Button onClick={onSave} disabled={!dirty || saving}>\n {labels.save}\n </Button>\n </Card>\n );\n}\n\nexport interface PasswordSettingLabels {\n title: string;\n current: string;\n next: string;\n confirm: string;\n submit: string;\n tooShort: string;\n mismatch: string;\n}\n\nexport function PasswordSetting({\n onSubmit,\n pending,\n minLength = 8,\n labels,\n}: {\n /** Called with the validated (current, new) pair; return a promise to auto-clear on success. */\n onSubmit: (currentPassword: string, newPassword: string) => void | Promise<unknown>;\n pending?: boolean;\n minLength?: number;\n labels: PasswordSettingLabels;\n}) {\n const [current, setCurrent] = useState(\"\");\n const [next, setNext] = useState(\"\");\n const [confirm, setConfirm] = useState(\"\");\n const [error, setError] = useState<string | null>(null);\n\n const submit = async () => {\n if (next.length < minLength) {\n setError(labels.tooShort);\n return;\n }\n if (next !== confirm) {\n setError(labels.mismatch);\n return;\n }\n setError(null);\n try {\n await onSubmit(current, next);\n setCurrent(\"\");\n setNext(\"\");\n setConfirm(\"\");\n } catch {\n // The app surfaces the failure (e.g. a toast); keep the fields for a retry.\n }\n };\n\n return (\n <Card className=\"p-4 space-y-3\">\n <div className=\"text-sm font-medium\">{labels.title}</div>\n <Input type=\"password\" autoComplete=\"current-password\" label={labels.current} value={current} onChange={(e) => setCurrent(e.target.value)} />\n <Input type=\"password\" autoComplete=\"new-password\" label={labels.next} value={next} onChange={(e) => setNext(e.target.value)} />\n <Input type=\"password\" autoComplete=\"new-password\" label={labels.confirm} value={confirm} onChange={(e) => setConfirm(e.target.value)} />\n {error && <p className=\"text-xs text-rose-600 dark:text-rose-400\">{error}</p>}\n <Button onClick={() => void submit()} disabled={!current || !next || !confirm || pending}>\n {labels.submit}\n </Button>\n </Card>\n );\n}\n\nexport interface TwoFactorSettingLabels {\n status: string;\n enabledText: string;\n disabledText: string;\n enable: string;\n scanHint: string;\n codeLabel: string;\n verify: string;\n disableSection: string;\n password: string;\n disable: string;\n}\n\nexport function TwoFactorSetting({\n enabled,\n setup,\n onStartSetup,\n onEnable,\n onDisable,\n busy,\n labels,\n}: {\n enabled: boolean;\n /** QR + secret returned by the app's setup call; null before setup starts. */\n setup: { qrSvg: string; secret: string } | null;\n onStartSetup: () => void;\n onEnable: (code: string) => void;\n onDisable: (password: string, code: string) => void;\n busy?: boolean;\n labels: TwoFactorSettingLabels;\n}) {\n const [code, setCode] = useState(\"\");\n const [password, setPassword] = useState(\"\");\n // Browsers autofill one-time-code fields with the saved username; keep the\n // field readOnly until focus to block that. It therefore has to keep the LOOK of\n // an editable field (FIELD_WRITABLE_LOOK): this is the one place where readOnly\n // does not mean \"you may not type here\", and without the opt-out the field would\n // sit there greyed until the moment it is focused.\n const [otpReadonly, setOtpReadonly] = useState(true);\n const onOtpFocus = () => setOtpReadonly(false);\n\n return (\n <Card className=\"p-4 space-y-3\">\n <div className=\"text-sm\">\n {labels.status}: {enabled ? labels.enabledText : labels.disabledText}\n </div>\n\n {!enabled && !setup && (\n <Button onClick={onStartSetup} disabled={busy}>\n {labels.enable}\n </Button>\n )}\n\n {setup && (\n <div className=\"space-y-2\">\n <div className=\"text-xs text-slate-600 dark:text-slate-400\">{labels.scanHint}</div>\n <div className=\"inline-block rounded-md bg-white p-2\">\n <img alt=\"QR\" src={`data:image/svg+xml;base64,${setup.qrSvg}`} className=\"h-48 w-48\" />\n </div>\n <div className=\"break-all font-mono text-xs\">{setup.secret}</div>\n <Input\n inputMode=\"numeric\"\n autoComplete=\"one-time-code\"\n readOnly={otpReadonly}\n inputClassName={FIELD_WRITABLE_LOOK}\n onFocus={onOtpFocus}\n maxLength={8}\n label={labels.codeLabel}\n value={code}\n onChange={(e) => setCode(e.target.value)}\n onKeyDown={(e: KeyboardEvent<HTMLInputElement>) => {\n if (e.key === \"Enter\" && code && !busy) {\n e.preventDefault();\n onEnable(code);\n }\n }}\n />\n <Button onClick={() => onEnable(code)} disabled={!code || busy}>\n {labels.verify}\n </Button>\n </div>\n )}\n\n {enabled && (\n <div className=\"space-y-3 border-t border-slate-200 pt-3 dark:border-slate-700\">\n <div className=\"text-sm font-medium\">{labels.disableSection}</div>\n <Input type=\"password\" autoComplete=\"current-password\" label={labels.password} value={password} onChange={(e) => setPassword(e.target.value)} />\n <Input\n inputMode=\"numeric\"\n autoComplete=\"one-time-code\"\n readOnly={otpReadonly}\n inputClassName={FIELD_WRITABLE_LOOK}\n onFocus={onOtpFocus}\n maxLength={8}\n label={labels.codeLabel}\n value={code}\n onChange={(e) => setCode(e.target.value)}\n onKeyDown={(e: KeyboardEvent<HTMLInputElement>) => {\n if (e.key === \"Enter\" && password && code && !busy) {\n e.preventDefault();\n onDisable(password, code);\n }\n }}\n />\n <Button variant=\"danger\" onClick={() => onDisable(password, code)} disabled={!password || !code || busy}>\n {labels.disable}\n </Button>\n </div>\n )}\n </Card>\n );\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/account-settings.tsx"],"sourcesContent":["import { useState } from \"react\";\nimport type { KeyboardEvent, ReactNode } from \"react\";\nimport { Button, Card, FIELD_WRITABLE_LOOK, Input } from \"./ui\";\nimport { UserAvatar } from \"./user-avatar\";\nimport { DEFAULT_COMMON_LABELS, useKitLabels } from \"../i18n/kit-labels\";\n\n/**\n * App-agnostic account-settings SECTIONS (feedback #333). The presentation lives\n * in @hb/ui; each app injects its own auth-API handlers + translated labels and\n * arranges these next to its own app-specific cards (appearance, budgets, …).\n * Every section is a self-contained <Card> owning its transient form state.\n */\n\nexport interface ProfileSettingLabels {\n title: string;\n email: string;\n role: string;\n memberSince: string;\n displayName: string;\n save: string;\n}\n\nexport function ProfileSetting({\n name,\n email,\n role,\n memberSince,\n value,\n onChange,\n onSave,\n saving,\n labels,\n}: {\n name?: string | null;\n email?: string | null;\n role?: ReactNode;\n memberSince?: ReactNode;\n value: string;\n onChange: (value: string) => void;\n onSave: () => void;\n saving?: boolean;\n labels: ProfileSettingLabels;\n}) {\n const dirty = value.trim().length > 0 && value.trim() !== (name ?? \"\");\n return (\n <Card className=\"p-4 space-y-3\">\n <div className=\"flex items-center gap-3\">\n <UserAvatar name={name} email={email} size=\"lg\" />\n <div className=\"min-w-0\">\n <div className=\"text-sm font-medium\">{labels.title}</div>\n <div className=\"truncate font-mono text-xs text-[var(--text-muted)]\">{email ?? \"—\"}</div>\n </div>\n </div>\n <div className=\"grid grid-cols-[max-content_1fr] gap-x-3 gap-y-1 text-sm\">\n <span className=\"text-[var(--text-muted)]\">{labels.role}</span>\n <span>{role ?? \"—\"}</span>\n <span className=\"text-[var(--text-muted)]\">{labels.memberSince}</span>\n <span>{memberSince ?? \"—\"}</span>\n </div>\n <Input\n id=\"display-name\"\n label={labels.displayName}\n value={value}\n onChange={(e) => onChange(e.target.value)}\n maxLength={120}\n />\n <Button onClick={onSave} disabled={!dirty || saving}>\n {labels.save}\n </Button>\n </Card>\n );\n}\n\nexport interface PasswordSettingLabels {\n title: string;\n current: string;\n next: string;\n confirm: string;\n submit: string;\n tooShort: string;\n mismatch: string;\n}\n\nexport function PasswordSetting({\n onSubmit,\n pending,\n minLength = 8,\n labels,\n}: {\n /** Called with the validated (current, new) pair; return a promise to auto-clear on success. */\n onSubmit: (currentPassword: string, newPassword: string) => void | Promise<unknown>;\n pending?: boolean;\n minLength?: number;\n labels: PasswordSettingLabels;\n}) {\n const [current, setCurrent] = useState(\"\");\n const [next, setNext] = useState(\"\");\n const [confirm, setConfirm] = useState(\"\");\n const [error, setError] = useState<string | null>(null);\n\n const submit = async () => {\n if (next.length < minLength) {\n setError(labels.tooShort);\n return;\n }\n if (next !== confirm) {\n setError(labels.mismatch);\n return;\n }\n setError(null);\n try {\n await onSubmit(current, next);\n setCurrent(\"\");\n setNext(\"\");\n setConfirm(\"\");\n } catch {\n // The app surfaces the failure (e.g. a toast); keep the fields for a retry.\n }\n };\n\n return (\n <Card className=\"p-4 space-y-3\">\n <div className=\"text-sm font-medium\">{labels.title}</div>\n <Input type=\"password\" autoComplete=\"current-password\" label={labels.current} value={current} onChange={(e) => setCurrent(e.target.value)} />\n <Input type=\"password\" autoComplete=\"new-password\" label={labels.next} value={next} onChange={(e) => setNext(e.target.value)} />\n <Input type=\"password\" autoComplete=\"new-password\" label={labels.confirm} value={confirm} onChange={(e) => setConfirm(e.target.value)} />\n {error && <p className=\"text-xs text-[var(--danger)]\">{error}</p>}\n <Button onClick={() => void submit()} disabled={!current || !next || !confirm || pending}>\n {labels.submit}\n </Button>\n </Card>\n );\n}\n\nexport interface TwoFactorSettingLabels {\n status: string;\n enabledText: string;\n disabledText: string;\n enable: string;\n scanHint: string;\n codeLabel: string;\n verify: string;\n disableSection: string;\n password: string;\n disable: string;\n /**\n * Alt text for the setup QR image, which shipped as a hardcoded `alt=\"QR\"`.\n *\n * OPTIONAL, alone among these keys, and not because it matters less: this\n * interface is annotated at consumer call sites (`const LABELS:\n * TwoFactorSettingLabels = {…}`), so a new REQUIRED key is a compile error in\n * every app on the next `npm update` — the additive-API rule in the README. It\n * falls back to English here the way `AmountInput`'s label keys do. Make it\n * required in the next major, when the three apps can be updated with it.\n */\n qrAlt?: string;\n}\n\n/** `qrAlt`'s English fallback, kept beside the interface rather than inline so the\n * one English word in this section is findable by the same `DEFAULT_*` grep as\n * every other one. Module-private: the key goes required in the next major. */\nconst DEFAULT_QR_ALT = \"QR code\";\n\nexport function TwoFactorSetting({\n enabled,\n setup,\n onStartSetup,\n onEnable,\n onDisable,\n busy,\n labels,\n}: {\n enabled: boolean;\n /** QR + secret returned by the app's setup call; null before setup starts. */\n setup: { qrSvg: string; secret: string } | null;\n onStartSetup: () => void;\n onEnable: (code: string) => void;\n onDisable: (password: string, code: string) => void;\n busy?: boolean;\n labels: TwoFactorSettingLabels;\n}) {\n const [code, setCode] = useState(\"\");\n const [password, setPassword] = useState(\"\");\n // Browsers autofill one-time-code fields with the saved username; keep the\n // field readOnly until focus to block that. It therefore has to keep the LOOK of\n // an editable field (FIELD_WRITABLE_LOOK): this is the one place where readOnly\n // does not mean \"you may not type here\", and without the opt-out the field would\n // sit there greyed until the moment it is focused.\n const [otpReadonly, setOtpReadonly] = useState(true);\n const onOtpFocus = () => setOtpReadonly(false);\n // Only for the \"Status: Enabled\" composition — the punctuation between a field's\n // name and its value is the language's (see `CommonLabels.fieldValue`).\n const common = useKitLabels(\"common\", DEFAULT_COMMON_LABELS);\n\n return (\n <Card className=\"p-4 space-y-3\">\n <div className=\"text-sm\">\n {common.fieldValue(labels.status, enabled ? labels.enabledText : labels.disabledText)}\n </div>\n\n {!enabled && !setup && (\n <Button onClick={onStartSetup} disabled={busy}>\n {labels.enable}\n </Button>\n )}\n\n {setup && (\n <div className=\"space-y-2\">\n <div className=\"text-xs text-[var(--text-secondary)]\">{labels.scanHint}</div>\n {/* `bg-white`, NOT `--bg-surface`, and it must stay that way: a QR code is\n read by a camera looking for maximum contrast between the modules and\n their quiet zone. On the dark theme a surface-coloured backing makes it\n slow to acquire, and on a warm light preset it lowers the contrast ratio\n the spec is written against. This is the one place in the kit where a\n literal colour is the correct answer. */}\n <div className=\"inline-block rounded-md bg-white p-2\">\n <img\n alt={labels.qrAlt ?? DEFAULT_QR_ALT}\n src={`data:image/svg+xml;base64,${setup.qrSvg}`}\n className=\"h-48 w-48\"\n />\n </div>\n <div className=\"break-all font-mono text-xs\">{setup.secret}</div>\n <Input\n inputMode=\"numeric\"\n autoComplete=\"one-time-code\"\n readOnly={otpReadonly}\n inputClassName={FIELD_WRITABLE_LOOK}\n onFocus={onOtpFocus}\n maxLength={8}\n label={labels.codeLabel}\n value={code}\n onChange={(e) => setCode(e.target.value)}\n onKeyDown={(e: KeyboardEvent<HTMLInputElement>) => {\n if (e.key === \"Enter\" && code && !busy) {\n e.preventDefault();\n onEnable(code);\n }\n }}\n />\n <Button onClick={() => onEnable(code)} disabled={!code || busy}>\n {labels.verify}\n </Button>\n </div>\n )}\n\n {enabled && (\n <div className=\"space-y-3 border-t border-[var(--border)] pt-3\">\n <div className=\"text-sm font-medium\">{labels.disableSection}</div>\n <Input type=\"password\" autoComplete=\"current-password\" label={labels.password} value={password} onChange={(e) => setPassword(e.target.value)} />\n <Input\n inputMode=\"numeric\"\n autoComplete=\"one-time-code\"\n readOnly={otpReadonly}\n inputClassName={FIELD_WRITABLE_LOOK}\n onFocus={onOtpFocus}\n maxLength={8}\n label={labels.codeLabel}\n value={code}\n onChange={(e) => setCode(e.target.value)}\n onKeyDown={(e: KeyboardEvent<HTMLInputElement>) => {\n if (e.key === \"Enter\" && password && code && !busy) {\n e.preventDefault();\n onDisable(password, code);\n }\n }}\n />\n <Button variant=\"danger\" onClick={() => onDisable(password, code)} disabled={!password || !code || busy}>\n {labels.disable}\n </Button>\n </div>\n )}\n </Card>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
|
-
import { ReactNode } from 'react';
|
|
2
|
+
import { ComponentPropsWithoutRef, ReactNode } from 'react';
|
|
3
3
|
|
|
4
4
|
type AlertTone = "danger" | "warning" | "neutral";
|
|
5
5
|
/** The tone's border + surface on their own — for a caller that already has a box
|
|
@@ -10,11 +10,15 @@ declare function toneFrameClass(tone: AlertTone): string;
|
|
|
10
10
|
* and surface plus this component's own radius and padding, the latter
|
|
11
11
|
* compensating the 2px colored border so toggling tones never shifts layout. */
|
|
12
12
|
declare function alertFrameClass(tone: AlertTone): string;
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
interface AlertBannerProps extends ComponentPropsWithoutRef<"div"> {
|
|
14
|
+
/** `neutral` is deliberately not offered: a callout with a warning triangle in it
|
|
15
|
+
* and no colour is a warning that looks like a note, which is the one thing this
|
|
16
|
+
* box must never be. The neutral frame is still available on its own through
|
|
17
|
+
* {@link toneFrameClass}. */
|
|
15
18
|
tone?: Exclude<AlertTone, "neutral">;
|
|
16
|
-
className?: string;
|
|
17
19
|
children: ReactNode;
|
|
18
|
-
}
|
|
20
|
+
}
|
|
21
|
+
/** Static warning/danger callout box with the shared frame and an icon. */
|
|
22
|
+
declare function AlertBanner({ tone, className, children, ...rest }: AlertBannerProps): react.JSX.Element;
|
|
19
23
|
|
|
20
|
-
export { AlertBanner, type AlertTone, alertFrameClass, toneFrameClass };
|
|
24
|
+
export { AlertBanner, type AlertBannerProps, type AlertTone, alertFrameClass, toneFrameClass };
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
"use client";
|
|
1
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
3
|
import { AlertTriangle } from "lucide-react";
|
|
3
4
|
import { cn } from "../lib/cn.js";
|
|
4
5
|
const TONE_FRAME = {
|
|
5
|
-
danger: "border-2 border-
|
|
6
|
-
warning: "border-2 border-
|
|
7
|
-
neutral: "border border-
|
|
6
|
+
danger: "border-2 border-[var(--danger-border)] bg-[var(--danger-bg)]",
|
|
7
|
+
warning: "border-2 border-[var(--warning-border)] bg-[var(--warning-bg)]",
|
|
8
|
+
neutral: "border border-[var(--border)]"
|
|
8
9
|
};
|
|
9
10
|
function toneFrameClass(tone) {
|
|
10
11
|
return TONE_FRAME[tone];
|
|
@@ -14,22 +15,25 @@ function alertFrameClass(tone) {
|
|
|
14
15
|
return `${box} ${TONE_FRAME[tone]}`;
|
|
15
16
|
}
|
|
16
17
|
const TONE_TEXT = {
|
|
17
|
-
danger: "text-
|
|
18
|
-
warning: "text-
|
|
18
|
+
danger: "text-[var(--danger)]",
|
|
19
|
+
warning: "text-[var(--warning)]"
|
|
19
20
|
};
|
|
20
21
|
const TONE_ICON = {
|
|
21
|
-
danger: "text-
|
|
22
|
-
warning: "text-
|
|
22
|
+
danger: "text-[var(--danger)]",
|
|
23
|
+
warning: "text-[var(--warning)]"
|
|
23
24
|
};
|
|
24
|
-
function AlertBanner({
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
25
|
+
function AlertBanner({ tone = "danger", className, children, ...rest }) {
|
|
26
|
+
return /* @__PURE__ */ jsxs(
|
|
27
|
+
"div",
|
|
28
|
+
{
|
|
29
|
+
...rest,
|
|
30
|
+
className: cn("flex items-start gap-2 text-sm", alertFrameClass(tone), TONE_TEXT[tone], className),
|
|
31
|
+
children: [
|
|
32
|
+
/* @__PURE__ */ jsx(AlertTriangle, { className: cn("mt-0.5 size-4 shrink-0", TONE_ICON[tone]) }),
|
|
33
|
+
/* @__PURE__ */ jsx("span", { className: "flex-1", children })
|
|
34
|
+
]
|
|
35
|
+
}
|
|
36
|
+
);
|
|
33
37
|
}
|
|
34
38
|
export {
|
|
35
39
|
AlertBanner,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/alert-banner.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { AlertTriangle } from \"lucide-react\";\nimport { cn } from \"../lib/cn\";\n\nexport type AlertTone = \"danger\" | \"warning\" | \"neutral\";\n\n/** Border + surface per tone, WITHOUT a radius or padding, so a caller that owns\n * its own box (a Card, say) can take the tone alone. Single source for both\n * {@link alertFrameClass} and {@link toneFrameClass}.\n *\n * The colored tones are 2px, not 1px, and that is load-bearing rather than\n * decorative. A 1px border is 1.25 device pixels on a 125%-scaled display — the\n * usual Windows setting — so whether it renders as a solid line or as two\n * half-lit pixels depends on where the box happens to land in device-pixel space.\n * The same card then shows a heavy left edge and a right edge that fades out,\n * which is what feedback #264, #277, #491 and #498 all reported. A 2px border is\n * 2.5 device pixels: it always covers at least two pixels fully, on both edges,\n * at any offset. Callers compensate the extra pixel in their own padding so\n * switching tones never shifts the layout. */\nconst TONE_FRAME: Record<AlertTone, string> = {\n danger: \"border-2 border-
|
|
1
|
+
{"version":3,"sources":["../../src/components/alert-banner.tsx"],"sourcesContent":["import type { ComponentPropsWithoutRef, ReactNode } from \"react\";\nimport { AlertTriangle } from \"lucide-react\";\nimport { cn } from \"../lib/cn\";\n\nexport type AlertTone = \"danger\" | \"warning\" | \"neutral\";\n\n/** Border + surface per tone, WITHOUT a radius or padding, so a caller that owns\n * its own box (a Card, say) can take the tone alone. Single source for both\n * {@link alertFrameClass} and {@link toneFrameClass}.\n *\n * The colored tones are 2px, not 1px, and that is load-bearing rather than\n * decorative. A 1px border is 1.25 device pixels on a 125%-scaled display — the\n * usual Windows setting — so whether it renders as a solid line or as two\n * half-lit pixels depends on where the box happens to land in device-pixel space.\n * The same card then shows a heavy left edge and a right edge that fades out,\n * which is what feedback #264, #277, #491 and #498 all reported. A 2px border is\n * 2.5 device pixels: it always covers at least two pixels fully, on both edges,\n * at any offset. Callers compensate the extra pixel in their own padding so\n * switching tones never shifts the layout. */\nconst TONE_FRAME: Record<AlertTone, string> = {\n danger: \"border-2 border-[var(--danger-border)] bg-[var(--danger-bg)]\",\n warning: \"border-2 border-[var(--warning-border)] bg-[var(--warning-bg)]\",\n neutral: \"border border-[var(--border)]\",\n};\n\n/** The tone's border + surface on their own — for a caller that already has a box\n * with its own radius and padding (e.g. a Card). Remember to shave 1px off that\n * padding for the colored tones, whose border is 2px wide. */\nexport function toneFrameClass(tone: AlertTone): string {\n return TONE_FRAME[tone];\n}\n\n/** Single source for the warning-callout frame (feedback #277): the tone's border\n * and surface plus this component's own radius and padding, the latter\n * compensating the 2px colored border so toggling tones never shifts layout. */\nexport function alertFrameClass(tone: AlertTone): string {\n const box = tone === \"neutral\" ? \"rounded-md p-3\" : \"rounded-md p-[11px]\";\n return `${box} ${TONE_FRAME[tone]}`;\n}\n\nconst TONE_TEXT: Record<Exclude<AlertTone, \"neutral\">, string> = {\n danger: \"text-[var(--danger)]\",\n warning: \"text-[var(--warning)]\",\n};\n\nconst TONE_ICON: Record<Exclude<AlertTone, \"neutral\">, string> = {\n danger: \"text-[var(--danger)]\",\n warning: \"text-[var(--warning)]\",\n};\n\nexport interface AlertBannerProps extends ComponentPropsWithoutRef<\"div\"> {\n /** `neutral` is deliberately not offered: a callout with a warning triangle in it\n * and no colour is a warning that looks like a note, which is the one thing this\n * box must never be. The neutral frame is still available on its own through\n * {@link toneFrameClass}. */\n tone?: Exclude<AlertTone, \"neutral\">;\n children: ReactNode;\n}\n\n/** Static warning/danger callout box with the shared frame and an icon. */\nexport function AlertBanner({ tone = \"danger\", className, children, ...rest }: AlertBannerProps) {\n return (\n <div\n // Arbitrary attributes first — a `data-tour` anchor, a test id, an\n // `aria-describedby` — then the layout and tone classes, which are merged\n // through `cn` rather than spread so a caller's `className` refines the box\n // instead of replacing it.\n {...rest}\n className={cn(\"flex items-start gap-2 text-sm\", alertFrameClass(tone), TONE_TEXT[tone], className)}\n >\n <AlertTriangle className={cn(\"mt-0.5 size-4 shrink-0\", TONE_ICON[tone])} />\n <span className=\"flex-1\">{children}</span>\n </div>\n );\n}\n"],"mappings":";AA8DI,SAQE,KARF;AA7DJ,SAAS,qBAAqB;AAC9B,SAAS,UAAU;AAiBnB,MAAM,aAAwC;AAAA,EAC5C,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,SAAS;AACX;AAKO,SAAS,eAAe,MAAyB;AACtD,SAAO,WAAW,IAAI;AACxB;AAKO,SAAS,gBAAgB,MAAyB;AACvD,QAAM,MAAM,SAAS,YAAY,mBAAmB;AACpD,SAAO,GAAG,GAAG,IAAI,WAAW,IAAI,CAAC;AACnC;AAEA,MAAM,YAA2D;AAAA,EAC/D,QAAQ;AAAA,EACR,SAAS;AACX;AAEA,MAAM,YAA2D;AAAA,EAC/D,QAAQ;AAAA,EACR,SAAS;AACX;AAYO,SAAS,YAAY,EAAE,OAAO,UAAU,WAAW,UAAU,GAAG,KAAK,GAAqB;AAC/F,SACE;AAAA,IAAC;AAAA;AAAA,MAKE,GAAG;AAAA,MACJ,WAAW,GAAG,kCAAkC,gBAAgB,IAAI,GAAG,UAAU,IAAI,GAAG,SAAS;AAAA,MAEjG;AAAA,4BAAC,iBAAc,WAAW,GAAG,0BAA0B,UAAU,IAAI,CAAC,GAAG;AAAA,QACzE,oBAAC,UAAK,WAAU,UAAU,UAAS;AAAA;AAAA;AAAA,EACrC;AAEJ;","names":[]}
|
|
@@ -2,6 +2,24 @@ import * as react from 'react';
|
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
3
|
import { CalculatorButtonLabels } from './calculator.js';
|
|
4
4
|
import { NumberPadSheetLabels } from './numpad-sheet.js';
|
|
5
|
+
import '../i18n/kit-labels.js';
|
|
6
|
+
import './data-table-labels.js';
|
|
7
|
+
import './mini-calendar.js';
|
|
8
|
+
import './popover.js';
|
|
9
|
+
import './chip.js';
|
|
10
|
+
import 'lucide-react';
|
|
11
|
+
import './field-sync.js';
|
|
12
|
+
import './ui.js';
|
|
13
|
+
import '../wizard/types.js';
|
|
14
|
+
import '../tour/tour.js';
|
|
15
|
+
import '../search/command-palette.js';
|
|
16
|
+
import './month-picker.js';
|
|
17
|
+
import './page-contents.js';
|
|
18
|
+
import './series-chart-labels.js';
|
|
19
|
+
import './sparkline.js';
|
|
20
|
+
import './stat-tile.js';
|
|
21
|
+
import './signature-pad.js';
|
|
22
|
+
import './password-strength.js';
|
|
5
23
|
|
|
6
24
|
interface AmountInputProps {
|
|
7
25
|
value: string;
|
|
@@ -17,10 +35,11 @@ interface AmountInputProps {
|
|
|
17
35
|
id?: string;
|
|
18
36
|
ariaLabel?: string;
|
|
19
37
|
/** Every user-facing string this component and the two it composes own, so a
|
|
20
|
-
* translating host can supply its own.
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
38
|
+
* translating host can supply its own. Each key optional, and each a per-field
|
|
39
|
+
* override of the kit-wide translation in `<UiKitProvider labels>`: `currency`
|
|
40
|
+
* and `currencySearch` of its `currency` namespace, the rest of `calculator`.
|
|
41
|
+
* `currency` names the picker chip, composed with the selected code through
|
|
42
|
+
* `common.fieldValue`; the rest are pass-through. */
|
|
24
43
|
labels?: {
|
|
25
44
|
currency?: string;
|
|
26
45
|
currencySearch?: string;
|
|
@@ -30,6 +49,13 @@ interface AmountInputProps {
|
|
|
30
49
|
pad?: NumberPadSheetLabels;
|
|
31
50
|
calculator?: CalculatorButtonLabels;
|
|
32
51
|
};
|
|
52
|
+
/** Translated currency names for the chip's picker, keyed by ISO code. The same
|
|
53
|
+
* sparse override `CurrencySelect` takes, and for the same reason: the names in
|
|
54
|
+
* `CURRENCIES` are shipped data, so without this they are the one English word
|
|
55
|
+
* left in a translated row. Not part of `labels` because it is keyed by DATA
|
|
56
|
+
* rather than by a fixed set of slots, and an app passes the same map to both
|
|
57
|
+
* pickers. */
|
|
58
|
+
currencyNames?: Record<string, string>;
|
|
33
59
|
/** Focus the field on mount — on mobile this also opens the numpad sheet, so a
|
|
34
60
|
* new-transaction form can jump straight to amount entry (feedback #70). */
|
|
35
61
|
autoFocus?: boolean;
|
|
@@ -1,13 +1,20 @@
|
|
|
1
|
+
"use client";
|
|
1
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
3
|
import { forwardRef, useCallback, useId, useMemo, useRef, useState } from "react";
|
|
3
4
|
import { ChevronDown } from "lucide-react";
|
|
4
|
-
import { CURRENCIES, CurrencyFlag, getCurrency } from "./currency-select.js";
|
|
5
|
+
import { CURRENCIES, CurrencyFlag, currencyName, getCurrency } from "./currency-select.js";
|
|
5
6
|
import { FIELD_BASE, FIELD_DISPLAY, FIELD_INVALID, FLOATING_INPUT_CLASS, FLOATING_LABEL_CLASS, PHONE_QUERY } from "./ui.js";
|
|
6
7
|
import { cn } from "../lib/cn.js";
|
|
7
8
|
import { useMediaQuery } from "../hooks/use-media-query.js";
|
|
8
9
|
import { CalculatorButton } from "./calculator.js";
|
|
9
10
|
import { NumberPadSheet } from "./numpad-sheet.js";
|
|
10
11
|
import { DropdownPanel, DropdownSearchHeader, useDropdownSearch } from "./dropdown.js";
|
|
12
|
+
import {
|
|
13
|
+
DEFAULT_COMMON_LABELS,
|
|
14
|
+
DEFAULT_CURRENCY_LABELS,
|
|
15
|
+
useKitLabels,
|
|
16
|
+
useKitLocale
|
|
17
|
+
} from "../i18n/kit-labels.js";
|
|
11
18
|
import {
|
|
12
19
|
commitExpression,
|
|
13
20
|
evaluateExpression,
|
|
@@ -31,7 +38,7 @@ function isResultOf(previous, text) {
|
|
|
31
38
|
return n !== null && formatResult(n) === text.trim();
|
|
32
39
|
}
|
|
33
40
|
const AmountInput = forwardRef(
|
|
34
|
-
({ value, onChange, currency, onCurrencyChange, placeholder, label, disabled, invalid, className, id, ariaLabel, autoFocus, tone = "neutral", negative = false, onNegativeChange, variant = "field", align = "start", labels }, ref) => {
|
|
41
|
+
({ value, onChange, currency, onCurrencyChange, placeholder, label, disabled, invalid, className, id, ariaLabel, autoFocus, tone = "neutral", negative = false, onNegativeChange, variant = "field", align = "start", labels, currencyNames }, ref) => {
|
|
35
42
|
const generatedId = useId();
|
|
36
43
|
const fieldId = id ?? generatedId;
|
|
37
44
|
const editable = !!onCurrencyChange;
|
|
@@ -68,14 +75,25 @@ const AmountInput = forwardRef(
|
|
|
68
75
|
const commit = () => handleText(commitExpression(shown));
|
|
69
76
|
const { open, setOpen, wrapperRef, panelRef, query, setQuery, inputRef } = useDropdownSearch();
|
|
70
77
|
const chipRef = useRef(null);
|
|
78
|
+
const currencyOverrides = useMemo(() => {
|
|
79
|
+
const out = {};
|
|
80
|
+
if (labels?.currency !== void 0) out.currency = labels.currency;
|
|
81
|
+
if (labels?.currencySearch !== void 0) out.search = labels.currencySearch;
|
|
82
|
+
return out;
|
|
83
|
+
}, [labels?.currency, labels?.currencySearch]);
|
|
84
|
+
const currencyText = useKitLabels("currency", DEFAULT_CURRENCY_LABELS, currencyOverrides);
|
|
85
|
+
const common = useKitLabels("common", DEFAULT_COMMON_LABELS);
|
|
86
|
+
const locale = useKitLocale();
|
|
71
87
|
const selected = getCurrency(currency);
|
|
72
88
|
const filtered = useMemo(() => {
|
|
73
|
-
const q = query.trim().
|
|
89
|
+
const q = query.trim().toLocaleLowerCase(locale);
|
|
74
90
|
if (!q) return CURRENCIES;
|
|
75
91
|
return CURRENCIES.filter(
|
|
76
|
-
(c) => c.code.toLowerCase().includes(q) ||
|
|
92
|
+
(c) => c.code.toLowerCase().includes(q) || // The name the row SHOWS, so a translated list is searchable in the
|
|
93
|
+
// language it is written in.
|
|
94
|
+
currencyName(c, currencyNames, locale).toLocaleLowerCase(locale).includes(q) || c.symbol.toLowerCase().includes(q)
|
|
77
95
|
);
|
|
78
|
-
}, [query]);
|
|
96
|
+
}, [query, currencyNames, locale]);
|
|
79
97
|
return /* @__PURE__ */ jsxs("div", { ref: wrapperRef, className: cn("w-full", className), children: [
|
|
80
98
|
/* @__PURE__ */ jsxs("div", { className: "relative w-full", children: [
|
|
81
99
|
/* @__PURE__ */ jsx(
|
|
@@ -155,7 +173,7 @@ const AmountInput = forwardRef(
|
|
|
155
173
|
{
|
|
156
174
|
"aria-hidden": true,
|
|
157
175
|
className: cn(
|
|
158
|
-
"pointer-events-none px-1 font-medium text-
|
|
176
|
+
"pointer-events-none px-1 font-medium text-[var(--text-muted)]",
|
|
159
177
|
// A text-xs chip next to a 36px figure reads as a stray footnote.
|
|
160
178
|
asDisplay ? "text-sm" : "text-xs"
|
|
161
179
|
),
|
|
@@ -168,9 +186,13 @@ const AmountInput = forwardRef(
|
|
|
168
186
|
ref: chipRef,
|
|
169
187
|
type: "button",
|
|
170
188
|
onClick: () => setOpen((v) => !v),
|
|
171
|
-
"aria-label":
|
|
189
|
+
"aria-label": (
|
|
190
|
+
// "Currency: EUR" through `common.fieldValue`, not a template literal:
|
|
191
|
+
// the colon-and-space is punctuation a translation has to own.
|
|
192
|
+
selected ? common.fieldValue(currencyText.currency, selected.code) : currencyText.currency
|
|
193
|
+
),
|
|
172
194
|
className: cn(
|
|
173
|
-
"flex items-center gap-1 rounded px-2 py-1 font-medium text-
|
|
195
|
+
"flex items-center gap-1 rounded px-2 py-1 font-medium text-[var(--text-secondary)] hover:bg-[var(--bg-hover)]",
|
|
174
196
|
asDisplay ? "text-sm" : "text-xs"
|
|
175
197
|
),
|
|
176
198
|
children: [
|
|
@@ -194,7 +216,7 @@ const AmountInput = forwardRef(
|
|
|
194
216
|
query,
|
|
195
217
|
onQueryChange: setQuery,
|
|
196
218
|
inputRef,
|
|
197
|
-
placeholder:
|
|
219
|
+
placeholder: currencyText.search
|
|
198
220
|
}
|
|
199
221
|
),
|
|
200
222
|
children: filtered.map((c) => {
|
|
@@ -208,14 +230,14 @@ const AmountInput = forwardRef(
|
|
|
208
230
|
setOpen(false);
|
|
209
231
|
},
|
|
210
232
|
className: cn(
|
|
211
|
-
"flex w-full items-center gap-2 px-3 py-1.5 text-sm text-left hover:bg-
|
|
212
|
-
active && "bg-
|
|
233
|
+
"flex w-full items-center gap-2 px-3 py-1.5 text-sm text-left hover:bg-[var(--bg-hover)]",
|
|
234
|
+
active && "bg-[var(--bg-active)]"
|
|
213
235
|
),
|
|
214
236
|
children: [
|
|
215
237
|
/* @__PURE__ */ jsx(CurrencyFlag, { country: c.country }),
|
|
216
|
-
/* @__PURE__ */ jsx("span", { className: "font-mono text-
|
|
217
|
-
/* @__PURE__ */ jsx("span", { className: "font-medium text-
|
|
218
|
-
/* @__PURE__ */ jsx("span", { className: "text-
|
|
238
|
+
/* @__PURE__ */ jsx("span", { className: "font-mono text-[var(--text-muted)] w-8 shrink-0 text-xs", children: c.symbol }),
|
|
239
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium text-[var(--text-primary)]", children: c.code }),
|
|
240
|
+
/* @__PURE__ */ jsx("span", { className: "text-[var(--text-muted)] truncate text-xs", children: currencyName(c, currencyNames, locale) })
|
|
219
241
|
]
|
|
220
242
|
}
|
|
221
243
|
) }, c.code);
|