@rakeyshgidwani/roger-ui-bank-theme-stan-design 0.1.4 → 0.1.6
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/CHANGELOG.md +1 -1
- package/dist/index.d.ts +131 -131
- package/dist/index.esm.js +148 -148
- package/dist/index.js +148 -148
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/src/components/ui/accessibility-demo.tsx +271 -0
- package/src/components/ui/advanced-component-architecture-demo.tsx +916 -0
- package/src/components/ui/advanced-transition-system-demo.tsx +670 -0
- package/src/components/ui/advanced-transition-system.tsx +395 -0
- package/src/components/ui/animation/animated-container.tsx +166 -0
- package/src/components/ui/animation/index.ts +19 -0
- package/src/components/ui/animation/staggered-container.tsx +68 -0
- package/src/components/ui/animation-demo.tsx +250 -0
- package/src/components/ui/badge.tsx +33 -0
- package/src/components/ui/battery-conscious-animation-demo.tsx +568 -0
- package/src/components/ui/border-radius-shadow-demo.tsx +187 -0
- package/src/components/ui/button.tsx +36 -0
- package/src/components/ui/card.tsx +207 -0
- package/src/components/ui/checkbox.tsx +30 -0
- package/src/components/ui/color-preview.tsx +411 -0
- package/src/components/ui/data-display/chart.tsx +653 -0
- package/src/components/ui/data-display/data-grid-simple.tsx +76 -0
- package/src/components/ui/data-display/data-grid.tsx +680 -0
- package/src/components/ui/data-display/list.tsx +456 -0
- package/src/components/ui/data-display/table.tsx +482 -0
- package/src/components/ui/data-display/timeline.tsx +441 -0
- package/src/components/ui/data-display/tree.tsx +602 -0
- package/src/components/ui/data-display/types.ts +536 -0
- package/src/components/ui/enterprise-mobile-experience-demo.tsx +749 -0
- package/src/components/ui/enterprise-mobile-experience.tsx +464 -0
- package/src/components/ui/feedback/alert.tsx +157 -0
- package/src/components/ui/feedback/progress.tsx +292 -0
- package/src/components/ui/feedback/skeleton.tsx +185 -0
- package/src/components/ui/feedback/toast.tsx +280 -0
- package/src/components/ui/feedback/types.ts +125 -0
- package/src/components/ui/font-preview.tsx +288 -0
- package/src/components/ui/form-demo.tsx +553 -0
- package/src/components/ui/hardware-acceleration-demo.tsx +547 -0
- package/src/components/ui/input.tsx +35 -0
- package/src/components/ui/label.tsx +16 -0
- package/src/components/ui/layout-demo.tsx +367 -0
- package/src/components/ui/layouts/adaptive-layout.tsx +139 -0
- package/src/components/ui/layouts/desktop-layout.tsx +224 -0
- package/src/components/ui/layouts/index.ts +10 -0
- package/src/components/ui/layouts/mobile-layout.tsx +162 -0
- package/src/components/ui/layouts/tablet-layout.tsx +197 -0
- package/src/components/ui/mobile-form-validation.tsx +451 -0
- package/src/components/ui/mobile-input-demo.tsx +201 -0
- package/src/components/ui/mobile-input.tsx +281 -0
- package/src/components/ui/mobile-skeleton-loading-demo.tsx +638 -0
- package/src/components/ui/navigation/breadcrumb.tsx +158 -0
- package/src/components/ui/navigation/index.ts +36 -0
- package/src/components/ui/navigation/menu.tsx +374 -0
- package/src/components/ui/navigation/navigation-demo.tsx +324 -0
- package/src/components/ui/navigation/pagination.tsx +272 -0
- package/src/components/ui/navigation/sidebar.tsx +383 -0
- package/src/components/ui/navigation/stepper.tsx +303 -0
- package/src/components/ui/navigation/tabs.tsx +205 -0
- package/src/components/ui/navigation/types.ts +299 -0
- package/src/components/ui/overlay/backdrop.tsx +81 -0
- package/src/components/ui/overlay/focus-manager.tsx +143 -0
- package/src/components/ui/overlay/index.ts +36 -0
- package/src/components/ui/overlay/modal.tsx +270 -0
- package/src/components/ui/overlay/overlay-manager.tsx +110 -0
- package/src/components/ui/overlay/popover.tsx +462 -0
- package/src/components/ui/overlay/portal.tsx +79 -0
- package/src/components/ui/overlay/tooltip.tsx +303 -0
- package/src/components/ui/overlay/types.ts +196 -0
- package/src/components/ui/performance-demo.tsx +596 -0
- package/src/components/ui/semantic-input-system-demo.tsx +502 -0
- package/src/components/ui/semantic-input-system-demo.tsx.disabled +873 -0
- package/src/components/ui/tablet-layout.tsx +192 -0
- package/src/components/ui/theme-customizer.tsx +386 -0
- package/src/components/ui/theme-preview.tsx +310 -0
- package/src/components/ui/theme-switcher.tsx +264 -0
- package/src/components/ui/theme-toggle.tsx +38 -0
- package/src/components/ui/token-demo.tsx +195 -0
- package/src/components/ui/touch-demo.tsx +462 -0
- package/src/components/ui/touch-friendly-interface-demo.tsx +519 -0
- package/src/components/ui/touch-friendly-interface.tsx +296 -0
- package/src/hooks/index.ts +190 -0
- package/src/hooks/use-accessibility-support.ts +518 -0
- package/src/hooks/use-adaptive-layout.ts +289 -0
- package/src/hooks/use-advanced-patterns.ts +294 -0
- package/src/hooks/use-advanced-transition-system.ts +393 -0
- package/src/hooks/use-animation-profile.ts +288 -0
- package/src/hooks/use-battery-animations.ts +384 -0
- package/src/hooks/use-battery-conscious-loading.ts +475 -0
- package/src/hooks/use-battery-optimization.ts +330 -0
- package/src/hooks/use-battery-status.ts +299 -0
- package/src/hooks/use-component-performance.ts +344 -0
- package/src/hooks/use-device-loading-states.ts +459 -0
- package/src/hooks/use-device.tsx +110 -0
- package/src/hooks/use-enterprise-mobile-experience.ts +488 -0
- package/src/hooks/use-form-feedback.ts +403 -0
- package/src/hooks/use-form-performance.ts +513 -0
- package/src/hooks/use-frame-rate.ts +251 -0
- package/src/hooks/use-gestures.ts +338 -0
- package/src/hooks/use-hardware-acceleration.ts +341 -0
- package/src/hooks/use-input-accessibility.ts +455 -0
- package/src/hooks/use-input-performance.ts +506 -0
- package/src/hooks/use-layout-performance.ts +319 -0
- package/src/hooks/use-loading-accessibility.ts +535 -0
- package/src/hooks/use-loading-performance.ts +473 -0
- package/src/hooks/use-memory-usage.ts +287 -0
- package/src/hooks/use-mobile-form-layout.ts +464 -0
- package/src/hooks/use-mobile-form-validation.ts +518 -0
- package/src/hooks/use-mobile-keyboard-optimization.ts +472 -0
- package/src/hooks/use-mobile-layout.ts +302 -0
- package/src/hooks/use-mobile-optimization.ts +406 -0
- package/src/hooks/use-mobile-skeleton.ts +402 -0
- package/src/hooks/use-mobile-touch.ts +414 -0
- package/src/hooks/use-performance-throttling.ts +348 -0
- package/src/hooks/use-performance.ts +316 -0
- package/src/hooks/use-reusable-architecture.ts +414 -0
- package/src/hooks/use-semantic-input-types.ts +357 -0
- package/src/hooks/use-semantic-input.ts +565 -0
- package/src/hooks/use-tablet-layout.ts +384 -0
- package/src/hooks/use-touch-friendly-input.ts +524 -0
- package/src/hooks/use-touch-friendly-interface.ts +331 -0
- package/src/hooks/use-touch-optimization.ts +375 -0
- package/src/index.ts +279 -279
- package/src/lib/utils.ts +6 -0
- package/src/themes/README.md +272 -0
- package/src/themes/ThemeContext.tsx +31 -0
- package/src/themes/ThemeProvider.tsx +232 -0
- package/src/themes/accessibility/index.ts +27 -0
- package/src/themes/accessibility.ts +259 -0
- package/src/themes/aria-patterns.ts +420 -0
- package/src/themes/base-themes.ts +55 -0
- package/src/themes/colorManager.ts +380 -0
- package/src/themes/examples/dark-theme.ts +154 -0
- package/src/themes/examples/minimal-theme.ts +108 -0
- package/src/themes/focus-management.ts +701 -0
- package/src/themes/fontLoader.ts +201 -0
- package/src/themes/high-contrast.ts +621 -0
- package/src/themes/index.ts +19 -0
- package/src/themes/inheritance.ts +227 -0
- package/src/themes/keyboard-navigation.ts +550 -0
- package/src/themes/motion-reduction.ts +662 -0
- package/src/themes/navigation.ts +238 -0
- package/src/themes/screen-reader.ts +645 -0
- package/src/themes/systemThemeDetector.ts +182 -0
- package/src/themes/themeCSSUpdater.ts +262 -0
- package/src/themes/themePersistence.ts +238 -0
- package/src/themes/themes/default.ts +586 -0
- package/src/themes/themes/harvey.ts +554 -0
- package/src/themes/themes/stan-design.ts +683 -0
- package/src/themes/types.ts +460 -0
- package/src/themes/useSystemTheme.ts +48 -0
- package/src/themes/useTheme.ts +87 -0
- package/src/themes/validation.ts +462 -0
- package/src/tokens/index.ts +34 -0
- package/src/tokens/tokenExporter.ts +397 -0
- package/src/tokens/tokenGenerator.ts +276 -0
- package/src/tokens/tokenManager.ts +248 -0
- package/src/tokens/tokenValidator.ts +543 -0
- package/src/tokens/types.ts +78 -0
- package/src/utils/bundle-analyzer.ts +260 -0
- package/src/utils/bundle-splitting.ts +483 -0
- package/src/utils/lazy-loading.ts +441 -0
- package/src/utils/performance-monitor.ts +513 -0
- package/src/utils/tree-shaking.ts +274 -0
|
@@ -0,0 +1,441 @@
|
|
|
1
|
+
import React, { useState, useMemo } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
TimelineProps,
|
|
4
|
+
TimelineItem,
|
|
5
|
+
TimelineAction,
|
|
6
|
+
TimelineItemProps
|
|
7
|
+
} from './types';
|
|
8
|
+
|
|
9
|
+
// Simple icon components (inline SVG)
|
|
10
|
+
const CalendarIcon: React.FC<{ className?: string }> = ({ className = '' }) => (
|
|
11
|
+
<svg
|
|
12
|
+
className={`timeline__icon ${className}`}
|
|
13
|
+
fill="none"
|
|
14
|
+
stroke="currentColor"
|
|
15
|
+
viewBox="0 0 24 24"
|
|
16
|
+
>
|
|
17
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
|
|
18
|
+
</svg>
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
const ClockIcon: React.FC<{ className?: string }> = ({ className = '' }) => (
|
|
22
|
+
<svg
|
|
23
|
+
className={`timeline__icon ${className}`}
|
|
24
|
+
fill="none"
|
|
25
|
+
stroke="currentColor"
|
|
26
|
+
viewBox="0 0 24 24"
|
|
27
|
+
>
|
|
28
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
|
|
29
|
+
</svg>
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
const UserIcon: React.FC<{ className?: string }> = ({ className = '' }) => (
|
|
33
|
+
<svg
|
|
34
|
+
className={`timeline__icon ${className}`}
|
|
35
|
+
fill="none"
|
|
36
|
+
stroke="currentColor"
|
|
37
|
+
viewBox="0 0 24 24"
|
|
38
|
+
>
|
|
39
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
|
|
40
|
+
</svg>
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
const BellIcon: React.FC<{ className?: string }> = ({ className = '' }) => (
|
|
44
|
+
<svg
|
|
45
|
+
className={`timeline__icon ${className}`}
|
|
46
|
+
fill="none"
|
|
47
|
+
stroke="currentColor"
|
|
48
|
+
viewBox="0 0 24 24"
|
|
49
|
+
>
|
|
50
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 17h5l-5 5-5-5h5V3h5v14z" />
|
|
51
|
+
</svg>
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
const SearchIcon: React.FC<{ className?: string }> = ({ className = '' }) => (
|
|
55
|
+
<svg
|
|
56
|
+
className={`timeline__icon ${className}`}
|
|
57
|
+
fill="none"
|
|
58
|
+
stroke="currentColor"
|
|
59
|
+
viewBox="0 0 24 24"
|
|
60
|
+
>
|
|
61
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
|
|
62
|
+
</svg>
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
const FilterIcon: React.FC<{ className?: string }> = ({ className = '' }) => (
|
|
66
|
+
<svg
|
|
67
|
+
className={`timeline__icon ${className}`}
|
|
68
|
+
fill="none"
|
|
69
|
+
stroke="currentColor"
|
|
70
|
+
viewBox="0 0 24 24"
|
|
71
|
+
>
|
|
72
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.414A1 1 0 013 6.707V4z" />
|
|
73
|
+
</svg>
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
const DotsIcon: React.FC<{ className?: string }> = ({ className = '' }) => (
|
|
77
|
+
<svg
|
|
78
|
+
className={`timeline__icon ${className}`}
|
|
79
|
+
fill="none"
|
|
80
|
+
stroke="currentColor"
|
|
81
|
+
viewBox="0 0 24 24"
|
|
82
|
+
>
|
|
83
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 5v.01M12 12v.01M12 19v.01M12 6a1 1 0 110-2 1 1 0 010 2zm0 7a1 1 0 110-2 1 1 0 010 2zm0 7a1 1 0 110-2 1 1 0 010 2z" />
|
|
84
|
+
</svg>
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
// Timeline Search and Filter Bar
|
|
88
|
+
const TimelineSearchFilterBar: React.FC<{
|
|
89
|
+
searchable: boolean;
|
|
90
|
+
searchValue: string;
|
|
91
|
+
onSearchChange: (value: string) => void;
|
|
92
|
+
filterable: boolean;
|
|
93
|
+
theme: string;
|
|
94
|
+
size: 'sm' | 'md' | 'lg';
|
|
95
|
+
}> = ({
|
|
96
|
+
searchable,
|
|
97
|
+
searchValue,
|
|
98
|
+
onSearchChange,
|
|
99
|
+
filterable,
|
|
100
|
+
size
|
|
101
|
+
}) => {
|
|
102
|
+
|
|
103
|
+
if (!searchable && !filterable) return null;
|
|
104
|
+
|
|
105
|
+
return (
|
|
106
|
+
<div className={`timeline__search-filter-bar timeline__search-filter-bar--${size}`}>
|
|
107
|
+
{searchable && (
|
|
108
|
+
<div className="timeline__search-container">
|
|
109
|
+
<div className="timeline__search-icon">
|
|
110
|
+
<SearchIcon />
|
|
111
|
+
</div>
|
|
112
|
+
<input
|
|
113
|
+
type="text"
|
|
114
|
+
value={searchValue}
|
|
115
|
+
onChange={(e) => onSearchChange(e.target.value)}
|
|
116
|
+
placeholder="Search timeline..."
|
|
117
|
+
className={`timeline__search-input timeline__search-input--${size}`}
|
|
118
|
+
/>
|
|
119
|
+
</div>
|
|
120
|
+
)}
|
|
121
|
+
|
|
122
|
+
{filterable && (
|
|
123
|
+
<button className={`timeline__filter-button timeline__filter-button--${size}`}>
|
|
124
|
+
<FilterIcon className="timeline__filter-icon" />
|
|
125
|
+
<span className="timeline__filter-text">Filter</span>
|
|
126
|
+
</button>
|
|
127
|
+
)}
|
|
128
|
+
</div>
|
|
129
|
+
);
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
// Timeline Item Actions Component
|
|
133
|
+
const TimelineItemActions: React.FC<{
|
|
134
|
+
actions: TimelineAction[];
|
|
135
|
+
item: TimelineItem;
|
|
136
|
+
theme: string;
|
|
137
|
+
size: 'sm' | 'md' | 'lg';
|
|
138
|
+
}> = ({ actions, item, size }) => {
|
|
139
|
+
const [showMenu, setShowMenu] = useState(false);
|
|
140
|
+
|
|
141
|
+
if (actions.length === 0) return null;
|
|
142
|
+
|
|
143
|
+
// If only one action, show it directly
|
|
144
|
+
if (actions.length === 1) {
|
|
145
|
+
const action = actions[0];
|
|
146
|
+
return (
|
|
147
|
+
<button
|
|
148
|
+
onClick={(e) => {
|
|
149
|
+
e.stopPropagation();
|
|
150
|
+
action.onClick(item);
|
|
151
|
+
}}
|
|
152
|
+
className={`timeline__action-button timeline__action-button--${size} ${action.variant === 'danger' ? 'timeline__action-button--danger' : ''}`}
|
|
153
|
+
title={action.label}
|
|
154
|
+
>
|
|
155
|
+
{action.icon || <DotsIcon className="timeline__action-icon" />}
|
|
156
|
+
</button>
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Multiple actions - show dropdown menu
|
|
161
|
+
return (
|
|
162
|
+
<div className="timeline__action-dropdown">
|
|
163
|
+
<button
|
|
164
|
+
onClick={(e) => {
|
|
165
|
+
e.stopPropagation();
|
|
166
|
+
setShowMenu(!showMenu);
|
|
167
|
+
}}
|
|
168
|
+
className={`timeline__action-button timeline__action-button--${size}`}
|
|
169
|
+
>
|
|
170
|
+
<DotsIcon className="timeline__action-icon" />
|
|
171
|
+
</button>
|
|
172
|
+
|
|
173
|
+
{showMenu && (
|
|
174
|
+
<>
|
|
175
|
+
{/* Backdrop */}
|
|
176
|
+
<div
|
|
177
|
+
className="timeline__action-backdrop"
|
|
178
|
+
onClick={() => setShowMenu(false)}
|
|
179
|
+
/>
|
|
180
|
+
|
|
181
|
+
{/* Menu */}
|
|
182
|
+
<div className="timeline__action-menu">
|
|
183
|
+
{actions.map((action, index) => (
|
|
184
|
+
<button
|
|
185
|
+
key={index}
|
|
186
|
+
onClick={(e) => {
|
|
187
|
+
e.stopPropagation();
|
|
188
|
+
action.onClick(item);
|
|
189
|
+
setShowMenu(false);
|
|
190
|
+
}}
|
|
191
|
+
className={`timeline__action-menu-item ${action.variant === 'danger' ? 'timeline__action-menu-item--danger' : ''}`}
|
|
192
|
+
>
|
|
193
|
+
{action.icon && <span className="timeline__action-menu-icon">{action.icon}</span>}
|
|
194
|
+
{action.label}
|
|
195
|
+
</button>
|
|
196
|
+
))}
|
|
197
|
+
</div>
|
|
198
|
+
</>
|
|
199
|
+
)}
|
|
200
|
+
</div>
|
|
201
|
+
);
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
// Individual Timeline Item Component
|
|
205
|
+
const TimelineItemComponent: React.FC<TimelineItemProps> = ({
|
|
206
|
+
item,
|
|
207
|
+
position,
|
|
208
|
+
isLast,
|
|
209
|
+
mode,
|
|
210
|
+
actions,
|
|
211
|
+
onClick,
|
|
212
|
+
theme,
|
|
213
|
+
size,
|
|
214
|
+
variant,
|
|
215
|
+
showConnector
|
|
216
|
+
}) => {
|
|
217
|
+
// const { getTheme } = useTheme();
|
|
218
|
+
// const themeConfig = getTheme(theme);
|
|
219
|
+
// const colors = themeConfig?.colors || getDefaultColors();
|
|
220
|
+
|
|
221
|
+
const isClickable = !!onClick;
|
|
222
|
+
|
|
223
|
+
const getIcon = () => {
|
|
224
|
+
if (item.icon) {
|
|
225
|
+
return item.icon;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
switch (item.type) {
|
|
229
|
+
case 'event': return <CalendarIcon className="timeline__dot-icon" />;
|
|
230
|
+
case 'task': return <ClockIcon className="timeline__dot-icon" />;
|
|
231
|
+
case 'user': return <UserIcon className="timeline__dot-icon" />;
|
|
232
|
+
case 'system': return <BellIcon className="timeline__dot-icon" />;
|
|
233
|
+
default: return null;
|
|
234
|
+
}
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
const formatTimestamp = (timestamp: string) => {
|
|
238
|
+
const date = new Date(timestamp);
|
|
239
|
+
return date.toLocaleString();
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
return (
|
|
243
|
+
<div className={`timeline__item timeline__item--${mode} timeline__item--${position} timeline__item--${size}`}>
|
|
244
|
+
{/* Content */}
|
|
245
|
+
<div
|
|
246
|
+
className={`timeline__item-content timeline__item-content--${size} timeline__item-content--${variant} ${isClickable ? 'timeline__item-content--clickable' : ''}`}
|
|
247
|
+
onClick={isClickable ? () => onClick(item) : undefined}
|
|
248
|
+
>
|
|
249
|
+
<div className={`timeline__item-content-wrapper timeline__item-content-wrapper--${position}`}>
|
|
250
|
+
{/* Header */}
|
|
251
|
+
<div className="timeline__item-header">
|
|
252
|
+
<div className="timeline__item-text">
|
|
253
|
+
<h3 className="timeline__item-title">
|
|
254
|
+
{item.title}
|
|
255
|
+
</h3>
|
|
256
|
+
<p className="timeline__item-timestamp">
|
|
257
|
+
{formatTimestamp(item.timestamp)}
|
|
258
|
+
</p>
|
|
259
|
+
</div>
|
|
260
|
+
|
|
261
|
+
{/* Actions */}
|
|
262
|
+
<div className="timeline__item-actions">
|
|
263
|
+
<TimelineItemActions
|
|
264
|
+
actions={actions}
|
|
265
|
+
item={item}
|
|
266
|
+
theme={theme}
|
|
267
|
+
size={size}
|
|
268
|
+
/>
|
|
269
|
+
</div>
|
|
270
|
+
</div>
|
|
271
|
+
|
|
272
|
+
{/* Content */}
|
|
273
|
+
{item.description && (
|
|
274
|
+
<p className="timeline__item-description">
|
|
275
|
+
{item.description}
|
|
276
|
+
</p>
|
|
277
|
+
)}
|
|
278
|
+
|
|
279
|
+
{/* Additional Content */}
|
|
280
|
+
{item.content && (
|
|
281
|
+
<div className="timeline__item-content-extra">
|
|
282
|
+
{item.content}
|
|
283
|
+
</div>
|
|
284
|
+
)}
|
|
285
|
+
|
|
286
|
+
{/* Metadata */}
|
|
287
|
+
{item.metadata && (
|
|
288
|
+
<div className="timeline__item-metadata">
|
|
289
|
+
{item.metadata.author && (
|
|
290
|
+
<span className="timeline__item-author">
|
|
291
|
+
By {item.metadata.author}
|
|
292
|
+
</span>
|
|
293
|
+
)}
|
|
294
|
+
{item.metadata.category && (
|
|
295
|
+
<span className="timeline__item-category">
|
|
296
|
+
{item.metadata.category}
|
|
297
|
+
</span>
|
|
298
|
+
)}
|
|
299
|
+
{item.metadata.tags && item.metadata.tags.map((tag, index) => (
|
|
300
|
+
<span
|
|
301
|
+
key={index}
|
|
302
|
+
className="timeline__item-tag"
|
|
303
|
+
>
|
|
304
|
+
#{tag}
|
|
305
|
+
</span>
|
|
306
|
+
))}
|
|
307
|
+
</div>
|
|
308
|
+
)}
|
|
309
|
+
</div>
|
|
310
|
+
</div>
|
|
311
|
+
|
|
312
|
+
{/* Timeline Dot */}
|
|
313
|
+
<div className="timeline__dot-container">
|
|
314
|
+
<div
|
|
315
|
+
className={`timeline__dot timeline__dot--${size} timeline__dot--${item.status || 'default'}`}
|
|
316
|
+
>
|
|
317
|
+
{getIcon()}
|
|
318
|
+
</div>
|
|
319
|
+
|
|
320
|
+
{/* Connector Line */}
|
|
321
|
+
{showConnector && !isLast && (
|
|
322
|
+
<div className={`timeline__connector timeline__connector--${size}`} />
|
|
323
|
+
)}
|
|
324
|
+
</div>
|
|
325
|
+
</div>
|
|
326
|
+
);
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
// Main Timeline Component
|
|
330
|
+
export const Timeline: React.FC<TimelineProps> = ({
|
|
331
|
+
items,
|
|
332
|
+
mode = 'left',
|
|
333
|
+
reverse = false,
|
|
334
|
+
actions = [],
|
|
335
|
+
onItemClick,
|
|
336
|
+
searchable = false,
|
|
337
|
+
searchValue = '',
|
|
338
|
+
onSearchChange,
|
|
339
|
+
filterable = false,
|
|
340
|
+
// filters = [], // TODO: Implement filter functionality
|
|
341
|
+
// onFiltersChange, // TODO: Implement filter functionality
|
|
342
|
+
showConnector = true,
|
|
343
|
+
loading = false,
|
|
344
|
+
error = null,
|
|
345
|
+
emptyMessage = 'No timeline items',
|
|
346
|
+
className = '',
|
|
347
|
+
theme = 'stan-design',
|
|
348
|
+
size = 'md',
|
|
349
|
+
variant = 'default'
|
|
350
|
+
}) => {
|
|
351
|
+
|
|
352
|
+
// Filter items based on search
|
|
353
|
+
const filteredItems = useMemo(() => {
|
|
354
|
+
if (!searchable || !searchValue.trim()) return items;
|
|
355
|
+
|
|
356
|
+
const searchLower = searchValue.toLowerCase();
|
|
357
|
+
return items.filter(item =>
|
|
358
|
+
item.title.toLowerCase().includes(searchLower) ||
|
|
359
|
+
item.description?.toLowerCase().includes(searchLower) ||
|
|
360
|
+
item.metadata?.author?.toLowerCase().includes(searchLower) ||
|
|
361
|
+
item.metadata?.category?.toLowerCase().includes(searchLower) ||
|
|
362
|
+
item.metadata?.tags?.some(tag => tag.toLowerCase().includes(searchLower))
|
|
363
|
+
);
|
|
364
|
+
}, [items, searchable, searchValue]);
|
|
365
|
+
|
|
366
|
+
// Sort items by timestamp
|
|
367
|
+
const sortedItems = useMemo(() => {
|
|
368
|
+
const sorted = [...filteredItems].sort((a, b) => {
|
|
369
|
+
const dateA = new Date(a.timestamp).getTime();
|
|
370
|
+
const dateB = new Date(b.timestamp).getTime();
|
|
371
|
+
return reverse ? dateB - dateA : dateA - dateB;
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
return sorted;
|
|
375
|
+
}, [filteredItems, reverse]);
|
|
376
|
+
|
|
377
|
+
if (loading) {
|
|
378
|
+
return (
|
|
379
|
+
<div className={`timeline__loading ${className || ''}`}>
|
|
380
|
+
Loading timeline...
|
|
381
|
+
</div>
|
|
382
|
+
);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
if (error) {
|
|
386
|
+
return (
|
|
387
|
+
<div className={`timeline__error ${className || ''}`}>
|
|
388
|
+
{error}
|
|
389
|
+
</div>
|
|
390
|
+
);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
return (
|
|
394
|
+
<div className={`timeline__container ${className || ''}`}>
|
|
395
|
+
{/* Search and Filter Bar */}
|
|
396
|
+
<TimelineSearchFilterBar
|
|
397
|
+
searchable={searchable}
|
|
398
|
+
searchValue={searchValue}
|
|
399
|
+
onSearchChange={onSearchChange || (() => {})}
|
|
400
|
+
filterable={filterable}
|
|
401
|
+
theme={theme}
|
|
402
|
+
size={size}
|
|
403
|
+
/>
|
|
404
|
+
|
|
405
|
+
{/* Timeline Container */}
|
|
406
|
+
<div className="timeline__timeline">
|
|
407
|
+
{sortedItems.length === 0 ? (
|
|
408
|
+
<div className="timeline__empty">
|
|
409
|
+
{emptyMessage}
|
|
410
|
+
</div>
|
|
411
|
+
) : (
|
|
412
|
+
<div className="timeline__items">
|
|
413
|
+
{sortedItems.map((item, index) => {
|
|
414
|
+
const position = mode === 'alternate'
|
|
415
|
+
? (index % 2 === 0 ? 'left' : 'right')
|
|
416
|
+
: mode;
|
|
417
|
+
|
|
418
|
+
return (
|
|
419
|
+
<TimelineItemComponent
|
|
420
|
+
key={item.id}
|
|
421
|
+
item={item}
|
|
422
|
+
position={position}
|
|
423
|
+
isLast={index === sortedItems.length - 1}
|
|
424
|
+
mode={mode}
|
|
425
|
+
actions={actions}
|
|
426
|
+
onClick={onItemClick}
|
|
427
|
+
theme={theme}
|
|
428
|
+
size={size}
|
|
429
|
+
variant={variant}
|
|
430
|
+
showConnector={showConnector}
|
|
431
|
+
/>
|
|
432
|
+
);
|
|
433
|
+
})}
|
|
434
|
+
</div>
|
|
435
|
+
)}
|
|
436
|
+
</div>
|
|
437
|
+
</div>
|
|
438
|
+
);
|
|
439
|
+
};
|
|
440
|
+
|
|
441
|
+
export default Timeline;
|