@nexxtmove/ui 1.2.4 → 1.2.6
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/components/Header/Header.vue.d.ts +7 -4
- package/dist/components/StepperHeader/StepperHeader.vue.d.ts +8 -3
- package/dist/components/TimelineEvent/TimelineTypes.d.ts +16 -9
- package/dist/components/TimelineLanes/TimelineLanes.vue.d.ts +16 -0
- package/dist/components/TimelineTrack/TimelineTrack.vue.d.ts +36 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +1041 -884
- package/dist/nuxt.js +3 -1
- package/package.json +9 -9
- package/src/components/Calendar/_CalendarYearView.vue +1 -2
- package/src/components/ComboboxPanel/ComboboxPanel.vue +2 -6
- package/src/components/CountrySelect/CountrySelect.vue +1 -4
- package/src/components/Header/Header.vue +8 -2
- package/src/components/ProgressBar/ProgressBar.vue +67 -13
- package/src/components/StepperHeader/StepperHeader.vue +8 -1
- package/src/components/TimelineEvent/TimelineEvent.vue +13 -2
- package/src/components/TimelineEvent/TimelineTypes.ts +41 -9
- package/src/components/TimelineLanes/TimelineLanes.vue +50 -0
- package/src/components/TimelinePhaseblock/TimelinePhaseblock.vue +2 -21
- package/src/components/TimelineTrack/TimelineTrack.vue +67 -0
- package/src/components.json +2 -0
- package/src/index.ts +10 -0
- package/src/nuxt.ts +6 -3
- package/tailwind/nexxtmove.tailwind.custom.css +39 -0
- package/tailwind/theme.css +10 -0
package/dist/nuxt.js
CHANGED
|
@@ -47613,7 +47613,9 @@ var Xw, Zw, Qw, $w, eT, tT = f((() => {
|
|
|
47613
47613
|
NexxtTabs: "components/Tabs/Tabs.vue",
|
|
47614
47614
|
NexxtTemplateCard: "components/TemplateCard/TemplateCard.vue",
|
|
47615
47615
|
NexxtTimelineEvent: "components/TimelineEvent/TimelineEvent.vue",
|
|
47616
|
+
NexxtTimelineLanes: "components/TimelineLanes/TimelineLanes.vue",
|
|
47616
47617
|
NexxtTimelinePhaseblock: "components/TimelinePhaseblock/TimelinePhaseblock.vue",
|
|
47618
|
+
NexxtTimelineTrack: "components/TimelineTrack/TimelineTrack.vue",
|
|
47617
47619
|
NexxtToast: "components/Toast/Toast.vue",
|
|
47618
47620
|
NexxtToaster: "components/Toaster/Toaster.vue",
|
|
47619
47621
|
NexxtToggleGroup: "components/ToggleGroup/ToggleGroup.vue",
|
|
@@ -47645,7 +47647,7 @@ var Xw, Zw, Qw, $w, eT, tT = f((() => {
|
|
|
47645
47647
|
getContents: () => `
|
|
47646
47648
|
@import "tailwindcss";
|
|
47647
47649
|
@import "@nexxtmove/ui/theme";
|
|
47648
|
-
@source "${e}/**/*.vue";
|
|
47650
|
+
@source "${e}/**/*.{vue,ts}";
|
|
47649
47651
|
`,
|
|
47650
47652
|
write: !0
|
|
47651
47653
|
}), o.options.css.push("#build/nexxtmove-ui.css");
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nexxtmove/ui",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.2.
|
|
4
|
+
"version": "1.2.6",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"./nuxt": "./dist/nuxt.js",
|
|
11
11
|
"./ui.css": "./dist/ui.css",
|
|
12
12
|
"./dist/ui.css": "./dist/ui.css",
|
|
13
|
-
"./theme": "./tailwind/
|
|
13
|
+
"./theme": "./tailwind/theme.css",
|
|
14
14
|
"./components.json": "./src/components.json",
|
|
15
15
|
"./components/*": "./src/components/*"
|
|
16
16
|
},
|
|
@@ -36,12 +36,12 @@
|
|
|
36
36
|
"@eslint/js": "^10.0.1",
|
|
37
37
|
"@nuxt/kit": "^4.5.2",
|
|
38
38
|
"@nuxt/schema": "^4.5.2",
|
|
39
|
-
"@storybook/addon-a11y": "^10.5.
|
|
39
|
+
"@storybook/addon-a11y": "^10.5.10",
|
|
40
40
|
"@storybook/addon-designs": "^11.1.4",
|
|
41
|
-
"@storybook/addon-docs": "^10.5.
|
|
42
|
-
"@storybook/addon-vitest": "^10.5.
|
|
43
|
-
"@storybook/builder-vite": "^10.5.
|
|
44
|
-
"@storybook/vue3-vite": "^10.5.
|
|
41
|
+
"@storybook/addon-docs": "^10.5.10",
|
|
42
|
+
"@storybook/addon-vitest": "^10.5.10",
|
|
43
|
+
"@storybook/builder-vite": "^10.5.10",
|
|
44
|
+
"@storybook/vue3-vite": "^10.5.10",
|
|
45
45
|
"@tailwindcss/vite": "^4.3.3",
|
|
46
46
|
"@types/node": "^26.2.0",
|
|
47
47
|
"@types/react": "^19.2.18",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"eslint": "^10.8.1",
|
|
56
56
|
"eslint-config-prettier": "^10.1.8",
|
|
57
57
|
"eslint-plugin-prettier": "^5.5.6",
|
|
58
|
-
"eslint-plugin-storybook": "10.5.
|
|
58
|
+
"eslint-plugin-storybook": "10.5.10",
|
|
59
59
|
"eslint-plugin-vue": "^10.10.0",
|
|
60
60
|
"flag-icons": "^7.5.0",
|
|
61
61
|
"globals": "^17.11.0",
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
"react": "^19.2.8",
|
|
66
66
|
"react-dom": "^19.2.8",
|
|
67
67
|
"remark-frontmatter": "^5.0.0",
|
|
68
|
-
"storybook": "^10.5.
|
|
68
|
+
"storybook": "^10.5.10",
|
|
69
69
|
"typescript-eslint": "^8.67.0",
|
|
70
70
|
"vite": "^8.2.1",
|
|
71
71
|
"vite-plugin-dts": "^5.0.3",
|
|
@@ -58,8 +58,7 @@ onMounted(async () => {
|
|
|
58
58
|
<!-- Scrollable drum -->
|
|
59
59
|
<div
|
|
60
60
|
ref="yearScrollerRef"
|
|
61
|
-
class="relative h-full snap-y snap-mandatory overflow-y-scroll
|
|
62
|
-
style="scrollbar-width: none"
|
|
61
|
+
class="relative scrollbar-none h-full snap-y snap-mandatory overflow-y-scroll"
|
|
63
62
|
>
|
|
64
63
|
<div class="h-18" />
|
|
65
64
|
<button
|
|
@@ -208,7 +208,7 @@ defineExpose({ focus })
|
|
|
208
208
|
role="listbox"
|
|
209
209
|
aria-multiselectable="true"
|
|
210
210
|
tabindex="0"
|
|
211
|
-
class="max-h-56 overflow-y-auto
|
|
211
|
+
class="scrollbar-thin max-h-56 overflow-y-auto"
|
|
212
212
|
>
|
|
213
213
|
<li
|
|
214
214
|
v-for="option in filteredOptions"
|
|
@@ -298,11 +298,7 @@ defineExpose({ focus })
|
|
|
298
298
|
</button>
|
|
299
299
|
</TransitionGroup>
|
|
300
300
|
</Transition>
|
|
301
|
-
<ul
|
|
302
|
-
role="listbox"
|
|
303
|
-
aria-multiselectable="true"
|
|
304
|
-
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"
|
|
305
|
-
>
|
|
301
|
+
<ul role="listbox" aria-multiselectable="true" class="scrollbar-thin max-h-56 overflow-y-auto">
|
|
306
302
|
<li
|
|
307
303
|
v-for="option in filteredOptions"
|
|
308
304
|
:key="getValue(option)"
|
|
@@ -125,10 +125,7 @@ const onOpen = async () => {
|
|
|
125
125
|
placeholder="Zoeken..."
|
|
126
126
|
/>
|
|
127
127
|
</div>
|
|
128
|
-
<ul
|
|
129
|
-
role="listbox"
|
|
130
|
-
class="max-h-60 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"
|
|
131
|
-
>
|
|
128
|
+
<ul role="listbox" class="scrollbar-thin max-h-60 overflow-y-auto">
|
|
132
129
|
<li
|
|
133
130
|
v-for="country in filteredCountries"
|
|
134
131
|
:key="country.code"
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
import Button from '../Button/Button.vue'
|
|
3
3
|
|
|
4
4
|
interface NexxtHeaderProps {
|
|
5
|
-
title
|
|
5
|
+
/** Optional when the title slot is used. */
|
|
6
|
+
title?: string
|
|
6
7
|
headingType?: 'heading-1' | 'heading-2' | 'heading-3' | 'heading-4'
|
|
7
8
|
backButtonText?: string
|
|
8
9
|
backButtonAction?: () => void
|
|
@@ -37,7 +38,12 @@ const {
|
|
|
37
38
|
{{ backButtonText }}
|
|
38
39
|
</Button>
|
|
39
40
|
</slot>
|
|
40
|
-
|
|
41
|
+
<!-- The title slot replaces the static title, keeping the same flex-1 and
|
|
42
|
+
min-w-0 sizing so a centered slot stays centered. -->
|
|
43
|
+
<div v-if="$slots.title" class="flex min-w-0 flex-1 items-center">
|
|
44
|
+
<slot name="title" />
|
|
45
|
+
</div>
|
|
46
|
+
<span v-else class="min-w-0 flex-1 truncate text-sapphire-500" :class="headingType">
|
|
41
47
|
{{ title }}
|
|
42
48
|
</span>
|
|
43
49
|
</div>
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { computed, watchEffect } from 'vue'
|
|
3
3
|
import AnimatedNumber from '../AnimatedNumber/AnimatedNumber.vue'
|
|
4
|
+
import Icon from '../Icon/Icon.vue'
|
|
5
|
+
import FloatingPanel from '../FloatingPanel/FloatingPanel.vue'
|
|
4
6
|
|
|
5
7
|
interface NexxtProgressBarProps {
|
|
6
8
|
variant?: 'stepper' | 'percentage'
|
|
@@ -15,7 +17,7 @@ defineOptions({
|
|
|
15
17
|
name: 'NexxtProgressBar',
|
|
16
18
|
})
|
|
17
19
|
|
|
18
|
-
defineEmits(['update:currentStep'])
|
|
20
|
+
const emit = defineEmits(['update:currentStep'])
|
|
19
21
|
|
|
20
22
|
const {
|
|
21
23
|
variant = 'stepper',
|
|
@@ -55,6 +57,11 @@ const stepDigits = computed(() => String(Math.max(steps, 0)).length)
|
|
|
55
57
|
// the compact treatment (current title + "x/y" counter) at every breakpoint.
|
|
56
58
|
const isCompact = computed(() => hasTitles.value && (stepTitles?.length ?? 0) > maxInlineSteps)
|
|
57
59
|
|
|
60
|
+
const selectStep = (index: number, close: () => void) => {
|
|
61
|
+
close()
|
|
62
|
+
if (index < clampedStep.value) emit('update:currentStep', index)
|
|
63
|
+
}
|
|
64
|
+
|
|
58
65
|
watchEffect(() => {
|
|
59
66
|
if (stepTitles && stepTitles.length !== steps) {
|
|
60
67
|
console.warn(
|
|
@@ -110,10 +117,64 @@ watchEffect(() => {
|
|
|
110
117
|
<span>{{ steps }}</span>
|
|
111
118
|
</div>
|
|
112
119
|
</div>
|
|
120
|
+
<div v-if="isCompact" class="order-first flex justify-center">
|
|
121
|
+
<FloatingPanel placement="bottom">
|
|
122
|
+
<template #trigger="{ isOpen, toggle }">
|
|
123
|
+
<button
|
|
124
|
+
type="button"
|
|
125
|
+
class="flex cursor-pointer items-center gap-1.5 rounded-xs body-normal text-sapphire-500 transition-colors duration-200 hover:text-sapphire-400 focus-visible:outline-3"
|
|
126
|
+
aria-haspopup="menu"
|
|
127
|
+
:aria-expanded="isOpen"
|
|
128
|
+
:aria-label="`Stap ${displayStep} van ${steps}: ${stepTitles?.[clampedStep]}. Kies een stap`"
|
|
129
|
+
@click="toggle"
|
|
130
|
+
>
|
|
131
|
+
{{ stepTitles?.[clampedStep] }}
|
|
132
|
+
<Icon
|
|
133
|
+
name="chevron-down"
|
|
134
|
+
class="text-xs transition-transform duration-200"
|
|
135
|
+
:class="{ 'rotate-180': isOpen }"
|
|
136
|
+
aria-hidden="true"
|
|
137
|
+
/>
|
|
138
|
+
</button>
|
|
139
|
+
</template>
|
|
140
|
+
|
|
141
|
+
<template #content="{ close }">
|
|
142
|
+
<ol
|
|
143
|
+
role="menu"
|
|
144
|
+
class="scrollbar-thin max-h-72 min-w-52 overflow-y-auto rounded-lg border border-gray-200 bg-white py-1"
|
|
145
|
+
aria-label="Progress steps"
|
|
146
|
+
>
|
|
147
|
+
<li v-for="(title, index) in stepTitles" :key="title">
|
|
148
|
+
<button
|
|
149
|
+
type="button"
|
|
150
|
+
role="menuitem"
|
|
151
|
+
class="flex w-full items-center gap-2 p-3 text-left small-normal whitespace-nowrap transition-colors duration-200 focus:outline-none"
|
|
152
|
+
:class="
|
|
153
|
+
index > clampedStep
|
|
154
|
+
? 'cursor-default text-gray-400'
|
|
155
|
+
: 'cursor-pointer text-gray-700 hover:bg-gray-100 hover:text-sapphire-400 focus-visible:bg-gray-100'
|
|
156
|
+
"
|
|
157
|
+
:disabled="index > clampedStep"
|
|
158
|
+
:aria-current="index === clampedStep ? 'step' : undefined"
|
|
159
|
+
@click="selectStep(index, close)"
|
|
160
|
+
>
|
|
161
|
+
<span
|
|
162
|
+
class="w-(--step-number-width) shrink-0 text-right tabular-nums"
|
|
163
|
+
:style="{ '--step-number-width': `${stepDigits}ch` }"
|
|
164
|
+
aria-hidden="true"
|
|
165
|
+
>
|
|
166
|
+
{{ index + 1 }}
|
|
167
|
+
</span>
|
|
168
|
+
<span :class="index === clampedStep ? 'text-sapphire-500' : ''">{{ title }}</span>
|
|
169
|
+
</button>
|
|
170
|
+
</li>
|
|
171
|
+
</ol>
|
|
172
|
+
</template>
|
|
173
|
+
</FloatingPanel>
|
|
174
|
+
</div>
|
|
113
175
|
<ol
|
|
114
|
-
v-if="hasTitles"
|
|
115
|
-
class="flex @max-md:order-first"
|
|
116
|
-
:class="isCompact ? 'order-first justify-center' : 'justify-between @max-md:justify-center'"
|
|
176
|
+
v-else-if="hasTitles"
|
|
177
|
+
class="flex justify-between @max-md:order-first @max-md:justify-center"
|
|
117
178
|
aria-label="Progress steps"
|
|
118
179
|
>
|
|
119
180
|
<li
|
|
@@ -121,15 +182,8 @@ watchEffect(() => {
|
|
|
121
182
|
:key="title"
|
|
122
183
|
class="rounded-xs transition-colors duration-500 focus-visible:outline-3"
|
|
123
184
|
:class="[
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
? 'body-normal'
|
|
127
|
-
: 'hidden'
|
|
128
|
-
: [
|
|
129
|
-
'@max-md:body-normal @min-md:small-normal',
|
|
130
|
-
index === clampedStep ? '@max-md:inline' : '@max-md:hidden',
|
|
131
|
-
],
|
|
132
|
-
index === clampedStep ? 'text-sapphire-500' : '',
|
|
185
|
+
'@max-md:body-normal @min-md:small-normal',
|
|
186
|
+
index === clampedStep ? 'text-sapphire-500 @max-md:inline' : '@max-md:hidden',
|
|
133
187
|
index < clampedStep ? 'cursor-pointer duration-200! hover:text-sapphire-400' : '',
|
|
134
188
|
]"
|
|
135
189
|
:aria-current="index === clampedStep ? 'step' : undefined"
|
|
@@ -3,7 +3,8 @@ import Header from '../Header/Header.vue'
|
|
|
3
3
|
import ProgressBar from '../ProgressBar/ProgressBar.vue'
|
|
4
4
|
|
|
5
5
|
interface NexxtStepperHeaderProps {
|
|
6
|
-
title
|
|
6
|
+
/** Optional when the title slot is used. */
|
|
7
|
+
title?: string
|
|
7
8
|
currentStep: InstanceType<typeof ProgressBar>['currentStep']
|
|
8
9
|
steps: InstanceType<typeof ProgressBar>['stepTitles']
|
|
9
10
|
backButtonText?: InstanceType<typeof Header>['backButtonText']
|
|
@@ -21,6 +22,12 @@ defineProps<NexxtStepperHeaderProps>()
|
|
|
21
22
|
|
|
22
23
|
<template>
|
|
23
24
|
<Header :title="title" :back-button-action="backButtonAction" :back-button-text="backButtonText">
|
|
25
|
+
<template v-if="$slots['back-button']" #back-button>
|
|
26
|
+
<slot name="back-button" />
|
|
27
|
+
</template>
|
|
28
|
+
<template v-if="$slots.title" #title>
|
|
29
|
+
<slot name="title" />
|
|
30
|
+
</template>
|
|
24
31
|
<template v-if="steps" #center>
|
|
25
32
|
<ProgressBar
|
|
26
33
|
class="w-full"
|
|
@@ -19,8 +19,16 @@ const defaultEventConfig: EventConfig = {
|
|
|
19
19
|
color: 'bg-gray-500',
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
// Resolved per field so an event can override only its label (e.g. a dynamic
|
|
23
|
+
// "Status gewijzigd van X naar Y") and still take icon and color from the map.
|
|
24
|
+
const eventConfig = computed<EventConfig>(() => {
|
|
25
|
+
const mapped = EVENT_CONFIG_MAP[props.event.type]
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
label: props.event.label ?? mapped?.label ?? props.event.type,
|
|
29
|
+
icon: props.event.icon ?? mapped?.icon ?? defaultEventConfig.icon,
|
|
30
|
+
color: props.event.color ?? mapped?.color ?? defaultEventConfig.color,
|
|
31
|
+
}
|
|
24
32
|
})
|
|
25
33
|
|
|
26
34
|
const props = withDefaults(
|
|
@@ -102,6 +110,9 @@ const isContactEvent = computed(() => {
|
|
|
102
110
|
</span>
|
|
103
111
|
<NexxtIcon name="external-link" class="ml-1 h-4 w-4 text-gray-950" />
|
|
104
112
|
</a>
|
|
113
|
+
<span v-else-if="event.description" class="mt-1 text-sm text-gray-500">
|
|
114
|
+
{{ event.description }}
|
|
115
|
+
</span>
|
|
105
116
|
<span v-if="event.request_reason" class="mt-1 text-sm text-gray-500">
|
|
106
117
|
Reden: {{ event.request_reason }}
|
|
107
118
|
</span>
|
|
@@ -1,15 +1,22 @@
|
|
|
1
1
|
export interface TimelineEvent {
|
|
2
|
-
id: number
|
|
2
|
+
id: string | number
|
|
3
3
|
type: string
|
|
4
4
|
happened_at: string
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
5
|
+
/** Required when the event is rendered in split view ('outbound' | 'inbound'). */
|
|
6
|
+
direction?: string
|
|
7
|
+
medium_type?: string | null
|
|
8
|
+
url?: string | null
|
|
9
|
+
page_title?: string | null
|
|
10
|
+
request_reason?: string | null
|
|
11
|
+
agenda_item_type?: string | null
|
|
12
|
+
source?: string
|
|
13
|
+
description?: string | null
|
|
14
|
+
/** Overrides the label from EVENT_CONFIG_MAP. Use for dynamic labels. */
|
|
15
|
+
label?: string | null
|
|
16
|
+
/** Overrides the icon from EVENT_CONFIG_MAP. */
|
|
17
|
+
icon?: string | null
|
|
18
|
+
/** Overrides the background color class from EVENT_CONFIG_MAP. */
|
|
19
|
+
color?: string | null
|
|
13
20
|
}
|
|
14
21
|
|
|
15
22
|
export interface TimelinePhase {
|
|
@@ -137,9 +144,34 @@ export const EVENT_CONFIG_MAP: Record<string, { label: string; icon: string; col
|
|
|
137
144
|
icon: 'pen-fancy',
|
|
138
145
|
color: 'bg-emerald-500',
|
|
139
146
|
},
|
|
147
|
+
proposal_created: { label: 'Voorstel aangemaakt', icon: 'file-plus', color: 'bg-blue-500' },
|
|
148
|
+
proposal_scheduled: { label: 'Voorstel ingepland', icon: 'calendar-clock', color: 'bg-blue-400' },
|
|
140
149
|
proposal_sent: { label: 'Voorstel verzonden', icon: 'paper-plane-top', color: 'bg-blue-400' },
|
|
150
|
+
proposal_delivered: {
|
|
151
|
+
label: 'Voorstel afgeleverd',
|
|
152
|
+
icon: 'envelope-circle-check',
|
|
153
|
+
color: 'bg-blue-300',
|
|
154
|
+
},
|
|
155
|
+
// Mail geopend (read_at) is a different moment than the proposal itself being
|
|
156
|
+
// viewed (opened_at, proposal_opened below).
|
|
157
|
+
proposal_mail_opened: {
|
|
158
|
+
label: 'Mail geopend',
|
|
159
|
+
icon: 'envelope-open-text',
|
|
160
|
+
color: 'bg-blue-200',
|
|
161
|
+
},
|
|
141
162
|
proposal_opened: { label: 'Voorstel geopend', icon: 'envelope-open', color: 'bg-blue-300' },
|
|
142
163
|
proposal_accepted: { label: 'Voorstel geaccepteerd', icon: 'check', color: 'bg-emerald-500' },
|
|
164
|
+
proposal_send_failed: {
|
|
165
|
+
label: 'Verzenden mislukt',
|
|
166
|
+
icon: 'triangle-exclamation',
|
|
167
|
+
color: 'bg-red-500',
|
|
168
|
+
},
|
|
169
|
+
proposal_expired: { label: 'Voorstel verlopen', icon: 'clock-rotate-left', color: 'bg-gray-500' },
|
|
170
|
+
proposal_status_changed: {
|
|
171
|
+
label: 'Status gewijzigd',
|
|
172
|
+
icon: 'repeat',
|
|
173
|
+
color: 'bg-purple-500',
|
|
174
|
+
},
|
|
143
175
|
|
|
144
176
|
// Aftercare
|
|
145
177
|
final_inspection_agenda_item_scheduled: {
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import NexxtIcon from '../Icon/Icon.vue'
|
|
3
|
+
import NexxtTooltip from '../Tooltip/Tooltip.vue'
|
|
4
|
+
|
|
5
|
+
defineOptions({
|
|
6
|
+
name: 'NexxtTimelineLanes',
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
withDefaults(
|
|
10
|
+
defineProps<{
|
|
11
|
+
leftLabel?: string
|
|
12
|
+
rightLabel?: string
|
|
13
|
+
/** Tooltip next to the left label. Omit or pass an empty string to hide it. */
|
|
14
|
+
leftTooltip?: string
|
|
15
|
+
/** Tooltip next to the right label. Omit or pass an empty string to hide it. */
|
|
16
|
+
rightTooltip?: string
|
|
17
|
+
}>(),
|
|
18
|
+
{
|
|
19
|
+
leftLabel: 'Outbound',
|
|
20
|
+
rightLabel: 'Inbound',
|
|
21
|
+
leftTooltip: 'Vanuit Nexxtmove',
|
|
22
|
+
rightTooltip: 'Vanuit relatie',
|
|
23
|
+
},
|
|
24
|
+
)
|
|
25
|
+
</script>
|
|
26
|
+
|
|
27
|
+
<template>
|
|
28
|
+
<div class="relative z-10">
|
|
29
|
+
<div class="flex items-center justify-between px-4 py-2">
|
|
30
|
+
<span class="flex gap-1">
|
|
31
|
+
{{ leftLabel }}
|
|
32
|
+
<NexxtTooltip v-if="leftTooltip">
|
|
33
|
+
<NexxtIcon name="circle-info" class="h-4 w-4 text-xs text-gray-600" />
|
|
34
|
+
<template #tooltip
|
|
35
|
+
><span class="text-sm">{{ leftTooltip }}</span></template
|
|
36
|
+
>
|
|
37
|
+
</NexxtTooltip>
|
|
38
|
+
</span>
|
|
39
|
+
<span class="flex gap-1">
|
|
40
|
+
{{ rightLabel }}
|
|
41
|
+
<NexxtTooltip v-if="rightTooltip">
|
|
42
|
+
<NexxtIcon name="circle-info" class="h-4 w-4 text-xs text-gray-600" />
|
|
43
|
+
<template #tooltip
|
|
44
|
+
><span class="text-sm">{{ rightTooltip }}</span></template
|
|
45
|
+
>
|
|
46
|
+
</NexxtTooltip>
|
|
47
|
+
</span>
|
|
48
|
+
</div>
|
|
49
|
+
</div>
|
|
50
|
+
</template>
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import NexxtIcon from '../Icon/Icon.vue'
|
|
3
|
-
import NexxtTooltip from '../Tooltip/Tooltip.vue'
|
|
4
3
|
import { computed } from 'vue'
|
|
5
4
|
|
|
6
5
|
defineOptions({
|
|
7
6
|
name: 'NexxtTimelinePhaseblock',
|
|
8
7
|
})
|
|
9
8
|
import NexxtTimelineEvent from '../TimelineEvent/TimelineEvent.vue'
|
|
9
|
+
import NexxtTimelineLanes from '../TimelineLanes/TimelineLanes.vue'
|
|
10
10
|
import { PHASE_MAP, type TimelinePhase } from '../TimelineEvent/TimelineTypes'
|
|
11
11
|
|
|
12
12
|
const props = defineProps<{
|
|
@@ -69,26 +69,7 @@ const getPhaseDirection = () => {
|
|
|
69
69
|
|
|
70
70
|
<template>
|
|
71
71
|
<div class="relative z-10 flex flex-col gap-16" :data-phase="phase.phase.value">
|
|
72
|
-
<
|
|
73
|
-
<div class="relative z-10">
|
|
74
|
-
<div class="flex items-center justify-between px-4 py-2">
|
|
75
|
-
<span class="flex gap-1">
|
|
76
|
-
Outbound
|
|
77
|
-
<NexxtTooltip>
|
|
78
|
-
<NexxtIcon name="circle-info" class="h-4 w-4 text-xs text-gray-600" />
|
|
79
|
-
<template #tooltip><span class="text-sm">Vanuit Nexxtmove</span></template>
|
|
80
|
-
</NexxtTooltip>
|
|
81
|
-
</span>
|
|
82
|
-
<span class="flex gap-1">
|
|
83
|
-
Inbound
|
|
84
|
-
<NexxtTooltip as-child>
|
|
85
|
-
<NexxtIcon name="circle-info" class="h-4 w-4 text-xs text-gray-600" />
|
|
86
|
-
<template #tooltip><span class="text-sm">Vanuit relatie</span></template>
|
|
87
|
-
</NexxtTooltip>
|
|
88
|
-
</span>
|
|
89
|
-
</div>
|
|
90
|
-
</div>
|
|
91
|
-
</template>
|
|
72
|
+
<NexxtTimelineLanes v-if="index === 0 && isSplitView" />
|
|
92
73
|
<!-- Left timeline line (always shown) -->
|
|
93
74
|
<div
|
|
94
75
|
class="absolute bottom-0 left-5 w-0 border-l"
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed } from 'vue'
|
|
3
|
+
|
|
4
|
+
defineOptions({
|
|
5
|
+
name: 'NexxtTimelineTrack',
|
|
6
|
+
})
|
|
7
|
+
import NexxtTimelineEvent from '../TimelineEvent/TimelineEvent.vue'
|
|
8
|
+
import NexxtTimelineLanes from '../TimelineLanes/TimelineLanes.vue'
|
|
9
|
+
import type { TimelineEvent as Event } from '../TimelineEvent/TimelineTypes'
|
|
10
|
+
|
|
11
|
+
const props = withDefaults(
|
|
12
|
+
defineProps<{
|
|
13
|
+
/** Events, newest first. Ignored when the default slot is used. */
|
|
14
|
+
events?: Event[]
|
|
15
|
+
splitView?: boolean
|
|
16
|
+
/** Border color class for the vertical timeline lines. */
|
|
17
|
+
color?: string
|
|
18
|
+
/** Set to false to hide the lane headers in split view. */
|
|
19
|
+
showLanes?: boolean
|
|
20
|
+
leftLabel?: string
|
|
21
|
+
rightLabel?: string
|
|
22
|
+
leftTooltip?: string
|
|
23
|
+
rightTooltip?: string
|
|
24
|
+
}>(),
|
|
25
|
+
{
|
|
26
|
+
events: () => [],
|
|
27
|
+
splitView: true,
|
|
28
|
+
color: 'border-gray-200',
|
|
29
|
+
showLanes: true,
|
|
30
|
+
leftLabel: 'Outbound',
|
|
31
|
+
rightLabel: 'Inbound',
|
|
32
|
+
leftTooltip: 'Vanuit Nexxtmove',
|
|
33
|
+
rightTooltip: 'Vanuit relatie',
|
|
34
|
+
},
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
const hasLanes = computed(() => props.splitView && props.showLanes)
|
|
38
|
+
</script>
|
|
39
|
+
|
|
40
|
+
<template>
|
|
41
|
+
<div class="relative z-10 flex flex-col gap-16">
|
|
42
|
+
<NexxtTimelineLanes
|
|
43
|
+
v-if="hasLanes"
|
|
44
|
+
:left-label="leftLabel"
|
|
45
|
+
:right-label="rightLabel"
|
|
46
|
+
:left-tooltip="leftTooltip"
|
|
47
|
+
:right-tooltip="rightTooltip"
|
|
48
|
+
/>
|
|
49
|
+
|
|
50
|
+
<!-- Left timeline line (always shown) -->
|
|
51
|
+
<div
|
|
52
|
+
class="absolute bottom-0 left-5 w-0 border-l"
|
|
53
|
+
:class="[color, hasLanes ? 'top-10' : 'top-0']"
|
|
54
|
+
></div>
|
|
55
|
+
|
|
56
|
+
<!-- Right timeline line (split view only) -->
|
|
57
|
+
<div
|
|
58
|
+
v-if="splitView"
|
|
59
|
+
class="absolute right-5 bottom-0 w-0 border-l"
|
|
60
|
+
:class="[color, hasLanes ? 'top-10' : 'top-0']"
|
|
61
|
+
></div>
|
|
62
|
+
|
|
63
|
+
<slot>
|
|
64
|
+
<NexxtTimelineEvent v-for="ev in events" :key="ev.id" :event="ev" :split-view="splitView" />
|
|
65
|
+
</slot>
|
|
66
|
+
</div>
|
|
67
|
+
</template>
|
package/src/components.json
CHANGED
|
@@ -48,7 +48,9 @@
|
|
|
48
48
|
"NexxtTabs": "components/Tabs/Tabs.vue",
|
|
49
49
|
"NexxtTemplateCard": "components/TemplateCard/TemplateCard.vue",
|
|
50
50
|
"NexxtTimelineEvent": "components/TimelineEvent/TimelineEvent.vue",
|
|
51
|
+
"NexxtTimelineLanes": "components/TimelineLanes/TimelineLanes.vue",
|
|
51
52
|
"NexxtTimelinePhaseblock": "components/TimelinePhaseblock/TimelinePhaseblock.vue",
|
|
53
|
+
"NexxtTimelineTrack": "components/TimelineTrack/TimelineTrack.vue",
|
|
52
54
|
"NexxtToast": "components/Toast/Toast.vue",
|
|
53
55
|
"NexxtToaster": "components/Toaster/Toaster.vue",
|
|
54
56
|
"NexxtToggleGroup": "components/ToggleGroup/ToggleGroup.vue",
|
package/src/index.ts
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
// Third-party type re-exports for consumers of this library
|
|
3
3
|
export type { Placement } from '@floating-ui/vue'
|
|
4
4
|
|
|
5
|
+
// Non-component modules that consumers need (types, config maps)
|
|
6
|
+
export { EVENT_CONFIG_MAP, PHASE_MAP } from './components/TimelineEvent/TimelineTypes'
|
|
7
|
+
export type {
|
|
8
|
+
TimelineEvent,
|
|
9
|
+
TimelinePhase,
|
|
10
|
+
TimelineResponse,
|
|
11
|
+
} from './components/TimelineEvent/TimelineTypes'
|
|
12
|
+
|
|
5
13
|
export { default as NexxtAnimatedNumber } from './components/AnimatedNumber/AnimatedNumber.vue'
|
|
6
14
|
export { default as NexxtAnnouncement } from './components/Announcement/Announcement.vue'
|
|
7
15
|
export { default as NexxtAvatar } from './components/Avatar/Avatar.vue'
|
|
@@ -51,7 +59,9 @@ export { default as NexxtTable } from './components/Table/Table.vue'
|
|
|
51
59
|
export { default as NexxtTabs } from './components/Tabs/Tabs.vue'
|
|
52
60
|
export { default as NexxtTemplateCard } from './components/TemplateCard/TemplateCard.vue'
|
|
53
61
|
export { default as NexxtTimelineEvent } from './components/TimelineEvent/TimelineEvent.vue'
|
|
62
|
+
export { default as NexxtTimelineLanes } from './components/TimelineLanes/TimelineLanes.vue'
|
|
54
63
|
export { default as NexxtTimelinePhaseblock } from './components/TimelinePhaseblock/TimelinePhaseblock.vue'
|
|
64
|
+
export { default as NexxtTimelineTrack } from './components/TimelineTrack/TimelineTrack.vue'
|
|
55
65
|
export { default as NexxtToast } from './components/Toast/Toast.vue'
|
|
56
66
|
export { default as NexxtToaster } from './components/Toaster/Toaster.vue'
|
|
57
67
|
export { default as NexxtToggleGroup } from './components/ToggleGroup/ToggleGroup.vue'
|
package/src/nuxt.ts
CHANGED
|
@@ -62,8 +62,11 @@ const module: NuxtModule<ModuleOptions> = defineNuxtModule<ModuleOptions>({
|
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
if (options.addCSS) {
|
|
65
|
-
// Tailwind scans the shipped
|
|
66
|
-
//
|
|
65
|
+
// Tailwind scans the shipped source for class names only — this does not
|
|
66
|
+
// involve the consumer's type-checker. The .ts files are scanned too
|
|
67
|
+
// because config maps hold class names outside any template (e.g.
|
|
68
|
+
// EVENT_CONFIG_MAP and PHASE_MAP in TimelineEvent/TimelineTypes.ts);
|
|
69
|
+
// without them those utilities are never generated.
|
|
67
70
|
const componentsDir = resolve('../src/components')
|
|
68
71
|
|
|
69
72
|
addTemplate({
|
|
@@ -71,7 +74,7 @@ const module: NuxtModule<ModuleOptions> = defineNuxtModule<ModuleOptions>({
|
|
|
71
74
|
getContents: () => `
|
|
72
75
|
@import "tailwindcss";
|
|
73
76
|
@import "@nexxtmove/ui/theme";
|
|
74
|
-
@source "${componentsDir}/**/*.vue";
|
|
77
|
+
@source "${componentsDir}/**/*.{vue,ts}";
|
|
75
78
|
`,
|
|
76
79
|
write: true,
|
|
77
80
|
})
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hand-written utilities.
|
|
3
|
+
*
|
|
4
|
+
* The other files in this folder are generated by the Figma design-tokens
|
|
5
|
+
* plugin and get overwritten on every token sync, so anything we author by
|
|
6
|
+
* hand lives here. It is pulled in through `tailwind/theme.css`, the entry
|
|
7
|
+
* behind the `@nexxtmove/ui/theme` export.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/* Slim, branded scrollbar for scrollable panels (dropdowns, lists, drawers). */
|
|
11
|
+
@utility scrollbar-thin {
|
|
12
|
+
scrollbar-width: thin;
|
|
13
|
+
scrollbar-color: var(--color-cornflower-blue-200) transparent;
|
|
14
|
+
|
|
15
|
+
&::-webkit-scrollbar {
|
|
16
|
+
@apply h-1.5 w-1.5;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
&::-webkit-scrollbar-track {
|
|
20
|
+
@apply bg-transparent;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
&::-webkit-scrollbar-thumb {
|
|
24
|
+
@apply rounded-full bg-cornflower-blue-200 transition-colors duration-200;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
&::-webkit-scrollbar-thumb:hover {
|
|
28
|
+
@apply bg-gray-300;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/* Scrollable, but without a visible scrollbar. */
|
|
33
|
+
@utility scrollbar-none {
|
|
34
|
+
scrollbar-width: none;
|
|
35
|
+
|
|
36
|
+
&::-webkit-scrollbar {
|
|
37
|
+
display: none;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Entry point behind the `@nexxtmove/ui/theme` export.
|
|
3
|
+
*
|
|
4
|
+
* Keep this file free of anything generated: `nexxtmove.tailwind.theme.css`
|
|
5
|
+
* (and the utilities/components it imports) is overwritten by the Figma
|
|
6
|
+
* design-tokens sync, so hand-written CSS belongs in the custom file below.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
@import './nexxtmove.tailwind.theme.css';
|
|
10
|
+
@import './nexxtmove.tailwind.custom.css';
|