@stackline/vue-multiselect-dropdown 2.0.0 → 3.0.1

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/README.md CHANGED
@@ -1,48 +1,45 @@
1
1
  # @stackline/vue-multiselect-dropdown
2
2
 
3
- > A maintained Vue 2 multiselect dropdown with controlled state, searchable/grouped options, lazy loading hooks, render functions, skins, body-overlay positioning, and ADA-friendly keyboard/ARIA behavior.
3
+ > A maintained Vue 3 multiselect dropdown with controlled state, searchable/grouped options, lazy loading hooks, render functions, skins, body-overlay positioning, and ADA-compliant keyboard/ARIA behavior.
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/@stackline/vue-multiselect-dropdown.svg?style=flat-square)](https://www.npmjs.com/package/@stackline/vue-multiselect-dropdown)
6
6
  [![license](https://img.shields.io/npm/l/@stackline/vue-multiselect-dropdown.svg?style=flat-square)](https://github.com/alexandroit/vue-multiselect-dropdown/blob/main/LICENSE)
7
- [![Vue 2](https://img.shields.io/badge/Vue-2.x-42b883?style=flat-square&logo=vue.js)](https://alexandro.net/docs/vue/multiselect/vue-2/)
7
+ [![Vue 3](https://img.shields.io/badge/Vue-3.x-42b883?style=flat-square&logo=vue.js)](https://alexandro.net/docs/vue/multiselect/vue-3/)
8
8
 
9
- **[Documentation & Live Demos](https://alexandro.net/docs/vue/multiselect/)** | **[Vue 2 Demo](https://alexandro.net/docs/vue/multiselect/vue-2/)** | **[npm](https://www.npmjs.com/package/@stackline/vue-multiselect-dropdown)** | **[Repository](https://github.com/alexandroit/vue-multiselect-dropdown)**
9
+ **[Documentation & Live Demos](https://alexandro.net/docs/vue/multiselect/)** | **[Vue 3 Demo](https://alexandro.net/docs/vue/multiselect/vue-3/)** | **[npm](https://www.npmjs.com/package/@stackline/vue-multiselect-dropdown)** | **[Repository](https://github.com/alexandroit/vue-multiselect-dropdown)**
10
10
 
11
- **Current validation package release:** `2.0.0` for Vue `2.x`
11
+ **Current validation package release:** `3.0.1` for Vue `3.x`
12
12
 
13
13
  ---
14
14
 
15
15
  ## Why this library?
16
16
 
17
- `@stackline/vue-multiselect-dropdown` provides a maintained Vue 2 multiselect component for applications that need predictable selection state, search, grouping, skins, keyboard support, and live tested examples.
17
+ `@stackline/vue-multiselect-dropdown` provides a maintained Vue 3 multiselect component for applications that need predictable selection state, search, grouping, skins, keyboard support, and live tested examples.
18
18
 
19
- The package follows a familiar Stackline settings contract while staying idiomatic for Vue 2: bind with `v-model`, pass `:data`, customize behavior through `:settings`, and listen for `@select`, `@de-select`, `@select-all`, `@de-select-all`, `@open`, and `@close`.
19
+ The package follows a familiar Stackline settings contract while staying idiomatic for Vue 3: bind with `v-model`, pass `:data`, customize behavior through `:settings`, and listen for `@select`, `@de-select`, `@select-all`, `@de-select-all`, `@open`, and `@close`.
20
20
 
21
21
  ## Vue Version Compatibility
22
22
 
23
23
  | Package family | Vue family | Peer range | First tested runtime | Demo link |
24
24
  | :---: | :---: | :---: | :---: | :--- |
25
- | **2.x** | **Vue 2 only** | **`>=2.0.0 <3.0.0`** | **2.0.0** | [Vue 2 family docs](https://alexandro.net/docs/vue/multiselect/vue-2/) |
25
+ | **3.x** | **Vue 3 only** | **`>=3.0.0 <4.0.0`** | **3.0.0** | [Vue 3 family docs](https://alexandro.net/docs/vue/multiselect/vue-3/) |
26
26
 
27
27
  ## Installation
28
28
 
29
29
  ```bash
30
- npm install @stackline/vue-multiselect-dropdown@2.0.0 --save-exact
30
+ npm install @stackline/vue-multiselect-dropdown@3.0.1 --save-exact
31
31
  ```
32
32
 
33
33
  ## Setup
34
34
 
35
35
  ```js
36
- import Vue from 'vue';
36
+ import { createApp } from 'vue';
37
37
  import {
38
38
  VueMultiselect,
39
39
  VueMultiselectDropdown
40
40
  } from '@stackline/vue-multiselect-dropdown';
41
41
 
42
- Vue.use(VueMultiselect);
43
-
44
- new Vue({
45
- el: '#app',
42
+ const app = createApp({
46
43
  components: { VueMultiselectDropdown },
47
44
  data() {
48
45
  return {
@@ -63,6 +60,9 @@ new Vue({
63
60
  };
64
61
  }
65
62
  });
63
+
64
+ app.use(VueMultiselect);
65
+ app.mount('#app');
66
66
  ```
67
67
 
68
68
  ```html
@@ -129,10 +129,10 @@ npm run build
129
129
  npm test
130
130
  ```
131
131
 
132
- Vue 2 docs:
132
+ Vue 3 docs:
133
133
 
134
134
  ```bash
135
- cd docs-src/vue-2
135
+ cd docs-src/vue-3
136
136
  npm install
137
137
  npm run build
138
138
  ```
package/dist/index.cjs CHANGED
@@ -27,6 +27,9 @@ __export(index_exports, {
27
27
  });
28
28
  module.exports = __toCommonJS(index_exports);
29
29
 
30
+ // src/VueMultiselectDropdown.ts
31
+ var import_vue = require("vue");
32
+
30
33
  // src/styles.ts
31
34
  var STYLE_ID = "stackline-vue-multiselect-dropdown-styles";
32
35
  var styles = `
@@ -69,6 +72,7 @@ var styles = `
69
72
  position: relative;
70
73
  display: flex;
71
74
  align-items: center;
75
+ align-content: center;
72
76
  flex-wrap: wrap;
73
77
  width: 100%;
74
78
  min-height: 56px;
@@ -106,15 +110,23 @@ var styles = `
106
110
  flex: 1 1 auto;
107
111
  min-width: 0;
108
112
  align-items: center;
113
+ align-content: center;
109
114
  gap: 8px;
110
115
  flex-wrap: wrap;
111
116
  }
112
117
 
113
118
  .vmsd-placeholder,
114
119
  .vmsd-single-value {
120
+ display: inline-flex;
121
+ align-items: center;
122
+ align-self: center;
123
+ justify-content: flex-start;
115
124
  min-width: 0;
125
+ max-width: 100%;
116
126
  color: var(--vmsd-muted);
117
127
  font-size: 0.95rem;
128
+ line-height: 1.25;
129
+ text-align: left;
118
130
  overflow: hidden;
119
131
  text-overflow: ellipsis;
120
132
  white-space: nowrap;
@@ -706,6 +718,7 @@ var styles = `
706
718
 
707
719
  .theme-classic .vmsd-trigger,
708
720
  .skin-classic .vmsd-trigger {
721
+ align-content: center;
709
722
  flex-wrap: nowrap;
710
723
  gap: 6px;
711
724
  min-height: 42px;
@@ -1063,7 +1076,7 @@ var styles = `
1063
1076
  }
1064
1077
  }
1065
1078
 
1066
- /* stackline-vue2-live-20260527 */
1079
+ /* stackline-vue3-live-20260527 */
1067
1080
  `;
1068
1081
  function ensureDropdownStyles() {
1069
1082
  if (typeof document === "undefined") {
@@ -1141,12 +1154,45 @@ function iconPath(name) {
1141
1154
  }
1142
1155
  return "M604.501,134.782c-9.999-10.05-26.222-10.05-36.221,0L306.014,422.558L43.721,134.782c-9.999-10.05-26.223-10.05-36.222,0s-9.999,26.35,0,36.399l279.103,306.241c5.331,5.357,12.422,7.652,19.386,7.296c6.988,0.356,14.055-1.939,19.386-7.296l279.128-306.268C614.5,161.106,614.5,144.832,604.501,134.782z";
1143
1156
  }
1144
- function renderIcon(h, name, className = "vmsd-icon") {
1157
+ function renderIcon(h2, name, className = "vmsd-icon") {
1145
1158
  const viewBox = name === "remove" ? "0 0 47.971 47.971" : name === "clear" ? "0 0 51.976 51.976" : name === "search" ? "0 0 615.52 615.52" : "0 0 612 612";
1146
- return h("svg", { class: className, attrs: { viewBox, focusable: "false", "aria-hidden": "true" } }, [
1147
- h("path", { attrs: { d: iconPath(name) } })
1159
+ return h2("svg", { class: className, attrs: { viewBox, focusable: "false", "aria-hidden": "true" } }, [
1160
+ h2("path", { attrs: { d: iconPath(name) } })
1148
1161
  ]);
1149
1162
  }
1163
+ function capitalize(value) {
1164
+ return value ? value.charAt(0).toUpperCase() + value.slice(1) : value;
1165
+ }
1166
+ function normalizeVue2RenderData(data) {
1167
+ if (!data) {
1168
+ return null;
1169
+ }
1170
+ const props = {};
1171
+ for (const [key, value] of Object.entries(data)) {
1172
+ if (key === "attrs" || key === "domProps") {
1173
+ Object.assign(props, value);
1174
+ continue;
1175
+ }
1176
+ if (key === "on" && value && typeof value === "object") {
1177
+ for (const [eventName, handler] of Object.entries(value)) {
1178
+ props[`on${capitalize(eventName)}`] = handler;
1179
+ }
1180
+ continue;
1181
+ }
1182
+ props[key] = value;
1183
+ }
1184
+ return props;
1185
+ }
1186
+ var h = (...args) => {
1187
+ const [tag, data, children] = args;
1188
+ if (args.length === 1) {
1189
+ return (0, import_vue.h)(tag);
1190
+ }
1191
+ if (args.length === 2 && (Array.isArray(data) || typeof data === "string" || typeof data === "number" || data == null)) {
1192
+ return (0, import_vue.h)(tag, null, data);
1193
+ }
1194
+ return (0, import_vue.h)(tag, normalizeVue2RenderData(data), children);
1195
+ };
1150
1196
  function isPrimitiveItem(item) {
1151
1197
  return typeof item === "string" || typeof item === "number" || typeof item === "boolean";
1152
1198
  }
@@ -1235,11 +1281,11 @@ function mergeUniqueItems(base, extra, settings) {
1235
1281
  }
1236
1282
  return Array.from(bucket.values());
1237
1283
  }
1238
- function callRenderFunction(renderFunction, h, item, context) {
1284
+ function callRenderFunction(renderFunction, h2, item, context) {
1239
1285
  if (!renderFunction) {
1240
1286
  return null;
1241
1287
  }
1242
- return renderFunction(item, context, h);
1288
+ return renderFunction(item, context, h2);
1243
1289
  }
1244
1290
  function escapeSelectorValue(value) {
1245
1291
  if (typeof CSS !== "undefined" && typeof CSS.escape === "function") {
@@ -1259,15 +1305,31 @@ function isTextInputTarget(target) {
1259
1305
  }
1260
1306
  var VueMultiselectDropdown = {
1261
1307
  name: "VueMultiselectDropdown",
1262
- model: {
1263
- prop: "value",
1264
- event: "input"
1265
- },
1308
+ emits: [
1309
+ "update:modelValue",
1310
+ "input",
1311
+ "update:selectedItems",
1312
+ "change",
1313
+ "select",
1314
+ "de-select",
1315
+ "select-all",
1316
+ "de-select-all",
1317
+ "group-select",
1318
+ "group-de-select",
1319
+ "scroll-to-end",
1320
+ "add-filter-new-item",
1321
+ "open",
1322
+ "close"
1323
+ ],
1266
1324
  props: {
1267
1325
  data: {
1268
1326
  type: Array,
1269
1327
  default: () => []
1270
1328
  },
1329
+ modelValue: {
1330
+ type: Array,
1331
+ default: void 0
1332
+ },
1271
1333
  value: {
1272
1334
  type: Array,
1273
1335
  default: void 0
@@ -1333,6 +1395,9 @@ var VueMultiselectDropdown = {
1333
1395
  if (Array.isArray(this.selectedItems)) {
1334
1396
  return this.selectedItems;
1335
1397
  }
1398
+ if (Array.isArray(this.modelValue)) {
1399
+ return this.modelValue;
1400
+ }
1336
1401
  if (Array.isArray(this.value)) {
1337
1402
  return this.value;
1338
1403
  }
@@ -1397,7 +1462,7 @@ var VueMultiselectDropdown = {
1397
1462
  window.addEventListener("resize", this.updateMenuPosition);
1398
1463
  window.addEventListener("scroll", this.updateMenuPosition, true);
1399
1464
  },
1400
- beforeDestroy() {
1465
+ beforeUnmount() {
1401
1466
  document.removeEventListener("click", this.onDocumentClick, true);
1402
1467
  document.removeEventListener("keydown", this.onDocumentKeydown, true);
1403
1468
  window.removeEventListener("resize", this.updateMenuPosition);
@@ -1419,9 +1484,10 @@ var VueMultiselectDropdown = {
1419
1484
  return this.filteredItems.filter((item) => !isDisabledItem(item));
1420
1485
  },
1421
1486
  emitSelection(items) {
1422
- if (!Array.isArray(this.selectedItems) && !Array.isArray(this.value)) {
1487
+ if (!Array.isArray(this.selectedItems) && !Array.isArray(this.modelValue) && !Array.isArray(this.value)) {
1423
1488
  this.internalSelected = items;
1424
1489
  }
1490
+ this.$emit("update:modelValue", items);
1425
1491
  this.$emit("input", items);
1426
1492
  this.$emit("update:selectedItems", items);
1427
1493
  this.$emit("change", items);
@@ -1786,7 +1852,7 @@ var VueMultiselectDropdown = {
1786
1852
  this.menuStyle = style;
1787
1853
  }
1788
1854
  },
1789
- render(h) {
1855
+ render() {
1790
1856
  const settings = this.resolvedSettings;
1791
1857
  const skin = String(settings.skin || settings.theme || "classic");
1792
1858
  const skinFallbackClass = ["classic", "material", "dark", "custom"].includes(skin) ? "" : "theme-custom";
@@ -2074,11 +2140,11 @@ var StacklineVueMultiselect = VueMultiselectDropdown;
2074
2140
 
2075
2141
  // src/plugin.ts
2076
2142
  var VueMultiselect = {
2077
- install(Vue) {
2078
- Vue.component("StacklineVueMultiselect", VueMultiselectDropdown);
2079
- Vue.component("VueMultiselectDropdown", VueMultiselectDropdown);
2080
- Vue.component("vue-multiselect-dropdown", VueMultiselectDropdown);
2081
- Vue.component("stackline-vue-multiselect", VueMultiselectDropdown);
2143
+ install(app) {
2144
+ app.component("StacklineVueMultiselect", VueMultiselectDropdown);
2145
+ app.component("VueMultiselectDropdown", VueMultiselectDropdown);
2146
+ app.component("vue-multiselect-dropdown", VueMultiselectDropdown);
2147
+ app.component("stackline-vue-multiselect", VueMultiselectDropdown);
2082
2148
  }
2083
2149
  };
2084
2150
  var plugin_default = VueMultiselect;
package/dist/index.d.cts CHANGED
@@ -70,22 +70,22 @@ interface VueMultiselectPlugin {
70
70
  install: (Vue: any) => void;
71
71
  }
72
72
 
73
- type CreateElement = (...args: any[]) => any;
74
73
  type GroupedItems<T> = Array<{
75
74
  name: string;
76
75
  items: T[];
77
76
  }>;
78
77
  declare const VueMultiselectDropdown: {
79
78
  name: string;
80
- model: {
81
- prop: string;
82
- event: string;
83
- };
79
+ emits: string[];
84
80
  props: {
85
81
  data: {
86
82
  type: ArrayConstructor;
87
83
  default: () => never[];
88
84
  };
85
+ modelValue: {
86
+ type: ArrayConstructor;
87
+ default: undefined;
88
+ };
89
89
  value: {
90
90
  type: ArrayConstructor;
91
91
  default: undefined;
@@ -151,7 +151,7 @@ declare const VueMultiselectDropdown: {
151
151
  };
152
152
  };
153
153
  mounted(this: any): void;
154
- beforeDestroy(this: any): void;
154
+ beforeUnmount(this: any): void;
155
155
  methods: {
156
156
  getLabel(this: any, item: DropdownItem): string;
157
157
  getKey(this: any, item: DropdownItem): string;
@@ -183,19 +183,20 @@ declare const VueMultiselectDropdown: {
183
183
  restoreMenuToComponent(this: any): void;
184
184
  updateMenuPosition(this: any): void;
185
185
  };
186
- render(this: any, h: CreateElement): any;
186
+ render(this: any): any;
187
187
  };
188
188
  declare const StacklineVueMultiselect: {
189
189
  name: string;
190
- model: {
191
- prop: string;
192
- event: string;
193
- };
190
+ emits: string[];
194
191
  props: {
195
192
  data: {
196
193
  type: ArrayConstructor;
197
194
  default: () => never[];
198
195
  };
196
+ modelValue: {
197
+ type: ArrayConstructor;
198
+ default: undefined;
199
+ };
199
200
  value: {
200
201
  type: ArrayConstructor;
201
202
  default: undefined;
@@ -261,7 +262,7 @@ declare const StacklineVueMultiselect: {
261
262
  };
262
263
  };
263
264
  mounted(this: any): void;
264
- beforeDestroy(this: any): void;
265
+ beforeUnmount(this: any): void;
265
266
  methods: {
266
267
  getLabel(this: any, item: DropdownItem): string;
267
268
  getKey(this: any, item: DropdownItem): string;
@@ -293,7 +294,7 @@ declare const StacklineVueMultiselect: {
293
294
  restoreMenuToComponent(this: any): void;
294
295
  updateMenuPosition(this: any): void;
295
296
  };
296
- render(this: any, h: CreateElement): any;
297
+ render(this: any): any;
297
298
  };
298
299
 
299
300
  declare const VueMultiselect: VueMultiselectPlugin;
package/dist/index.d.ts CHANGED
@@ -70,22 +70,22 @@ interface VueMultiselectPlugin {
70
70
  install: (Vue: any) => void;
71
71
  }
72
72
 
73
- type CreateElement = (...args: any[]) => any;
74
73
  type GroupedItems<T> = Array<{
75
74
  name: string;
76
75
  items: T[];
77
76
  }>;
78
77
  declare const VueMultiselectDropdown: {
79
78
  name: string;
80
- model: {
81
- prop: string;
82
- event: string;
83
- };
79
+ emits: string[];
84
80
  props: {
85
81
  data: {
86
82
  type: ArrayConstructor;
87
83
  default: () => never[];
88
84
  };
85
+ modelValue: {
86
+ type: ArrayConstructor;
87
+ default: undefined;
88
+ };
89
89
  value: {
90
90
  type: ArrayConstructor;
91
91
  default: undefined;
@@ -151,7 +151,7 @@ declare const VueMultiselectDropdown: {
151
151
  };
152
152
  };
153
153
  mounted(this: any): void;
154
- beforeDestroy(this: any): void;
154
+ beforeUnmount(this: any): void;
155
155
  methods: {
156
156
  getLabel(this: any, item: DropdownItem): string;
157
157
  getKey(this: any, item: DropdownItem): string;
@@ -183,19 +183,20 @@ declare const VueMultiselectDropdown: {
183
183
  restoreMenuToComponent(this: any): void;
184
184
  updateMenuPosition(this: any): void;
185
185
  };
186
- render(this: any, h: CreateElement): any;
186
+ render(this: any): any;
187
187
  };
188
188
  declare const StacklineVueMultiselect: {
189
189
  name: string;
190
- model: {
191
- prop: string;
192
- event: string;
193
- };
190
+ emits: string[];
194
191
  props: {
195
192
  data: {
196
193
  type: ArrayConstructor;
197
194
  default: () => never[];
198
195
  };
196
+ modelValue: {
197
+ type: ArrayConstructor;
198
+ default: undefined;
199
+ };
199
200
  value: {
200
201
  type: ArrayConstructor;
201
202
  default: undefined;
@@ -261,7 +262,7 @@ declare const StacklineVueMultiselect: {
261
262
  };
262
263
  };
263
264
  mounted(this: any): void;
264
- beforeDestroy(this: any): void;
265
+ beforeUnmount(this: any): void;
265
266
  methods: {
266
267
  getLabel(this: any, item: DropdownItem): string;
267
268
  getKey(this: any, item: DropdownItem): string;
@@ -293,7 +294,7 @@ declare const StacklineVueMultiselect: {
293
294
  restoreMenuToComponent(this: any): void;
294
295
  updateMenuPosition(this: any): void;
295
296
  };
296
- render(this: any, h: CreateElement): any;
297
+ render(this: any): any;
297
298
  };
298
299
 
299
300
  declare const VueMultiselect: VueMultiselectPlugin;
package/dist/index.js CHANGED
@@ -1,3 +1,6 @@
1
+ // src/VueMultiselectDropdown.ts
2
+ import { h as vueH } from "vue";
3
+
1
4
  // src/styles.ts
2
5
  var STYLE_ID = "stackline-vue-multiselect-dropdown-styles";
3
6
  var styles = `
@@ -40,6 +43,7 @@ var styles = `
40
43
  position: relative;
41
44
  display: flex;
42
45
  align-items: center;
46
+ align-content: center;
43
47
  flex-wrap: wrap;
44
48
  width: 100%;
45
49
  min-height: 56px;
@@ -77,15 +81,23 @@ var styles = `
77
81
  flex: 1 1 auto;
78
82
  min-width: 0;
79
83
  align-items: center;
84
+ align-content: center;
80
85
  gap: 8px;
81
86
  flex-wrap: wrap;
82
87
  }
83
88
 
84
89
  .vmsd-placeholder,
85
90
  .vmsd-single-value {
91
+ display: inline-flex;
92
+ align-items: center;
93
+ align-self: center;
94
+ justify-content: flex-start;
86
95
  min-width: 0;
96
+ max-width: 100%;
87
97
  color: var(--vmsd-muted);
88
98
  font-size: 0.95rem;
99
+ line-height: 1.25;
100
+ text-align: left;
89
101
  overflow: hidden;
90
102
  text-overflow: ellipsis;
91
103
  white-space: nowrap;
@@ -677,6 +689,7 @@ var styles = `
677
689
 
678
690
  .theme-classic .vmsd-trigger,
679
691
  .skin-classic .vmsd-trigger {
692
+ align-content: center;
680
693
  flex-wrap: nowrap;
681
694
  gap: 6px;
682
695
  min-height: 42px;
@@ -1034,7 +1047,7 @@ var styles = `
1034
1047
  }
1035
1048
  }
1036
1049
 
1037
- /* stackline-vue2-live-20260527 */
1050
+ /* stackline-vue3-live-20260527 */
1038
1051
  `;
1039
1052
  function ensureDropdownStyles() {
1040
1053
  if (typeof document === "undefined") {
@@ -1112,12 +1125,45 @@ function iconPath(name) {
1112
1125
  }
1113
1126
  return "M604.501,134.782c-9.999-10.05-26.222-10.05-36.221,0L306.014,422.558L43.721,134.782c-9.999-10.05-26.223-10.05-36.222,0s-9.999,26.35,0,36.399l279.103,306.241c5.331,5.357,12.422,7.652,19.386,7.296c6.988,0.356,14.055-1.939,19.386-7.296l279.128-306.268C614.5,161.106,614.5,144.832,604.501,134.782z";
1114
1127
  }
1115
- function renderIcon(h, name, className = "vmsd-icon") {
1128
+ function renderIcon(h2, name, className = "vmsd-icon") {
1116
1129
  const viewBox = name === "remove" ? "0 0 47.971 47.971" : name === "clear" ? "0 0 51.976 51.976" : name === "search" ? "0 0 615.52 615.52" : "0 0 612 612";
1117
- return h("svg", { class: className, attrs: { viewBox, focusable: "false", "aria-hidden": "true" } }, [
1118
- h("path", { attrs: { d: iconPath(name) } })
1130
+ return h2("svg", { class: className, attrs: { viewBox, focusable: "false", "aria-hidden": "true" } }, [
1131
+ h2("path", { attrs: { d: iconPath(name) } })
1119
1132
  ]);
1120
1133
  }
1134
+ function capitalize(value) {
1135
+ return value ? value.charAt(0).toUpperCase() + value.slice(1) : value;
1136
+ }
1137
+ function normalizeVue2RenderData(data) {
1138
+ if (!data) {
1139
+ return null;
1140
+ }
1141
+ const props = {};
1142
+ for (const [key, value] of Object.entries(data)) {
1143
+ if (key === "attrs" || key === "domProps") {
1144
+ Object.assign(props, value);
1145
+ continue;
1146
+ }
1147
+ if (key === "on" && value && typeof value === "object") {
1148
+ for (const [eventName, handler] of Object.entries(value)) {
1149
+ props[`on${capitalize(eventName)}`] = handler;
1150
+ }
1151
+ continue;
1152
+ }
1153
+ props[key] = value;
1154
+ }
1155
+ return props;
1156
+ }
1157
+ var h = (...args) => {
1158
+ const [tag, data, children] = args;
1159
+ if (args.length === 1) {
1160
+ return vueH(tag);
1161
+ }
1162
+ if (args.length === 2 && (Array.isArray(data) || typeof data === "string" || typeof data === "number" || data == null)) {
1163
+ return vueH(tag, null, data);
1164
+ }
1165
+ return vueH(tag, normalizeVue2RenderData(data), children);
1166
+ };
1121
1167
  function isPrimitiveItem(item) {
1122
1168
  return typeof item === "string" || typeof item === "number" || typeof item === "boolean";
1123
1169
  }
@@ -1206,11 +1252,11 @@ function mergeUniqueItems(base, extra, settings) {
1206
1252
  }
1207
1253
  return Array.from(bucket.values());
1208
1254
  }
1209
- function callRenderFunction(renderFunction, h, item, context) {
1255
+ function callRenderFunction(renderFunction, h2, item, context) {
1210
1256
  if (!renderFunction) {
1211
1257
  return null;
1212
1258
  }
1213
- return renderFunction(item, context, h);
1259
+ return renderFunction(item, context, h2);
1214
1260
  }
1215
1261
  function escapeSelectorValue(value) {
1216
1262
  if (typeof CSS !== "undefined" && typeof CSS.escape === "function") {
@@ -1230,15 +1276,31 @@ function isTextInputTarget(target) {
1230
1276
  }
1231
1277
  var VueMultiselectDropdown = {
1232
1278
  name: "VueMultiselectDropdown",
1233
- model: {
1234
- prop: "value",
1235
- event: "input"
1236
- },
1279
+ emits: [
1280
+ "update:modelValue",
1281
+ "input",
1282
+ "update:selectedItems",
1283
+ "change",
1284
+ "select",
1285
+ "de-select",
1286
+ "select-all",
1287
+ "de-select-all",
1288
+ "group-select",
1289
+ "group-de-select",
1290
+ "scroll-to-end",
1291
+ "add-filter-new-item",
1292
+ "open",
1293
+ "close"
1294
+ ],
1237
1295
  props: {
1238
1296
  data: {
1239
1297
  type: Array,
1240
1298
  default: () => []
1241
1299
  },
1300
+ modelValue: {
1301
+ type: Array,
1302
+ default: void 0
1303
+ },
1242
1304
  value: {
1243
1305
  type: Array,
1244
1306
  default: void 0
@@ -1304,6 +1366,9 @@ var VueMultiselectDropdown = {
1304
1366
  if (Array.isArray(this.selectedItems)) {
1305
1367
  return this.selectedItems;
1306
1368
  }
1369
+ if (Array.isArray(this.modelValue)) {
1370
+ return this.modelValue;
1371
+ }
1307
1372
  if (Array.isArray(this.value)) {
1308
1373
  return this.value;
1309
1374
  }
@@ -1368,7 +1433,7 @@ var VueMultiselectDropdown = {
1368
1433
  window.addEventListener("resize", this.updateMenuPosition);
1369
1434
  window.addEventListener("scroll", this.updateMenuPosition, true);
1370
1435
  },
1371
- beforeDestroy() {
1436
+ beforeUnmount() {
1372
1437
  document.removeEventListener("click", this.onDocumentClick, true);
1373
1438
  document.removeEventListener("keydown", this.onDocumentKeydown, true);
1374
1439
  window.removeEventListener("resize", this.updateMenuPosition);
@@ -1390,9 +1455,10 @@ var VueMultiselectDropdown = {
1390
1455
  return this.filteredItems.filter((item) => !isDisabledItem(item));
1391
1456
  },
1392
1457
  emitSelection(items) {
1393
- if (!Array.isArray(this.selectedItems) && !Array.isArray(this.value)) {
1458
+ if (!Array.isArray(this.selectedItems) && !Array.isArray(this.modelValue) && !Array.isArray(this.value)) {
1394
1459
  this.internalSelected = items;
1395
1460
  }
1461
+ this.$emit("update:modelValue", items);
1396
1462
  this.$emit("input", items);
1397
1463
  this.$emit("update:selectedItems", items);
1398
1464
  this.$emit("change", items);
@@ -1757,7 +1823,7 @@ var VueMultiselectDropdown = {
1757
1823
  this.menuStyle = style;
1758
1824
  }
1759
1825
  },
1760
- render(h) {
1826
+ render() {
1761
1827
  const settings = this.resolvedSettings;
1762
1828
  const skin = String(settings.skin || settings.theme || "classic");
1763
1829
  const skinFallbackClass = ["classic", "material", "dark", "custom"].includes(skin) ? "" : "theme-custom";
@@ -2045,11 +2111,11 @@ var StacklineVueMultiselect = VueMultiselectDropdown;
2045
2111
 
2046
2112
  // src/plugin.ts
2047
2113
  var VueMultiselect = {
2048
- install(Vue) {
2049
- Vue.component("StacklineVueMultiselect", VueMultiselectDropdown);
2050
- Vue.component("VueMultiselectDropdown", VueMultiselectDropdown);
2051
- Vue.component("vue-multiselect-dropdown", VueMultiselectDropdown);
2052
- Vue.component("stackline-vue-multiselect", VueMultiselectDropdown);
2114
+ install(app) {
2115
+ app.component("StacklineVueMultiselect", VueMultiselectDropdown);
2116
+ app.component("VueMultiselectDropdown", VueMultiselectDropdown);
2117
+ app.component("vue-multiselect-dropdown", VueMultiselectDropdown);
2118
+ app.component("stackline-vue-multiselect", VueMultiselectDropdown);
2053
2119
  }
2054
2120
  };
2055
2121
  var plugin_default = VueMultiselect;
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@stackline/vue-multiselect-dropdown",
3
- "version": "2.0.0",
4
- "description": "A maintained Vue 2 multiselect dropdown with controlled state, skins, live docs, body overlays, and ADA-friendly keyboard support.",
3
+ "version": "3.0.1",
4
+ "description": "Maintained Vue 3 multiselect dropdown with ADA-compliant keyboard/ARIA support, v-model state, Stackline skins, body overlays, live docs, search, grouping, lazy loading, and render functions.",
5
5
  "keywords": [
6
6
  "vue",
7
- "vue 2",
7
+ "vue 3",
8
8
  "multiselect",
9
9
  "multiselect-dropdown",
10
10
  "multi-select",
@@ -15,7 +15,7 @@
15
15
  "accessibility",
16
16
  "aria"
17
17
  ],
18
- "homepage": "https://alexandro.net/docs/vue/multiselect/vue-2/",
18
+ "homepage": "https://alexandro.net/docs/vue/multiselect/vue-3/",
19
19
  "bugs": {
20
20
  "url": "https://github.com/alexandroit/vue-multiselect-dropdown/issues"
21
21
  },
@@ -43,16 +43,19 @@
43
43
  "sideEffects": false,
44
44
  "scripts": {
45
45
  "build": "tsup",
46
- "clean": "rm -rf dist docs/vue-2",
46
+ "clean": "rm -rf dist docs/vue-3",
47
47
  "docs:install:vue-2": "cd docs-src/vue-2 && npm install",
48
+ "docs:install:vue-3": "cd docs-src/vue-3 && npm install",
48
49
  "build:docs:vue-2": "cd docs-src/vue-2 && npm run build",
49
- "build:docs": "npm run build:docs:vue-2",
50
+ "build:docs:vue-3": "cd docs-src/vue-3 && npm run build",
51
+ "build:docs": "npm run build:docs:vue-3",
50
52
  "publish:verdaccio:vue-2": "npm run build && npm publish --registry=http://127.0.0.1:4873 --@stackline:registry=http://127.0.0.1:4873 --tag vue-2 --access public",
53
+ "publish:verdaccio:vue-3": "npm run build && npm publish --registry=http://127.0.0.1:4873 --@stackline:registry=http://127.0.0.1:4873 --tag vue-3 --access public",
51
54
  "typecheck": "tsc --noEmit",
52
55
  "test": "node --test tests/*.test.cjs"
53
56
  },
54
57
  "peerDependencies": {
55
- "vue": ">=2.0.0 <3.0.0"
58
+ "vue": ">=3.0.0 <4.0.0"
56
59
  },
57
60
  "publishConfig": {
58
61
  "access": "public"
@@ -60,6 +63,6 @@
60
63
  "devDependencies": {
61
64
  "tsup": "8.5.1",
62
65
  "typescript": "5.9.3",
63
- "vue": "2.0.0"
66
+ "vue": "3.0.0"
64
67
  }
65
68
  }