@jini-ai/ui 0.5.4 → 0.5.6
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/CHANGELOG.md +10 -0
- package/dist/core/theme/validation.js +40 -38
- package/dist/features/a2ui/renderer.js +15 -13
- package/dist/features/about/react/components/AboutTab.js +64 -34
- package/dist/features/about/rules.d.ts +2 -2
- package/dist/features/about/rules.js +156 -129
- package/dist/features/admin-widgets/components/ImagePreviewModal.d.ts +4 -3
- package/dist/features/admin-widgets/components/ImagePreviewModal.js +6 -10
- package/dist/features/admin-widgets/components/InfoTip.d.ts +3 -2
- package/dist/features/admin-widgets/components/InfoTip.hooks.js +2 -0
- package/dist/features/admin-widgets/components/InfoTip.js +34 -7
- package/dist/features/admin-widgets/components/InfoTip.rules.d.ts +13 -0
- package/dist/features/admin-widgets/components/InfoTip.rules.js +43 -10
- package/dist/features/admin-widgets/components/Lightbox/Lightbox.d.ts +30 -0
- package/dist/features/admin-widgets/components/Lightbox/Lightbox.hooks.d.ts +40 -0
- package/dist/features/admin-widgets/components/Lightbox/Lightbox.hooks.js +128 -0
- package/dist/features/admin-widgets/components/Lightbox/Lightbox.js +18 -0
- package/dist/features/admin-widgets/components/MiddleTruncate.d.ts +14 -0
- package/dist/features/admin-widgets/components/MiddleTruncate.js +12 -0
- package/dist/features/admin-widgets/components/MiddleTruncate.rules.d.ts +20 -0
- package/dist/features/admin-widgets/components/MiddleTruncate.rules.js +21 -0
- package/dist/features/admin-widgets/components/ScrollJump/ScrollJump.d.ts +18 -0
- package/dist/features/admin-widgets/components/ScrollJump/ScrollJump.hooks.d.ts +16 -0
- package/dist/features/admin-widgets/components/ScrollJump/ScrollJump.hooks.js +45 -0
- package/dist/features/admin-widgets/components/ScrollJump/ScrollJump.js +18 -0
- package/dist/features/admin-widgets/components/ScrollJump/ScrollJump.rules.d.ts +32 -0
- package/dist/features/admin-widgets/components/ScrollJump/ScrollJump.rules.js +26 -0
- package/dist/features/admin-widgets/components/Select/Select.hooks.d.ts +21 -0
- package/dist/features/admin-widgets/components/Select/Select.hooks.js +24 -2
- package/dist/features/admin-widgets/components/Select/Select.js +4 -3
- package/dist/features/admin-widgets/components/SuggestInput/SuggestInput.hooks.js +29 -14
- package/dist/features/admin-widgets/index.d.ts +14 -2
- package/dist/features/admin-widgets/index.js +7 -1
- package/dist/features/admin-widgets/styles/admin-widgets.css +1 -0
- package/dist/features/asset-grid/react/components/AssetCard.js +7 -1
- package/dist/features/asset-grid/react/components/AssetGrid.d.ts +1 -1
- package/dist/features/asset-grid/react/components/AssetGrid.js +38 -18
- package/dist/features/asset-grid/react/components/useAssetGridFilters.d.ts +21 -0
- package/dist/features/asset-grid/react/components/useAssetGridFilters.js +19 -0
- package/dist/features/asset-grid/react/hooks/useAssetGridKeyboardShortcuts.js +16 -11
- package/dist/features/asset-grid/react/hooks/useAssetGridLiveUpdates.d.ts +2 -0
- package/dist/features/asset-grid/react/hooks/useAssetGridLiveUpdates.js +35 -11
- package/dist/features/asset-tree-browser/react/components/AssetTreeBrowser.d.ts +6 -6
- package/dist/features/asset-tree-browser/react/components/AssetTreeBrowser.js +10 -129
- package/dist/features/asset-tree-browser/react/components/AssetTreeBrowserParts.d.ts +50 -0
- package/dist/features/asset-tree-browser/react/components/AssetTreeBrowserParts.js +95 -0
- package/dist/features/asset-tree-browser/react/components/useAssetTreeBrowserController.d.ts +56 -0
- package/dist/features/asset-tree-browser/react/components/useAssetTreeBrowserController.js +80 -0
- package/dist/features/asset-tree-browser/rules.js +40 -22
- package/dist/features/browser-chrome/rules.js +74 -70
- package/dist/features/charts/browser.js +11 -5
- package/dist/features/charts/react.js +2 -2
- package/dist/features/charts/table.js +2 -2
- package/dist/features/command-palette/react/hooks/useCommandPalette.js +3 -9
- package/dist/features/connectors/ConnectorsBrowser.d.ts +4 -4
- package/dist/features/connectors/ConnectorsBrowser.js +26 -14
- package/dist/features/connectors/components/ConnectorCard.js +67 -38
- package/dist/features/connectors/components/ConnectorDetailDrawer.js +62 -31
- package/dist/features/connectors/components/ConnectorGrid.js +11 -6
- package/dist/features/connectors/hooks/useConnectorAuthorization.js +6 -0
- package/dist/features/connectors/hooks/useConnectorCatalog.d.ts +2 -0
- package/dist/features/connectors/hooks/useConnectorCatalog.js +32 -13
- package/dist/features/connectors/hooks/useWiredConnectorsBrowser.d.ts +3 -3
- package/dist/features/connectors/rules.d.ts +8 -1
- package/dist/features/connectors/rules.js +42 -20
- package/dist/features/connectors/types.d.ts +11 -0
- package/dist/features/execution/constants.d.ts +4 -10
- package/dist/features/execution/constants.js +9 -94
- package/dist/features/execution/react/components/ByokProviderForm.d.ts +2 -4
- package/dist/features/execution/react/components/ByokProviderForm.js +140 -43
- package/dist/features/execution/react/components/ExecutionTab.d.ts +1 -2
- package/dist/features/execution/react/components/ExecutionTab.js +52 -30
- package/dist/features/execution/react/components/LocalCliAgentCard.js +12 -3
- package/dist/features/execution/react/components/LocalCliAgentList.js +14 -3
- package/dist/features/execution/react/components/LocalCliAgentSettings.js +14 -3
- package/dist/features/execution/react/hooks/useLocalCliAgentCard.js +54 -36
- package/dist/features/execution/react/hooks/useReasoningControl.d.ts +1 -1
- package/dist/features/execution/react/hooks/useReasoningControl.js +116 -75
- package/dist/features/execution/rules.d.ts +13 -2
- package/dist/features/execution/rules.js +51 -11
- package/dist/features/execution/types.d.ts +6 -42
- package/dist/features/execution/types.js +0 -17
- package/dist/features/external-mcp/react/components/ExternalMcpTab.d.ts +6 -2
- package/dist/features/external-mcp/react/components/ExternalMcpTab.js +9 -2
- package/dist/features/file-dropzone/react/components/FileDropzone.js +24 -25
- package/dist/features/file-dropzone/react/components/FileDropzoneLightbox.d.ts +2 -2
- package/dist/features/file-dropzone/react/components/FileDropzoneLightbox.js +35 -30
- package/dist/features/file-dropzone/react/components/FileDropzoneThumbnailGrid.js +30 -24
- package/dist/features/file-dropzone/react/components/FileDropzoneZone.d.ts +14 -0
- package/dist/features/file-dropzone/react/components/FileDropzoneZone.js +26 -0
- package/dist/features/html-editor/canvas-content-wrapper.js +13 -13
- package/dist/features/html-editor/canvas-embed-placeholders.js +46 -37
- package/dist/features/html-editor/css.js +76 -69
- package/dist/features/html-editor/editor-blocks.js +47 -33
- package/dist/features/html-editor/editor-serialization.js +6 -5
- package/dist/features/html-editor/protected-elements.js +39 -28
- package/dist/features/html-editor/react/hooks/useInteractiveHtmlEditor.js +4 -4
- package/dist/features/html-editor/source-splice.js +51 -30
- package/dist/features/i18n/dictionaries/settings-dialog.ja.js +1 -0
- package/dist/features/i18n/dictionaries/settings-dialog.zh-CN.js +1 -0
- package/dist/features/i18n/dictionaries/settings-dialog.zh-TW.js +1 -0
- package/dist/features/i18n/locale.js +20 -16
- package/dist/features/iframe-pool/react/dom-sync.js +8 -6
- package/dist/features/integrations/react/components/CodexInstallToggleButton.js +19 -4
- package/dist/features/integrations/react/components/IntegrationsTab.js +60 -14
- package/dist/features/interactive-ui/interactive-ui.css +1 -1
- package/dist/features/interactive-ui/providers/native/data-table.manifest.d.ts +6 -6
- package/dist/features/interactive-ui/providers/shadcn/data-table.js +10 -7
- package/dist/features/interactive-ui/providers/shadcn/data-table.manifest.d.ts +6 -6
- package/dist/features/lexical-rich-text-editor/mention-parser.js +60 -57
- package/dist/features/lexical-rich-text-editor/react/hooks/useKeyboardCommands.js +33 -38
- package/dist/features/lexical-rich-text-editor/rules.js +87 -74
- package/dist/features/lexical-rich-text-editor/serialize.js +27 -25
- package/dist/features/mcp-ui/secret-card.js +111 -61
- package/dist/features/mcp-ui/surfaces/confirmation.js +39 -21
- package/dist/features/mcp-ui/surfaces/document.d.ts +1 -1
- package/dist/features/mcp-ui/surfaces/document.js +10 -2
- package/dist/features/mcp-ui/surfaces/fields.js +30 -50
- package/dist/features/mcp-ui/surfaces/form.d.ts +6 -0
- package/dist/features/mcp-ui/surfaces/form.js +43 -21
- package/dist/features/mcp-ui/surfaces/outcome.js +30 -20
- package/dist/features/mcp-ui/surfaces/text-input.js +33 -21
- package/dist/features/mcp-ui/surfaces/tokens.d.ts +6 -1
- package/dist/features/mcp-ui/surfaces/tokens.js +5 -2
- package/dist/features/media-providers/constants.d.ts +1 -0
- package/dist/features/media-providers/constants.js +2 -81
- package/dist/features/media-providers/index.d.ts +1 -1
- package/dist/features/media-providers/ports.d.ts +2 -0
- package/dist/features/media-providers/react/components/MediaProviderCard.d.ts +22 -0
- package/dist/features/media-providers/react/components/MediaProviderCard.js +50 -0
- package/dist/features/media-providers/react/components/MediaProvidersTab.d.ts +5 -6
- package/dist/features/media-providers/react/components/MediaProvidersTab.js +40 -72
- package/dist/features/media-providers/react/components/media-providers-tab-labels.d.ts +11 -0
- package/dist/features/media-providers/react/components/media-providers-tab-labels.js +57 -0
- package/dist/features/media-providers/react/hooks/useMediaProvidersTab.d.ts +4 -2
- package/dist/features/media-providers/react/hooks/useMediaProvidersTab.js +37 -23
- package/dist/features/media-providers/rules.js +17 -10
- package/dist/features/media-providers/types.d.ts +4 -22
- package/dist/features/media-providers/types.js +3 -8
- package/dist/features/memory/formatters.js +127 -92
- package/dist/features/memory/react/components/MemoryConnectedPanel.js +92 -36
- package/dist/features/memory/react/components/MemoryList.js +37 -18
- package/dist/features/memory/react/hooks/useMemoryExtractions.store.js +42 -36
- package/dist/features/notifications/react/components/NotificationsTab.js +28 -28
- package/dist/features/observability/long-task.d.ts +0 -12
- package/dist/features/observability/long-task.js +15 -19
- package/dist/features/observability/resource-error.d.ts +0 -10
- package/dist/features/observability/resource-error.js +18 -13
- package/dist/features/observability/stuck-run.js +10 -6
- package/dist/features/observability/telemetry-url.d.ts +6 -0
- package/dist/features/observability/telemetry-url.js +16 -0
- package/dist/features/observability/white-screen.js +12 -24
- package/dist/features/panel-kit/fetch-query/adapter.react.d.ts +2 -2
- package/dist/features/panel-kit/fetch-query/adapter.react.js +4 -2
- package/dist/features/panel-kit/fetch-query/adapter.tanstack.js +46 -33
- package/dist/features/panel-kit/helpers/standing-draft-local-backup.js +3 -1
- package/dist/features/panel-kit/hooks/use-settings-slice.hooks.js +3 -1
- package/dist/features/privacy/react/components/PrivacyTab.js +24 -13
- package/dist/features/progress-card/components/ProgressCard.js +7 -3
- package/dist/features/progress-card/reference-adapters.js +59 -54
- package/dist/features/project-locations/react/components/ProjectLocationsTab.js +24 -15
- package/dist/features/project-locations/react/hooks/useProjectLocationsTab.d.ts +8 -0
- package/dist/features/project-locations/react/hooks/useProjectLocationsTab.js +31 -23
- package/dist/features/resource-dashboard/react/components/ResourceBoard.js +6 -47
- package/dist/features/resource-dashboard/react/components/ResourceBoardView.d.ts +2 -2
- package/dist/features/resource-dashboard/react/components/ResourceBoardView.js +3 -6
- package/dist/features/resource-dashboard/react/components/ResourceBoardViewParts.d.ts +15 -0
- package/dist/features/resource-dashboard/react/components/ResourceBoardViewParts.js +35 -0
- package/dist/features/resource-dashboard/react/components/ResourceCard.js +5 -16
- package/dist/features/resource-dashboard/react/components/ResourceCardParts.d.ts +34 -0
- package/dist/features/resource-dashboard/react/components/ResourceCardParts.js +34 -0
- package/dist/features/resource-dashboard/react/components/ResourceRowList.js +3 -1
- package/dist/features/resource-dashboard/react/components/ResourceRowListItem.js +2 -2
- package/dist/features/resource-dashboard/react/components/ResourceRowListItemParts.d.ts +19 -0
- package/dist/features/resource-dashboard/react/components/ResourceRowListItemParts.js +11 -0
- package/dist/features/resource-dashboard/react/components/ResourceRowListView.d.ts +1 -1
- package/dist/features/resource-dashboard/react/components/ResourceRowListView.js +4 -4
- package/dist/features/resource-dashboard/react/components/ResourceRowListViewParts.d.ts +10 -0
- package/dist/features/resource-dashboard/react/components/ResourceRowListViewParts.js +20 -0
- package/dist/features/resource-dashboard/react/components/resourceBoardProps.d.ts +48 -0
- package/dist/features/resource-dashboard/react/components/resourceBoardProps.js +70 -0
- package/dist/features/resource-dashboard/react/components/resourceRowListProps.d.ts +19 -0
- package/dist/features/resource-dashboard/react/components/resourceRowListProps.js +14 -0
- package/dist/features/resource-dashboard/react/components/translatedErrorLabelProps.d.ts +16 -0
- package/dist/features/resource-dashboard/react/components/translatedErrorLabelProps.js +13 -0
- package/dist/features/resource-dashboard/rules.js +2 -8
- package/dist/features/sketch-editor/dom.js +150 -99
- package/dist/features/sketch-editor/react/components/SketchEditor.js +26 -15
- package/dist/features/skills/react/components/SkillDraftForm.js +13 -19
- package/dist/features/skills/react/components/SkillDraftForm.labels.d.ts +31 -0
- package/dist/features/skills/react/components/SkillDraftForm.labels.js +54 -0
- package/dist/features/skills/react/components/SkillRow.js +38 -18
- package/dist/features/skills/react/components/SkillRow.labels.d.ts +7 -0
- package/dist/features/skills/react/components/SkillRow.labels.js +18 -0
- package/dist/features/skills/react/components/SkillsTab.js +24 -14
- package/dist/features/skills/react/components/SkillsTab.labels.d.ts +7 -0
- package/dist/features/skills/react/components/SkillsTab.labels.js +11 -0
- package/dist/features/skills/rules.js +32 -23
- package/dist/features/source-config-list/agent-handles.js +2 -1
- package/dist/features/source-config-list/dependencies.js +53 -49
- package/dist/features/source-config-list/index.d.ts +3 -3
- package/dist/features/source-config-list/react/components/SourceConfigAddForm.d.ts +6 -5
- package/dist/features/source-config-list/react/components/SourceConfigAddForm.js +38 -7
- package/dist/features/source-config-list/react/components/SourceConfigField.js +69 -25
- package/dist/features/source-config-list/react/components/SourceConfigItemCard.d.ts +6 -5
- package/dist/features/source-config-list/react/components/SourceConfigItemCard.js +76 -20
- package/dist/features/source-config-list/react/components/SourceConfigListView.d.ts +5 -3
- package/dist/features/source-config-list/react/components/SourceConfigListView.js +33 -3
- package/dist/features/source-config-list/react/components/SourceConfigTestControl.js +17 -1
- package/dist/features/source-config-list/react/components/SourceConfigTrustOptions.d.ts +11 -0
- package/dist/features/source-config-list/react/components/SourceConfigTrustOptions.js +12 -0
- package/dist/features/source-config-list/types.d.ts +14 -0
- package/dist/features/tab-launcher-menu/react/components/TabLauncherMenu.js +25 -1
- package/dist/features/tab-launcher-menu/react/hooks/useTabLauncherMenu.js +15 -16
- package/dist/features/tab-strip/react/components/TabBar.js +8 -1
- package/dist/features/tabbed-dialog/react/components/TabbedDialog.js +8 -30
- package/dist/features/tabbed-dialog/react/components/TabbedDialogChrome.d.ts +15 -0
- package/dist/features/tabbed-dialog/react/components/TabbedDialogChrome.js +8 -0
- package/dist/features/tabbed-dialog/react/components/TabbedDialogHeader.d.ts +12 -0
- package/dist/features/tabbed-dialog/react/components/TabbedDialogHeader.js +6 -0
- package/dist/features/tabbed-dialog/react/components/TabbedDialogSidebar.d.ts +27 -0
- package/dist/features/tabbed-dialog/react/components/TabbedDialogSidebar.js +12 -0
- package/dist/features/tabbed-dialog/react/components/tabbedDialogView.d.ts +43 -0
- package/dist/features/tabbed-dialog/react/components/tabbedDialogView.js +59 -0
- package/dist/features/version-manager/dependencies.d.ts +3 -1
- package/dist/features/version-manager/dependencies.js +3 -2
- package/dist/features/version-manager/react/components/VersionManagerModal.js +10 -6
- package/dist/features/version-manager/react/hooks/useVersionManager.js +7 -4
- package/dist/features/viewer-shell/react/components/CommentSidePanel.js +103 -50
- package/dist/features/viewer-shell/react/components/MarkdownSplitPane.js +28 -8
- package/dist/features/viewer-shell/react/hooks/useCommentReorder.js +22 -16
- package/dist/features/viewer-shell/rules.js +19 -23
- package/dist/react/components/CustomSelect.d.ts +38 -1
- package/dist/react/components/CustomSelect.js +94 -38
- package/dist/react/components/OnboardingDropdown.js +20 -8
- package/dist/react/components/RemixIcon.stylesheet.js +3 -1
- package/dist/react/components/Toast.js +13 -2
- package/dist/react/components/TooltipLayer.js +5 -14
- package/dist/react/components/WorkingDirPicker.js +78 -50
- package/dist/react/components/remixicon-font-recovery.js +38 -30
- package/dist/react/components/select-popover.d.ts +8 -6
- package/dist/react/components/select-popover.js +33 -29
- package/dist/react/mcp-ui/McpUiHost.d.ts +1 -1
- package/dist/react/mcp-ui/McpUiHost.js +10 -11
- package/dist/react/mcp-ui/host-message-source.d.ts +14 -2
- package/dist/react/mcp-ui/host-message-source.js +15 -5
- package/dist/react/mcp-ui/mcp-ui-host-frame.d.ts +41 -0
- package/dist/react/mcp-ui/mcp-ui-host-frame.js +39 -0
- package/dist/renderers/annotation-canvas/react/components/AnnotationCanvas.js +97 -69
- package/dist/renderers/preview-modal-shell/react/components/PreviewModalShell.js +59 -30
- package/dist/renderers/preview-modal-shell/react/components/icons.js +4 -1
- package/dist/renderers/react/components/ArtifactView.js +9 -6
- package/dist/renderers/srcdoc/build.d.ts +4 -1
- package/dist/renderers/srcdoc/build.js +62 -48
- package/dist/styles/admin.css +29 -14
- package/dist/styles/lightbox.css +142 -0
- package/dist/styles/variables.css +128 -0
- package/dist/theme/apply.js +9 -5
- package/dist/utils/auto-open-file.d.ts +2 -1
- package/dist/utils/auto-open-file.js +69 -85
- package/dist/utils/markdown-scroll-sync.js +108 -87
- package/dist/utils/polygon-selection.js +25 -26
- package/dist/utils/smooth-scroll-to-top.d.ts +1 -1
- package/dist/utils/smooth-scroll-to-top.js +2 -32
- package/dist/utils/sse.js +26 -26
- package/dist/utils/unit-bezier.d.ts +7 -0
- package/dist/utils/unit-bezier.js +38 -0
- package/package.json +7 -10
- package/dist/features/admin-widgets/components/ImagePreviewModal.hooks.d.ts +0 -13
- package/dist/features/admin-widgets/components/ImagePreviewModal.hooks.js +0 -46
|
@@ -157,13 +157,50 @@ export interface UseCustomSelectResult {
|
|
|
157
157
|
* consumer.
|
|
158
158
|
*/
|
|
159
159
|
export declare function useCustomSelect({ value, options, onChange, portal, placeholder, onOpenChange, }: UseCustomSelectParams, _optional?: Record<string, never>): UseCustomSelectResult;
|
|
160
|
+
export interface CustomSelectOriginTheme {
|
|
161
|
+
'data-admin-theme'?: string;
|
|
162
|
+
'data-color-scheme'?: string;
|
|
163
|
+
'data-theme'?: string;
|
|
164
|
+
}
|
|
165
|
+
/** Resolve theme identity and the nearest scheme separately, so a local light pin beats a dark root.
|
|
166
|
+
* @complexity O(d) time, O(1) space for d DOM ancestors. No host-specific theme ids are needed. */
|
|
167
|
+
export declare function resolveCustomSelectOriginTheme({ trigger }: {
|
|
168
|
+
trigger: HTMLElement | null;
|
|
169
|
+
}, _optional?: Record<string, never>): CustomSelectOriginTheme;
|
|
170
|
+
/**
|
|
171
|
+
* The theme of the place this menu was opened FROM.
|
|
172
|
+
*
|
|
173
|
+
* A portalled menu is a child of `document.body`, so it escapes any `data-theme` set on an ancestor
|
|
174
|
+
* of the trigger — and this package's theming works precisely by declaring `--jini-*` tokens on
|
|
175
|
+
* `[data-theme='light']` / `[data-theme='dark']`. A host that pins a theme on a WRAPPER rather than
|
|
176
|
+
* on `document.documentElement` therefore gets a correctly-themed control and a menu themed by
|
|
177
|
+
* whatever the document happens to be.
|
|
178
|
+
*
|
|
179
|
+
* Measured in a host admin that is light-only and pins `data-theme="light"` on its panel wrapper:
|
|
180
|
+
* on a machine set to dark, the trigger rendered light and its own dropdown rendered dark — one
|
|
181
|
+
* control, two themes, four pixels apart. The host cannot fix that from CSS either, because the
|
|
182
|
+
* tokens resolve on the portal node's ancestors, which it has no way to select.
|
|
183
|
+
*
|
|
184
|
+
* So the menu carries its origin's theme with it. The nearest scheme boundary wins, with
|
|
185
|
+
* canonical `data-color-scheme` taking precedence over legacy `data-theme` on the same node.
|
|
186
|
+
* Theme identity is independent of scheme. Observe the trigger's ancestors while open so a
|
|
187
|
+
* host's live theme/scheme change updates an existing portal too. With neither attribute, leave
|
|
188
|
+
* document-level theming untouched. Only applied when portalling — an inline menu is still
|
|
189
|
+
* inside the subtree and inherits correctly on its own.
|
|
190
|
+
* @complexity O(d) time/observer storage for d ancestors; disposed on close or unmount.
|
|
191
|
+
*/
|
|
192
|
+
export declare function useCustomSelectOriginTheme({ buttonRef, open, portal }: {
|
|
193
|
+
buttonRef: MutableRefObject<HTMLButtonElement | null>;
|
|
194
|
+
open: boolean;
|
|
195
|
+
portal: boolean;
|
|
196
|
+
}, _optional?: Record<string, never>): CustomSelectOriginTheme;
|
|
160
197
|
/**
|
|
161
198
|
* Accessible custom `<select>` replacement with grouped options, full keyboard
|
|
162
199
|
* navigation, and an optional portal-rendered menu. All behavior lives in
|
|
163
200
|
* {@link useCustomSelect}; this component only wires the hook's state to the
|
|
164
201
|
* trigger button and the listbox menu.
|
|
165
202
|
*/
|
|
166
|
-
export declare function CustomSelect({ value, options, onChange, ariaLabel, labelledBy, className, triggerClassName, menuClassName, disabled, placeholder, portal, portalContainer, title, onFocus, menuHeader, onOpenChange, useCustomSelect: useCustomSelectHook, }: CustomSelectProps
|
|
203
|
+
export declare function CustomSelect({ value, options, onChange, ariaLabel, labelledBy, className, triggerClassName, menuClassName, disabled, placeholder, portal, portalContainer, title, onFocus, menuHeader, onOpenChange, useCustomSelect: useCustomSelectHook, }: CustomSelectProps): import("react").JSX.Element;
|
|
167
204
|
/** One row in the listbox: a selectable option button that reports hover
|
|
168
205
|
* (activation) and click (choice) back to {@link CustomSelect}. */
|
|
169
206
|
export declare function CustomSelectOptionButton({ option, selected, active, id, onChoose, onActive, }: {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
// NOTE: verified zero real consumers in the vendored source snapshot before
|
|
3
3
|
// porting (see packages/ui/source-map.md) — shipped anyway since it's small,
|
|
4
4
|
// self-contained, and correct; a host may still find it useful as a select
|
|
@@ -8,7 +8,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
8
8
|
// pattern (see TooltipLayer.tsx). Every seam is exported: pure helpers, the
|
|
9
9
|
// `useCustomSelect` hook that owns all state/refs/effects/handlers, the
|
|
10
10
|
// `CustomSelectOptionButton` leaf, and the dumb `CustomSelect` render.
|
|
11
|
-
import { useEffect, useId, useMemo, useRef, useState } from 'react';
|
|
11
|
+
import { useEffect, useId, useLayoutEffect, useMemo, useRef, useState } from 'react';
|
|
12
12
|
import { createPortal } from 'react-dom';
|
|
13
13
|
import { useSelectPopover, isSelectEventInside, openSelectOnKey, applySelectNavigationKey, resolveSelectPortalContainer } from './select-popover.js';
|
|
14
14
|
import { Icon } from './Icon.js';
|
|
@@ -185,52 +185,108 @@ export function useCustomSelect({ value, options, onChange, portal, placeholder,
|
|
|
185
185
|
onMenuKeyDown,
|
|
186
186
|
};
|
|
187
187
|
}
|
|
188
|
+
/** Scheme attributes from one boundary node: canonical `data-color-scheme` wins; a legacy
|
|
189
|
+
* `data-theme` supplies the scheme only when it is `light`/`dark`, but is always mirrored. */
|
|
190
|
+
function resolveCustomSelectSchemeAttributes({ canonical, legacy }) {
|
|
191
|
+
const scheme = canonical ?? (legacy === 'light' || legacy === 'dark' ? legacy : undefined);
|
|
192
|
+
return {
|
|
193
|
+
...(scheme ? { 'data-color-scheme': scheme } : {}),
|
|
194
|
+
...(canonical || legacy ? { 'data-theme': canonical ?? legacy } : {}),
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
/** Resolve theme identity and the nearest scheme separately, so a local light pin beats a dark root.
|
|
198
|
+
* @complexity O(d) time, O(1) space for d DOM ancestors. No host-specific theme ids are needed. */
|
|
199
|
+
export function resolveCustomSelectOriginTheme({ trigger }, _optional = {}) {
|
|
200
|
+
const identity = trigger?.closest('[data-admin-theme]')?.getAttribute('data-admin-theme');
|
|
201
|
+
const boundary = trigger?.closest('[data-color-scheme], [data-theme]');
|
|
202
|
+
return {
|
|
203
|
+
...(identity ? { 'data-admin-theme': identity } : {}),
|
|
204
|
+
...resolveCustomSelectSchemeAttributes({
|
|
205
|
+
canonical: boundary?.getAttribute('data-color-scheme'),
|
|
206
|
+
legacy: boundary?.getAttribute('data-theme'),
|
|
207
|
+
}),
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* The theme of the place this menu was opened FROM.
|
|
212
|
+
*
|
|
213
|
+
* A portalled menu is a child of `document.body`, so it escapes any `data-theme` set on an ancestor
|
|
214
|
+
* of the trigger — and this package's theming works precisely by declaring `--jini-*` tokens on
|
|
215
|
+
* `[data-theme='light']` / `[data-theme='dark']`. A host that pins a theme on a WRAPPER rather than
|
|
216
|
+
* on `document.documentElement` therefore gets a correctly-themed control and a menu themed by
|
|
217
|
+
* whatever the document happens to be.
|
|
218
|
+
*
|
|
219
|
+
* Measured in a host admin that is light-only and pins `data-theme="light"` on its panel wrapper:
|
|
220
|
+
* on a machine set to dark, the trigger rendered light and its own dropdown rendered dark — one
|
|
221
|
+
* control, two themes, four pixels apart. The host cannot fix that from CSS either, because the
|
|
222
|
+
* tokens resolve on the portal node's ancestors, which it has no way to select.
|
|
223
|
+
*
|
|
224
|
+
* So the menu carries its origin's theme with it. The nearest scheme boundary wins, with
|
|
225
|
+
* canonical `data-color-scheme` taking precedence over legacy `data-theme` on the same node.
|
|
226
|
+
* Theme identity is independent of scheme. Observe the trigger's ancestors while open so a
|
|
227
|
+
* host's live theme/scheme change updates an existing portal too. With neither attribute, leave
|
|
228
|
+
* document-level theming untouched. Only applied when portalling — an inline menu is still
|
|
229
|
+
* inside the subtree and inherits correctly on its own.
|
|
230
|
+
* @complexity O(d) time/observer storage for d ancestors; disposed on close or unmount.
|
|
231
|
+
*/
|
|
232
|
+
export function useCustomSelectOriginTheme({ buttonRef, open, portal }, _optional = {}) {
|
|
233
|
+
const [attributes, setAttributes] = useState({});
|
|
234
|
+
useLayoutEffect(() => {
|
|
235
|
+
if (!open || !portal)
|
|
236
|
+
return;
|
|
237
|
+
const update = () => {
|
|
238
|
+
const next = resolveCustomSelectOriginTheme({ trigger: buttonRef.current });
|
|
239
|
+
setAttributes((current) => current['data-admin-theme'] === next['data-admin-theme']
|
|
240
|
+
&& current['data-color-scheme'] === next['data-color-scheme']
|
|
241
|
+
&& current['data-theme'] === next['data-theme'] ? current : next);
|
|
242
|
+
};
|
|
243
|
+
update();
|
|
244
|
+
const observer = new MutationObserver(update);
|
|
245
|
+
// Observe only the origin chain, not portal nodes: copying attributes must not retrigger itself.
|
|
246
|
+
for (let node = buttonRef.current; node; node = node.parentElement) {
|
|
247
|
+
observer.observe(node, { attributes: true, attributeFilter: ['data-admin-theme', 'data-color-scheme', 'data-theme'] });
|
|
248
|
+
}
|
|
249
|
+
return () => observer.disconnect();
|
|
250
|
+
}, [buttonRef, open, portal]);
|
|
251
|
+
return portal ? attributes : {};
|
|
252
|
+
}
|
|
253
|
+
/** Inline geometry for a portalled menu once it has been measured; an inline menu is laid out by CSS. */
|
|
254
|
+
function customSelectMenuStyle({ portal, position }) {
|
|
255
|
+
return portal && position
|
|
256
|
+
? {
|
|
257
|
+
top: position.top,
|
|
258
|
+
left: position.left,
|
|
259
|
+
width: position.width,
|
|
260
|
+
maxHeight: position.maxHeight,
|
|
261
|
+
}
|
|
262
|
+
: undefined;
|
|
263
|
+
}
|
|
264
|
+
/** Where an open menu renders: inline, or portalled only after its first measurement. */
|
|
265
|
+
function mountCustomSelectMenu({ menu, portal, position, trigger }, { container } = {}) {
|
|
266
|
+
if (!portal)
|
|
267
|
+
return menu;
|
|
268
|
+
return position ? createPortal(menu, resolveSelectPortalContainer({ trigger }, { container })) : null;
|
|
269
|
+
}
|
|
270
|
+
/** The listbox rows: groups render a labelled block of option buttons, flat options render directly. */
|
|
271
|
+
function CustomSelectMenuOptions({ options, value, activeValue, optionIdByValue, onChoose, onActive }) {
|
|
272
|
+
const renderOption = (option) => (_jsx(CustomSelectOptionButton, { option: option, selected: option.value === value, active: option.value === activeValue, id: optionIdByValue.get(option.value), onChoose: onChoose, onActive: onActive }, option.value));
|
|
273
|
+
return (_jsx(_Fragment, { children: options.map((item) => (isCustomSelectGroup(item) ? (_jsxs("div", { className: "jini-select-group", children: [_jsx("div", { className: "jini-select-group-label", children: item.label }), item.options.map(renderOption)] }, `group:${item.label}`)) : renderOption(item))) }));
|
|
274
|
+
}
|
|
188
275
|
/**
|
|
189
276
|
* Accessible custom `<select>` replacement with grouped options, full keyboard
|
|
190
277
|
* navigation, and an optional portal-rendered menu. All behavior lives in
|
|
191
278
|
* {@link useCustomSelect}; this component only wires the hook's state to the
|
|
192
279
|
* trigger button and the listbox menu.
|
|
193
280
|
*/
|
|
194
|
-
export function CustomSelect({ value, options, onChange, ariaLabel, labelledBy, className, triggerClassName, menuClassName, disabled = false, placeholder, portal = true, portalContainer, title, onFocus, menuHeader, onOpenChange, useCustomSelect: useCustomSelectHook = useCustomSelect, }
|
|
281
|
+
export function CustomSelect({ value, options, onChange, ariaLabel, labelledBy, className, triggerClassName, menuClassName, disabled = false, placeholder, portal = true, portalContainer, title, onFocus, menuHeader, onOpenChange, useCustomSelect: useCustomSelectHook = useCustomSelect, }) {
|
|
195
282
|
const { idBase, buttonRef, menuRef, open, activeValue, position, selectedLabel, optionIdByValue, activeOptionId, choose, setActiveValue, toggleOpen, onButtonKeyDown, onMenuKeyDown, } = useCustomSelectHook({ value, options, onChange, portal, placeholder, onOpenChange });
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
*
|
|
199
|
-
* A portalled menu is a child of `document.body`, so it escapes any `data-theme` set on an ancestor
|
|
200
|
-
* of the trigger — and this package's theming works precisely by declaring `--jini-*` tokens on
|
|
201
|
-
* `[data-theme='light']` / `[data-theme='dark']`. A host that pins a theme on a WRAPPER rather than
|
|
202
|
-
* on `document.documentElement` therefore gets a correctly-themed control and a menu themed by
|
|
203
|
-
* whatever the document happens to be.
|
|
204
|
-
*
|
|
205
|
-
* Measured in a host admin that is light-only and pins `data-theme="light"` on its panel wrapper:
|
|
206
|
-
* on a machine set to dark, the trigger rendered light and its own dropdown rendered dark — one
|
|
207
|
-
* control, two themes, four pixels apart. The host cannot fix that from CSS either, because the
|
|
208
|
-
* tokens resolve on the portal node's ancestors, which it has no way to select.
|
|
209
|
-
*
|
|
210
|
-
* So the menu carries its origin's theme with it. Read from the trigger's nearest `[data-theme]`
|
|
211
|
-
* ancestor; `null` when there is none, which leaves document-level theming untouched and every
|
|
212
|
-
* existing caller byte-identical. Only applied when portalling — an inline menu is still inside the
|
|
213
|
-
* subtree and inherits correctly on its own.
|
|
214
|
-
*/
|
|
215
|
-
const originTheme = buttonRef.current?.closest('[data-theme]')?.getAttribute('data-theme') ?? null;
|
|
216
|
-
const menu = (_jsxs("div", { ref: menuRef, id: `${idBase}-menu`, onKeyDown: onMenuKeyDown, ...(portal && originTheme ? { 'data-theme': originTheme } : {}), className: [
|
|
283
|
+
const originTheme = useCustomSelectOriginTheme({ buttonRef, open, portal });
|
|
284
|
+
const menu = (_jsxs("div", { ref: menuRef, id: `${idBase}-menu`, onKeyDown: onMenuKeyDown, ...originTheme, className: [
|
|
217
285
|
'jini-select-menu',
|
|
218
286
|
portal ? 'portal' : 'inline',
|
|
219
287
|
menuClassName,
|
|
220
|
-
].filter(Boolean).join(' '), role: "listbox", "aria-label": ariaLabel, style: portal
|
|
221
|
-
|
|
222
|
-
top: position.top,
|
|
223
|
-
left: position.left,
|
|
224
|
-
width: position.width,
|
|
225
|
-
maxHeight: position.maxHeight,
|
|
226
|
-
}
|
|
227
|
-
: undefined, children: [menuHeader, options.map((item) => {
|
|
228
|
-
if (isCustomSelectGroup(item)) {
|
|
229
|
-
return (_jsxs("div", { className: "jini-select-group", children: [_jsx("div", { className: "jini-select-group-label", children: item.label }), item.options.map((option) => (_jsx(CustomSelectOptionButton, { option: option, selected: option.value === value, active: option.value === activeValue, id: optionIdByValue.get(option.value), onChoose: choose, onActive: setActiveValue }, option.value)))] }, `group:${item.label}`));
|
|
230
|
-
}
|
|
231
|
-
return (_jsx(CustomSelectOptionButton, { option: item, selected: item.value === value, active: item.value === activeValue, id: optionIdByValue.get(item.value), onChoose: choose, onActive: setActiveValue }, item.value));
|
|
232
|
-
})] }));
|
|
233
|
-
return (_jsxs("div", { className: ['jini-select', className].filter(Boolean).join(' '), children: [_jsxs("button", { ref: buttonRef, type: "button", className: ['jini-select-trigger', triggerClassName].filter(Boolean).join(' '), role: "combobox", value: value, "aria-haspopup": "listbox", "aria-expanded": open, "aria-controls": `${idBase}-menu`, "aria-activedescendant": activeOptionId, "aria-describedby": labelledBy, "aria-label": `${ariaLabel}: ${selectedLabel}`, disabled: disabled, title: title, onClick: toggleOpen, onKeyDown: onButtonKeyDown, onFocus: onFocus, children: [_jsx("span", { id: `${idBase}-value`, className: "jini-select-value", children: selectedLabel }), _jsx(Icon, { name: "chevron-down", size: 14 })] }), open ? (portal ? (position ? createPortal(menu, resolveSelectPortalContainer({ trigger: buttonRef.current }, { container: portalContainer })) : null) : menu) : null] }));
|
|
288
|
+
].filter(Boolean).join(' '), role: "listbox", "aria-label": ariaLabel, style: customSelectMenuStyle({ portal, position }), children: [menuHeader, _jsx(CustomSelectMenuOptions, { options: options, value: value, activeValue: activeValue, optionIdByValue: optionIdByValue, onChoose: choose, onActive: setActiveValue })] }));
|
|
289
|
+
return (_jsxs("div", { className: ['jini-select', className].filter(Boolean).join(' '), children: [_jsxs("button", { ref: buttonRef, type: "button", className: ['jini-select-trigger', triggerClassName].filter(Boolean).join(' '), role: "combobox", value: value, "aria-haspopup": "listbox", "aria-expanded": open, "aria-controls": `${idBase}-menu`, "aria-activedescendant": activeOptionId, "aria-describedby": labelledBy, "aria-label": `${ariaLabel}: ${selectedLabel}`, disabled: disabled, title: title, onClick: toggleOpen, onKeyDown: onButtonKeyDown, onFocus: onFocus, children: [_jsx("span", { id: `${idBase}-value`, className: "jini-select-value", children: selectedLabel }), _jsx(Icon, { name: "chevron-down", size: 14 })] }), open ? mountCustomSelectMenu({ menu, portal, position, trigger: buttonRef.current }, { container: portalContainer }) : null] }));
|
|
234
290
|
}
|
|
235
291
|
/** One row in the listbox: a selectable option button that reports hover
|
|
236
292
|
* (activation) and click (choice) back to {@link CustomSelect}. */
|
|
@@ -237,6 +237,25 @@ export function useOnboardingDropdown(props) {
|
|
|
237
237
|
// ---------------------------------------------------------------------------
|
|
238
238
|
// Dumb component
|
|
239
239
|
// ---------------------------------------------------------------------------
|
|
240
|
+
function OnboardingDropdownSearch({ query, setQuery, placeholder, label }) {
|
|
241
|
+
return (_jsxs("label", { className: "onboarding-view__select-search", onClick: (event) => event.stopPropagation(), children: [_jsx(Icon, { name: "search", size: 14 }), _jsx("input", { type: "search", value: query, placeholder: placeholder, "aria-label": label, autoFocus: true, onChange: (event) => setQuery(event.target.value), onKeyDown: (event) => {
|
|
242
|
+
if (event.key !== 'Escape') {
|
|
243
|
+
event.stopPropagation();
|
|
244
|
+
}
|
|
245
|
+
} })] }));
|
|
246
|
+
}
|
|
247
|
+
function OnboardingDropdownOptionButton({ option, selected, selectOption }) {
|
|
248
|
+
return (_jsxs("button", { type: "button", className: `onboarding-view__select-option${selected ? ' is-selected' : ''}`, role: "option", "aria-selected": selected, onClick: () => selectOption(option, selected), children: [_jsx("span", { children: option.label }), selected ? _jsx(Icon, { name: "check", size: 15 }) : null] }));
|
|
249
|
+
}
|
|
250
|
+
function OnboardingDropdownOptions({ label, multiple, visibleOptions, selectedValues, emptyMessage, selectOption }) {
|
|
251
|
+
return (_jsxs("div", { className: "onboarding-view__select-options", role: "listbox", "aria-label": label, "aria-multiselectable": multiple || undefined, children: [visibleOptions.map((option) => (_jsx(OnboardingDropdownOptionButton, { option: option, selected: selectedValues.includes(option.value), selectOption: selectOption }, option.value))), visibleOptions.length === 0 ? (_jsx("div", { className: "onboarding-view__select-empty", children: emptyMessage })) : null] }));
|
|
252
|
+
}
|
|
253
|
+
function OnboardingDropdownTrigger({ open, hasValue, triggerLabel, toggleOpen }) {
|
|
254
|
+
return (_jsxs("button", { type: "button", className: `onboarding-view__select-trigger${open ? ' is-open' : ''}${hasValue ? ' has-value' : ''}`, "aria-haspopup": "listbox", "aria-expanded": open, title: triggerLabel, onClick: toggleOpen, children: [_jsx("span", { children: triggerLabel }), _jsx(Icon, { name: "chevron-down", size: 16 })] }));
|
|
255
|
+
}
|
|
256
|
+
function OnboardingDropdownMenu({ label, placeholder, multiple, searchable, searchPlaceholder, query, setQuery, menuMaxHeight, ...listProps }) {
|
|
257
|
+
return (_jsxs("div", { className: "onboarding-view__select-menu", "data-searchable": searchable || undefined, style: { '--onboarding-select-menu-max-height': `${menuMaxHeight}px` }, children: [searchable ? (_jsx(OnboardingDropdownSearch, { query: query, setQuery: setQuery, placeholder: searchPlaceholder || placeholder, label: searchPlaceholder || label })) : null, _jsx(OnboardingDropdownOptions, { label: label, multiple: multiple, ...listProps })] }));
|
|
258
|
+
}
|
|
240
259
|
/**
|
|
241
260
|
* Searchable/multi-select onboarding dropdown. All behavior lives in
|
|
242
261
|
* {@link useOnboardingDropdown}; this component only renders the field,
|
|
@@ -245,13 +264,6 @@ export function useOnboardingDropdown(props) {
|
|
|
245
264
|
export function OnboardingDropdown(props) {
|
|
246
265
|
const { label, placeholder, multiple = false, searchable = false, searchPlaceholder, sourceTone, className, useOnboardingDropdown: useOnboardingDropdownHook = useOnboardingDropdown, } = props;
|
|
247
266
|
const { open, query, setQuery, toggleOpen, rootRef, resolvedPlacement, menuMaxHeight, selectedValues, hasValue, triggerLabel, visibleOptions, emptyMessage, selectOption, } = useOnboardingDropdownHook(props);
|
|
248
|
-
return (_jsxs("div", { className: ['onboarding-view__select-field', className].filter(Boolean).join(' '), "data-placement": resolvedPlacement, "data-open": open || undefined, ref: rootRef, children: [_jsx("span", { className: "onboarding-view__select-label", "data-source-tone": sourceTone || undefined, children: label }),
|
|
249
|
-
if (event.key !== 'Escape') {
|
|
250
|
-
event.stopPropagation();
|
|
251
|
-
}
|
|
252
|
-
} })] })) : null, _jsxs("div", { className: "onboarding-view__select-options", role: "listbox", "aria-label": label, "aria-multiselectable": multiple || undefined, children: [visibleOptions.map((option) => {
|
|
253
|
-
const selected = selectedValues.includes(option.value);
|
|
254
|
-
return (_jsxs("button", { type: "button", className: `onboarding-view__select-option${selected ? ' is-selected' : ''}`, role: "option", "aria-selected": selected, onClick: () => selectOption(option, selected), children: [_jsx("span", { children: option.label }), selected ? _jsx(Icon, { name: "check", size: 15 }) : null] }, option.value));
|
|
255
|
-
}), visibleOptions.length === 0 ? (_jsx("div", { className: "onboarding-view__select-empty", children: emptyMessage })) : null] })] })) : null] }));
|
|
267
|
+
return (_jsxs("div", { className: ['onboarding-view__select-field', className].filter(Boolean).join(' '), "data-placement": resolvedPlacement, "data-open": open || undefined, ref: rootRef, children: [_jsx("span", { className: "onboarding-view__select-label", "data-source-tone": sourceTone || undefined, children: label }), _jsx(OnboardingDropdownTrigger, { open: open, hasValue: hasValue, triggerLabel: triggerLabel, toggleOpen: toggleOpen }), open ? (_jsx(OnboardingDropdownMenu, { label: label, placeholder: placeholder, multiple: multiple, searchable: searchable, searchPlaceholder: searchPlaceholder, query: query, setQuery: setQuery, menuMaxHeight: menuMaxHeight, visibleOptions: visibleOptions, selectedValues: selectedValues, emptyMessage: emptyMessage, selectOption: selectOption })) : null] }));
|
|
256
268
|
}
|
|
257
269
|
//# sourceMappingURL=OnboardingDropdown.js.map
|
|
@@ -13,7 +13,9 @@ function recoverFont({ stylesheet }) {
|
|
|
13
13
|
createFontFace: ({ family, source, descriptors }) => new FontFace(family, source, descriptors),
|
|
14
14
|
timers: {
|
|
15
15
|
setTimeout: (run, delay) => globalThis.setTimeout(run, delay),
|
|
16
|
-
|
|
16
|
+
// Only the recovery's stop handle clears a timer, and this module never stops a recovery (it
|
|
17
|
+
// lives as long as the page), so this is a plain binding rather than a wrapper of its own.
|
|
18
|
+
clearTimeout: globalThis.clearTimeout.bind(globalThis),
|
|
17
19
|
},
|
|
18
20
|
});
|
|
19
21
|
if (sheet.sheet) {
|
|
@@ -116,6 +116,18 @@ export function useToastAutoDismiss(options) {
|
|
|
116
116
|
return { leaving, fireDismiss };
|
|
117
117
|
}
|
|
118
118
|
// --- Dumb component --------------------------------------------------------
|
|
119
|
+
function ToastIcon({ iconName }) {
|
|
120
|
+
return iconName ? (_jsx("span", { className: "jini-toast-icon", "aria-hidden": true, children: _jsx(Icon, { name: iconName, size: 14 }) })) : null;
|
|
121
|
+
}
|
|
122
|
+
function ToastActionButton({ actionLabel, actionAriaLabel, onAction }) {
|
|
123
|
+
return actionLabel && onAction ? (_jsx("button", { type: "button", className: "jini-toast-action", onClick: onAction, "aria-label": actionAriaLabel ?? actionLabel, children: actionLabel })) : null;
|
|
124
|
+
}
|
|
125
|
+
/** A code toast is pinned open, so it gets a labelled "Dismiss" button instead of the close glyph. */
|
|
126
|
+
function ToastDismissButton({ code, onDismiss }) {
|
|
127
|
+
if (!onDismiss)
|
|
128
|
+
return null;
|
|
129
|
+
return code ? (_jsx("button", { type: "button", className: "jini-toast-dismiss", onClick: onDismiss, "aria-label": "Dismiss", children: "Dismiss" })) : (_jsx("button", { type: "button", className: "jini-toast-close", onClick: onDismiss, "aria-label": "Dismiss", children: _jsx(Icon, { name: "close", size: 13 }) }));
|
|
130
|
+
}
|
|
119
131
|
/**
|
|
120
132
|
* Pure render of a transient toast. All timing/state lives in
|
|
121
133
|
* {@link useToastAutoDismiss}; all derivations in the module-level helpers.
|
|
@@ -125,7 +137,6 @@ export function useToastAutoDismiss(options) {
|
|
|
125
137
|
export function Toast({ message, className, details, actionLabel, actionAriaLabel, onAction, code, ttlMs = TOAST_DEFAULT_TTL, onDismiss, role = 'status', tone = 'default', placement = 'bottom', useToastAutoDismiss: useToastAutoDismissHook = useToastAutoDismiss, }) {
|
|
126
138
|
const effectiveTtl = toastEffectiveTtl(code, ttlMs);
|
|
127
139
|
const { leaving } = useToastAutoDismissHook({ message, details, code, effectiveTtl, onDismiss });
|
|
128
|
-
|
|
129
|
-
return (_jsxs("div", { className: toastClassName({ tone, placement, className, leaving }), role: role, "aria-live": toastAriaLive(role), children: [_jsxs("div", { className: "jini-toast-body", children: [iconName ? (_jsx("span", { className: "jini-toast-icon", "aria-hidden": true, children: _jsx(Icon, { name: iconName, size: 14 }) })) : null, _jsx("div", { className: "jini-toast-message", children: message })] }), details ? _jsx("div", { className: "jini-toast-details", children: details }) : null, code ? (_jsx("pre", { className: "jini-toast-code", children: code })) : null, actionLabel && onAction ? (_jsx("button", { type: "button", className: "jini-toast-action", onClick: onAction, "aria-label": actionAriaLabel ?? actionLabel, children: actionLabel })) : null, !code && onDismiss ? (_jsx("button", { type: "button", className: "jini-toast-close", onClick: onDismiss, "aria-label": "Dismiss", children: _jsx(Icon, { name: "close", size: 13 }) })) : null, code && onDismiss ? (_jsx("button", { type: "button", className: "jini-toast-dismiss", onClick: onDismiss, "aria-label": "Dismiss", children: "Dismiss" })) : null] }));
|
|
140
|
+
return (_jsxs("div", { className: toastClassName({ tone, placement, className, leaving }), role: role, "aria-live": toastAriaLive(role), children: [_jsxs("div", { className: "jini-toast-body", children: [_jsx(ToastIcon, { iconName: toastToneIcon(tone) }), _jsx("div", { className: "jini-toast-message", children: message })] }), details ? _jsx("div", { className: "jini-toast-details", children: details }) : null, code ? (_jsx("pre", { className: "jini-toast-code", children: code })) : null, _jsx(ToastActionButton, { actionLabel: actionLabel, actionAriaLabel: actionAriaLabel, onAction: onAction }), _jsx(ToastDismissButton, { code: code, onDismiss: onDismiss })] }));
|
|
130
141
|
}
|
|
131
142
|
//# sourceMappingURL=Toast.js.map
|
|
@@ -210,7 +210,7 @@ export function useTooltipLayer() {
|
|
|
210
210
|
if (target)
|
|
211
211
|
showTooltip(target);
|
|
212
212
|
};
|
|
213
|
-
const
|
|
213
|
+
const onTargetExit = (event) => {
|
|
214
214
|
const target = readTooltipTarget(event.target);
|
|
215
215
|
if (!target)
|
|
216
216
|
return;
|
|
@@ -240,15 +240,6 @@ export function useTooltipLayer() {
|
|
|
240
240
|
}
|
|
241
241
|
suppressNativeTitle(target);
|
|
242
242
|
};
|
|
243
|
-
const onFocusOut = (event) => {
|
|
244
|
-
const target = readTooltipTarget(event.target);
|
|
245
|
-
if (!target)
|
|
246
|
-
return;
|
|
247
|
-
const next = event.relatedTarget;
|
|
248
|
-
if (next instanceof Node && target.contains(next))
|
|
249
|
-
return;
|
|
250
|
-
hideTooltip();
|
|
251
|
-
};
|
|
252
243
|
const onKeyDown = (event) => {
|
|
253
244
|
lastInputRef.current = 'keyboard';
|
|
254
245
|
if (event.key === 'Escape')
|
|
@@ -258,23 +249,23 @@ export function useTooltipLayer() {
|
|
|
258
249
|
}
|
|
259
250
|
};
|
|
260
251
|
document.addEventListener('pointerover', onPointerOver);
|
|
261
|
-
document.addEventListener('pointerout',
|
|
252
|
+
document.addEventListener('pointerout', onTargetExit);
|
|
262
253
|
document.addEventListener('pointerdown', onPointerDown);
|
|
263
254
|
document.addEventListener('pointercancel', onPointerCancel);
|
|
264
255
|
document.addEventListener('click', onClick);
|
|
265
256
|
document.addEventListener('focusin', onFocusIn);
|
|
266
|
-
document.addEventListener('focusout',
|
|
257
|
+
document.addEventListener('focusout', onTargetExit);
|
|
267
258
|
document.addEventListener('keydown', onKeyDown);
|
|
268
259
|
window.addEventListener('resize', scheduleUpdatePosition);
|
|
269
260
|
window.addEventListener('scroll', scheduleUpdatePosition, true);
|
|
270
261
|
return () => {
|
|
271
262
|
document.removeEventListener('pointerover', onPointerOver);
|
|
272
|
-
document.removeEventListener('pointerout',
|
|
263
|
+
document.removeEventListener('pointerout', onTargetExit);
|
|
273
264
|
document.removeEventListener('pointerdown', onPointerDown);
|
|
274
265
|
document.removeEventListener('pointercancel', onPointerCancel);
|
|
275
266
|
document.removeEventListener('click', onClick);
|
|
276
267
|
document.removeEventListener('focusin', onFocusIn);
|
|
277
|
-
document.removeEventListener('focusout',
|
|
268
|
+
document.removeEventListener('focusout', onTargetExit);
|
|
278
269
|
document.removeEventListener('keydown', onKeyDown);
|
|
279
270
|
window.removeEventListener('resize', scheduleUpdatePosition);
|
|
280
271
|
window.removeEventListener('scroll', scheduleUpdatePosition, true);
|
|
@@ -119,22 +119,62 @@ export function useWorkingDirPicker(input) {
|
|
|
119
119
|
toggleRecent: flyout.toggle,
|
|
120
120
|
};
|
|
121
121
|
}
|
|
122
|
+
const MENU_ITEM_SELECTOR = '[role="menuitem"]:not(:disabled)';
|
|
123
|
+
const RECENT_ITEM_SELECTOR = '[data-testid="working-dir-recent"]';
|
|
124
|
+
const RECENT_LIST_SELECTOR = '[data-testid="working-dir-recent-list"]';
|
|
122
125
|
/**
|
|
123
|
-
*
|
|
124
|
-
* "Choose folder"
|
|
125
|
-
*
|
|
126
|
-
* {@link useWorkingDirPicker}. This component is a dumb render.
|
|
126
|
+
* The menu item a navigation key moves focus to, or `null` for any other key. `count` is never 0:
|
|
127
|
+
* the "Choose folder" item is always rendered and never disabled. ArrowUp from `currentIndex` -1
|
|
128
|
+
* (focus on no menu item) yields `count - 2`, pinned as-is by the component tests.
|
|
127
129
|
*/
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
130
|
+
function resolveMenuNavigationIndex({ key, currentIndex, count }) {
|
|
131
|
+
if (key === 'Home')
|
|
132
|
+
return 0;
|
|
133
|
+
if (key === 'End')
|
|
134
|
+
return count - 1;
|
|
135
|
+
if (key === 'ArrowDown')
|
|
136
|
+
return (currentIndex + 1 + count) % count;
|
|
137
|
+
if (key === 'ArrowUp')
|
|
138
|
+
return (currentIndex - 1 + count) % count;
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
/** Arrow/Home/End roving focus across the panel's enabled menu items; true when the key was handled. */
|
|
142
|
+
function handleMenuNavigationKey({ event }) {
|
|
143
|
+
const menuItems = [...event.currentTarget.querySelectorAll(MENU_ITEM_SELECTOR)];
|
|
144
|
+
const currentIndex = menuItems.indexOf(document.activeElement);
|
|
145
|
+
const nextIndex = resolveMenuNavigationIndex({ key: event.key, currentIndex, count: menuItems.length });
|
|
146
|
+
if (nextIndex === null)
|
|
147
|
+
return false;
|
|
148
|
+
event.preventDefault();
|
|
149
|
+
menuItems[nextIndex]?.focus();
|
|
150
|
+
return true;
|
|
151
|
+
}
|
|
152
|
+
/** ArrowRight on "Recent folders" opens its flyout; ArrowLeft inside the flyout closes it again. */
|
|
153
|
+
function handleRecentFlyoutKey({ event, showRecent, hideRecent }) {
|
|
154
|
+
const panel = event.currentTarget;
|
|
155
|
+
if (event.key === 'ArrowRight' && event.target === panel.querySelector(RECENT_ITEM_SELECTOR)) {
|
|
156
|
+
event.preventDefault();
|
|
157
|
+
showRecent();
|
|
158
|
+
window.setTimeout(() => {
|
|
159
|
+
panel.querySelector(`${RECENT_LIST_SELECTOR} [role="menuitem"]`)?.focus();
|
|
160
|
+
}, 0);
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
if (event.key === 'ArrowLeft'
|
|
164
|
+
&& event.target instanceof HTMLElement
|
|
165
|
+
&& event.target.closest(RECENT_LIST_SELECTOR)) {
|
|
166
|
+
event.preventDefault();
|
|
167
|
+
hideRecent();
|
|
168
|
+
panel.querySelector(RECENT_ITEM_SELECTOR)?.focus();
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
/** Focus the first menu item on open; return focus to the trigger after the panel closes. */
|
|
172
|
+
function useWorkingDirFocusHandoff({ open, triggerRef, panelRef }) {
|
|
132
173
|
const wasOpenRef = useRef(false);
|
|
133
|
-
const { open, recentOpen, wrapRef, labels: t, toggle, pick, selectRecent, clear, showRecent, hideRecent, toggleRecent, } = useWorkingDirPickerHook({ onPickDirectory, onSelectRecent, onClear, onOpen, labels });
|
|
134
174
|
useEffect(() => {
|
|
135
175
|
if (open) {
|
|
136
176
|
wasOpenRef.current = true;
|
|
137
|
-
panelRef.current?.querySelector(
|
|
177
|
+
panelRef.current?.querySelector(MENU_ITEM_SELECTOR)?.focus();
|
|
138
178
|
return;
|
|
139
179
|
}
|
|
140
180
|
if (!wasOpenRef.current)
|
|
@@ -142,48 +182,36 @@ export function WorkingDirPicker({ workingDir, recentDirs, onPickDirectory, onSe
|
|
|
142
182
|
wasOpenRef.current = false;
|
|
143
183
|
const timeout = window.setTimeout(() => triggerRef.current?.focus(), 0);
|
|
144
184
|
return () => window.clearTimeout(timeout);
|
|
145
|
-
}, [open]);
|
|
185
|
+
}, [open, panelRef, triggerRef]);
|
|
186
|
+
}
|
|
187
|
+
function WorkingDirTrigger({ triggerRef, workingDir, invalid, open, panelId, t, toggle }) {
|
|
188
|
+
return (_jsx("div", { className: "jini-working-dir-trigger-row", children: _jsxs("button", { ref: triggerRef, type: "button", className: `jini-working-dir-trigger${invalid ? ' invalid' : ''}`, "data-testid": "working-dir-trigger", "aria-haspopup": "menu", "aria-expanded": open, "aria-controls": panelId, title: invalid ? t.missing : (workingDir ?? t.hint), onClick: toggle, children: [_jsx(Icon, { name: "folder", size: 13, className: "jini-working-dir-trigger-icon" }), _jsx("span", { className: "jini-working-dir-trigger-label", children: workingDir ? basename(workingDir) : t.trigger }), _jsx(Icon, { name: "chevron-down", size: 11, className: "jini-working-dir-trigger-chevron" })] }) }));
|
|
189
|
+
}
|
|
190
|
+
function WorkingDirRecentFlyout({ recentDirs, placement, t, selectRecent }) {
|
|
191
|
+
return (_jsx("div", { className: `jini-working-dir-flyout${placement === 'up' ? ' up' : ''}`, role: "menu", "data-testid": "working-dir-recent-list", children: recentDirs.length === 0 ? (_jsx("div", { className: "jini-working-dir-empty", children: t.recentEmpty })) : (recentDirs.map((dir) => (_jsxs("button", { type: "button", role: "menuitem", className: "jini-working-dir-recent-item", title: dir, onClick: () => selectRecent(dir), children: [_jsx(Icon, { name: "folder", size: 13, className: "jini-working-dir-item-icon" }), _jsx("span", { className: "jini-working-dir-recent-name", children: basename(dir) }), _jsx("span", { className: "jini-working-dir-recent-path", children: dir })] }, dir)))) }));
|
|
192
|
+
}
|
|
193
|
+
function WorkingDirPanel({ panelRef, panelId, placement, workingDir, recentDirs, canClear, t, picker, onKeyDown }) {
|
|
194
|
+
const { recentOpen, pick, selectRecent, clear, showRecent, hideRecent, toggleRecent } = picker;
|
|
195
|
+
return (_jsxs("div", { ref: panelRef, id: panelId, className: `jini-working-dir-panel${placement === 'up' ? ' up' : ''}`, role: "menu", "data-testid": "working-dir-panel", onKeyDown: onKeyDown, children: [_jsxs("button", { type: "button", role: "menuitem", className: "jini-working-dir-item", "data-testid": "working-dir-pick", onClick: pick, children: [_jsx(Icon, { name: "folder", size: 14, className: "jini-working-dir-item-icon" }), _jsx("span", { children: workingDir ? t.replace : t.pick })] }), _jsxs("div", { className: "jini-working-dir-submenu-row", onMouseEnter: showRecent, onMouseLeave: hideRecent, children: [_jsxs("button", { type: "button", role: "menuitem", className: "jini-working-dir-item", "aria-haspopup": "menu", "aria-expanded": recentOpen, "data-testid": "working-dir-recent", onClick: toggleRecent, children: [_jsx(Icon, { name: "history", size: 14, className: "jini-working-dir-item-icon" }), _jsx("span", { children: t.recent }), _jsx(Icon, { name: "chevron-right", size: 12, className: "jini-working-dir-item-chevron" })] }), recentOpen ? (_jsx(WorkingDirRecentFlyout, { recentDirs: recentDirs, placement: placement, t: t, selectRecent: selectRecent })) : null] }), canClear ? (_jsxs("button", { type: "button", role: "menuitem", className: "jini-working-dir-item", "data-testid": "working-dir-clear", onClick: clear, children: [_jsx(Icon, { name: "close", size: 14, className: "jini-working-dir-item-icon" }), _jsx("span", { children: t.clear })] })) : null] }));
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Working-directory picker: a borderless trigger that opens a panel with
|
|
199
|
+
* "Choose folder" and a "Recent folders" submenu. Layout is left to the host
|
|
200
|
+
* via `className`; all open/close/keyboard state lives in
|
|
201
|
+
* {@link useWorkingDirPicker}. This component is a dumb render.
|
|
202
|
+
*/
|
|
203
|
+
export function WorkingDirPicker({ workingDir, recentDirs, onPickDirectory, onSelectRecent, onClear, className, placement = 'down', invalid = false, onOpen, labels, useWorkingDirPicker: useWorkingDirPickerHook = useWorkingDirPicker, }) {
|
|
204
|
+
const panelId = useId();
|
|
205
|
+
const triggerRef = useRef(null);
|
|
206
|
+
const panelRef = useRef(null);
|
|
207
|
+
const picker = useWorkingDirPickerHook({ onPickDirectory, onSelectRecent, onClear, onOpen, labels });
|
|
208
|
+
const { open, wrapRef, labels: t, toggle, showRecent, hideRecent } = picker;
|
|
209
|
+
useWorkingDirFocusHandoff({ open, triggerRef, panelRef });
|
|
146
210
|
const handlePanelKeyDown = useCallback((event) => {
|
|
147
|
-
|
|
148
|
-
const currentIndex = menuItems.indexOf(document.activeElement);
|
|
149
|
-
if (event.key === 'ArrowDown'
|
|
150
|
-
|| event.key === 'ArrowUp'
|
|
151
|
-
|| event.key === 'Home'
|
|
152
|
-
|| event.key === 'End') {
|
|
153
|
-
if (menuItems.length === 0)
|
|
154
|
-
return;
|
|
155
|
-
event.preventDefault();
|
|
156
|
-
const nextIndex = event.key === 'Home'
|
|
157
|
-
? 0
|
|
158
|
-
: event.key === 'End'
|
|
159
|
-
? menuItems.length - 1
|
|
160
|
-
: event.key === 'ArrowDown'
|
|
161
|
-
? (currentIndex + 1 + menuItems.length) % menuItems.length
|
|
162
|
-
: (currentIndex - 1 + menuItems.length) % menuItems.length;
|
|
163
|
-
menuItems[nextIndex]?.focus();
|
|
211
|
+
if (handleMenuNavigationKey({ event }))
|
|
164
212
|
return;
|
|
165
|
-
}
|
|
166
|
-
if (event.key === 'ArrowRight'
|
|
167
|
-
&& event.target === panelRef.current?.querySelector('[data-testid="working-dir-recent"]')) {
|
|
168
|
-
event.preventDefault();
|
|
169
|
-
showRecent();
|
|
170
|
-
window.setTimeout(() => {
|
|
171
|
-
panelRef.current
|
|
172
|
-
?.querySelector('[data-testid="working-dir-recent-list"] [role="menuitem"]')
|
|
173
|
-
?.focus();
|
|
174
|
-
}, 0);
|
|
175
|
-
return;
|
|
176
|
-
}
|
|
177
|
-
if (event.key === 'ArrowLeft'
|
|
178
|
-
&& event.target instanceof HTMLElement
|
|
179
|
-
&& event.target.closest('[data-testid="working-dir-recent-list"]')) {
|
|
180
|
-
event.preventDefault();
|
|
181
|
-
hideRecent();
|
|
182
|
-
panelRef.current
|
|
183
|
-
?.querySelector('[data-testid="working-dir-recent"]')
|
|
184
|
-
?.focus();
|
|
185
|
-
}
|
|
213
|
+
handleRecentFlyoutKey({ event, showRecent, hideRecent });
|
|
186
214
|
}, [hideRecent, showRecent]);
|
|
187
|
-
return (_jsxs("div", { ref: wrapRef, className: `jini-working-dir-picker${className ? ` ${className}` : ''}`, "data-testid": "working-dir-picker", children: [_jsx(
|
|
215
|
+
return (_jsxs("div", { ref: wrapRef, className: `jini-working-dir-picker${className ? ` ${className}` : ''}`, "data-testid": "working-dir-picker", children: [_jsx(WorkingDirTrigger, { triggerRef: triggerRef, workingDir: workingDir, invalid: invalid, open: open, panelId: panelId, t: t, toggle: toggle }), open ? (_jsx(WorkingDirPanel, { panelRef: panelRef, panelId: panelId, placement: placement, workingDir: workingDir, recentDirs: recentDirs, canClear: Boolean(workingDir && onClear), t: t, picker: picker, onKeyDown: handlePanelKeyDown })) : null] }));
|
|
188
216
|
}
|
|
189
217
|
//# sourceMappingURL=WorkingDirPicker.js.map
|
|
@@ -66,6 +66,36 @@ export function startRemixIconFontRecovery({ fonts, timers, createFace }, { retr
|
|
|
66
66
|
timers.clearTimeout(pending);
|
|
67
67
|
};
|
|
68
68
|
}
|
|
69
|
+
/** The sheet's rules, or `undefined` when it has none yet or they are cross-origin inaccessible. */
|
|
70
|
+
function readStylesheetRules({ stylesheet }) {
|
|
71
|
+
try {
|
|
72
|
+
return stylesheet.sheet?.cssRules;
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
return undefined;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
function isRemixIconFaceRule(rule) {
|
|
79
|
+
if (rule.type !== 5)
|
|
80
|
+
return false; // CSSRule.FONT_FACE_RULE (no global constructor in SSR/fakes).
|
|
81
|
+
const family = rule.style.getPropertyValue('font-family').replace(/["']/g, '').trim();
|
|
82
|
+
return family === 'remixicon';
|
|
83
|
+
}
|
|
84
|
+
/** The first RemixIcon face's url(...) source, or `undefined` when there is no such face or it has no url(). */
|
|
85
|
+
function findRemixIconFontSource({ rules }) {
|
|
86
|
+
const face = Array.from(rules).find(isRemixIconFaceRule);
|
|
87
|
+
// This package owns one url(...) WOFF2 source, not a general CSS font-source parser.
|
|
88
|
+
return face?.style.getPropertyValue('src').match(/url\(\s*["']?([^"')]+)["']?\s*\)/)?.[1];
|
|
89
|
+
}
|
|
90
|
+
/** The source URL tagged with the attempt number; only http(s) URLs accept a query parameter. */
|
|
91
|
+
function retryFontUrl({ url, attempt }) {
|
|
92
|
+
// A distinct URL also bypasses the browser's cached failed font resource after a restart.
|
|
93
|
+
const freshUrl = new URL(url.href);
|
|
94
|
+
if (freshUrl.protocol === 'http:' || freshUrl.protocol === 'https:') {
|
|
95
|
+
freshUrl.searchParams.set('jini-font-retry', String(attempt));
|
|
96
|
+
}
|
|
97
|
+
return freshUrl;
|
|
98
|
+
}
|
|
69
99
|
/**
|
|
70
100
|
* Recover this package's single WOFF2 face using the owning stylesheet's resolved URL.
|
|
71
101
|
* CSSOM, rather than a bundled-module URL, preserves host asset rewriting and linked CSS bases.
|
|
@@ -75,36 +105,14 @@ export function startRemixIconFontRecovery({ fonts, timers, createFace }, { retr
|
|
|
75
105
|
* @example startRemixIconStylesheetRecovery({ stylesheet, fonts, timers, baseUrl, createFontFace });
|
|
76
106
|
*/
|
|
77
107
|
export function startRemixIconStylesheetRecovery({ stylesheet, fonts, timers, baseUrl, createFontFace }, options = {}) {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
}
|
|
82
|
-
catch {
|
|
108
|
+
const rules = readStylesheetRules({ stylesheet });
|
|
109
|
+
const source = rules && findRemixIconFontSource({ rules });
|
|
110
|
+
if (!source)
|
|
83
111
|
return () => { };
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
continue; // CSSRule.FONT_FACE_RULE (no global constructor in SSR/fakes).
|
|
90
|
-
const style = rule.style;
|
|
91
|
-
const family = style.getPropertyValue('font-family').replace(/["']/g, '').trim();
|
|
92
|
-
if (family !== 'remixicon')
|
|
93
|
-
continue;
|
|
94
|
-
// This package owns one url(...) WOFF2 source, not a general CSS font-source parser.
|
|
95
|
-
const source = style.getPropertyValue('src').match(/url\(\s*["']?([^"')]+)["']?\s*\)/)?.[1];
|
|
96
|
-
if (!source)
|
|
97
|
-
return () => { };
|
|
98
|
-
const url = new URL(source.trim(), stylesheet.sheet?.href ?? baseUrl);
|
|
99
|
-
return startRemixIconFontRecovery({ fonts, timers, createFace({ attempt }) {
|
|
100
|
-
// A distinct URL also bypasses the browser's cached failed font resource after a restart.
|
|
101
|
-
const freshUrl = new URL(url.href);
|
|
102
|
-
if (freshUrl.protocol === 'http:' || freshUrl.protocol === 'https:') {
|
|
103
|
-
freshUrl.searchParams.set('jini-font-retry', String(attempt));
|
|
104
|
-
}
|
|
105
|
-
return createFontFace({ family: 'remixicon', source: `url("${freshUrl.href}") format("woff2")`, descriptors: { display: 'swap' } });
|
|
106
|
-
} }, options);
|
|
107
|
-
}
|
|
108
|
-
return () => { };
|
|
112
|
+
const url = new URL(source.trim(), stylesheet.sheet?.href ?? baseUrl);
|
|
113
|
+
return startRemixIconFontRecovery({ fonts, timers, createFace({ attempt }) {
|
|
114
|
+
const freshUrl = retryFontUrl({ url, attempt });
|
|
115
|
+
return createFontFace({ family: 'remixicon', source: `url("${freshUrl.href}") format("woff2")`, descriptors: { display: 'swap' } });
|
|
116
|
+
} }, options);
|
|
109
117
|
}
|
|
110
118
|
//# sourceMappingURL=remixicon-font-recovery.js.map
|