@paris-ias/list 1.0.4 → 1.0.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/dist/module.ts +119 -0
- package/dist/runtime/components/events/Badges.vue +73 -0
- package/dist/runtime/components/events/DateTimePlace.vue +77 -0
- package/dist/runtime/components/events/DenseItem.vue +40 -0
- package/dist/runtime/components/events/ExpandedItem.vue +11 -0
- package/dist/runtime/components/events/ListContainer.vue +41 -0
- package/dist/runtime/components/events/RegisterModal.vue +51 -0
- package/dist/runtime/components/events/RelatedItem.vue +44 -0
- package/dist/runtime/components/events/RowsItem.vue +114 -0
- package/dist/runtime/components/events/View.vue +333 -0
- package/dist/runtime/components/fellowships/Badges.vue +48 -0
- package/dist/runtime/components/fellowships/DenseItem.vue +39 -0
- package/dist/runtime/components/fellowships/ExpandedItem.vue +7 -0
- package/dist/runtime/components/fellowships/RegisterModal.vue +41 -0
- package/dist/runtime/components/fellowships/RowsItem.vue +61 -0
- package/dist/runtime/components/fellowships/View.vue +210 -0
- package/dist/runtime/components/list/atoms/FiltersMenu.vue +46 -0
- package/dist/runtime/components/list/atoms/SearchInput.vue +129 -0
- package/dist/runtime/components/list/atoms/SearchItem.vue +59 -0
- package/dist/runtime/components/list/atoms/SearchString.vue +161 -0
- package/dist/runtime/components/list/atoms/SortMenu.vue +97 -0
- package/dist/runtime/components/list/atoms/ViewMenu.vue +71 -0
- package/dist/runtime/components/list/inputs/AutoComplete.vue +22 -0
- package/dist/runtime/components/list/inputs/BooleanSwitch.vue +18 -0
- package/dist/runtime/components/list/inputs/Checkbox.vue +21 -0
- package/dist/runtime/components/list/inputs/Select.vue +25 -0
- package/dist/runtime/components/list/molecules/Filters.vue +97 -0
- package/dist/runtime/components/list/molecules/Header.vue +47 -0
- package/dist/runtime/components/list/molecules/Pagination.vue +243 -0
- package/dist/runtime/components/list/organisms/List.vue +92 -0
- package/dist/runtime/components/list/views/Dense.vue +25 -0
- package/dist/runtime/components/list/views/Expanded.vue +10 -0
- package/dist/runtime/components/list/views/Grid.vue +13 -0
- package/dist/runtime/components/list/views/Rows.vue +13 -0
- package/dist/runtime/components/list/views/Table.vue +13 -0
- package/dist/runtime/components/misc/atoms/CountUp.vue +198 -0
- package/dist/runtime/components/misc/atoms/DateStamp.vue +104 -0
- package/dist/runtime/components/misc/atoms/ImageContainer.vue +105 -0
- package/dist/runtime/components/misc/atoms/ShareMenu.vue +60 -0
- package/dist/runtime/components/misc/atoms/Socials.vue +127 -0
- package/dist/runtime/components/misc/molecules/ChipContainer.vue +35 -0
- package/dist/runtime/components/misc/molecules/Related.vue +41 -0
- package/dist/runtime/components/misc/molecules/RelatedItems.vue +29 -0
- package/dist/runtime/components/misc/molecules/SearchItem.vue +26 -0
- package/dist/runtime/components/news/DenseItem.vue +62 -0
- package/dist/runtime/components/news/ExpandedItem.vue +153 -0
- package/dist/runtime/components/news/Header.vue +9 -0
- package/dist/runtime/components/news/RelatedItem.vue +44 -0
- package/dist/runtime/components/news/RowsItem.vue +160 -0
- package/dist/runtime/components/news/View.vue +190 -0
- package/dist/runtime/components/people/DenseItem.vue +37 -0
- package/dist/runtime/components/people/ExpandedItem.vue +16 -0
- package/dist/runtime/components/people/GroupBadges.vue +56 -0
- package/dist/runtime/components/people/RelatedItem.vue +41 -0
- package/dist/runtime/components/people/RowsItem.vue +95 -0
- package/dist/runtime/components/people/View.vue +162 -0
- package/dist/runtime/components/projects/ExpandedItem.vue +14 -0
- package/dist/runtime/components/projects/RelatedItem.vue +44 -0
- package/dist/runtime/components/projects/RowsItem.vue +106 -0
- package/dist/runtime/components/projects/View.vue +131 -0
- package/dist/runtime/components/publications/RelatedItem.vue +44 -0
- package/dist/runtime/components/publications/RowsItem.vue +105 -0
- package/dist/runtime/components/publications/View.vue +139 -0
- package/dist/runtime/composables/useFetchItem.ts +64 -0
- package/dist/runtime/composables/useIcons.ts +30 -0
- package/dist/runtime/composables/useUtils.ts +75 -0
- package/dist/runtime/graphql/queries/buildFiltersValues.gql +35 -0
- package/dist/runtime/graphql/queries/item/action.gql +0 -0
- package/dist/runtime/graphql/queries/item/apps.gql +0 -0
- package/dist/runtime/graphql/queries/item/events.gql +120 -0
- package/dist/runtime/graphql/queries/item/fellowships.gql +164 -0
- package/dist/runtime/graphql/queries/item/news.gql +129 -0
- package/dist/runtime/graphql/queries/item/people.gql +174 -0
- package/dist/runtime/graphql/queries/item/projects.gql +171 -0
- package/dist/runtime/graphql/queries/item/publications.gql +169 -0
- package/dist/runtime/graphql/queries/item/users.gql +0 -0
- package/dist/runtime/graphql/queries/list/action.gql +0 -0
- package/dist/runtime/graphql/queries/list/apps.gql +32 -0
- package/dist/runtime/graphql/queries/list/events.gql +44 -0
- package/dist/runtime/graphql/queries/list/fellowships.gql +53 -0
- package/dist/runtime/graphql/queries/list/news.gql +39 -0
- package/dist/runtime/graphql/queries/list/people.gql +49 -0
- package/dist/runtime/graphql/queries/list/projects.gql +37 -0
- package/dist/runtime/graphql/queries/list/publications.gql +37 -0
- package/dist/runtime/graphql/queries/list/search.gql +148 -0
- package/dist/runtime/graphql/queries/list/users.gql +32 -0
- package/dist/runtime/graphql/queries/login.gql +0 -0
- package/dist/runtime/plugins/pinia.ts +88 -0
- package/dist/runtime/plugins/vuetify.js +21 -0
- package/dist/runtime/stores/factory.ts +18 -0
- package/dist/runtime/stores/root.ts +353 -0
- package/dist/runtime/translations/en.json +436 -0
- package/dist/runtime/translations/fr.json +429 -0
- package/dist/runtime/types/imports.d.ts +13 -0
- package/dist/runtime/types/stores.d.ts +11 -0
- package/example/.env.example +3 -0
- package/example/nuxt.config.ts +19 -0
- package/example/pages/index.vue +27 -0
- package/package.json +8 -17
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-menu>
|
|
3
|
+
<template #activator="{ props: menu }">
|
|
4
|
+
<v-tooltip location="top">
|
|
5
|
+
<template #activator="{ props: tooltip }">
|
|
6
|
+
<v-btn
|
|
7
|
+
x-large
|
|
8
|
+
tile
|
|
9
|
+
flat
|
|
10
|
+
:icon="'mdi-' + current?.icon || defaultView?.icon"
|
|
11
|
+
:class="{
|
|
12
|
+
'mt-3': isXsDisplay,
|
|
13
|
+
}"
|
|
14
|
+
v-bind="mergeProps(menu, tooltip)"
|
|
15
|
+
/>
|
|
16
|
+
</template>
|
|
17
|
+
<div
|
|
18
|
+
v-html="
|
|
19
|
+
$t('list.view-mode') +
|
|
20
|
+
$t('list.' + current.name || defaultView.name)
|
|
21
|
+
"
|
|
22
|
+
/>
|
|
23
|
+
</v-tooltip>
|
|
24
|
+
</template>
|
|
25
|
+
<v-list density="compact">
|
|
26
|
+
<v-list-item
|
|
27
|
+
v-for="(value, key, index) in items"
|
|
28
|
+
:key="index"
|
|
29
|
+
@click="updateView(value.name || key)"
|
|
30
|
+
>
|
|
31
|
+
<template #prepend>
|
|
32
|
+
<v-icon>mdi-{{ value.icon }}</v-icon>
|
|
33
|
+
</template>
|
|
34
|
+
<v-list-item-title>{{
|
|
35
|
+
$t("list." + (value.name || key))
|
|
36
|
+
}}</v-list-item-title>
|
|
37
|
+
</v-list-item>
|
|
38
|
+
</v-list>
|
|
39
|
+
</v-menu>
|
|
40
|
+
</template>
|
|
41
|
+
|
|
42
|
+
<script setup>
|
|
43
|
+
import { useRootStore } from "../../../stores/root"
|
|
44
|
+
import { mergeProps } from "vue"
|
|
45
|
+
import { useDisplay } from "vuetify"
|
|
46
|
+
const { $stores } = useNuxtApp()
|
|
47
|
+
|
|
48
|
+
const props = defineProps({
|
|
49
|
+
type: {
|
|
50
|
+
type: String,
|
|
51
|
+
default: "articles",
|
|
52
|
+
required: true,
|
|
53
|
+
},
|
|
54
|
+
})
|
|
55
|
+
const { xs: isXsDisplay } = useDisplay()
|
|
56
|
+
|
|
57
|
+
const rootStore = useRootStore()
|
|
58
|
+
const items = ref($stores[props.type].views)
|
|
59
|
+
|
|
60
|
+
const current = ref($stores[props.type].view)
|
|
61
|
+
|
|
62
|
+
const updateView = async (value) => {
|
|
63
|
+
await rootStore.updateView({ value, type: props.type })
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
onMounted(() => {
|
|
67
|
+
// Add any logic needed on component mount
|
|
68
|
+
})
|
|
69
|
+
</script>
|
|
70
|
+
|
|
71
|
+
<style lang="scss"></style>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-autocomplete
|
|
3
|
+
v-model="val"
|
|
4
|
+
:items="props.items"
|
|
5
|
+
:multiple="$stores[type].filters[name]?.multiple || false"
|
|
6
|
+
/>
|
|
7
|
+
</template>
|
|
8
|
+
<script setup>
|
|
9
|
+
import { useRootStore } from "../../../stores/root"
|
|
10
|
+
const rootStore = useRootStore()
|
|
11
|
+
const { $stores } = useNuxtApp()
|
|
12
|
+
const props = defineProps(["type", "items", "name"])
|
|
13
|
+
const val = computed({
|
|
14
|
+
get() {
|
|
15
|
+
return $stores[props.type].filters[props.name]?.value || []
|
|
16
|
+
},
|
|
17
|
+
set(value) {
|
|
18
|
+
return rootStore.updateFilter(props.name, value, props.type)
|
|
19
|
+
},
|
|
20
|
+
})
|
|
21
|
+
</script>
|
|
22
|
+
<style lang="scss"></style>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-switch v-model="val" />
|
|
3
|
+
</template>
|
|
4
|
+
<script setup>
|
|
5
|
+
import { useRootStore } from "../../../stores/root"
|
|
6
|
+
const rootStore = useRootStore()
|
|
7
|
+
const props = defineProps(["type", "items", "name"])
|
|
8
|
+
const { $stores } = useNuxtApp()
|
|
9
|
+
const val = computed({
|
|
10
|
+
get() {
|
|
11
|
+
return $stores[props.type].filters[props.name]?.value
|
|
12
|
+
},
|
|
13
|
+
set(value) {
|
|
14
|
+
rootStore.updateFilter(props.name, value, props.type)
|
|
15
|
+
},
|
|
16
|
+
})
|
|
17
|
+
</script>
|
|
18
|
+
<style lang="scss"></style>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-checkbox v-model="val" />
|
|
3
|
+
</template>
|
|
4
|
+
<script setup>
|
|
5
|
+
import { useRootStore } from "../../../stores/root"
|
|
6
|
+
const rootStore = useRootStore()
|
|
7
|
+
const props = defineProps(["type", "items", "name"])
|
|
8
|
+
const { $stores } = useNuxtApp()
|
|
9
|
+
const val = computed({
|
|
10
|
+
get() {
|
|
11
|
+
return $stores[props.type].filters[props.name]?.value
|
|
12
|
+
},
|
|
13
|
+
set(value) {
|
|
14
|
+
rootStore.updateFilter(props.name, value, props.type)
|
|
15
|
+
},
|
|
16
|
+
})
|
|
17
|
+
onMounted(() => {
|
|
18
|
+
console.log(props.data)
|
|
19
|
+
})
|
|
20
|
+
</script>
|
|
21
|
+
<style lang="scss"></style>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-select
|
|
3
|
+
v-model="val"
|
|
4
|
+
:items="props.items"
|
|
5
|
+
:multiple="$stores[type].filters[name]?.multiple || false"
|
|
6
|
+
/>
|
|
7
|
+
</template>
|
|
8
|
+
<script setup>
|
|
9
|
+
import { useRootStore } from "../../../stores/root"
|
|
10
|
+
const rootStore = useRootStore()
|
|
11
|
+
const { $stores } = useNuxtApp()
|
|
12
|
+
const props = defineProps(["type", "items", "name"])
|
|
13
|
+
const val = computed({
|
|
14
|
+
get() {
|
|
15
|
+
return $stores[props.type].filters[props.name]?.value || []
|
|
16
|
+
},
|
|
17
|
+
set(value) {
|
|
18
|
+
rootStore.updateFilter(props.name, value, props.type)
|
|
19
|
+
},
|
|
20
|
+
})
|
|
21
|
+
onMounted(() => {
|
|
22
|
+
console.log(props)
|
|
23
|
+
})
|
|
24
|
+
</script>
|
|
25
|
+
<style lang="scss"></style>
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-row>
|
|
3
|
+
<template v-for="filterItem in Object.keys($stores[type].filters)">
|
|
4
|
+
<v-col v-if="computeVisibility(filterItem)" cols="12" sm="6" md="4">
|
|
5
|
+
<component
|
|
6
|
+
:is="ComponentName(filterItem)"
|
|
7
|
+
tile
|
|
8
|
+
:name="filterItem"
|
|
9
|
+
hide-details
|
|
10
|
+
:filter="true"
|
|
11
|
+
:dense="smAndDown"
|
|
12
|
+
:items="getItems(filterItem)"
|
|
13
|
+
clearable
|
|
14
|
+
:label="$t('list.filters.' + type + '.' + filterItem + '.label')"
|
|
15
|
+
min-height="56"
|
|
16
|
+
variant="outlined"
|
|
17
|
+
:loading="$stores[type].loading"
|
|
18
|
+
:type="type"
|
|
19
|
+
color="black"
|
|
20
|
+
style="min-width: 150px"
|
|
21
|
+
class="transition-swing pb-1"
|
|
22
|
+
/>
|
|
23
|
+
<!-- <template
|
|
24
|
+
v-else
|
|
25
|
+
v-for="filterItem in Object.keys(rootStore[type].list.filters).slice()"
|
|
26
|
+
>
|
|
27
|
+
<div v-if="computeVisibility(filterItem)">
|
|
28
|
+
{{ filterItem }}
|
|
29
|
+
</div>
|
|
30
|
+
</template> -->
|
|
31
|
+
</v-col>
|
|
32
|
+
</template></v-row
|
|
33
|
+
>
|
|
34
|
+
</template>
|
|
35
|
+
|
|
36
|
+
<script setup>
|
|
37
|
+
import { useRootStore } from "../../../stores/root"
|
|
38
|
+
import { useDisplay } from "vuetify"
|
|
39
|
+
const { smAndDown } = useDisplay()
|
|
40
|
+
const i18n = useI18n()
|
|
41
|
+
const { locale, messages } = useI18n()
|
|
42
|
+
const { $stores } = useNuxtApp()
|
|
43
|
+
const rootStore = useRootStore()
|
|
44
|
+
const props = defineProps(["type", "expanded"])
|
|
45
|
+
|
|
46
|
+
const ComponentName = (name) => {
|
|
47
|
+
return resolveComponent(
|
|
48
|
+
"ListInputs" + capitalize($stores[props.type].filters[name].type)
|
|
49
|
+
)
|
|
50
|
+
}
|
|
51
|
+
const getItems = (name) => {
|
|
52
|
+
if ($stores[props.type].filters[name].type === "Checkbox") {
|
|
53
|
+
return []
|
|
54
|
+
}
|
|
55
|
+
if (
|
|
56
|
+
messages.value[locale.value].list.filters[props.type][name] === undefined
|
|
57
|
+
) {
|
|
58
|
+
console.log("name not found, no item for this filmter: ", name)
|
|
59
|
+
return []
|
|
60
|
+
}
|
|
61
|
+
// TODO replace with package based values
|
|
62
|
+
return Object.keys(
|
|
63
|
+
messages.value[locale.value].list.filters[props.type][name]
|
|
64
|
+
)
|
|
65
|
+
.filter((key) => key !== "label")
|
|
66
|
+
.map((item) => ({
|
|
67
|
+
title: i18n.t(`list.filters.${props.type}.${name}.${item}`),
|
|
68
|
+
value: item,
|
|
69
|
+
}))
|
|
70
|
+
}
|
|
71
|
+
onMounted(() => {
|
|
72
|
+
rootStore.loadRouteQuery(props.type)
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
const computeVisibility = (filterItem) => {
|
|
76
|
+
return (
|
|
77
|
+
// if anything is set in the visibility key
|
|
78
|
+
!$stores[props.type].filters[filterItem].visibility ||
|
|
79
|
+
$stores[props.type].filters[filterItem].visibility?.default ||
|
|
80
|
+
$stores[props.type].filters[filterItem].visibility?.switchIf.find(
|
|
81
|
+
//for each of the rules set in the switchIf key
|
|
82
|
+
(rule) => {
|
|
83
|
+
// we check if each of the condition are fulfilled
|
|
84
|
+
return Object.keys(rule).find((value, index, obj) => {
|
|
85
|
+
return $stores[props.type].filters[value].multiple
|
|
86
|
+
? $stores[props.type].filters[value]?.value &&
|
|
87
|
+
$stores[props.type].filters[value]?.value.includes(rule[value])
|
|
88
|
+
: $stores[props.type].filters[value]?.value === rule[value]
|
|
89
|
+
})
|
|
90
|
+
}
|
|
91
|
+
)
|
|
92
|
+
)
|
|
93
|
+
return true
|
|
94
|
+
}
|
|
95
|
+
</script>
|
|
96
|
+
|
|
97
|
+
<style lang="scss" scoped></style>
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-row no-gutters>
|
|
3
|
+
<v-col cols="12">
|
|
4
|
+
<!-- <v-btn
|
|
5
|
+
v-if="rootStore.addBtn"
|
|
6
|
+
x-large
|
|
7
|
+
:height="mdAndUp ? '56' : '40'"
|
|
8
|
+
outlined
|
|
9
|
+
:rounded="0"
|
|
10
|
+
color="primary"
|
|
11
|
+
:to="localePath($route.fullPath + '/create')"
|
|
12
|
+
>
|
|
13
|
+
<v-icon left>mdi-plus</v-icon>
|
|
14
|
+
{{ $t("new-x", { item: $tc("x-" + type, 1) }) }}
|
|
15
|
+
</v-btn> -->
|
|
16
|
+
<div class="d-flex">
|
|
17
|
+
<ListAtomsFiltersMenu
|
|
18
|
+
:open="filtersOpen"
|
|
19
|
+
@open="filtersOpen = $event"
|
|
20
|
+
/>
|
|
21
|
+
<v-spacer />
|
|
22
|
+
<ListAtomsViewMenu :type="type" />
|
|
23
|
+
<ListAtomsSortMenu :type="type" />
|
|
24
|
+
</div>
|
|
25
|
+
<v-expand-transition>
|
|
26
|
+
<div v-if="filtersOpen" class="mb-7">
|
|
27
|
+
<ListMoleculesFilters :type="type" /></div
|
|
28
|
+
></v-expand-transition>
|
|
29
|
+
<ListAtomsSearchInput :type="type" />
|
|
30
|
+
<ListAtomsSearchString :type="type" />
|
|
31
|
+
</v-col>
|
|
32
|
+
</v-row>
|
|
33
|
+
</template>
|
|
34
|
+
|
|
35
|
+
<script setup>
|
|
36
|
+
const route = useRoute()
|
|
37
|
+
|
|
38
|
+
const filtersOpen = ref(!!Object.keys(route.query)?.length)
|
|
39
|
+
|
|
40
|
+
const props = defineProps({
|
|
41
|
+
type: {
|
|
42
|
+
type: String,
|
|
43
|
+
required: false,
|
|
44
|
+
default: "",
|
|
45
|
+
},
|
|
46
|
+
})
|
|
47
|
+
</script>
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-btn-toggle
|
|
3
|
+
value="currentPage"
|
|
4
|
+
role="navigation"
|
|
5
|
+
aria-label="Pagination Navigation"
|
|
6
|
+
>
|
|
7
|
+
<!-- TODO: switch to page as route param -->
|
|
8
|
+
<v-btn
|
|
9
|
+
v-if="!(hidePrevNext && firstPageSelected())"
|
|
10
|
+
:disabled="firstPageSelected()"
|
|
11
|
+
min-width="35"
|
|
12
|
+
height="35"
|
|
13
|
+
width="35"
|
|
14
|
+
:tabindex="!hidePrevNext && firstPageSelected() ? -1 : 0"
|
|
15
|
+
aria-label="Previous Page"
|
|
16
|
+
nuxt
|
|
17
|
+
@click="updatePage(currentPage - 1)"
|
|
18
|
+
@keyup.enter="updatePage(currentPage - 1)"
|
|
19
|
+
>
|
|
20
|
+
<v-icon>mdi-chevron-left</v-icon>
|
|
21
|
+
</v-btn>
|
|
22
|
+
|
|
23
|
+
<template v-for="(page, index) in renderPages" :key="page.key">
|
|
24
|
+
<v-btn
|
|
25
|
+
v-if="page.isGap"
|
|
26
|
+
min-width="35"
|
|
27
|
+
height="35"
|
|
28
|
+
width="35"
|
|
29
|
+
icon
|
|
30
|
+
nuxt
|
|
31
|
+
@keyup.enter="updatePage(getGapPage(index))"
|
|
32
|
+
@click="updatePage(getGapPage(index))"
|
|
33
|
+
>
|
|
34
|
+
...
|
|
35
|
+
</v-btn>
|
|
36
|
+
<template v-else>
|
|
37
|
+
<v-btn
|
|
38
|
+
:class="{ 'active-page': page.current }"
|
|
39
|
+
tabindex="0"
|
|
40
|
+
outlined
|
|
41
|
+
min-width="35"
|
|
42
|
+
height="35"
|
|
43
|
+
tile
|
|
44
|
+
nuxt
|
|
45
|
+
:color="page.current ? 'white' : 'black'"
|
|
46
|
+
text
|
|
47
|
+
width="35"
|
|
48
|
+
:aria-current="page.current ? 'true' : 'false'"
|
|
49
|
+
:aria-label="
|
|
50
|
+
page.current
|
|
51
|
+
? `Current page, Page ${page.value}`
|
|
52
|
+
: `Goto Page ${page.value}`
|
|
53
|
+
"
|
|
54
|
+
@click="updatePage(page.value)"
|
|
55
|
+
@keyup.enter="updatePage(page.value)"
|
|
56
|
+
>
|
|
57
|
+
{{ page.value }}
|
|
58
|
+
</v-btn>
|
|
59
|
+
</template>
|
|
60
|
+
</template>
|
|
61
|
+
|
|
62
|
+
<v-btn
|
|
63
|
+
v-if="!(hidePrevNext && lastPageSelected())"
|
|
64
|
+
:tabindex="!hidePrevNext && lastPageSelected() ? -1 : 0"
|
|
65
|
+
:disabled="lastPageSelected()"
|
|
66
|
+
aria-label="Next Page"
|
|
67
|
+
min-width="35"
|
|
68
|
+
height="35"
|
|
69
|
+
width="35"
|
|
70
|
+
nuxt
|
|
71
|
+
@click="updatePage(currentPage + 1)"
|
|
72
|
+
@keyup.enter="updatePage(currentPage + 1)"
|
|
73
|
+
>
|
|
74
|
+
<v-icon>mdi-chevron-right</v-icon>
|
|
75
|
+
</v-btn>
|
|
76
|
+
</v-btn-toggle>
|
|
77
|
+
</template>
|
|
78
|
+
|
|
79
|
+
<script setup>
|
|
80
|
+
import { computed } from "vue"
|
|
81
|
+
import { useRoute, useRouter } from "vue-router"
|
|
82
|
+
import { useRootStore } from "../../../stores/root"
|
|
83
|
+
|
|
84
|
+
const route = useRoute()
|
|
85
|
+
const router = useRouter()
|
|
86
|
+
const rootStore = useRootStore()
|
|
87
|
+
|
|
88
|
+
// THIS COMPONENT IS INITIALLY BASED ON https://github.com/ashwinkshenoy/vue-simple/tree/master/packages/vs-pagination
|
|
89
|
+
// AND MODIFIED TO FIT INTO OUR NEEDS (Vuetify + nuxt 3)
|
|
90
|
+
const props = defineProps({
|
|
91
|
+
totalPages: {
|
|
92
|
+
type: Number,
|
|
93
|
+
required: true,
|
|
94
|
+
},
|
|
95
|
+
currentPage: {
|
|
96
|
+
type: Number,
|
|
97
|
+
default: 1,
|
|
98
|
+
},
|
|
99
|
+
pagePadding: {
|
|
100
|
+
type: Number,
|
|
101
|
+
default: 1,
|
|
102
|
+
validator: (value) => {
|
|
103
|
+
return value > 0
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
pageGap: {
|
|
107
|
+
type: Number,
|
|
108
|
+
default: 2,
|
|
109
|
+
validator: (value) => {
|
|
110
|
+
return value > 0
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
hidePrevNext: {
|
|
114
|
+
type: Boolean,
|
|
115
|
+
default: false,
|
|
116
|
+
},
|
|
117
|
+
type: {
|
|
118
|
+
type: String,
|
|
119
|
+
default: "",
|
|
120
|
+
required: true,
|
|
121
|
+
},
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
const renderPages = computed(() => {
|
|
125
|
+
const pages = []
|
|
126
|
+
for (let pageIndex = 1; pageIndex <= props.totalPages; pageIndex++) {
|
|
127
|
+
if (
|
|
128
|
+
pageIndex === props.currentPage ||
|
|
129
|
+
pageIndex < props.pageGap ||
|
|
130
|
+
pageIndex > props.totalPages - props.pageGap + 1
|
|
131
|
+
) {
|
|
132
|
+
pages.push(createPage(pageIndex))
|
|
133
|
+
continue
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
let minimum
|
|
137
|
+
let maximum
|
|
138
|
+
|
|
139
|
+
if (props.currentPage <= props.pageGap + props.pagePadding) {
|
|
140
|
+
minimum = props.pageGap + 1
|
|
141
|
+
maximum = minimum + props.pagePadding * 2
|
|
142
|
+
} else if (
|
|
143
|
+
props.currentPage >=
|
|
144
|
+
props.totalPages - props.pageGap - props.pagePadding
|
|
145
|
+
) {
|
|
146
|
+
maximum = props.totalPages - props.pageGap
|
|
147
|
+
minimum = maximum - props.pagePadding * 2
|
|
148
|
+
} else {
|
|
149
|
+
minimum = props.currentPage - props.pagePadding
|
|
150
|
+
maximum = props.currentPage + props.pagePadding
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (
|
|
154
|
+
(pageIndex >= minimum && pageIndex <= props.currentPage) ||
|
|
155
|
+
(pageIndex >= props.currentPage && pageIndex <= maximum)
|
|
156
|
+
) {
|
|
157
|
+
pages.push(createPage(pageIndex))
|
|
158
|
+
continue
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (pageIndex === props.pageGap) {
|
|
162
|
+
if (
|
|
163
|
+
minimum > props.pageGap + 1 &&
|
|
164
|
+
props.currentPage > props.pageGap + props.pagePadding + 1
|
|
165
|
+
) {
|
|
166
|
+
pages.push(createGap(pageIndex))
|
|
167
|
+
} else {
|
|
168
|
+
pages.push(createPage(pageIndex))
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
continue
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (pageIndex === props.totalPages - props.pageGap + 1) {
|
|
175
|
+
if (
|
|
176
|
+
maximum < props.totalPages - props.pageGap &&
|
|
177
|
+
props.currentPage < props.totalPages - props.pageGap - props.pagePadding
|
|
178
|
+
) {
|
|
179
|
+
pages.push(createGap(pageIndex))
|
|
180
|
+
} else {
|
|
181
|
+
pages.push(createPage(pageIndex))
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
continue
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return pages
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
const createPage = (pageIndex) => {
|
|
192
|
+
return {
|
|
193
|
+
key: pageIndex,
|
|
194
|
+
current: props.currentPage === pageIndex,
|
|
195
|
+
value: pageIndex,
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const firstPageSelected = () => {
|
|
200
|
+
return props.currentPage === 1
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const lastPageSelected = () => {
|
|
204
|
+
return props.currentPage === props.totalPages || props.totalPages === 0
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const createGap = (pageIndex) => {
|
|
208
|
+
return {
|
|
209
|
+
key: pageIndex,
|
|
210
|
+
isGap: true,
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const updatePage = (page) => {
|
|
215
|
+
rootStore.updatePage({ page, type: props.type })
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const getGapPage = (index) => {
|
|
219
|
+
return Math.floor(
|
|
220
|
+
renderPages.value[index - 1].key +
|
|
221
|
+
((renderPages.value[index + 1].key || props.totalPages) -
|
|
222
|
+
renderPages.value[index - 1].key) /
|
|
223
|
+
2
|
|
224
|
+
)
|
|
225
|
+
}
|
|
226
|
+
// Watch for changes in the route query and update the currentPage accordingly
|
|
227
|
+
watch(
|
|
228
|
+
() => route.query.page,
|
|
229
|
+
(newPage) => {
|
|
230
|
+
if (newPage) {
|
|
231
|
+
props.currentPage = parseInt(newPage, 10)
|
|
232
|
+
}
|
|
233
|
+
},
|
|
234
|
+
{ immediate: true }
|
|
235
|
+
)
|
|
236
|
+
</script>
|
|
237
|
+
|
|
238
|
+
<style>
|
|
239
|
+
.active-page {
|
|
240
|
+
background-color: #000 !important;
|
|
241
|
+
color: #f5f5f5 !important;
|
|
242
|
+
}
|
|
243
|
+
</style>
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<ListMoleculesHeader :type="type" />
|
|
3
|
+
<component :is="view">
|
|
4
|
+
<component
|
|
5
|
+
:is="itemTemplate"
|
|
6
|
+
v-for="(item, index) in items"
|
|
7
|
+
:key="index"
|
|
8
|
+
:item="item"
|
|
9
|
+
:index="index"
|
|
10
|
+
/>
|
|
11
|
+
</component>
|
|
12
|
+
<div class="text-center">
|
|
13
|
+
<ListMoleculesPagination
|
|
14
|
+
v-if="numberOfPages > 1"
|
|
15
|
+
:type="type"
|
|
16
|
+
color="black"
|
|
17
|
+
large
|
|
18
|
+
:current-page="page"
|
|
19
|
+
:total-pages="numberOfPages"
|
|
20
|
+
:page-padding="1"
|
|
21
|
+
:page-gap="2"
|
|
22
|
+
:hide-prev-next="false"
|
|
23
|
+
/>
|
|
24
|
+
</div>
|
|
25
|
+
</template>
|
|
26
|
+
<script setup>
|
|
27
|
+
import { useRootStore } from "../../../stores/root"
|
|
28
|
+
import { capitalize } from "../../../composables/useUtils"
|
|
29
|
+
const { $stores } = useNuxtApp()
|
|
30
|
+
const { locale } = useI18n()
|
|
31
|
+
|
|
32
|
+
const rootStore = useRootStore()
|
|
33
|
+
const props = defineProps({
|
|
34
|
+
addBtn: {
|
|
35
|
+
type: Boolean,
|
|
36
|
+
required: false,
|
|
37
|
+
default: false,
|
|
38
|
+
},
|
|
39
|
+
type: {
|
|
40
|
+
type: String,
|
|
41
|
+
default: "",
|
|
42
|
+
required: true,
|
|
43
|
+
},
|
|
44
|
+
layout: {
|
|
45
|
+
type: Object,
|
|
46
|
+
required: false,
|
|
47
|
+
default: () => {
|
|
48
|
+
return {
|
|
49
|
+
cols: 12,
|
|
50
|
+
xl: 12,
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
pagination: {
|
|
55
|
+
type: Object,
|
|
56
|
+
required: false,
|
|
57
|
+
default: () => {
|
|
58
|
+
return {}
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
addButton: {
|
|
62
|
+
type: Boolean,
|
|
63
|
+
required: false,
|
|
64
|
+
default: false,
|
|
65
|
+
},
|
|
66
|
+
items: [Object],
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
const view = computed(() =>
|
|
70
|
+
resolveComponent("ListViews" + capitalize($stores[props.type].view.name))
|
|
71
|
+
)
|
|
72
|
+
const itemTemplate = computed(() =>
|
|
73
|
+
resolveComponent(
|
|
74
|
+
(
|
|
75
|
+
capitalize(props.type) +
|
|
76
|
+
capitalize($stores[props.type].view.name) +
|
|
77
|
+
"Item"
|
|
78
|
+
).toString()
|
|
79
|
+
)
|
|
80
|
+
)
|
|
81
|
+
const numberOfPages = computed(() => $stores[props.type].numberOfPages)
|
|
82
|
+
|
|
83
|
+
const page = computed(() => +$stores[props.type].page)
|
|
84
|
+
|
|
85
|
+
const items = computed(() => $stores[props.type].items)
|
|
86
|
+
|
|
87
|
+
try {
|
|
88
|
+
await rootStore.update(props.type, locale.value)
|
|
89
|
+
} catch (error) {
|
|
90
|
+
console.log("error fetching update list: ", error)
|
|
91
|
+
}
|
|
92
|
+
</script>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<section class="my-6 ml-2 pb-6">
|
|
3
|
+
<slot>
|
|
4
|
+
<!-- fallback content -->
|
|
5
|
+
Dense view
|
|
6
|
+
</slot>
|
|
7
|
+
</section>
|
|
8
|
+
</template>
|
|
9
|
+
|
|
10
|
+
<script setup lang="ts"></script>
|
|
11
|
+
<style>
|
|
12
|
+
section {
|
|
13
|
+
display: table;
|
|
14
|
+
width: 100%;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
section > * {
|
|
18
|
+
display: table-row;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
section .v-col.dense {
|
|
22
|
+
display: table-cell;
|
|
23
|
+
vertical-align: middle;
|
|
24
|
+
}
|
|
25
|
+
</style>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
GRID DISPLAY
|
|
3
|
+
<template v-for="(item, index) in items" :key="index">
|
|
4
|
+
<slot name="data" :item="item" />
|
|
5
|
+
</template>
|
|
6
|
+
</template>
|
|
7
|
+
|
|
8
|
+
<script setup lang="ts">
|
|
9
|
+
const props = defineProps({
|
|
10
|
+
items: [Object],
|
|
11
|
+
type: String,
|
|
12
|
+
})
|
|
13
|
+
</script>
|