@fy-/fws-vue 2.1.6 → 2.1.7
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/components/fws/CmsArticleBoxed.vue +23 -20
- package/components/fws/CmsArticleSingle.vue +74 -68
- package/components/fws/DataTable.vue +132 -125
- package/components/fws/FilterData.vue +99 -101
- package/components/fws/UserData.vue +33 -32
- package/components/fws/UserFlow.vue +163 -155
- package/components/fws/UserOAuth2.vue +73 -72
- package/components/fws/UserProfile.vue +98 -101
- package/components/fws/UserProfileStrict.vue +65 -64
- package/components/ssr/ClientOnly.ts +7 -7
- package/components/ui/DefaultBreadcrumb.vue +13 -13
- package/components/ui/DefaultConfirm.vue +35 -34
- package/components/ui/DefaultDateSelection.vue +19 -17
- package/components/ui/DefaultDropdown.vue +25 -25
- package/components/ui/DefaultDropdownLink.vue +15 -14
- package/components/ui/DefaultGallery.vue +179 -168
- package/components/ui/DefaultInput.vue +121 -126
- package/components/ui/DefaultLoader.vue +17 -17
- package/components/ui/DefaultModal.vue +35 -33
- package/components/ui/DefaultNotif.vue +50 -52
- package/components/ui/DefaultPaging.vue +92 -95
- package/components/ui/DefaultSidebar.vue +29 -25
- package/components/ui/DefaultTagInput.vue +121 -119
- package/components/ui/transitions/CollapseTransition.vue +1 -1
- package/components/ui/transitions/ExpandTransition.vue +1 -1
- package/components/ui/transitions/FadeTransition.vue +1 -1
- package/components/ui/transitions/ScaleTransition.vue +1 -1
- package/components/ui/transitions/SlideTransition.vue +3 -3
- package/composables/event-bus.ts +10 -8
- package/composables/rest.ts +59 -56
- package/composables/seo.ts +106 -95
- package/composables/ssr.ts +64 -62
- package/composables/templating.ts +57 -57
- package/composables/translations.ts +13 -13
- package/env.d.ts +6 -4
- package/index.ts +101 -98
- package/package.json +7 -7
- package/stores/serverRouter.ts +25 -25
- package/stores/user.ts +79 -72
- package/types.d.ts +65 -65
package/composables/seo.ts
CHANGED
|
@@ -1,116 +1,120 @@
|
|
|
1
|
-
|
|
2
|
-
import
|
|
3
|
-
import { useHead, useSeoMeta } from
|
|
4
|
-
import {
|
|
5
|
-
import { useRoute } from "vue-router";
|
|
1
|
+
import type { Ref } from 'vue'
|
|
2
|
+
import { getLocale, getPrefix, getURL } from '@fy-/fws-js'
|
|
3
|
+
import { useHead, useSeoMeta } from '@unhead/vue'
|
|
4
|
+
import { useRoute } from 'vue-router'
|
|
6
5
|
|
|
7
6
|
export interface LazyHead {
|
|
8
|
-
name?: string
|
|
9
|
-
title?: string
|
|
10
|
-
image?: string
|
|
11
|
-
imageType?: string
|
|
12
|
-
imageWidth?: string
|
|
13
|
-
imageHeight?: string
|
|
14
|
-
description?: string
|
|
15
|
-
published?: string
|
|
16
|
-
modified?: string
|
|
17
|
-
keywords?: string
|
|
18
|
-
type?:
|
|
19
|
-
searchAction?: string
|
|
20
|
-
next?: string
|
|
21
|
-
prev?: string
|
|
22
|
-
locale?: string
|
|
23
|
-
robots?: string
|
|
24
|
-
url?: string
|
|
25
|
-
canonical?: string
|
|
26
|
-
isAdult?: boolean
|
|
27
|
-
alternateLocales?: string[]
|
|
28
|
-
twitterCreator?: string
|
|
7
|
+
name?: string
|
|
8
|
+
title?: string
|
|
9
|
+
image?: string
|
|
10
|
+
imageType?: string
|
|
11
|
+
imageWidth?: string
|
|
12
|
+
imageHeight?: string
|
|
13
|
+
description?: string
|
|
14
|
+
published?: string
|
|
15
|
+
modified?: string
|
|
16
|
+
keywords?: string
|
|
17
|
+
type?: 'blog' | 'search' | 'article' | 'website'
|
|
18
|
+
searchAction?: string
|
|
19
|
+
next?: string
|
|
20
|
+
prev?: string
|
|
21
|
+
locale?: string
|
|
22
|
+
robots?: string
|
|
23
|
+
url?: string
|
|
24
|
+
canonical?: string
|
|
25
|
+
isAdult?: boolean
|
|
26
|
+
alternateLocales?: string[]
|
|
27
|
+
twitterCreator?: string
|
|
29
28
|
}
|
|
30
29
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const
|
|
34
|
-
const
|
|
30
|
+
// eslint-disable-next-line unused-imports/no-unused-vars
|
|
31
|
+
export function useSeo(seoData: Ref<LazyHead>, initial: boolean = false) {
|
|
32
|
+
const route = useRoute()
|
|
33
|
+
const currentLocale = getLocale()
|
|
34
|
+
const url = getURL()
|
|
35
35
|
|
|
36
36
|
useHead({
|
|
37
37
|
meta: () => {
|
|
38
|
-
const metas: any[] = []
|
|
38
|
+
const metas: any[] = []
|
|
39
39
|
if (seoData.value.isAdult) {
|
|
40
40
|
metas.push({
|
|
41
|
-
name:
|
|
42
|
-
content:
|
|
43
|
-
key:
|
|
44
|
-
})
|
|
41
|
+
name: 'rating',
|
|
42
|
+
content: 'RTA-5042-1996-1400-1577-RTA',
|
|
43
|
+
key: 'rating',
|
|
44
|
+
})
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
return metas
|
|
47
|
+
return metas
|
|
48
48
|
},
|
|
49
49
|
link: () => {
|
|
50
|
-
const links: any[] = []
|
|
50
|
+
const links: any[] = []
|
|
51
|
+
const page
|
|
52
|
+
= route.query.page && Number.parseInt(route.query.page.toString()) > 1
|
|
53
|
+
? `?page=${route.query.page}`
|
|
54
|
+
: ''
|
|
51
55
|
|
|
52
56
|
links.push({
|
|
53
|
-
rel:
|
|
54
|
-
href: `${url.Scheme}://${url.Host}${
|
|
55
|
-
|
|
56
|
-
});
|
|
57
|
+
rel: 'canonical',
|
|
58
|
+
href: `${url.Scheme}://${url.Host}${getURL().Path}${page}`,
|
|
59
|
+
})
|
|
57
60
|
|
|
58
61
|
seoData.value.alternateLocales?.forEach((locale) => {
|
|
59
62
|
if (locale !== currentLocale) {
|
|
60
63
|
links.push({
|
|
61
|
-
rel:
|
|
64
|
+
rel: 'alternate',
|
|
62
65
|
hreflang: locale,
|
|
63
66
|
href: `${getURL().Scheme}://${
|
|
64
67
|
getURL().Host
|
|
65
|
-
}/l/${locale}${getURL().Path.replace(getPrefix(),
|
|
68
|
+
}/l/${locale}${getURL().Path.replace(getPrefix(), '')}`,
|
|
66
69
|
key: `alternate-${locale}`,
|
|
67
|
-
})
|
|
70
|
+
})
|
|
68
71
|
}
|
|
69
|
-
})
|
|
72
|
+
})
|
|
70
73
|
|
|
71
74
|
if (seoData.value.image) {
|
|
72
75
|
links.push({
|
|
73
|
-
rel:
|
|
76
|
+
rel: 'preload',
|
|
74
77
|
href: seoData.value.image,
|
|
75
|
-
as:
|
|
76
|
-
fetchpriority:
|
|
77
|
-
})
|
|
78
|
+
as: 'image',
|
|
79
|
+
fetchpriority: 'high',
|
|
80
|
+
})
|
|
78
81
|
}
|
|
79
82
|
|
|
80
|
-
return links
|
|
83
|
+
return links
|
|
81
84
|
},
|
|
82
|
-
})
|
|
85
|
+
})
|
|
83
86
|
|
|
84
87
|
useSeoMeta({
|
|
85
88
|
ogUrl: () => `${getURL().Scheme}://${getURL().Host}${route.fullPath}`,
|
|
86
89
|
ogLocale: () => {
|
|
87
90
|
if (currentLocale) {
|
|
88
|
-
return currentLocale.replace(
|
|
91
|
+
return currentLocale.replace('-', '_')
|
|
89
92
|
}
|
|
90
93
|
},
|
|
91
94
|
robots:
|
|
92
|
-
|
|
93
|
-
title: () => seoData.value.title,
|
|
95
|
+
'index, follow, max-image-preview:large, max-snippet:-1, max-video-preview:-1',
|
|
96
|
+
title: () => seoData.value.title || '',
|
|
94
97
|
ogTitle: () => seoData.value.title,
|
|
95
98
|
ogDescription: () => seoData.value.description,
|
|
96
|
-
twitterCard:
|
|
99
|
+
twitterCard: 'summary_large_image',
|
|
97
100
|
ogSiteName: () => seoData.value.name,
|
|
98
101
|
twitterTitle: () => seoData.value.title,
|
|
99
102
|
twitterDescription: () => seoData.value.description,
|
|
100
103
|
ogImageAlt: () => {
|
|
101
104
|
if (seoData.value.image) {
|
|
102
|
-
return seoData.value.title
|
|
105
|
+
return seoData.value.title
|
|
103
106
|
}
|
|
104
|
-
return undefined
|
|
107
|
+
return undefined
|
|
105
108
|
},
|
|
106
|
-
|
|
109
|
+
// @ts-expect-error: Type 'string' is not assignable to type 'undefined'.
|
|
110
|
+
ogType: () => (seoData.value.type ? seoData.value.type : 'website'),
|
|
107
111
|
twitterCreator: () => seoData.value.twitterCreator,
|
|
108
112
|
twitterSite: () => seoData.value.twitterCreator,
|
|
109
113
|
twitterImageAlt: () => {
|
|
110
114
|
if (seoData.value.image) {
|
|
111
|
-
return seoData.value.title
|
|
115
|
+
return seoData.value.title
|
|
112
116
|
}
|
|
113
|
-
return undefined
|
|
117
|
+
return undefined
|
|
114
118
|
},
|
|
115
119
|
description: () => seoData.value.description,
|
|
116
120
|
keywords: () => seoData.value.keywords,
|
|
@@ -118,68 +122,75 @@ export const useSeo = (seoData: Ref<LazyHead>, initial: boolean = false) => {
|
|
|
118
122
|
articleModifiedTime: () => seoData.value.modified,
|
|
119
123
|
ogImageSecureUrl: () => {
|
|
120
124
|
if (seoData.value.image) {
|
|
121
|
-
if (seoData.value.image.includes(
|
|
125
|
+
if (seoData.value.image.includes('?vars=')) {
|
|
122
126
|
if (seoData.value.imageType) {
|
|
123
127
|
return seoData.value.image.replace(
|
|
124
|
-
|
|
125
|
-
`.${seoData.value.imageType.replace(
|
|
126
|
-
)
|
|
127
|
-
}
|
|
128
|
-
|
|
128
|
+
'?vars=',
|
|
129
|
+
`.${seoData.value.imageType.replace('image/', '')}?vars=`,
|
|
130
|
+
)
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
return seoData.value.image.replace('?vars=', '.png?vars=')
|
|
129
134
|
}
|
|
130
135
|
}
|
|
131
|
-
return seoData.value.image
|
|
136
|
+
return seoData.value.image
|
|
132
137
|
}
|
|
133
138
|
},
|
|
134
139
|
ogImageUrl: () => {
|
|
135
140
|
if (seoData.value.image) {
|
|
136
|
-
if (seoData.value.image.includes(
|
|
141
|
+
if (seoData.value.image.includes('?vars=')) {
|
|
137
142
|
if (seoData.value.imageType) {
|
|
138
143
|
return seoData.value.image.replace(
|
|
139
|
-
|
|
140
|
-
`.${seoData.value.imageType.replace(
|
|
141
|
-
)
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
+
'?vars=',
|
|
145
|
+
`.${seoData.value.imageType.replace('image/', '')}?vars=`,
|
|
146
|
+
)
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
return seoData.value.image.replace('?vars=', '.png?vars=')
|
|
144
150
|
}
|
|
145
151
|
}
|
|
146
|
-
return seoData.value.image
|
|
152
|
+
return seoData.value.image
|
|
147
153
|
}
|
|
148
154
|
},
|
|
149
155
|
ogImageType: () => {
|
|
150
156
|
if (seoData.value.imageType) {
|
|
151
|
-
|
|
152
|
-
? seoData.value.imageType
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
157
|
+
const type = seoData.value.imageType.includes('image/')
|
|
158
|
+
? seoData.value.imageType
|
|
159
|
+
: `image/${seoData.value.imageType}`
|
|
160
|
+
if (type === 'image/jpeg' || type === 'image/gif' || type === 'image/png') {
|
|
161
|
+
return type
|
|
162
|
+
}
|
|
163
|
+
return 'image/png'
|
|
156
164
|
}
|
|
157
|
-
return undefined
|
|
165
|
+
return undefined
|
|
158
166
|
},
|
|
159
167
|
twitterImageUrl: () => {
|
|
160
168
|
if (seoData.value.image) {
|
|
161
|
-
if (seoData.value.image.includes(
|
|
169
|
+
if (seoData.value.image.includes('?vars=')) {
|
|
162
170
|
if (seoData.value.imageType) {
|
|
163
171
|
return seoData.value.image.replace(
|
|
164
|
-
|
|
165
|
-
`.${seoData.value.imageType.replace(
|
|
166
|
-
)
|
|
167
|
-
}
|
|
168
|
-
|
|
172
|
+
'?vars=',
|
|
173
|
+
`.${seoData.value.imageType.replace('image/', '')}?vars=`,
|
|
174
|
+
)
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
return seoData.value.image.replace('?vars=', '.png?vars=')
|
|
169
178
|
}
|
|
170
179
|
}
|
|
171
|
-
return seoData.value.image
|
|
180
|
+
return seoData.value.image
|
|
172
181
|
}
|
|
173
182
|
},
|
|
174
183
|
twitterImageType() {
|
|
175
184
|
if (seoData.value.imageType) {
|
|
176
|
-
|
|
177
|
-
? seoData.value.imageType
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
185
|
+
const type = seoData.value.imageType.includes('image/')
|
|
186
|
+
? seoData.value.imageType
|
|
187
|
+
: `image/${seoData.value.imageType}`
|
|
188
|
+
if (type === 'image/jpeg' || type === 'image/gif' || type === 'image/png') {
|
|
189
|
+
return type
|
|
190
|
+
}
|
|
191
|
+
return 'image/png'
|
|
181
192
|
}
|
|
182
|
-
return undefined
|
|
193
|
+
return undefined
|
|
183
194
|
},
|
|
184
|
-
})
|
|
185
|
-
}
|
|
195
|
+
})
|
|
196
|
+
}
|
package/composables/ssr.ts
CHANGED
|
@@ -1,39 +1,39 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
1
|
+
import type { Pinia } from 'pinia'
|
|
2
|
+
import type { Router } from 'vue-router'
|
|
3
|
+
import { getInitialState, getPath, getURL, getUUID } from '@fy-/fws-js'
|
|
4
|
+
import { renderSSRHead } from '@unhead/ssr'
|
|
5
|
+
import { renderToString } from '@vue/server-renderer'
|
|
6
|
+
import { useServerRouter } from '../stores/serverRouter'
|
|
7
7
|
|
|
8
8
|
export interface SSRResult {
|
|
9
9
|
initial: {
|
|
10
|
-
isSSR: boolean
|
|
11
|
-
pinia?: string
|
|
12
|
-
}
|
|
13
|
-
uuid?: string
|
|
14
|
-
meta?: string
|
|
15
|
-
link?: string
|
|
16
|
-
bodyAttributes?: string
|
|
17
|
-
bodyTagsOpen?: string
|
|
18
|
-
htmlAttributes?: string
|
|
19
|
-
bodyTags?: string
|
|
20
|
-
app?: string
|
|
21
|
-
statusCode?: number
|
|
22
|
-
redirect?: string
|
|
10
|
+
isSSR: boolean
|
|
11
|
+
pinia?: string
|
|
12
|
+
}
|
|
13
|
+
uuid?: string
|
|
14
|
+
meta?: string
|
|
15
|
+
link?: string
|
|
16
|
+
bodyAttributes?: string
|
|
17
|
+
bodyTagsOpen?: string
|
|
18
|
+
htmlAttributes?: string
|
|
19
|
+
bodyTags?: string
|
|
20
|
+
app?: string
|
|
21
|
+
statusCode?: number
|
|
22
|
+
redirect?: string
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
export function isServerRendered() {
|
|
26
|
-
const state = getInitialState()
|
|
27
|
-
if (state && state.isSSR) return true
|
|
28
|
-
return false
|
|
26
|
+
const state = getInitialState()
|
|
27
|
+
if (state && state.isSSR) return true
|
|
28
|
+
return false
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
export function initVueClient(router: Router, pinia: Pinia) {
|
|
32
|
-
const state = getInitialState()
|
|
32
|
+
const state = getInitialState()
|
|
33
33
|
if (state.isSSR && state && state.pinia) {
|
|
34
|
-
pinia.state.value = state.pinia
|
|
34
|
+
pinia.state.value = state.pinia
|
|
35
35
|
}
|
|
36
|
-
useServerRouter(pinia)._setRouter(router)
|
|
36
|
+
useServerRouter(pinia)._setRouter(router)
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
export async function initVueServer(
|
|
@@ -41,13 +41,13 @@ export async function initVueServer(
|
|
|
41
41
|
callback: Function,
|
|
42
42
|
options: { url?: string } = {},
|
|
43
43
|
) {
|
|
44
|
-
const url
|
|
45
|
-
options.url || `${getPath()}${getURL().Query ? `?${getURL().Query}` :
|
|
46
|
-
const { app, router, head, pinia } = await createApp(true)
|
|
47
|
-
const serverRouter = useServerRouter(pinia)
|
|
48
|
-
serverRouter._setRouter(router)
|
|
49
|
-
await router.push(url)
|
|
50
|
-
await router.isReady()
|
|
44
|
+
const url
|
|
45
|
+
= options.url || `${getPath()}${getURL().Query ? `?${getURL().Query}` : ''}`
|
|
46
|
+
const { app, router, head, pinia } = await createApp(true)
|
|
47
|
+
const serverRouter = useServerRouter(pinia)
|
|
48
|
+
serverRouter._setRouter(router)
|
|
49
|
+
await router.push(url)
|
|
50
|
+
await router.isReady()
|
|
51
51
|
|
|
52
52
|
const result: SSRResult = {
|
|
53
53
|
uuid: getUUID(),
|
|
@@ -55,47 +55,49 @@ export async function initVueServer(
|
|
|
55
55
|
isSSR: true,
|
|
56
56
|
pinia: undefined,
|
|
57
57
|
},
|
|
58
|
-
}
|
|
58
|
+
}
|
|
59
59
|
|
|
60
60
|
if (url !== serverRouter.route.fullPath) {
|
|
61
|
-
result.redirect = serverRouter.route.value.fullPath
|
|
62
|
-
result.statusCode = 307
|
|
63
|
-
callback(result)
|
|
64
|
-
return result
|
|
61
|
+
result.redirect = serverRouter.route.value.fullPath
|
|
62
|
+
result.statusCode = 307
|
|
63
|
+
callback(result)
|
|
64
|
+
return result
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
try {
|
|
68
|
-
const html = await renderToString(app)
|
|
69
|
-
const payload = await renderSSRHead(head)
|
|
68
|
+
const html = await renderToString(app)
|
|
69
|
+
const payload = await renderSSRHead(head)
|
|
70
70
|
|
|
71
|
-
result.meta = payload.headTags
|
|
72
|
-
result.bodyAttributes = payload.bodyAttrs
|
|
73
|
-
result.htmlAttributes = payload.htmlAttrs
|
|
74
|
-
result.bodyTags = payload.bodyTags
|
|
75
|
-
result.bodyTagsOpen = payload.bodyTagsOpen
|
|
76
|
-
result.app = html
|
|
77
|
-
if (serverRouter.status
|
|
71
|
+
result.meta = payload.headTags
|
|
72
|
+
result.bodyAttributes = payload.bodyAttrs
|
|
73
|
+
result.htmlAttributes = payload.htmlAttrs
|
|
74
|
+
result.bodyTags = payload.bodyTags
|
|
75
|
+
result.bodyTagsOpen = payload.bodyTagsOpen
|
|
76
|
+
result.app = html
|
|
77
|
+
if (serverRouter.status !== 200) {
|
|
78
78
|
if ([301, 302, 303, 307].includes(serverRouter.status)) {
|
|
79
79
|
if (serverRouter.redirect) {
|
|
80
|
-
result.statusCode = serverRouter.status
|
|
81
|
-
result.redirect = serverRouter.redirect
|
|
80
|
+
result.statusCode = serverRouter.status
|
|
81
|
+
result.redirect = serverRouter.redirect
|
|
82
82
|
}
|
|
83
|
-
}
|
|
84
|
-
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
result.statusCode = serverRouter.status
|
|
85
86
|
}
|
|
86
87
|
}
|
|
87
88
|
|
|
88
|
-
serverRouter._router = null
|
|
89
|
-
result.initial.pinia = pinia.state.value
|
|
90
|
-
callback(result)
|
|
91
|
-
return result
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
89
|
+
serverRouter._router = null
|
|
90
|
+
result.initial.pinia = pinia.state.value
|
|
91
|
+
callback(result)
|
|
92
|
+
return result
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
// console.error(error);
|
|
96
|
+
result.statusCode = 500
|
|
97
|
+
const err
|
|
98
|
+
= error instanceof Error ? `${error.message}\n${error.stack}` : error
|
|
99
|
+
result.app = `<pre>${err}</pre>`
|
|
100
|
+
callback(result)
|
|
101
|
+
return result
|
|
100
102
|
}
|
|
101
103
|
}
|
|
@@ -1,88 +1,88 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { getLocale } from '@fy-/fws-js'
|
|
2
|
+
import { format as formatDateTimeago } from 'timeago.js'
|
|
3
|
+
import { useTranslation } from './translations'
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
function cropText(str: string, ml = 100, end = '...') {
|
|
6
6
|
if (str.length > ml) {
|
|
7
|
-
return `${str.slice(0, ml)}${end}
|
|
7
|
+
return `${str.slice(0, ml)}${end}`
|
|
8
8
|
}
|
|
9
|
-
return str
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
const r = parseInt(backgroundColor.substring(1, 3), 16)
|
|
13
|
-
const g = parseInt(backgroundColor.substring(3, 5), 16)
|
|
14
|
-
const b = parseInt(backgroundColor.substring(5, 7), 16)
|
|
9
|
+
return str
|
|
10
|
+
}
|
|
11
|
+
function getContrastingTextColor(backgroundColor: string) {
|
|
12
|
+
const r = Number.parseInt(backgroundColor.substring(1, 3), 16)
|
|
13
|
+
const g = Number.parseInt(backgroundColor.substring(3, 5), 16)
|
|
14
|
+
const b = Number.parseInt(backgroundColor.substring(5, 7), 16)
|
|
15
15
|
|
|
16
|
-
const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255
|
|
16
|
+
const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255
|
|
17
17
|
|
|
18
|
-
return luminance > 0.5 ?
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
if (!+bytes) return
|
|
18
|
+
return luminance > 0.5 ? '#000000' : '#FFFFFF'
|
|
19
|
+
}
|
|
20
|
+
function formatBytes(bytes: number, decimals = 2) {
|
|
21
|
+
if (!+bytes) return '0 Bytes'
|
|
22
22
|
|
|
23
|
-
const k = 1024
|
|
24
|
-
const dm = decimals < 0 ? 0 : decimals
|
|
25
|
-
const sizes = [
|
|
23
|
+
const k = 1024
|
|
24
|
+
const dm = decimals < 0 ? 0 : decimals
|
|
25
|
+
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
|
|
26
26
|
|
|
27
|
-
const i = Math.floor(Math.log(bytes) / Math.log(k))
|
|
27
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k))
|
|
28
28
|
|
|
29
|
-
return `${parseFloat((bytes /
|
|
30
|
-
}
|
|
29
|
+
return `${Number.parseFloat((bytes / k ** i).toFixed(dm))} ${sizes[i]}`
|
|
30
|
+
}
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
let _dt = dt as number
|
|
34
|
-
if (typeof dt ===
|
|
35
|
-
_dt = Date.parse(dt)
|
|
32
|
+
function formatDate(dt: Date | string | number) {
|
|
33
|
+
let _dt = dt as number
|
|
34
|
+
if (typeof dt === 'string') {
|
|
35
|
+
_dt = Date.parse(dt)
|
|
36
36
|
if (Number.isNaN(_dt)) {
|
|
37
|
-
_dt = parseInt(dt)
|
|
37
|
+
_dt = Number.parseInt(dt)
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
const translate = useTranslation()
|
|
42
|
-
return translate(
|
|
41
|
+
const translate = useTranslation()
|
|
42
|
+
return translate('global_datetime', {
|
|
43
43
|
val: new Date(_dt),
|
|
44
44
|
formatParams: {
|
|
45
45
|
val: {
|
|
46
|
-
year:
|
|
47
|
-
month:
|
|
48
|
-
day:
|
|
46
|
+
year: 'numeric',
|
|
47
|
+
month: 'long',
|
|
48
|
+
day: 'numeric',
|
|
49
49
|
},
|
|
50
50
|
},
|
|
51
|
-
})
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
let _dt = dt as number
|
|
55
|
-
if (typeof dt ===
|
|
56
|
-
_dt = Date.parse(dt)
|
|
51
|
+
})
|
|
52
|
+
}
|
|
53
|
+
function formatDatetime(dt: Date | string | number) {
|
|
54
|
+
let _dt = dt as number
|
|
55
|
+
if (typeof dt === 'string') {
|
|
56
|
+
_dt = Date.parse(dt)
|
|
57
57
|
if (Number.isNaN(_dt)) {
|
|
58
|
-
_dt = parseInt(dt)
|
|
58
|
+
_dt = Number.parseInt(dt)
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
|
-
const translate = useTranslation()
|
|
62
|
-
return translate(
|
|
61
|
+
const translate = useTranslation()
|
|
62
|
+
return translate('global_datetime', {
|
|
63
63
|
val: new Date(_dt),
|
|
64
64
|
formatParams: {
|
|
65
65
|
val: {
|
|
66
|
-
year:
|
|
67
|
-
month:
|
|
68
|
-
day:
|
|
69
|
-
hour:
|
|
70
|
-
minute:
|
|
71
|
-
second:
|
|
66
|
+
year: 'numeric',
|
|
67
|
+
month: 'long',
|
|
68
|
+
day: 'numeric',
|
|
69
|
+
hour: 'numeric',
|
|
70
|
+
minute: 'numeric',
|
|
71
|
+
second: 'numeric',
|
|
72
72
|
},
|
|
73
73
|
},
|
|
74
|
-
})
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
let _dt = dt as number
|
|
78
|
-
if (typeof dt ===
|
|
79
|
-
_dt = Date.parse(dt)
|
|
74
|
+
})
|
|
75
|
+
}
|
|
76
|
+
function formatTimeago(dt: Date | string | number) {
|
|
77
|
+
let _dt = dt as number
|
|
78
|
+
if (typeof dt === 'string') {
|
|
79
|
+
_dt = Date.parse(dt)
|
|
80
80
|
if (Number.isNaN(_dt)) {
|
|
81
|
-
_dt = parseInt(dt)
|
|
81
|
+
_dt = Number.parseInt(dt)
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
|
-
return formatDateTimeago(new Date(_dt), getLocale().replace(
|
|
85
|
-
}
|
|
84
|
+
return formatDateTimeago(new Date(_dt), getLocale().replace('_', '-'))
|
|
85
|
+
}
|
|
86
86
|
|
|
87
87
|
export {
|
|
88
88
|
cropText,
|
|
@@ -91,4 +91,4 @@ export {
|
|
|
91
91
|
formatDatetime,
|
|
92
92
|
formatTimeago,
|
|
93
93
|
getContrastingTextColor,
|
|
94
|
-
}
|
|
94
|
+
}
|
|
@@ -1,29 +1,29 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
1
|
+
import type { I18nBackend } from '@fy-/fws-js'
|
|
2
|
+
import type { TFunction } from 'i18next'
|
|
3
|
+
import i18next from 'i18next'
|
|
4
|
+
import { inject } from 'vue'
|
|
5
5
|
|
|
6
|
-
export type I18nextTranslate = typeof i18next.t
|
|
6
|
+
export type I18nextTranslate = typeof i18next.t
|
|
7
7
|
|
|
8
8
|
export function useTranslation() {
|
|
9
|
-
const translate = inject<TFunction>(
|
|
10
|
-
if (!translate) throw new Error(
|
|
9
|
+
const translate = inject<TFunction>('fwsVueTranslate')
|
|
10
|
+
if (!translate) throw new Error('Did you apply app.use(fwsVue)?')
|
|
11
11
|
|
|
12
|
-
return translate
|
|
12
|
+
return translate
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
export function i18nextPromise(
|
|
16
16
|
backend: typeof I18nBackend,
|
|
17
|
-
locale: string =
|
|
17
|
+
locale: string = 'en-US',
|
|
18
18
|
debug: boolean = false,
|
|
19
|
-
ns: string =
|
|
19
|
+
ns: string = 'translation',
|
|
20
20
|
) {
|
|
21
21
|
return i18next.use(backend).init({
|
|
22
22
|
ns: [ns],
|
|
23
23
|
defaultNS: ns,
|
|
24
|
-
debug
|
|
24
|
+
debug,
|
|
25
25
|
lng: locale,
|
|
26
|
-
load:
|
|
26
|
+
load: 'currentOnly',
|
|
27
27
|
initImmediate: false,
|
|
28
|
-
})
|
|
28
|
+
})
|
|
29
29
|
}
|
package/env.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
/// <reference types="vite/client" />
|
|
2
|
-
declare module
|
|
3
|
-
import type { DefineComponent } from
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
declare module '*.vue' {
|
|
3
|
+
import type { DefineComponent } from 'vue'
|
|
4
|
+
|
|
5
|
+
// eslint-disable-next-line ts/no-empty-object-type
|
|
6
|
+
const component: DefineComponent<{}, {}, any>
|
|
7
|
+
export default component
|
|
6
8
|
}
|