@shohojdhara/atomix 0.5.1 → 0.5.4
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 +45 -33
- 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 +699 -0
- package/dist/config.js +17 -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 +1881 -790
- package/dist/index.esm.js +2713 -816
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +2693 -780
- 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.d.ts +1390 -276
- package/dist/theme.js +2133 -625
- 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/config-loader.js +30 -20
- 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 +38 -323
- package/src/lib/config/loader.ts +419 -0
- package/src/lib/config/public-api.ts +43 -0
- package/src/lib/config/types.ts +389 -0
- package/src/lib/config/validator.ts +305 -0
- package/src/lib/theme/adapters/index.ts +1 -1
- package/src/lib/theme/adapters/themeAdapter.ts +358 -229
- package/src/lib/theme/components/ThemeToggle.tsx +276 -0
- package/src/lib/theme/config/configLoader.ts +351 -0
- package/src/lib/theme/config/loader.ts +221 -0
- package/src/lib/theme/core/createTheme.ts +126 -50
- package/src/lib/theme/core/createThemeObject.ts +7 -4
- 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/hooks/useThemeSwitcher.ts +164 -0
- package/src/lib/theme/index.ts +322 -38
- package/src/lib/theme/runtime/ThemeProvider.tsx +45 -11
- package/src/lib/theme/runtime/__tests__/ThemeProvider.test.tsx +44 -393
- package/src/lib/theme/runtime/useTheme.ts +1 -0
- package/src/lib/theme/tokens/tokens.ts +101 -1
- package/src/lib/theme/types.ts +91 -0
- package/src/lib/theme/utils/performanceMonitor.ts +315 -0
- package/src/lib/theme/utils/responsive.ts +280 -0
- package/src/lib/theme/utils/themeUtils.ts +531 -117
- 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
|
@@ -5,6 +5,7 @@ import { Spinner } from '../Spinner/Spinner';
|
|
|
5
5
|
import { Icon, type PhosphorIconsType } from '../Icon/Icon';
|
|
6
6
|
import { BUTTON, THEME_NAMING } from '../../lib/constants/components';
|
|
7
7
|
import { ThemeNaming } from '../../lib/utils/themeNaming';
|
|
8
|
+
import { renderSlot } from '../../lib/patterns';
|
|
8
9
|
|
|
9
10
|
export type ButtonAsProp = {
|
|
10
11
|
as?: ElementType;
|
|
@@ -52,6 +53,7 @@ export const Button = React.memo(
|
|
|
52
53
|
tabIndex,
|
|
53
54
|
style,
|
|
54
55
|
linkComponent,
|
|
56
|
+
slots,
|
|
55
57
|
...props
|
|
56
58
|
},
|
|
57
59
|
ref
|
|
@@ -161,17 +163,34 @@ export const Button = React.memo(
|
|
|
161
163
|
)}
|
|
162
164
|
aria-hidden="true"
|
|
163
165
|
>
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
166
|
+
{renderSlot(
|
|
167
|
+
slots?.spinner,
|
|
168
|
+
{
|
|
169
|
+
className: ThemeNaming.bemClass(
|
|
170
|
+
THEME_NAMING.BUTTON_PREFIX,
|
|
171
|
+
THEME_NAMING.SPINNER_ELEMENT
|
|
172
|
+
),
|
|
173
|
+
size: spinnerSize,
|
|
174
|
+
variant:
|
|
175
|
+
variant === 'link' ||
|
|
176
|
+
(typeof variant === 'string' && variant.startsWith('outline-'))
|
|
177
|
+
? 'primary'
|
|
178
|
+
: variant === 'danger'
|
|
179
|
+
? 'error'
|
|
180
|
+
: (variant as any),
|
|
181
|
+
} as any,
|
|
182
|
+
<Spinner
|
|
183
|
+
size={spinnerSize}
|
|
184
|
+
variant={
|
|
185
|
+
variant === 'link' ||
|
|
186
|
+
(typeof variant === 'string' && variant.startsWith('outline-'))
|
|
187
|
+
? 'primary'
|
|
188
|
+
: variant === 'danger'
|
|
189
|
+
? 'error'
|
|
190
|
+
: (variant as any)
|
|
191
|
+
}
|
|
192
|
+
/>
|
|
193
|
+
)}
|
|
175
194
|
</span>
|
|
176
195
|
)}
|
|
177
196
|
{iconElement && !loading && (
|
|
@@ -182,7 +201,18 @@ export const Button = React.memo(
|
|
|
182
201
|
)}
|
|
183
202
|
aria-hidden="true"
|
|
184
203
|
>
|
|
185
|
-
{
|
|
204
|
+
{renderSlot(
|
|
205
|
+
slots?.icon,
|
|
206
|
+
{
|
|
207
|
+
className: ThemeNaming.bemClass(
|
|
208
|
+
THEME_NAMING.BUTTON_PREFIX,
|
|
209
|
+
THEME_NAMING.ICON_ELEMENT
|
|
210
|
+
),
|
|
211
|
+
children: iconElement,
|
|
212
|
+
size: iconSize,
|
|
213
|
+
} as any,
|
|
214
|
+
iconElement
|
|
215
|
+
)}
|
|
186
216
|
</span>
|
|
187
217
|
)}
|
|
188
218
|
{!iconOnly && buttonText && (
|
|
@@ -192,7 +222,17 @@ export const Button = React.memo(
|
|
|
192
222
|
THEME_NAMING.LABEL_ELEMENT
|
|
193
223
|
)}
|
|
194
224
|
>
|
|
195
|
-
{
|
|
225
|
+
{renderSlot(
|
|
226
|
+
slots?.label,
|
|
227
|
+
{
|
|
228
|
+
className: ThemeNaming.bemClass(
|
|
229
|
+
THEME_NAMING.BUTTON_PREFIX,
|
|
230
|
+
THEME_NAMING.LABEL_ELEMENT
|
|
231
|
+
),
|
|
232
|
+
children: buttonText,
|
|
233
|
+
} as any,
|
|
234
|
+
buttonText
|
|
235
|
+
)}
|
|
196
236
|
</span>
|
|
197
237
|
)}
|
|
198
238
|
</>
|
|
@@ -218,48 +258,65 @@ export const Button = React.memo(
|
|
|
218
258
|
|
|
219
259
|
let content: React.ReactElement;
|
|
220
260
|
|
|
221
|
-
// Render
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
261
|
+
// Render the button content using the root slot if provided
|
|
262
|
+
const buttonChildren = renderSlot(
|
|
263
|
+
slots?.root,
|
|
264
|
+
{
|
|
265
|
+
className: buttonClass,
|
|
266
|
+
children: buttonContent,
|
|
267
|
+
disabled: isDisabled,
|
|
268
|
+
loading: loading,
|
|
269
|
+
onClick: handleClickEvent,
|
|
270
|
+
type: type,
|
|
271
|
+
'aria-label': safeAriaLabel,
|
|
272
|
+
'aria-disabled': isDisabled,
|
|
273
|
+
'aria-busy': loading,
|
|
274
|
+
} as any,
|
|
275
|
+
(() => {
|
|
276
|
+
// Render as anchor if href is provided
|
|
277
|
+
if (shouldRenderAsLink) {
|
|
278
|
+
// Use custom linkComponent if provided (e.g., Next.js Link)
|
|
279
|
+
if (linkComponent) {
|
|
280
|
+
const LinkComp = linkComponent as React.ComponentType<any>;
|
|
281
|
+
const linkProps = {
|
|
282
|
+
...buttonProps,
|
|
283
|
+
ref: ref as any, // linkComponent usually forwards ref to anchor
|
|
284
|
+
href: isDisabled ? undefined : href,
|
|
285
|
+
to: isDisabled ? undefined : href,
|
|
286
|
+
target,
|
|
287
|
+
rel: target === '_blank' ? 'noopener noreferrer' : undefined,
|
|
288
|
+
};
|
|
234
289
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
290
|
+
return <LinkComp {...linkProps}>{buttonContent}</LinkComp>;
|
|
291
|
+
} else {
|
|
292
|
+
// Fallback to regular anchor tag
|
|
293
|
+
return (
|
|
294
|
+
<a
|
|
295
|
+
{...buttonProps}
|
|
296
|
+
ref={ref as React.Ref<HTMLAnchorElement>}
|
|
297
|
+
href={isDisabled ? undefined : href}
|
|
298
|
+
target={target}
|
|
299
|
+
rel={target === '_blank' ? 'noopener noreferrer' : undefined}
|
|
300
|
+
>
|
|
301
|
+
{buttonContent}
|
|
302
|
+
</a>
|
|
303
|
+
);
|
|
304
|
+
}
|
|
305
|
+
} else {
|
|
306
|
+
// Default button rendering
|
|
307
|
+
return (
|
|
308
|
+
<Component
|
|
309
|
+
{...buttonProps}
|
|
310
|
+
ref={ref}
|
|
311
|
+
type={Component === 'button' ? type : undefined}
|
|
312
|
+
disabled={isDisabled}
|
|
313
|
+
>
|
|
314
|
+
{buttonContent}
|
|
315
|
+
</Component>
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
})()
|
|
319
|
+
);
|
|
263
320
|
|
|
264
321
|
if (glass) {
|
|
265
322
|
// Default glass props
|
|
@@ -270,10 +327,10 @@ export const Button = React.memo(
|
|
|
270
327
|
elasticity: 0,
|
|
271
328
|
};
|
|
272
329
|
const glassProps = glass === true ? defaultGlassProps : { ...defaultGlassProps, ...glass };
|
|
273
|
-
return <AtomixGlass {...glassProps}>{
|
|
330
|
+
return <AtomixGlass {...glassProps}>{buttonChildren}</AtomixGlass>;
|
|
274
331
|
}
|
|
275
332
|
|
|
276
|
-
return
|
|
333
|
+
return buttonChildren;
|
|
277
334
|
}
|
|
278
335
|
)
|
|
279
336
|
);
|
|
@@ -282,4 +339,4 @@ Button.displayName = 'Button';
|
|
|
282
339
|
|
|
283
340
|
export type { ButtonProps };
|
|
284
341
|
|
|
285
|
-
export default Button;
|
|
342
|
+
export default Button;
|
|
@@ -89,8 +89,7 @@ type CalloutComponent = React.FC<CalloutProps> & {
|
|
|
89
89
|
Content: typeof CalloutContent;
|
|
90
90
|
};
|
|
91
91
|
|
|
92
|
-
|
|
93
|
-
({
|
|
92
|
+
const CalloutComponentBase = ({
|
|
94
93
|
title,
|
|
95
94
|
children,
|
|
96
95
|
icon,
|
|
@@ -212,8 +211,9 @@ export const Callout: CalloutComponent = memo(
|
|
|
212
211
|
{calloutContent}
|
|
213
212
|
</div>
|
|
214
213
|
);
|
|
215
|
-
|
|
216
|
-
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
export const Callout = memo(CalloutComponentBase) as unknown as CalloutComponent;
|
|
217
217
|
|
|
218
218
|
Callout.displayName = 'Callout';
|
|
219
219
|
|
|
@@ -273,7 +273,7 @@ const ChartRenderer = memo(
|
|
|
273
273
|
const transform = useMemo(() => {
|
|
274
274
|
if (!chartContext) return '';
|
|
275
275
|
return `translate(${chartContext.panOffset.x}px, ${chartContext.panOffset.y}px) scale(${chartContext.zoomLevel})`;
|
|
276
|
-
}, [chartContext
|
|
276
|
+
}, [chartContext]);
|
|
277
277
|
|
|
278
278
|
// Calculate chart data with enhanced features using responsive dimensions
|
|
279
279
|
// This MUST be called before any early returns to maintain consistent hook order
|
|
@@ -2,7 +2,7 @@ import { forwardRef, memo, useMemo } from 'react';
|
|
|
2
2
|
import BaseChart from './BaseChart';
|
|
3
3
|
import ChartTooltip from './ChartTooltip';
|
|
4
4
|
import { PieChartProps } from './PieChart';
|
|
5
|
-
import { ChartDataPoint, ChartRenderContentParams } from './types';
|
|
5
|
+
import { ChartDataPoint, ChartRenderContentParams, ChartDataset } from './types';
|
|
6
6
|
|
|
7
7
|
interface DonutChartProps extends Omit<PieChartProps, 'type'> {
|
|
8
8
|
/**
|
|
@@ -63,15 +63,18 @@ const DonutChart = memo(
|
|
|
63
63
|
ref
|
|
64
64
|
) => {
|
|
65
65
|
// Use the first dataset for donut chart
|
|
66
|
-
const dataset =
|
|
66
|
+
const dataset = useMemo(
|
|
67
|
+
() => (datasets.length > 0 ? (datasets[0] as ChartDataset) : { label: '', data: [] as ChartDataPoint[] }),
|
|
68
|
+
[datasets]
|
|
69
|
+
);
|
|
67
70
|
|
|
68
71
|
// Prepare data for donut chart (calculations will be done in renderContent with actual dimensions)
|
|
69
72
|
const chartData = useMemo(() => {
|
|
70
73
|
if (!dataset?.data?.length) return null;
|
|
71
74
|
|
|
72
75
|
// Filter out invalid data points
|
|
73
|
-
const validDataPoints = dataset?.data
|
|
74
|
-
point =>
|
|
76
|
+
const validDataPoints = (dataset?.data || []).filter(
|
|
77
|
+
(point: ChartDataPoint) =>
|
|
75
78
|
typeof point.value === 'number' &&
|
|
76
79
|
!isNaN(point.value) &&
|
|
77
80
|
isFinite(point.value) &&
|
|
@@ -117,16 +120,16 @@ const DonutChart = memo(
|
|
|
117
120
|
|
|
118
121
|
const chartColors = dataset?.color
|
|
119
122
|
? [dataset.color]
|
|
120
|
-
: dataset?.data?.map((_, i) => defaultColors[i % defaultColors.length]) || defaultColors;
|
|
123
|
+
: dataset?.data?.map((_, i: number) => defaultColors[i % defaultColors.length]) || defaultColors;
|
|
121
124
|
|
|
122
125
|
// Calculate total value
|
|
123
|
-
const total = chartData.validDataPoints.reduce((sum, point) => sum + point.value, 0);
|
|
126
|
+
const total = chartData.validDataPoints.reduce((sum: number, point: ChartDataPoint) => sum + point.value, 0);
|
|
124
127
|
|
|
125
128
|
// Calculate angles for each slice
|
|
126
129
|
const padAngleRad = ((pieOptions.padAngle || 1) * Math.PI) / 180;
|
|
127
130
|
let currentAngle = ((pieOptions.startAngle || 0) * Math.PI) / 180;
|
|
128
131
|
|
|
129
|
-
const slices = chartData.validDataPoints.map((point, index) => {
|
|
132
|
+
const slices = chartData.validDataPoints.map((point: ChartDataPoint, index: number) => {
|
|
130
133
|
const percentage = point.value / total;
|
|
131
134
|
const sliceAngle = percentage * (2 * Math.PI) - padAngleRad;
|
|
132
135
|
const startAngle = currentAngle;
|
|
@@ -176,7 +179,7 @@ const DonutChart = memo(
|
|
|
176
179
|
|
|
177
180
|
return (
|
|
178
181
|
<>
|
|
179
|
-
{slices.map((slice, index) => {
|
|
182
|
+
{slices.map((slice: any, index: number) => {
|
|
180
183
|
const isHovered = hoveredPoint?.pointIndex === index;
|
|
181
184
|
|
|
182
185
|
return (
|
|
@@ -33,19 +33,20 @@ export const EdgePanelFooter = forwardRef<HTMLDivElement, React.HTMLAttributes<H
|
|
|
33
33
|
);
|
|
34
34
|
EdgePanelFooter.displayName = 'EdgePanelFooter';
|
|
35
35
|
|
|
36
|
-
export const EdgePanelCloseButton = forwardRef<
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
36
|
+
export const EdgePanelCloseButton = forwardRef<
|
|
37
|
+
HTMLButtonElement,
|
|
38
|
+
React.ButtonHTMLAttributes<HTMLButtonElement>
|
|
39
|
+
>(({ className = '', onClick, ...props }, ref) => (
|
|
40
|
+
<button
|
|
41
|
+
ref={ref}
|
|
42
|
+
className={`c-edge-panel__close c-btn c-btn--icon ${className}`.trim()}
|
|
43
|
+
onClick={onClick}
|
|
44
|
+
aria-label="Close panel"
|
|
45
|
+
{...props}
|
|
46
|
+
>
|
|
47
|
+
<Icon name="X" />
|
|
48
|
+
</button>
|
|
49
|
+
));
|
|
49
50
|
EdgePanelCloseButton.displayName = 'EdgePanelCloseButton';
|
|
50
51
|
|
|
51
52
|
/**
|
|
@@ -82,113 +83,116 @@ type EdgePanelComponent = React.FC<EdgePanelProps> & {
|
|
|
82
83
|
CloseButton: typeof EdgePanelCloseButton;
|
|
83
84
|
};
|
|
84
85
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
86
|
+
const EdgePanelComponentBase = ({
|
|
87
|
+
title,
|
|
88
|
+
children,
|
|
89
|
+
position = 'start',
|
|
90
|
+
mode = 'slide',
|
|
91
|
+
isOpen = false,
|
|
92
|
+
onOpenChange,
|
|
93
|
+
backdrop = true,
|
|
94
|
+
closeOnBackdropClick = true,
|
|
95
|
+
closeOnEscape = true,
|
|
96
|
+
className = '',
|
|
97
|
+
style,
|
|
98
|
+
glass,
|
|
99
|
+
}: EdgePanelProps) => {
|
|
100
|
+
const {
|
|
101
|
+
isOpen: isOpenState,
|
|
102
|
+
containerRef,
|
|
103
|
+
backdropRef,
|
|
104
|
+
generateEdgePanelClass,
|
|
105
|
+
closePanel,
|
|
106
|
+
handleBackdropClick,
|
|
107
|
+
} = useEdgePanel({
|
|
108
|
+
position,
|
|
109
|
+
mode,
|
|
110
|
+
isOpen,
|
|
92
111
|
onOpenChange,
|
|
93
|
-
backdrop
|
|
94
|
-
closeOnBackdropClick
|
|
95
|
-
closeOnEscape
|
|
96
|
-
className = '',
|
|
97
|
-
style,
|
|
112
|
+
backdrop,
|
|
113
|
+
closeOnBackdropClick,
|
|
114
|
+
closeOnEscape,
|
|
98
115
|
glass,
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
// If we return null here, animations might be cut off.
|
|
131
|
-
// Usually EdgePanel/Drawer should stay mounted but hidden or conditionally mounted.
|
|
132
|
-
// The original code returned null if !isOpenState && isOpen === false.
|
|
133
|
-
// Let's keep that logic.
|
|
134
|
-
if (!isOpenState && isOpen === false) {
|
|
135
|
-
return null;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
const defaultGlassProps = {
|
|
139
|
-
elasticity: 0,
|
|
140
|
-
};
|
|
141
|
-
|
|
142
|
-
const glassProps = glass === true ? defaultGlassProps : { ...defaultGlassProps, ...glass };
|
|
143
|
-
|
|
144
|
-
// Check for compound components
|
|
145
|
-
const hasCompoundComponents = React.Children.toArray(children).some((child) =>
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
// Moved useRef outside of conditional rendering to fix hook order issue
|
|
119
|
+
const glassContentRef = useRef<HTMLDivElement>(null);
|
|
120
|
+
|
|
121
|
+
const panelClass = generateEdgePanelClass({
|
|
122
|
+
position,
|
|
123
|
+
isOpen,
|
|
124
|
+
className: glass ? `${className} c-edge-panel--glass` : className,
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
// If not open and not controlled by parent, don't render
|
|
128
|
+
// Note: useEdgePanel manages internal state if onOpenChange is not provided?
|
|
129
|
+
// Looking at useEdgePanel (implied): it seems to return isOpenState.
|
|
130
|
+
// If we return null here, animations might be cut off.
|
|
131
|
+
// Usually EdgePanel/Drawer should stay mounted but hidden or conditionally mounted.
|
|
132
|
+
// The original code returned null if !isOpenState && isOpen === false.
|
|
133
|
+
// Let's keep that logic.
|
|
134
|
+
if (!isOpenState && isOpen === false) {
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const defaultGlassProps = {
|
|
139
|
+
elasticity: 0,
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
const glassProps = glass === true ? defaultGlassProps : { ...defaultGlassProps, ...glass };
|
|
143
|
+
|
|
144
|
+
// Check for compound components
|
|
145
|
+
const hasCompoundComponents = React.Children.toArray(children).some(
|
|
146
|
+
child =>
|
|
146
147
|
React.isValidElement(child) &&
|
|
147
|
-
['EdgePanelHeader', 'EdgePanelBody', 'EdgePanelFooter'].includes(
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
148
|
+
['EdgePanelHeader', 'EdgePanelBody', 'EdgePanelFooter'].includes(
|
|
149
|
+
(child.type as any).displayName
|
|
150
|
+
)
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
const panelContent = hasCompoundComponents ? (
|
|
154
|
+
children
|
|
155
|
+
) : (
|
|
156
|
+
<>
|
|
157
|
+
<div className="c-edge-panel__header">
|
|
158
|
+
<h4>{title}</h4>
|
|
159
|
+
<button
|
|
160
|
+
className="c-edge-panel__close c-btn c-btn--icon"
|
|
161
|
+
onClick={() => closePanel()}
|
|
162
|
+
aria-label="Close panel"
|
|
163
|
+
>
|
|
164
|
+
<Icon name="X" />
|
|
165
|
+
</button>
|
|
166
|
+
</div>
|
|
167
|
+
<div className="c-edge-panel__body">{children}</div>
|
|
168
|
+
</>
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
return (
|
|
172
|
+
<div className={panelClass} data-position={position} data-mode={mode} style={style}>
|
|
173
|
+
{backdrop && (
|
|
174
|
+
<div ref={backdropRef} className="c-edge-panel__backdrop" onClick={handleBackdropClick} />
|
|
175
|
+
)}
|
|
176
|
+
<div ref={containerRef} className="c-edge-panel__container">
|
|
177
|
+
{glass ? (
|
|
178
|
+
<AtomixGlass {...glassProps}>
|
|
179
|
+
<div
|
|
180
|
+
ref={glassContentRef}
|
|
181
|
+
className="c-edge-panel__glass-content"
|
|
182
|
+
style={{ borderRadius: containerRef.current?.style.borderRadius }}
|
|
183
|
+
>
|
|
184
|
+
{panelContent}
|
|
185
|
+
</div>
|
|
186
|
+
</AtomixGlass>
|
|
187
|
+
) : (
|
|
188
|
+
panelContent
|
|
172
189
|
)}
|
|
173
|
-
<div ref={containerRef} className="c-edge-panel__container">
|
|
174
|
-
{glass ? (
|
|
175
|
-
<AtomixGlass {...glassProps}>
|
|
176
|
-
<div
|
|
177
|
-
ref={glassContentRef}
|
|
178
|
-
className="c-edge-panel__glass-content"
|
|
179
|
-
style={{ borderRadius: containerRef.current?.style.borderRadius }}
|
|
180
|
-
>
|
|
181
|
-
{panelContent}
|
|
182
|
-
</div>
|
|
183
|
-
</AtomixGlass>
|
|
184
|
-
) : (
|
|
185
|
-
panelContent
|
|
186
|
-
)}
|
|
187
|
-
</div>
|
|
188
190
|
</div>
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
191
|
+
</div>
|
|
192
|
+
);
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
export const EdgePanel = memo(EdgePanelComponentBase) as unknown as EdgePanelComponent;
|
|
192
196
|
|
|
193
197
|
EdgePanel.displayName = 'EdgePanel';
|
|
194
198
|
EdgePanel.Header = EdgePanelHeader;
|
|
@@ -12,8 +12,7 @@ export type SelectComponent = React.FC<SelectProps> & {
|
|
|
12
12
|
/**
|
|
13
13
|
* Select - A component for dropdown selection
|
|
14
14
|
*/
|
|
15
|
-
|
|
16
|
-
({
|
|
15
|
+
const SelectComponentBase = ({
|
|
17
16
|
options,
|
|
18
17
|
value,
|
|
19
18
|
onChange,
|
|
@@ -314,8 +313,9 @@ export const Select: SelectComponent = memo(
|
|
|
314
313
|
}
|
|
315
314
|
|
|
316
315
|
return selectContent;
|
|
317
|
-
|
|
318
|
-
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
export const Select = memo(SelectComponentBase) as unknown as SelectComponent;
|
|
319
319
|
|
|
320
320
|
export type { SelectProps };
|
|
321
321
|
|
|
@@ -9,8 +9,7 @@ export type ListComponent = React.FC<ListProps> & {
|
|
|
9
9
|
Item: typeof ListItem;
|
|
10
10
|
};
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
({ children, variant = 'default', className = '', style, ...props }: ListProps) => {
|
|
12
|
+
const ListComponentBase = ({ children, variant = 'default', className = '', style, ...props }: ListProps) => {
|
|
14
13
|
// Generate CSS classes
|
|
15
14
|
const listClasses = [LIST.BASE_CLASS, variant !== 'default' && `c-list--${variant}`, className]
|
|
16
15
|
.filter(Boolean)
|
|
@@ -34,8 +33,9 @@ export const List: ListComponent = memo(
|
|
|
34
33
|
})}
|
|
35
34
|
</ListElement>
|
|
36
35
|
);
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export const List = memo(ListComponentBase) as unknown as ListComponent;
|
|
39
39
|
|
|
40
40
|
List.displayName = 'List';
|
|
41
41
|
List.Item = ListItem;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useState, useEffect, useRef, forwardRef } from 'react';
|
|
1
|
+
import React, { useState, useEffect, useRef, forwardRef, useCallback } from 'react';
|
|
2
2
|
import { SideMenuProps } from '../../../lib/types/components';
|
|
3
3
|
import { useSideMenu } from '../../../lib/composables/useSideMenu';
|
|
4
4
|
import { Icon } from '../../Icon';
|
|
@@ -116,16 +116,16 @@ export const SideMenu = forwardRef<HTMLDivElement, SideMenuProps>(
|
|
|
116
116
|
delete nestedInnerRefs.current[index];
|
|
117
117
|
}
|
|
118
118
|
});
|
|
119
|
-
}, [menuItems
|
|
119
|
+
}, [menuItems]);
|
|
120
120
|
|
|
121
121
|
// Helper function to update nested wrapper height
|
|
122
|
-
const updateNestedHeight = (index: number, isOpen: boolean) => {
|
|
122
|
+
const updateNestedHeight = useCallback((index: number, isOpen: boolean) => {
|
|
123
123
|
const wrapper = nestedWrapperRefs.current[index];
|
|
124
124
|
const inner = nestedInnerRefs.current[index];
|
|
125
125
|
if (wrapper && inner) {
|
|
126
126
|
wrapper.style.height = isOpen ? `${inner.scrollHeight}px` : '0px';
|
|
127
127
|
}
|
|
128
|
-
};
|
|
128
|
+
}, []);
|
|
129
129
|
|
|
130
130
|
// Set initial heights for nested wrappers on mount and when menuItems change
|
|
131
131
|
useEffect(() => {
|
|
@@ -141,7 +141,7 @@ export const SideMenu = forwardRef<HTMLDivElement, SideMenuProps>(
|
|
|
141
141
|
return () => clearTimeout(timeoutId);
|
|
142
142
|
// Only run when menuItems change, nestedItemStates is read but not in deps to avoid loops
|
|
143
143
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
144
|
-
}, [menuItems
|
|
144
|
+
}, [menuItems, updateNestedHeight]);
|
|
145
145
|
|
|
146
146
|
// Update nested wrapper heights when state changes
|
|
147
147
|
useEffect(() => {
|
|
@@ -162,7 +162,7 @@ export const SideMenu = forwardRef<HTMLDivElement, SideMenuProps>(
|
|
|
162
162
|
return () => {
|
|
163
163
|
frameIds.forEach(id => cancelAnimationFrame(id));
|
|
164
164
|
};
|
|
165
|
-
}, [nestedItemStates, menuItems
|
|
165
|
+
}, [nestedItemStates, menuItems, updateNestedHeight]);
|
|
166
166
|
|
|
167
167
|
// Combine refs using utility
|
|
168
168
|
const combinedRef = useForkRef(sideMenuRef, ref);
|
|
@@ -5,7 +5,7 @@ import React, { useRef, useEffect, useState } from 'react';
|
|
|
5
5
|
*/
|
|
6
6
|
export interface PhotoViewerImageProps {
|
|
7
7
|
/** Ref to the image element */
|
|
8
|
-
imageRef: React.RefObject<HTMLImageElement>;
|
|
8
|
+
imageRef: React.RefObject<HTMLImageElement | null>;
|
|
9
9
|
/** Ref to the container element */
|
|
10
10
|
containerRef?: React.RefObject<HTMLDivElement | null>;
|
|
11
11
|
/** Image source URL */
|