@nexxtmove/ui 0.1.57 → 0.1.59
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 +379 -46
- package/dist/index.js +2956 -1864
- package/dist/nuxt.js +29 -28
- package/package.json +2 -1
- package/src/components/Combobox/Combobox.vue +28 -68
- package/src/components/DatePicker/DatePicker.vue +39 -82
- package/src/components/DropdownButton/DropdownButton.vue +47 -76
- package/src/components/FloatingPanel/FloatingPanel.vue +143 -0
- package/src/components/Modal/Modal.vue +1 -1
- package/src/components/TabbedContent/TabbedContent.vue +11 -1
- package/src/components.json +1 -0
- package/src/index.ts +1 -0
package/dist/nuxt.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { defineNuxtModule as c, addComponent as s, addTemplate as a } from "@nuxt/kit";
|
|
2
|
-
const x = "components/AnimatedNumber/AnimatedNumber.vue",
|
|
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 = {
|
|
3
3
|
NexxtAnimatedNumber: x,
|
|
4
|
-
NexxtAvatar:
|
|
5
|
-
NexxtButton:
|
|
4
|
+
NexxtAvatar: i,
|
|
5
|
+
NexxtButton: r,
|
|
6
6
|
NexxtCalendar: m,
|
|
7
7
|
NexxtCard: u,
|
|
8
8
|
NexxtCheckbox: p,
|
|
@@ -12,33 +12,34 @@ const x = "components/AnimatedNumber/AnimatedNumber.vue", r = "components/Avatar
|
|
|
12
12
|
NexxtContactInfoCard: C,
|
|
13
13
|
NexxtCustomerJourneyTile: N,
|
|
14
14
|
NexxtDatePicker: b,
|
|
15
|
-
NexxtDoughnutChart:
|
|
16
|
-
NexxtDropdownButton:
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
15
|
+
NexxtDoughnutChart: P,
|
|
16
|
+
NexxtDropdownButton: T,
|
|
17
|
+
NexxtFloatingPanel: S,
|
|
18
|
+
NexxtHeader: h,
|
|
19
|
+
NexxtIcon: k,
|
|
20
|
+
NexxtInfoBlock: I,
|
|
21
|
+
NexxtInputField: f,
|
|
22
|
+
NexxtModal: g,
|
|
22
23
|
NexxtPagination: y,
|
|
23
|
-
NexxtPresenceIndicator:
|
|
24
|
-
NexxtProgressBar:
|
|
25
|
-
NexxtPropertyCard:
|
|
26
|
-
NexxtPropertyListing:
|
|
27
|
-
NexxtSkeleton:
|
|
24
|
+
NexxtPresenceIndicator: M,
|
|
25
|
+
NexxtProgressBar: B,
|
|
26
|
+
NexxtPropertyCard: D,
|
|
27
|
+
NexxtPropertyListing: A,
|
|
28
|
+
NexxtSkeleton: F,
|
|
28
29
|
NexxtSkeletonPropertyCard: H,
|
|
29
30
|
NexxtSocialIcons: w,
|
|
30
31
|
NexxtSocialMediaCustomTemplate: E,
|
|
31
|
-
NexxtSocialMediaTemplate:
|
|
32
|
-
NexxtSocialMediaType:
|
|
33
|
-
NexxtSource:
|
|
34
|
-
NexxtStepperHeader:
|
|
35
|
-
NexxtTabbedContent:
|
|
36
|
-
NexxtTable:
|
|
37
|
-
NexxtTabs:
|
|
38
|
-
NexxtTimelineEvent:
|
|
39
|
-
NexxtTimelinePhaseblock:
|
|
40
|
-
NexxtTooltip:
|
|
41
|
-
},
|
|
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({
|
|
42
43
|
meta: {
|
|
43
44
|
name: "@nexxtmove/ui",
|
|
44
45
|
configKey: "nexxtmoveUi",
|
|
@@ -50,7 +51,7 @@ const x = "components/AnimatedNumber/AnimatedNumber.vue", r = "components/Avatar
|
|
|
50
51
|
addCSS: !0
|
|
51
52
|
},
|
|
52
53
|
setup(e, o) {
|
|
53
|
-
for (const [t, n] of Object.entries(
|
|
54
|
+
for (const [t, n] of Object.entries(G))
|
|
54
55
|
s({
|
|
55
56
|
name: t,
|
|
56
57
|
export: "default",
|
|
@@ -69,5 +70,5 @@ const x = "components/AnimatedNumber/AnimatedNumber.vue", r = "components/Avatar
|
|
|
69
70
|
}
|
|
70
71
|
});
|
|
71
72
|
export {
|
|
72
|
-
|
|
73
|
+
R as default
|
|
73
74
|
};
|
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.59",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
"tailwind"
|
|
21
21
|
],
|
|
22
22
|
"dependencies": {
|
|
23
|
+
"@floating-ui/vue": "^1.1.11",
|
|
23
24
|
"@number-flow/vue": "^0.5.0",
|
|
24
25
|
"date-fns": "^4.1.0",
|
|
25
26
|
"date-fns-tz": "^3.2.0",
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
<script lang="ts" setup>
|
|
2
|
-
import { ref, computed,
|
|
2
|
+
import { ref, computed, nextTick } from 'vue'
|
|
3
|
+
import type { Placement } from '@floating-ui/vue'
|
|
3
4
|
import NexxtButton from '../Button/Button.vue'
|
|
4
5
|
import NexxtIcon from '../Icon/Icon.vue'
|
|
6
|
+
import NexxtFloatingPanel from '../FloatingPanel/FloatingPanel.vue'
|
|
5
7
|
import ComboboxPanel from '../ComboboxPanel/ComboboxPanel.vue'
|
|
6
8
|
import type { ComboboxOptions } from '../ComboboxPanel/combobox-types'
|
|
7
9
|
|
|
@@ -23,78 +25,36 @@ const {
|
|
|
23
25
|
|
|
24
26
|
const selected = defineModel<(string | number)[]>({ default: [] })
|
|
25
27
|
|
|
26
|
-
const isOpen = ref(false)
|
|
27
28
|
const panelRef = ref<InstanceType<typeof ComboboxPanel> | null>(null)
|
|
28
|
-
const wrapperRef = ref<HTMLElement | null>(null)
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
panelRef.value?.focus()
|
|
34
|
-
}
|
|
35
|
-
})
|
|
30
|
+
const floatingPlacement = computed(
|
|
31
|
+
(): Placement => (placement === 'right' ? 'bottom-end' : 'bottom-start'),
|
|
32
|
+
)
|
|
36
33
|
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
34
|
+
const onPanelOpen = async () => {
|
|
35
|
+
await nextTick()
|
|
36
|
+
panelRef.value?.focus()
|
|
41
37
|
}
|
|
42
|
-
|
|
43
|
-
const handleKeydown = (event: KeyboardEvent) => {
|
|
44
|
-
if (event.key === 'Escape' && isOpen.value) {
|
|
45
|
-
isOpen.value = false
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
onMounted(() => {
|
|
50
|
-
document.addEventListener('mousedown', handleClickOutside)
|
|
51
|
-
document.addEventListener('keydown', handleKeydown)
|
|
52
|
-
})
|
|
53
|
-
onUnmounted(() => {
|
|
54
|
-
document.removeEventListener('mousedown', handleClickOutside)
|
|
55
|
-
document.removeEventListener('keydown', handleKeydown)
|
|
56
|
-
})
|
|
57
|
-
|
|
58
|
-
const transitionClasses = computed(() => {
|
|
59
|
-
const origin = placement === 'right' ? 'origin-top-right' : 'origin-top-left'
|
|
60
|
-
return {
|
|
61
|
-
enterActiveClass: `transition-all duration-200 ease-out ${origin}`,
|
|
62
|
-
leaveActiveClass: `transition-all duration-150 ease-in ${origin}`,
|
|
63
|
-
enterFromClass: 'opacity-0 scale-95 -translate-y-3',
|
|
64
|
-
leaveToClass: 'opacity-0 scale-95 -translate-y-3',
|
|
65
|
-
enterToClass: 'opacity-100 scale-100 translate-y-0',
|
|
66
|
-
leaveFromClass: 'opacity-100 scale-100 translate-y-0',
|
|
67
|
-
}
|
|
68
|
-
})
|
|
69
38
|
</script>
|
|
70
39
|
|
|
71
40
|
<template>
|
|
72
|
-
<
|
|
73
|
-
<
|
|
74
|
-
:variant
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
>
|
|
80
|
-
<
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
:
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
:options="props.options"
|
|
92
|
-
:allow-new="props.allowNew"
|
|
93
|
-
:icon
|
|
94
|
-
:placeholder
|
|
95
|
-
:floating="false"
|
|
96
|
-
/>
|
|
97
|
-
</div>
|
|
98
|
-
</Transition>
|
|
99
|
-
</div>
|
|
41
|
+
<NexxtFloatingPanel :placement="floatingPlacement" @open="onPanelOpen">
|
|
42
|
+
<template #trigger="{ isOpen, toggle }">
|
|
43
|
+
<NexxtButton :variant :icon :aria-expanded="isOpen" aria-haspopup="listbox" @click="toggle">
|
|
44
|
+
<span class="flex gap-2"><slot>Combobox</slot><NexxtIcon name="chevron-down" /></span>
|
|
45
|
+
</NexxtButton>
|
|
46
|
+
</template>
|
|
47
|
+
|
|
48
|
+
<template #content>
|
|
49
|
+
<ComboboxPanel
|
|
50
|
+
ref="panelRef"
|
|
51
|
+
v-model="selected"
|
|
52
|
+
:options="props.options"
|
|
53
|
+
:allow-new="props.allowNew"
|
|
54
|
+
:icon
|
|
55
|
+
:placeholder
|
|
56
|
+
:floating="false"
|
|
57
|
+
/>
|
|
58
|
+
</template>
|
|
59
|
+
</NexxtFloatingPanel>
|
|
100
60
|
</template>
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
<script lang="ts" setup>
|
|
2
|
-
import { ref, computed,
|
|
2
|
+
import { ref, computed, nextTick } from 'vue'
|
|
3
|
+
import type { Placement } from '@floating-ui/vue'
|
|
3
4
|
import NexxtButton from '../Button/Button.vue'
|
|
4
5
|
import NexxtCalendar from '../Calendar/Calendar.vue'
|
|
6
|
+
import NexxtFloatingPanel from '../FloatingPanel/FloatingPanel.vue'
|
|
5
7
|
import type { NexxtCalendarProps } from '../Calendar/calendar.types'
|
|
6
8
|
|
|
7
9
|
export type CalendarAnchor = 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left'
|
|
@@ -36,110 +38,65 @@ const calendarProps = computed((): NexxtCalendarProps => {
|
|
|
36
38
|
|
|
37
39
|
// ── State ─────────────────────────────────────────────────────────────────────
|
|
38
40
|
|
|
39
|
-
const
|
|
40
|
-
const wrapperRef = ref<HTMLElement | null>(null)
|
|
41
|
-
const triggerRef = ref<HTMLElement | null>(null)
|
|
41
|
+
const panelRef = ref<InstanceType<typeof NexxtFloatingPanel> | null>(null)
|
|
42
42
|
const popoverRef = ref<HTMLElement | null>(null)
|
|
43
43
|
|
|
44
|
-
// ──
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
await nextTick()
|
|
55
|
-
popoverRef.value?.focus()
|
|
56
|
-
}
|
|
57
|
-
})
|
|
44
|
+
// ── Anchor → Floating UI placement ───────────────────────────────────────────
|
|
45
|
+
|
|
46
|
+
const anchorToPlacement: Record<CalendarAnchor, Placement> = {
|
|
47
|
+
'bottom-right': 'bottom-end',
|
|
48
|
+
'bottom-left': 'bottom-start',
|
|
49
|
+
'top-right': 'top-end',
|
|
50
|
+
'top-left': 'top-start',
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const floatingPlacement = computed(() => anchorToPlacement[props.anchor])
|
|
58
54
|
|
|
59
55
|
// ── Handlers ──────────────────────────────────────────────────────────────────
|
|
60
56
|
|
|
61
57
|
const handleSelect = () => {
|
|
62
|
-
if (props.autoClose)
|
|
63
|
-
isOpen.value = false
|
|
64
|
-
}
|
|
58
|
+
if (props.autoClose) panelRef.value?.close()
|
|
65
59
|
}
|
|
66
60
|
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
isOpen.value = false
|
|
71
|
-
}
|
|
61
|
+
const onPanelOpen = async () => {
|
|
62
|
+
await nextTick()
|
|
63
|
+
popoverRef.value?.focus()
|
|
72
64
|
}
|
|
73
65
|
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
66
|
+
const onPanelClose = async () => {
|
|
67
|
+
await nextTick()
|
|
68
|
+
const focusable = panelRef.value?.triggerRef?.querySelector<HTMLElement>(
|
|
69
|
+
'button, input, [tabindex]:not([tabindex="-1"])',
|
|
70
|
+
)
|
|
71
|
+
focusable?.focus()
|
|
78
72
|
}
|
|
79
|
-
|
|
80
|
-
onMounted(() => document.addEventListener('mousedown', handleClickOutside))
|
|
81
|
-
onUnmounted(() => document.removeEventListener('mousedown', handleClickOutside))
|
|
82
|
-
|
|
83
|
-
// ── Popover placement ─────────────────────────────────────────────────────────
|
|
84
|
-
|
|
85
|
-
const popoverClasses = computed(() => {
|
|
86
|
-
const map: Record<CalendarAnchor, string> = {
|
|
87
|
-
'bottom-right': 'top-full right-0 mt-2 origin-top-right',
|
|
88
|
-
'bottom-left': 'top-full left-0 mt-2 origin-top-left',
|
|
89
|
-
'top-right': 'bottom-full right-0 mb-2 origin-bottom-right',
|
|
90
|
-
'top-left': 'bottom-full left-0 mb-2 origin-bottom-left',
|
|
91
|
-
}
|
|
92
|
-
return map[props.anchor]
|
|
93
|
-
})
|
|
94
|
-
|
|
95
|
-
const transitionClasses = computed(() => {
|
|
96
|
-
const isBottom = props.anchor.startsWith('bottom')
|
|
97
|
-
|
|
98
|
-
const origins: Record<CalendarAnchor, string> = {
|
|
99
|
-
'bottom-left': 'origin-top-left',
|
|
100
|
-
'bottom-right': 'origin-top-right',
|
|
101
|
-
'top-left': 'origin-bottom-left',
|
|
102
|
-
'top-right': 'origin-bottom-right',
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
const translateClass = isBottom ? '-translate-y-3' : 'translate-y-3'
|
|
106
|
-
|
|
107
|
-
return {
|
|
108
|
-
enterActiveClass: `transition-all duration-200 ease-out ${origins[props.anchor]}`,
|
|
109
|
-
leaveActiveClass: `transition-all duration-150 ease-in ${origins[props.anchor]}`,
|
|
110
|
-
enterFromClass: `opacity-0 scale-95 ${translateClass}`,
|
|
111
|
-
leaveToClass: `opacity-0 scale-95 ${translateClass}`,
|
|
112
|
-
enterToClass: 'opacity-100 scale-100 translate-y-0',
|
|
113
|
-
leaveFromClass: 'opacity-100 scale-100 translate-y-0',
|
|
114
|
-
}
|
|
115
|
-
})
|
|
116
73
|
</script>
|
|
117
74
|
|
|
118
75
|
<template>
|
|
119
|
-
<
|
|
120
|
-
|
|
121
|
-
|
|
76
|
+
<NexxtFloatingPanel
|
|
77
|
+
ref="panelRef"
|
|
78
|
+
:placement="floatingPlacement"
|
|
79
|
+
@open="onPanelOpen"
|
|
80
|
+
@close="onPanelClose"
|
|
81
|
+
>
|
|
82
|
+
<template #trigger="{ isOpen, toggle }">
|
|
83
|
+
<slot name="trigger" :is-open="isOpen" :toggle="toggle">
|
|
122
84
|
<NexxtButton
|
|
123
85
|
icon="calendar"
|
|
124
86
|
variant="secondary"
|
|
125
87
|
:aria-expanded="isOpen"
|
|
126
88
|
aria-haspopup="grid"
|
|
127
|
-
@click="
|
|
89
|
+
@click="toggle"
|
|
128
90
|
>
|
|
129
91
|
{{ props.placeholder }}
|
|
130
92
|
</NexxtButton>
|
|
131
93
|
</slot>
|
|
132
|
-
</
|
|
133
|
-
|
|
134
|
-
<
|
|
135
|
-
<div
|
|
136
|
-
v-if="isOpen"
|
|
137
|
-
ref="popoverRef"
|
|
138
|
-
tabindex="-1"
|
|
139
|
-
:class="['absolute z-50 outline-none', popoverClasses]"
|
|
140
|
-
>
|
|
94
|
+
</template>
|
|
95
|
+
|
|
96
|
+
<template #content>
|
|
97
|
+
<div ref="popoverRef" tabindex="-1" class="outline-none">
|
|
141
98
|
<NexxtCalendar v-model="model" v-bind="calendarProps" @select="handleSelect" />
|
|
142
99
|
</div>
|
|
143
|
-
</
|
|
144
|
-
</
|
|
100
|
+
</template>
|
|
101
|
+
</NexxtFloatingPanel>
|
|
145
102
|
</template>
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
<script lang="ts" setup>
|
|
2
|
-
import { ref,
|
|
2
|
+
import { ref, computed } from 'vue'
|
|
3
|
+
import type { Placement } from '@floating-ui/vue'
|
|
3
4
|
import NexxtIcon from '../Icon/Icon.vue'
|
|
4
5
|
import NexxtCheckbox from '../Checkbox/Checkbox.vue'
|
|
6
|
+
import NexxtFloatingPanel from '../FloatingPanel/FloatingPanel.vue'
|
|
5
7
|
|
|
6
8
|
interface NexxtDropdownButton {
|
|
7
9
|
label: string
|
|
@@ -44,8 +46,12 @@ const emit = defineEmits<{
|
|
|
44
46
|
checkboxChange: [label: string, checked: boolean]
|
|
45
47
|
}>()
|
|
46
48
|
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
+
const panelRef = ref<InstanceType<typeof NexxtFloatingPanel> | null>(null)
|
|
50
|
+
|
|
51
|
+
const floatingPlacement = computed(
|
|
52
|
+
(): Placement => (placement === 'right' ? 'bottom-end' : 'bottom-start'),
|
|
53
|
+
)
|
|
54
|
+
|
|
49
55
|
const flatItems = computed<NexxtDropdownButton[]>(() =>
|
|
50
56
|
options.length > 0 && 'items' in options[0]
|
|
51
57
|
? (options as NexxtDropdownButtonGroup[]).flatMap((g) => g.items)
|
|
@@ -75,35 +81,17 @@ const handleAction = (action: string | (() => void)) => {
|
|
|
75
81
|
}
|
|
76
82
|
}
|
|
77
83
|
|
|
78
|
-
const toggleDropdown = () => {
|
|
79
|
-
isOpen.value = !isOpen.value
|
|
80
|
-
}
|
|
81
|
-
|
|
82
84
|
const handleOptionClick = (option: NexxtDropdownButton) => {
|
|
83
85
|
if (option.checkbox) {
|
|
84
86
|
const newValue = !checkedItems.value[option.label]
|
|
85
87
|
toggledItems.value[option.label] = newValue
|
|
86
88
|
emit('checkboxChange', option.label, newValue)
|
|
87
89
|
} else {
|
|
88
|
-
|
|
90
|
+
panelRef.value?.close()
|
|
89
91
|
}
|
|
90
92
|
handleAction(option.action)
|
|
91
93
|
}
|
|
92
94
|
|
|
93
|
-
const handleClickOutside = (event: MouseEvent) => {
|
|
94
|
-
if (wrapperRef.value && !wrapperRef.value.contains(event.target as Node)) {
|
|
95
|
-
isOpen.value = false
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
onMounted(() => {
|
|
100
|
-
document.addEventListener('mousedown', handleClickOutside)
|
|
101
|
-
})
|
|
102
|
-
|
|
103
|
-
onUnmounted(() => {
|
|
104
|
-
document.removeEventListener('mousedown', handleClickOutside)
|
|
105
|
-
})
|
|
106
|
-
|
|
107
95
|
const isGrouped = (
|
|
108
96
|
opts: NexxtDropdownButton[] | NexxtDropdownButtonGroup[],
|
|
109
97
|
): opts is NexxtDropdownButtonGroup[] => opts.length > 0 && 'items' in opts[0]
|
|
@@ -136,66 +124,49 @@ const buttonClasses = computed(() => {
|
|
|
136
124
|
|
|
137
125
|
return ''
|
|
138
126
|
})
|
|
139
|
-
|
|
140
|
-
const transitionClasses = computed(() => {
|
|
141
|
-
const origin = placement === 'right' ? 'origin-top-right' : 'origin-top-left'
|
|
142
|
-
|
|
143
|
-
return {
|
|
144
|
-
enterActiveClass: `transition-all duration-200 ease-out ${origin}`,
|
|
145
|
-
leaveActiveClass: `transition-all duration-150 ease-in ${origin}`,
|
|
146
|
-
enterFromClass: 'opacity-0 scale-95 -translate-y-3',
|
|
147
|
-
leaveToClass: 'opacity-0 scale-95 -translate-y-3',
|
|
148
|
-
enterToClass: 'opacity-100 scale-100 translate-y-0',
|
|
149
|
-
leaveFromClass: 'opacity-100 scale-100 translate-y-0',
|
|
150
|
-
}
|
|
151
|
-
})
|
|
152
127
|
</script>
|
|
153
128
|
|
|
154
129
|
<template>
|
|
155
|
-
<
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
130
|
+
<NexxtFloatingPanel ref="panelRef" :placement="floatingPlacement">
|
|
131
|
+
<template #trigger="{ isOpen, toggle }">
|
|
132
|
+
<div class="inline-flex items-center overflow-hidden rounded-lg" :class="componentClasses">
|
|
133
|
+
<template v-if="!iconOnly">
|
|
134
|
+
<button
|
|
135
|
+
class="flex cursor-pointer items-center gap-2 px-4 py-2 transition-colors duration-200"
|
|
136
|
+
:class="buttonClasses"
|
|
137
|
+
@click="action && handleAction(action)"
|
|
138
|
+
>
|
|
139
|
+
<NexxtIcon v-if="icon" :name="icon" aria-hidden="true" />
|
|
140
|
+
{{ label }}
|
|
141
|
+
</button>
|
|
142
|
+
<span
|
|
143
|
+
class="h-7 w-px rounded-sm"
|
|
144
|
+
:class="variant === 'primary' ? 'bg-white' : 'bg-gray-200'"
|
|
145
|
+
aria-hidden="true"
|
|
146
|
+
></span>
|
|
147
|
+
</template>
|
|
162
148
|
<button
|
|
163
|
-
class="
|
|
164
|
-
:class="buttonClasses"
|
|
165
|
-
|
|
149
|
+
class="w-10 shrink-0 cursor-pointer transition-colors duration-200"
|
|
150
|
+
:class="[buttonClasses, iconOnly ? 'p-2' : 'px-2 py-2']"
|
|
151
|
+
aria-haspopup="menu"
|
|
152
|
+
:aria-expanded="isOpen"
|
|
153
|
+
:aria-label="`Open ${label} menu`"
|
|
154
|
+
@click="toggle"
|
|
166
155
|
>
|
|
167
|
-
<NexxtIcon
|
|
168
|
-
|
|
156
|
+
<NexxtIcon
|
|
157
|
+
:name="triggerIcon"
|
|
158
|
+
class="transition-transform duration-200"
|
|
159
|
+
:class="{ 'rotate-180': isOpen && triggerIcon === 'chevron-down' }"
|
|
160
|
+
aria-hidden="true"
|
|
161
|
+
/>
|
|
169
162
|
</button>
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
></span>
|
|
175
|
-
</template>
|
|
176
|
-
<button
|
|
177
|
-
class="w-10 shrink-0 cursor-pointer transition-colors duration-200"
|
|
178
|
-
:class="[buttonClasses, iconOnly ? 'p-2' : 'px-2 py-2']"
|
|
179
|
-
@click="iconOnly ? toggleDropdown() : toggleDropdown()"
|
|
180
|
-
aria-haspopup="menu"
|
|
181
|
-
:aria-expanded="isOpen"
|
|
182
|
-
:aria-label="`Open ${label} menu`"
|
|
183
|
-
>
|
|
184
|
-
<NexxtIcon
|
|
185
|
-
:name="triggerIcon"
|
|
186
|
-
class="transition-transform duration-200"
|
|
187
|
-
:class="{ 'rotate-180': isOpen && triggerIcon === 'chevron-down' }"
|
|
188
|
-
aria-hidden="true"
|
|
189
|
-
/>
|
|
190
|
-
</button>
|
|
191
|
-
</div>
|
|
192
|
-
|
|
193
|
-
<Transition v-bind="transitionClasses">
|
|
163
|
+
</div>
|
|
164
|
+
</template>
|
|
165
|
+
|
|
166
|
+
<template #content>
|
|
194
167
|
<div
|
|
195
|
-
v-if="isOpen"
|
|
196
168
|
role="menu"
|
|
197
|
-
class="
|
|
198
|
-
:class="placement === 'right' ? 'right-0' : 'left-0'"
|
|
169
|
+
class="min-w-full rounded-lg border border-gray-200 bg-white whitespace-nowrap"
|
|
199
170
|
>
|
|
200
171
|
<template v-for="(group, groupIndex) in groupedOptions" :key="group.title || groupIndex">
|
|
201
172
|
<div
|
|
@@ -225,6 +196,6 @@ const transitionClasses = computed(() => {
|
|
|
225
196
|
</button>
|
|
226
197
|
</template>
|
|
227
198
|
</div>
|
|
228
|
-
</
|
|
229
|
-
</
|
|
199
|
+
</template>
|
|
200
|
+
</NexxtFloatingPanel>
|
|
230
201
|
</template>
|