@userfrosting/sprinkle-core 6.0.0-alpha.1 → 6.0.0-alpha.2
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/app/assets/composables/index.ts +1 -0
- package/app/assets/composables/sprunjer.ts +24 -7
- package/app/assets/composables/usePageMeta.ts +112 -0
- package/app/assets/interfaces/ApiResponse.ts +8 -0
- package/app/assets/interfaces/index.ts +26 -1
- package/app/assets/interfaces/sprunjer.ts +35 -2
- package/app/assets/interfaces/sprunjerApi.ts +33 -0
- package/package.json +1 -1
|
@@ -33,10 +33,10 @@
|
|
|
33
33
|
*/
|
|
34
34
|
import { ref, toValue, watchEffect, computed } from 'vue'
|
|
35
35
|
import axios from 'axios'
|
|
36
|
-
import type { AssociativeArray, Sprunjer } from '../interfaces'
|
|
36
|
+
import type { AssociativeArray, Sprunjer, SprunjerData, SprunjerResponse } from '../interfaces'
|
|
37
37
|
|
|
38
38
|
export const useSprunjer = (
|
|
39
|
-
dataUrl:
|
|
39
|
+
dataUrl: string | (() => string),
|
|
40
40
|
defaultSorts: AssociativeArray = {},
|
|
41
41
|
defaultFilters: AssociativeArray = {},
|
|
42
42
|
defaultSize: number = 10,
|
|
@@ -48,8 +48,15 @@ export const useSprunjer = (
|
|
|
48
48
|
const sorts = ref<AssociativeArray>(defaultSorts)
|
|
49
49
|
const filters = ref<AssociativeArray>(defaultFilters)
|
|
50
50
|
|
|
51
|
-
// Raw data
|
|
52
|
-
const data = ref<
|
|
51
|
+
// Raw data - Init with default data
|
|
52
|
+
const data = ref<SprunjerData>({
|
|
53
|
+
count: 0,
|
|
54
|
+
count_filtered: 0,
|
|
55
|
+
rows: [],
|
|
56
|
+
listable: {},
|
|
57
|
+
sortable: [],
|
|
58
|
+
filterable: []
|
|
59
|
+
})
|
|
53
60
|
|
|
54
61
|
// State
|
|
55
62
|
const loading = ref<boolean>(false)
|
|
@@ -60,7 +67,7 @@ export const useSprunjer = (
|
|
|
60
67
|
async function fetch() {
|
|
61
68
|
loading.value = true
|
|
62
69
|
axios
|
|
63
|
-
.get(toValue(dataUrl), {
|
|
70
|
+
.get<SprunjerResponse>(toValue(dataUrl), {
|
|
64
71
|
params: {
|
|
65
72
|
size: size.value,
|
|
66
73
|
page: page.value,
|
|
@@ -69,13 +76,23 @@ export const useSprunjer = (
|
|
|
69
76
|
}
|
|
70
77
|
})
|
|
71
78
|
.then((response) => {
|
|
72
|
-
data
|
|
73
|
-
|
|
79
|
+
// Assign the response data to the Sprunje Data.
|
|
80
|
+
// Note both object can't be assigned directly, as the response
|
|
81
|
+
// object is not a SprunjeData object.
|
|
82
|
+
data.value.count = response.data.count
|
|
83
|
+
data.value.count_filtered = response.data.count_filtered
|
|
84
|
+
data.value.rows = response.data.rows
|
|
85
|
+
data.value.listable = response.data.listable ?? {}
|
|
86
|
+
data.value.sortable = response.data.sortable ?? []
|
|
87
|
+
data.value.filterable = response.data.filterable ?? []
|
|
74
88
|
})
|
|
75
89
|
.catch((err) => {
|
|
76
90
|
// TODO : User toast alert, or export alert
|
|
77
91
|
console.error(err)
|
|
78
92
|
})
|
|
93
|
+
.finally(() => {
|
|
94
|
+
loading.value = false
|
|
95
|
+
})
|
|
79
96
|
}
|
|
80
97
|
|
|
81
98
|
/**
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { computed, ref, watch } from 'vue'
|
|
2
|
+
import { useRoute } from 'vue-router'
|
|
3
|
+
import { useConfigStore } from '../stores'
|
|
4
|
+
import { defineStore } from 'pinia'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Page Meta Composable
|
|
8
|
+
*
|
|
9
|
+
* Handles the page meta data such as title, description, plus generate
|
|
10
|
+
* breadcrumbs from the frontend router. The title, description and breadcrumbs
|
|
11
|
+
* are updated automatically when the route changes.
|
|
12
|
+
*
|
|
13
|
+
* Available States : breadcrumbs, title, description
|
|
14
|
+
*/
|
|
15
|
+
export const usePageMeta = defineStore('pageMeta', () => {
|
|
16
|
+
/**
|
|
17
|
+
* Globally provided properties
|
|
18
|
+
*/
|
|
19
|
+
const route = useRoute()
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* States
|
|
23
|
+
*
|
|
24
|
+
* - title: The current page title
|
|
25
|
+
* - description: The current page description
|
|
26
|
+
* - breadcrumbs: The current page breadcrumbs
|
|
27
|
+
* - hideBreadcrumbs: Ask the component to hide the breadcrumbs on the page
|
|
28
|
+
* - hideTitle: Ask the component to hide the title on the page
|
|
29
|
+
*/
|
|
30
|
+
const title = ref<string>('')
|
|
31
|
+
const description = ref<string>('')
|
|
32
|
+
const breadcrumbs = ref<Breadcrumb[]>([])
|
|
33
|
+
const hideBreadcrumbs = ref<boolean>(false)
|
|
34
|
+
const hideTitle = ref<boolean>(false)
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Actions - Refresh the breadcrumbs, title and description
|
|
38
|
+
*/
|
|
39
|
+
function refresh() {
|
|
40
|
+
// Reset default visibility attributes
|
|
41
|
+
hideBreadcrumbs.value = false
|
|
42
|
+
hideTitle.value = false
|
|
43
|
+
|
|
44
|
+
// Get route trail
|
|
45
|
+
const matchedRoutes = route.matched
|
|
46
|
+
|
|
47
|
+
// Filter to remove routes without title and assign values as defined
|
|
48
|
+
// in the breadcrumbs interface.
|
|
49
|
+
const crumbs = matchedRoutes
|
|
50
|
+
.filter(
|
|
51
|
+
(routeItem) => routeItem.meta.title !== undefined && routeItem.meta.title !== ''
|
|
52
|
+
)
|
|
53
|
+
.map((routeItem) => {
|
|
54
|
+
return {
|
|
55
|
+
label: routeItem.meta?.title || '',
|
|
56
|
+
to: routeItem.path
|
|
57
|
+
}
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
// Add site title as first breadcrumb
|
|
61
|
+
crumbs.unshift({
|
|
62
|
+
label: siteTitle.value,
|
|
63
|
+
to: '/'
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
// Replace ref with new values
|
|
67
|
+
breadcrumbs.value = crumbs
|
|
68
|
+
|
|
69
|
+
// Update Page Title & Description with current route
|
|
70
|
+
title.value = route.meta.title || ''
|
|
71
|
+
description.value = route.meta.description || ''
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Update the document title
|
|
75
|
+
function updatePageTitle() {
|
|
76
|
+
document.title = pageFullTitle.value
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Update the document description in the HTML meta tag
|
|
80
|
+
function updatePageDescription() {
|
|
81
|
+
const descriptionElement = document.querySelector('head meta[name="description"]')
|
|
82
|
+
descriptionElement?.setAttribute('content', description.value)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Computed Properties - Getters
|
|
87
|
+
*
|
|
88
|
+
* - siteTitle: Return the site title from the config Store
|
|
89
|
+
* - pageFullTitle: Return the full page title
|
|
90
|
+
*/
|
|
91
|
+
const siteTitle = computed<string>(() => useConfigStore().get('site.title') || '')
|
|
92
|
+
const pageFullTitle = computed<string>(() => {
|
|
93
|
+
return title.value ? title.value + ' | ' + siteTitle.value : siteTitle.value
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Watchers - route, page title and description changes
|
|
98
|
+
*/
|
|
99
|
+
watch(route, refresh, { immediate: true })
|
|
100
|
+
watch(pageFullTitle, updatePageTitle, { immediate: true })
|
|
101
|
+
watch(description, updatePageDescription, { immediate: true })
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Returns the states and actions
|
|
105
|
+
*/
|
|
106
|
+
return { breadcrumbs, title, description, hideBreadcrumbs, hideTitle }
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
interface Breadcrumb {
|
|
110
|
+
label: string
|
|
111
|
+
to: string
|
|
112
|
+
}
|
|
@@ -1,4 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interface for custom routes meta fields.
|
|
3
|
+
*
|
|
4
|
+
* Meta Fields Added :
|
|
5
|
+
* - title: string - Page title
|
|
6
|
+
* - description: string - Page description
|
|
7
|
+
*
|
|
8
|
+
* Theses fields are used to set the document title and description, as well as
|
|
9
|
+
* for breadcrumbs generation.
|
|
10
|
+
*
|
|
11
|
+
* @see https://router.vuejs.org/guide/advanced/meta.html#TypeScript
|
|
12
|
+
*/
|
|
13
|
+
import 'vue-router'
|
|
14
|
+
|
|
15
|
+
declare module 'vue-router' {
|
|
16
|
+
interface RouteMeta {
|
|
17
|
+
title?: string
|
|
18
|
+
description?: string
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
1
22
|
export type { AlertInterface } from './alerts'
|
|
2
23
|
export type { AssociativeArray } from './common'
|
|
3
24
|
export { Severity } from './severity'
|
|
4
|
-
export type { Sprunjer } from './sprunjer'
|
|
25
|
+
export type { Sprunjer, SprunjerData, SprunjerListable, SprunjerListableOption } from './sprunjer'
|
|
26
|
+
export type { SprunjerRequest, SprunjerResponse } from './sprunjerApi'
|
|
27
|
+
|
|
28
|
+
// Misc
|
|
29
|
+
export type { ApiResponse } from './ApiResponse'
|
|
@@ -7,12 +7,12 @@ import type { Ref, ComputedRef } from 'vue'
|
|
|
7
7
|
import type { AssociativeArray } from '.'
|
|
8
8
|
|
|
9
9
|
export interface Sprunjer {
|
|
10
|
-
dataUrl:
|
|
10
|
+
dataUrl: string | (() => string)
|
|
11
11
|
size: Ref<number>
|
|
12
12
|
page: Ref<number>
|
|
13
13
|
sorts: Ref<AssociativeArray>
|
|
14
14
|
filters: Ref<AssociativeArray>
|
|
15
|
-
data: Ref<
|
|
15
|
+
data: Ref<SprunjerData>
|
|
16
16
|
fetch: () => void
|
|
17
17
|
loading: Ref<boolean>
|
|
18
18
|
totalPages: ComputedRef<number>
|
|
@@ -24,3 +24,36 @@ export interface Sprunjer {
|
|
|
24
24
|
last: ComputedRef<number>
|
|
25
25
|
toggleSort: (column: string) => void
|
|
26
26
|
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Sprunjer Data. Represents the data that is returned from any Sprunjer
|
|
30
|
+
* Composable. It is different than SprunjerResponse, as the response if what
|
|
31
|
+
* the API return, Data is what Vue provides. Both are similar, but Data doesn't
|
|
32
|
+
* have optional values.
|
|
33
|
+
*
|
|
34
|
+
* N.B.: "rows" uses a generic array. It can contain any object, and should
|
|
35
|
+
* actually be can be extended for each Sprunjer
|
|
36
|
+
*/
|
|
37
|
+
export interface SprunjerData {
|
|
38
|
+
count: number
|
|
39
|
+
count_filtered: number
|
|
40
|
+
rows: any[]
|
|
41
|
+
listable: SprunjerListable
|
|
42
|
+
sortable: string[]
|
|
43
|
+
filterable: string[]
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Sprunjer Listable. Represents a listable for a Sprunjer.
|
|
48
|
+
*/
|
|
49
|
+
export interface SprunjerListable {
|
|
50
|
+
[key: string]: SprunjerListableOption[]
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Sprunjer Listable Option. Represents a listable option for a Sprunjer.
|
|
55
|
+
*/
|
|
56
|
+
export interface SprunjerListableOption {
|
|
57
|
+
value: string
|
|
58
|
+
text: string
|
|
59
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { AssociativeArray, SprunjerListable } from '.'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Sprunje API Interfaces - What the API expects and what it returns.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Sprunjer Response. All the data that is returned from any Sprunjer API.
|
|
9
|
+
* Note listable, sortable and filterable are optional when dealing with the API.
|
|
10
|
+
*
|
|
11
|
+
* N.B.: "rows" uses a generic array. It can contain any object, and should
|
|
12
|
+
* actually be can be extended for each Sprunjer
|
|
13
|
+
*/
|
|
14
|
+
export interface SprunjerResponse {
|
|
15
|
+
count: number
|
|
16
|
+
count_filtered: number
|
|
17
|
+
rows: any[]
|
|
18
|
+
listable?: SprunjerListable
|
|
19
|
+
sortable?: string[]
|
|
20
|
+
filterable?: string[]
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Sprunjer Request. All the parameters that can be passed to a Sprunjer.
|
|
25
|
+
* All parameters are optional.
|
|
26
|
+
*/
|
|
27
|
+
export interface SprunjerRequest {
|
|
28
|
+
size?: number
|
|
29
|
+
page?: number
|
|
30
|
+
sorts?: AssociativeArray
|
|
31
|
+
filters?: AssociativeArray
|
|
32
|
+
format?: string
|
|
33
|
+
}
|