@shohojdhara/atomix 0.5.1 → 0.5.2
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/atomix.config.ts +12 -0
- package/build-tools/webpack-loader.js +5 -4
- package/dist/atomix.css +138 -17
- package/dist/atomix.css.map +1 -1
- package/dist/atomix.min.css +1 -1
- package/dist/atomix.min.css.map +1 -1
- package/dist/build-tools/webpack-loader.js +5 -4
- package/dist/charts.d.ts +23 -23
- package/dist/charts.js +40 -37
- package/dist/charts.js.map +1 -1
- package/dist/config.d.ts +624 -0
- package/dist/config.js +59 -0
- package/dist/config.js.map +1 -0
- package/dist/core.d.ts +2 -2
- package/dist/core.js +111 -50
- package/dist/core.js.map +1 -1
- package/dist/forms.d.ts +3 -6
- package/dist/forms.js +2 -2
- package/dist/forms.js.map +1 -1
- package/dist/heavy.d.ts +1 -1
- package/dist/heavy.js +173 -111
- package/dist/heavy.js.map +1 -1
- package/dist/index.d.ts +98 -65
- package/dist/index.esm.js +427 -422
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +394 -391
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/dist/layout.js +59 -60
- package/dist/layout.js.map +1 -1
- package/dist/theme.js +4 -4
- package/dist/theme.js.map +1 -1
- package/package.json +14 -9
- package/scripts/atomix-cli.js +15 -1
- package/scripts/cli/__tests__/complexity-utils.test.js +24 -0
- package/scripts/cli/__tests__/detector.test.js +50 -0
- package/scripts/cli/__tests__/template-engine.test.js +23 -0
- package/scripts/cli/__tests__/test-setup.js +3 -0
- package/scripts/cli/commands/doctor.js +15 -3
- package/scripts/cli/commands/generate.js +113 -51
- package/scripts/cli/internal/ai-engine.js +30 -10
- package/scripts/cli/internal/complexity-utils.js +60 -0
- package/scripts/cli/internal/component-validator.js +49 -16
- package/scripts/cli/internal/generator.js +89 -36
- package/scripts/cli/internal/hook-generator.js +5 -2
- package/scripts/cli/internal/itcss-generator.js +16 -12
- package/scripts/cli/templates/next-templates.js +81 -30
- package/scripts/cli/templates/storybook-templates.js +12 -2
- package/scripts/cli/utils/detector.js +45 -7
- package/scripts/cli/utils/diagnostics.js +78 -0
- package/scripts/cli/utils/telemetry.js +13 -0
- package/src/components/Accordion/Accordion.stories.tsx +4 -0
- package/src/components/AtomixGlass/AtomixGlassContainer.tsx +1 -1
- package/src/components/AtomixGlass/__snapshots__/AtomixGlass.test.tsx.snap +219 -0
- package/src/components/AtomixGlass/glass-utils.ts +1 -1
- package/src/components/Button/Button.tsx +114 -57
- package/src/components/Callout/Callout.tsx +4 -4
- package/src/components/Chart/ChartRenderer.tsx +1 -1
- package/src/components/Chart/DonutChart.tsx +11 -8
- package/src/components/EdgePanel/EdgePanel.tsx +119 -115
- package/src/components/Form/Select.tsx +4 -4
- package/src/components/List/List.tsx +4 -4
- package/src/components/Navigation/SideMenu/SideMenu.tsx +6 -6
- package/src/components/PhotoViewer/PhotoViewerImage.tsx +1 -1
- package/src/components/ProductReview/ProductReview.tsx +4 -2
- package/src/components/Rating/Rating.tsx +4 -2
- package/src/components/SectionIntro/SectionIntro.tsx +4 -2
- package/src/components/Steps/Steps.tsx +1 -1
- package/src/components/Tabs/Tabs.tsx +5 -5
- package/src/components/Testimonial/Testimonial.tsx +4 -2
- package/src/components/VideoPlayer/VideoPlayer.tsx +4 -2
- package/src/layouts/CssGrid/CssGrid.stories.tsx +464 -0
- package/src/layouts/CssGrid/CssGrid.tsx +215 -0
- package/src/layouts/CssGrid/index.ts +8 -0
- package/src/layouts/CssGrid/scripts/CssGrid.js +284 -0
- package/src/layouts/CssGrid/scripts/index.js +43 -0
- package/src/layouts/Grid/scripts/Container.js +139 -0
- package/src/layouts/Grid/scripts/Grid.js +184 -0
- package/src/layouts/Grid/scripts/GridCol.js +273 -0
- package/src/layouts/Grid/scripts/Row.js +154 -0
- package/src/layouts/Grid/scripts/index.js +48 -0
- package/src/layouts/MasonryGrid/MasonryGrid.tsx +71 -59
- package/src/lib/composables/atomix-glass/useGlassSize.ts +1 -1
- package/src/lib/composables/useAccordion.ts +5 -5
- package/src/lib/composables/useAtomixGlass.ts +3 -3
- package/src/lib/composables/useBarChart.ts +2 -2
- package/src/lib/composables/useChart.ts +3 -2
- package/src/lib/composables/useChartToolbar.ts +48 -66
- package/src/lib/composables/useDataTable.ts +1 -1
- package/src/lib/composables/useDatePicker.ts +2 -2
- package/src/lib/composables/useEdgePanel.ts +45 -54
- package/src/lib/composables/useHeroBackgroundSlider.ts +5 -5
- package/src/lib/composables/usePhotoViewer.ts +2 -3
- package/src/lib/composables/usePieChart.ts +1 -1
- package/src/lib/composables/usePopover.ts +151 -139
- package/src/lib/composables/useSideMenu.ts +28 -41
- package/src/lib/composables/useSlider.ts +2 -6
- package/src/lib/composables/useTooltip.ts +2 -2
- package/src/lib/config/index.ts +39 -0
- package/src/lib/theme/devtools/Comparator.tsx +1 -1
- package/src/lib/theme/devtools/Inspector.tsx +1 -1
- package/src/lib/theme/devtools/LiveEditor.tsx +1 -1
- package/src/lib/theme/runtime/ThemeProvider.tsx +1 -1
- package/src/styles/01-settings/_index.scss +1 -0
- package/src/styles/01-settings/_settings.atomix-glass.scss +174 -0
- package/src/styles/01-settings/_settings.masonry-grid.scss +42 -6
- package/src/styles/02-tools/_tools.glass.scss +6 -0
- package/src/styles/05-objects/_objects.masonry-grid.scss +162 -24
- package/src/styles/06-components/_components.atomix-glass.scss +4 -4
- package/src/lib/composables/useBreadcrumb.ts +0 -81
- package/src/lib/composables/useChartInteractions.ts +0 -123
- package/src/lib/composables/useChartPerformance.ts +0 -347
- package/src/lib/composables/useDropdown.ts +0 -338
- package/src/lib/composables/useModal.ts +0 -110
- package/src/lib/hooks/usePerformanceMonitor.ts +0 -148
- package/src/lib/utils/displacement-generator.ts +0 -92
- package/src/lib/utils/memoryMonitor.ts +0 -191
- package/src/styles/01-settings/_settings.testtypecheck.scss +0 -53
- package/src/styles/01-settings/_settings.typedbutton.scss +0 -53
- package/src/styles/06-components/_components.testbutton.scss +0 -212
- package/src/styles/06-components/_components.testtypecheck.scss +0 -212
- package/src/styles/06-components/_components.typedbutton.scss +0 -212
|
@@ -1,41 +1,116 @@
|
|
|
1
1
|
@use '../01-settings/settings.config' as config;
|
|
2
2
|
@use '../01-settings/settings.masonry-grid' as masonry;
|
|
3
3
|
@use '../01-settings/settings.breakpoints' as breakpoints;
|
|
4
|
+
@use '../01-settings/settings.design-tokens' as tokens;
|
|
4
5
|
@use '../02-tools/tools.rem' as *;
|
|
6
|
+
@use '../02-tools/tools.media-queries' as mq;
|
|
5
7
|
@use 'sass:math';
|
|
6
8
|
|
|
9
|
+
// === MODERN CSS GRID FALLBACK IMPLEMENTATION ===
|
|
10
|
+
// Provides graceful degradation when JavaScript is disabled
|
|
11
|
+
.o-masonry-grid--css-fallback {
|
|
12
|
+
display: grid;
|
|
13
|
+
gap: masonry.$masonry-grid-gap;
|
|
14
|
+
// Use auto-fit with minmax for responsive columns
|
|
15
|
+
grid-template-columns: repeat(auto-fit, minmax(var(--atomix-masonry-min-column-width, 250px), 1fr));
|
|
16
|
+
|
|
17
|
+
// Mobile-first responsive adjustments
|
|
18
|
+
@include mq.media-up('sm') {
|
|
19
|
+
gap: masonry.$masonry-grid-gap-sm;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
@include mq.media-up('md') {
|
|
23
|
+
gap: masonry.$masonry-grid-gap;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
@include mq.media-up('lg') {
|
|
27
|
+
gap: masonry.$masonry-grid-gap-lg;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.o-masonry-grid__item {
|
|
31
|
+
// CSS Grid items don't need position: absolute
|
|
32
|
+
position: static;
|
|
33
|
+
opacity: 1;
|
|
34
|
+
visibility: visible;
|
|
35
|
+
width: 100%;
|
|
36
|
+
|
|
37
|
+
// Enhanced accessibility for CSS Grid
|
|
38
|
+
&:focus {
|
|
39
|
+
outline: masonry.$masonry-grid-focus-ring-width solid masonry.$masonry-grid-focus-ring-color;
|
|
40
|
+
outline-offset: masonry.$masonry-grid-focus-ring-offset;
|
|
41
|
+
z-index: masonry.$masonry-grid-z-index-focus;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
7
46
|
.o-masonry-grid {
|
|
8
47
|
$root: &;
|
|
9
48
|
|
|
10
|
-
//
|
|
49
|
+
// Mobile-first base styles with design system tokens
|
|
11
50
|
position: relative;
|
|
12
51
|
width: 100%;
|
|
13
|
-
min-height:
|
|
52
|
+
min-height: masonry.$masonry-grid-min-height-mobile;
|
|
14
53
|
|
|
15
|
-
//
|
|
54
|
+
// Enhanced focus management for accessibility
|
|
55
|
+
&:focus-within {
|
|
56
|
+
.o-masonry-grid__item:focus {
|
|
57
|
+
outline: masonry.$masonry-grid-focus-ring-width solid masonry.$masonry-grid-focus-ring-color;
|
|
58
|
+
outline-offset: masonry.$masonry-grid-focus-ring-offset;
|
|
59
|
+
z-index: masonry.$masonry-grid-z-index-focus;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Animation variant using design system transitions
|
|
16
64
|
&--animate {
|
|
17
65
|
.o-masonry-grid__item {
|
|
18
|
-
transition:
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
66
|
+
transition: masonry.$masonry-grid-item-transition;
|
|
67
|
+
// Modern CSS custom property for fine-grained animation control
|
|
68
|
+
@supports (transition-behavior: allow-discrete) {
|
|
69
|
+
transition-behavior: allow-discrete;
|
|
70
|
+
}
|
|
22
71
|
}
|
|
23
72
|
}
|
|
24
73
|
|
|
25
|
-
// Item styles
|
|
74
|
+
// Item styles with enhanced accessibility and design system integration
|
|
26
75
|
&__item {
|
|
27
76
|
box-sizing: border-box;
|
|
28
77
|
width: 100%;
|
|
78
|
+
// Design system color tokens for background
|
|
79
|
+
background-color: var(--#{config.$prefix}body-bg);
|
|
80
|
+
|
|
81
|
+
// Enhanced focus states with design system tokens
|
|
82
|
+
&:focus {
|
|
83
|
+
outline: masonry.$masonry-grid-focus-ring-width solid masonry.$masonry-grid-focus-ring-color;
|
|
84
|
+
outline-offset: masonry.$masonry-grid-focus-ring-offset;
|
|
85
|
+
z-index: masonry.$masonry-grid-z-index-focus;
|
|
86
|
+
|
|
87
|
+
// Subtle scale effect for better visual feedback
|
|
88
|
+
transform: scale(1.02);
|
|
89
|
+
transition: transform var(--#{config.$prefix}transition-duration-fast) var(--#{config.$prefix}transition-timing-base);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// High contrast mode support
|
|
93
|
+
@media (prefers-contrast: high) {
|
|
94
|
+
&:focus {
|
|
95
|
+
outline-width: calc(#{masonry.$masonry-grid-focus-ring-width} * 2);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
29
98
|
|
|
30
99
|
// Prevent FOUC (Flash of Unstyled Content)
|
|
31
100
|
&:not([style*='position: absolute']) {
|
|
32
101
|
opacity: 0;
|
|
33
102
|
visibility: hidden;
|
|
103
|
+
// Use CSS Custom Property for progressive enhancement
|
|
104
|
+
@supports (view-transition-name: masonry-item) {
|
|
105
|
+
view-transition-name: masonry-item;
|
|
106
|
+
}
|
|
34
107
|
}
|
|
35
108
|
}
|
|
36
109
|
|
|
37
|
-
// Loading states
|
|
110
|
+
// Loading states with modern design system colors
|
|
38
111
|
&__item-loading {
|
|
112
|
+
position: relative;
|
|
113
|
+
|
|
39
114
|
&::before {
|
|
40
115
|
content: '';
|
|
41
116
|
position: absolute;
|
|
@@ -43,54 +118,117 @@
|
|
|
43
118
|
left: 0;
|
|
44
119
|
width: 100%;
|
|
45
120
|
height: 100%;
|
|
46
|
-
background
|
|
47
|
-
|
|
48
|
-
|
|
121
|
+
// Design system loading state background
|
|
122
|
+
background: var(--#{config.$prefix}gray-10);
|
|
123
|
+
z-index: masonry.$masonry-grid-z-index-loading;
|
|
124
|
+
border-radius: var(--#{config.$prefix}border-radius-sm);
|
|
125
|
+
|
|
126
|
+
// Smooth fade-in animation
|
|
127
|
+
animation: masonry-loading-fade-in 0.5s ease-out forwards;
|
|
49
128
|
}
|
|
50
129
|
|
|
51
130
|
img {
|
|
52
131
|
opacity: 0;
|
|
132
|
+
// CSS @property for smoother opacity transitions
|
|
133
|
+
@supports (property: opacity) and (animation-timeline: view()) {
|
|
134
|
+
@property --masonry-img-opacity {
|
|
135
|
+
syntax: '<number>';
|
|
136
|
+
inherits: false;
|
|
137
|
+
initial-value: 0;
|
|
138
|
+
}
|
|
139
|
+
opacity: var(--masonry-img-opacity);
|
|
140
|
+
}
|
|
53
141
|
}
|
|
54
142
|
}
|
|
55
143
|
|
|
56
|
-
//
|
|
57
|
-
&__item-loaded
|
|
58
|
-
|
|
144
|
+
// Enhanced loaded state with modern CSS features
|
|
145
|
+
&__item-loaded {
|
|
146
|
+
img {
|
|
147
|
+
// Modern CSS animation with design system tokens
|
|
148
|
+
animation: masonry-item-fade-in masonry.$masonry-grid-item-animation-duration masonry.$masonry-grid-item-animation-timing forwards;
|
|
149
|
+
|
|
150
|
+
// CSS @property for precise opacity control
|
|
151
|
+
@supports (property: opacity) and (animation-timeline: view()) {
|
|
152
|
+
@property --masonry-img-opacity {
|
|
153
|
+
syntax: '<number>';
|
|
154
|
+
inherits: false;
|
|
155
|
+
initial-value: 0;
|
|
156
|
+
}
|
|
157
|
+
opacity: var(--masonry-img-opacity);
|
|
158
|
+
animation: masonry-item-fade-in-advanced masonry.$masonry-grid-item-animation-duration masonry.$masonry-grid-item-animation-timing forwards;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
59
161
|
}
|
|
60
162
|
|
|
61
|
-
//
|
|
163
|
+
// Modern progressive loading indicator
|
|
62
164
|
&--loading-images {
|
|
63
165
|
position: relative;
|
|
64
166
|
|
|
65
167
|
&::after {
|
|
66
168
|
content: '';
|
|
67
169
|
position: absolute;
|
|
68
|
-
bottom:
|
|
69
|
-
right:
|
|
70
|
-
width:
|
|
71
|
-
height:
|
|
72
|
-
border:
|
|
170
|
+
bottom: var(--#{config.$prefix}spacing-16);
|
|
171
|
+
right: var(--#{config.$prefix}spacing-16);
|
|
172
|
+
width: masonry.$masonry-grid-loading-indicator-size;
|
|
173
|
+
height: masonry.$masonry-grid-loading-indicator-size;
|
|
174
|
+
border: masonry.$masonry-grid-loading-indicator-border-width solid rgba(0, 0, 0, 0.1);
|
|
73
175
|
border-radius: 50%;
|
|
74
|
-
border-top-color:
|
|
176
|
+
border-top-color: masonry.$masonry-grid-loading-indicator-color;
|
|
75
177
|
animation: masonry-spinner 0.8s linear infinite;
|
|
76
|
-
z-index:
|
|
178
|
+
z-index: masonry.$masonry-grid-z-index-loading;
|
|
77
179
|
pointer-events: none;
|
|
180
|
+
|
|
181
|
+
// Dark mode support
|
|
182
|
+
@media (prefers-color-scheme: dark) {
|
|
183
|
+
border-color: rgba(255, 255, 255, 0.1);
|
|
184
|
+
border-top-color: var(--#{config.$prefix}brand-border-subtle-dark);
|
|
185
|
+
}
|
|
78
186
|
}
|
|
79
187
|
}
|
|
80
188
|
|
|
81
|
-
//
|
|
189
|
+
// Responsive adjustments for desktop
|
|
190
|
+
@include mq.media-up('md') {
|
|
191
|
+
min-height: masonry.$masonry-grid-min-height-desktop;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Modern animations with CSS custom properties
|
|
82
195
|
@keyframes masonry-spinner {
|
|
83
196
|
to {
|
|
84
197
|
transform: rotate(360deg);
|
|
85
198
|
}
|
|
86
199
|
}
|
|
87
200
|
|
|
201
|
+
@keyframes masonry-loading-fade-in {
|
|
202
|
+
from {
|
|
203
|
+
opacity: 0;
|
|
204
|
+
transform: scale(0.95);
|
|
205
|
+
}
|
|
206
|
+
to {
|
|
207
|
+
opacity: 1;
|
|
208
|
+
transform: scale(1);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
88
212
|
@keyframes masonry-item-fade-in {
|
|
89
213
|
from {
|
|
90
214
|
opacity: 0;
|
|
215
|
+
transform: translateY(20px);
|
|
91
216
|
}
|
|
92
217
|
to {
|
|
93
218
|
opacity: 1;
|
|
219
|
+
transform: translateY(0);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Advanced animation with CSS @property for smooth transitions
|
|
224
|
+
@keyframes masonry-item-fade-in-advanced {
|
|
225
|
+
from {
|
|
226
|
+
--masonry-img-opacity: 0;
|
|
227
|
+
transform: translateY(20px) scale(0.95);
|
|
228
|
+
}
|
|
229
|
+
to {
|
|
230
|
+
--masonry-img-opacity: 1;
|
|
231
|
+
transform: translateY(0) scale(1);
|
|
94
232
|
}
|
|
95
233
|
}
|
|
96
234
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Component: AtomixGlass
|
|
2
2
|
// =============================================================================
|
|
3
3
|
|
|
4
|
+
@use '../01-settings/settings.atomix-glass' as *;
|
|
4
5
|
@use '../02-tools/tools.component' as *;
|
|
5
6
|
|
|
6
7
|
.c-atomix-glass {
|
|
@@ -9,7 +10,6 @@
|
|
|
9
10
|
// Local, sane defaults (kept minimal)
|
|
10
11
|
--_glass-radius: var(--atomix-glass-radius, var(--atomix-radius-md, 0.75rem));
|
|
11
12
|
--_glass-transform: var(--atomix-glass-transform, translateZ(0));
|
|
12
|
-
--_glass-backdrop: var(--atomix-glass-border-backdrop, blur(14px) saturate(140%));
|
|
13
13
|
|
|
14
14
|
--atomix-glass-position: absolute;
|
|
15
15
|
--atomix-glass-border-width: var(--atomix-spacing-0-5, 0.125rem);
|
|
@@ -221,7 +221,7 @@
|
|
|
221
221
|
&__border-backdrop {
|
|
222
222
|
mix-blend-mode: overlay;
|
|
223
223
|
z-index: var(--_glass-z-border-1);
|
|
224
|
-
backdrop-filter:
|
|
224
|
+
backdrop-filter: blur(30px) saturate(140%) brightness(110%);
|
|
225
225
|
box-shadow: var(--atomix-glass-border-shadow);
|
|
226
226
|
}
|
|
227
227
|
|
|
@@ -291,8 +291,8 @@
|
|
|
291
291
|
|
|
292
292
|
&__content {
|
|
293
293
|
position: relative;
|
|
294
|
-
width: var(--atomix-glass-
|
|
295
|
-
height: var(--atomix-glass-
|
|
294
|
+
width: var(--atomix-glass-width);
|
|
295
|
+
height: var(--atomix-glass-height);
|
|
296
296
|
border-radius: var(--_glass-radius);
|
|
297
297
|
z-index: var(--_glass-z-content);
|
|
298
298
|
text-shadow: var(--atomix-glass-container-text-shadow);
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
import { BreadcrumbItemType } from '../../components/Breadcrumb/Breadcrumb';
|
|
2
|
-
import { BREADCRUMB } from '../constants/components';
|
|
3
|
-
|
|
4
|
-
interface BreadcrumbOptions {
|
|
5
|
-
items: BreadcrumbItemType[];
|
|
6
|
-
divider?: React.ReactNode;
|
|
7
|
-
className?: string;
|
|
8
|
-
'aria-label'?: string;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Breadcrumb state and functionality
|
|
13
|
-
* @param initialOptions - Initial breadcrumb options
|
|
14
|
-
* @returns Breadcrumb state and methods
|
|
15
|
-
*/
|
|
16
|
-
export function useBreadcrumb(initialOptions?: Partial<BreadcrumbOptions>) {
|
|
17
|
-
// Default breadcrumb options
|
|
18
|
-
const defaultOptions: BreadcrumbOptions = {
|
|
19
|
-
items: [],
|
|
20
|
-
divider: BREADCRUMB.DEFAULTS.DIVIDER,
|
|
21
|
-
className: '',
|
|
22
|
-
'aria-label': 'Breadcrumb',
|
|
23
|
-
...initialOptions,
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Generate breadcrumb class based on options
|
|
28
|
-
* @param options - Breadcrumb options
|
|
29
|
-
* @returns Class string
|
|
30
|
-
*/
|
|
31
|
-
const generateBreadcrumbClass = (options: Partial<BreadcrumbOptions>): string => {
|
|
32
|
-
const { className = '' } = options;
|
|
33
|
-
|
|
34
|
-
return [BREADCRUMB.CLASSES.BASE, className].filter(Boolean).join(' ').trim();
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Generate breadcrumb item class
|
|
39
|
-
* @param item - Breadcrumb item
|
|
40
|
-
* @param isLast - Whether this is the last item
|
|
41
|
-
* @returns Class string
|
|
42
|
-
*/
|
|
43
|
-
const generateItemClass = (item: BreadcrumbItemType, isLast: boolean): string => {
|
|
44
|
-
return [BREADCRUMB.CLASSES.ITEM, item.active || isLast ? BREADCRUMB.CLASSES.ACTIVE : '']
|
|
45
|
-
.filter(Boolean)
|
|
46
|
-
.join(' ')
|
|
47
|
-
.trim();
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Check if an item should be rendered as a link
|
|
52
|
-
* @param item - Breadcrumb item
|
|
53
|
-
* @param isLast - Whether this is the last item
|
|
54
|
-
* @returns Whether item should be a link
|
|
55
|
-
*/
|
|
56
|
-
const isItemLink = (item: BreadcrumbItemType, isLast: boolean): boolean => {
|
|
57
|
-
return Boolean(item.href && !item.active && !isLast);
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Parse items from a JSON string
|
|
62
|
-
* @param jsonString - JSON string of items
|
|
63
|
-
* @returns Array of breadcrumb items
|
|
64
|
-
*/
|
|
65
|
-
const parseItemsFromJson = (jsonString: string): BreadcrumbItemType[] => {
|
|
66
|
-
try {
|
|
67
|
-
return JSON.parse(jsonString) as BreadcrumbItemType[];
|
|
68
|
-
} catch (error) {
|
|
69
|
-
console.error('Error parsing breadcrumb items:', error);
|
|
70
|
-
return [];
|
|
71
|
-
}
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
return {
|
|
75
|
-
defaultOptions,
|
|
76
|
-
generateBreadcrumbClass,
|
|
77
|
-
generateItemClass,
|
|
78
|
-
isItemLink,
|
|
79
|
-
parseItemsFromJson,
|
|
80
|
-
};
|
|
81
|
-
}
|
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
import { useCallback, useRef, useState } from 'react';
|
|
2
|
-
|
|
3
|
-
interface InteractionState {
|
|
4
|
-
isDragging: boolean;
|
|
5
|
-
isZooming: boolean;
|
|
6
|
-
lastPointerPos: { x: number; y: number } | null;
|
|
7
|
-
zoomLevel: number;
|
|
8
|
-
panOffset: { x: number; y: number };
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export function useChartInteractions() {
|
|
12
|
-
const [state, setState] = useState<InteractionState>({
|
|
13
|
-
isDragging: false,
|
|
14
|
-
isZooming: false,
|
|
15
|
-
lastPointerPos: null,
|
|
16
|
-
zoomLevel: 1,
|
|
17
|
-
panOffset: { x: 0, y: 0 },
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
const wheelTimeoutRef = useRef<NodeJS.Timeout | null>(null);
|
|
21
|
-
const lastWheelTime = useRef(0);
|
|
22
|
-
|
|
23
|
-
// Improved wheel handling for both mouse and trackpad
|
|
24
|
-
const handleWheel = useCallback((event: WheelEvent) => {
|
|
25
|
-
event.preventDefault();
|
|
26
|
-
|
|
27
|
-
const now = Date.now();
|
|
28
|
-
const timeDelta = now - lastWheelTime.current;
|
|
29
|
-
lastWheelTime.current = now;
|
|
30
|
-
|
|
31
|
-
// Detect trackpad vs mouse wheel
|
|
32
|
-
const isTrackpad = Math.abs(event.deltaY) < 50 && timeDelta < 50;
|
|
33
|
-
const sensitivity = isTrackpad ? 0.01 : 0.1;
|
|
34
|
-
|
|
35
|
-
if (wheelTimeoutRef.current) {
|
|
36
|
-
clearTimeout(wheelTimeoutRef.current);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
setState(prev => {
|
|
40
|
-
const rect = (event.target as Element).getBoundingClientRect();
|
|
41
|
-
const centerX = event.clientX - rect.left;
|
|
42
|
-
const centerY = event.clientY - rect.top;
|
|
43
|
-
|
|
44
|
-
// Zoom calculation
|
|
45
|
-
const zoomFactor = 1 - event.deltaY * sensitivity;
|
|
46
|
-
const newZoomLevel = Math.max(0.1, Math.min(10, prev.zoomLevel * zoomFactor));
|
|
47
|
-
|
|
48
|
-
// Pan to zoom center
|
|
49
|
-
const zoomRatio = newZoomLevel / prev.zoomLevel;
|
|
50
|
-
const newPanOffset = {
|
|
51
|
-
x: centerX - (centerX - prev.panOffset.x) * zoomRatio,
|
|
52
|
-
y: centerY - (centerY - prev.panOffset.y) * zoomRatio,
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
return {
|
|
56
|
-
...prev,
|
|
57
|
-
zoomLevel: newZoomLevel,
|
|
58
|
-
panOffset: newPanOffset,
|
|
59
|
-
isZooming: true,
|
|
60
|
-
};
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
wheelTimeoutRef.current = setTimeout(() => {
|
|
64
|
-
setState(prev => ({ ...prev, isZooming: false }));
|
|
65
|
-
}, 150);
|
|
66
|
-
}, []);
|
|
67
|
-
|
|
68
|
-
// Unified pointer events for mouse and touch
|
|
69
|
-
const handlePointerDown = useCallback((event: PointerEvent) => {
|
|
70
|
-
setState(prev => ({
|
|
71
|
-
...prev,
|
|
72
|
-
isDragging: true,
|
|
73
|
-
lastPointerPos: { x: event.clientX, y: event.clientY },
|
|
74
|
-
}));
|
|
75
|
-
(event.target as Element).setPointerCapture(event.pointerId);
|
|
76
|
-
}, []);
|
|
77
|
-
|
|
78
|
-
const handlePointerMove = useCallback((event: PointerEvent) => {
|
|
79
|
-
setState(prev => {
|
|
80
|
-
if (!prev.isDragging || !prev.lastPointerPos) return prev;
|
|
81
|
-
|
|
82
|
-
const deltaX = event.clientX - prev.lastPointerPos.x;
|
|
83
|
-
const deltaY = event.clientY - prev.lastPointerPos.y;
|
|
84
|
-
|
|
85
|
-
return {
|
|
86
|
-
...prev,
|
|
87
|
-
panOffset: {
|
|
88
|
-
x: prev.panOffset.x + deltaX,
|
|
89
|
-
y: prev.panOffset.y + deltaY,
|
|
90
|
-
},
|
|
91
|
-
lastPointerPos: { x: event.clientX, y: event.clientY },
|
|
92
|
-
};
|
|
93
|
-
});
|
|
94
|
-
}, []);
|
|
95
|
-
|
|
96
|
-
const handlePointerUp = useCallback((event: PointerEvent) => {
|
|
97
|
-
setState(prev => ({
|
|
98
|
-
...prev,
|
|
99
|
-
isDragging: false,
|
|
100
|
-
lastPointerPos: null,
|
|
101
|
-
}));
|
|
102
|
-
(event.target as Element).releasePointerCapture(event.pointerId);
|
|
103
|
-
}, []);
|
|
104
|
-
|
|
105
|
-
const resetView = useCallback(() => {
|
|
106
|
-
setState(prev => ({
|
|
107
|
-
...prev,
|
|
108
|
-
zoomLevel: 1,
|
|
109
|
-
panOffset: { x: 0, y: 0 },
|
|
110
|
-
}));
|
|
111
|
-
}, []);
|
|
112
|
-
|
|
113
|
-
return {
|
|
114
|
-
state,
|
|
115
|
-
handlers: {
|
|
116
|
-
onWheel: handleWheel,
|
|
117
|
-
onPointerDown: handlePointerDown,
|
|
118
|
-
onPointerMove: handlePointerMove,
|
|
119
|
-
onPointerUp: handlePointerUp,
|
|
120
|
-
},
|
|
121
|
-
resetView,
|
|
122
|
-
};
|
|
123
|
-
}
|