@leavepulse/ui 0.87.1 → 0.89.1
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/dist/assets/{LpCommandPalette-DCZB4EAf.css → LpCommandPalette-CO_5TFOQ.css} +38 -19
- package/dist/assets/{LpDropzone-CfL3Vrx4.css → LpDropzone-CVGUQqgY.css} +10 -10
- package/dist/assets/{LpImageEditor-y3B-aW3H.css → LpImageEditor-D_ECuegW.css} +6 -6
- package/dist/assets/{LpTable-BqZNpWgC.css → LpTable-DAVNb-x2.css} +25 -25
- package/dist/chunks/{ContextMenuItems-D0jObgzN.js → ContextMenuItems-LujPZSWM.js} +2 -1
- package/dist/chunks/{DropzoneLinkField-DhwYRnvd.js → DropzoneLinkField-Ckuh8zFC.js} +2 -2
- package/dist/chunks/{IconCloseButton-CmWOJAXG.js → IconCloseButton-C_V7qngw.js} +1 -1
- package/dist/chunks/{LpActionBar-Cx_MSZl9.js → LpActionBar-43WHmTsM.js} +1 -1
- package/dist/chunks/{LpAddressInput-CfqQR5jE.js → LpAddressInput-BCqR0zmv.js} +5 -5
- package/dist/chunks/{LpAlert-CYtbpRYW.js → LpAlert-Q0h7Gucu.js} +1 -1
- package/dist/chunks/{LpAppShell-BQG98odS.js → LpAppShell-BWaVRsDB.js} +3 -3
- package/dist/chunks/{LpAutocomplete-VNID5zdy.js → LpAutocomplete-YPxAPHEo.js} +5 -5
- package/dist/chunks/{LpButton-DSHioIfD.js → LpButton-BvVoF-PO.js} +1 -1
- package/dist/chunks/{LpCheckbox-P7tEw8GO.js → LpCheckbox-C9VnPIQf.js} +1 -1
- package/dist/chunks/{LpCodeBlock-BNThgUdQ.js → LpCodeBlock-BgWcg2mk.js} +3 -3
- package/dist/chunks/{LpCodeDisplay-DsiE-Qy9.js → LpCodeDisplay-DBi1YPLp.js} +3 -3
- package/dist/chunks/{LpColorPicker-EXpwcX-t.js → LpColorPicker-KGMG8QEh.js} +4 -4
- package/dist/chunks/{LpConfirmDialog-DPiPVRn_.js → LpConfirmDialog-y6k0ryWR.js} +2 -2
- package/dist/chunks/{LpConfirmHost-Ddiljlog.js → LpConfirmHost-D-X0tDym.js} +1 -1
- package/dist/chunks/{LpContextMenu-DYpDm6iI.js → LpContextMenu-DEFirphS.js} +3 -3
- package/dist/chunks/{LpContextSwitcher-BPDSkKyA.js → LpContextSwitcher-COw6GHPW.js} +1 -1
- package/dist/chunks/{LpCopyButton-CxX682t4.js → LpCopyButton-BGzw95Xc.js} +2 -2
- package/dist/chunks/{LpDataList-DCtF7krm.js → LpDataList-B56Gu6Yt.js} +3 -3
- package/dist/chunks/{LpDatePicker-BElEHVm6.js → LpDatePicker-Dlyvgsan.js} +2 -2
- package/dist/chunks/{LpDateRangePicker-vLt-drEu.js → LpDateRangePicker-CAn2yGbn.js} +1 -1
- package/dist/chunks/{LpDrawer-C97k-3L2.js → LpDrawer-iRHwJOGn.js} +4 -4
- package/dist/chunks/{LpDropdownMenu-BPEsqZCN.js → LpDropdownMenu-CdCe7D2d.js} +3 -3
- package/dist/chunks/{LpEditable-CeRAzYMv.js → LpEditable-DfRUAGkA.js} +2 -2
- package/dist/chunks/{LpEmptyState-DrHBR3yh.js → LpEmptyState-DLOm07KH.js} +1 -1
- package/dist/chunks/{LpFileTree-BcQgsw4e.js → LpFileTree-BN_zybBN.js} +3 -3
- package/dist/chunks/{LpFileTreeNode-Dk2WqkPF.js → LpFileTreeNode-CC48DbT5.js} +3 -3
- package/dist/chunks/{LpHintLayer-CUSRLtLu.js → LpHintLayer-nuMqfzvy.js} +3 -3
- package/dist/chunks/{LpHints-BF40CP4_.js → LpHints-ByAd3a76.js} +3 -3
- package/dist/chunks/{LpHoverCard-CHE9jAJG.js → LpHoverCard-DYguY2AP.js} +10 -61
- package/dist/chunks/{LpInput-BU5IsOlE.js → LpInput-DGMpxrcg.js} +1 -1
- package/dist/chunks/{LpInspector-CEko6-pS.js → LpInspector-oTfkOdYd.js} +2 -2
- package/dist/chunks/{LpLightbox-REei-y5e.js → LpLightbox-a24V_0Wk.js} +19 -7
- package/dist/chunks/{LpLogInspector-Bwz351LY.js → LpLogInspector-BhJTJIst.js} +1 -1
- package/dist/chunks/{LpLogViewer-BCkqdH66.js → LpLogViewer-BuIzr_6h.js} +4 -4
- package/dist/chunks/{LpMediaGallery-BF1Bch3v.js → LpMediaGallery-B7DOV_aL.js} +2 -2
- package/dist/chunks/{LpModal-BK2T5uIG.js → LpModal-D1E7rQzw.js} +5 -5
- package/dist/chunks/{LpModalNav-Dk4eHpg6.js → LpModalNav-DivqT1NV.js} +5 -4
- package/dist/chunks/{LpNotificationBell-CMwUXhFg.js → LpNotificationBell-KRgdRZs-.js} +4 -4
- package/dist/chunks/{LpNotificationFeed-BXsRp9jm.js → LpNotificationFeed-CMGYU8c-.js} +5 -5
- package/dist/chunks/{LpNumberField-QUMXvpqx.js → LpNumberField-C0LSu-tl.js} +1 -1
- package/dist/chunks/{LpPaletteTheme-wiJtP5hV.js → LpPaletteTheme-CLwO-SDf.js} +3 -3
- package/dist/chunks/{LpPasswordInput-W9AZfzaL.js → LpPasswordInput-LPVI7D3v.js} +2 -2
- package/dist/chunks/{LpPhoneInput-DsRv-Qla.js → LpPhoneInput-BdglZsGH.js} +5 -5
- package/dist/chunks/{LpPie-pbrNFbRP.js → LpPie-CsiBXe-e.js} +3 -3
- package/dist/chunks/{LpPipeline-BHWmvOiv.js → LpPipeline-DVcoSs--.js} +1 -1
- package/dist/chunks/{LpPopover-BQSuxtai.js → LpPopover-w2rr4x7n.js} +2 -2
- package/dist/chunks/{LpRadio-CWA5Eb3M.js → LpRadio-4KyxeSz4.js} +1 -1
- package/dist/chunks/{LpRadioGroup-D94KMStE.js → LpRadioGroup-Cr_fmrBY.js} +1 -1
- package/dist/chunks/{LpRangeSlider-CkP9hYUC.js → LpRangeSlider-DbaITHtC.js} +1 -1
- package/dist/chunks/{LpRelativeTime-0zI-44ve.js → LpRelativeTime-CN5bWD73.js} +1 -1
- package/dist/chunks/{LpRepeater-DLLcbQ6J.js → LpRepeater-C0rns2xW.js} +1 -1
- package/dist/chunks/{LpSecretField-Au7ZQTtm.js → LpSecretField-BwrZ4TfT.js} +5 -5
- package/dist/chunks/{LpSection-RKCwB0xN.js → LpSection-CwsfNR7N.js} +1 -1
- package/dist/chunks/{LpSegmented-DD8MPj4j.js → LpSegmented-DzFYP0ZF.js} +1 -1
- package/dist/chunks/{LpSelect-BeXz1TWh.js → LpSelect-Dffg5r4I.js} +5 -5
- package/dist/chunks/{LpSidebar-DUFlf6Uu.js → LpSidebar-DQtCYlT4.js} +4 -4
- package/dist/chunks/{LpSidebarNav-C34YEZIR.js → LpSidebarNav-CFcI7--7.js} +1 -1
- package/dist/chunks/{LpSidebarRow-BeEGgsZi.js → LpSidebarRow-C5jXpcRL.js} +2 -2
- package/dist/chunks/{LpSlider-BCy5rjwK.js → LpSlider-DUd4gXnw.js} +1 -1
- package/dist/chunks/{LpStat-eVz7iUfD.js → LpStat-SyVb5JXm.js} +1 -1
- package/dist/chunks/{LpSwitch-D7hmVsif.js → LpSwitch-D-mcaA6C.js} +1 -1
- package/dist/chunks/{LpTableFilter-BGFNKyUX.js → LpTableFilter-CILHffC1.js} +3 -3
- package/dist/chunks/{LpTagsInput-D9uOSlvf.js → LpTagsInput-DD8-xe3F.js} +2 -2
- package/dist/chunks/{LpTextarea-DwE-lQt4.js → LpTextarea-CQg-dVxN.js} +1 -1
- package/dist/chunks/{LpThemeStudio-COn2H76d.js → LpThemeStudio-BPPctXgd.js} +4 -4
- package/dist/chunks/{LpThemeSwitcher-B3R3nNid.js → LpThemeSwitcher-B3c44xFH.js} +4 -4
- package/dist/chunks/{LpTimeField-Mi8H1g_9.js → LpTimeField-Dio7Tj9A.js} +1 -1
- package/dist/chunks/{LpToaster-DOJn6Vey.js → LpToaster-Cdx1tnHB.js} +2 -2
- package/dist/chunks/{LpTooltip-Bz_rrq5S.js → LpTooltip-BKde8Xjv.js} +43 -7
- package/dist/chunks/{LpTopologyCanvas-5RFMP4gR.js → LpTopologyCanvas-CQ0l6A5h.js} +3 -3
- package/dist/chunks/{LpTour-DUI_hoPv.js → LpTour-BGUKUxSZ.js} +1 -1
- package/dist/chunks/{LpTruncate-CynJhsEw.js → LpTruncate-C_C8eiIE.js} +2 -2
- package/dist/chunks/{LpUptimeBar-G7PfNJUH.js → LpUptimeBar-Dt6SY_-m.js} +1 -1
- package/dist/chunks/{LpVideoCompress-CPUxXh-c.js → LpVideoCompress-CvxNdyjp.js} +2 -2
- package/dist/chunks/{LpVideoPlayer-BbCjEQAx.js → LpVideoPlayer-FMuxTAU8.js} +4 -4
- package/dist/chunks/{LpWindow-Dp_0nNqg.js → LpWindow-DsPeiDCZ.js} +1 -1
- package/dist/chunks/{LpWorkspaceSwitcher-bjkncjSl.js → LpWorkspaceSwitcher-DCIiOQt_.js} +4 -4
- package/dist/chunks/{PickerField-CjOTUXDK.js → PickerField-DzF3UVnX.js} +1 -1
- package/dist/chunks/{RevealToggle-Bl1NcfxB.js → RevealToggle-BsYxY6Oz.js} +2 -2
- package/dist/chunks/{TourCard-BovXtMbO.js → TourCard-yrLqv4Oz.js} +1 -1
- package/dist/chunks/{VideoVolume-DE3i1ree.js → VideoVolume-CzL5mZPE.js} +2 -2
- package/dist/components/ContextMenuItems.vue.js +1 -1
- package/dist/components/DropzoneLinkField.vue.js +1 -1
- package/dist/components/IconCloseButton.vue.js +1 -1
- package/dist/components/LpActionBar.vue.js +1 -1
- package/dist/components/LpAddressInput.vue.js +1 -1
- package/dist/components/LpAlert.vue.js +1 -1
- package/dist/components/LpAppShell.vue.js +1 -1
- package/dist/components/LpAutocomplete.vue.js +1 -1
- package/dist/components/LpButton.vue.js +1 -1
- package/dist/components/LpChart.vue.js +2 -2
- package/dist/components/LpCheckbox.vue.js +1 -1
- package/dist/components/LpCodeBlock.vue.js +1 -1
- package/dist/components/LpCodeDisplay.vue.js +1 -1
- package/dist/components/LpColorPicker.vue.js +1 -1
- package/dist/components/LpCommandPalette.vue.d.ts +2 -0
- package/dist/components/LpCommandPalette.vue.js +69 -33
- package/dist/components/LpConfirmDialog.vue.js +1 -1
- package/dist/components/LpConfirmHost.vue.js +1 -1
- package/dist/components/LpContextMenu.vue.js +1 -1
- package/dist/components/LpContextSwitcher.vue.js +1 -1
- package/dist/components/LpCopyButton.vue.js +1 -1
- package/dist/components/LpDataList.vue.js +1 -1
- package/dist/components/LpDatePicker.vue.js +1 -1
- package/dist/components/LpDateRangePicker.vue.js +1 -1
- package/dist/components/LpDock.vue.js +1 -1
- package/dist/components/LpDrawer.vue.js +1 -1
- package/dist/components/LpDropdownMenu.vue.js +1 -1
- package/dist/components/LpDropzone.vue.js +5 -5
- package/dist/components/LpEditable.vue.js +1 -1
- package/dist/components/LpEmptyState.vue.js +1 -1
- package/dist/components/LpFileTree.vue.js +1 -1
- package/dist/components/LpFileTreeNode.vue.js +1 -1
- package/dist/components/LpHintLayer.vue.js +1 -1
- package/dist/components/LpHints.vue.js +1 -1
- package/dist/components/LpHoverCard.vue.d.ts +3 -3
- package/dist/components/LpHoverCard.vue.js +1 -1
- package/dist/components/LpImageEditor.vue.js +4 -4
- package/dist/components/LpInput.vue.js +1 -1
- package/dist/components/LpInspector.vue.js +1 -1
- package/dist/components/LpLightbox.vue.js +1 -1
- package/dist/components/LpLogInspector.vue.js +1 -1
- package/dist/components/LpLogViewer.vue.js +1 -1
- package/dist/components/LpMediaGallery.vue.js +1 -1
- package/dist/components/LpModal.vue.js +1 -1
- package/dist/components/LpModalNav.vue.js +1 -1
- package/dist/components/LpNotificationBell.vue.js +1 -1
- package/dist/components/LpNotificationFeed.vue.js +1 -1
- package/dist/components/LpNumberField.vue.js +1 -1
- package/dist/components/LpPaletteTheme.vue.js +1 -1
- package/dist/components/LpPasswordInput.vue.js +1 -1
- package/dist/components/LpPhoneInput.vue.js +1 -1
- package/dist/components/LpPie.vue.js +1 -1
- package/dist/components/LpPipeline.vue.js +1 -1
- package/dist/components/LpPopover.vue.js +1 -1
- package/dist/components/LpRadio.vue.js +1 -1
- package/dist/components/LpRadioGroup.vue.js +1 -1
- package/dist/components/LpRangeSlider.vue.js +1 -1
- package/dist/components/LpRelativeTime.vue.js +1 -1
- package/dist/components/LpRepeater.vue.js +1 -1
- package/dist/components/LpSecretField.vue.js +1 -1
- package/dist/components/LpSection.vue.js +1 -1
- package/dist/components/LpSegmented.vue.js +1 -1
- package/dist/components/LpSelect.vue.js +1 -1
- package/dist/components/LpShortcutsDialog.vue.js +1 -1
- package/dist/components/LpSidebar.vue.js +1 -1
- package/dist/components/LpSidebarNav.vue.js +1 -1
- package/dist/components/LpSidebarRow.vue.js +1 -1
- package/dist/components/LpSlider.vue.js +1 -1
- package/dist/components/LpStat.vue.js +1 -1
- package/dist/components/LpSwitch.vue.js +1 -1
- package/dist/components/LpTable.vue.d.ts +7 -0
- package/dist/components/LpTable.vue.js +48 -41
- package/dist/components/LpTableFilter.vue.js +1 -1
- package/dist/components/LpTabs.vue.js +1 -1
- package/dist/components/LpTagsInput.vue.js +1 -1
- package/dist/components/LpTextarea.vue.js +1 -1
- package/dist/components/LpThemeStudio.vue.js +1 -1
- package/dist/components/LpThemeSwitcher.vue.js +1 -1
- package/dist/components/LpTimeField.vue.js +1 -1
- package/dist/components/LpToaster.vue.js +1 -1
- package/dist/components/LpTooltip.vue.d.ts +9 -0
- package/dist/components/LpTooltip.vue.js +1 -1
- package/dist/components/LpTopologyCanvas.vue.js +1 -1
- package/dist/components/LpTour.vue.js +1 -1
- package/dist/components/LpTruncate.vue.js +1 -1
- package/dist/components/LpUptimeBar.vue.js +1 -1
- package/dist/components/LpVideoCompress.vue.js +1 -1
- package/dist/components/LpVideoPlayer.vue.js +1 -1
- package/dist/components/LpVideoTrim.vue.js +4 -4
- package/dist/components/LpWindow.vue.js +1 -1
- package/dist/components/LpWorkspaceSwitcher.vue.js +1 -1
- package/dist/components/PickerField.vue.js +1 -1
- package/dist/components/RevealToggle.vue.js +1 -1
- package/dist/components/TourCard.vue.js +1 -1
- package/dist/components/VideoVolume.vue.js +1 -1
- package/dist/components/badge.d.ts +1 -1
- package/dist/components/badge.js +1 -1
- package/dist/components/dropdown.d.ts +14 -0
- package/dist/components/dropdown.js +7 -1
- package/dist/components/menuWhen.js +1 -1
- package/dist/components/modalDepth.d.ts +1 -0
- package/dist/components/modalDepth.js +2 -0
- package/dist/composables/useReveal.js +3 -3
- package/dist/composables/useTouchHold.d.ts +19 -0
- package/dist/composables/useTouchHold.js +60 -0
- package/dist/index.js +72 -71
- package/package.json +1 -1
- package/src/components/ContextMenuItems.vue +2 -1
- package/src/components/IconCloseButton.vue +1 -1
- package/src/components/LpAlert.vue +1 -1
- package/src/components/LpAutocomplete.vue +4 -4
- package/src/components/LpButton.vue +1 -1
- package/src/components/LpCheckbox.vue +1 -1
- package/src/components/LpCodeBlock.vue +2 -2
- package/src/components/LpColorPicker.vue +3 -3
- package/src/components/LpCommandPalette.vue +89 -7
- package/src/components/LpContextMenu.vue +2 -2
- package/src/components/LpDataList.vue +1 -1
- package/src/components/LpDrawer.vue +7 -4
- package/src/components/LpDropdownMenu.vue +2 -2
- package/src/components/LpDropzone.vue +2 -2
- package/src/components/LpEditable.vue +2 -2
- package/src/components/LpEmptyState.vue +1 -1
- package/src/components/LpFileTreeNode.vue +1 -1
- package/src/components/LpHintLayer.vue +2 -2
- package/src/components/LpHoverCard.vue +11 -80
- package/src/components/LpImageEditor.vue +1 -1
- package/src/components/LpInput.vue +1 -1
- package/src/components/LpLightbox.vue +20 -3
- package/src/components/LpModal.vue +4 -4
- package/src/components/LpModalNav.vue +2 -1
- package/src/components/LpNumberField.vue +1 -1
- package/src/components/LpPhoneInput.vue +5 -5
- package/src/components/LpPopover.vue +2 -2
- package/src/components/LpRadio.vue +1 -1
- package/src/components/LpRadioGroup.vue +1 -1
- package/src/components/LpRangeSlider.vue +1 -1
- package/src/components/LpSection.vue +9 -6
- package/src/components/LpSegmented.vue +1 -1
- package/src/components/LpSelect.vue +4 -4
- package/src/components/LpSlider.vue +1 -1
- package/src/components/LpStat.vue +1 -1
- package/src/components/LpSwitch.vue +1 -1
- package/src/components/LpTable.vue +13 -1
- package/src/components/LpTableFilter.vue +1 -1
- package/src/components/LpTagsInput.vue +2 -2
- package/src/components/LpTextarea.vue +1 -1
- package/src/components/LpThemeSwitcher.vue +2 -2
- package/src/components/LpTimeField.vue +1 -1
- package/src/components/LpToaster.vue +1 -1
- package/src/components/LpTooltip.vue +59 -5
- package/src/components/badge.ts +1 -1
- package/src/components/dropdown.ts +24 -0
- package/src/components/modalDepth.ts +6 -0
- package/src/composables/useReveal.ts +7 -4
- package/src/composables/useTouchHold.ts +92 -0
- package/src/locales/uk.ts +1 -0
- package/src/tokens/utilities.css +34 -0
|
@@ -18,6 +18,7 @@ import { useGlass } from "../composables/useGlass"
|
|
|
18
18
|
import { useLabels } from "../composables/useLpConfig"
|
|
19
19
|
import LpButton from "./LpButton.vue"
|
|
20
20
|
import LpIcon from "./LpIcon.vue"
|
|
21
|
+
import { MODAL_MAX_HEIGHT } from "./modalDepth"
|
|
21
22
|
import LpModal from "./LpModal.vue"
|
|
22
23
|
import LpSidebar from "./LpSidebar.vue"
|
|
23
24
|
import type { SidebarItem, SidebarSection } from "./sidebar"
|
|
@@ -132,7 +133,7 @@ const triggerClass = computed(() => TRIGGER_VISIBILITY[props.mobileBreakpoint])
|
|
|
132
133
|
* mirrors LpModal's max-height, which is not a class this file can put in the
|
|
133
134
|
* consuming app's scan.
|
|
134
135
|
*/
|
|
135
|
-
const railStyle = { maxHeight:
|
|
136
|
+
const railStyle = { maxHeight: MODAL_MAX_HEIGHT }
|
|
136
137
|
|
|
137
138
|
/*
|
|
138
139
|
* The rail is part of the dialog, so it takes the same surface: a glass
|
|
@@ -177,7 +177,7 @@ usePaletteControl({ disabled: () => field.value.disabled, invalid: () => field.v
|
|
|
177
177
|
:id="field.id"
|
|
178
178
|
:aria-describedby="describedBy"
|
|
179
179
|
:aria-invalid="field.invalid || undefined"
|
|
180
|
-
class="min-w-0 flex-1 bg-transparent text-ink outline-none placeholder:text-muted [appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none"
|
|
180
|
+
class="lp-entry min-w-0 flex-1 bg-transparent text-ink outline-none placeholder:text-muted [appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none"
|
|
181
181
|
:class="steppers ? 'text-center @max-[8.5rem]:px-3 @max-[8.5rem]:text-left' : icon || $slots.leading ? 'pl-2 pr-3' : 'px-3'"
|
|
182
182
|
/>
|
|
183
183
|
<!-- The unit sits between the value and the increment button, so a long one
|
|
@@ -55,7 +55,7 @@ import { useFieldNotice, type FieldNoticeOptions } from "../composables/useField
|
|
|
55
55
|
import { useInputFilter } from "../composables/useInputFilter"
|
|
56
56
|
import { useLabels } from "../composables/useLpConfig"
|
|
57
57
|
import { type Country, filterCountries, flagEmoji, loadCountries, matchCountryByValue } from "./countries"
|
|
58
|
-
import { OPTION_ITEM, optionStagger, COMBOBOX_PANEL, POPOVER_PANEL } from "./dropdown"
|
|
58
|
+
import { OPTION_ITEM, optionStagger, COMBOBOX_FIT, COMBOBOX_PANEL, POPOVER_PANEL } from "./dropdown"
|
|
59
59
|
import LpFieldNotice from "./LpFieldNotice.vue"
|
|
60
60
|
import LpIcon from "./LpIcon.vue"
|
|
61
61
|
import {
|
|
@@ -403,7 +403,7 @@ const guardOutside = useOutsideGuard()
|
|
|
403
403
|
@escape-key-down="guardOutside"
|
|
404
404
|
v-bind="COMBOBOX_PANEL"
|
|
405
405
|
align="start"
|
|
406
|
-
:class="[POPOVER_PANEL, 'z-(--z-popover)
|
|
406
|
+
:class="[POPOVER_PANEL, 'z-(--z-popover) w-72 max-w-[calc(100vw-1rem)] overflow-hidden', COMBOBOX_FIT]"
|
|
407
407
|
>
|
|
408
408
|
<div class="border-b border-line p-2">
|
|
409
409
|
<!--
|
|
@@ -417,11 +417,11 @@ const guardOutside = useOutsideGuard()
|
|
|
417
417
|
:placeholder="searchPlaceholder ?? text.search"
|
|
418
418
|
:aria-label="text.searchLabel"
|
|
419
419
|
auto-focus
|
|
420
|
-
class="h-(--size-control-sm) w-full rounded-inner border border-line bg-surface-soft px-2.5 text-sm text-ink outline-none placeholder:text-muted focus:border-brand"
|
|
420
|
+
class="lp-entry h-(--size-control-sm) w-full rounded-inner border border-line bg-surface-soft px-2.5 text-sm text-ink outline-none placeholder:text-muted focus:border-brand"
|
|
421
421
|
@input="query = ($event.target as HTMLInputElement).value"
|
|
422
422
|
/>
|
|
423
423
|
</div>
|
|
424
|
-
<ComboboxViewport class="max-h-56 overflow-y-auto p-1">
|
|
424
|
+
<ComboboxViewport class="max-h-56 min-h-0 flex-1 overflow-y-auto p-1">
|
|
425
425
|
<ComboboxEmpty class="px-3 py-4 text-center text-sm italic text-muted">
|
|
426
426
|
{{ emptyText ?? text.empty }}
|
|
427
427
|
</ComboboxEmpty>
|
|
@@ -454,7 +454,7 @@ const guardOutside = useOutsideGuard()
|
|
|
454
454
|
:required="field.required || undefined"
|
|
455
455
|
:aria-invalid="field.invalid || undefined"
|
|
456
456
|
:aria-describedby="mergeDescribedBy(rest['aria-describedby'], field.describedBy, notice.message.value ? noticeId : undefined)"
|
|
457
|
-
class="lp-autofill min-w-0 flex-1 self-stretch bg-transparent px-3 outline-none placeholder:text-muted"
|
|
457
|
+
class="lp-autofill lp-entry min-w-0 flex-1 self-stretch bg-transparent px-3 outline-none placeholder:text-muted"
|
|
458
458
|
@beforeinput="onBeforeEdit"
|
|
459
459
|
@paste="onPaste"
|
|
460
460
|
@compositionend="onCompositionEnd"
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
} from "reka-ui"
|
|
8
8
|
import { computed, useAttrs } from "vue"
|
|
9
9
|
import { mergeClass } from "../composables/useMergedClass"
|
|
10
|
-
import { POPOVER_PANEL } from "./dropdown"
|
|
10
|
+
import { PANEL_FIT, POPOVER_PANEL } from "./dropdown"
|
|
11
11
|
import { useOutsideGuard, usePortalTarget } from "../composables/useView"
|
|
12
12
|
|
|
13
13
|
// The trigger owns its attributes through the slot; fallthrough tags the panel.
|
|
@@ -37,7 +37,7 @@ const open = defineModel<boolean>("open")
|
|
|
37
37
|
// Merged rather than appended, so `panelClass="w-80 p-0"` replaces the default
|
|
38
38
|
// width and padding instead of racing them in stylesheet order.
|
|
39
39
|
const panel = computed(() =>
|
|
40
|
-
mergeClass(POPOVER_PANEL, "z-(--z-popover) w-64 rounded-card p-3 text-sm text-ink outline-none", props.panelClass),
|
|
40
|
+
mergeClass(POPOVER_PANEL, PANEL_FIT, "z-(--z-popover) w-64 rounded-card p-3 text-sm text-ink outline-none", props.panelClass),
|
|
41
41
|
)
|
|
42
42
|
|
|
43
43
|
/*
|
|
@@ -32,7 +32,7 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(() =>
|
|
|
32
32
|
<RadioGroupItem
|
|
33
33
|
:value="value"
|
|
34
34
|
:disabled="disabled"
|
|
35
|
-
class="flex size-[18px] shrink-0 items-center justify-center rounded-full border border-line-strong bg-surface-soft outline-none transition-[color,border-color,box-shadow,scale] duration-[var(--duration-fast)] ease-[var(--ease-emphasized)] not-data-[state=checked]:group-hover/radio:border-brand active:scale-90 motion-reduce:active:scale-100 focus-visible:ring-2 focus-visible:ring-ring data-[state=checked]:border-brand disabled:cursor-not-allowed disabled:opacity-55"
|
|
35
|
+
class="lp-tap flex size-[18px] shrink-0 items-center justify-center rounded-full border border-line-strong bg-surface-soft outline-none transition-[color,border-color,box-shadow,scale] duration-[var(--duration-fast)] ease-[var(--ease-emphasized)] not-data-[state=checked]:group-hover/radio:border-brand active:scale-90 motion-reduce:active:scale-100 focus-visible:ring-2 focus-visible:ring-ring data-[state=checked]:border-brand disabled:cursor-not-allowed disabled:opacity-55"
|
|
36
36
|
>
|
|
37
37
|
<RadioGroupIndicator
|
|
38
38
|
class="flex data-[state=checked]:animate-[indicator-in_140ms_var(--ease-emphasized)]"
|
|
@@ -68,7 +68,7 @@ usePaletteControl({ disabled: () => field.value.disabled })
|
|
|
68
68
|
:disabled="opt.disabled"
|
|
69
69
|
:aria-describedby="opt.description ? `${uid}-${i}-desc` : undefined"
|
|
70
70
|
:class="opt.description ? 'mt-px' : ''"
|
|
71
|
-
class="flex size-[18px] shrink-0 items-center justify-center rounded-full border border-line-strong bg-surface-soft outline-none transition-[color,border-color,box-shadow,scale] duration-[var(--duration-fast)] ease-[var(--ease-emphasized)] not-data-[state=checked]:group-hover/radio:border-brand active:scale-90 motion-reduce:active:scale-100 focus-visible:ring-2 focus-visible:ring-ring data-[state=checked]:border-brand disabled:cursor-not-allowed disabled:opacity-55"
|
|
71
|
+
class="lp-tap flex size-[18px] shrink-0 items-center justify-center rounded-full border border-line-strong bg-surface-soft outline-none transition-[color,border-color,box-shadow,scale] duration-[var(--duration-fast)] ease-[var(--ease-emphasized)] not-data-[state=checked]:group-hover/radio:border-brand active:scale-90 motion-reduce:active:scale-100 focus-visible:ring-2 focus-visible:ring-ring data-[state=checked]:border-brand disabled:cursor-not-allowed disabled:opacity-55"
|
|
72
72
|
>
|
|
73
73
|
<RadioGroupIndicator
|
|
74
74
|
class="flex data-[state=checked]:animate-[indicator-in_140ms_var(--ease-emphasized)]"
|
|
@@ -64,7 +64,7 @@ function onCommit(v: number[] | undefined) {
|
|
|
64
64
|
const { field, wrapper, ownAttrs } = useOwnField(() => ({ error: props.error, disabled: props.disabled }))
|
|
65
65
|
|
|
66
66
|
const { class: rootClass, attrs: rest } = useMergedAttrs(
|
|
67
|
-
`relative flex h-5 w-full touch-none select-none items-center data-[disabled]:opacity-55 ${SLIDER_GLIDE}`, () => ownAttrs())
|
|
67
|
+
`lp-tap relative flex h-5 w-full touch-none select-none items-center data-[disabled]:opacity-55 ${SLIDER_GLIDE}`, () => ownAttrs())
|
|
68
68
|
const { dragging, onPointerDown } = useSliderDrag()
|
|
69
69
|
|
|
70
70
|
const thumbClass =
|
|
@@ -112,12 +112,15 @@ const classes = computed(() =>
|
|
|
112
112
|
line of their own even when they would have fitted beside the title.
|
|
113
113
|
With it the column takes the space that is left and shrinks first.
|
|
114
114
|
|
|
115
|
-
`
|
|
116
|
-
routinely carry something with no spaces in it (a
|
|
117
|
-
container id), and an unbreakable run pushes
|
|
118
|
-
side of whatever card the section sits in.
|
|
119
|
-
shrink; only this lets the text inside it give
|
|
120
|
-
|
|
115
|
+
`overflow-wrap: anywhere` covers the title AND the description in one
|
|
116
|
+
place. Both routinely carry something with no spaces in it (a
|
|
117
|
+
hostname, a URL, a container id), and an unbreakable run pushes
|
|
118
|
+
straight out through the side of whatever card the section sits in.
|
|
119
|
+
min-w-0 lets the column shrink; only this lets the text inside it give
|
|
120
|
+
way. `break-words` was not enough: the title is an item of a flex row,
|
|
121
|
+
and break-word leaves its minimum width at the whole run, so a hash
|
|
122
|
+
in a title widened a phone page until the browser zoomed it out. -->
|
|
123
|
+
<div class="min-w-0 flex-1 [overflow-wrap:anywhere]">
|
|
121
124
|
<div class="flex flex-wrap items-center gap-2">
|
|
122
125
|
<LpIcon v-if="icon" :name="icon" :size="16" class="shrink-0 text-muted" />
|
|
123
126
|
<component :is="as" :class="classes.title()">
|
|
@@ -135,7 +135,7 @@ usePaletteControl({ disabled: () => field.value.disabled })
|
|
|
135
135
|
:value="opt.value"
|
|
136
136
|
:disabled="opt.disabled"
|
|
137
137
|
:aria-label="opt.ariaLabel ?? (opt.label ? undefined : opt.value)"
|
|
138
|
-
class="relative inline-flex h-full items-center justify-center gap-1.5 rounded-inner font-medium outline-none transition-colors duration-[var(--duration-fast)] ease-[var(--ease-emphasized)] focus-visible:ring-2 focus-visible:ring-ring data-[state=on]:text-ink data-[state=off]:text-muted data-[state=off]:hover:text-ink disabled:cursor-not-allowed disabled:opacity-55"
|
|
138
|
+
class="lp-tap relative inline-flex h-full items-center justify-center gap-1.5 rounded-inner font-medium outline-none transition-colors duration-[var(--duration-fast)] ease-[var(--ease-emphasized)] focus-visible:ring-2 focus-visible:ring-ring data-[state=on]:text-ink data-[state=off]:text-muted data-[state=off]:hover:text-ink disabled:cursor-not-allowed disabled:opacity-55"
|
|
139
139
|
:class="[block ? 'flex-1' : '', size === 'lg' ? 'px-4' : 'px-3', hovered === opt.value ? 'text-ink' : '']"
|
|
140
140
|
@pointerenter="onEnter(opt)"
|
|
141
141
|
@pointerup="releaseFocusAfterPointer"
|
|
@@ -43,7 +43,7 @@ import FieldWrapper from "./FieldWrapper.vue"
|
|
|
43
43
|
import { useFieldBlur, useOwnField, mergeDescribedBy } from "../composables/useField"
|
|
44
44
|
import { useLabels } from "../composables/useLpConfig"
|
|
45
45
|
import { useMergedAttrs } from "../composables/useMergedClass"
|
|
46
|
-
import { OPTION_ITEM, optionStagger, POPOVER_PANEL } from "./dropdown"
|
|
46
|
+
import { COMBOBOX_FIT, OPTION_ITEM, optionStagger, POPOVER_PANEL } from "./dropdown"
|
|
47
47
|
import LpIcon from "./LpIcon.vue"
|
|
48
48
|
import OptionContent from "./OptionContent.vue"
|
|
49
49
|
import LpRollingText from "./LpRollingText.vue"
|
|
@@ -599,18 +599,18 @@ usePaletteControl({ disabled: () => field.value.disabled, invalid: () => field.v
|
|
|
599
599
|
@escape-key-down="guardOutside"
|
|
600
600
|
position="popper"
|
|
601
601
|
:side-offset="6"
|
|
602
|
-
:class="[POPOVER_PANEL, 'z-(--z-popover)
|
|
602
|
+
:class="[POPOVER_PANEL, 'z-(--z-popover) w-(--reka-combobox-trigger-width) overflow-hidden', COMBOBOX_FIT]"
|
|
603
603
|
>
|
|
604
604
|
<div v-if="searchable || serverSide" class="border-b border-line p-2">
|
|
605
605
|
<ComboboxInput
|
|
606
606
|
v-model="query"
|
|
607
607
|
:placeholder="searchPlaceholderText"
|
|
608
608
|
:aria-label="labels.searchLabel"
|
|
609
|
-
class="h-(--size-control-sm) w-full rounded-md border border-line bg-surface-soft px-2.5 text-sm text-ink outline-none placeholder:text-muted focus:border-brand"
|
|
609
|
+
class="lp-entry h-(--size-control-sm) w-full rounded-md border border-line bg-surface-soft px-2.5 text-sm text-ink outline-none placeholder:text-muted focus:border-brand"
|
|
610
610
|
/>
|
|
611
611
|
</div>
|
|
612
612
|
|
|
613
|
-
<ComboboxViewport class="max-h-56 overflow-y-auto p-1">
|
|
613
|
+
<ComboboxViewport class="max-h-56 min-h-0 flex-1 overflow-y-auto p-1">
|
|
614
614
|
<div v-if="loading" class="px-3 py-4 text-center text-sm italic text-muted">
|
|
615
615
|
{{ loadingLabel }}
|
|
616
616
|
</div>
|
|
@@ -51,7 +51,7 @@ const { field, wrapper, ownAttrs } = useOwnField(() => ({ error: props.error, di
|
|
|
51
51
|
|
|
52
52
|
// A slider is full-width by default; putting one in a toolbar means constraining it, and a fixed h-5 has to yield to a denser row.
|
|
53
53
|
const { class: rootClass, attrs: rest } = useMergedAttrs(
|
|
54
|
-
`relative flex h-5 w-full touch-none select-none items-center data-[disabled]:opacity-55 ${SLIDER_GLIDE}`, () => ownAttrs())
|
|
54
|
+
`lp-tap relative flex h-5 w-full touch-none select-none items-center data-[disabled]:opacity-55 ${SLIDER_GLIDE}`, () => ownAttrs())
|
|
55
55
|
const { dragging, onPointerDown } = useSliderDrag()
|
|
56
56
|
|
|
57
57
|
usePaletteControl({ disabled: () => field.value.disabled })
|
|
@@ -174,7 +174,7 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(
|
|
|
174
174
|
the same as a live server row elsewhere on the page. -->
|
|
175
175
|
<LpStatus v-if="online" state="online" size="sm" hide-label />
|
|
176
176
|
<LpIcon v-if="icon" :name="icon" :size="14" class="text-muted" />
|
|
177
|
-
<span class="text-[11px] font-semibold uppercase tracking-[0.08em] text-muted">
|
|
177
|
+
<span class="min-w-0 text-[11px] font-semibold uppercase tracking-[0.08em] text-muted [overflow-wrap:anywhere]">
|
|
178
178
|
<Transition v-bind="FADE">
|
|
179
179
|
<LpSkeleton v-if="loading && !label" class="inline-block h-[0.8em] w-20 align-middle" />
|
|
180
180
|
<span v-else>{{ label }}</span>
|
|
@@ -58,7 +58,7 @@ const slots = useSlots()
|
|
|
58
58
|
const labelled = computed(() => Boolean(props.label || props.description || slots.default))
|
|
59
59
|
|
|
60
60
|
const TRACK =
|
|
61
|
-
"group/switch inline-flex h-5 w-9 shrink-0 items-center rounded-pill border border-line bg-surface-soft px-0.5 outline-none transition-colors duration-[var(--duration-fast)] focus-visible:ring-2 focus-visible:ring-ring data-[state=checked]:border-transparent data-[state=checked]:bg-brand disabled:cursor-not-allowed disabled:opacity-55"
|
|
61
|
+
"lp-tap group/switch inline-flex h-5 w-9 shrink-0 items-center rounded-pill border border-line bg-surface-soft px-0.5 outline-none transition-colors duration-[var(--duration-fast)] focus-visible:ring-2 focus-visible:ring-ring data-[state=checked]:border-transparent data-[state=checked]:bg-brand disabled:cursor-not-allowed disabled:opacity-55"
|
|
62
62
|
const THUMB =
|
|
63
63
|
"h-3.5 w-3.5 rounded-full bg-ink shadow transition-[translate,width,background-color] duration-[var(--duration-fast)] ease-[var(--ease-emphasized)] group-active/switch:w-5 group-data-[state=checked]/switch:translate-x-4 group-data-[state=checked]/switch:bg-ink-inverse group-data-[state=checked]/switch:group-active/switch:translate-x-2.5 motion-reduce:group-active/switch:w-3.5"
|
|
64
64
|
|
|
@@ -2362,7 +2362,18 @@ usePaletteCommands(() => {
|
|
|
2362
2362
|
</dl>
|
|
2363
2363
|
</div>
|
|
2364
2364
|
</slot>
|
|
2365
|
+
<!-- `#line` is to a list line what `#card` is to a card: the inside
|
|
2366
|
+
only. Without it the line is built from the columns: the first
|
|
2367
|
+
names the row, the rest follow it in one muted line, which
|
|
2368
|
+
reads well for words and badly for meters or several badges. -->
|
|
2365
2369
|
<div v-else class="min-w-0 flex-1">
|
|
2370
|
+
<slot
|
|
2371
|
+
name="line"
|
|
2372
|
+
:row="entry.row"
|
|
2373
|
+
:selected="isSelected(keyFor(entry.row, entry.index))"
|
|
2374
|
+
:toggle="() => toggleRow(keyFor(entry.row, entry.index), !isSelected(keyFor(entry.row, entry.index)))"
|
|
2375
|
+
:open-menu="openRowMenu"
|
|
2376
|
+
>
|
|
2366
2377
|
<div v-if="titleColumn" class="min-w-0 truncate font-medium text-ink">
|
|
2367
2378
|
<slot :name="`cell-${titleColumn.key}`" :row="entry.row" :value="entry.row[titleColumn.key]" :open-menu="openRowMenu">{{ entry.row[titleColumn.key] }}</slot>
|
|
2368
2379
|
</div>
|
|
@@ -2374,7 +2385,8 @@ usePaletteCommands(() => {
|
|
|
2374
2385
|
</span>
|
|
2375
2386
|
</template>
|
|
2376
2387
|
</div>
|
|
2377
|
-
</
|
|
2388
|
+
</slot>
|
|
2389
|
+
</div>
|
|
2378
2390
|
</div>
|
|
2379
2391
|
</template>
|
|
2380
2392
|
</TransitionGroup>
|
|
@@ -163,7 +163,7 @@ const action =
|
|
|
163
163
|
<span v-if="option.count !== undefined" class="text-xs text-muted tabular-nums">{{ option.count }}</span>
|
|
164
164
|
<button
|
|
165
165
|
type="button"
|
|
166
|
-
class="rounded-control px-1.5 py-0.5 text-xs font-medium text-brand opacity-0 outline-none transition-opacity duration-fast group-hover/opt:opacity-100 hover:bg-brand-soft focus-visible:opacity-100 focus-visible:ring-2 focus-visible:ring-ring"
|
|
166
|
+
class="rounded-control px-1.5 py-0.5 text-xs font-medium text-brand opacity-0 outline-none transition-opacity duration-fast group-hover/opt:opacity-100 [@media(hover:none)]:opacity-100 hover:bg-brand-soft focus-visible:opacity-100 focus-visible:ring-2 focus-visible:ring-ring"
|
|
167
167
|
@click="set(onlyValues(values, [option.value]))"
|
|
168
168
|
>
|
|
169
169
|
{{ labels.only }}
|
|
@@ -513,7 +513,7 @@ usePaletteControl({ label: () => props.label, disabled: () => field.value.disabl
|
|
|
513
513
|
:aria-expanded="options ? listOpen : undefined"
|
|
514
514
|
:aria-controls="options ? listId : undefined"
|
|
515
515
|
:aria-activedescendant="listOpen && activeIndex >= 0 ? optionId(activeIndex) : undefined"
|
|
516
|
-
class="min-w-24 flex-1 bg-transparent px-1 outline-none placeholder:text-muted"
|
|
516
|
+
class="lp-entry min-w-24 flex-1 bg-transparent px-1 outline-none placeholder:text-muted"
|
|
517
517
|
@focus="onFocus"
|
|
518
518
|
@keydown="onKeydown"
|
|
519
519
|
@beforeinput="onBeforeInput"
|
|
@@ -535,7 +535,7 @@ usePaletteControl({ label: () => props.label, disabled: () => field.value.disabl
|
|
|
535
535
|
align="start"
|
|
536
536
|
:side-offset="6"
|
|
537
537
|
:collision-padding="8"
|
|
538
|
-
:class="[POPOVER_PANEL, 'z-(--z-popover) max-h-
|
|
538
|
+
:class="[POPOVER_PANEL, 'z-(--z-popover) max-h-[min(18rem,var(--reka-popper-available-height,18rem))] w-(--reka-popper-anchor-width) overflow-y-auto p-1 outline-none']"
|
|
539
539
|
@open-auto-focus.prevent
|
|
540
540
|
@close-auto-focus.prevent
|
|
541
541
|
@interact-outside="keepOpenInside"
|
|
@@ -82,7 +82,7 @@ defineExpose({ el: box, fit })
|
|
|
82
82
|
const textareaClass = computed(() =>
|
|
83
83
|
mergeClass(
|
|
84
84
|
props.autosize ? "w-full resize-none" : "w-full resize-y",
|
|
85
|
-
"rounded-control border bg-surface-soft px-3 py-2 text-sm text-ink outline-none transition-colors duration-[var(--duration-fast)] placeholder:text-muted focus:border-brand focus:ring-2 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-55",
|
|
85
|
+
"lp-entry rounded-control border bg-surface-soft px-3 py-2 text-sm text-ink outline-none transition-colors duration-[var(--duration-fast)] placeholder:text-muted focus:border-brand focus:ring-2 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-55",
|
|
86
86
|
field.value.invalid ? "border-danger focus:border-danger focus:ring-danger-soft" : "border-line",
|
|
87
87
|
hasField.value ? "" : (attrs.class as string),
|
|
88
88
|
),
|
|
@@ -20,7 +20,7 @@ import { presets } from "../theme/presets"
|
|
|
20
20
|
import { STUDIO_SYSTEM, useThemeStudio } from "../theme/studio"
|
|
21
21
|
import type { TokenSet } from "../theme/tokens"
|
|
22
22
|
import { useTheme } from "../theme/useTheme"
|
|
23
|
-
import { POPOVER_PANEL } from "./dropdown"
|
|
23
|
+
import { PANEL_FIT, POPOVER_PANEL } from "./dropdown"
|
|
24
24
|
import LpContextMenu, { type ContextMenuItemDef } from "./LpContextMenu.vue"
|
|
25
25
|
import LpIcon from "./LpIcon.vue"
|
|
26
26
|
import LpRollingText from "./LpRollingText.vue"
|
|
@@ -374,7 +374,7 @@ usePaletteTheme(() => tuner ?? undefined, { hide: props.studioHide })
|
|
|
374
374
|
align="end"
|
|
375
375
|
:side-offset="6"
|
|
376
376
|
:aria-label="text.studio"
|
|
377
|
-
:class="[POPOVER_PANEL, 'z-(--z-popover) w-96
|
|
377
|
+
:class="[POPOVER_PANEL, PANEL_FIT, 'z-(--z-popover) w-96 rounded-card p-3 text-sm text-ink outline-none']"
|
|
378
378
|
data-lp-glide-position
|
|
379
379
|
@interact-outside="keepForTrigger"
|
|
380
380
|
@escape-key-down="guardOutside"
|
|
@@ -80,7 +80,7 @@ const shellSize = CONTROL_SIZE
|
|
|
80
80
|
// `w-full` is the default, not a rule: a time column of `w-28` must win over it.
|
|
81
81
|
const { class: rootClass, attrs: rest } = useMergedAttrs(() =>
|
|
82
82
|
[
|
|
83
|
-
"flex w-full items-center gap-0.5 rounded-control border bg-surface-soft px-3 text-ink transition-colors duration-[var(--duration-fast)] focus-within:ring-2 focus-within:ring-ring data-[disabled]:cursor-not-allowed data-[disabled]:opacity-55",
|
|
83
|
+
"lp-entry flex w-full items-center gap-0.5 rounded-control border bg-surface-soft px-3 text-ink transition-colors duration-[var(--duration-fast)] focus-within:ring-2 focus-within:ring-ring data-[disabled]:cursor-not-allowed data-[disabled]:opacity-55",
|
|
84
84
|
shellSize[props.size],
|
|
85
85
|
field.value.invalid ? "border-danger focus-within:ring-danger-soft" : "border-line focus-within:border-brand",
|
|
86
86
|
].join(" "), () => ownAttrs())
|
|
@@ -330,7 +330,7 @@ const portalTo = usePortalTarget()
|
|
|
330
330
|
<Teleport :to="portalTo">
|
|
331
331
|
<ToastViewport
|
|
332
332
|
:data-lp-toasts="viewportId"
|
|
333
|
-
class="fixed bottom-0 right-0 z-(--z-toast) flex w-[min(92vw,22rem)] flex-col gap-2 p-4 outline-none"
|
|
333
|
+
class="fixed bottom-0 right-0 z-(--z-toast) flex w-[min(92vw,22rem)] flex-col gap-2 p-4 pr-[max(1rem,env(safe-area-inset-right))] pb-[max(1rem,env(safe-area-inset-bottom))] outline-none"
|
|
334
334
|
/>
|
|
335
335
|
</Teleport>
|
|
336
336
|
</ToastProvider>
|
|
@@ -36,13 +36,14 @@ import {
|
|
|
36
36
|
TooltipRoot,
|
|
37
37
|
TooltipTrigger,
|
|
38
38
|
} from "reka-ui"
|
|
39
|
-
import { onBeforeUnmount, ref, watch, type FunctionalComponent } from "vue"
|
|
39
|
+
import { computed, mergeProps, onBeforeUnmount, ref, useAttrs, watch, type FunctionalComponent } from "vue"
|
|
40
40
|
import {
|
|
41
41
|
springSettled,
|
|
42
42
|
springStep,
|
|
43
43
|
type SpringState,
|
|
44
44
|
} from "../composables/usePillTransition"
|
|
45
45
|
import { PANEL_SURFACE, TOOLTIP_ANIM } from "./dropdown"
|
|
46
|
+
import { useTouchHold } from "../composables/useTouchHold"
|
|
46
47
|
import { prefersReducedMotion } from "../composables/easing"
|
|
47
48
|
import { useOutsideGuard, usePortalTarget, useView, viewWindow } from "../composables/useView"
|
|
48
49
|
|
|
@@ -86,8 +87,16 @@ const props = withDefaults(
|
|
|
86
87
|
* a scroll that should have dismissed it.
|
|
87
88
|
*/
|
|
88
89
|
disabled?: boolean
|
|
90
|
+
/**
|
|
91
|
+
* Touch has no hover, and a tap is the control's own. With `hold` a
|
|
92
|
+
* press-and-hold shows the tip (and that press does not also press the
|
|
93
|
+
* control); the next touch anywhere, or Escape, hides it. `none` leaves
|
|
94
|
+
* touch to the trigger. A followed tip (`followCursor`) never holds: its
|
|
95
|
+
* trigger is a chart or canvas with its own touch reading.
|
|
96
|
+
*/
|
|
97
|
+
touch?: "hold" | "none"
|
|
89
98
|
}>(),
|
|
90
|
-
{ side: "top" },
|
|
99
|
+
{ side: "top", touch: "hold" },
|
|
91
100
|
)
|
|
92
101
|
|
|
93
102
|
/*
|
|
@@ -123,6 +132,9 @@ const target = { x: 0, y: 0 }
|
|
|
123
132
|
const offset = ref({ x: 0, y: 0 })
|
|
124
133
|
const tipBody = ref<HTMLElement | null>(null)
|
|
125
134
|
|
|
135
|
+
/* How long a finger rests on the trigger before the tip shows. */
|
|
136
|
+
const HOLD_MS = 450
|
|
137
|
+
|
|
126
138
|
/* reka's own collision padding, which the follow offset would otherwise undo. */
|
|
127
139
|
const EDGE = 8
|
|
128
140
|
|
|
@@ -241,7 +253,30 @@ const open = ref(false)
|
|
|
241
253
|
/* What reka last asked for, regardless of whether it was allowed at the time. */
|
|
242
254
|
const wanted = ref(false)
|
|
243
255
|
|
|
256
|
+
/*
|
|
257
|
+
* A tip opened by a touch hold stays until the next touch or Escape. reka
|
|
258
|
+
* would close it as soon as the finger lifts (a touch leaves the trigger
|
|
259
|
+
* then), so while it is held its own requests to close are not taken.
|
|
260
|
+
*/
|
|
261
|
+
const heldOpen = ref(false)
|
|
262
|
+
const hold = useTouchHold({
|
|
263
|
+
enabled: () => props.touch === "hold" && !props.followCursor && !props.disabled,
|
|
264
|
+
delay: () => HOLD_MS,
|
|
265
|
+
onHold: () => {
|
|
266
|
+
heldOpen.value = true
|
|
267
|
+
wanted.value = true
|
|
268
|
+
open.value = true
|
|
269
|
+
},
|
|
270
|
+
})
|
|
271
|
+
|
|
272
|
+
function release() {
|
|
273
|
+
heldOpen.value = false
|
|
274
|
+
wanted.value = false
|
|
275
|
+
open.value = false
|
|
276
|
+
}
|
|
277
|
+
|
|
244
278
|
const onOpenChange = (next: boolean) => {
|
|
279
|
+
if (!next && heldOpen.value) return
|
|
245
280
|
// What reka wants, kept apart from what is currently allowed.
|
|
246
281
|
wanted.value = next
|
|
247
282
|
open.value = next && !props.disabled
|
|
@@ -265,8 +300,11 @@ const onOpenChange = (next: boolean) => {
|
|
|
265
300
|
watch(
|
|
266
301
|
() => props.disabled,
|
|
267
302
|
(off) => {
|
|
268
|
-
if (off)
|
|
269
|
-
|
|
303
|
+
if (off) {
|
|
304
|
+
hold.cancel()
|
|
305
|
+
heldOpen.value = false
|
|
306
|
+
open.value = false
|
|
307
|
+
} else if (wanted.value) open.value = true
|
|
270
308
|
},
|
|
271
309
|
)
|
|
272
310
|
|
|
@@ -289,6 +327,18 @@ watch(
|
|
|
289
327
|
},
|
|
290
328
|
)
|
|
291
329
|
|
|
330
|
+
watch(heldOpen, (held, _, onCleanup) => {
|
|
331
|
+
if (!held) return
|
|
332
|
+
const win = viewWindow(view.value)
|
|
333
|
+
const onKey = (event: KeyboardEvent) => event.key === "Escape" && release()
|
|
334
|
+
win.addEventListener("pointerdown", release, true)
|
|
335
|
+
win.addEventListener("keydown", onKey, true)
|
|
336
|
+
onCleanup(() => {
|
|
337
|
+
win.removeEventListener("pointerdown", release, true)
|
|
338
|
+
win.removeEventListener("keydown", onKey, true)
|
|
339
|
+
})
|
|
340
|
+
})
|
|
341
|
+
|
|
292
342
|
onBeforeUnmount(stopSpring)
|
|
293
343
|
|
|
294
344
|
// Overlays open in the window this component renders in (an LpFrame's, inside one).
|
|
@@ -302,6 +352,9 @@ const guardOutside = useOutsideGuard()
|
|
|
302
352
|
// LpPopover's, as-child) lost its id, ARIA state and click, and its popover
|
|
303
353
|
// never opened. They go to the trigger, which merges them onto the slot's element.
|
|
304
354
|
defineOptions({ inheritAttrs: false })
|
|
355
|
+
// The caller's own listeners on the trigger run beside the hold's, not instead of them.
|
|
356
|
+
const attrs = useAttrs()
|
|
357
|
+
const triggerAttrs = computed(() => mergeProps(attrs, hold.handlers))
|
|
305
358
|
</script>
|
|
306
359
|
|
|
307
360
|
<template>
|
|
@@ -324,8 +377,9 @@ defineOptions({ inheritAttrs: false })
|
|
|
324
377
|
lands on the real button rather than on a wrapper. -->
|
|
325
378
|
<TooltipTrigger
|
|
326
379
|
as-child
|
|
327
|
-
v-bind="
|
|
380
|
+
v-bind="triggerAttrs"
|
|
328
381
|
:aria-label="label ? content : undefined"
|
|
382
|
+
:class="touch === 'hold' && !followCursor ? '[-webkit-touch-callout:none]' : undefined"
|
|
329
383
|
@pointermove="onPointerMove"
|
|
330
384
|
>
|
|
331
385
|
<slot />
|
package/src/components/badge.ts
CHANGED
|
@@ -5,4 +5,4 @@
|
|
|
5
5
|
* keyboard handling), so it still looks like the badge's own.
|
|
6
6
|
*/
|
|
7
7
|
export const BADGE_REMOVE =
|
|
8
|
-
"grid size-4 shrink-0 place-items-center rounded-pill text-muted outline-none transition-colors duration-fast hover:bg-ink/10 hover:text-ink focus-visible:ring-2 focus-visible:ring-ring"
|
|
8
|
+
"lp-tap grid size-4 shrink-0 place-items-center rounded-pill text-muted outline-none transition-colors duration-fast hover:bg-ink/10 hover:text-ink focus-visible:ring-2 focus-visible:ring-ring"
|
|
@@ -45,6 +45,21 @@ export const PANEL_SURFACE =
|
|
|
45
45
|
*/
|
|
46
46
|
export const POPOVER_PANEL = PANEL_SURFACE + " " + POPOVER_ANIM
|
|
47
47
|
|
|
48
|
+
/**
|
|
49
|
+
* Keeps a free-form panel (popover, menu, hover card) inside the window. Its
|
|
50
|
+
* width stops 0.5rem short of each edge, so a w-96 panel or a long menu label
|
|
51
|
+
* no longer runs off a phone; its height stops at the room reka measured on the
|
|
52
|
+
* side it opened, and the rest scrolls. A date picker opened in landscape used
|
|
53
|
+
* to hang off the top of the screen with its first weeks out of reach.
|
|
54
|
+
*/
|
|
55
|
+
export const PANEL_FIT = "max-w-[calc(100vw-1rem)] max-h-(--reka-popper-available-height) overflow-y-auto"
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* A list row on a touch screen: 2.5rem tall instead of 2, so a thumb picks the
|
|
59
|
+
* row it is on and not its neighbour. A mouse keeps the compact row.
|
|
60
|
+
*/
|
|
61
|
+
export const TOUCH_ROW = "[@media(hover:none)_and_(pointer:coarse)]:py-2.5"
|
|
62
|
+
|
|
48
63
|
/**
|
|
49
64
|
* A selectable option row: layout, highlight state, and the staggered cascade.
|
|
50
65
|
* Cascades downward by default and upward when the panel is flipped above the
|
|
@@ -53,6 +68,7 @@ export const POPOVER_PANEL = PANEL_SURFACE + " " + POPOVER_ANIM
|
|
|
53
68
|
*/
|
|
54
69
|
export const OPTION_ITEM =
|
|
55
70
|
"flex cursor-pointer items-center gap-2 rounded-inner px-2.5 py-1.5 text-sm text-ink outline-none " +
|
|
71
|
+
TOUCH_ROW + " " +
|
|
56
72
|
"animate-[rise-in_180ms_var(--ease-emphasized)_both] [--rise:-6px] group-data-[side=top]:[--rise:6px] " +
|
|
57
73
|
"data-[highlighted]:bg-brand-soft data-[highlighted]:text-brand"
|
|
58
74
|
|
|
@@ -74,6 +90,14 @@ export const optionStagger = (index: number): { animationDelay: string } => ({
|
|
|
74
90
|
/* Where a combobox list sits under its field. */
|
|
75
91
|
export const COMBOBOX_PANEL = { collisionPadding: 8, position: "popper", sideOffset: 6 } as const
|
|
76
92
|
|
|
93
|
+
/*
|
|
94
|
+
* A combobox list's height: 18rem, or less where reka measured less room on the
|
|
95
|
+
* side it opened (a phone in landscape has about 170px under a field). The
|
|
96
|
+
* panel is a column so its option list, which carries `min-h-0 flex-1`, is what
|
|
97
|
+
* shrinks and scrolls; the search field above it stays.
|
|
98
|
+
*/
|
|
99
|
+
export const COMBOBOX_FIT = "flex flex-col max-h-[min(18rem,var(--reka-popper-available-height,18rem))]"
|
|
100
|
+
|
|
77
101
|
export const CLOSE_ICON =
|
|
78
102
|
"transition-transform duration-medium ease-[var(--ease-emphasized)] " +
|
|
79
103
|
"group-hover:rotate-90 group-active:scale-90 motion-reduce:group-hover:rotate-0"
|
|
@@ -2,6 +2,12 @@ import { computed, ref, type ComputedRef, type Ref } from "vue"
|
|
|
2
2
|
|
|
3
3
|
const STEP = 20
|
|
4
4
|
|
|
5
|
+
/*
|
|
6
|
+
* A dialog's ceiling: 90% of the window, and never closer than 1rem to its
|
|
7
|
+
* edges or to a notch or home indicator on a phone that draws under them.
|
|
8
|
+
*/
|
|
9
|
+
export const MODAL_MAX_HEIGHT = "min(90vh, calc(100dvh - 2rem - env(safe-area-inset-top) - env(safe-area-inset-bottom)))"
|
|
10
|
+
|
|
5
11
|
/*
|
|
6
12
|
* One opening-order stack for every modal surface. Pinned to a global symbol
|
|
7
13
|
* like the toast queue: a barrel import and a deep import can load this module
|
|
@@ -69,9 +69,12 @@ export function useReveal(options: UseRevealOptions = {}): UseReveal {
|
|
|
69
69
|
const { threshold = 0.25, rootMargin = "0px" } = options
|
|
70
70
|
|
|
71
71
|
const el = ref<Element | null>(null)
|
|
72
|
-
//
|
|
73
|
-
//
|
|
74
|
-
|
|
72
|
+
// The same starting value on the server and on the hydrating client, or every
|
|
73
|
+
// component with an entrance hydrated with a mismatch (the server said
|
|
74
|
+
// revealed, the client said waiting). Only an explicit `immediate` starts
|
|
75
|
+
// revealed; the observer, and reduced motion, decide on the client once
|
|
76
|
+
// mounted, the way useMediaQuery does.
|
|
77
|
+
const revealed = ref(!!toValue(options.immediate))
|
|
75
78
|
|
|
76
79
|
let observer: IntersectionObserver | null = null
|
|
77
80
|
|
|
@@ -88,7 +91,7 @@ export function useReveal(options: UseRevealOptions = {}): UseReveal {
|
|
|
88
91
|
watch(
|
|
89
92
|
[el, () => toValue(options.immediate)],
|
|
90
93
|
([node, immediate]) => {
|
|
91
|
-
if (revealed.value) return
|
|
94
|
+
if (revealed.value || typeof window === "undefined") return
|
|
92
95
|
if (immediate || prefersReducedMotion()) {
|
|
93
96
|
reveal()
|
|
94
97
|
return
|