@pequity/squirrel 5.2.3 → 5.3.0

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.
Files changed (37) hide show
  1. package/dist/cjs/chunks/component.js +4 -0
  2. package/dist/cjs/chunks/p-dropdown-select.js +1 -1
  3. package/dist/cjs/chunks/p-icon.js +2180 -0
  4. package/dist/cjs/chunks/string.js +13 -0
  5. package/dist/cjs/component.js +28 -0
  6. package/dist/cjs/index.js +32 -2188
  7. package/dist/cjs/p-loading.js +4 -7
  8. package/dist/cjs/string.js +32 -11
  9. package/dist/cjs/useSelectList.js +1 -1
  10. package/dist/es/chunks/component.js +5 -0
  11. package/dist/es/chunks/p-dropdown-select.js +1 -1
  12. package/dist/es/chunks/p-icon.js +2181 -0
  13. package/dist/es/chunks/string.js +14 -0
  14. package/dist/es/component.js +27 -0
  15. package/dist/es/index.js +31 -2187
  16. package/dist/es/p-loading.js +4 -7
  17. package/dist/es/string.js +32 -12
  18. package/dist/es/useSelectList.js +1 -1
  19. package/dist/squirrel/components/p-action-bar/p-action-bar.types.d.ts +3 -2
  20. package/dist/squirrel/utils/component.d.ts +2 -0
  21. package/dist/squirrel/utils/component.spec.d.ts +1 -0
  22. package/dist/squirrel/utils/string.d.ts +1 -0
  23. package/dist/squirrel/utils/string.spec.d.ts +1 -0
  24. package/dist/style.css +6 -6
  25. package/package.json +2 -2
  26. package/squirrel/components/p-action-bar/p-action-bar.spec.js +46 -14
  27. package/squirrel/components/p-action-bar/p-action-bar.stories.js +6 -0
  28. package/squirrel/components/p-action-bar/p-action-bar.types.ts +4 -2
  29. package/squirrel/components/p-action-bar/p-action-bar.vue +9 -3
  30. package/squirrel/components/p-avatar/p-avatar.spec.js +15 -0
  31. package/squirrel/components/p-icon/p-icon.spec.js +0 -1
  32. package/squirrel/components/p-loading/p-loading.vue +2 -7
  33. package/squirrel/components/p-table-td/p-table-td.spec.js +0 -1
  34. package/squirrel/utils/component.spec.ts +29 -0
  35. package/squirrel/utils/component.ts +5 -0
  36. package/squirrel/utils/{string.spec.js → string.spec.ts} +19 -1
  37. package/squirrel/utils/string.ts +2 -0
@@ -1,4 +1,5 @@
1
1
  import { defineComponent, ref, watch, toValue, onBeforeUnmount, openBlock, createBlock, Teleport, createVNode, Transition, withCtx, unref, createElementBlock, createElementVNode, normalizeStyle, resolveDynamicComponent, normalizeProps, mergeProps, normalizeClass, toDisplayString, createCommentVNode } from "vue";
2
+ import { i as isComponent } from "./chunks/component.js";
2
3
  import { usePLoading } from "./usePLoading.js";
3
4
  import { _ as _export_sfc } from "./chunks/_plugin-vue_export-helper.js";
4
5
  const _hoisted_1 = {
@@ -32,10 +33,6 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
32
33
  },
33
34
  { flush: "post" }
34
35
  );
35
- const isComponent = (content2) => {
36
- const isComponent2 = typeof content2 === "object" && content2 !== null;
37
- return isComponent2;
38
- };
39
36
  onBeforeUnmount(() => {
40
37
  loadingHide();
41
38
  });
@@ -57,7 +54,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
57
54
  "enter-active-class": "transition duration-500"
58
55
  }, {
59
56
  default: withCtx(() => [
60
- isComponent(unref(content)) ? (openBlock(), createBlock(resolveDynamicComponent(unref(content)), normalizeProps(mergeProps({ key: 0 }, unref(componentProps))), null, 16)) : (openBlock(), createElementBlock("div", {
57
+ unref(isComponent)(unref(content)) ? (openBlock(), createBlock(resolveDynamicComponent(unref(content)), normalizeProps(mergeProps({ key: 0 }, unref(componentProps))), null, 16)) : (openBlock(), createElementBlock("div", {
61
58
  key: 1,
62
59
  class: normalizeClass(textDivClass)
63
60
  }, toDisplayString(unref(content)), 1))
@@ -74,7 +71,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
74
71
  ref_key: "dimsReference",
75
72
  ref: dimsReference
76
73
  }, [
77
- isComponent(unref(content)) ? (openBlock(), createBlock(resolveDynamicComponent(unref(content)), normalizeProps(mergeProps({ key: 0 }, unref(componentProps))), null, 16)) : (openBlock(), createElementBlock("div", {
74
+ unref(isComponent)(unref(content)) ? (openBlock(), createBlock(resolveDynamicComponent(unref(content)), normalizeProps(mergeProps({ key: 0 }, unref(componentProps))), null, 16)) : (openBlock(), createElementBlock("div", {
78
75
  key: 1,
79
76
  class: normalizeClass(textDivClass)
80
77
  }, toDisplayString(unref(content)), 1))
@@ -84,7 +81,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
84
81
  };
85
82
  }
86
83
  });
87
- const pLoading = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-f0f6bdd4"]]);
84
+ const pLoading = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-980d32e1"]]);
88
85
  export {
89
86
  pLoading as default
90
87
  };
package/dist/es/string.js CHANGED
@@ -1,12 +1,32 @@
1
- const toString = (value) => {
2
- if (typeof value === "number" && (!isFinite(value) || isNaN(value))) {
3
- return "";
4
- }
5
- if (typeof value !== "string" && typeof value !== "number") {
6
- return "";
7
- }
8
- return String(value);
9
- };
10
- export {
11
- toString
12
- };
1
+ import { t as toString, i as isString } from "./chunks/string.js";
2
+ describe("toString", () => {
3
+ it.each(["", /* @__PURE__ */ new Date(), NaN, Infinity, {}, [], true, false, null, void 0])(
4
+ "returns an empty string if input is %s",
5
+ (val) => {
6
+ expect(toString(val)).toBe("");
7
+ }
8
+ );
9
+ it("returns a string when the value is a string", () => {
10
+ expect(toString("test")).toBe("test");
11
+ });
12
+ it("converts the value to a string when the value is a number", () => {
13
+ expect(toString(290349822e-2)).toBe("2903498.22");
14
+ });
15
+ });
16
+ describe("isString", () => {
17
+ it("should return true for a string", () => {
18
+ expect(isString("hello")).toBe(true);
19
+ });
20
+ it.each([
21
+ [123, false],
22
+ [{}, false],
23
+ [[], false],
24
+ [null, false],
25
+ [void 0, false],
26
+ [true, false],
27
+ [() => {
28
+ }, false]
29
+ ])("should return %s for %p", (input, expected) => {
30
+ expect(isString(input)).toBe(expected);
31
+ });
32
+ });
@@ -2,7 +2,7 @@ import { ref, computed, watch, onUnmounted, nextTick } from "vue";
2
2
  import { setupListKeyboardNavigation } from "./listKeyboardNavigation.js";
3
3
  import { cloneDeep, uniqBy } from "lodash-es";
4
4
  import { isObject } from "./object.js";
5
- import { toString } from "./string.js";
5
+ import { t as toString } from "./chunks/string.js";
6
6
  import { useVirtualizer } from "@tanstack/vue-virtual";
7
7
  const createInternalItems = (items) => {
8
8
  let arr = Array.isArray(items) ? items.slice() : [];
@@ -1,10 +1,11 @@
1
+ import { type Component } from 'vue';
1
2
  export type PActionBarAction = {
2
3
  name?: string;
3
4
  label: string;
4
- icon?: string;
5
+ icon?: string | Component;
5
6
  subActions?: {
6
7
  name: string;
7
8
  label: string;
8
- icon?: string;
9
+ icon?: string | Component;
9
10
  }[];
10
11
  };
@@ -0,0 +1,2 @@
1
+ import { type Component } from 'vue';
2
+ export declare const isComponent: (val: unknown) => val is Component;
@@ -0,0 +1 @@
1
+ export {};
@@ -1 +1,2 @@
1
+ export declare const isString: (val: unknown) => val is string;
1
2
  export declare const toString: (value: unknown) => string;
@@ -0,0 +1 @@
1
+ export {};
package/dist/style.css CHANGED
@@ -381,12 +381,12 @@ from {
381
381
  to {
382
382
  opacity: 0;
383
383
  }
384
- }.fadeInDown[data-v-f0f6bdd4] {
384
+ }.fadeInDown[data-v-980d32e1] {
385
385
  animation-duration: 0.4s;
386
386
  animation-fill-mode: both;
387
- animation-name: fadeInDown-f0f6bdd4;
387
+ animation-name: fadeInDown-980d32e1;
388
388
  }
389
- @keyframes fadeInDown-f0f6bdd4 {
389
+ @keyframes fadeInDown-980d32e1 {
390
390
  0% {
391
391
  opacity: 0;
392
392
  transform: translate3d(0, -100%, 0);
@@ -396,12 +396,12 @@ to {
396
396
  transform: none;
397
397
  }
398
398
  }
399
- .fadeOutUp[data-v-f0f6bdd4] {
399
+ .fadeOutUp[data-v-980d32e1] {
400
400
  animation-duration: 0.25s;
401
401
  animation-fill-mode: both;
402
- animation-name: fadeOutUp-f0f6bdd4;
402
+ animation-name: fadeOutUp-980d32e1;
403
403
  }
404
- @keyframes fadeOutUp-f0f6bdd4 {
404
+ @keyframes fadeOutUp-980d32e1 {
405
405
  0% {
406
406
  opacity: 1;
407
407
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@pequity/squirrel",
3
3
  "description": "Squirrel component library",
4
- "version": "5.2.3",
4
+ "version": "5.3.0",
5
5
  "packageManager": "pnpm@9.11.0",
6
6
  "type": "module",
7
7
  "scripts": {
@@ -72,7 +72,7 @@
72
72
  "@tanstack/vue-virtual": "3.10.8",
73
73
  "@types/jsdom": "^21.1.7",
74
74
  "@types/lodash-es": "^4.17.12",
75
- "@types/node": "^22.7.0",
75
+ "@types/node": "^22.7.2",
76
76
  "@vitejs/plugin-vue": "^5.1.4",
77
77
  "@vitest/coverage-v8": "^2.1.1",
78
78
  "@vue/compiler-sfc": "3.4.38",
@@ -1,26 +1,15 @@
1
1
  import PActionBar from '@squirrel/components/p-action-bar/p-action-bar.vue';
2
2
  import { createWrapperFor } from '@tests/vitest.helpers';
3
+ import { defineComponent } from 'vue';
3
4
 
4
5
  const createPDropdownStub = () => {
5
- return {
6
+ return defineComponent({
6
7
  name: 'PDropdownStub',
7
- template: `
8
- <div class="pdropdown-stub">
9
- <slot></slot>
10
- <div v-if="show" class="pdropdown-stub-popper">
11
- <slot name="popper"></slot>
12
- </div>
13
- </div>`,
14
8
  data() {
15
9
  return {
16
10
  show: false,
17
11
  };
18
12
  },
19
- methods: {
20
- toggleShow() {
21
- this.show = !this.show;
22
- },
23
- },
24
13
  async mounted() {
25
14
  const btn = this.$el.querySelector('button');
26
15
 
@@ -31,7 +20,19 @@ const createPDropdownStub = () => {
31
20
 
32
21
  btn.removeEventListener('click', this.toggleShow);
33
22
  },
34
- };
23
+ methods: {
24
+ toggleShow() {
25
+ this.show = !this.show;
26
+ },
27
+ },
28
+ template: `
29
+ <div class="p-dropdown-stub">
30
+ <slot></slot>
31
+ <div v-if="show" class="p-dropdown-stub-popper">
32
+ <slot name="popper"></slot>
33
+ </div>
34
+ </div>`,
35
+ });
35
36
  };
36
37
 
37
38
  const createComponentWrapper = (args = {}) =>
@@ -103,6 +104,37 @@ describe('PActionBar.vue', () => {
103
104
  expect(actionBtn.text()).toContain('Say Hi');
104
105
  });
105
106
 
107
+ it('renders p-icons', async () => {
108
+ const wrapper = createComponentWrapper({
109
+ props: {
110
+ show: true,
111
+ label: 'Hello World',
112
+ actions: [
113
+ { label: 'Say Hi', name: 'greet', icon: 'test-icon-1' },
114
+ {
115
+ label: 'Action menu',
116
+ icon: 'test-icon-2',
117
+ subActions: [{ label: 'Say Bye', name: 'bye', icon: 'test-icon-3' }],
118
+ },
119
+ ],
120
+ },
121
+ });
122
+
123
+ const mainDiv = await wrapper.find('.teleport-stub > .fixed.bottom-6');
124
+
125
+ await mainDiv.find('.p-dropdown-stub > button').trigger('click');
126
+
127
+ await wrapper.vm.$nextTick();
128
+
129
+ const icons = mainDiv.findAll('iconify-icon');
130
+
131
+ expect(icons.length).toBe(3);
132
+
133
+ expect(icons[0].attributes().icon).toBe('test-icon-1');
134
+ expect(icons[1].attributes().icon).toBe('test-icon-2');
135
+ expect(icons[2].attributes().icon).toBe('test-icon-3');
136
+ });
137
+
106
138
  it('attrs fall through ', async () => {
107
139
  const wrapper = createComponentWrapper({
108
140
  props: {
@@ -14,6 +14,7 @@ const actionBarActions = [
14
14
  },
15
15
  {
16
16
  label: 'Say Hi',
17
+ icon: 'delete',
17
18
  name: 'greet',
18
19
  },
19
20
  {
@@ -29,6 +30,11 @@ const actionBarActions = [
29
30
  name: 'subaction2',
30
31
  icon: PaginateRightIcon,
31
32
  },
33
+ {
34
+ label: 'Subaction 3',
35
+ name: 'subaction3',
36
+ icon: 'ph:android-logo',
37
+ },
32
38
  ],
33
39
  },
34
40
  ];
@@ -1,10 +1,12 @@
1
+ import { type Component } from 'vue';
2
+
1
3
  export type PActionBarAction = {
2
4
  name?: string;
3
5
  label: string;
4
- icon?: string;
6
+ icon?: string | Component;
5
7
  subActions?: {
6
8
  name: string;
7
9
  label: string;
8
- icon?: string;
10
+ icon?: string | Component;
9
11
  }[];
10
12
  };
@@ -16,14 +16,16 @@
16
16
  @click="$emit('click:action', actionOrMenu.name)"
17
17
  >
18
18
  <div class="flex items-center gap-2 px-1 py-0.5">
19
- <Component :is="actionOrMenu.icon" v-if="actionOrMenu.icon" class="h-4 w-4" />
19
+ <Component :is="actionOrMenu.icon" v-if="isComponent(actionOrMenu.icon)" class="h-4 w-4" />
20
+ <PIcon v-if="isString(actionOrMenu.icon)" :icon="actionOrMenu.icon" width="16px" height="16px" />
20
21
  <div>{{ actionOrMenu.label }}</div>
21
22
  </div>
22
23
  </PBtn>
23
24
  <PDropdown v-else placement="top" strategy="fixed">
24
25
  <PBtn size="sm" type="secondary-ghost-dark">
25
26
  <div class="flex items-center gap-2 px-1 py-0.5">
26
- <Component :is="actionOrMenu.icon" v-if="actionOrMenu.icon" class="h-4 w-4" />
27
+ <Component :is="actionOrMenu.icon" v-if="isComponent(actionOrMenu.icon)" class="h-4 w-4" />
28
+ <PIcon v-if="isString(actionOrMenu.icon)" :icon="actionOrMenu.icon" width="16px" height="16px" />
27
29
  <div>{{ actionOrMenu.label }}</div>
28
30
  </div>
29
31
  </PBtn>
@@ -37,7 +39,8 @@
37
39
  @click="$emit('click:action', subaction.name)"
38
40
  >
39
41
  <div class="flex items-center gap-2 px-1 py-0.5">
40
- <Component :is="subaction.icon" v-if="subaction.icon" class="h-4 w-4" />
42
+ <Component :is="subaction.icon" v-if="isComponent(subaction.icon)" class="h-4 w-4" />
43
+ <PIcon v-if="isString(subaction.icon)" :icon="subaction.icon" width="16px" height="16px" />
41
44
  <div>{{ subaction.label }}</div>
42
45
  </div>
43
46
  </PBtn>
@@ -56,7 +59,10 @@
56
59
  import PBtn from '@squirrel/components/p-btn/p-btn.vue';
57
60
  import PCloseBtn from '@squirrel/components/p-close-btn/p-close-btn.vue';
58
61
  import PDropdown from '@squirrel/components/p-dropdown/p-dropdown.vue';
62
+ import PIcon from '@squirrel/components/p-icon/p-icon.vue';
59
63
  import { type PActionBarAction } from '@squirrel/components/p-action-bar/p-action-bar.types';
64
+ import { isComponent } from '@squirrel/utils/component';
65
+ import { isString } from '@squirrel/utils/string';
60
66
 
61
67
  defineOptions({
62
68
  name: 'PActionBar',
@@ -67,4 +67,19 @@ describe('PAvatar.vue', () => {
67
67
 
68
68
  expect(wrapper.find('div.items-center').classes()).toContain('custom-label-class');
69
69
  });
70
+
71
+ it('should reset imageBroken when imageSrc changes', async () => {
72
+ const wrapper = createWrapperFor(PAvatar, {
73
+ props: {
74
+ label: 'Test Label',
75
+ imageSrc: 'initial-src.jpg',
76
+ },
77
+ });
78
+
79
+ await wrapper.setData({ imageBroken: true });
80
+ expect(wrapper.vm.imageBroken).toBe(true);
81
+
82
+ await wrapper.setProps({ imageSrc: 'new-src.jpg' });
83
+ expect(wrapper.vm.imageBroken).toBe(false);
84
+ });
70
85
  });
@@ -1,7 +1,6 @@
1
1
  import PIcon from '@squirrel/components/p-icon/p-icon.vue';
2
2
  import { P_ICON_ALIASES } from '@squirrel/components/p-icon/p-icon.types';
3
3
  import { createWrapperFor } from '@tests/vitest.helpers';
4
- import { expect } from 'vitest';
5
4
 
6
5
  const createWrapper = (props, attrs) => {
7
6
  return createWrapperFor(PIcon, {
@@ -28,7 +28,8 @@
28
28
  </template>
29
29
 
30
30
  <script setup lang="ts">
31
- import { type Component, onBeforeUnmount, ref, toValue, watch } from 'vue';
31
+ import { isComponent } from '@squirrel/utils/component';
32
+ import { onBeforeUnmount, ref, toValue, watch } from 'vue';
32
33
  import { usePLoading } from '@squirrel/components/p-loading/usePLoading';
33
34
 
34
35
  const textDivClass = `overflow-hidden whitespace-nowrap px-4 pt-1 h-8 text-center text-sm font-semibold text-p-purple-60`;
@@ -53,12 +54,6 @@ watch(
53
54
  { flush: 'post' }
54
55
  );
55
56
 
56
- const isComponent = (content: unknown): content is Component => {
57
- const isComponent = typeof content === 'object' && content !== null;
58
-
59
- return isComponent;
60
- };
61
-
62
57
  onBeforeUnmount(() => {
63
58
  loadingHide();
64
59
  });
@@ -6,7 +6,6 @@ import {
6
6
  isLastColFixedInjectionKey,
7
7
  } from '@squirrel/components/p-table/p-table.types';
8
8
  import { createWrapperFor } from '@tests/vitest.helpers';
9
- import { expect } from 'vitest';
10
9
  import { ref } from 'vue';
11
10
 
12
11
  const DEFAULT_TD_CLASSES = [
@@ -0,0 +1,29 @@
1
+ import PIcon from '@squirrel/components/p-icon/p-icon.vue';
2
+ import { isComponent } from '@squirrel/utils/component';
3
+
4
+ describe('isComponent', () => {
5
+ it('should return true for a valid component object', () => {
6
+ expect(isComponent(PIcon)).toBe(true);
7
+ });
8
+
9
+ it('should return false for null', () => {
10
+ expect(isComponent(null)).toBe(false);
11
+ });
12
+
13
+ it.each([
14
+ ['string', 'string'],
15
+ ['number', 123],
16
+ ['boolean', true],
17
+ ['undefined', undefined],
18
+ ['symbol', Symbol('symbol')],
19
+ ['function', () => {}],
20
+ ['array', []],
21
+ ['date', new Date()],
22
+ ['regexp', /regex/],
23
+ ['error', new Error('error')],
24
+ ['map', new Map()],
25
+ ['set', new Set()],
26
+ ])('should return false for a non-component object: %s', (_, value) => {
27
+ expect(isComponent(value)).toBe(false);
28
+ });
29
+ });
@@ -0,0 +1,5 @@
1
+ import { type Component } from 'vue';
2
+ import { isPlainObject } from 'lodash-es';
3
+
4
+ // Vue produces a different type of object in dev and prod mode, so we cannot check for e.g `typeof val.render === 'function`
5
+ export const isComponent = (val: unknown): val is Component => isPlainObject(val);
@@ -1,4 +1,4 @@
1
- import { toString } from '@squirrel/utils/string';
1
+ import { isString, toString } from '@squirrel/utils/string';
2
2
 
3
3
  describe('toString', () => {
4
4
  it.each(['', new Date(), NaN, Infinity, {}, [], true, false, null, undefined])(
@@ -16,3 +16,21 @@ describe('toString', () => {
16
16
  expect(toString(2903498.22)).toBe('2903498.22');
17
17
  });
18
18
  });
19
+
20
+ describe('isString', () => {
21
+ it('should return true for a string', () => {
22
+ expect(isString('hello')).toBe(true);
23
+ });
24
+
25
+ it.each([
26
+ [123, false],
27
+ [{}, false],
28
+ [[], false],
29
+ [null, false],
30
+ [undefined, false],
31
+ [true, false],
32
+ [() => {}, false],
33
+ ])('should return %s for %p', (input, expected) => {
34
+ expect(isString(input)).toBe(expected);
35
+ });
36
+ });
@@ -1,3 +1,5 @@
1
+ export const isString = (val: unknown): val is string => typeof val === 'string';
2
+
1
3
  export const toString = (value: unknown): string => {
2
4
  if (typeof value === 'number' && (!isFinite(value) || isNaN(value))) {
3
5
  return '';