@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
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import { useEffect, useMemo, useRef, useState } from "react";
|
|
2
|
+
import { Calculator as CalculatorIcon, Delete } from "lucide-react";
|
|
3
|
+
import { Popover } from "./popover";
|
|
4
|
+
import { cn } from "../lib/cn";
|
|
5
|
+
import { evaluateExpression, formatResult, isBareAmount, splitLeadingSign } from "../lib/calc";
|
|
6
|
+
import {
|
|
7
|
+
DEFAULT_CALCULATOR_LABELS,
|
|
8
|
+
useKitLabels,
|
|
9
|
+
type CalculatorLabels,
|
|
10
|
+
} from "../i18n/kit-labels";
|
|
11
|
+
|
|
12
|
+
/** Screen-reader names for the calculator panel's own controls — any part of the
|
|
13
|
+
* kit's `calculator` namespace. Once three optional keys of its own; widened, not
|
|
14
|
+
* replaced, so every object that type-checked before still does. */
|
|
15
|
+
export type CalculatorButtonLabels = Partial<CalculatorLabels>;
|
|
16
|
+
|
|
17
|
+
interface CalculatorButtonProps {
|
|
18
|
+
/** Current field text — seeds the keypad so the user can keep calculating
|
|
19
|
+
* from what's already entered. */
|
|
20
|
+
value: string;
|
|
21
|
+
/**
|
|
22
|
+
* Called with the evaluated result whenever the running expression resolves
|
|
23
|
+
* to a number, so the host field tracks the keypad live.
|
|
24
|
+
*
|
|
25
|
+
* `expression` is the TEXT that result came out of — `"-42+50"` for an 8 — and
|
|
26
|
+
* is omitted when there was no calculation (a cleared pad). A host that keeps
|
|
27
|
+
* the sign of its figure OUTSIDE the text (see `AmountInput`'s `negative` /
|
|
28
|
+
* `onNegativeChange`, where a direction toggle owns it) cannot otherwise tell
|
|
29
|
+
* that 8 from a freshly typed one: the first is a sum that came out positive
|
|
30
|
+
* and has to take the direction with it, the second is a magnitude that must
|
|
31
|
+
* leave the direction alone. Hosts whose text carries its own sign can take
|
|
32
|
+
* the one argument and ignore this.
|
|
33
|
+
*/
|
|
34
|
+
onChange: (value: string, expression?: string) => void;
|
|
35
|
+
className?: string;
|
|
36
|
+
/** The trigger's accessible name; wins over `calculator.open` from the provider. */
|
|
37
|
+
ariaLabel?: string;
|
|
38
|
+
/** Screen-reader names for the panel's own controls. `ariaLabel` above already
|
|
39
|
+
* covers the trigger; these three were hardcoded English inside the popover,
|
|
40
|
+
* which a translating host had no way to reach. */
|
|
41
|
+
labels?: CalculatorButtonLabels;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Keypad layout (4 columns). The final C/= row is rendered separately. `name` is
|
|
45
|
+
* the key of the {@link CalculatorLabels} entry that names a non-digit key; a digit
|
|
46
|
+
* is named by its own glyph. */
|
|
47
|
+
type Key =
|
|
48
|
+
| { kind: "ins"; label: string; ins: string; accent?: boolean; name?: KeyName }
|
|
49
|
+
| { kind: "back" };
|
|
50
|
+
|
|
51
|
+
type KeyName = "plus" | "minus" | "times" | "divide" | "decimal";
|
|
52
|
+
|
|
53
|
+
const KEYS: Key[] = [
|
|
54
|
+
{ kind: "ins", label: "7", ins: "7" },
|
|
55
|
+
{ kind: "ins", label: "8", ins: "8" },
|
|
56
|
+
{ kind: "ins", label: "9", ins: "9" },
|
|
57
|
+
{ kind: "ins", label: "÷", ins: "÷", accent: true, name: "divide" },
|
|
58
|
+
{ kind: "ins", label: "4", ins: "4" },
|
|
59
|
+
{ kind: "ins", label: "5", ins: "5" },
|
|
60
|
+
{ kind: "ins", label: "6", ins: "6" },
|
|
61
|
+
{ kind: "ins", label: "×", ins: "×", accent: true, name: "times" },
|
|
62
|
+
{ kind: "ins", label: "1", ins: "1" },
|
|
63
|
+
{ kind: "ins", label: "2", ins: "2" },
|
|
64
|
+
{ kind: "ins", label: "3", ins: "3" },
|
|
65
|
+
{ kind: "ins", label: "−", ins: "−", accent: true, name: "minus" },
|
|
66
|
+
{ kind: "ins", label: "0", ins: "0" },
|
|
67
|
+
{ kind: "ins", label: ".", ins: ".", name: "decimal" },
|
|
68
|
+
{ kind: "back" },
|
|
69
|
+
{ kind: "ins", label: "+", ins: "+", accent: true, name: "plus" },
|
|
70
|
+
];
|
|
71
|
+
|
|
72
|
+
const KEY_BASE =
|
|
73
|
+
"flex h-9 items-center justify-center rounded-md text-sm font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-[var(--border-strong)]";
|
|
74
|
+
// Two key families, two fills: digits sit on the inset surface and darken on
|
|
75
|
+
// hover, operators sit a step down on `--border` and lift toward that surface —
|
|
76
|
+
// the same split `NumberPadSheet` uses for this keypad on a phone. The tokens
|
|
77
|
+
// flip with the theme, so neither needs a `dark:` twin.
|
|
78
|
+
const KEY_DIGIT =
|
|
79
|
+
"bg-[var(--bg-surface-2)] text-[var(--text-primary)] hover:bg-[var(--bg-hover)]";
|
|
80
|
+
const KEY_ACCENT =
|
|
81
|
+
"bg-[var(--border)] text-[var(--text-secondary)] hover:bg-[var(--bg-surface-2)]";
|
|
82
|
+
|
|
83
|
+
function seed(initial: string): string {
|
|
84
|
+
const t = initial.trim();
|
|
85
|
+
return t === "-" ? "" : t;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function CalculatorPanel({
|
|
89
|
+
initial,
|
|
90
|
+
onChange,
|
|
91
|
+
labels,
|
|
92
|
+
}: {
|
|
93
|
+
initial: string;
|
|
94
|
+
onChange: (value: string, expression?: string) => void;
|
|
95
|
+
labels: CalculatorLabels;
|
|
96
|
+
}) {
|
|
97
|
+
const [expr, setExpr] = useState(() => seed(initial));
|
|
98
|
+
const inputRef = useRef<HTMLInputElement>(null);
|
|
99
|
+
// What the pad SHOWS. Normally the expression it holds — but when the host owns
|
|
100
|
+
// the sign of the figure, the magnitude it hands back is not the whole number:
|
|
101
|
+
// type 50 into a pad opened on an outflow and the host answers "-50". Derived
|
|
102
|
+
// rather than stored, so there is no echo to guard against and a host that
|
|
103
|
+
// ignores `onChange` entirely (a static `value`) still sees its own keystrokes:
|
|
104
|
+
// the pad only defers when the host's text is character-for-character its own
|
|
105
|
+
// last number with a minus in front.
|
|
106
|
+
const host = splitLeadingSign(initial.trim());
|
|
107
|
+
const text = host.sign === "-" && isBareAmount(expr) && host.rest === expr.trim() ? initial.trim() : expr;
|
|
108
|
+
const result = evaluateExpression(text);
|
|
109
|
+
|
|
110
|
+
// Focus the display on open so the user can type or hit Enter immediately.
|
|
111
|
+
useEffect(() => {
|
|
112
|
+
inputRef.current?.focus();
|
|
113
|
+
}, []);
|
|
114
|
+
|
|
115
|
+
// Update the host field whenever the expression resolves to a number; an
|
|
116
|
+
// incomplete expression (e.g. "12+") simply leaves the field untouched. The
|
|
117
|
+
// expression rides along with the result — see `onChange` above.
|
|
118
|
+
const apply = (next: string) => {
|
|
119
|
+
setExpr(next);
|
|
120
|
+
const n = evaluateExpression(next);
|
|
121
|
+
if (n !== null) onChange(formatResult(n), next);
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
const clearAll = () => {
|
|
125
|
+
setExpr("");
|
|
126
|
+
onChange("");
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const equals = () => {
|
|
130
|
+
if (result === null) return;
|
|
131
|
+
const out = formatResult(result);
|
|
132
|
+
setExpr(out);
|
|
133
|
+
onChange(out, text);
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
return (
|
|
137
|
+
<div className="space-y-2">
|
|
138
|
+
<input
|
|
139
|
+
ref={inputRef}
|
|
140
|
+
aria-label={labels.calculation}
|
|
141
|
+
value={text}
|
|
142
|
+
inputMode="decimal"
|
|
143
|
+
onChange={(e) => apply(e.target.value)}
|
|
144
|
+
onKeyDown={(e) => {
|
|
145
|
+
if (e.key === "Enter") {
|
|
146
|
+
e.preventDefault();
|
|
147
|
+
equals();
|
|
148
|
+
}
|
|
149
|
+
}}
|
|
150
|
+
className="block w-full rounded-md border border-[var(--border)] bg-[var(--bg-surface)] px-2 py-1.5 text-right font-mono text-sm text-[var(--text-primary)] focus:border-[var(--border-strong)] focus:ring-[var(--border-strong)]"
|
|
151
|
+
/>
|
|
152
|
+
<div className="h-4 pr-1 text-right font-mono text-xs text-[var(--text-placeholder)]">
|
|
153
|
+
{result !== null && formatResult(result) !== text.trim() ? `= ${formatResult(result)}` : ""}
|
|
154
|
+
</div>
|
|
155
|
+
<div className="grid grid-cols-4 gap-1.5">
|
|
156
|
+
{KEYS.map((key, i) =>
|
|
157
|
+
key.kind === "back" ? (
|
|
158
|
+
<button
|
|
159
|
+
key="back"
|
|
160
|
+
type="button"
|
|
161
|
+
aria-label={labels.backspace}
|
|
162
|
+
onClick={() => apply(text.slice(0, -1))}
|
|
163
|
+
className={cn(KEY_BASE, KEY_ACCENT)}
|
|
164
|
+
>
|
|
165
|
+
<Delete className="size-4" />
|
|
166
|
+
</button>
|
|
167
|
+
) : (
|
|
168
|
+
<button
|
|
169
|
+
key={`${key.ins}-${i}`}
|
|
170
|
+
type="button"
|
|
171
|
+
aria-label={key.name ? labels[key.name] : undefined}
|
|
172
|
+
onClick={() => apply(text + key.ins)}
|
|
173
|
+
className={cn(KEY_BASE, key.accent ? KEY_ACCENT : KEY_DIGIT)}
|
|
174
|
+
>
|
|
175
|
+
{key.label}
|
|
176
|
+
</button>
|
|
177
|
+
),
|
|
178
|
+
)}
|
|
179
|
+
<button
|
|
180
|
+
type="button"
|
|
181
|
+
// "C" is a convention, not a name: read aloud it is the letter.
|
|
182
|
+
aria-label={labels.clear}
|
|
183
|
+
onClick={clearAll}
|
|
184
|
+
className={cn(KEY_BASE, "col-span-2 bg-[var(--border)] text-[var(--text-secondary)] hover:bg-[var(--bg-surface-2)]")}
|
|
185
|
+
>
|
|
186
|
+
C
|
|
187
|
+
</button>
|
|
188
|
+
<button
|
|
189
|
+
type="button"
|
|
190
|
+
aria-label={labels.equals}
|
|
191
|
+
onClick={equals}
|
|
192
|
+
className={cn(
|
|
193
|
+
KEY_BASE,
|
|
194
|
+
// The one chip that inverts against the page. There is no hover token for
|
|
195
|
+
// an inverted fill, so the hover is the chip mixed back toward the
|
|
196
|
+
// surface — a step quieter in either theme, and still a colour, so
|
|
197
|
+
// KEY_BASE's `transition-colors` carries it.
|
|
198
|
+
"col-span-2 bg-[var(--bg-inverse)] text-[var(--text-inverse)] hover:bg-[color-mix(in_oklab,var(--bg-inverse)_88%,var(--bg-surface))]",
|
|
199
|
+
)}
|
|
200
|
+
>
|
|
201
|
+
=
|
|
202
|
+
</button>
|
|
203
|
+
</div>
|
|
204
|
+
</div>
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* A calculator icon that opens a small keypad popover anchored to its host
|
|
210
|
+
* field. Designed as a trailing-edge adornment: render it inside the field's
|
|
211
|
+
* `relative` wrapper, positioned absolutely. The keypad seeds from the current
|
|
212
|
+
* field value and writes evaluated results straight back through `onChange`.
|
|
213
|
+
*/
|
|
214
|
+
export function CalculatorButton({
|
|
215
|
+
value,
|
|
216
|
+
onChange,
|
|
217
|
+
className,
|
|
218
|
+
ariaLabel,
|
|
219
|
+
labels: labelsProp,
|
|
220
|
+
}: CalculatorButtonProps) {
|
|
221
|
+
// prop > provider > English, with the older `ariaLabel` prop folded in as the
|
|
222
|
+
// `open` key it has always been.
|
|
223
|
+
const fromProps = useMemo(
|
|
224
|
+
() => (ariaLabel === undefined ? labelsProp : { ...labelsProp, open: ariaLabel }),
|
|
225
|
+
[ariaLabel, labelsProp],
|
|
226
|
+
);
|
|
227
|
+
const labels = useKitLabels("calculator", DEFAULT_CALCULATOR_LABELS, fromProps);
|
|
228
|
+
return (
|
|
229
|
+
<Popover
|
|
230
|
+
width={224}
|
|
231
|
+
labels={{ panel: labels.panel }}
|
|
232
|
+
trigger={({ open, toggle, ref }) => (
|
|
233
|
+
<button
|
|
234
|
+
ref={ref}
|
|
235
|
+
type="button"
|
|
236
|
+
tabIndex={-1}
|
|
237
|
+
onClick={toggle}
|
|
238
|
+
aria-label={labels.open}
|
|
239
|
+
aria-expanded={open}
|
|
240
|
+
className={cn(
|
|
241
|
+
"flex items-center justify-center text-[var(--text-placeholder)] transition-colors hover:text-[var(--text-secondary)]",
|
|
242
|
+
className,
|
|
243
|
+
)}
|
|
244
|
+
>
|
|
245
|
+
<CalculatorIcon className="size-4" />
|
|
246
|
+
</button>
|
|
247
|
+
)}
|
|
248
|
+
>
|
|
249
|
+
{() => <CalculatorPanel initial={value} onChange={onChange} labels={labels} />}
|
|
250
|
+
</Popover>
|
|
251
|
+
);
|
|
252
|
+
}
|