@pequity/squirrel 1.2.11 → 2.0.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 (39) hide show
  1. package/dist/cjs/p-checkbox.js +21 -11
  2. package/dist/cjs/p-loading.js +50 -17
  3. package/dist/cjs/usePLoading.js +27 -10
  4. package/dist/es/p-checkbox.js +22 -12
  5. package/dist/es/p-loading.js +51 -18
  6. package/dist/es/usePLoading.js +28 -11
  7. package/dist/squirrel/components/p-alert/p-alert.vue.d.ts +2 -2
  8. package/dist/squirrel/components/p-btn/p-btn.vue.d.ts +5 -4
  9. package/dist/squirrel/components/p-card/p-card.vue.d.ts +9 -7
  10. package/dist/squirrel/components/p-checkbox/p-checkbox.vue.d.ts +7 -5
  11. package/dist/squirrel/components/p-date-picker/p-date-picker.vue.d.ts +4 -4
  12. package/dist/squirrel/components/p-dropdown-select/p-dropdown-select.vue.d.ts +29 -27
  13. package/dist/squirrel/components/p-info-icon/p-info-icon.vue.d.ts +6 -4
  14. package/dist/squirrel/components/p-inline-date-picker/p-inline-date-picker.vue.d.ts +4 -4
  15. package/dist/squirrel/components/p-input/p-input.vue.d.ts +6 -6
  16. package/dist/squirrel/components/p-input-number/p-input-number.vue.d.ts +6 -6
  17. package/dist/squirrel/components/p-input-percent/p-input-percent.vue.d.ts +5 -4
  18. package/dist/squirrel/components/p-input-search/p-input-search.vue.d.ts +2 -2
  19. package/dist/squirrel/components/p-loading/usePLoading.d.ts +7 -3
  20. package/dist/squirrel/components/p-modal/p-modal.vue.d.ts +10 -8
  21. package/dist/squirrel/components/p-pagination-info/p-pagination-info.vue.d.ts +6 -4
  22. package/dist/squirrel/components/p-select/p-select.vue.d.ts +14 -11
  23. package/dist/squirrel/components/p-select-btn/p-select-btn.vue.d.ts +8 -6
  24. package/dist/squirrel/components/p-select-list/p-select-list.vue.d.ts +29 -27
  25. package/dist/squirrel/components/p-select-pill/p-select-pill.vue.d.ts +4 -4
  26. package/dist/squirrel/components/p-table/p-table.vue.d.ts +8 -6
  27. package/dist/squirrel/components/p-table-td/p-table-td.vue.d.ts +6 -4
  28. package/dist/squirrel/components/p-textarea/p-textarea.vue.d.ts +4 -4
  29. package/dist/squirrel/components/p-toggle/p-toggle.vue.d.ts +4 -4
  30. package/dist/squirrel/utils/inputClassesMixin.d.ts +2 -2
  31. package/dist/squirrel/utils/object.d.ts +1 -1
  32. package/dist/style.css +7 -7
  33. package/package.json +22 -22
  34. package/squirrel/components/p-checkbox/p-checkbox.vue +16 -2
  35. package/squirrel/components/p-loading/p-loading.spec.js +71 -13
  36. package/squirrel/components/p-loading/p-loading.stories.js +61 -4
  37. package/squirrel/components/p-loading/p-loading.vue +27 -3
  38. package/squirrel/components/p-loading/usePLoading.ts +46 -13
  39. package/squirrel/components/p-select-list/p-select-list.utils.ts +1 -1
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@pequity/squirrel",
3
3
  "description": "Squirrel component library",
4
- "version": "1.2.11",
4
+ "version": "2.0.0",
5
5
  "packageManager": "pnpm@8.9.2",
6
6
  "type": "module",
7
7
  "scripts": {
@@ -58,26 +58,26 @@
58
58
  "@popperjs/core": "2.11.8",
59
59
  "@semantic-release/changelog": "^6.0.3",
60
60
  "@semantic-release/git": "^10.0.1",
61
- "@storybook/addon-a11y": "^8.1.10",
62
- "@storybook/addon-actions": "^8.1.10",
63
- "@storybook/addon-essentials": "^8.1.10",
64
- "@storybook/addon-interactions": "^8.1.10",
65
- "@storybook/addon-links": "^8.1.10",
66
- "@storybook/blocks": "^8.1.10",
61
+ "@storybook/addon-a11y": "^8.1.11",
62
+ "@storybook/addon-actions": "^8.1.11",
63
+ "@storybook/addon-essentials": "^8.1.11",
64
+ "@storybook/addon-interactions": "^8.1.11",
65
+ "@storybook/addon-links": "^8.1.11",
66
+ "@storybook/blocks": "^8.1.11",
67
67
  "@storybook/jest": "^0.2.3",
68
- "@storybook/manager-api": "^8.1.10",
68
+ "@storybook/manager-api": "^8.1.11",
69
69
  "@storybook/test-runner": "^0.19.0",
70
70
  "@storybook/testing-library": "^0.2.2",
71
- "@storybook/theming": "^8.1.10",
72
- "@storybook/vue3": "^8.1.10",
73
- "@storybook/vue3-vite": "^8.1.10",
74
- "@tanstack/vue-virtual": "3.7.0",
71
+ "@storybook/theming": "^8.1.11",
72
+ "@storybook/vue3": "^8.1.11",
73
+ "@storybook/vue3-vite": "^8.1.11",
74
+ "@tanstack/vue-virtual": "3.8.1",
75
75
  "@types/jest": "^29.5.12",
76
76
  "@types/jsdom": "^21.1.7",
77
77
  "@types/lodash-es": "^4.17.12",
78
- "@types/node": "^20.14.8",
78
+ "@types/node": "^20.14.10",
79
79
  "@vitejs/plugin-vue": "^5.0.5",
80
- "@vue/compiler-sfc": "3.4.30",
80
+ "@vue/compiler-sfc": "3.4.31",
81
81
  "@vue/test-utils": "^2.4.6",
82
82
  "@vue/vue3-jest": "^29.2.6",
83
83
  "autoprefixer": "^10.4.19",
@@ -86,31 +86,31 @@
86
86
  "eslint": "^8.57.0",
87
87
  "eslint-plugin-storybook": "^0.8.0",
88
88
  "floating-vue": "5.2.2",
89
- "glob": "^10.4.2",
89
+ "glob": "^10.4.3",
90
90
  "husky": "^9.0.11",
91
91
  "jest": "^29.7.0",
92
92
  "jest-environment-jsdom": "^29.7.0",
93
93
  "lint-staged": "^15.2.7",
94
94
  "lodash-es": "4.17.21",
95
95
  "make-coverage-badge": "^1.2.0",
96
- "postcss": "^8.4.38",
96
+ "postcss": "^8.4.39",
97
97
  "prettier": "^3.3.2",
98
98
  "prettier-plugin-tailwindcss": "^0.6.5",
99
99
  "resolve-tspaths": "^0.8.19",
100
- "rimraf": "^5.0.7",
100
+ "rimraf": "^5.0.8",
101
101
  "sass": "^1.77.6",
102
102
  "semantic-release": "^24.0.0",
103
- "storybook": "^8.1.10",
103
+ "storybook": "^8.1.11",
104
104
  "svgo": "^3.3.2",
105
105
  "tailwindcss": "^3.4.4",
106
106
  "ts-jest": "^29.1.5",
107
- "typescript": "5.4.5",
107
+ "typescript": "5.5.3",
108
108
  "v-calendar": "3.1.2",
109
- "vite": "^5.3.1",
110
- "vue": "3.4.30",
109
+ "vite": "^5.3.3",
110
+ "vue": "3.4.31",
111
111
  "vue-currency-input": "3.1.0",
112
112
  "vue-router": "4.4.0",
113
113
  "vue-toastification": "2.0.0-rc.5",
114
- "vue-tsc": "2.0.22"
114
+ "vue-tsc": "2.0.26"
115
115
  }
116
116
  }
@@ -1,11 +1,11 @@
1
1
  <template>
2
- <label class="inline-flex items-center">
2
+ <label class="inline-flex items-center" :class="[{ hidden: $attrs.hidden }, $attrs.class]" :style="style">
3
3
  <slot name="label-before"></slot>
4
4
  <input
5
5
  type="checkbox"
6
6
  class="h-4 w-4 shrink-0 appearance-none rounded border border-p-gray-30 bg-surface duration-100 ease-in-out checked:border-none checked:bg-primary disabled:opacity-50"
7
+ v-bind="attrs"
7
8
  :checked="modelValue"
8
- v-bind="$attrs"
9
9
  @change="$emit('update:modelValue', ($event.target as HTMLInputElement).checked)"
10
10
  />
11
11
  <slot name="label">
@@ -17,6 +17,8 @@
17
17
  </template>
18
18
 
19
19
  <script setup lang="ts">
20
+ import { type StyleValue, computed, useAttrs } from 'vue';
21
+
20
22
  defineOptions({
21
23
  name: 'PCheckbox',
22
24
  inheritAttrs: false,
@@ -34,6 +36,18 @@ defineProps({
34
36
  default: '',
35
37
  },
36
38
  });
39
+
40
+ const allAttrs = useAttrs();
41
+
42
+ const attrs = computed(() => {
43
+ const { class: classes, style, ...rest } = allAttrs;
44
+
45
+ return rest;
46
+ });
47
+
48
+ const style = computed(() => {
49
+ return allAttrs.style as StyleValue;
50
+ });
37
51
  </script>
38
52
 
39
53
  <style lang="css" scoped>
@@ -1,4 +1,5 @@
1
1
  import PLoading from '@squirrel/components/p-loading/p-loading.vue';
2
+ import PSkeletonLoader from '@squirrel/components/p-skeleton-loader/p-skeleton-loader.vue';
2
3
  import { createWrapperFor, sleep, waitNT } from '@tests/jest.helpers';
3
4
  import { usePLoading } from '@squirrel/components/p-loading/usePLoading';
4
5
 
@@ -9,25 +10,43 @@ const createAppWrapper = () =>
9
10
  });
10
11
 
11
12
  const createWrapper = (options) => {
12
- return createWrapperFor({
13
- template: `
13
+ return createWrapperFor(
14
+ {
15
+ template: `
14
16
  <button class="show" @click="loadingShow"></button>
15
17
  <button class="hide" @click="loadingHide"></button>
16
18
  <button class="request-1-sec" @click="fireRequest(1000)"></button>
17
19
  <button class="request-3-secs" @click="fireRequest(3000)"></button>
20
+ <button class="request-1-sec-text" @click="fireRequest(1000, '1 sec')"></button>
21
+ <button class="request-3-secs-text" @click="fireRequest(3000, '3 secs')"></button>
22
+ <button class="request-1-sec-component" @click="fireRequestComponent(1000)"></button>
18
23
  `,
19
- setup() {
20
- const { loadingShow, loadingHide } = usePLoading(options);
21
-
22
- const fireRequest = async (time) => {
23
- loadingShow();
24
- await sleep(time);
25
- loadingHide();
26
- };
27
-
28
- return { loadingShow, loadingHide, fireRequest };
24
+ setup() {
25
+ const { loadingShow, loadingHide } = usePLoading(options);
26
+
27
+ const fireRequest = async (time, text) => {
28
+ const hide = loadingShow(text);
29
+ await sleep(time);
30
+ hide();
31
+ };
32
+
33
+ const fireRequestComponent = async (time) => {
34
+ const hide = loadingShow(PSkeletonLoader);
35
+ await new Promise((resolve) => setTimeout(resolve, time));
36
+ hide();
37
+ };
38
+
39
+ return { loadingShow, loadingHide, fireRequest, fireRequestComponent };
40
+ },
29
41
  },
30
- });
42
+ {
43
+ global: {
44
+ stubs: {
45
+ PSkeletonLoader: true,
46
+ },
47
+ },
48
+ }
49
+ );
31
50
  };
32
51
 
33
52
  describe('PLoading.vue', () => {
@@ -117,4 +136,43 @@ describe('PLoading.vue', () => {
117
136
  await waitNT(appWrapper.vm);
118
137
  expect(appWrapper.find('[aria-busy]').exists()).toBe(true);
119
138
  });
139
+
140
+ it(`shows the loading indicator with variable text`, async () => {
141
+ const wrapper = createWrapper({ delay: 200 });
142
+
143
+ await wrapper.find('.request-3-secs-text').trigger('click');
144
+
145
+ jest.advanceTimersByTime(300);
146
+ await waitNT(appWrapper.vm);
147
+ expect(appWrapper.find('[aria-busy]').text()).toBe('3 secs');
148
+
149
+ await wrapper.find('.request-1-sec-text').trigger('click');
150
+
151
+ jest.advanceTimersByTime(300);
152
+ await waitNT(appWrapper.vm);
153
+ expect(appWrapper.find('[aria-busy]').text()).toBe('1 sec');
154
+
155
+ jest.advanceTimersByTime(1200);
156
+ await waitNT(appWrapper.vm);
157
+ expect(appWrapper.find('[aria-busy]').text()).toBe('3 secs');
158
+
159
+ jest.advanceTimersByTime(1200);
160
+ await waitNT(appWrapper.vm);
161
+ expect(appWrapper.find('[aria-busy]').exists()).toBe(false);
162
+ });
163
+
164
+ it(`accepts a component as the content`, async () => {
165
+ const wrapper = createWrapper({ delay: 200 });
166
+
167
+ await wrapper.find('.request-1-sec-component').trigger('click');
168
+
169
+ jest.advanceTimersByTime(300);
170
+ await waitNT(appWrapper.vm);
171
+ expect(appWrapper.find('[aria-busy]').exists()).toBe(true);
172
+ expect(appWrapper.findComponent(PSkeletonLoader).exists()).toBe(true);
173
+
174
+ jest.advanceTimersByTime(800);
175
+ await waitNT(appWrapper.vm);
176
+ expect(appWrapper.find('[aria-busy]').exists()).toBe(false);
177
+ });
120
178
  });
@@ -1,3 +1,4 @@
1
+ import ComputationStatus from '@/playground/components/ComputationStatus.vue';
1
2
  import PBtn from '@squirrel/components/p-btn/p-btn.vue';
2
3
  import PLoading from '@squirrel/components/p-loading/p-loading.vue';
3
4
  import { usePLoading } from '@squirrel/components/p-loading/usePLoading';
@@ -23,15 +24,15 @@ export const Default = {
23
24
  render: (args) => ({
24
25
  components: { PLoading, PBtn },
25
26
  setup() {
26
- const { loadingShow, loadingHide } = usePLoading();
27
+ const { loadingShow } = usePLoading();
27
28
 
28
29
  const fireRequest = async (time) => {
29
- loadingShow();
30
+ const hide = loadingShow();
30
31
  await new Promise((resolve) => setTimeout(resolve, time));
31
- loadingHide();
32
+ hide();
32
33
  };
33
34
 
34
- return { args, loadingShow, loadingHide, fireRequest };
35
+ return { args, loadingShow, fireRequest };
35
36
  },
36
37
  template: `
37
38
  <PLoading />
@@ -47,3 +48,59 @@ export const Default = {
47
48
  `,
48
49
  }),
49
50
  };
51
+
52
+ export const WithVariableText = {
53
+ render: (args) => ({
54
+ components: { PLoading, PBtn },
55
+ setup() {
56
+ const { loadingShow } = usePLoading();
57
+
58
+ const fireRequestText = async (time, text) => {
59
+ const hide = loadingShow(text);
60
+ await new Promise((resolve) => setTimeout(resolve, time));
61
+ hide();
62
+ };
63
+
64
+ return { args, loadingShow, fireRequestText };
65
+ },
66
+ template: `
67
+ <PLoading />
68
+ <div class="mt-10">
69
+ <div>Demo with variable loading text</div>
70
+ <div class="flex gap-4 mt-2">
71
+ <PBtn @click="fireRequestText(1000, '1 sec')">Fire request 1 sec</PBtn>
72
+ <PBtn @click="fireRequestText(3000, '3 secs')">Fire request 3 secs</PBtn>
73
+ <PBtn @click="fireRequestText(5000, '5 secs')">Fire request 5 secs</PBtn>
74
+ </div>
75
+ </div>
76
+ `,
77
+ }),
78
+ };
79
+
80
+ export const WithComponentAsContent = {
81
+ render: (args) => ({
82
+ components: { PLoading, PBtn },
83
+ setup() {
84
+ const { loadingShow } = usePLoading();
85
+
86
+ const fireRequestComponent = async (time) => {
87
+ const hide = loadingShow(ComputationStatus);
88
+ await new Promise((resolve) => setTimeout(resolve, time));
89
+ hide();
90
+ };
91
+
92
+ return { args, loadingShow, fireRequestComponent };
93
+ },
94
+ template: `
95
+ <PLoading />
96
+ <div class="mt-10">
97
+ <div>Demo with a component as content</div>
98
+ <div class="flex gap-4 mt-2">
99
+ <PBtn @click="fireRequestComponent(1000)">Fire request 1 sec</PBtn>
100
+ <PBtn @click="fireRequestComponent(3000)">Fire request 3 secs</PBtn>
101
+ <PBtn @click="fireRequestComponent(5000)">Fire request 5 secs</PBtn>
102
+ </div>
103
+ </div>
104
+ `,
105
+ }),
106
+ };
@@ -2,22 +2,46 @@
2
2
  <Transition name="pm-backdrop-transition" enter-active-class="fadeInDown" leave-active-class="fadeOutUp">
3
3
  <div v-if="show" class="fixed left-0 top-0 z-[120] flex w-full justify-center" aria-live="polite" aria-busy="true">
4
4
  <div
5
- class="h-8 rounded-b border-x border-b border-p-gray-30 bg-p-blue-10 px-4 pt-2 text-sm font-semibold leading-none text-p-purple-60 shadow-sm"
5
+ :style="{ width: `${width}px` }"
6
+ class="h-8 overflow-hidden whitespace-nowrap rounded-b border-x border-b border-p-gray-30 bg-p-blue-10 px-4 pt-2 text-center text-sm font-semibold leading-none text-p-purple-60 shadow-sm transition-all duration-500"
6
7
  >
7
- Loading...
8
+ <Component :is="content" v-if="isComponent(content)" />
9
+ <template v-else>{{ content }}</template>
8
10
  </div>
9
11
  </div>
10
12
  </Transition>
13
+ <div v-if="content" class="invisible fixed">
14
+ <div ref="widthReference" class="inline-flex px-4">
15
+ <Component :is="content" v-if="isComponent(content)" />
16
+ <template v-else>{{ content }}</template>
17
+ </div>
18
+ </div>
11
19
  </template>
12
20
 
13
21
  <script setup lang="ts">
22
+ import { type Component, ref, toValue, watch } from 'vue';
14
23
  import { usePLoading } from '@squirrel/components/p-loading/usePLoading';
15
24
 
16
25
  defineOptions({
17
26
  name: 'PLoading',
18
27
  });
19
28
 
20
- const { show } = usePLoading();
29
+ const { show, content } = usePLoading();
30
+ const widthReference = ref<HTMLElement | null>(null);
31
+ const width = ref(0);
32
+
33
+ watch(
34
+ () => toValue(content),
35
+ () => {
36
+ if (widthReference.value) {
37
+ width.value = widthReference.value.offsetWidth;
38
+ }
39
+ },
40
+ { flush: 'post' }
41
+ );
42
+
43
+ const isComponent = (content: unknown): content is Component =>
44
+ typeof content === 'object' && content !== null && 'render' in content && typeof content.render === 'function';
21
45
  </script>
22
46
 
23
47
  <style scoped lang="scss">
@@ -1,20 +1,40 @@
1
- import { getCurrentScope, onScopeDispose, ref } from 'vue';
1
+ import { type Component, type Ref, computed, getCurrentScope, markRaw, onScopeDispose, ref } from 'vue';
2
2
  import { uniqueId } from 'lodash-es';
3
3
 
4
4
  type Options = {
5
5
  delay?: number;
6
6
  };
7
7
 
8
+ type Content = string | Component;
9
+
10
+ type LoadingItem = {
11
+ id: string;
12
+ content: Content;
13
+ };
14
+
15
+ type UsePLoading = {
16
+ show: Ref<boolean>;
17
+ content: Content;
18
+ loadingShow: (content?: Content) => () => void;
19
+ loadingHide: () => void;
20
+ };
21
+
22
+ const LOADING_TEXT = 'Loading...';
8
23
  const show = ref(false);
9
- const loadingIds: string[] = [];
24
+ const loadingItems = ref<LoadingItem[]>([]);
25
+ const content = computed(() => {
26
+ return loadingItems.value[loadingItems.value.length - 1]?.content;
27
+ });
10
28
  let timer: ReturnType<typeof setTimeout>;
11
29
 
12
- export const usePLoading = (options?: Options) => {
30
+ export const usePLoading = (options?: Options): UsePLoading => {
13
31
  const { delay } = { delay: 200, ...options };
14
32
  const scope = getCurrentScope();
15
33
 
16
- const loadingShow = () => {
17
- if (loadingIds.length === 0 && delay > 0) {
34
+ const loadingShow = (content?: Content) => {
35
+ const loadingId = uniqueId();
36
+
37
+ if (loadingItems.value.length === 0 && delay > 0) {
18
38
  timer = setTimeout(() => {
19
39
  show.value = true;
20
40
  }, delay);
@@ -22,23 +42,36 @@ export const usePLoading = (options?: Options) => {
22
42
  show.value = true;
23
43
  }
24
44
 
25
- loadingIds.push(uniqueId());
45
+ loadingItems.value.push({
46
+ id: loadingId,
47
+ content: content && typeof content === 'object' ? markRaw(content) : content || LOADING_TEXT,
48
+ });
49
+
50
+ const hide = () => {
51
+ loadingItems.value = loadingItems.value.filter((item) => item.id !== loadingId);
52
+ clearTimeout(timer);
53
+
54
+ if (loadingItems.value.length === 0) {
55
+ show.value = false;
56
+ }
57
+ };
58
+
59
+ return hide;
26
60
  };
27
61
 
28
62
  const loadingHide = () => {
29
- if (loadingIds.length === 1) {
30
- show.value = false;
31
- }
32
-
63
+ loadingItems.value = [];
64
+ show.value = false;
33
65
  clearTimeout(timer);
34
- loadingIds.pop();
35
66
  };
36
67
 
37
68
  if (scope) {
38
69
  onScopeDispose(() => {
39
- loadingHide();
70
+ loadingItems.value = [];
71
+ show.value = false;
72
+ clearTimeout(timer);
40
73
  });
41
74
  }
42
75
 
43
- return { show, loadingShow, loadingHide };
76
+ return { show, content, loadingShow, loadingHide };
44
77
  };
@@ -27,7 +27,7 @@ export const createInternalValue = (
27
27
  if (multiple) {
28
28
  val = (modelValue as AnyObject[]).map((v) => v[itemValue as keyof typeof modelValue]) as AnyValue[];
29
29
  } else {
30
- val = modelValue ? modelValue[itemValue as keyof typeof modelValue] : modelValue;
30
+ val = modelValue ? (modelValue as AnyObject)[itemValue as keyof typeof modelValue] : modelValue;
31
31
  }
32
32
  }
33
33