@leavepulse/ui 0.78.0 → 0.78.2
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/chunks/{DropzoneLinkField-DXIr0eFz.js → DropzoneLinkField-CyE7TJ5f.js} +1 -1
- package/dist/chunks/FieldLeading-mr81aEAP.js +27 -0
- package/dist/chunks/LpAddressInput-ft8fQOGx.js +401 -0
- package/dist/chunks/{LpAppShell-BNAVTQsY.js → LpAppShell-DkvU6IBB.js} +23 -4
- package/dist/chunks/LpAutocomplete-CrZbd0uv.js +310 -0
- package/dist/chunks/LpCheckbox-CCWCwD0y.js +129 -0
- package/dist/chunks/{LpCodeBlock-DuzmnIHl.js → LpCodeBlock-DBqy4RLd.js} +1 -1
- package/dist/chunks/{LpCodeDisplay-CO8fngxa.js → LpCodeDisplay-D59uPwRb.js} +3 -3
- package/dist/chunks/LpColorPicker-BONURBFN.js +574 -0
- package/dist/chunks/{LpConfirmDialog-6oCIJTDa.js → LpConfirmDialog-CnrmzkuN.js} +1 -1
- package/dist/chunks/{LpConfirmHost-DgHvfT6s.js → LpConfirmHost-CwbObVGH.js} +1 -1
- package/dist/chunks/{LpContextMenu--eMGIql6.js → LpContextMenu-Bx_IhQDS.js} +5 -5
- package/dist/chunks/{LpCopyButton-7EOSIuwa.js → LpCopyButton-sAMvWeLP.js} +1 -1
- package/dist/chunks/{LpDataList-DgBeM01y.js → LpDataList-CaVfsozH.js} +2 -2
- package/dist/chunks/LpDatePicker-C2AOtxgF.js +171 -0
- package/dist/chunks/{LpDateRangePicker-CNQXdTVv.js → LpDateRangePicker-CiXdIAyh.js} +43 -35
- package/dist/chunks/{LpDrawer-Bt9UUqt_.js → LpDrawer-DXNQFpAA.js} +1 -1
- package/dist/chunks/{LpFileTree-BSx1kBoV.js → LpFileTree-CpNurwZp.js} +3 -3
- package/dist/chunks/{LpFileTreeNode-4dVmFHb3.js → LpFileTreeNode-DdF8Cq-s.js} +2 -2
- package/dist/chunks/{LpHoverCard-DanH3Nz1.js → LpHoverCard-CHE9jAJG.js} +6 -6
- package/dist/chunks/{LpInfraNode-BAU40Km2.js → LpInfraNode-Bb5nbuFY.js} +17 -0
- package/dist/chunks/{LpInput-DL6HEJdi.js → LpInput-CbI7z5fQ.js} +21 -25
- package/dist/chunks/{LpInspector-CuoMAhiw.js → LpInspector-DO3fTCQ1.js} +2 -2
- package/dist/chunks/{LpLaneNode-HtnQf2FF.js → LpLaneNode-Bqcdm8Rf.js} +19 -1
- package/dist/chunks/{LpLightbox-xvpQ64MM.js → LpLightbox-CtDTQV01.js} +1 -1
- package/dist/chunks/{LpLogInspector-6cNnXqp4.js → LpLogInspector-CcSJSGan.js} +1 -1
- package/dist/chunks/{LpLogViewer-Dvb1E9_G.js → LpLogViewer-CWu49Dko.js} +4 -4
- package/dist/chunks/{LpMediaGallery-Df-tiWeq.js → LpMediaGallery-BYEwySoF.js} +2 -2
- package/dist/chunks/{LpModal-DrtQ_1A2.js → LpModal-DDvrSAnf.js} +1 -1
- package/dist/chunks/{LpModalNav-DkmGCnZE.js → LpModalNav-DI3BW5ff.js} +2 -2
- package/dist/chunks/{LpNotificationBell-D_wklO1m.js → LpNotificationBell-DUbVyK6R.js} +3 -3
- package/dist/chunks/LpNumberField-C8LfUz_R.js +154 -0
- package/dist/chunks/LpOtpInput-B1X9l9no.js +140 -0
- package/dist/chunks/LpPageScrollbar-D2O0xhJw.js +164 -0
- package/dist/chunks/{LpPaletteTheme-dVtxHx-F.js → LpPaletteTheme-B-xuZiKK.js} +2 -2
- package/dist/chunks/{LpPasswordInput-B-zD6tUS.js → LpPasswordInput-Duc_3wPv.js} +2 -2
- package/dist/chunks/{LpPhoneInput-BSL7NX4z.js → LpPhoneInput-CctcRK9K.js} +124 -118
- package/dist/chunks/{LpPie-9t_guO49.js → LpPie-oVNHjsKD.js} +2 -2
- package/dist/chunks/{LpPipeline-rjXvEDRJ.js → LpPipeline-BSXT6oUJ.js} +1 -1
- package/dist/chunks/LpRadioGroup-DTM0nd9G.js +103 -0
- package/dist/chunks/LpRangeSlider-DJsEZXad.js +97 -0
- package/dist/chunks/{LpRelativeTime-BovUk8MP.js → LpRelativeTime-0zI-44ve.js} +1 -1
- package/dist/chunks/LpRepeater-Czib3rmv.js +193 -0
- package/dist/chunks/{LpScrollArea-ay4Xo_hg.js → LpScrollArea-CqvwfZs2.js} +9 -12
- package/dist/chunks/{LpSecretField-DBvU6iAw.js → LpSecretField-BtlCk3IH.js} +4 -4
- package/dist/chunks/LpSegmented-sskrJKJJ.js +128 -0
- package/dist/chunks/LpSelect-JFZehOkd.js +515 -0
- package/dist/chunks/{LpServiceNode-D8uJ8sGk.js → LpServiceNode-Cln2ntdw.js} +17 -0
- package/dist/chunks/{LpSidebar-kSkpPh3-.js → LpSidebar-CvRvVBTt.js} +4 -4
- package/dist/chunks/{LpSidebarNav-C2mvqrY2.js → LpSidebarNav-CE0jQS8i.js} +2 -2
- package/dist/chunks/{LpSidebarRow-eRP4Q31q.js → LpSidebarRow-DOTg1txp.js} +2 -2
- package/dist/chunks/LpSlider-C4ilCtZX.js +76 -0
- package/dist/chunks/{LpStat-pMNZgFcq.js → LpStat-DPjc6_fX.js} +17 -4
- package/dist/chunks/LpSwitch-DyAYTchz.js +122 -0
- package/dist/chunks/{LpTableFilter-DgARTsA1.js → LpTableFilter-CJg5eIcV.js} +2 -2
- package/dist/chunks/{LpTagsInput-Dt0mteSj.js → LpTagsInput-CkNxpNYa.js} +4 -3
- package/dist/chunks/{LpTextarea-Co0b9PVx.js → LpTextarea-CFK_5QB8.js} +3 -2
- package/dist/chunks/{LpThemeStudio-DzYfE-5k.js → LpThemeStudio-R-wohbCY.js} +3 -3
- package/dist/chunks/{LpThemeSwitcher-CFLkdbBL.js → LpThemeSwitcher-Bzy15MCP.js} +2 -2
- package/dist/chunks/LpTimeField-B5n0m8m0.js +115 -0
- package/dist/chunks/{LpToaster-QFj0P-5m.js → LpToaster-Ct_ZOhCh.js} +1 -1
- package/dist/chunks/{LpTooltip-BJf3lZMq.js → LpTooltip-Bz_rrq5S.js} +5 -5
- package/dist/chunks/{LpTopologyCanvas-BOHHHZ_6.js → LpTopologyCanvas-BNgw9RyZ.js} +14 -9
- package/dist/chunks/{LpTruncate-C6u3gjc6.js → LpTruncate-ellq9RYM.js} +2 -2
- package/dist/chunks/{LpUptimeBar-BnYmR2rj.js → LpUptimeBar-BoxcCDVY.js} +1 -1
- package/dist/chunks/{LpWorkspaceSwitcher-D0e9upAy.js → LpWorkspaceSwitcher-ugv-fyig.js} +2 -2
- package/dist/chunks/{RevealToggle-CCPDE8Mv.js → RevealToggle-D_iyrB8d.js} +1 -1
- package/dist/component-names.d.ts +1 -1
- package/dist/components/DropzoneLinkField.vue.js +1 -1
- package/dist/components/FieldLeading.vue.d.ts +16 -0
- package/dist/components/FieldLeading.vue.js +4 -0
- package/dist/components/LpAddressInput.vue.d.ts +4 -0
- package/dist/components/LpAddressInput.vue.js +1 -1
- package/dist/components/LpAlert.vue.d.ts +3 -3
- package/dist/components/LpAppShell.vue.d.ts +16 -1
- package/dist/components/LpAppShell.vue.js +1 -1
- package/dist/components/LpAutocomplete.vue.d.ts +4 -0
- package/dist/components/LpAutocomplete.vue.js +1 -1
- package/dist/components/LpBadge.vue.d.ts +2 -2
- package/dist/components/LpChart.vue.d.ts +1 -1
- package/dist/components/LpChart.vue.js +1 -1
- package/dist/components/LpCheckbox.vue.d.ts +4 -0
- 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/LpCollapse.vue.d.ts +1 -1
- package/dist/components/LpColorPicker.vue.d.ts +5 -3
- package/dist/components/LpColorPicker.vue.js +1 -1
- package/dist/components/LpCommandPalette.vue.js +1 -1
- 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/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/LpDropzone.vue.js +1 -1
- package/dist/components/LpFileTree.vue.js +1 -1
- package/dist/components/LpFileTreeNode.vue.js +1 -1
- package/dist/components/LpHoverCard.vue.js +1 -1
- package/dist/components/LpImageEditor.vue.d.ts +1 -1
- package/dist/components/LpInfraNode.vue.d.ts +11 -2
- package/dist/components/LpInfraNode.vue.js +1 -1
- package/dist/components/LpInput.vue.js +1 -1
- package/dist/components/LpInspector.vue.js +1 -1
- package/dist/components/LpLaneNode.vue.d.ts +2 -3
- package/dist/components/LpLaneNode.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/LpNumberField.vue.d.ts +17 -2
- package/dist/components/LpNumberField.vue.js +1 -1
- package/dist/components/LpOtpInput.vue.d.ts +4 -0
- package/dist/components/LpOtpInput.vue.js +1 -1
- package/dist/components/LpPageScrollbar.vue.d.ts +10 -0
- package/dist/components/LpPageScrollbar.vue.js +4 -0
- package/dist/components/LpPaletteTheme.vue.js +1 -1
- package/dist/components/LpPasswordInput.vue.js +1 -1
- package/dist/components/LpPhoneInput.vue.d.ts +4 -0
- 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/LpRadioGroup.vue.d.ts +4 -0
- package/dist/components/LpRadioGroup.vue.js +1 -1
- package/dist/components/LpRangeSlider.vue.d.ts +4 -0
- package/dist/components/LpRangeSlider.vue.js +1 -1
- package/dist/components/LpRelativeTime.vue.js +1 -1
- package/dist/components/LpRepeater.vue.d.ts +2 -0
- package/dist/components/LpRepeater.vue.js +1 -1
- package/dist/components/LpScrollArea.vue.d.ts +1 -1
- package/dist/components/LpScrollArea.vue.js +1 -1
- package/dist/components/LpSecretField.vue.js +1 -1
- package/dist/components/LpSegmented.vue.d.ts +4 -0
- package/dist/components/LpSegmented.vue.js +1 -1
- package/dist/components/LpSelect.vue.d.ts +2 -0
- package/dist/components/LpSelect.vue.js +1 -1
- package/dist/components/LpServiceNode.vue.d.ts +8 -2
- package/dist/components/LpServiceNode.vue.js +1 -1
- package/dist/components/LpShinyText.vue.d.ts +1 -1
- package/dist/components/LpShortcutsDialog.vue.js +1 -1
- package/dist/components/LpSidebar.vue.d.ts +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.d.ts +4 -0
- package/dist/components/LpSlider.vue.js +1 -1
- package/dist/components/LpStat.vue.d.ts +16 -0
- package/dist/components/LpStat.vue.js +1 -1
- package/dist/components/LpSwitch.vue.d.ts +4 -0
- package/dist/components/LpSwitch.vue.js +1 -1
- package/dist/components/LpTable.vue.js +5 -5
- package/dist/components/LpTableFilter.vue.js +1 -1
- package/dist/components/LpTabs.vue.d.ts +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.d.ts +4 -0
- package/dist/components/LpTimeField.vue.js +1 -1
- package/dist/components/LpToaster.vue.js +1 -1
- package/dist/components/LpTooltip.vue.js +1 -1
- package/dist/components/LpTopologyCanvas.vue.d.ts +2 -2
- package/dist/components/LpTopologyCanvas.vue.js +1 -1
- package/dist/components/LpTruncate.vue.js +1 -1
- package/dist/components/LpUptimeBar.vue.js +1 -1
- package/dist/components/LpWorkspaceSwitcher.vue.js +1 -1
- package/dist/components/RevealToggle.vue.js +1 -1
- package/dist/components/calendar.d.ts +2 -0
- package/dist/components/menuWhen.js +1 -1
- package/dist/components/scrollbar.d.ts +34 -0
- package/dist/components/scrollbar.js +47 -0
- package/dist/composables/propCheck.d.ts +1 -1
- package/dist/composables/propCheck.js +15 -4
- package/dist/composables/useField.d.ts +50 -0
- package/dist/composables/useField.js +31 -2
- package/dist/composables/useMergedClass.d.ts +6 -2
- package/dist/composables/useMergedClass.js +6 -6
- package/dist/composables/usePaletteCommands.js +4 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.js +170 -168
- package/package.json +1 -1
- package/src/component-names.ts +1 -0
- package/src/components/FieldLeading.vue +17 -0
- package/src/components/LpAddressInput.vue +8 -4
- package/src/components/LpAppShell.vue +23 -0
- package/src/components/LpAutocomplete.vue +11 -5
- package/src/components/LpCheckbox.vue +8 -6
- package/src/components/LpColorPicker.vue +7 -4
- package/src/components/LpContextMenu.vue +7 -0
- package/src/components/LpDatePicker.vue +8 -4
- package/src/components/LpDateRangePicker.vue +8 -4
- package/src/components/LpHoverCard.vue +7 -0
- package/src/components/LpInfraNode.vue +4 -3
- package/src/components/LpInput.vue +7 -7
- package/src/components/LpLaneNode.vue +5 -1
- package/src/components/LpNumberField.vue +18 -7
- package/src/components/LpOtpInput.vue +8 -6
- package/src/components/LpPageScrollbar.vue +195 -0
- package/src/components/LpPhoneInput.vue +11 -6
- package/src/components/LpRadioGroup.vue +9 -5
- package/src/components/LpRangeSlider.vue +8 -4
- package/src/components/LpRepeater.vue +16 -4
- package/src/components/LpScrollArea.vue +27 -23
- package/src/components/LpSegmented.vue +8 -4
- package/src/components/LpSelect.vue +8 -7
- package/src/components/LpServiceNode.vue +4 -2
- package/src/components/LpSlider.vue +8 -4
- package/src/components/LpStat.vue +25 -5
- package/src/components/LpSwitch.vue +8 -5
- package/src/components/LpTagsInput.vue +4 -3
- package/src/components/LpTextarea.vue +3 -2
- package/src/components/LpTimeField.vue +8 -6
- package/src/components/LpTooltip.vue +7 -0
- package/src/components/LpTopologyCanvas.vue +3 -1
- package/src/components/calendar.ts +2 -0
- package/src/components/scrollbar.ts +71 -0
- package/src/composables/propCheck.ts +20 -5
- package/src/composables/useField.ts +52 -0
- package/src/composables/useMergedClass.ts +11 -5
- package/src/composables/usePaletteCommands.ts +5 -2
- package/src/index.ts +1 -0
- package/dist/chunks/LpAddressInput-ZqoZ-N2Y.js +0 -393
- package/dist/chunks/LpAutocomplete-DkY6CSyD.js +0 -302
- package/dist/chunks/LpCheckbox-D6RuDW4X.js +0 -124
- package/dist/chunks/LpColorPicker-BxAHEnR5.js +0 -567
- package/dist/chunks/LpDatePicker-DwSDY0mt.js +0 -163
- package/dist/chunks/LpNumberField-CNHsmSDZ.js +0 -134
- package/dist/chunks/LpOtpInput-BRpLBbOD.js +0 -132
- package/dist/chunks/LpRadioGroup-BrazjLY_.js +0 -95
- package/dist/chunks/LpRangeSlider-fz61tzc-.js +0 -89
- package/dist/chunks/LpRepeater-BkJHSPI1.js +0 -180
- package/dist/chunks/LpSegmented-DbEJhbv-.js +0 -123
- package/dist/chunks/LpSelect-Y-iuSymW.js +0 -512
- package/dist/chunks/LpSlider-A4ixovgS.js +0 -68
- package/dist/chunks/LpSwitch-DnC2-0aV.js +0 -115
- package/dist/chunks/LpTimeField-DeOcKWJq.js +0 -110
|
@@ -65,6 +65,7 @@ import LpBottomNav from "./LpBottomNav.vue"
|
|
|
65
65
|
import LpContextSwitcher from "./LpContextSwitcher.vue"
|
|
66
66
|
import LpButton from "./LpButton.vue"
|
|
67
67
|
import LpIcon from "./LpIcon.vue"
|
|
68
|
+
import LpPageScrollbar from "./LpPageScrollbar.vue"
|
|
68
69
|
import LpScrollArea from "./LpScrollArea.vue"
|
|
69
70
|
import LpShift from "./LpShift.vue"
|
|
70
71
|
import LpSidebar from "./LpSidebar.vue"
|
|
@@ -130,6 +131,13 @@ const props = withDefaults(
|
|
|
130
131
|
* remounts the page.
|
|
131
132
|
*/
|
|
132
133
|
scroll?: "document" | "region"
|
|
134
|
+
/**
|
|
135
|
+
* While the document scrolls, draw the kit's overlay bar for it
|
|
136
|
+
* (LpPageScrollbar) under the header and above the bottom bar, in place of
|
|
137
|
+
* the browser's, which takes width from the page. `false` keeps the
|
|
138
|
+
* browser's. Where the browser's bars float anyway (phones) it draws none.
|
|
139
|
+
*/
|
|
140
|
+
pageScrollbar?: boolean
|
|
133
141
|
/** Loading skeleton for the nav. */
|
|
134
142
|
loading?: boolean
|
|
135
143
|
/** Text overrides: the burger's name and the skip link. */
|
|
@@ -206,6 +214,7 @@ const props = withDefaults(
|
|
|
206
214
|
divider: true,
|
|
207
215
|
contentClass: "mx-auto w-full max-w-6xl",
|
|
208
216
|
scroll: "document",
|
|
217
|
+
pageScrollbar: true,
|
|
209
218
|
// Unset, not false: an absent boolean would hand the sidebar "expanded"
|
|
210
219
|
// and override the state it remembers.
|
|
211
220
|
compact: undefined,
|
|
@@ -380,6 +389,18 @@ watch(bleeding, (bleed) => {
|
|
|
380
389
|
void nextTick(() => view.scrollTo({ top, behavior: "instant" }))
|
|
381
390
|
}
|
|
382
391
|
})
|
|
392
|
+
// The bottom bar's height, for the page's scrollbar to stop above it (0 where it is hidden).
|
|
393
|
+
const bottomBarEl = ref<{ $el: HTMLElement } | null>(null)
|
|
394
|
+
const bottomBarHeight = ref(0)
|
|
395
|
+
let bottomBarSize: ResizeObserver | undefined
|
|
396
|
+
onMounted(() => {
|
|
397
|
+
const el = bottomBarEl.value?.$el
|
|
398
|
+
if (!el || el.nodeType !== 1) return
|
|
399
|
+
bottomBarSize = new (nodeWindow(el).ResizeObserver)(() => (bottomBarHeight.value = el.offsetHeight))
|
|
400
|
+
bottomBarSize.observe(el)
|
|
401
|
+
})
|
|
402
|
+
onBeforeUnmount(() => bottomBarSize?.disconnect())
|
|
403
|
+
|
|
383
404
|
let releaseDocument: (() => void) | undefined
|
|
384
405
|
onMounted(() => {
|
|
385
406
|
if (!documentScroll || !pageEl.value) return
|
|
@@ -748,8 +769,10 @@ const backToTopClass = computed(() =>
|
|
|
748
769
|
<slot name="aside" />
|
|
749
770
|
</LpScrollArea>
|
|
750
771
|
</aside>
|
|
772
|
+
<LpPageScrollbar v-if="windowScrolls && pageScrollbar" top="var(--lp-shell-top)" :bottom="`${bottomBarHeight}px`" />
|
|
751
773
|
<LpBottomNav
|
|
752
774
|
v-if="bottomNav"
|
|
775
|
+
ref="bottomBarEl"
|
|
753
776
|
:class="[burgerHidden[mobileBreakpoint], spanClass, windowScrolls && `${STICKY} bottom-0`]"
|
|
754
777
|
:items="bottomItems"
|
|
755
778
|
:is-active="activeTest"
|
|
@@ -10,6 +10,7 @@ export type AutocompleteLabels = {
|
|
|
10
10
|
</script>
|
|
11
11
|
|
|
12
12
|
<script setup lang="ts">
|
|
13
|
+
import FieldWrapper from "./FieldWrapper.vue"
|
|
13
14
|
import IconCloseButton from "./IconCloseButton.vue"
|
|
14
15
|
import { CONTROL_SIZE } from "./controlSize"
|
|
15
16
|
import { usePaletteControl, type PaletteControlOptions } from "../composables/usePaletteCommands"
|
|
@@ -35,8 +36,8 @@ import {
|
|
|
35
36
|
ComboboxRoot,
|
|
36
37
|
ComboboxViewport,
|
|
37
38
|
} from "reka-ui"
|
|
38
|
-
import { computed, nextTick, ref, useId, watch } from "vue"
|
|
39
|
-
import {
|
|
39
|
+
import { computed, nextTick, ref, useId, watch, useAttrs } from "vue"
|
|
40
|
+
import { useOwnField, mergeDescribedBy } from "../composables/useField"
|
|
40
41
|
import { useInputFilter } from "../composables/useInputFilter"
|
|
41
42
|
import { useLabels } from "../composables/useLpConfig"
|
|
42
43
|
import { CLOSE_ICON, OPTION_ITEM, optionStagger, COMBOBOX_PANEL, POPOVER_PANEL } from "./dropdown"
|
|
@@ -64,6 +65,8 @@ const props = withDefaults(
|
|
|
64
65
|
size?: "sm" | "md" | "lg"
|
|
65
66
|
icon?: string
|
|
66
67
|
invalid?: boolean
|
|
68
|
+
/** A message under the control, as on LpInput: it marks it invalid and shows under it (what `form.field()` hands it). */
|
|
69
|
+
error?: string
|
|
67
70
|
emptyText?: string
|
|
68
71
|
loading?: boolean
|
|
69
72
|
loadingText?: string
|
|
@@ -122,7 +125,8 @@ const inputAttrs = computed(() => {
|
|
|
122
125
|
return rest
|
|
123
126
|
})
|
|
124
127
|
|
|
125
|
-
const
|
|
128
|
+
const attrs = useAttrs()
|
|
129
|
+
const { field, wrapper, ownAttrs } = useOwnField(() => ({ error: props.error, invalid: props.invalid || isInvalid(props.modelValue), disabled: props.disabled }))
|
|
126
130
|
const isInvalidState = computed(() => field.value.invalid)
|
|
127
131
|
const labels = useLabels<AutocompleteLabels>(
|
|
128
132
|
"autocomplete",
|
|
@@ -270,14 +274,15 @@ usePaletteControl({ disabled: () => field.value.disabled, invalid: () => field.v
|
|
|
270
274
|
</script>
|
|
271
275
|
|
|
272
276
|
<template>
|
|
277
|
+
<FieldWrapper v-bind="wrapper">
|
|
273
278
|
<ComboboxRoot
|
|
274
279
|
:open="open"
|
|
275
280
|
:ignore-filter="true"
|
|
276
281
|
:reset-search-term-on-blur="false"
|
|
277
282
|
:reset-search-term-on-select="false"
|
|
278
283
|
:disabled="field.disabled"
|
|
279
|
-
:class="['relative', attrs.class]"
|
|
280
|
-
:style="attrs.style as never"
|
|
284
|
+
:class="['relative', ownAttrs() ? attrs.class : undefined]"
|
|
285
|
+
:style="ownAttrs() ? (attrs.style as never) : undefined"
|
|
281
286
|
@update:open="onOpenChange"
|
|
282
287
|
>
|
|
283
288
|
<ComboboxAnchor
|
|
@@ -410,4 +415,5 @@ usePaletteControl({ disabled: () => field.value.disabled, invalid: () => field.v
|
|
|
410
415
|
</ComboboxContent>
|
|
411
416
|
</ComboboxPortal>
|
|
412
417
|
</ComboboxRoot>
|
|
418
|
+
</FieldWrapper>
|
|
413
419
|
</template>
|
|
@@ -6,12 +6,13 @@ export default { inheritAttrs: false }
|
|
|
6
6
|
</script>
|
|
7
7
|
|
|
8
8
|
<script setup lang="ts">
|
|
9
|
+
import FieldWrapper from "./FieldWrapper.vue"
|
|
9
10
|
import { usePaletteControl, type PaletteControlOptions } from "../composables/usePaletteCommands"
|
|
10
11
|
import { CheckboxIndicator, CheckboxRoot } from "reka-ui"
|
|
11
12
|
import { computed, useId } from "vue"
|
|
12
13
|
import LpIcon from "./LpIcon.vue"
|
|
13
14
|
import LpLoader from "./LpLoader.vue"
|
|
14
|
-
import { isAttrOn, mergeDescribedBy,
|
|
15
|
+
import { isAttrOn, mergeDescribedBy, useOwnField } from "../composables/useField"
|
|
15
16
|
import { useOptimisticToggle } from "../composables/useOptimisticToggle"
|
|
16
17
|
import { useMergedAttrs } from "../composables/useMergedClass"
|
|
17
18
|
|
|
@@ -22,6 +23,8 @@ const props = withDefaults(defineProps<{
|
|
|
22
23
|
/** Mixed state (e.g. a "select all" with a partial selection). Shows a dash. */
|
|
23
24
|
indeterminate?: boolean
|
|
24
25
|
disabled?: boolean
|
|
26
|
+
/** A message under the control, as on LpInput: it marks it invalid and shows under it (what `form.field()` hands it). */
|
|
27
|
+
error?: string
|
|
25
28
|
label?: string
|
|
26
29
|
/** Secondary line under the label, as on LpSwitch. */
|
|
27
30
|
description?: string
|
|
@@ -67,8 +70,7 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(() =>
|
|
|
67
70
|
[
|
|
68
71
|
"group/check inline-flex cursor-pointer gap-2 text-sm text-ink",
|
|
69
72
|
props.description ? "items-start" : "items-center",
|
|
70
|
-
].join(" "),
|
|
71
|
-
)
|
|
73
|
+
].join(" "), () => ownAttrs())
|
|
72
74
|
|
|
73
75
|
/*
|
|
74
76
|
* The box is the control, so what describes it (aria-*, name, title) goes to
|
|
@@ -89,9 +91,7 @@ const parts = computed(() => {
|
|
|
89
91
|
return { label, control }
|
|
90
92
|
})
|
|
91
93
|
|
|
92
|
-
const field =
|
|
93
|
-
wrapped: false,
|
|
94
|
-
id: props.id,
|
|
94
|
+
const { field, wrapper, ownAttrs } = useOwnField(() => ({ error: props.error, id: props.id,
|
|
95
95
|
required: isAttrOn(rest.value.required),
|
|
96
96
|
disabled: props.disabled,
|
|
97
97
|
}))
|
|
@@ -104,6 +104,7 @@ usePaletteControl({ label: () => props.label, disabled: () => field.value.disabl
|
|
|
104
104
|
</script>
|
|
105
105
|
|
|
106
106
|
<template>
|
|
107
|
+
<FieldWrapper v-bind="wrapper">
|
|
107
108
|
<label
|
|
108
109
|
:class="rootClass"
|
|
109
110
|
v-bind="parts.label">
|
|
@@ -143,4 +144,5 @@ usePaletteControl({ label: () => props.label, disabled: () => field.value.disabl
|
|
|
143
144
|
</span>
|
|
144
145
|
<slot v-else>{{ label }}</slot>
|
|
145
146
|
</label>
|
|
147
|
+
</FieldWrapper>
|
|
146
148
|
</template>
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
* keeps the colours settled on in browser storage and offers them back above
|
|
30
30
|
* the presets.
|
|
31
31
|
*/
|
|
32
|
+
import FieldWrapper from "./FieldWrapper.vue"
|
|
32
33
|
import { CONTROL_SIZE } from "./controlSize"
|
|
33
34
|
import { clamp } from "../composables/clamp"
|
|
34
35
|
import {
|
|
@@ -41,7 +42,7 @@ import {
|
|
|
41
42
|
import { computed, nextTick, ref, useAttrs, watch } from "vue"
|
|
42
43
|
import { useKitStorage } from "../composables/usePersisted"
|
|
43
44
|
import { prefersReducedMotion } from "../composables/easing"
|
|
44
|
-
import {
|
|
45
|
+
import { useOwnField, mergeDescribedBy } from "../composables/useField"
|
|
45
46
|
import {
|
|
46
47
|
clampHsv,
|
|
47
48
|
DEFAULT_SWATCHES,
|
|
@@ -103,6 +104,8 @@ const props = withDefaults(
|
|
|
103
104
|
hideInput?: boolean
|
|
104
105
|
disabled?: boolean
|
|
105
106
|
invalid?: boolean
|
|
107
|
+
/** A message under the control, as on LpInput: it marks it invalid and shows under it (what `form.field()` hands it). */
|
|
108
|
+
error?: string
|
|
106
109
|
size?: "sm" | "md" | "lg"
|
|
107
110
|
/** Trigger text beside the swatch. Defaults to the colour's own value. */
|
|
108
111
|
label?: string
|
|
@@ -623,9 +626,7 @@ const menuItems = computed<ContextMenuItemDef[]>(() => {
|
|
|
623
626
|
})
|
|
624
627
|
|
|
625
628
|
const attrs = useAttrs()
|
|
626
|
-
const field =
|
|
627
|
-
wrapped: false,
|
|
628
|
-
invalid: props.invalid,
|
|
629
|
+
const { field, wrapper, ownAttrs } = useOwnField(() => ({ error: props.error, invalid: props.invalid,
|
|
629
630
|
disabled: props.disabled,
|
|
630
631
|
}))
|
|
631
632
|
|
|
@@ -639,6 +640,7 @@ const guardOutside = useOutsideGuard()
|
|
|
639
640
|
</script>
|
|
640
641
|
|
|
641
642
|
<template>
|
|
643
|
+
<FieldWrapper v-bind="wrapper">
|
|
642
644
|
<PopoverRoot v-model:open="open">
|
|
643
645
|
<!-- reka supports chaining `as-child` triggers onto one element, but only
|
|
644
646
|
when each link forwards the props down — and LpContextMenu is a wrapper
|
|
@@ -879,4 +881,5 @@ const guardOutside = useOutsideGuard()
|
|
|
879
881
|
</PopoverContent>
|
|
880
882
|
</PopoverPortal>
|
|
881
883
|
</PopoverRoot>
|
|
884
|
+
</FieldWrapper>
|
|
882
885
|
</template>
|
|
@@ -159,6 +159,12 @@ const portalTo = usePortalTarget()
|
|
|
159
159
|
const guardOutside = useOutsideGuard()
|
|
160
160
|
|
|
161
161
|
useMenuPalette(props)
|
|
162
|
+
|
|
163
|
+
// Several roots (trigger and portal), so Vue cannot hand attributes on by
|
|
164
|
+
// itself and dropped them: a trigger wrapped around this one from outside (an
|
|
165
|
+
// LpPopover's, as-child) lost its id, ARIA state and click, and its popover
|
|
166
|
+
// never opened. They go to the trigger, which merges them onto the slot's element.
|
|
167
|
+
defineOptions({ inheritAttrs: false })
|
|
162
168
|
</script>
|
|
163
169
|
|
|
164
170
|
<template>
|
|
@@ -169,6 +175,7 @@ useMenuPalette(props)
|
|
|
169
175
|
<ContextMenuRoot @update:open="opened">
|
|
170
176
|
<ContextMenuTrigger
|
|
171
177
|
as-child
|
|
178
|
+
v-bind="$attrs"
|
|
172
179
|
:disabled="!enabled"
|
|
173
180
|
:data-lp-context-menu="hinted ? '' : undefined"
|
|
174
181
|
@click.capture="leftClick"
|
|
@@ -32,8 +32,9 @@ export interface DatePickerLabels extends Record<string, string> {
|
|
|
32
32
|
* week") and, unlike the range picker's, are opt-in, so forms that already use
|
|
33
33
|
* the picker keep the plain calendar they were laid out around.
|
|
34
34
|
*/
|
|
35
|
-
import
|
|
36
|
-
import {
|
|
35
|
+
import FieldWrapper from "./FieldWrapper.vue"
|
|
36
|
+
import { computed, ref, useAttrs } from "vue"
|
|
37
|
+
import { useOwnField } from "../composables/useField"
|
|
37
38
|
import { useLocale } from "../composables/useLocale"
|
|
38
39
|
import { useLabels } from "../composables/useLpConfig"
|
|
39
40
|
import type { CalendarLabels, PickerFieldProps } from "./calendar"
|
|
@@ -131,7 +132,8 @@ function clear() {
|
|
|
131
132
|
emit("update:modelValue", undefined)
|
|
132
133
|
}
|
|
133
134
|
|
|
134
|
-
const
|
|
135
|
+
const attrs = useAttrs()
|
|
136
|
+
const { field, wrapper, controlAttrs } = useOwnField(() => ({ error: props.error, invalid: props.invalid, disabled: props.disabled }))
|
|
135
137
|
const text = useLabels<DatePickerLabels>(
|
|
136
138
|
"datePicker",
|
|
137
139
|
{
|
|
@@ -172,8 +174,9 @@ const presetOptions = computed(() => presetList.value.map((preset) => ({
|
|
|
172
174
|
</script>
|
|
173
175
|
|
|
174
176
|
<template>
|
|
177
|
+
<FieldWrapper v-bind="wrapper">
|
|
175
178
|
<PickerField
|
|
176
|
-
v-bind="
|
|
179
|
+
v-bind="controlAttrs"
|
|
177
180
|
v-model:open="open"
|
|
178
181
|
:field="field"
|
|
179
182
|
:display="display"
|
|
@@ -221,4 +224,5 @@ const presetOptions = computed(() => presetList.value.map((preset) => ({
|
|
|
221
224
|
</div>
|
|
222
225
|
</div>
|
|
223
226
|
</PickerField>
|
|
227
|
+
</FieldWrapper>
|
|
224
228
|
</template>
|
|
@@ -9,8 +9,9 @@ export default { inheritAttrs: false }
|
|
|
9
9
|
* Picking the second end or a preset fills the field and closes the popover.
|
|
10
10
|
* Two months sit side by side where there is room, one on a phone.
|
|
11
11
|
*/
|
|
12
|
-
import
|
|
13
|
-
import {
|
|
12
|
+
import FieldWrapper from "./FieldWrapper.vue"
|
|
13
|
+
import { computed, ref, useAttrs } from "vue"
|
|
14
|
+
import { useOwnField } from "../composables/useField"
|
|
14
15
|
import { useLocale } from "../composables/useLocale"
|
|
15
16
|
import { useLabels } from "../composables/useLpConfig"
|
|
16
17
|
import { useMediaQuery } from "../composables/useMediaQuery"
|
|
@@ -121,7 +122,8 @@ function presetFits(preset: DateRangePreset): boolean {
|
|
|
121
122
|
|
|
122
123
|
const wide = useMediaQuery("(min-width: 640px)")
|
|
123
124
|
|
|
124
|
-
const
|
|
125
|
+
const attrs = useAttrs()
|
|
126
|
+
const { field, wrapper, controlAttrs } = useOwnField(() => ({ error: props.error, invalid: props.invalid, disabled: props.disabled }))
|
|
125
127
|
const showClear = computed(() => props.clearable && !!value.value && !field.value.disabled)
|
|
126
128
|
|
|
127
129
|
const presetOptions = computed(() => presetList.value.map((preset) => ({
|
|
@@ -133,8 +135,9 @@ const presetOptions = computed(() => presetList.value.map((preset) => ({
|
|
|
133
135
|
</script>
|
|
134
136
|
|
|
135
137
|
<template>
|
|
138
|
+
<FieldWrapper v-bind="wrapper">
|
|
136
139
|
<PickerField
|
|
137
|
-
v-bind="
|
|
140
|
+
v-bind="controlAttrs"
|
|
138
141
|
v-model:open="open"
|
|
139
142
|
:field="field"
|
|
140
143
|
:display="display"
|
|
@@ -163,4 +166,5 @@ const presetOptions = computed(() => presetList.value.map((preset) => ({
|
|
|
163
166
|
@update:model-value="pick"
|
|
164
167
|
/>
|
|
165
168
|
</PickerField>
|
|
169
|
+
</FieldWrapper>
|
|
166
170
|
</template>
|
|
@@ -165,6 +165,12 @@ watch(
|
|
|
165
165
|
)
|
|
166
166
|
|
|
167
167
|
onBeforeUnmount(cancelHold)
|
|
168
|
+
|
|
169
|
+
// Several roots (trigger and portal), so Vue cannot hand attributes on by
|
|
170
|
+
// itself and dropped them: a trigger wrapped around this one from outside (an
|
|
171
|
+
// LpPopover's, as-child) lost its id, ARIA state and click, and its popover
|
|
172
|
+
// never opened. They go to the trigger, which merges them onto the slot's element.
|
|
173
|
+
defineOptions({ inheritAttrs: false })
|
|
168
174
|
</script>
|
|
169
175
|
|
|
170
176
|
<template>
|
|
@@ -172,6 +178,7 @@ onBeforeUnmount(cancelHold)
|
|
|
172
178
|
<HoverCardRoot v-else v-model:open="open" :open-delay="openDelay" :close-delay="closeDelay">
|
|
173
179
|
<HoverCardTrigger
|
|
174
180
|
as-child
|
|
181
|
+
v-bind="$attrs"
|
|
175
182
|
:class="touch === 'hold' ? '[-webkit-touch-callout:none]' : undefined"
|
|
176
183
|
@pointerdown="onPointerDown"
|
|
177
184
|
@pointermove="onPointerMove"
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* `@source` over the kit emits these classes — scoped CSS in a published
|
|
9
9
|
* component doesn't reach apps that only import the kit's token css.
|
|
10
10
|
*/
|
|
11
|
-
import { Handle, Position } from "@vue-flow/core"
|
|
11
|
+
import { Handle, Position, type NodeProps } from "@vue-flow/core"
|
|
12
12
|
import { computed } from "vue"
|
|
13
13
|
import LpBadge from "./LpBadge.vue"
|
|
14
14
|
import LpStatus from "./LpStatus.vue"
|
|
@@ -66,8 +66,9 @@ export interface InfraNodeLabels {
|
|
|
66
66
|
old?: string
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
|
|
70
|
-
|
|
69
|
+
// Vue Flow hands a custom node all of NodeProps. Declared, they stop falling
|
|
70
|
+
// through onto the node's root as attributes (position="[object Object]").
|
|
71
|
+
const props = defineProps<Omit<NodeProps<InfraNodeData>, "selected"> & {
|
|
71
72
|
selected?: boolean
|
|
72
73
|
/** Extra/overriding role styles, merged over the defaults below. */
|
|
73
74
|
roles?: Record<string, InfraRoleStyle>
|
|
@@ -7,12 +7,13 @@ import { CONTROL_SIZE } from "./controlSize"
|
|
|
7
7
|
import { usePaletteControl, type PaletteControlOptions } from "../composables/usePaletteCommands"
|
|
8
8
|
import { tv, type VariantProps } from "tailwind-variants"
|
|
9
9
|
import { computed, ref, useAttrs, useSlots } from "vue"
|
|
10
|
-
import { fieldWrapperBinds, isAttrOn, mergeDescribedBy, useFieldControl } from "../composables/useField"
|
|
10
|
+
import { fieldWrapperBinds, isAttrOn, mergeDescribedBy, useField, useFieldControl, wrapsItself } from "../composables/useField"
|
|
11
11
|
import { useLabels } from "../composables/useLpConfig"
|
|
12
12
|
import { useInputFilter } from "../composables/useInputFilter"
|
|
13
13
|
import { DEFAULT_MASK_TOKENS, useInputMask, type MaskSpec, type MaskTokens } from "../composables/useInputMask"
|
|
14
14
|
import { mergeClass } from "../composables/useMergedClass"
|
|
15
15
|
import FieldWrapper from "./FieldWrapper.vue"
|
|
16
|
+
import FieldLeading from "./FieldLeading.vue"
|
|
16
17
|
import LpIcon from "./LpIcon.vue"
|
|
17
18
|
|
|
18
19
|
// Single source of truth for the field shell — bare input and adorned input
|
|
@@ -137,9 +138,8 @@ const slots = useSlots()
|
|
|
137
138
|
const attrs = useAttrs()
|
|
138
139
|
const hasLeading = computed(() => !!slots.leading || !!props.icon)
|
|
139
140
|
const hasTrailing = computed(() => !!slots.trailing || showClear.value)
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
)
|
|
141
|
+
const outerFieldOf = useField()
|
|
142
|
+
const hasField = computed(() => wrapsItself(props, outerFieldOf))
|
|
143
143
|
const field = useFieldControl(() => ({
|
|
144
144
|
wrapped: hasField.value,
|
|
145
145
|
hasMessage: !!props.error || !!props.hint,
|
|
@@ -189,9 +189,9 @@ usePaletteControl({ label: () => props.label, disabled: () => field.value.disabl
|
|
|
189
189
|
:class="mergeClass(shellClass, hasLeading ? 'pl-2.5' : '', hasTrailing ? 'pr-1.5' : '', hasField ? '' : (attrs.class as string))"
|
|
190
190
|
:style="hasField ? undefined : attrs.style"
|
|
191
191
|
>
|
|
192
|
-
<
|
|
193
|
-
<
|
|
194
|
-
</
|
|
192
|
+
<FieldLeading :icon="icon">
|
|
193
|
+
<template v-if="$slots.leading" #default><slot name="leading" /></template>
|
|
194
|
+
</FieldLeading>
|
|
195
195
|
<input
|
|
196
196
|
ref="inputEl"
|
|
197
197
|
v-bind="inputAttrs"
|
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
* tab stays clickable-looking but is inert. Tailwind utilities only so styles
|
|
11
11
|
* reach consumer apps via @source.
|
|
12
12
|
*/
|
|
13
|
+
import type { NodeProps } from "@vue-flow/core"
|
|
14
|
+
|
|
13
15
|
export interface LaneNodeData {
|
|
14
16
|
label: string
|
|
15
17
|
/** Optional accent colour token for the lane border + label. */
|
|
@@ -18,7 +20,9 @@ export interface LaneNodeData {
|
|
|
18
20
|
height: number
|
|
19
21
|
}
|
|
20
22
|
|
|
21
|
-
|
|
23
|
+
// Vue Flow hands a custom node all of NodeProps. Declared, they stop falling
|
|
24
|
+
// through onto the node's root as attributes (position="[object Object]").
|
|
25
|
+
const props = defineProps<NodeProps<LaneNodeData>>()
|
|
22
26
|
</script>
|
|
23
27
|
|
|
24
28
|
<template>
|
|
@@ -6,6 +6,7 @@ export default { inheritAttrs: false }
|
|
|
6
6
|
</script>
|
|
7
7
|
|
|
8
8
|
<script setup lang="ts">
|
|
9
|
+
import FieldWrapper from "./FieldWrapper.vue"
|
|
9
10
|
import { CONTROL_SIZE } from "./controlSize"
|
|
10
11
|
import { usePaletteControl, type PaletteControlOptions } from "../composables/usePaletteCommands"
|
|
11
12
|
// Numeric input with stepper buttons (reka NumberField). Themed via tokens.
|
|
@@ -15,9 +16,10 @@ import {
|
|
|
15
16
|
NumberFieldInput,
|
|
16
17
|
NumberFieldRoot,
|
|
17
18
|
} from "reka-ui"
|
|
18
|
-
import { computed, useId } from "vue"
|
|
19
|
+
import { computed, useId, useAttrs } from "vue"
|
|
20
|
+
import FieldLeading from "./FieldLeading.vue"
|
|
19
21
|
import LpIcon from "./LpIcon.vue"
|
|
20
|
-
import { focusLeft,
|
|
22
|
+
import { focusLeft, useOwnField, mergeDescribedBy } from "../composables/useField"
|
|
21
23
|
import { useLabels } from "../composables/useLpConfig"
|
|
22
24
|
import { useMergedClass } from "../composables/useMergedClass"
|
|
23
25
|
|
|
@@ -67,7 +69,11 @@ const props = withDefaults(defineProps<{
|
|
|
67
69
|
// "Depth", "Weight" tells the reader nothing about which one wants grams.
|
|
68
70
|
// Not part of the value: it is never typed, selected, or submitted.
|
|
69
71
|
unit?: string
|
|
72
|
+
/** A leading icon, as on LpInput; the `leading` slot replaces it. */
|
|
73
|
+
icon?: string
|
|
70
74
|
invalid?: boolean
|
|
75
|
+
/** A message under the control, as on LpInput: it marks it invalid and shows under it (what `form.field()` hands it). */
|
|
76
|
+
error?: string
|
|
71
77
|
/** Control height, matching LpInput. */
|
|
72
78
|
size?: "sm" | "md" | "lg"
|
|
73
79
|
labels?: Partial<NumberFieldLabels>
|
|
@@ -96,7 +102,8 @@ const text = useLabels<NumberFieldLabels>(
|
|
|
96
102
|
|
|
97
103
|
const SIZE = CONTROL_SIZE
|
|
98
104
|
|
|
99
|
-
const
|
|
105
|
+
const attrs = useAttrs()
|
|
106
|
+
const { field, wrapper, ownAttrs } = useOwnField(() => ({ error: props.error, invalid: props.invalid, disabled: props.disabled }))
|
|
100
107
|
const describedBy = computed(
|
|
101
108
|
() => mergeDescribedBy(attrs["aria-describedby"], field.value.describedBy, props.unit ? unitId : undefined),
|
|
102
109
|
)
|
|
@@ -123,13 +130,13 @@ const rootClass = useMergedClass(() =>
|
|
|
123
130
|
field.value.invalid
|
|
124
131
|
? "border-danger focus-within:border-danger focus-within:ring-danger-soft"
|
|
125
132
|
: "border-line focus-within:border-brand focus-within:ring-ring",
|
|
126
|
-
].join(" "),
|
|
127
|
-
)
|
|
133
|
+
].join(" "), () => ownAttrs())
|
|
128
134
|
|
|
129
135
|
usePaletteControl({ disabled: () => field.value.disabled, invalid: () => field.value.invalid })
|
|
130
136
|
</script>
|
|
131
137
|
|
|
132
138
|
<template>
|
|
139
|
+
<FieldWrapper v-bind="wrapper">
|
|
133
140
|
<NumberFieldRoot
|
|
134
141
|
:model-value="modelValue ?? undefined"
|
|
135
142
|
:min="min"
|
|
@@ -146,11 +153,14 @@ usePaletteControl({ disabled: () => field.value.disabled, invalid: () => field.v
|
|
|
146
153
|
:required="field.required"
|
|
147
154
|
data-lp-ring-owner
|
|
148
155
|
:class="rootClass"
|
|
149
|
-
:style="attrs.style"
|
|
156
|
+
:style="ownAttrs() ? attrs.style : undefined"
|
|
150
157
|
:name="typeof attrs.name === 'string' ? attrs.name : undefined"
|
|
151
158
|
@update:model-value="(v) => emit('update:modelValue', Number.isNaN(v) ? null : (v ?? null))"
|
|
152
159
|
@focusout="onFocusOut"
|
|
153
160
|
>
|
|
161
|
+
<FieldLeading :icon="icon" class="pl-2.5">
|
|
162
|
+
<template v-if="$slots.leading" #default><slot name="leading" /></template>
|
|
163
|
+
</FieldLeading>
|
|
154
164
|
<NumberFieldDecrement
|
|
155
165
|
v-if="steppers"
|
|
156
166
|
:aria-label="text.decrease"
|
|
@@ -168,7 +178,7 @@ usePaletteControl({ disabled: () => field.value.disabled, invalid: () => field.v
|
|
|
168
178
|
:aria-describedby="describedBy"
|
|
169
179
|
:aria-invalid="field.invalid || undefined"
|
|
170
180
|
class="min-w-0 flex-1 bg-transparent text-ink outline-none placeholder:text-muted [appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none"
|
|
171
|
-
:class="steppers ? 'text-center @max-[8.5rem]:px-3 @max-[8.5rem]:text-left' : 'px-3'"
|
|
181
|
+
:class="steppers ? 'text-center @max-[8.5rem]:px-3 @max-[8.5rem]:text-left' : icon || $slots.leading ? 'pl-2 pr-3' : 'px-3'"
|
|
172
182
|
/>
|
|
173
183
|
<!-- The unit sits between the value and the increment button, so a long one
|
|
174
184
|
narrows the field rather than covering the digits. Not focusable and
|
|
@@ -186,4 +196,5 @@ usePaletteControl({ disabled: () => field.value.disabled, invalid: () => field.v
|
|
|
186
196
|
/>
|
|
187
197
|
</NumberFieldIncrement>
|
|
188
198
|
</NumberFieldRoot>
|
|
199
|
+
</FieldWrapper>
|
|
189
200
|
</template>
|
|
@@ -19,9 +19,10 @@ export default { inheritAttrs: false }
|
|
|
19
19
|
* size's width and no further, and shrinks with the row when the container is
|
|
20
20
|
* narrower, so eight cells fit a phone instead of running off its edge.
|
|
21
21
|
*/
|
|
22
|
+
import FieldWrapper from "./FieldWrapper.vue"
|
|
22
23
|
import { PinInputInput, PinInputRoot } from "reka-ui"
|
|
23
24
|
import { computed, ref, watch } from "vue"
|
|
24
|
-
import {
|
|
25
|
+
import { useOwnField, mergeDescribedBy } from "../composables/useField"
|
|
25
26
|
import { useMergedAttrs } from "../composables/useMergedClass"
|
|
26
27
|
import { groupBreaks, joinCells, splitCode, upperCells, type OtpCells } from "./otp"
|
|
27
28
|
|
|
@@ -50,6 +51,8 @@ const props = withDefaults(
|
|
|
50
51
|
group?: number | number[]
|
|
51
52
|
/** Error state — tints the cells red (e.g. wrong code). */
|
|
52
53
|
invalid?: boolean
|
|
54
|
+
/** A message under the control, as on LpInput: it marks it invalid and shows under it (what `form.field()` hands it). */
|
|
55
|
+
error?: string
|
|
53
56
|
size?: "sm" | "md" | "lg"
|
|
54
57
|
}>(),
|
|
55
58
|
{ length: 6, size: "md", alphanumeric: false, uppercase: undefined },
|
|
@@ -97,9 +100,7 @@ const cells = computed<string[]>({
|
|
|
97
100
|
},
|
|
98
101
|
})
|
|
99
102
|
|
|
100
|
-
const field =
|
|
101
|
-
wrapped: false,
|
|
102
|
-
invalid: props.invalid,
|
|
103
|
+
const { field, wrapper, ownAttrs } = useOwnField(() => ({ error: props.error, invalid: props.invalid,
|
|
103
104
|
disabled: props.disabled,
|
|
104
105
|
}))
|
|
105
106
|
|
|
@@ -144,11 +145,11 @@ const cellSize = {
|
|
|
144
145
|
}
|
|
145
146
|
|
|
146
147
|
const { class: rootClass, attrs: rest } = useMergedAttrs(
|
|
147
|
-
"flex min-w-0 max-w-full items-center gap-1.5 sm:gap-2",
|
|
148
|
-
)
|
|
148
|
+
"flex min-w-0 max-w-full items-center gap-1.5 sm:gap-2", () => ownAttrs())
|
|
149
149
|
</script>
|
|
150
150
|
|
|
151
151
|
<template>
|
|
152
|
+
<FieldWrapper v-bind="wrapper">
|
|
152
153
|
<PinInputRoot
|
|
153
154
|
ref="root"
|
|
154
155
|
v-model="cells"
|
|
@@ -188,4 +189,5 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(
|
|
|
188
189
|
/>
|
|
189
190
|
</template>
|
|
190
191
|
</PinInputRoot>
|
|
192
|
+
</FieldWrapper>
|
|
191
193
|
</template>
|