@eifi1/ui-kit 0.14.1 → 0.14.2
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/README.md +4 -3
- package/dist/components/amount-input.d.ts +8 -0
- package/dist/components/amount-input.js +10 -4
- package/dist/components/amount-input.js.map +1 -1
- package/dist/components/error-boundary.js +2 -1
- package/dist/components/error-boundary.js.map +1 -1
- package/dist/components/money-field.d.ts +92 -0
- package/dist/components/money-field.js +40 -0
- package/dist/components/money-field.js.map +1 -0
- package/dist/components/number-field.d.ts +12 -1
- package/dist/components/number-field.js +4 -0
- package/dist/components/number-field.js.map +1 -1
- package/dist/components/page-header.js +29 -3
- package/dist/components/page-header.js.map +1 -1
- package/dist/components/text-link.js +2 -1
- package/dist/components/text-link.js.map +1 -1
- package/dist/feedback/feedback-dialog.d.ts +10 -1
- package/dist/feedback/feedback-dialog.js +13 -3
- package/dist/feedback/feedback-dialog.js.map +1 -1
- package/dist/i18n/locales/de.js +1 -0
- package/dist/i18n/locales/de.js.map +1 -1
- package/dist/i18n/locales/es.js +1 -0
- package/dist/i18n/locales/es.js.map +1 -1
- package/dist/i18n/locales/fr.js +1 -0
- package/dist/i18n/locales/fr.js.map +1 -1
- package/dist/i18n/locales/hu.js +1 -0
- package/dist/i18n/locales/hu.js.map +1 -1
- package/dist/i18n/locales/it.js +1 -0
- package/dist/i18n/locales/it.js.map +1 -1
- package/dist/i18n/locales/zh.js +1 -0
- package/dist/i18n/locales/zh.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/document-navigation.d.ts +15 -0
- package/dist/lib/document-navigation.js +13 -0
- package/dist/lib/document-navigation.js.map +1 -0
- package/package.json +7 -4
- package/src/components/amount-input.tsx +18 -4
- package/src/components/error-boundary.tsx +2 -1
- package/src/components/money-field.tsx +75 -0
- package/src/components/number-field.tsx +14 -0
- package/src/components/page-header.tsx +38 -3
- package/src/components/text-link.tsx +2 -1
- package/src/feedback/feedback-dialog.tsx +20 -2
- package/src/i18n/locales/de.ts +1 -0
- package/src/i18n/locales/es.ts +1 -0
- package/src/i18n/locales/fr.ts +1 -0
- package/src/i18n/locales/hu.ts +1 -0
- package/src/i18n/locales/it.ts +1 -0
- package/src/i18n/locales/zh.ts +1 -0
- package/src/index.ts +1 -0
- package/src/lib/document-navigation.ts +17 -0
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["// @eifi1/ui-kit — shared design-system barrel.\n//\n// Domain-free UI + theme surface shared across apps (lead app: Keksdose). Consumers import\n// components/theme from \"@eifi1/ui-kit\" and the token stylesheet from \"@eifi1/ui-kit/tokens.css\".\n\n// ── lib ────────────────────────────────────────────────────────────────────\nexport * from \"./lib/calc\";\nexport { cn } from \"./lib/cn\";\nexport { logger, setStoreLog } from \"./lib/logger\";\n// Intl number / money / percent / date / relative-time formatting, and `useKitFormat()`\n// for the same bound to the provider's locale (kastlan's formatters.ts, English \"5m ago\").\nexport * from \"./lib/format\";\n// localStorage that never throws (Safari private mode, blocked site data).\nexport { readStored, writeStored } from \"./lib/safe-storage\";\n// Date helpers are also available via the \"@eifi1/ui-kit/dates\" subpath.\n\n// ── hooks ────────────────────────────────────────────────────────────────────\nexport { useMediaQuery } from \"./hooks/use-media-query\";\nexport { useBodyScrollLock } from \"./hooks/use-body-scroll-lock\";\nexport { useAnchoredRect } from \"./hooks/use-anchored-rect\";\nexport type { AnchorRect } from \"./hooks/use-anchored-rect\";\nexport { useAnchoredPanel, anchoredPanelPlacement, useVisualViewport } from \"./hooks/use-anchored-panel\";\nexport type {\n AnchoredPanel,\n AnchoredPanelOptions,\n ViewportBox,\n} from \"./hooks/use-anchored-panel\";\nexport { useEscapeKey, useOutsideClick } from \"./hooks/use-dismiss\";\n// Focus containment for an overlay, and a live region for a change that moves no focus.\n// Both were extracted from the two components that already did them correctly (Modal and\n// CommandPalette) so the rest of the kit stops being the exception.\nexport { useFocusTrap } from \"./hooks/use-focus-trap\";\nexport type { FocusTrapOptions } from \"./hooks/use-focus-trap\";\nexport { useAnnounce } from \"./hooks/use-announce\";\nexport type { UseAnnounceReturn, UseAnnounceOptions, AnnounceRegionProps } from \"./hooks/use-announce\";\nexport { useOverlayHistory } from \"./hooks/use-overlay-history\";\nexport { useCloseTransition, OVERLAY_EXIT_MS } from \"./hooks/use-close-transition\";\nexport { useRowSwipe } from \"./hooks/use-row-swipe\";\nexport type { SwipeStage, RowSwipeOptions, RowSwipeReturn } from \"./hooks/use-row-swipe\";\n// Any element as a file drop target, screened like FileButton (kastlan).\nexport { useFileDrop, dragHasFiles } from \"./hooks/use-file-drop\";\nexport type { UseFileDropOptions, UseFileDropReturn, FileDropProps } from \"./hooks/use-file-drop\";\n// kastlan's search boxes each carried a hand-rolled copy, two without the cleanup.\nexport { useDebounce, useDebouncedCallback } from \"./hooks/use-debounce\";\nexport type { DebouncedCallbackOptions, DebouncedFunction } from \"./hooks/use-debounce\";\n// One search param as state, the active tab in `?tab=`, and a dialog's open state in the\n// URL (Modal's `urlParam`) — kastlan's and keksdose's use-search-param-state, in the kit.\nexport { useSearchParamState, useTabParam, useDialogParam } from \"./hooks/use-search-param-state\";\nexport type { SearchParamStateOptions, DialogParam } from \"./hooks/use-search-param-state\";\n// Copy that reports whether it worked — keksdose's copy buttons said \"Copied\" when it had not.\nexport { useCopyToClipboard, copyToClipboard } from \"./hooks/use-copy-to-clipboard\";\nexport type { CopyState, UseCopyToClipboardOptions, UseCopyToClipboardReturn } from \"./hooks/use-copy-to-clipboard\";\n// Keyboard shortcuts (\"Mod+K\", \"Ctrl+Shift+F\") that leave typing in fields alone.\nexport { useHotkey, parseHotkey, matchesHotkey } from \"./hooks/use-hotkey\";\nexport type { Hotkey, ParsedHotkey, UseHotkeyOptions } from \"./hooks/use-hotkey\";\n// A protected file as an object URL, fetched with the app's own auth (kastlan's AuthedImage).\nexport { useAuthedSrc } from \"./hooks/use-authed-src\";\nexport type { AuthedFetcher, AuthedSrcStatus, UseAuthedSrcOptions, UseAuthedSrcResult } from \"./hooks/use-authed-src\";\n\n// ── theme / palettes ─────────────────────────────────────────────────────────\nexport * from \"./theme/chart-palette\";\n// Colour maths (sRGB ↔ OKLCH, WCAG contrast, CVD simulation) and the palette deriver\n// that builds a whole, contrast-solved palette from one brand colour plus any colours\n// pinned by hand. `auditPalette` / `auditChartRamp` measure a palette rather than\n// trusting a comment about it.\nexport * from \"./theme/color\";\nexport * from \"./theme/palette-derive\";\nexport * from \"./theme/palette-presets\";\nexport * from \"./theme/theme-store\";\nexport * from \"./theme/palette-store\";\n\n// ── components ───────────────────────────────────────────────────────────────\nexport * from \"./components/ui\";\n// The label-ABOVE field group (label, hint, error, required), inside wizards or out.\nexport * from \"./components/field\";\nexport * from \"./components/search-field\";\nexport * from \"./components/dropdown\";\nexport * from \"./components/popover\";\nexport { SwipeableRow } from \"./components/swipeable-row\";\nexport type { SwipeAction, SwipeableRowProps } from \"./components/swipeable-row\";\nexport * from \"./components/calculator\";\nexport * from \"./components/numpad-sheet\";\nexport * from \"./components/number-input\";\nexport * from \"./components/currency-select\";\nexport * from \"./components/amount-input\";\nexport * from \"./components/combobox\";\nexport * from \"./components/picker-sheet\";\nexport * from \"./components/entity-combobox\";\nexport * from \"./components/multi-entity-combobox\";\nexport * from \"./components/multi-select\";\nexport * from \"./components/tooltip\";\nexport * from \"./components/user-avatar\";\nexport * from \"./components/settings-fields\";\n// Sync state for a database-backed field: the engine (`useFieldSync`) and the\n// affordance (`FieldSyncIndicator`), kept separate because the state is useful\n// without the dot — a form can gate its submit on any field being `pending`.\nexport * from \"./components/field-sync\";\nexport * from \"./components/month-picker\";\nexport * from \"./components/checkbox\";\nexport * from \"./components/switch\";\nexport * from \"./components/slider\";\nexport * from \"./components/time-input\";\nexport * from \"./components/number-field\";\nexport * from \"./components/sparkline\";\nexport * from \"./components/stat-tile\";\nexport * from \"./components/signature-pad\";\nexport * from \"./components/password-strength\";\nexport * from \"./components/page-contents\";\nexport * from \"./components/disclosure\";\nexport * from \"./components/dialog-frame\";\nexport * from \"./components/danger-confirm\";\n// `useConfirm()` — the promise-based replacement for `window.confirm`, one host per app.\nexport * from \"./components/confirm-dialog\";\n// A non-modal corner panel and its round trigger (keksdose's assistant launcher).\nexport * from \"./components/floating-panel\";\n// The count / clear / actions bar a selection brings up (keksdose's three copies).\nexport * from \"./components/bulk-action-bar\";\nexport * from \"./components/swatch-picker\";\nexport * from \"./components/icon-picker\";\nexport * from \"./components/choice-card\";\nexport * from \"./components/autocomplete\";\nexport * from \"./components/measured-grid\";\nexport { useWindowedRows } from \"./hooks/use-windowed-rows\";\nexport type { WindowedRows } from \"./hooks/use-windowed-rows\";\n// Named, not `export *`: file-button.tsx also holds the screening helpers the\n// dropzone shares, which are internal.\nexport { FileButton, useFilePicker, matchesAccept, DEFAULT_FILE_PICKER_LABELS } from \"./components/file-button\";\nexport type {\n FileButtonProps,\n UseFilePickerOptions,\n UseFilePickerReturn,\n FilePickerLabels,\n FileRejection,\n FileRejectionReason,\n FileScreenOptions,\n} from \"./components/file-button\";\nexport * from \"./components/treemap\";\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\";\nexport * from \"./components/pie-chart\";\nexport * from \"./components/pie-chart-labels\";\nexport * from \"./components/account-settings\";\n// 0.12.0: the fourth account section, and the QR code TwoFactorSetting draws its\n// otpauth URI with (encoder in lib/qr-encode.ts — no dependency, see there).\nexport * from \"./components/passkeys-setting\";\nexport * from \"./components/qr-code\";\nexport { encodeQr } from \"./lib/qr-encode\";\nexport type { QrEncodeOptions, QrErrorCorrection, QrMatrix } from \"./lib/qr-encode\";\nexport * from \"./components/alert-banner\";\nexport * from \"./components/toggle-group\";\n// A wrapping set of links/buttons marked `aria-current` — Tabs-like looks, not tabs.\nexport * from \"./components/nav-pills\";\n// A pill carrying one VALUE — inert, a link, or a toggle, depending on which prop it is\n// given — and the list field built from it. Distinct from Button on purpose: a row of\n// buttons reads as \"choose an action\", a row of chips as \"here are the things\".\nexport * from \"./components/chip\";\nexport * from \"./components/wizard-stepper\";\nexport * from \"./components/hover-menu\";\nexport * from \"./components/modal\";\nexport * from \"./components/full-bleed-dialog\";\nexport * from \"./components/grouped-picker\";\nexport * from \"./components/file-dropzone\";\nexport * from \"./components/mini-calendar\";\nexport * from \"./components/calendar-heatmap\";\nexport * from \"./components/date-picker\";\nexport * from \"./components/tree-view\";\nexport * from \"./components/chart\";\n// 0.8.0 layout and feedback primitives the apps hand-rolled or took from shadcn/Radix.\nexport * from \"./components/description-list\";\n// The Save / Cancel row forms and dialogs end in, and the editable line-item repeater\n// (journal lines, lease components, invoice positions). 0.12.0.\nexport * from \"./components/form-actions\";\nexport * from \"./components/line-items\";\nexport * from \"./components/progress-bar\";\n// Named: skeleton.tsx also holds SKELETON_CLASS, the look StatTile shares — internal.\nexport { Skeleton } from \"./components/skeleton\";\nexport type { SkeletonProps, SkeletonShape } from \"./components/skeleton\";\n// 0.12.0: a centred spinner with words, signed figures and deltas, a render-error fallback.\nexport * from \"./components/loading-state\";\nexport * from \"./components/signed-amount\";\nexport * from \"./components/error-boundary\";\nexport * from \"./components/copy-button\";\nexport { AuthedImage, DEFAULT_AUTHED_IMAGE_LABELS } from \"./components/authed-image\";\nexport type { AuthedImageProps, AuthedImageLabels } from \"./components/authed-image\";\nexport * from \"./components/image-grid\";\nexport * from \"./components/lightbox\";\nexport * from \"./components/button-group\";\nexport * from \"./components/table\";\nexport * from \"./components/separator\";\n// Named: scroll-area.tsx also holds the overflow hook and scrollbar class Table shares.\nexport { ScrollArea } from \"./components/scroll-area\";\nexport type { ScrollAreaProps } from \"./components/scroll-area\";\n// 0.10.0 rows, menu rows, text primitives and page chrome the apps drew by hand.\nexport * from \"./components/list\";\nexport * from \"./components/menu-item\";\nexport * from \"./components/text\";\nexport * from \"./components/status-dot\";\nexport * from \"./components/page-header\";\nexport * from \"./components/breadcrumbs\";\n// The one inline link (kastlan's EntityLink / CellLink / legal and source links), through\n// `<UiKitProvider linkComponent>`. Named: the file also holds the kit's internal link picker.\nexport { TextLink } from \"./components/text-link\";\nexport type {\n TextLinkProps,\n TextLinkRenderProps,\n TextLinkTone,\n TextLinkUnderline,\n TextLinkCurrent,\n} from \"./components/text-link\";\n// The toast layer over sonner (an OPTIONAL peer, loaded lazily): `toast` mirrors sonner's\n// API so apps migrate by swapping the import; `<Toaster>` carries the placement, theme,\n// tones and z-index both apps had wired by hand.\nexport { toast, Toaster, DEFAULT_TOAST_LABELS, TOAST_ACTION_DURATION } from \"./components/toast\";\nexport type {\n ToastId,\n ToastAction,\n ToastOptions,\n ToastPromiseOptions,\n ToastUndoOptions,\n ToastRedoOptions,\n ToastLabels,\n ToastPosition,\n ToastSwipeDirection,\n ToasterOffset,\n ToasterProps,\n} from \"./components/toast\";\n\n// ── data-table suite ─────────────────────────────────────────────────────────\nexport * from \"./components/data-table-labels\";\nexport * from \"./components/data-table-sort\";\nexport * from \"./components/data-table-filters\";\nexport * from \"./components/data-table-pagination\";\nexport { FilterPopover } from \"./components/data-table-filter-popover\";\n// data-table re-exports SortState internally; export the rest explicitly to\n// avoid a duplicate SortState star-export (it comes from data-table-sort).\nexport { DataTable } from \"./components/data-table\";\nexport type {\n DataTableColumn,\n DataTableCellProps,\n DataTableHeadProps,\n DataTableProps,\n ServerPagination,\n FilterState,\n MobileSwipeActions,\n DataTableDensity,\n DataTableChrome,\n DataTableRowAction,\n} from \"./components/data-table\";\n// 0.12.0: the yes/no cell, and a server table's state held by its owner, URL-synced.\nexport * from \"./components/data-table-cells\";\nexport { useTableUrlState, readTableUrlState } from \"./components/use-table-state\";\nexport type {\n TableUrlState,\n TableUrlSync,\n TableUrlStateProps,\n UseTableUrlStateOptions,\n UseTableUrlStateReturn,\n} from \"./components/use-table-state\";\n\n// ── shell (composable app chrome) ────────────────────────────────────────────\nexport * from \"./shell/topbar-controls\";\nexport * from \"./shell/top-bar\";\nexport * from \"./shell/app-shell\";\nexport * from \"./shell/option-switcher-menu\";\nexport * from \"./shell/role-switcher\";\nexport * from \"./shell/topbar-action-menu\";\nexport * from \"./shell/top-bar-brand\";\nexport * from \"./shell/auth-layout\";\n\n// ── feedback ─────────────────────────────────────────────────────────────────\n// The form somebody files a report with, and the parts an inbox is built from:\n// the status vocabulary both apps share value for value, the policy about what a\n// row may move to from where it stands, and the look of a status control, a\n// category badge and an opened report. The app still wires its own API, columns\n// and strings — see the note at the top of `feedback-inbox.tsx` for what is\n// deliberately left to it.\nexport * from \"./feedback/feedback-attachment\";\nexport * from \"./feedback/feedback-dialog\";\nexport * from \"./feedback/feedback-inbox\";\nexport * from \"./feedback/feedback-thread\";\n\n// ── multi-step wizard ────────────────────────────────────────────────────────\n// The engine (step machine, validators, collected data), the chrome around it\n// (indicator + nav bar + cancel confirm) and the review step it ends on.\n// `components/wizard-stepper` next door is a different, much smaller thing: a bare\n// step indicator for a two-step import flow, with no engine.\n//\n// Two modules were removed here rather than kept as stock. `wizard-field` held a\n// `WizardField`/`WizardSelectField` pair that had never been rendered in any app —\n// a field group nothing was built from, which this comment used to advertise. And\n// `use-rhf-wizard-step` was the only thing in the package that touched\n// react-hook-form, for a hook neither consumer called: keeping it meant an optional\n// peer, a dev dependency and a type import in every consumer's typecheck, bought\n// for nobody. A design system may carry stock; untested stock nothing has ever\n// rendered is not stock, it is a liability with an export.\nexport * from \"./wizard/types\";\nexport * from \"./wizard/use-wizard\";\nexport * from \"./wizard/wizard-context\";\nexport * from \"./wizard/validation\";\nexport * from \"./wizard/wizard-step\";\nexport * from \"./wizard/wizard-summary\";\nexport * from \"./wizard/stepper-nav\";\n\n// ── guided tours ─────────────────────────────────────────────────────────────\nexport * from \"./tour/tour\";\n\n// ── command palette / global search ──────────────────────────────────────────\nexport * from \"./search/command-palette\";\nexport * from \"./search/search-index\";\nexport * from \"./search/global-search\";\n\n// ── i18n: one label tree, one optional provider ──────────────────────────────\n// `UiKitLabels` names every string the kit renders; `<UiKitProvider labels locale>`\n// hands a translation to every component below it. Precedence: a component's own\n// prop > the provider > the English default. See src/i18n/kit-labels.tsx.\nexport * from \"./i18n/kit-labels\";\nexport * from \"./i18n/defaults\";\n"],"mappings":";AAMA,cAAc;AACd,SAAS,UAAU;AACnB,SAAS,QAAQ,mBAAmB;AAGpC,cAAc;AAEd,SAAS,YAAY,mBAAmB;AAIxC,SAAS,qBAAqB;AAC9B,SAAS,yBAAyB;AAClC,SAAS,uBAAuB;AAEhC,SAAS,kBAAkB,wBAAwB,yBAAyB;AAM5E,SAAS,cAAc,uBAAuB;AAI9C,SAAS,oBAAoB;AAE7B,SAAS,mBAAmB;AAE5B,SAAS,yBAAyB;AAClC,SAAS,oBAAoB,uBAAuB;AACpD,SAAS,mBAAmB;AAG5B,SAAS,aAAa,oBAAoB;AAG1C,SAAS,aAAa,4BAA4B;AAIlD,SAAS,qBAAqB,aAAa,sBAAsB;AAGjE,SAAS,oBAAoB,uBAAuB;AAGpD,SAAS,WAAW,aAAa,qBAAqB;AAGtD,SAAS,oBAAoB;AAI7B,cAAc;AAKd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AAGd,cAAc;AAEd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,SAAS,oBAAoB;AAE7B,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AAId,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AAEd,cAAc;AAEd,cAAc;AAEd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,SAAS,uBAAuB;AAIhC,SAAS,YAAY,eAAe,eAAe,kCAAkC;AAUrF,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AAGd,cAAc;AACd,cAAc;AACd,SAAS,gBAAgB;AAEzB,cAAc;AACd,cAAc;AAEd,cAAc;AAId,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AAEd,cAAc;AAGd,cAAc;AACd,cAAc;AACd,cAAc;AAEd,SAAS,gBAAgB;AAGzB,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,SAAS,aAAa,mCAAmC;AAEzD,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AAEd,SAAS,kBAAkB;AAG3B,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AAGd,SAAS,gBAAgB;AAWzB,SAAS,OAAO,SAAS,sBAAsB,6BAA6B;AAgB5E,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,SAAS,qBAAqB;AAG9B,SAAS,iBAAiB;AAc1B,cAAc;AACd,SAAS,kBAAkB,yBAAyB;AAUpD,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AASd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AAgBd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AAGd,cAAc;AAGd,cAAc;AACd,cAAc;AACd,cAAc;AAMd,cAAc;AACd,cAAc;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["// @eifi1/ui-kit — shared design-system barrel.\n//\n// Domain-free UI + theme surface shared across apps (lead app: Keksdose). Consumers import\n// components/theme from \"@eifi1/ui-kit\" and the token stylesheet from \"@eifi1/ui-kit/tokens.css\".\n\n// ── lib ────────────────────────────────────────────────────────────────────\nexport * from \"./lib/calc\";\nexport { cn } from \"./lib/cn\";\nexport { logger, setStoreLog } from \"./lib/logger\";\n// Intl number / money / percent / date / relative-time formatting, and `useKitFormat()`\n// for the same bound to the provider's locale (kastlan's formatters.ts, English \"5m ago\").\nexport * from \"./lib/format\";\n// localStorage that never throws (Safari private mode, blocked site data).\nexport { readStored, writeStored } from \"./lib/safe-storage\";\n// Date helpers are also available via the \"@eifi1/ui-kit/dates\" subpath.\n\n// ── hooks ────────────────────────────────────────────────────────────────────\nexport { useMediaQuery } from \"./hooks/use-media-query\";\nexport { useBodyScrollLock } from \"./hooks/use-body-scroll-lock\";\nexport { useAnchoredRect } from \"./hooks/use-anchored-rect\";\nexport type { AnchorRect } from \"./hooks/use-anchored-rect\";\nexport { useAnchoredPanel, anchoredPanelPlacement, useVisualViewport } from \"./hooks/use-anchored-panel\";\nexport type {\n AnchoredPanel,\n AnchoredPanelOptions,\n ViewportBox,\n} from \"./hooks/use-anchored-panel\";\nexport { useEscapeKey, useOutsideClick } from \"./hooks/use-dismiss\";\n// Focus containment for an overlay, and a live region for a change that moves no focus.\n// Both were extracted from the two components that already did them correctly (Modal and\n// CommandPalette) so the rest of the kit stops being the exception.\nexport { useFocusTrap } from \"./hooks/use-focus-trap\";\nexport type { FocusTrapOptions } from \"./hooks/use-focus-trap\";\nexport { useAnnounce } from \"./hooks/use-announce\";\nexport type { UseAnnounceReturn, UseAnnounceOptions, AnnounceRegionProps } from \"./hooks/use-announce\";\nexport { useOverlayHistory } from \"./hooks/use-overlay-history\";\nexport { useCloseTransition, OVERLAY_EXIT_MS } from \"./hooks/use-close-transition\";\nexport { useRowSwipe } from \"./hooks/use-row-swipe\";\nexport type { SwipeStage, RowSwipeOptions, RowSwipeReturn } from \"./hooks/use-row-swipe\";\n// Any element as a file drop target, screened like FileButton (kastlan).\nexport { useFileDrop, dragHasFiles } from \"./hooks/use-file-drop\";\nexport type { UseFileDropOptions, UseFileDropReturn, FileDropProps } from \"./hooks/use-file-drop\";\n// kastlan's search boxes each carried a hand-rolled copy, two without the cleanup.\nexport { useDebounce, useDebouncedCallback } from \"./hooks/use-debounce\";\nexport type { DebouncedCallbackOptions, DebouncedFunction } from \"./hooks/use-debounce\";\n// One search param as state, the active tab in `?tab=`, and a dialog's open state in the\n// URL (Modal's `urlParam`) — kastlan's and keksdose's use-search-param-state, in the kit.\nexport { useSearchParamState, useTabParam, useDialogParam } from \"./hooks/use-search-param-state\";\nexport type { SearchParamStateOptions, DialogParam } from \"./hooks/use-search-param-state\";\n// Copy that reports whether it worked — keksdose's copy buttons said \"Copied\" when it had not.\nexport { useCopyToClipboard, copyToClipboard } from \"./hooks/use-copy-to-clipboard\";\nexport type { CopyState, UseCopyToClipboardOptions, UseCopyToClipboardReturn } from \"./hooks/use-copy-to-clipboard\";\n// Keyboard shortcuts (\"Mod+K\", \"Ctrl+Shift+F\") that leave typing in fields alone.\nexport { useHotkey, parseHotkey, matchesHotkey } from \"./hooks/use-hotkey\";\nexport type { Hotkey, ParsedHotkey, UseHotkeyOptions } from \"./hooks/use-hotkey\";\n// A protected file as an object URL, fetched with the app's own auth (kastlan's AuthedImage).\nexport { useAuthedSrc } from \"./hooks/use-authed-src\";\nexport type { AuthedFetcher, AuthedSrcStatus, UseAuthedSrcOptions, UseAuthedSrcResult } from \"./hooks/use-authed-src\";\n\n// ── theme / palettes ─────────────────────────────────────────────────────────\nexport * from \"./theme/chart-palette\";\n// Colour maths (sRGB ↔ OKLCH, WCAG contrast, CVD simulation) and the palette deriver\n// that builds a whole, contrast-solved palette from one brand colour plus any colours\n// pinned by hand. `auditPalette` / `auditChartRamp` measure a palette rather than\n// trusting a comment about it.\nexport * from \"./theme/color\";\nexport * from \"./theme/palette-derive\";\nexport * from \"./theme/palette-presets\";\nexport * from \"./theme/theme-store\";\nexport * from \"./theme/palette-store\";\n\n// ── components ───────────────────────────────────────────────────────────────\nexport * from \"./components/ui\";\n// The label-ABOVE field group (label, hint, error, required), inside wizards or out.\nexport * from \"./components/field\";\nexport * from \"./components/search-field\";\nexport * from \"./components/dropdown\";\nexport * from \"./components/popover\";\nexport { SwipeableRow } from \"./components/swipeable-row\";\nexport type { SwipeAction, SwipeableRowProps } from \"./components/swipeable-row\";\nexport * from \"./components/calculator\";\nexport * from \"./components/numpad-sheet\";\nexport * from \"./components/number-input\";\nexport * from \"./components/currency-select\";\nexport * from \"./components/amount-input\";\nexport * from \"./components/money-field\";\nexport * from \"./components/combobox\";\nexport * from \"./components/picker-sheet\";\nexport * from \"./components/entity-combobox\";\nexport * from \"./components/multi-entity-combobox\";\nexport * from \"./components/multi-select\";\nexport * from \"./components/tooltip\";\nexport * from \"./components/user-avatar\";\nexport * from \"./components/settings-fields\";\n// Sync state for a database-backed field: the engine (`useFieldSync`) and the\n// affordance (`FieldSyncIndicator`), kept separate because the state is useful\n// without the dot — a form can gate its submit on any field being `pending`.\nexport * from \"./components/field-sync\";\nexport * from \"./components/month-picker\";\nexport * from \"./components/checkbox\";\nexport * from \"./components/switch\";\nexport * from \"./components/slider\";\nexport * from \"./components/time-input\";\nexport * from \"./components/number-field\";\nexport * from \"./components/sparkline\";\nexport * from \"./components/stat-tile\";\nexport * from \"./components/signature-pad\";\nexport * from \"./components/password-strength\";\nexport * from \"./components/page-contents\";\nexport * from \"./components/disclosure\";\nexport * from \"./components/dialog-frame\";\nexport * from \"./components/danger-confirm\";\n// `useConfirm()` — the promise-based replacement for `window.confirm`, one host per app.\nexport * from \"./components/confirm-dialog\";\n// A non-modal corner panel and its round trigger (keksdose's assistant launcher).\nexport * from \"./components/floating-panel\";\n// The count / clear / actions bar a selection brings up (keksdose's three copies).\nexport * from \"./components/bulk-action-bar\";\nexport * from \"./components/swatch-picker\";\nexport * from \"./components/icon-picker\";\nexport * from \"./components/choice-card\";\nexport * from \"./components/autocomplete\";\nexport * from \"./components/measured-grid\";\nexport { useWindowedRows } from \"./hooks/use-windowed-rows\";\nexport type { WindowedRows } from \"./hooks/use-windowed-rows\";\n// Named, not `export *`: file-button.tsx also holds the screening helpers the\n// dropzone shares, which are internal.\nexport { FileButton, useFilePicker, matchesAccept, DEFAULT_FILE_PICKER_LABELS } from \"./components/file-button\";\nexport type {\n FileButtonProps,\n UseFilePickerOptions,\n UseFilePickerReturn,\n FilePickerLabels,\n FileRejection,\n FileRejectionReason,\n FileScreenOptions,\n} from \"./components/file-button\";\nexport * from \"./components/treemap\";\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\";\nexport * from \"./components/pie-chart\";\nexport * from \"./components/pie-chart-labels\";\nexport * from \"./components/account-settings\";\n// 0.12.0: the fourth account section, and the QR code TwoFactorSetting draws its\n// otpauth URI with (encoder in lib/qr-encode.ts — no dependency, see there).\nexport * from \"./components/passkeys-setting\";\nexport * from \"./components/qr-code\";\nexport { encodeQr } from \"./lib/qr-encode\";\nexport type { QrEncodeOptions, QrErrorCorrection, QrMatrix } from \"./lib/qr-encode\";\nexport * from \"./components/alert-banner\";\nexport * from \"./components/toggle-group\";\n// A wrapping set of links/buttons marked `aria-current` — Tabs-like looks, not tabs.\nexport * from \"./components/nav-pills\";\n// A pill carrying one VALUE — inert, a link, or a toggle, depending on which prop it is\n// given — and the list field built from it. Distinct from Button on purpose: a row of\n// buttons reads as \"choose an action\", a row of chips as \"here are the things\".\nexport * from \"./components/chip\";\nexport * from \"./components/wizard-stepper\";\nexport * from \"./components/hover-menu\";\nexport * from \"./components/modal\";\nexport * from \"./components/full-bleed-dialog\";\nexport * from \"./components/grouped-picker\";\nexport * from \"./components/file-dropzone\";\nexport * from \"./components/mini-calendar\";\nexport * from \"./components/calendar-heatmap\";\nexport * from \"./components/date-picker\";\nexport * from \"./components/tree-view\";\nexport * from \"./components/chart\";\n// 0.8.0 layout and feedback primitives the apps hand-rolled or took from shadcn/Radix.\nexport * from \"./components/description-list\";\n// The Save / Cancel row forms and dialogs end in, and the editable line-item repeater\n// (journal lines, lease components, invoice positions). 0.12.0.\nexport * from \"./components/form-actions\";\nexport * from \"./components/line-items\";\nexport * from \"./components/progress-bar\";\n// Named: skeleton.tsx also holds SKELETON_CLASS, the look StatTile shares — internal.\nexport { Skeleton } from \"./components/skeleton\";\nexport type { SkeletonProps, SkeletonShape } from \"./components/skeleton\";\n// 0.12.0: a centred spinner with words, signed figures and deltas, a render-error fallback.\nexport * from \"./components/loading-state\";\nexport * from \"./components/signed-amount\";\nexport * from \"./components/error-boundary\";\nexport * from \"./components/copy-button\";\nexport { AuthedImage, DEFAULT_AUTHED_IMAGE_LABELS } from \"./components/authed-image\";\nexport type { AuthedImageProps, AuthedImageLabels } from \"./components/authed-image\";\nexport * from \"./components/image-grid\";\nexport * from \"./components/lightbox\";\nexport * from \"./components/button-group\";\nexport * from \"./components/table\";\nexport * from \"./components/separator\";\n// Named: scroll-area.tsx also holds the overflow hook and scrollbar class Table shares.\nexport { ScrollArea } from \"./components/scroll-area\";\nexport type { ScrollAreaProps } from \"./components/scroll-area\";\n// 0.10.0 rows, menu rows, text primitives and page chrome the apps drew by hand.\nexport * from \"./components/list\";\nexport * from \"./components/menu-item\";\nexport * from \"./components/text\";\nexport * from \"./components/status-dot\";\nexport * from \"./components/page-header\";\nexport * from \"./components/breadcrumbs\";\n// The one inline link (kastlan's EntityLink / CellLink / legal and source links), through\n// `<UiKitProvider linkComponent>`. Named: the file also holds the kit's internal link picker.\nexport { TextLink } from \"./components/text-link\";\nexport type {\n TextLinkProps,\n TextLinkRenderProps,\n TextLinkTone,\n TextLinkUnderline,\n TextLinkCurrent,\n} from \"./components/text-link\";\n// The toast layer over sonner (an OPTIONAL peer, loaded lazily): `toast` mirrors sonner's\n// API so apps migrate by swapping the import; `<Toaster>` carries the placement, theme,\n// tones and z-index both apps had wired by hand.\nexport { toast, Toaster, DEFAULT_TOAST_LABELS, TOAST_ACTION_DURATION } from \"./components/toast\";\nexport type {\n ToastId,\n ToastAction,\n ToastOptions,\n ToastPromiseOptions,\n ToastUndoOptions,\n ToastRedoOptions,\n ToastLabels,\n ToastPosition,\n ToastSwipeDirection,\n ToasterOffset,\n ToasterProps,\n} from \"./components/toast\";\n\n// ── data-table suite ─────────────────────────────────────────────────────────\nexport * from \"./components/data-table-labels\";\nexport * from \"./components/data-table-sort\";\nexport * from \"./components/data-table-filters\";\nexport * from \"./components/data-table-pagination\";\nexport { FilterPopover } from \"./components/data-table-filter-popover\";\n// data-table re-exports SortState internally; export the rest explicitly to\n// avoid a duplicate SortState star-export (it comes from data-table-sort).\nexport { DataTable } from \"./components/data-table\";\nexport type {\n DataTableColumn,\n DataTableCellProps,\n DataTableHeadProps,\n DataTableProps,\n ServerPagination,\n FilterState,\n MobileSwipeActions,\n DataTableDensity,\n DataTableChrome,\n DataTableRowAction,\n} from \"./components/data-table\";\n// 0.12.0: the yes/no cell, and a server table's state held by its owner, URL-synced.\nexport * from \"./components/data-table-cells\";\nexport { useTableUrlState, readTableUrlState } from \"./components/use-table-state\";\nexport type {\n TableUrlState,\n TableUrlSync,\n TableUrlStateProps,\n UseTableUrlStateOptions,\n UseTableUrlStateReturn,\n} from \"./components/use-table-state\";\n\n// ── shell (composable app chrome) ────────────────────────────────────────────\nexport * from \"./shell/topbar-controls\";\nexport * from \"./shell/top-bar\";\nexport * from \"./shell/app-shell\";\nexport * from \"./shell/option-switcher-menu\";\nexport * from \"./shell/role-switcher\";\nexport * from \"./shell/topbar-action-menu\";\nexport * from \"./shell/top-bar-brand\";\nexport * from \"./shell/auth-layout\";\n\n// ── feedback ─────────────────────────────────────────────────────────────────\n// The form somebody files a report with, and the parts an inbox is built from:\n// the status vocabulary both apps share value for value, the policy about what a\n// row may move to from where it stands, and the look of a status control, a\n// category badge and an opened report. The app still wires its own API, columns\n// and strings — see the note at the top of `feedback-inbox.tsx` for what is\n// deliberately left to it.\nexport * from \"./feedback/feedback-attachment\";\nexport * from \"./feedback/feedback-dialog\";\nexport * from \"./feedback/feedback-inbox\";\nexport * from \"./feedback/feedback-thread\";\n\n// ── multi-step wizard ────────────────────────────────────────────────────────\n// The engine (step machine, validators, collected data), the chrome around it\n// (indicator + nav bar + cancel confirm) and the review step it ends on.\n// `components/wizard-stepper` next door is a different, much smaller thing: a bare\n// step indicator for a two-step import flow, with no engine.\n//\n// Two modules were removed here rather than kept as stock. `wizard-field` held a\n// `WizardField`/`WizardSelectField` pair that had never been rendered in any app —\n// a field group nothing was built from, which this comment used to advertise. And\n// `use-rhf-wizard-step` was the only thing in the package that touched\n// react-hook-form, for a hook neither consumer called: keeping it meant an optional\n// peer, a dev dependency and a type import in every consumer's typecheck, bought\n// for nobody. A design system may carry stock; untested stock nothing has ever\n// rendered is not stock, it is a liability with an export.\nexport * from \"./wizard/types\";\nexport * from \"./wizard/use-wizard\";\nexport * from \"./wizard/wizard-context\";\nexport * from \"./wizard/validation\";\nexport * from \"./wizard/wizard-step\";\nexport * from \"./wizard/wizard-summary\";\nexport * from \"./wizard/stepper-nav\";\n\n// ── guided tours ─────────────────────────────────────────────────────────────\nexport * from \"./tour/tour\";\n\n// ── command palette / global search ──────────────────────────────────────────\nexport * from \"./search/command-palette\";\nexport * from \"./search/search-index\";\nexport * from \"./search/global-search\";\n\n// ── i18n: one label tree, one optional provider ──────────────────────────────\n// `UiKitLabels` names every string the kit renders; `<UiKitProvider labels locale>`\n// hands a translation to every component below it. Precedence: a component's own\n// prop > the provider > the English default. See src/i18n/kit-labels.tsx.\nexport * from \"./i18n/kit-labels\";\nexport * from \"./i18n/defaults\";\n"],"mappings":";AAMA,cAAc;AACd,SAAS,UAAU;AACnB,SAAS,QAAQ,mBAAmB;AAGpC,cAAc;AAEd,SAAS,YAAY,mBAAmB;AAIxC,SAAS,qBAAqB;AAC9B,SAAS,yBAAyB;AAClC,SAAS,uBAAuB;AAEhC,SAAS,kBAAkB,wBAAwB,yBAAyB;AAM5E,SAAS,cAAc,uBAAuB;AAI9C,SAAS,oBAAoB;AAE7B,SAAS,mBAAmB;AAE5B,SAAS,yBAAyB;AAClC,SAAS,oBAAoB,uBAAuB;AACpD,SAAS,mBAAmB;AAG5B,SAAS,aAAa,oBAAoB;AAG1C,SAAS,aAAa,4BAA4B;AAIlD,SAAS,qBAAqB,aAAa,sBAAsB;AAGjE,SAAS,oBAAoB,uBAAuB;AAGpD,SAAS,WAAW,aAAa,qBAAqB;AAGtD,SAAS,oBAAoB;AAI7B,cAAc;AAKd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AAGd,cAAc;AAEd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,SAAS,oBAAoB;AAE7B,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AAId,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AAEd,cAAc;AAEd,cAAc;AAEd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,SAAS,uBAAuB;AAIhC,SAAS,YAAY,eAAe,eAAe,kCAAkC;AAUrF,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AAGd,cAAc;AACd,cAAc;AACd,SAAS,gBAAgB;AAEzB,cAAc;AACd,cAAc;AAEd,cAAc;AAId,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AAEd,cAAc;AAGd,cAAc;AACd,cAAc;AACd,cAAc;AAEd,SAAS,gBAAgB;AAGzB,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,SAAS,aAAa,mCAAmC;AAEzD,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AAEd,SAAS,kBAAkB;AAG3B,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AAGd,SAAS,gBAAgB;AAWzB,SAAS,OAAO,SAAS,sBAAsB,6BAA6B;AAgB5E,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,SAAS,qBAAqB;AAG9B,SAAS,iBAAiB;AAc1B,cAAc;AACd,SAAS,kBAAkB,yBAAyB;AAUpD,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AASd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AAgBd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AAGd,cAAc;AAGd,cAAc;AACd,cAAc;AACd,cAAc;AAMd,cAAc;AACd,cAAc;","names":[]}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The full-document navigations the kit makes, behind one object the tests can spy on.
|
|
3
|
+
*
|
|
4
|
+
* jsdom implements neither `location.reload` nor `location.replace`, and its
|
|
5
|
+
* `window.location` is unforgeable: a test cannot stub it by redefining the property
|
|
6
|
+
* once the suite runs each file in its own VM context (`pool: "vmThreads"`, which took
|
|
7
|
+
* the suite from ~3 minutes to ~1). `vi.spyOn(documentNavigation, "reload")` works in
|
|
8
|
+
* every pool. Internal, not part of the package's surface.
|
|
9
|
+
*/
|
|
10
|
+
declare const documentNavigation: {
|
|
11
|
+
reload(): void;
|
|
12
|
+
replace(href: string): void;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export { documentNavigation };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/lib/document-navigation.ts"],"sourcesContent":["/**\n * The full-document navigations the kit makes, behind one object the tests can spy on.\n *\n * jsdom implements neither `location.reload` nor `location.replace`, and its\n * `window.location` is unforgeable: a test cannot stub it by redefining the property\n * once the suite runs each file in its own VM context (`pool: \"vmThreads\"`, which took\n * the suite from ~3 minutes to ~1). `vi.spyOn(documentNavigation, \"reload\")` works in\n * every pool. Internal, not part of the package's surface.\n */\nexport const documentNavigation = {\n reload(): void {\n window.location.reload();\n },\n replace(href: string): void {\n window.location.replace(href);\n },\n};\n"],"mappings":";AASO,MAAM,qBAAqB;AAAA,EAChC,SAAe;AACb,WAAO,SAAS,OAAO;AAAA,EACzB;AAAA,EACA,QAAQ,MAAoB;AAC1B,WAAO,SAAS,QAAQ,IAAI;AAAA,EAC9B;AACF;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eifi1/ui-kit",
|
|
3
|
-
"version": "0.14.
|
|
3
|
+
"version": "0.14.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "App-agnostic shared design system: domain-free React components, theme and tokens.",
|
|
6
6
|
"_sideEffects": "The barrel (src/index.ts) re-exports EVERY component, including components/chart, which statically imports recharts. Without this field a bundler must assume each of those modules has side effects, so it cannot drop the unused ones: importing a Button from '@eifi1/ui-kit' dragged all 433KB of recharts into the consumer's ENTRY chunk, and Keksdose was preloading it on every page load, login included. Nothing in src/ imports for side effects (no bare imports, no CSS imports from JS, no module-level global writes) — the token stylesheet is its own './tokens.css' export — so this is safe. CSS is still listed so any future stylesheet survives. Re-checked when `flag-icons` left `dependencies` and tsup started emitting a `\"use client\"` banner: neither touches this. The flag stylesheet was never imported from JS in the first place (that was the finding), and a directive prologue is a string literal every bundler lifts out before it decides whether a module is inert.",
|
|
@@ -68,8 +68,8 @@
|
|
|
68
68
|
"LICENSE"
|
|
69
69
|
],
|
|
70
70
|
"scripts": {
|
|
71
|
-
"typecheck": "tsc --noEmit && tsc -p tsconfig.node.json",
|
|
72
|
-
"lint": "eslint . --max-warnings 0",
|
|
71
|
+
"typecheck": "tsc --noEmit --incremental --tsBuildInfoFile node_modules/.cache/tsc/app.tsbuildinfo && tsc -p tsconfig.node.json --incremental --tsBuildInfoFile node_modules/.cache/tsc/node.tsbuildinfo",
|
|
72
|
+
"lint": "eslint . --max-warnings 0 --cache --cache-location node_modules/.cache/eslint/",
|
|
73
73
|
"check:tokens": "node scripts/check-token-discipline.mjs",
|
|
74
74
|
"test:coverage": "vitest run --coverage",
|
|
75
75
|
"check:graph": "node scripts/check-module-graph.mjs",
|
|
@@ -77,7 +77,10 @@
|
|
|
77
77
|
"check:tailwind": "node scripts/check-tailwind-scan.mjs",
|
|
78
78
|
"check:security": "npm audit --audit-level=high",
|
|
79
79
|
"check:commits": "node scripts/check-commit-range.cjs",
|
|
80
|
-
"check": "npm run typecheck && npm run lint && npm run check:tokens && npm run
|
|
80
|
+
"check:static": "npm run typecheck && npm run lint && npm run check:tokens && npm run check:security && npm run check:commits",
|
|
81
|
+
"check:test": "npm run test:coverage",
|
|
82
|
+
"check:build": "npm run build && npm run check:graph && npm run check:package && npm run build:showcase && npm run check:tailwind",
|
|
83
|
+
"check": "node scripts/check.mjs",
|
|
81
84
|
"test": "vitest run",
|
|
82
85
|
"test:watch": "vitest",
|
|
83
86
|
"build": "node scripts/tsup-quiet.mjs && node scripts/fix-esm-extensions.mjs",
|
|
@@ -172,6 +172,14 @@ interface AmountInputProps {
|
|
|
172
172
|
/** The largest amount the field settles on (a deposit release capped at the
|
|
173
173
|
* balance). A higher figure is lowered to it when it settles. */
|
|
174
174
|
max?: number;
|
|
175
|
+
/**
|
|
176
|
+
* The SETTLED figure, dot-decimal, sign included — fired on blur, on Enter, on the
|
|
177
|
+
* numpad's "=" and on every result the calculator writes back, never on a keystroke
|
|
178
|
+
* ("1200+" is not a figure yet). `onChange` still reports every keystroke; this is
|
|
179
|
+
* for a host that stores a number and must not see drafts (kastlan 52; see
|
|
180
|
+
* {@link MoneyField}, which is built on it).
|
|
181
|
+
*/
|
|
182
|
+
onCommit?: (value: string) => void;
|
|
175
183
|
}
|
|
176
184
|
|
|
177
185
|
/** The currency's minor unit (CHF 2, JPY 0), or `undefined` for no currency or a code
|
|
@@ -295,7 +303,7 @@ function isResultOf(previous: string, text: string, settle: (text: string) => st
|
|
|
295
303
|
}
|
|
296
304
|
|
|
297
305
|
export const AmountInput = forwardRef<HTMLInputElement, AmountInputProps>(
|
|
298
|
-
({ value, onChange, currency, onCurrencyChange, placeholder, label, disabled, invalid: invalidProp, className, id, ariaLabel, "aria-describedby": ariaDescribedBy, "aria-invalid": ariaInvalid, "aria-required": ariaRequired, autoFocus, tone = "neutral", negative = false, onNegativeChange, variant = "field", align = "start", labels, currencyNames, digits: digitsProp, min, max }, ref) => {
|
|
306
|
+
({ value, onChange, currency, onCurrencyChange, placeholder, label, disabled, invalid: invalidProp, className, id, ariaLabel, "aria-describedby": ariaDescribedBy, "aria-invalid": ariaInvalid, "aria-required": ariaRequired, autoFocus, tone = "neutral", negative = false, onNegativeChange, variant = "field", align = "start", labels, currencyNames, digits: digitsProp, min, max, onCommit }, ref) => {
|
|
299
307
|
const generatedId = useId();
|
|
300
308
|
const fieldId = id ?? generatedId;
|
|
301
309
|
const invalid = Boolean(invalidProp) || ariaInvalid === true || ariaInvalid === "true";
|
|
@@ -399,7 +407,9 @@ export const AmountInput = forwardRef<HTMLInputElement, AmountInputProps>(
|
|
|
399
407
|
};
|
|
400
408
|
const commit = () => {
|
|
401
409
|
setDraft(null);
|
|
402
|
-
|
|
410
|
+
const settled = settle(commitExpression(shown));
|
|
411
|
+
handleText(settled);
|
|
412
|
+
onCommit?.(settled);
|
|
403
413
|
};
|
|
404
414
|
const typed = (raw: string) => {
|
|
405
415
|
setDraft(raw);
|
|
@@ -533,7 +543,9 @@ export const AmountInput = forwardRef<HTMLInputElement, AmountInputProps>(
|
|
|
533
543
|
value={display}
|
|
534
544
|
onChange={(result, expression) => {
|
|
535
545
|
setDraft(null);
|
|
536
|
-
|
|
546
|
+
const settled = settle(result);
|
|
547
|
+
handleText(settled, expression);
|
|
548
|
+
onCommit?.(settled);
|
|
537
549
|
}}
|
|
538
550
|
className="px-1.5"
|
|
539
551
|
ariaLabel={labels?.calculatorTrigger}
|
|
@@ -622,7 +634,9 @@ export const AmountInput = forwardRef<HTMLInputElement, AmountInputProps>(
|
|
|
622
634
|
onChange={typed}
|
|
623
635
|
onResult={(result) => {
|
|
624
636
|
setDraft(null);
|
|
625
|
-
|
|
637
|
+
const settled = settle(result);
|
|
638
|
+
handleText(settled);
|
|
639
|
+
onCommit?.(settled);
|
|
626
640
|
}}
|
|
627
641
|
onDone={() => innerRef.current?.blur()}
|
|
628
642
|
label={label}
|
|
@@ -5,6 +5,7 @@ import { useKitLabels } from "../i18n/kit-labels";
|
|
|
5
5
|
import { Button, EmptyState } from "./ui";
|
|
6
6
|
import { CopyButton } from "./copy-button";
|
|
7
7
|
import { Disclosure } from "./disclosure";
|
|
8
|
+
import { documentNavigation } from "../lib/document-navigation";
|
|
8
9
|
|
|
9
10
|
/** The words of the default fallback. */
|
|
10
11
|
export interface ErrorBoundaryLabels {
|
|
@@ -277,7 +278,7 @@ function isOffline(): boolean {
|
|
|
277
278
|
|
|
278
279
|
function reloadPage(): void {
|
|
279
280
|
try {
|
|
280
|
-
|
|
281
|
+
documentNavigation.reload();
|
|
281
282
|
} catch {
|
|
282
283
|
/* nothing left to do from here */
|
|
283
284
|
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { useState, type ComponentProps, type Ref } from "react";
|
|
2
|
+
import { AmountInput } from "./amount-input";
|
|
3
|
+
|
|
4
|
+
type AmountInputProps = ComponentProps<typeof AmountInput>;
|
|
5
|
+
|
|
6
|
+
export interface MoneyFieldProps
|
|
7
|
+
extends Omit<AmountInputProps, "value" | "onChange" | "onCommit" | "negative" | "onNegativeChange" | "ref"> {
|
|
8
|
+
/** The amount, or `null` for an empty field. */
|
|
9
|
+
value: number | null;
|
|
10
|
+
/**
|
|
11
|
+
* The settled amount — on blur, on Enter, on the numpad's "=" and on a calculator
|
|
12
|
+
* result; never a half-typed "1200+". Rounded to the currency's minor unit (or
|
|
13
|
+
* `digits`) and clamped to `min` / `max`, like {@link AmountInput}. `null` when the
|
|
14
|
+
* field was emptied. Not fired when the settled amount equals `value`, so an
|
|
15
|
+
* Enter-then-blur saves once.
|
|
16
|
+
*/
|
|
17
|
+
onCommit: (value: number | null) => void;
|
|
18
|
+
ref?: Ref<HTMLInputElement>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** The number a settled text reads as: `null` for empty, `undefined` for no number
|
|
22
|
+
* (a lone "-" or "."). */
|
|
23
|
+
function parseSettled(text: string): number | null | undefined {
|
|
24
|
+
const t = text.trim();
|
|
25
|
+
if (t === "") return null;
|
|
26
|
+
const n = Number(t);
|
|
27
|
+
return Number.isFinite(n) ? n : undefined;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* {@link AmountInput} with a NUMBER in and a number out, for money edited outside a
|
|
32
|
+
* form — a budget row, a table cell (kastlan 52: new-budget-page edited a CHF amount per
|
|
33
|
+
* row with `NumberField` because AmountInput is string-valued and reports every
|
|
34
|
+
* keystroke, "1200+" included, so a number-state row needed its own parse-and-settle
|
|
35
|
+
* glue). This is that glue, the same one `RhfMoneyField` has: the text being typed
|
|
36
|
+
* lives here, and only a settled figure reaches `onCommit`.
|
|
37
|
+
*
|
|
38
|
+
* The currency chip, the calculator, the numpad, the locale's decimal mark, the
|
|
39
|
+
* rounding to the currency's minor unit and `min` / `max` are AmountInput's. A caller-
|
|
40
|
+
* owned sign (`negative`) is not offered: the figure here carries its own minus.
|
|
41
|
+
*
|
|
42
|
+
* ```tsx
|
|
43
|
+
* <MoneyField ariaLabel="Amount" currency="CHF" value={row.amount}
|
|
44
|
+
* onCommit={(amount) => updateRow(row.id, { amount })} />
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
export function MoneyField({ value, onCommit, ref, ...props }: MoneyFieldProps) {
|
|
48
|
+
const external = value === null ? "" : String(value);
|
|
49
|
+
// The draft is re-read from `value` only when `value` moves away from what the draft
|
|
50
|
+
// already says (a reset, another row's save), so a commit's own echo keeps the
|
|
51
|
+
// typist's spelling ("12.50" stays "12.50" when 12.5 comes back).
|
|
52
|
+
const [draft, setDraft] = useState(external);
|
|
53
|
+
const [seen, setSeen] = useState(value);
|
|
54
|
+
if (seen !== value) {
|
|
55
|
+
setSeen(value);
|
|
56
|
+
if (parseSettled(draft) !== value) setDraft(external);
|
|
57
|
+
}
|
|
58
|
+
return (
|
|
59
|
+
<AmountInput
|
|
60
|
+
{...props}
|
|
61
|
+
ref={ref}
|
|
62
|
+
value={draft}
|
|
63
|
+
onChange={setDraft}
|
|
64
|
+
onCommit={(text) => {
|
|
65
|
+
const next = parseSettled(text);
|
|
66
|
+
// No number ("-" alone): back to the stored amount rather than a guess.
|
|
67
|
+
if (next === undefined) {
|
|
68
|
+
setDraft(external);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
if (next !== value) onCommit(next);
|
|
72
|
+
}}
|
|
73
|
+
/>
|
|
74
|
+
);
|
|
75
|
+
}
|
|
@@ -304,3 +304,17 @@ export function NumberField({
|
|
|
304
304
|
</div>
|
|
305
305
|
);
|
|
306
306
|
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* A {@link NumberField} for whole numbers: `digits={0}` and `calculator={false}` as
|
|
310
|
+
* defaults, each overridable — the preset `RhfIntegerField` (`@eifi1/ui-kit/rhf`) applies to a
|
|
311
|
+
* form-bound field, for the plain one (kastlan 51: a building's construction year in a
|
|
312
|
+
* wizard's local state, an entity id in the document upload spelled the two props out
|
|
313
|
+
* by hand).
|
|
314
|
+
*
|
|
315
|
+
* `??` rather than a spread over the defaults, so a wrapper forwarding its own optional
|
|
316
|
+
* `digits={props.digits}` still gets the preset.
|
|
317
|
+
*/
|
|
318
|
+
export function IntegerField(props: NumberFieldProps) {
|
|
319
|
+
return <NumberField {...props} digits={props.digits ?? 0} calculator={props.calculator ?? false} />;
|
|
320
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { useLayoutEffect, useRef, useState } from "react";
|
|
1
2
|
import type { ComponentPropsWithoutRef, ElementType, ReactNode } from "react";
|
|
2
3
|
import { cn } from "../lib/cn";
|
|
3
4
|
import { SECTION_LABEL_CLASS } from "./text";
|
|
@@ -168,6 +169,37 @@ export function PageHeader({
|
|
|
168
169
|
// wraps instead: actions that do not fit go to a line of their own, as the app's old
|
|
169
170
|
// `flex-wrap` header did.
|
|
170
171
|
const keepsWords = mobileLayout === "inline" && !truncateTitle;
|
|
172
|
+
// keksdose, after H1: once the actions have wrapped below the title, the phone's
|
|
173
|
+
// header was THREE rows — title / actions / secondary — where the app's old header
|
|
174
|
+
// shared the second row between the month nav and the toggles (~48px of phone
|
|
175
|
+
// height). So the secondary group's full-width basis (G6b: a row of its own) is
|
|
176
|
+
// dropped exactly when the actions already sit below the title; then it joins their
|
|
177
|
+
// row if it fits, and wraps under them if not. While the actions share the title's
|
|
178
|
+
// row, nothing changes: the secondary group never climbs onto the title's row.
|
|
179
|
+
//
|
|
180
|
+
// CSS cannot see "the previous item wrapped", so it is measured — by a
|
|
181
|
+
// ResizeObserver, whose first callback runs on `observe`. It is stable: the
|
|
182
|
+
// secondary group comes after the actions, so its basis cannot move them. No
|
|
183
|
+
// ResizeObserver (jsdom) or no layout keeps the basis, the 0.14.0 layout.
|
|
184
|
+
const measuresWrap = keepsWords && hasSecondary && actions != null;
|
|
185
|
+
const titleRef = useRef<HTMLDivElement>(null);
|
|
186
|
+
const actionsRef = useRef<HTMLDivElement>(null);
|
|
187
|
+
const [actionsWrapped, setActionsWrapped] = useState(false);
|
|
188
|
+
useLayoutEffect(() => {
|
|
189
|
+
const titleBox = titleRef.current;
|
|
190
|
+
const actionsBox = actionsRef.current;
|
|
191
|
+
if (!measuresWrap || !titleBox || !actionsBox || typeof ResizeObserver === "undefined") return;
|
|
192
|
+
const observer = new ResizeObserver(() => {
|
|
193
|
+
const t = titleBox.getBoundingClientRect();
|
|
194
|
+
const a = actionsBox.getBoundingClientRect();
|
|
195
|
+
// Unmeasured (display: none, no layout): keep the full-width basis.
|
|
196
|
+
setActionsWrapped(t.height > 0 && a.height > 0 && a.top >= t.bottom - 1);
|
|
197
|
+
});
|
|
198
|
+
observer.observe(titleBox);
|
|
199
|
+
observer.observe(actionsBox);
|
|
200
|
+
if (titleBox.parentElement) observer.observe(titleBox.parentElement);
|
|
201
|
+
return () => observer.disconnect();
|
|
202
|
+
}, [measuresWrap]);
|
|
171
203
|
return (
|
|
172
204
|
<div {...rest} className={cn("flex min-w-0 flex-col gap-2", className)}>
|
|
173
205
|
{breadcrumbs}
|
|
@@ -184,7 +216,7 @@ export function PageHeader({
|
|
|
184
216
|
align,
|
|
185
217
|
)}
|
|
186
218
|
>
|
|
187
|
-
<div className={cn("flex-1", keepsWords ? "min-w-min" : "min-w-0")}>
|
|
219
|
+
<div ref={titleRef} className={cn("flex-1", keepsWords ? "min-w-min" : "min-w-0")}>
|
|
188
220
|
{eyebrow != null && <p className={cn(SECTION_LABEL_CLASS.xs, "mb-1")}>{eyebrow}</p>}
|
|
189
221
|
<Heading
|
|
190
222
|
className={cn(
|
|
@@ -198,7 +230,10 @@ export function PageHeader({
|
|
|
198
230
|
{description != null && <p className="mt-1 text-sm text-[var(--text-muted)]">{description}</p>}
|
|
199
231
|
</div>
|
|
200
232
|
{actions != null && (
|
|
201
|
-
<div
|
|
233
|
+
<div
|
|
234
|
+
ref={actionsRef}
|
|
235
|
+
className={cn("flex shrink-0 flex-wrap items-center gap-2", hasSecondary && "sm:order-2")}
|
|
236
|
+
>
|
|
202
237
|
{actions}
|
|
203
238
|
</div>
|
|
204
239
|
)}
|
|
@@ -206,7 +241,7 @@ export function PageHeader({
|
|
|
206
241
|
<div
|
|
207
242
|
className={cn(
|
|
208
243
|
"flex flex-wrap items-center gap-2 sm:order-1 sm:shrink-0",
|
|
209
|
-
wrapsSecondary && "basis-full sm:basis-auto",
|
|
244
|
+
wrapsSecondary && (measuresWrap && actionsWrapped ? "sm:basis-auto" : "basis-full sm:basis-auto"),
|
|
210
245
|
)}
|
|
211
246
|
>
|
|
212
247
|
{secondaryActions}
|
|
@@ -4,6 +4,7 @@ import type { LucideIcon } from "lucide-react";
|
|
|
4
4
|
import { cn } from "../lib/cn";
|
|
5
5
|
import { DEFAULT_COMMON_LABELS, useKitLabels, useKitLink } from "../i18n/kit-labels";
|
|
6
6
|
import type { KitLinkComponent } from "../i18n/kit-labels";
|
|
7
|
+
import { documentNavigation } from "../lib/document-navigation";
|
|
7
8
|
|
|
8
9
|
/* ── Choosing what draws a link ──────────────────────────────────────────── */
|
|
9
10
|
|
|
@@ -98,7 +99,7 @@ export function replacingClick(
|
|
|
98
99
|
onClick?.(event);
|
|
99
100
|
if (!isPlainNavigationClick(event)) return;
|
|
100
101
|
event.preventDefault();
|
|
101
|
-
|
|
102
|
+
documentNavigation.replace(href);
|
|
102
103
|
};
|
|
103
104
|
}
|
|
104
105
|
|
|
@@ -53,6 +53,9 @@ export interface FeedbackDialogTextLabels {
|
|
|
53
53
|
category: string;
|
|
54
54
|
subject: string;
|
|
55
55
|
body: string;
|
|
56
|
+
/** The body's label when `requireBody={false}`: "What happened? (optional)".
|
|
57
|
+
* Optional, so a complete `UiKitLabels` typed before 0.14.2 still compiles. */
|
|
58
|
+
bodyOptional?: string;
|
|
56
59
|
/** The heading over the attachment buttons. */
|
|
57
60
|
attachment: string;
|
|
58
61
|
/** The line beside the buttons naming the shortcut. */
|
|
@@ -66,6 +69,7 @@ export const DEFAULT_FEEDBACK_DIALOG_LABELS: FeedbackDialogTextLabels = {
|
|
|
66
69
|
category: "Category",
|
|
67
70
|
subject: "Subject",
|
|
68
71
|
body: "What happened?",
|
|
72
|
+
bodyOptional: "What happened? (optional)",
|
|
69
73
|
attachment: "Screenshot",
|
|
70
74
|
submitHint: "Ctrl/⌘ + Enter to send",
|
|
71
75
|
cancel: "Cancel",
|
|
@@ -108,6 +112,7 @@ export function FeedbackDialog({
|
|
|
108
112
|
maxAttachmentBytes = DEFAULT_MAX_ATTACHMENT_BYTES,
|
|
109
113
|
onAttachmentError,
|
|
110
114
|
onCaptureScreenshot,
|
|
115
|
+
requireBody = true,
|
|
111
116
|
}: {
|
|
112
117
|
open: boolean;
|
|
113
118
|
onClose: () => void;
|
|
@@ -129,6 +134,12 @@ export function FeedbackDialog({
|
|
|
129
134
|
* attachment"; the returned file is fed through the same validation + preview.
|
|
130
135
|
*/
|
|
131
136
|
onCaptureScreenshot?: () => Promise<File | null>;
|
|
137
|
+
/**
|
|
138
|
+
* `false`: a report with only a subject can be sent, and the body's label says it is
|
|
139
|
+
* optional (`bodyOptional`, unless `labels.body` is passed). Default `true`, the
|
|
140
|
+
* dialog's behaviour until 0.14.2 (keksdose K1: its backend takes title-only reports).
|
|
141
|
+
*/
|
|
142
|
+
requireBody?: boolean;
|
|
132
143
|
}) {
|
|
133
144
|
const [title, setTitle] = useState("");
|
|
134
145
|
const [body, setBody] = useState("");
|
|
@@ -151,7 +162,9 @@ export function FeedbackDialog({
|
|
|
151
162
|
}
|
|
152
163
|
}
|
|
153
164
|
|
|
154
|
-
|
|
165
|
+
// Trimmed: a subject or body of spaces is empty (keksdose's API answers a blank title
|
|
166
|
+
// with a 422, and a blank report is no report).
|
|
167
|
+
const canSubmit = !!title.trim() && (!requireBody || !!body.trim()) && !submitting;
|
|
155
168
|
const trySubmit = () => {
|
|
156
169
|
if (canSubmit) void onSubmit({ title, body, category, attachment });
|
|
157
170
|
};
|
|
@@ -174,7 +187,12 @@ export function FeedbackDialog({
|
|
|
174
187
|
<Input label={labels.subject} value={title} onChange={(e) => setTitle(e.target.value)} variant="display" />
|
|
175
188
|
);
|
|
176
189
|
const bodyField = (
|
|
177
|
-
<Textarea
|
|
190
|
+
<Textarea
|
|
191
|
+
rows={5}
|
|
192
|
+
label={requireBody ? labels.body : (labelsProp?.body ?? labels.bodyOptional ?? labels.body)}
|
|
193
|
+
value={body}
|
|
194
|
+
onChange={(e) => setBody(e.target.value)}
|
|
195
|
+
/>
|
|
178
196
|
);
|
|
179
197
|
const attachmentField = (
|
|
180
198
|
<FeedbackAttachmentField
|
package/src/i18n/locales/de.ts
CHANGED
|
@@ -26,6 +26,7 @@ export function uiKitLabelsDe(numberLocale = "de-DE"): UiKitLabels {
|
|
|
26
26
|
category: "Kategorie",
|
|
27
27
|
subject: "Betreff",
|
|
28
28
|
body: "Was ist passiert?",
|
|
29
|
+
bodyOptional: "Was ist passiert? (optional)",
|
|
29
30
|
attachment: "Screenshot",
|
|
30
31
|
submitHint: "Strg/⌘ + Enter zum Senden",
|
|
31
32
|
cancel: "Abbrechen",
|
package/src/i18n/locales/es.ts
CHANGED
|
@@ -27,6 +27,7 @@ export function uiKitLabelsEs(numberLocale = "es-ES"): UiKitLabels {
|
|
|
27
27
|
category: "Categoría",
|
|
28
28
|
subject: "Asunto",
|
|
29
29
|
body: "¿Qué ha pasado?",
|
|
30
|
+
bodyOptional: "¿Qué ha pasado? (opcional)",
|
|
30
31
|
attachment: "Captura de pantalla",
|
|
31
32
|
submitHint: "Ctrl/⌘ + Intro para enviar",
|
|
32
33
|
cancel: "Cancelar",
|
package/src/i18n/locales/fr.ts
CHANGED
|
@@ -27,6 +27,7 @@ export function uiKitLabelsFr(numberLocale = "fr-FR"): UiKitLabels {
|
|
|
27
27
|
category: "Catégorie",
|
|
28
28
|
subject: "Objet",
|
|
29
29
|
body: "Que s’est-il passé\u202f?",
|
|
30
|
+
bodyOptional: "Que s’est-il passé\u202f? (facultatif)",
|
|
30
31
|
attachment: "Capture d’écran",
|
|
31
32
|
submitHint: "Ctrl/⌘ + Entrée pour envoyer",
|
|
32
33
|
cancel: "Annuler",
|
package/src/i18n/locales/hu.ts
CHANGED
|
@@ -26,6 +26,7 @@ export function uiKitLabelsHu(numberLocale = "hu-HU"): UiKitLabels {
|
|
|
26
26
|
category: "Kategória",
|
|
27
27
|
subject: "Tárgy",
|
|
28
28
|
body: "Mi történt?",
|
|
29
|
+
bodyOptional: "Mi történt? (nem kötelező)",
|
|
29
30
|
attachment: "Képernyőkép",
|
|
30
31
|
submitHint: "Ctrl/⌘ + Enter a küldéshez",
|
|
31
32
|
cancel: "Mégse",
|
package/src/i18n/locales/it.ts
CHANGED
|
@@ -27,6 +27,7 @@ export function uiKitLabelsIt(numberLocale = "it-IT"): UiKitLabels {
|
|
|
27
27
|
category: "Categoria",
|
|
28
28
|
subject: "Oggetto",
|
|
29
29
|
body: "Cosa è successo?",
|
|
30
|
+
bodyOptional: "Cosa è successo? (facoltativo)",
|
|
30
31
|
attachment: "Screenshot",
|
|
31
32
|
submitHint: "Ctrl/⌘ + Invio per inviare",
|
|
32
33
|
cancel: "Annulla",
|
package/src/i18n/locales/zh.ts
CHANGED
package/src/index.ts
CHANGED
|
@@ -83,6 +83,7 @@ export * from "./components/numpad-sheet";
|
|
|
83
83
|
export * from "./components/number-input";
|
|
84
84
|
export * from "./components/currency-select";
|
|
85
85
|
export * from "./components/amount-input";
|
|
86
|
+
export * from "./components/money-field";
|
|
86
87
|
export * from "./components/combobox";
|
|
87
88
|
export * from "./components/picker-sheet";
|
|
88
89
|
export * from "./components/entity-combobox";
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The full-document navigations the kit makes, behind one object the tests can spy on.
|
|
3
|
+
*
|
|
4
|
+
* jsdom implements neither `location.reload` nor `location.replace`, and its
|
|
5
|
+
* `window.location` is unforgeable: a test cannot stub it by redefining the property
|
|
6
|
+
* once the suite runs each file in its own VM context (`pool: "vmThreads"`, which took
|
|
7
|
+
* the suite from ~3 minutes to ~1). `vi.spyOn(documentNavigation, "reload")` works in
|
|
8
|
+
* every pool. Internal, not part of the package's surface.
|
|
9
|
+
*/
|
|
10
|
+
export const documentNavigation = {
|
|
11
|
+
reload(): void {
|
|
12
|
+
window.location.reload();
|
|
13
|
+
},
|
|
14
|
+
replace(href: string): void {
|
|
15
|
+
window.location.replace(href);
|
|
16
|
+
},
|
|
17
|
+
};
|