@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,76 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useId, useState, type ReactElement } from "react";
|
|
4
|
+
import { cx } from "../lib/cx.ts";
|
|
5
|
+
import { useT } from "../lib/context.tsx";
|
|
6
|
+
import { iconButtonClass } from "../Button/button-class.ts";
|
|
7
|
+
import { Icon } from "../Icon/Icon.tsx";
|
|
8
|
+
import { type MarketingNavProps } from "./Marketing.types.ts";
|
|
9
|
+
|
|
10
|
+
function NavLinks({ links }: { links: MarketingNavProps["links"] }): ReactElement {
|
|
11
|
+
return (
|
|
12
|
+
<ul>
|
|
13
|
+
{links.map((link) => (
|
|
14
|
+
<li key={link.label}>
|
|
15
|
+
<a href={link.href} aria-current={link.current ? "page" : undefined}>
|
|
16
|
+
{link.label}
|
|
17
|
+
</a>
|
|
18
|
+
</li>
|
|
19
|
+
))}
|
|
20
|
+
</ul>
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The sticky header of a public page: skip link, brand, section links and the primary call to
|
|
26
|
+
* action. From bp-lg the links sit in the bar; below it they move into a panel opened by a labeled
|
|
27
|
+
* toggle. `actions` stays visible at every width.
|
|
28
|
+
*/
|
|
29
|
+
export function MarketingNav({
|
|
30
|
+
brand,
|
|
31
|
+
homeHref = "/",
|
|
32
|
+
label,
|
|
33
|
+
links,
|
|
34
|
+
actions,
|
|
35
|
+
secondaryActions,
|
|
36
|
+
skipHref = "#main",
|
|
37
|
+
}: MarketingNavProps): ReactElement {
|
|
38
|
+
const t = useT();
|
|
39
|
+
const [open, setOpen] = useState(false);
|
|
40
|
+
const panelId = useId();
|
|
41
|
+
return (
|
|
42
|
+
<header className="xos-mkt-nav">
|
|
43
|
+
<a className="xos-mkt-skip" href={skipHref}>
|
|
44
|
+
{t("mk.skip")}
|
|
45
|
+
</a>
|
|
46
|
+
<div className="xos-mkt-wrap xos-mkt-nav-bar">
|
|
47
|
+
<a className="xos-mkt-nav-brand" href={homeHref}>
|
|
48
|
+
{brand}
|
|
49
|
+
</a>
|
|
50
|
+
<nav className="xos-mkt-nav-links" aria-label={label ?? t("nav.main")}>
|
|
51
|
+
<NavLinks links={links} />
|
|
52
|
+
</nav>
|
|
53
|
+
<div className="xos-mkt-nav-actions">
|
|
54
|
+
{secondaryActions ? (
|
|
55
|
+
<div className="xos-mkt-nav-secondary">{secondaryActions}</div>
|
|
56
|
+
) : null}
|
|
57
|
+
{actions}
|
|
58
|
+
<button
|
|
59
|
+
type="button"
|
|
60
|
+
className={cx(iconButtonClass("ghost"), "xos-mkt-nav-toggle")}
|
|
61
|
+
aria-expanded={open}
|
|
62
|
+
aria-controls={panelId}
|
|
63
|
+
aria-label={t(open ? "mk.nav.close" : "mk.nav.menu")}
|
|
64
|
+
onClick={() => setOpen(!open)}
|
|
65
|
+
>
|
|
66
|
+
<Icon name={open ? "X" : "Menu"} />
|
|
67
|
+
</button>
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
70
|
+
<nav id={panelId} className="xos-mkt-nav-panel" hidden={!open} aria-label={t("mk.nav.menu")}>
|
|
71
|
+
<NavLinks links={links} />
|
|
72
|
+
{secondaryActions ? <div className="xos-mkt-actions">{secondaryActions}</div> : null}
|
|
73
|
+
</nav>
|
|
74
|
+
</header>
|
|
75
|
+
);
|
|
76
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type ReactElement } from "react";
|
|
2
|
+
import { Icon } from "../Icon/Icon.tsx";
|
|
3
|
+
|
|
4
|
+
/** A checked list of short claims, shared by the persona, surface and plan cards. */
|
|
5
|
+
export function MarketingPoints({ points }: { points: string[] }): ReactElement {
|
|
6
|
+
return (
|
|
7
|
+
<ul className="xos-mkt-points">
|
|
8
|
+
{points.map((point) => (
|
|
9
|
+
<li key={point}>
|
|
10
|
+
<Icon name="Check" size={16} />
|
|
11
|
+
<span>{point}</span>
|
|
12
|
+
</li>
|
|
13
|
+
))}
|
|
14
|
+
</ul>
|
|
15
|
+
);
|
|
16
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { type ReactElement } from "react";
|
|
2
|
+
import { cx } from "../lib/cx.ts";
|
|
3
|
+
import { Icon } from "../Icon/Icon.tsx";
|
|
4
|
+
import { type MarketingSectionProps } from "./Marketing.types.ts";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* One section of a public page: eyebrow, heading, the direct answer that opens it, then its
|
|
8
|
+
* content. The heading labels the section when the section has an id.
|
|
9
|
+
*/
|
|
10
|
+
export function MarketingSection({
|
|
11
|
+
id,
|
|
12
|
+
eyebrow,
|
|
13
|
+
glyph,
|
|
14
|
+
title,
|
|
15
|
+
lead,
|
|
16
|
+
badge,
|
|
17
|
+
tone = "canvas",
|
|
18
|
+
align = "start",
|
|
19
|
+
headingLevel = 2,
|
|
20
|
+
children,
|
|
21
|
+
}: MarketingSectionProps): ReactElement {
|
|
22
|
+
const Heading = `h${headingLevel}` as const;
|
|
23
|
+
const titleId = id ? `${id}-title` : undefined;
|
|
24
|
+
return (
|
|
25
|
+
<section
|
|
26
|
+
id={id}
|
|
27
|
+
aria-labelledby={titleId}
|
|
28
|
+
className={cx(
|
|
29
|
+
"xos-mkt-section",
|
|
30
|
+
tone === "surface" && "xos-mkt-section-surface",
|
|
31
|
+
align === "center" && "xos-mkt-center",
|
|
32
|
+
)}
|
|
33
|
+
>
|
|
34
|
+
<div className="xos-mkt-wrap">
|
|
35
|
+
<header className="xos-mkt-section-head">
|
|
36
|
+
{eyebrow ? (
|
|
37
|
+
<p className="xos-mkt-eyebrow">
|
|
38
|
+
{glyph ? <Icon name={glyph} size={14} /> : null}
|
|
39
|
+
{eyebrow}
|
|
40
|
+
</p>
|
|
41
|
+
) : null}
|
|
42
|
+
<Heading id={titleId} className="xos-mkt-display-40">
|
|
43
|
+
{title}
|
|
44
|
+
</Heading>
|
|
45
|
+
{lead ? <p className="xos-mkt-lead">{lead}</p> : null}
|
|
46
|
+
{badge ?? null}
|
|
47
|
+
</header>
|
|
48
|
+
{children}
|
|
49
|
+
</div>
|
|
50
|
+
</section>
|
|
51
|
+
);
|
|
52
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useEffect, useId, useRef, useState, type KeyboardEvent, type ReactElement } from "react";
|
|
4
|
+
import { cx } from "../lib/cx.ts";
|
|
5
|
+
import { Icon } from "../Icon/Icon.tsx";
|
|
6
|
+
import { cardClass } from "../Surface/Surface.tsx";
|
|
7
|
+
import { ButtonLink } from "./ButtonLink.tsx";
|
|
8
|
+
import { MarketingPoints } from "./MarketingPoints.tsx";
|
|
9
|
+
import { type PersonaSwitcherProps } from "./Marketing.types.ts";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Tabs by audience. Every panel is rendered, so each audience's copy is in the document; the hash
|
|
13
|
+
* keeps the choice linkable (#for-finance). Arrow keys, Home and End move between tabs.
|
|
14
|
+
*/
|
|
15
|
+
export function PersonaSwitcher({
|
|
16
|
+
label,
|
|
17
|
+
personas,
|
|
18
|
+
value,
|
|
19
|
+
hash = true,
|
|
20
|
+
hashPrefix = "for-",
|
|
21
|
+
onChange,
|
|
22
|
+
}: PersonaSwitcherProps): ReactElement {
|
|
23
|
+
const base = useId();
|
|
24
|
+
const [current, setCurrent] = useState(value ?? personas[0]?.value ?? "");
|
|
25
|
+
const tabs = useRef(new Map<string, HTMLButtonElement>());
|
|
26
|
+
const values = personas.map((persona) => persona.value).join("\n");
|
|
27
|
+
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
if (!hash) return undefined;
|
|
30
|
+
const read = () => {
|
|
31
|
+
const target = decodeURIComponent(window.location.hash.replace(/^#/, ""));
|
|
32
|
+
const match = values.split("\n").find((v) => hashPrefix + v === target);
|
|
33
|
+
if (match !== undefined) setCurrent(match);
|
|
34
|
+
};
|
|
35
|
+
read();
|
|
36
|
+
window.addEventListener("hashchange", read);
|
|
37
|
+
return () => window.removeEventListener("hashchange", read);
|
|
38
|
+
}, [hash, hashPrefix, values]);
|
|
39
|
+
|
|
40
|
+
const pick = (next: string, focus: boolean) => {
|
|
41
|
+
setCurrent(next);
|
|
42
|
+
if (hash) {
|
|
43
|
+
try {
|
|
44
|
+
window.history.replaceState(null, "", `#${hashPrefix}${next}`);
|
|
45
|
+
} catch {
|
|
46
|
+
// A sandboxed frame refuses history writes; the tab still changes.
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
onChange?.(next);
|
|
50
|
+
if (focus) tabs.current.get(next)?.focus();
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const onKeyDown = (event: KeyboardEvent<HTMLButtonElement>, index: number) => {
|
|
54
|
+
const count = personas.length;
|
|
55
|
+
const rtl = event.currentTarget.closest("[dir]")?.getAttribute("dir") === "rtl";
|
|
56
|
+
const step = event.key === "ArrowRight" ? 1 : event.key === "ArrowLeft" ? -1 : 0;
|
|
57
|
+
const next =
|
|
58
|
+
event.key === "Home"
|
|
59
|
+
? 0
|
|
60
|
+
: event.key === "End"
|
|
61
|
+
? count - 1
|
|
62
|
+
: step !== 0
|
|
63
|
+
? (index + (rtl ? -step : step) + count) % count
|
|
64
|
+
: -1;
|
|
65
|
+
const persona = personas[next];
|
|
66
|
+
if (persona === undefined) return;
|
|
67
|
+
event.preventDefault();
|
|
68
|
+
pick(persona.value, true);
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
return (
|
|
72
|
+
<div className="xos-mkt-persona">
|
|
73
|
+
<div className="xos-mkt-persona-tabs" role="tablist" aria-label={label}>
|
|
74
|
+
{personas.map((persona, index) => {
|
|
75
|
+
const on = current === persona.value;
|
|
76
|
+
return (
|
|
77
|
+
<button
|
|
78
|
+
key={persona.value}
|
|
79
|
+
type="button"
|
|
80
|
+
role="tab"
|
|
81
|
+
id={`${base}t${index}`}
|
|
82
|
+
className="xos-mkt-persona-tab"
|
|
83
|
+
aria-selected={on}
|
|
84
|
+
aria-controls={`${base}p${index}`}
|
|
85
|
+
tabIndex={on ? 0 : -1}
|
|
86
|
+
ref={(el) => {
|
|
87
|
+
if (el) tabs.current.set(persona.value, el);
|
|
88
|
+
else tabs.current.delete(persona.value);
|
|
89
|
+
}}
|
|
90
|
+
onClick={() => pick(persona.value, false)}
|
|
91
|
+
onKeyDown={(event) => onKeyDown(event, index)}
|
|
92
|
+
>
|
|
93
|
+
{persona.icon ? <Icon name={persona.icon} size={14} /> : null}
|
|
94
|
+
{persona.label}
|
|
95
|
+
</button>
|
|
96
|
+
);
|
|
97
|
+
})}
|
|
98
|
+
</div>
|
|
99
|
+
{personas.map((persona, index) => (
|
|
100
|
+
<div
|
|
101
|
+
key={persona.value}
|
|
102
|
+
role="tabpanel"
|
|
103
|
+
id={`${base}p${index}`}
|
|
104
|
+
aria-labelledby={`${base}t${index}`}
|
|
105
|
+
className="xos-mkt-persona-panel"
|
|
106
|
+
hidden={current !== persona.value}
|
|
107
|
+
tabIndex={0}
|
|
108
|
+
>
|
|
109
|
+
<div className="xos-mkt-persona-copy">
|
|
110
|
+
<h3>{persona.title}</h3>
|
|
111
|
+
<p>{persona.body}</p>
|
|
112
|
+
{persona.primary || persona.secondary ? (
|
|
113
|
+
<div className="xos-mkt-actions">
|
|
114
|
+
{persona.primary ? (
|
|
115
|
+
<ButtonLink href={persona.primary.href} variant="primary" size="xl">
|
|
116
|
+
{persona.primary.label}
|
|
117
|
+
</ButtonLink>
|
|
118
|
+
) : null}
|
|
119
|
+
{persona.secondary ? (
|
|
120
|
+
<ButtonLink href={persona.secondary.href} size="xl">
|
|
121
|
+
{persona.secondary.label}
|
|
122
|
+
</ButtonLink>
|
|
123
|
+
) : null}
|
|
124
|
+
</div>
|
|
125
|
+
) : null}
|
|
126
|
+
</div>
|
|
127
|
+
{persona.points ? (
|
|
128
|
+
<div className={cx(cardClass, "xos-mkt-persona-points")}>
|
|
129
|
+
{persona.pointsTitle ? <p className="xos-mkt-label">{persona.pointsTitle}</p> : null}
|
|
130
|
+
<MarketingPoints points={persona.points} />
|
|
131
|
+
</div>
|
|
132
|
+
) : null}
|
|
133
|
+
</div>
|
|
134
|
+
))}
|
|
135
|
+
</div>
|
|
136
|
+
);
|
|
137
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { type ReactElement } from "react";
|
|
2
|
+
import { marketingCopy } from "./marketing-copy.ts";
|
|
3
|
+
import { type PriceDisplayProps } from "./Marketing.types.ts";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* One price with its billing period and tax label. A null amount is unpriced and renders Quoted,
|
|
7
|
+
* never zero; a plan sold by quote shows its starting price with "From". Whole amounts print
|
|
8
|
+
* without decimals.
|
|
9
|
+
*/
|
|
10
|
+
export function PriceDisplay({
|
|
11
|
+
amount,
|
|
12
|
+
currency = "USD",
|
|
13
|
+
period,
|
|
14
|
+
taxMode,
|
|
15
|
+
from = false,
|
|
16
|
+
locale,
|
|
17
|
+
messages,
|
|
18
|
+
}: PriceDisplayProps): ReactElement {
|
|
19
|
+
const copy = marketingCopy({ locale, messages });
|
|
20
|
+
const { t } = copy;
|
|
21
|
+
const unpriced = amount === null || amount === undefined;
|
|
22
|
+
const digits = unpriced || Math.round(amount) === amount ? 0 : 2;
|
|
23
|
+
const money = unpriced
|
|
24
|
+
? null
|
|
25
|
+
: new Intl.NumberFormat(copy.locale, {
|
|
26
|
+
style: "currency",
|
|
27
|
+
currency,
|
|
28
|
+
minimumFractionDigits: digits,
|
|
29
|
+
maximumFractionDigits: digits,
|
|
30
|
+
}).format(amount);
|
|
31
|
+
return (
|
|
32
|
+
<p className="xos-mkt-price">
|
|
33
|
+
<span className="xos-mkt-price-amount tabular-nums">
|
|
34
|
+
{money === null ? (
|
|
35
|
+
<span className="text-text-secondary not-italic">{t("mk.price.quoted")}</span>
|
|
36
|
+
) : from ? (
|
|
37
|
+
t("mk.price.from", { price: money })
|
|
38
|
+
) : (
|
|
39
|
+
money
|
|
40
|
+
)}
|
|
41
|
+
</span>
|
|
42
|
+
{money !== null && period ? (
|
|
43
|
+
<span className="xos-mkt-price-period">
|
|
44
|
+
{t(period === "year" ? "mk.price.perYear" : "mk.price.perMonth")}
|
|
45
|
+
</span>
|
|
46
|
+
) : null}
|
|
47
|
+
{taxMode ? (
|
|
48
|
+
<span className="xos-mkt-price-tax">
|
|
49
|
+
{t(taxMode === "inclusive" ? "mk.price.taxInclusive" : "mk.price.taxExclusive", {
|
|
50
|
+
currency,
|
|
51
|
+
})}
|
|
52
|
+
</span>
|
|
53
|
+
) : null}
|
|
54
|
+
</p>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useState, type CSSProperties, type ReactElement } from "react";
|
|
4
|
+
import { cx } from "../lib/cx.ts";
|
|
5
|
+
import { useLocale, useMessages, useT } from "../lib/context.tsx";
|
|
6
|
+
import { SegmentedControl } from "../Navigation/SegmentedControl.tsx";
|
|
7
|
+
import { cardClass } from "../Surface/Surface.tsx";
|
|
8
|
+
import { ButtonLink } from "./ButtonLink.tsx";
|
|
9
|
+
import { MarketingPoints } from "./MarketingPoints.tsx";
|
|
10
|
+
import { PriceDisplay } from "./PriceDisplay.tsx";
|
|
11
|
+
import { type PricingTableProps } from "./Marketing.types.ts";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Plans side by side. The billing toggle announces its change, the terms sit with the plans, and
|
|
15
|
+
* no plan is preselected. A plan with no price for the period is unpriced and reads Quoted.
|
|
16
|
+
*/
|
|
17
|
+
export function PricingTable({
|
|
18
|
+
periods,
|
|
19
|
+
period,
|
|
20
|
+
currency,
|
|
21
|
+
taxMode,
|
|
22
|
+
plans,
|
|
23
|
+
terms,
|
|
24
|
+
onChange,
|
|
25
|
+
}: PricingTableProps): ReactElement {
|
|
26
|
+
const t = useT();
|
|
27
|
+
const locale = useLocale();
|
|
28
|
+
const messages = useMessages();
|
|
29
|
+
const [selected, setSelected] = useState(period ?? periods[0]?.value ?? "");
|
|
30
|
+
const [announcement, setAnnouncement] = useState("");
|
|
31
|
+
const current = periods.find((p) => p.value === selected) ?? periods[0];
|
|
32
|
+
|
|
33
|
+
const change = (next: string) => {
|
|
34
|
+
setSelected(next);
|
|
35
|
+
const chosen = periods.find((p) => p.value === next);
|
|
36
|
+
if (chosen) setAnnouncement(t("mk.pricing.changed", { period: chosen.label }));
|
|
37
|
+
onChange?.(next);
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<div className="xos-mkt-pricing">
|
|
42
|
+
<div className="xos-mkt-pricing-bar">
|
|
43
|
+
<SegmentedControl
|
|
44
|
+
label={t("mk.pricing.period")}
|
|
45
|
+
value={selected}
|
|
46
|
+
options={periods.map((p) => ({ value: p.value, label: p.label }))}
|
|
47
|
+
onChange={change}
|
|
48
|
+
/>
|
|
49
|
+
{periods.map((p) =>
|
|
50
|
+
p.note ? (
|
|
51
|
+
<span key={p.value} className="xos-mkt-note">
|
|
52
|
+
{p.note}
|
|
53
|
+
</span>
|
|
54
|
+
) : null,
|
|
55
|
+
)}
|
|
56
|
+
<span className="sr-only" role="status" aria-live="polite">
|
|
57
|
+
{announcement}
|
|
58
|
+
</span>
|
|
59
|
+
</div>
|
|
60
|
+
<ul className="xos-mkt-plans" style={{ "--xos-mkt-n": plans.length } as CSSProperties}>
|
|
61
|
+
{plans.map((plan) => {
|
|
62
|
+
const quoted = plan.quoteFrom !== undefined;
|
|
63
|
+
const amount = plan.quoteFrom ?? plan.prices?.[selected] ?? null;
|
|
64
|
+
const free = amount === 0;
|
|
65
|
+
return (
|
|
66
|
+
<li
|
|
67
|
+
key={plan.name}
|
|
68
|
+
className={cx(cardClass, "xos-mkt-plan", plan.recommended && "xos-mkt-plan-rec")}
|
|
69
|
+
>
|
|
70
|
+
<div className="xos-mkt-plan-head">
|
|
71
|
+
<h3>{plan.name}</h3>
|
|
72
|
+
{plan.recommended ? (
|
|
73
|
+
<span className="xos-mkt-tag">{t("mk.pricing.recommended")}</span>
|
|
74
|
+
) : null}
|
|
75
|
+
</div>
|
|
76
|
+
<p className="xos-mkt-plan-blurb">{plan.blurb}</p>
|
|
77
|
+
<PriceDisplay
|
|
78
|
+
amount={amount}
|
|
79
|
+
from={quoted}
|
|
80
|
+
period={free ? null : quoted ? "month" : (current?.unit ?? null)}
|
|
81
|
+
taxMode={free ? null : (taxMode ?? null)}
|
|
82
|
+
locale={locale}
|
|
83
|
+
messages={messages}
|
|
84
|
+
{...(currency === undefined ? {} : { currency })}
|
|
85
|
+
/>
|
|
86
|
+
{plan.note ? <p className="xos-mkt-note">{plan.note}</p> : null}
|
|
87
|
+
{plan.cta ? (
|
|
88
|
+
<ButtonLink
|
|
89
|
+
href={plan.cta.href}
|
|
90
|
+
variant={plan.recommended ? "primary" : "secondary"}
|
|
91
|
+
size="xl"
|
|
92
|
+
className="xos-mkt-plan-cta"
|
|
93
|
+
>
|
|
94
|
+
{plan.cta.label}
|
|
95
|
+
</ButtonLink>
|
|
96
|
+
) : null}
|
|
97
|
+
{plan.limits ? (
|
|
98
|
+
<dl className="xos-mkt-plan-limits">
|
|
99
|
+
{plan.limits.map((limit) => (
|
|
100
|
+
<div key={limit.label}>
|
|
101
|
+
<dt>{limit.label}</dt>
|
|
102
|
+
<dd className="tabular-nums">{limit.value}</dd>
|
|
103
|
+
</div>
|
|
104
|
+
))}
|
|
105
|
+
</dl>
|
|
106
|
+
) : null}
|
|
107
|
+
{plan.features ? (
|
|
108
|
+
<div className="xos-mkt-plan-features">
|
|
109
|
+
{plan.featuresTitle ? (
|
|
110
|
+
<p className="xos-mkt-label">{plan.featuresTitle}</p>
|
|
111
|
+
) : null}
|
|
112
|
+
<MarketingPoints points={plan.features} />
|
|
113
|
+
</div>
|
|
114
|
+
) : null}
|
|
115
|
+
</li>
|
|
116
|
+
);
|
|
117
|
+
})}
|
|
118
|
+
</ul>
|
|
119
|
+
{terms ? <p className="xos-mkt-note xos-mkt-pricing-terms">{terms}</p> : null}
|
|
120
|
+
</div>
|
|
121
|
+
);
|
|
122
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { type CSSProperties, type ReactElement } from "react";
|
|
2
|
+
import { formatNumber } from "../lib/format.ts";
|
|
3
|
+
import { marketingCopy } from "./marketing-copy.ts";
|
|
4
|
+
import { type ProofStatStripProps } from "./Marketing.types.ts";
|
|
5
|
+
|
|
6
|
+
/** Verifiable figures. Each one links to the file that proves it. */
|
|
7
|
+
export function ProofStatStrip({
|
|
8
|
+
label,
|
|
9
|
+
items,
|
|
10
|
+
locale,
|
|
11
|
+
messages,
|
|
12
|
+
}: ProofStatStripProps): ReactElement {
|
|
13
|
+
const copy = marketingCopy({ locale, messages });
|
|
14
|
+
return (
|
|
15
|
+
<ul
|
|
16
|
+
className="xos-mkt-proof"
|
|
17
|
+
aria-label={label}
|
|
18
|
+
style={{ "--xos-mkt-n": items.length } as CSSProperties}
|
|
19
|
+
>
|
|
20
|
+
{items.map((item) => (
|
|
21
|
+
<li key={item.label}>
|
|
22
|
+
<strong className="xos-mkt-proof-value tabular-nums">
|
|
23
|
+
{typeof item.value === "number" ? formatNumber(copy.locale, item.value) : item.value}
|
|
24
|
+
</strong>
|
|
25
|
+
<span className="xos-mkt-proof-label">{item.label}</span>
|
|
26
|
+
{item.source ? (
|
|
27
|
+
<a className="xos-mkt-proof-src" href={item.source.href}>
|
|
28
|
+
{copy.t("mk.proof.source", { label: item.source.label })}
|
|
29
|
+
</a>
|
|
30
|
+
) : null}
|
|
31
|
+
</li>
|
|
32
|
+
))}
|
|
33
|
+
</ul>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { type ReactElement } from "react";
|
|
2
|
+
import { type ScreenshotFrameProps } from "./Marketing.types.ts";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* A product image in a window frame, or live XOS components standing in for one. Live content is
|
|
6
|
+
* inert and exposed as one image named by `alt`, so it adds no tab stops and reads as a picture.
|
|
7
|
+
*/
|
|
8
|
+
export function ScreenshotFrame({
|
|
9
|
+
src,
|
|
10
|
+
alt,
|
|
11
|
+
width,
|
|
12
|
+
height,
|
|
13
|
+
ratio = "16 / 10",
|
|
14
|
+
title,
|
|
15
|
+
chrome = true,
|
|
16
|
+
eager = false,
|
|
17
|
+
caption,
|
|
18
|
+
children,
|
|
19
|
+
}: ScreenshotFrameProps): ReactElement {
|
|
20
|
+
return (
|
|
21
|
+
<figure className="xos-mkt-shot">
|
|
22
|
+
{chrome ? (
|
|
23
|
+
<div className="xos-mkt-shot-bar" aria-hidden="true">
|
|
24
|
+
<i />
|
|
25
|
+
<i />
|
|
26
|
+
<i />
|
|
27
|
+
{title ? <span>{title}</span> : null}
|
|
28
|
+
</div>
|
|
29
|
+
) : null}
|
|
30
|
+
{src ? (
|
|
31
|
+
<div className="xos-mkt-shot-view" style={{ aspectRatio: ratio }}>
|
|
32
|
+
<img
|
|
33
|
+
src={src}
|
|
34
|
+
alt={alt}
|
|
35
|
+
width={width}
|
|
36
|
+
height={height}
|
|
37
|
+
loading={eager ? "eager" : "lazy"}
|
|
38
|
+
decoding="async"
|
|
39
|
+
/>
|
|
40
|
+
</div>
|
|
41
|
+
) : (
|
|
42
|
+
<div
|
|
43
|
+
className="xos-mkt-shot-view xos-mkt-shot-live"
|
|
44
|
+
role="img"
|
|
45
|
+
aria-label={alt}
|
|
46
|
+
style={height === undefined ? undefined : { blockSize: height }}
|
|
47
|
+
>
|
|
48
|
+
<div inert aria-hidden="true">
|
|
49
|
+
{children}
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
)}
|
|
53
|
+
{caption ? <figcaption>{caption}</figcaption> : null}
|
|
54
|
+
</figure>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { type ReactElement } from "react";
|
|
2
|
+
import { cx } from "../lib/cx.ts";
|
|
3
|
+
import { Icon } from "../Icon/Icon.tsx";
|
|
4
|
+
import { Tag } from "../Chips/Tag.tsx";
|
|
5
|
+
import { cardClass } from "../Surface/Surface.tsx";
|
|
6
|
+
import { linkClass } from "../Link/link-class.ts";
|
|
7
|
+
import { MarketingPoints } from "./MarketingPoints.tsx";
|
|
8
|
+
import { type SurfaceTriptychProps } from "./Marketing.types.ts";
|
|
9
|
+
|
|
10
|
+
/** The three surfaces side by side, each with its audience, its status and what it covers. */
|
|
11
|
+
export function SurfaceTriptych({ items }: SurfaceTriptychProps): ReactElement {
|
|
12
|
+
return (
|
|
13
|
+
<ul className="xos-mkt-grid xos-mkt-grid-3">
|
|
14
|
+
{items.map((item) => (
|
|
15
|
+
<li key={item.name} className={cx(cardClass, "xos-mkt-feature")}>
|
|
16
|
+
<div className="xos-mkt-feature-head">
|
|
17
|
+
{item.icon ? (
|
|
18
|
+
<span className="xos-mkt-tile" aria-hidden="true">
|
|
19
|
+
<Icon name={item.icon} size={16} />
|
|
20
|
+
</span>
|
|
21
|
+
) : null}
|
|
22
|
+
<h3>{item.name}</h3>
|
|
23
|
+
{item.status ? (
|
|
24
|
+
<span className="xos-mkt-feature-status">
|
|
25
|
+
<Tag {...(item.status.tone === undefined ? {} : { tone: item.status.tone })}>
|
|
26
|
+
{item.status.label}
|
|
27
|
+
</Tag>
|
|
28
|
+
</span>
|
|
29
|
+
) : null}
|
|
30
|
+
</div>
|
|
31
|
+
{item.audience ? <p className="xos-mkt-feature-aud">{item.audience}</p> : null}
|
|
32
|
+
<p>{item.body}</p>
|
|
33
|
+
{item.points ? <MarketingPoints points={item.points} /> : null}
|
|
34
|
+
{item.link ? (
|
|
35
|
+
<a href={item.link.href} className={cx(linkClass, "xos-mkt-feature-link")}>
|
|
36
|
+
{item.link.label}
|
|
37
|
+
</a>
|
|
38
|
+
) : null}
|
|
39
|
+
</li>
|
|
40
|
+
))}
|
|
41
|
+
</ul>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { type ReactElement } from "react";
|
|
2
|
+
import { cx } from "../lib/cx.ts";
|
|
3
|
+
import { Icon } from "../Icon/Icon.tsx";
|
|
4
|
+
import { avatarClass, initials } from "../Avatar/avatar-class.ts";
|
|
5
|
+
import { cardClass } from "../Surface/Surface.tsx";
|
|
6
|
+
import { type TestimonialProps } from "./Marketing.types.ts";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* A customer's words. The disclosure line is required: it states the speaker's connection to the
|
|
10
|
+
* publisher.
|
|
11
|
+
*/
|
|
12
|
+
export function Testimonial({
|
|
13
|
+
quote,
|
|
14
|
+
name,
|
|
15
|
+
role,
|
|
16
|
+
org,
|
|
17
|
+
disclosure,
|
|
18
|
+
}: TestimonialProps): ReactElement {
|
|
19
|
+
return (
|
|
20
|
+
<figure className={cx(cardClass, "xos-mkt-quote")}>
|
|
21
|
+
<blockquote>
|
|
22
|
+
<p>{quote}</p>
|
|
23
|
+
</blockquote>
|
|
24
|
+
<figcaption>
|
|
25
|
+
<span
|
|
26
|
+
role="img"
|
|
27
|
+
aria-label={name}
|
|
28
|
+
title={name}
|
|
29
|
+
className={cx(avatarClass, "size-(--ui-avatar) text-text-11")}
|
|
30
|
+
>
|
|
31
|
+
{initials(name)}
|
|
32
|
+
</span>
|
|
33
|
+
<span className="xos-mkt-quote-who">
|
|
34
|
+
<strong>{name}</strong>
|
|
35
|
+
<span>{role}</span>
|
|
36
|
+
{org ? <span>{org}</span> : null}
|
|
37
|
+
</span>
|
|
38
|
+
</figcaption>
|
|
39
|
+
<p className="xos-mkt-quote-disclosure">
|
|
40
|
+
<Icon name="Info" size={12} />
|
|
41
|
+
<span>{disclosure}</span>
|
|
42
|
+
</p>
|
|
43
|
+
</figure>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { currentConfig, translate, type Messages, type MessageVars } from "../lib/i18n.ts";
|
|
2
|
+
|
|
3
|
+
/** A locale and a `t` bound to it, for components that render without hooks or context. */
|
|
4
|
+
export interface MarketingCopy {
|
|
5
|
+
locale: string;
|
|
6
|
+
t: (key: string, vars?: MessageVars) => string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Resolves the shared Marketing strings without a provider: the `locale` and `messages` props when
|
|
11
|
+
* the page gives them, otherwise what `configure` set. It reads no context, so a server component
|
|
12
|
+
* can call it (ADR 0027).
|
|
13
|
+
*/
|
|
14
|
+
export function marketingCopy({
|
|
15
|
+
locale,
|
|
16
|
+
messages,
|
|
17
|
+
}: {
|
|
18
|
+
locale: string | undefined;
|
|
19
|
+
messages: Messages | undefined;
|
|
20
|
+
}): MarketingCopy {
|
|
21
|
+
const config = currentConfig();
|
|
22
|
+
const resolved = locale ?? config.locale;
|
|
23
|
+
const merged = messages === undefined ? config.messages : { ...config.messages, ...messages };
|
|
24
|
+
return { locale: resolved, t: (key, vars) => translate(resolved, key, vars, merged) };
|
|
25
|
+
}
|