@shohojdhara/atomix 0.3.6 → 0.3.7
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 +3 -3
- package/dist/charts.js +50 -142
- package/dist/charts.js.map +1 -1
- package/dist/core.js +179 -274
- package/dist/core.js.map +1 -1
- package/dist/forms.js +50 -142
- package/dist/forms.js.map +1 -1
- package/dist/heavy.js +179 -274
- package/dist/heavy.js.map +1 -1
- package/dist/index.d.ts +669 -703
- package/dist/index.esm.js +966 -1649
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +1211 -1890
- 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 +163 -334
- package/dist/theme.js +774 -1473
- package/dist/theme.js.map +1 -1
- package/package.json +1 -1
- package/src/components/AtomixGlass/AtomixGlass.tsx +128 -356
- package/src/components/AtomixGlass/AtomixGlassContainer.tsx +1 -1
- package/src/components/Button/Button.tsx +85 -167
- package/src/lib/composables/useAtomixGlass.ts +7 -7
- package/src/lib/config/loader.ts +2 -3
- package/src/lib/constants/components.ts +7 -0
- package/src/lib/hooks/usePerformanceMonitor.ts +1 -1
- package/src/lib/hooks/useThemeTokens.ts +105 -0
- package/src/lib/theme/config/configLoader.ts +60 -219
- package/src/lib/theme/config/loader.ts +15 -21
- package/src/lib/theme/constants/constants.ts +1 -1
- package/src/lib/theme/core/ThemeRegistry.ts +75 -279
- package/src/lib/theme/core/composeTheme.ts +14 -64
- package/src/lib/theme/core/createTheme.ts +54 -40
- package/src/lib/theme/core/createThemeObject.ts +2 -2
- package/src/lib/theme/core/index.ts +15 -1
- package/src/lib/theme/errors/errors.ts +1 -1
- package/src/lib/theme/generators/generateCSSNested.ts +130 -0
- package/src/lib/theme/generators/index.ts +6 -0
- package/src/lib/theme/index.ts +35 -10
- package/src/lib/theme/runtime/ThemeApplicator.ts +1 -1
- package/src/lib/theme/runtime/ThemeErrorBoundary.tsx +4 -4
- package/src/lib/theme/runtime/ThemeProvider.tsx +261 -554
- package/src/lib/theme/runtime/index.ts +1 -0
- package/src/lib/theme/runtime/useThemeTokens.ts +131 -0
- package/src/lib/theme/utils/componentTheming.ts +132 -0
- package/src/lib/theme/utils/naming.ts +100 -0
- package/src/lib/theme/utils/themeUtils.ts +6 -6
- package/src/lib/utils/componentUtils.ts +1 -1
- package/src/lib/utils/memoryMonitor.ts +3 -3
- package/src/lib/utils/themeNaming.ts +135 -0
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import React, { ElementType, forwardRef, useCallback
|
|
1
|
+
import React, { ElementType, forwardRef, useCallback } from 'react';
|
|
2
2
|
import { useButton } from '../../lib/composables/useButton';
|
|
3
3
|
import { ButtonProps } from '../../lib/types/components';
|
|
4
4
|
import { AtomixGlass } from '../AtomixGlass/AtomixGlass';
|
|
5
5
|
import { Spinner } from '../Spinner/Spinner';
|
|
6
6
|
import { Icon, type PhosphorIconsType } from '../Icon/Icon';
|
|
7
|
-
import { BUTTON } from '../../lib/constants/components';
|
|
7
|
+
import { BUTTON, THEME_NAMING } from '../../lib/constants/components';
|
|
8
|
+
import { ThemeNaming } from '../../lib/utils/themeNaming';
|
|
8
9
|
|
|
9
10
|
export type ButtonAsProp = {
|
|
10
11
|
as?: ElementType;
|
|
@@ -62,13 +63,7 @@ export const Button = React.memo(
|
|
|
62
63
|
const shouldRenderAsLink = Boolean(href && !isDisabled);
|
|
63
64
|
|
|
64
65
|
// Resolve icon element - support both icon (ReactNode) and iconName (string)
|
|
65
|
-
const iconElement =
|
|
66
|
-
if (loading) return null;
|
|
67
|
-
if (iconName) {
|
|
68
|
-
return <Icon name={iconName as PhosphorIconsType} size={iconSize} />;
|
|
69
|
-
}
|
|
70
|
-
return icon;
|
|
71
|
-
}, [icon, iconName, iconSize, loading]);
|
|
66
|
+
const iconElement = iconName ? <Icon name={iconName as PhosphorIconsType} size={iconSize} /> : icon;
|
|
72
67
|
|
|
73
68
|
const { generateButtonClass, handleClick } = useButton({
|
|
74
69
|
variant,
|
|
@@ -83,24 +78,23 @@ export const Button = React.memo(
|
|
|
83
78
|
selected,
|
|
84
79
|
});
|
|
85
80
|
|
|
86
|
-
const buttonClass =
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
);
|
|
81
|
+
const buttonClass = [
|
|
82
|
+
BUTTON.BASE_CLASS,
|
|
83
|
+
ThemeNaming.variantClass(THEME_NAMING.BUTTON_PREFIX, variant),
|
|
84
|
+
size !== 'md' ? ThemeNaming.sizeClass(THEME_NAMING.BUTTON_PREFIX, size) : '',
|
|
85
|
+
iconOnly ? ThemeNaming.stateClass(THEME_NAMING.BUTTON_PREFIX, THEME_NAMING.ICON_ELEMENT) : '',
|
|
86
|
+
rounded ? ThemeNaming.stateClass(THEME_NAMING.BUTTON_PREFIX, 'rounded') : '',
|
|
87
|
+
isDisabled ? ThemeNaming.stateClass(THEME_NAMING.BUTTON_PREFIX, 'disabled') : '',
|
|
88
|
+
glass ? ThemeNaming.stateClass(THEME_NAMING.BUTTON_PREFIX, 'glass') : '',
|
|
89
|
+
loading ? BUTTON.CLASSES.LOADING : '',
|
|
90
|
+
fullWidth ? BUTTON.CLASSES.FULL_WIDTH : '',
|
|
91
|
+
block ? BUTTON.CLASSES.BLOCK : '',
|
|
92
|
+
active ? BUTTON.CLASSES.ACTIVE : '',
|
|
93
|
+
selected ? BUTTON.CLASSES.SELECTED : '',
|
|
94
|
+
className,
|
|
95
|
+
]
|
|
96
|
+
.filter(Boolean)
|
|
97
|
+
.join(' ');
|
|
104
98
|
|
|
105
99
|
// Handle click with loading check
|
|
106
100
|
const handleClickEvent = useCallback(
|
|
@@ -145,117 +139,76 @@ export const Button = React.memo(
|
|
|
145
139
|
);
|
|
146
140
|
|
|
147
141
|
// Determine button text
|
|
148
|
-
const buttonText =
|
|
149
|
-
if (loading && loadingText) return loadingText;
|
|
150
|
-
if (loading && !loadingText) return label || children;
|
|
151
|
-
return label || children;
|
|
152
|
-
}, [loading, loadingText, label, children]);
|
|
142
|
+
const buttonText = loading && loadingText ? loadingText : label || children;
|
|
153
143
|
|
|
154
144
|
// Determine spinner size based on button size
|
|
155
|
-
const spinnerSize =
|
|
156
|
-
if (size === 'sm') return 'sm';
|
|
157
|
-
if (size === 'lg') return 'md';
|
|
158
|
-
return 'sm';
|
|
159
|
-
}, [size]);
|
|
145
|
+
const spinnerSize = size === 'sm' ? 'sm' : size === 'lg' ? 'md' : 'sm';
|
|
160
146
|
|
|
161
147
|
// Button content with icon positioning
|
|
162
|
-
const buttonContent =
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
{
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
if (iconPosition === 'end') {
|
|
187
|
-
return (
|
|
188
|
-
<>
|
|
189
|
-
{labelElement}
|
|
190
|
-
{spinnerElement}
|
|
191
|
-
{iconSpan}
|
|
192
|
-
</>
|
|
193
|
-
);
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
return (
|
|
197
|
-
<>
|
|
198
|
-
{spinnerElement}
|
|
199
|
-
{iconSpan}
|
|
200
|
-
{labelElement}
|
|
201
|
-
</>
|
|
202
|
-
);
|
|
203
|
-
}, [iconElement, iconPosition, iconOnly, buttonText, loading, spinnerSize, variant]);
|
|
148
|
+
const buttonContent = (
|
|
149
|
+
<>
|
|
150
|
+
{loading && (
|
|
151
|
+
<span className={ThemeNaming.bemClass(THEME_NAMING.BUTTON_PREFIX, THEME_NAMING.SPINNER_ELEMENT)} aria-hidden="true">
|
|
152
|
+
<Spinner
|
|
153
|
+
size={spinnerSize}
|
|
154
|
+
variant={
|
|
155
|
+
variant === 'link' || (typeof variant === 'string' && variant.startsWith('outline-'))
|
|
156
|
+
? 'primary'
|
|
157
|
+
: (variant === 'danger' ? 'error' : (variant as any))
|
|
158
|
+
}
|
|
159
|
+
/>
|
|
160
|
+
</span>
|
|
161
|
+
)}
|
|
162
|
+
{iconElement && !loading && (
|
|
163
|
+
<span className={ThemeNaming.bemClass(THEME_NAMING.BUTTON_PREFIX, THEME_NAMING.ICON_ELEMENT)} aria-hidden="true">
|
|
164
|
+
{iconElement}
|
|
165
|
+
</span>
|
|
166
|
+
)}
|
|
167
|
+
{!iconOnly && buttonText && (
|
|
168
|
+
<span className={ThemeNaming.bemClass(THEME_NAMING.BUTTON_PREFIX, THEME_NAMING.LABEL_ELEMENT)}>{buttonText}</span>
|
|
169
|
+
)}
|
|
170
|
+
</>
|
|
171
|
+
);
|
|
204
172
|
|
|
205
173
|
// Button props
|
|
206
|
-
const buttonProps =
|
|
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
|
-
handleBlurEvent,
|
|
235
|
-
isDisabled,
|
|
236
|
-
loading,
|
|
237
|
-
ariaLabel,
|
|
238
|
-
iconOnly,
|
|
239
|
-
label,
|
|
240
|
-
children,
|
|
241
|
-
ariaDescribedBy,
|
|
242
|
-
ariaExpanded,
|
|
243
|
-
ariaControls,
|
|
244
|
-
tabIndex,
|
|
245
|
-
style,
|
|
246
|
-
props,
|
|
247
|
-
]
|
|
248
|
-
);
|
|
174
|
+
const buttonProps = {
|
|
175
|
+
ref,
|
|
176
|
+
className: buttonClass,
|
|
177
|
+
type: Component === 'button' && !shouldRenderAsLink ? type : undefined,
|
|
178
|
+
onClick: handleClickEvent,
|
|
179
|
+
onMouseEnter: onHover ? handleMouseEnter : undefined,
|
|
180
|
+
onFocus: onFocus ? handleFocusEvent : undefined,
|
|
181
|
+
onBlur: onBlur ? handleBlurEvent : undefined,
|
|
182
|
+
disabled: isDisabled && Component === 'button' && !shouldRenderAsLink,
|
|
183
|
+
'aria-disabled': isDisabled,
|
|
184
|
+
'aria-busy': loading,
|
|
185
|
+
'aria-label': ariaLabel || (iconOnly ? label || children : undefined),
|
|
186
|
+
'aria-describedby': ariaDescribedBy,
|
|
187
|
+
'aria-expanded': ariaExpanded,
|
|
188
|
+
'aria-controls': ariaControls,
|
|
189
|
+
tabIndex: tabIndex !== undefined ? tabIndex : (isDisabled ? -1 : 0),
|
|
190
|
+
style,
|
|
191
|
+
...props,
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
// Default glass props
|
|
195
|
+
const defaultGlassProps = {
|
|
196
|
+
displacementScale: 20,
|
|
197
|
+
blurAmount: 0,
|
|
198
|
+
saturation: 200,
|
|
199
|
+
elasticity: 0,
|
|
200
|
+
};
|
|
201
|
+
const glassProps = glass === true ? defaultGlassProps : { ...defaultGlassProps, ...glass };
|
|
249
202
|
|
|
250
203
|
// Render as anchor if href is provided
|
|
251
204
|
if (shouldRenderAsLink) {
|
|
252
205
|
const { ref: _, ...buttonPropsWithoutRef } = buttonProps;
|
|
253
206
|
const anchorButtonProps = {
|
|
254
207
|
...buttonPropsWithoutRef,
|
|
255
|
-
type: undefined,
|
|
208
|
+
type: undefined,
|
|
256
209
|
disabled: undefined,
|
|
257
210
|
};
|
|
258
|
-
|
|
211
|
+
|
|
259
212
|
// Use custom LinkComponent if provided (e.g., Next.js Link)
|
|
260
213
|
if (LinkComponent) {
|
|
261
214
|
const LinkComp = LinkComponent as React.ComponentType<any>;
|
|
@@ -266,25 +219,14 @@ export const Button = React.memo(
|
|
|
266
219
|
target,
|
|
267
220
|
rel: target === '_blank' ? 'noopener noreferrer' : undefined,
|
|
268
221
|
};
|
|
269
|
-
|
|
222
|
+
|
|
270
223
|
const linkElement = (
|
|
271
224
|
<LinkComp {...linkProps}>
|
|
272
225
|
{buttonContent}
|
|
273
226
|
</LinkComp>
|
|
274
227
|
);
|
|
275
228
|
|
|
276
|
-
|
|
277
|
-
const defaultGlassProps = {
|
|
278
|
-
displacementScale: 20,
|
|
279
|
-
blurAmount: 0,
|
|
280
|
-
saturation: 200,
|
|
281
|
-
elasticity: 0,
|
|
282
|
-
};
|
|
283
|
-
const glassProps = glass === true ? defaultGlassProps : { ...defaultGlassProps, ...glass };
|
|
284
|
-
return <AtomixGlass {...glassProps}>{linkElement}</AtomixGlass>;
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
return linkElement;
|
|
229
|
+
return glass ? <AtomixGlass {...glassProps}>{linkElement}</AtomixGlass> : linkElement;
|
|
288
230
|
}
|
|
289
231
|
|
|
290
232
|
// Fallback to regular anchor tag
|
|
@@ -294,39 +236,15 @@ export const Button = React.memo(
|
|
|
294
236
|
</a>
|
|
295
237
|
);
|
|
296
238
|
|
|
297
|
-
|
|
298
|
-
const defaultGlassProps = {
|
|
299
|
-
displacementScale: 20,
|
|
300
|
-
blurAmount: 0,
|
|
301
|
-
saturation: 200,
|
|
302
|
-
elasticity: 0,
|
|
303
|
-
};
|
|
304
|
-
const glassProps = glass === true ? defaultGlassProps : { ...defaultGlassProps, ...glass };
|
|
305
|
-
return <AtomixGlass {...glassProps}>{anchorElement}</AtomixGlass>;
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
return anchorElement;
|
|
239
|
+
return glass ? <AtomixGlass {...glassProps}>{anchorElement}</AtomixGlass> : anchorElement;
|
|
309
240
|
}
|
|
310
241
|
|
|
311
242
|
// Default button rendering
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
blurAmount: 0,
|
|
316
|
-
saturation: 200,
|
|
317
|
-
elasticity: 0,
|
|
318
|
-
};
|
|
319
|
-
|
|
320
|
-
const glassProps = glass === true ? defaultGlassProps : { ...defaultGlassProps, ...glass };
|
|
321
|
-
|
|
322
|
-
return (
|
|
323
|
-
<AtomixGlass {...glassProps}>
|
|
324
|
-
<Component {...buttonProps}>{buttonContent}</Component>
|
|
325
|
-
</AtomixGlass>
|
|
326
|
-
);
|
|
327
|
-
}
|
|
243
|
+
const buttonElement = (
|
|
244
|
+
<Component {...buttonProps}>{buttonContent}</Component>
|
|
245
|
+
);
|
|
328
246
|
|
|
329
|
-
return <
|
|
247
|
+
return glass ? <AtomixGlass {...glassProps}>{buttonElement}</AtomixGlass> : buttonElement;
|
|
330
248
|
}
|
|
331
249
|
)
|
|
332
250
|
);
|
|
@@ -335,4 +253,4 @@ Button.displayName = 'Button';
|
|
|
335
253
|
|
|
336
254
|
export type { ButtonProps };
|
|
337
255
|
|
|
338
|
-
export default Button;
|
|
256
|
+
export default Button;
|
|
@@ -309,14 +309,14 @@ export function useAtomixGlass({
|
|
|
309
309
|
// timestamp: new Date().toISOString(),
|
|
310
310
|
// });
|
|
311
311
|
// }
|
|
312
|
-
} else if (process.env
|
|
312
|
+
} else if ((typeof process === 'undefined' || process.env?.NODE_ENV !== 'production') && debugCornerRadius) {
|
|
313
313
|
// console.log(
|
|
314
314
|
// '[AtomixGlass] No corner radius found, using default:',
|
|
315
315
|
// CONSTANTS.DEFAULT_CORNER_RADIUS
|
|
316
316
|
// );
|
|
317
317
|
}
|
|
318
318
|
} catch (error) {
|
|
319
|
-
if (process.env
|
|
319
|
+
if ((typeof process === 'undefined' || process.env?.NODE_ENV !== 'production') && debugCornerRadius) {
|
|
320
320
|
console.error('[AtomixGlass] Error extracting corner radius:', error);
|
|
321
321
|
}
|
|
322
322
|
}
|
|
@@ -412,7 +412,7 @@ export function useAtomixGlass({
|
|
|
412
412
|
}
|
|
413
413
|
} catch (styleError) {
|
|
414
414
|
// Silently continue if getting computed style fails for this element
|
|
415
|
-
if (process.env
|
|
415
|
+
if (typeof process === 'undefined' || process.env?.NODE_ENV === 'development') {
|
|
416
416
|
console.debug('AtomixGlass: Error getting computed style for element:', styleError);
|
|
417
417
|
}
|
|
418
418
|
}
|
|
@@ -484,7 +484,7 @@ export function useAtomixGlass({
|
|
|
484
484
|
}
|
|
485
485
|
} catch (error) {
|
|
486
486
|
// Enhanced error logging with context
|
|
487
|
-
if (process.env
|
|
487
|
+
if (typeof process === 'undefined' || process.env?.NODE_ENV === 'development') {
|
|
488
488
|
console.warn('AtomixGlass: Error detecting background brightness:', error);
|
|
489
489
|
}
|
|
490
490
|
const result = false;
|
|
@@ -607,7 +607,7 @@ export function useAtomixGlass({
|
|
|
607
607
|
setInternalMouseOffset(newOffset);
|
|
608
608
|
setInternalGlobalMousePosition(globalPos);
|
|
609
609
|
|
|
610
|
-
if (process.env
|
|
610
|
+
if ((typeof process === 'undefined' || process.env?.NODE_ENV !== 'production') && enablePerformanceMonitoring) {
|
|
611
611
|
const endTime = performance.now();
|
|
612
612
|
const duration = endTime - startTime;
|
|
613
613
|
// if (duration > 5) {
|
|
@@ -964,7 +964,7 @@ export function useAtomixGlass({
|
|
|
964
964
|
};
|
|
965
965
|
|
|
966
966
|
// Debug logging
|
|
967
|
-
if (process.env
|
|
967
|
+
if ((typeof process === 'undefined' || process.env?.NODE_ENV !== 'production') && debugOverLight) {
|
|
968
968
|
console.log('[AtomixGlass] OverLight Config:', {
|
|
969
969
|
isOverLight,
|
|
970
970
|
config: {
|
|
@@ -996,7 +996,7 @@ export function useAtomixGlass({
|
|
|
996
996
|
}
|
|
997
997
|
|
|
998
998
|
// Debug logging for non-object configs
|
|
999
|
-
if (process.env
|
|
999
|
+
if ((typeof process === 'undefined' || process.env?.NODE_ENV !== 'production') && debugOverLight) {
|
|
1000
1000
|
console.log('[AtomixGlass] OverLight Config:', {
|
|
1001
1001
|
isOverLight,
|
|
1002
1002
|
configType: typeof overLight === 'boolean' ? (overLight ? 'true' : 'false') : overLight,
|
package/src/lib/config/loader.ts
CHANGED
|
@@ -46,7 +46,7 @@ export function loadAtomixConfig(
|
|
|
46
46
|
// In browser environments, config loading is not supported
|
|
47
47
|
if (typeof window !== 'undefined') {
|
|
48
48
|
if (required) {
|
|
49
|
-
throw new Error('
|
|
49
|
+
throw new Error('loadAtomixConfig: Not available in browser environment. Config loading requires Node.js/SSR environment.');
|
|
50
50
|
}
|
|
51
51
|
return defaultConfig;
|
|
52
52
|
}
|
|
@@ -144,5 +144,4 @@ export function resolveConfigPath(): string | null {
|
|
|
144
144
|
return null;
|
|
145
145
|
}
|
|
146
146
|
|
|
147
|
-
export default loadAtomixConfig;
|
|
148
|
-
|
|
147
|
+
export default loadAtomixConfig;
|
|
@@ -32,6 +32,13 @@ export const CLASS_PREFIX = {
|
|
|
32
32
|
/**
|
|
33
33
|
* Button-specific constants
|
|
34
34
|
*/
|
|
35
|
+
export const THEME_NAMING = {
|
|
36
|
+
BUTTON_PREFIX: 'btn',
|
|
37
|
+
ICON_ELEMENT: 'icon',
|
|
38
|
+
LABEL_ELEMENT: 'label',
|
|
39
|
+
SPINNER_ELEMENT: 'spinner',
|
|
40
|
+
};
|
|
41
|
+
|
|
35
42
|
export const BUTTON = {
|
|
36
43
|
BASE_CLASS: 'c-btn',
|
|
37
44
|
ICON_CLASS: 'c-btn__icon',
|
|
@@ -77,7 +77,7 @@ export interface UsePerformanceMonitorOptions {
|
|
|
77
77
|
export function usePerformanceMonitor(options: UsePerformanceMonitorOptions) {
|
|
78
78
|
const {
|
|
79
79
|
componentName,
|
|
80
|
-
logToConsole = process.env
|
|
80
|
+
logToConsole = (typeof process === 'undefined' || process.env?.NODE_ENV === 'development'),
|
|
81
81
|
warnThreshold = 16,
|
|
82
82
|
onMetrics,
|
|
83
83
|
} = options;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { useContext } from 'react';
|
|
2
|
+
import { ThemeContext } from '../theme/runtime/ThemeContext';
|
|
3
|
+
import { useTheme } from '../theme/runtime/useTheme';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Standardized hook for accessing theme tokens in components
|
|
7
|
+
*
|
|
8
|
+
* Provides consistent access to theme values across all components
|
|
9
|
+
* using either CSS custom properties or theme object values.
|
|
10
|
+
*/
|
|
11
|
+
export function useThemeTokens() {
|
|
12
|
+
const { theme, activeTheme } = useTheme();
|
|
13
|
+
|
|
14
|
+
// Helper function to get CSS variable value
|
|
15
|
+
const getToken = useCallback((tokenName: string, fallback?: string) => {
|
|
16
|
+
if (typeof window === 'undefined') return fallback || '';
|
|
17
|
+
|
|
18
|
+
const cssVarName = `--atomix-${tokenName}`;
|
|
19
|
+
const computedStyle = getComputedStyle(document.documentElement);
|
|
20
|
+
return computedStyle.getPropertyValue(cssVarName).trim() || fallback || '';
|
|
21
|
+
}, []);
|
|
22
|
+
|
|
23
|
+
// Helper function to get theme object value
|
|
24
|
+
const getThemeValue = useCallback((path: string, fallback?: any) => {
|
|
25
|
+
if (!activeTheme) return fallback;
|
|
26
|
+
|
|
27
|
+
// Navigate through nested theme object using dot notation
|
|
28
|
+
return path.split('.').reduce((obj, key) => obj?.[key], activeTheme) || fallback;
|
|
29
|
+
}, [activeTheme]);
|
|
30
|
+
|
|
31
|
+
// Return unified API for accessing theme values
|
|
32
|
+
return {
|
|
33
|
+
theme,
|
|
34
|
+
activeTheme,
|
|
35
|
+
getToken,
|
|
36
|
+
getThemeValue,
|
|
37
|
+
// Commonly used tokens with fallbacks
|
|
38
|
+
colors: {
|
|
39
|
+
primary: getToken('primary', '#3b82f6'),
|
|
40
|
+
secondary: getToken('secondary', '#10b981'),
|
|
41
|
+
error: getToken('error', '#ef4444'),
|
|
42
|
+
success: getToken('success', '#22c55e'),
|
|
43
|
+
warning: getToken('warning', '#eab308'),
|
|
44
|
+
info: getToken('info', '#3b82f6'),
|
|
45
|
+
light: getToken('light', '#f9fafb'),
|
|
46
|
+
dark: getToken('dark', '#111827'),
|
|
47
|
+
},
|
|
48
|
+
spacing: {
|
|
49
|
+
1: getToken('spacing-1', '0.25rem'),
|
|
50
|
+
2: getToken('spacing-2', '0.5rem'),
|
|
51
|
+
3: getToken('spacing-3', '0.75rem'),
|
|
52
|
+
4: getToken('spacing-4', '1rem'),
|
|
53
|
+
5: getToken('spacing-5', '1.25rem'),
|
|
54
|
+
6: getToken('spacing-6', '1.5rem'),
|
|
55
|
+
8: getToken('spacing-8', '2rem'),
|
|
56
|
+
10: getToken('spacing-10', '2.5rem'),
|
|
57
|
+
12: getToken('spacing-12', '3rem'),
|
|
58
|
+
16: getToken('spacing-16', '4rem'),
|
|
59
|
+
20: getToken('spacing-20', '5rem'),
|
|
60
|
+
},
|
|
61
|
+
borderRadius: {
|
|
62
|
+
sm: getToken('border-radius-sm', '0.25rem'),
|
|
63
|
+
md: getToken('border-radius-md', '0.5rem'),
|
|
64
|
+
lg: getToken('border-radius-lg', '0.75rem'),
|
|
65
|
+
xl: getToken('border-radius-xl', '1rem'),
|
|
66
|
+
full: getToken('border-radius-full', '9999px'),
|
|
67
|
+
},
|
|
68
|
+
typography: {
|
|
69
|
+
fontFamily: {
|
|
70
|
+
sans: getToken('font-sans-serif', 'Inter, system-ui, sans-serif'),
|
|
71
|
+
serif: getToken('font-serif', 'Georgia, serif'),
|
|
72
|
+
mono: getToken('font-monospace', 'Fira Code, monospace'),
|
|
73
|
+
},
|
|
74
|
+
fontSize: {
|
|
75
|
+
xs: getToken('font-size-xs', '0.75rem'),
|
|
76
|
+
sm: getToken('font-size-sm', '0.875rem'),
|
|
77
|
+
md: getToken('font-size-md', '1rem'),
|
|
78
|
+
lg: getToken('font-size-lg', '1.125rem'),
|
|
79
|
+
xl: getToken('font-size-xl', '1.25rem'),
|
|
80
|
+
'2xl': getToken('font-size-2xl', '1.5rem'),
|
|
81
|
+
'3xl': getToken('font-size-3xl', '1.875rem'),
|
|
82
|
+
'4xl': getToken('font-size-4xl', '2.25rem'),
|
|
83
|
+
},
|
|
84
|
+
fontWeight: {
|
|
85
|
+
light: getToken('font-weight-light', '300'),
|
|
86
|
+
normal: getToken('font-weight-normal', '400'),
|
|
87
|
+
medium: getToken('font-weight-medium', '500'),
|
|
88
|
+
semibold: getToken('font-weight-semibold', '600'),
|
|
89
|
+
bold: getToken('font-weight-bold', '700'),
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
shadows: {
|
|
93
|
+
sm: getToken('box-shadow-sm', '0 1px 2px 0 rgba(0, 0, 0, 0.05)'),
|
|
94
|
+
md: getToken('box-shadow-md', '0 4px 6px -1px rgba(0, 0, 0, 0.1)'),
|
|
95
|
+
lg: getToken('box-shadow-lg', '0 10px 15px -3px rgba(0, 0, 0, 0.1)'),
|
|
96
|
+
xl: getToken('box-shadow-xl', '0 20px 25px -5px rgba(0, 0, 0, 0.1)'),
|
|
97
|
+
inset: getToken('box-shadow-inset', 'inset 0 2px 4px 0 rgba(0, 0, 0, 0.06)'),
|
|
98
|
+
},
|
|
99
|
+
transitions: {
|
|
100
|
+
fast: getToken('transition-fast', '150ms'),
|
|
101
|
+
base: getToken('transition-base', '200ms'),
|
|
102
|
+
slow: getToken('transition-slow', '300ms'),
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
}
|