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 +1 -1
- package/src/component-pool/range/range.tsx +36 -13
- package/src/components/index.ts +1 -0
- package/src/components/mobile-components/mobile-side-menu.tsx +0 -1
- package/src/components/mobile-components/mobile-top-sys-icon.tsx +8 -3
- package/src/components/notice-message.tsx +0 -1
- package/src/components/slide-page-component-demo.tsx +364 -0
- package/src/components/slide-page-component.tsx +777 -0
- package/src/components/slide-tab-component.tsx +158 -79
- package/src/demo/demo-frame-helper.tsx +7 -0
- package/src/demo/demo-registry.ts +2 -0
- package/src/frames/responsive-frame.tsx +0 -1
- package/src/frames/slider-frame-demo.tsx +12 -1
- package/src/frames/slider-frame.tsx +2 -0
- package/src/frames/slider-helper.tsx +7 -1
- package/src/frames/top-frame.tsx +0 -1
package/package.json
CHANGED
|
@@ -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 (
|
|
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 (
|
|
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,
|
|
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={
|
|
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={
|
|
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={
|
|
638
|
+
disabled={isDisabled}
|
|
616
639
|
/>
|
|
617
640
|
)}
|
|
618
641
|
|
package/src/components/index.ts
CHANGED
|
@@ -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
|
|
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
|
|
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=
|
|
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
|
+
};
|