@celestia-island/hikari 0.4.4 → 0.30.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/README.md +1 -1
- package/package.json +22 -7
- package/src/DemoApp.tsx +491 -0
- package/src/animation/registerAnimations.test.ts +94 -0
- package/src/animation/registerAnimations.ts +103 -0
- package/src/components/HkAboutModal.scss +95 -0
- package/src/components/HkAboutModal.tsx +114 -0
- package/src/components/HkAdaptiveDialog.scss +22 -0
- package/src/components/HkAdaptiveDialog.test.tsx +271 -0
- package/src/components/HkAdaptiveDialog.tsx +167 -0
- package/src/components/HkAdminHeader.test.tsx +185 -0
- package/src/components/HkAdminHeader.tsx +308 -0
- package/src/components/HkAdminShell.test.tsx +237 -0
- package/src/components/HkAdminShell.tsx +129 -0
- package/src/components/HkAdminTablePage.tsx +70 -0
- package/src/components/HkAdminWidgets.test.ts +89 -0
- package/src/components/HkAlert.tsx +1 -1
- package/src/components/HkAltSignIn.scss +55 -0
- package/src/components/HkAltSignIn.test.tsx +88 -0
- package/src/components/HkAltSignIn.tsx +92 -0
- package/src/components/HkAttachmentModal.scss +49 -0
- package/src/components/HkAttachmentModal.tsx +318 -0
- package/src/components/HkAuthCard.tsx +50 -0
- package/src/components/HkAuthSubmitButton.scss +12 -0
- package/src/components/HkAuthSubmitButton.tsx +94 -0
- package/src/components/HkAvatar.scss +93 -0
- package/src/components/HkAvatar.tsx +2 -2
- package/src/components/HkBlockingToast.scss +123 -0
- package/src/components/HkBlockingToast.test.tsx +221 -0
- package/src/components/HkBlockingToast.tsx +182 -0
- package/src/components/HkBreadcrumb.scss +624 -0
- package/src/components/HkBreadcrumb.tsx +2 -2
- package/src/components/HkButton.scss +16 -2
- package/src/components/HkCaptchaModal.scss +14 -0
- package/src/components/HkCaptchaModal.tsx +62 -0
- package/src/components/HkCaptchaWidget.scss +30 -0
- package/src/components/HkCaptchaWidget.tsx +181 -0
- package/src/components/HkCard.scss +1 -1
- package/src/components/HkChatTypes.ts +65 -0
- package/src/components/HkCheckbox.scss +4 -9
- package/src/components/HkCheckbox.tsx +18 -6
- package/src/components/HkColorPicker.scss +120 -3
- package/src/components/HkColorPicker.tsx +130 -19
- package/src/components/HkColorPickerTouch.test.tsx +145 -0
- package/src/components/HkColorSchemeDialog.scss +52 -0
- package/src/components/HkColorSchemeDialog.test.ts +201 -0
- package/src/components/HkColorSchemeDialog.tsx +96 -0
- package/src/components/HkColorSchemeEditor.test.ts +270 -0
- package/src/components/HkColorSchemeEditor.tsx +410 -0
- package/src/components/HkConfirmDialog.scss +43 -3
- package/src/components/HkConfirmDialog.tsx +7 -4
- package/src/components/HkConnectionInfo.ts +80 -0
- package/src/components/HkConnectionStatus.tsx +158 -0
- package/src/components/HkCookieConsent.scss +22 -0
- package/src/components/HkCookieConsent.test.tsx +70 -0
- package/src/components/HkCookieConsent.tsx +56 -0
- package/src/components/HkCountdownDigit.tsx +105 -0
- package/src/components/HkDatePicker.scss +361 -0
- package/src/components/HkDatePicker.test.ts +459 -0
- package/src/components/HkDatePicker.tsx +568 -0
- package/src/components/HkDateTimePicker.scss +65 -37
- package/src/components/HkDateTimePicker.test.ts +344 -0
- package/src/components/HkDateTimePicker.tsx +249 -82
- package/src/components/HkDraggableGrid.scss +4 -1
- package/src/components/HkDraggableList.scss +4 -1
- package/src/components/HkDrawer.scss +27 -13
- package/src/components/HkDrawer.test.tsx +73 -0
- package/src/components/HkDrawer.tsx +157 -10
- package/src/components/HkErrorBoundary.scss +5 -2
- package/src/components/HkErrorBoundary.tsx +7 -4
- package/src/components/HkExpansionPanel.scss +143 -0
- package/src/components/HkExpansionPanel.test.tsx +109 -0
- package/src/components/HkExpansionPanel.tsx +101 -0
- package/src/components/HkFab.scss +162 -0
- package/src/components/HkFab.test.tsx +217 -0
- package/src/components/HkFab.tsx +214 -0
- package/src/components/HkHoverRevealAction.scss +6 -0
- package/src/components/HkHoverRevealAction.test.ts +68 -0
- package/src/components/HkHoverRevealAction.tsx +84 -6
- package/src/components/HkIcon.scss +123 -0
- package/src/components/HkIcon.tsx +7 -23
- package/src/components/HkIconButton.scss +170 -0
- package/src/components/HkIconButton.test.tsx +75 -0
- package/src/components/HkIconButton.tsx +4 -3
- package/src/components/HkIconButtonVars.scss +180 -0
- package/src/components/HkInput.affix.test.ts +253 -0
- package/src/components/HkInput.enter.test.ts +88 -0
- package/src/components/HkInput.scss +127 -8
- package/src/components/HkInput.tsx +245 -13
- package/src/components/HkKeywordSearchModal.scss +13 -0
- package/src/components/HkKeywordSearchModal.tsx +46 -9
- package/src/components/HkLabel.scss +34 -0
- package/src/components/HkLabel.test.tsx +61 -0
- package/src/components/HkLabel.tsx +45 -0
- package/src/components/HkListTransition.scss +26 -0
- package/src/components/HkListTransition.tsx +57 -0
- package/src/components/HkLocalePicker.scss +37 -0
- package/src/components/HkLocalePicker.tsx +133 -0
- package/src/components/HkLocalePickerPopup.tsx +35 -19
- package/src/components/HkLocalizedInput.scss +277 -0
- package/src/components/HkLocalizedInput.test.tsx +693 -0
- package/src/components/HkLocalizedInput.tsx +495 -0
- package/src/components/HkLogWindow.scss +121 -0
- package/src/components/HkLogWindow.tsx +195 -0
- package/src/components/HkLogo.scss +1 -1
- package/src/components/HkLogo.tsx +1 -1
- package/src/components/HkMarkdownRenderer.scss +22 -2
- package/src/components/HkMarkdownRenderer.tsx +33 -3
- package/src/components/HkMediaControlBar.tsx +20 -13
- package/src/components/HkMenu.scss +200 -0
- package/src/components/HkMenu.test.ts +779 -0
- package/src/components/HkMenu.tsx +770 -0
- package/src/components/HkMenuActionItem.scss +58 -0
- package/src/components/HkMenuActionItem.tsx +41 -0
- package/src/components/HkMenuIdentityItem.scss +70 -0
- package/src/components/HkMenuIdentityItem.tsx +42 -0
- package/src/components/HkMenuPanel.scss +20 -0
- package/src/components/HkMenuPanel.test.tsx +118 -0
- package/src/components/HkMenuPanel.tsx +32 -0
- package/src/components/HkMinimap.tsx +16 -8
- package/src/components/HkModal.autofollow.test.ts +44 -0
- package/src/components/HkModal.scss +170 -53
- package/src/components/HkModal.sheetgap.test.ts +82 -0
- package/src/components/HkModal.test.tsx +334 -0
- package/src/components/HkModal.tsx +314 -68
- package/src/components/HkModalBreadcrumb.scss +4 -1
- package/src/components/HkModalBreadcrumb.test.tsx +111 -0
- package/src/components/HkModalBreadcrumb.tsx +34 -11
- package/src/components/HkModelCatalog.ts +98 -0
- package/src/components/HkModelTag.scss +130 -0
- package/src/components/HkModelTag.test.tsx +111 -0
- package/src/components/HkModelTag.tsx +190 -0
- package/src/components/HkNavItem.scss +13 -2
- package/src/components/HkNavItem.tsx +2 -1
- package/src/components/HkNavSidebar.tsx +99 -0
- package/src/components/HkNumberInput.scss +12 -0
- package/src/components/HkNumberInput.tsx +15 -8
- package/src/components/HkPageHeader.scss +79 -0
- package/src/components/HkPageHeader.test.ts +67 -0
- package/src/components/HkPageHeader.tsx +51 -0
- package/src/components/HkPasswordInput.scss +29 -17
- package/src/components/HkPasswordInput.test.ts +281 -0
- package/src/components/HkPasswordInput.tsx +337 -59
- package/src/components/HkPickerPane.scss +158 -0
- package/src/components/HkPlaceholderMarquee.scss +109 -0
- package/src/components/HkPlaceholderMarquee.test.ts +305 -0
- package/src/components/HkPlaceholderMarquee.tsx +170 -0
- package/src/components/HkPopover.scss +134 -10
- package/src/components/HkPopover.sheetdock.test.ts +34 -0
- package/src/components/HkPopover.surfacevars.test.ts +20 -0
- package/src/components/HkPopover.titleSuppress.test.tsx +265 -0
- package/src/components/HkPopover.tsx +225 -50
- package/src/components/HkPopoverSheet.test.tsx +130 -0
- package/src/components/HkPopupSelect.scss +30 -10
- package/src/components/HkPopupSelect.tsx +21 -1
- package/src/components/HkProgressDialog.scss +31 -0
- package/src/components/HkProgressDialog.tsx +37 -19
- package/src/components/HkProtocolModal.scss +26 -0
- package/src/components/HkProtocolModal.tsx +112 -0
- package/src/components/HkRadio.scss +1 -16
- package/src/components/HkRadio.tsx +5 -2
- package/src/components/HkRichInput.scss +162 -0
- package/src/components/HkRichInput.tsx +335 -0
- package/src/components/HkRollingNumber.test.ts +90 -0
- package/src/components/HkRollingNumber.tsx +39 -1
- package/src/components/HkScrollContainer.scss +71 -54
- package/src/components/HkScrollContainer.test.ts +160 -0
- package/src/components/HkScrollContainer.tsx +187 -160
- package/src/components/HkSearchInput.tsx +3 -3
- package/src/components/HkSecretRevealModal.scss +61 -0
- package/src/components/HkSecretRevealModal.tsx +91 -0
- package/src/components/HkSelect.scss +263 -12
- package/src/components/HkSelect.sheetdock.test.ts +30 -0
- package/src/components/HkSelect.test.tsx +88 -0
- package/src/components/HkSelect.tsx +59 -74
- package/src/components/HkSelectPanel.test.tsx +666 -0
- package/src/components/HkSelectPanel.tsx +595 -0
- package/src/components/HkSelectSheet.test.tsx +132 -0
- package/src/components/HkSelectionGrid.scss +1 -1
- package/src/components/HkSelectionGrid.tsx +2 -1
- package/src/components/HkSelectionWaterfall.scss +1 -1
- package/src/components/HkShareBar.scss +40 -0
- package/src/components/HkShareBar.tsx +50 -0
- package/src/components/HkSidebar.scss +15 -10
- package/src/components/HkSidebar.tsx +26 -2
- package/src/components/HkSignInCard.test.ts +154 -0
- package/src/components/HkSignInCard.tsx +155 -0
- package/src/components/HkSlider.scss +99 -0
- package/src/components/HkSlider.test.tsx +155 -0
- package/src/components/HkSlider.tsx +173 -0
- package/src/components/HkSpinner.scss +36 -1
- package/src/components/HkSpinner.test.tsx +63 -0
- package/src/components/HkSpinner.tsx +16 -1
- package/src/components/HkSplash.scss +1 -1
- package/src/components/HkSplash.tsx +3 -1
- package/src/components/HkStatCard.scss +73 -0
- package/src/components/HkStatCard.tsx +46 -0
- package/src/components/HkStatusBar.test.tsx +178 -0
- package/src/components/HkStatusBar.tsx +379 -0
- package/src/components/HkStatusPill.scss +73 -0
- package/src/components/HkStatusPill.tsx +41 -0
- package/src/components/HkStepFlow.scss +75 -0
- package/src/components/HkStepFlow.test.tsx +246 -0
- package/src/components/HkStepFlow.tsx +115 -0
- package/src/components/HkSwitch.scss +5 -17
- package/src/components/HkSwitch.tsx +6 -3
- package/src/components/HkTable.scss +20 -1
- package/src/components/HkTable.tsx +28 -3
- package/src/components/HkTabs.scss +222 -48
- package/src/components/HkTabs.test.ts +517 -0
- package/src/components/HkTabs.tsx +567 -56
- package/src/components/HkTag.tsx +2 -1
- package/src/components/HkTextarea.tsx +5 -0
- package/src/components/HkThemeToggle.scss +232 -0
- package/src/components/HkThemeToggle.test.ts +231 -0
- package/src/components/HkThemeToggle.tsx +289 -0
- package/src/components/HkTimeline.scss +134 -1
- package/src/components/HkTimeline.test.tsx +319 -0
- package/src/components/HkTimeline.tsx +303 -54
- package/src/components/HkToast.scss +34 -5
- package/src/components/HkToast.test.tsx +109 -0
- package/src/components/HkToast.tsx +57 -8
- package/src/components/HkTokenUsageBadge.scss +53 -0
- package/src/components/HkTokenUsageBadge.test.tsx +59 -0
- package/src/components/HkTokenUsageBadge.tsx +54 -0
- package/src/components/HkTokenUsagePanel.scss +95 -0
- package/src/components/HkTokenUsagePanel.tsx +140 -0
- package/src/components/HkToolBlock.scss +428 -0
- package/src/components/HkToolBlock.tsx +639 -0
- package/src/components/HkTooltip.scss +14 -2
- package/src/components/HkTooltip.tsx +25 -2
- package/src/components/HkTrendChart.tsx +27 -16
- package/src/components/HkVoiceInputPopup.scss +77 -0
- package/src/components/HkVoiceInputPopup.tsx +97 -0
- package/src/components/HkZoomToolbar.scss +4 -1
- package/src/components/HkZoomToolbar.tsx +7 -4
- package/src/components/_hikari-vars.scss +6 -0
- package/src/composables/highlightLanguages.ts +109 -0
- package/src/composables/iconRegistry.ts +127 -0
- package/src/composables/messaging/browserTransport.ts +96 -0
- package/src/composables/messaging/index.ts +204 -0
- package/src/composables/messaging/messaging.test.ts +256 -0
- package/src/composables/messaging/toastTransport.ts +26 -0
- package/src/composables/messaging/types.ts +115 -0
- package/src/composables/useActionBar.ts +66 -0
- package/src/composables/useAttachments.ts +154 -0
- package/src/composables/useConnectionProbe.ts +142 -0
- package/src/composables/useDeferredTransition.ts +16 -0
- package/src/composables/useEngineHealth.ts +69 -0
- package/src/composables/useHighlight.ts +124 -0
- package/src/composables/useMeasuredHighlight.ts +150 -0
- package/src/composables/useOverlayScrollbar.test.ts +221 -0
- package/src/composables/useOverlayScrollbar.ts +296 -0
- package/src/composables/usePageTitle.ts +37 -0
- package/src/composables/useReducedMotion.ts +34 -0
- package/src/composables/useResourceListModal.tsx +147 -0
- package/src/composables/useSafeArea.ts +137 -0
- package/src/composables/useSendShortcut.ts +35 -0
- package/src/composables/useSizeMorph.test.ts +156 -0
- package/src/composables/useSizeMorph.ts +149 -0
- package/src/composables/useSurfaceTransition.test.ts +96 -0
- package/src/composables/useSurfaceTransition.ts +71 -0
- package/src/composables/useZoomPan.ts +192 -0
- package/src/demo.scss +9 -1
- package/src/demo.ts +5 -1
- package/src/i18n/locales/ar/chat.json +38 -0
- package/src/i18n/locales/ar/components.json +141 -40
- package/src/i18n/locales/ar/components.toml +59 -0
- package/src/i18n/locales/ar/platform.json +65 -0
- package/src/i18n/locales/de/chat.json +38 -0
- package/src/i18n/locales/de/components.json +139 -38
- package/src/i18n/locales/de/components.toml +59 -0
- package/src/i18n/locales/de/platform.json +65 -0
- package/src/i18n/locales/en/chat.json +38 -0
- package/src/i18n/locales/en/components.json +104 -3
- package/src/i18n/locales/en/components.toml +59 -0
- package/src/i18n/locales/en/platform.json +65 -0
- package/src/i18n/locales/es/chat.json +38 -0
- package/src/i18n/locales/es/components.json +141 -40
- package/src/i18n/locales/es/components.toml +59 -0
- package/src/i18n/locales/es/platform.json +65 -0
- package/src/i18n/locales/fr/chat.json +38 -0
- package/src/i18n/locales/fr/components.json +137 -36
- package/src/i18n/locales/fr/components.toml +59 -0
- package/src/i18n/locales/fr/platform.json +65 -0
- package/src/i18n/locales/ja/chat.json +38 -0
- package/src/i18n/locales/ja/components.json +104 -3
- package/src/i18n/locales/ja/components.toml +59 -0
- package/src/i18n/locales/ja/platform.json +65 -0
- package/src/i18n/locales/ko/chat.json +38 -0
- package/src/i18n/locales/ko/components.json +104 -3
- package/src/i18n/locales/ko/components.toml +59 -0
- package/src/i18n/locales/ko/platform.json +65 -0
- package/src/i18n/locales/pt/chat.json +38 -0
- package/src/i18n/locales/pt/components.json +141 -40
- package/src/i18n/locales/pt/components.toml +59 -0
- package/src/i18n/locales/pt/platform.json +65 -0
- package/src/i18n/locales/ru/chat.json +38 -0
- package/src/i18n/locales/ru/components.json +141 -40
- package/src/i18n/locales/ru/components.toml +59 -0
- package/src/i18n/locales/ru/platform.json +65 -0
- package/src/i18n/locales/zh-Hans/chat.json +38 -0
- package/src/i18n/locales/zh-Hans/components.json +109 -4
- package/src/i18n/locales/zh-Hans/components.toml +59 -0
- package/src/i18n/locales/zh-Hans/platform.json +65 -0
- package/src/i18n/locales/zh-Hant/chat.json +38 -0
- package/src/i18n/locales/zh-Hant/components.json +109 -4
- package/src/i18n/locales/zh-Hant/components.toml +59 -0
- package/src/i18n/locales/zh-Hant/platform.json +65 -0
- package/src/index.ts +225 -6
- package/src/plugins/bootScriptsPlugin.ts +47 -0
- package/src/plugins/buildHashPlugin.ts +67 -0
- package/src/plugins/index.ts +2 -0
- package/src/render.ts +1 -1
- package/src/runtime/animationBus.test.ts +112 -0
- package/src/runtime/animationBus.ts +33 -2
- package/src/runtime/backStack.test.ts +334 -0
- package/src/runtime/backStack.ts +377 -0
- package/src/runtime/env.ts +4 -0
- package/src/runtime/index.ts +15 -3
- package/src/runtime/intervalBus.test.ts +118 -0
- package/src/runtime/intervalBus.ts +189 -0
- package/src/runtime/mobileViewport.test.ts +132 -0
- package/src/runtime/mobileViewport.ts +157 -0
- package/src/runtime/pageLifecycle.ts +91 -0
- package/src/runtime/useBlockingToast.ts +112 -0
- package/src/runtime/useClipboard.ts +28 -0
- package/src/runtime/useConfirm.ts +15 -7
- package/src/runtime/useMediaQuery.test.ts +67 -0
- package/src/runtime/useMediaQuery.ts +89 -0
- package/src/runtime/useOverlay.test.ts +160 -0
- package/src/runtime/useOverlay.ts +59 -12
- package/src/runtime/usePopupManager.test.ts +200 -0
- package/src/runtime/usePopupManager.ts +106 -10
- package/src/runtime/useToast.test.ts +90 -0
- package/src/runtime/useToast.ts +20 -2
- package/src/styles/admin-tokens.scss +744 -0
- package/src/styles/index.scss +5 -3
- package/src/styles/theme/_glass.scss +33 -0
- package/src/styles/theme/_layout.scss +123 -0
- package/src/styles/theme/_scrollbar.scss +73 -0
- package/src/styles/theme/_tokens.scss +69 -0
- package/src/styles/theme/base.scss +617 -0
- package/src/styles/theme/foundation.scss +219 -0
- package/src/styles/theme/mixins.scss +793 -0
- package/src/styles/theme/themes.scss +25 -0
- package/src/styles/theme/variables.scss +125 -0
- package/src/theme/_field.scss +116 -0
- package/src/theme/fontContext.test.ts +179 -0
- package/src/theme/fontContext.ts +169 -0
- package/src/theme/index.ts +19 -2
- package/src/theme/presets.ts +86 -18
- package/src/theme/theme.scss +61 -1
- package/src/theme/tokenGroups.test.ts +534 -0
- package/src/theme/tokenGroups.ts +552 -0
- package/src/theme/useSolarTime.test.ts +113 -0
- package/src/theme/useSolarTime.ts +95 -20
- package/src/theme/useTheme.test.ts +72 -0
- package/src/theme/useTheme.ts +144 -9
- package/src/tokens.scss +110 -0
- package/src/utils/base64.ts +31 -0
- package/src/utils/connectivity.ts +51 -0
- package/src/utils/dom.test.ts +131 -0
- package/src/utils/dom.ts +133 -0
- package/src/utils/download.ts +22 -0
- package/src/utils/fileType.ts +127 -0
- package/src/utils/format.ts +98 -0
- package/src/utils/fuzzy.ts +77 -0
- package/src/utils/fuzzySearch.test.ts +87 -0
- package/src/utils/healthProbe.ts +64 -0
- package/src/utils/objectPath.ts +59 -0
- package/src/utils/password.ts +53 -0
- package/src/utils/pow.test.ts +45 -0
- package/src/utils/pow.ts +226 -0
- package/src/utils/powNonce.ts +95 -0
- package/src/utils/validation.test.ts +78 -0
- package/src/DemoApp.vue +0 -400
- package/src/components/HkCollapse.tsx +0 -48
- package/src/components/HkMorphingTabs.scss +0 -141
- package/src/components/HkMorphingTabs.tsx +0 -113
- package/src/composables/useBreakpoint.ts +0 -41
- package/src/composables/useClipboard.ts +0 -37
- package/src/composables/useConfirm.ts +0 -35
- package/src/composables/useToast.ts +0 -2
- package/src/runtime/useAsyncData.ts +0 -30
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
.s-secret-modal {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-direction: column;
|
|
4
|
+
gap: var(--space-12, 0.75rem);
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
.s-secret-modal-notice {
|
|
8
|
+
display: flex;
|
|
9
|
+
align-items: center;
|
|
10
|
+
gap: var(--space-8, 0.5rem);
|
|
11
|
+
padding: var(--space-8, 0.5rem) var(--space-12, 0.75rem);
|
|
12
|
+
border-radius: var(--radius-md, 8px);
|
|
13
|
+
background: rgb(var(--color-warning) / 12%);
|
|
14
|
+
color: rgb(var(--color-warning));
|
|
15
|
+
font-size: var(--text-xs, 0.75rem);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.s-secret-modal-value {
|
|
19
|
+
display: block;
|
|
20
|
+
padding: var(--space-12, 0.75rem);
|
|
21
|
+
border-radius: var(--radius-md, 8px);
|
|
22
|
+
background: rgb(var(--color-surface) / 0.6);
|
|
23
|
+
border: 1px solid var(--border-faint, rgb(var(--color-border) / 12%));
|
|
24
|
+
color: rgb(var(--color-text));
|
|
25
|
+
font-size: var(--text-sm, 0.8125rem);
|
|
26
|
+
word-break: break-all;
|
|
27
|
+
white-space: pre-wrap;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.s-secret-modal-actions {
|
|
31
|
+
display: flex;
|
|
32
|
+
justify-content: flex-end;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.s-secret-modal-copy {
|
|
36
|
+
display: inline-flex;
|
|
37
|
+
align-items: center;
|
|
38
|
+
gap: var(--space-6, 0.375rem);
|
|
39
|
+
padding: var(--space-6, 0.375rem) var(--space-12, 0.75rem);
|
|
40
|
+
border: none;
|
|
41
|
+
border-radius: var(--radius-sm, 6px);
|
|
42
|
+
background: rgb(var(--color-primary));
|
|
43
|
+
color: rgb(var(--color-on-solid-text, 255 255 255));
|
|
44
|
+
font-size: var(--text-sm, 0.8125rem);
|
|
45
|
+
font-weight: 600;
|
|
46
|
+
cursor: pointer;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.s-secret-modal-copy:hover {
|
|
50
|
+
filter: brightness(1.1);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.s-secret-modal-copy[data-copied] {
|
|
54
|
+
background: rgb(var(--color-success));
|
|
55
|
+
color: rgb(var(--color-on-solid-text, 255 255 255));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.s-secret-modal-copy:disabled {
|
|
59
|
+
opacity: 0.5;
|
|
60
|
+
cursor: not-allowed;
|
|
61
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { defineComponent, ref, watch } from "vue";
|
|
2
|
+
import { TriangleAlert as AlertTriangle, Check, Copy } from "lucide-vue-next";
|
|
3
|
+
import { HModal, useClipboard, useI18n } from "@celestia-island/hikari";
|
|
4
|
+
|
|
5
|
+
import "./HkSecretRevealModal.scss";
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* HkSecretRevealModal — show-once secret / API-key reveal panel.
|
|
10
|
+
*
|
|
11
|
+
* Displays a one-time secret (API key, token, password) with a "shown
|
|
12
|
+
* only once" notice and a copy button. `copied` fires on successful copy.
|
|
13
|
+
* The value is a prop: the caller decides how to obtain/discard it (e.g.
|
|
14
|
+
* clear it once the modal closes).
|
|
15
|
+
*/
|
|
16
|
+
export const HkSecretRevealModal = defineComponent({
|
|
17
|
+
name: "HkSecretRevealModal",
|
|
18
|
+
props: {
|
|
19
|
+
modelValue: { type: Boolean, default: false },
|
|
20
|
+
/** Secret label (e.g. "API Key"). */
|
|
21
|
+
label: { type: String, required: true },
|
|
22
|
+
/** The secret value to reveal. */
|
|
23
|
+
value: { type: String, required: true },
|
|
24
|
+
/** Copy button label override. */
|
|
25
|
+
copyLabel: { type: String, default: undefined },
|
|
26
|
+
/** "Shown once" notice override. */
|
|
27
|
+
notice: { type: String, default: undefined },
|
|
28
|
+
title: { type: String, default: undefined },
|
|
29
|
+
},
|
|
30
|
+
emits: {
|
|
31
|
+
"update:modelValue": (_v: boolean) => true,
|
|
32
|
+
copied: () => true,
|
|
33
|
+
},
|
|
34
|
+
setup(props, { emit }) {
|
|
35
|
+
const { t } = useI18n();
|
|
36
|
+
const clipboard = useClipboard();
|
|
37
|
+
const copied = ref(false);
|
|
38
|
+
|
|
39
|
+
watch(
|
|
40
|
+
() => props.modelValue,
|
|
41
|
+
(open) => {
|
|
42
|
+
if (open) copied.value = false;
|
|
43
|
+
},
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
async function handleCopy() {
|
|
47
|
+
if (!props.value) return;
|
|
48
|
+
const ok = await clipboard.copy(props.value);
|
|
49
|
+
if (ok) {
|
|
50
|
+
copied.value = true;
|
|
51
|
+
emit("copied");
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return () => (
|
|
56
|
+
<HModal
|
|
57
|
+
modelValue={props.modelValue}
|
|
58
|
+
onUpdate:modelValue={(v: boolean) => emit("update:modelValue", v)}
|
|
59
|
+
title={props.title ?? `${props.label} — ${t("hikari::secret.title")}`}
|
|
60
|
+
width="34rem"
|
|
61
|
+
footerActions={[
|
|
62
|
+
{
|
|
63
|
+
label: t("hikari::secret.close"),
|
|
64
|
+
variant: "secondary",
|
|
65
|
+
onClick: () => emit("update:modelValue", false),
|
|
66
|
+
},
|
|
67
|
+
]}
|
|
68
|
+
>
|
|
69
|
+
<div class="s-secret-modal">
|
|
70
|
+
<div class="s-secret-modal-notice">
|
|
71
|
+
<AlertTriangle size={14} />
|
|
72
|
+
<span>{props.notice ?? t("hikari::secret.notice")}</span>
|
|
73
|
+
</div>
|
|
74
|
+
<code class="s-secret-modal-value">{props.value}</code>
|
|
75
|
+
<div class="s-secret-modal-actions">
|
|
76
|
+
<button
|
|
77
|
+
type="button"
|
|
78
|
+
class="s-secret-modal-copy"
|
|
79
|
+
data-copied={copied.value || undefined}
|
|
80
|
+
onClick={() => void handleCopy()}
|
|
81
|
+
disabled={!props.value}
|
|
82
|
+
>
|
|
83
|
+
{copied.value ? <Check size={14} /> : <Copy size={14} />}
|
|
84
|
+
{copied.value ? t("hikari::secret.copied") : props.copyLabel ?? t("hikari::secret.copy")}
|
|
85
|
+
</button>
|
|
86
|
+
</div>
|
|
87
|
+
</div>
|
|
88
|
+
</HModal>
|
|
89
|
+
);
|
|
90
|
+
},
|
|
91
|
+
});
|
|
@@ -9,10 +9,13 @@
|
|
|
9
9
|
|
|
10
10
|
.hk-select-label {
|
|
11
11
|
display: block;
|
|
12
|
-
font-size: var(--text-
|
|
13
|
-
font-weight:
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
font-size: var(--text-xs);
|
|
13
|
+
font-weight: 600;
|
|
14
|
+
letter-spacing: 0.02em;
|
|
15
|
+
margin-bottom: var(--space-4);
|
|
16
|
+
/* 72% keeps the caption hierarchy while surviving dark-theme tinted
|
|
17
|
+
* surfaces; override per-app with --hk-field-label-alpha. */
|
|
18
|
+
color: rgb(var(--color-text) / var(--hk-field-label-alpha, 72%));
|
|
16
19
|
}
|
|
17
20
|
|
|
18
21
|
.hk-select-required {
|
|
@@ -26,9 +29,9 @@
|
|
|
26
29
|
width: 100%;
|
|
27
30
|
padding: var(--space-8) var(--space-32) var(--space-8) var(--space-12);
|
|
28
31
|
min-height: 2.5rem;
|
|
29
|
-
background: rgb(var(--color-surface)
|
|
30
|
-
|
|
31
|
-
border: 1px solid var(--
|
|
32
|
+
background: color-mix(in srgb, rgb(var(--color-surface)) 55%, transparent);
|
|
33
|
+
|
|
34
|
+
border: 1px solid color-mix(in srgb, rgb(var(--color-text)) 14%, transparent);
|
|
32
35
|
border-radius: var(--radius-md);
|
|
33
36
|
font-family: inherit;
|
|
34
37
|
font-size: var(--text-base);
|
|
@@ -87,20 +90,112 @@
|
|
|
87
90
|
transform: rotate(180deg);
|
|
88
91
|
}
|
|
89
92
|
|
|
90
|
-
|
|
93
|
+
/* Fixed positioning wrapper around the desktop popout (HkSelectPanel):
|
|
94
|
+
* carries the inline anchor coords + popup-manager z-index and is the
|
|
95
|
+
* track host for the overlay scrollbar (the popout teleports to body,
|
|
96
|
+
* whose box is no positioning context). Geometry-neutral: the popout
|
|
97
|
+
* shrink-wraps the host exactly as the popout shrink-wrapped itself
|
|
98
|
+
* when it was the fixed element. The 180px width floor lives HERE, not
|
|
99
|
+
* on the popout: matchAnchorWidth's inline `min-width` lands on the host,
|
|
100
|
+
* and a stylesheet floor on the CHILD would override that match for
|
|
101
|
+
* anchors narrower than 180px (the floor must lose to the inline style,
|
|
102
|
+
* which can only happen on the same element). */
|
|
103
|
+
.hk-select-popout-host {
|
|
91
104
|
position: fixed;
|
|
92
|
-
z-index: 1100;
|
|
93
105
|
min-width: 180px;
|
|
94
|
-
|
|
106
|
+
|
|
107
|
+
/* Pop motion family — the popout grows out of the anchor edge it sits
|
|
108
|
+
* on (data-side/data-align are set by HkSelectPanel from the RESOLVED
|
|
109
|
+
* placement, including after an auto-flip) and retracts back into it.
|
|
110
|
+
* Same tokens as HkPopover/HkTooltip so every anchored surface moves
|
|
111
|
+
* with one curve. */
|
|
112
|
+
&[data-side="bottom"] { transform-origin: top left; }
|
|
113
|
+
&[data-side="bottom"][data-align="end"] { transform-origin: top right; }
|
|
114
|
+
&[data-side="top"] { transform-origin: bottom left; }
|
|
115
|
+
&[data-side="top"][data-align="end"] { transform-origin: bottom right; }
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.hk-select-popout-enter-active {
|
|
119
|
+
transition:
|
|
120
|
+
opacity var(--hk-pop-in-duration, 0.2s)
|
|
121
|
+
var(--hk-pop-in-opacity-ease, cubic-bezier(0.16, 1, 0.3, 1)),
|
|
122
|
+
transform var(--hk-pop-in-duration, 0.2s)
|
|
123
|
+
var(--hk-pop-in-transform-ease, cubic-bezier(0.34, 1.56, 0.64, 1));
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.hk-select-popout-leave-active {
|
|
127
|
+
transition:
|
|
128
|
+
opacity var(--hk-pop-out-duration, 0.15s)
|
|
129
|
+
var(--hk-pop-out-ease, cubic-bezier(0.4, 0, 1, 1)),
|
|
130
|
+
transform var(--hk-pop-out-duration, 0.15s)
|
|
131
|
+
var(--hk-pop-out-ease, cubic-bezier(0.4, 0, 1, 1));
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.hk-select-popout-enter-from,
|
|
135
|
+
.hk-select-popout-leave-to {
|
|
136
|
+
opacity: 0;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
.hk-select-popout-host[data-side="bottom"] {
|
|
140
|
+
&.hk-select-popout-enter-from {
|
|
141
|
+
transform: translateY(calc(-1 * var(--hk-pop-offset, 4px)))
|
|
142
|
+
scale(var(--hk-pop-scale, 0.97));
|
|
143
|
+
}
|
|
144
|
+
&.hk-select-popout-leave-to {
|
|
145
|
+
transform: translateY(var(--hk-pop-offset, 4px))
|
|
146
|
+
scale(var(--hk-pop-scale, 0.97));
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
.hk-select-popout-host[data-side="top"] {
|
|
151
|
+
&.hk-select-popout-enter-from {
|
|
152
|
+
transform: translateY(var(--hk-pop-offset, 4px))
|
|
153
|
+
scale(var(--hk-pop-scale, 0.97));
|
|
154
|
+
}
|
|
155
|
+
&.hk-select-popout-leave-to {
|
|
156
|
+
transform: translateY(calc(-1 * var(--hk-pop-offset, 4px)))
|
|
157
|
+
scale(var(--hk-pop-scale, 0.97));
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
@media (prefers-reduced-motion: reduce) {
|
|
162
|
+
.hk-select-popout-enter-active,
|
|
163
|
+
.hk-select-popout-leave-active {
|
|
164
|
+
transition: none;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.hk-select-popout {
|
|
169
|
+
/* Positioning lives on the host wrapper above — the popout is its
|
|
170
|
+
* static, box-filling child (width resolves from the host, whose
|
|
171
|
+
* floor/match rule is documented there). */
|
|
172
|
+
position: static;
|
|
173
|
+
/* Menu/switcher panels overflow a bare 240px cap even on tall viewports
|
|
174
|
+
(user feedback 2026-08-30: identity header + rows ≈ 320px+). Keep 240px
|
|
175
|
+
as the floor for select lists on short viewports, otherwise let the
|
|
176
|
+
panel size to its content up to a viewport-relative ceiling. */
|
|
177
|
+
max-height: max(240px, min(36rem, calc(100vh - 32px)));
|
|
178
|
+
max-height: max(240px, min(36rem, calc(100dvh - 32px)));
|
|
95
179
|
display: flex;
|
|
96
180
|
flex-direction: column;
|
|
97
|
-
|
|
181
|
+
gap: var(--space-2);
|
|
182
|
+
/* 8px panel inset (was 4px): option hover blocks used to run almost
|
|
183
|
+
edge-to-edge, reading as a glued full-width strip on touch widths. */
|
|
184
|
+
padding: var(--space-8);
|
|
98
185
|
border-radius: var(--radius-md);
|
|
99
186
|
overflow-y: auto;
|
|
100
|
-
background: rgb(var(--color-surface)
|
|
187
|
+
background: color-mix(in srgb, rgb(var(--color-surface)) 55%, transparent);
|
|
101
188
|
backdrop-filter: blur(var(--blur-md));
|
|
102
189
|
border: 1px solid var(--border-subtle);
|
|
103
190
|
box-shadow: var(--shadow-dropdown);
|
|
191
|
+
|
|
192
|
+
/* Overlay scrollbar (useOverlayScrollbar) — the native chrome is
|
|
193
|
+
always hidden, never styled. */
|
|
194
|
+
scrollbar-width: none;
|
|
195
|
+
|
|
196
|
+
&::-webkit-scrollbar {
|
|
197
|
+
display: none;
|
|
198
|
+
}
|
|
104
199
|
}
|
|
105
200
|
|
|
106
201
|
.hk-select-option {
|
|
@@ -141,3 +236,159 @@
|
|
|
141
236
|
font-size: var(--text-xs);
|
|
142
237
|
color: rgb(var(--color-error));
|
|
143
238
|
}
|
|
239
|
+
|
|
240
|
+
// ------
|
|
241
|
+
// Mobile bottom sheet (useBreakpoint().isMobile) — the popout docks to the
|
|
242
|
+
// bottom edge: scrim (click closes) + grabber + label + touch-sized option
|
|
243
|
+
// rows. Desktop popout above is untouched. Same visual contract as the
|
|
244
|
+
// popover/modal mobile dockings.
|
|
245
|
+
// ------
|
|
246
|
+
.hk-select-sheet-scrim {
|
|
247
|
+
position: fixed;
|
|
248
|
+
inset: 0;
|
|
249
|
+
pointer-events: auto;
|
|
250
|
+
background: rgb(0 0 0 / 40%);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
.hk-select-sheet-panel {
|
|
254
|
+
position: fixed;
|
|
255
|
+
left: 0;
|
|
256
|
+
right: 0;
|
|
257
|
+
/* Float off the bottom edge with the SAME host-tunable gap as the
|
|
258
|
+
* HkModal mobile docking (--hk-sheet-bottom-gap) — every bottom-up
|
|
259
|
+
* window reads as one sheet family; was glued to bottom: 0, which is
|
|
260
|
+
* exactly why dropdown sheets read as "not the standard modal". */
|
|
261
|
+
bottom: var(--hk-sheet-bottom-gap, 0.375rem);
|
|
262
|
+
display: flex;
|
|
263
|
+
flex-direction: column;
|
|
264
|
+
/* The panel height hugs its content (up to the cap below); the sheet
|
|
265
|
+
* morphs smoothly when content grows/shrinks — a language list gaining
|
|
266
|
+
* or losing rows, a select whose options filter. `height: auto` growth
|
|
267
|
+
* cannot fire a CSS transition, so HkSelectPanel's useSizeMorph pins
|
|
268
|
+
* the measured natural height as px (see useSizeMorph) and the
|
|
269
|
+
* transition below animates old→new px; `interpolate-size: allow-keywords`
|
|
270
|
+
* stays harmless for the enter/leave slide. */
|
|
271
|
+
interpolate-size: allow-keywords;
|
|
272
|
+
transition:
|
|
273
|
+
height var(--duration-fast, 0.15s) var(--ease-standard, cubic-bezier(0.4, 0, 0.2, 1)),
|
|
274
|
+
max-height var(--duration-fast, 0.15s) var(--ease-standard, cubic-bezier(0.4, 0, 0.2, 1));
|
|
275
|
+
max-height: min(
|
|
276
|
+
72vh,
|
|
277
|
+
calc(
|
|
278
|
+
100vh - var(--hk-sheet-top-inset, 4.25rem) -
|
|
279
|
+
var(--hk-sheet-bottom-gap, 0.375rem)
|
|
280
|
+
)
|
|
281
|
+
);
|
|
282
|
+
background: rgb(var(--color-surface));
|
|
283
|
+
border-radius: var(--hk-modal-radius, var(--hi-radius-lg, 12px))
|
|
284
|
+
var(--hk-modal-radius, var(--hi-radius-lg, 12px))
|
|
285
|
+
0 0;
|
|
286
|
+
box-shadow: 0 -8px 32px rgb(0 0 0 / 20%);
|
|
287
|
+
outline: none;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
.hk-select-sheet-grabber {
|
|
291
|
+
flex-shrink: 0;
|
|
292
|
+
padding: 14px 0 20px;
|
|
293
|
+
margin: 0 auto;
|
|
294
|
+
width: 100%;
|
|
295
|
+
cursor: pointer;
|
|
296
|
+
display: flex;
|
|
297
|
+
justify-content: center;
|
|
298
|
+
|
|
299
|
+
&::before {
|
|
300
|
+
content: "";
|
|
301
|
+
width: 2.25rem;
|
|
302
|
+
height: 4px;
|
|
303
|
+
border-radius: 999px;
|
|
304
|
+
background: rgb(var(--color-border, 100 100 100) / 40%);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
.hk-select-sheet-title {
|
|
309
|
+
flex-shrink: 0;
|
|
310
|
+
/* Left inset tracks the sheet ENTRIES' text edge, not their card
|
|
311
|
+
* edge: the list pads option blocks in by --space-16 and rows carry
|
|
312
|
+
* roughly another --space-16 of leading padding inside, so the old
|
|
313
|
+
* single --space-16 left the title hanging left of every row it
|
|
314
|
+
* named instead of reading as the list's heading. Hosts tune it via
|
|
315
|
+
* --hk-sheet-title-inset; the trailing inset stays --space-16. */
|
|
316
|
+
padding: 0 var(--space-16) var(--space-8)
|
|
317
|
+
var(--hk-sheet-title-inset, calc(var(--space-16) * 2));
|
|
318
|
+
font-size: var(--text-xs);
|
|
319
|
+
font-weight: 600;
|
|
320
|
+
letter-spacing: 0.02em;
|
|
321
|
+
color: rgb(var(--color-muted));
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/* Track host for the sheet list's overlay scrollbar — owns the flex
|
|
325
|
+
slot in the sheet column so the rail hugs the LIST band, not the
|
|
326
|
+
grabber/title bands above it. */
|
|
327
|
+
.hk-select-sheet-body {
|
|
328
|
+
flex: 1;
|
|
329
|
+
min-height: 0;
|
|
330
|
+
display: flex;
|
|
331
|
+
flex-direction: column;
|
|
332
|
+
position: relative;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
.hk-select-sheet-list {
|
|
336
|
+
flex: 1;
|
|
337
|
+
min-height: 0;
|
|
338
|
+
overflow-y: auto;
|
|
339
|
+
overscroll-behavior: contain;
|
|
340
|
+
/* Side insets so the option blocks (full-row hover pills) read as
|
|
341
|
+
cards inside the sheet instead of an edge-to-edge strip glued to
|
|
342
|
+
both screen sides — matching the popover family's breathing room.
|
|
343
|
+
Bottom padding stacks the home-bar safe-area inset ON TOP of the
|
|
344
|
+
base row gap, so the last row never hides behind the gesture bar
|
|
345
|
+
on notched phones (the modal footer adds a --hk-sheet-footer-gap
|
|
346
|
+
on top of this family baseline for its action buttons). */
|
|
347
|
+
padding-inline: var(--space-16);
|
|
348
|
+
padding-bottom: calc(var(--space-16) + env(safe-area-inset-bottom, 0px));
|
|
349
|
+
|
|
350
|
+
/* Overlay scrollbar (useOverlayScrollbar) — native chrome hidden. */
|
|
351
|
+
scrollbar-width: none;
|
|
352
|
+
|
|
353
|
+
&::-webkit-scrollbar {
|
|
354
|
+
display: none;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
.hk-select-option {
|
|
358
|
+
padding: var(--space-14) var(--space-16);
|
|
359
|
+
font-size: var(--text-base);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/* Leading in-content heading hidden by the sheet duplicate-title
|
|
364
|
+
* filter (HkSelectPanel) — must beat any consumer display rule, like
|
|
365
|
+
* the UA [hidden] contract. */
|
|
366
|
+
.hk-select-sheet-list .hk-sheet-dup-title {
|
|
367
|
+
display: none !important;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
.hk-select-sheet-enter-active {
|
|
371
|
+
transition:
|
|
372
|
+
opacity 0.25s ease-out,
|
|
373
|
+
transform 0.3s cubic-bezier(0.16, 1, 0.3, 1);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
.hk-select-sheet-leave-active {
|
|
377
|
+
transition:
|
|
378
|
+
opacity 0.2s ease-in,
|
|
379
|
+
transform 0.25s cubic-bezier(0.4, 0, 1, 1);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
.hk-select-sheet-enter-from,
|
|
383
|
+
.hk-select-sheet-leave-to {
|
|
384
|
+
opacity: 0;
|
|
385
|
+
transform: translateY(100%);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
@media (prefers-reduced-motion: reduce) {
|
|
389
|
+
.hk-select-sheet-enter-active,
|
|
390
|
+
.hk-select-sheet-leave-active,
|
|
391
|
+
.hk-select-sheet-panel {
|
|
392
|
+
transition: none;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Source contract for the dropdown sheet docking (2026-08-30 user
|
|
3
|
+
* report: the localized input's language menu sheet "didn't look like
|
|
4
|
+
* it came from the standard modal"). The HkSelectPanel sheet — the
|
|
5
|
+
* shared surface under HkSelect / HkMenu / HkPopupSelect — must dock
|
|
6
|
+
* with the SAME host-tunable bottom gap and home-bar safe-area padding
|
|
7
|
+
* as the HkModal mobile docking, so every bottom-up window belongs to
|
|
8
|
+
* one visual family. It used to be glued to `bottom: 0`.
|
|
9
|
+
*/
|
|
10
|
+
import { describe, expect, it } from "vitest";
|
|
11
|
+
import { readFileSync } from "node:fs";
|
|
12
|
+
import { dirname, join } from "node:path";
|
|
13
|
+
import { fileURLToPath } from "node:url";
|
|
14
|
+
|
|
15
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
16
|
+
const src = readFileSync(join(here, "HkSelect.scss"), "utf-8");
|
|
17
|
+
|
|
18
|
+
describe("HkSelectPanel mobile sheet docking contract", () => {
|
|
19
|
+
it("floats the sheet on --hk-sheet-bottom-gap (not hard 0)", () => {
|
|
20
|
+
const block = src.match(/\.hk-select-sheet-panel\s*{[^}]*}/)?.[0] ?? "";
|
|
21
|
+
expect(block).toContain("bottom: var(--hk-sheet-bottom-gap");
|
|
22
|
+
// Same default as the HkModal sheet contract.
|
|
23
|
+
expect(block).toMatch(/--hk-sheet-bottom-gap,\s*0\.375rem/);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("stacks the safe-area inset on the sheet list bottom padding", () => {
|
|
27
|
+
const block = src.match(/\.hk-select-sheet-list\s*{[^}]*}/)?.[0] ?? "";
|
|
28
|
+
expect(block).toContain("env(safe-area-inset-bottom");
|
|
29
|
+
});
|
|
30
|
+
});
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
2
|
+
import { createApp, h, nextTick } from "vue";
|
|
3
|
+
|
|
4
|
+
import { HSelect } from "../index";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Mobile select contract: below the touch breakpoint the select renders
|
|
8
|
+
* as a bottom sheet (scrim + panel + grabber) instead of an anchored
|
|
9
|
+
* popout. The sheet's option list must carry side insets — full-row
|
|
10
|
+
* option pills used to glue to both screen edges and read as a broken
|
|
11
|
+
* edge-to-edge strip (the language picker popover keeps margins, which
|
|
12
|
+
* made the mismatch visible).
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const originalWidth = window.innerWidth;
|
|
16
|
+
|
|
17
|
+
function setViewportWidth(px: number) {
|
|
18
|
+
Object.defineProperty(window, "innerWidth", {
|
|
19
|
+
configurable: true,
|
|
20
|
+
writable: true,
|
|
21
|
+
value: px,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async function mountSelect() {
|
|
26
|
+
const el = document.createElement("div");
|
|
27
|
+
document.body.appendChild(el);
|
|
28
|
+
const app = createApp({
|
|
29
|
+
render: () =>
|
|
30
|
+
h(HSelect, {
|
|
31
|
+
modelValue: "",
|
|
32
|
+
"onUpdate:modelValue": () => {},
|
|
33
|
+
options: [
|
|
34
|
+
{ value: "a", label: "Option A" },
|
|
35
|
+
{ value: "b", label: "Option B" },
|
|
36
|
+
],
|
|
37
|
+
}),
|
|
38
|
+
});
|
|
39
|
+
app.mount(el);
|
|
40
|
+
await nextTick();
|
|
41
|
+
return { el, app };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
describe("HSelect mobile sheet", () => {
|
|
45
|
+
afterEach(() => {
|
|
46
|
+
setViewportWidth(originalWidth);
|
|
47
|
+
document.body.innerHTML = "";
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it("renders a bottom sheet with side-inset options on touch widths", async () => {
|
|
51
|
+
setViewportWidth(375);
|
|
52
|
+
const { app } = await mountSelect();
|
|
53
|
+
const trigger = document.querySelector<HTMLButtonElement>(".hk-select-trigger")!;
|
|
54
|
+
trigger.click();
|
|
55
|
+
await nextTick();
|
|
56
|
+
|
|
57
|
+
const panel = document.querySelector<HTMLElement>(".hk-select-sheet-panel");
|
|
58
|
+
expect(panel).not.toBeNull();
|
|
59
|
+
const scrim = document.querySelector(".hk-select-sheet-scrim");
|
|
60
|
+
expect(scrim).not.toBeNull();
|
|
61
|
+
// No anchored popout in sheet mode.
|
|
62
|
+
expect(document.querySelector(".hk-select-popout")).toBeNull();
|
|
63
|
+
|
|
64
|
+
// The list node must exist (side insets are enforced in SCSS —
|
|
65
|
+
// padding-inline on .hk-select-sheet-list — which happy-dom does not
|
|
66
|
+
// compute; the visual assertion lives in the stylesheet).
|
|
67
|
+
const list = document.querySelector<HTMLElement>(".hk-select-sheet-list");
|
|
68
|
+
expect(list).not.toBeNull();
|
|
69
|
+
const options = panel!.querySelectorAll(".hk-select-option");
|
|
70
|
+
expect(options.length).toBe(2);
|
|
71
|
+
|
|
72
|
+
app.unmount();
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it("renders an anchored popout on desktop widths", async () => {
|
|
76
|
+
setViewportWidth(1280);
|
|
77
|
+
const { app } = await mountSelect();
|
|
78
|
+
const trigger = document.querySelector<HTMLButtonElement>(".hk-select-trigger")!;
|
|
79
|
+
trigger.click();
|
|
80
|
+
await nextTick();
|
|
81
|
+
|
|
82
|
+
expect(document.querySelector(".hk-select-sheet-panel")).toBeNull();
|
|
83
|
+
const popout = document.querySelector<HTMLElement>(".hk-select-popout");
|
|
84
|
+
expect(popout).not.toBeNull();
|
|
85
|
+
|
|
86
|
+
app.unmount();
|
|
87
|
+
});
|
|
88
|
+
});
|