bt-core-app 0.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.
- package/.vscode/extensions.json +3 -0
- package/README.md +45 -0
- package/index.html +13 -0
- package/package.json +39 -0
- package/public/vite.svg +1 -0
- package/src/assets/vue.svg +1 -0
- package/src/components/BT-Btn.vue +40 -0
- package/src/components/BT-Col.vue +36 -0
- package/src/components/BT-Span.vue +21 -0
- package/src/components/Dialog-Confirm.vue +47 -0
- package/src/components/Dialog-Select-Date.vue +89 -0
- package/src/components/Dialog-Select.vue +140 -0
- package/src/components/Dialog-Text.vue +59 -0
- package/src/composables/actions-tracker.ts +99 -0
- package/src/composables/actions.ts +354 -0
- package/src/composables/api.ts +471 -0
- package/src/composables/auth.ts +382 -0
- package/src/composables/cosmetics.ts +178 -0
- package/src/composables/csv.ts +198 -0
- package/src/composables/dates.ts +79 -0
- package/src/composables/demo.ts +25 -0
- package/src/composables/dialogs.ts +115 -0
- package/src/composables/document-meta.ts +40 -0
- package/src/composables/draggable.ts +189 -0
- package/src/composables/filters.ts +256 -0
- package/src/composables/forage.ts +49 -0
- package/src/composables/helpers.ts +694 -0
- package/src/composables/id.ts +20 -0
- package/src/composables/list.ts +601 -0
- package/src/composables/navigation.ts +214 -0
- package/src/composables/presets.ts +20 -0
- package/src/composables/pwa.ts +89 -0
- package/src/composables/resizable.ts +382 -0
- package/src/composables/rules.ts +40 -0
- package/src/composables/stores.ts +797 -0
- package/src/composables/track.ts +55 -0
- package/src/composables/urls.ts +11 -0
- package/src/core.ts +92 -0
- package/src/index.ts +16 -0
- package/src/types.ts +13 -0
- package/src/useApi.ts +68 -0
- package/src/vite-env.d.ts +1 -0
- package/test/api.test.ts +84 -0
- package/test/forage.test.ts +31 -0
- package/test/helpers.test.ts +231 -0
- package/test/navigation.test.ts +99 -0
- package/test/stores-last-update.test.ts +138 -0
- package/test/stores-session.test.ts +118 -0
- package/test/track.test.ts +29 -0
- package/test/utils.ts +15 -0
- package/tsconfig.json +33 -0
- package/tsconfig.node.json +11 -0
- package/vite.config.ts +19 -0
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
import { isLengthyArray } from '@/composables/helpers';
|
|
2
|
+
import { DateTime } from 'luxon'
|
|
3
|
+
import { type RemovableRef, useStorage } from '@vueuse/core'
|
|
4
|
+
import { type BTDemo } from '@/composables/demo'
|
|
5
|
+
import { useRouter } from 'vue-router'
|
|
6
|
+
import { toValue } from 'vue'
|
|
7
|
+
|
|
8
|
+
export interface AuthItem {
|
|
9
|
+
children?: AuthItem[]
|
|
10
|
+
ignoreSuspension?: boolean
|
|
11
|
+
permissions?: string[]
|
|
12
|
+
requiresAuth?: boolean
|
|
13
|
+
subscriptions?: string[]
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface AuthSubscription {
|
|
17
|
+
code: string
|
|
18
|
+
value: number
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface BaseAuthCredentials {
|
|
22
|
+
expiresOn?: string
|
|
23
|
+
isGlobalAdmin?: boolean
|
|
24
|
+
isLoggedIn?: boolean
|
|
25
|
+
isSuspended?: boolean
|
|
26
|
+
permissions?: string
|
|
27
|
+
subscriptionCode?: string
|
|
28
|
+
timeZone?: string
|
|
29
|
+
token?: string
|
|
30
|
+
userID?: string
|
|
31
|
+
userPermissions?: string[]
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// const state = useStorage<BaseAuthCredentials>('auth-credentials', {})
|
|
35
|
+
// let defaultsSet = false
|
|
36
|
+
let defaultTimeZone = 'Australia/Melbourne'
|
|
37
|
+
|
|
38
|
+
// export type FindAuthItem = (navName?: string | AuthItem) => AuthItem | undefined
|
|
39
|
+
export type GetAuthUrl = (redirectPath?: string, state?: string) => string
|
|
40
|
+
|
|
41
|
+
export interface CreateAuthOptions {
|
|
42
|
+
defaultTimeZone?: string,
|
|
43
|
+
demo?: BTDemo,
|
|
44
|
+
/**expiry token date format. Defaults to 'd/MM/yyyy h:mm:ss a' */
|
|
45
|
+
expiryTokenFormat?: string
|
|
46
|
+
/**retrieve the auth item */
|
|
47
|
+
getAuthItem: (navName?: string | AuthItem) => AuthItem | null
|
|
48
|
+
/**retrieve the url to navigate to for the OAuth 2.0 process */
|
|
49
|
+
getAuthUrl: GetAuthUrl
|
|
50
|
+
/**sets current credentials on top of default function*/
|
|
51
|
+
setCredentials?: (state: RemovableRef<any>, payload: any) => void
|
|
52
|
+
/**suboptions */
|
|
53
|
+
subscriptionOptions?: AuthSubscription[]
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface BTAuth {
|
|
57
|
+
authState: string
|
|
58
|
+
canEdit: (navName?: string) => boolean
|
|
59
|
+
canEditPermit: (permit: string) => boolean
|
|
60
|
+
canView: (navName?: string) => boolean
|
|
61
|
+
canViewPermit: (permit: string) => boolean
|
|
62
|
+
credentials: any
|
|
63
|
+
doShow: (subcodes?: string[], permissions?: string[], action?: 'view' | 'edit') => boolean
|
|
64
|
+
doShowByNav: (navName?: string | AuthItem, includeChildren?: boolean) => boolean
|
|
65
|
+
getAuthUrl: (redirectPath?: string) => string
|
|
66
|
+
getTimeZone: () => string
|
|
67
|
+
login: (redirectPath?: string) => void
|
|
68
|
+
logout: (navNameRedirect?: string) => void
|
|
69
|
+
setAuth: (jwtToken?: string) => void
|
|
70
|
+
tryLogin: () => boolean | undefined
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// /**returns current timezone */
|
|
74
|
+
// export function useTimeZone() {
|
|
75
|
+
// return state.value.timeZone ?? defaultTimeZone
|
|
76
|
+
// }
|
|
77
|
+
|
|
78
|
+
// export function useAuthData() {
|
|
79
|
+
// return state.value
|
|
80
|
+
// }
|
|
81
|
+
|
|
82
|
+
export function createAuth(options: CreateAuthOptions): BTAuth {
|
|
83
|
+
const router = useRouter()
|
|
84
|
+
const expiryFormat = options.expiryTokenFormat ?? 'd/MM/yyyy h:mm:ss a'
|
|
85
|
+
const authState = useStorage<string>('auth-credentials-state', (Math.random().toString(36).substring(4, 19) + Math.random().toString(12).substring(1, 11)))
|
|
86
|
+
const state = useStorage<BaseAuthCredentials>('auth-credentials', {})
|
|
87
|
+
|
|
88
|
+
// if (!defaultsSet) {
|
|
89
|
+
// setDefaults()
|
|
90
|
+
// defaultsSet = true
|
|
91
|
+
// }
|
|
92
|
+
|
|
93
|
+
// const { endDemo, isDemoing } = useDemo()
|
|
94
|
+
|
|
95
|
+
/**can edit if navName is undefined, isGlobalAdmin, not suspended, or user permissions allow editing of this navItem */
|
|
96
|
+
function canEdit(navName?: string): boolean {
|
|
97
|
+
if (navName == null) return true
|
|
98
|
+
|
|
99
|
+
var navItem = options.getAuthItem(navName);
|
|
100
|
+
if (navItem == null) return false
|
|
101
|
+
if (navItem.requiresAuth === false) return true
|
|
102
|
+
|
|
103
|
+
const mState = toValue(state)
|
|
104
|
+
if (mState.isSuspended && navItem.ignoreSuspension !== true) return false
|
|
105
|
+
if (mState.isGlobalAdmin) return true
|
|
106
|
+
|
|
107
|
+
if (navItem.permissions != null) {
|
|
108
|
+
for (var ii = 0; ii < navItem.permissions.length; ii++) {
|
|
109
|
+
if (!canEditPermit(navItem.permissions[ii])) {
|
|
110
|
+
return false
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**purely tests for whether this permit is in the user's permissions list.
|
|
119
|
+
* Or else the user just needs the 'everything.edit' permission.
|
|
120
|
+
*/
|
|
121
|
+
function canEditPermit(permit: string): boolean {
|
|
122
|
+
const mState = toValue(state)
|
|
123
|
+
|
|
124
|
+
if (mState.userPermissions != null) {
|
|
125
|
+
for (var i = 0; i < mState.userPermissions.length; i++) {
|
|
126
|
+
var parts = mState.userPermissions[i].split('.');
|
|
127
|
+
if (mState.userPermissions[i] == 'everything.edit' ||
|
|
128
|
+
permit == parts[0] && parts.length > 1 && parts[1] == 'edit') {
|
|
129
|
+
return true;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return false
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**can view if navName is undefined, isGlobalAdmin, not susepended, or user permissions allow viewing of this navItem */
|
|
138
|
+
function canView(navName: string | undefined): boolean {
|
|
139
|
+
if (navName == null) return true
|
|
140
|
+
|
|
141
|
+
var navItem = options.getAuthItem(navName);
|
|
142
|
+
if (navItem == null) return false
|
|
143
|
+
if (navItem.requiresAuth === false) return true
|
|
144
|
+
|
|
145
|
+
const mState = toValue(state)
|
|
146
|
+
if (mState.isSuspended && navItem.ignoreSuspension !== true) return false
|
|
147
|
+
if (mState.isGlobalAdmin) return true
|
|
148
|
+
|
|
149
|
+
if (navItem.permissions != null) {
|
|
150
|
+
for (var ii = 0; ii < navItem.permissions.length; ii++) {
|
|
151
|
+
if (!canViewPermit(navItem.permissions[ii])) {
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return true;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**purely tests for whether this permit is in the user's permissions list.
|
|
161
|
+
* Or else the user just needs the 'everything.view' permission or '[permit].edit'.
|
|
162
|
+
*/
|
|
163
|
+
function canViewPermit(permit: string): boolean {
|
|
164
|
+
const mState = toValue(state)
|
|
165
|
+
|
|
166
|
+
if (mState.userPermissions != null) {
|
|
167
|
+
for (var i = 0; i < mState.userPermissions.length; i++) {
|
|
168
|
+
var parts = mState.userPermissions[i].split('.');
|
|
169
|
+
if (permit == parts[0] || permit == 'everything' ||
|
|
170
|
+
mState.userPermissions[i] == 'everything.view' ||
|
|
171
|
+
mState.userPermissions[i] == 'everything.edit') {
|
|
172
|
+
return true;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return false;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**doesn't check for suspension
|
|
181
|
+
* if user
|
|
182
|
+
*/
|
|
183
|
+
function doShow(subcodes?: string[], permissions?: string[], action?: 'view' | 'edit'): boolean {
|
|
184
|
+
let doShowRes = true
|
|
185
|
+
|
|
186
|
+
const mState = toValue(state)
|
|
187
|
+
if (mState.isGlobalAdmin) return true
|
|
188
|
+
|
|
189
|
+
if (permissions != null && permissions.length > 0) {
|
|
190
|
+
permissions.forEach((permit: string) => {
|
|
191
|
+
if (action == 'edit') {
|
|
192
|
+
if (!canEditPermit(permit)) {
|
|
193
|
+
doShowRes = false
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
//otherwise it will be view
|
|
198
|
+
if (!canViewPermit(permit)) {
|
|
199
|
+
doShowRes = false
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
})
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (subcodes != null && subcodes.length > 0) {
|
|
206
|
+
if (!subcodes.some((subCode: string) => isWithinSubscription(subCode))) {
|
|
207
|
+
doShowRes = false
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return doShowRes
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function doShowByNav(navName?: string | AuthItem, includeChildren: boolean = false) {
|
|
215
|
+
var navItem = options.getAuthItem(navName);
|
|
216
|
+
let doShowRes = false;
|
|
217
|
+
|
|
218
|
+
if (navItem == null) return false
|
|
219
|
+
|
|
220
|
+
let subcodes = navItem.subscriptions ?? []
|
|
221
|
+
let permissions = navItem.permissions ?? []
|
|
222
|
+
let oRes = doShow(subcodes, permissions, 'view');
|
|
223
|
+
|
|
224
|
+
if (oRes || !includeChildren || !isLengthyArray(navItem.children))
|
|
225
|
+
return oRes
|
|
226
|
+
|
|
227
|
+
if (navItem?.children != null) {
|
|
228
|
+
navItem.children.forEach(child => {
|
|
229
|
+
if (doShowByNav(child, true)) {
|
|
230
|
+
doShowRes = true;
|
|
231
|
+
}
|
|
232
|
+
})
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return doShowRes;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function getAuthUrl(redirectPath?: string) {
|
|
239
|
+
return options.getAuthUrl(redirectPath, authState.value)
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**undefined or unfound is worth 0 */
|
|
243
|
+
function getSubscriptionCodeValue(subCode?: string) {
|
|
244
|
+
if (subCode == null) return 0
|
|
245
|
+
let subList = options.subscriptionOptions ?? []
|
|
246
|
+
return subList.find(x => x.code == subCode)?.value ?? 0
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**identifies whether user's subscription level allows for this subCode */
|
|
250
|
+
function isWithinSubscription(subCode?: string) {
|
|
251
|
+
const mState = toValue(state)
|
|
252
|
+
|
|
253
|
+
if (mState.subscriptionCode == null) return false
|
|
254
|
+
if (mState.subscriptionCode == subCode) return true
|
|
255
|
+
|
|
256
|
+
const subLevelNeeded = getSubscriptionCodeValue(subCode)
|
|
257
|
+
const subCompanyLevel = getSubscriptionCodeValue(mState.subscriptionCode)
|
|
258
|
+
|
|
259
|
+
return subLevelNeeded <= subCompanyLevel
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**decrypts jwt token */
|
|
263
|
+
function jwtDecrypt(token: string) {
|
|
264
|
+
const base64Url = token.split('.')[1];
|
|
265
|
+
const base64 = base64Url.replace(/-/g, "+").replace(/_/g, "/");
|
|
266
|
+
const jsonPayload = decodeURIComponent(
|
|
267
|
+
atob(base64)
|
|
268
|
+
.split("")
|
|
269
|
+
.map(function(c) {
|
|
270
|
+
return "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2);
|
|
271
|
+
})
|
|
272
|
+
.join("")
|
|
273
|
+
);
|
|
274
|
+
|
|
275
|
+
return JSON.parse(jsonPayload);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**clears credentials and potentially redirects */
|
|
279
|
+
function logout(navNameRedirect?: string) {
|
|
280
|
+
state.value = undefined
|
|
281
|
+
|
|
282
|
+
//ensure time zone still exists
|
|
283
|
+
setDefaults()
|
|
284
|
+
|
|
285
|
+
//clear stores
|
|
286
|
+
|
|
287
|
+
//maybe redirect
|
|
288
|
+
if (navNameRedirect != null) {
|
|
289
|
+
if (router.currentRoute.value.name != navNameRedirect) {
|
|
290
|
+
router.push({ name: navNameRedirect })
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**Redirects to OAuth 2.0 process if not yet logged in and token expired. Ends demo. */
|
|
296
|
+
function login(redirectPath?: string) {
|
|
297
|
+
const mState = toValue(state)
|
|
298
|
+
|
|
299
|
+
if (!mState.isLoggedIn) {
|
|
300
|
+
options.demo?.endDemo()
|
|
301
|
+
if (tokenExpired()) {
|
|
302
|
+
logout()
|
|
303
|
+
window.location.href = getAuthUrl(redirectPath) //}response_type=code&client_id=appClient1&redirect_path=${redirectPath}` : getAuthUrl()
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**if not demoing, then will decrypt the jwtToken and attempt to set credentials */
|
|
309
|
+
function setAuth(jwtToken?: string) {
|
|
310
|
+
if (jwtToken == null) return
|
|
311
|
+
|
|
312
|
+
if (options.demo == null || !options.demo.isDemoing.value) {
|
|
313
|
+
const d = jwtDecrypt(jwtToken)
|
|
314
|
+
|
|
315
|
+
setDefaultAuth(d)
|
|
316
|
+
|
|
317
|
+
if (options.setCredentials != null) {
|
|
318
|
+
options.setCredentials(state as RemovableRef<any>, d)
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function setDefaultAuth(d: any) {
|
|
324
|
+
const v = state.value
|
|
325
|
+
v.expiresOn = d.ExpiresOn
|
|
326
|
+
v.isGlobalAdmin = d.IsGlobalAdmin == 'True'
|
|
327
|
+
v.isLoggedIn = true
|
|
328
|
+
v.permissions = d.Permissions
|
|
329
|
+
v.subscriptionCode = d.Subscription
|
|
330
|
+
// v.subscriptionsInUse = d.SubscriptionsInUse
|
|
331
|
+
v.token = d
|
|
332
|
+
v.userID = d.UserLoginID
|
|
333
|
+
v.userPermissions = d.Permissions?.split(',') ?? []
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function setDefaults() {
|
|
337
|
+
state.value.timeZone ??= (options.defaultTimeZone ?? defaultTimeZone)
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function getTimeZone() {
|
|
341
|
+
return state.value.timeZone ?? options.defaultTimeZone ?? defaultTimeZone
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**returns whether or not logged in. Potentially redirects to OAauth 2.0 process if logged in and expired */
|
|
345
|
+
function tryLogin() {
|
|
346
|
+
const mState = toValue(state)
|
|
347
|
+
|
|
348
|
+
//what if no internet?
|
|
349
|
+
if (mState.isLoggedIn && tokenExpired()) {
|
|
350
|
+
logout()
|
|
351
|
+
window.location.href = getAuthUrl()
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
return mState.isLoggedIn
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
function tokenExpired() {
|
|
358
|
+
const mState = toValue(state)
|
|
359
|
+
if (mState == null || mState.expiresOn == null) return true
|
|
360
|
+
|
|
361
|
+
const e1 = DateTime.fromFormat(mState.expiresOn, expiryFormat)
|
|
362
|
+
const e2 = DateTime.utc()
|
|
363
|
+
return e1 <= e2
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
return {
|
|
367
|
+
authState: authState.value,
|
|
368
|
+
canEdit,
|
|
369
|
+
canEditPermit,
|
|
370
|
+
canView,
|
|
371
|
+
canViewPermit,
|
|
372
|
+
credentials: state.value,
|
|
373
|
+
doShow,
|
|
374
|
+
doShowByNav,
|
|
375
|
+
getAuthUrl,
|
|
376
|
+
getTimeZone,
|
|
377
|
+
login,
|
|
378
|
+
logout,
|
|
379
|
+
setAuth,
|
|
380
|
+
tryLogin
|
|
381
|
+
}
|
|
382
|
+
}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { useStorage } from '@vueuse/core'
|
|
2
|
+
import { useTheme } from 'vuetify';
|
|
3
|
+
|
|
4
|
+
interface CosmeticData {
|
|
5
|
+
dark?: BaseCosmeticTheme,
|
|
6
|
+
drawer?: boolean,
|
|
7
|
+
drawerStick?: boolean,
|
|
8
|
+
light?: BaseCosmeticTheme,
|
|
9
|
+
theme?: string
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface BaseCosmeticTheme {
|
|
13
|
+
primary: string,
|
|
14
|
+
secondary: string,
|
|
15
|
+
accent: string,
|
|
16
|
+
error: string,
|
|
17
|
+
info: string,
|
|
18
|
+
success: string,
|
|
19
|
+
warning: string,
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const defaultLight: BaseCosmeticTheme = {
|
|
23
|
+
primary: '#192233',
|
|
24
|
+
secondary: '#192233',
|
|
25
|
+
accent: '#2a76a2',
|
|
26
|
+
error: '#FF5252',
|
|
27
|
+
info: '#2196F3',
|
|
28
|
+
success: '#4CAF50',
|
|
29
|
+
warning: '#FB8C00'
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const defaultDark: BaseCosmeticTheme = {
|
|
33
|
+
primary: '#1d5474',
|
|
34
|
+
secondary: '#192233',
|
|
35
|
+
accent: '#7fcbf7',
|
|
36
|
+
error: '#FF5252',
|
|
37
|
+
info: '#2196F3',
|
|
38
|
+
success: '#4CAF50',
|
|
39
|
+
warning: '#FB8C00'
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface UseCosmeticsOptions<T extends BaseCosmeticTheme> {
|
|
43
|
+
defaultDarkTheme?: T
|
|
44
|
+
defaultLightTheme?: T
|
|
45
|
+
defaultDrawer?: boolean
|
|
46
|
+
defaultDrawerStick?: boolean
|
|
47
|
+
defaultTheme?: string
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface BTCosmetics {
|
|
51
|
+
state: CosmeticData
|
|
52
|
+
resetCosmetics: (toDefault: boolean) => void
|
|
53
|
+
setTemporaryColor: (color: string) => void
|
|
54
|
+
toggleDrawer: () => void
|
|
55
|
+
toggleDrawerStick: () => void
|
|
56
|
+
toggleLightDark: () => void
|
|
57
|
+
undoTemporaryColor: () => void
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function createCosmetics<T extends BaseCosmeticTheme>(options: UseCosmeticsOptions<T>): BTCosmetics {
|
|
61
|
+
const themeManager = useTheme();
|
|
62
|
+
const state = useStorage<CosmeticData>('cosmetics', {
|
|
63
|
+
theme: options.defaultTheme ?? 'light',
|
|
64
|
+
light: options.defaultLightTheme ?? defaultLight,
|
|
65
|
+
dark: options.defaultDarkTheme ?? defaultDark,
|
|
66
|
+
drawer: options.defaultDrawer ?? true,
|
|
67
|
+
drawerStick: options.defaultDrawerStick ?? false
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
// if (!defaultsSet) {
|
|
71
|
+
// state.value.theme ??= (options.defaultTheme ?? 'light')
|
|
72
|
+
// state.value.light ??= (options.defaultLightTheme ?? defaultLight)
|
|
73
|
+
// state.value.dark ??= (options.defaultDarkTheme ?? defaultDark)
|
|
74
|
+
// state.value.drawer ??= (options.defaultDrawer ?? true)
|
|
75
|
+
// state.value.drawerStick ??= (options.defaultDrawerStick ?? false)
|
|
76
|
+
// defaultsSet = true
|
|
77
|
+
// }
|
|
78
|
+
|
|
79
|
+
/**used for initiating the themes and colors from storage when the web app is loaded */
|
|
80
|
+
// function loadCosmetics() {
|
|
81
|
+
themeManager.global.name.value = state.value.theme!
|
|
82
|
+
|
|
83
|
+
//load dark colors
|
|
84
|
+
let darkColors = themeManager.themes.value.dark.colors
|
|
85
|
+
let storedDarkTheme = state.value.dark!
|
|
86
|
+
const darkThemeKeys = Object.keys(storedDarkTheme)
|
|
87
|
+
darkThemeKeys.forEach(darkKey => {
|
|
88
|
+
const k = darkKey as keyof typeof storedDarkTheme
|
|
89
|
+
darkColors[darkKey] = storedDarkTheme[k]
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
let lightColors = themeManager.themes.value.light.colors
|
|
93
|
+
let storedLightTheme = state.value.light!
|
|
94
|
+
const lightThemeKeys = Object.keys(storedLightTheme)
|
|
95
|
+
lightThemeKeys.forEach(lightKey => {
|
|
96
|
+
const lKey = lightKey as keyof typeof storedLightTheme
|
|
97
|
+
lightColors[lKey] = storedLightTheme[lKey]
|
|
98
|
+
})
|
|
99
|
+
// }
|
|
100
|
+
|
|
101
|
+
/**resets to the default colors or last saved colors of the current theme */
|
|
102
|
+
function resetCosmetics(toDefault: boolean) {
|
|
103
|
+
|
|
104
|
+
themeManager.global.name.value = state.value.theme!
|
|
105
|
+
|
|
106
|
+
if (themeManager.global.name.value == 'dark') {
|
|
107
|
+
//dark
|
|
108
|
+
if (toDefault)
|
|
109
|
+
state.value.dark = { ...(options.defaultDarkTheme ?? defaultDark) };
|
|
110
|
+
|
|
111
|
+
themeManager.themes.value.dark = {
|
|
112
|
+
...themeManager.themes.value.dark,
|
|
113
|
+
colors: {
|
|
114
|
+
...themeManager.themes.value.dark.colors,
|
|
115
|
+
...state.value.dark
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
//light
|
|
121
|
+
if (toDefault)
|
|
122
|
+
state.value.light = { ...(options.defaultLightTheme ?? defaultLight) };
|
|
123
|
+
|
|
124
|
+
themeManager.themes.value.light = {
|
|
125
|
+
...themeManager.themes.value.light,
|
|
126
|
+
colors: {
|
|
127
|
+
...themeManager.themes.value.light.colors,
|
|
128
|
+
...state.value.light
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**sets a temporary primary color that is not saved but is applied to cosmetics */
|
|
135
|
+
function setTemporaryColor(color: string) {
|
|
136
|
+
const dark = themeManager.themes.value.dark
|
|
137
|
+
const light = themeManager.themes.value.light
|
|
138
|
+
|
|
139
|
+
dark.colors.primary = color
|
|
140
|
+
light.colors.primary = color
|
|
141
|
+
|
|
142
|
+
document.querySelector('meta[name="theme-color"]')?.setAttribute("content", color)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function toggleDrawer() {
|
|
146
|
+
state.value.drawer = !state.value.drawer
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function toggleDrawerStick() {
|
|
150
|
+
state.value.drawerStick = !state.value.drawerStick
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function toggleLightDark() {
|
|
154
|
+
state.value.theme = state.value.theme == 'dark' ? 'light' : 'dark'
|
|
155
|
+
themeManager.global.name.value = state.value.theme
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function undoTemporaryColor() {
|
|
159
|
+
const dark = themeManager.themes.value.dark
|
|
160
|
+
const light = themeManager.themes.value.light
|
|
161
|
+
|
|
162
|
+
dark.colors.primary = (state.value.dark ?? options.defaultDarkTheme ?? defaultDark).primary
|
|
163
|
+
light.colors.primary = (state.value.light ?? options.defaultLightTheme ?? defaultLight).primary
|
|
164
|
+
|
|
165
|
+
document.querySelector('meta[name="theme-color"]')?.setAttribute("content", '')
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return {
|
|
169
|
+
state: state.value,
|
|
170
|
+
// loadCosmetics,
|
|
171
|
+
resetCosmetics,
|
|
172
|
+
setTemporaryColor,
|
|
173
|
+
toggleDrawer,
|
|
174
|
+
toggleDrawerStick,
|
|
175
|
+
toggleLightDark,
|
|
176
|
+
undoTemporaryColor
|
|
177
|
+
}
|
|
178
|
+
}
|