@pequity/squirrel 6.0.7 → 6.0.9

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.
@@ -74,14 +74,14 @@ const _sfc_main = /* @__PURE__ */ vue.defineComponent({
74
74
  }
75
75
  },
76
76
  /**
77
- * Set property of **items**’s text value
77
+ * Set property of **items**'s text value
78
78
  */
79
79
  itemText: {
80
80
  type: String,
81
81
  default: "text"
82
82
  },
83
83
  /**
84
- * Set property of **items**’s value - must be primitive.
84
+ * Set property of **items**'s value - must be primitive.
85
85
  */
86
86
  itemValue: {
87
87
  type: [String, Number],
@@ -160,9 +160,16 @@ const _sfc_main = /* @__PURE__ */ vue.defineComponent({
160
160
  disabledBy: {
161
161
  type: String,
162
162
  default: "disabled"
163
+ },
164
+ /**
165
+ * Enables the ability to create new items when no search results are found
166
+ */
167
+ creatable: {
168
+ type: Boolean,
169
+ default: false
163
170
  }
164
171
  },
165
- emits: ["update:modelValue", "select"],
172
+ emits: ["update:modelValue", "select", "create"],
166
173
  setup(__props, { emit: __emit }) {
167
174
  const emit = __emit;
168
175
  const props = __props;
@@ -224,6 +231,9 @@ const _sfc_main = /* @__PURE__ */ vue.defineComponent({
224
231
  return rest;
225
232
  });
226
233
  const style = vue.computed(() => $attrs.style);
234
+ const selectableItemsCount = vue.computed(
235
+ () => internalItems.value.filter((item) => !isDisabled(item) || isSelected(item[props.itemValue])).length
236
+ );
227
237
  vue.watch(
228
238
  dropdownShow,
229
239
  (nV) => {
@@ -264,6 +274,10 @@ const _sfc_main = /* @__PURE__ */ vue.defineComponent({
264
274
  destroyNavigationSvc();
265
275
  (_b = (_a = formControl.value) == null ? void 0 : _a.querySelector("button")) == null ? void 0 : _b.focus();
266
276
  };
277
+ const handleCreate = () => {
278
+ emit("create", search.value);
279
+ dropdownShow.value = false;
280
+ };
267
281
  return (_ctx, _cache) => {
268
282
  const _directive_close_popper = vue.resolveDirective("close-popper");
269
283
  return vue.openBlock(), vue.createElementBlock("div", {
@@ -398,7 +412,18 @@ const _sfc_main = /* @__PURE__ */ vue.defineComponent({
398
412
  !vue.unref(computedItems).length ? vue.renderSlot(_ctx.$slots, "no-items", { key: 0 }, () => [
399
413
  vue.createElementVNode("div", {
400
414
  class: vue.normalizeClass(["flex items-center justify-center", vue.unref(pSelectList.SIZES)[__props.size]])
401
- }, "No items found", 2)
415
+ }, [
416
+ __props.creatable && vue.unref(search) ? (vue.openBlock(), vue.createElementBlock("button", {
417
+ key: 0,
418
+ class: "hover:text-primary-hover flex items-center gap-2 font-medium text-p-blue-40",
419
+ onClick: handleCreate
420
+ }, [
421
+ vue.createVNode(pIcon_vue_vue_type_script_setup_true_lang._sfc_main, { icon: "fe:plus-circle" }),
422
+ vue.createTextVNode(" Add '" + vue.toDisplayString(vue.unref(search)) + "' ", 1)
423
+ ])) : (vue.openBlock(), vue.createElementBlock(vue.Fragment, { key: 1 }, [
424
+ vue.createTextVNode("No items found")
425
+ ], 64))
426
+ ], 2)
402
427
  ]) : vue.createCommentVNode("", true)
403
428
  ], 6)
404
429
  ], 16)
@@ -418,7 +443,7 @@ const _sfc_main = /* @__PURE__ */ vue.defineComponent({
418
443
  key: 1,
419
444
  item: __props.multiple ? vue.unref(selectedItems) : vue.unref(selectedItems)[0]
420
445
  }, () => [
421
- vue.createElementVNode("div", _hoisted_3, vue.toDisplayString(__props.multiple && vue.unref(selectedItems).length > 1 ? `${vue.unref(selectedItems).length} option${vue.unref(selectedItems).length > 1 ? "s" : ""} selected` : vue.unref(selectedItems)[0][__props.itemText]), 1)
446
+ vue.createElementVNode("div", _hoisted_3, vue.toDisplayString(__props.multiple ? vue.unref(selectedItems).length === selectableItemsCount.value ? "All options selected" : `${vue.unref(selectedItems).length} option${vue.unref(selectedItems).length > 1 ? "s" : ""} selected` : vue.unref(selectedItems)[0][__props.itemText]), 1)
422
447
  ]),
423
448
  __props.clearable && vue.unref(selectedItems).length ? (vue.openBlock(), vue.createElementBlock("button", {
424
449
  key: 2,
@@ -1,4 +1,4 @@
1
- import { defineComponent, ref, useAttrs, computed, watch, onMounted, onUnmounted, resolveDirective, openBlock, createElementBlock, normalizeClass, unref, normalizeStyle, toDisplayString, createCommentVNode, createVNode, mergeProps, withCtx, createElementVNode, isRef, Fragment, renderList, withDirectives, renderSlot, withModifiers, vShow } from "vue";
1
+ import { defineComponent, ref, useAttrs, computed, watch, onMounted, onUnmounted, resolveDirective, openBlock, createElementBlock, normalizeClass, unref, normalizeStyle, toDisplayString, createCommentVNode, createVNode, mergeProps, withCtx, createElementVNode, isRef, Fragment, renderList, withDirectives, renderSlot, createTextVNode, withModifiers, vShow } from "vue";
2
2
  import PDropdown from "../p-dropdown.js";
3
3
  import { _ as _sfc_main$1 } from "./p-icon.js";
4
4
  import PInputSearch from "../p-input-search.js";
@@ -73,14 +73,14 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
73
73
  }
74
74
  },
75
75
  /**
76
- * Set property of **items**’s text value
76
+ * Set property of **items**'s text value
77
77
  */
78
78
  itemText: {
79
79
  type: String,
80
80
  default: "text"
81
81
  },
82
82
  /**
83
- * Set property of **items**’s value - must be primitive.
83
+ * Set property of **items**'s value - must be primitive.
84
84
  */
85
85
  itemValue: {
86
86
  type: [String, Number],
@@ -159,9 +159,16 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
159
159
  disabledBy: {
160
160
  type: String,
161
161
  default: "disabled"
162
+ },
163
+ /**
164
+ * Enables the ability to create new items when no search results are found
165
+ */
166
+ creatable: {
167
+ type: Boolean,
168
+ default: false
162
169
  }
163
170
  },
164
- emits: ["update:modelValue", "select"],
171
+ emits: ["update:modelValue", "select", "create"],
165
172
  setup(__props, { emit: __emit }) {
166
173
  const emit = __emit;
167
174
  const props = __props;
@@ -223,6 +230,9 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
223
230
  return rest;
224
231
  });
225
232
  const style = computed(() => $attrs.style);
233
+ const selectableItemsCount = computed(
234
+ () => internalItems.value.filter((item) => !isDisabled(item) || isSelected(item[props.itemValue])).length
235
+ );
226
236
  watch(
227
237
  dropdownShow,
228
238
  (nV) => {
@@ -263,6 +273,10 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
263
273
  destroyNavigationSvc();
264
274
  (_b = (_a = formControl.value) == null ? void 0 : _a.querySelector("button")) == null ? void 0 : _b.focus();
265
275
  };
276
+ const handleCreate = () => {
277
+ emit("create", search.value);
278
+ dropdownShow.value = false;
279
+ };
266
280
  return (_ctx, _cache) => {
267
281
  const _directive_close_popper = resolveDirective("close-popper");
268
282
  return openBlock(), createElementBlock("div", {
@@ -397,7 +411,18 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
397
411
  !unref(computedItems).length ? renderSlot(_ctx.$slots, "no-items", { key: 0 }, () => [
398
412
  createElementVNode("div", {
399
413
  class: normalizeClass(["flex items-center justify-center", unref(SIZES)[__props.size]])
400
- }, "No items found", 2)
414
+ }, [
415
+ __props.creatable && unref(search) ? (openBlock(), createElementBlock("button", {
416
+ key: 0,
417
+ class: "hover:text-primary-hover flex items-center gap-2 font-medium text-p-blue-40",
418
+ onClick: handleCreate
419
+ }, [
420
+ createVNode(_sfc_main$1, { icon: "fe:plus-circle" }),
421
+ createTextVNode(" Add '" + toDisplayString(unref(search)) + "' ", 1)
422
+ ])) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [
423
+ createTextVNode("No items found")
424
+ ], 64))
425
+ ], 2)
401
426
  ]) : createCommentVNode("", true)
402
427
  ], 6)
403
428
  ], 16)
@@ -417,7 +442,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
417
442
  key: 1,
418
443
  item: __props.multiple ? unref(selectedItems) : unref(selectedItems)[0]
419
444
  }, () => [
420
- createElementVNode("div", _hoisted_3, toDisplayString(__props.multiple && unref(selectedItems).length > 1 ? `${unref(selectedItems).length} option${unref(selectedItems).length > 1 ? "s" : ""} selected` : unref(selectedItems)[0][__props.itemText]), 1)
445
+ createElementVNode("div", _hoisted_3, toDisplayString(__props.multiple ? unref(selectedItems).length === selectableItemsCount.value ? "All options selected" : `${unref(selectedItems).length} option${unref(selectedItems).length > 1 ? "s" : ""} selected` : unref(selectedItems)[0][__props.itemText]), 1)
421
446
  ]),
422
447
  __props.clearable && unref(selectedItems).length ? (openBlock(), createElementBlock("button", {
423
448
  key: 2,
@@ -262,14 +262,14 @@ declare const __VLS_component: import("vue").DefineComponent<import("vue").Extra
262
262
  validator(value: Size): boolean;
263
263
  };
264
264
  /**
265
- * Set property of **items**’s text value
265
+ * Set property of **items**'s text value
266
266
  */
267
267
  itemText: {
268
268
  type: StringConstructor;
269
269
  default: string;
270
270
  };
271
271
  /**
272
- * Set property of **items**’s value - must be primitive.
272
+ * Set property of **items**'s value - must be primitive.
273
273
  */
274
274
  itemValue: {
275
275
  type: (StringConstructor | NumberConstructor)[];
@@ -349,9 +349,17 @@ declare const __VLS_component: import("vue").DefineComponent<import("vue").Extra
349
349
  type: PropType<string>;
350
350
  default: string;
351
351
  };
352
+ /**
353
+ * Enables the ability to create new items when no search results are found
354
+ */
355
+ creatable: {
356
+ type: BooleanConstructor;
357
+ default: boolean;
358
+ };
352
359
  }>, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
353
360
  select: (...args: any[]) => void;
354
361
  "update:modelValue": (...args: any[]) => void;
362
+ create: (...args: any[]) => void;
355
363
  }, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
356
364
  modelValue: {
357
365
  type: PropType<ModelValue>;
@@ -392,14 +400,14 @@ declare const __VLS_component: import("vue").DefineComponent<import("vue").Extra
392
400
  validator(value: Size): boolean;
393
401
  };
394
402
  /**
395
- * Set property of **items**’s text value
403
+ * Set property of **items**'s text value
396
404
  */
397
405
  itemText: {
398
406
  type: StringConstructor;
399
407
  default: string;
400
408
  };
401
409
  /**
402
- * Set property of **items**’s value - must be primitive.
410
+ * Set property of **items**'s value - must be primitive.
403
411
  */
404
412
  itemValue: {
405
413
  type: (StringConstructor | NumberConstructor)[];
@@ -479,9 +487,17 @@ declare const __VLS_component: import("vue").DefineComponent<import("vue").Extra
479
487
  type: PropType<string>;
480
488
  default: string;
481
489
  };
490
+ /**
491
+ * Enables the ability to create new items when no search results are found
492
+ */
493
+ creatable: {
494
+ type: BooleanConstructor;
495
+ default: boolean;
496
+ };
482
497
  }>> & Readonly<{
483
498
  onSelect?: ((...args: any[]) => any) | undefined;
484
499
  "onUpdate:modelValue"?: ((...args: any[]) => any) | undefined;
500
+ onCreate?: ((...args: any[]) => any) | undefined;
485
501
  }>, {
486
502
  size: "sm" | "md" | "lg";
487
503
  label: string;
@@ -505,6 +521,7 @@ declare const __VLS_component: import("vue").DefineComponent<import("vue").Extra
505
521
  placeholderSearch: string;
506
522
  selectedTopShown: boolean;
507
523
  disabledBy: string;
524
+ creatable: boolean;
508
525
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {
509
526
  formControl: HTMLDivElement;
510
527
  button: HTMLButtonElement;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@pequity/squirrel",
3
3
  "description": "Squirrel component library",
4
- "version": "6.0.7",
4
+ "version": "6.0.9",
5
5
  "packageManager": "pnpm@9.15.4",
6
6
  "type": "module",
7
7
  "scripts": {
@@ -193,6 +193,42 @@ describe('PDropdownSelect.vue', () => {
193
193
 
194
194
  expect(selectedItems.length).toBe(selectedItemsOptions.length);
195
195
  expect(wrapper.vm.$data.selected.length).toBe(items.length);
196
+ expect(wrapper.find('button').text()).toBe('All options selected');
197
+
198
+ cleanup(wrapper);
199
+ });
200
+
201
+ it('shows "All options selected" when all selectable options and disabled-selected options are selected', async () => {
202
+ useVirtualizer.mockImplementation(() => createMockedVirtualizer(10));
203
+
204
+ const items = cloneDeep(filterListItems).slice(0, 10);
205
+ items[0].disabled = true;
206
+ items[1].disabled = true;
207
+ // Pre-select one disabled item
208
+ const wrapper = createWrapper({ selected: [1], items }, { multiple: true });
209
+
210
+ await wrapper.find('button').trigger('click');
211
+ await wrapper.findByText('Select all').trigger('click');
212
+
213
+ const selectedItemsOptions = wrapper.findAll('[p-select-list-option-item]');
214
+ const selectedItems = wrapper.findAll('[p-select-list-option-item].selected');
215
+
216
+ // Should have all non-disabled items (8) plus the pre-selected disabled item (1)
217
+ expect(selectedItems.length).toBe(selectedItemsOptions.length - 1);
218
+ expect(wrapper.vm.$data.selected.length).toBe(items.length - 1);
219
+ expect(wrapper.find('button').text()).toBe('All options selected');
220
+
221
+ cleanup(wrapper);
222
+ });
223
+
224
+ it('shows number of selected options when not all non-disabled options are selected', async () => {
225
+ useVirtualizer.mockImplementation(() => createMockedVirtualizer(10));
226
+
227
+ const items = cloneDeep(filterListItems).slice(0, 10);
228
+ items[0].disabled = true;
229
+ const wrapper = createWrapper({ selected: [2, 3], items }, { multiple: true });
230
+
231
+ expect(wrapper.find('button').text()).toBe('2 options selected');
196
232
 
197
233
  cleanup(wrapper);
198
234
  });
@@ -713,4 +749,102 @@ describe('PDropdownSelect.vue', () => {
713
749
 
714
750
  cleanup(wrapper);
715
751
  });
752
+
753
+ describe('creatable functionality', () => {
754
+ it('shows create option when no items match search and creatable is true', async () => {
755
+ useVirtualizer.mockImplementation(() => createMockedVirtualizer(20));
756
+ const wrapper = createWrapper({ selected: null }, { searchable: true, creatable: true });
757
+ await wrapper.find('button').trigger('click');
758
+ await sleep(200);
759
+ const searchInput = wrapper.find('input.text-night');
760
+ await searchInput.setValue('New Item');
761
+ const createButton = wrapper.find('button.hover\\:text-primary-hover');
762
+ expect(createButton.exists()).toBe(true);
763
+ expect(createButton.text()).toBe("Add 'New Item'");
764
+ cleanup(wrapper);
765
+ });
766
+
767
+ it('does not show create option when creatable is false', async () => {
768
+ useVirtualizer.mockImplementation(() => createMockedVirtualizer(20));
769
+ const wrapper = createWrapper({ selected: null }, { searchable: true, creatable: false });
770
+ await wrapper.find('button').trigger('click');
771
+ await sleep(200);
772
+ const searchInput = wrapper.find('input.text-night');
773
+ await searchInput.setValue('New Item');
774
+ const createButton = wrapper.find('button.hover\\:text-primary-hover');
775
+ expect(createButton.exists()).toBe(false);
776
+ expect(wrapper.text()).toContain('No items found');
777
+ cleanup(wrapper);
778
+ });
779
+
780
+ it('emits create event when clicking create option', async () => {
781
+ useVirtualizer.mockImplementation(() => createMockedVirtualizer(20));
782
+ const wrapper = createWrapper({ selected: null }, { searchable: true, creatable: true });
783
+ await wrapper.find('button').trigger('click');
784
+ await sleep(200);
785
+ const searchInput = wrapper.find('input.text-night');
786
+ await searchInput.setValue('New Item');
787
+ const createButton = wrapper.find('button.hover\\:text-primary-hover');
788
+ await createButton.trigger('click');
789
+ const pDropdownSelectCmp = wrapper.findComponent(PDropdownSelect);
790
+ expect(pDropdownSelectCmp.emitted().create[0]).toEqual(['New Item']);
791
+ expect(wrapper.find('.pdropdown-stub-popper').exists()).toBe(false);
792
+ cleanup(wrapper);
793
+ });
794
+
795
+ it('does not update model value when creating new item', async () => {
796
+ useVirtualizer.mockImplementation(() => createMockedVirtualizer(20));
797
+ const wrapper = createWrapper({ selected: null }, { searchable: true, creatable: true });
798
+ await wrapper.find('button').trigger('click');
799
+ await sleep(200);
800
+ const searchInput = wrapper.find('input.text-night');
801
+ await searchInput.setValue('New Item');
802
+ const createButton = wrapper.find('button.hover\\:text-primary-hover');
803
+ await createButton.trigger('click');
804
+ const pDropdownSelectCmp = wrapper.findComponent(PDropdownSelect);
805
+ expect(pDropdownSelectCmp.emitted()['update:modelValue']).toBeFalsy();
806
+ expect(wrapper.vm.$data.selected).toBe(null);
807
+ cleanup(wrapper);
808
+ });
809
+
810
+ it('does not create object value when valueIsObject is true', async () => {
811
+ useVirtualizer.mockImplementation(() => createMockedVirtualizer(20));
812
+ const wrapper = createWrapper({ selected: null }, { searchable: true, creatable: true, valueIsObject: true });
813
+ await wrapper.find('button').trigger('click');
814
+ await sleep(200);
815
+ const searchInput = wrapper.find('input.text-night');
816
+ await searchInput.setValue('New Item');
817
+ const createButton = wrapper.find('button.hover\\:text-primary-hover');
818
+ await createButton.trigger('click');
819
+ const pDropdownSelectCmp = wrapper.findComponent(PDropdownSelect);
820
+ expect(pDropdownSelectCmp.emitted().create[0]).toEqual(['New Item']);
821
+ expect(pDropdownSelectCmp.emitted()['update:modelValue']).toBeFalsy();
822
+ expect(wrapper.vm.$data.selected).toBe(null);
823
+ cleanup(wrapper);
824
+ });
825
+
826
+ it('emits create event with search value when using custom itemValue and itemText', async () => {
827
+ useVirtualizer.mockImplementation(() => createMockedVirtualizer(20));
828
+ const wrapper = createWrapper(
829
+ { selected: null },
830
+ {
831
+ searchable: true,
832
+ creatable: true,
833
+ valueIsObject: true,
834
+ itemValue: 'customValue',
835
+ itemText: 'customText',
836
+ }
837
+ );
838
+ await wrapper.find('button').trigger('click');
839
+ await sleep(200);
840
+ const searchInput = wrapper.find('input.text-night');
841
+ await searchInput.setValue('New Item');
842
+ const createButton = wrapper.find('button.hover\\:text-primary-hover');
843
+ await createButton.trigger('click');
844
+ const pDropdownSelectCmp = wrapper.findComponent(PDropdownSelect);
845
+ expect(pDropdownSelectCmp.emitted().create[0]).toEqual(['New Item']);
846
+ expect(pDropdownSelectCmp.emitted()['update:modelValue']).toBeFalsy();
847
+ cleanup(wrapper);
848
+ });
849
+ });
716
850
  });
@@ -312,3 +312,39 @@ export const ClearableMultiple = {
312
312
  clearable: true,
313
313
  },
314
314
  };
315
+
316
+ export const Creatable = {
317
+ render: (args) => ({
318
+ components: { PDropdownSelect },
319
+ setup() {
320
+ const value = ref(null);
321
+ return { args, value };
322
+ },
323
+ template: `
324
+ <div>
325
+ <PDropdownSelect
326
+ v-model="value"
327
+ v-bind="args"
328
+ @create="(searchTerm) => alert('Create item: ' + searchTerm)"
329
+ />
330
+ <div class="mt-4">Selected: {{ value }}</div>
331
+ </div>
332
+ `,
333
+ }),
334
+ parameters: {
335
+ docs: {
336
+ description: {
337
+ story:
338
+ 'Example with creatable functionality enabled. When no search results are found, an option to create a new item is shown.',
339
+ },
340
+ },
341
+ },
342
+ args: {
343
+ ...Default.args,
344
+ label: 'Creatable dropdown',
345
+ items: items2,
346
+ searchable: true,
347
+ creatable: true,
348
+ placeholder: 'Search or create an item...',
349
+ },
350
+ };
@@ -32,8 +32,10 @@
32
32
  <slot v-else name="selected-item" :item="multiple ? selectedItems : selectedItems[0]">
33
33
  <div class="truncate text-left">
34
34
  {{
35
- multiple && selectedItems.length > 1
36
- ? `${selectedItems.length} option${selectedItems.length > 1 ? 's' : ''} selected`
35
+ multiple
36
+ ? selectedItems.length === selectableItemsCount
37
+ ? 'All options selected'
38
+ : `${selectedItems.length} option${selectedItems.length > 1 ? 's' : ''} selected`
37
39
  : selectedItems[0][itemText]
38
40
  }}
39
41
  </div>
@@ -148,7 +150,18 @@
148
150
  </div>
149
151
  </div>
150
152
  <slot v-if="!computedItems.length" name="no-items">
151
- <div :class="['flex items-center justify-center', SIZES[size]]">No items found</div>
153
+ <div :class="['flex items-center justify-center', SIZES[size]]">
154
+ <template v-if="creatable && search">
155
+ <button
156
+ class="hover:text-primary-hover flex items-center gap-2 font-medium text-p-blue-40"
157
+ @click="handleCreate"
158
+ >
159
+ <PIcon icon="fe:plus-circle" />
160
+ Add '{{ search }}'
161
+ </button>
162
+ </template>
163
+ <template v-else>No items found</template>
164
+ </div>
152
165
  </slot>
153
166
  </div>
154
167
  </div>
@@ -197,7 +210,7 @@ defineSlots<{
197
210
  item(props: { item: any; isItemSelected: boolean; itemTextSplit: string[] }): unknown;
198
211
  }>();
199
212
 
200
- const emit = defineEmits(['update:modelValue', 'select']);
213
+ const emit = defineEmits(['update:modelValue', 'select', 'create']);
201
214
 
202
215
  const props = defineProps({
203
216
  modelValue: {
@@ -241,14 +254,14 @@ const props = defineProps({
241
254
  },
242
255
  },
243
256
  /**
244
- * Set property of **items**’s text value
257
+ * Set property of **items**'s text value
245
258
  */
246
259
  itemText: {
247
260
  type: String,
248
261
  default: 'text',
249
262
  },
250
263
  /**
251
- * Set property of **items**’s value - must be primitive.
264
+ * Set property of **items**'s value - must be primitive.
252
265
  */
253
266
  itemValue: {
254
267
  type: [String, Number],
@@ -328,6 +341,13 @@ const props = defineProps({
328
341
  type: String as PropType<string>,
329
342
  default: 'disabled',
330
343
  },
344
+ /**
345
+ * Enables the ability to create new items when no search results are found
346
+ */
347
+ creatable: {
348
+ type: Boolean,
349
+ default: false,
350
+ },
331
351
  });
332
352
 
333
353
  // Async helpers
@@ -401,6 +421,10 @@ const attrs = computed(() => {
401
421
 
402
422
  const style = computed(() => $attrs.style as StyleValue);
403
423
 
424
+ const selectableItemsCount = computed(
425
+ () => internalItems.value.filter((item) => !isDisabled(item) || isSelected(item[props.itemValue])).length
426
+ );
427
+
404
428
  // Watch
405
429
  // Sorts internalItems putting the selected ones first
406
430
  watch(
@@ -453,4 +477,9 @@ const onHide = () => {
453
477
  destroyNavigationSvc();
454
478
  (formControl.value?.querySelector('button') as HTMLElement)?.focus();
455
479
  };
480
+
481
+ const handleCreate = () => {
482
+ emit('create', search.value);
483
+ dropdownShow.value = false;
484
+ };
456
485
  </script>