@shohojdhara/atomix 0.3.5 → 0.3.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/README.md +101 -199
- package/atomix.config.ts +241 -0
- package/dist/atomix.css +260 -179
- package/dist/atomix.css.map +1 -1
- package/dist/atomix.min.css +250 -179
- package/dist/atomix.min.css.map +1 -1
- package/dist/charts.js +61 -66
- package/dist/charts.js.map +1 -1
- package/dist/core.js +47 -31
- package/dist/core.js.map +1 -1
- package/dist/forms.js +47 -31
- package/dist/forms.js.map +1 -1
- package/dist/heavy.js +47 -31
- package/dist/heavy.js.map +1 -1
- package/dist/index.d.ts +1841 -1633
- package/dist/index.esm.js +4975 -4113
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +5151 -4290
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/dist/theme.d.ts +1572 -1442
- package/dist/theme.js +4816 -4080
- package/dist/theme.js.map +1 -1
- package/package.json +6 -20
- package/src/components/Accordion/Accordion.stories.tsx +50 -17
- package/src/components/AtomixGlass/AtomixGlass.tsx +65 -31
- package/src/components/AtomixGlass/AtomixGlassContainer.tsx +11 -4
- package/src/components/AtomixGlass/stories/AtomixGlass.stories.tsx +1 -32
- package/src/components/AtomixGlass/stories/Examples.stories.tsx +2 -2
- package/src/components/AtomixGlass/stories/shared-components.tsx +0 -31
- package/src/components/Avatar/Avatar.stories.tsx +7 -0
- package/src/components/Badge/Badge.stories.tsx +91 -13
- package/src/components/Block/Block.stories.tsx +7 -23
- package/src/components/Breadcrumb/Breadcrumb.stories.tsx +7 -0
- package/src/components/Button/Button.stories.tsx +141 -22
- package/src/components/Button/ButtonGroup.stories.tsx +315 -0
- package/src/components/Button/ButtonGroup.tsx +67 -0
- package/src/components/Button/index.ts +2 -0
- package/src/components/Callout/Callout.stories.tsx +8 -6
- package/src/components/Card/Card.stories.tsx +82 -28
- package/src/components/Chart/AnimatedChart.tsx +0 -1
- package/src/components/Chart/AreaChart.tsx +0 -1
- package/src/components/Chart/BarChart.tsx +0 -1
- package/src/components/Chart/BubbleChart.tsx +0 -1
- package/src/components/Chart/CandlestickChart.tsx +0 -1
- package/src/components/Chart/Chart.stories.tsx +5 -7
- package/src/components/Chart/Chart.tsx +0 -16
- package/src/components/Chart/ChartRenderer.tsx +1 -1
- package/src/components/Chart/DonutChart.tsx +0 -1
- package/src/components/Chart/FunnelChart.tsx +0 -1
- package/src/components/Chart/GaugeChart.tsx +0 -1
- package/src/components/Chart/HeatmapChart.tsx +0 -1
- package/src/components/Chart/LineChart.tsx +0 -1
- package/src/components/Chart/MultiAxisChart.tsx +0 -1
- package/src/components/Chart/PieChart.tsx +0 -1
- package/src/components/Chart/RadarChart.tsx +0 -1
- package/src/components/Chart/ScatterChart.tsx +0 -1
- package/src/components/Chart/WaterfallChart.tsx +0 -1
- package/src/components/ColorModeToggle/ColorModeToggle.stories.tsx +7 -0
- package/src/components/DataTable/DataTable.stories.tsx +23 -16
- package/src/components/DatePicker/DatePicker.stories.tsx +27 -19
- package/src/components/Dropdown/Dropdown.stories.tsx +11 -19
- package/src/components/EdgePanel/EdgePanel.stories.tsx +1 -0
- package/src/components/Footer/Footer.stories.tsx +8 -6
- package/src/components/Footer/FooterLink.tsx +9 -2
- package/src/components/Form/Checkbox.stories.tsx +7 -0
- package/src/components/Form/Form.stories.tsx +7 -0
- package/src/components/Form/FormGroup.stories.tsx +9 -1
- package/src/components/Form/Input.stories.tsx +69 -16
- package/src/components/Form/Radio.stories.tsx +9 -1
- package/src/components/Form/Select.stories.tsx +9 -1
- package/src/components/Form/Textarea.stories.tsx +10 -2
- package/src/components/Hero/Hero.stories.tsx +7 -0
- package/src/components/List/List.stories.tsx +7 -0
- package/src/components/Messages/Messages.stories.tsx +8 -7
- package/src/components/Modal/Modal.stories.tsx +17 -6
- package/src/components/Navigation/Menu/Menu.stories.tsx +7 -0
- package/src/components/Navigation/Nav/Nav.stories.tsx +7 -0
- package/src/components/Navigation/Navbar/Navbar.stories.tsx +1 -0
- package/src/components/Navigation/SideMenu/SideMenu.stories.tsx +1 -1
- package/src/components/Pagination/Pagination.stories.tsx +188 -111
- package/src/components/Pagination/Pagination.tsx +83 -3
- package/src/components/PhotoViewer/PhotoViewer.stories.tsx +10 -5
- package/src/components/Popover/Popover.stories.tsx +191 -115
- package/src/components/ProductReview/ProductReview.stories.tsx +80 -58
- package/src/components/Progress/Progress.stories.tsx +79 -49
- package/src/components/Rating/Rating.stories.tsx +109 -84
- package/src/components/River/River.stories.tsx +194 -114
- package/src/components/SectionIntro/SectionIntro.stories.tsx +19 -9
- package/src/components/Slider/Slider.stories.tsx +7 -0
- package/src/components/Spinner/Spinner.stories.tsx +15 -11
- package/src/components/Steps/Steps.stories.tsx +132 -98
- package/src/components/Tabs/Tabs.stories.tsx +163 -112
- package/src/components/Testimonial/Testimonial.stories.tsx +114 -68
- package/src/components/Todo/Todo.stories.tsx +38 -12
- package/src/components/Toggle/Toggle.stories.tsx +61 -28
- package/src/components/Tooltip/Tooltip.stories.tsx +318 -200
- package/src/components/Upload/Upload.stories.tsx +122 -84
- package/src/components/VideoPlayer/VideoPlayer.stories.tsx +7 -24
- package/src/components/index.ts +1 -0
- package/src/lib/composables/useAtomixGlass.ts +2 -3
- package/src/lib/composables/useNavbar.ts +0 -10
- package/src/lib/config/loader.ts +2 -1
- package/src/lib/constants/components.ts +10 -0
- package/src/lib/hooks/useComponentCustomization.ts +1 -1
- package/src/lib/theme/README.md +174 -0
- package/src/lib/theme/adapters/index.ts +31 -0
- package/src/lib/theme/adapters/themeAdapter.ts +287 -0
- package/src/lib/theme/config/__tests__/configLoader.test.ts +207 -0
- package/src/lib/theme/config/configLoader.ts +254 -0
- package/src/lib/theme/config/loader.ts +37 -48
- package/src/lib/theme/config/types.ts +2 -2
- package/src/lib/theme/config/validator.ts +15 -91
- package/src/lib/theme/{constants.ts → constants/constants.ts} +0 -18
- package/src/lib/theme/constants/index.ts +8 -0
- package/src/lib/theme/core/ThemeRegistry.ts +19 -6
- package/src/lib/theme/core/__tests__/createTheme.test.ts +132 -0
- package/src/lib/theme/core/composeTheme.ts +155 -0
- package/src/lib/theme/core/createTheme.ts +94 -0
- package/src/lib/theme/{createTheme.ts → core/createThemeObject.ts} +10 -6
- package/src/lib/theme/core/index.ts +5 -19
- package/src/lib/theme/devtools/Comparator.tsx +346 -22
- package/src/lib/theme/devtools/IMPROVEMENTS.md +139 -38
- package/src/lib/theme/devtools/Inspector.tsx +335 -51
- package/src/lib/theme/devtools/LiveEditor.tsx +478 -107
- package/src/lib/theme/devtools/Preview.tsx +471 -221
- package/src/lib/theme/{core → devtools}/ThemeValidator.ts +1 -1
- package/src/lib/theme/devtools/index.ts +14 -4
- package/src/lib/theme/devtools/useHistory.ts +130 -0
- package/src/lib/theme/errors/index.ts +12 -0
- package/src/lib/theme/generators/cssFile.ts +79 -0
- package/src/lib/theme/generators/generateCSS.ts +89 -0
- package/src/lib/theme/{generateCSSVariables.ts → generators/generateCSSVariables.ts} +3 -13
- package/src/lib/theme/generators/index.ts +19 -0
- package/src/lib/theme/i18n/rtl.ts +5 -6
- package/src/lib/theme/index.ts +120 -15
- package/src/lib/theme/runtime/ThemeApplicator.ts +52 -111
- package/src/lib/theme/{ThemeContext.tsx → runtime/ThemeContext.tsx} +1 -1
- package/src/lib/theme/runtime/ThemeErrorBoundary.tsx +1 -1
- package/src/lib/theme/runtime/ThemeProvider.tsx +456 -179
- package/src/lib/theme/runtime/index.ts +1 -2
- package/src/lib/theme/runtime/useTheme.ts +1 -2
- package/src/lib/theme/test/testTheme.ts +385 -0
- package/src/lib/theme/tokens/index.ts +12 -0
- package/src/lib/theme/tokens/tokens.ts +721 -0
- package/src/lib/theme/types.ts +6 -42
- package/src/lib/theme/{utils.ts → utils/domUtils.ts} +2 -2
- package/src/lib/theme/utils/index.ts +11 -0
- package/src/lib/theme/utils/injectCSS.ts +90 -0
- package/src/lib/theme/utils/themeHelpers.ts +78 -0
- package/src/lib/theme/{themeUtils.ts → utils/themeUtils.ts} +1 -1
- package/src/lib/theme-tools.ts +7 -8
- package/src/lib/types/components.ts +40 -130
- package/src/lib/utils/componentUtils.ts +1 -1
- package/src/styles/01-settings/_settings.design-tokens.scss +4 -1
- package/src/styles/02-tools/_tools.button.scss +66 -79
- package/src/styles/06-components/_components.atomix-glass.scss +13 -3
- package/src/styles/06-components/_components.pagination.scss +88 -0
- package/scripts/sync-theme-config.js +0 -309
- package/src/lib/theme/composeTheme.ts +0 -370
- package/src/lib/theme/core/ThemeCache.ts +0 -283
- package/src/lib/theme/core/ThemeEngine.test.ts +0 -146
- package/src/lib/theme/core/ThemeEngine.ts +0 -665
- package/src/lib/theme/createThemeFromConfig.ts +0 -132
- package/src/lib/theme/devtools/CLI.ts +0 -364
- package/src/lib/theme/runtime/ThemeManager.test.ts +0 -192
- package/src/lib/theme/runtime/ThemeManager.ts +0 -446
- package/src/styles/03-generic/_generated-root.css +0 -26
- package/src/themes/README.md +0 -442
- package/src/themes/themes.config.js +0 -68
- /package/src/lib/theme/{cssVariableMapper.ts → adapters/cssVariableMapper.ts} +0 -0
- /package/src/lib/theme/{errors.ts → errors/errors.ts} +0 -0
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
* Theme Comparator Component
|
|
3
3
|
*
|
|
4
4
|
* React component for comparing two themes side-by-side
|
|
5
|
+
* Enhanced with search/filter and improved visual diff styling
|
|
5
6
|
*/
|
|
6
7
|
|
|
7
|
-
import React, { useMemo } from 'react';
|
|
8
|
+
import React, { useMemo, useState, useCallback, useEffect, useRef } from 'react';
|
|
8
9
|
import type { Theme } from '../types';
|
|
9
10
|
|
|
10
11
|
/**
|
|
@@ -28,6 +29,7 @@ interface Difference {
|
|
|
28
29
|
valueA: any;
|
|
29
30
|
valueB: any;
|
|
30
31
|
type: 'added' | 'removed' | 'changed';
|
|
32
|
+
category?: string;
|
|
31
33
|
}
|
|
32
34
|
|
|
33
35
|
/**
|
|
@@ -42,9 +44,37 @@ export const ThemeComparator: React.FC<ThemeComparatorProps> = ({
|
|
|
42
44
|
className,
|
|
43
45
|
style,
|
|
44
46
|
}) => {
|
|
47
|
+
const [searchQuery, setSearchQuery] = useState<string>('');
|
|
48
|
+
const [debouncedSearchQuery, setDebouncedSearchQuery] = useState<string>('');
|
|
49
|
+
const [filterType, setFilterType] = useState<'all' | 'added' | 'removed' | 'changed'>('all');
|
|
50
|
+
const [filterCategory, setFilterCategory] = useState<string>('all');
|
|
51
|
+
const searchTimeoutRef = useRef<NodeJS.Timeout>();
|
|
52
|
+
|
|
53
|
+
// Debounce search query
|
|
54
|
+
useEffect(() => {
|
|
55
|
+
if (searchTimeoutRef.current) {
|
|
56
|
+
clearTimeout(searchTimeoutRef.current);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
searchTimeoutRef.current = setTimeout(() => {
|
|
60
|
+
setDebouncedSearchQuery(searchQuery);
|
|
61
|
+
}, 300);
|
|
62
|
+
|
|
63
|
+
return () => {
|
|
64
|
+
if (searchTimeoutRef.current) {
|
|
65
|
+
clearTimeout(searchTimeoutRef.current);
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
}, [searchQuery]);
|
|
69
|
+
|
|
45
70
|
const differences = useMemo(() => {
|
|
46
71
|
const diffs: Difference[] = [];
|
|
47
72
|
|
|
73
|
+
const getCategory = (path: string): string => {
|
|
74
|
+
const firstSegment = path.split('.')[0];
|
|
75
|
+
return firstSegment || 'other';
|
|
76
|
+
};
|
|
77
|
+
|
|
48
78
|
const compareObjects = (objA: any, objB: any, path: string = '') => {
|
|
49
79
|
const keysA = Object.keys(objA || {});
|
|
50
80
|
const keysB = Object.keys(objB || {});
|
|
@@ -63,6 +93,7 @@ export const ThemeComparator: React.FC<ThemeComparatorProps> = ({
|
|
|
63
93
|
valueA: undefined,
|
|
64
94
|
valueB,
|
|
65
95
|
type: 'added',
|
|
96
|
+
category: getCategory(currentPath),
|
|
66
97
|
});
|
|
67
98
|
} else if (valueA !== undefined && valueB === undefined) {
|
|
68
99
|
diffs.push({
|
|
@@ -70,6 +101,7 @@ export const ThemeComparator: React.FC<ThemeComparatorProps> = ({
|
|
|
70
101
|
valueA,
|
|
71
102
|
valueB: undefined,
|
|
72
103
|
type: 'removed',
|
|
104
|
+
category: getCategory(currentPath),
|
|
73
105
|
});
|
|
74
106
|
} else if (typeof valueA === 'object' && typeof valueB === 'object' && !Array.isArray(valueA) && !Array.isArray(valueB)) {
|
|
75
107
|
compareObjects(valueA, valueB, currentPath);
|
|
@@ -79,6 +111,7 @@ export const ThemeComparator: React.FC<ThemeComparatorProps> = ({
|
|
|
79
111
|
valueA,
|
|
80
112
|
valueB,
|
|
81
113
|
type: 'changed',
|
|
114
|
+
category: getCategory(currentPath),
|
|
82
115
|
});
|
|
83
116
|
}
|
|
84
117
|
}
|
|
@@ -88,6 +121,39 @@ export const ThemeComparator: React.FC<ThemeComparatorProps> = ({
|
|
|
88
121
|
return diffs;
|
|
89
122
|
}, [themeA, themeB]);
|
|
90
123
|
|
|
124
|
+
// Filter differences
|
|
125
|
+
const filteredDifferences = useMemo(() => {
|
|
126
|
+
let filtered = differences;
|
|
127
|
+
|
|
128
|
+
// Filter by type
|
|
129
|
+
if (filterType !== 'all') {
|
|
130
|
+
filtered = filtered.filter(d => d.type === filterType);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Filter by category
|
|
134
|
+
if (filterCategory !== 'all') {
|
|
135
|
+
filtered = filtered.filter(d => d.category === filterCategory);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Filter by search query
|
|
139
|
+
if (debouncedSearchQuery) {
|
|
140
|
+
const queryLower = debouncedSearchQuery.toLowerCase();
|
|
141
|
+
filtered = filtered.filter(d =>
|
|
142
|
+
d.path.toLowerCase().includes(queryLower) ||
|
|
143
|
+
String(d.valueA).toLowerCase().includes(queryLower) ||
|
|
144
|
+
String(d.valueB).toLowerCase().includes(queryLower)
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return filtered;
|
|
149
|
+
}, [differences, filterType, filterCategory, debouncedSearchQuery]);
|
|
150
|
+
|
|
151
|
+
// Get unique categories
|
|
152
|
+
const categories = useMemo(() => {
|
|
153
|
+
const cats = new Set(differences.map(d => d.category).filter(Boolean));
|
|
154
|
+
return Array.from(cats).sort();
|
|
155
|
+
}, [differences]);
|
|
156
|
+
|
|
91
157
|
const formatValue = (value: any): string => {
|
|
92
158
|
if (value === undefined) return 'undefined';
|
|
93
159
|
if (value === null) return 'null';
|
|
@@ -105,6 +171,38 @@ export const ThemeComparator: React.FC<ThemeComparatorProps> = ({
|
|
|
105
171
|
}
|
|
106
172
|
};
|
|
107
173
|
|
|
174
|
+
const getTypeBackground = (type: Difference['type']): string => {
|
|
175
|
+
switch (type) {
|
|
176
|
+
case 'added': return 'rgba(76, 175, 80, 0.1)';
|
|
177
|
+
case 'removed': return 'rgba(244, 67, 54, 0.1)';
|
|
178
|
+
case 'changed': return 'rgba(255, 152, 0, 0.1)';
|
|
179
|
+
default: return 'transparent';
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
const highlightText = (text: string, query: string): React.ReactNode => {
|
|
184
|
+
if (!query) return text;
|
|
185
|
+
|
|
186
|
+
const parts = text.split(new RegExp(`(${query})`, 'gi'));
|
|
187
|
+
return parts.map((part, index) =>
|
|
188
|
+
part.toLowerCase() === query.toLowerCase() ? (
|
|
189
|
+
<mark key={index} className="search-highlight">{part}</mark>
|
|
190
|
+
) : (
|
|
191
|
+
part
|
|
192
|
+
)
|
|
193
|
+
);
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
const stats = useMemo(() => {
|
|
197
|
+
return {
|
|
198
|
+
total: differences.length,
|
|
199
|
+
added: differences.filter(d => d.type === 'added').length,
|
|
200
|
+
removed: differences.filter(d => d.type === 'removed').length,
|
|
201
|
+
changed: differences.filter(d => d.type === 'changed').length,
|
|
202
|
+
filtered: filteredDifferences.length,
|
|
203
|
+
};
|
|
204
|
+
}, [differences, filteredDifferences]);
|
|
205
|
+
|
|
108
206
|
return (
|
|
109
207
|
<div className={`atomix-theme-comparator ${className || ''}`} style={style}>
|
|
110
208
|
<div className="comparator-header">
|
|
@@ -119,40 +217,122 @@ export const ThemeComparator: React.FC<ThemeComparatorProps> = ({
|
|
|
119
217
|
</div>
|
|
120
218
|
</div>
|
|
121
219
|
|
|
220
|
+
{/* Filter Controls */}
|
|
221
|
+
<div className="filter-controls">
|
|
222
|
+
<div className="filter-group">
|
|
223
|
+
<label>Search:</label>
|
|
224
|
+
<input
|
|
225
|
+
type="text"
|
|
226
|
+
placeholder="Search differences..."
|
|
227
|
+
value={searchQuery}
|
|
228
|
+
onChange={(e) => setSearchQuery(e.target.value)}
|
|
229
|
+
className="search-input"
|
|
230
|
+
/>
|
|
231
|
+
{searchQuery && (
|
|
232
|
+
<button
|
|
233
|
+
className="clear-search"
|
|
234
|
+
onClick={() => setSearchQuery('')}
|
|
235
|
+
title="Clear search"
|
|
236
|
+
>
|
|
237
|
+
×
|
|
238
|
+
</button>
|
|
239
|
+
)}
|
|
240
|
+
</div>
|
|
241
|
+
|
|
242
|
+
<div className="filter-group">
|
|
243
|
+
<label>Type:</label>
|
|
244
|
+
<select
|
|
245
|
+
value={filterType}
|
|
246
|
+
onChange={(e) => setFilterType(e.target.value as any)}
|
|
247
|
+
className="filter-select"
|
|
248
|
+
>
|
|
249
|
+
<option value="all">All Types</option>
|
|
250
|
+
<option value="added">Added</option>
|
|
251
|
+
<option value="removed">Removed</option>
|
|
252
|
+
<option value="changed">Changed</option>
|
|
253
|
+
</select>
|
|
254
|
+
</div>
|
|
255
|
+
|
|
256
|
+
{categories.length > 0 && (
|
|
257
|
+
<div className="filter-group">
|
|
258
|
+
<label>Category:</label>
|
|
259
|
+
<select
|
|
260
|
+
value={filterCategory}
|
|
261
|
+
onChange={(e) => setFilterCategory(e.target.value)}
|
|
262
|
+
className="filter-select"
|
|
263
|
+
>
|
|
264
|
+
<option value="all">All Categories</option>
|
|
265
|
+
{categories.map(cat => (
|
|
266
|
+
<option key={cat} value={cat}>{cat}</option>
|
|
267
|
+
))}
|
|
268
|
+
</select>
|
|
269
|
+
</div>
|
|
270
|
+
)}
|
|
271
|
+
|
|
272
|
+
{(searchQuery || filterType !== 'all' || filterCategory !== 'all') && (
|
|
273
|
+
<button
|
|
274
|
+
className="clear-filters"
|
|
275
|
+
onClick={() => {
|
|
276
|
+
setSearchQuery('');
|
|
277
|
+
setFilterType('all');
|
|
278
|
+
setFilterCategory('all');
|
|
279
|
+
}}
|
|
280
|
+
>
|
|
281
|
+
Clear Filters
|
|
282
|
+
</button>
|
|
283
|
+
)}
|
|
284
|
+
</div>
|
|
285
|
+
|
|
122
286
|
<div className="comparator-stats">
|
|
123
287
|
<div className="stat">
|
|
124
|
-
<span className="stat-value">{
|
|
288
|
+
<span className="stat-value">{stats.total}</span>
|
|
125
289
|
<span className="stat-label">Total Differences</span>
|
|
126
290
|
</div>
|
|
127
|
-
<div className="stat">
|
|
291
|
+
<div className="stat stat-added">
|
|
128
292
|
<span className="stat-value" style={{ color: '#4caf50' }}>
|
|
129
|
-
{
|
|
293
|
+
{stats.added}
|
|
130
294
|
</span>
|
|
131
295
|
<span className="stat-label">Added</span>
|
|
132
296
|
</div>
|
|
133
|
-
<div className="stat">
|
|
297
|
+
<div className="stat stat-removed">
|
|
134
298
|
<span className="stat-value" style={{ color: '#f44336' }}>
|
|
135
|
-
{
|
|
299
|
+
{stats.removed}
|
|
136
300
|
</span>
|
|
137
301
|
<span className="stat-label">Removed</span>
|
|
138
302
|
</div>
|
|
139
|
-
<div className="stat">
|
|
303
|
+
<div className="stat stat-changed">
|
|
140
304
|
<span className="stat-value" style={{ color: '#ff9800' }}>
|
|
141
|
-
{
|
|
305
|
+
{stats.changed}
|
|
142
306
|
</span>
|
|
143
307
|
<span className="stat-label">Changed</span>
|
|
144
308
|
</div>
|
|
309
|
+
{stats.filtered !== stats.total && (
|
|
310
|
+
<div className="stat stat-filtered">
|
|
311
|
+
<span className="stat-value" style={{ color: '#2196f3' }}>
|
|
312
|
+
{stats.filtered}
|
|
313
|
+
</span>
|
|
314
|
+
<span className="stat-label">Filtered</span>
|
|
315
|
+
</div>
|
|
316
|
+
)}
|
|
145
317
|
</div>
|
|
146
318
|
|
|
147
|
-
{
|
|
319
|
+
{filteredDifferences.length === 0 ? (
|
|
148
320
|
<div className="no-differences">
|
|
149
|
-
|
|
321
|
+
{differences.length === 0 ? (
|
|
322
|
+
<>✅ Themes are identical</>
|
|
323
|
+
) : (
|
|
324
|
+
<>No differences match your filters. Try adjusting your search or filters.</>
|
|
325
|
+
)}
|
|
150
326
|
</div>
|
|
151
327
|
) : (
|
|
152
328
|
<div className="differences-list">
|
|
153
|
-
<h4>Differences</h4>
|
|
154
|
-
{
|
|
155
|
-
<div
|
|
329
|
+
<h4>Differences ({filteredDifferences.length})</h4>
|
|
330
|
+
{filteredDifferences.map((diff, index) => (
|
|
331
|
+
<div
|
|
332
|
+
key={index}
|
|
333
|
+
className={`difference-item difference-${diff.type}`}
|
|
334
|
+
style={{ backgroundColor: getTypeBackground(diff.type) }}
|
|
335
|
+
>
|
|
156
336
|
<div className="difference-header">
|
|
157
337
|
<span
|
|
158
338
|
className="difference-type"
|
|
@@ -160,20 +340,33 @@ export const ThemeComparator: React.FC<ThemeComparatorProps> = ({
|
|
|
160
340
|
>
|
|
161
341
|
{diff.type}
|
|
162
342
|
</span>
|
|
163
|
-
<code className="difference-path">
|
|
343
|
+
<code className="difference-path">
|
|
344
|
+
{highlightText(diff.path, debouncedSearchQuery)}
|
|
345
|
+
</code>
|
|
346
|
+
{diff.category && (
|
|
347
|
+
<span className="difference-category">{diff.category}</span>
|
|
348
|
+
)}
|
|
164
349
|
</div>
|
|
165
350
|
<div className="difference-values">
|
|
166
|
-
<div className=
|
|
351
|
+
<div className={`value-column value-${diff.type === 'added' ? 'empty' : 'filled'}`}>
|
|
167
352
|
<div className="value-label">{themeA.name}</div>
|
|
168
353
|
<pre className="value-content">
|
|
169
|
-
{
|
|
354
|
+
{diff.type === 'added' ? (
|
|
355
|
+
<span className="value-empty">—</span>
|
|
356
|
+
) : (
|
|
357
|
+
highlightText(formatValue(diff.valueA), debouncedSearchQuery)
|
|
358
|
+
)}
|
|
170
359
|
</pre>
|
|
171
360
|
</div>
|
|
172
361
|
<div className="value-divider">→</div>
|
|
173
|
-
<div className=
|
|
362
|
+
<div className={`value-column value-${diff.type === 'removed' ? 'empty' : 'filled'}`}>
|
|
174
363
|
<div className="value-label">{themeB.name}</div>
|
|
175
364
|
<pre className="value-content">
|
|
176
|
-
{
|
|
365
|
+
{diff.type === 'removed' ? (
|
|
366
|
+
<span className="value-empty">—</span>
|
|
367
|
+
) : (
|
|
368
|
+
highlightText(formatValue(diff.valueB), debouncedSearchQuery)
|
|
369
|
+
)}
|
|
177
370
|
</pre>
|
|
178
371
|
</div>
|
|
179
372
|
</div>
|
|
@@ -227,6 +420,90 @@ export const ThemeComparator: React.FC<ThemeComparatorProps> = ({
|
|
|
227
420
|
color: #666;
|
|
228
421
|
}
|
|
229
422
|
|
|
423
|
+
.filter-controls {
|
|
424
|
+
display: flex;
|
|
425
|
+
gap: 16px;
|
|
426
|
+
align-items: center;
|
|
427
|
+
margin-bottom: 24px;
|
|
428
|
+
padding: 16px;
|
|
429
|
+
background: #f5f5f5;
|
|
430
|
+
border-radius: 8px;
|
|
431
|
+
flex-wrap: wrap;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
.filter-group {
|
|
435
|
+
display: flex;
|
|
436
|
+
align-items: center;
|
|
437
|
+
gap: 8px;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
.filter-group label {
|
|
441
|
+
font-size: 14px;
|
|
442
|
+
font-weight: 500;
|
|
443
|
+
color: #666;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
.search-input {
|
|
447
|
+
padding: 8px 12px;
|
|
448
|
+
border: 1px solid #e0e0e0;
|
|
449
|
+
border-radius: 4px;
|
|
450
|
+
font-size: 14px;
|
|
451
|
+
width: 250px;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
.search-input:focus {
|
|
455
|
+
outline: none;
|
|
456
|
+
border-color: #2196f3;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
.clear-search {
|
|
460
|
+
background: #f44336;
|
|
461
|
+
color: white;
|
|
462
|
+
border: none;
|
|
463
|
+
border-radius: 50%;
|
|
464
|
+
width: 24px;
|
|
465
|
+
height: 24px;
|
|
466
|
+
cursor: pointer;
|
|
467
|
+
font-size: 18px;
|
|
468
|
+
line-height: 1;
|
|
469
|
+
display: flex;
|
|
470
|
+
align-items: center;
|
|
471
|
+
justify-content: center;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
.clear-search:hover {
|
|
475
|
+
background: #d32f2f;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
.filter-select {
|
|
479
|
+
padding: 8px 12px;
|
|
480
|
+
border: 1px solid #e0e0e0;
|
|
481
|
+
border-radius: 4px;
|
|
482
|
+
font-size: 14px;
|
|
483
|
+
background: white;
|
|
484
|
+
cursor: pointer;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
.clear-filters {
|
|
488
|
+
padding: 8px 16px;
|
|
489
|
+
background: #666;
|
|
490
|
+
color: white;
|
|
491
|
+
border: none;
|
|
492
|
+
border-radius: 4px;
|
|
493
|
+
cursor: pointer;
|
|
494
|
+
font-size: 14px;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
.clear-filters:hover {
|
|
498
|
+
background: #555;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
.search-highlight {
|
|
502
|
+
background: #fff59d;
|
|
503
|
+
padding: 2px 4px;
|
|
504
|
+
border-radius: 2px;
|
|
505
|
+
}
|
|
506
|
+
|
|
230
507
|
.comparator-stats {
|
|
231
508
|
display: grid;
|
|
232
509
|
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
|
|
@@ -241,6 +518,22 @@ export const ThemeComparator: React.FC<ThemeComparatorProps> = ({
|
|
|
241
518
|
border-radius: 8px;
|
|
242
519
|
}
|
|
243
520
|
|
|
521
|
+
.stat-added {
|
|
522
|
+
border-left: 4px solid #4caf50;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
.stat-removed {
|
|
526
|
+
border-left: 4px solid #f44336;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
.stat-changed {
|
|
530
|
+
border-left: 4px solid #ff9800;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
.stat-filtered {
|
|
534
|
+
border-left: 4px solid #2196f3;
|
|
535
|
+
}
|
|
536
|
+
|
|
244
537
|
.stat-value {
|
|
245
538
|
display: block;
|
|
246
539
|
font-size: 32px;
|
|
@@ -272,9 +565,22 @@ export const ThemeComparator: React.FC<ThemeComparatorProps> = ({
|
|
|
272
565
|
|
|
273
566
|
.difference-item {
|
|
274
567
|
margin-bottom: 16px;
|
|
275
|
-
border:
|
|
568
|
+
border: 2px solid;
|
|
276
569
|
border-radius: 8px;
|
|
277
570
|
overflow: hidden;
|
|
571
|
+
transition: all 0.2s ease;
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
.difference-added {
|
|
575
|
+
border-color: #4caf50;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
.difference-removed {
|
|
579
|
+
border-color: #f44336;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
.difference-changed {
|
|
583
|
+
border-color: #ff9800;
|
|
278
584
|
}
|
|
279
585
|
|
|
280
586
|
.difference-header {
|
|
@@ -282,7 +588,7 @@ export const ThemeComparator: React.FC<ThemeComparatorProps> = ({
|
|
|
282
588
|
align-items: center;
|
|
283
589
|
gap: 12px;
|
|
284
590
|
padding: 12px;
|
|
285
|
-
background:
|
|
591
|
+
background: rgba(0, 0, 0, 0.05);
|
|
286
592
|
}
|
|
287
593
|
|
|
288
594
|
.difference-type {
|
|
@@ -298,6 +604,16 @@ export const ThemeComparator: React.FC<ThemeComparatorProps> = ({
|
|
|
298
604
|
font-family: 'Monaco', 'Menlo', monospace;
|
|
299
605
|
font-size: 13px;
|
|
300
606
|
color: #1976d2;
|
|
607
|
+
flex: 1;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
.difference-category {
|
|
611
|
+
font-size: 11px;
|
|
612
|
+
padding: 2px 6px;
|
|
613
|
+
background: rgba(0, 0, 0, 0.1);
|
|
614
|
+
border-radius: 4px;
|
|
615
|
+
color: #666;
|
|
616
|
+
text-transform: uppercase;
|
|
301
617
|
}
|
|
302
618
|
|
|
303
619
|
.difference-values {
|
|
@@ -312,6 +628,10 @@ export const ThemeComparator: React.FC<ThemeComparatorProps> = ({
|
|
|
312
628
|
min-width: 0;
|
|
313
629
|
}
|
|
314
630
|
|
|
631
|
+
.value-column.value-empty {
|
|
632
|
+
opacity: 0.5;
|
|
633
|
+
}
|
|
634
|
+
|
|
315
635
|
.value-label {
|
|
316
636
|
font-size: 12px;
|
|
317
637
|
font-weight: bold;
|
|
@@ -322,7 +642,7 @@ export const ThemeComparator: React.FC<ThemeComparatorProps> = ({
|
|
|
322
642
|
.value-content {
|
|
323
643
|
margin: 0;
|
|
324
644
|
padding: 12px;
|
|
325
|
-
background:
|
|
645
|
+
background: rgba(255, 255, 255, 0.7);
|
|
326
646
|
border-radius: 4px;
|
|
327
647
|
font-family: 'Monaco', 'Menlo', monospace;
|
|
328
648
|
font-size: 12px;
|
|
@@ -331,6 +651,11 @@ export const ThemeComparator: React.FC<ThemeComparatorProps> = ({
|
|
|
331
651
|
word-break: break-all;
|
|
332
652
|
}
|
|
333
653
|
|
|
654
|
+
.value-empty {
|
|
655
|
+
color: #999;
|
|
656
|
+
font-style: italic;
|
|
657
|
+
}
|
|
658
|
+
|
|
334
659
|
.value-divider {
|
|
335
660
|
font-size: 20px;
|
|
336
661
|
color: #666;
|
|
@@ -340,4 +665,3 @@ export const ThemeComparator: React.FC<ThemeComparatorProps> = ({
|
|
|
340
665
|
</div>
|
|
341
666
|
);
|
|
342
667
|
};
|
|
343
|
-
|