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