@flux-ui/components 3.0.0-next.26 → 3.0.0-next.28

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 (49) hide show
  1. package/dist/component/FluxAnimatedColors.vue.d.ts +1 -0
  2. package/dist/component/FluxBorderShine.vue.d.ts +1 -1
  3. package/dist/component/FluxFormInput.vue.d.ts +1 -0
  4. package/dist/component/FluxGallery.vue.d.ts +2 -2
  5. package/dist/component/FluxItemActions.vue.d.ts +4 -1
  6. package/dist/component/FluxItemContent.vue.d.ts +4 -1
  7. package/dist/component/FluxItemMedia.vue.d.ts +5 -1
  8. package/dist/component/FluxPrompt.vue.d.ts +2 -0
  9. package/dist/component/FluxTableCell.vue.d.ts +1 -0
  10. package/dist/component/primitive/SelectBase.vue.d.ts +5 -3
  11. package/dist/component/primitive/SliderBase.vue.d.ts +2 -2
  12. package/dist/composable/private/index.d.ts +0 -1
  13. package/dist/index.css +39 -23
  14. package/dist/index.js +3909 -811
  15. package/dist/index.js.map +1 -1
  16. package/package.json +8 -8
  17. package/src/component/FluxAnimatedColors.vue +6 -4
  18. package/src/component/FluxAutoGrid.vue +1 -1
  19. package/src/component/FluxContainer.vue +1 -1
  20. package/src/component/FluxExpandable.vue +1 -1
  21. package/src/component/FluxFader.vue +2 -1
  22. package/src/component/FluxFilterOptionAsync.vue +1 -2
  23. package/src/component/FluxFilterOptionsAsync.vue +1 -2
  24. package/src/component/FluxFormInput.vue +7 -0
  25. package/src/component/FluxFormSelectAsync.vue +2 -2
  26. package/src/component/FluxGrid.vue +1 -1
  27. package/src/component/FluxIcon.vue +0 -12
  28. package/src/component/FluxItemActions.vue +6 -2
  29. package/src/component/FluxItemContent.vue +5 -1
  30. package/src/component/FluxItemMedia.vue +10 -1
  31. package/src/component/FluxPressable.vue +1 -1
  32. package/src/component/FluxStack.vue +1 -1
  33. package/src/component/FluxTabBar.vue +2 -1
  34. package/src/component/FluxTableCell.vue +3 -1
  35. package/src/component/primitive/AnchorPopup.vue +1 -1
  36. package/src/component/primitive/FilterItem.vue +1 -1
  37. package/src/component/primitive/SelectBase.vue +2 -1
  38. package/src/composable/private/index.ts +0 -1
  39. package/src/css/component/Badge.module.scss +1 -1
  40. package/src/css/component/Icon.module.scss +0 -17
  41. package/src/css/component/Item.module.scss +18 -4
  42. package/src/css/component/Pane.module.scss +10 -1
  43. package/src/css/component/Tab.module.scss +7 -0
  44. package/src/css/component/Table.module.scss +1 -1
  45. package/src/css/typography.scss +5 -1
  46. package/src/css/variables.scss +1 -1
  47. package/src/util/createDialogRenderer.ts +3 -1
  48. package/dist/composable/private/useLoaded.d.ts +0 -5
  49. package/src/composable/private/useLoaded.ts +0 -21
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@flux-ui/components",
3
3
  "description": "A set of opiniated UI components.",
4
- "version": "3.0.0-next.26",
4
+ "version": "3.0.0-next.28",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "funding": "https://github.com/sponsors/basmilius",
@@ -51,9 +51,10 @@
51
51
  "typings": "./dist/index.d.ts",
52
52
  "sideEffects": false,
53
53
  "dependencies": {
54
- "@basmilius/utils": "^2.27.0",
55
- "@flux-ui/internals": "3.0.0-next.26",
56
- "@flux-ui/types": "3.0.0-next.26",
54
+ "@basmilius/common": "^3.8.0",
55
+ "@basmilius/utils": "^3.8.0",
56
+ "@flux-ui/internals": "3.0.0-next.28",
57
+ "@flux-ui/types": "3.0.0-next.28",
57
58
  "@fortawesome/fontawesome-common-types": "^7.2.0",
58
59
  "clsx": "^2.1.1",
59
60
  "imask": "^7.6.1",
@@ -62,17 +63,16 @@
62
63
  "vue": "^3.6.0-beta.6"
63
64
  },
64
65
  "devDependencies": {
65
- "@basmilius/vite-preset": "^2.27.0",
66
+ "@basmilius/vite-preset": "^3.8.0",
66
67
  "@types/lodash-es": "^4.17.12",
67
68
  "@types/luxon": "^3.7.1",
68
- "@types/node": "^25.3.0",
69
+ "@types/node": "^25.3.1",
69
70
  "@vitejs/plugin-vue": "^6.0.4",
70
- "@vue/language-core": "^2.2.12",
71
71
  "@vue/tsconfig": "^0.8.1",
72
72
  "pinia": "^3.0.4",
73
73
  "sass-embedded": "^1.97.3",
74
74
  "typescript": "^5.9.3",
75
75
  "vite": "^8.0.0-beta.15",
76
- "vue-tsc": "^3.2.4"
76
+ "vue-tsc": "^3.2.5"
77
77
  }
78
78
  }
@@ -7,8 +7,8 @@
7
7
  <script
8
8
  lang="ts"
9
9
  setup>
10
+ import { useComponentId } from '@basmilius/common';
10
11
  import { mulberry32 } from '@basmilius/utils';
11
- import { useComponentId } from '@flux-ui/internals';
12
12
  import { computed, onBeforeUnmount, onMounted, ref, unref, useTemplateRef, watch } from 'vue';
13
13
  import $style from '$flux/css/component/Visual.module.scss';
14
14
 
@@ -19,12 +19,14 @@
19
19
  colors,
20
20
  incrementor = 1,
21
21
  opacity = .5,
22
- seed
22
+ seed,
23
+ static: isStatic
23
24
  } = defineProps<{
24
25
  readonly colors?: string[];
25
26
  readonly incrementor?: number;
26
27
  readonly opacity?: number;
27
28
  readonly seed?: number;
29
+ readonly static?: boolean;
28
30
  }>();
29
31
 
30
32
  const canvasRef = useTemplateRef('canvas');
@@ -119,7 +121,7 @@
119
121
  context.restore();
120
122
  }
121
123
 
122
- schedule();
124
+ !isStatic && schedule();
123
125
  }
124
126
 
125
127
  watch(canvasRef, canvas => {
@@ -136,6 +138,6 @@
136
138
 
137
139
  watch(polygons, () => {
138
140
  cancel();
139
- schedule();
141
+ !isStatic && schedule();
140
142
  });
141
143
  </script>
@@ -15,7 +15,7 @@
15
15
  import $style from '$flux/css/component/Layout.module.scss';
16
16
 
17
17
  const {
18
- gap = 30
18
+ gap = 21
19
19
  } = defineProps<{
20
20
  readonly gap?: number;
21
21
  readonly minColumnWidth: number;
@@ -12,7 +12,7 @@
12
12
  import $style from '$flux/css/component/Layout.module.scss';
13
13
 
14
14
  const {
15
- gutter = 30
15
+ gutter = 21
16
16
  } = defineProps<{
17
17
  readonly gutter?: number;
18
18
  }>();
@@ -50,7 +50,7 @@
50
50
  <script
51
51
  lang="ts"
52
52
  setup>
53
- import { useComponentId } from '@flux-ui/internals';
53
+ import { useComponentId } from '@basmilius/common';
54
54
  import type { FluxIconName } from '@flux-ui/types';
55
55
  import { computed, getCurrentInstance, onBeforeMount, onUnmounted, ref, unref, useId, watch } from 'vue';
56
56
  import { useExpandableGroupInjection } from '$flux/composable';
@@ -9,7 +9,8 @@
9
9
  <script
10
10
  lang="ts"
11
11
  setup>
12
- import { unrefTemplateElement, useInterval } from '@flux-ui/internals';
12
+ import { useInterval } from '@basmilius/common';
13
+ import { unrefTemplateElement } from '@flux-ui/internals';
13
14
  import { computed, ref, unref, useTemplateRef, watch } from 'vue';
14
15
  import $style from '$flux/css/component/Fader.module.scss';
15
16
 
@@ -12,11 +12,10 @@
12
12
  <script
13
13
  lang="ts"
14
14
  setup>
15
- import { useDebouncedRef } from '@flux-ui/internals';
15
+ import { useDebouncedRef, useLoaded } from '@basmilius/common';
16
16
  import type { FluxFilterOptionRow, FluxFilterValue, FluxFilterValueSingle, FluxIconName } from '@flux-ui/types';
17
17
  import { computed, ref, unref, watch } from 'vue';
18
18
  import { useFilterInjection } from '$flux/composable';
19
- import { useLoaded } from '$flux/composable/private';
20
19
  import { isFluxFilterOptionItem } from '$flux/data';
21
20
  import { FilterOptionBase } from './primitive';
22
21
 
@@ -12,11 +12,10 @@
12
12
  <script
13
13
  lang="ts"
14
14
  setup>
15
- import { useDebouncedRef } from '@flux-ui/internals';
15
+ import { useDebouncedRef, useLoaded } from '@basmilius/common';
16
16
  import type { FluxFilterOptionRow, FluxFilterValue, FluxFilterValueSingle, FluxIconName } from '@flux-ui/types';
17
17
  import { computed, ref, unref, watch } from 'vue';
18
18
  import { useFilterInjection } from '$flux/composable';
19
- import { useLoaded } from '$flux/composable/private';
20
19
  import { isFluxFilterOptionItem } from '$flux/data';
21
20
  import { FilterOptionBase } from './primitive';
22
21
 
@@ -49,6 +49,11 @@
49
49
  :class="$style.formInputIconTrailing"
50
50
  :name="iconTrailing"
51
51
  :size="18"/>
52
+
53
+ <FluxSpinner
54
+ v-if="isLoading"
55
+ :class="$style.formInputIconTrailing"
56
+ :size="18"/>
52
57
  </div>
53
58
  </template>
54
59
 
@@ -63,6 +68,7 @@
63
68
  import { useDisabled, useFormFieldInjection } from '$flux/composable';
64
69
  import { inputMask } from '$flux/data';
65
70
  import FluxIcon from './FluxIcon.vue';
71
+ import FluxSpinner from './FluxSpinner.vue';
66
72
  import $style from '$flux/css/component/Form.module.scss';
67
73
 
68
74
  const emit = defineEmits<{
@@ -87,6 +93,7 @@
87
93
  readonly iconTrailing?: FluxIconName;
88
94
  readonly disabled?: boolean;
89
95
  readonly isCondensed?: boolean;
96
+ readonly isLoading?: boolean;
90
97
  readonly isReadonly?: boolean;
91
98
  readonly isSecondary?: boolean;
92
99
  readonly max?: string | number;
@@ -16,12 +16,12 @@
16
16
  <script
17
17
  lang="ts"
18
18
  setup>
19
- import { useDebouncedRef } from '@flux-ui/internals';
19
+ import { useDebouncedRef, useLoaded } from '@basmilius/common';
20
20
  import type { FluxFormSelectEntry, FluxFormSelectValue, FluxFormSelectValueSingle } from '@flux-ui/types';
21
21
  import { computed, ref, toRef, unref, watch } from 'vue';
22
22
  import { SelectBase } from '$flux/component/primitive';
23
23
  import { useDisabled } from '$flux/composable';
24
- import { useFormSelect, useLoaded } from '$flux/composable/private';
24
+ import { useFormSelect } from '$flux/composable/private';
25
25
  import { isFluxFormSelectOption } from '$flux/data';
26
26
 
27
27
  const modelSearch = defineModel<string>('searchQuery', {
@@ -16,7 +16,7 @@
16
16
 
17
17
  const {
18
18
  columns = 12,
19
- gap = 30
19
+ gap = 21
20
20
  } = defineProps<{
21
21
  readonly columns?: number;
22
22
  readonly gap?: number;
@@ -17,18 +17,6 @@
17
17
  fill="currentColor"/>
18
18
  </svg>
19
19
 
20
- <i
21
- v-else-if="name"
22
- :class="$style.materialSymbolIcon"
23
- :style="{
24
- fontSize: size && `${size}px`
25
- }"
26
- role="img"
27
- aria-hidden="true"
28
- @click="onClick">
29
- {{ name }}
30
- </i>
31
-
32
20
  <i
33
21
  v-else
34
22
  :class="$style.icon"/>
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
  <FluxActionBar
3
- :class="$style.itemActions"
3
+ :class="[$style.itemActions, isCenter && $style.isCenter]"
4
4
  #primary>
5
5
  <slot/>
6
6
  </FluxActionBar>
@@ -9,6 +9,10 @@
9
9
  <script
10
10
  lang="ts"
11
11
  setup>
12
- import FluxActionBar from './FluxActionBar.vue';
13
12
  import $style from '$flux/css/component/Item.module.scss';
13
+ import FluxActionBar from './FluxActionBar.vue';
14
+
15
+ defineProps<{
16
+ readonly isCenter?: boolean;
17
+ }>();
14
18
  </script>
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <div :class="$style.itemContent">
2
+ <div :class="[$style.itemContent, isCenter && $style.isCenter]">
3
3
  <slot/>
4
4
  </div>
5
5
  </template>
@@ -8,4 +8,8 @@
8
8
  lang="ts"
9
9
  setup>
10
10
  import $style from '$flux/css/component/Item.module.scss';
11
+
12
+ defineProps<{
13
+ readonly isCenter?: boolean;
14
+ }>();
11
15
  </script>
@@ -1,5 +1,9 @@
1
1
  <template>
2
- <div :class="$style.itemMedia">
2
+ <div
3
+ :class="[$style.itemMedia, isCenter && $style.isCenter]"
4
+ :style="{
5
+ '--size': size && `${size}px`
6
+ }">
3
7
  <slot/>
4
8
  </div>
5
9
  </template>
@@ -8,4 +12,9 @@
8
12
  lang="ts"
9
13
  setup>
10
14
  import $style from '$flux/css/component/Item.module.scss';
15
+
16
+ defineProps<{
17
+ readonly isCenter?: boolean;
18
+ readonly size?: number;
19
+ }>();
11
20
  </script>
@@ -4,7 +4,7 @@
4
4
  v-bind="$attrs"
5
5
  :rel="rel"
6
6
  :target="target"
7
- :to="to"
7
+ :to="to as any"
8
8
  @click="onClick($event)"
9
9
  @mouseenter="$emit('mouseenter', $event)"
10
10
  @mouseleave="$emit('mouseleave', $event)">
@@ -24,7 +24,7 @@
24
24
 
25
25
  const {
26
26
  direction = 'vertical',
27
- gap = 30
27
+ gap = 21
28
28
  } = defineProps<{
29
29
  readonly direction?: FluxDirection;
30
30
  readonly gap?: number;
@@ -40,7 +40,8 @@
40
40
  <script
41
41
  lang="ts"
42
42
  setup>
43
- import { unrefTemplateElement, useEventListener, useMutationObserver } from '@flux-ui/internals';
43
+ import { useMutationObserver } from '@basmilius/common';
44
+ import { unrefTemplateElement, useEventListener } from '@flux-ui/internals';
44
45
  import { clsx } from 'clsx';
45
46
  import { onMounted, ref, useTemplateRef } from 'vue';
46
47
  import { FluxFadeTransition } from '$flux/transition';
@@ -12,7 +12,8 @@
12
12
  <div
13
13
  :class="$style.tableCellContent"
14
14
  :style="{
15
- flexFlow: contentDirection
15
+ flexFlow: contentDirection,
16
+ gap: contentGap && `${contentGap}px`
16
17
  }">
17
18
  <slot/>
18
19
  </div>
@@ -31,6 +32,7 @@
31
32
  contentDirection = 'row'
32
33
  } = defineProps<{
33
34
  readonly contentDirection?: 'column' | 'row';
35
+ readonly contentGap?: number;
34
36
  }>();
35
37
 
36
38
  defineSlots<{
@@ -13,8 +13,8 @@
13
13
  <script
14
14
  lang="ts"
15
15
  setup>
16
+ import { useMutationObserver } from '@basmilius/common';
16
17
  import { isHtmlElement } from '@basmilius/utils';
17
- import { useMutationObserver } from '@flux-ui/internals';
18
18
  import type { FluxDirection } from '@flux-ui/types';
19
19
  import { type ComponentPublicInstance, onMounted, onUnmounted, reactive, ref, unref, useTemplateRef, watchEffect } from 'vue';
20
20
 
@@ -12,9 +12,9 @@
12
12
  <script
13
13
  lang="ts"
14
14
  setup>
15
+ import { useLoaded } from '@basmilius/common';
15
16
  import type { FluxFilterItem, FluxFilterValue } from '@flux-ui/types';
16
17
  import { computed, ref, unref, watch } from 'vue';
17
- import { useLoaded } from '$flux/composable/private';
18
18
  import FluxMenuItem from '$flux/component/FluxMenuItem.vue';
19
19
 
20
20
  const emit = defineEmits<{
@@ -133,7 +133,8 @@
133
133
  <script
134
134
  lang="ts"
135
135
  setup>
136
- import { unrefTemplateElement, useClickOutside } from '@flux-ui/internals';
136
+ import { useClickOutside } from '@basmilius/common';
137
+ import { unrefTemplateElement } from '@flux-ui/internals';
137
138
  import type { FluxFormSelectOption, FluxFormSelectOptions } from '@flux-ui/types';
138
139
  import { clsx } from 'clsx';
139
140
  import { type ComponentPublicInstance, computed, nextTick, ref, toRef, unref, useTemplateRef, watch } from 'vue';
@@ -1,3 +1,2 @@
1
1
  export { default as useFormSelect } from './useFormSelect';
2
- export { default as useLoaded } from './useLoaded';
3
2
  export { default as useTranslate } from './useTranslate';
@@ -91,7 +91,7 @@
91
91
  .badgeGray {
92
92
  composes: badge;
93
93
 
94
- --color: var(--gray-700);
94
+ --color: var(--foreground);
95
95
 
96
96
  .badgeLabel {
97
97
  color: var(--foreground-prominent);
@@ -13,19 +13,6 @@
13
13
  display: inline-block;
14
14
  }
15
15
 
16
- .materialSymbolIcon {
17
- composes: icon;
18
-
19
- display: inline-flex;
20
- color: currentColor;
21
- font-family: 'Material Symbols Outlined', sans-serif;
22
- font-weight: 400;
23
- font-style: normal;
24
- user-select: none;
25
- -webkit-font-feature-settings: 'liga';
26
- -webkit-font-smoothing: antialiased;
27
- }
28
-
29
16
  .iconBoxed {
30
17
  composes: basePane from './base/Pane.module.scss';
31
18
 
@@ -40,10 +27,6 @@
40
27
  .icon {
41
28
  font-size: .45em;
42
29
  }
43
-
44
- .materialSymbolIcon {
45
- font-size: .5em;
46
- }
47
30
  }
48
31
 
49
32
  .iconBoxedDefault {
@@ -4,6 +4,7 @@
4
4
  display: flex;
5
5
  flex-flow: row nowrap;
6
6
  gap: 21px;
7
+ text-align: left;
7
8
  }
8
9
 
9
10
  .itemActions {
@@ -13,22 +14,25 @@
13
14
  .itemContent {
14
15
  display: flex;
15
16
  flex-flow: column;
17
+ flex-grow: 1;
16
18
  gap: 3px;
17
19
  }
18
20
 
19
21
  .itemMedia {
22
+ --size: 48px;
23
+
20
24
  flex-shrink: 0;
21
25
  }
22
26
 
23
27
  .itemMedia > img {
24
- height: 48px;
25
- width: 48px;
28
+ height: var(--size);
29
+ width: var(--size);
26
30
  object-fit: cover;
27
31
  object-position: center;
28
32
  }
29
33
 
30
34
  .itemMedia > .avatar {
31
- font-size: 48px;
35
+ font-size: var(--size);
32
36
  }
33
37
 
34
38
  .itemMedia > .icon {
@@ -36,7 +40,13 @@
36
40
  }
37
41
 
38
42
  .itemMedia > .iconBoxed {
39
- font-size: 48px;
43
+ font-size: var(--size);
44
+ }
45
+
46
+ .itemActions.isCenter,
47
+ .itemContent.isCenter,
48
+ .itemMedia.isCenter {
49
+ align-self: center;
40
50
  }
41
51
 
42
52
  .itemStack {
@@ -57,3 +67,7 @@
57
67
  .basePaneStructure > .itemStack > .item {
58
68
  margin: 0;
59
69
  }
70
+
71
+ .basePaneStructure:is(a, button):hover:has(> .item) {
72
+ background-color: color-mix(in srgb, rgb(from var(--surface-hover) r g b / .125), var(--surface));
73
+ }
@@ -18,13 +18,22 @@
18
18
  --surface: var(--background);
19
19
  }
20
20
 
21
+ :is(.paneDefault, .paneFlat, .paneWell):is(button) {
22
+ padding: 0;
23
+ }
21
24
 
22
25
  :is(.paneDefault, .paneFlat, .paneWell):is(a, button) {
23
- transition: box-shadow 210ms var(--swift-out);
26
+ cursor: pointer;
27
+ transition: 210ms var(--swift-out);
28
+ transition-property: background, box-shadow;
24
29
 
25
30
  &:hover {
26
31
  box-shadow: var(--shadow-lg);
27
32
  }
33
+
34
+ &:active {
35
+ box-shadow: var(--shadow-xs);
36
+ }
28
37
  }
29
38
 
30
39
  .paneHeader {
@@ -108,6 +108,13 @@
108
108
  border-color: var(--foreground);
109
109
  }
110
110
  }
111
+
112
+ &:disabled,
113
+ &[aria-disabled=true] {
114
+ box-shadow: none;
115
+ opacity: .5;
116
+ pointer-events: none;
117
+ }
111
118
  }
112
119
 
113
120
  .tabBarItemActive {
@@ -106,7 +106,7 @@ tfoot .tableCell {
106
106
  }
107
107
 
108
108
  .tableActions {
109
- margin: -3px 0 -3px -3px;
109
+ margin: -4px 0 -4px -3px;
110
110
  }
111
111
 
112
112
  .tableFill {
@@ -36,7 +36,7 @@
36
36
  color: var(--primary-600);
37
37
  text-decoration: underline;
38
38
  text-decoration-thickness: 1px;
39
- text-underline-offset: 4px;
39
+ text-underline-offset: 3px;
40
40
 
41
41
  &:hover {
42
42
  text-decoration: none;
@@ -47,6 +47,10 @@
47
47
  line-height: 1.6;
48
48
  }
49
49
 
50
+ small {
51
+ font-size: 14px;
52
+ }
53
+
50
54
  blockquote {
51
55
  position: relative;
52
56
  margin: 0 0 0 30px;
@@ -205,7 +205,7 @@
205
205
  --background: var(--gray-50);
206
206
  --foreground: var(--gray-700);
207
207
  --foreground-prominent: var(--gray-900);
208
- --foreground-secondary: var(--gray-400);
208
+ --foreground-secondary: var(--gray-500);
209
209
  --surface: var(--gray-25);
210
210
  --surface-loader: rgb(from var(--gray-25) r g b / .75);
211
211
  --surface-stroke: var(--gray-200);
@@ -11,8 +11,10 @@ type Props = {
11
11
  };
12
12
 
13
13
  const TARGET_SELECTOR = `.${$style.overlayProvider.replaceAll(' ', '.')}`;
14
+ let DIALOG_ID = 0;
14
15
 
15
16
  export default function (attrs: object, props: Props, emit: Emit, slots: Slots, className: string, transition: Component): RenderFunction {
17
+ const dialogId = `flux-dialog:${DIALOG_ID++}`;
16
18
  let unregister: Function | null = null;
17
19
  let zIndex = 0;
18
20
 
@@ -58,7 +60,7 @@ export default function (attrs: object, props: Props, emit: Emit, slots: Slots,
58
60
  }
59
61
 
60
62
  content = h('div', {
61
- key: props.viewKey,
63
+ key: props.viewKey ?? dialogId,
62
64
  ref: dialogRef,
63
65
  class: [className, zIndex === dialogCount && $style.isCurrent],
64
66
  style: {
@@ -1,5 +0,0 @@
1
- import { ComputedRef } from 'vue';
2
- export default function (): {
3
- isLoading: ComputedRef<boolean>;
4
- loaded: <T extends Function>(fn: T) => T;
5
- };
@@ -1,21 +0,0 @@
1
- import { computed, shallowRef, unref } from 'vue';
2
-
3
- export default function () {
4
- const tasks = shallowRef(0);
5
-
6
- const isLoading = computed(() => unref(tasks) > 0);
7
-
8
- function loaded<T extends Function>(fn: T): T {
9
- return (async (...args: any[]) => {
10
- tasks.value++;
11
-
12
- return await fn(...args)
13
- .finally(() => tasks.value--);
14
- }) as unknown as T;
15
- }
16
-
17
- return {
18
- isLoading,
19
- loaded
20
- };
21
- }