vanilla-jui 1.4.7 → 1.5.1
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/README.md +10 -4
- package/dist/index.d.ts +418 -970
- package/dist/index.js +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/style.css +1 -1
- package/package.json +3 -3
- package/src/components/accordion.ts +466 -346
- package/src/components/flow.ts +779 -1133
- package/src/components/form.ts +666 -509
- package/src/components/menu.ts +198 -328
- package/src/components/modal.ts +510 -747
- package/src/components/offcanvas.ts +273 -367
- package/src/components/pagination.ts +201 -375
- package/src/components/swiper.ts +689 -1107
- package/src/components/tabs.ts +467 -778
- package/src/components/toc.ts +191 -244
- package/src/core/component.ts +332 -0
- package/src/core/motion.ts +298 -0
- package/src/core/presence.ts +233 -0
- package/src/core/scheduler.ts +91 -0
- package/src/core/view.ts +31 -0
- package/src/css/accordion.css +94 -3
- package/src/css/content.css +18 -24
- package/src/css/drop.css +4 -1
- package/src/css/form.css +8 -0
- package/src/css/menu.css +10 -5
- package/src/css/modal.css +2 -1
- package/src/css/offcanvas.css +5 -17
- package/src/css/swiper.css +2 -1
- package/src/css/tabs.css +8 -17
- package/src/css/themes/default/accordion.css +0 -48
- package/src/css/themes/default/swiper.css +1 -1
- package/src/css/themes/default/tabs.css +4 -6
- package/src/css/toast.css +1 -17
- package/src/css/tooltip.css +2 -1
- package/src/index.ts +8 -2
- package/src/locales/index.ts +3 -0
- package/src/primitives/drop.ts +253 -262
- package/src/primitives/loading.ts +22 -4
- package/src/primitives/parabola.ts +123 -164
- package/src/primitives/sticky.ts +297 -0
- package/src/primitives/theme.ts +164 -222
- package/src/primitives/toast.ts +309 -284
- package/src/primitives/tooltip.ts +65 -66
- package/src/utilities/browser.ts +38 -0
- package/src/utilities/dom.ts +0 -54
- package/src/utilities/refs.ts +61 -0
- package/src/utilities/state.ts +73 -0
- package/src/utilities/timer.ts +11 -31
- package/src/validation/validator.ts +289 -389
- package/src/components/sticky.ts +0 -297
- package/src/core/Component.ts +0 -377
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
import {
|
|
2
|
+
For,
|
|
2
3
|
createDeepStore,
|
|
3
4
|
createEffect,
|
|
4
|
-
|
|
5
|
+
createMemo,
|
|
5
6
|
flushSync,
|
|
6
7
|
jsx,
|
|
7
|
-
untrack,
|
|
8
8
|
} from 'vanilla-signal';
|
|
9
9
|
|
|
10
|
-
import
|
|
11
|
-
type
|
|
12
|
-
|
|
13
|
-
} from '../core/
|
|
10
|
+
import {
|
|
11
|
+
type FunctionalComponent,
|
|
12
|
+
defineComponent,
|
|
13
|
+
} from '../core/component.ts';
|
|
14
14
|
import { icon } from '../primitives/icons.ts';
|
|
15
|
+
import { joinClasses } from '../utilities/class-name.ts';
|
|
15
16
|
import { isPlainObject } from '../utilities/object.ts';
|
|
16
17
|
import {
|
|
17
18
|
type ResolveSchema,
|
|
@@ -65,7 +66,6 @@ interface PaginationState extends Record<string, unknown> {
|
|
|
65
66
|
total: number;
|
|
66
67
|
page: PaginationPage;
|
|
67
68
|
count: PaginationCount;
|
|
68
|
-
pageCount: number;
|
|
69
69
|
locked: boolean;
|
|
70
70
|
}
|
|
71
71
|
|
|
@@ -83,40 +83,21 @@ interface MoreItem {
|
|
|
83
83
|
type PaginationItem = PageItem | MoreItem;
|
|
84
84
|
type PageAction = 'prev' | 'next';
|
|
85
85
|
|
|
86
|
-
interface
|
|
87
|
-
|
|
88
|
-
list: HTMLElement | null;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
interface PaginationRuntime extends ComponentRuntime {
|
|
92
|
-
built: boolean;
|
|
93
|
-
itemsKey: string;
|
|
94
|
-
changeId: number;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
interface PaginationCleanupExtras {
|
|
98
|
-
state?: (() => void) | null;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
interface PaginationSnapshot {
|
|
102
|
-
total: number;
|
|
103
|
-
size: number;
|
|
104
|
-
current: number;
|
|
105
|
-
sibling: number;
|
|
106
|
-
boundary: number;
|
|
107
|
-
locked: boolean;
|
|
86
|
+
interface PaginationActions {
|
|
87
|
+
go(page: number): Pagination;
|
|
108
88
|
}
|
|
109
89
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
boundary: 1,
|
|
90
|
+
export type Pagination = FunctionalComponent<
|
|
91
|
+
ResolvedPaginationProps,
|
|
92
|
+
PaginationState,
|
|
93
|
+
HTMLElement,
|
|
94
|
+
PaginationActions
|
|
95
|
+
> & {
|
|
96
|
+
readonly pageCount: number;
|
|
118
97
|
};
|
|
119
98
|
|
|
99
|
+
const DEFAULT_PAGE: PaginationPage = { size: 10, current: 1 };
|
|
100
|
+
const DEFAULT_COUNT: PaginationCount = { sibling: 1, boundary: 1 };
|
|
120
101
|
const DEFAULT_CLASS_NAMES: PaginationClassNames = {
|
|
121
102
|
root: 'j-pagination',
|
|
122
103
|
list: 'pagination',
|
|
@@ -188,6 +169,7 @@ const PAGINATION_STATE_SCHEMA = {
|
|
|
188
169
|
total: TOTAL_RULE,
|
|
189
170
|
page: PAGE_RULE,
|
|
190
171
|
count: COUNT_RULE,
|
|
172
|
+
locked: { type: 'boolean' },
|
|
191
173
|
};
|
|
192
174
|
|
|
193
175
|
function clamp(value: number, min: number, max: number): number {
|
|
@@ -255,11 +237,9 @@ function resolvePageItems(
|
|
|
255
237
|
}
|
|
256
238
|
|
|
257
239
|
const pages = new Set<number>();
|
|
258
|
-
|
|
259
240
|
for (let page = 1; page <= Math.min(boundary, pageCount); page++) {
|
|
260
241
|
pages.add(page);
|
|
261
242
|
}
|
|
262
|
-
|
|
263
243
|
for (
|
|
264
244
|
let page = Math.max(1, current - sibling);
|
|
265
245
|
page <= Math.min(pageCount, current + sibling);
|
|
@@ -267,7 +247,6 @@ function resolvePageItems(
|
|
|
267
247
|
) {
|
|
268
248
|
pages.add(page);
|
|
269
249
|
}
|
|
270
|
-
|
|
271
250
|
for (
|
|
272
251
|
let page = Math.max(1, pageCount - boundary + 1);
|
|
273
252
|
page <= pageCount;
|
|
@@ -278,404 +257,251 @@ function resolvePageItems(
|
|
|
278
257
|
|
|
279
258
|
const sorted = Array.from(pages).sort((a, b) => a - b);
|
|
280
259
|
const items: PaginationItem[] = [];
|
|
281
|
-
|
|
282
260
|
for (const page of sorted) {
|
|
283
261
|
const previous = items[items.length - 1];
|
|
284
262
|
if (previous?.type === 'page' && page - previous.page > 1) {
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
263
|
+
items.push(
|
|
264
|
+
page - previous.page === 2
|
|
265
|
+
? createPageItem(previous.page + 1)
|
|
266
|
+
: createMoreItem(`more-${previous.page}-${page}`)
|
|
267
|
+
);
|
|
290
268
|
}
|
|
291
269
|
items.push(createPageItem(page));
|
|
292
270
|
}
|
|
293
|
-
|
|
294
271
|
return items;
|
|
295
272
|
}
|
|
296
273
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
PaginationDOM
|
|
301
|
-
> {
|
|
302
|
-
declare runtime: PaginationRuntime;
|
|
303
|
-
declare state: PaginationState;
|
|
304
|
-
declare cleanup: Component['cleanup'] & PaginationCleanupExtras;
|
|
305
|
-
|
|
306
|
-
constructor(input: PaginationProps = {}) {
|
|
307
|
-
const props = normalizeProps(input);
|
|
308
|
-
super(props);
|
|
309
|
-
|
|
310
|
-
this.dom.list = null;
|
|
311
|
-
this.cleanup.state = null;
|
|
312
|
-
|
|
313
|
-
this.runtime.built = false;
|
|
314
|
-
this.runtime.itemsKey = '';
|
|
315
|
-
this.runtime.changeId = 0;
|
|
316
|
-
|
|
317
|
-
this.state = createDeepStore({
|
|
318
|
-
total: props.total,
|
|
319
|
-
page: clonePage(props.page),
|
|
320
|
-
count: cloneCount(props.count),
|
|
321
|
-
pageCount: getPageCount(props.total, props.page.size),
|
|
322
|
-
locked: false,
|
|
323
|
-
}) as PaginationState;
|
|
274
|
+
function requirePageItem(item: PaginationItem): PageItem {
|
|
275
|
+
if (item.type !== 'page') {
|
|
276
|
+
throw new Error('Pagination.itemView: expects page item.');
|
|
324
277
|
}
|
|
278
|
+
return item;
|
|
279
|
+
}
|
|
325
280
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
281
|
+
export function createPagination(input: PaginationProps = {}): Pagination {
|
|
282
|
+
const props = normalizeProps(input);
|
|
283
|
+
const state = createDeepStore({
|
|
284
|
+
total: props.total,
|
|
285
|
+
page: clonePage(props.page),
|
|
286
|
+
count: cloneCount(props.count),
|
|
287
|
+
locked: false,
|
|
288
|
+
}) as PaginationState;
|
|
289
|
+
let changeId = 0;
|
|
290
|
+
let pagination: Pagination;
|
|
291
|
+
|
|
292
|
+
const pageCount = createMemo(() => getPageCount(state.total, state.page.size));
|
|
293
|
+
const isLocked = (): boolean => props.lock && state.locked;
|
|
294
|
+
const isPrevDisabled = (): boolean => isLocked() || state.page.current <= 1;
|
|
295
|
+
const isNextDisabled = (): boolean =>
|
|
296
|
+
isLocked() || state.page.current >= pageCount();
|
|
297
|
+
const pageItems = createMemo(() =>
|
|
298
|
+
resolvePageItems(state.page.current, pageCount(), state.count)
|
|
299
|
+
);
|
|
300
|
+
|
|
301
|
+
const unlock = (id: number): void => {
|
|
302
|
+
if (props.lock && !pagination.runtime.destroyed && id === changeId) {
|
|
303
|
+
flushSync(() => {
|
|
304
|
+
state.locked = false;
|
|
305
|
+
});
|
|
329
306
|
}
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
this.dom.root = jsx('div', {
|
|
333
|
-
className: this.props.className.root,
|
|
334
|
-
role: 'navigation',
|
|
335
|
-
'aria-label': 'Pagination',
|
|
336
|
-
'data-pagination': 'root',
|
|
337
|
-
children: jsx('ul', {
|
|
338
|
-
className: this.props.className.list,
|
|
339
|
-
'data-pagination-list': '',
|
|
340
|
-
'aria-live': 'polite',
|
|
341
|
-
}),
|
|
342
|
-
}) as HTMLElement;
|
|
343
|
-
|
|
344
|
-
this.dom.list = this.dom.root.querySelector('[data-pagination-list]');
|
|
345
|
-
this.runtime.built = true;
|
|
346
|
-
this.bindState();
|
|
347
|
-
this.bindEvents();
|
|
348
|
-
this.emit('init', this.props);
|
|
349
|
-
return this;
|
|
350
|
-
}
|
|
307
|
+
};
|
|
351
308
|
|
|
352
|
-
go(page: number):
|
|
353
|
-
|
|
309
|
+
const go = (page: number): Pagination => {
|
|
310
|
+
if (pagination.runtime.destroyed) {
|
|
311
|
+
throw new Error('Pagination.go: instance destroyed');
|
|
312
|
+
}
|
|
313
|
+
if (!pagination.runtime.built) {
|
|
314
|
+
throw new Error('Pagination.go: call build() first.');
|
|
315
|
+
}
|
|
354
316
|
validateParam(
|
|
355
317
|
'page',
|
|
356
318
|
page,
|
|
357
|
-
{
|
|
358
|
-
type: 'number',
|
|
359
|
-
integer: true,
|
|
360
|
-
},
|
|
319
|
+
{ type: 'number', integer: true },
|
|
361
320
|
'Pagination.go'
|
|
362
321
|
);
|
|
322
|
+
if (isLocked()) return pagination;
|
|
363
323
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
const
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
const shouldLock =
|
|
370
|
-
this.props.lock && typeof this.props.onChange === 'function';
|
|
371
|
-
const changeId = shouldLock
|
|
372
|
-
? ++this.runtime.changeId
|
|
373
|
-
: this.runtime.changeId;
|
|
324
|
+
const nextPage = clamp(page, 1, pageCount());
|
|
325
|
+
if (nextPage === state.page.current) return pagination;
|
|
326
|
+
const shouldLock = props.lock && typeof props.onChange === 'function';
|
|
327
|
+
const currentChangeId = shouldLock ? ++changeId : changeId;
|
|
374
328
|
|
|
375
329
|
flushSync(() => {
|
|
376
|
-
|
|
377
|
-
if (shouldLock)
|
|
330
|
+
state.page.current = nextPage;
|
|
331
|
+
if (shouldLock) state.locked = true;
|
|
378
332
|
});
|
|
379
333
|
|
|
380
|
-
if (
|
|
334
|
+
if (props.onChange) {
|
|
381
335
|
let result: void | Promise<unknown>;
|
|
382
336
|
try {
|
|
383
|
-
result =
|
|
337
|
+
result = props.onChange(nextPage, pagination);
|
|
384
338
|
} catch (error) {
|
|
385
|
-
|
|
339
|
+
unlock(currentChangeId);
|
|
386
340
|
throw error;
|
|
387
341
|
}
|
|
388
342
|
|
|
389
343
|
if (shouldLock && result && typeof result.then === 'function') {
|
|
390
|
-
const
|
|
391
|
-
void Promise.resolve(result).then(
|
|
344
|
+
const release = () => unlock(currentChangeId);
|
|
345
|
+
void Promise.resolve(result).then(release, release);
|
|
392
346
|
} else {
|
|
393
|
-
|
|
347
|
+
unlock(currentChangeId);
|
|
394
348
|
}
|
|
395
349
|
}
|
|
350
|
+
return pagination;
|
|
351
|
+
};
|
|
396
352
|
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
private bindState(): void {
|
|
401
|
-
if (this.cleanup.state) return;
|
|
402
|
-
|
|
403
|
-
this.cleanup.state = createRoot((dispose) => {
|
|
404
|
-
createEffect(() => {
|
|
405
|
-
const snapshot: PaginationSnapshot = {
|
|
406
|
-
total: this.state.total,
|
|
407
|
-
size: this.state.page.size,
|
|
408
|
-
current: this.state.page.current,
|
|
409
|
-
sibling: this.state.count.sibling,
|
|
410
|
-
boundary: this.state.count.boundary,
|
|
411
|
-
locked: this.state.locked,
|
|
412
|
-
};
|
|
413
|
-
untrack(() => this.syncState(snapshot));
|
|
414
|
-
});
|
|
415
|
-
return dispose;
|
|
416
|
-
});
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
private syncState(snapshot: PaginationSnapshot): void {
|
|
420
|
-
validateParam('total', snapshot.total, TOTAL_RULE, 'Pagination.state');
|
|
421
|
-
validateParam(
|
|
422
|
-
'page',
|
|
423
|
-
{ size: snapshot.size, current: snapshot.current },
|
|
424
|
-
PAGE_RULE,
|
|
425
|
-
'Pagination.state'
|
|
426
|
-
);
|
|
427
|
-
validateParam(
|
|
428
|
-
'count',
|
|
429
|
-
{ sibling: snapshot.sibling, boundary: snapshot.boundary },
|
|
430
|
-
COUNT_RULE,
|
|
431
|
-
'Pagination.state'
|
|
432
|
-
);
|
|
433
|
-
|
|
434
|
-
const pageCount = getPageCount(snapshot.total, snapshot.size);
|
|
435
|
-
const current = clamp(snapshot.current, 1, pageCount);
|
|
436
|
-
|
|
437
|
-
if (
|
|
438
|
-
this.state.pageCount !== pageCount ||
|
|
439
|
-
this.state.page.current !== current
|
|
440
|
-
) {
|
|
441
|
-
flushSync(() => {
|
|
442
|
-
this.state.pageCount = pageCount;
|
|
443
|
-
this.state.page.current = current;
|
|
444
|
-
});
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
this.renderItems();
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
private renderItems(): void {
|
|
451
|
-
if (!this.dom.list) return;
|
|
452
|
-
|
|
453
|
-
const items = this.getPageItems();
|
|
454
|
-
const itemsKey = [
|
|
455
|
-
this.state.page.current,
|
|
456
|
-
this.state.pageCount,
|
|
457
|
-
this.isLocked() ? 'locked' : 'unlocked',
|
|
458
|
-
...items.map((item) => item.key),
|
|
459
|
-
].join('|');
|
|
460
|
-
|
|
461
|
-
if (itemsKey === this.runtime.itemsKey) return;
|
|
462
|
-
|
|
463
|
-
this.runtime.itemsKey = itemsKey;
|
|
464
|
-
this.dom.list.textContent = '';
|
|
465
|
-
this.dom.list.append(
|
|
466
|
-
this.buildControlItem('prev'),
|
|
467
|
-
...items.map((item) => this.buildPageItem(item)),
|
|
468
|
-
this.buildControlItem('next')
|
|
469
|
-
);
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
private getPageItems(): PaginationItem[] {
|
|
473
|
-
return resolvePageItems(this.state.page.current, this.state.pageCount, {
|
|
474
|
-
sibling: this.state.count.sibling,
|
|
475
|
-
boundary: this.state.count.boundary,
|
|
476
|
-
});
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
private isLocked(): boolean {
|
|
480
|
-
return this.props.lock && this.state.locked;
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
private isPrevDisabled(): boolean {
|
|
484
|
-
return this.isLocked() || this.state.page.current <= 1;
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
private isNextDisabled(): boolean {
|
|
488
|
-
return this.isLocked() || this.state.page.current >= this.state.pageCount;
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
private buildControlItem(type: PageAction): HTMLElement {
|
|
492
|
-
const disabled =
|
|
493
|
-
type === 'prev' ? this.isPrevDisabled() : this.isNextDisabled();
|
|
353
|
+
const controlView = (type: PageAction): HTMLElement => {
|
|
354
|
+
const disabled = type === 'prev' ? isPrevDisabled : isNextDisabled;
|
|
494
355
|
return jsx('li', {
|
|
495
|
-
className:
|
|
356
|
+
className: props.className.item,
|
|
496
357
|
'data-pagination-control': type,
|
|
497
358
|
children: jsx('button', {
|
|
498
|
-
className:
|
|
359
|
+
className: props.className.button,
|
|
499
360
|
type: 'button',
|
|
500
361
|
'data-page-action': type,
|
|
501
362
|
'aria-label':
|
|
502
363
|
type === 'prev' ? 'Go to previous page' : 'Go to next page',
|
|
503
|
-
'aria-disabled': disabled ? 'true' : 'false',
|
|
504
|
-
tabindex: disabled ? '-1' : null,
|
|
364
|
+
'aria-disabled': () => (disabled() ? 'true' : 'false'),
|
|
365
|
+
tabindex: () => (disabled() ? '-1' : null),
|
|
505
366
|
disabled,
|
|
367
|
+
onClick: () => {
|
|
368
|
+
if (!disabled()) {
|
|
369
|
+
go(state.page.current + (type === 'prev' ? -1 : 1));
|
|
370
|
+
}
|
|
371
|
+
},
|
|
506
372
|
children: icon(type === 'prev' ? 'arrow-left' : 'arrow-right'),
|
|
507
373
|
}),
|
|
508
374
|
}) as HTMLElement;
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
private buildPageItem(item: PaginationItem): HTMLElement {
|
|
512
|
-
const disabled = this.isLocked();
|
|
375
|
+
};
|
|
513
376
|
|
|
514
|
-
|
|
377
|
+
const itemView = (itemAccessor: () => PaginationItem): HTMLElement => {
|
|
378
|
+
if (itemAccessor().type === 'more') {
|
|
515
379
|
return jsx('li', {
|
|
516
|
-
className:
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
380
|
+
className: () =>
|
|
381
|
+
joinClasses(
|
|
382
|
+
props.className.item,
|
|
383
|
+
itemAccessor().type === 'more' ? props.className.more : ''
|
|
384
|
+
),
|
|
385
|
+
'data-pagination-more': () =>
|
|
386
|
+
itemAccessor().type === 'more' ? itemAccessor().key : null,
|
|
520
387
|
children: jsx('span', {
|
|
521
|
-
className:
|
|
388
|
+
className: props.className.button,
|
|
522
389
|
children: icon('more'),
|
|
523
390
|
}),
|
|
524
391
|
}) as HTMLElement;
|
|
525
392
|
}
|
|
526
393
|
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
className: this.props.className.item,
|
|
530
|
-
'data-pagination-item': String(item.page),
|
|
531
|
-
children: jsx('span', {
|
|
532
|
-
className: this.props.className.current,
|
|
533
|
-
'data-current-page': String(item.page),
|
|
534
|
-
'aria-current': 'page',
|
|
535
|
-
'aria-label': `Page ${item.page}, current page`,
|
|
536
|
-
children: disabled
|
|
537
|
-
? jsx('i', {
|
|
538
|
-
className: this.props.className.loading,
|
|
539
|
-
children: icon('loader'),
|
|
540
|
-
})
|
|
541
|
-
: String(item.page),
|
|
542
|
-
}),
|
|
543
|
-
}) as HTMLElement;
|
|
544
|
-
}
|
|
394
|
+
const page = (): number => requirePageItem(itemAccessor()).page;
|
|
395
|
+
const isCurrentPage = (): boolean => page() === state.page.current;
|
|
545
396
|
|
|
546
397
|
return jsx('li', {
|
|
547
|
-
className:
|
|
548
|
-
|
|
398
|
+
className: () =>
|
|
399
|
+
joinClasses(
|
|
400
|
+
props.className.item,
|
|
401
|
+
itemAccessor().type === 'more' ? props.className.more : ''
|
|
402
|
+
),
|
|
403
|
+
'data-pagination-more': () =>
|
|
404
|
+
itemAccessor().type === 'more' ? itemAccessor().key : null,
|
|
405
|
+
'data-pagination-item': () => {
|
|
406
|
+
const item = itemAccessor();
|
|
407
|
+
return item.type === 'page' ? String(item.page) : null;
|
|
408
|
+
},
|
|
549
409
|
children: jsx('button', {
|
|
550
|
-
className:
|
|
410
|
+
className: () =>
|
|
411
|
+
isCurrentPage() ? props.className.current : props.className.button,
|
|
551
412
|
type: 'button',
|
|
552
|
-
'data-page': String(
|
|
553
|
-
'
|
|
554
|
-
'aria-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
413
|
+
'data-page': () => String(page()),
|
|
414
|
+
'data-current-page': () => (isCurrentPage() ? String(page()) : null),
|
|
415
|
+
'aria-current': () => (isCurrentPage() ? 'page' : null),
|
|
416
|
+
'aria-label': () =>
|
|
417
|
+
isCurrentPage()
|
|
418
|
+
? `Page ${page()}, current page`
|
|
419
|
+
: `Go to page ${page()}`,
|
|
420
|
+
'aria-disabled': () =>
|
|
421
|
+
isLocked() || isCurrentPage() ? 'true' : 'false',
|
|
422
|
+
tabindex: () => (isLocked() || isCurrentPage() ? '-1' : null),
|
|
423
|
+
disabled: () => isLocked() || isCurrentPage(),
|
|
424
|
+
onClick: () => {
|
|
425
|
+
const current = page();
|
|
426
|
+
if (!isLocked() && !isCurrentPage()) go(current);
|
|
427
|
+
},
|
|
428
|
+
children: () =>
|
|
429
|
+
isLocked() && isCurrentPage()
|
|
430
|
+
? jsx('i', {
|
|
431
|
+
className: props.className.loading,
|
|
432
|
+
children: icon('loader'),
|
|
433
|
+
})
|
|
434
|
+
: String(page()),
|
|
558
435
|
}),
|
|
559
436
|
}) as HTMLElement;
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
private bindEvents(): void {
|
|
563
|
-
this.unbindEvents();
|
|
564
|
-
const root = this.dom.root;
|
|
565
|
-
if (!root) return;
|
|
566
|
-
|
|
567
|
-
this.cleanup.events.on('click', root, 'click', (event) => {
|
|
568
|
-
const source = event.target;
|
|
569
|
-
if (!(source instanceof Element)) return;
|
|
570
|
-
|
|
571
|
-
const target = source.closest<HTMLElement>(
|
|
572
|
-
'[data-page], [data-page-action]'
|
|
573
|
-
);
|
|
574
|
-
if (!target || !root.contains(target)) return;
|
|
575
|
-
|
|
576
|
-
event.preventDefault();
|
|
577
|
-
|
|
578
|
-
const action = target.dataset.pageAction;
|
|
579
|
-
if (action === 'prev' && !this.isPrevDisabled()) {
|
|
580
|
-
this.go(this.state.page.current - 1);
|
|
581
|
-
return;
|
|
582
|
-
}
|
|
437
|
+
};
|
|
583
438
|
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
439
|
+
pagination = defineComponent<
|
|
440
|
+
ResolvedPaginationProps,
|
|
441
|
+
PaginationState,
|
|
442
|
+
HTMLElement,
|
|
443
|
+
PaginationActions
|
|
444
|
+
>({
|
|
445
|
+
name: 'Pagination',
|
|
446
|
+
props,
|
|
447
|
+
state,
|
|
448
|
+
actions: { go },
|
|
449
|
+
normalizeStatePatch(patch) {
|
|
450
|
+
const next = { ...patch };
|
|
451
|
+
if (Object.hasOwn(next, 'page') && isPlainObject(next.page)) {
|
|
452
|
+
next.page = { ...state.page, ...next.page } as PaginationPage;
|
|
587
453
|
}
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
this.go(Number(target.dataset.page));
|
|
454
|
+
if (Object.hasOwn(next, 'count') && isPlainObject(next.count)) {
|
|
455
|
+
next.count = { ...state.count, ...next.count } as PaginationCount;
|
|
591
456
|
}
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
flushSync(() => {
|
|
606
|
-
this.state.locked = false;
|
|
607
|
-
});
|
|
608
|
-
}
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
private assertActive(method: string): void {
|
|
612
|
-
if (this.runtime.destroyed) {
|
|
613
|
-
throw new Error(`Pagination.${method}: instance has been destroyed.`);
|
|
614
|
-
}
|
|
615
|
-
if (!this.runtime.built) {
|
|
616
|
-
throw new Error(`Pagination.${method}: call build() first.`);
|
|
617
|
-
}
|
|
618
|
-
}
|
|
619
|
-
|
|
620
|
-
protected override normalizeStatePatch(
|
|
621
|
-
patch: Partial<PaginationState>
|
|
622
|
-
): Partial<PaginationState> {
|
|
623
|
-
const nextPatch = { ...patch };
|
|
624
|
-
if (Object.hasOwn(nextPatch, 'page') && isPlainObject(nextPatch.page)) {
|
|
625
|
-
nextPatch.page = {
|
|
626
|
-
...this.state.page,
|
|
627
|
-
...(nextPatch.page as Partial<PaginationPage>),
|
|
628
|
-
};
|
|
629
|
-
}
|
|
630
|
-
if (Object.hasOwn(nextPatch, 'count') && isPlainObject(nextPatch.count)) {
|
|
631
|
-
nextPatch.count = {
|
|
632
|
-
...this.state.count,
|
|
633
|
-
...(nextPatch.count as Partial<PaginationCount>),
|
|
634
|
-
};
|
|
635
|
-
}
|
|
636
|
-
return nextPatch;
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
protected override validateStatePatch(patch: Partial<PaginationState>): void {
|
|
640
|
-
validateParam(
|
|
641
|
-
'state',
|
|
642
|
-
patch,
|
|
643
|
-
{
|
|
644
|
-
type: 'plainObject',
|
|
645
|
-
},
|
|
646
|
-
'Pagination.setState'
|
|
647
|
-
);
|
|
648
|
-
|
|
649
|
-
for (const key of Object.keys(patch)) {
|
|
650
|
-
if (!Object.hasOwn(PAGINATION_STATE_SCHEMA, key)) {
|
|
651
|
-
throw new Error(
|
|
652
|
-
`Pagination.setState: "${key}" is not a supported state key.`
|
|
457
|
+
return next;
|
|
458
|
+
},
|
|
459
|
+
validateStatePatch(patch) {
|
|
460
|
+
for (const key of Object.keys(patch)) {
|
|
461
|
+
if (!Object.hasOwn(PAGINATION_STATE_SCHEMA, key)) {
|
|
462
|
+
throw new Error(`Pagination.setState: "${key}" is not supported.`);
|
|
463
|
+
}
|
|
464
|
+
const stateKey = key as keyof typeof PAGINATION_STATE_SCHEMA;
|
|
465
|
+
validateParam(
|
|
466
|
+
key,
|
|
467
|
+
patch[key as keyof PaginationState],
|
|
468
|
+
PAGINATION_STATE_SCHEMA[stateKey],
|
|
469
|
+
'Pagination.setState'
|
|
653
470
|
);
|
|
654
471
|
}
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
472
|
+
},
|
|
473
|
+
view: () => {
|
|
474
|
+
createEffect(() => {
|
|
475
|
+
const current = clamp(state.page.current, 1, pageCount());
|
|
476
|
+
if (current !== state.page.current) state.page.current = current;
|
|
477
|
+
});
|
|
478
|
+
return jsx('div', {
|
|
479
|
+
className: props.className.root,
|
|
480
|
+
role: 'navigation',
|
|
481
|
+
'aria-label': 'Pagination',
|
|
482
|
+
'data-pagination': 'root',
|
|
483
|
+
children: jsx('ul', {
|
|
484
|
+
className: props.className.list,
|
|
485
|
+
'data-pagination-list': '',
|
|
486
|
+
'aria-live': 'polite',
|
|
487
|
+
children: [
|
|
488
|
+
controlView('prev'),
|
|
489
|
+
For({
|
|
490
|
+
each: pageItems,
|
|
491
|
+
key: (item: PaginationItem) => item.key,
|
|
492
|
+
children: (itemAccessor: () => PaginationItem) =>
|
|
493
|
+
itemView(itemAccessor),
|
|
494
|
+
}),
|
|
495
|
+
controlView('next'),
|
|
496
|
+
],
|
|
497
|
+
}),
|
|
498
|
+
}) as HTMLElement;
|
|
499
|
+
},
|
|
500
|
+
}) as Pagination;
|
|
678
501
|
|
|
679
|
-
|
|
680
|
-
|
|
502
|
+
Object.defineProperty(pagination, 'pageCount', {
|
|
503
|
+
enumerable: true,
|
|
504
|
+
get: pageCount,
|
|
505
|
+
});
|
|
506
|
+
return pagination;
|
|
681
507
|
}
|