@pequity/squirrel 6.0.5 → 6.0.7

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
@@ -76,7 +76,7 @@ Import and use the components you need in your Vue 3 project:
76
76
  </template>
77
77
 
78
78
  <script setup lang="ts">
79
- import { PBtn, PRingLoader } from '@pequity/squirrel';
79
+ import { PBtn } from '@pequity/squirrel';
80
80
  </script>
81
81
  ```
82
82
 
@@ -111,7 +111,7 @@ Then, in your consumer project's `.env.local` file, add an `VUE_APP_SQUIRREL_LOC
111
111
 
112
112
  Finally, in your project's `vite.config` file, add the following:
113
113
 
114
- > Heads up! The `vite.config.mts` file of the `pequity/frontendv2` already includes the following configuration.
114
+ > Heads up! The `vite.config.ts` file of the `pequity/frontendv2` already includes the following configuration.
115
115
 
116
116
  ```js
117
117
  import { defineConfig, searchForWorkspaceRoot } from 'vite';
@@ -11,10 +11,7 @@ const text = require("../text.js");
11
11
  const lodashEs = require("lodash-es");
12
12
  const _imports_0 = "data:image/svg+xml,%3csvg%20width='18'%20height='12'%20viewBox='0%200%2018%2012'%20fill='none'%20xmlns='http://www.w3.org/2000/svg'%3e%3cpath%20d='M16.1383%200.166992L6.30411%209.83366L1.69828%205.27533L0.526611%206.46033L5.71578%2011.597C5.87174%2011.7509%206.08205%2011.8372%206.3012%2011.8372C6.52034%2011.8372%206.73065%2011.7509%206.88661%2011.597L17.3033%201.35366L16.1383%200.166992Z'%20fill='%231A123B'%20/%3e%3c/svg%3e";
13
13
  const _hoisted_1 = ["data-has-error"];
14
- const _hoisted_2 = {
15
- key: 0,
16
- class: "truncate text-left text-p-gray-40"
17
- };
14
+ const _hoisted_2 = { class: "truncate text-left text-p-gray-40" };
18
15
  const _hoisted_3 = { class: "truncate text-left" };
19
16
  const _hoisted_4 = {
20
17
  key: 0,
@@ -415,13 +412,15 @@ const _sfc_main = /* @__PURE__ */ vue.defineComponent({
415
412
  "aria-haspopup": "listbox",
416
413
  onClick: _cache[1] || (_cache[1] = ($event) => dropdownShow.value = !dropdownShow.value)
417
414
  }), [
418
- !vue.unref(internalValue).length || !vue.unref(selectedItems).length ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_2, vue.toDisplayString(__props.placeholder || " "), 1)) : vue.renderSlot(_ctx.$slots, "selected-item", {
415
+ !vue.unref(internalValue).length || !vue.unref(selectedItems).length ? vue.renderSlot(_ctx.$slots, "placeholder", { key: 0 }, () => [
416
+ vue.createElementVNode("div", _hoisted_2, vue.toDisplayString(__props.placeholder || " "), 1)
417
+ ]) : vue.renderSlot(_ctx.$slots, "selected-item", {
419
418
  key: 1,
420
419
  item: __props.multiple ? vue.unref(selectedItems) : vue.unref(selectedItems)[0]
421
420
  }, () => [
422
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)
423
422
  ]),
424
- __props.clearable && vue.unref(internalValue).length ? (vue.openBlock(), vue.createElementBlock("button", {
423
+ __props.clearable && vue.unref(selectedItems).length ? (vue.openBlock(), vue.createElementBlock("button", {
425
424
  key: 2,
426
425
  class: vue.normalizeClass(["absolute top-1/2 flex -translate-y-1/2 items-center justify-center text-p-gray-40 hover:text-p-gray-60", [vue.unref(pSelectList.SIZES)[__props.size], CLEAR_BUTTON_SPACING[__props.size]]]),
427
426
  "aria-label": "Clear selection",
@@ -217,12 +217,17 @@ const useSelectList = (props, inputSearch, virtualizerRef, emit) => {
217
217
  emit("update:modelValue", toArrOfObjIfNeeded(toEmit));
218
218
  };
219
219
  const clearAll = () => {
220
- if (!props.multiple) return;
221
220
  search.value = "";
222
- const disabledItemsValues = internalItems.value.filter((item) => isDisabled(item)).map((item) => item[props.itemValue]);
223
- const selectedItemsValues = internalValue.value;
224
- const selectedDisabledItems = lodashEs.intersection(disabledItemsValues, selectedItemsValues);
225
- emit("update:modelValue", toArrOfObjIfNeeded(selectedDisabledItems));
221
+ if (props.multiple) {
222
+ const disabledItemsValues = internalItems.value.filter((item) => isDisabled(item)).map((item) => item[props.itemValue]);
223
+ const selectedItemsValues = internalValue.value;
224
+ const selectedDisabledItems = lodashEs.intersection(disabledItemsValues, selectedItemsValues);
225
+ emit("update:modelValue", toArrOfObjIfNeeded(selectedDisabledItems));
226
+ } else {
227
+ if (!isDisabled(selectedItems.value[0])) {
228
+ emit("update:modelValue", null);
229
+ }
230
+ }
226
231
  };
227
232
  vue.watch(
228
233
  () => props.items,
@@ -10,10 +10,7 @@ import { splitStringForHighlight } from "../text.js";
10
10
  import { omit } from "lodash-es";
11
11
  const _imports_0 = "data:image/svg+xml,%3csvg%20width='18'%20height='12'%20viewBox='0%200%2018%2012'%20fill='none'%20xmlns='http://www.w3.org/2000/svg'%3e%3cpath%20d='M16.1383%200.166992L6.30411%209.83366L1.69828%205.27533L0.526611%206.46033L5.71578%2011.597C5.87174%2011.7509%206.08205%2011.8372%206.3012%2011.8372C6.52034%2011.8372%206.73065%2011.7509%206.88661%2011.597L17.3033%201.35366L16.1383%200.166992Z'%20fill='%231A123B'%20/%3e%3c/svg%3e";
12
12
  const _hoisted_1 = ["data-has-error"];
13
- const _hoisted_2 = {
14
- key: 0,
15
- class: "truncate text-left text-p-gray-40"
16
- };
13
+ const _hoisted_2 = { class: "truncate text-left text-p-gray-40" };
17
14
  const _hoisted_3 = { class: "truncate text-left" };
18
15
  const _hoisted_4 = {
19
16
  key: 0,
@@ -414,13 +411,15 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
414
411
  "aria-haspopup": "listbox",
415
412
  onClick: _cache[1] || (_cache[1] = ($event) => dropdownShow.value = !dropdownShow.value)
416
413
  }), [
417
- !unref(internalValue).length || !unref(selectedItems).length ? (openBlock(), createElementBlock("div", _hoisted_2, toDisplayString(__props.placeholder || " "), 1)) : renderSlot(_ctx.$slots, "selected-item", {
414
+ !unref(internalValue).length || !unref(selectedItems).length ? renderSlot(_ctx.$slots, "placeholder", { key: 0 }, () => [
415
+ createElementVNode("div", _hoisted_2, toDisplayString(__props.placeholder || " "), 1)
416
+ ]) : renderSlot(_ctx.$slots, "selected-item", {
418
417
  key: 1,
419
418
  item: __props.multiple ? unref(selectedItems) : unref(selectedItems)[0]
420
419
  }, () => [
421
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)
422
421
  ]),
423
- __props.clearable && unref(internalValue).length ? (openBlock(), createElementBlock("button", {
422
+ __props.clearable && unref(selectedItems).length ? (openBlock(), createElementBlock("button", {
424
423
  key: 2,
425
424
  class: normalizeClass(["absolute top-1/2 flex -translate-y-1/2 items-center justify-center text-p-gray-40 hover:text-p-gray-60", [unref(SIZES)[__props.size], CLEAR_BUTTON_SPACING[__props.size]]]),
426
425
  "aria-label": "Clear selection",
@@ -215,12 +215,17 @@ const useSelectList = (props, inputSearch, virtualizerRef, emit) => {
215
215
  emit("update:modelValue", toArrOfObjIfNeeded(toEmit));
216
216
  };
217
217
  const clearAll = () => {
218
- if (!props.multiple) return;
219
218
  search.value = "";
220
- const disabledItemsValues = internalItems.value.filter((item) => isDisabled(item)).map((item) => item[props.itemValue]);
221
- const selectedItemsValues = internalValue.value;
222
- const selectedDisabledItems = intersection(disabledItemsValues, selectedItemsValues);
223
- emit("update:modelValue", toArrOfObjIfNeeded(selectedDisabledItems));
219
+ if (props.multiple) {
220
+ const disabledItemsValues = internalItems.value.filter((item) => isDisabled(item)).map((item) => item[props.itemValue]);
221
+ const selectedItemsValues = internalValue.value;
222
+ const selectedDisabledItems = intersection(disabledItemsValues, selectedItemsValues);
223
+ emit("update:modelValue", toArrOfObjIfNeeded(selectedDisabledItems));
224
+ } else {
225
+ if (!isDisabled(selectedItems.value[0])) {
226
+ emit("update:modelValue", null);
227
+ }
228
+ }
224
229
  };
225
230
  watch(
226
231
  () => props.items,
@@ -8,6 +8,7 @@ declare function __VLS_template(): {
8
8
  'selected-item'(props: {
9
9
  item: any;
10
10
  }): unknown;
11
+ placeholder(): unknown;
11
12
  'no-items'(): unknown;
12
13
  item(props: {
13
14
  item: any;
@@ -18,6 +19,7 @@ declare function __VLS_template(): {
18
19
  'selected-item'(props: {
19
20
  item: any;
20
21
  }): unknown;
22
+ placeholder(): unknown;
21
23
  'no-items'(): unknown;
22
24
  item(props: {
23
25
  item: any;
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@pequity/squirrel",
3
3
  "description": "Squirrel component library",
4
- "version": "6.0.5",
5
- "packageManager": "pnpm@9.15.3",
4
+ "version": "6.0.7",
5
+ "packageManager": "pnpm@9.15.4",
6
6
  "type": "module",
7
7
  "scripts": {
8
8
  "preinstall": "npx only-allow pnpm",
@@ -53,54 +53,54 @@
53
53
  "@playwright/test": "^1.49.1",
54
54
  "@semantic-release/changelog": "^6.0.3",
55
55
  "@semantic-release/git": "^10.0.1",
56
- "@storybook/addon-a11y": "^8.4.7",
57
- "@storybook/addon-actions": "^8.4.7",
58
- "@storybook/addon-essentials": "^8.4.7",
59
- "@storybook/addon-interactions": "^8.4.7",
60
- "@storybook/addon-links": "^8.4.7",
61
- "@storybook/blocks": "^8.4.7",
62
- "@storybook/manager-api": "^8.4.7",
63
- "@storybook/test": "^8.4.7",
56
+ "@storybook/addon-a11y": "^8.5.0",
57
+ "@storybook/addon-actions": "^8.5.0",
58
+ "@storybook/addon-essentials": "^8.5.0",
59
+ "@storybook/addon-interactions": "^8.5.0",
60
+ "@storybook/addon-links": "^8.5.0",
61
+ "@storybook/blocks": "^8.5.0",
62
+ "@storybook/manager-api": "^8.5.0",
63
+ "@storybook/test": "^8.5.0",
64
64
  "@storybook/test-runner": "^0.21.0",
65
- "@storybook/theming": "^8.4.7",
66
- "@storybook/vue3": "^8.4.7",
67
- "@storybook/vue3-vite": "^8.4.7",
65
+ "@storybook/theming": "^8.5.0",
66
+ "@storybook/vue3": "^8.5.0",
67
+ "@storybook/vue3-vite": "^8.5.0",
68
68
  "@tanstack/vue-virtual": "3.11.2",
69
69
  "@types/jsdom": "^21.1.7",
70
70
  "@types/lodash-es": "^4.17.12",
71
- "@types/node": "^22.10.5",
71
+ "@types/node": "^22.10.7",
72
72
  "@vitejs/plugin-vue": "^5.2.1",
73
- "@vitest/coverage-v8": "^2.1.8",
73
+ "@vitest/coverage-v8": "^3.0.3",
74
74
  "@vue/compiler-sfc": "3.5.13",
75
75
  "@vue/test-utils": "^2.4.6",
76
76
  "@vuepic/vue-datepicker": "11.0.1",
77
77
  "autoprefixer": "^10.4.20",
78
78
  "dayjs": "1.11.13",
79
- "eslint": "^9.17.0",
79
+ "eslint": "^9.18.0",
80
80
  "eslint-plugin-storybook": "^0.11.2",
81
81
  "floating-vue": "5.2.2",
82
82
  "glob": "^11.0.1",
83
83
  "husky": "^9.1.7",
84
84
  "iconify-icon": "^2.3.0",
85
85
  "jsdom": "^26.0.0",
86
- "lint-staged": "^15.3.0",
86
+ "lint-staged": "^15.4.1",
87
87
  "lodash-es": "4.17.21",
88
88
  "make-coverage-badge": "^1.2.0",
89
- "postcss": "^8.4.49",
89
+ "postcss": "^8.5.1",
90
90
  "prettier": "^3.4.2",
91
- "prettier-plugin-tailwindcss": "^0.6.9",
91
+ "prettier-plugin-tailwindcss": "^0.6.10",
92
92
  "resolve-tspaths": "^0.8.23",
93
93
  "rimraf": "^6.0.1",
94
- "sass": "^1.83.1",
94
+ "sass": "^1.83.4",
95
95
  "semantic-release": "^24.2.1",
96
- "storybook": "^8.4.7",
96
+ "storybook": "^8.5.0",
97
97
  "svgo": "^3.3.2",
98
98
  "tailwindcss": "^3.4.17",
99
99
  "typescript": "5.7.3",
100
- "vite": "^6.0.7",
101
- "vitest": "^2.1.8",
100
+ "vite": "^6.0.11",
101
+ "vitest": "^3.0.3",
102
102
  "vue": "3.5.13",
103
- "vue-currency-input": "3.1.0",
103
+ "vue-currency-input": "3.2.1",
104
104
  "vue-router": "4.5.0",
105
105
  "vue-toastification": "2.0.0-rc.5",
106
106
  "vue-tsc": "2.2.0"
@@ -653,6 +653,17 @@ describe('PDropdownSelect.vue', () => {
653
653
  cleanup(wrapper);
654
654
  });
655
655
 
656
+ it('does not render clear button when there is no value selected', async () => {
657
+ useVirtualizer.mockImplementation(() => createMockedVirtualizer(20));
658
+
659
+ const wrapper = createWrapper({ selected: null }, { clearable: true });
660
+
661
+ const clearButton = wrapper.find('button[aria-label="Clear selection"]');
662
+ expect(clearButton.exists()).toBe(false);
663
+
664
+ cleanup(wrapper);
665
+ });
666
+
656
667
  it('clears multiple selections when clearable is true', async () => {
657
668
  useVirtualizer.mockImplementation(() => createMockedVirtualizer(20));
658
669
 
@@ -666,4 +677,40 @@ describe('PDropdownSelect.vue', () => {
666
677
 
667
678
  cleanup(wrapper);
668
679
  });
680
+
681
+ it('clears a single selection select when clearable is true', async () => {
682
+ useVirtualizer.mockImplementation(() => createMockedVirtualizer(10));
683
+
684
+ const items = cloneDeep(filterListItems).slice(0, 10);
685
+ items[0].disabled = false;
686
+ const wrapper = createWrapper({ selected: 1, items }, { multiple: false, clearable: true });
687
+
688
+ expect(wrapper.vm.$data.selected).toEqual(1);
689
+
690
+ const clearButton = wrapper.find('button[aria-label="Clear selection"]');
691
+ expect(clearButton.exists()).toBe(true);
692
+
693
+ await clearButton.trigger('click');
694
+ expect(wrapper.vm.$data.selected).toEqual(null);
695
+
696
+ cleanup(wrapper);
697
+ });
698
+
699
+ it('does not clear a single selection select when the item is disabled', async () => {
700
+ useVirtualizer.mockImplementation(() => createMockedVirtualizer(10));
701
+
702
+ const items = cloneDeep(filterListItems).slice(0, 10);
703
+ items[0].disabled = true;
704
+ const wrapper = createWrapper({ selected: 1, items }, { multiple: false, clearable: true });
705
+
706
+ expect(wrapper.vm.$data.selected).toEqual(1);
707
+
708
+ const clearButton = wrapper.find('button[aria-label="Clear selection"]');
709
+ expect(clearButton.exists()).toBe(true);
710
+
711
+ await clearButton.trigger('click');
712
+ expect(wrapper.vm.$data.selected).toEqual(1);
713
+
714
+ cleanup(wrapper);
715
+ });
669
716
  });
@@ -24,9 +24,11 @@
24
24
  aria-haspopup="listbox"
25
25
  @click="dropdownShow = !dropdownShow"
26
26
  >
27
- <div v-if="!internalValue.length || !selectedItems.length" class="truncate text-left text-p-gray-40">
28
- {{ placeholder || '&nbsp;' }}
29
- </div>
27
+ <slot v-if="!internalValue.length || !selectedItems.length" name="placeholder">
28
+ <div class="truncate text-left text-p-gray-40">
29
+ {{ placeholder || '&nbsp;' }}
30
+ </div>
31
+ </slot>
30
32
  <slot v-else name="selected-item" :item="multiple ? selectedItems : selectedItems[0]">
31
33
  <div class="truncate text-left">
32
34
  {{
@@ -36,9 +38,9 @@
36
38
  }}
37
39
  </div>
38
40
  </slot>
39
- <!-- Add clear button -->
41
+ <!-- Clear selection button -->
40
42
  <button
41
- v-if="clearable && internalValue.length"
43
+ v-if="clearable && selectedItems.length"
42
44
  class="absolute top-1/2 flex -translate-y-1/2 items-center justify-center text-p-gray-40 hover:text-p-gray-60"
43
45
  :class="[SIZES[size], CLEAR_BUTTON_SPACING[size]]"
44
46
  aria-label="Clear selection"
@@ -189,6 +191,7 @@ defineOptions({
189
191
  defineSlots<{
190
192
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
191
193
  'selected-item'(props: { item: any }): unknown;
194
+ 'placeholder'(): unknown;
192
195
  'no-items'(): unknown;
193
196
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
194
197
  item(props: { item: any; isItemSelected: boolean; itemTextSplit: string[] }): unknown;
@@ -299,20 +299,24 @@ export const useSelectList = (props: Props, inputSearch: InputSearch, virtualize
299
299
  };
300
300
 
301
301
  const clearAll = () => {
302
- if (!props.multiple) return;
303
-
304
302
  search.value = '';
305
303
 
306
304
  // We cannot clear disabled items that are selected
307
- const disabledItemsValues = internalItems.value
308
- .filter((item) => isDisabled(item))
309
- .map((item) => item[props.itemValue]);
305
+ if (props.multiple) {
306
+ const disabledItemsValues = internalItems.value
307
+ .filter((item) => isDisabled(item))
308
+ .map((item) => item[props.itemValue]);
310
309
 
311
- const selectedItemsValues = internalValue.value;
310
+ const selectedItemsValues = internalValue.value;
312
311
 
313
- const selectedDisabledItems = intersection(disabledItemsValues, selectedItemsValues);
312
+ const selectedDisabledItems = intersection(disabledItemsValues, selectedItemsValues);
314
313
 
315
- emit('update:modelValue', toArrOfObjIfNeeded(selectedDisabledItems));
314
+ emit('update:modelValue', toArrOfObjIfNeeded(selectedDisabledItems));
315
+ } else {
316
+ if (!isDisabled(selectedItems.value[0])) {
317
+ emit('update:modelValue', null);
318
+ }
319
+ }
316
320
  };
317
321
 
318
322
  // Watch