sprintify-ui 0.0.19 → 0.0.21
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/dist/sprintify-ui.es.js +6877 -4264
- package/dist/types/src/components/BaseActionItem.vue.d.ts +121 -0
- package/dist/types/src/components/BaseAlert.vue.d.ts +3 -3
- package/dist/types/src/components/BaseAppDialogs.vue.d.ts +1 -1
- package/dist/types/src/components/BaseBoolean.vue.d.ts +2 -2
- package/dist/types/src/components/BaseCounter.vue.d.ts +3 -3
- package/dist/types/src/components/BaseDialog.vue.d.ts +4 -4
- package/dist/types/src/components/BaseInput.vue.d.ts +1 -1
- package/dist/types/src/components/BaseLayoutNotificationDropdown.vue.d.ts +24 -0
- package/dist/types/src/components/BaseLayoutNotificationItem.vue.d.ts +14 -0
- package/dist/types/src/components/BaseLayoutSidebar.vue.d.ts +114 -0
- package/dist/types/src/components/BaseLayoutSidebarConfigurable.vue.d.ts +182 -0
- package/dist/types/src/components/BaseLayoutStacked.vue.d.ts +76 -0
- package/dist/types/src/components/BaseLayoutStackedConfigurable.vue.d.ts +182 -0
- package/dist/types/src/components/BaseMenu.vue.d.ts +17 -15
- package/dist/types/src/components/BaseMenuItem.vue.d.ts +5 -5
- package/dist/types/src/components/BaseNavbar.vue.d.ts +20 -1
- package/dist/types/src/components/BaseNavbarItem.vue.d.ts +17 -71
- package/dist/types/src/components/BaseNavbarItemContent.vue.d.ts +11 -2
- package/dist/types/src/components/BaseNavbarSideItem.vue.d.ts +26 -0
- package/dist/types/src/components/BaseNavbarSideItemContent.vue.d.ts +50 -0
- package/dist/types/src/components/BaseSwitch.vue.d.ts +3 -3
- package/dist/types/src/components/BaseSystemAlert.vue.d.ts +8 -8
- package/dist/types/src/components/BaseTableColumn.vue.d.ts +1 -1
- package/dist/types/src/components/index.d.ts +6 -1
- package/dist/types/src/index.d.ts +12 -0
- package/dist/types/src/types/Notification.d.ts +8 -0
- package/dist/types/src/types/types.d.ts +9 -0
- package/package.json +6 -3
- package/src/components/BaseActionItem.vue +68 -0
- package/src/components/{HasMany.stories.js → BaseHasMany.stories.js} +0 -0
- package/src/components/BaseLayoutNotificationDropdown.vue +101 -0
- package/src/components/BaseLayoutNotificationItem.vue +58 -0
- package/src/components/BaseLayoutSidebar.vue +199 -0
- package/src/components/BaseLayoutSidebarConfigurable.stories.js +129 -0
- package/src/components/BaseLayoutSidebarConfigurable.vue +118 -0
- package/src/components/BaseLayoutStacked.vue +52 -0
- package/src/components/BaseLayoutStackedConfigurable.stories.js +110 -0
- package/src/components/BaseLayoutStackedConfigurable.vue +129 -0
- package/src/components/BaseMediaItem.vue +7 -7
- package/src/components/BaseMediaLibrary.vue +3 -3
- package/src/components/BaseMediaPreview.vue +1 -1
- package/src/components/BaseMenu.vue +12 -4
- package/src/components/BaseNavbar.stories.js +18 -14
- package/src/components/BaseNavbar.vue +19 -3
- package/src/components/BaseNavbarItem.vue +17 -43
- package/src/components/BaseNavbarItemContent.vue +36 -16
- package/src/components/BaseNavbarSideItem.vue +42 -0
- package/src/components/BaseNavbarSideItemContent.vue +77 -0
- package/src/components/index.ts +11 -0
- package/src/lang/en.json +3 -0
- package/src/lang/fr.json +3 -0
- package/src/types/Notification.ts +10 -0
- package/src/types/types.ts +11 -0
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import BaseActionItem from './BaseActionItem.vue';
|
|
1
2
|
import BaseAlert from './BaseAlert.vue';
|
|
2
3
|
import BaseApp from './BaseApp.vue';
|
|
3
4
|
import BaseAppDialogs from './BaseAppDialogs.vue';
|
|
@@ -59,4 +60,8 @@ import BaseTable from './BaseTable.vue';
|
|
|
59
60
|
import BaseTableColumn from './BaseTableColumn.vue';
|
|
60
61
|
import BaseTextarea from './BaseTextarea.vue';
|
|
61
62
|
import BaseTextareaAutoresize from './BaseTextareaAutoresize.vue';
|
|
62
|
-
|
|
63
|
+
import BaseLayoutStacked from './BaseLayoutStacked.vue';
|
|
64
|
+
import BaseLayoutStackedConfigurable from './BaseLayoutStackedConfigurable.vue';
|
|
65
|
+
import BaseLayoutSidebar from './BaseLayoutSidebar.vue';
|
|
66
|
+
import BaseLayoutSidebarConfigurable from './BaseLayoutSidebarConfigurable.vue';
|
|
67
|
+
export { BaseActionItem, BaseAlert, BaseApp, BaseAppDialogs, BaseAppNotifications, BaseAutocomplete, BaseAutocompleteFetch, BaseAvatar, BaseBadge, BaseBelongsTo, BaseBoolean, BaseBreadcrumbs, BaseButton, BaseCard, BaseCardRow, BaseCharacterCounter, BaseClipboard, BaseContainer, BaseCounter, BaseDataIterator, BaseDataTable, BaseDatePicker, BaseDateSelect, BaseDescriptionList, BaseDescriptionListItem, BaseDialog, BaseFilePicker, BaseFileUploader, BaseHasMany, BaseIcon, BaseInput, BaseInputLabel, BaseLoadingCover, BaseMediaItem, BaseMediaLibrary, BaseMediaPreview, BaseMenu, BaseMenuItem, BaseModalCenter, BaseModalSide, BaseNavbar, BaseNavbarItem, BaseNavbarItemContent, BasePagination, BasePanel, BasePassword, BaseProcessRing, BaseReadMore, BaseSelect, BaseSideNavigation, BaseSideNavigationItem, BaseSkeleton, BaseSwitch, BaseSystemAlert, BaseTabs, BaseTabItem, BaseTagAutocomplete, BaseTagAutocompleteFetch, BaseTable, BaseTableColumn, BaseTextarea, BaseTextareaAutoresize, BaseLayoutStacked, BaseLayoutStackedConfigurable, BaseLayoutSidebar, BaseLayoutSidebarConfigurable, };
|
|
@@ -32,6 +32,7 @@ declare const messages: {
|
|
|
32
32
|
next_month: string;
|
|
33
33
|
none: string;
|
|
34
34
|
nothing_found: string;
|
|
35
|
+
notifications_empty: string;
|
|
35
36
|
or: string;
|
|
36
37
|
pagination_detail: string;
|
|
37
38
|
previous: string;
|
|
@@ -40,6 +41,7 @@ declare const messages: {
|
|
|
40
41
|
remove: string;
|
|
41
42
|
remove_file: string;
|
|
42
43
|
remove_file_description: string;
|
|
44
|
+
see_all_notifications: string;
|
|
43
45
|
select_an_item: string;
|
|
44
46
|
select_an_option: string;
|
|
45
47
|
success: string;
|
|
@@ -56,6 +58,7 @@ declare const messages: {
|
|
|
56
58
|
up_to_x: string;
|
|
57
59
|
upload_failed: string;
|
|
58
60
|
whoops: string;
|
|
61
|
+
x_ago: string;
|
|
59
62
|
x_rows_selected: string;
|
|
60
63
|
year: string;
|
|
61
64
|
yes_delete: string;
|
|
@@ -90,6 +93,7 @@ declare const messages: {
|
|
|
90
93
|
next_month: string;
|
|
91
94
|
none: string;
|
|
92
95
|
nothing_found: string;
|
|
96
|
+
notifications_empty: string;
|
|
93
97
|
or: string;
|
|
94
98
|
pagination_detail: string;
|
|
95
99
|
previous: string;
|
|
@@ -98,6 +102,7 @@ declare const messages: {
|
|
|
98
102
|
remove: string;
|
|
99
103
|
remove_file: string;
|
|
100
104
|
remove_file_description: string;
|
|
105
|
+
see_all_notifications: string;
|
|
101
106
|
select_an_item: string;
|
|
102
107
|
select_an_option: string;
|
|
103
108
|
success: string;
|
|
@@ -114,6 +119,7 @@ declare const messages: {
|
|
|
114
119
|
up_to_x: string;
|
|
115
120
|
upload_failed: string;
|
|
116
121
|
whoops: string;
|
|
122
|
+
x_ago: string;
|
|
117
123
|
x_rows_selected: string;
|
|
118
124
|
year: string;
|
|
119
125
|
yes_delete: string;
|
|
@@ -159,6 +165,7 @@ declare const config: {
|
|
|
159
165
|
next_month: string;
|
|
160
166
|
none: string;
|
|
161
167
|
nothing_found: string;
|
|
168
|
+
notifications_empty: string;
|
|
162
169
|
or: string;
|
|
163
170
|
pagination_detail: string;
|
|
164
171
|
previous: string;
|
|
@@ -167,6 +174,7 @@ declare const config: {
|
|
|
167
174
|
remove: string;
|
|
168
175
|
remove_file: string;
|
|
169
176
|
remove_file_description: string;
|
|
177
|
+
see_all_notifications: string;
|
|
170
178
|
select_an_item: string;
|
|
171
179
|
select_an_option: string;
|
|
172
180
|
success: string;
|
|
@@ -183,6 +191,7 @@ declare const config: {
|
|
|
183
191
|
up_to_x: string;
|
|
184
192
|
upload_failed: string;
|
|
185
193
|
whoops: string;
|
|
194
|
+
x_ago: string;
|
|
186
195
|
x_rows_selected: string;
|
|
187
196
|
year: string;
|
|
188
197
|
yes_delete: string;
|
|
@@ -217,6 +226,7 @@ declare const config: {
|
|
|
217
226
|
next_month: string;
|
|
218
227
|
none: string;
|
|
219
228
|
nothing_found: string;
|
|
229
|
+
notifications_empty: string;
|
|
220
230
|
or: string;
|
|
221
231
|
pagination_detail: string;
|
|
222
232
|
previous: string;
|
|
@@ -225,6 +235,7 @@ declare const config: {
|
|
|
225
235
|
remove: string;
|
|
226
236
|
remove_file: string;
|
|
227
237
|
remove_file_description: string;
|
|
238
|
+
see_all_notifications: string;
|
|
228
239
|
select_an_item: string;
|
|
229
240
|
select_an_option: string;
|
|
230
241
|
success: string;
|
|
@@ -241,6 +252,7 @@ declare const config: {
|
|
|
241
252
|
up_to_x: string;
|
|
242
253
|
upload_failed: string;
|
|
243
254
|
whoops: string;
|
|
255
|
+
x_ago: string;
|
|
244
256
|
x_rows_selected: string;
|
|
245
257
|
year: string;
|
|
246
258
|
yes_delete: string;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { RouteLocationRaw } from 'vue-router';
|
|
2
2
|
import { UploadedFile } from './UploadedFile';
|
|
3
|
+
import { Notification as AppNotification } from './Notification';
|
|
3
4
|
export interface Breadcrumb {
|
|
4
5
|
icon?: string;
|
|
5
6
|
to: RouteLocationRaw;
|
|
@@ -144,3 +145,11 @@ export interface Notification {
|
|
|
144
145
|
color: 'info' | 'success' | 'danger' | 'warning';
|
|
145
146
|
duration: number;
|
|
146
147
|
}
|
|
148
|
+
/**
|
|
149
|
+
* App Notification
|
|
150
|
+
*/
|
|
151
|
+
export interface NotificationsConfig {
|
|
152
|
+
items: AppNotification[];
|
|
153
|
+
footerLabel?: string;
|
|
154
|
+
footerTo?: RouteLocationRaw;
|
|
155
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sprintify-ui",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.21",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"build": "rimraf dist && vue-tsc && vite build",
|
|
6
6
|
"build-fast": "rimraf dist && vite build",
|
|
@@ -17,15 +17,16 @@
|
|
|
17
17
|
"@tailwindcss/typography": "^0.5.8",
|
|
18
18
|
"@vueuse/core": "^9.0.0",
|
|
19
19
|
"axios": "^0.26.1",
|
|
20
|
+
"humanize-duration": "^3.0.0",
|
|
20
21
|
"lodash": "^4.17.21",
|
|
21
22
|
"luxon": "^3.0.0",
|
|
23
|
+
"pikaday": "^1.8.2",
|
|
22
24
|
"pinia": "^2.0.0",
|
|
23
25
|
"qs": "^6.0.0",
|
|
24
26
|
"tailwindcss": "^3.0.0",
|
|
25
27
|
"vue": "^3.0.0",
|
|
26
28
|
"vue-i18n": "^9.0.0",
|
|
27
|
-
"vue-router": "^4.0.0"
|
|
28
|
-
"pikaday": "^1.8.2"
|
|
29
|
+
"vue-router": "^4.0.0"
|
|
29
30
|
},
|
|
30
31
|
"dependencies": {
|
|
31
32
|
"@headlessui/vue": "^1.7.4"
|
|
@@ -44,6 +45,7 @@
|
|
|
44
45
|
"@tailwindcss/forms": "^0.5.3",
|
|
45
46
|
"@tailwindcss/line-clamp": "^0.4.2",
|
|
46
47
|
"@tailwindcss/typography": "^0.5.8",
|
|
48
|
+
"@types/humanize-duration": "^3.27.1",
|
|
47
49
|
"@types/luxon": "^3.1.0",
|
|
48
50
|
"@types/object-hash": "^2.2.1",
|
|
49
51
|
"@types/pikaday": "^1.7.6",
|
|
@@ -63,6 +65,7 @@
|
|
|
63
65
|
"eslint-plugin-storybook": "^0.6.7",
|
|
64
66
|
"eslint-plugin-vue": "^9.7.0",
|
|
65
67
|
"eslint-plugin-vue-scoped-css": "^2.2.0",
|
|
68
|
+
"humanize-duration": "^3.27.3",
|
|
66
69
|
"lodash": "^4.17.21",
|
|
67
70
|
"luxon": "^3.1.0",
|
|
68
71
|
"object-hash": "^3.0.0",
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<RouterLink
|
|
3
|
+
v-if="item.to"
|
|
4
|
+
v-slot="{ isActive, href: slotHref, navigate }"
|
|
5
|
+
custom
|
|
6
|
+
:to="item.to"
|
|
7
|
+
>
|
|
8
|
+
<a
|
|
9
|
+
:active="isActive"
|
|
10
|
+
:href="slotHref"
|
|
11
|
+
:class="itemClass"
|
|
12
|
+
:aria-current="isActive ? 'page' : undefined"
|
|
13
|
+
@click.prevent="onClick(navigate)"
|
|
14
|
+
>
|
|
15
|
+
<slot :active="isActive"> </slot>
|
|
16
|
+
</a>
|
|
17
|
+
</RouterLink>
|
|
18
|
+
<button
|
|
19
|
+
v-else-if="item.action"
|
|
20
|
+
type="button"
|
|
21
|
+
:class="itemClass"
|
|
22
|
+
@click="onClick(item.action)"
|
|
23
|
+
>
|
|
24
|
+
<slot :active="false"> </slot>
|
|
25
|
+
</button>
|
|
26
|
+
<a
|
|
27
|
+
v-else-if="item.href"
|
|
28
|
+
:href="item.href"
|
|
29
|
+
:class="itemClass"
|
|
30
|
+
@click="onClick()"
|
|
31
|
+
>
|
|
32
|
+
<slot :active="false"> </slot>
|
|
33
|
+
</a>
|
|
34
|
+
</template>
|
|
35
|
+
|
|
36
|
+
<script setup lang="ts">
|
|
37
|
+
import { PropType } from 'vue';
|
|
38
|
+
import { ActionItem } from '@/types/types';
|
|
39
|
+
|
|
40
|
+
defineProps({
|
|
41
|
+
item: {
|
|
42
|
+
required: true,
|
|
43
|
+
type: Object as PropType<ActionItem>,
|
|
44
|
+
},
|
|
45
|
+
dark: {
|
|
46
|
+
default: false,
|
|
47
|
+
type: Boolean,
|
|
48
|
+
},
|
|
49
|
+
itemClass: {
|
|
50
|
+
default: '',
|
|
51
|
+
type: [String, Array, Object],
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
const closeMenu = inject('closeMenu') as () => void;
|
|
56
|
+
|
|
57
|
+
const emit = defineEmits(['click']);
|
|
58
|
+
|
|
59
|
+
type asyncFunc = () => Promise<void>;
|
|
60
|
+
|
|
61
|
+
async function onClick(func: asyncFunc | null = null) {
|
|
62
|
+
if (func) {
|
|
63
|
+
await func();
|
|
64
|
+
}
|
|
65
|
+
emit('click');
|
|
66
|
+
closeMenu();
|
|
67
|
+
}
|
|
68
|
+
</script>
|
|
File without changes
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<BaseMenu
|
|
3
|
+
class="inline-block"
|
|
4
|
+
:position="mobile ? 'custom' : 'bottom-left'"
|
|
5
|
+
:menu-class="['sm:w-[320px] w-[280px]', mobile ? 'right-4' : '']"
|
|
6
|
+
>
|
|
7
|
+
<template #button="{ open }">
|
|
8
|
+
<div
|
|
9
|
+
class="relative flex items-center rounded-full p-1.5"
|
|
10
|
+
:class="[open ? '' : '']"
|
|
11
|
+
>
|
|
12
|
+
<BaseIcon
|
|
13
|
+
icon="heroicons:bell"
|
|
14
|
+
class="h-6 w-6"
|
|
15
|
+
:class="[dark ? 'text-slate-300' : 'text-slate-600']"
|
|
16
|
+
></BaseIcon>
|
|
17
|
+
<BaseCounter
|
|
18
|
+
v-if="notificationsConfig.items.length"
|
|
19
|
+
class="absolute top-0 right-0"
|
|
20
|
+
:count="notificationsConfig.items.length"
|
|
21
|
+
></BaseCounter>
|
|
22
|
+
</div>
|
|
23
|
+
</template>
|
|
24
|
+
|
|
25
|
+
<template #items>
|
|
26
|
+
<template
|
|
27
|
+
v-for="notification in notificationsConfig.items"
|
|
28
|
+
:key="notification.id + 'link'"
|
|
29
|
+
>
|
|
30
|
+
<BaseLayoutNotificationItem
|
|
31
|
+
:notification="notification"
|
|
32
|
+
></BaseLayoutNotificationItem>
|
|
33
|
+
</template>
|
|
34
|
+
<div
|
|
35
|
+
v-if="notificationsConfig.items.length == 0"
|
|
36
|
+
class="flex items-center justify-center p-6"
|
|
37
|
+
>
|
|
38
|
+
<div class="">
|
|
39
|
+
<BaseIcon
|
|
40
|
+
icon="heroicons:inbox-stack"
|
|
41
|
+
class="mx-auto mb-2 h-8 w-8 text-slate-400"
|
|
42
|
+
></BaseIcon>
|
|
43
|
+
<p class="text-center text-sm text-slate-700">
|
|
44
|
+
{{ $t('sui.notifications_empty') }}
|
|
45
|
+
</p>
|
|
46
|
+
</div>
|
|
47
|
+
</div>
|
|
48
|
+
|
|
49
|
+
<div
|
|
50
|
+
v-if="notificationsConfig.footerTo"
|
|
51
|
+
class="mt-1 border-t border-slate-200 pt-1"
|
|
52
|
+
>
|
|
53
|
+
<RouterLink
|
|
54
|
+
v-slot="{ href, navigate }"
|
|
55
|
+
custom
|
|
56
|
+
:to="notificationsConfig.footerTo"
|
|
57
|
+
>
|
|
58
|
+
<MenuItem as="a" :href="href" @click="navigate">
|
|
59
|
+
<div
|
|
60
|
+
class="hover block px-3 py-2 text-center text-sm font-medium text-primary-600 hover:bg-slate-100"
|
|
61
|
+
>
|
|
62
|
+
{{
|
|
63
|
+
notificationsConfig.footerLabel
|
|
64
|
+
? notificationsConfig.footerLabel
|
|
65
|
+
: $t('sui.see_all_notifications')
|
|
66
|
+
}}
|
|
67
|
+
</div>
|
|
68
|
+
</MenuItem>
|
|
69
|
+
</RouterLink>
|
|
70
|
+
</div>
|
|
71
|
+
</template>
|
|
72
|
+
</BaseMenu>
|
|
73
|
+
</template>
|
|
74
|
+
|
|
75
|
+
<script lang="ts" setup>
|
|
76
|
+
import { PropType } from 'vue';
|
|
77
|
+
import { BaseIcon } from '.';
|
|
78
|
+
import BaseCounter from './BaseCounter.vue';
|
|
79
|
+
import BaseLayoutNotificationItem from './BaseLayoutNotificationItem.vue';
|
|
80
|
+
import BaseMenu from './BaseMenu.vue';
|
|
81
|
+
import { useBreakpoints } from '@/composables/breakpoints';
|
|
82
|
+
import { NotificationsConfig } from '../types/types';
|
|
83
|
+
import { MenuItem } from '@headlessui/vue';
|
|
84
|
+
|
|
85
|
+
defineProps({
|
|
86
|
+
notificationsConfig: {
|
|
87
|
+
required: true,
|
|
88
|
+
type: Object as PropType<NotificationsConfig>,
|
|
89
|
+
},
|
|
90
|
+
dark: {
|
|
91
|
+
default: false,
|
|
92
|
+
type: Boolean,
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
const breakpoints = useBreakpoints();
|
|
97
|
+
|
|
98
|
+
const mobile = computed((): boolean => {
|
|
99
|
+
return breakpoints.smaller('sm').value;
|
|
100
|
+
});
|
|
101
|
+
</script>
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<router-link v-slot="{ href, navigate }" custom :to="notification.to">
|
|
3
|
+
<MenuItem v-slot="{ active }" as="a" :href="href" @click="navigate">
|
|
4
|
+
<div class="px-3 py-2" :class="[active ? 'bg-slate-100' : '']">
|
|
5
|
+
<div
|
|
6
|
+
class="text-sm leading-tight text-slate-800"
|
|
7
|
+
v-html="notification.text"
|
|
8
|
+
></div>
|
|
9
|
+
<p v-if="createdAt" class="mt-1 text-xs text-slate-400">
|
|
10
|
+
{{ createdAt }}
|
|
11
|
+
</p>
|
|
12
|
+
</div>
|
|
13
|
+
</MenuItem>
|
|
14
|
+
</router-link>
|
|
15
|
+
</template>
|
|
16
|
+
|
|
17
|
+
<script lang="ts" setup>
|
|
18
|
+
import { Notification } from '@/types/Notification';
|
|
19
|
+
import { MenuItem } from '@headlessui/vue';
|
|
20
|
+
import { DateTime } from 'luxon';
|
|
21
|
+
import { PropType } from 'vue';
|
|
22
|
+
import humanizeDuration from 'humanize-duration';
|
|
23
|
+
|
|
24
|
+
const i18n = useI18n();
|
|
25
|
+
|
|
26
|
+
const props = defineProps({
|
|
27
|
+
notification: {
|
|
28
|
+
required: true,
|
|
29
|
+
type: Object as PropType<Notification>,
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const now = ref(DateTime.now());
|
|
34
|
+
|
|
35
|
+
const intervalId = setInterval(() => {
|
|
36
|
+
now.value.plus({ second: 1 });
|
|
37
|
+
}, 1000);
|
|
38
|
+
|
|
39
|
+
const createdAt = computed(() => {
|
|
40
|
+
if (props.notification.created_at) {
|
|
41
|
+
const duration = DateTime.fromISO(props.notification.created_at).diff(
|
|
42
|
+
now.value
|
|
43
|
+
).milliseconds;
|
|
44
|
+
|
|
45
|
+
const durationHuman = humanizeDuration(duration, {
|
|
46
|
+
language: i18n.locale.value,
|
|
47
|
+
round: true,
|
|
48
|
+
largest: 1,
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
return i18n.t('sui.x_ago', { duration: durationHuman });
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
onBeforeUnmount(() => {
|
|
56
|
+
clearInterval(intervalId);
|
|
57
|
+
});
|
|
58
|
+
</script>
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="min-h-full">
|
|
3
|
+
<TransitionRoot as="template" :show="showMobileMenu">
|
|
4
|
+
<Dialog
|
|
5
|
+
as="div"
|
|
6
|
+
class="relative z-40 xl:hidden"
|
|
7
|
+
@close="showMobileMenu = false"
|
|
8
|
+
>
|
|
9
|
+
<TransitionChild
|
|
10
|
+
as="template"
|
|
11
|
+
enter="transition-opacity ease-linear duration-300"
|
|
12
|
+
enter-from="opacity-0"
|
|
13
|
+
enter-to="opacity-100"
|
|
14
|
+
leave="transition-opacity ease-linear duration-300"
|
|
15
|
+
leave-from="opacity-100"
|
|
16
|
+
leave-to="opacity-0"
|
|
17
|
+
>
|
|
18
|
+
<div class="fixed inset-0 bg-slate-600 bg-opacity-75" />
|
|
19
|
+
</TransitionChild>
|
|
20
|
+
|
|
21
|
+
<div class="fixed inset-0 z-40 flex">
|
|
22
|
+
<TransitionChild
|
|
23
|
+
as="template"
|
|
24
|
+
enter="transition ease-in-out duration-300 transform"
|
|
25
|
+
enter-from="-translate-x-full"
|
|
26
|
+
enter-to="translate-x-0"
|
|
27
|
+
leave="transition ease-in-out duration-300 transform"
|
|
28
|
+
leave-from="translate-x-0"
|
|
29
|
+
leave-to="-translate-x-full"
|
|
30
|
+
>
|
|
31
|
+
<DialogPanel
|
|
32
|
+
class="relative flex w-full max-w-xs flex-1 flex-col pt-5 pb-4"
|
|
33
|
+
:class="[dark ? 'bg-slate-800' : 'bg-white']"
|
|
34
|
+
>
|
|
35
|
+
<TransitionChild
|
|
36
|
+
as="template"
|
|
37
|
+
enter="ease-in-out duration-300"
|
|
38
|
+
enter-from="opacity-0"
|
|
39
|
+
enter-to="opacity-100"
|
|
40
|
+
leave="ease-in-out duration-300"
|
|
41
|
+
leave-from="opacity-100"
|
|
42
|
+
leave-to="opacity-0"
|
|
43
|
+
>
|
|
44
|
+
<div class="absolute top-0 right-0 -mr-12 pt-2">
|
|
45
|
+
<button
|
|
46
|
+
type="button"
|
|
47
|
+
class="ml-1 flex h-10 w-10 items-center justify-center rounded-full"
|
|
48
|
+
@click="showMobileMenu = false"
|
|
49
|
+
>
|
|
50
|
+
<span class="sr-only">Close sidebar</span>
|
|
51
|
+
<BaseIcon
|
|
52
|
+
icon="heroicons:x-mark"
|
|
53
|
+
class="h-6 w-6 text-white"
|
|
54
|
+
aria-hidden="true"
|
|
55
|
+
/>
|
|
56
|
+
</button>
|
|
57
|
+
</div>
|
|
58
|
+
</TransitionChild>
|
|
59
|
+
<div class="flex flex-shrink-0 items-center px-4">
|
|
60
|
+
<img class="block h-8 w-auto" :src="logoUrl" :alt="appName" />
|
|
61
|
+
</div>
|
|
62
|
+
<div class="mt-5 h-0 flex-1 overflow-y-auto">
|
|
63
|
+
<nav>
|
|
64
|
+
<slot name="menu" />
|
|
65
|
+
</nav>
|
|
66
|
+
</div>
|
|
67
|
+
</DialogPanel>
|
|
68
|
+
</TransitionChild>
|
|
69
|
+
<div class="w-14 flex-shrink-0" aria-hidden="true">
|
|
70
|
+
<!-- Dummy element to force sidebar to shrink to fit close icon -->
|
|
71
|
+
</div>
|
|
72
|
+
</div>
|
|
73
|
+
</Dialog>
|
|
74
|
+
</TransitionRoot>
|
|
75
|
+
|
|
76
|
+
<div class="flex min-h-full flex-col xl:pl-64">
|
|
77
|
+
<div class="sticky top-0 left-0 z-10 shrink-0 shadow">
|
|
78
|
+
<BaseSystemAlert
|
|
79
|
+
v-for="systemAlert in systemAlerts"
|
|
80
|
+
:key="systemAlert.id"
|
|
81
|
+
:color="systemAlert.color"
|
|
82
|
+
:to="systemAlert.to"
|
|
83
|
+
:action="systemAlert.action"
|
|
84
|
+
:closable="systemAlert.closable"
|
|
85
|
+
@close="systemAlertStore.remove(systemAlert)"
|
|
86
|
+
>
|
|
87
|
+
{{ systemAlert.message }}
|
|
88
|
+
</BaseSystemAlert>
|
|
89
|
+
|
|
90
|
+
<div class="flex h-16 bg-white">
|
|
91
|
+
<button
|
|
92
|
+
type="button"
|
|
93
|
+
class="border-r border-slate-200 px-4 text-slate-500 xl:hidden"
|
|
94
|
+
@click="showMobileMenu = true"
|
|
95
|
+
>
|
|
96
|
+
<span class="sr-only">Open sidebar</span>
|
|
97
|
+
<BaseIcon
|
|
98
|
+
icon="heroicons:bars-3-bottom-left"
|
|
99
|
+
class="h-6 w-6"
|
|
100
|
+
aria-hidden="true"
|
|
101
|
+
/>
|
|
102
|
+
</button>
|
|
103
|
+
|
|
104
|
+
<!-- Navbar -->
|
|
105
|
+
<div class="flex flex-1">
|
|
106
|
+
<slot name="navbar" />
|
|
107
|
+
</div>
|
|
108
|
+
</div>
|
|
109
|
+
</div>
|
|
110
|
+
|
|
111
|
+
<!-- Position: relative to contain Loading Covers -->
|
|
112
|
+
<main class="relative min-h-full flex-1">
|
|
113
|
+
<slot />
|
|
114
|
+
</main>
|
|
115
|
+
</div>
|
|
116
|
+
|
|
117
|
+
<!-- Static sidebar for desktop -->
|
|
118
|
+
<div class="z-10 hidden xl:fixed xl:inset-y-0 xl:flex xl:w-64 xl:flex-col">
|
|
119
|
+
<!-- Sidebar component, swap this element with another sidebar if you like -->
|
|
120
|
+
<div
|
|
121
|
+
class="flex min-h-0 flex-1 flex-col"
|
|
122
|
+
:class="[dark ? 'bg-slate-800' : 'bg-white shadow']"
|
|
123
|
+
>
|
|
124
|
+
<div
|
|
125
|
+
class="flex h-16 flex-shrink-0 items-center px-4"
|
|
126
|
+
:class="[dark ? 'bg-slate-900' : 'bg-white']"
|
|
127
|
+
>
|
|
128
|
+
<img class="block h-8 w-auto" :src="logoUrl" :alt="appName" />
|
|
129
|
+
</div>
|
|
130
|
+
<div class="flex flex-1">
|
|
131
|
+
<nav class="flex-1">
|
|
132
|
+
<slot name="menu" />
|
|
133
|
+
</nav>
|
|
134
|
+
</div>
|
|
135
|
+
</div>
|
|
136
|
+
</div>
|
|
137
|
+
</div>
|
|
138
|
+
</template>
|
|
139
|
+
|
|
140
|
+
<script setup lang="ts">
|
|
141
|
+
import { ref } from 'vue';
|
|
142
|
+
import {
|
|
143
|
+
Dialog,
|
|
144
|
+
DialogPanel,
|
|
145
|
+
TransitionChild,
|
|
146
|
+
TransitionRoot,
|
|
147
|
+
} from '@headlessui/vue';
|
|
148
|
+
|
|
149
|
+
import { useSystemAlertStore } from '../stores/systemAlerts';
|
|
150
|
+
import { BaseIcon } from '.';
|
|
151
|
+
import BaseSystemAlert from './BaseSystemAlert.vue';
|
|
152
|
+
|
|
153
|
+
const props = defineProps({
|
|
154
|
+
appName: {
|
|
155
|
+
default: '',
|
|
156
|
+
type: String,
|
|
157
|
+
},
|
|
158
|
+
logoUrl: {
|
|
159
|
+
default: 'https://sprintify.witify.io/img/logo/logo-side-dark.svg',
|
|
160
|
+
type: String,
|
|
161
|
+
},
|
|
162
|
+
dark: {
|
|
163
|
+
default: false,
|
|
164
|
+
type: Boolean,
|
|
165
|
+
},
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* System Alert And Navigation size
|
|
170
|
+
*/
|
|
171
|
+
|
|
172
|
+
const systemAlertStore = useSystemAlertStore();
|
|
173
|
+
|
|
174
|
+
const systemAlerts = computed(() => {
|
|
175
|
+
return systemAlertStore.systemAlerts;
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Show/Hide sidebar
|
|
180
|
+
*/
|
|
181
|
+
|
|
182
|
+
const showMobileMenu = ref(false);
|
|
183
|
+
|
|
184
|
+
function toggleMenu() {
|
|
185
|
+
showMobileMenu.value = !showMobileMenu.value;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function openMenu() {
|
|
189
|
+
showMobileMenu.value = true;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function closeMenu() {
|
|
193
|
+
showMobileMenu.value = false;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
provide('toggleMenu', toggleMenu);
|
|
197
|
+
provide('openMenu', openMenu);
|
|
198
|
+
provide('closeMenu', closeMenu);
|
|
199
|
+
</script>
|