@octabits-io/nuxt-ui-kit 0.13.0 → 0.14.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.
@@ -45,7 +45,9 @@ function useLocaleTabs(hasValue, source, registerOverride = false) {
45
45
  const isVariant = (loc) => baseLocaleOf(loc) !== loc && supportedLocales.value.includes(baseLocaleOf(loc));
46
46
  const visibleLocales = computed(() => {
47
47
  const filtered = toValue(registerOverride) ? supportedLocales.value : supportedLocales.value.filter((loc) => !isVariant(loc));
48
- return filtered.includes(defaultLocale.value) ? filtered : [...filtered, defaultLocale.value];
48
+ const withDefault = filtered.includes(defaultLocale.value) ? filtered : [...filtered, defaultLocale.value];
49
+ if (!defaultLocale.value) return withDefault;
50
+ return [defaultLocale.value, ...withDefault.filter((loc) => loc !== defaultLocale.value)];
49
51
  });
50
52
  const active = ref("");
51
53
  watchEffect(() => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@octabits-io/nuxt-ui-kit",
3
- "version": "0.13.0",
3
+ "version": "0.14.1",
4
4
  "description": "Frontend kit for Nuxt/Vue admin SPAs: OIDC session harness (oidc-client-ts), Eden Treaty client factory, auth/org store cores, and a route-guard builder — factory-style seams the app wires into its own plugins, stores, and middleware",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -78,9 +78,9 @@
78
78
  "elysia": "^1.4.29",
79
79
  "oidc-client-ts": "^3.5.0",
80
80
  "vitest": "^4.1.10",
81
- "vue": "^3.5.40",
81
+ "vue": "^3.5.41",
82
82
  "zod": "^4.4.3",
83
- "@octabits-io/framework": "^0.8.0"
83
+ "@octabits-io/framework": "^0.21.1"
84
84
  },
85
85
  "peerDependencies": {
86
86
  "@elysiajs/eden": "^1.4.0",
@@ -3,6 +3,11 @@
3
3
  // explicit — no reliance on the consumer's auto-import configuration.
4
4
  // i18n key contract: dateRange.* (checkIn/checkOut/errors.*/availability*/
5
5
  // atTime/nextDay/checking) and period.travel.nights / period.booking.days.
6
+ //
7
+ // Sizing contract: the root is an inline-size @container (the inputs stack
8
+ // below 320px of own width), so its intrinsic width is 0. Parents must give
9
+ // it a definite width — block/grid context, `flex-1`, or an explicit
10
+ // `w-*`/`basis-*` — never shrink-to-fit.
6
11
  import { computed, ref, watch } from 'vue'
7
12
  import { useI18n } from 'vue-i18n'
8
13
  import { CalendarDate } from '@internationalized/date'
@@ -510,109 +515,120 @@ const timeHintParts = computed(() =>
510
515
  </script>
511
516
 
512
517
  <template>
513
- <div class="flex flex-col gap-1">
514
- <div class="flex items-center gap-2">
515
- <UInputDate
516
- v-model="startDate"
517
- :is-date-disabled="isDateDisabled"
518
- :size="size"
519
- :disabled="disabled"
520
- :color="inputColor"
521
- :aria-label="startLabel"
522
- class="flex-1"
523
- >
524
- <template #trailing>
525
- <UPopover v-model:open="startPopoverOpen">
526
- <UButton
527
- color="neutral"
528
- variant="link"
529
- size="sm"
530
- :icon="icon"
531
- :aria-label="startLabel"
532
- :disabled="disabled"
533
- class="px-0"
534
- />
535
- <template #content>
536
- <div class="flex flex-col" @pointerleave="hoveredDay = null">
537
- <p class="flex items-baseline justify-between gap-2 px-3 pt-2 pb-1">
538
- <span class="text-xs font-medium text-muted uppercase tracking-wide">
539
- {{ startPopoverTitle }}
540
- </span>
541
- <span v-if="spanLabel('start')" class="text-xs font-medium text-primary">
542
- {{ spanLabel('start') }}
543
- </span>
544
- </p>
545
- <UCalendar
546
- :model-value="startDate"
547
- :placeholder="startDate ?? endDate"
548
- :is-date-disabled="isDateDisabled"
549
- :ui="calendarUi"
550
- class="p-2"
551
- @update:model-value="onStartCalendarSelect"
552
- >
553
- <template #day="{ day }">
554
- <span :class="dayPillClass(day, 'start')" @pointerenter="hoveredDay = day.toString()">
555
- {{ day.day }}
518
+ <div class="@container flex flex-col gap-1">
519
+ <!-- Container-responsive: side-by-side from 320px of *own* width, stacked
520
+ below. Own-width queries keep the behavior correct inside modals,
521
+ narrow panels, and next to the assistant dock alike. When stacked,
522
+ the arrow disappears and each input gets a compact label instead
523
+ (the popover titles), so the two identical inputs stay tellable. -->
524
+ <div class="flex flex-col gap-2 @xs:flex-row @xs:items-center">
525
+ <div class="flex w-full min-w-0 flex-col gap-1 @xs:flex-1">
526
+ <span class="text-xs text-muted @xs:hidden" aria-hidden="true">{{ startPopoverTitle }}</span>
527
+ <UInputDate
528
+ v-model="startDate"
529
+ :is-date-disabled="isDateDisabled"
530
+ :size="size"
531
+ :disabled="disabled"
532
+ :color="inputColor"
533
+ :aria-label="startLabel"
534
+ class="w-full"
535
+ >
536
+ <template #trailing>
537
+ <UPopover v-model:open="startPopoverOpen">
538
+ <UButton
539
+ color="neutral"
540
+ variant="link"
541
+ size="sm"
542
+ :icon="icon"
543
+ :aria-label="startLabel"
544
+ :disabled="disabled"
545
+ class="px-0"
546
+ />
547
+ <template #content>
548
+ <div class="flex flex-col" @pointerleave="hoveredDay = null">
549
+ <p class="flex items-baseline justify-between gap-2 px-3 pt-2 pb-1">
550
+ <span class="text-xs font-medium text-muted uppercase tracking-wide">
551
+ {{ startPopoverTitle }}
552
+ </span>
553
+ <span v-if="spanLabel('start')" class="text-xs font-medium text-primary">
554
+ {{ spanLabel('start') }}
555
+ </span>
556
+ </p>
557
+ <UCalendar
558
+ :model-value="startDate"
559
+ :placeholder="startDate ?? endDate"
560
+ :is-date-disabled="isDateDisabled"
561
+ :ui="calendarUi"
562
+ class="p-2"
563
+ @update:model-value="onStartCalendarSelect"
564
+ >
565
+ <template #day="{ day }">
566
+ <span :class="dayPillClass(day, 'start')" @pointerenter="hoveredDay = day.toString()">
567
+ {{ day.day }}
568
+ </span>
569
+ </template>
570
+ </UCalendar>
571
+ </div>
572
+ </template>
573
+ </UPopover>
574
+ </template>
575
+ </UInputDate>
576
+ </div>
577
+
578
+ <span class="hidden shrink-0 text-muted @xs:inline" aria-hidden="true">→</span>
579
+
580
+ <div class="flex w-full min-w-0 flex-col gap-1 @xs:flex-1">
581
+ <span class="text-xs text-muted @xs:hidden" aria-hidden="true">{{ endPopoverTitle }}</span>
582
+ <UInputDate
583
+ v-model="endDate"
584
+ :is-date-disabled="isEndDateDisabled"
585
+ :size="size"
586
+ :disabled="disabled"
587
+ :color="inputColor"
588
+ :aria-label="endLabel"
589
+ class="w-full"
590
+ >
591
+ <template #trailing>
592
+ <UPopover v-model:open="endPopoverOpen">
593
+ <UButton
594
+ color="neutral"
595
+ variant="link"
596
+ size="sm"
597
+ :icon="icon"
598
+ :aria-label="endLabel"
599
+ :disabled="disabled"
600
+ class="px-0"
601
+ />
602
+ <template #content>
603
+ <div class="flex flex-col" @pointerleave="hoveredDay = null">
604
+ <p class="flex items-baseline justify-between gap-2 px-3 pt-2 pb-1">
605
+ <span class="text-xs font-medium text-muted uppercase tracking-wide">
606
+ {{ endPopoverTitle }}
556
607
  </span>
557
- </template>
558
- </UCalendar>
559
- </div>
560
- </template>
561
- </UPopover>
562
- </template>
563
- </UInputDate>
564
-
565
- <span class="text-muted shrink-0" aria-hidden="true">→</span>
566
-
567
- <UInputDate
568
- v-model="endDate"
569
- :is-date-disabled="isEndDateDisabled"
570
- :size="size"
571
- :disabled="disabled"
572
- :color="inputColor"
573
- :aria-label="endLabel"
574
- class="flex-1"
575
- >
576
- <template #trailing>
577
- <UPopover v-model:open="endPopoverOpen">
578
- <UButton
579
- color="neutral"
580
- variant="link"
581
- size="sm"
582
- :icon="icon"
583
- :aria-label="endLabel"
584
- :disabled="disabled"
585
- class="px-0"
586
- />
587
- <template #content>
588
- <div class="flex flex-col" @pointerleave="hoveredDay = null">
589
- <p class="flex items-baseline justify-between gap-2 px-3 pt-2 pb-1">
590
- <span class="text-xs font-medium text-muted uppercase tracking-wide">
591
- {{ endPopoverTitle }}
592
- </span>
593
- <span v-if="spanLabel('end')" class="text-xs font-medium text-primary">
594
- {{ spanLabel('end') }}
595
- </span>
596
- </p>
597
- <UCalendar
598
- :model-value="endDate"
599
- :placeholder="endDate ?? startDate"
600
- :is-date-disabled="isEndDateDisabled"
601
- :ui="calendarUi"
602
- class="p-2"
603
- @update:model-value="onEndCalendarSelect"
604
- >
605
- <template #day="{ day }">
606
- <span :class="dayPillClass(day, 'end')" @pointerenter="hoveredDay = day.toString()">
607
- {{ day.day }}
608
+ <span v-if="spanLabel('end')" class="text-xs font-medium text-primary">
609
+ {{ spanLabel('end') }}
608
610
  </span>
609
- </template>
610
- </UCalendar>
611
- </div>
612
- </template>
613
- </UPopover>
614
- </template>
615
- </UInputDate>
611
+ </p>
612
+ <UCalendar
613
+ :model-value="endDate"
614
+ :placeholder="endDate ?? startDate"
615
+ :is-date-disabled="isEndDateDisabled"
616
+ :ui="calendarUi"
617
+ class="p-2"
618
+ @update:model-value="onEndCalendarSelect"
619
+ >
620
+ <template #day="{ day }">
621
+ <span :class="dayPillClass(day, 'end')" @pointerenter="hoveredDay = day.toString()">
622
+ {{ day.day }}
623
+ </span>
624
+ </template>
625
+ </UCalendar>
626
+ </div>
627
+ </template>
628
+ </UPopover>
629
+ </template>
630
+ </UInputDate>
631
+ </div>
616
632
  </div>
617
633
 
618
634
  <!-- Slot for a derived summary (e.g. the resulting travel period) shown
@@ -11,6 +11,11 @@
11
11
  // i18n key contract: flexPeriod.* (earliestStart/latestEnd/nightsLabel/
12
12
  // clear/windowSpan/flexibility/example/errors.*) and period.travel.nights,
13
13
  // plus the composed DateRangeInput's dateRange.* keys.
14
+ //
15
+ // Sizing contract: the root is an inline-size @container (single row from
16
+ // 512px of own width, stacked below), so its intrinsic width is 0. Parents
17
+ // must give it a definite width — block/grid context, `flex-1`, or an
18
+ // explicit `w-*`/`basis-*` — never shrink-to-fit.
14
19
  import { computed, watch } from 'vue'
15
20
  import { useI18n } from 'vue-i18n'
16
21
  import UInputNumber from '@nuxt/ui/components/InputNumber.vue'
@@ -188,8 +193,13 @@ watch(
188
193
  </script>
189
194
 
190
195
  <template>
191
- <div class="flex flex-col gap-1">
192
- <div class="flex items-start gap-2">
196
+ <div class="@container flex flex-col gap-1">
197
+ <!-- Container-responsive: one row (window + nights + clear) from 512px of
198
+ *own* width; below that the window goes full-width and nights + clear
199
+ drop to their own line (the nights input gains a compact label, since
200
+ its placeholder vanishes once a value is set). The inner
201
+ DateRangeInput additionally stacks its two dates below 320px. -->
202
+ <div class="flex flex-col gap-2 @lg:flex-row @lg:items-start">
193
203
  <DateRangeInput
194
204
  v-model="innerPeriod"
195
205
  kind="travel"
@@ -198,29 +208,34 @@ watch(
198
208
  :icon="icon"
199
209
  :start-label="startAriaLabel"
200
210
  :end-label="endAriaLabel"
201
- class="flex-1"
202
- />
203
- <UInputNumber
204
- v-model="nightsValue"
205
- :min="minNights"
206
- :max="maxNights"
207
- :size="size"
208
- :disabled="disabled"
209
- :color="nightsInputColor"
210
- :aria-label="t('flexPeriod.nightsLabel')"
211
- :placeholder="t('flexPeriod.nightsLabel')"
212
- class="w-28 shrink-0"
213
- />
214
- <UButton
215
- v-if="clearable && hasAnyValue"
216
- icon="i-lucide-x"
217
- color="neutral"
218
- variant="ghost"
219
- :size="size"
220
- :disabled="disabled"
221
- :aria-label="t('flexPeriod.clear')"
222
- @click="clearAll"
211
+ class="w-full @lg:flex-1"
223
212
  />
213
+ <div class="flex items-end gap-2 @lg:shrink-0">
214
+ <div class="flex w-full flex-col gap-1 @lg:w-28">
215
+ <span class="text-xs text-muted @lg:hidden" aria-hidden="true">{{ t('flexPeriod.nightsLabel') }}</span>
216
+ <UInputNumber
217
+ v-model="nightsValue"
218
+ :min="minNights"
219
+ :max="maxNights"
220
+ :size="size"
221
+ :disabled="disabled"
222
+ :color="nightsInputColor"
223
+ :aria-label="t('flexPeriod.nightsLabel')"
224
+ :placeholder="t('flexPeriod.nightsLabel')"
225
+ class="w-full"
226
+ />
227
+ </div>
228
+ <UButton
229
+ v-if="clearable && hasAnyValue"
230
+ icon="i-lucide-x"
231
+ color="neutral"
232
+ variant="ghost"
233
+ :size="size"
234
+ :disabled="disabled"
235
+ :aria-label="t('flexPeriod.clear')"
236
+ @click="clearAll"
237
+ />
238
+ </div>
224
239
  </div>
225
240
 
226
241
  <!-- Slot for a derived summary shown between the inputs and the hints,