@leavepulse/ui 0.37.6 → 0.37.7

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.
@@ -3,13 +3,13 @@ import { PopoverRoot, PopoverTrigger, PopoverPortal, PopoverContent } from "reka
3
3
  import { POPOVER_PANEL } from "../components/dropdown.js";
4
4
  import { _ as _sfc_main$2 } from "./LpCalendar-B7Ko5RdR.js";
5
5
  import { _ as _sfc_main$1 } from "./LpIcon-CCnX5_2j.js";
6
+ import { _ as _sfc_main$3 } from "./LpTimeField-K6nAGboX.js";
6
7
  const _hoisted_1 = ["disabled"];
7
8
  const _hoisted_2 = ["onKeydown"];
8
9
  const _hoisted_3 = {
9
10
  key: 0,
10
11
  class: "flex items-center gap-2 border-t border-line px-3 py-2"
11
12
  };
12
- const _hoisted_4 = ["value", "disabled"];
13
13
  const _sfc_main = /* @__PURE__ */ defineComponent({
14
14
  __name: "LpDatePicker",
15
15
  props: {
@@ -133,13 +133,14 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
133
133
  size: 15,
134
134
  class: "shrink-0 text-muted"
135
135
  }),
136
- createElementVNode("input", {
137
- type: "time",
138
- value: minute.value,
136
+ createVNode(_sfc_main$3, {
137
+ "model-value": minute.value || void 0,
139
138
  disabled: !day.value,
140
- class: "h-(--size-control-sm) flex-1 rounded-control border border-line bg-surface-soft px-2 text-sm text-ink outline-none transition-colors focus-visible:border-brand focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-55",
141
- onInput: _cache[0] || (_cache[0] = ($event) => onTime($event.target.value))
142
- }, null, 40, _hoisted_4),
139
+ icon: false,
140
+ size: "sm",
141
+ class: "flex-1",
142
+ "onUpdate:modelValue": _cache[0] || (_cache[0] = (at) => onTime(at || ""))
143
+ }, null, 8, ["model-value", "disabled"]),
143
144
  createElementVNode("button", {
144
145
  type: "button",
145
146
  class: "shrink-0 rounded-control px-2 py-1 text-xs text-muted transition-colors hover:text-ink",
@@ -0,0 +1,90 @@
1
+ import { defineComponent, computed, openBlock, createBlock, unref, normalizeClass, withCtx, createCommentVNode, createElementBlock, Fragment, renderList, createTextVNode, toDisplayString } from "vue";
2
+ import { Time } from "@internationalized/date";
3
+ import { TimeFieldRoot, TimeFieldInput } from "reka-ui";
4
+ import { _ as _sfc_main$1 } from "./LpIcon-CCnX5_2j.js";
5
+ const _sfc_main = /* @__PURE__ */ defineComponent({
6
+ __name: "LpTimeField",
7
+ props: {
8
+ modelValue: {},
9
+ disabled: { type: Boolean },
10
+ invalid: { type: Boolean },
11
+ size: { default: "md" },
12
+ hourCycle: { default: 24 },
13
+ locale: {},
14
+ icon: { type: Boolean, default: true }
15
+ },
16
+ emits: ["update:modelValue"],
17
+ setup(__props, { emit: __emit }) {
18
+ const props = __props;
19
+ const emit = __emit;
20
+ const value = computed(() => {
21
+ const match = /^(\d{2}):(\d{2})/.exec(props.modelValue || "");
22
+ return match ? new Time(Number(match[1]), Number(match[2])) : void 0;
23
+ });
24
+ function onUpdate(next) {
25
+ if (!next || typeof next !== "object" || !("hour" in next) || !("minute" in next)) {
26
+ emit("update:modelValue", void 0);
27
+ return;
28
+ }
29
+ const pad = (n) => String(n).padStart(2, "0");
30
+ emit("update:modelValue", `${pad(next.hour)}:${pad(next.minute)}`);
31
+ }
32
+ const shellSize = {
33
+ sm: "h-(--size-control-sm) text-xs",
34
+ md: "h-(--size-control-md) text-sm",
35
+ lg: "h-(--size-control-lg) text-sm"
36
+ };
37
+ return (_ctx, _cache) => {
38
+ return openBlock(), createBlock(unref(TimeFieldRoot), {
39
+ "model-value": value.value,
40
+ "hour-cycle": __props.hourCycle,
41
+ locale: __props.locale,
42
+ granularity: "minute",
43
+ disabled: __props.disabled,
44
+ class: normalizeClass(["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", [
45
+ shellSize[__props.size],
46
+ __props.invalid ? "border-danger focus-within:ring-danger-soft" : "border-line focus-within:border-brand"
47
+ ]]),
48
+ "onUpdate:modelValue": onUpdate
49
+ }, {
50
+ default: withCtx(({ segments }) => [
51
+ __props.icon ? (openBlock(), createBlock(_sfc_main$1, {
52
+ key: 0,
53
+ name: "lucide:clock",
54
+ size: 15,
55
+ class: "mr-1.5 shrink-0 text-muted"
56
+ })) : createCommentVNode("", true),
57
+ (openBlock(true), createElementBlock(Fragment, null, renderList(segments, (item) => {
58
+ return openBlock(), createElementBlock(Fragment, {
59
+ key: item.part
60
+ }, [
61
+ item.part === "literal" ? (openBlock(), createBlock(unref(TimeFieldInput), {
62
+ key: 0,
63
+ part: item.part,
64
+ class: "text-muted"
65
+ }, {
66
+ default: withCtx(() => [
67
+ createTextVNode(toDisplayString(item.value), 1)
68
+ ]),
69
+ _: 2
70
+ }, 1032, ["part"])) : (openBlock(), createBlock(unref(TimeFieldInput), {
71
+ key: 1,
72
+ part: item.part,
73
+ class: "rounded-sm px-0.5 tabular-nums outline-none focus:bg-brand-soft focus:text-brand data-[placeholder]:text-muted"
74
+ }, {
75
+ default: withCtx(() => [
76
+ createTextVNode(toDisplayString(item.value), 1)
77
+ ]),
78
+ _: 2
79
+ }, 1032, ["part"]))
80
+ ], 64);
81
+ }), 128))
82
+ ]),
83
+ _: 1
84
+ }, 8, ["model-value", "hour-cycle", "locale", "disabled", "class"]);
85
+ };
86
+ }
87
+ });
88
+ export {
89
+ _sfc_main as _
90
+ };
@@ -1 +1 @@
1
- export declare const COMPONENT_NAMES: readonly ["LayoutCanvas", "LayoutNode", "LpActionBar", "LpAddressInput", "LpAlert", "LpAppShell", "LpAutocomplete", "LpAvatar", "LpBadge", "LpBreadcrumbs", "LpButton", "LpCalendar", "LpCard", "LpChart", "LpCheckbox", "LpCodeBlock", "LpColorPicker", "LpCommandPalette", "LpConfirmDialog", "LpContextMenu", "LpDatePicker", "LpDisclosure", "LpDivider", "LpDrawer", "LpDropdownMenu", "LpDropzone", "LpEmptyState", "LpFileTree", "LpFileTreeNode", "LpFormField", "LpIcon", "LpImageEditor", "LpInfraNode", "LpInput", "LpLaneNode", "LpLightbox", "LpLink", "LpLogViewer", "LpMapPicker", "LpMediaGallery", "LpModal", "LpModalNav", "LpNotificationBell", "LpNumberField", "LpNumberFlow", "LpOtpInput", "LpPagination", "LpPasswordInput", "LpPhoneInput", "LpPie", "LpPopover", "LpProgress", "LpRadio", "LpRadioGroup", "LpRollingText", "LpScrollArea", "LpSection", "LpSegmented", "LpSelect", "LpServiceNode", "LpShift", "LpShortcutsDialog", "LpSidebar", "LpSidebarNav", "LpSkeleton", "LpSlider", "LpSparkline", "LpStat", "LpStepper", "LpSwitch", "LpTable", "LpTableOfContents", "LpTabs", "LpTextarea", "LpThemeSwitcher", "LpTilt", "LpToaster", "LpTooltip", "LpTopologyCanvas", "LpUptimeBar", "LpWaypointEdge"];
1
+ export declare const COMPONENT_NAMES: readonly ["LayoutCanvas", "LayoutNode", "LpActionBar", "LpAddressInput", "LpAlert", "LpAppShell", "LpAutocomplete", "LpAvatar", "LpBadge", "LpBreadcrumbs", "LpButton", "LpCalendar", "LpCard", "LpChart", "LpCheckbox", "LpCodeBlock", "LpColorPicker", "LpCommandPalette", "LpConfirmDialog", "LpContextMenu", "LpDatePicker", "LpDisclosure", "LpDivider", "LpDrawer", "LpDropdownMenu", "LpDropzone", "LpEmptyState", "LpFileTree", "LpFileTreeNode", "LpFormField", "LpIcon", "LpImageEditor", "LpInfraNode", "LpInput", "LpLaneNode", "LpLightbox", "LpLink", "LpLogViewer", "LpMapPicker", "LpMediaGallery", "LpModal", "LpModalNav", "LpNotificationBell", "LpNumberField", "LpNumberFlow", "LpOtpInput", "LpPagination", "LpPasswordInput", "LpPhoneInput", "LpPie", "LpPopover", "LpProgress", "LpRadio", "LpRadioGroup", "LpRollingText", "LpScrollArea", "LpSection", "LpSegmented", "LpSelect", "LpServiceNode", "LpShift", "LpShortcutsDialog", "LpSidebar", "LpSidebarNav", "LpSkeleton", "LpSlider", "LpSparkline", "LpStat", "LpStepper", "LpSwitch", "LpTable", "LpTableOfContents", "LpTabs", "LpTextarea", "LpThemeSwitcher", "LpTilt", "LpTimeField", "LpToaster", "LpTooltip", "LpTopologyCanvas", "LpUptimeBar", "LpWaypointEdge"];
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpDatePicker-BPbvu3cZ.js";
1
+ import { _ as _sfc_main } from "../chunks/LpDatePicker-CwOToxGq.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
@@ -0,0 +1,24 @@
1
+ type __VLS_Props = {
2
+ /** Selected time "HH:mm" (v-model). */
3
+ modelValue?: string;
4
+ disabled?: boolean;
5
+ invalid?: boolean;
6
+ size?: "sm" | "md" | "lg";
7
+ /** 24 or 12-hour clock, independent of the browser locale. */
8
+ hourCycle?: 12 | 24;
9
+ /** Locale for segment order and the AM/PM marker in 12-hour mode. */
10
+ locale?: string;
11
+ /** Show the clock icon at the start of the field. */
12
+ icon?: boolean;
13
+ };
14
+ declare const __VLS_export: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {} & {
15
+ "update:modelValue": (value: string | undefined) => any;
16
+ }, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{
17
+ "onUpdate:modelValue"?: ((value: string | undefined) => any) | undefined;
18
+ }>, {
19
+ size: "sm" | "md" | "lg";
20
+ icon: boolean;
21
+ hourCycle: 12 | 24;
22
+ }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
23
+ declare const _default: typeof __VLS_export;
24
+ export default _default;
@@ -0,0 +1,4 @@
1
+ import { _ as _sfc_main } from "../chunks/LpTimeField-K6nAGboX.js";
2
+ export {
3
+ _sfc_main as default
4
+ };
package/dist/index.d.ts CHANGED
@@ -62,6 +62,7 @@ export { default as LpModalNav } from './components/LpModalNav.vue';
62
62
  export { default as LpNotificationBell } from './components/LpNotificationBell.vue';
63
63
  export type { NotificationItem, NotificationLabels, } from './components/LpNotificationBell.vue';
64
64
  export { default as LpNumberField } from './components/LpNumberField.vue';
65
+ export { default as LpTimeField } from './components/LpTimeField.vue';
65
66
  export { default as LpNumberFlow } from './components/LpNumberFlow.vue';
66
67
  export { default as LpOtpInput } from './components/LpOtpInput.vue';
67
68
  export { default as LpPagination } from './components/LpPagination.vue';
package/dist/index.js CHANGED
@@ -21,7 +21,7 @@ import { DEFAULT_SWATCHES, clampHsv, formatColor, hsvToHsl, hsvToRgb, isLight, n
21
21
  import { default as default3 } from "./components/LpCommandPalette.vue.js";
22
22
  import { _ as _17 } from "./chunks/LpConfirmDialog-BH7az_E7.js";
23
23
  import { _ as _18 } from "./chunks/LpContextMenu-CwW1QXe_.js";
24
- import { _ as _19 } from "./chunks/LpDatePicker-BPbvu3cZ.js";
24
+ import { _ as _19 } from "./chunks/LpDatePicker-CwOToxGq.js";
25
25
  import { _ as _20 } from "./chunks/LpDisclosure-ChQYmjlR.js";
26
26
  import { _ as _21 } from "./chunks/LpDivider-CUX46KR0.js";
27
27
  import { _ as _22 } from "./chunks/LpDrawer-E67O8fFP.js";
@@ -44,43 +44,44 @@ import { _ as _34 } from "./chunks/LpModal-Dq_qXN8-.js";
44
44
  import { _ as _35 } from "./chunks/LpModalNav-CqxXbTkl.js";
45
45
  import { _ as _36 } from "./chunks/LpNotificationBell-DyGGsMAM.js";
46
46
  import { _ as _37 } from "./chunks/LpNumberField-CWuX08iX.js";
47
- import { _ as _38 } from "./chunks/LpNumberFlow-amWYaeuA.js";
48
- import { _ as _39 } from "./chunks/LpOtpInput-y2v-pmiN.js";
49
- import { _ as _40 } from "./chunks/LpPagination-5FxjmmB5.js";
50
- import { _ as _41 } from "./chunks/LpPasswordInput--sjYCkLS.js";
51
- import { _ as _42 } from "./chunks/LpPie-D6OnzwlJ.js";
52
- import { _ as _43 } from "./chunks/LpPhoneInput-BDSKVl_z.js";
47
+ import { _ as _38 } from "./chunks/LpTimeField-K6nAGboX.js";
48
+ import { _ as _39 } from "./chunks/LpNumberFlow-amWYaeuA.js";
49
+ import { _ as _40 } from "./chunks/LpOtpInput-y2v-pmiN.js";
50
+ import { _ as _41 } from "./chunks/LpPagination-5FxjmmB5.js";
51
+ import { _ as _42 } from "./chunks/LpPasswordInput--sjYCkLS.js";
52
+ import { _ as _43 } from "./chunks/LpPie-D6OnzwlJ.js";
53
+ import { _ as _44 } from "./chunks/LpPhoneInput-BDSKVl_z.js";
53
54
  import { flagEmoji, loadCountries, matchCountryByValue } from "./components/countries.js";
54
- import { _ as _44 } from "./chunks/LpPopover-8268UmlC.js";
55
- import { _ as _45 } from "./chunks/LpProgress-Btq5W4Ut.js";
56
- import { _ as _46 } from "./chunks/LpRadio-B5AfqTpZ.js";
57
- import { _ as _47 } from "./chunks/LpRadioGroup-xPIuVDO_.js";
58
- import { _ as _48 } from "./chunks/LpRollingText-CM5gkSeF.js";
59
- import { _ as _49 } from "./chunks/LpScrollArea-D8xyKXcF.js";
60
- import { _ as _50 } from "./chunks/LpSegmented-fV0Sk92p.js";
61
- import { _ as _51 } from "./chunks/LpSection-aM6J_u51.js";
62
- import { _ as _52 } from "./chunks/LpSelect-ANIVtrpM.js";
63
- import { _ as _53 } from "./chunks/LpServiceNode-D8uJ8sGk.js";
64
- import { _ as _54 } from "./chunks/LpShift-DXNsauQZ.js";
55
+ import { _ as _45 } from "./chunks/LpPopover-8268UmlC.js";
56
+ import { _ as _46 } from "./chunks/LpProgress-Btq5W4Ut.js";
57
+ import { _ as _47 } from "./chunks/LpRadio-B5AfqTpZ.js";
58
+ import { _ as _48 } from "./chunks/LpRadioGroup-xPIuVDO_.js";
59
+ import { _ as _49 } from "./chunks/LpRollingText-CM5gkSeF.js";
60
+ import { _ as _50 } from "./chunks/LpScrollArea-D8xyKXcF.js";
61
+ import { _ as _51 } from "./chunks/LpSegmented-fV0Sk92p.js";
62
+ import { _ as _52 } from "./chunks/LpSection-aM6J_u51.js";
63
+ import { _ as _53 } from "./chunks/LpSelect-ANIVtrpM.js";
64
+ import { _ as _54 } from "./chunks/LpServiceNode-D8uJ8sGk.js";
65
+ import { _ as _55 } from "./chunks/LpShift-DXNsauQZ.js";
65
66
  import { default as default6 } from "./components/LpShortcutsDialog.vue.js";
66
- import { _ as _55 } from "./chunks/LpSidebar-B1wLj-B2.js";
67
- import { _ as _56 } from "./chunks/LpSkeleton-CTI-vBgW.js";
68
- import { _ as _57 } from "./chunks/LpSlider-BwNIBkAK.js";
69
- import { _ as _58 } from "./chunks/LpSparkline-C6xQ2dwv.js";
70
- import { _ as _59 } from "./chunks/LpStat-CQaW8R--.js";
71
- import { _ as _60 } from "./chunks/LpStepper-Bk-XaQYU.js";
72
- import { _ as _61 } from "./chunks/LpSwitch-xgnRU2k0.js";
67
+ import { _ as _56 } from "./chunks/LpSidebar-B1wLj-B2.js";
68
+ import { _ as _57 } from "./chunks/LpSkeleton-CTI-vBgW.js";
69
+ import { _ as _58 } from "./chunks/LpSlider-BwNIBkAK.js";
70
+ import { _ as _59 } from "./chunks/LpSparkline-C6xQ2dwv.js";
71
+ import { _ as _60 } from "./chunks/LpStat-CQaW8R--.js";
72
+ import { _ as _61 } from "./chunks/LpStepper-Bk-XaQYU.js";
73
+ import { _ as _62 } from "./chunks/LpSwitch-xgnRU2k0.js";
73
74
  import { default as default7 } from "./components/LpTable.vue.js";
74
- import { _ as _62 } from "./chunks/LpTableOfContents-CjvxPMpU.js";
75
- import { _ as _63 } from "./chunks/LpTabs-Cefe_lys.js";
76
- import { _ as _64 } from "./chunks/LpTextarea-t7F6gv8E.js";
75
+ import { _ as _63 } from "./chunks/LpTableOfContents-CjvxPMpU.js";
76
+ import { _ as _64 } from "./chunks/LpTabs-Cefe_lys.js";
77
+ import { _ as _65 } from "./chunks/LpTextarea-t7F6gv8E.js";
77
78
  import { p as presets, a as parseTheme, s as serializeTheme } from "./chunks/LpThemeSwitcher-Cue-myST.js";
78
- import { D, _ as _65, N, T, b, c, d, e, f, g, i, l, h, j, n, r, t, k, m, u, v } from "./chunks/LpThemeSwitcher-Cue-myST.js";
79
- import { _ as _66 } from "./chunks/LpTilt-DuyKbpkA.js";
80
- import { _ as _67 } from "./chunks/LpToaster-gDB1EaU3.js";
81
- import { _ as _68 } from "./chunks/LpTooltip-hCLPAqMG.js";
82
- import { _ as _69 } from "./chunks/LpTopologyCanvas-Cm1L39uL.js";
83
- import { _ as _70 } from "./chunks/LpUptimeBar-DvAMno0S.js";
79
+ import { D, _ as _66, N, T, b, c, d, e, f, g, i, l, h, j, n, r, t, k, m, u, v } from "./chunks/LpThemeSwitcher-Cue-myST.js";
80
+ import { _ as _67 } from "./chunks/LpTilt-DuyKbpkA.js";
81
+ import { _ as _68 } from "./chunks/LpToaster-gDB1EaU3.js";
82
+ import { _ as _69 } from "./chunks/LpTooltip-hCLPAqMG.js";
83
+ import { _ as _70 } from "./chunks/LpTopologyCanvas-Cm1L39uL.js";
84
+ import { _ as _71 } from "./chunks/LpUptimeBar-DvAMno0S.js";
84
85
  import { deserializeLayout, serializeLayout } from "./layout/tree.js";
85
86
  import { addLeaf, addTab, countBlocks, countLeaves, makeLayout, moveLeaf, removeBlock, removeLeaf, reorderTab, resizeAt, setActiveTab } from "./layout/tree.js";
86
87
  import { blockTitle, defineBlocks } from "./layout/registry.js";
@@ -174,41 +175,42 @@ export {
174
175
  _35 as LpModalNav,
175
176
  _36 as LpNotificationBell,
176
177
  _37 as LpNumberField,
177
- _38 as LpNumberFlow,
178
- _39 as LpOtpInput,
179
- _40 as LpPagination,
180
- _41 as LpPasswordInput,
181
- _43 as LpPhoneInput,
182
- _42 as LpPie,
183
- _44 as LpPopover,
184
- _45 as LpProgress,
185
- _46 as LpRadio,
186
- _47 as LpRadioGroup,
187
- _48 as LpRollingText,
188
- _49 as LpScrollArea,
189
- _51 as LpSection,
190
- _50 as LpSegmented,
191
- _52 as LpSelect,
192
- _53 as LpServiceNode,
193
- _54 as LpShift,
178
+ _39 as LpNumberFlow,
179
+ _40 as LpOtpInput,
180
+ _41 as LpPagination,
181
+ _42 as LpPasswordInput,
182
+ _44 as LpPhoneInput,
183
+ _43 as LpPie,
184
+ _45 as LpPopover,
185
+ _46 as LpProgress,
186
+ _47 as LpRadio,
187
+ _48 as LpRadioGroup,
188
+ _49 as LpRollingText,
189
+ _50 as LpScrollArea,
190
+ _52 as LpSection,
191
+ _51 as LpSegmented,
192
+ _53 as LpSelect,
193
+ _54 as LpServiceNode,
194
+ _55 as LpShift,
194
195
  default6 as LpShortcutsDialog,
195
- _55 as LpSidebar,
196
- _56 as LpSkeleton,
197
- _57 as LpSlider,
198
- _58 as LpSparkline,
199
- _59 as LpStat,
200
- _60 as LpStepper,
201
- _61 as LpSwitch,
196
+ _56 as LpSidebar,
197
+ _57 as LpSkeleton,
198
+ _58 as LpSlider,
199
+ _59 as LpSparkline,
200
+ _60 as LpStat,
201
+ _61 as LpStepper,
202
+ _62 as LpSwitch,
202
203
  default7 as LpTable,
203
- _62 as LpTableOfContents,
204
- _63 as LpTabs,
205
- _64 as LpTextarea,
206
- _65 as LpThemeSwitcher,
207
- _66 as LpTilt,
208
- _67 as LpToaster,
209
- _68 as LpTooltip,
210
- _69 as LpTopologyCanvas,
211
- _70 as LpUptimeBar,
204
+ _63 as LpTableOfContents,
205
+ _64 as LpTabs,
206
+ _65 as LpTextarea,
207
+ _66 as LpThemeSwitcher,
208
+ _67 as LpTilt,
209
+ _38 as LpTimeField,
210
+ _68 as LpToaster,
211
+ _69 as LpTooltip,
212
+ _70 as LpTopologyCanvas,
213
+ _71 as LpUptimeBar,
212
214
  N as NO_BACKDROP_ATTR,
213
215
  OSM_TILES,
214
216
  T as THEME_COOKIE,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leavepulse/ui",
3
- "version": "0.37.6",
3
+ "version": "0.37.7",
4
4
  "type": "module",
5
5
  "description": "LeavePulse token-driven Vue component kit.",
6
6
  "repository": {
@@ -78,6 +78,7 @@ export const COMPONENT_NAMES = [
78
78
  "LpTextarea",
79
79
  "LpThemeSwitcher",
80
80
  "LpTilt",
81
+ "LpTimeField",
81
82
  "LpToaster",
82
83
  "LpTooltip",
83
84
  "LpTopologyCanvas",
@@ -6,10 +6,10 @@
6
6
  * min/max + isDisabled forwarded to the calendar. Themed like the other inputs.
7
7
  *
8
8
  * With `time`, the model carries a minute too ("YYYY-MM-DDTHH:mm") and the
9
- * popover grows a time row under the calendar. Anything that needs a moment
10
- * rather than a day — an expiry, a scheduled start — would otherwise fall back
11
- * to a native `datetime-local`, which the browser paints in its own locale and
12
- * chrome, breaking the form it sits in. Picking a day keeps the time already
9
+ * popover grows a time row (LpTimeField) under the calendar. Anything that
10
+ * needs a moment rather than a day — an expiry, a scheduled start — would
11
+ * otherwise fall back to a native `datetime-local`, which the browser paints in
12
+ * its own locale and chrome, breaking the form it sits in. Picking a day keeps the time already
13
13
  * chosen (default `defaultTime`), so the popover closes on the first click for
14
14
  * the common case.
15
15
  */
@@ -18,6 +18,7 @@ import { computed, ref } from "vue"
18
18
  import { POPOVER_PANEL } from "./dropdown"
19
19
  import LpCalendar from "./LpCalendar.vue"
20
20
  import LpIcon from "./LpIcon.vue"
21
+ import LpTimeField from "./LpTimeField.vue"
21
22
 
22
23
  const props = withDefaults(
23
24
  defineProps<{
@@ -152,12 +153,13 @@ function clear() {
152
153
  />
153
154
  <div v-if="time" class="flex items-center gap-2 border-t border-line px-3 py-2">
154
155
  <LpIcon name="lucide:clock" :size="15" class="shrink-0 text-muted" />
155
- <input
156
- type="time"
157
- :value="minute"
156
+ <LpTimeField
157
+ :model-value="minute || undefined"
158
158
  :disabled="!day"
159
- class="h-(--size-control-sm) flex-1 rounded-control border border-line bg-surface-soft px-2 text-sm text-ink outline-none transition-colors focus-visible:border-brand focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-55"
160
- @input="onTime(($event.target as HTMLInputElement).value)"
159
+ :icon="false"
160
+ size="sm"
161
+ class="flex-1"
162
+ @update:model-value="(at) => onTime(at || '')"
161
163
  />
162
164
  <button
163
165
  type="button"
@@ -0,0 +1,89 @@
1
+ <script setup lang="ts">
2
+ /*
3
+ * Time-of-day field: hour and minute segments, typed or stepped with the arrow
4
+ * keys (reka TimeField). The model is "HH:mm" (v-model), the same shape a
5
+ * native `<input type="time">` produces, so it drops into a form that used one.
6
+ *
7
+ * It exists because the native input is painted by the browser in the user's
8
+ * system locale — a 24-hour app shows "--:-- --" with an AM/PM segment, in the
9
+ * browser's chrome rather than the kit's. Here the clock is fixed by
10
+ * `hourCycle` (24 by default) and the segments are themed like every other
11
+ * field.
12
+ */
13
+ import { Time } from "@internationalized/date"
14
+ import { TimeFieldInput, TimeFieldRoot } from "reka-ui"
15
+ import { computed } from "vue"
16
+ import LpIcon from "./LpIcon.vue"
17
+
18
+ const props = withDefaults(
19
+ defineProps<{
20
+ /** Selected time "HH:mm" (v-model). */
21
+ modelValue?: string
22
+ disabled?: boolean
23
+ invalid?: boolean
24
+ size?: "sm" | "md" | "lg"
25
+ /** 24 or 12-hour clock, independent of the browser locale. */
26
+ hourCycle?: 12 | 24
27
+ /** Locale for segment order and the AM/PM marker in 12-hour mode. */
28
+ locale?: string
29
+ /** Show the clock icon at the start of the field. */
30
+ icon?: boolean
31
+ }>(),
32
+ { size: "md", hourCycle: 24, icon: true },
33
+ )
34
+
35
+ const emit = defineEmits<{
36
+ (e: "update:modelValue", value: string | undefined): void
37
+ }>()
38
+
39
+ const value = computed(() => {
40
+ const match = /^(\d{2}):(\d{2})/.exec(props.modelValue || "")
41
+ return match ? new Time(Number(match[1]), Number(match[2])) : undefined
42
+ })
43
+
44
+ function onUpdate(next: unknown) {
45
+ if (!next || typeof next !== "object" || !("hour" in next) || !("minute" in next)) {
46
+ emit("update:modelValue", undefined)
47
+ return
48
+ }
49
+ const pad = (n: unknown) => String(n).padStart(2, "0")
50
+ emit("update:modelValue", `${pad(next.hour)}:${pad(next.minute)}`)
51
+ }
52
+
53
+ const shellSize = {
54
+ sm: "h-(--size-control-sm) text-xs",
55
+ md: "h-(--size-control-md) text-sm",
56
+ lg: "h-(--size-control-lg) text-sm",
57
+ }
58
+ </script>
59
+
60
+ <template>
61
+ <TimeFieldRoot
62
+ v-slot="{ segments }"
63
+ :model-value="value"
64
+ :hour-cycle="hourCycle"
65
+ :locale="locale"
66
+ granularity="minute"
67
+ :disabled="disabled"
68
+ class="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"
69
+ :class="[
70
+ shellSize[size],
71
+ invalid ? 'border-danger focus-within:ring-danger-soft' : 'border-line focus-within:border-brand',
72
+ ]"
73
+ @update:model-value="onUpdate"
74
+ >
75
+ <LpIcon v-if="icon" name="lucide:clock" :size="15" class="mr-1.5 shrink-0 text-muted" />
76
+ <template v-for="item in segments" :key="item.part">
77
+ <TimeFieldInput v-if="item.part === 'literal'" :part="item.part" class="text-muted">
78
+ {{ item.value }}
79
+ </TimeFieldInput>
80
+ <TimeFieldInput
81
+ v-else
82
+ :part="item.part"
83
+ class="rounded-sm px-0.5 tabular-nums outline-none focus:bg-brand-soft focus:text-brand data-[placeholder]:text-muted"
84
+ >
85
+ {{ item.value }}
86
+ </TimeFieldInput>
87
+ </template>
88
+ </TimeFieldRoot>
89
+ </template>
package/src/index.ts CHANGED
@@ -112,6 +112,7 @@ export type {
112
112
  NotificationLabels,
113
113
  } from "./components/LpNotificationBell.vue"
114
114
  export { default as LpNumberField } from "./components/LpNumberField.vue"
115
+ export { default as LpTimeField } from "./components/LpTimeField.vue"
115
116
  export { default as LpNumberFlow } from "./components/LpNumberFlow.vue"
116
117
  export { default as LpOtpInput } from "./components/LpOtpInput.vue"
117
118
  export { default as LpPagination } from "./components/LpPagination.vue"