lupine.components 1.1.57 → 1.1.59

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lupine.components",
3
- "version": "1.1.57",
3
+ "version": "1.1.59",
4
4
  "license": "MIT",
5
5
  "author": "uuware.com",
6
6
  "homepage": "https://github.com/uuware/lupine.js",
@@ -3,6 +3,7 @@ import { bindGlobalStyle, getGlobalStylesId, CssProps, RefProps } from 'lupine.w
3
3
  export type RangeHookProps = {
4
4
  setValue: (val: number | [number, number]) => void;
5
5
  getValue: () => number | [number, number];
6
+ setDisabled?: (disabled: boolean) => void;
6
7
  };
7
8
 
8
9
  export type RangeProps = {
@@ -24,6 +25,7 @@ export type RangeProps = {
24
25
  trackColor?: string;
25
26
  fillColor?: string;
26
27
  showButtons?: boolean;
28
+ thumbSize?: number | string;
27
29
  };
28
30
 
29
31
  // 1. Define Static CSS completely decoupled from Reactivity (isVertical)
@@ -63,11 +65,11 @@ const rangeCss: CssProps = {
63
65
  },
64
66
  '&.horizontal .&-wrapper': {
65
67
  width: '100%',
66
- height: '20px',
68
+ height: 'var(--range-thumb-size, 20px)',
67
69
  flex: 1,
68
70
  },
69
71
  '&.vertical .&-wrapper': {
70
- width: '20px',
72
+ width: 'var(--range-thumb-size, 20px)',
71
73
  height: '100%',
72
74
  flex: 1,
73
75
  },
@@ -123,7 +125,7 @@ const rangeCss: CssProps = {
123
125
  transform: 'none',
124
126
  },
125
127
  '&.vertical .&-input': {
126
- height: '20px',
128
+ height: 'var(--range-thumb-size, 20px)',
127
129
  top: '100%',
128
130
  left: 0,
129
131
  transformOrigin: '0 0',
@@ -134,8 +136,8 @@ const rangeCss: CssProps = {
134
136
  appearance: 'none',
135
137
  WebkitAppearance: 'none',
136
138
  pointerEvents: 'auto',
137
- width: '20px',
138
- height: '20px',
139
+ width: 'var(--range-thumb-size, 20px)',
140
+ height: 'var(--range-thumb-size, 20px)',
139
141
  backgroundColor: '#fff',
140
142
  border: '2px solid var(--range-fill-color, var(--primary-accent-color, #0a74c9))',
141
143
  borderRadius: '50%',
@@ -151,8 +153,8 @@ const rangeCss: CssProps = {
151
153
  },
152
154
  '.&-input::-moz-range-thumb': {
153
155
  pointerEvents: 'auto',
154
- width: '20px',
155
- height: '20px',
156
+ width: 'var(--range-thumb-size, 20px)',
157
+ height: 'var(--range-thumb-size, 20px)',
156
158
  backgroundColor: '#fff',
157
159
  border: '2px solid var(--range-fill-color, var(--primary-accent-color, #0a74c9))',
158
160
  borderRadius: '50%',
@@ -280,6 +282,9 @@ export const Range = (props: RangeProps) => {
280
282
  const customStyle: any = { ...props.style };
281
283
  if (props.trackColor) customStyle['--range-track-color'] = props.trackColor;
282
284
  if (props.fillColor) customStyle['--range-fill-color'] = props.fillColor;
285
+ if (props.thumbSize) {
286
+ customStyle['--range-thumb-size'] = typeof props.thumbSize === 'number' ? `${props.thumbSize}px` : props.thumbSize;
287
+ }
283
288
 
284
289
  // Evaluate the immutable hash exact ONCE for all range implementations.
285
290
  const globalCssId = getGlobalStylesId(rangeCss);
@@ -307,9 +312,10 @@ export const Range = (props: RangeProps) => {
307
312
 
308
313
  let currentVal1 = val1;
309
314
  let currentVal2 = val2;
315
+ let isDisabled = !!props.disabled;
310
316
 
311
317
  const adjustByStep = (direction: -1 | 1) => {
312
- if (props.disabled) return;
318
+ if (isDisabled) return;
313
319
  if (isRangeMode) {
314
320
  let newVal = currentVal1 + direction * step;
315
321
  newVal = Math.max(min, Math.min(currentVal2, newVal));
@@ -417,7 +423,7 @@ export const Range = (props: RangeProps) => {
417
423
  };
418
424
 
419
425
  const handleContainerClick = (e: any) => {
420
- if (props.disabled) return;
426
+ if (isDisabled) return;
421
427
  if (e.target && e.target.tagName === 'INPUT') return;
422
428
 
423
429
  const wrapper = ref.$('.&-wrapper') as HTMLElement;
@@ -461,6 +467,7 @@ export const Range = (props: RangeProps) => {
461
467
  };
462
468
 
463
469
  const handleInput = (e: Event, isThumb2: boolean) => {
470
+ if (isDisabled) return;
464
471
  let val = parseFloat((e.target as HTMLInputElement).value);
465
472
 
466
473
  // Magnetic snapping
@@ -496,6 +503,7 @@ export const Range = (props: RangeProps) => {
496
503
  };
497
504
 
498
505
  const handleChange = () => {
506
+ if (isDisabled) return;
499
507
  if (props.onChange) {
500
508
  if (isRangeMode) {
501
509
  props.onChange([currentVal1, currentVal2]);
@@ -522,6 +530,21 @@ export const Range = (props: RangeProps) => {
522
530
  updateVisuals();
523
531
  };
524
532
  props.hook.getValue = () => (isRangeMode ? [currentVal1, currentVal2] : currentVal1);
533
+ props.hook.setDisabled = (disabled: boolean) => {
534
+ isDisabled = disabled;
535
+ const container = (ref.current as HTMLElement) || (ref.$?.('.&-container') as HTMLElement);
536
+ if (container) {
537
+ if (disabled) {
538
+ container.classList.add('disabled');
539
+ } else {
540
+ container.classList.remove('disabled');
541
+ }
542
+ }
543
+ const inputs = ref.$all?.('.&-input') as NodeListOf<HTMLInputElement>;
544
+ inputs?.forEach?.((input) => {
545
+ input.disabled = disabled;
546
+ });
547
+ };
525
548
  }
526
549
 
527
550
  const ref: RefProps & { onUnload?: (el: Element) => Promise<void> | void } = {
@@ -563,7 +586,7 @@ export const Range = (props: RangeProps) => {
563
586
 
564
587
  return (
565
588
  <div
566
- class={[globalCssId, '&-container', props.class, props.disabled ? 'disabled' : '', isVertical ? 'vertical' : 'horizontal']
589
+ class={[globalCssId, '&-container', props.class, isDisabled ? 'disabled' : '', isVertical ? 'vertical' : 'horizontal']
567
590
  .join(' ')
568
591
  .trim()}
569
592
  ref={ref}
@@ -588,7 +611,7 @@ export const Range = (props: RangeProps) => {
588
611
  value={val1}
589
612
  onInput={(e) => handleInput(e, false)}
590
613
  onChange={handleChange}
591
- disabled={props.disabled}
614
+ disabled={isDisabled}
592
615
  />
593
616
  <input
594
617
  type='range'
@@ -599,7 +622,7 @@ export const Range = (props: RangeProps) => {
599
622
  value={val2}
600
623
  onInput={(e) => handleInput(e, true)}
601
624
  onChange={handleChange}
602
- disabled={props.disabled}
625
+ disabled={isDisabled}
603
626
  />
604
627
  </>
605
628
  ) : (
@@ -612,7 +635,7 @@ export const Range = (props: RangeProps) => {
612
635
  value={val1}
613
636
  onInput={(e) => handleInput(e, false)}
614
637
  onChange={handleChange}
615
- disabled={props.disabled}
638
+ disabled={isDisabled}
616
639
  />
617
640
  )}
618
641
 
@@ -36,6 +36,7 @@ export * from './redirect';
36
36
  export * from './resizable-splitter';
37
37
  export * from './select-angle-component';
38
38
  export * from './select-with-title';
39
+ export * from './slide-page-component';
39
40
  export * from './slide-tab-component';
40
41
  export * from './spinner';
41
42
  export * from './stars-component';
@@ -244,7 +244,6 @@ export const MobileSideMenu = (props: { children: VNode<any>; autoExtend?: boole
244
244
  overflowY: 'auto',
245
245
  transform: 'translateX(-100%)',
246
246
  boxShadow: 'var(--cover-box-shadow)',
247
- 'padding-top ': isSafeAreaSupported() ? 'constant(safe-area-inset-top)' : '0',
248
247
  'padding-top': isSafeAreaSupported() ? 'env(safe-area-inset-top)' : '0',
249
248
  },
250
249
  [MediaQueryRange.TabletAbove]: {
@@ -1,8 +1,13 @@
1
- // import { NotificationColor, NotificationMessage, updateTheme, PopupMenuWithIcon, CssProps } from 'lupine.components';
1
+ // import { NotificationColor, NotificationMessage, updateTheme, PopupMenuWithIcon, CssProps } from "lupine.components";
2
2
  import { CssProps } from 'lupine.components';
3
3
  import { MobileSideMenuHelper } from './mobile-side-menu';
4
4
 
5
- export const MobileTopSysIcon = () => {
5
+ export interface MobileTopSysIconProps {
6
+ icon?: string;
7
+ }
8
+
9
+ export const MobileTopSysIcon = (props: MobileTopSysIconProps = {}) => {
10
+ const icon = props?.icon || 'bs-list';
6
11
  const css: CssProps = {
7
12
  cursor: 'pointer',
8
13
  display: 'flex',
@@ -20,7 +25,7 @@ export const MobileTopSysIcon = () => {
20
25
  };
21
26
  return (
22
27
  <div css={css} onClick={() => MobileSideMenuHelper.show()} class='mobile-top-sys-icon'>
23
- <i class='ifc-icon bs-list'></i>
28
+ <i class={`ifc-icon ${icon}`}></i>
24
29
  </div>
25
30
  );
26
31
  };
@@ -57,7 +57,6 @@ export class NotificationMessage {
57
57
  padding: '0 10px',
58
58
  zIndex: 'var(--layer-notice)',
59
59
  pointerEvents: 'none',
60
- 'padding-top ': isSafeAreaSupported() ? 'constant(safe-area-inset-top)' : '',
61
60
  'padding-top': isSafeAreaSupported() ? 'env(safe-area-inset-top)' : '',
62
61
  '>div': {
63
62
  pointerEvents: 'auto',
@@ -0,0 +1,364 @@
1
+ import { DemoStory } from '../demo/demo-types';
2
+ import { SlidePageComponent, SlidePageProps } from './slide-page-component';
3
+
4
+ const samplePages = [
5
+ {
6
+ id: 'page-1',
7
+ title: 'Explore Lupine Framework',
8
+ link: 'https://github.com/uuware/lupine.js',
9
+ target: '_blank',
10
+ content: (
11
+ <div
12
+ style={{
13
+ width: '100%',
14
+ height: '100%',
15
+ padding: '28px 32px',
16
+ boxSizing: 'border-box',
17
+ background: 'linear-gradient(135deg, #091e3a 0%, #173865 50%, #2b6cb0 100%)',
18
+ color: '#ffffff',
19
+ display: 'flex',
20
+ flexDirection: 'column',
21
+ justifyContent: 'space-between',
22
+ position: 'relative',
23
+ overflow: 'hidden',
24
+ }}
25
+ >
26
+ <div>
27
+ <div style={{ display: 'flex', alignItems: 'center', gap: '8px', marginBottom: '12px' }}>
28
+ <span
29
+ style={{
30
+ fontSize: '11px',
31
+ fontWeight: 700,
32
+ textTransform: 'uppercase',
33
+ letterSpacing: '0.05em',
34
+ padding: '3px 10px',
35
+ borderRadius: '12px',
36
+ background: 'rgba(2, 132, 199, 0.45)',
37
+ border: '1px solid rgba(56, 189, 248, 0.5)',
38
+ color: '#e0f2fe',
39
+ }}
40
+ >
41
+ 🔗 Clickable Card (With Link)
42
+ </span>
43
+ </div>
44
+ <h2 style={{ margin: '0 0 8px 0', fontSize: '22px', fontWeight: 700, letterSpacing: '-0.02em' }}>
45
+ Explore Lupine.js Architecture
46
+ </h2>
47
+ <p style={{ margin: 0, fontSize: '14px', lineHeight: '1.5', color: '#cbd5e1', maxWidth: '520px' }}>
48
+ A lightweight, high-performance TypeScript full-stack framework with zero-re-render DOM updates and native mobile navigation.
49
+ </p>
50
+ </div>
51
+ <div style={{ display: 'flex', alignItems: 'center', gap: '8px', marginTop: '16px' }}>
52
+ <span
53
+ style={{
54
+ display: 'inline-flex',
55
+ alignItems: 'center',
56
+ gap: '6px',
57
+ fontSize: '13px',
58
+ fontWeight: 600,
59
+ padding: '6px 14px',
60
+ borderRadius: '8px',
61
+ backgroundColor: '#38bdf8',
62
+ color: '#0f172a',
63
+ }}
64
+ >
65
+ Visit GitHub Repository ↗
66
+ </span>
67
+ <span style={{ fontSize: '12px', color: '#94a3b8' }}>Click anywhere on this slide to open</span>
68
+ </div>
69
+ </div>
70
+ ),
71
+ },
72
+ {
73
+ id: 'page-2',
74
+ title: 'Reactive DOM Performance',
75
+ // No link: behaves as a normal card
76
+ content: (
77
+ <div
78
+ style={{
79
+ width: '100%',
80
+ height: '100%',
81
+ padding: '28px 32px',
82
+ boxSizing: 'border-box',
83
+ background: 'linear-gradient(135deg, #18181b 0%, #27272a 60%, #3f3f46 100%)',
84
+ color: '#ffffff',
85
+ display: 'flex',
86
+ flexDirection: 'column',
87
+ justifyContent: 'space-between',
88
+ }}
89
+ >
90
+ <div>
91
+ <div style={{ display: 'flex', alignItems: 'center', gap: '8px', marginBottom: '12px' }}>
92
+ <span
93
+ style={{
94
+ fontSize: '11px',
95
+ fontWeight: 700,
96
+ textTransform: 'uppercase',
97
+ letterSpacing: '0.05em',
98
+ padding: '3px 10px',
99
+ borderRadius: '12px',
100
+ background: 'rgba(161, 161, 170, 0.25)',
101
+ border: '1px solid rgba(212, 212, 216, 0.35)',
102
+ color: '#f4f4f5',
103
+ }}
104
+ >
105
+ 📄 Normal Card (No Link)
106
+ </span>
107
+ </div>
108
+ <h2 style={{ margin: '0 0 8px 0', fontSize: '22px', fontWeight: 700, letterSpacing: '-0.02em' }}>
109
+ Direct DOM Updates & State Isolation
110
+ </h2>
111
+ <p style={{ margin: '0 0 16px 0', fontSize: '14px', lineHeight: '1.5', color: '#d4d4d8' }}>
112
+ Standard non-clickable presentation card showcasing core reactive capabilities:
113
+ </p>
114
+ </div>
115
+ <div style={{ display: 'flex', gap: '12px', flexWrap: 'wrap' }}>
116
+ <div
117
+ style={{
118
+ padding: '8px 14px',
119
+ borderRadius: '8px',
120
+ background: 'rgba(255, 255, 255, 0.08)',
121
+ border: '1px solid rgba(255, 255, 255, 0.1)',
122
+ }}
123
+ >
124
+ <div style={{ fontSize: '16px', fontWeight: 700, color: '#38bdf8' }}>⚡ 0ms</div>
125
+ <div style={{ fontSize: '11px', color: '#a1a1aa' }}>Re-render overhead</div>
126
+ </div>
127
+ <div
128
+ style={{
129
+ padding: '8px 14px',
130
+ borderRadius: '8px',
131
+ background: 'rgba(255, 255, 255, 0.08)',
132
+ border: '1px solid rgba(255, 255, 255, 0.1)',
133
+ }}
134
+ >
135
+ <div style={{ fontSize: '16px', fontWeight: 700, color: '#4ade80' }}>🎯 CSS &</div>
136
+ <div style={{ fontSize: '11px', color: '#a1a1aa' }}>Scoped namespace</div>
137
+ </div>
138
+ <div
139
+ style={{
140
+ padding: '8px 14px',
141
+ borderRadius: '8px',
142
+ background: 'rgba(255, 255, 255, 0.08)',
143
+ border: '1px solid rgba(255, 255, 255, 0.1)',
144
+ }}
145
+ >
146
+ <div style={{ fontSize: '16px', fontWeight: 700, color: '#fbbf24' }}>📱 60fps</div>
147
+ <div style={{ fontSize: '11px', color: '#a1a1aa' }}>Native touch snap</div>
148
+ </div>
149
+ </div>
150
+ </div>
151
+ ),
152
+ },
153
+ {
154
+ id: 'page-3',
155
+ title: 'Component Pool & Widgets',
156
+ link: '#component-pool',
157
+ content: (
158
+ <div
159
+ style={{
160
+ width: '100%',
161
+ height: '100%',
162
+ padding: '28px 32px',
163
+ boxSizing: 'border-box',
164
+ background: 'linear-gradient(135deg, #064e3b 0%, #047857 55%, #059669 100%)',
165
+ color: '#ffffff',
166
+ display: 'flex',
167
+ flexDirection: 'column',
168
+ justifyContent: 'space-between',
169
+ }}
170
+ >
171
+ <div>
172
+ <div style={{ display: 'flex', alignItems: 'center', gap: '8px', marginBottom: '12px' }}>
173
+ <span
174
+ style={{
175
+ fontSize: '11px',
176
+ fontWeight: 700,
177
+ textTransform: 'uppercase',
178
+ letterSpacing: '0.05em',
179
+ padding: '3px 10px',
180
+ borderRadius: '12px',
181
+ background: 'rgba(16, 185, 129, 0.35)',
182
+ border: '1px solid rgba(110, 231, 183, 0.5)',
183
+ color: '#d1fae5',
184
+ }}
185
+ >
186
+ 🔗 Clickable Card (With Link)
187
+ </span>
188
+ </div>
189
+ <h2 style={{ margin: '0 0 8px 0', fontSize: '22px', fontWeight: 700, letterSpacing: '-0.02em' }}>
190
+ Slide Tab & Slide Page Suite
191
+ </h2>
192
+ <p style={{ margin: 0, fontSize: '14px', lineHeight: '1.5', color: '#a7f3d0', maxWidth: '520px' }}>
193
+ Standardized components built on top of native CSS scroll-snap with automated indicators and gesture support.
194
+ </p>
195
+ </div>
196
+ <div style={{ display: 'flex', alignItems: 'center', gap: '8px', marginTop: '16px' }}>
197
+ <span
198
+ style={{
199
+ display: 'inline-flex',
200
+ alignItems: 'center',
201
+ gap: '6px',
202
+ fontSize: '13px',
203
+ fontWeight: 600,
204
+ padding: '6px 14px',
205
+ borderRadius: '8px',
206
+ backgroundColor: '#6ee7b7',
207
+ color: '#064e3b',
208
+ }}
209
+ >
210
+ Explore Components ↗
211
+ </span>
212
+ <span style={{ fontSize: '12px', color: '#d1fae5' }}>Click to navigate internal link</span>
213
+ </div>
214
+ </div>
215
+ ),
216
+ },
217
+ {
218
+ id: 'page-4',
219
+ title: 'Customizable Slide Options',
220
+ // No link: behaves as a normal card
221
+ content: (
222
+ <div
223
+ style={{
224
+ width: '100%',
225
+ height: '100%',
226
+ padding: '28px 32px',
227
+ boxSizing: 'border-box',
228
+ background: 'linear-gradient(135deg, #4c1d95 0%, #6d28d9 55%, #7c3aed 100%)',
229
+ color: '#ffffff',
230
+ display: 'flex',
231
+ flexDirection: 'column',
232
+ justifyContent: 'space-between',
233
+ }}
234
+ >
235
+ <div>
236
+ <div style={{ display: 'flex', alignItems: 'center', gap: '8px', marginBottom: '12px' }}>
237
+ <span
238
+ style={{
239
+ fontSize: '11px',
240
+ fontWeight: 700,
241
+ textTransform: 'uppercase',
242
+ letterSpacing: '0.05em',
243
+ padding: '3px 10px',
244
+ borderRadius: '12px',
245
+ background: 'rgba(196, 181, 253, 0.25)',
246
+ border: '1px solid rgba(221, 214, 254, 0.4)',
247
+ color: '#f5f3ff',
248
+ }}
249
+ >
250
+ 📄 Normal Card (No Link)
251
+ </span>
252
+ </div>
253
+ <h2 style={{ margin: '0 0 8px 0', fontSize: '22px', fontWeight: 700, letterSpacing: '-0.02em' }}>
254
+ Configurable Controls & Hooks
255
+ </h2>
256
+ <p style={{ margin: 0, fontSize: '14px', lineHeight: '1.5', color: '#ddd6fe', maxWidth: '540px' }}>
257
+ Configure autoplay duration, dot indicators, arrow toggles, hover pause, or take programmatic control via hook moveTo(), setIndex(), and setPause().
258
+ </p>
259
+ </div>
260
+ <div style={{ display: 'flex', gap: '8px', alignItems: 'center' }}>
261
+ <span style={{ fontSize: '12px', color: '#c4b5fd' }}>
262
+ 💡 Tip: Hovering over any slide automatically pauses the autoplay cycle.
263
+ </span>
264
+ </div>
265
+ </div>
266
+ ),
267
+ },
268
+ ];
269
+
270
+ export const slidePageDemo: DemoStory<SlidePageProps> = {
271
+ id: 'slide-page-demo',
272
+ text: 'Slide Page',
273
+ args: {
274
+ pages: samplePages,
275
+ autoplay: true,
276
+ interval: 4000,
277
+ showDots: true,
278
+ showArrows: true,
279
+ pauseOnHover: true,
280
+ },
281
+ argTypes: {
282
+ autoplay: {
283
+ control: 'boolean',
284
+ description: 'Automatically rotate slides at a set interval',
285
+ },
286
+ interval: {
287
+ control: 'number',
288
+ description: 'Autoplay rotation interval in milliseconds',
289
+ },
290
+ showDots: {
291
+ control: 'boolean',
292
+ description: 'Display navigation dot indicators at the bottom',
293
+ },
294
+ showArrows: {
295
+ control: 'boolean',
296
+ description: 'Display left and right arrow buttons',
297
+ },
298
+ pauseOnHover: {
299
+ control: 'boolean',
300
+ description: 'Pause autoplay rotation when mouse hovers over slide',
301
+ },
302
+ },
303
+ render: (args: SlidePageProps) => {
304
+ return (
305
+ <div style={{ display: 'flex', flexDirection: 'column', gap: '16px', width: '100%', maxWidth: '800px' }}>
306
+ <div
307
+ style={{
308
+ height: '240px',
309
+ width: '100%',
310
+ display: 'flex',
311
+ flexDirection: 'column',
312
+ border: 'var(--primary-border, 1px solid rgba(60, 60, 67, 0.15))',
313
+ borderRadius: '16px',
314
+ overflow: 'hidden',
315
+ boxShadow: '0 8px 24px rgba(0, 0, 0, 0.12)',
316
+ position: 'relative',
317
+ }}
318
+ >
319
+ <SlidePageComponent {...args} />
320
+ </div>
321
+ <div
322
+ style={{
323
+ padding: '12px 16px',
324
+ borderRadius: '8px',
325
+ background: 'var(--bg-container, #f2f2f7)',
326
+ fontSize: '13px',
327
+ color: 'var(--text-secondary, #64748b)',
328
+ lineHeight: '1.5',
329
+ display: 'flex',
330
+ flexDirection: 'column',
331
+ gap: '4px',
332
+ }}
333
+ >
334
+ <div>
335
+ <strong>Interactive behavior:</strong> Items with <code>link</code> are rendered as clickable elements (e.g. Slide 1 & Slide 3) with pointer cursor and hover states. Items without <code>link</code> (Slide 2 & Slide 4) render as standard content cards.
336
+ </div>
337
+ </div>
338
+ </div>
339
+ );
340
+ },
341
+ code: `import { SlidePageComponent } from 'lupine.components';
342
+
343
+ const pages = [
344
+ {
345
+ title: 'Slide with Link',
346
+ link: 'https://example.com',
347
+ target: '_blank',
348
+ content: <div>Clickable Slide Content</div>,
349
+ },
350
+ {
351
+ title: 'Regular Slide Card',
352
+ content: <div>Standard Non-Clickable Card Content</div>,
353
+ },
354
+ ];
355
+
356
+ <SlidePageComponent
357
+ pages={pages}
358
+ autoplay={true}
359
+ interval={4000}
360
+ showDots={true}
361
+ showArrows={true}
362
+ />
363
+ `,
364
+ };