@nexxtmove/ui 0.1.61 → 0.1.63

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/nuxt.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { defineNuxtModule as c, addComponent as s, addTemplate as a } from "@nuxt/kit";
2
- const x = "components/AnimatedNumber/AnimatedNumber.vue", i = "components/Avatar/Avatar.vue", r = "components/Button/Button.vue", m = "components/Calendar/Calendar.vue", u = "components/Card/Card.vue", p = "components/Checkbox/Checkbox.vue", l = "components/Chip/Chip.vue", d = "components/Combobox/Combobox.vue", v = "components/ComboboxPanel/ComboboxPanel.vue", C = "components/ContactInfoCard/ContactInfoCard.vue", N = "components/CustomerJourneyTile/CustomerJourneyTile.vue", b = "components/DatePicker/DatePicker.vue", P = "components/DoughnutChart/DoughnutChart.vue", T = "components/DropdownButton/DropdownButton.vue", S = "components/FloatingPanel/FloatingPanel.vue", h = "components/Header/Header.vue", k = "components/Icon/Icon.vue", I = "components/InfoBlock/InfoBlock.vue", f = "components/InputField/InputField.vue", g = "components/Modal/Modal.vue", y = "components/Pagination/Pagination.vue", M = "components/PresenceIndicator/PresenceIndicator.vue", B = "components/ProgressBar/ProgressBar.vue", D = "components/PropertyCard/PropertyCard.vue", A = "components/PropertyListing/PropertyListing.vue", F = "components/Skeleton/Skeleton.vue", H = "components/SkeletonPropertyCard/SkeletonPropertyCard.vue", w = "components/SocialIcons/SocialIcons.vue", E = "components/SocialMediaCustomTemplate/SocialMediaCustomTemplate.vue", J = "components/SocialMediaTemplate/SocialMediaTemplate.vue", L = "components/SocialMediaType/SocialMediaType.vue", j = "components/Source/Source.vue", $ = "components/StepperHeader/StepperHeader.vue", K = "components/TabbedContent/TabbedContent.vue", O = "components/Table/Table.vue", U = "components/Tabs/Tabs.vue", _ = "components/TimelineEvent/TimelineEvent.vue", q = "components/TimelinePhaseblock/TimelinePhaseblock.vue", z = "components/Tooltip/Tooltip.vue", G = {
2
+ const x = "components/AnimatedNumber/AnimatedNumber.vue", i = "components/Avatar/Avatar.vue", r = "components/Button/Button.vue", m = "components/Calendar/Calendar.vue", u = "components/Card/Card.vue", p = "components/Checkbox/Checkbox.vue", l = "components/Chip/Chip.vue", d = "components/Combobox/Combobox.vue", v = "components/ComboboxPanel/ComboboxPanel.vue", C = "components/ContactInfoCard/ContactInfoCard.vue", N = "components/CustomerJourneyTile/CustomerJourneyTile.vue", b = "components/DatePicker/DatePicker.vue", P = "components/DoughnutChart/DoughnutChart.vue", S = "components/DropdownButton/DropdownButton.vue", T = "components/FloatingPanel/FloatingPanel.vue", h = "components/Header/Header.vue", k = "components/Icon/Icon.vue", I = "components/InfoBlock/InfoBlock.vue", f = "components/InputField/InputField.vue", g = "components/Modal/Modal.vue", y = "components/Pagination/Pagination.vue", M = "components/PresenceIndicator/PresenceIndicator.vue", B = "components/ProgressBar/ProgressBar.vue", D = "components/PropertyCard/PropertyCard.vue", A = "components/PropertyListing/PropertyListing.vue", F = "components/Select/Select.vue", H = "components/Skeleton/Skeleton.vue", w = "components/SkeletonPropertyCard/SkeletonPropertyCard.vue", E = "components/SocialIcons/SocialIcons.vue", J = "components/SocialMediaCustomTemplate/SocialMediaCustomTemplate.vue", L = "components/SocialMediaTemplate/SocialMediaTemplate.vue", j = "components/SocialMediaType/SocialMediaType.vue", $ = "components/Source/Source.vue", K = "components/StepperHeader/StepperHeader.vue", O = "components/TabbedContent/TabbedContent.vue", U = "components/Table/Table.vue", _ = "components/Tabs/Tabs.vue", q = "components/TimelineEvent/TimelineEvent.vue", z = "components/TimelinePhaseblock/TimelinePhaseblock.vue", G = "components/Tooltip/Tooltip.vue", Q = {
3
3
  NexxtAnimatedNumber: x,
4
4
  NexxtAvatar: i,
5
5
  NexxtButton: r,
@@ -13,8 +13,8 @@ const x = "components/AnimatedNumber/AnimatedNumber.vue", i = "components/Avatar
13
13
  NexxtCustomerJourneyTile: N,
14
14
  NexxtDatePicker: b,
15
15
  NexxtDoughnutChart: P,
16
- NexxtDropdownButton: T,
17
- NexxtFloatingPanel: S,
16
+ NexxtDropdownButton: S,
17
+ NexxtFloatingPanel: T,
18
18
  NexxtHeader: h,
19
19
  NexxtIcon: k,
20
20
  NexxtInfoBlock: I,
@@ -25,21 +25,22 @@ const x = "components/AnimatedNumber/AnimatedNumber.vue", i = "components/Avatar
25
25
  NexxtProgressBar: B,
26
26
  NexxtPropertyCard: D,
27
27
  NexxtPropertyListing: A,
28
- NexxtSkeleton: F,
29
- NexxtSkeletonPropertyCard: H,
30
- NexxtSocialIcons: w,
31
- NexxtSocialMediaCustomTemplate: E,
32
- NexxtSocialMediaTemplate: J,
33
- NexxtSocialMediaType: L,
34
- NexxtSource: j,
35
- NexxtStepperHeader: $,
36
- NexxtTabbedContent: K,
37
- NexxtTable: O,
38
- NexxtTabs: U,
39
- NexxtTimelineEvent: _,
40
- NexxtTimelinePhaseblock: q,
41
- NexxtTooltip: z
42
- }, R = c({
28
+ NexxtSelect: F,
29
+ NexxtSkeleton: H,
30
+ NexxtSkeletonPropertyCard: w,
31
+ NexxtSocialIcons: E,
32
+ NexxtSocialMediaCustomTemplate: J,
33
+ NexxtSocialMediaTemplate: L,
34
+ NexxtSocialMediaType: j,
35
+ NexxtSource: $,
36
+ NexxtStepperHeader: K,
37
+ NexxtTabbedContent: O,
38
+ NexxtTable: U,
39
+ NexxtTabs: _,
40
+ NexxtTimelineEvent: q,
41
+ NexxtTimelinePhaseblock: z,
42
+ NexxtTooltip: G
43
+ }, V = c({
43
44
  meta: {
44
45
  name: "@nexxtmove/ui",
45
46
  configKey: "nexxtmoveUi",
@@ -51,7 +52,7 @@ const x = "components/AnimatedNumber/AnimatedNumber.vue", i = "components/Avatar
51
52
  addCSS: !0
52
53
  },
53
54
  setup(e, o) {
54
- for (const [t, n] of Object.entries(G))
55
+ for (const [t, n] of Object.entries(Q))
55
56
  s({
56
57
  name: t,
57
58
  export: "default",
@@ -70,5 +71,5 @@ const x = "components/AnimatedNumber/AnimatedNumber.vue", i = "components/Avatar
70
71
  }
71
72
  });
72
73
  export {
73
- R as default
74
+ V as default
74
75
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@nexxtmove/ui",
3
3
  "type": "module",
4
- "version": "0.1.61",
4
+ "version": "0.1.63",
5
5
  "exports": {
6
6
  ".": {
7
7
  "types": "./dist/index.d.ts",
@@ -21,6 +21,7 @@ import CalendarHeader from './_CalendarHeader.vue'
21
21
  import CalendarDayView from './_CalendarDayView.vue'
22
22
  import CalendarMonthView from './_CalendarMonthView.vue'
23
23
  import CalendarYearView from './_CalendarYearView.vue'
24
+ import CalendarTimePicker from './_CalendarTimePicker.vue'
24
25
  import type { NexxtCalendarProps } from './calendar.types'
25
26
 
26
27
  export type { NexxtCalendarProps }
@@ -44,6 +45,7 @@ const {
44
45
  maxDate,
45
46
  monthYearPicker = false,
46
47
  timezone = 'UTC',
48
+ datetime = false,
47
49
  } = defineProps<NexxtCalendarProps>()
48
50
 
49
51
  // ── State ─────────────────────────────────────────────────────────────────────
@@ -235,5 +237,7 @@ const currentDayTransition = computed(() =>
235
237
  />
236
238
  </Transition>
237
239
  </div>
240
+
241
+ <CalendarTimePicker v-if="datetime" v-model="model" />
238
242
  </div>
239
243
  </template>
@@ -0,0 +1,98 @@
1
+ <script lang="ts" setup>
2
+ import { computed } from 'vue'
3
+ import { setHours, setMinutes, getHours, getMinutes } from 'date-fns'
4
+ import NexxtIcon from '../Icon/Icon.vue'
5
+
6
+ const props = defineProps<{
7
+ modelValue: Date | null
8
+ }>()
9
+
10
+ const emit = defineEmits<{
11
+ (e: 'update:modelValue', value: Date | null): void
12
+ }>()
13
+
14
+ const hours = computed({
15
+ get: () => {
16
+ if (props.modelValue) return getHours(props.modelValue)
17
+ return getHours(new Date())
18
+ },
19
+ set: (val) => {
20
+ const base = props.modelValue || new Date()
21
+ emit('update:modelValue', setHours(base, Number(val)))
22
+ },
23
+ })
24
+
25
+ const minutes = computed({
26
+ get: () => {
27
+ if (props.modelValue) return getMinutes(props.modelValue)
28
+ return getMinutes(new Date())
29
+ },
30
+ set: (val) => {
31
+ const base = props.modelValue || new Date()
32
+ emit('update:modelValue', setMinutes(base, Number(val)))
33
+ },
34
+ })
35
+
36
+ const pad = (n: number) => n.toString().padStart(2, '0')
37
+
38
+ const incrementHours = () => {
39
+ hours.value = (hours.value + 1) % 24
40
+ }
41
+ const decrementHours = () => {
42
+ hours.value = (hours.value + 23) % 24
43
+ }
44
+ const incrementMinutes = () => {
45
+ minutes.value = (minutes.value + 1) % 60
46
+ }
47
+ const decrementMinutes = () => {
48
+ minutes.value = (minutes.value + 59) % 60
49
+ }
50
+ </script>
51
+
52
+ <template>
53
+ <div class="flex items-center justify-center gap-4 border-t border-gray-100 pt-4">
54
+ <!-- Hours -->
55
+ <div class="flex flex-col items-center">
56
+ <button
57
+ type="button"
58
+ class="flex h-8 w-8 items-center justify-center rounded-full text-gray-400 transition-colors hover:bg-gray-100 hover:text-cornflower-blue-500"
59
+ @click="incrementHours"
60
+ >
61
+ <NexxtIcon name="chevron-up" />
62
+ </button>
63
+ <span class="font-medium text-lg text-gray-900 tabular-nums">
64
+ {{ pad(hours) }}
65
+ </span>
66
+ <button
67
+ type="button"
68
+ class="flex h-8 w-8 items-center justify-center rounded-full text-gray-400 transition-colors hover:bg-gray-100 hover:text-cornflower-blue-500"
69
+ @click="decrementHours"
70
+ >
71
+ <NexxtIcon name="chevron-down" />
72
+ </button>
73
+ </div>
74
+
75
+ <span class="mb-1 font-medium text-xl text-gray-400">:</span>
76
+
77
+ <!-- Minutes -->
78
+ <div class="flex flex-col items-center">
79
+ <button
80
+ type="button"
81
+ class="flex h-8 w-8 items-center justify-center rounded-full text-gray-400 transition-colors hover:bg-gray-100 hover:text-cornflower-blue-500"
82
+ @click="incrementMinutes"
83
+ >
84
+ <NexxtIcon name="chevron-up" />
85
+ </button>
86
+ <span class="font-medium text-lg text-gray-900 tabular-nums">
87
+ {{ pad(minutes) }}
88
+ </span>
89
+ <button
90
+ type="button"
91
+ class="flex h-8 w-8 items-center justify-center rounded-full text-gray-400 transition-colors hover:bg-gray-100 hover:text-cornflower-blue-500"
92
+ @click="decrementMinutes"
93
+ >
94
+ <NexxtIcon name="chevron-down" />
95
+ </button>
96
+ </div>
97
+ </div>
98
+ </template>
@@ -8,4 +8,5 @@ export interface NexxtCalendarProps {
8
8
  maxDate?: Date
9
9
  monthYearPicker?: boolean
10
10
  timezone?: string
11
+ datetime?: boolean
11
12
  }
@@ -55,7 +55,7 @@ const floatingPlacement = computed(() => anchorToPlacement[props.anchor])
55
55
  // ── Handlers ──────────────────────────────────────────────────────────────────
56
56
 
57
57
  const handleSelect = () => {
58
- if (props.autoClose) panelRef.value?.close()
58
+ if (props.autoClose && !props.datetime) panelRef.value?.close()
59
59
  }
60
60
 
61
61
  const onPanelOpen = async () => {
@@ -1,11 +1,12 @@
1
1
  <script lang="ts" setup>
2
2
  import { ref, computed, onMounted, onUnmounted } from 'vue'
3
- import { useFloating, autoUpdate, flip, shift, offset } from '@floating-ui/vue'
3
+ import { useFloating, autoUpdate, flip, shift, offset, size } from '@floating-ui/vue'
4
4
  import type { Placement } from '@floating-ui/vue'
5
5
 
6
6
  export interface NexxtFloatingPanelProps {
7
7
  placement?: Placement
8
8
  offsetDistance?: number
9
+ matchWidth?: boolean
9
10
  }
10
11
 
11
12
  defineOptions({ name: 'NexxtFloatingPanel' })
@@ -13,6 +14,7 @@ defineOptions({ name: 'NexxtFloatingPanel' })
13
14
  const props = withDefaults(defineProps<NexxtFloatingPanelProps>(), {
14
15
  placement: 'bottom-start',
15
16
  offsetDistance: 8,
17
+ matchWidth: false,
16
18
  })
17
19
 
18
20
  const emit = defineEmits<{
@@ -26,6 +28,21 @@ const isLeaving = ref(false)
26
28
  const triggerRef = ref<HTMLElement | null>(null)
27
29
  const floatingRef = ref<HTMLElement | null>(null)
28
30
 
31
+ const middleware = computed(() => [
32
+ offset(props.offsetDistance),
33
+ flip(),
34
+ shift({ padding: 8 }),
35
+ ...(props.matchWidth
36
+ ? [
37
+ size({
38
+ apply: ({ rects, elements }) => {
39
+ elements.floating.style.minWidth = `${rects.reference.width}px`
40
+ },
41
+ }),
42
+ ]
43
+ : []),
44
+ ])
45
+
29
46
  const {
30
47
  floatingStyles,
31
48
  placement: computedPlacement,
@@ -33,7 +50,7 @@ const {
33
50
  } = useFloating(triggerRef, floatingRef, {
34
51
  placement: computed(() => props.placement),
35
52
  whileElementsMounted: autoUpdate,
36
- middleware: [offset(props.offsetDistance), flip(), shift({ padding: 8 })],
53
+ middleware,
37
54
  // fixed strategy escapes overflow clipping (e.g. inside a Modal) without
38
55
  // needing a body teleport that Reka UI's dialog would block with pointer-events:none.
39
56
  strategy: 'fixed',
@@ -0,0 +1,118 @@
1
+ <script lang="ts" setup>
2
+ import { computed } from 'vue'
3
+ import NexxtIcon from '../Icon/Icon.vue'
4
+ import NexxtFloatingPanel from '../FloatingPanel/FloatingPanel.vue'
5
+ import type Icon from '../Icon/Icon.vue'
6
+
7
+ export type SelectOption = { label: string; value: string | number } | string
8
+
9
+ const getLabel = (opt: SelectOption) => (typeof opt === 'string' ? opt : opt.label)
10
+ const getValue = (opt: SelectOption): string | number => (typeof opt === 'string' ? opt : opt.value)
11
+
12
+ const generateId = () => 'select-' + Math.random().toString(36).slice(2, 10)
13
+ const selectId = generateId()
14
+
15
+ defineOptions({ name: 'NexxtSelect' })
16
+
17
+ interface NexxtSelectProps {
18
+ options: SelectOption[]
19
+ placeholder?: string
20
+ label?: string
21
+ error?: boolean
22
+ errorMessage?: string
23
+ leftIcon?: InstanceType<typeof Icon>['name']
24
+ required?: boolean
25
+ }
26
+
27
+ const props = withDefaults(defineProps<NexxtSelectProps>(), {
28
+ placeholder: 'Selecteer...',
29
+ })
30
+
31
+ const model = defineModel<string | number | null>({ default: null })
32
+
33
+ const selectedLabel = computed(() => {
34
+ if (model.value === null || model.value === undefined) return null
35
+ const found = props.options.find((opt) => getValue(opt) === model.value)
36
+ return found ? getLabel(found) : null
37
+ })
38
+
39
+ const select = (opt: SelectOption, close: () => void) => {
40
+ model.value = getValue(opt)
41
+ close()
42
+ }
43
+ </script>
44
+
45
+ <template>
46
+ <div class="inline-flex flex-col gap-1">
47
+ <label
48
+ v-if="label"
49
+ class="text-color-gray-700 justify-start body-normal text-sm"
50
+ :for="selectId"
51
+ >
52
+ {{ label }}
53
+ <span v-if="required" class="text-brick-500">*</span>
54
+ </label>
55
+
56
+ <NexxtFloatingPanel placement="bottom" match-width>
57
+ <template #trigger="{ isOpen, toggle }">
58
+ <div
59
+ :id="selectId"
60
+ role="combobox"
61
+ :aria-expanded="isOpen"
62
+ aria-haspopup="listbox"
63
+ tabindex="0"
64
+ class="flex w-full cursor-pointer items-center gap-2 rounded-lg ring transition-colors ease-out focus:ring-cornflower-blue-500 focus:outline-none"
65
+ :class="[error ? 'ring-brick-400' : 'ring-gray-200', { 'pl-3': leftIcon, 'pr-3': true }]"
66
+ @click="toggle"
67
+ @keydown.enter.prevent="toggle"
68
+ @keydown.space.prevent="toggle"
69
+ >
70
+ <span v-if="leftIcon" class="flex items-center">
71
+ <NexxtIcon :name="leftIcon" />
72
+ </span>
73
+ <span
74
+ class="flex h-10 w-full min-w-60 items-center text-sm"
75
+ :class="[selectedLabel ? 'text-gray-900' : 'text-gray-400', { 'pl-3': !leftIcon }]"
76
+ >
77
+ {{ selectedLabel ?? placeholder }}
78
+ </span>
79
+ <NexxtIcon
80
+ name="chevron-down"
81
+ class="shrink-0 text-gray-400 transition-transform duration-200"
82
+ :class="{ 'rotate-180': isOpen }"
83
+ />
84
+ </div>
85
+ </template>
86
+
87
+ <template #content="{ close }">
88
+ <ul role="listbox" class="overflow-hidden rounded-lg bg-white shadow-xs ring ring-gray-100">
89
+ <li
90
+ v-for="opt in options"
91
+ :key="getValue(opt)"
92
+ role="option"
93
+ :aria-selected="model === getValue(opt)"
94
+ class="flex cursor-pointer items-center justify-between px-4 py-3 text-sm text-gray-900 transition-colors duration-150 hover:bg-gray-50 hover:text-purple-600"
95
+ :class="{ 'font-medium text-cornflower-blue-600': model === getValue(opt) }"
96
+ @click="select(opt, close)"
97
+ >
98
+ {{ getLabel(opt) }}
99
+ <NexxtIcon v-if="model === getValue(opt)" name="check" class="text-xs" />
100
+ </li>
101
+ </ul>
102
+ </template>
103
+ </NexxtFloatingPanel>
104
+
105
+ <Transition
106
+ enter-active-class="transition-all duration-200 ease-out"
107
+ enter-from-class="opacity-0 -translate-y-1"
108
+ enter-to-class="opacity-100 translate-y-0"
109
+ leave-active-class="transition-all duration-150 ease-in"
110
+ leave-from-class="opacity-100 translate-y-0"
111
+ leave-to-class="opacity-0 -translate-y-1"
112
+ >
113
+ <p v-if="error && errorMessage" class="text-sm text-brick-400">
114
+ {{ errorMessage }}
115
+ </p>
116
+ </Transition>
117
+ </div>
118
+ </template>
@@ -24,6 +24,7 @@
24
24
  "NexxtProgressBar": "components/ProgressBar/ProgressBar.vue",
25
25
  "NexxtPropertyCard": "components/PropertyCard/PropertyCard.vue",
26
26
  "NexxtPropertyListing": "components/PropertyListing/PropertyListing.vue",
27
+ "NexxtSelect": "components/Select/Select.vue",
27
28
  "NexxtSkeleton": "components/Skeleton/Skeleton.vue",
28
29
  "NexxtSkeletonPropertyCard": "components/SkeletonPropertyCard/SkeletonPropertyCard.vue",
29
30
  "NexxtSocialIcons": "components/SocialIcons/SocialIcons.vue",
package/src/index.ts CHANGED
@@ -24,6 +24,7 @@ export { default as NexxtPresenceIndicator } from './components/PresenceIndicato
24
24
  export { default as NexxtProgressBar } from './components/ProgressBar/ProgressBar.vue'
25
25
  export { default as NexxtPropertyCard } from './components/PropertyCard/PropertyCard.vue'
26
26
  export { default as NexxtPropertyListing } from './components/PropertyListing/PropertyListing.vue'
27
+ export { default as NexxtSelect } from './components/Select/Select.vue'
27
28
  export { default as NexxtSkeleton } from './components/Skeleton/Skeleton.vue'
28
29
  export { default as NexxtSkeletonPropertyCard } from './components/SkeletonPropertyCard/SkeletonPropertyCard.vue'
29
30
  export { default as NexxtSocialIcons } from './components/SocialIcons/SocialIcons.vue'