@shohojdhara/atomix 0.5.0 → 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 +230 -83
- 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 +24 -23
- package/dist/charts.js +271 -369
- 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 +3 -2
- package/dist/core.js +342 -382
- package/dist/core.js.map +1 -1
- package/dist/forms.d.ts +4 -6
- package/dist/forms.js +233 -334
- package/dist/forms.js.map +1 -1
- package/dist/heavy.d.ts +11 -2
- package/dist/heavy.js +406 -445
- package/dist/heavy.js.map +1 -1
- package/dist/index.d.ts +109 -65
- package/dist/index.esm.js +654 -748
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +621 -717
- 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 +24 -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 +1 -133
- 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/AtomixGlass.tsx +188 -128
- package/src/components/AtomixGlass/AtomixGlassContainer.tsx +63 -91
- package/src/components/AtomixGlass/PerformanceDashboard.tsx +153 -201
- package/src/components/AtomixGlass/__snapshots__/AtomixGlass.test.tsx.snap +9 -6
- package/src/components/AtomixGlass/glass-utils.ts +51 -1
- package/src/components/AtomixGlass/stories/AnimationFeatures.stories.tsx +52 -46
- package/src/components/AtomixGlass/stories/Examples.stories.tsx +573 -236
- package/src/components/AtomixGlass/stories/Playground.stories.tsx +88 -41
- package/src/components/AtomixGlass/stories/argTypes.ts +19 -19
- package/src/components/AtomixGlass/stories/shared-components.tsx +7 -12
- package/src/components/AtomixGlass/stories/types.ts +3 -3
- 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 +111 -74
- package/src/lib/composables/useAtomixGlassStyles.ts +0 -2
- 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/constants/components.ts +1 -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/lib/types/components.ts +1 -0
- 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 +160 -99
- package/scripts/cli/__tests__/README.md +0 -81
- package/scripts/cli/__tests__/basic.test.js +0 -116
- package/scripts/cli/__tests__/clean.test.js +0 -278
- package/scripts/cli/__tests__/component-generator.test.js +0 -332
- package/scripts/cli/__tests__/component-validator.test.js +0 -433
- package/scripts/cli/__tests__/generator.test.js +0 -613
- package/scripts/cli/__tests__/glass-motion.test.js +0 -256
- package/scripts/cli/__tests__/integration.test.js +0 -938
- package/scripts/cli/__tests__/migrate.test.js +0 -74
- package/scripts/cli/__tests__/security.test.js +0 -206
- package/scripts/cli/__tests__/theme-bridge.test.js +0 -507
- package/scripts/cli/__tests__/token-manager.test.js +0 -251
- package/scripts/cli/__tests__/token-provider.test.js +0 -361
- package/scripts/cli/__tests__/utils.test.js +0 -165
- package/src/components/AtomixGlass/stories/AnimationTests.stories.tsx +0 -95
- package/src/components/AtomixGlass/stories/CardExamples.stories.tsx +0 -212
- package/src/components/AtomixGlass/stories/Customization.stories.tsx +0 -131
- package/src/components/AtomixGlass/stories/DashboardExamples.stories.tsx +0 -348
- package/src/components/AtomixGlass/stories/EcommerceExamples.stories.tsx +0 -410
- package/src/components/AtomixGlass/stories/FormExamples.stories.tsx +0 -436
- package/src/components/AtomixGlass/stories/HeroExamples.stories.tsx +0 -264
- package/src/components/AtomixGlass/stories/InteractivePlayground.stories.tsx +0 -247
- package/src/components/AtomixGlass/stories/MobileUIExamples.stories.tsx +0 -418
- package/src/components/AtomixGlass/stories/ModalExamples.stories.tsx +0 -402
- package/src/components/AtomixGlass/stories/Modes.stories.tsx +0 -1082
- package/src/components/AtomixGlass/stories/Overview.stories.tsx +0 -497
- package/src/components/AtomixGlass/stories/Performance.stories.tsx +0 -103
- package/src/components/AtomixGlass/stories/PresetGallery.stories.tsx +0 -335
- package/src/components/AtomixGlass/stories/Shaders.stories.tsx +0 -395
- package/src/components/AtomixGlass/stories/WidgetExamples.stories.tsx +0 -441
- package/src/components/TypedButton/TypedButton.stories.tsx +0 -59
- package/src/components/TypedButton/TypedButton.tsx +0 -39
- package/src/components/TypedButton/index.ts +0 -2
- 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/composables/useTypedButton.ts +0 -66
- 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,4 +1,4 @@
|
|
|
1
|
-
import { useState, useRef, useEffect, RefObject } from 'react';
|
|
1
|
+
import { useState, useRef, useEffect, RefObject, useCallback } from 'react';
|
|
2
2
|
|
|
3
3
|
type PopoverPosition = 'top' | 'bottom' | 'left' | 'right';
|
|
4
4
|
type PopoverTrigger = 'click' | 'hover';
|
|
@@ -57,14 +57,17 @@ export const usePopover = ({
|
|
|
57
57
|
const isOpenState = isControlled ? controlledIsOpen : isOpen;
|
|
58
58
|
|
|
59
59
|
// Define setIsOpen function before using it in useEffect
|
|
60
|
-
const setIsOpen = (
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
onOpenChange
|
|
66
|
-
|
|
67
|
-
|
|
60
|
+
const setIsOpen = useCallback(
|
|
61
|
+
(newIsOpen: boolean) => {
|
|
62
|
+
if (!isControlled) {
|
|
63
|
+
setIsOpenState(newIsOpen);
|
|
64
|
+
}
|
|
65
|
+
if (onOpenChange) {
|
|
66
|
+
onOpenChange(newIsOpen);
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
[isControlled, onOpenChange]
|
|
70
|
+
);
|
|
68
71
|
|
|
69
72
|
// Handle hover events if trigger is hover
|
|
70
73
|
useEffect(() => {
|
|
@@ -115,141 +118,150 @@ export const usePopover = ({
|
|
|
115
118
|
popoverRef.current.addEventListener('mouseenter', handlePopoverMouseEnter);
|
|
116
119
|
popoverRef.current.addEventListener('mouseleave', handlePopoverMouseLeave);
|
|
117
120
|
|
|
121
|
+
const currentTrigger = triggerRef.current;
|
|
122
|
+
const currentPopover = popoverRef.current;
|
|
123
|
+
|
|
118
124
|
return () => {
|
|
119
|
-
if (
|
|
120
|
-
|
|
121
|
-
|
|
125
|
+
if (currentTrigger) {
|
|
126
|
+
currentTrigger.removeEventListener('mouseenter', handleTriggerMouseEnter);
|
|
127
|
+
currentTrigger.removeEventListener('mouseleave', handleTriggerMouseLeave);
|
|
122
128
|
}
|
|
123
|
-
if (
|
|
124
|
-
|
|
125
|
-
|
|
129
|
+
if (currentPopover) {
|
|
130
|
+
currentPopover.removeEventListener('mouseenter', handlePopoverMouseEnter);
|
|
131
|
+
currentPopover.removeEventListener('mouseleave', handlePopoverMouseLeave);
|
|
126
132
|
}
|
|
127
133
|
if (timeoutRef.current !== null) {
|
|
128
134
|
window.clearTimeout(timeoutRef.current);
|
|
129
135
|
}
|
|
130
136
|
};
|
|
131
|
-
}, [trigger, delay, isOpenState]);
|
|
132
|
-
|
|
133
|
-
const updatePosition = (
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
// Calculate space available in each direction
|
|
154
|
-
const spaceTop = triggerRect.top;
|
|
155
|
-
const spaceBottom = viewportHeight - triggerRect.bottom;
|
|
156
|
-
const spaceLeft = triggerRect.left;
|
|
157
|
-
const spaceRight = viewportWidth - triggerRect.right;
|
|
158
|
-
|
|
159
|
-
// Determine best position based on available space
|
|
160
|
-
let bestPosition: PopoverPosition = position === 'auto' ? 'top' : (position as PopoverPosition);
|
|
161
|
-
|
|
162
|
-
// If specified position is 'auto', find the position with most space
|
|
163
|
-
if (position === 'auto') {
|
|
164
|
-
const spaces = [
|
|
165
|
-
{ position: 'top', space: spaceTop },
|
|
166
|
-
{ position: 'right', space: spaceRight },
|
|
167
|
-
{ position: 'bottom', space: spaceBottom },
|
|
168
|
-
{ position: 'left', space: spaceLeft },
|
|
169
|
-
];
|
|
170
|
-
|
|
171
|
-
// Sort by available space (descending)
|
|
172
|
-
spaces.sort((a, b) => b.space - a.space);
|
|
173
|
-
|
|
174
|
-
// Select position with most space
|
|
175
|
-
bestPosition = spaces[0]?.position as PopoverPosition;
|
|
176
|
-
} else {
|
|
177
|
-
// Check if the preferred position has enough space
|
|
178
|
-
const needsFlip =
|
|
179
|
-
(position === 'top' &&
|
|
180
|
-
spaceTop < popoverRect.height + offset &&
|
|
181
|
-
spaceBottom >= popoverRect.height + offset) ||
|
|
182
|
-
(position === 'bottom' &&
|
|
183
|
-
spaceBottom < popoverRect.height + offset &&
|
|
184
|
-
spaceTop >= popoverRect.height + offset) ||
|
|
185
|
-
(position === 'left' &&
|
|
186
|
-
spaceLeft < popoverRect.width + offset &&
|
|
187
|
-
spaceRight >= popoverRect.width + offset) ||
|
|
188
|
-
(position === 'right' &&
|
|
189
|
-
spaceRight < popoverRect.width + offset &&
|
|
190
|
-
spaceLeft >= popoverRect.width + offset);
|
|
191
|
-
|
|
192
|
-
if (needsFlip) {
|
|
193
|
-
// Flip to the opposite side
|
|
194
|
-
const oppositePositions: Record<PopoverPosition | 'auto', PopoverPosition> = {
|
|
195
|
-
top: 'bottom',
|
|
196
|
-
bottom: 'top',
|
|
197
|
-
left: 'right',
|
|
198
|
-
right: 'left',
|
|
199
|
-
auto: 'bottom',
|
|
200
|
-
};
|
|
201
|
-
bestPosition = oppositePositions[position as PopoverPosition | 'auto'];
|
|
137
|
+
}, [trigger, delay, isOpenState, setIsOpen]);
|
|
138
|
+
|
|
139
|
+
const updatePosition = useCallback(
|
|
140
|
+
(event?: Event) => {
|
|
141
|
+
if (!triggerRef.current || !popoverRef.current) return;
|
|
142
|
+
|
|
143
|
+
const triggerRect = triggerRef.current.getBoundingClientRect();
|
|
144
|
+
const popoverRect = popoverRef.current.getBoundingClientRect();
|
|
145
|
+
const viewportWidth = window.innerWidth;
|
|
146
|
+
const viewportHeight = window.innerHeight;
|
|
147
|
+
|
|
148
|
+
// Check if the trigger is near viewport edges
|
|
149
|
+
const isNearViewportEdge =
|
|
150
|
+
triggerRect.top < 50 ||
|
|
151
|
+
triggerRect.bottom > viewportHeight - 50 ||
|
|
152
|
+
triggerRect.left < 50 ||
|
|
153
|
+
triggerRect.right > viewportWidth - 50;
|
|
154
|
+
|
|
155
|
+
// If this is a scroll update and trigger isn't near edges, skip repositioning
|
|
156
|
+
if (event?.type === 'scroll' && !isNearViewportEdge) {
|
|
157
|
+
return;
|
|
202
158
|
}
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
159
|
+
|
|
160
|
+
// Calculate space available in each direction
|
|
161
|
+
const spaceTop = triggerRect.top;
|
|
162
|
+
const spaceBottom = viewportHeight - triggerRect.bottom;
|
|
163
|
+
const spaceLeft = triggerRect.left;
|
|
164
|
+
const spaceRight = viewportWidth - triggerRect.right;
|
|
165
|
+
|
|
166
|
+
// Determine best position based on available space
|
|
167
|
+
let bestPosition: PopoverPosition =
|
|
168
|
+
position === 'auto' ? 'top' : (position as PopoverPosition);
|
|
169
|
+
|
|
170
|
+
// If specified position is 'auto', find the position with most space
|
|
171
|
+
if (position === 'auto') {
|
|
172
|
+
const spaces = [
|
|
173
|
+
{ position: 'top', space: spaceTop },
|
|
174
|
+
{ position: 'right', space: spaceRight },
|
|
175
|
+
{ position: 'bottom', space: spaceBottom },
|
|
176
|
+
{ position: 'left', space: spaceLeft },
|
|
177
|
+
];
|
|
178
|
+
|
|
179
|
+
// Sort by available space (descending)
|
|
180
|
+
spaces.sort((a, b) => b.space - a.space);
|
|
181
|
+
|
|
182
|
+
// Select position with most space
|
|
183
|
+
bestPosition = spaces[0]?.position as PopoverPosition;
|
|
184
|
+
} else {
|
|
185
|
+
// Check if the preferred position has enough space
|
|
186
|
+
const needsFlip =
|
|
187
|
+
(position === 'top' &&
|
|
188
|
+
spaceTop < popoverRect.height + offset &&
|
|
189
|
+
spaceBottom >= popoverRect.height + offset) ||
|
|
190
|
+
(position === 'bottom' &&
|
|
191
|
+
spaceBottom < popoverRect.height + offset &&
|
|
192
|
+
spaceTop >= popoverRect.height + offset) ||
|
|
193
|
+
(position === 'left' &&
|
|
194
|
+
spaceLeft < popoverRect.width + offset &&
|
|
195
|
+
spaceRight >= popoverRect.width + offset) ||
|
|
196
|
+
(position === 'right' &&
|
|
197
|
+
spaceRight < popoverRect.width + offset &&
|
|
198
|
+
spaceLeft >= popoverRect.width + offset);
|
|
199
|
+
|
|
200
|
+
if (needsFlip) {
|
|
201
|
+
// Flip to the opposite side
|
|
202
|
+
const oppositePositions: Record<PopoverPosition | 'auto', PopoverPosition> = {
|
|
203
|
+
top: 'bottom',
|
|
204
|
+
bottom: 'top',
|
|
205
|
+
left: 'right',
|
|
206
|
+
right: 'left',
|
|
207
|
+
auto: 'bottom',
|
|
208
|
+
};
|
|
209
|
+
bestPosition = oppositePositions[position as PopoverPosition | 'auto'];
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
setCurrentPosition(bestPosition);
|
|
214
|
+
|
|
215
|
+
// Calculate position based on the determined best position
|
|
216
|
+
let top = 0;
|
|
217
|
+
let left = 0;
|
|
218
|
+
|
|
219
|
+
// Calculate viewport-relative position
|
|
220
|
+
switch (bestPosition) {
|
|
221
|
+
case 'top':
|
|
222
|
+
top = triggerRect.top - popoverRect.height - offset;
|
|
223
|
+
left = triggerRect.left + triggerRect.width / 2 - popoverRect.width / 2;
|
|
224
|
+
break;
|
|
225
|
+
case 'bottom':
|
|
226
|
+
top = triggerRect.bottom + offset;
|
|
227
|
+
left = triggerRect.left + triggerRect.width / 2 - popoverRect.width / 2;
|
|
228
|
+
break;
|
|
229
|
+
case 'left':
|
|
230
|
+
top = triggerRect.top + triggerRect.height / 2 - popoverRect.height / 2;
|
|
231
|
+
left = triggerRect.left - popoverRect.width - offset;
|
|
232
|
+
break;
|
|
233
|
+
case 'right':
|
|
234
|
+
top = triggerRect.top + triggerRect.height / 2 - popoverRect.height / 2;
|
|
235
|
+
left = triggerRect.right + offset;
|
|
236
|
+
break;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Constrain to viewport boundaries
|
|
240
|
+
if (left < 0) {
|
|
241
|
+
left = 5;
|
|
242
|
+
} else if (left + popoverRect.width > viewportWidth) {
|
|
243
|
+
left = viewportWidth - popoverRect.width - 5;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
if (top < 0) {
|
|
247
|
+
top = 5;
|
|
248
|
+
} else if (top + popoverRect.height > viewportHeight) {
|
|
249
|
+
top = viewportHeight - popoverRect.height - 5;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Add scroll position to convert viewport coordinates to absolute position
|
|
253
|
+
const absoluteTop = top + window.scrollY;
|
|
254
|
+
const absoluteLeft = left + window.scrollX;
|
|
255
|
+
|
|
256
|
+
// Apply position using absolute positioning to follow when scrolling
|
|
257
|
+
if (popoverRef.current) {
|
|
258
|
+
popoverRef.current.style.position = 'absolute';
|
|
259
|
+
popoverRef.current.style.top = `${absoluteTop}px`;
|
|
260
|
+
popoverRef.current.style.left = `${absoluteLeft}px`;
|
|
261
|
+
}
|
|
262
|
+
},
|
|
263
|
+
[position, offset]
|
|
264
|
+
);
|
|
253
265
|
|
|
254
266
|
// Position the popover
|
|
255
267
|
useEffect(() => {
|
|
@@ -289,7 +301,7 @@ export const usePopover = ({
|
|
|
289
301
|
}
|
|
290
302
|
clearInterval(intervalId);
|
|
291
303
|
};
|
|
292
|
-
}, [isOpenState,
|
|
304
|
+
}, [isOpenState, updatePosition]);
|
|
293
305
|
|
|
294
306
|
// Handle click outside to close popover
|
|
295
307
|
useEffect(() => {
|
|
@@ -311,7 +323,7 @@ export const usePopover = ({
|
|
|
311
323
|
return () => {
|
|
312
324
|
document.removeEventListener('mousedown', handleClickOutside);
|
|
313
325
|
};
|
|
314
|
-
}, [isOpenState, closeOnClickOutside]);
|
|
326
|
+
}, [isOpenState, closeOnClickOutside, setIsOpen]);
|
|
315
327
|
|
|
316
328
|
// Handle escape key to close popover
|
|
317
329
|
useEffect(() => {
|
|
@@ -328,7 +340,7 @@ export const usePopover = ({
|
|
|
328
340
|
return () => {
|
|
329
341
|
document.removeEventListener('keydown', handleEscapeKey);
|
|
330
342
|
};
|
|
331
|
-
}, [isOpenState, closeOnEscape]);
|
|
343
|
+
}, [isOpenState, closeOnEscape, setIsOpen]);
|
|
332
344
|
|
|
333
345
|
// Clean up on unmount
|
|
334
346
|
useEffect(() => {
|
|
@@ -8,20 +8,18 @@ import { SIDE_MENU } from '../constants/components';
|
|
|
8
8
|
* @returns SideMenu state and methods
|
|
9
9
|
*/
|
|
10
10
|
export function useSideMenu(initialProps?: Partial<SideMenuProps>) {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
};
|
|
11
|
+
const {
|
|
12
|
+
collapsible = true,
|
|
13
|
+
collapsibleDesktop = false,
|
|
14
|
+
defaultCollapsedDesktop = false,
|
|
15
|
+
isOpen,
|
|
16
|
+
onToggle,
|
|
17
|
+
disabled = false,
|
|
18
|
+
} = initialProps || {};
|
|
19
19
|
|
|
20
20
|
// Local open state for when not controlled externally
|
|
21
21
|
const [isOpenState, setIsOpenState] = useState(
|
|
22
|
-
|
|
23
|
-
? !defaultProps.defaultCollapsedDesktop
|
|
24
|
-
: defaultProps.isOpen || false
|
|
22
|
+
defaultCollapsedDesktop !== undefined ? !defaultCollapsedDesktop : isOpen || false
|
|
25
23
|
);
|
|
26
24
|
|
|
27
25
|
// Refs for managing responsive behavior
|
|
@@ -31,19 +29,18 @@ export function useSideMenu(initialProps?: Partial<SideMenuProps>) {
|
|
|
31
29
|
|
|
32
30
|
// Update local state when external state changes
|
|
33
31
|
useEffect(() => {
|
|
34
|
-
if (typeof
|
|
35
|
-
setIsOpenState(
|
|
36
|
-
} else if (
|
|
37
|
-
setIsOpenState(!
|
|
32
|
+
if (typeof isOpen !== 'undefined') {
|
|
33
|
+
setIsOpenState(isOpen);
|
|
34
|
+
} else if (defaultCollapsedDesktop !== undefined) {
|
|
35
|
+
setIsOpenState(!defaultCollapsedDesktop);
|
|
38
36
|
}
|
|
39
|
-
}, [
|
|
37
|
+
}, [isOpen, defaultCollapsedDesktop]);
|
|
40
38
|
|
|
41
39
|
// Set initial height on mount
|
|
42
40
|
useEffect(() => {
|
|
43
41
|
const isMobile = window.innerWidth < 768;
|
|
44
|
-
const shouldCollapse = isMobile ?
|
|
45
|
-
const currentOpen =
|
|
46
|
-
typeof defaultProps.isOpen !== 'undefined' ? defaultProps.isOpen : isOpenState;
|
|
42
|
+
const shouldCollapse = isMobile ? collapsible : collapsibleDesktop;
|
|
43
|
+
const currentOpen = typeof isOpen !== 'undefined' ? isOpen : isOpenState;
|
|
47
44
|
|
|
48
45
|
if (shouldCollapse && wrapperRef.current && innerRef.current) {
|
|
49
46
|
// Use setTimeout to ensure DOM is fully rendered
|
|
@@ -62,13 +59,13 @@ export function useSideMenu(initialProps?: Partial<SideMenuProps>) {
|
|
|
62
59
|
wrapperRef.current.style.height = 'auto';
|
|
63
60
|
}
|
|
64
61
|
return undefined;
|
|
65
|
-
}, []);
|
|
62
|
+
}, [collapsible, collapsibleDesktop, isOpen, isOpenState]);
|
|
66
63
|
|
|
67
64
|
// Handle responsive behavior - vertical collapse for both mobile and desktop
|
|
68
65
|
useEffect(() => {
|
|
69
66
|
const handleResize = () => {
|
|
70
67
|
const isMobile = window.innerWidth < 768; // MD breakpoint
|
|
71
|
-
const shouldCollapse = isMobile ?
|
|
68
|
+
const shouldCollapse = isMobile ? collapsible : collapsibleDesktop;
|
|
72
69
|
|
|
73
70
|
if (!shouldCollapse) {
|
|
74
71
|
// Not collapsible - always show content
|
|
@@ -77,8 +74,7 @@ export function useSideMenu(initialProps?: Partial<SideMenuProps>) {
|
|
|
77
74
|
}
|
|
78
75
|
} else if (wrapperRef.current && innerRef.current) {
|
|
79
76
|
// Set proper height for vertical animation (both mobile and desktop)
|
|
80
|
-
const currentOpen =
|
|
81
|
-
typeof defaultProps.isOpen !== 'undefined' ? defaultProps.isOpen : isOpenState;
|
|
77
|
+
const currentOpen = typeof isOpen !== 'undefined' ? isOpen : isOpenState;
|
|
82
78
|
|
|
83
79
|
// Use requestAnimationFrame to ensure DOM is ready
|
|
84
80
|
requestAnimationFrame(() => {
|
|
@@ -101,22 +97,15 @@ export function useSideMenu(initialProps?: Partial<SideMenuProps>) {
|
|
|
101
97
|
clearTimeout(timeoutId);
|
|
102
98
|
window.removeEventListener('resize', handleResize);
|
|
103
99
|
};
|
|
104
|
-
}, [
|
|
105
|
-
defaultProps.collapsible,
|
|
106
|
-
defaultProps.collapsibleDesktop,
|
|
107
|
-
defaultProps.isOpen,
|
|
108
|
-
defaultProps.onToggle,
|
|
109
|
-
isOpenState,
|
|
110
|
-
]);
|
|
100
|
+
}, [collapsible, collapsibleDesktop, isOpen, onToggle, isOpenState]);
|
|
111
101
|
|
|
112
102
|
// Update wrapper height when open state changes (both mobile and desktop)
|
|
113
103
|
useEffect(() => {
|
|
114
104
|
const isMobile = window.innerWidth < 768;
|
|
115
|
-
const shouldCollapse = isMobile ?
|
|
105
|
+
const shouldCollapse = isMobile ? collapsible : collapsibleDesktop;
|
|
116
106
|
|
|
117
107
|
if (shouldCollapse && wrapperRef.current && innerRef.current) {
|
|
118
|
-
const currentOpen =
|
|
119
|
-
typeof defaultProps.isOpen !== 'undefined' ? defaultProps.isOpen : isOpenState;
|
|
108
|
+
const currentOpen = typeof isOpen !== 'undefined' ? isOpen : isOpenState;
|
|
120
109
|
|
|
121
110
|
// Use requestAnimationFrame to ensure DOM is ready
|
|
122
111
|
requestAnimationFrame(() => {
|
|
@@ -132,7 +121,7 @@ export function useSideMenu(initialProps?: Partial<SideMenuProps>) {
|
|
|
132
121
|
// Not collapsible - always show content
|
|
133
122
|
wrapperRef.current.style.height = 'auto';
|
|
134
123
|
}
|
|
135
|
-
}, [
|
|
124
|
+
}, [isOpen, isOpenState, collapsible, collapsibleDesktop]);
|
|
136
125
|
|
|
137
126
|
/**
|
|
138
127
|
* Generate side menu class based on properties
|
|
@@ -159,14 +148,13 @@ export function useSideMenu(initialProps?: Partial<SideMenuProps>) {
|
|
|
159
148
|
* Handle toggle click (mobile)
|
|
160
149
|
*/
|
|
161
150
|
const handleToggle = () => {
|
|
162
|
-
if (
|
|
151
|
+
if (disabled) return;
|
|
163
152
|
|
|
164
|
-
const newState =
|
|
165
|
-
typeof defaultProps.isOpen !== 'undefined' ? !defaultProps.isOpen : !isOpenState;
|
|
153
|
+
const newState = typeof isOpen !== 'undefined' ? !isOpen : !isOpenState;
|
|
166
154
|
|
|
167
|
-
if (typeof
|
|
155
|
+
if (typeof onToggle === 'function') {
|
|
168
156
|
// Controlled component
|
|
169
|
-
|
|
157
|
+
onToggle(newState);
|
|
170
158
|
} else {
|
|
171
159
|
// Uncontrolled component
|
|
172
160
|
setIsOpenState(newState);
|
|
@@ -185,11 +173,10 @@ export function useSideMenu(initialProps?: Partial<SideMenuProps>) {
|
|
|
185
173
|
* @returns Current open state
|
|
186
174
|
*/
|
|
187
175
|
const getCurrentOpenState = (): boolean => {
|
|
188
|
-
return typeof
|
|
176
|
+
return typeof isOpen !== 'undefined' ? isOpen : isOpenState;
|
|
189
177
|
};
|
|
190
178
|
|
|
191
179
|
return {
|
|
192
|
-
defaultProps,
|
|
193
180
|
isOpenState: getCurrentOpenState(),
|
|
194
181
|
wrapperRef,
|
|
195
182
|
innerRef,
|
|
@@ -39,7 +39,7 @@ export function useSlider(options: UseSliderOptions): UseSliderReturn {
|
|
|
39
39
|
onSlideChange,
|
|
40
40
|
} = options;
|
|
41
41
|
|
|
42
|
-
const slides = Array.isArray(rawSlides) ? rawSlides : [];
|
|
42
|
+
const slides = useMemo(() => (Array.isArray(rawSlides) ? rawSlides : []), [rawSlides]);
|
|
43
43
|
|
|
44
44
|
const containerRef = useRef<HTMLDivElement | null>(null);
|
|
45
45
|
const wrapperRef = useRef<HTMLDivElement | null>(null);
|
|
@@ -364,8 +364,6 @@ export function useSlider(options: UseSliderOptions): UseSliderReturn {
|
|
|
364
364
|
isTransitioning,
|
|
365
365
|
speed,
|
|
366
366
|
onSlideChange,
|
|
367
|
-
allSlides.length,
|
|
368
|
-
loopedSlides,
|
|
369
367
|
autoplay,
|
|
370
368
|
]);
|
|
371
369
|
|
|
@@ -426,8 +424,6 @@ export function useSlider(options: UseSliderOptions): UseSliderReturn {
|
|
|
426
424
|
isTransitioning,
|
|
427
425
|
speed,
|
|
428
426
|
onSlideChange,
|
|
429
|
-
allSlides.length,
|
|
430
|
-
loopedSlides,
|
|
431
427
|
autoplay,
|
|
432
428
|
]);
|
|
433
429
|
|
|
@@ -458,7 +454,7 @@ export function useSlider(options: UseSliderOptions): UseSliderReturn {
|
|
|
458
454
|
onSlideChange?.(index);
|
|
459
455
|
}, speed);
|
|
460
456
|
},
|
|
461
|
-
[realIndex, isTransitioning, speed, onSlideChange, loop,
|
|
457
|
+
[realIndex, isTransitioning, speed, onSlideChange, loop, slides.length, autoplay]
|
|
462
458
|
);
|
|
463
459
|
|
|
464
460
|
const handleTouchStart = useCallback(
|
|
@@ -28,8 +28,8 @@ interface UseTooltipResult {
|
|
|
28
28
|
isVisible: boolean;
|
|
29
29
|
isPositioned: boolean;
|
|
30
30
|
tooltipId: string;
|
|
31
|
-
triggerRef: RefObject<HTMLDivElement>;
|
|
32
|
-
tooltipRef: RefObject<HTMLDivElement>;
|
|
31
|
+
triggerRef: RefObject<HTMLDivElement | null>;
|
|
32
|
+
tooltipRef: RefObject<HTMLDivElement | null>;
|
|
33
33
|
tooltipStyle: React.CSSProperties;
|
|
34
34
|
arrowStyle: React.CSSProperties;
|
|
35
35
|
showTooltip: () => void;
|
package/src/lib/config/index.ts
CHANGED
|
@@ -206,6 +206,29 @@ export interface TokenEngineConfig {
|
|
|
206
206
|
};
|
|
207
207
|
}
|
|
208
208
|
|
|
209
|
+
/**
|
|
210
|
+
* CLI component generator defaults (merged before CLI flags; flags win).
|
|
211
|
+
*/
|
|
212
|
+
export interface GeneratorConfig {
|
|
213
|
+
/** Default output directory for generated components */
|
|
214
|
+
outputPath?: string;
|
|
215
|
+
/** Override detected framework */
|
|
216
|
+
framework?: 'react' | 'next' | 'vanilla';
|
|
217
|
+
/** Per-feature defaults (CLI --no-* flags override) */
|
|
218
|
+
features?: {
|
|
219
|
+
storybook?: boolean;
|
|
220
|
+
hook?: boolean;
|
|
221
|
+
styles?: boolean;
|
|
222
|
+
tests?: boolean;
|
|
223
|
+
};
|
|
224
|
+
/** Composable hooks directory relative to project root */
|
|
225
|
+
hookOutputDir?: string;
|
|
226
|
+
/** Story file: side-effect import for global Atomix styles */
|
|
227
|
+
storybookCssImport?: string;
|
|
228
|
+
/** Barrel file strategy for new components */
|
|
229
|
+
barrel?: 'index' | 'none';
|
|
230
|
+
}
|
|
231
|
+
|
|
209
232
|
/**
|
|
210
233
|
* Atomix Configuration Interface
|
|
211
234
|
*
|
|
@@ -241,6 +264,17 @@ export interface AtomixConfig {
|
|
|
241
264
|
model?: string;
|
|
242
265
|
/** API key for the provider */
|
|
243
266
|
apiKey?: string;
|
|
267
|
+
/** Temperature for AI creativity (0.0-1.0, default: 0.7) */
|
|
268
|
+
temperature?: number;
|
|
269
|
+
/** Maximum tokens per AI response (default: 4000) */
|
|
270
|
+
maxTokens?: number;
|
|
271
|
+
/** Rate limiting configuration */
|
|
272
|
+
rateLimit?: {
|
|
273
|
+
/** Maximum requests allowed */
|
|
274
|
+
requests: number;
|
|
275
|
+
/** Time window in milliseconds */
|
|
276
|
+
windowMs: number;
|
|
277
|
+
};
|
|
244
278
|
};
|
|
245
279
|
|
|
246
280
|
/**
|
|
@@ -255,6 +289,11 @@ export interface AtomixConfig {
|
|
|
255
289
|
anonymize?: boolean;
|
|
256
290
|
};
|
|
257
291
|
|
|
292
|
+
/**
|
|
293
|
+
* `atomix generate` defaults (CLI overrides these)
|
|
294
|
+
*/
|
|
295
|
+
generator?: GeneratorConfig;
|
|
296
|
+
|
|
258
297
|
/**
|
|
259
298
|
* Theme customization (Tailwind-like)
|
|
260
299
|
*
|
|
@@ -1631,6 +1631,7 @@ export const ATOMIX_GLASS = {
|
|
|
1631
1631
|
FILTER_OVERLAY_CLASS: 'c-atomix-glass__filter-overlay',
|
|
1632
1632
|
FILTER_SHADOW_CLASS: 'c-atomix-glass__filter-shadow',
|
|
1633
1633
|
CONTENT_CLASS: 'c-atomix-glass__content',
|
|
1634
|
+
BORDER_BACKDROP_CLASS: 'c-atomix-glass__border-backdrop',
|
|
1634
1635
|
BORDER_1_CLASS: 'c-atomix-glass__border-1',
|
|
1635
1636
|
BORDER_2_CLASS: 'c-atomix-glass__border-2',
|
|
1636
1637
|
HOVER_1_CLASS: 'c-atomix-glass__hover-1',
|
|
@@ -48,7 +48,7 @@ export const ThemeComparator: React.FC<ThemeComparatorProps> = ({
|
|
|
48
48
|
const [debouncedSearchQuery, setDebouncedSearchQuery] = useState<string>('');
|
|
49
49
|
const [filterType, setFilterType] = useState<'all' | 'added' | 'removed' | 'changed'>('all');
|
|
50
50
|
const [filterCategory, setFilterCategory] = useState<string>('all');
|
|
51
|
-
const searchTimeoutRef = useRef<NodeJS.Timeout>();
|
|
51
|
+
const searchTimeoutRef = useRef<NodeJS.Timeout | undefined>(undefined);
|
|
52
52
|
|
|
53
53
|
// Debounce search query
|
|
54
54
|
useEffect(() => {
|
|
@@ -57,7 +57,7 @@ export const ThemeInspector: React.FC<ThemeInspectorProps> = ({
|
|
|
57
57
|
const [searchQuery, setSearchQuery] = useState<string>('');
|
|
58
58
|
const [debouncedSearchQuery, setDebouncedSearchQuery] = useState<string>('');
|
|
59
59
|
const [copiedPath, setCopiedPath] = useState<string | null>(null);
|
|
60
|
-
const searchTimeoutRef = useRef<NodeJS.Timeout>();
|
|
60
|
+
const searchTimeoutRef = useRef<NodeJS.Timeout | undefined>(undefined);
|
|
61
61
|
|
|
62
62
|
// Debounce search query
|
|
63
63
|
useEffect(() => {
|
|
@@ -200,7 +200,7 @@ export const ThemeLiveEditor: React.FC<ThemeLiveEditorProps> = ({
|
|
|
200
200
|
);
|
|
201
201
|
|
|
202
202
|
// Debounced JSON update to history
|
|
203
|
-
const jsonUpdateTimeoutRef = useRef<NodeJS.Timeout>();
|
|
203
|
+
const jsonUpdateTimeoutRef = useRef<NodeJS.Timeout | undefined>(undefined);
|
|
204
204
|
useEffect(() => {
|
|
205
205
|
if (error) return;
|
|
206
206
|
|
|
@@ -91,7 +91,7 @@ export const ThemeProvider: React.FC<ThemeProviderProps> = ({
|
|
|
91
91
|
|
|
92
92
|
// Default fallback
|
|
93
93
|
return 'default';
|
|
94
|
-
}, [defaultTheme, enablePersistence, storageKey]);
|
|
94
|
+
}, [defaultTheme, enablePersistence, storageKey, storageAdapter]);
|
|
95
95
|
|
|
96
96
|
// Initialize state - handle both string and DesignTokens for defaultTheme
|
|
97
97
|
const [currentTheme, setCurrentTheme] = useState<string>(() => {
|