@nexxtmove/ui 0.1.45 → 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/index.d.ts +3 -0
- package/dist/index.js +1551 -1382
- package/dist/nuxt.js +25 -24
- package/package.json +9 -9
- package/src/components/Combobox/Combobox.vue +182 -0
- package/src/components/DropdownButton/DropdownButton.vue +24 -5
- package/src/components/Table/Table.vue +126 -129
- package/src/components.json +1 -0
- package/src/index.ts +1 -0
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",
|
|
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:
|
|
5
|
-
NexxtButton:
|
|
4
|
+
NexxtAvatar: x,
|
|
5
|
+
NexxtButton: m,
|
|
6
6
|
NexxtCalendar: u,
|
|
7
7
|
NexxtCheckbox: p,
|
|
8
8
|
NexxtChip: l,
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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:
|
|
17
|
-
NexxtPagination:
|
|
18
|
-
NexxtProgressBar:
|
|
19
|
-
NexxtSocialIcons:
|
|
20
|
-
NexxtSocialMediaCustomTemplate:
|
|
21
|
-
NexxtSocialMediaTemplate:
|
|
22
|
-
NexxtSocialMediaType:
|
|
23
|
-
NexxtStepperHeader:
|
|
24
|
-
NexxtTable:
|
|
25
|
-
NexxtTimelineEvent:
|
|
26
|
-
NexxtTimelinePhaseblock:
|
|
27
|
-
NexxtTooltip:
|
|
28
|
-
},
|
|
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(
|
|
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
|
-
|
|
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.
|
|
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.
|
|
38
|
-
"@storybook/addon-designs": "^11.1.
|
|
39
|
-
"@storybook/addon-docs": "^10.3.
|
|
40
|
-
"@storybook/addon-vitest": "^10.3.
|
|
41
|
-
"@storybook/builder-vite": "^10.3.
|
|
42
|
-
"@storybook/vue3-vite": "^10.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.
|
|
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.
|
|
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>
|
|
@@ -46,7 +46,26 @@ const emit = defineEmits<{
|
|
|
46
46
|
|
|
47
47
|
const isOpen = ref(false)
|
|
48
48
|
const wrapperRef = ref<HTMLElement | null>(null)
|
|
49
|
-
const
|
|
49
|
+
const flatItems = computed<NexxtDropdownButton[]>(() =>
|
|
50
|
+
options.length > 0 && 'items' in options[0]
|
|
51
|
+
? (options as NexxtDropdownButtonGroup[]).flatMap((g) => g.items)
|
|
52
|
+
: (options as NexxtDropdownButton[]),
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
const toggledItems = ref<Record<string, boolean>>({})
|
|
56
|
+
|
|
57
|
+
const checkedItems = computed(() =>
|
|
58
|
+
Object.fromEntries(
|
|
59
|
+
flatItems.value
|
|
60
|
+
.filter((item) => item.checkbox)
|
|
61
|
+
.map((item) => [
|
|
62
|
+
item.label,
|
|
63
|
+
item.label in toggledItems.value
|
|
64
|
+
? toggledItems.value[item.label]
|
|
65
|
+
: (item.selected ?? false),
|
|
66
|
+
]),
|
|
67
|
+
),
|
|
68
|
+
)
|
|
50
69
|
|
|
51
70
|
const handleAction = (action: string | (() => void)) => {
|
|
52
71
|
if (typeof action === 'function') {
|
|
@@ -63,7 +82,7 @@ const toggleDropdown = () => {
|
|
|
63
82
|
const handleOptionClick = (option: NexxtDropdownButton) => {
|
|
64
83
|
if (option.checkbox) {
|
|
65
84
|
const newValue = !checkedItems.value[option.label]
|
|
66
|
-
|
|
85
|
+
toggledItems.value[option.label] = newValue
|
|
67
86
|
emit('checkboxChange', option.label, newValue)
|
|
68
87
|
} else {
|
|
69
88
|
isOpen.value = false
|
|
@@ -181,8 +200,8 @@ const transitionClasses = computed(() => {
|
|
|
181
200
|
<template v-for="(group, groupIndex) in groupedOptions" :key="group.title || groupIndex">
|
|
182
201
|
<div
|
|
183
202
|
v-if="group.title"
|
|
184
|
-
class="border-b border-gray-100 px-
|
|
185
|
-
:class="groupIndex > 0 ? '
|
|
203
|
+
class="border-b border-gray-100 px-6 py-3 extra-small-normal text-gray-600"
|
|
204
|
+
:class="groupIndex > 0 ? 'border-t pt-2' : ''"
|
|
186
205
|
>
|
|
187
206
|
{{ group.title }}
|
|
188
207
|
</div>
|
|
@@ -191,7 +210,7 @@ const transitionClasses = computed(() => {
|
|
|
191
210
|
:key="option.label"
|
|
192
211
|
:role="option.checkbox ? 'menuitemcheckbox' : 'menuitem'"
|
|
193
212
|
:aria-checked="option.checkbox ? (checkedItems[option.label] ?? false) : undefined"
|
|
194
|
-
class="flex w-full min-w-42 cursor-pointer items-center gap-2 px-
|
|
213
|
+
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"
|
|
195
214
|
:class="!option.checkbox && option.selected ? 'bg-cornflower-blue-100' : ''"
|
|
196
215
|
@click="handleOptionClick(option)"
|
|
197
216
|
>
|
|
@@ -113,148 +113,145 @@ const handleRowKeydown = (event: KeyboardEvent, rowIndex: number) => {
|
|
|
113
113
|
|
|
114
114
|
<template>
|
|
115
115
|
<div class="flex flex-col gap-3">
|
|
116
|
-
<div
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
>
|
|
123
|
-
<div
|
|
124
|
-
<
|
|
125
|
-
|
|
116
|
+
<div
|
|
117
|
+
role="grid"
|
|
118
|
+
:aria-colcount="columns.length + (selectable ? 1 : 0)"
|
|
119
|
+
:aria-rowcount="rows.length + 1"
|
|
120
|
+
class="min-w-full"
|
|
121
|
+
>
|
|
122
|
+
<div class="sticky top-0 z-10 mb-0.5">
|
|
123
|
+
<div v-if="$slots.toolbar">
|
|
124
|
+
<slot name="toolbar" />
|
|
125
|
+
</div>
|
|
126
|
+
<div
|
|
127
|
+
class="grid rounded-md bg-gray-50"
|
|
128
|
+
:style="{ gridTemplateColumns: gridTemplate }"
|
|
129
|
+
role="row"
|
|
130
|
+
aria-rowindex="1"
|
|
131
|
+
>
|
|
132
|
+
<div
|
|
133
|
+
v-if="selectable"
|
|
134
|
+
class="flex items-center justify-center px-2 py-3"
|
|
135
|
+
role="columnheader"
|
|
136
|
+
aria-colindex="1"
|
|
137
|
+
>
|
|
138
|
+
<Checkbox
|
|
139
|
+
:model-value="allSelected"
|
|
140
|
+
:indeterminate="someSelected"
|
|
141
|
+
aria-label="Select all rows"
|
|
142
|
+
@update:model-value="toggleAll"
|
|
143
|
+
/>
|
|
126
144
|
</div>
|
|
127
145
|
<div
|
|
128
|
-
|
|
129
|
-
:
|
|
130
|
-
|
|
131
|
-
|
|
146
|
+
v-for="(column, colIndex) in columns"
|
|
147
|
+
:key="column.key"
|
|
148
|
+
class="flex items-center gap-1.5 px-4 py-3 heading-4 text-slate-700"
|
|
149
|
+
:class="{
|
|
150
|
+
'cursor-pointer select-none hover:text-slate-900 focus-visible:ring-2 focus-visible:ring-cornflower-blue-500 focus-visible:outline-none focus-visible:ring-inset':
|
|
151
|
+
column.sortable,
|
|
152
|
+
}"
|
|
153
|
+
role="columnheader"
|
|
154
|
+
:aria-colindex="colIndex + (selectable ? 2 : 1)"
|
|
155
|
+
:aria-sort="
|
|
156
|
+
column.sortable
|
|
157
|
+
? sortKey === column.key
|
|
158
|
+
? sortDirection === 'asc'
|
|
159
|
+
? 'ascending'
|
|
160
|
+
: 'descending'
|
|
161
|
+
: 'none'
|
|
162
|
+
: undefined
|
|
163
|
+
"
|
|
164
|
+
:tabindex="column.sortable ? 0 : undefined"
|
|
165
|
+
@click="handleSort(column)"
|
|
166
|
+
@keydown.enter.prevent="handleSort(column)"
|
|
167
|
+
@keydown.space.prevent="handleSort(column)"
|
|
132
168
|
>
|
|
133
|
-
<
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
/>
|
|
145
|
-
</div>
|
|
146
|
-
<div
|
|
147
|
-
v-for="(column, colIndex) in columns"
|
|
148
|
-
:key="column.key"
|
|
149
|
-
class="flex items-center gap-1.5 px-4 py-3 heading-4 text-slate-700"
|
|
150
|
-
:class="{
|
|
151
|
-
'cursor-pointer select-none hover:text-slate-900 focus-visible:ring-2 focus-visible:ring-cornflower-blue-500 focus-visible:outline-none focus-visible:ring-inset':
|
|
152
|
-
column.sortable,
|
|
153
|
-
}"
|
|
154
|
-
role="columnheader"
|
|
155
|
-
:aria-colindex="colIndex + (selectable ? 2 : 1)"
|
|
156
|
-
:aria-sort="
|
|
157
|
-
column.sortable
|
|
158
|
-
? sortKey === column.key
|
|
159
|
-
? sortDirection === 'asc'
|
|
160
|
-
? 'ascending'
|
|
161
|
-
: 'descending'
|
|
162
|
-
: 'none'
|
|
163
|
-
: undefined
|
|
169
|
+
<span :class="sortKey === column.key ? 'text-cornflower-blue-600' : ''">{{
|
|
170
|
+
column.label
|
|
171
|
+
}}</span>
|
|
172
|
+
<Icon
|
|
173
|
+
v-if="column.sortable"
|
|
174
|
+
:name="
|
|
175
|
+
sortKey === column.key
|
|
176
|
+
? sortDirection === 'asc'
|
|
177
|
+
? 'angle-down'
|
|
178
|
+
: 'angle-up'
|
|
179
|
+
: 'angles-up-down'
|
|
164
180
|
"
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
@keydown.space.prevent="handleSort(column)"
|
|
169
|
-
>
|
|
170
|
-
<span :class="sortKey === column.key ? 'text-cornflower-blue-600' : ''">{{
|
|
171
|
-
column.label
|
|
172
|
-
}}</span>
|
|
173
|
-
<Icon
|
|
174
|
-
v-if="column.sortable"
|
|
175
|
-
:name="
|
|
176
|
-
sortKey === column.key
|
|
177
|
-
? sortDirection === 'asc'
|
|
178
|
-
? 'angle-down'
|
|
179
|
-
: 'angle-up'
|
|
180
|
-
: 'angles-up-down'
|
|
181
|
-
"
|
|
182
|
-
class="shrink-0 text-xs"
|
|
183
|
-
:class="sortKey === column.key ? 'text-cornflower-blue-600' : 'text-slate-400'"
|
|
184
|
-
/>
|
|
185
|
-
</div>
|
|
181
|
+
class="shrink-0 text-xs"
|
|
182
|
+
:class="sortKey === column.key ? 'text-cornflower-blue-600' : 'text-slate-400'"
|
|
183
|
+
/>
|
|
186
184
|
</div>
|
|
187
185
|
</div>
|
|
186
|
+
</div>
|
|
188
187
|
|
|
189
|
-
|
|
188
|
+
<div role="rowgroup" class="relative">
|
|
189
|
+
<div
|
|
190
|
+
v-for="(row, rowIndex) in rows"
|
|
191
|
+
:key="rowIndex"
|
|
192
|
+
ref="rowRefs"
|
|
193
|
+
class="grid border-b border-gray-100 transition-colors duration-150 hover:bg-cornflower-blue-50 focus-visible:bg-slate-50 focus-visible:ring-2 focus-visible:ring-cornflower-blue-500 focus-visible:outline-none focus-visible:ring-inset"
|
|
194
|
+
:class="[
|
|
195
|
+
{
|
|
196
|
+
'hover:rounded-md': !isRowSelected(rows[rowIndex]),
|
|
197
|
+
'border-white/80 bg-cornflower-blue-100': selectable && isRowSelected(rows[rowIndex]),
|
|
198
|
+
'border-transparent': selectable && isRowSelected(rows[rowIndex + 1]),
|
|
199
|
+
},
|
|
200
|
+
getRowRoundedClass(rowIndex),
|
|
201
|
+
]"
|
|
202
|
+
:style="{ gridTemplateColumns: gridTemplate }"
|
|
203
|
+
role="row"
|
|
204
|
+
:aria-rowindex="rowIndex + 2"
|
|
205
|
+
:aria-selected="selectable ? isRowSelected(row) : undefined"
|
|
206
|
+
tabindex="0"
|
|
207
|
+
@click="handleRowClick($event, rowIndex)"
|
|
208
|
+
@keydown="handleRowKeydown($event, rowIndex)"
|
|
209
|
+
>
|
|
190
210
|
<div
|
|
191
|
-
v-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
:class="[
|
|
196
|
-
{
|
|
197
|
-
'hover:rounded-md': !isRowSelected(rows[rowIndex]),
|
|
198
|
-
'border-white/80 bg-cornflower-blue-100':
|
|
199
|
-
selectable && isRowSelected(rows[rowIndex]),
|
|
200
|
-
'border-transparent': selectable && isRowSelected(rows[rowIndex + 1]),
|
|
201
|
-
},
|
|
202
|
-
getRowRoundedClass(rowIndex),
|
|
203
|
-
]"
|
|
204
|
-
:style="{ gridTemplateColumns: gridTemplate }"
|
|
205
|
-
role="row"
|
|
206
|
-
:aria-rowindex="rowIndex + 2"
|
|
207
|
-
:aria-selected="selectable ? isRowSelected(row) : undefined"
|
|
208
|
-
tabindex="0"
|
|
209
|
-
@click="handleRowClick($event, rowIndex)"
|
|
210
|
-
@keydown="handleRowKeydown($event, rowIndex)"
|
|
211
|
+
v-if="selectable"
|
|
212
|
+
class="flex items-center justify-center px-2 py-4"
|
|
213
|
+
role="gridcell"
|
|
214
|
+
:aria-colindex="1"
|
|
211
215
|
>
|
|
212
|
-
<
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
>
|
|
218
|
-
<Checkbox
|
|
219
|
-
:model-value="isRowSelected(row)"
|
|
220
|
-
:aria-label="`Select row ${rowIndex + 1}`"
|
|
221
|
-
@update:model-value="toggleRow(row)"
|
|
222
|
-
/>
|
|
223
|
-
</div>
|
|
224
|
-
<div
|
|
225
|
-
v-for="(column, colIndex) in columns"
|
|
226
|
-
:key="column.key"
|
|
227
|
-
class="flex items-center truncate px-4 py-4 extra-small-normal text-slate-700"
|
|
228
|
-
role="gridcell"
|
|
229
|
-
:aria-colindex="colIndex + (selectable ? 2 : 1)"
|
|
230
|
-
>
|
|
231
|
-
<slot :name="cellSlotName(column.key)" :row="row" :value="rowValue(row, column.key)">
|
|
232
|
-
{{ rowValue(row, column.key) }}
|
|
233
|
-
</slot>
|
|
234
|
-
</div>
|
|
216
|
+
<Checkbox
|
|
217
|
+
:model-value="isRowSelected(row)"
|
|
218
|
+
:aria-label="`Select row ${rowIndex + 1}`"
|
|
219
|
+
@update:model-value="toggleRow(row)"
|
|
220
|
+
/>
|
|
235
221
|
</div>
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
222
|
+
<div
|
|
223
|
+
v-for="(column, colIndex) in columns"
|
|
224
|
+
:key="column.key"
|
|
225
|
+
class="flex items-center truncate px-4 py-4 extra-small-normal text-slate-700"
|
|
226
|
+
role="gridcell"
|
|
227
|
+
:aria-colindex="colIndex + (selectable ? 2 : 1)"
|
|
228
|
+
>
|
|
229
|
+
<slot :name="cellSlotName(column.key)" :row="row" :value="rowValue(row, column.key)">
|
|
230
|
+
{{ rowValue(row, column.key) }}
|
|
231
|
+
</slot>
|
|
239
232
|
</div>
|
|
233
|
+
</div>
|
|
240
234
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
leave-active-class="transition-opacity duration-200"
|
|
244
|
-
enter-from-class="opacity-0"
|
|
245
|
-
leave-to-class="opacity-0"
|
|
246
|
-
>
|
|
247
|
-
<div
|
|
248
|
-
v-if="loading"
|
|
249
|
-
data-testid="loading-overlay"
|
|
250
|
-
class="absolute inset-0 flex items-center justify-center rounded-md bg-white/40 backdrop-blur-sm"
|
|
251
|
-
>
|
|
252
|
-
<slot name="loading"
|
|
253
|
-
><Icon name="spinner-third" class="fa-spin text-3xl text-purple-500"
|
|
254
|
-
/></slot>
|
|
255
|
-
</div>
|
|
256
|
-
</Transition>
|
|
235
|
+
<div v-if="!rows.length" class="px-4 py-8 text-center text-sm text-slate-400" role="row">
|
|
236
|
+
<slot name="empty">Geen data beschikbaar</slot>
|
|
257
237
|
</div>
|
|
238
|
+
|
|
239
|
+
<Transition
|
|
240
|
+
enter-active-class="transition-opacity duration-200"
|
|
241
|
+
leave-active-class="transition-opacity duration-200"
|
|
242
|
+
enter-from-class="opacity-0"
|
|
243
|
+
leave-to-class="opacity-0"
|
|
244
|
+
>
|
|
245
|
+
<div
|
|
246
|
+
v-if="loading"
|
|
247
|
+
data-testid="loading-overlay"
|
|
248
|
+
class="absolute inset-0 flex items-center justify-center rounded-md bg-white/40 backdrop-blur-sm"
|
|
249
|
+
>
|
|
250
|
+
<slot name="loading"
|
|
251
|
+
><Icon name="spinner-third" class="fa-spin text-3xl text-purple-500"
|
|
252
|
+
/></slot>
|
|
253
|
+
</div>
|
|
254
|
+
</Transition>
|
|
258
255
|
</div>
|
|
259
256
|
</div>
|
|
260
257
|
</div>
|
package/src/components.json
CHANGED
|
@@ -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'
|