@ghxstship/xos-ui 0.0.0-stage → 4.16.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 +21 -0
- package/README.md +32 -2
- package/package.json +76 -4
- package/scripts/generate-stories.mjs +85 -0
- package/src/AIDisclosureBadge/README.md +7 -0
- package/src/AccessGridMatrix/README.md +3 -0
- package/src/Accordion/README.md +7 -0
- package/src/ActivityFeedItem/README.md +3 -0
- package/src/AgencySlate/README.md +3 -0
- package/src/AppShell/README.md +5 -0
- package/src/ApplicationForm/README.md +3 -0
- package/src/AssertionRankBadge/README.md +5 -0
- package/src/AvailabilityCalendar/README.md +3 -0
- package/src/Avatar/Avatar.tsx +56 -0
- package/src/Avatar/README.md +3 -0
- package/src/Avatar/avatar-class.ts +19 -0
- package/src/AvatarStack/README.md +3 -0
- package/src/Badge/README.md +3 -0
- package/src/Banner/README.md +3 -0
- package/src/BidSheet/README.md +3 -0
- package/src/Board/README.md +6 -0
- package/src/BrandMark/BrandMark.tsx +95 -0
- package/src/BrandMark/README.md +7 -0
- package/src/BrandSlot/README.md +6 -0
- package/src/Breadcrumbs/README.md +3 -0
- package/src/BudgetGrid/README.md +10 -0
- package/src/BulkActionBar/README.md +3 -0
- package/src/Button/Button.tsx +100 -0
- package/src/Button/README.md +10 -0
- package/src/Button/button-class.ts +54 -0
- package/src/ButtonLink/README.md +7 -0
- package/src/Calendar/README.md +3 -0
- package/src/Card/README.md +3 -0
- package/src/Chart/README.md +9 -0
- package/src/Charts/Calendar.tsx +228 -0
- package/src/Charts/Chart.tsx +547 -0
- package/src/Charts/Charts.tsx +20 -0
- package/src/Charts/Charts.types.ts +40 -0
- package/src/Charts/Gallery.tsx +25 -0
- package/src/Charts/Heatmap.tsx +116 -0
- package/src/Charts/OrgChart.tsx +53 -0
- package/src/Charts/ResourceSchedule.tsx +91 -0
- package/src/Charts/Sparkline.tsx +59 -0
- package/src/Charts/Timeline.tsx +777 -0
- package/src/Charts/categories.ts +21 -0
- package/src/Charts/timeline-plan.ts +191 -0
- package/src/Checkbox/README.md +6 -0
- package/src/ChecklistRunner/README.md +3 -0
- package/src/Chips/Badge.tsx +27 -0
- package/src/Chips/Chips.shared.tsx +34 -0
- package/src/Chips/Chips.tsx +21 -0
- package/src/Chips/Chips.types.ts +35 -0
- package/src/Chips/DepartmentChip.tsx +16 -0
- package/src/Chips/PhaseChip.tsx +16 -0
- package/src/Chips/PropertyChip.tsx +15 -0
- package/src/Chips/RecordKindChip.tsx +14 -0
- package/src/Chips/StateChip.tsx +19 -0
- package/src/Chips/Tag.tsx +47 -0
- package/src/Chips/URIDChip.tsx +14 -0
- package/src/Chips/chipClass.tsx +8 -0
- package/src/Chips/codeClass.tsx +6 -0
- package/src/ClockButton/README.md +6 -0
- package/src/CodeSample/README.md +7 -0
- package/src/ColorPicker/README.md +5 -0
- package/src/Combobox/README.md +7 -0
- package/src/CommandMenu/CommandMenu.tsx +179 -0
- package/src/CommandMenu/README.md +5 -0
- package/src/ComparisonTable/README.md +9 -0
- package/src/Compass/ChecklistRunner.tsx +128 -0
- package/src/Compass/ClockButton.tsx +57 -0
- package/src/Compass/Compass.shared.tsx +58 -0
- package/src/Compass/Compass.tsx +26 -0
- package/src/Compass/Compass.types.ts +79 -0
- package/src/Compass/DaySheetView.tsx +104 -0
- package/src/Compass/ForceUpdateScreen.tsx +29 -0
- package/src/Compass/LiveActivity.tsx +60 -0
- package/src/Compass/OfflineBanner.tsx +31 -0
- package/src/Compass/PhotoCapture.tsx +69 -0
- package/src/Compass/QuickIncidentSheet.tsx +46 -0
- package/src/Compass/ScanSheet.tsx +83 -0
- package/src/Compass/ShiftCard.tsx +55 -0
- package/src/Compass/SignaturePad.tsx +71 -0
- package/src/Compass/SyncConflictSheet.tsx +60 -0
- package/src/ConsentBanner/ConsentBanner.tsx +210 -0
- package/src/ConsentBanner/ConsentBannerStatic.tsx +163 -0
- package/src/ConsentBanner/ConsentSettings.tsx +213 -0
- package/src/ConsentBanner/README.md +101 -0
- package/src/ConsentBanner/consent.ts +238 -0
- package/src/ConsentBanner/index.ts +40 -0
- package/src/ConsentBanner/static.ts +11 -0
- package/src/ConsentBanner/useConsent.ts +160 -0
- package/src/ContextMenu/README.md +3 -0
- package/src/CoordinateMatrix/README.md +6 -0
- package/src/CredentialBadge/README.md +7 -0
- package/src/CurrencyInput/README.md +7 -0
- package/src/DataTable/DataTable.tsx +1034 -0
- package/src/DataTable/DataTableResizer.tsx +72 -0
- package/src/DataTable/DataTableVirtualizer.tsx +58 -0
- package/src/DataTable/README.md +9 -0
- package/src/DatePicker/README.md +5 -0
- package/src/DateRangePicker/README.md +3 -0
- package/src/DaySheetView/README.md +3 -0
- package/src/DepartmentChip/README.md +3 -0
- package/src/DepartmentGlyph/README.md +18 -0
- package/src/Dialog/README.md +3 -0
- package/src/DiffViewer/README.md +3 -0
- package/src/Divider/README.md +3 -0
- package/src/Drawer/README.md +3 -0
- package/src/DropdownMenu/README.md +3 -0
- package/src/EPKViewer/README.md +3 -0
- package/src/Editing/ColorPicker.tsx +125 -0
- package/src/Editing/DiffViewer.tsx +49 -0
- package/src/Editing/Editing.tsx +29 -0
- package/src/Editing/Editing.types.ts +71 -0
- package/src/Editing/FilterBuilder.tsx +72 -0
- package/src/Editing/QuickAdd.tsx +102 -0
- package/src/Editing/QuickAddChips.tsx +180 -0
- package/src/Editing/contrastRatio.tsx +11 -0
- package/src/Editing/parseQuickAdd.tsx +187 -0
- package/src/Editing/quickAddCore.ts +204 -0
- package/src/Editing/timeShorthand.ts +157 -0
- package/src/Editing/useQuickAdd.ts +201 -0
- package/src/EmergencyCodeCard/README.md +10 -0
- package/src/EmptyState/README.md +5 -0
- package/src/EngagementTimeline/README.md +3 -0
- package/src/ErrorState/README.md +3 -0
- package/src/FAQ/README.md +7 -0
- package/src/FeatureGrid/README.md +7 -0
- package/src/Feedback/Banner.tsx +40 -0
- package/src/Feedback/EmptyState.tsx +32 -0
- package/src/Feedback/ErrorState.tsx +32 -0
- package/src/Feedback/Feedback.shared.tsx +38 -0
- package/src/Feedback/Feedback.tsx +18 -0
- package/src/Feedback/Feedback.types.ts +40 -0
- package/src/Feedback/InlineAlert.tsx +19 -0
- package/src/Feedback/RefusalNotice.tsx +46 -0
- package/src/Feedback/RegionBoundary.tsx +102 -0
- package/src/Feedback/Toast.tsx +31 -0
- package/src/Feedback/ToastRegion.tsx +26 -0
- package/src/Feedback/UndoToast.tsx +97 -0
- package/src/FeedbackWidget/README.md +3 -0
- package/src/Field/Field.tsx +81 -0
- package/src/FileDrop/README.md +3 -0
- package/src/FilePreviewer/README.md +3 -0
- package/src/FilterBuilder/README.md +3 -0
- package/src/ForceUpdateScreen/README.md +3 -0
- package/src/Forms/Checkbox.tsx +14 -0
- package/src/Forms/CurrencyInput.tsx +40 -0
- package/src/Forms/Forms.shared.tsx +5 -0
- package/src/Forms/Forms.tsx +20 -0
- package/src/Forms/Forms.types.ts +33 -0
- package/src/Forms/Input.tsx +28 -0
- package/src/Forms/Money.tsx +15 -0
- package/src/Forms/NumberInput.tsx +85 -0
- package/src/Forms/Radio.tsx +31 -0
- package/src/Forms/Select.tsx +54 -0
- package/src/Forms/Switch.tsx +14 -0
- package/src/Forms/Textarea.tsx +30 -0
- package/src/Gallery/README.md +5 -0
- package/src/GateLifecycleRail/README.md +7 -0
- package/src/GateReadinessPanel/README.md +8 -0
- package/src/Gateway/AgencySlate.tsx +76 -0
- package/src/Gateway/ApplicationForm.tsx +48 -0
- package/src/Gateway/AvailabilityCalendar.tsx +82 -0
- package/src/Gateway/EPKViewer.tsx +77 -0
- package/src/Gateway/EngagementTimeline.tsx +43 -0
- package/src/Gateway/Gateway.shared.tsx +5 -0
- package/src/Gateway/Gateway.tsx +30 -0
- package/src/Gateway/Gateway.types.ts +69 -0
- package/src/Gateway/OnboardingPacket.tsx +61 -0
- package/src/Gateway/OpportunityCard.tsx +73 -0
- package/src/Gateway/OpportunityFilters.tsx +64 -0
- package/src/Gateway/PaymentTracker.tsx +71 -0
- package/src/Gateway/PayoutDetailsForm.tsx +62 -0
- package/src/Gateway/ProfileEditor.tsx +45 -0
- package/src/Gateway/ProgressBar.tsx +30 -0
- package/src/Gateway/RatingDialog.tsx +83 -0
- package/src/Gateway/UpNextCard.tsx +76 -0
- package/src/Glyphs/Glyphs.tsx +66 -0
- package/src/Grids/BidSheet.tsx +63 -0
- package/src/Grids/BudgetGrid.tsx +164 -0
- package/src/Grids/Grids.shared.tsx +5 -0
- package/src/Grids/Grids.tsx +11 -0
- package/src/Grids/Grids.types.ts +38 -0
- package/src/Grids/SpreadsheetGrid.tsx +445 -0
- package/src/Grids/columnTotal.tsx +14 -0
- package/src/Grids/glForCode.tsx +11 -0
- package/src/Grids/parseCell.tsx +16 -0
- package/src/Help/FeedbackWidget.tsx +89 -0
- package/src/Help/Help.tsx +14 -0
- package/src/Help/Help.types.ts +51 -0
- package/src/Help/HelpPanel.tsx +116 -0
- package/src/Help/NotificationCenter.tsx +100 -0
- package/src/Help/OnboardingChecklist.tsx +57 -0
- package/src/Help/ShortcutEditor.tsx +81 -0
- package/src/Help/WhatsNew.tsx +33 -0
- package/src/HelpPanel/README.md +5 -0
- package/src/HoverCard/README.md +3 -0
- package/src/Icon/Icon.tsx +47 -0
- package/src/Icon/README.md +9 -0
- package/src/IconButton/README.md +6 -0
- package/src/Identity/BrandSlot.tsx +20 -0
- package/src/Identity/BrandSlotFrame.tsx +38 -0
- package/src/Identity/CredentialBadge.tsx +92 -0
- package/src/Identity/Identity.tsx +14 -0
- package/src/Identity/Identity.types.ts +25 -0
- package/src/Identity/PresenceIndicator.tsx +37 -0
- package/src/Identity/QRCode.tsx +22 -0
- package/src/Identity/WhiteLabelPreview.tsx +33 -0
- package/src/Identity/qrPath.tsx +16 -0
- package/src/Identity/tokenVariable.tsx +9 -0
- package/src/InlineAlert/README.md +3 -0
- package/src/Input/README.md +8 -0
- package/src/Kbd/Kbd.tsx +30 -0
- package/src/Kbd/README.md +6 -0
- package/src/Layout/AppShell.tsx +151 -0
- package/src/Layout/Layout.tsx +14 -0
- package/src/Layout/Layout.types.ts +43 -0
- package/src/Layout/PageHeader.tsx +22 -0
- package/src/Layout/ResizablePanel.tsx +81 -0
- package/src/Layout/ScrollRegion.tsx +107 -0
- package/src/Layout/SplitView.tsx +78 -0
- package/src/Layout/Stepper.tsx +39 -0
- package/src/LegalText/LegalText.tsx +289 -0
- package/src/LegalText/README.md +10 -0
- package/src/LegalText/index.ts +29 -0
- package/src/LegalText/legal-markdown.ts +581 -0
- package/src/Link/Link.tsx +25 -0
- package/src/Link/README.md +5 -0
- package/src/Link/link-class.ts +10 -0
- package/src/LiveActivity/README.md +3 -0
- package/src/LogoSlotWall/README.md +7 -0
- package/src/MapView/README.md +5 -0
- package/src/Marketing/AIDisclosureBadge.tsx +31 -0
- package/src/Marketing/ButtonLink.tsx +32 -0
- package/src/Marketing/CodeSample.tsx +47 -0
- package/src/Marketing/ComparisonScroll.tsx +7 -0
- package/src/Marketing/ComparisonTable.tsx +104 -0
- package/src/Marketing/FAQ.tsx +26 -0
- package/src/Marketing/FeatureGrid.tsx +65 -0
- package/src/Marketing/GateLifecycleRail.tsx +52 -0
- package/src/Marketing/LogoSlotWall.tsx +53 -0
- package/src/Marketing/Marketing.tsx +49 -0
- package/src/Marketing/Marketing.types.ts +64 -0
- package/src/Marketing/MarketingFooter.tsx +88 -0
- package/src/Marketing/MarketingHero.tsx +45 -0
- package/src/Marketing/MarketingNav.tsx +76 -0
- package/src/Marketing/MarketingPoints.tsx +16 -0
- package/src/Marketing/MarketingSection.tsx +52 -0
- package/src/Marketing/PersonaSwitcher.tsx +137 -0
- package/src/Marketing/PriceDisplay.tsx +56 -0
- package/src/Marketing/PricingTable.tsx +122 -0
- package/src/Marketing/ProofStatStrip.tsx +35 -0
- package/src/Marketing/ScreenshotFrame.tsx +56 -0
- package/src/Marketing/SurfaceTriptych.tsx +43 -0
- package/src/Marketing/Testimonial.tsx +45 -0
- package/src/Marketing/marketing-copy.ts +25 -0
- package/src/Marketing/provided.tsx +36 -0
- package/src/MarketingFooter/README.md +7 -0
- package/src/MarketingHero/README.md +7 -0
- package/src/MarketingNav/README.md +7 -0
- package/src/MarketingSection/README.md +7 -0
- package/src/Media/FilePreviewer.tsx +86 -0
- package/src/Media/MapView.tsx +200 -0
- package/src/Media/Media.tsx +11 -0
- package/src/Media/Media.types.ts +47 -0
- package/src/Media/RichTextEditor.tsx +198 -0
- package/src/Media/toLngLat.tsx +13 -0
- package/src/Menus/ContextMenu.tsx +106 -0
- package/src/Menus/DropdownMenu.tsx +51 -0
- package/src/Menus/Menus.shared.tsx +67 -0
- package/src/Menus/Menus.tsx +13 -0
- package/src/Menus/Menus.types.ts +49 -0
- package/src/Menus/OrgSwitcher.tsx +69 -0
- package/src/Menus/compactTriggerClass.tsx +7 -0
- package/src/Menus/menuClass.tsx +4 -0
- package/src/Menus/menuItemClass.tsx +7 -0
- package/src/Menus/orgSwitcherTriggerClass.tsx +7 -0
- package/src/Money/README.md +6 -0
- package/src/MultiSelect/README.md +3 -0
- package/src/Navigation/Accordion.tsx +59 -0
- package/src/Navigation/Breadcrumbs.tsx +59 -0
- package/src/Navigation/MoreFields.tsx +42 -0
- package/src/Navigation/Navigation.shared.tsx +32 -0
- package/src/Navigation/Navigation.tsx +21 -0
- package/src/Navigation/Navigation.types.ts +63 -0
- package/src/Navigation/Pagination.tsx +30 -0
- package/src/Navigation/SegmentedControl.tsx +87 -0
- package/src/Navigation/Sidebar.tsx +106 -0
- package/src/Navigation/TabBar.tsx +61 -0
- package/src/Navigation/Tabs.tsx +79 -0
- package/src/Navigation/TopNav.tsx +86 -0
- package/src/Navigation/sidebar-classes.ts +18 -0
- package/src/NotificationCenter/README.md +3 -0
- package/src/NumberInput/README.md +3 -0
- package/src/OfflineBanner/README.md +3 -0
- package/src/OnboardingChecklist/README.md +3 -0
- package/src/OnboardingPacket/README.md +3 -0
- package/src/OpportunityCard/README.md +5 -0
- package/src/OpportunityFilters/README.md +3 -0
- package/src/OrgChart/README.md +5 -0
- package/src/OrgSwitcher/README.md +3 -0
- package/src/Overlays/Dialog.tsx +54 -0
- package/src/Overlays/Drawer.tsx +62 -0
- package/src/Overlays/HoverCard.tsx +59 -0
- package/src/Overlays/Overlays.shared.tsx +12 -0
- package/src/Overlays/Overlays.tsx +20 -0
- package/src/Overlays/Overlays.types.ts +66 -0
- package/src/Overlays/Popover.tsx +70 -0
- package/src/Overlays/PropertyList.tsx +19 -0
- package/src/Overlays/ShortcutSheet.tsx +62 -0
- package/src/Overlays/SidePeek.tsx +117 -0
- package/src/Overlays/Tooltip.tsx +73 -0
- package/src/Overlays/panelClass.tsx +6 -0
- package/src/Overlays/popoverPanelClass.tsx +6 -0
- package/src/PageHeader/README.md +3 -0
- package/src/Pagination/README.md +3 -0
- package/src/PaymentTracker/README.md +3 -0
- package/src/PayoutDetailsForm/README.md +3 -0
- package/src/PersonaSwitcher/README.md +7 -0
- package/src/PhaseChip/README.md +5 -0
- package/src/PhaseGlyph/README.md +17 -0
- package/src/PhotoCapture/README.md +3 -0
- package/src/Pickers/Combobox.tsx +91 -0
- package/src/Pickers/DatePicker.tsx +60 -0
- package/src/Pickers/DateRangePicker.tsx +78 -0
- package/src/Pickers/FileDrop.tsx +84 -0
- package/src/Pickers/MultiSelect.tsx +118 -0
- package/src/Pickers/Pickers.shared.tsx +337 -0
- package/src/Pickers/Pickers.tsx +17 -0
- package/src/Pickers/Pickers.types.ts +47 -0
- package/src/Pickers/Slider.tsx +59 -0
- package/src/Pickers/TimePicker.tsx +68 -0
- package/src/Pickers/rankOptions.tsx +12 -0
- package/src/Popover/README.md +3 -0
- package/src/PresenceIndicator/README.md +3 -0
- package/src/PriceDisplay/README.md +7 -0
- package/src/PricingTable/README.md +9 -0
- package/src/ProfileEditor/README.md +3 -0
- package/src/ProgressBar/README.md +3 -0
- package/src/ProofStatStrip/README.md +7 -0
- package/src/PropertyChip/README.md +3 -0
- package/src/ProvenanceBadge/README.md +3 -0
- package/src/QRCode/README.md +6 -0
- package/src/QuickAdd/README.md +5 -0
- package/src/QuickIncidentSheet/README.md +3 -0
- package/src/Radio/README.md +3 -0
- package/src/RadioChannelTable/README.md +7 -0
- package/src/RatingDialog/README.md +3 -0
- package/src/ReconciliationTable/README.md +3 -0
- package/src/RecordKindChip/README.md +3 -0
- package/src/RecordKindGlyph/README.md +34 -0
- package/src/RecordRow/README.md +7 -0
- package/src/RefusalNotice/README.md +5 -0
- package/src/ResizablePanel/README.md +3 -0
- package/src/ResourceSchedule/README.md +6 -0
- package/src/RichTextEditor/README.md +3 -0
- package/src/RichTextEditor/extensions.ts +217 -0
- package/src/RunOfShowLive/README.md +5 -0
- package/src/ScanSheet/README.md +3 -0
- package/src/ScreenshotFrame/README.md +7 -0
- package/src/ScrollRegion/README.md +9 -0
- package/src/SegmentedControl/README.md +5 -0
- package/src/Select/README.md +5 -0
- package/src/ShiftCard/README.md +3 -0
- package/src/ShortcutEditor/README.md +3 -0
- package/src/ShortcutSheet/README.md +3 -0
- package/src/SidePeek/README.md +6 -0
- package/src/Sidebar/README.md +5 -0
- package/src/SignaturePad/README.md +3 -0
- package/src/Skeleton/README.md +3 -0
- package/src/Slider/README.md +3 -0
- package/src/SplitView/README.md +3 -0
- package/src/SpreadsheetGrid/README.md +9 -0
- package/src/StalenessIndicator/README.md +3 -0
- package/src/StatTile/README.md +6 -0
- package/src/StateChip/README.md +6 -0
- package/src/StateIcon/README.md +6 -0
- package/src/StateIcon/StateIcon.tsx +127 -0
- package/src/StatusHeatmap/README.md +15 -0
- package/src/Stepper/README.md +3 -0
- package/src/Surface/Skeleton.tsx +21 -0
- package/src/Surface/Surface.tsx +63 -0
- package/src/SurfaceTriptych/README.md +7 -0
- package/src/Switch/README.md +5 -0
- package/src/SyncConflictSheet/README.md +3 -0
- package/src/TabBar/README.md +7 -0
- package/src/Tabs/README.md +6 -0
- package/src/Tag/README.md +3 -0
- package/src/TemplateAuth/README.md +6 -0
- package/src/TemplateCollection/README.md +6 -0
- package/src/TemplateDashboard/README.md +8 -0
- package/src/TemplateDocument/README.md +5 -0
- package/src/TemplateGatewayShell/README.md +5 -0
- package/src/TemplateGridEditor/README.md +3 -0
- package/src/TemplateRecord/README.md +3 -0
- package/src/TemplateSettings/README.md +5 -0
- package/src/TemplateSplitView/README.md +3 -0
- package/src/TemplateSystem/README.md +5 -0
- package/src/TemplateWizard/README.md +3 -0
- package/src/Templates/TemplateAuth.tsx +16 -0
- package/src/Templates/TemplateCollection.tsx +35 -0
- package/src/Templates/TemplateDashboard.tsx +200 -0
- package/src/Templates/TemplateDocument.tsx +24 -0
- package/src/Templates/TemplateGatewayShell.tsx +36 -0
- package/src/Templates/TemplateGridEditor.tsx +17 -0
- package/src/Templates/TemplateRecord.tsx +31 -0
- package/src/Templates/TemplateSettings.tsx +26 -0
- package/src/Templates/TemplateSplitView.tsx +27 -0
- package/src/Templates/TemplateSystem.tsx +19 -0
- package/src/Templates/TemplateSystemLink.tsx +55 -0
- package/src/Templates/TemplateWizard.tsx +43 -0
- package/src/Templates/Templates.tsx +29 -0
- package/src/Templates/Templates.types.ts +153 -0
- package/src/Testimonial/README.md +7 -0
- package/src/Textarea/README.md +3 -0
- package/src/TimePicker/README.md +5 -0
- package/src/Timeline/README.md +6 -0
- package/src/Toast/README.md +3 -0
- package/src/Tooltip/README.md +3 -0
- package/src/TopNav/README.md +5 -0
- package/src/TreeView/README.md +3 -0
- package/src/URIDChip/README.md +5 -0
- package/src/UndoToast/README.md +3 -0
- package/src/UpNextCard/README.md +6 -0
- package/src/Views/ActivityFeedItem.tsx +52 -0
- package/src/Views/Board.tsx +229 -0
- package/src/Views/BulkActionBar.tsx +76 -0
- package/src/Views/RecordList.tsx +146 -0
- package/src/Views/RecordRow.tsx +132 -0
- package/src/Views/StatTile.tsx +88 -0
- package/src/Views/TreeView.tsx +161 -0
- package/src/Views/Views.tsx +18 -0
- package/src/Views/Views.types.ts +99 -0
- package/src/WhatsNew/README.md +3 -0
- package/src/WhiteLabelPreview/README.md +8 -0
- package/src/XOS/AccessGridMatrix.tsx +111 -0
- package/src/XOS/AssertionRankBadge.tsx +27 -0
- package/src/XOS/CoordinateMatrix.tsx +123 -0
- package/src/XOS/EmergencyCodeCard.tsx +109 -0
- package/src/XOS/GateReadinessPanel.tsx +100 -0
- package/src/XOS/ProvenanceBadge.tsx +19 -0
- package/src/XOS/RadioChannelTable.tsx +50 -0
- package/src/XOS/ReconciliationTable.tsx +64 -0
- package/src/XOS/RunOfShowLive.tsx +97 -0
- package/src/XOS/StalenessIndicator.tsx +31 -0
- package/src/XOS/StatusHeatmap.tsx +200 -0
- package/src/XOS/XOS.shared.tsx +90 -0
- package/src/XOS/XOS.tsx +29 -0
- package/src/XOS/XOS.types.ts +99 -0
- package/src/XOS/resolveStep.tsx +16 -0
- package/src/brand/defaults/README.md +13 -0
- package/src/brand/defaults/app-icon.svg +6 -0
- package/src/brand/defaults/favicon.svg +6 -0
- package/src/brand/defaults/logo-dark.svg +6 -0
- package/src/brand/defaults/logo-light.svg +6 -0
- package/src/brand/defaults/splash.svg +6 -0
- package/src/index.ts +606 -0
- package/src/lib/context.tsx +156 -0
- package/src/lib/cx.ts +4 -0
- package/src/lib/format.ts +200 -0
- package/src/lib/hooks.ts +187 -0
- package/src/lib/i18n.ts +97 -0
- package/src/lib/icons.ts +45 -0
- package/src/lib/live.ts +92 -0
- package/src/lib/preferences.ts +242 -0
- package/src/lib/route-transition.tsx +22 -0
- package/src/lib/selection.ts +92 -0
- package/src/lib/shortcut-targets.ts +125 -0
- package/src/lib/theme.ts +50 -0
- package/src/lib/undo-stack.ts +125 -0
- package/src/marketing.ts +33 -0
- package/src/pwa/action-outcome.tsx +81 -0
- package/src/pwa/analytics.ts +236 -0
- package/src/pwa/browser-push.tsx +132 -0
- package/src/pwa/client.ts +126 -0
- package/src/pwa/index.ts +40 -0
- package/src/pwa/offline-key.ts +18 -0
- package/src/pwa/service-worker.ts +229 -0
- package/src/static.ts +10 -0
- package/src/styles/base.css +208 -0
- package/src/styles/components.css +453 -0
- package/src/styles/index.css +16 -0
- package/src/styles/marketing.css +1260 -0
- package/src/styles/reference.css +5792 -0
- package/src/types/reference.d.ts +1608 -0
- package/tailwind.config.cjs +3 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { Component, type ErrorInfo, type ReactElement, type ReactNode } from "react";
|
|
2
|
+
import { useT } from "../lib/context.tsx";
|
|
3
|
+
import { Icon } from "../Icon/Icon.tsx";
|
|
4
|
+
import { Button } from "../Button/Button.tsx";
|
|
5
|
+
import { TONE_ICON, TONE_ICON_CLASS, alertClass } from "./Feedback.shared.tsx";
|
|
6
|
+
|
|
7
|
+
export interface RegionBoundaryProps {
|
|
8
|
+
children: ReactNode;
|
|
9
|
+
/** Replaces the default title ("This section did not load"), for example with the tile's name. */
|
|
10
|
+
title?: string;
|
|
11
|
+
/** Called once per caught error, for the app's error reporting. Pass-through errors are not reported. */
|
|
12
|
+
onError?: (error: unknown, info: ErrorInfo) => void;
|
|
13
|
+
/**
|
|
14
|
+
* Retry calls this with `reset`, so the app can fetch the region again and clear the error in one
|
|
15
|
+
* step (a router refresh and `reset` in one transition). Without it, Retry calls `reset` alone.
|
|
16
|
+
*/
|
|
17
|
+
onRetry?: (reset: () => void) => void;
|
|
18
|
+
/**
|
|
19
|
+
* Errors that are control flow, not failures (a framework's redirect or not found), which the
|
|
20
|
+
* boundary throws on to the boundaries above it instead of showing Retry.
|
|
21
|
+
*/
|
|
22
|
+
passThrough?: (error: unknown) => boolean;
|
|
23
|
+
/** The error clears when this changes, such as the route's path after a navigation. */
|
|
24
|
+
resetKey?: unknown;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface State {
|
|
28
|
+
failed: boolean;
|
|
29
|
+
error: unknown;
|
|
30
|
+
resetKey: unknown;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* One region of a page (a dashboard tile, a record panel, a peek section) that fails on its own: an
|
|
35
|
+
* error thrown while it renders replaces the region with a short alert and Retry, reported through
|
|
36
|
+
* `onError`, and the rest of the page keeps working. Retry renders the region again.
|
|
37
|
+
*/
|
|
38
|
+
export class RegionBoundary extends Component<RegionBoundaryProps, State> {
|
|
39
|
+
override state: State = { failed: false, error: null, resetKey: this.props.resetKey };
|
|
40
|
+
|
|
41
|
+
static getDerivedStateFromError(error: unknown): Partial<State> {
|
|
42
|
+
return { failed: true, error };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
static getDerivedStateFromProps(props: RegionBoundaryProps, state: State): Partial<State> | null {
|
|
46
|
+
if (Object.is(props.resetKey, state.resetKey)) return null;
|
|
47
|
+
return { failed: false, error: null, resetKey: props.resetKey };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
override componentDidCatch(error: unknown, info: ErrorInfo): void {
|
|
51
|
+
if (this.props.passThrough?.(error)) return;
|
|
52
|
+
this.props.onError?.(error, info);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
reset = (): void => {
|
|
56
|
+
this.setState({ failed: false, error: null });
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
retry = (): void => {
|
|
60
|
+
if (this.props.onRetry) this.props.onRetry(this.reset);
|
|
61
|
+
else this.reset();
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
override render(): ReactNode {
|
|
65
|
+
if (!this.state.failed) return this.props.children;
|
|
66
|
+
// Thrown from render, so the nearest boundary above this one catches it.
|
|
67
|
+
if (this.props.passThrough?.(this.state.error)) throw this.state.error;
|
|
68
|
+
return (
|
|
69
|
+
<RegionError
|
|
70
|
+
{...(this.props.title ? { title: this.props.title } : {})}
|
|
71
|
+
onRetry={this.retry}
|
|
72
|
+
/>
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** What a failed region shows: the danger icon, what happened, that the page still works, and Retry. */
|
|
78
|
+
export function RegionError({
|
|
79
|
+
title,
|
|
80
|
+
onRetry,
|
|
81
|
+
}: {
|
|
82
|
+
title?: string;
|
|
83
|
+
onRetry: () => void;
|
|
84
|
+
}): ReactElement {
|
|
85
|
+
const t = useT();
|
|
86
|
+
return (
|
|
87
|
+
<div className={alertClass} role="alert">
|
|
88
|
+
<span className={TONE_ICON_CLASS.danger}>
|
|
89
|
+
<Icon name={TONE_ICON.danger} />
|
|
90
|
+
</span>
|
|
91
|
+
<div className="flex flex-col items-start gap-8">
|
|
92
|
+
<div className="flex flex-col gap-2">
|
|
93
|
+
<div className="font-semibold">{title ?? t("region.title")}</div>
|
|
94
|
+
<div className="text-text-secondary">{t("region.body")}</div>
|
|
95
|
+
</div>
|
|
96
|
+
<Button size="sm" icon="RefreshCw" onClick={onRetry}>
|
|
97
|
+
{t("action.retry")}
|
|
98
|
+
</Button>
|
|
99
|
+
</div>
|
|
100
|
+
</div>
|
|
101
|
+
);
|
|
102
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { useContext, type ReactElement } from "react";
|
|
2
|
+
import { Button } from "../Button/Button.tsx";
|
|
3
|
+
import { InToastRegion, toastClass } from "./Feedback.shared.tsx";
|
|
4
|
+
import { type ToastProps } from "./Feedback.types.ts";
|
|
5
|
+
|
|
6
|
+
/** Confirms a completed action, with an optional action and its shortcut. Announced politely. */
|
|
7
|
+
export function Toast({
|
|
8
|
+
message,
|
|
9
|
+
actionLabel,
|
|
10
|
+
actionIcon,
|
|
11
|
+
shortcut,
|
|
12
|
+
onAction,
|
|
13
|
+
}: ToastProps): ReactElement {
|
|
14
|
+
const inRegion = useContext(InToastRegion);
|
|
15
|
+
return (
|
|
16
|
+
<div className={toastClass} {...(inRegion ? {} : { role: "status" })}>
|
|
17
|
+
<span>{message}</span>
|
|
18
|
+
{actionLabel ? (
|
|
19
|
+
<Button
|
|
20
|
+
size="sm"
|
|
21
|
+
variant="ghost"
|
|
22
|
+
onClick={onAction}
|
|
23
|
+
{...(actionIcon ? { icon: actionIcon } : {})}
|
|
24
|
+
{...(shortcut ? { shortcut } : {})}
|
|
25
|
+
>
|
|
26
|
+
{actionLabel}
|
|
27
|
+
</Button>
|
|
28
|
+
) : null}
|
|
29
|
+
</div>
|
|
30
|
+
);
|
|
31
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type ReactElement, type ReactNode } from "react";
|
|
2
|
+
import { InToastRegion } from "./Feedback.shared.tsx";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* A polite live region that stacks toasts at the bottom of the viewport. Mount it for the life of
|
|
6
|
+
* the page and render toasts into it: a live region added together with its content is often not
|
|
7
|
+
* announced.
|
|
8
|
+
*/
|
|
9
|
+
export function ToastRegion({
|
|
10
|
+
label,
|
|
11
|
+
children,
|
|
12
|
+
}: {
|
|
13
|
+
label: string;
|
|
14
|
+
children?: ReactNode;
|
|
15
|
+
}): ReactElement {
|
|
16
|
+
return (
|
|
17
|
+
<section
|
|
18
|
+
aria-label={label}
|
|
19
|
+
aria-live="polite"
|
|
20
|
+
aria-atomic="false"
|
|
21
|
+
className="fixed bottom-16 end-16 z-toast flex flex-col items-end gap-8 max-w-[calc(100vw-var(--space-32))]"
|
|
22
|
+
>
|
|
23
|
+
<InToastRegion.Provider value={true}>{children}</InToastRegion.Provider>
|
|
24
|
+
</section>
|
|
25
|
+
);
|
|
26
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import {
|
|
2
|
+
useContext,
|
|
3
|
+
useEffect,
|
|
4
|
+
useRef,
|
|
5
|
+
useState,
|
|
6
|
+
type CSSProperties,
|
|
7
|
+
type ReactElement,
|
|
8
|
+
} from "react";
|
|
9
|
+
import { useT } from "../lib/context.tsx";
|
|
10
|
+
import { Button } from "../Button/Button.tsx";
|
|
11
|
+
import { InToastRegion, toastClass } from "./Feedback.shared.tsx";
|
|
12
|
+
import { type UndoToastProps } from "./Feedback.types.ts";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Replaces a confirmation for a reversible action: 8 seconds with a visible timer, Undo on the
|
|
16
|
+
* button and on Cmd or Ctrl+Z. Shift+Cmd or Ctrl+Z is redo, not undo, and inside a text field the
|
|
17
|
+
* keys stay the field's own. The timer pauses while the pointer is over the toast or focus is in it
|
|
18
|
+
* (WCAG 2.2.1, Timing Adjustable), and resumes with the time that was left.
|
|
19
|
+
*/
|
|
20
|
+
export function UndoToast({
|
|
21
|
+
message,
|
|
22
|
+
seconds = 8,
|
|
23
|
+
onUndo,
|
|
24
|
+
onExpire,
|
|
25
|
+
undoLabel,
|
|
26
|
+
}: UndoToastProps): ReactElement {
|
|
27
|
+
const t = useT();
|
|
28
|
+
const inRegion = useContext(InToastRegion);
|
|
29
|
+
const [done, setDone] = useState(false);
|
|
30
|
+
const [paused, setPaused] = useState(false);
|
|
31
|
+
// Milliseconds still left on the timer, kept across pauses.
|
|
32
|
+
const left = useRef(seconds * 1000);
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
if (done || paused) return undefined;
|
|
35
|
+
const started = Date.now();
|
|
36
|
+
const timer = setTimeout(() => {
|
|
37
|
+
setDone(true);
|
|
38
|
+
onExpire?.();
|
|
39
|
+
}, left.current);
|
|
40
|
+
const onKey = (e: KeyboardEvent) => {
|
|
41
|
+
if (
|
|
42
|
+
(e.metaKey || e.ctrlKey) &&
|
|
43
|
+
!e.shiftKey &&
|
|
44
|
+
e.key.toLowerCase() === "z" &&
|
|
45
|
+
// In a text field (anything editable: :read-write) the keys stay the field's own undo.
|
|
46
|
+
!(e.target as Element).matches?.(":read-write")
|
|
47
|
+
) {
|
|
48
|
+
e.preventDefault();
|
|
49
|
+
setDone(true);
|
|
50
|
+
onUndo?.();
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
document.addEventListener("keydown", onKey);
|
|
54
|
+
return () => {
|
|
55
|
+
clearTimeout(timer);
|
|
56
|
+
left.current = Math.max(0, left.current - (Date.now() - started));
|
|
57
|
+
document.removeEventListener("keydown", onKey);
|
|
58
|
+
};
|
|
59
|
+
}, [done, paused, onUndo, onExpire]);
|
|
60
|
+
return (
|
|
61
|
+
<div
|
|
62
|
+
className={toastClass}
|
|
63
|
+
{...(inRegion ? {} : { role: "status" })}
|
|
64
|
+
onPointerEnter={() => setPaused(true)}
|
|
65
|
+
onPointerLeave={() => setPaused(false)}
|
|
66
|
+
onFocus={() => setPaused(true)}
|
|
67
|
+
onBlur={(e) => {
|
|
68
|
+
if (!e.currentTarget.contains(e.relatedTarget as Node | null)) setPaused(false);
|
|
69
|
+
}}
|
|
70
|
+
>
|
|
71
|
+
<span>{message}</span>
|
|
72
|
+
<Button
|
|
73
|
+
size="sm"
|
|
74
|
+
variant="ghost"
|
|
75
|
+
icon="Undo2"
|
|
76
|
+
shortcut="⌘Z"
|
|
77
|
+
disabled={done}
|
|
78
|
+
onClick={() => {
|
|
79
|
+
setDone(true);
|
|
80
|
+
onUndo?.();
|
|
81
|
+
}}
|
|
82
|
+
>
|
|
83
|
+
{undoLabel ?? t("action.undo")}
|
|
84
|
+
</Button>
|
|
85
|
+
<span
|
|
86
|
+
aria-hidden="true"
|
|
87
|
+
className="xos-anim-drain absolute start-0 bottom-0 h-(--stroke-width-focus) w-full bg-accent"
|
|
88
|
+
style={
|
|
89
|
+
{
|
|
90
|
+
"--xos-seconds": seconds,
|
|
91
|
+
animationPlayState: paused ? "paused" : "running",
|
|
92
|
+
} as CSSProperties
|
|
93
|
+
}
|
|
94
|
+
/>
|
|
95
|
+
</div>
|
|
96
|
+
);
|
|
97
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { useId, type CSSProperties, type ReactElement, type ReactNode } from "react";
|
|
2
|
+
import { cx } from "../lib/cx.ts";
|
|
3
|
+
import { Icon } from "../Icon/Icon.tsx";
|
|
4
|
+
|
|
5
|
+
/** Label, control and help or error, shared by every form field. */
|
|
6
|
+
export const fieldClass = cx("flex flex-col gap-4 w-(--ui-field) max-w-full box-border");
|
|
7
|
+
export const labelClass = cx("text-text-12 font-medium text-text-secondary");
|
|
8
|
+
export const helpClass = cx("text-text-12 text-text-secondary max-w-full");
|
|
9
|
+
export const errorClass = cx("flex items-center gap-4 text-text-12 text-danger-text");
|
|
10
|
+
|
|
11
|
+
/** The bordered control box shared by inputs, selects and triggers. */
|
|
12
|
+
export const controlClass = cx(
|
|
13
|
+
"h-control-md px-8 rounded-control border border-border-control bg-bg-surface text-text-primary",
|
|
14
|
+
"font-sans text-text-13 placeholder:text-text-tertiary",
|
|
15
|
+
"hover:not-focus-visible:border-text-tertiary focus-visible:border-transparent",
|
|
16
|
+
"aria-invalid:border-danger disabled:opacity-disabled disabled:cursor-not-allowed",
|
|
17
|
+
"pointer-coarse:min-h-target-coarse forced-colors:border-[CanvasText]",
|
|
18
|
+
"in-data-[theme=sunlight]:border-stroke-focus",
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
export function useFieldId(id: string | undefined): string {
|
|
22
|
+
const generated = useId();
|
|
23
|
+
return id ?? `xos-${generated.replace(/:/g, "")}`;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Ids for the help or error line that a control's aria-describedby points to. */
|
|
27
|
+
export function describedBy(id: string, help: string | undefined, error: string | undefined) {
|
|
28
|
+
return error ? `${id}-err` : help ? `${id}-help` : undefined;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function widthStyle(width: number | string | undefined): CSSProperties | undefined {
|
|
32
|
+
return width === undefined ? undefined : { inlineSize: width };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface FieldProps {
|
|
36
|
+
id: string;
|
|
37
|
+
label?: ReactNode | undefined;
|
|
38
|
+
help?: ReactNode | undefined;
|
|
39
|
+
error?: ReactNode | undefined;
|
|
40
|
+
width?: number | string | undefined;
|
|
41
|
+
/** Renders the label as a plain caption when the control is not labelable (a group). */
|
|
42
|
+
labelFor?: boolean;
|
|
43
|
+
children: ReactNode;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function Field({
|
|
47
|
+
id,
|
|
48
|
+
label,
|
|
49
|
+
help,
|
|
50
|
+
error,
|
|
51
|
+
width,
|
|
52
|
+
labelFor = true,
|
|
53
|
+
children,
|
|
54
|
+
}: FieldProps): ReactElement {
|
|
55
|
+
return (
|
|
56
|
+
<div className={fieldClass} style={widthStyle(width)}>
|
|
57
|
+
{label ? (
|
|
58
|
+
labelFor ? (
|
|
59
|
+
<label className={labelClass} htmlFor={id} id={`${id}-label`}>
|
|
60
|
+
{label}
|
|
61
|
+
</label>
|
|
62
|
+
) : (
|
|
63
|
+
<span className={labelClass} id={`${id}-label`}>
|
|
64
|
+
{label}
|
|
65
|
+
</span>
|
|
66
|
+
)
|
|
67
|
+
) : null}
|
|
68
|
+
{children}
|
|
69
|
+
{error ? (
|
|
70
|
+
<div className={errorClass} id={`${id}-err`}>
|
|
71
|
+
<Icon name="alert" size={12} />
|
|
72
|
+
{error}
|
|
73
|
+
</div>
|
|
74
|
+
) : help ? (
|
|
75
|
+
<div className={helpClass} id={`${id}-help`}>
|
|
76
|
+
{help}
|
|
77
|
+
</div>
|
|
78
|
+
) : null}
|
|
79
|
+
</div>
|
|
80
|
+
);
|
|
81
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type ReactElement } from "react";
|
|
2
|
+
import { cx } from "../lib/cx.ts";
|
|
3
|
+
import { checkLabelClass } from "./Forms.shared.tsx";
|
|
4
|
+
import { type CheckboxProps } from "./Forms.types.ts";
|
|
5
|
+
|
|
6
|
+
/** A native checkbox with its label; the whole label is the target. */
|
|
7
|
+
export function Checkbox({ label, className, ...rest }: CheckboxProps): ReactElement {
|
|
8
|
+
return (
|
|
9
|
+
<label className={cx(checkLabelClass, className)}>
|
|
10
|
+
<input type="checkbox" {...rest} className="xos-check-input" />
|
|
11
|
+
{label}
|
|
12
|
+
</label>
|
|
13
|
+
);
|
|
14
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { useState, type ReactElement } from "react";
|
|
2
|
+
import { cx } from "../lib/cx.ts";
|
|
3
|
+
import { useT } from "../lib/context.tsx";
|
|
4
|
+
import { Field, controlClass, describedBy, useFieldId } from "../Field/Field.tsx";
|
|
5
|
+
import { type CurrencyInputProps } from "./Forms.types.ts";
|
|
6
|
+
|
|
7
|
+
/** A NULL-aware amount field: blank is unpriced and never coerced to 0. */
|
|
8
|
+
export function CurrencyInput({
|
|
9
|
+
label,
|
|
10
|
+
value,
|
|
11
|
+
currency = "USD",
|
|
12
|
+
help,
|
|
13
|
+
unpricedLabel,
|
|
14
|
+
id,
|
|
15
|
+
onChange,
|
|
16
|
+
}: CurrencyInputProps): ReactElement {
|
|
17
|
+
const t = useT();
|
|
18
|
+
const fieldId = useFieldId(id);
|
|
19
|
+
const [blank, setBlank] = useState(value === null || value === undefined);
|
|
20
|
+
const helpText = help ?? (blank ? t("money.blankHelp") : undefined);
|
|
21
|
+
return (
|
|
22
|
+
<Field id={fieldId} label={label} help={helpText}>
|
|
23
|
+
<div className="flex items-center gap-6">
|
|
24
|
+
<input
|
|
25
|
+
id={fieldId}
|
|
26
|
+
className={cx(controlClass, "flex-1 min-w-0 text-end tabular-nums")}
|
|
27
|
+
inputMode="decimal"
|
|
28
|
+
placeholder={unpricedLabel ?? t("money.unpriced")}
|
|
29
|
+
defaultValue={value === null || value === undefined ? "" : String(value)}
|
|
30
|
+
aria-describedby={describedBy(fieldId, helpText, undefined)}
|
|
31
|
+
onChange={(e) => {
|
|
32
|
+
setBlank(e.target.value.trim() === "");
|
|
33
|
+
onChange?.(e);
|
|
34
|
+
}}
|
|
35
|
+
/>
|
|
36
|
+
<span className="text-text-12 text-text-tertiary">{currency}</span>
|
|
37
|
+
</div>
|
|
38
|
+
</Field>
|
|
39
|
+
);
|
|
40
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export {
|
|
2
|
+
type InputProps,
|
|
3
|
+
type TextareaProps,
|
|
4
|
+
type SelectProps,
|
|
5
|
+
type CheckboxProps,
|
|
6
|
+
type SwitchProps,
|
|
7
|
+
type RadioProps,
|
|
8
|
+
type NumberInputProps,
|
|
9
|
+
type CurrencyInputProps,
|
|
10
|
+
type MoneyProps,
|
|
11
|
+
} from "./Forms.types.ts";
|
|
12
|
+
export { Input } from "./Input.tsx";
|
|
13
|
+
export { Textarea } from "./Textarea.tsx";
|
|
14
|
+
export { Select } from "./Select.tsx";
|
|
15
|
+
export { Checkbox } from "./Checkbox.tsx";
|
|
16
|
+
export { Switch } from "./Switch.tsx";
|
|
17
|
+
export { Radio } from "./Radio.tsx";
|
|
18
|
+
export { NumberInput } from "./NumberInput.tsx";
|
|
19
|
+
export { Money } from "./Money.tsx";
|
|
20
|
+
export { CurrencyInput } from "./CurrencyInput.tsx";
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
CheckboxProps as ReferenceCheckboxProps,
|
|
3
|
+
CurrencyInputProps as ReferenceCurrencyInputProps,
|
|
4
|
+
InputProps as ReferenceInputProps,
|
|
5
|
+
MoneyProps as ReferenceMoneyProps,
|
|
6
|
+
NumberInputProps as ReferenceNumberInputProps,
|
|
7
|
+
RadioProps as ReferenceRadioProps,
|
|
8
|
+
SelectProps as ReferenceSelectProps,
|
|
9
|
+
SwitchProps as ReferenceSwitchProps,
|
|
10
|
+
TextareaProps as ReferenceTextareaProps,
|
|
11
|
+
} from "../types/reference";
|
|
12
|
+
|
|
13
|
+
export type InputProps = ReferenceInputProps;
|
|
14
|
+
|
|
15
|
+
export type TextareaProps = ReferenceTextareaProps;
|
|
16
|
+
|
|
17
|
+
export type SelectProps = ReferenceSelectProps & { id?: string; name?: string };
|
|
18
|
+
|
|
19
|
+
export type CheckboxProps = ReferenceCheckboxProps;
|
|
20
|
+
|
|
21
|
+
export type SwitchProps = ReferenceSwitchProps;
|
|
22
|
+
|
|
23
|
+
export type RadioProps = ReferenceRadioProps;
|
|
24
|
+
|
|
25
|
+
export type NumberInputProps = ReferenceNumberInputProps & {
|
|
26
|
+
id?: string;
|
|
27
|
+
min?: number;
|
|
28
|
+
max?: number;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export type CurrencyInputProps = ReferenceCurrencyInputProps;
|
|
32
|
+
|
|
33
|
+
export type MoneyProps = ReferenceMoneyProps;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type ReactElement } from "react";
|
|
2
|
+
import { cx } from "../lib/cx.ts";
|
|
3
|
+
import { Field, controlClass, describedBy, useFieldId } from "../Field/Field.tsx";
|
|
4
|
+
import { type InputProps } from "./Forms.types.ts";
|
|
5
|
+
|
|
6
|
+
/** A labeled single-line field with help text or an inline error naming the field, problem and fix. */
|
|
7
|
+
export function Input({
|
|
8
|
+
label,
|
|
9
|
+
help,
|
|
10
|
+
error,
|
|
11
|
+
id,
|
|
12
|
+
width,
|
|
13
|
+
className,
|
|
14
|
+
...rest
|
|
15
|
+
}: InputProps): ReactElement {
|
|
16
|
+
const fieldId = useFieldId(id);
|
|
17
|
+
return (
|
|
18
|
+
<Field id={fieldId} label={label} help={help} error={error} width={width}>
|
|
19
|
+
<input
|
|
20
|
+
id={fieldId}
|
|
21
|
+
aria-invalid={error ? true : undefined}
|
|
22
|
+
aria-describedby={describedBy(fieldId, help, error)}
|
|
23
|
+
{...rest}
|
|
24
|
+
className={cx(controlClass, className)}
|
|
25
|
+
/>
|
|
26
|
+
</Field>
|
|
27
|
+
);
|
|
28
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type ReactElement } from "react";
|
|
2
|
+
import { useFormat, useT } from "../lib/context.tsx";
|
|
3
|
+
import { type MoneyProps } from "./Forms.types.ts";
|
|
4
|
+
|
|
5
|
+
/** An amount with tabular numerals, or Unpriced when null. A blank price is Unpriced; a zero is a claim. */
|
|
6
|
+
export function Money({ amount, currency, unpricedLabel }: MoneyProps): ReactElement {
|
|
7
|
+
const t = useT();
|
|
8
|
+
const fmt = useFormat();
|
|
9
|
+
if (amount === null || amount === undefined) {
|
|
10
|
+
return (
|
|
11
|
+
<span className="text-text-secondary not-italic">{unpricedLabel ?? t("money.unpriced")}</span>
|
|
12
|
+
);
|
|
13
|
+
}
|
|
14
|
+
return <span className="tabular-nums">{fmt.currency(amount, currency)}</span>;
|
|
15
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { useState, type ChangeEvent, type ReactElement } from "react";
|
|
2
|
+
import { cx } from "../lib/cx.ts";
|
|
3
|
+
import { useT } from "../lib/context.tsx";
|
|
4
|
+
import { Icon } from "../Icon/Icon.tsx";
|
|
5
|
+
import { iconButtonClass } from "../Button/Button.tsx";
|
|
6
|
+
import { Field, controlClass, describedBy, useFieldId } from "../Field/Field.tsx";
|
|
7
|
+
import { type NumberInputProps } from "./Forms.types.ts";
|
|
8
|
+
|
|
9
|
+
function parseNumber(text: string): number | null {
|
|
10
|
+
if (text.trim() === "") return null;
|
|
11
|
+
const n = Number(text);
|
|
12
|
+
return Number.isFinite(n) ? n : null;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** A number with units and steppers. Blank stays null, never 0; Arrow Up and Down step. */
|
|
16
|
+
export function NumberInput({
|
|
17
|
+
label,
|
|
18
|
+
help,
|
|
19
|
+
value,
|
|
20
|
+
unit,
|
|
21
|
+
step = 1,
|
|
22
|
+
placeholder: hint,
|
|
23
|
+
onChange,
|
|
24
|
+
id,
|
|
25
|
+
min,
|
|
26
|
+
max,
|
|
27
|
+
}: NumberInputProps): ReactElement {
|
|
28
|
+
const t = useT();
|
|
29
|
+
const fieldId = useFieldId(id);
|
|
30
|
+
const [current, setCurrent] = useState<number | null>(value ?? null);
|
|
31
|
+
const [text, setText] = useState(value === null || value === undefined ? "" : String(value));
|
|
32
|
+
const clamp = (n: number) => Math.min(max ?? Infinity, Math.max(min ?? -Infinity, n));
|
|
33
|
+
const set = (n: number | null) => {
|
|
34
|
+
const next = n === null ? null : clamp(n);
|
|
35
|
+
setCurrent(next);
|
|
36
|
+
setText(next === null ? "" : String(next));
|
|
37
|
+
onChange?.(next);
|
|
38
|
+
};
|
|
39
|
+
return (
|
|
40
|
+
<Field id={fieldId} label={label} help={help}>
|
|
41
|
+
<div className="flex items-center gap-4">
|
|
42
|
+
<button
|
|
43
|
+
type="button"
|
|
44
|
+
className={iconButtonClass()}
|
|
45
|
+
aria-label={t("number.decrease")}
|
|
46
|
+
onClick={() => set((current ?? 0) - step)}
|
|
47
|
+
>
|
|
48
|
+
<Icon name="Minus" size={14} />
|
|
49
|
+
</button>
|
|
50
|
+
<input
|
|
51
|
+
id={fieldId}
|
|
52
|
+
className={cx(controlClass, "flex-1 min-w-0 text-end tabular-nums")}
|
|
53
|
+
inputMode="decimal"
|
|
54
|
+
value={text}
|
|
55
|
+
placeholder={hint}
|
|
56
|
+
aria-describedby={describedBy(fieldId, help, undefined)}
|
|
57
|
+
onChange={(e: ChangeEvent<HTMLInputElement>) => {
|
|
58
|
+
setText(e.target.value);
|
|
59
|
+
const n = parseNumber(e.target.value);
|
|
60
|
+
setCurrent(n);
|
|
61
|
+
onChange?.(n);
|
|
62
|
+
}}
|
|
63
|
+
onKeyDown={(e) => {
|
|
64
|
+
if (e.key === "ArrowUp") {
|
|
65
|
+
e.preventDefault();
|
|
66
|
+
set((current ?? 0) + step);
|
|
67
|
+
} else if (e.key === "ArrowDown") {
|
|
68
|
+
e.preventDefault();
|
|
69
|
+
set((current ?? 0) - step);
|
|
70
|
+
}
|
|
71
|
+
}}
|
|
72
|
+
/>
|
|
73
|
+
{unit ? <span className="text-text-12 text-text-tertiary">{unit}</span> : null}
|
|
74
|
+
<button
|
|
75
|
+
type="button"
|
|
76
|
+
className={iconButtonClass()}
|
|
77
|
+
aria-label={t("number.increase")}
|
|
78
|
+
onClick={() => set((current ?? 0) + step)}
|
|
79
|
+
>
|
|
80
|
+
<Icon name="Plus" size={14} />
|
|
81
|
+
</button>
|
|
82
|
+
</div>
|
|
83
|
+
</Field>
|
|
84
|
+
);
|
|
85
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { type ReactElement } from "react";
|
|
2
|
+
import { cx } from "../lib/cx.ts";
|
|
3
|
+
import { helpClass, labelClass, useFieldId } from "../Field/Field.tsx";
|
|
4
|
+
import { checkLabelClass } from "./Forms.shared.tsx";
|
|
5
|
+
import { type RadioProps } from "./Forms.types.ts";
|
|
6
|
+
|
|
7
|
+
/** A group of native radios in a fieldset; arrow keys move between options as the platform does. */
|
|
8
|
+
export function Radio({ label, name, value, options, onChange }: RadioProps): ReactElement {
|
|
9
|
+
const groupName = useFieldId(name);
|
|
10
|
+
return (
|
|
11
|
+
<fieldset className="flex flex-col gap-8 m-0 p-0 border-0">
|
|
12
|
+
<legend className={cx(labelClass, "mb-8")}>{label}</legend>
|
|
13
|
+
{options.map((o) => (
|
|
14
|
+
<label key={o.value} className={cx(checkLabelClass, "items-start")}>
|
|
15
|
+
<input
|
|
16
|
+
type="radio"
|
|
17
|
+
name={groupName}
|
|
18
|
+
value={o.value}
|
|
19
|
+
defaultChecked={value === o.value}
|
|
20
|
+
onChange={onChange}
|
|
21
|
+
className="xos-check-input"
|
|
22
|
+
/>
|
|
23
|
+
<span className="flex flex-col">
|
|
24
|
+
<span>{o.label}</span>
|
|
25
|
+
{o.help ? <span className={helpClass}>{o.help}</span> : null}
|
|
26
|
+
</span>
|
|
27
|
+
</label>
|
|
28
|
+
))}
|
|
29
|
+
</fieldset>
|
|
30
|
+
);
|
|
31
|
+
}
|