@nexxtmove/ui 0.1.46 → 0.1.47

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,31 +1,32 @@
1
1
  import { defineNuxtModule as c, addComponent as s, addTemplate as a } from "@nuxt/kit";
2
- const i = "components/AnimatedNumber/AnimatedNumber.vue", m = "components/Avatar/Avatar.vue", x = "components/Button/Button.vue", u = "components/Calendar/Calendar.vue", p = "components/Checkbox/Checkbox.vue", l = "components/Chip/Chip.vue", r = "components/CustomerJourneyTile/CustomerJourneyTile.vue", d = "components/DatePicker/DatePicker.vue", v = "components/DropdownButton/DropdownButton.vue", N = "components/Header/Header.vue", T = "components/Icon/Icon.vue", C = "components/InfoBlock/InfoBlock.vue", S = "components/InputField/InputField.vue", b = "components/Modal/Modal.vue", f = "components/Pagination/Pagination.vue", h = "components/ProgressBar/ProgressBar.vue", M = "components/SocialIcons/SocialIcons.vue", P = "components/SocialMediaCustomTemplate/SocialMediaCustomTemplate.vue", k = "components/SocialMediaTemplate/SocialMediaTemplate.vue", B = "components/SocialMediaType/SocialMediaType.vue", I = "components/StepperHeader/StepperHeader.vue", g = "components/Table/Table.vue", y = "components/TimelineEvent/TimelineEvent.vue", A = "components/TimelinePhaseblock/TimelinePhaseblock.vue", D = "components/Tooltip/Tooltip.vue", H = {
2
+ const i = "components/AnimatedNumber/AnimatedNumber.vue", x = "components/Avatar/Avatar.vue", m = "components/Button/Button.vue", u = "components/Calendar/Calendar.vue", p = "components/Checkbox/Checkbox.vue", l = "components/Chip/Chip.vue", r = "components/Combobox/Combobox.vue", d = "components/CustomerJourneyTile/CustomerJourneyTile.vue", v = "components/DatePicker/DatePicker.vue", N = "components/DropdownButton/DropdownButton.vue", T = "components/Header/Header.vue", C = "components/Icon/Icon.vue", b = "components/InfoBlock/InfoBlock.vue", S = "components/InputField/InputField.vue", f = "components/Modal/Modal.vue", h = "components/Pagination/Pagination.vue", M = "components/ProgressBar/ProgressBar.vue", P = "components/SocialIcons/SocialIcons.vue", k = "components/SocialMediaCustomTemplate/SocialMediaCustomTemplate.vue", B = "components/SocialMediaTemplate/SocialMediaTemplate.vue", I = "components/SocialMediaType/SocialMediaType.vue", g = "components/StepperHeader/StepperHeader.vue", y = "components/Table/Table.vue", A = "components/TimelineEvent/TimelineEvent.vue", D = "components/TimelinePhaseblock/TimelinePhaseblock.vue", H = "components/Tooltip/Tooltip.vue", w = {
3
3
  NexxtAnimatedNumber: i,
4
- NexxtAvatar: m,
5
- NexxtButton: x,
4
+ NexxtAvatar: x,
5
+ NexxtButton: m,
6
6
  NexxtCalendar: u,
7
7
  NexxtCheckbox: p,
8
8
  NexxtChip: l,
9
- NexxtCustomerJourneyTile: r,
10
- NexxtDatePicker: d,
11
- NexxtDropdownButton: v,
12
- NexxtHeader: N,
13
- NexxtIcon: T,
14
- NexxtInfoBlock: C,
9
+ NexxtCombobox: r,
10
+ NexxtCustomerJourneyTile: d,
11
+ NexxtDatePicker: v,
12
+ NexxtDropdownButton: N,
13
+ NexxtHeader: T,
14
+ NexxtIcon: C,
15
+ NexxtInfoBlock: b,
15
16
  NexxtInputField: S,
16
- NexxtModal: b,
17
- NexxtPagination: f,
18
- NexxtProgressBar: h,
19
- NexxtSocialIcons: M,
20
- NexxtSocialMediaCustomTemplate: P,
21
- NexxtSocialMediaTemplate: k,
22
- NexxtSocialMediaType: B,
23
- NexxtStepperHeader: I,
24
- NexxtTable: g,
25
- NexxtTimelineEvent: y,
26
- NexxtTimelinePhaseblock: A,
27
- NexxtTooltip: D
28
- }, E = c({
17
+ NexxtModal: f,
18
+ NexxtPagination: h,
19
+ NexxtProgressBar: M,
20
+ NexxtSocialIcons: P,
21
+ NexxtSocialMediaCustomTemplate: k,
22
+ NexxtSocialMediaTemplate: B,
23
+ NexxtSocialMediaType: I,
24
+ NexxtStepperHeader: g,
25
+ NexxtTable: y,
26
+ NexxtTimelineEvent: A,
27
+ NexxtTimelinePhaseblock: D,
28
+ NexxtTooltip: H
29
+ }, F = c({
29
30
  meta: {
30
31
  name: "@nexxtmove/ui",
31
32
  configKey: "nexxtmoveUi",
@@ -37,7 +38,7 @@ const i = "components/AnimatedNumber/AnimatedNumber.vue", m = "components/Avatar
37
38
  addCSS: !0
38
39
  },
39
40
  setup(e, o) {
40
- for (const [t, n] of Object.entries(H))
41
+ for (const [t, n] of Object.entries(w))
41
42
  s({
42
43
  name: t,
43
44
  export: "default",
@@ -56,5 +57,5 @@ const i = "components/AnimatedNumber/AnimatedNumber.vue", m = "components/Avatar
56
57
  }
57
58
  });
58
59
  export {
59
- E as default
60
+ F as default
60
61
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@nexxtmove/ui",
3
3
  "type": "module",
4
- "version": "0.1.46",
4
+ "version": "0.1.47",
5
5
  "exports": {
6
6
  ".": {
7
7
  "types": "./dist/index.d.ts",
@@ -34,12 +34,12 @@
34
34
  "@eslint/js": "^9.39.2",
35
35
  "@nuxt/kit": "^4.3.1",
36
36
  "@nuxt/schema": "^4.3.1",
37
- "@storybook/addon-a11y": "^10.3.3",
38
- "@storybook/addon-designs": "^11.1.2",
39
- "@storybook/addon-docs": "^10.3.3",
40
- "@storybook/addon-vitest": "^10.3.3",
41
- "@storybook/builder-vite": "^10.3.3",
42
- "@storybook/vue3-vite": "^10.3.3",
37
+ "@storybook/addon-a11y": "^10.3.5",
38
+ "@storybook/addon-designs": "^11.1.3",
39
+ "@storybook/addon-docs": "^10.3.5",
40
+ "@storybook/addon-vitest": "^10.3.5",
41
+ "@storybook/builder-vite": "^10.3.5",
42
+ "@storybook/vue3-vite": "^10.3.5",
43
43
  "@tailwindcss/vite": "^4.1.18",
44
44
  "@types/node": "^25.0.3",
45
45
  "@types/react": "^19.2.8",
@@ -53,7 +53,7 @@
53
53
  "eslint": "^9.39.2",
54
54
  "eslint-config-prettier": "^10.1.8",
55
55
  "eslint-plugin-prettier": "^5.5.5",
56
- "eslint-plugin-storybook": "10.3.3",
56
+ "eslint-plugin-storybook": "10.3.5",
57
57
  "eslint-plugin-vue": "^10.6.2",
58
58
  "globals": "^17.0.0",
59
59
  "jsdom": "^28.1.0",
@@ -62,7 +62,7 @@
62
62
  "react": "^19.2.3",
63
63
  "react-dom": "^19.2.3",
64
64
  "remark-frontmatter": "^5.0.0",
65
- "storybook": "^10.3.3",
65
+ "storybook": "^10.3.5",
66
66
  "typescript-eslint": "^8.52.0",
67
67
  "vite": "^7.3.1",
68
68
  "vite-plugin-dts": "^4.5.4",
@@ -0,0 +1,182 @@
1
+ <script lang="ts" setup>
2
+ import { ref, computed, watch, nextTick, onMounted, onUnmounted } from 'vue'
3
+ import NexxtButton from '../Button/Button.vue'
4
+ import NexxtChip from '../Chip/Chip.vue'
5
+ import NexxtIcon from '../Icon/Icon.vue'
6
+ import NexxtInputField from '../InputField/InputField.vue'
7
+
8
+ interface NexxtComboboxProps {
9
+ variant?: InstanceType<typeof NexxtButton>['variant']
10
+ icon?: InstanceType<typeof NexxtIcon>['name']
11
+ placeholder?: string
12
+ placement?: 'left' | 'right'
13
+ options: (string | { label: string; value: string | number })[]
14
+ }
15
+
16
+ const props = defineProps<NexxtComboboxProps>()
17
+
18
+ const {
19
+ variant = 'secondary',
20
+ placement = 'right',
21
+ icon = 'tag',
22
+ placeholder = 'Zoeken...',
23
+ } = props
24
+
25
+ const selected = defineModel<(string | number)[]>({ default: [] })
26
+
27
+ const isOpen = ref(false)
28
+ const inputFieldRef = ref<InstanceType<typeof NexxtInputField> | null>(null)
29
+ const wrapperRef = ref<HTMLElement | null>(null)
30
+ const search = ref('')
31
+
32
+ const getLabel = (option: string | { label: string; value: string | number }) =>
33
+ typeof option === 'string' ? option : option.label
34
+
35
+ const getValue = (option: string | { label: string; value: string | number }) =>
36
+ typeof option === 'string' ? option : option.value
37
+
38
+ const selectedLabels = computed(() =>
39
+ selected.value.map((val) => {
40
+ const match = props.options.find((o) => getValue(o) === val)
41
+ return { value: val, label: match ? getLabel(match) : String(val) }
42
+ }),
43
+ )
44
+
45
+ const filteredOptions = computed(() => {
46
+ const q = search.value.toLowerCase()
47
+ return props.options
48
+ .filter((o) => !selected.value.includes(getValue(o)))
49
+ .filter((o) => getLabel(o).toLowerCase().includes(q))
50
+ })
51
+
52
+ const selectOption = (option: string | { label: string; value: string | number }) => {
53
+ selected.value = [...selected.value, getValue(option)]
54
+ search.value = ''
55
+ }
56
+
57
+ const removeSelected = (val: string | number) => {
58
+ selected.value = selected.value.filter((v) => v !== val)
59
+ }
60
+
61
+ watch(isOpen, async (open) => {
62
+ if (open) {
63
+ await nextTick()
64
+ inputFieldRef.value?.$el.querySelector('input')?.focus()
65
+ }
66
+ })
67
+
68
+ const handleClickOutside = (event: MouseEvent) => {
69
+ if (wrapperRef.value && !wrapperRef.value.contains(event.target as Node)) {
70
+ isOpen.value = false
71
+ }
72
+ }
73
+
74
+ const handleKeydown = (event: KeyboardEvent) => {
75
+ if (event.key === 'Escape' && isOpen.value) {
76
+ isOpen.value = false
77
+ }
78
+ }
79
+
80
+ onMounted(() => {
81
+ document.addEventListener('mousedown', handleClickOutside)
82
+ document.addEventListener('keydown', handleKeydown)
83
+ })
84
+ onUnmounted(() => {
85
+ document.removeEventListener('mousedown', handleClickOutside)
86
+ document.removeEventListener('keydown', handleKeydown)
87
+ })
88
+
89
+ const transitionClasses = computed(() => {
90
+ const origin = placement === 'right' ? 'origin-top-right' : 'origin-top-left'
91
+ return {
92
+ enterActiveClass: `transition-all duration-200 ease-out ${origin}`,
93
+ leaveActiveClass: `transition-all duration-150 ease-in ${origin}`,
94
+ enterFromClass: 'opacity-0 scale-95 -translate-y-3',
95
+ leaveToClass: 'opacity-0 scale-95 -translate-y-3',
96
+ enterToClass: 'opacity-100 scale-100 translate-y-0',
97
+ leaveFromClass: 'opacity-100 scale-100 translate-y-0',
98
+ }
99
+ })
100
+ </script>
101
+
102
+ <template>
103
+ <div ref="wrapperRef" class="relative">
104
+ <NexxtButton
105
+ :variant
106
+ :icon
107
+ :aria-expanded="isOpen"
108
+ aria-haspopup="listbox"
109
+ @click="isOpen = !isOpen"
110
+ >
111
+ <span class="flex gap-2"><slot>Combobox</slot><NexxtIcon name="chevron-down" /> </span>
112
+ </NexxtButton>
113
+ <Transition v-bind="transitionClasses">
114
+ <div
115
+ v-if="isOpen"
116
+ class="absolute top-full z-10 mt-2.5 min-w-80 rounded-md border border-gray-200 bg-white whitespace-nowrap"
117
+ :class="placement === 'right' ? 'right-0' : 'left-0'"
118
+ >
119
+ <div class="p-2">
120
+ <NexxtInputField
121
+ ref="inputFieldRef"
122
+ v-model="search"
123
+ class="w-full"
124
+ :left-icon="icon"
125
+ :placeholder
126
+ />
127
+ </div>
128
+ <Transition
129
+ enter-active-class="transition-all duration-200 ease-out overflow-hidden"
130
+ enter-from-class="max-h-0 opacity-0"
131
+ enter-to-class="max-h-40 opacity-100"
132
+ leave-active-class="transition-all duration-150 ease-in overflow-hidden"
133
+ leave-from-class="max-h-40 opacity-100"
134
+ leave-to-class="max-h-0 opacity-0"
135
+ >
136
+ <TransitionGroup
137
+ v-if="selectedLabels.length > 0"
138
+ tag="div"
139
+ class="flex flex-wrap gap-1 border-b border-gray-200 p-2 pt-0"
140
+ enter-active-class="transition-all duration-150 ease-out"
141
+ enter-from-class="opacity-0 scale-75"
142
+ enter-to-class="opacity-100 scale-100"
143
+ leave-active-class="transition-all duration-100 ease-in"
144
+ leave-from-class="opacity-100 scale-100"
145
+ leave-to-class="opacity-0 scale-75"
146
+ >
147
+ <button
148
+ v-for="item in selectedLabels"
149
+ :key="item.value"
150
+ class="origin-left cursor-pointer"
151
+ :aria-label="`Verwijder ${item.label}`"
152
+ @click="removeSelected(item.value)"
153
+ >
154
+ <NexxtChip variant="ghost">
155
+ {{ item.label }}
156
+ <span>
157
+ <NexxtIcon name="xmark" class="text-xs" aria-hidden="true" />
158
+ </span>
159
+ </NexxtChip>
160
+ </button>
161
+ </TransitionGroup>
162
+ </Transition>
163
+ <ul
164
+ role="listbox"
165
+ aria-multiselectable="true"
166
+ class="max-h-56 overflow-y-auto [&::-webkit-scrollbar]:w-1.5 [&::-webkit-scrollbar-thumb]:rounded-full [&::-webkit-scrollbar-thumb]:bg-cornflower-blue-200 [&::-webkit-scrollbar-thumb:hover]:bg-gray-300 [&::-webkit-scrollbar-track]:bg-transparent"
167
+ >
168
+ <li
169
+ v-for="option in filteredOptions"
170
+ :key="getValue(option)"
171
+ role="option"
172
+ aria-selected="false"
173
+ class="flex w-full min-w-42 cursor-pointer items-center gap-2 px-6 py-3 text-left text-sm whitespace-nowrap text-gray-700 transition-colors duration-200 hover:bg-gray-100 hover:text-purple-600 focus:bg-gray-100 focus:outline-none"
174
+ @click="selectOption(option)"
175
+ >
176
+ {{ getLabel(option) }}
177
+ </li>
178
+ </ul>
179
+ </div>
180
+ </Transition>
181
+ </div>
182
+ </template>
@@ -5,6 +5,7 @@
5
5
  "NexxtCalendar": "components/Calendar/Calendar.vue",
6
6
  "NexxtCheckbox": "components/Checkbox/Checkbox.vue",
7
7
  "NexxtChip": "components/Chip/Chip.vue",
8
+ "NexxtCombobox": "components/Combobox/Combobox.vue",
8
9
  "NexxtCustomerJourneyTile": "components/CustomerJourneyTile/CustomerJourneyTile.vue",
9
10
  "NexxtDatePicker": "components/DatePicker/DatePicker.vue",
10
11
  "NexxtDropdownButton": "components/DropdownButton/DropdownButton.vue",
package/src/index.ts CHANGED
@@ -5,6 +5,7 @@ export { default as NexxtButton } from './components/Button/Button.vue'
5
5
  export { default as NexxtCalendar } from './components/Calendar/Calendar.vue'
6
6
  export { default as NexxtCheckbox } from './components/Checkbox/Checkbox.vue'
7
7
  export { default as NexxtChip } from './components/Chip/Chip.vue'
8
+ export { default as NexxtCombobox } from './components/Combobox/Combobox.vue'
8
9
  export { default as NexxtCustomerJourneyTile } from './components/CustomerJourneyTile/CustomerJourneyTile.vue'
9
10
  export { default as NexxtDatePicker } from './components/DatePicker/DatePicker.vue'
10
11
  export { default as NexxtDropdownButton } from './components/DropdownButton/DropdownButton.vue'