@isoftdata/svelte-table 2.9.1 → 2.9.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,303 +1,303 @@
1
- <script
2
- module
3
- lang="ts"
4
- >
5
- export type Page = {
6
- number: number
7
- startIndex: number
8
- endIndex: number
9
- hideOnMobile?: boolean
10
- }
11
-
12
- //
13
- </script>
14
-
15
- <script
16
- lang="ts"
17
- generics="I"
18
- >
19
- import type { ClassValue } from 'svelte/elements'
20
-
21
- import { innerWidth } from 'svelte/reactivity/window'
22
-
23
- interface Props {
24
- items: Array<I>
25
- class?: ClassValue
26
- currentPageNumber: number
27
- perPageCount: number
28
- currentPageItems: Array<I>
29
- totalItemsCount?: number
30
- showInfoFooter?: boolean
31
- lastPageNumber?: number
32
- pageChange?: (context: { pageNumber: number }) => void
33
- }
34
-
35
- let {
36
- //
37
- items,
38
- class: classNames = '',
39
- currentPageNumber = $bindable(),
40
- perPageCount,
41
- currentPageItems = $bindable([]),
42
- totalItemsCount = 0,
43
- showInfoFooter = false,
44
- lastPageNumber = $bindable(Math.ceil(totalItemsCount / perPageCount)),
45
- pageChange,
46
- }: Props = $props()
47
-
48
- const computedTotalItemsCount = $derived(totalItemsCount || items.length)
49
- const pages = $derived(getPages(perPageCount, computedTotalItemsCount))
50
- const displayPages: Array<Page> = $derived(getDisplayPages(pages, currentPageNumber))
51
- const mobileSize = $derived(!!innerWidth.current && innerWidth.current < 576)
52
- const showPagination = $derived(perPageCount && computedTotalItemsCount > perPageCount)
53
- const buttonWidth = $derived.by(() => {
54
- // keep all the buttons the same size, but make them big enough to fit large page #s
55
- if (mobileSize) {
56
- return lastPageNumber >= 100 ? 51 : lastPageNumber >= 10 ? 40 : 36
57
- } else {
58
- return lastPageNumber >= 100 ? 40 : lastPageNumber >= 10 ? 36 : 28
59
- }
60
- })
61
-
62
- const numPagesHiddenOnMobile = $derived(
63
- displayPages.length &&
64
- displayPages.reduce((sum, page) => {
65
- if (page.hideOnMobile) {
66
- return sum++
67
- }
68
- return sum
69
- }, 0),
70
- )
71
- const somePagesHidden = $derived(displayPages.length < pages.length || (mobileSize && numPagesHiddenOnMobile > 0))
72
- $effect(() => {
73
- if (pages.length && pages.length < currentPageNumber) {
74
- currentPageNumber = pages.length > 0 ? pages.length : 1
75
- }
76
- })
77
- $effect(() => {
78
- const currentPage =
79
- pages.length > 0 && items.length > perPageCount ? pages.find(page => page.number === currentPageNumber) : null
80
- currentPageItems = currentPage ? items.slice(currentPage.startIndex, currentPage.endIndex) : items
81
- })
82
- $effect(() => {
83
- lastPageNumber = pages.length
84
- })
85
-
86
- export function setPageNumber(pageNumber: number) {
87
- currentPageNumber = pageNumber
88
- }
89
-
90
- export function setPageVisibleByItemIndex(itemIndex: number) {
91
- const foundPage = pages.find(page => {
92
- return itemIndex >= page.startIndex && itemIndex < page.endIndex
93
- })
94
- if (foundPage && foundPage.number !== currentPageNumber) {
95
- currentPageNumber = foundPage.number
96
- pageChange?.({ pageNumber: foundPage.number })
97
- }
98
- }
99
-
100
- function getPages(perPageCount: number, totalItemsCount: number) {
101
- if (perPageCount > 0) {
102
- const pagesCount = Math.ceil(totalItemsCount / perPageCount)
103
- const newPages = new Array<Page>()
104
-
105
- let n = 1
106
- while (n <= pagesCount) {
107
- newPages.push({
108
- number: n,
109
- startIndex: n === 1 ? 0 : perPageCount * (n - 1),
110
- endIndex: perPageCount * n,
111
- })
112
- n++
113
- }
114
- return newPages
115
- }
116
- return []
117
- }
118
-
119
- function getDisplayPages(pages: Array<Page>, currentPageNumber: number): Array<Page> {
120
- if (!pages.length) {
121
- return []
122
- }
123
- const lastPageIndex = pages.length - 1
124
- return pages.reduce((sum, page) => {
125
- if (
126
- !(
127
- lastPageIndex <= 7 ||
128
- page.number === currentPageNumber || // current page always
129
- (page.number <= 5 && currentPageNumber <= 4) || // current page in beginning of list
130
- (page.number > currentPageNumber - 2 && page.number < currentPageNumber + 2) || // one on either side of current
131
- (page.number >= lastPageIndex - 3 && currentPageNumber >= lastPageIndex - 2)
132
- )
133
- ) {
134
- page.hideOnMobile = true // TODO : maybe we don't need this
135
- } else {
136
- page.hideOnMobile = false
137
- }
138
- if (
139
- lastPageIndex <= 9 ||
140
- currentPageNumber == page.number || // current page
141
- (page.number <= 7 && currentPageNumber <= 4) || // beginning of list
142
- (page.number > currentPageNumber - 3 && page.number < currentPageNumber + 3) || // middle of list
143
- (page.number > lastPageIndex - 6 && currentPageNumber > lastPageIndex - 3) // end of list
144
- ) {
145
- return sum.concat(page)
146
- }
147
- return sum
148
- }, new Array<Page>())
149
- }
150
-
151
- function pageClicked(pageNumber: number) {
152
- currentPageNumber = pageNumber
153
- pageChange?.({ pageNumber })
154
- }
155
- </script>
156
-
157
- {#if showPagination}
158
- <div class={['d-flex justify-content-center', classNames]}>
159
- <nav aria-label="Page navigation">
160
- <ul class="pagination pagination-sm mb-0">
161
- <li
162
- class="page-item text-center unselectable hideOnMobile arrowWidth"
163
- class:disabled={currentPageNumber === 1}
164
- class:d-none={perPageCount == 0 || !perPageCount}
165
- >
166
- <button
167
- type="button"
168
- class="page-link w-100"
169
- onclick={() => pageClicked(1)}
170
- aria-label="Previous"
171
- role="link"
172
- >
173
- <i class="far fa-chevron-double-left"></i>
174
- </button>
175
- </li>
176
- <li
177
- class="page-item text-center unselectable arrowWidth"
178
- class:disabled={currentPageNumber === 1}
179
- class:d-none={perPageCount == 0 || !perPageCount}
180
- >
181
- <button
182
- class="page-link w-100"
183
- onclick={() => pageClicked(currentPageNumber - 1)}
184
- aria-label="Previous"
185
- >
186
- <i class="far fa-chevron-left"></i>
187
- </button>
188
- </li>
189
- {#if currentPageNumber > 4 && somePagesHidden}
190
- <li
191
- class="page-item text-center"
192
- style="min-width: {buttonWidth}px;"
193
- >
194
- <button
195
- class="page-link w-100"
196
- onclick={() => pageClicked(1)}
197
- aria-label="First"
198
- >1
199
- </button>
200
- </li>
201
- <li class="page-item text-center disabled arrowWidth">
202
- <span
203
- class="page-link w-100"
204
- style="cursor: default;">...</span
205
- >
206
- </li>
207
- {/if}
208
- {#each displayPages as page}
209
- <li
210
- class="page-item text-center unselectable"
211
- class:active={page.number === currentPageNumber}
212
- class:hideOnMobile={page.hideOnMobile}
213
- style="min-width: {buttonWidth}px;"
214
- >
215
- <button
216
- class="page-link w-100"
217
- onclick={() => pageClicked(page.number)}>{page.number}</button
218
- >
219
- </li>
220
- {/each}
221
- {#if currentPageNumber < pages.length - 3 && somePagesHidden}
222
- <li class="page-item text-center disabled arrowWidth">
223
- <span
224
- class="page-link w-100"
225
- style="cursor: default;">...</span
226
- >
227
- </li>
228
- <li
229
- class="page-item text-center"
230
- style="min-width: {buttonWidth}px;"
231
- >
232
- <button
233
- class="page-link w-100"
234
- onclick={() => pageClicked(pages.length)}
235
- aria-label="Last"
236
- >{pages.length}
237
- </button>
238
- </li>
239
- {/if}
240
- <li
241
- class="page-item text-center unselectable arrowWidth"
242
- class:disabled={currentPageNumber === lastPageNumber}
243
- class:d-none={perPageCount == 0 || !perPageCount}
244
- >
245
- <button
246
- class="page-link w-100"
247
- onclick={() => pageClicked(currentPageNumber + 1)}
248
- aria-label="Next"
249
- >
250
- <i class="far fa-chevron-right"></i>
251
- </button>
252
- </li>
253
- <li
254
- class="page-item text-center unselectable hideOnMobile arrowWidth"
255
- class:disabled={currentPageNumber === lastPageNumber}
256
- class:d-none={perPageCount == 0 || !perPageCount}
257
- >
258
- <button
259
- class="page-link w-100"
260
- onclick={() => pageClicked(lastPageNumber)}
261
- aria-label="Last"
262
- >
263
- <i class="far fa-chevron-double-right"></i>
264
- </button>
265
- </li>
266
- </ul>
267
- {#if showInfoFooter}
268
- <div class="text-center">
269
- <small
270
- >{1 + (currentPageNumber - 1) * perPageCount}-{currentPageNumber * perPageCount > computedTotalItemsCount
271
- ? computedTotalItemsCount
272
- : currentPageNumber * perPageCount} of {computedTotalItemsCount}
273
- results</small
274
- >
275
- </div>
276
- {/if}
277
- </nav>
278
- </div>
279
- {/if}
280
-
281
- <style>
282
- @media screen and (max-width: 576px) {
283
- .hideOnMobile {
284
- display: none;
285
- }
286
- .pagination-sm .page-link {
287
- padding: 0.5rem 0.75rem;
288
- font-size: 1rem;
289
- line-height: 1.25;
290
- }
291
- .arrowWidth {
292
- width: 39px;
293
- }
294
- }
295
- @media screen and (min-width: 576px) {
296
- .arrowWidth {
297
- width: 31px;
298
- }
299
- }
300
- .page-item:not(:last-child) > .page-link {
301
- border-right: 0;
302
- }
303
- </style>
1
+ <script
2
+ module
3
+ lang="ts"
4
+ >
5
+ export type Page = {
6
+ number: number
7
+ startIndex: number
8
+ endIndex: number
9
+ hideOnMobile?: boolean
10
+ }
11
+
12
+ //
13
+ </script>
14
+
15
+ <script
16
+ lang="ts"
17
+ generics="I"
18
+ >
19
+ import type { ClassValue } from 'svelte/elements'
20
+
21
+ import { innerWidth } from 'svelte/reactivity/window'
22
+
23
+ interface Props {
24
+ items: Array<I>
25
+ class?: ClassValue
26
+ currentPageNumber: number
27
+ perPageCount: number
28
+ currentPageItems: Array<I>
29
+ totalItemsCount?: number
30
+ showInfoFooter?: boolean
31
+ lastPageNumber?: number
32
+ pageChange?: (context: { pageNumber: number }) => void
33
+ }
34
+
35
+ let {
36
+ //
37
+ items,
38
+ class: classNames = '',
39
+ currentPageNumber = $bindable(),
40
+ perPageCount,
41
+ currentPageItems = $bindable([]),
42
+ totalItemsCount = 0,
43
+ showInfoFooter = false,
44
+ lastPageNumber = $bindable(Math.ceil(totalItemsCount / perPageCount)),
45
+ pageChange,
46
+ }: Props = $props()
47
+
48
+ const computedTotalItemsCount = $derived(totalItemsCount || items.length)
49
+ const pages = $derived(getPages(perPageCount, computedTotalItemsCount))
50
+ const displayPages: Array<Page> = $derived(getDisplayPages(pages, currentPageNumber))
51
+ const mobileSize = $derived(!!innerWidth.current && innerWidth.current < 576)
52
+ const showPagination = $derived(perPageCount && computedTotalItemsCount > perPageCount)
53
+ const buttonWidth = $derived.by(() => {
54
+ // keep all the buttons the same size, but make them big enough to fit large page #s
55
+ if (mobileSize) {
56
+ return lastPageNumber >= 100 ? 51 : lastPageNumber >= 10 ? 40 : 36
57
+ } else {
58
+ return lastPageNumber >= 100 ? 40 : lastPageNumber >= 10 ? 36 : 28
59
+ }
60
+ })
61
+
62
+ const numPagesHiddenOnMobile = $derived(
63
+ displayPages.length &&
64
+ displayPages.reduce((sum, page) => {
65
+ if (page.hideOnMobile) {
66
+ return sum++
67
+ }
68
+ return sum
69
+ }, 0),
70
+ )
71
+ const somePagesHidden = $derived(displayPages.length < pages.length || (mobileSize && numPagesHiddenOnMobile > 0))
72
+ $effect(() => {
73
+ if (pages.length && pages.length < currentPageNumber) {
74
+ currentPageNumber = pages.length > 0 ? pages.length : 1
75
+ }
76
+ })
77
+ $effect(() => {
78
+ const currentPage =
79
+ pages.length > 0 && items.length > perPageCount ? pages.find(page => page.number === currentPageNumber) : null
80
+ currentPageItems = currentPage ? items.slice(currentPage.startIndex, currentPage.endIndex) : items
81
+ })
82
+ $effect(() => {
83
+ lastPageNumber = pages.length
84
+ })
85
+
86
+ export function setPageNumber(pageNumber: number) {
87
+ currentPageNumber = pageNumber
88
+ }
89
+
90
+ export function setPageVisibleByItemIndex(itemIndex: number) {
91
+ const foundPage = pages.find(page => {
92
+ return itemIndex >= page.startIndex && itemIndex < page.endIndex
93
+ })
94
+ if (foundPage && foundPage.number !== currentPageNumber) {
95
+ currentPageNumber = foundPage.number
96
+ pageChange?.({ pageNumber: foundPage.number })
97
+ }
98
+ }
99
+
100
+ function getPages(perPageCount: number, totalItemsCount: number) {
101
+ if (perPageCount > 0) {
102
+ const pagesCount = Math.ceil(totalItemsCount / perPageCount)
103
+ const newPages = new Array<Page>()
104
+
105
+ let n = 1
106
+ while (n <= pagesCount) {
107
+ newPages.push({
108
+ number: n,
109
+ startIndex: n === 1 ? 0 : perPageCount * (n - 1),
110
+ endIndex: perPageCount * n,
111
+ })
112
+ n++
113
+ }
114
+ return newPages
115
+ }
116
+ return []
117
+ }
118
+
119
+ function getDisplayPages(pages: Array<Page>, currentPageNumber: number): Array<Page> {
120
+ if (!pages.length) {
121
+ return []
122
+ }
123
+ const lastPageIndex = pages.length - 1
124
+ return pages.reduce((sum, page) => {
125
+ if (
126
+ !(
127
+ lastPageIndex <= 7 ||
128
+ page.number === currentPageNumber || // current page always
129
+ (page.number <= 5 && currentPageNumber <= 4) || // current page in beginning of list
130
+ (page.number > currentPageNumber - 2 && page.number < currentPageNumber + 2) || // one on either side of current
131
+ (page.number >= lastPageIndex - 3 && currentPageNumber >= lastPageIndex - 2)
132
+ )
133
+ ) {
134
+ page.hideOnMobile = true // TODO : maybe we don't need this
135
+ } else {
136
+ page.hideOnMobile = false
137
+ }
138
+ if (
139
+ lastPageIndex <= 9 ||
140
+ currentPageNumber == page.number || // current page
141
+ (page.number <= 7 && currentPageNumber <= 4) || // beginning of list
142
+ (page.number > currentPageNumber - 3 && page.number < currentPageNumber + 3) || // middle of list
143
+ (page.number > lastPageIndex - 6 && currentPageNumber > lastPageIndex - 3) // end of list
144
+ ) {
145
+ return sum.concat(page)
146
+ }
147
+ return sum
148
+ }, new Array<Page>())
149
+ }
150
+
151
+ function pageClicked(pageNumber: number) {
152
+ currentPageNumber = pageNumber
153
+ pageChange?.({ pageNumber })
154
+ }
155
+ </script>
156
+
157
+ {#if showPagination}
158
+ <div class={['d-flex justify-content-center', classNames]}>
159
+ <nav aria-label="Page navigation">
160
+ <ul class="pagination pagination-sm mb-0">
161
+ <li
162
+ class="page-item text-center unselectable hideOnMobile arrowWidth"
163
+ class:disabled={currentPageNumber === 1}
164
+ class:d-none={perPageCount == 0 || !perPageCount}
165
+ >
166
+ <button
167
+ type="button"
168
+ class="page-link w-100"
169
+ onclick={() => pageClicked(1)}
170
+ aria-label="Previous"
171
+ role="link"
172
+ >
173
+ <i class="far fa-chevron-double-left"></i>
174
+ </button>
175
+ </li>
176
+ <li
177
+ class="page-item text-center unselectable arrowWidth"
178
+ class:disabled={currentPageNumber === 1}
179
+ class:d-none={perPageCount == 0 || !perPageCount}
180
+ >
181
+ <button
182
+ class="page-link w-100"
183
+ onclick={() => pageClicked(currentPageNumber - 1)}
184
+ aria-label="Previous"
185
+ >
186
+ <i class="far fa-chevron-left"></i>
187
+ </button>
188
+ </li>
189
+ {#if currentPageNumber > 4 && somePagesHidden}
190
+ <li
191
+ class="page-item text-center"
192
+ style="min-width: {buttonWidth}px;"
193
+ >
194
+ <button
195
+ class="page-link w-100"
196
+ onclick={() => pageClicked(1)}
197
+ aria-label="First"
198
+ >1
199
+ </button>
200
+ </li>
201
+ <li class="page-item text-center disabled arrowWidth">
202
+ <span
203
+ class="page-link w-100"
204
+ style="cursor: default;">...</span
205
+ >
206
+ </li>
207
+ {/if}
208
+ {#each displayPages as page}
209
+ <li
210
+ class="page-item text-center unselectable"
211
+ class:active={page.number === currentPageNumber}
212
+ class:hideOnMobile={page.hideOnMobile}
213
+ style="min-width: {buttonWidth}px;"
214
+ >
215
+ <button
216
+ class="page-link w-100"
217
+ onclick={() => pageClicked(page.number)}>{page.number}</button
218
+ >
219
+ </li>
220
+ {/each}
221
+ {#if currentPageNumber < pages.length - 3 && somePagesHidden}
222
+ <li class="page-item text-center disabled arrowWidth">
223
+ <span
224
+ class="page-link w-100"
225
+ style="cursor: default;">...</span
226
+ >
227
+ </li>
228
+ <li
229
+ class="page-item text-center"
230
+ style="min-width: {buttonWidth}px;"
231
+ >
232
+ <button
233
+ class="page-link w-100"
234
+ onclick={() => pageClicked(pages.length)}
235
+ aria-label="Last"
236
+ >{pages.length}
237
+ </button>
238
+ </li>
239
+ {/if}
240
+ <li
241
+ class="page-item text-center unselectable arrowWidth"
242
+ class:disabled={currentPageNumber === lastPageNumber}
243
+ class:d-none={perPageCount == 0 || !perPageCount}
244
+ >
245
+ <button
246
+ class="page-link w-100"
247
+ onclick={() => pageClicked(currentPageNumber + 1)}
248
+ aria-label="Next"
249
+ >
250
+ <i class="far fa-chevron-right"></i>
251
+ </button>
252
+ </li>
253
+ <li
254
+ class="page-item text-center unselectable hideOnMobile arrowWidth"
255
+ class:disabled={currentPageNumber === lastPageNumber}
256
+ class:d-none={perPageCount == 0 || !perPageCount}
257
+ >
258
+ <button
259
+ class="page-link w-100"
260
+ onclick={() => pageClicked(lastPageNumber)}
261
+ aria-label="Last"
262
+ >
263
+ <i class="far fa-chevron-double-right"></i>
264
+ </button>
265
+ </li>
266
+ </ul>
267
+ {#if showInfoFooter}
268
+ <div class="text-center">
269
+ <small
270
+ >{1 + (currentPageNumber - 1) * perPageCount}-{currentPageNumber * perPageCount > computedTotalItemsCount
271
+ ? computedTotalItemsCount
272
+ : currentPageNumber * perPageCount} of {computedTotalItemsCount}
273
+ results</small
274
+ >
275
+ </div>
276
+ {/if}
277
+ </nav>
278
+ </div>
279
+ {/if}
280
+
281
+ <style>
282
+ @media screen and (max-width: 576px) {
283
+ .hideOnMobile {
284
+ display: none;
285
+ }
286
+ .pagination-sm .page-link {
287
+ padding: 0.5rem 0.75rem;
288
+ font-size: 1rem;
289
+ line-height: 1.25;
290
+ }
291
+ .arrowWidth {
292
+ width: 39px;
293
+ }
294
+ }
295
+ @media screen and (min-width: 576px) {
296
+ .arrowWidth {
297
+ width: 31px;
298
+ }
299
+ }
300
+ .page-item:not(:last-child) > .page-link {
301
+ border-right: 0;
302
+ }
303
+ </style>