@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.
@@ -1 +1,2 @@
1
1
  export { useSprunjer } from './sprunjer'
2
+ export { usePageMeta } from './usePageMeta'
@@ -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: any,
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<any>({})
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.value = response.data
73
- loading.value = false
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
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Interfaces - What the API expects and what it returns
3
+ *
4
+ * Generic API Response interface.
5
+ */
6
+ export interface ApiResponse {
7
+ message: string
8
+ }
@@ -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: any
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<any>
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
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@userfrosting/sprinkle-core",
3
- "version": "6.0.0-alpha.1",
3
+ "version": "6.0.0-alpha.2",
4
4
  "type": "module",
5
5
  "description": "Core Sprinkle for UserFrosting",
6
6
  "funding": "https://opencollective.com/userfrosting",