@nexxtmove/ui 1.3.1 → 1.3.2
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/DropdownButton/DropdownButton.vue.d.ts +6 -0
- package/dist/components/InputField/InputField.vue.d.ts +4 -1
- package/dist/index.js +50 -38
- package/package.json +1 -1
- package/src/components/DropdownButton/DropdownButton.vue +31 -2
- package/src/components/InputField/InputField.vue +13 -1
|
@@ -12,6 +12,12 @@ interface NexxtDropdownButtonGroup {
|
|
|
12
12
|
}
|
|
13
13
|
type NexxtDropdownButtonBase = {
|
|
14
14
|
variant?: 'primary' | 'secondary';
|
|
15
|
+
/**
|
|
16
|
+
* Accessible name for the trigger button. Supply this whenever the button
|
|
17
|
+
* has no visible text, which is every `iconOnly` case. Defaults to the
|
|
18
|
+
* visible label when there is one, and otherwise to a generic Dutch label.
|
|
19
|
+
*/
|
|
20
|
+
ariaLabel?: string;
|
|
15
21
|
options: NexxtDropdownButton[] | NexxtDropdownButtonGroup[];
|
|
16
22
|
placement?: 'left' | 'right';
|
|
17
23
|
triggerIcon?: InstanceType<typeof NexxtIcon>['name'];
|
|
@@ -17,7 +17,10 @@ declare var __VLS_6: {};
|
|
|
17
17
|
type __VLS_Slots = {} & {
|
|
18
18
|
right?: (props: typeof __VLS_6) => any;
|
|
19
19
|
};
|
|
20
|
-
declare const __VLS_base: import('vue').DefineComponent<__VLS_PublicProps, {
|
|
20
|
+
declare const __VLS_base: import('vue').DefineComponent<__VLS_PublicProps, {
|
|
21
|
+
focus: () => void | undefined;
|
|
22
|
+
inputEl: Readonly<import('vue').ShallowRef<HTMLInputElement | null>>;
|
|
23
|
+
}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
|
|
21
24
|
"update:modelValue": (value: string | number | undefined) => any;
|
|
22
25
|
}, string, import('vue').PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
23
26
|
"onUpdate:modelValue"?: ((value: string | number | undefined) => any) | undefined;
|
package/dist/index.js
CHANGED
|
@@ -1615,19 +1615,24 @@ var ut = { funda: { render: lt } }, Q = /* @__PURE__ */ p({
|
|
|
1615
1615
|
modelModifiers: {}
|
|
1616
1616
|
}),
|
|
1617
1617
|
emits: ["update:modelValue"],
|
|
1618
|
-
setup(e) {
|
|
1619
|
-
let
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1618
|
+
setup(e, { expose: t }) {
|
|
1619
|
+
let r = "input-field-" + Math.random().toString(36).slice(2, 10), a = M(e, "modelValue"), l = N("inputEl");
|
|
1620
|
+
t({
|
|
1621
|
+
focus: () => l.value?.focus(),
|
|
1622
|
+
inputEl: l
|
|
1623
|
+
});
|
|
1624
|
+
let u = i(() => m()?.attrs.type === "number"), p = () => {
|
|
1625
|
+
let e = Number(a.value || 0);
|
|
1626
|
+
a.value = e + 1;
|
|
1627
|
+
}, h = () => {
|
|
1628
|
+
let e = Number(a.value || 0);
|
|
1629
|
+
a.value = e - 1;
|
|
1625
1630
|
};
|
|
1626
|
-
return (
|
|
1631
|
+
return (t, i) => (w(), s("div", vn, [
|
|
1627
1632
|
e.label ? (w(), s("label", {
|
|
1628
1633
|
key: 0,
|
|
1629
1634
|
class: "text-color-gray-700 justify-start body-normal text-sm",
|
|
1630
|
-
for:
|
|
1635
|
+
for: r
|
|
1631
1636
|
}, [d(k(e.label) + " ", 1), e.required ? (w(), s("span", yn, "*")) : o("", !0)])) : o("", !0),
|
|
1632
1637
|
c("div", { class: y(["group flex items-center gap-2 overflow-hidden rounded-lg ring transition-colors ease-out focus-within:ring-cornflower-blue-500", [e.error ? "ring-brick-400" : "ring-gray-200", {
|
|
1633
1638
|
"pl-3": e.leftIcon,
|
|
@@ -1635,18 +1640,20 @@ var ut = { funda: { render: lt } }, Q = /* @__PURE__ */ p({
|
|
|
1635
1640
|
}]]) }, [
|
|
1636
1641
|
e.leftIcon ? (w(), s("span", bn, [f(Q, { name: e.leftIcon }, null, 8, ["name"])])) : o("", !0),
|
|
1637
1642
|
z(c("input", _({
|
|
1638
|
-
|
|
1639
|
-
|
|
1643
|
+
ref_key: "inputEl",
|
|
1644
|
+
ref: l,
|
|
1645
|
+
id: r,
|
|
1646
|
+
"onUpdate:modelValue": i[0] ||= (e) => a.value = e,
|
|
1640
1647
|
placeholder: e.placeholder
|
|
1641
|
-
},
|
|
1648
|
+
}, t.$attrs, { class: ["bg-transparant h-10 w-full min-w-60 text-sm text-gray-900 placeholder-gray-400 focus:outline-none [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none", {
|
|
1642
1649
|
"pl-3": !e.leftIcon,
|
|
1643
1650
|
"pr-3": !e.rightIcon
|
|
1644
|
-
}] }), null, 16, xn), [[F,
|
|
1645
|
-
D(
|
|
1651
|
+
}] }), null, 16, xn), [[F, a.value]]),
|
|
1652
|
+
D(t.$slots, "right", {}, () => [u.value ? (w(), s("div", Sn, [c("button", {
|
|
1646
1653
|
type: "button",
|
|
1647
1654
|
class: "flex h-5 w-8 cursor-pointer items-center justify-center pt-2 text-gray-400 transition-colors group-focus-within:text-gray-950 hover:text-cornflower-blue-600 focus:text-cornflower-blue-600 focus:outline-none",
|
|
1648
1655
|
"aria-label": "Verhogen",
|
|
1649
|
-
onClick:
|
|
1656
|
+
onClick: p
|
|
1650
1657
|
}, [f(Q, {
|
|
1651
1658
|
name: "chevron-up",
|
|
1652
1659
|
class: "text-[10px]",
|
|
@@ -1655,7 +1662,7 @@ var ut = { funda: { render: lt } }, Q = /* @__PURE__ */ p({
|
|
|
1655
1662
|
type: "button",
|
|
1656
1663
|
class: "flex h-5 w-8 cursor-pointer items-center justify-center pb-2 text-gray-400 transition-colors group-focus-within:text-gray-950 hover:text-cornflower-blue-600 focus:text-cornflower-blue-600 focus:outline-none",
|
|
1657
1664
|
"aria-label": "Verlagen",
|
|
1658
|
-
onClick:
|
|
1665
|
+
onClick: h
|
|
1659
1666
|
}, [f(Q, {
|
|
1660
1667
|
name: "chevron-down",
|
|
1661
1668
|
class: "text-[10px]",
|
|
@@ -3149,6 +3156,7 @@ var ar = /* @__PURE__ */ p({
|
|
|
3149
3156
|
__name: "DropdownButton",
|
|
3150
3157
|
props: {
|
|
3151
3158
|
variant: { default: "primary" },
|
|
3159
|
+
ariaLabel: {},
|
|
3152
3160
|
options: {},
|
|
3153
3161
|
placement: { default: "right" },
|
|
3154
3162
|
triggerIcon: { default: "chevron-down" },
|
|
@@ -3162,26 +3170,30 @@ var ar = /* @__PURE__ */ p({
|
|
|
3162
3170
|
},
|
|
3163
3171
|
emits: ["navigate", "checkboxChange"],
|
|
3164
3172
|
setup(t, { emit: n }) {
|
|
3165
|
-
let r =
|
|
3166
|
-
|
|
3167
|
-
|
|
3173
|
+
let r = i(() => t.ariaLabel ?? (t.label ? `${t.label} menu openen` : "Menu openen")), l = n, u = T(null), p = i(() => t.placement === "right" ? "bottom-end" : "bottom-start"), m = i(() => t.options.length > 0 && "items" in t.options[0] ? t.options.flatMap((e) => e.items) : t.options), h = T({});
|
|
3174
|
+
I(() => m.value.filter((e) => e.checkbox).map((e) => e.selected ?? !1), () => {
|
|
3175
|
+
h.value = {};
|
|
3176
|
+
}, { deep: !0 });
|
|
3177
|
+
let g = i(() => Object.fromEntries(m.value.filter((e) => e.checkbox).map((e) => [e.label, e.label in h.value ? h.value[e.label] : e.selected ?? !1]))), _ = (e) => {
|
|
3178
|
+
typeof e == "function" ? e() : l("navigate", e);
|
|
3179
|
+
}, v = (e) => {
|
|
3168
3180
|
if (e.checkbox) {
|
|
3169
|
-
let t = !
|
|
3170
|
-
|
|
3171
|
-
} else
|
|
3172
|
-
|
|
3173
|
-
},
|
|
3181
|
+
let t = !g.value[e.label];
|
|
3182
|
+
h.value[e.label] = t, l("checkboxChange", e.label, t);
|
|
3183
|
+
} else u.value?.close();
|
|
3184
|
+
_(e.action);
|
|
3185
|
+
}, b = (e) => e.length > 0 && "items" in e[0], x = i(() => b(t.options) ? t.options : [{
|
|
3174
3186
|
title: "",
|
|
3175
3187
|
items: t.options
|
|
3176
|
-
}]),
|
|
3177
|
-
return (n,
|
|
3188
|
+
}]), S = i(() => t.variant === "primary" ? "bg-button-bg-primary-default text-button-text-primary-default" : t.variant === "secondary" ? "bg-button-bg-secondary-default text-button-text-secondary-default border-button-border-secondary-default border border-solid hover:border-button-border-secondary-hover" : ""), C = i(() => t.variant === "primary" ? "bg-button-bg-primary-default hover:bg-button-bg-primary-hover focus-visible:bg-button-bg-primary-focussed focus-visible:outline-button-border-primary-focussed focus-visible:outline focus-visible:outline-solid active:bg-button-bg-primary-selected" : t.variant === "secondary" ? "bg-button-bg-secondary-default hover:bg-button-bg-secondary-hover focus-visible:bg-button-bg-secondary-focussed focus-visible:outline-button-border-secondary-focussed focus-visible:outline focus-visible:outline-solid active:bg-button-bg-secondary-selected" : "");
|
|
3189
|
+
return (n, i) => (w(), a($, {
|
|
3178
3190
|
ref_key: "panelRef",
|
|
3179
|
-
ref:
|
|
3180
|
-
placement:
|
|
3191
|
+
ref: u,
|
|
3192
|
+
placement: p.value
|
|
3181
3193
|
}, {
|
|
3182
|
-
trigger: R(({ isOpen: n, toggle:
|
|
3183
|
-
class: y(["flex cursor-pointer items-center gap-2 px-4 py-2 transition-colors duration-200",
|
|
3184
|
-
onClick: (e) => t.action ?
|
|
3194
|
+
trigger: R(({ isOpen: n, toggle: i }) => [c("div", { class: y(["inline-flex items-center overflow-hidden rounded-lg", S.value]) }, [t.iconOnly ? o("", !0) : (w(), s(e, { key: 0 }, [c("button", {
|
|
3195
|
+
class: y(["flex cursor-pointer items-center gap-2 px-4 py-2 transition-colors duration-200", C.value]),
|
|
3196
|
+
onClick: (e) => t.action ? _(t.action) : i()
|
|
3185
3197
|
}, [t.icon ? (w(), a(Q, {
|
|
3186
3198
|
key: 0,
|
|
3187
3199
|
name: t.icon,
|
|
@@ -3190,28 +3202,28 @@ var ar = /* @__PURE__ */ p({
|
|
|
3190
3202
|
class: y(["h-7 w-px rounded-sm", t.variant === "primary" ? "bg-white" : "bg-gray-200"]),
|
|
3191
3203
|
"aria-hidden": "true"
|
|
3192
3204
|
}, null, 2)], 64)), c("button", {
|
|
3193
|
-
class: y(["w-10 shrink-0 cursor-pointer transition-colors duration-200", [
|
|
3205
|
+
class: y(["w-10 shrink-0 cursor-pointer transition-colors duration-200", [C.value, t.iconOnly ? "p-2" : "px-2 py-2"]]),
|
|
3194
3206
|
"aria-haspopup": "menu",
|
|
3195
3207
|
"aria-expanded": n,
|
|
3196
|
-
"aria-label":
|
|
3197
|
-
onClick:
|
|
3208
|
+
"aria-label": r.value,
|
|
3209
|
+
onClick: i
|
|
3198
3210
|
}, [f(Q, {
|
|
3199
3211
|
name: t.triggerIcon,
|
|
3200
3212
|
class: y(["transition-transform duration-200", { "rotate-180": n && t.triggerIcon === "chevron-down" }]),
|
|
3201
3213
|
"aria-hidden": "true"
|
|
3202
3214
|
}, null, 8, ["name", "class"])], 10, br)], 2)]),
|
|
3203
|
-
content: R(() => [c("div", xr, [(w(!0), s(e, null, E(
|
|
3215
|
+
content: R(() => [c("div", xr, [(w(!0), s(e, null, E(x.value, (t, n) => (w(), s(e, { key: t.title || n }, [t.title ? (w(), s("div", {
|
|
3204
3216
|
key: 0,
|
|
3205
3217
|
class: y(["border-b border-gray-100 px-6 py-3 extra-small-normal text-gray-600", n > 0 ? "border-t pt-2" : ""])
|
|
3206
3218
|
}, k(t.title), 3)) : o("", !0), (w(!0), s(e, null, E(t.items, (e) => (w(), s("button", {
|
|
3207
3219
|
key: e.label,
|
|
3208
3220
|
role: e.checkbox ? "menuitemcheckbox" : "menuitem",
|
|
3209
|
-
"aria-checked": e.checkbox ?
|
|
3221
|
+
"aria-checked": e.checkbox ? g.value[e.label] ?? !1 : void 0,
|
|
3210
3222
|
class: y(["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", !e.checkbox && e.selected ? "bg-cornflower-blue-100" : ""]),
|
|
3211
|
-
onClick: (t) =>
|
|
3223
|
+
onClick: (t) => v(e)
|
|
3212
3224
|
}, [e.checkbox ? (w(), a(tn, {
|
|
3213
3225
|
key: 0,
|
|
3214
|
-
"model-value":
|
|
3226
|
+
"model-value": g.value[e.label] ?? !1,
|
|
3215
3227
|
class: "pointer-events-none"
|
|
3216
3228
|
}, null, 8, ["model-value"])) : e.icon ? (w(), a(Q, {
|
|
3217
3229
|
key: 1,
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script lang="ts" setup>
|
|
2
|
-
import { ref, computed } from 'vue'
|
|
2
|
+
import { ref, computed, watch } from 'vue'
|
|
3
3
|
import type { Placement } from '@floating-ui/vue'
|
|
4
4
|
import NexxtIcon from '../Icon/Icon.vue'
|
|
5
5
|
import NexxtCheckbox from '../Checkbox/Checkbox.vue'
|
|
@@ -20,6 +20,12 @@ interface NexxtDropdownButtonGroup {
|
|
|
20
20
|
|
|
21
21
|
type NexxtDropdownButtonBase = {
|
|
22
22
|
variant?: 'primary' | 'secondary'
|
|
23
|
+
/**
|
|
24
|
+
* Accessible name for the trigger button. Supply this whenever the button
|
|
25
|
+
* has no visible text, which is every `iconOnly` case. Defaults to the
|
|
26
|
+
* visible label when there is one, and otherwise to a generic Dutch label.
|
|
27
|
+
*/
|
|
28
|
+
ariaLabel?: string
|
|
23
29
|
options: NexxtDropdownButton[] | NexxtDropdownButtonGroup[]
|
|
24
30
|
placement?: 'left' | 'right'
|
|
25
31
|
triggerIcon?: InstanceType<typeof NexxtIcon>['name']
|
|
@@ -39,8 +45,16 @@ const {
|
|
|
39
45
|
placement = 'right',
|
|
40
46
|
iconOnly = false,
|
|
41
47
|
triggerIcon = 'chevron-down',
|
|
48
|
+
ariaLabel,
|
|
42
49
|
} = defineProps<NexxtDropdownButtonProps>()
|
|
43
50
|
|
|
51
|
+
// `label` is optional in the `iconOnly` branch of the props union, so
|
|
52
|
+
// interpolating it produced a literal "Open undefined menu" on every icon-only
|
|
53
|
+
// trigger. The fallback chain never yields `undefined`.
|
|
54
|
+
const triggerAriaLabel = computed(
|
|
55
|
+
() => ariaLabel ?? (label ? `${label} menu openen` : 'Menu openen'),
|
|
56
|
+
)
|
|
57
|
+
|
|
44
58
|
const emit = defineEmits<{
|
|
45
59
|
navigate: [url: string]
|
|
46
60
|
checkboxChange: [label: string, checked: boolean]
|
|
@@ -58,8 +72,23 @@ const flatItems = computed<NexxtDropdownButton[]>(() =>
|
|
|
58
72
|
: (options as NexxtDropdownButton[]),
|
|
59
73
|
)
|
|
60
74
|
|
|
75
|
+
// Local echo of a click, so a checkbox responds instantly instead of waiting
|
|
76
|
+
// for the parent to feed `selected` back in.
|
|
61
77
|
const toggledItems = ref<Record<string, boolean>>({})
|
|
62
78
|
|
|
79
|
+
// The echo must not outlive the prop it shadows. Once an item was toggled its
|
|
80
|
+
// label stayed in `toggledItems` forever, so `selected` was permanently
|
|
81
|
+
// ignored and a parent could never reset a filter menu. Clearing the echo
|
|
82
|
+
// whenever the incoming `selected` values change hands control back to the
|
|
83
|
+
// parent while keeping the component usable uncontrolled.
|
|
84
|
+
watch(
|
|
85
|
+
() => flatItems.value.filter((item) => item.checkbox).map((item) => item.selected ?? false),
|
|
86
|
+
() => {
|
|
87
|
+
toggledItems.value = {}
|
|
88
|
+
},
|
|
89
|
+
{ deep: true },
|
|
90
|
+
)
|
|
91
|
+
|
|
63
92
|
const checkedItems = computed(() =>
|
|
64
93
|
Object.fromEntries(
|
|
65
94
|
flatItems.value
|
|
@@ -150,7 +179,7 @@ const buttonClasses = computed(() => {
|
|
|
150
179
|
:class="[buttonClasses, iconOnly ? 'p-2' : 'px-2 py-2']"
|
|
151
180
|
aria-haspopup="menu"
|
|
152
181
|
:aria-expanded="isOpen"
|
|
153
|
-
:aria-label="
|
|
182
|
+
:aria-label="triggerAriaLabel"
|
|
154
183
|
@click="toggle"
|
|
155
184
|
>
|
|
156
185
|
<NexxtIcon
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { computed, getCurrentInstance } from 'vue'
|
|
2
|
+
import { computed, getCurrentInstance, useTemplateRef } from 'vue'
|
|
3
3
|
import Icon from '../Icon/Icon.vue'
|
|
4
4
|
|
|
5
5
|
const INPUT_ID = 'input-field-' + Math.random().toString(36).slice(2, 10)
|
|
@@ -22,6 +22,17 @@ defineProps<NexxtInputFieldProps>()
|
|
|
22
22
|
|
|
23
23
|
const modelValue = defineModel<string | number>()
|
|
24
24
|
|
|
25
|
+
const inputEl = useTemplateRef<HTMLInputElement>('inputEl')
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Focus the underlying input. Exposed so callers do not have to reach through
|
|
29
|
+
* the component with `$el.querySelector('input')`, which breaks the moment the
|
|
30
|
+
* internal markup changes.
|
|
31
|
+
*/
|
|
32
|
+
const focus = () => inputEl.value?.focus()
|
|
33
|
+
|
|
34
|
+
defineExpose({ focus, inputEl })
|
|
35
|
+
|
|
25
36
|
const isNumber = computed(() => {
|
|
26
37
|
const instance = getCurrentInstance()
|
|
27
38
|
return instance?.attrs.type === 'number'
|
|
@@ -58,6 +69,7 @@ const decrement = () => {
|
|
|
58
69
|
</span>
|
|
59
70
|
|
|
60
71
|
<input
|
|
72
|
+
ref="inputEl"
|
|
61
73
|
:id="INPUT_ID"
|
|
62
74
|
v-model="modelValue"
|
|
63
75
|
:placeholder="placeholder"
|