@paris-ias/list 1.0.117 → 1.0.118

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.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@paris-ias/list",
3
3
  "configKey": "list",
4
- "version": "1.0.117",
4
+ "version": "1.0.118",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.0",
7
7
  "unbuild": "3.5.0"
@@ -3,93 +3,71 @@
3
3
  value="currentPage"
4
4
  role="navigation"
5
5
  aria-label="Pagination Navigation"
6
+ class="d-inline-flex"
6
7
  >
7
- <!-- TODO: switch to page as route param -->
8
+ <!-- Previous button -->
8
9
  <v-btn
9
- v-if="!(hidePrevNext && firstPageSelected())"
10
- :disabled="firstPageSelected()"
10
+ v-if="!(hidePrevNext && isFirstPage)"
11
+ :disabled="isFirstPage"
11
12
  min-width="35"
12
13
  height="35"
13
14
  width="35"
14
- :tabindex="!hidePrevNext && firstPageSelected() ? -1 : 0"
15
+ :tabindex="isFirstPage && hidePrevNext ? -1 : 0"
15
16
  aria-label="Previous Page"
16
- nuxt
17
- @click="updatePage(currentPage - 1)"
18
- @keyup.enter="updatePage(currentPage - 1)"
17
+ @click="onChange(currentPage - 1)"
18
+ @keyup.enter="onChange(currentPage - 1)"
19
19
  >
20
20
  <v-icon>mdi-chevron-left</v-icon>
21
21
  </v-btn>
22
22
 
23
+ <!-- Page buttons and gaps -->
23
24
  <template v-for="(page, index) in renderPages" :key="page.key">
25
+ <!-- Ellipsis gap -->
24
26
  <v-btn
25
27
  v-if="page.isGap"
28
+ icon
26
29
  min-width="35"
27
30
  height="35"
28
31
  width="35"
29
- icon
30
- nuxt
31
- @keyup.enter="updatePage(getGapPage(index))"
32
- @click="updatePage(getGapPage(index))"
32
+ @click="onChange(getGapPage(index))"
33
+ @keyup.enter="onChange(getGapPage(index))"
33
34
  >
34
35
  ...
35
36
  </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
- :active="!!page.current"
46
- :color="!!page.current ? 'white' : 'black'"
47
- text
48
- width="35"
49
- :aria-current="!!page.current ? 'true' : 'false'"
50
- :aria-label="
51
- page.current
52
- ? `Current page, Page ${page.value}`
53
- : `Goto Page ${page.value}`
54
- "
55
- @click="updatePage(page.value)"
56
- @keyup.enter="updatePage(page.value)"
57
- >
58
- {{ page.value }}
59
- </v-btn> -->
60
37
 
61
- <v-btn
62
- :class="['page-button', { 'active-page': page.current }]"
63
- tabindex="0"
64
- min-width="35"
65
- height="35"
66
- tile
67
- width="35"
68
- aria-current="page"
69
- :aria-label="
70
- page.current
71
- ? `Current page, Page ${page.value}`
72
- : `Goto Page ${page.value}`
73
- "
74
- @click="updatePage(page.value)"
75
- @keyup.enter="updatePage(page.value)"
76
- >
77
- {{ page.value }}
78
- </v-btn>
79
- </template>
38
+ <!-- Page number -->
39
+ <v-btn
40
+ v-else
41
+ :class="['page-button', { 'active-page': page.current }]"
42
+ tabindex="0"
43
+ min-width="35"
44
+ height="35"
45
+ tile
46
+ width="35"
47
+ :aria-current="page.current ? 'page' : undefined"
48
+ :aria-label="
49
+ page.current
50
+ ? `Current page, Page ${page.value}`
51
+ : `Go to Page ${page.value}`
52
+ "
53
+ @click="onChange(page.value)"
54
+ @keyup.enter="onChange(page.value)"
55
+ >
56
+ {{ page.value }}
57
+ </v-btn>
80
58
  </template>
81
59
 
60
+ <!-- Next button -->
82
61
  <v-btn
83
- v-if="!(hidePrevNext && lastPageSelected())"
84
- :tabindex="!hidePrevNext && lastPageSelected() ? -1 : 0"
85
- :disabled="lastPageSelected()"
62
+ v-if="!(hidePrevNext && isLastPage)"
63
+ :disabled="isLastPage"
64
+ :tabindex="isLastPage && hidePrevNext ? -1 : 0"
86
65
  aria-label="Next Page"
87
66
  min-width="35"
88
67
  height="35"
89
68
  width="35"
90
- nuxt
91
- @click="updatePage(currentPage + 1)"
92
- @keyup.enter="updatePage(currentPage + 1)"
69
+ @click="onChange(currentPage + 1)"
70
+ @keyup.enter="onChange(currentPage + 1)"
93
71
  >
94
72
  <v-icon>mdi-chevron-right</v-icon>
95
73
  </v-btn>
@@ -97,117 +75,76 @@
97
75
  </template>
98
76
 
99
77
  <script setup>
100
- import { useRootStore } from "../../../stores/root";
101
- import { useRoute, computed, useI18n } from "#imports";
102
- const { locale } = useI18n();
103
- const route = useRoute();
104
- const rootStore = useRootStore();
78
+ import { computed } from "vue";
105
79
  const props = defineProps({
106
- totalPages: {
107
- type: Number,
108
- required: true
109
- },
110
- currentPage: {
111
- type: Number,
112
- default: 1
113
- },
114
- pagePadding: {
115
- type: Number,
116
- default: 1,
117
- validator: (value) => {
118
- return value > 0;
119
- }
120
- },
121
- pageGap: {
122
- type: Number,
123
- default: 2,
124
- validator: (value) => {
125
- return value > 0;
126
- }
127
- },
128
- hidePrevNext: {
129
- type: Boolean,
130
- default: false
131
- },
132
- type: {
133
- type: String,
134
- default: "",
135
- required: true
136
- }
80
+ currentPage: { type: Number, required: true },
81
+ totalPages: { type: Number, required: true },
82
+ pagePadding: { type: Number, default: 1, validator: (v) => v > 0 },
83
+ pageGap: { type: Number, default: 2, validator: (v) => v > 0 },
84
+ hidePrevNext: { type: Boolean, default: false }
137
85
  });
86
+ const emit = defineEmits(["update"]);
87
+ const isFirstPage = computed(() => props.currentPage === 1);
88
+ const isLastPage = computed(
89
+ () => props.currentPage === props.totalPages || props.totalPages === 0
90
+ );
138
91
  const renderPages = computed(() => {
92
+ const createPage = (pageIndex) => ({
93
+ key: `page-${pageIndex}`,
94
+ value: pageIndex,
95
+ current: pageIndex === props.currentPage
96
+ });
97
+ const createGap = (pageIndex) => ({ key: `gap-${pageIndex}`, isGap: true });
139
98
  const pages = [];
140
- for (let pageIndex = 1; pageIndex <= props.totalPages; pageIndex++) {
141
- if (pageIndex === props.currentPage || pageIndex < props.pageGap || pageIndex > props.totalPages - props.pageGap + 1) {
142
- pages.push(createPage(pageIndex));
99
+ for (let i = 1; i <= props.totalPages; i++) {
100
+ if (i === props.currentPage || i < props.pageGap || i > props.totalPages - props.pageGap + 1) {
101
+ pages.push(createPage(i));
143
102
  continue;
144
103
  }
145
- let minimum;
146
- let maximum;
104
+ let min, max;
147
105
  if (props.currentPage <= props.pageGap + props.pagePadding) {
148
- minimum = props.pageGap + 1;
149
- maximum = minimum + props.pagePadding * 2;
106
+ min = props.pageGap + 1;
107
+ max = min + props.pagePadding * 2;
150
108
  } else if (props.currentPage >= props.totalPages - props.pageGap - props.pagePadding) {
151
- maximum = props.totalPages - props.pageGap;
152
- minimum = maximum - props.pagePadding * 2;
109
+ max = props.totalPages - props.pageGap;
110
+ min = max - props.pagePadding * 2;
153
111
  } else {
154
- minimum = props.currentPage - props.pagePadding;
155
- maximum = props.currentPage + props.pagePadding;
112
+ min = props.currentPage - props.pagePadding;
113
+ max = props.currentPage + props.pagePadding;
156
114
  }
157
- if (pageIndex >= minimum && pageIndex <= props.currentPage || pageIndex >= props.currentPage && pageIndex <= maximum) {
158
- pages.push(createPage(pageIndex));
115
+ if (i >= min && i <= props.currentPage || i <= max && i >= props.currentPage) {
116
+ pages.push(createPage(i));
159
117
  continue;
160
118
  }
161
- if (pageIndex === props.pageGap) {
162
- if (minimum > props.pageGap + 1 && props.currentPage > props.pageGap + props.pagePadding + 1) {
163
- pages.push(createGap(pageIndex));
119
+ if (i === props.pageGap) {
120
+ if (min > props.pageGap + 1 && props.currentPage > props.pageGap + props.pagePadding + 1) {
121
+ pages.push(createGap(i));
164
122
  } else {
165
- pages.push(createPage(pageIndex));
123
+ pages.push(createPage(i));
166
124
  }
167
125
  continue;
168
126
  }
169
- if (pageIndex === props.totalPages - props.pageGap + 1) {
170
- if (maximum < props.totalPages - props.pageGap && props.currentPage < props.totalPages - props.pageGap - props.pagePadding) {
171
- pages.push(createGap(pageIndex));
127
+ if (i === props.totalPages - props.pageGap + 1) {
128
+ if (max < props.totalPages - props.pageGap && props.currentPage < props.totalPages - props.pageGap - props.pagePadding) {
129
+ pages.push(createGap(i));
172
130
  } else {
173
- pages.push(createPage(pageIndex));
131
+ pages.push(createPage(i));
174
132
  }
175
133
  continue;
176
134
  }
177
135
  }
178
136
  return pages;
179
137
  });
180
- const createPage = (pageIndex) => {
181
- return {
182
- key: pageIndex,
183
- current: props.currentPage === pageIndex,
184
- value: pageIndex
185
- };
186
- };
187
- const firstPageSelected = () => {
188
- return props.currentPage === 1;
189
- };
190
- const lastPageSelected = () => {
191
- return props.currentPage === props.totalPages || props.totalPages === 0;
192
- };
193
- const createGap = (pageIndex) => {
194
- return {
195
- key: pageIndex,
196
- isGap: true
197
- };
198
- };
199
- const emit = defineEmits(["update"]);
200
- const updatePage = (page) => {
201
- rootStore.updatePage({ page, type: props.type, lang: locale.value });
202
- emit("update", page);
203
- };
204
138
  const getGapPage = (index) => {
205
- return Math.floor(
206
- renderPages.value[index - 1].key + ((renderPages.value[index + 1].key || props.totalPages) - renderPages.value[index - 1].key) / 2
207
- );
139
+ const before = renderPages.value[index - 1];
140
+ const after = renderPages.value[index + 1] || { value: props.totalPages };
141
+ return Math.floor((before.value + after.value) / 2);
208
142
  };
143
+ function onChange(page) {
144
+ emit("update", page);
145
+ }
209
146
  </script>
210
147
 
211
- <style>
148
+ <style scoped>
212
149
  .page-button{background-color:transparent;border:1px solid rgba(0,0,0,.2);color:#000;transition:background-color .3s ease,color .3s ease,transform .2s ease}.page-button:hover{background-color:#f0f0f0}.page-button.active-page{background-color:#000!important;color:#fff!important;transform:scale(1.05)}.page-button:focus{box-shadow:0 0 0 2px rgba(0,0,0,.3);outline:none}
213
150
  </style>
@@ -32,6 +32,7 @@
32
32
  </template>
33
33
 
34
34
  <script setup>
35
+ import { nextTick } from "vue";
35
36
  import { useRootStore } from "../../../stores/root";
36
37
  import { capitalize } from "../../../composables/useUtils";
37
38
  import {
@@ -91,26 +92,53 @@ const itemTemplate = computed(
91
92
  )
92
93
  );
93
94
  const numberOfPages = computed(() => $stores[props.type].numberOfPages);
94
- const page = computed(() => +$stores[props.type].page);
95
+ const page = computed(() => {
96
+ const p = parseInt(route.query.page, 10);
97
+ return !isNaN(p) && p > 0 ? p : 1;
98
+ });
95
99
  const items = computed(() => $stores[props.type].items);
96
100
  console.log("setup list");
97
- onMounted(() => {
98
- console.log("mounted list");
101
+ onMounted(async () => {
102
+ rootStore.loadRouteQuery(props.type);
103
+ const pageParam = parseInt(route.query.page, 10);
104
+ if (!isNaN(pageParam) && pageParam > 1) {
105
+ await rootStore.updatePage({
106
+ page: pageParam,
107
+ type: props.type,
108
+ lang: locale.value
109
+ });
110
+ }
111
+ try {
112
+ await rootStore.update(props.type, locale.value);
113
+ } catch (e) {
114
+ console.error("Error fetching list:", e);
115
+ }
99
116
  });
100
- rootStore.loadRouteQuery(props.type);
101
117
  try {
102
118
  await rootStore.update(props.type, locale.value);
103
119
  } catch (error) {
104
120
  console.log("error fetching update list: ", error);
105
121
  }
122
+ watch(
123
+ () => page.value,
124
+ async (newPage) => {
125
+ const query = {
126
+ ...route.query,
127
+ page: newPage > 1 ? String(newPage) : void 0
128
+ };
129
+ navigateTo({ query }, { replace: true });
130
+ await nextTick();
131
+ window.scrollTo({ top: 0, behavior: "smooth" });
132
+ }
133
+ );
106
134
  onBeforeUnmount(() => {
107
135
  rootStore.resetState(props.type, locale.value);
108
136
  });
109
- const onPageChange = (newPage) => {
110
- rootStore.updatePage({ page: newPage, type: props.type, lang: locale.value });
111
- window.scrollTo({
112
- top: 0,
113
- behavior: "smooth"
137
+ async function onPageChange(newPage) {
138
+ await rootStore.updatePage({
139
+ page: newPage,
140
+ type: props.type,
141
+ lang: locale.value
114
142
  });
115
- };
143
+ }
116
144
  </script>
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "license": "AGPL-3.0-only",
3
3
  "main": "./dist/module.mjs",
4
- "version": "1.0.117",
4
+ "version": "1.0.118",
5
5
  "name": "@paris-ias/list",
6
6
  "repository": {
7
7
  "url": "git+https://github.com/IEA-Paris/list.git",