sprintify-ui 0.0.9 → 0.0.11
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 +14 -3
- package/dist/sprintify-ui.es.js +3851 -6637
- package/dist/style.css +2 -2
- package/dist/tailwindcss/index.js +283 -32
- package/dist/types/src/components/BaseAutocomplete.vue.d.ts +0 -1
- package/dist/types/src/components/BaseAutocompleteFetch.vue.d.ts +0 -1
- package/dist/types/src/components/BaseBelongsTo.vue.d.ts +13 -3
- package/dist/types/src/components/BaseDataTable.vue.d.ts +95 -64
- package/dist/types/src/components/BaseDialog.vue.d.ts +8 -8
- package/dist/types/src/components/BaseFilePicker.vue.d.ts +3 -3
- package/dist/types/src/components/BaseLoadingCover.vue.d.ts +12 -12
- package/dist/types/src/components/BaseMenuItem.vue.d.ts +4 -4
- package/dist/types/src/components/BaseNavbarItemContent.vue.d.ts +4 -4
- package/dist/types/src/components/BaseTableColumn.vue.d.ts +4 -4
- package/dist/types/src/svg/BaseEmptyState.vue.d.ts +2 -0
- package/dist/types/src/{components/BaseSpinner.vue.d.ts → svg/BaseSpinnerLarge.vue.d.ts} +0 -0
- package/dist/types/src/svg/BaseSpinnerSmall.vue.d.ts +44 -0
- package/dist/types/src/types/types.d.ts +1 -1
- package/package.json +4 -2
- package/src/assets/form.css +1 -10
- package/src/assets/main.css +0 -1
- package/src/assets/tailwind.css +3 -5
- package/src/components/BaseAutocomplete.stories.js +7 -4
- package/src/components/BaseAutocomplete.vue +44 -15
- package/src/components/BaseAutocompleteFetch.stories.js +6 -3
- package/src/components/BaseAutocompleteFetch.vue +8 -3
- package/src/components/BaseBelongsTo.stories.js +9 -4
- package/src/components/BaseBelongsTo.vue +1 -0
- package/src/components/BaseButton.stories.js +11 -3
- package/src/components/BaseCard.vue +1 -1
- package/src/components/BaseDataIterator.stories.js +102 -3
- package/src/components/BaseDataIterator.vue +44 -12
- package/src/components/BaseDataTable.stories.js +149 -2
- package/src/components/BaseDataTable.vue +34 -28
- package/src/components/BaseDataTableToggleColumns.vue +1 -1
- package/src/components/BaseDateSelect.vue +6 -2
- package/src/components/BaseDescriptionListItem.vue +40 -4
- package/src/components/BaseDialog.stories.js +51 -0
- package/src/components/BaseDialog.vue +13 -7
- package/src/components/BaseFilePicker.stories.js +51 -0
- package/src/components/BaseFilePicker.vue +6 -6
- package/src/components/BaseFileUploader.stories.js +80 -0
- package/src/components/BaseFileUploader.vue +12 -3
- package/src/components/BaseLoadingCover.vue +8 -16
- package/src/components/BaseTable.vue +42 -29
- package/src/components/BaseTableColumn.vue +2 -2
- package/src/svg/BaseEmptyState.vue +34 -0
- package/src/{components/BaseSpinner.vue → svg/BaseSpinnerLarge.vue} +0 -0
- package/src/svg/BaseSpinnerSmall.vue +9 -0
- package/src/types/types.ts +1 -1
- package/src/assets/button.css +0 -80
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sprintify-ui",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.11",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"build": "rimraf dist && vue-tsc && vite build",
|
|
6
6
|
"docs:dev": "vitepress dev docs",
|
|
@@ -23,7 +23,8 @@
|
|
|
23
23
|
"tailwindcss": "^3.0.0",
|
|
24
24
|
"vue": "^3.0.0",
|
|
25
25
|
"vue-i18n": "^9.0.0",
|
|
26
|
-
"vue-router": "^4.0.0"
|
|
26
|
+
"vue-router": "^4.0.0",
|
|
27
|
+
"pikaday": "^1.8.2"
|
|
27
28
|
},
|
|
28
29
|
"dependencies": {
|
|
29
30
|
"@headlessui/vue": "^1.7.4"
|
|
@@ -72,6 +73,7 @@
|
|
|
72
73
|
"prettier-plugin-tailwindcss": "^0.1.13",
|
|
73
74
|
"qs": "^6.11.0",
|
|
74
75
|
"rimraf": "^3.0.2",
|
|
76
|
+
"rollup-plugin-analyzer": "^4.0.0",
|
|
75
77
|
"scroll-lock": "^2.1.5",
|
|
76
78
|
"tailwindcss": "^3.2.4",
|
|
77
79
|
"typescript": "^4.4.4",
|
package/src/assets/form.css
CHANGED
package/src/assets/main.css
CHANGED
package/src/assets/tailwind.css
CHANGED
|
@@ -87,16 +87,19 @@ export const SlotFooter = (args) => {
|
|
|
87
87
|
components: { BaseAutocomplete },
|
|
88
88
|
setup() {
|
|
89
89
|
const value = ref(null);
|
|
90
|
-
|
|
90
|
+
function onClick() {
|
|
91
|
+
alert(1);
|
|
92
|
+
}
|
|
93
|
+
return { args, value, onClick };
|
|
91
94
|
},
|
|
92
95
|
template: `
|
|
93
96
|
<BaseAutocomplete
|
|
94
97
|
v-model="value"
|
|
95
|
-
v-bind="args"
|
|
98
|
+
v-bind="args"
|
|
96
99
|
>
|
|
97
100
|
<template #footer>
|
|
98
|
-
<div class="text-center p-
|
|
99
|
-
<button class="btn btn-sm w-full btn-slate-200-outline">This is the footer 💯</button>
|
|
101
|
+
<div class="text-center p-2 border-t">
|
|
102
|
+
<button @click=onClick class="btn btn-sm w-full btn-slate-200-outline">This is the footer 💯</button>
|
|
100
103
|
</div>
|
|
101
104
|
</template>
|
|
102
105
|
</BaseAutocomplete>
|
|
@@ -50,7 +50,10 @@
|
|
|
50
50
|
v-show="showDropdown"
|
|
51
51
|
class="absolute top-1 z-[1] min-h-[110px] w-full overflow-hidden rounded border border-slate-300 bg-white shadow-md"
|
|
52
52
|
>
|
|
53
|
-
<div
|
|
53
|
+
<div
|
|
54
|
+
ref="dropdown"
|
|
55
|
+
class="max-h-[214px] min-h-[75px] w-full overflow-y-auto"
|
|
56
|
+
>
|
|
54
57
|
<slot v-if="filteredNormalizedOptions.length == 0" name="empty">
|
|
55
58
|
<div
|
|
56
59
|
class="flex items-center justify-center px-5 py-10 text-center text-slate-600"
|
|
@@ -59,7 +62,7 @@
|
|
|
59
62
|
</div>
|
|
60
63
|
</slot>
|
|
61
64
|
|
|
62
|
-
<ul v-else>
|
|
65
|
+
<ul v-else class="p-1">
|
|
63
66
|
<li
|
|
64
67
|
v-for="option in filteredNormalizedOptions"
|
|
65
68
|
:key="option.value"
|
|
@@ -70,7 +73,7 @@
|
|
|
70
73
|
type="button"
|
|
71
74
|
tabindex="-1"
|
|
72
75
|
@click="onSelect(option)"
|
|
73
|
-
@mousedown="dontLooseFocus"
|
|
76
|
+
@mousedown.prevent="dontLooseFocus"
|
|
74
77
|
>
|
|
75
78
|
<slot
|
|
76
79
|
name="option"
|
|
@@ -91,12 +94,12 @@
|
|
|
91
94
|
</button>
|
|
92
95
|
</li>
|
|
93
96
|
</ul>
|
|
97
|
+
</div>
|
|
94
98
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
:
|
|
98
|
-
|
|
99
|
-
/>
|
|
99
|
+
<div ref="footer">
|
|
100
|
+
<div v-if="$slots.footer" class="bg-white">
|
|
101
|
+
<slot :options="filteredNormalizedOptions" name="footer" />
|
|
102
|
+
</div>
|
|
100
103
|
</div>
|
|
101
104
|
|
|
102
105
|
<Transition>
|
|
@@ -131,7 +134,7 @@ import {
|
|
|
131
134
|
Selection,
|
|
132
135
|
NormalizedSelection,
|
|
133
136
|
} from '@/types/types';
|
|
134
|
-
import { useInfiniteScroll } from '@vueuse/core';
|
|
137
|
+
import { useInfiniteScroll, useMutationObserver } from '@vueuse/core';
|
|
135
138
|
import BaseSkeleton from './BaseSkeleton.vue';
|
|
136
139
|
|
|
137
140
|
const props = defineProps({
|
|
@@ -198,7 +201,7 @@ onMounted(() => {
|
|
|
198
201
|
() => {
|
|
199
202
|
emit('scrollBottom');
|
|
200
203
|
},
|
|
201
|
-
{ distance:
|
|
204
|
+
{ distance: 60 }
|
|
202
205
|
);
|
|
203
206
|
});
|
|
204
207
|
|
|
@@ -255,12 +258,18 @@ const filteredNormalizedOptions = computed((): NormalizedOption[] => {
|
|
|
255
258
|
});
|
|
256
259
|
});
|
|
257
260
|
|
|
258
|
-
|
|
259
|
-
|
|
261
|
+
function preventUnfocus(elements: HTMLElement[]) {
|
|
262
|
+
elements.forEach((e) => {
|
|
263
|
+
e.removeEventListener('mousedown', dontLooseFocus);
|
|
264
|
+
});
|
|
265
|
+
elements.forEach((e) => {
|
|
266
|
+
e.addEventListener('mousedown', dontLooseFocus);
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const dontLooseFocus = (event: Event) => {
|
|
260
271
|
inputElement.value?.focus();
|
|
261
|
-
|
|
262
|
-
next();
|
|
263
|
-
}
|
|
272
|
+
event.preventDefault();
|
|
264
273
|
};
|
|
265
274
|
|
|
266
275
|
const onTextFocus = () => {
|
|
@@ -373,4 +382,24 @@ const setKeywords = (input: string) => {
|
|
|
373
382
|
keywords.value = input;
|
|
374
383
|
emit('typing', input);
|
|
375
384
|
};
|
|
385
|
+
|
|
386
|
+
const footer = ref(null) as Ref<HTMLDivElement | null>;
|
|
387
|
+
|
|
388
|
+
function preventUnfocusOnFooter() {
|
|
389
|
+
const elements = (footer.value?.querySelectorAll('button, a') ??
|
|
390
|
+
[]) as HTMLElement[];
|
|
391
|
+
preventUnfocus(elements);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
onMounted(() => {
|
|
395
|
+
preventUnfocusOnFooter();
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
useMutationObserver(
|
|
399
|
+
footer,
|
|
400
|
+
() => {
|
|
401
|
+
preventUnfocusOnFooter();
|
|
402
|
+
},
|
|
403
|
+
{ attributes: false, childList: true }
|
|
404
|
+
);
|
|
376
405
|
</script>
|
|
@@ -79,7 +79,10 @@ export const SlotFooter = (args) => {
|
|
|
79
79
|
components: { BaseAutocompleteFetch },
|
|
80
80
|
setup() {
|
|
81
81
|
const value = ref(null);
|
|
82
|
-
|
|
82
|
+
function onClick() {
|
|
83
|
+
alert(1);
|
|
84
|
+
}
|
|
85
|
+
return { args, value, onClick };
|
|
83
86
|
},
|
|
84
87
|
template: `
|
|
85
88
|
<BaseAutocompleteFetch
|
|
@@ -87,8 +90,8 @@ export const SlotFooter = (args) => {
|
|
|
87
90
|
v-bind="args"
|
|
88
91
|
>
|
|
89
92
|
<template #footer>
|
|
90
|
-
<div class="text-center p-
|
|
91
|
-
<button class="btn btn-sm w-full btn-slate-200-outline">This is the footer 💯</button>
|
|
93
|
+
<div class="text-center p-2 border-t">
|
|
94
|
+
<button @click=onClick class="btn btn-sm w-full btn-slate-200-outline">This is the footer 💯</button>
|
|
92
95
|
</div>
|
|
93
96
|
</template>
|
|
94
97
|
</BaseAutocompleteFetch>
|
|
@@ -22,12 +22,17 @@
|
|
|
22
22
|
|
|
23
23
|
<template #footer="footerProps">
|
|
24
24
|
<slot name="footer" v-bind="footerProps" :keywords="keywords">
|
|
25
|
-
<div
|
|
25
|
+
<div
|
|
26
|
+
v-if="createNewUrl"
|
|
27
|
+
class="p-2"
|
|
28
|
+
:class="{
|
|
29
|
+
'border-t border-slate-300': footerProps.options.length > 0,
|
|
30
|
+
}"
|
|
31
|
+
>
|
|
26
32
|
<router-link
|
|
27
33
|
:to="createNewUrl"
|
|
28
34
|
target="_blank"
|
|
29
|
-
class="btn flex items-center justify-center
|
|
30
|
-
@mousedown="footerProps.dontLooseFocus"
|
|
35
|
+
class="btn btn-slate-200-outline flex items-center justify-center"
|
|
31
36
|
>
|
|
32
37
|
<BaseIcon
|
|
33
38
|
icon="heroicons-solid:plus"
|
|
@@ -40,10 +40,12 @@ export const Disabled = (args) => {
|
|
|
40
40
|
return {
|
|
41
41
|
components: { BaseBelongsTo },
|
|
42
42
|
setup() {
|
|
43
|
-
|
|
43
|
+
const value = ref(null);
|
|
44
|
+
return { args, value };
|
|
44
45
|
},
|
|
45
46
|
template: `<BaseBelongsTo
|
|
46
47
|
v-bind="args"
|
|
48
|
+
v-model="value"
|
|
47
49
|
:current-model="{title: 'Dark Vader', id: 1}"
|
|
48
50
|
:disabled="true"
|
|
49
51
|
></BaseBelongsTo>`,
|
|
@@ -88,7 +90,10 @@ export const SlotFooter = (args) => {
|
|
|
88
90
|
components: { BaseBelongsTo },
|
|
89
91
|
setup() {
|
|
90
92
|
const value = ref(null);
|
|
91
|
-
|
|
93
|
+
function onClick() {
|
|
94
|
+
alert(1);
|
|
95
|
+
}
|
|
96
|
+
return { args, value, onClick };
|
|
92
97
|
},
|
|
93
98
|
template: `
|
|
94
99
|
<BaseBelongsTo
|
|
@@ -96,8 +101,8 @@ export const SlotFooter = (args) => {
|
|
|
96
101
|
v-bind="args"
|
|
97
102
|
>
|
|
98
103
|
<template #footer>
|
|
99
|
-
<div class="text-center p-
|
|
100
|
-
<button class="btn btn-sm w-full btn-slate-200-outline">This is the footer 💯</button>
|
|
104
|
+
<div class="text-center p-2 border-t">
|
|
105
|
+
<button @click=onClick class="btn btn-sm w-full btn-slate-200-outline">This is the footer 💯</button>
|
|
101
106
|
</div>
|
|
102
107
|
</template>
|
|
103
108
|
</BaseBelongsTo>
|
|
@@ -29,6 +29,7 @@ import { PropType } from 'vue';
|
|
|
29
29
|
import { RouteLocationRaw } from 'vue-router';
|
|
30
30
|
import { AxiosResponse } from 'axios';
|
|
31
31
|
import { config } from '@/index';
|
|
32
|
+
import BaseAutocompleteFetch from './BaseAutocompleteFetch.vue';
|
|
32
33
|
|
|
33
34
|
const props = defineProps({
|
|
34
35
|
modelValue: {
|
|
@@ -5,8 +5,11 @@ const colors = [
|
|
|
5
5
|
'btn-secondary',
|
|
6
6
|
'btn-secondary-outline',
|
|
7
7
|
'btn-success',
|
|
8
|
+
'btn-success-outline',
|
|
8
9
|
'btn-warning',
|
|
10
|
+
'btn-warning-outline',
|
|
9
11
|
'btn-danger',
|
|
12
|
+
'btn-danger-outline',
|
|
10
13
|
'btn-white',
|
|
11
14
|
'btn-slate-100',
|
|
12
15
|
'btn-slate-100-outline',
|
|
@@ -41,7 +44,7 @@ const Template = (args) => ({
|
|
|
41
44
|
|
|
42
45
|
export const Demo = Template.bind({});
|
|
43
46
|
Demo.args = {
|
|
44
|
-
class: '
|
|
47
|
+
class: '',
|
|
45
48
|
};
|
|
46
49
|
|
|
47
50
|
export const Loading = Template.bind({});
|
|
@@ -55,8 +58,13 @@ export const Colors = (args) => ({
|
|
|
55
58
|
return { args, colors };
|
|
56
59
|
},
|
|
57
60
|
template: `
|
|
61
|
+
<p class="text-xs text-slate-600 leading-tight mb-1">btn</p>
|
|
62
|
+
<BaseButton class="btn">
|
|
63
|
+
Click here
|
|
64
|
+
</BaseButton>
|
|
65
|
+
<div class="mb-4"></div>
|
|
58
66
|
<div v-for="color in colors" :key="color" class="mb-4">
|
|
59
|
-
<p class="text-xs text-slate-600 leading-tight mb-1">{{ color }}</p>
|
|
67
|
+
<p class="text-xs text-slate-600 leading-tight mb-1">btn {{ color }}</p>
|
|
60
68
|
<BaseButton :class="color">
|
|
61
69
|
Click here
|
|
62
70
|
</BaseButton>
|
|
@@ -71,7 +79,7 @@ export const Sizes = (args) => ({
|
|
|
71
79
|
},
|
|
72
80
|
template: `
|
|
73
81
|
<div v-for="size in sizes" :key="size" class="mb-4">
|
|
74
|
-
<p class="text-xs text-slate-600 leading-tight mb-1">{{ size }}</p>
|
|
82
|
+
<p class="text-xs text-slate-600 leading-tight mb-1">btn {{ size }}</p>
|
|
75
83
|
<BaseButton class="btn-primary" :class="size">
|
|
76
84
|
Click here
|
|
77
85
|
</BaseButton>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import BaseDataIterator from './BaseDataIterator.vue';
|
|
2
|
+
import BaseSelect from './BaseSelect.vue';
|
|
2
3
|
import BaseCard from './BaseCard.vue';
|
|
3
4
|
import BaseCardRow from './BaseCardRow.vue';
|
|
4
5
|
import BaseLoadingCover from './BaseLoadingCover.vue';
|
|
@@ -43,16 +44,67 @@ const template = `
|
|
|
43
44
|
|
|
44
45
|
<BaseLoadingCover
|
|
45
46
|
v-show="loading"
|
|
46
|
-
|
|
47
|
+
size="lg"
|
|
48
|
+
backdropClass="bg-white bg-opacity-50"
|
|
47
49
|
>
|
|
48
50
|
</BaseLoadingCover>
|
|
49
51
|
</div>
|
|
50
52
|
</template>
|
|
53
|
+
|
|
54
|
+
<template #filters="{ query, updateQueryValue }">
|
|
55
|
+
<div class="space-y-3">
|
|
56
|
+
<div>
|
|
57
|
+
<p class="mb-1 text-sm">
|
|
58
|
+
Type
|
|
59
|
+
</p>
|
|
60
|
+
<BaseSelect
|
|
61
|
+
:model-value="query.type ?? null"
|
|
62
|
+
class="w-full rounded border-slate-300"
|
|
63
|
+
@update:model-value="updateQueryValue('type', $event)"
|
|
64
|
+
>
|
|
65
|
+
<option value="">-</option>
|
|
66
|
+
<option value="video">
|
|
67
|
+
Video
|
|
68
|
+
</option>
|
|
69
|
+
<option value="article">
|
|
70
|
+
Article
|
|
71
|
+
</option>
|
|
72
|
+
</BaseSelect>
|
|
73
|
+
</div>
|
|
74
|
+
<div>
|
|
75
|
+
<p class="mb-1 text-sm">
|
|
76
|
+
Access Level
|
|
77
|
+
</p>
|
|
78
|
+
<BaseSelect
|
|
79
|
+
:model-value="query.access_level ?? null"
|
|
80
|
+
class="w-full rounded border-slate-300"
|
|
81
|
+
@update:model-value="updateQueryValue('access_level', $event)"
|
|
82
|
+
>
|
|
83
|
+
<option value="">-</option>
|
|
84
|
+
<option value="public">
|
|
85
|
+
Public
|
|
86
|
+
</option>
|
|
87
|
+
<option value="member">
|
|
88
|
+
Member
|
|
89
|
+
</option>
|
|
90
|
+
<option value="vip">
|
|
91
|
+
VIP
|
|
92
|
+
</option>
|
|
93
|
+
</BaseSelect>
|
|
94
|
+
</div>
|
|
95
|
+
</div>
|
|
96
|
+
</template>
|
|
51
97
|
</BaseDataIterator>
|
|
52
98
|
`;
|
|
53
99
|
|
|
54
100
|
const Template = (args) => ({
|
|
55
|
-
components: {
|
|
101
|
+
components: {
|
|
102
|
+
BaseDataIterator,
|
|
103
|
+
BaseCard,
|
|
104
|
+
BaseCardRow,
|
|
105
|
+
BaseLoadingCover,
|
|
106
|
+
BaseSelect,
|
|
107
|
+
},
|
|
56
108
|
setup() {
|
|
57
109
|
return { args };
|
|
58
110
|
},
|
|
@@ -83,7 +135,54 @@ Demo.args = {
|
|
|
83
135
|
],
|
|
84
136
|
};
|
|
85
137
|
|
|
86
|
-
|
|
138
|
+
const SimpleTemplate = (args) => ({
|
|
139
|
+
components: {
|
|
140
|
+
BaseDataIterator,
|
|
141
|
+
BaseCard,
|
|
142
|
+
BaseCardRow,
|
|
143
|
+
BaseLoadingCover,
|
|
144
|
+
},
|
|
145
|
+
setup() {
|
|
146
|
+
return { args };
|
|
147
|
+
},
|
|
148
|
+
template: `
|
|
149
|
+
<BaseDataIterator v-bind="args">
|
|
150
|
+
<template #default="{ items, loading }">
|
|
151
|
+
<div class="relative">
|
|
152
|
+
<div class="space-y-1.5">
|
|
153
|
+
<a
|
|
154
|
+
v-for="item in items"
|
|
155
|
+
:key="item.id"
|
|
156
|
+
:href="item.website_url"
|
|
157
|
+
target="_blank"
|
|
158
|
+
class="block group"
|
|
159
|
+
>
|
|
160
|
+
<BaseCard class="group-hover:bg-slate-100">
|
|
161
|
+
<BaseCardRow size="sm">
|
|
162
|
+
<div class="font-medium text-slate-900">
|
|
163
|
+
{{ item.title }}
|
|
164
|
+
</div>
|
|
165
|
+
<p class="text-xs leading-tight text-slate-500">
|
|
166
|
+
{{ item.subtitle }}
|
|
167
|
+
</p>
|
|
168
|
+
</BaseCardRow>
|
|
169
|
+
</BaseCard>
|
|
170
|
+
</a>
|
|
171
|
+
</div>
|
|
172
|
+
|
|
173
|
+
<BaseLoadingCover
|
|
174
|
+
v-show="loading"
|
|
175
|
+
size="lg"
|
|
176
|
+
backdropClass="bg-white bg-opacity-50"
|
|
177
|
+
>
|
|
178
|
+
</BaseLoadingCover>
|
|
179
|
+
</div>
|
|
180
|
+
</template>
|
|
181
|
+
</BaseDataIterator>
|
|
182
|
+
`,
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
export const Simple = SimpleTemplate.bind({});
|
|
87
186
|
Simple.args = {
|
|
88
187
|
searchable: false,
|
|
89
188
|
actions: [],
|
|
@@ -6,7 +6,10 @@
|
|
|
6
6
|
'grid-cols-[1fr_300px]': hasSidebar,
|
|
7
7
|
}"
|
|
8
8
|
>
|
|
9
|
-
<div
|
|
9
|
+
<div
|
|
10
|
+
class="min-w-0"
|
|
11
|
+
:class="{ 'col-span-1': !mobileLayout, 'col-span-2': mobileLayout }"
|
|
12
|
+
>
|
|
10
13
|
<!-- Header -->
|
|
11
14
|
<div class="mb-4 flex space-x-2 empty:mb-0">
|
|
12
15
|
<!-- Search bar -->
|
|
@@ -52,7 +55,7 @@
|
|
|
52
55
|
<!-- Filters (mobile) -->
|
|
53
56
|
<button
|
|
54
57
|
v-if="mobileLayout && hasFilters"
|
|
55
|
-
class="btn flex h-11 items-center justify-center py-1 text-base"
|
|
58
|
+
class="btn flex h-11 items-center justify-center py-1 text-base shadow-sm"
|
|
56
59
|
type="button"
|
|
57
60
|
@click="showFilters = true"
|
|
58
61
|
>
|
|
@@ -131,7 +134,7 @@
|
|
|
131
134
|
</div>
|
|
132
135
|
</div>
|
|
133
136
|
|
|
134
|
-
<div v-if="!mobileLayout
|
|
137
|
+
<div v-if="!mobileLayout" ref="sidebar">
|
|
135
138
|
<slot
|
|
136
139
|
name="sidebarTop"
|
|
137
140
|
:pagination-metadata="paginationMetadata"
|
|
@@ -201,7 +204,6 @@ const DEFAULT_QUERY = {
|
|
|
201
204
|
import { cloneDeep, debounce, isArray, merge, set } from 'lodash';
|
|
202
205
|
import hash from 'object-hash';
|
|
203
206
|
import { ComputedRef, PropType, Ref } from 'vue';
|
|
204
|
-
import { useBreakpoints } from '@/composables/breakpoints';
|
|
205
207
|
import {
|
|
206
208
|
Collection,
|
|
207
209
|
DataTableQuery,
|
|
@@ -217,6 +219,7 @@ import BaseCardRow from './BaseCardRow.vue';
|
|
|
217
219
|
import BasePaginationSimple from './BasePaginationSimple.vue';
|
|
218
220
|
import BaseModalSide from './BaseModalSide.vue';
|
|
219
221
|
import { config } from '@/index';
|
|
222
|
+
import { useMutationObserver, useResizeObserver } from '@vueuse/core';
|
|
220
223
|
|
|
221
224
|
const props = defineProps({
|
|
222
225
|
/**
|
|
@@ -299,7 +302,10 @@ const route = useRoute();
|
|
|
299
302
|
const router = useRouter();
|
|
300
303
|
const routeName = route.name;
|
|
301
304
|
|
|
302
|
-
const
|
|
305
|
+
const width = ref(800);
|
|
306
|
+
useResizeObserver(dataIteratorNode, () => {
|
|
307
|
+
width.value = dataIteratorNode.value?.clientWidth ?? 800;
|
|
308
|
+
});
|
|
303
309
|
|
|
304
310
|
/** Data table state */
|
|
305
311
|
|
|
@@ -315,7 +321,7 @@ const query = ref(cloneDeep(props.defaultQuery)) as Ref<DataTableQuery>;
|
|
|
315
321
|
const slots = useSlots();
|
|
316
322
|
|
|
317
323
|
const mobileLayout = computed(() => {
|
|
318
|
-
return
|
|
324
|
+
return width.value < 1024;
|
|
319
325
|
});
|
|
320
326
|
|
|
321
327
|
const hasFilters = computed((): boolean => {
|
|
@@ -323,14 +329,40 @@ const hasFilters = computed((): boolean => {
|
|
|
323
329
|
return numberOfFilterSlots !== undefined;
|
|
324
330
|
});
|
|
325
331
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
+
/*
|
|
333
|
+
|--------------------------------------------------------------------------
|
|
334
|
+
| Has sidebar observer
|
|
335
|
+
|--------------------------------------------------------------------------
|
|
336
|
+
*/
|
|
337
|
+
|
|
338
|
+
const hasSidebar = ref(false);
|
|
339
|
+
const sidebar = ref(null) as Ref<null | HTMLElement>;
|
|
340
|
+
|
|
341
|
+
function checkIfSidebarIsEmpty() {
|
|
342
|
+
hasSidebar.value = (sidebar?.value?.childElementCount ?? 0) > 0;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
const checkIfSidebarIsEmptyDebounce = debounce(checkIfSidebarIsEmpty, 100);
|
|
346
|
+
|
|
347
|
+
useMutationObserver(sidebar, checkIfSidebarIsEmptyDebounce, {
|
|
348
|
+
attributes: false,
|
|
349
|
+
childList: true,
|
|
332
350
|
});
|
|
333
351
|
|
|
352
|
+
onMounted(() => {
|
|
353
|
+
checkIfSidebarIsEmpty();
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
watch(
|
|
357
|
+
() => mobileLayout.value,
|
|
358
|
+
() => {
|
|
359
|
+
// After the sidebar appears...
|
|
360
|
+
nextTick(() => {
|
|
361
|
+
checkIfSidebarIsEmpty();
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
);
|
|
365
|
+
|
|
334
366
|
/*
|
|
335
367
|
|--------------------------------------------------------------------------
|
|
336
368
|
| Query params
|