vanilla-jui 1.3.11 → 1.4.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.
Files changed (106) hide show
  1. package/dist/index.d.ts +2921 -0
  2. package/dist/index.js +1 -1
  3. package/dist/index.umd.js +1 -1
  4. package/dist/style.css +1 -1
  5. package/package.json +4 -3
  6. package/src/components/accordion.ts +522 -0
  7. package/src/components/drop.ts +497 -0
  8. package/src/components/flow.ts +1579 -0
  9. package/src/components/form.ts +953 -0
  10. package/src/components/{icons.js → icons.ts} +24 -11
  11. package/src/components/menu.ts +615 -0
  12. package/src/components/{modal.js → modal.ts} +560 -237
  13. package/src/components/offcanvas.ts +457 -0
  14. package/src/components/{pagination.js → pagination.ts} +251 -76
  15. package/src/components/parabola.ts +335 -0
  16. package/src/components/{sticky.js → sticky.ts} +123 -98
  17. package/src/components/{swiper.js → swiper.ts} +510 -232
  18. package/src/components/tabs.ts +984 -0
  19. package/src/components/theme.ts +437 -0
  20. package/src/components/toast.ts +371 -0
  21. package/src/components/toc.ts +385 -0
  22. package/src/components/tooltip.ts +179 -0
  23. package/src/components/validator.ts +596 -0
  24. package/src/core/{Component.js → Component.ts} +134 -27
  25. package/src/css/accordion.css +0 -45
  26. package/src/css/animation.css +31 -1
  27. package/src/css/avatar.css +7 -33
  28. package/src/css/badge.css +11 -59
  29. package/src/css/breadcrumb.css +9 -32
  30. package/src/css/button.css +23 -224
  31. package/src/css/card.css +1 -1
  32. package/src/css/color.css +863 -1013
  33. package/src/css/color_p3.css +822 -975
  34. package/src/css/components.css +1 -0
  35. package/src/css/drop.css +7 -7
  36. package/src/css/flow.css +1 -1
  37. package/src/css/form.css +1 -1
  38. package/src/css/icon.css +7 -12
  39. package/src/css/index.css +5 -2
  40. package/src/css/loading.css +36 -20
  41. package/src/css/menu.css +1 -1
  42. package/src/css/modal.css +15 -44
  43. package/src/css/offcanvas.css +14 -40
  44. package/src/css/popup.css +4 -4
  45. package/src/css/status.css +0 -2
  46. package/src/css/swiper.css +35 -50
  47. package/src/css/tabs.css +9 -31
  48. package/src/css/tag.css +16 -77
  49. package/src/css/themes/default/accordion.css +62 -0
  50. package/src/css/themes/default/avatar.css +39 -0
  51. package/src/css/themes/default/badge.css +63 -0
  52. package/src/css/themes/default/breadcrumb.css +28 -0
  53. package/src/css/themes/default/button.css +204 -0
  54. package/src/css/themes/default/drop.css +11 -0
  55. package/src/css/themes/default/index.css +16 -0
  56. package/src/css/themes/default/loading.css +23 -0
  57. package/src/css/themes/default/modal.css +40 -0
  58. package/src/css/themes/default/offcanvas.css +13 -0
  59. package/src/css/themes/default/popup.css +7 -0
  60. package/src/css/themes/default/swiper.css +35 -0
  61. package/src/css/themes/default/tabs.css +37 -0
  62. package/src/css/themes/default/tag.css +85 -0
  63. package/src/css/themes/default/tip.css +49 -0
  64. package/src/css/themes/default/toast.css +70 -0
  65. package/src/css/themes/default/tooltip.css +14 -0
  66. package/src/css/themes/index.css +1 -0
  67. package/src/css/tip.css +20 -39
  68. package/src/css/toast.css +18 -80
  69. package/src/css/tokens.css +8 -3
  70. package/src/css/tooltip.css +10 -10
  71. package/src/index.ts +37 -0
  72. package/src/locales/{index.js → index.ts} +1 -2
  73. package/src/types.d.ts +1 -0
  74. package/src/utilities/browser.ts +131 -0
  75. package/src/utilities/cache.ts +57 -0
  76. package/src/utilities/core.ts +614 -0
  77. package/src/utilities/dom.ts +468 -0
  78. package/src/utilities/events.ts +106 -0
  79. package/src/utilities/http.ts +35 -0
  80. package/src/utilities/storage.ts +46 -0
  81. package/src/components/accordion.js +0 -373
  82. package/src/components/components.js +0 -0
  83. package/src/components/drop.js +0 -437
  84. package/src/components/flow.js +0 -1133
  85. package/src/components/form.js +0 -585
  86. package/src/components/menu.js +0 -422
  87. package/src/components/offcanvas.js +0 -332
  88. package/src/components/parabola.js +0 -295
  89. package/src/components/tabs.js +0 -746
  90. package/src/components/theme.js +0 -353
  91. package/src/components/toast.js +0 -329
  92. package/src/components/toc.js +0 -226
  93. package/src/components/tooltip.js +0 -86
  94. package/src/components/validator.js +0 -586
  95. package/src/index.js +0 -45
  96. package/src/locales/index.min.js +0 -1
  97. package/src/utilities/browser.js +0 -111
  98. package/src/utilities/cache.js +0 -159
  99. package/src/utilities/core.js +0 -502
  100. package/src/utilities/debug.js +0 -103
  101. package/src/utilities/dom.js +0 -313
  102. package/src/utilities/events.js +0 -69
  103. package/src/utilities/http.js +0 -30
  104. package/src/utilities/legacy-dom.js +0 -179
  105. package/src/utilities/storage.js +0 -46
  106. /package/src/icons/{index.js → index.ts} +0 -0
@@ -6,26 +6,195 @@ import {
6
6
  h,
7
7
  } from 'vanilla-signal';
8
8
 
9
- import Component from '../core/Component.js';
10
- import { resolveProps, validateParam } from '../utilities/core.js';
9
+ import Component, {
10
+ type ComponentDOM,
11
+ type ComponentRuntime,
12
+ type ComponentUpdateOptions,
13
+ } from '../core/Component.ts';
14
+ import {
15
+ type ResolveSchema,
16
+ isPlainObject,
17
+ resolveProps,
18
+ validateParam,
19
+ } from '../utilities/core.ts';
11
20
  import {
12
21
  all,
22
+ type DOMReference,
13
23
  isElement,
14
24
  isRenderableContent,
15
25
  normalizeContentNodes,
16
26
  q,
27
+ type RenderableContent,
17
28
  requireContainer,
18
- requireRenderDOM,
19
- } from '../utilities/dom.js';
20
- import { icon } from './icons.js';
29
+ } from '../utilities/dom.ts';
30
+ import { icon } from './icons.ts';
21
31
 
22
32
  const SWIPE_THRESHOLD = 6;
23
33
  const AUTOPLAY_DELAY_FLOOR = 16;
24
34
 
35
+ export interface SwiperClassNames {
36
+ root: string;
37
+ wrapper: string;
38
+ slide: string;
39
+ image: string;
40
+ title: string;
41
+ pagination: string;
42
+ paginationHorizontal: string;
43
+ paginationClickable: string;
44
+ paginationBulletGroup: string;
45
+ indicator: string;
46
+ bullet: string;
47
+ navigation: string;
48
+ prev: string;
49
+ next: string;
50
+ active: string;
51
+ disabled: string;
52
+ loading: string;
53
+ loaded: string;
54
+ error: string;
55
+ }
56
+
57
+ export type SwiperClassNameConfig = Partial<SwiperClassNames>;
58
+
59
+ export interface SwiperDataItem extends Record<string, unknown> {
60
+ image?: string | null;
61
+ url?: string | null;
62
+ title?: string | null;
63
+ sort?: number | null;
64
+ blank?: boolean | null;
65
+ children?: RenderableContent<SwiperSlideContext> | null;
66
+ }
67
+
68
+ interface NormalizedSwiperDataItem extends SwiperDataItem {
69
+ blank: boolean;
70
+ index: number;
71
+ }
72
+
73
+ export interface SwiperSlideContext {
74
+ swiper: Swiper;
75
+ item: NormalizedSwiperDataItem;
76
+ index: number;
77
+ }
78
+
79
+ export interface SwiperOptions extends Record<string, unknown> {
80
+ data?: SwiperDataItem[] | null;
81
+ loop?: boolean;
82
+ autoplay?: boolean;
83
+ delay?: number;
84
+ lazyload?: boolean;
85
+ pagination?: boolean;
86
+ navigation?: boolean;
87
+ speed?: number;
88
+ touchRatio?: number;
89
+ touchAngle?: number;
90
+ longSwipesMs?: number;
91
+ longSwipesRatio?: number;
92
+ preventClick?: boolean;
93
+ className?: SwiperClassNameConfig;
94
+ }
95
+
96
+ interface ResolvedSwiperOptions extends Record<string, unknown> {
97
+ data: SwiperDataItem[] | null;
98
+ loop: boolean;
99
+ autoplay: boolean;
100
+ delay: number;
101
+ lazyload: boolean;
102
+ pagination: boolean;
103
+ navigation: boolean;
104
+ speed: number;
105
+ touchRatio: number;
106
+ touchAngle: number;
107
+ longSwipesMs: number;
108
+ longSwipesRatio: number;
109
+ preventClick: boolean;
110
+ className: SwiperClassNames;
111
+ }
112
+
113
+ interface SwiperState extends Record<string, unknown> {
114
+ index: number;
115
+ trackIndex: number;
116
+ transform: number;
117
+ animating: boolean;
118
+ width: number;
119
+ }
120
+
121
+ interface SwiperDOM extends ComponentDOM {
122
+ root: HTMLElement | null;
123
+ container: DOMReference;
124
+ mountTarget: Element | null;
125
+ createdRoot: boolean;
126
+ createdSlides: boolean;
127
+ wrapper: HTMLElement | null;
128
+ slides: HTMLElement[];
129
+ pagination: HTMLElement | null;
130
+ prevButton: HTMLButtonElement | null;
131
+ nextButton: HTMLButtonElement | null;
132
+ bullets: HTMLButtonElement[];
133
+ createdPagination: boolean;
134
+ createdPrevButton: boolean;
135
+ createdNextButton: boolean;
136
+ }
137
+
138
+ interface SwipeLog {
139
+ x: number;
140
+ y: number;
141
+ time: number;
142
+ }
143
+
144
+ interface SwipePoint {
145
+ pageX?: number;
146
+ pageY?: number;
147
+ clientX?: number;
148
+ clientY?: number;
149
+ }
150
+
151
+ interface SwiperRuntime extends ComponentRuntime {
152
+ logs: SwipeLog[];
153
+ startTarget: EventTarget | null;
154
+ touching: boolean;
155
+ scrolling: boolean;
156
+ swiping: boolean;
157
+ clickPrevented: boolean;
158
+ timer: ReturnType<typeof setInterval> | null;
159
+ imageCleanups: Set<() => void>;
160
+ realCount: number;
161
+ }
162
+
163
+ interface SwiperCleanupExtras {
164
+ bindings?: (() => void) | null;
165
+ navBindings?: (() => void) | null;
166
+ }
167
+
168
+ type SwiperUpdateOptions = Partial<ComponentUpdateOptions>;
169
+ type SwiperDirection = 'prev' | 'next';
170
+ type CreatedNavigationKey = 'createdPrevButton' | 'createdNextButton';
171
+
172
+ const DEFAULT_CLASS_NAMES: SwiperClassNames = {
173
+ root: 'j-swiper',
174
+ wrapper: 'swiper-wrapper',
175
+ slide: 'swiper-slide',
176
+ image: 'swiper-image',
177
+ title: 'swiper-slide-title',
178
+ pagination: 'swiper-pagination',
179
+ paginationHorizontal: 'is-horizontal',
180
+ paginationClickable: 'is-clickable',
181
+ paginationBulletGroup: 'is-bullet',
182
+ indicator: 'swiper-pagination-indicator',
183
+ bullet: 'swiper-pagination-bullet',
184
+ navigation: 'swiper-navigation',
185
+ prev: 'is-prev',
186
+ next: 'is-next',
187
+ active: 'is-active',
188
+ disabled: 'is-disabled',
189
+ loading: 'loading',
190
+ loaded: 'loaded',
191
+ error: 'error',
192
+ };
193
+
25
194
  const SWIPER_OPTIONS_SCHEMA = {
26
195
  data: {
27
196
  default: null,
28
- validate: (value) => value == null || Array.isArray(value),
197
+ validate: (value: unknown) => value == null || Array.isArray(value),
29
198
  message: 'expects an array or null.',
30
199
  },
31
200
  loop: { default: true, type: 'boolean' },
@@ -33,7 +202,7 @@ const SWIPER_OPTIONS_SCHEMA = {
33
202
  delay: {
34
203
  default: 3000,
35
204
  type: 'number',
36
- validate: (value) => value >= 0,
205
+ validate: (value: unknown) => Number(value) >= 0,
37
206
  message: 'expects a positive number or 0.',
38
207
  },
39
208
  lazyload: { default: true, type: 'boolean' },
@@ -42,82 +211,121 @@ const SWIPER_OPTIONS_SCHEMA = {
42
211
  speed: {
43
212
  default: 300,
44
213
  type: 'number',
45
- validate: (value) => value >= 0,
214
+ validate: (value: unknown) => Number(value) >= 0,
46
215
  message: 'expects a positive number or 0.',
47
216
  },
48
217
  touchRatio: {
49
218
  default: 1,
50
219
  type: 'number',
51
- validate: (value) => value > 0,
220
+ validate: (value: unknown) => Number(value) > 0,
52
221
  message: 'expects a number greater than 0.',
53
222
  },
54
223
  touchAngle: {
55
224
  default: 45,
56
225
  type: 'number',
57
- validate: (value) => value >= 0 && value <= 90,
226
+ validate: (value: unknown) => Number(value) >= 0 && Number(value) <= 90,
58
227
  message: 'expects a number between 0 and 90.',
59
228
  },
60
229
  longSwipesMs: {
61
230
  default: 300,
62
231
  type: 'number',
63
- validate: (value) => value >= 0,
232
+ validate: (value: unknown) => Number(value) >= 0,
64
233
  message: 'expects a positive number or 0.',
65
234
  },
66
235
  longSwipesRatio: {
67
236
  default: 0.05,
68
237
  type: 'number',
69
- validate: (value) => value >= 0 && value <= 1,
238
+ validate: (value: unknown) => Number(value) >= 0 && Number(value) <= 1,
70
239
  message: 'expects a number between 0 and 1.',
71
240
  },
72
241
  preventClick: { default: true, type: 'boolean' },
73
- };
242
+ className: {
243
+ default: DEFAULT_CLASS_NAMES,
244
+ type: 'object',
245
+ normalize: (value: unknown) => ({
246
+ ...DEFAULT_CLASS_NAMES,
247
+ ...(value && typeof value === 'object' ? value : {}),
248
+ }),
249
+ },
250
+ } satisfies ResolveSchema<SwiperOptions>;
74
251
 
75
252
  const SWIPER_DATA_ITEM_RULE = {
76
- validate: (value) => {
253
+ validate: (value: unknown) => {
77
254
  if (!value || typeof value !== 'object' || Array.isArray(value)) {
78
255
  return false;
79
256
  }
257
+ const item = value as SwiperDataItem;
80
258
 
81
259
  return (
82
- (value.image == null || typeof value.image === 'string') &&
83
- (value.url == null || typeof value.url === 'string') &&
84
- (value.title == null || typeof value.title === 'string') &&
85
- (value.sort == null || typeof value.sort === 'number') &&
86
- (value.blank == null || typeof value.blank === 'boolean') &&
87
- (value.children == null || isRenderableContent(value.children))
260
+ (item.image == null || typeof item.image === 'string') &&
261
+ (item.url == null || typeof item.url === 'string') &&
262
+ (item.title == null || typeof item.title === 'string') &&
263
+ (item.sort == null || typeof item.sort === 'number') &&
264
+ (item.blank == null || typeof item.blank === 'boolean') &&
265
+ (item.children == null || isRenderableContent(item.children))
88
266
  );
89
267
  },
90
268
  message:
91
269
  'expects items with optional image, url, title, sort, blank and children fields.',
92
270
  };
93
271
 
94
- function isInteractiveTarget(target) {
95
- return !!target?.closest?.(
96
- 'a, button, input, textarea, select, label, [data-swiper-ignore]'
272
+ function isInteractiveTarget(target: EventTarget | null): boolean {
273
+ return (
274
+ target instanceof Element &&
275
+ !!target.closest(
276
+ 'a, button, input, textarea, select, label, [data-swiper-ignore]'
277
+ )
97
278
  );
98
279
  }
99
280
 
100
- function normalizeNumber(value, fallback = 0) {
281
+ function normalizeNumber(value: unknown, fallback = 0): number {
101
282
  const number = Number(value);
102
283
  return Number.isFinite(number) ? number : fallback;
103
284
  }
104
285
 
105
- function getRenderableNodes(content, context) {
286
+ function getRenderableNodes(
287
+ content: RenderableContent<SwiperSlideContext>,
288
+ context: SwiperSlideContext
289
+ ): Node[] {
106
290
  return normalizeContentNodes(content, context);
107
291
  }
108
292
 
109
- function resolveSwiperRoot(container) {
110
- const roots = all('.j-swiper', container);
293
+ function normalizeOptions(options: SwiperOptions = {}): ResolvedSwiperOptions {
294
+ const props = resolveProps(
295
+ options,
296
+ SWIPER_OPTIONS_SCHEMA,
297
+ 'Swiper.options'
298
+ ) as ResolvedSwiperOptions;
299
+ return {
300
+ ...props,
301
+ className: { ...props.className },
302
+ };
303
+ }
304
+
305
+ function resolveSwiperRoot(container: Element): HTMLElement {
306
+ const roots = all<HTMLElement>('[data-swiper="root"]', container);
111
307
  validateParam(
112
308
  'container',
113
309
  roots,
114
310
  {
115
- validate: (value) => value.length === 1,
116
- message: 'expects exactly one .j-swiper descendant.',
311
+ validate: (value: unknown) => Array.isArray(value) && value.length === 1,
312
+ message: 'expects exactly one [data-swiper="root"] descendant.',
117
313
  },
118
314
  'Swiper'
119
315
  );
120
- return roots[0];
316
+ return roots[0] as HTMLElement;
317
+ }
318
+
319
+ function classList(...tokens: Array<string | null | undefined>): string {
320
+ return tokens.filter(Boolean).join(' ');
321
+ }
322
+
323
+ function setDataMarker(element: Element, name: string, value = ''): void {
324
+ element.setAttribute(name, value);
325
+ }
326
+
327
+ function isObjectPatch(value: unknown): value is SwiperOptions {
328
+ return isPlainObject(value);
121
329
  }
122
330
 
123
331
  /**
@@ -126,18 +334,26 @@ function resolveSwiperRoot(container) {
126
334
  * 支持链接 slide、图片 lazyload、分页、导航、loop 和桌面/移动端拖拽滑动。
127
335
  * 使用 vanilla-signal 响应式管理 pagination 和 navigation 状态。
128
336
  */
129
- export class Swiper extends Component {
337
+ export class Swiper extends Component<
338
+ ResolvedSwiperOptions,
339
+ SwiperState,
340
+ SwiperDOM
341
+ > {
342
+ declare props: ResolvedSwiperOptions;
343
+ declare state: SwiperState;
344
+ declare dom: SwiperDOM;
345
+ declare runtime: SwiperRuntime;
346
+ declare cleanup: Component['cleanup'] & SwiperCleanupExtras;
347
+
348
+ private _built: boolean;
349
+
130
350
  /**
131
351
  * 创建轮播实例。
132
352
  * @param {Element|Node|string|Array} container 挂载容器、选择器或 JSX/h 返回节点。
133
353
  * @param {object} [options={}] Swiper 配置。
134
354
  */
135
- constructor(container, options = {}) {
136
- const resolvedOptions = resolveProps(
137
- options,
138
- SWIPER_OPTIONS_SCHEMA,
139
- 'Swiper.options'
140
- );
355
+ constructor(container: DOMReference, options: SwiperOptions = {}) {
356
+ const resolvedOptions = normalizeOptions(options);
141
357
 
142
358
  super(resolvedOptions);
143
359
 
@@ -181,13 +397,11 @@ export class Swiper extends Component {
181
397
  * 构建或绑定 Swiper DOM。
182
398
  * @returns {Swiper} 当前实例。
183
399
  */
184
- build() {
400
+ build(): this {
185
401
  if (this.runtime.destroyed)
186
402
  throw new Error('Swiper.build: instance destroyed');
187
403
  if (this._built) return this;
188
404
 
189
- requireRenderDOM('Swiper');
190
-
191
405
  const hasData = Array.isArray(this.props.data);
192
406
  const root = requireContainer(this.dom.container, 'Swiper');
193
407
 
@@ -196,7 +410,10 @@ export class Swiper extends Component {
196
410
 
197
411
  if (hasData) {
198
412
  mountTarget = root;
199
- mountRoot = h('div', { className: 'j-swiper' });
413
+ mountRoot = h('div', {
414
+ className: this.props.className.root,
415
+ 'data-swiper': 'root',
416
+ }) as HTMLElement;
200
417
  mountTarget.appendChild(mountRoot);
201
418
  } else {
202
419
  mountRoot = resolveSwiperRoot(root);
@@ -210,22 +427,34 @@ export class Swiper extends Component {
210
427
 
211
428
  const wrapper = hasData
212
429
  ? this.createDataView(mountRoot, this.props.data)
213
- : q('.swiper-wrapper', mountRoot);
430
+ : q<HTMLElement>('[data-swiper-wrapper]', mountRoot);
214
431
  validateParam(
215
432
  'wrapper',
216
433
  wrapper,
217
434
  {
218
435
  validate: isElement,
219
- message: 'expects .swiper-wrapper in the root element.',
436
+ message: 'expects [data-swiper-wrapper] in the root element.',
220
437
  },
221
438
  'Swiper'
222
439
  );
223
440
 
224
- this.dom.wrapper = wrapper;
225
- this.dom.slides = all('.swiper-slide', wrapper);
226
- this.dom.pagination = q('.swiper-pagination', mountRoot);
227
- this.dom.prevButton = q('.swiper-navigation.is-prev', mountRoot);
228
- this.dom.nextButton = q('.swiper-navigation.is-next', mountRoot);
441
+ this.dom.wrapper = wrapper as HTMLElement;
442
+ this.dom.slides = all<HTMLElement>(
443
+ '[data-swiper-slide]',
444
+ this.dom.wrapper
445
+ );
446
+ this.dom.pagination = q<HTMLElement>(
447
+ '[data-swiper-pagination]',
448
+ mountRoot
449
+ );
450
+ this.dom.prevButton = q<HTMLButtonElement>(
451
+ '[data-action="prev"]',
452
+ mountRoot
453
+ );
454
+ this.dom.nextButton = q<HTMLButtonElement>(
455
+ '[data-action="next"]',
456
+ mountRoot
457
+ );
229
458
  this.dom.bullets = [];
230
459
  this.dom.createdPagination = false;
231
460
  this.dom.createdPrevButton = false;
@@ -244,47 +473,51 @@ export class Swiper extends Component {
244
473
  return this;
245
474
  }
246
475
 
247
- set index(v) {
476
+ set index(v: unknown) {
248
477
  this.setState({ index: normalizeNumber(v) });
249
478
  }
250
479
 
251
- set trackIndex(v) {
480
+ set trackIndex(v: unknown) {
252
481
  this.setState({ trackIndex: normalizeNumber(v) });
253
482
  }
254
483
 
255
- set transform(v) {
484
+ set transform(v: unknown) {
256
485
  this.setState({ transform: normalizeNumber(v) });
257
486
  }
258
487
 
259
- set animating(v) {
488
+ set animating(v: unknown) {
260
489
  this.setState({ animating: !!v });
261
490
  }
262
491
 
263
- set width(v) {
492
+ set width(v: unknown) {
264
493
  this.setState({ width: normalizeNumber(v) });
265
494
  }
266
495
 
267
- get realCount() {
496
+ get realCount(): number {
268
497
  if (this.runtime?.destroyed) return 0;
269
498
  return this.runtime?.realCount || 0;
270
499
  }
271
500
 
272
- get realIndex() {
501
+ get realIndex(): number {
273
502
  return this.toRealIndex();
274
503
  }
275
504
 
276
- assertBuilt(method) {
505
+ assertBuilt(method: string): void {
277
506
  if (!this._built) {
278
507
  throw new Error(`Swiper.${method}: call build() first.`);
279
508
  }
280
509
  }
281
510
 
282
- createDataView(root, data) {
511
+ createDataView(
512
+ root: HTMLElement,
513
+ data: SwiperDataItem[] | null
514
+ ): HTMLElement {
283
515
  const items = this.normalizeData(data);
284
516
  const wrapper = h('div', {
285
- className: 'swiper-wrapper',
517
+ className: this.props.className.wrapper,
518
+ 'data-swiper-wrapper': '',
286
519
  'aria-live': 'polite',
287
- });
520
+ }) as HTMLElement;
288
521
  root.textContent = '';
289
522
 
290
523
  items.forEach((item, index) => {
@@ -295,7 +528,9 @@ export class Swiper extends Component {
295
528
  return wrapper;
296
529
  }
297
530
 
298
- normalizeData(data) {
531
+ normalizeData(data: SwiperDataItem[] | null): NormalizedSwiperDataItem[] {
532
+ if (!Array.isArray(data)) return [];
533
+
299
534
  data.forEach((item, index) => {
300
535
  validateParam(
301
536
  String(index),
@@ -305,7 +540,7 @@ export class Swiper extends Component {
305
540
  );
306
541
  });
307
542
 
308
- const items = data.map((item, index) => ({
543
+ const items: NormalizedSwiperDataItem[] = data.map((item, index) => ({
309
544
  ...item,
310
545
  blank: item.blank !== false,
311
546
  index,
@@ -320,15 +555,16 @@ export class Swiper extends Component {
320
555
  });
321
556
  }
322
557
 
323
- createDataSlide(item, index) {
558
+ createDataSlide(item: NormalizedSwiperDataItem, index: number): HTMLElement {
324
559
  const slide = h(item.url ? 'a' : 'div', {
325
- className: 'swiper-slide',
560
+ className: this.props.className.slide,
326
561
  href: item.url || undefined,
327
562
  target: item.url ? (item.blank ? '_blank' : '_self') : undefined,
563
+ 'data-swiper-slide': String(index),
328
564
  'data-swiper-index': String(index),
329
565
  role: 'group',
330
566
  'aria-label': `Slide ${index + 1}`,
331
- });
567
+ }) as HTMLElement;
332
568
 
333
569
  if (item.children != null) {
334
570
  slide.append(
@@ -339,10 +575,10 @@ export class Swiper extends Component {
339
575
 
340
576
  if (item.image) {
341
577
  const img = h('img', {
342
- className: 'swiper-image',
578
+ className: this.props.className.image,
343
579
  alt: item.title || '',
344
580
  loading: 'lazy',
345
- });
581
+ }) as HTMLImageElement;
346
582
  if (this.props.lazyload) img.dataset.lazy = item.image;
347
583
  else img.src = item.image;
348
584
  slide.appendChild(img);
@@ -351,16 +587,17 @@ export class Swiper extends Component {
351
587
  if (item.title) {
352
588
  slide.appendChild(
353
589
  h('span', {
354
- className: 'swiper-slide-title',
590
+ className: this.props.className.title,
355
591
  children: item.title,
356
- })
592
+ }) as HTMLElement
357
593
  );
358
594
  }
359
595
 
360
596
  return slide;
361
597
  }
362
598
 
363
- onInit() {
599
+ protected onInit(): void {
600
+ if (!this.dom.root || !this.dom.wrapper) return;
364
601
  this.dom.wrapper.setAttribute('aria-live', 'polite');
365
602
  this.updateSize();
366
603
 
@@ -380,7 +617,7 @@ export class Swiper extends Component {
380
617
  if (this.props.autoplay) this.play();
381
618
  }
382
619
 
383
- onDestroy() {
620
+ protected onDestroy(): void {
384
621
  this.pause();
385
622
  this.clearImageCleanups();
386
623
  this.cleanup.events.clear();
@@ -399,21 +636,27 @@ export class Swiper extends Component {
399
636
  this._built = false;
400
637
  }
401
638
 
402
- updateSize() {
639
+ updateSize(): void {
403
640
  this.assertBuilt('updateSize');
641
+ if (!this.dom.root) return;
404
642
  this.width = this.dom.root.clientWidth || this.dom.root.offsetWidth;
405
643
  }
406
644
 
407
- refreshSlides() {
408
- this.dom.slides = all('.swiper-slide', this.dom.wrapper);
645
+ refreshSlides(): void {
646
+ if (!this.dom.wrapper) return;
647
+ this.dom.slides = all<HTMLElement>('[data-swiper-slide]', this.dom.wrapper);
409
648
  this.runtime.realCount = this.dom.slides.filter(
410
649
  (slide) => !slide.hasAttribute('data-clone')
411
650
  ).length;
412
651
  }
413
652
 
414
- initLoop() {
415
- const first = this.dom.slides[0].cloneNode(true);
416
- const last = this.dom.slides[this.dom.slides.length - 1].cloneNode(true);
653
+ initLoop(): void {
654
+ if (!this.dom.wrapper || this.dom.slides.length === 0) return;
655
+
656
+ const first = this.dom.slides[0].cloneNode(true) as HTMLElement;
657
+ const last = this.dom.slides[this.dom.slides.length - 1].cloneNode(
658
+ true
659
+ ) as HTMLElement;
417
660
 
418
661
  first.setAttribute('data-clone', '');
419
662
  last.setAttribute('data-clone', '');
@@ -425,14 +668,16 @@ export class Swiper extends Component {
425
668
  this.refreshSlides();
426
669
  }
427
670
 
428
- setupStyles() {
671
+ setupStyles(): void {
672
+ if (!this.dom.root) return;
429
673
  this.dom.root.style.setProperty(
430
674
  '--swiper-slide-width',
431
675
  `${this.state.width}px`
432
676
  );
433
677
  }
434
678
 
435
- reInitView() {
679
+ reInitView(): void {
680
+ if (!this.dom.wrapper) return;
436
681
  this.pause();
437
682
  this.clearImageCleanups();
438
683
  this.cleanup.bindings?.();
@@ -465,7 +710,7 @@ export class Swiper extends Component {
465
710
  if (this.props.autoplay) this.play();
466
711
  }
467
712
 
468
- clearPagination() {
713
+ clearPagination(): void {
469
714
  this.cleanup.bindings?.();
470
715
  this.cleanup.bindings = null;
471
716
  this.dom.bullets.forEach((_, index) => {
@@ -482,7 +727,7 @@ export class Swiper extends Component {
482
727
  }
483
728
  }
484
729
 
485
- clearNavigation() {
730
+ clearNavigation(): void {
486
731
  this.cleanup.navBindings?.();
487
732
  this.cleanup.navBindings = null;
488
733
  this.cleanup.events.off('nav:prev');
@@ -499,89 +744,76 @@ export class Swiper extends Component {
499
744
  }
500
745
  }
501
746
 
502
- bindEvents() {
747
+ bindEvents(): void {
748
+ const root = this.dom.root;
749
+ const wrapper = this.dom.wrapper;
750
+ if (!root || !wrapper) return;
751
+
503
752
  this.cleanup.events.on(
504
753
  'touchstart',
505
- this.dom.wrapper,
754
+ wrapper,
506
755
  'touchstart',
507
756
  (event) => {
508
- if (event.touches[0]) this.onStart(event.touches[0], event.target);
757
+ const touchEvent = event as TouchEvent;
758
+ if (touchEvent.touches[0])
759
+ this.onStart(touchEvent.touches[0], touchEvent.target);
509
760
  },
510
761
  { passive: true }
511
762
  );
512
763
  this.cleanup.events.on(
513
764
  'touchmove',
514
- this.dom.wrapper,
765
+ wrapper,
515
766
  'touchmove',
516
767
  (event) => {
517
- if (event.touches[0]) this.onMove(event.touches[0], event);
768
+ const touchEvent = event as TouchEvent;
769
+ if (touchEvent.touches[0]) this.onMove(touchEvent.touches[0], event);
518
770
  },
519
771
  { passive: false }
520
772
  );
521
- this.cleanup.events.on(
522
- 'touchend',
523
- this.dom.wrapper,
524
- 'touchend',
525
- (event) => {
526
- if (event.changedTouches[0]) this.pushLog(event.changedTouches[0]);
527
- this.onEnd();
528
- }
529
- );
530
- this.cleanup.events.on(
531
- 'touchcancel',
532
- this.dom.wrapper,
533
- 'touchcancel',
534
- () => {
535
- this.resetDrag(true);
536
- }
537
- );
773
+ this.cleanup.events.on('touchend', wrapper, 'touchend', (event) => {
774
+ const touchEvent = event as TouchEvent;
775
+ if (touchEvent.changedTouches[0])
776
+ this.pushLog(touchEvent.changedTouches[0]);
777
+ this.onEnd();
778
+ });
779
+ this.cleanup.events.on('touchcancel', wrapper, 'touchcancel', () => {
780
+ this.resetDrag(true);
781
+ });
538
782
  this.cleanup.events.on('window:touchcancel', window, 'touchcancel', () => {
539
783
  this.resetDrag(true);
540
784
  });
541
- this.cleanup.events.on(
542
- 'mousedown',
543
- this.dom.wrapper,
544
- 'mousedown',
545
- (event) => {
546
- if (event.button !== 0) return;
547
- this.onStart(event, event.target);
548
- this.dom.wrapper.style.cursor = 'grabbing';
549
- }
550
- );
551
- this.cleanup.events.on(
552
- 'mousemove',
553
- this.dom.wrapper,
554
- 'mousemove',
555
- (event) => {
556
- if (event.buttons === 1) this.onMove(event, event);
557
- }
558
- );
785
+ this.cleanup.events.on('mousedown', wrapper, 'mousedown', (event) => {
786
+ const mouseEvent = event as MouseEvent;
787
+ if (mouseEvent.button !== 0) return;
788
+ this.onStart(mouseEvent, mouseEvent.target);
789
+ wrapper.style.cursor = 'grabbing';
790
+ });
791
+ this.cleanup.events.on('mousemove', wrapper, 'mousemove', (event) => {
792
+ const mouseEvent = event as MouseEvent;
793
+ if (mouseEvent.buttons === 1) this.onMove(mouseEvent, event);
794
+ });
559
795
  this.cleanup.events.on('window:mousemove', window, 'mousemove', (event) => {
560
- if (this.runtime.touching && event.buttons === 1)
561
- this.onMove(event, event);
796
+ const mouseEvent = event as MouseEvent;
797
+ if (this.runtime.touching && mouseEvent.buttons === 1)
798
+ this.onMove(mouseEvent, event);
562
799
  });
563
- this.cleanup.events.on('mouseup', this.dom.wrapper, 'mouseup', (event) => {
564
- this.dom.wrapper.style.cursor = 'grab';
565
- this.pushLog(event);
800
+ this.cleanup.events.on('mouseup', wrapper, 'mouseup', (event) => {
801
+ wrapper.style.cursor = 'grab';
802
+ this.pushLog(event as MouseEvent);
566
803
  this.onEnd();
567
804
  });
568
805
  this.cleanup.events.on('window:mouseup', window, 'mouseup', (event) => {
569
806
  if (!this.runtime.touching) return;
570
- this.dom.wrapper.style.cursor = 'grab';
571
- this.pushLog(event);
807
+ wrapper.style.cursor = 'grab';
808
+ this.pushLog(event as MouseEvent);
572
809
  this.onEnd();
573
810
  });
574
- this.cleanup.events.on(
575
- 'wrapper:mouseleave',
576
- this.dom.wrapper,
577
- 'mouseleave',
578
- () => {
579
- this.dom.wrapper.style.cursor = 'grab';
580
- }
581
- );
811
+ this.cleanup.events.on('wrapper:mouseleave', wrapper, 'mouseleave', () => {
812
+ wrapper.style.cursor = 'grab';
813
+ });
582
814
  this.cleanup.events.on(
583
815
  'click',
584
- this.dom.wrapper,
816
+ wrapper,
585
817
  'click',
586
818
  (event) => {
587
819
  if (!this.runtime.clickPrevented || !this.props.preventClick) return;
@@ -591,30 +823,24 @@ export class Swiper extends Component {
591
823
  },
592
824
  { capture: true }
593
825
  );
594
- this.cleanup.events.on(
595
- 'dragstart',
596
- this.dom.wrapper,
597
- 'dragstart',
598
- (event) => event.preventDefault()
826
+ this.cleanup.events.on('dragstart', wrapper, 'dragstart', (event) =>
827
+ event.preventDefault()
599
828
  );
600
- this.cleanup.events.on(
601
- 'transitionend',
602
- this.dom.wrapper,
603
- 'transitionend',
604
- (event) => this.onTransitionEnd(event)
829
+ this.cleanup.events.on('transitionend', wrapper, 'transitionend', (event) =>
830
+ this.onTransitionEnd(event)
605
831
  );
606
832
  this.cleanup.events.on('window:resize', window, 'resize', () =>
607
833
  this.update(null, { force: true })
608
834
  );
609
- this.cleanup.events.on('root:mouseenter', this.dom.root, 'mouseenter', () =>
835
+ this.cleanup.events.on('root:mouseenter', root, 'mouseenter', () =>
610
836
  this.pause()
611
837
  );
612
- this.cleanup.events.on('root:mouseleave', this.dom.root, 'mouseleave', () =>
838
+ this.cleanup.events.on('root:mouseleave', root, 'mouseleave', () =>
613
839
  this.resume()
614
840
  );
615
841
  }
616
842
 
617
- onStart(point, target = null) {
843
+ onStart(point: SwipePoint, target: EventTarget | null = null): void {
618
844
  if (this.state.animating) return;
619
845
 
620
846
  this.runtime.logs = [];
@@ -626,10 +852,10 @@ export class Swiper extends Component {
626
852
  this.runtime.clickPrevented = false;
627
853
 
628
854
  this.pause();
629
- this.dom.wrapper.style.transition = 'none';
855
+ if (this.dom.wrapper) this.dom.wrapper.style.transition = 'none';
630
856
  }
631
857
 
632
- onMove(point, event) {
858
+ onMove(point: SwipePoint, event: Event): void {
633
859
  if (
634
860
  !this.runtime.touching ||
635
861
  this.runtime.scrolling ||
@@ -668,7 +894,7 @@ export class Swiper extends Component {
668
894
  }
669
895
  }
670
896
 
671
- onEnd() {
897
+ onEnd(): void {
672
898
  if (!this.runtime.touching) return;
673
899
 
674
900
  this.runtime.touching = false;
@@ -706,7 +932,7 @@ export class Swiper extends Component {
706
932
  this.resume();
707
933
  }
708
934
 
709
- resetDrag(animate = true) {
935
+ resetDrag(animate = true): void {
710
936
  const target =
711
937
  this.state.trackIndex === 0
712
938
  ? 0
@@ -723,10 +949,12 @@ export class Swiper extends Component {
723
949
  this.resume();
724
950
  }
725
951
 
726
- onTransitionEnd(event) {
952
+ onTransitionEnd(event?: Event): void {
727
953
  if (
728
954
  event &&
729
- (event.target !== this.dom.wrapper || event.propertyName !== 'transform')
955
+ (event.target !== this.dom.wrapper ||
956
+ !('propertyName' in event) ||
957
+ event.propertyName !== 'transform')
730
958
  )
731
959
  return;
732
960
 
@@ -743,7 +971,7 @@ export class Swiper extends Component {
743
971
  }
744
972
  }
745
973
 
746
- pushLog(point) {
974
+ pushLog(point: SwipePoint): void {
747
975
  this.runtime.logs.push({
748
976
  x: point.pageX ?? point.clientX ?? 0,
749
977
  y: point.pageY ?? point.clientY ?? 0,
@@ -752,7 +980,7 @@ export class Swiper extends Component {
752
980
  if (this.runtime.logs.length > 5) this.runtime.logs.shift();
753
981
  }
754
982
 
755
- getDuration() {
983
+ getDuration(): number {
756
984
  if (this.runtime.logs.length === 0) return 0;
757
985
  return (
758
986
  this.runtime.logs[this.runtime.logs.length - 1].time -
@@ -760,14 +988,14 @@ export class Swiper extends Component {
760
988
  );
761
989
  }
762
990
 
763
- getOffset() {
991
+ getOffset(): { x: number; y: number } {
764
992
  if (this.runtime.logs.length === 0) return { x: 0, y: 0 };
765
993
  const first = this.runtime.logs[0];
766
994
  const last = this.runtime.logs[this.runtime.logs.length - 1];
767
995
  return { x: last.x - first.x, y: last.y - first.y };
768
996
  }
769
997
 
770
- toRealIndex(index = this.state.trackIndex) {
998
+ toRealIndex(index = this.state.trackIndex): number {
771
999
  if (!this.realCount) return 0;
772
1000
  if (!this.props.loop) return index;
773
1001
  if (index === 0) return this.realCount - 1;
@@ -775,13 +1003,13 @@ export class Swiper extends Component {
775
1003
  return index - 1;
776
1004
  }
777
1005
 
778
- trackIndexForRealIndex(index) {
1006
+ trackIndexForRealIndex(index: number): number {
779
1007
  if (!this.realCount) return 0;
780
1008
  const target = Math.max(0, Math.min(index, this.realCount - 1));
781
1009
  return this.props.loop && this.realCount > 1 ? target + 1 : target;
782
1010
  }
783
1011
 
784
- setTrackIndex(trackIndex, animate = true) {
1012
+ setTrackIndex(trackIndex: number, animate: boolean | null = true): void {
785
1013
  const target = normalizeNumber(trackIndex);
786
1014
  this.setState({
787
1015
  trackIndex: target,
@@ -791,11 +1019,11 @@ export class Swiper extends Component {
791
1019
  if (animate != null) this.render(animate);
792
1020
  }
793
1021
 
794
- slideTo(index) {
1022
+ slideTo(index: number): void {
795
1023
  return this.slideToTrack(this.trackIndexForRealIndex(index));
796
1024
  }
797
1025
 
798
- slideToTrack(idx) {
1026
+ slideToTrack(idx: number): void {
799
1027
  this.assertBuilt('slideToTrack');
800
1028
  if (this.state.animating) return;
801
1029
  if (this.dom.slides.length === 0) return;
@@ -815,20 +1043,21 @@ export class Swiper extends Component {
815
1043
  if (this.props.lazyload) this.loadImages();
816
1044
  }
817
1045
 
818
- next() {
1046
+ next(): void {
819
1047
  this.assertBuilt('next');
820
1048
  if (this.state.animating) return;
821
1049
  this.slideToTrack(this.state.trackIndex + 1);
822
1050
  }
823
1051
 
824
- prev() {
1052
+ prev(): void {
825
1053
  this.assertBuilt('prev');
826
1054
  if (this.state.animating) return;
827
1055
  this.slideToTrack(this.state.trackIndex - 1);
828
1056
  }
829
1057
 
830
- render(animate) {
1058
+ render(animate: boolean): void {
831
1059
  this.assertBuilt('render');
1060
+ if (!this.dom.wrapper) return;
832
1061
  if (animate) {
833
1062
  this.dom.wrapper.style.transition = `transform ${this.props.speed}ms cubic-bezier(0.25, 0.46, 0.45, 0.94)`;
834
1063
  this.animating = true;
@@ -840,7 +1069,7 @@ export class Swiper extends Component {
840
1069
  this.dom.wrapper.style.transform = `translate3d(${this.state.transform}px, 0, 0)`;
841
1070
  }
842
1071
 
843
- loadImages() {
1072
+ loadImages(): void {
844
1073
  this.assertBuilt('loadImages');
845
1074
  const indices = [this.state.trackIndex];
846
1075
  if (this.state.trackIndex > 0) indices.push(this.state.trackIndex - 1);
@@ -851,11 +1080,11 @@ export class Swiper extends Component {
851
1080
  const slide = this.dom.slides[index];
852
1081
  if (!slide) return;
853
1082
 
854
- const img = q('img[data-lazy]', slide);
1083
+ const img = q<HTMLImageElement>('img[data-lazy]', slide);
855
1084
  if (!img || img.src) return;
856
1085
 
857
- img.classList.add('loading');
858
- img.src = img.dataset.lazy;
1086
+ img.classList.add(this.props.className.loading);
1087
+ img.src = img.dataset.lazy || '';
859
1088
  const cleanup = () => {
860
1089
  img.onload = null;
861
1090
  img.onerror = null;
@@ -863,47 +1092,63 @@ export class Swiper extends Component {
863
1092
  };
864
1093
  this.runtime.imageCleanups.add(cleanup);
865
1094
  img.onload = () => {
866
- img.classList.remove('loading');
867
- img.classList.add('loaded');
1095
+ img.classList.remove(this.props.className.loading);
1096
+ img.classList.add(this.props.className.loaded);
868
1097
  cleanup();
869
1098
  };
870
1099
  img.onerror = () => {
871
- img.classList.remove('loading');
872
- img.classList.add('error');
1100
+ img.classList.remove(this.props.className.loading);
1101
+ img.classList.add(this.props.className.error);
873
1102
  cleanup();
874
1103
  };
875
1104
  });
876
1105
  }
877
1106
 
878
- clearImageCleanups() {
1107
+ clearImageCleanups(): void {
879
1108
  this.runtime.imageCleanups?.forEach((cleanup) => cleanup());
880
1109
  this.runtime.imageCleanups?.clear();
881
1110
  }
882
1111
 
883
- initPagination() {
1112
+ initPagination(): void {
1113
+ if (!this.dom.root) return;
1114
+
884
1115
  if (!this.dom.pagination) {
885
1116
  this.dom.pagination = h('div', {
886
- className: 'swiper-pagination is-horizontal is-clickable is-bullet',
887
- });
1117
+ className: classList(
1118
+ this.props.className.pagination,
1119
+ this.props.className.paginationHorizontal,
1120
+ this.props.className.paginationClickable,
1121
+ this.props.className.paginationBulletGroup
1122
+ ),
1123
+ 'data-swiper-pagination': '',
1124
+ }) as HTMLElement;
888
1125
  this.dom.root.appendChild(this.dom.pagination);
889
1126
  this.dom.createdPagination = true;
890
1127
  } else {
891
1128
  this.dom.pagination.classList.add(
892
- 'is-horizontal',
893
- 'is-clickable',
894
- 'is-bullet'
1129
+ this.props.className.paginationHorizontal,
1130
+ this.props.className.paginationClickable,
1131
+ this.props.className.paginationBulletGroup
895
1132
  );
1133
+ setDataMarker(this.dom.pagination, 'data-swiper-pagination');
896
1134
  }
897
1135
 
898
- this.dom.pagination.textContent = '';
1136
+ const pagination = this.dom.pagination;
1137
+ if (!pagination) return;
1138
+
1139
+ pagination.textContent = '';
899
1140
  this.dom.bullets = [];
900
1141
 
901
1142
  for (let index = 0; index < this.realCount; index++) {
902
1143
  const bullet = h('button', {
903
1144
  type: 'button',
904
- className: 'swiper-pagination-indicator swiper-pagination-bullet',
1145
+ className: classList(
1146
+ this.props.className.indicator,
1147
+ this.props.className.bullet
1148
+ ),
1149
+ 'data-swiper-bullet': String(index),
905
1150
  'aria-label': `Go to slide ${index + 1}`,
906
- });
1151
+ }) as HTMLButtonElement;
907
1152
  this.cleanup.events.on(`bullet:${index}:click`, bullet, 'click', () => {
908
1153
  this.slideTo(index);
909
1154
  });
@@ -912,18 +1157,22 @@ export class Swiper extends Component {
912
1157
  bullet,
913
1158
  'keydown',
914
1159
  (event) => {
915
- if (event.key !== 'Enter' && event.key !== ' ') return;
1160
+ const keyboardEvent = event as KeyboardEvent;
1161
+ if (keyboardEvent.key !== 'Enter' && keyboardEvent.key !== ' ')
1162
+ return;
916
1163
  event.preventDefault();
917
1164
  this.slideTo(index);
918
1165
  }
919
1166
  );
920
1167
  this.dom.bullets.push(bullet);
921
- this.dom.pagination.appendChild(bullet);
1168
+ pagination.appendChild(bullet);
922
1169
  }
923
1170
 
924
1171
  this.cleanup.bindings = createRoot((dispose) => {
925
1172
  this.dom.bullets.forEach((bullet, i) => {
926
- bindClass(bullet, 'is-active', () => this.state.index === i);
1173
+ bindClass(bullet, this.props.className.active, () => {
1174
+ return this.state.index === i;
1175
+ });
927
1176
  bindAttr(bullet, 'aria-current', () =>
928
1177
  this.state.index === i ? 'true' : null
929
1178
  );
@@ -932,33 +1181,31 @@ export class Swiper extends Component {
932
1181
  });
933
1182
  }
934
1183
 
935
- initNavigation() {
936
- this.dom.prevButton = this.ensureNavigation('prev', 'arrow-left');
937
- this.dom.nextButton = this.ensureNavigation('next', 'arrow-right');
1184
+ initNavigation(): void {
1185
+ const prevButton = this.ensureNavigation('prev', 'arrow-left');
1186
+ const nextButton = this.ensureNavigation('next', 'arrow-right');
1187
+ this.dom.prevButton = prevButton;
1188
+ this.dom.nextButton = nextButton;
938
1189
 
939
- this.cleanup.events.on('nav:prev', this.dom.prevButton, 'click', () =>
940
- this.prev()
941
- );
942
- this.cleanup.events.on('nav:next', this.dom.nextButton, 'click', () =>
943
- this.next()
944
- );
1190
+ this.cleanup.events.on('nav:prev', prevButton, 'click', () => this.prev());
1191
+ this.cleanup.events.on('nav:next', nextButton, 'click', () => this.next());
945
1192
 
946
1193
  if (!this.props.loop) {
947
1194
  this.cleanup.navBindings = createRoot((dispose) => {
948
1195
  bindClass(
949
- this.dom.prevButton,
950
- 'is-disabled',
1196
+ prevButton,
1197
+ this.props.className.disabled,
951
1198
  () => this.state.trackIndex <= 0
952
1199
  );
953
- bindAttr(this.dom.prevButton, 'disabled', () =>
1200
+ bindAttr(prevButton, 'disabled', () =>
954
1201
  this.state.trackIndex <= 0 ? '' : null
955
1202
  );
956
1203
  bindClass(
957
- this.dom.nextButton,
958
- 'is-disabled',
1204
+ nextButton,
1205
+ this.props.className.disabled,
959
1206
  () => this.state.trackIndex >= this.dom.slides.length - 1
960
1207
  );
961
- bindAttr(this.dom.nextButton, 'disabled', () =>
1208
+ bindAttr(nextButton, 'disabled', () =>
962
1209
  this.state.trackIndex >= this.dom.slides.length - 1 ? '' : null
963
1210
  );
964
1211
  return dispose;
@@ -966,27 +1213,53 @@ export class Swiper extends Component {
966
1213
  }
967
1214
  }
968
1215
 
969
- ensureNavigation(direction, iconName) {
970
- const key =
1216
+ ensureNavigation(
1217
+ direction: SwiperDirection,
1218
+ iconName: 'arrow-left' | 'arrow-right'
1219
+ ): HTMLButtonElement {
1220
+ if (!this.dom.root) {
1221
+ throw new Error('Swiper.ensureNavigation: root not found.');
1222
+ }
1223
+
1224
+ const key: CreatedNavigationKey =
971
1225
  direction === 'prev' ? 'createdPrevButton' : 'createdNextButton';
972
- const className = `swiper-navigation is-${direction}`;
973
- let button = q(`.swiper-navigation.is-${direction}`, this.dom.root);
1226
+ const directionClass =
1227
+ direction === 'prev'
1228
+ ? this.props.className.prev
1229
+ : this.props.className.next;
1230
+ const className = classList(
1231
+ this.props.className.navigation,
1232
+ directionClass
1233
+ );
1234
+ let button = q<HTMLButtonElement>(
1235
+ `[data-action="${direction}"]`,
1236
+ this.dom.root
1237
+ );
974
1238
 
975
1239
  if (!button) {
976
- button = h('button', { type: 'button', className });
1240
+ button = h('button', {
1241
+ type: 'button',
1242
+ className,
1243
+ 'data-action': direction,
1244
+ 'data-swiper-navigation': direction,
1245
+ }) as HTMLButtonElement;
977
1246
  this.dom.root.appendChild(button);
978
1247
  this.dom[key] = true;
979
- } else if (!button.matches('button')) {
1248
+ } else if (button.tagName.toLowerCase() !== 'button') {
980
1249
  button.setAttribute('role', 'button');
981
1250
  button.setAttribute('tabindex', '0');
982
- button.classList.add('swiper-navigation', `is-${direction}`);
1251
+ button.classList.add(this.props.className.navigation, directionClass);
1252
+ setDataMarker(button, 'data-swiper-navigation', direction);
1253
+ } else {
1254
+ button.classList.add(this.props.className.navigation, directionClass);
1255
+ setDataMarker(button, 'data-swiper-navigation', direction);
983
1256
  }
984
1257
 
985
1258
  button.setAttribute(
986
1259
  'aria-label',
987
1260
  direction === 'prev' ? 'Previous slide' : 'Next slide'
988
1261
  );
989
- if (!button.querySelector('svg')) {
1262
+ if (!q('svg', button)) {
990
1263
  button.textContent = '';
991
1264
  button.appendChild(icon(iconName));
992
1265
  }
@@ -994,7 +1267,7 @@ export class Swiper extends Component {
994
1267
  return button;
995
1268
  }
996
1269
 
997
- play() {
1270
+ play(): void {
998
1271
  this.assertBuilt('play');
999
1272
  if (this.runtime.destroyed || this.runtime.timer) return;
1000
1273
  if (this.realCount <= 1) return;
@@ -1002,40 +1275,37 @@ export class Swiper extends Component {
1002
1275
  this.runtime.timer = setInterval(() => this.next(), delay);
1003
1276
  }
1004
1277
 
1005
- pause() {
1278
+ pause(): void {
1006
1279
  if (!this.runtime.timer) return;
1007
1280
  clearInterval(this.runtime.timer);
1008
1281
  this.runtime.timer = null;
1009
1282
  }
1010
1283
 
1011
- resume() {
1284
+ resume(): void {
1012
1285
  if (this.runtime.destroyed) return;
1013
1286
  if (this.props.autoplay && !this.runtime.timer) this.play();
1014
1287
  }
1015
1288
 
1016
- restartAutoplay() {
1289
+ restartAutoplay(): void {
1017
1290
  this.pause();
1018
1291
  if (this.props.autoplay) this.play();
1019
1292
  }
1020
1293
 
1021
- update(propsPatch = {}, { force = false } = {}) {
1294
+ update(
1295
+ propsPatch: SwiperOptions | null | undefined = {},
1296
+ { force = false }: SwiperUpdateOptions = {}
1297
+ ): this {
1022
1298
  if (this.runtime.destroyed)
1023
1299
  throw new Error('Component.update: instance destroyed');
1024
1300
  this.assertBuilt('update');
1025
1301
 
1026
- const patch =
1027
- propsPatch && typeof propsPatch === 'object' && !Array.isArray(propsPatch)
1028
- ? propsPatch
1029
- : {};
1030
- const nextProps = resolveProps(
1031
- Object.assign({}, this.props, patch),
1032
- SWIPER_OPTIONS_SCHEMA,
1033
- 'Swiper.options'
1034
- );
1035
- const normalizedPatch = {};
1302
+ const patch = isObjectPatch(propsPatch) ? propsPatch : {};
1303
+ const nextProps = normalizeOptions(Object.assign({}, this.props, patch));
1304
+ const normalizedPatch: Partial<ResolvedSwiperOptions> = {};
1036
1305
 
1037
1306
  Object.keys(patch).forEach((key) => {
1038
- normalizedPatch[key] = nextProps[key];
1307
+ const propKey = key as keyof ResolvedSwiperOptions;
1308
+ normalizedPatch[propKey] = nextProps[propKey];
1039
1309
  });
1040
1310
 
1041
1311
  this.props = nextProps;
@@ -1045,7 +1315,10 @@ export class Swiper extends Component {
1045
1315
  return this;
1046
1316
  }
1047
1317
 
1048
- onUpdate(propsPatch = {}) {
1318
+ protected onUpdate(
1319
+ propsPatch: Partial<ResolvedSwiperOptions> | null | undefined = {},
1320
+ _options: Required<ComponentUpdateOptions> = { force: false }
1321
+ ): void {
1049
1322
  if (
1050
1323
  propsPatch &&
1051
1324
  Object.prototype.hasOwnProperty.call(propsPatch, 'data')
@@ -1076,7 +1349,7 @@ export class Swiper extends Component {
1076
1349
  else this.pause();
1077
1350
  }
1078
1351
 
1079
- updateData(data = this.props.data) {
1352
+ updateData(data: SwiperDataItem[] | null = this.props.data): this {
1080
1353
  if (this.runtime.destroyed)
1081
1354
  throw new Error('Swiper.updateData: instance destroyed');
1082
1355
  this.assertBuilt('updateData');
@@ -1094,6 +1367,8 @@ export class Swiper extends Component {
1094
1367
 
1095
1368
  this.props.data = data;
1096
1369
  const realIndex = this.state.index;
1370
+ const wrapper = this.dom.wrapper;
1371
+ if (!wrapper) return this;
1097
1372
 
1098
1373
  this.pause();
1099
1374
  this.clearImageCleanups();
@@ -1104,9 +1379,9 @@ export class Swiper extends Component {
1104
1379
  this.cleanup.events.clear();
1105
1380
 
1106
1381
  const items = this.normalizeData(data);
1107
- this.dom.wrapper.textContent = '';
1382
+ wrapper.textContent = '';
1108
1383
  items.forEach((item, index) => {
1109
- this.dom.wrapper.appendChild(this.createDataSlide(item, index));
1384
+ wrapper.appendChild(this.createDataSlide(item, index));
1110
1385
  });
1111
1386
  this.refreshSlides();
1112
1387
  this.runtime.realCount = this.dom.slides.length;
@@ -1118,6 +1393,9 @@ export class Swiper extends Component {
1118
1393
  }
1119
1394
  }
1120
1395
 
1121
- export function createSwiper(container, input = {}) {
1122
- return new Swiper(input);
1396
+ export function createSwiper(
1397
+ container: DOMReference,
1398
+ input: SwiperOptions = {}
1399
+ ): Swiper {
1400
+ return new Swiper(container, input);
1123
1401
  }