@shohojdhara/atomix 0.3.2 → 0.3.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/README.md +58 -21
- package/dist/atomix.css +96 -121
- package/dist/atomix.min.css +3 -3
- package/dist/index.d.ts +7937 -7765
- package/dist/index.esm.js +3677 -4031
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +3648 -3952
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/package.json +44 -16
- package/scripts/atomix-cli.js +1764 -0
- package/scripts/build-themes.js +208 -0
- package/scripts/cli/interactive-init.js +520 -0
- package/scripts/cli/migration-tools.js +603 -0
- package/scripts/cli/theme-bridge.js +129 -0
- package/scripts/cli/token-manager.js +519 -0
- package/scripts/sync-theme-config.js +309 -0
- package/src/components/Button/Button.tsx +36 -1
- package/src/components/List/ListGroup.tsx +1 -2
- package/src/components/Popover/Popover.tsx +2 -2
- package/src/components/Tooltip/Tooltip.stories.tsx +49 -12
- package/src/components/Tooltip/Tooltip.tsx +32 -58
- package/src/lib/composables/useTooltip.ts +285 -0
- package/src/lib/config/index.ts +275 -0
- package/src/lib/config/loader.ts +105 -0
- package/src/lib/constants/cssVariables.ts +390 -0
- package/src/lib/hooks/__tests__/useComponentCustomization.test.ts +151 -0
- package/src/lib/hooks/index.ts +19 -0
- package/src/lib/hooks/useComponentCustomization.ts +175 -0
- package/src/lib/index.ts +14 -1
- package/src/lib/patterns/__tests__/slots.test.ts +108 -0
- package/src/lib/patterns/index.ts +35 -0
- package/src/lib/patterns/slots.tsx +421 -0
- package/src/lib/theme/composeTheme.ts +0 -5
- package/src/lib/theme/config/index.ts +1 -1
- package/src/lib/theme/config/loader.ts +75 -41
- package/src/lib/theme/config/types.ts +21 -7
- package/src/lib/theme/config/validator.ts +1 -1
- package/src/lib/theme/constants.ts +12 -2
- package/src/lib/theme/createTheme.ts +2 -135
- package/src/lib/theme/createThemeFromConfig.ts +132 -0
- package/src/lib/theme/cssVariableMapper.ts +261 -0
- package/src/lib/theme/devtools/CLI.ts +161 -76
- package/src/lib/theme/devtools/Comparator.tsx +343 -0
- package/src/lib/theme/devtools/IMPROVEMENTS.md +429 -0
- package/src/lib/theme/devtools/Inspector.tsx +21 -6
- package/src/lib/theme/devtools/LiveEditor.tsx +393 -0
- package/src/lib/theme/devtools/README.md +433 -0
- package/src/lib/theme/devtools/index.ts +12 -11
- package/src/lib/theme/generateCSSVariables.ts +79 -38
- package/src/lib/theme/index.ts +45 -246
- package/src/lib/theme/runtime/ThemeApplicator.ts +252 -0
- package/src/lib/theme/runtime/ThemeManager.test.ts +17 -1
- package/src/lib/theme/runtime/ThemeManager.ts +7 -7
- package/src/lib/theme/themeUtils.ts +27 -5
- package/src/lib/theme/types.ts +59 -1
- package/src/lib/theme-tools.ts +125 -0
- package/src/lib/types/components.ts +260 -72
- package/src/lib/types/partProps.ts +426 -0
- package/src/lib/utils/__tests__/componentUtils.test.ts +144 -0
- package/src/lib/utils/componentUtils.ts +163 -0
- package/src/lib/utils/index.ts +17 -57
- package/src/styles/01-settings/_settings.colors.scss +10 -10
- package/src/styles/01-settings/_settings.navbar.scss +1 -1
- package/src/styles/01-settings/_settings.tooltip.scss +1 -1
- package/src/styles/03-generic/_generated-root.css +5 -0
- package/src/styles/06-components/_components.navbar.scss +12 -5
- package/src/styles/06-components/_components.tooltip.scss +31 -81
- package/src/themes/README.md +442 -0
- package/src/themes/themes.config.js +35 -0
- package/src/lib/theme/errors.test.ts +0 -207
- package/src/lib/theme/generators/CSSGenerator.ts +0 -311
- package/src/lib/theme/generators/ConfigGenerator.ts +0 -287
- package/src/lib/theme/generators/TypeGenerator.ts +0 -228
- package/src/lib/theme/generators/index.ts +0 -21
- package/src/lib/theme/monitoring/ThemeAnalytics.ts +0 -409
- package/src/lib/theme/monitoring/index.ts +0 -17
- package/src/lib/theme/overrides/ComponentOverrides.ts +0 -243
- package/src/lib/theme/overrides/index.ts +0 -15
- package/src/lib/theme/studio/ThemeStudio.tsx +0 -312
- package/src/lib/theme/studio/index.ts +0 -8
- package/src/lib/theme/whitelabel/WhiteLabelManager.ts +0 -364
- package/src/lib/theme/whitelabel/index.ts +0 -13
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
import { useState, useRef, useEffect, RefObject, useCallback } from 'react';
|
|
2
|
+
|
|
3
|
+
export type TooltipPosition =
|
|
4
|
+
| 'top'
|
|
5
|
+
| 'bottom'
|
|
6
|
+
| 'left'
|
|
7
|
+
| 'right'
|
|
8
|
+
| 'top-left'
|
|
9
|
+
| 'top-right'
|
|
10
|
+
| 'bottom-left'
|
|
11
|
+
| 'bottom-right';
|
|
12
|
+
|
|
13
|
+
export type TooltipTrigger = 'click' | 'hover';
|
|
14
|
+
|
|
15
|
+
interface UseTooltipProps {
|
|
16
|
+
position?: TooltipPosition;
|
|
17
|
+
trigger?: TooltipTrigger;
|
|
18
|
+
offset?: number;
|
|
19
|
+
delay?: number;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface TooltipStyles {
|
|
23
|
+
tooltip: React.CSSProperties;
|
|
24
|
+
arrow: React.CSSProperties;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface UseTooltipResult {
|
|
28
|
+
isVisible: boolean;
|
|
29
|
+
isPositioned: boolean;
|
|
30
|
+
tooltipId: string;
|
|
31
|
+
triggerRef: RefObject<HTMLDivElement>;
|
|
32
|
+
tooltipRef: RefObject<HTMLDivElement>;
|
|
33
|
+
tooltipStyle: React.CSSProperties;
|
|
34
|
+
arrowStyle: React.CSSProperties;
|
|
35
|
+
showTooltip: () => void;
|
|
36
|
+
hideTooltip: () => void;
|
|
37
|
+
toggleTooltip: () => void;
|
|
38
|
+
triggerProps: React.HTMLAttributes<HTMLDivElement>;
|
|
39
|
+
wrapperProps: React.HTMLAttributes<HTMLDivElement>;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Calculate tooltip and arrow positions based on trigger and tooltip dimensions
|
|
44
|
+
*/
|
|
45
|
+
const calculateTooltipPosition = (
|
|
46
|
+
position: TooltipPosition,
|
|
47
|
+
triggerRect: DOMRect,
|
|
48
|
+
tooltipRect: DOMRect,
|
|
49
|
+
wrapperRect: DOMRect,
|
|
50
|
+
offset: number,
|
|
51
|
+
arrowSize: number
|
|
52
|
+
): TooltipStyles => {
|
|
53
|
+
const tooltipWidth = tooltipRect.width || 0;
|
|
54
|
+
const tooltipHeight = tooltipRect.height || 0;
|
|
55
|
+
const triggerWidth = triggerRect.width;
|
|
56
|
+
const triggerHeight = triggerRect.height;
|
|
57
|
+
|
|
58
|
+
const tooltipStyle: React.CSSProperties = {
|
|
59
|
+
'--atomix-tooltip-offset': `${offset}px`,
|
|
60
|
+
} as React.CSSProperties;
|
|
61
|
+
|
|
62
|
+
const arrowStyle: React.CSSProperties = {};
|
|
63
|
+
|
|
64
|
+
switch (position) {
|
|
65
|
+
case 'top':
|
|
66
|
+
tooltipStyle.top = `${triggerRect.top - wrapperRect.top - tooltipHeight - offset}px`;
|
|
67
|
+
tooltipStyle.left = `${triggerRect.left - wrapperRect.left + triggerWidth / 2 - tooltipWidth / 2}px`;
|
|
68
|
+
arrowStyle.bottom = `${arrowSize / -2}px`;
|
|
69
|
+
arrowStyle.left = `${tooltipWidth / 2 - arrowSize / 2}px`;
|
|
70
|
+
break;
|
|
71
|
+
|
|
72
|
+
case 'bottom':
|
|
73
|
+
tooltipStyle.top = `${triggerRect.bottom - wrapperRect.top + offset}px`;
|
|
74
|
+
tooltipStyle.left = `${triggerRect.left - wrapperRect.left + triggerWidth / 2 - tooltipWidth / 2}px`;
|
|
75
|
+
arrowStyle.top = `${arrowSize / -2}px`;
|
|
76
|
+
arrowStyle.left = `${tooltipWidth / 2 - arrowSize / 2}px`;
|
|
77
|
+
break;
|
|
78
|
+
|
|
79
|
+
case 'left':
|
|
80
|
+
tooltipStyle.right = `${wrapperRect.right - triggerRect.left + offset}px`;
|
|
81
|
+
tooltipStyle.top = `${triggerRect.top - wrapperRect.top + triggerHeight / 2 - tooltipHeight / 2}px`;
|
|
82
|
+
arrowStyle.right = `${arrowSize / -2}px`;
|
|
83
|
+
arrowStyle.top = `${tooltipHeight / 2 - arrowSize / 2}px`;
|
|
84
|
+
break;
|
|
85
|
+
|
|
86
|
+
case 'right':
|
|
87
|
+
tooltipStyle.left = `${triggerRect.right - wrapperRect.left + offset}px`;
|
|
88
|
+
tooltipStyle.top = `${triggerRect.top - wrapperRect.top + triggerHeight / 2 - tooltipHeight / 2}px`;
|
|
89
|
+
arrowStyle.left = `${arrowSize / -2}px`;
|
|
90
|
+
arrowStyle.top = `${tooltipHeight / 2 - arrowSize / 2}px`;
|
|
91
|
+
break;
|
|
92
|
+
|
|
93
|
+
case 'top-left':
|
|
94
|
+
tooltipStyle.bottom = `${wrapperRect.bottom - triggerRect.top + offset}px`;
|
|
95
|
+
tooltipStyle.left = `${triggerRect.left - wrapperRect.left}px`;
|
|
96
|
+
arrowStyle.bottom = `${arrowSize / -2}px`;
|
|
97
|
+
arrowStyle.left = `${arrowSize}px`;
|
|
98
|
+
break;
|
|
99
|
+
|
|
100
|
+
case 'top-right':
|
|
101
|
+
tooltipStyle.bottom = `${wrapperRect.bottom - triggerRect.top + offset}px`;
|
|
102
|
+
tooltipStyle.right = `${wrapperRect.right - triggerRect.right}px`;
|
|
103
|
+
arrowStyle.bottom = `${arrowSize / -2}px`;
|
|
104
|
+
arrowStyle.right = `${arrowSize}px`;
|
|
105
|
+
break;
|
|
106
|
+
|
|
107
|
+
case 'bottom-left':
|
|
108
|
+
tooltipStyle.top = `${triggerRect.bottom - wrapperRect.top + offset}px`;
|
|
109
|
+
tooltipStyle.left = `${triggerRect.left - wrapperRect.left}px`;
|
|
110
|
+
arrowStyle.top = `${arrowSize / -2}px`;
|
|
111
|
+
arrowStyle.left = `${arrowSize}px`;
|
|
112
|
+
break;
|
|
113
|
+
|
|
114
|
+
case 'bottom-right':
|
|
115
|
+
tooltipStyle.top = `${triggerRect.bottom - wrapperRect.top + offset}px`;
|
|
116
|
+
tooltipStyle.right = `${wrapperRect.right - triggerRect.right}px`;
|
|
117
|
+
arrowStyle.top = `${arrowSize / -2}px`;
|
|
118
|
+
arrowStyle.right = `${arrowSize}px`;
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return { tooltip: tooltipStyle, arrow: arrowStyle };
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Get arrow size from CSS custom property
|
|
127
|
+
*/
|
|
128
|
+
const getArrowSize = (element: HTMLElement): number => {
|
|
129
|
+
const arrowSizeValue = getComputedStyle(element)
|
|
130
|
+
.getPropertyValue('--atomix-tooltip-arrow-size')
|
|
131
|
+
.trim();
|
|
132
|
+
|
|
133
|
+
if (!arrowSizeValue) return 8; // Default fallback
|
|
134
|
+
|
|
135
|
+
// Try to parse as rem (e.g., "0.5rem")
|
|
136
|
+
const remMatch = arrowSizeValue.match(/([\d.]+)rem/);
|
|
137
|
+
if (remMatch?.[1]) {
|
|
138
|
+
return parseFloat(remMatch[1]) * 16; // Convert rem to px
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Try to parse as px (e.g., "8px")
|
|
142
|
+
const pxMatch = arrowSizeValue.match(/([\d.]+)px/);
|
|
143
|
+
if (pxMatch?.[1]) {
|
|
144
|
+
return parseFloat(pxMatch[1]);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return 8; // Default fallback
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Hook for managing tooltip state and positioning logic
|
|
152
|
+
*/
|
|
153
|
+
export const useTooltip = ({
|
|
154
|
+
position = 'top',
|
|
155
|
+
trigger = 'hover',
|
|
156
|
+
offset = 10,
|
|
157
|
+
delay = 200,
|
|
158
|
+
}: UseTooltipProps): UseTooltipResult => {
|
|
159
|
+
const [isVisible, setIsVisible] = useState(false);
|
|
160
|
+
const [isPositioned, setIsPositioned] = useState(false);
|
|
161
|
+
const [tooltipStyle, setTooltipStyle] = useState<React.CSSProperties>({});
|
|
162
|
+
const [arrowStyle, setArrowStyle] = useState<React.CSSProperties>({});
|
|
163
|
+
|
|
164
|
+
const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
165
|
+
const triggerRef = useRef<HTMLDivElement>(null);
|
|
166
|
+
const tooltipRef = useRef<HTMLDivElement>(null);
|
|
167
|
+
const tooltipId = `tooltip-${Math.random().toString(36).slice(2, 11)}`;
|
|
168
|
+
|
|
169
|
+
const showTooltip = useCallback(() => {
|
|
170
|
+
if (timeoutRef.current) {
|
|
171
|
+
clearTimeout(timeoutRef.current);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (delay > 0) {
|
|
175
|
+
timeoutRef.current = setTimeout(() => {
|
|
176
|
+
setIsVisible(true);
|
|
177
|
+
}, delay);
|
|
178
|
+
} else {
|
|
179
|
+
setIsVisible(true);
|
|
180
|
+
}
|
|
181
|
+
}, [delay]);
|
|
182
|
+
|
|
183
|
+
const hideTooltip = useCallback(() => {
|
|
184
|
+
if (timeoutRef.current) {
|
|
185
|
+
clearTimeout(timeoutRef.current);
|
|
186
|
+
}
|
|
187
|
+
setIsVisible(false);
|
|
188
|
+
setIsPositioned(false);
|
|
189
|
+
}, []);
|
|
190
|
+
|
|
191
|
+
const toggleTooltip = useCallback(() => {
|
|
192
|
+
if (isVisible) {
|
|
193
|
+
hideTooltip();
|
|
194
|
+
} else {
|
|
195
|
+
showTooltip();
|
|
196
|
+
}
|
|
197
|
+
}, [isVisible, showTooltip, hideTooltip]);
|
|
198
|
+
|
|
199
|
+
// Calculate and update tooltip position
|
|
200
|
+
const updatePosition = useCallback(() => {
|
|
201
|
+
if (!triggerRef.current || !tooltipRef.current) return;
|
|
202
|
+
|
|
203
|
+
const triggerRect = triggerRef.current.getBoundingClientRect();
|
|
204
|
+
const tooltipRect = tooltipRef.current.getBoundingClientRect();
|
|
205
|
+
const wrapperElement = triggerRef.current.parentElement;
|
|
206
|
+
|
|
207
|
+
if (!wrapperElement) return;
|
|
208
|
+
|
|
209
|
+
const wrapperRect = wrapperElement.getBoundingClientRect();
|
|
210
|
+
const arrowSize = getArrowSize(tooltipRef.current);
|
|
211
|
+
|
|
212
|
+
const styles = calculateTooltipPosition(
|
|
213
|
+
position,
|
|
214
|
+
triggerRect,
|
|
215
|
+
tooltipRect,
|
|
216
|
+
wrapperRect,
|
|
217
|
+
offset,
|
|
218
|
+
arrowSize
|
|
219
|
+
);
|
|
220
|
+
|
|
221
|
+
setTooltipStyle(styles.tooltip);
|
|
222
|
+
setArrowStyle(styles.arrow);
|
|
223
|
+
setIsPositioned(true);
|
|
224
|
+
}, [position, offset]);
|
|
225
|
+
|
|
226
|
+
// Position tooltip when visible
|
|
227
|
+
useEffect(() => {
|
|
228
|
+
if (!isVisible || !triggerRef.current || !tooltipRef.current) {
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Use single RAF to ensure tooltip is rendered before calculating
|
|
233
|
+
const rafId = requestAnimationFrame(() => {
|
|
234
|
+
updatePosition();
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
// Recalculate on window resize and scroll
|
|
238
|
+
const handleUpdate = () => {
|
|
239
|
+
updatePosition();
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
window.addEventListener('resize', handleUpdate);
|
|
243
|
+
window.addEventListener('scroll', handleUpdate, true);
|
|
244
|
+
|
|
245
|
+
return () => {
|
|
246
|
+
cancelAnimationFrame(rafId);
|
|
247
|
+
window.removeEventListener('resize', handleUpdate);
|
|
248
|
+
window.removeEventListener('scroll', handleUpdate, true);
|
|
249
|
+
};
|
|
250
|
+
}, [isVisible, updatePosition]);
|
|
251
|
+
|
|
252
|
+
// Setup trigger props
|
|
253
|
+
const triggerProps: React.HTMLAttributes<HTMLDivElement> = {
|
|
254
|
+
'aria-describedby': isVisible ? tooltipId : undefined,
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
const wrapperProps: React.HTMLAttributes<HTMLDivElement> = {};
|
|
258
|
+
|
|
259
|
+
if (trigger === 'hover') {
|
|
260
|
+
wrapperProps.onMouseEnter = showTooltip;
|
|
261
|
+
wrapperProps.onMouseLeave = hideTooltip;
|
|
262
|
+
triggerProps.onFocus = showTooltip;
|
|
263
|
+
triggerProps.onBlur = hideTooltip;
|
|
264
|
+
} else if (trigger === 'click') {
|
|
265
|
+
triggerProps.onClick = toggleTooltip;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return {
|
|
269
|
+
isVisible,
|
|
270
|
+
isPositioned,
|
|
271
|
+
tooltipId,
|
|
272
|
+
triggerRef,
|
|
273
|
+
tooltipRef,
|
|
274
|
+
tooltipStyle,
|
|
275
|
+
arrowStyle,
|
|
276
|
+
showTooltip,
|
|
277
|
+
hideTooltip,
|
|
278
|
+
toggleTooltip,
|
|
279
|
+
triggerProps,
|
|
280
|
+
wrapperProps,
|
|
281
|
+
};
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
export default useTooltip;
|
|
285
|
+
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Atomix Configuration System
|
|
3
|
+
*
|
|
4
|
+
* Tailwind-like configuration for customizing the Atomix Design System.
|
|
5
|
+
*
|
|
6
|
+
* External developers can create `atomix.config.ts` in their project root
|
|
7
|
+
* to customize design tokens, similar to Tailwind's tailwind.config.js
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* // atomix.config.ts (in your project)
|
|
12
|
+
* import { defineConfig } from '@shohojdhara/atomix/config';
|
|
13
|
+
*
|
|
14
|
+
* export default defineConfig({
|
|
15
|
+
* theme: {
|
|
16
|
+
* extend: {
|
|
17
|
+
* colors: {
|
|
18
|
+
* primary: { main: '#7AFFD7' },
|
|
19
|
+
* },
|
|
20
|
+
* },
|
|
21
|
+
* },
|
|
22
|
+
* });
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import type { Theme } from '../theme/types';
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Color Scale (1-10)
|
|
30
|
+
*/
|
|
31
|
+
export interface ColorScale {
|
|
32
|
+
1?: string;
|
|
33
|
+
2?: string;
|
|
34
|
+
3?: string;
|
|
35
|
+
4?: string;
|
|
36
|
+
5?: string;
|
|
37
|
+
6?: string;
|
|
38
|
+
7?: string;
|
|
39
|
+
8?: string;
|
|
40
|
+
9?: string;
|
|
41
|
+
10?: string;
|
|
42
|
+
[key: string]: string | undefined;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Palette Color Options
|
|
47
|
+
*/
|
|
48
|
+
export interface PaletteColorOptions {
|
|
49
|
+
main: string;
|
|
50
|
+
light?: string;
|
|
51
|
+
dark?: string;
|
|
52
|
+
contrastText?: string;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Design Tokens Schema (Tailwind-like)
|
|
57
|
+
*/
|
|
58
|
+
export interface ThemeTokens {
|
|
59
|
+
/** Color palette */
|
|
60
|
+
colors?: Record<string, string | PaletteColorOptions | ColorScale | Record<string, string>>;
|
|
61
|
+
/** Spacing scale */
|
|
62
|
+
spacing?: Record<string, string>;
|
|
63
|
+
/** Border radius scale */
|
|
64
|
+
borderRadius?: Record<string, string>;
|
|
65
|
+
/** Typography scale and settings */
|
|
66
|
+
typography?: {
|
|
67
|
+
fontFamilies?: Record<string, string>;
|
|
68
|
+
fontSizes?: Record<string, string>;
|
|
69
|
+
fontWeights?: Record<string, string | number>;
|
|
70
|
+
lineHeights?: Record<string, string | number>;
|
|
71
|
+
letterSpacings?: Record<string, string>;
|
|
72
|
+
};
|
|
73
|
+
/** Shadow scale */
|
|
74
|
+
shadows?: Record<string, string>;
|
|
75
|
+
/** Z-index scale */
|
|
76
|
+
zIndex?: Record<string, string | number>;
|
|
77
|
+
/** Breakpoints scale */
|
|
78
|
+
breakpoints?: Record<string, string | number>;
|
|
79
|
+
/** Transitions settings */
|
|
80
|
+
transitions?: {
|
|
81
|
+
durations?: Record<string, string>;
|
|
82
|
+
easings?: Record<string, string>;
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* CSS Theme Definition
|
|
88
|
+
*/
|
|
89
|
+
export interface CSSThemeDefinition {
|
|
90
|
+
type: 'css';
|
|
91
|
+
name: string;
|
|
92
|
+
class?: string;
|
|
93
|
+
description?: string;
|
|
94
|
+
author?: string;
|
|
95
|
+
version?: string;
|
|
96
|
+
tags?: string[];
|
|
97
|
+
supportsDarkMode?: boolean;
|
|
98
|
+
status?: 'stable' | 'beta' | 'experimental' | 'deprecated';
|
|
99
|
+
a11y?: {
|
|
100
|
+
contrastTarget?: number;
|
|
101
|
+
modes?: string[];
|
|
102
|
+
};
|
|
103
|
+
color?: string;
|
|
104
|
+
features?: string[];
|
|
105
|
+
dependencies?: string[];
|
|
106
|
+
cssPath?: string;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* JavaScript Theme Definition
|
|
111
|
+
*/
|
|
112
|
+
export interface JSThemeDefinition {
|
|
113
|
+
type: 'js';
|
|
114
|
+
name: string;
|
|
115
|
+
class?: string;
|
|
116
|
+
description?: string;
|
|
117
|
+
author?: string;
|
|
118
|
+
version?: string;
|
|
119
|
+
tags?: string[];
|
|
120
|
+
supportsDarkMode?: boolean;
|
|
121
|
+
status?: 'stable' | 'beta' | 'experimental' | 'deprecated';
|
|
122
|
+
a11y?: {
|
|
123
|
+
contrastTarget?: number;
|
|
124
|
+
modes?: string[];
|
|
125
|
+
};
|
|
126
|
+
color?: string;
|
|
127
|
+
features?: string[];
|
|
128
|
+
dependencies?: string[];
|
|
129
|
+
createTheme: () => Theme;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Theme Definition (CSS or JS)
|
|
134
|
+
*/
|
|
135
|
+
export type ThemeDefinition = CSSThemeDefinition | JSThemeDefinition;
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Build configuration (migrated from theme.config.ts)
|
|
139
|
+
*/
|
|
140
|
+
export interface BuildConfig {
|
|
141
|
+
output?: {
|
|
142
|
+
directory?: string;
|
|
143
|
+
formats?: {
|
|
144
|
+
expanded?: string;
|
|
145
|
+
compressed?: string;
|
|
146
|
+
};
|
|
147
|
+
};
|
|
148
|
+
sass?: {
|
|
149
|
+
style?: 'expanded' | 'compressed';
|
|
150
|
+
sourceMap?: boolean;
|
|
151
|
+
loadPaths?: string[];
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Runtime configuration (migrated from theme.config.ts)
|
|
157
|
+
*/
|
|
158
|
+
export interface RuntimeConfig {
|
|
159
|
+
basePath?: string;
|
|
160
|
+
cdnPath?: string | null;
|
|
161
|
+
preload?: string[];
|
|
162
|
+
lazy?: boolean;
|
|
163
|
+
defaultTheme?: string;
|
|
164
|
+
storageKey?: string;
|
|
165
|
+
dataAttribute?: string;
|
|
166
|
+
enablePersistence?: boolean;
|
|
167
|
+
useMinified?: boolean;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Integration settings (migrated from theme.config.ts)
|
|
172
|
+
*/
|
|
173
|
+
export interface IntegrationConfig {
|
|
174
|
+
cssVariables?: Record<string, string>;
|
|
175
|
+
classNames?: {
|
|
176
|
+
theme?: string;
|
|
177
|
+
colorMode?: string;
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Atomix Configuration Interface
|
|
183
|
+
*
|
|
184
|
+
* Tailwind-like configuration for external developers.
|
|
185
|
+
* Focus on theme customization - build/runtime configs are internal only.
|
|
186
|
+
*/
|
|
187
|
+
export interface AtomixConfig {
|
|
188
|
+
/**
|
|
189
|
+
* CSS variable prefix (default: 'atomix')
|
|
190
|
+
*
|
|
191
|
+
* Change this to customize all CSS variable names.
|
|
192
|
+
* Example: prefix: 'myapp' → --myapp-primary instead of --atomix-primary
|
|
193
|
+
*/
|
|
194
|
+
prefix?: string;
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Theme customization (Tailwind-like)
|
|
198
|
+
*
|
|
199
|
+
* Use `extend` to add or override design tokens.
|
|
200
|
+
* Use `tokens` to completely replace the default token system (advanced).
|
|
201
|
+
*/
|
|
202
|
+
theme?: {
|
|
203
|
+
/**
|
|
204
|
+
* Extend the default design tokens
|
|
205
|
+
*
|
|
206
|
+
* This is the recommended way to customize Atomix.
|
|
207
|
+
* Your values will override or extend the base tokens.
|
|
208
|
+
*/
|
|
209
|
+
extend?: ThemeTokens;
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Override the default tokens entirely (advanced)
|
|
213
|
+
*
|
|
214
|
+
* Use with caution - this replaces the entire token system.
|
|
215
|
+
* Most users should use `extend` instead.
|
|
216
|
+
*/
|
|
217
|
+
tokens?: ThemeTokens;
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Register custom themes (optional)
|
|
221
|
+
*
|
|
222
|
+
* Define CSS or JavaScript themes that can be loaded dynamically.
|
|
223
|
+
*/
|
|
224
|
+
themes?: Record<string, ThemeDefinition>;
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
// Internal configurations (for library development only)
|
|
228
|
+
// These are not needed for external developers
|
|
229
|
+
/** @internal Build configuration (internal use only) */
|
|
230
|
+
build?: BuildConfig;
|
|
231
|
+
/** @internal Runtime configuration (internal use only) */
|
|
232
|
+
runtime?: RuntimeConfig;
|
|
233
|
+
/** @internal Integration settings (internal use only) */
|
|
234
|
+
integration?: IntegrationConfig;
|
|
235
|
+
/** @internal Theme dependencies mapping (internal use only) */
|
|
236
|
+
dependencies?: Record<string, string[]>;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Helper function to define Atomix configuration with type safety
|
|
241
|
+
*
|
|
242
|
+
* @param config - Atomix configuration object
|
|
243
|
+
* @returns The configuration object
|
|
244
|
+
*/
|
|
245
|
+
/**
|
|
246
|
+
* Helper function to define Atomix configuration with type safety
|
|
247
|
+
*
|
|
248
|
+
* Similar to Tailwind's defineConfig, provides autocomplete and type checking.
|
|
249
|
+
*
|
|
250
|
+
* @param config - Atomix configuration object
|
|
251
|
+
* @returns The configuration object
|
|
252
|
+
*
|
|
253
|
+
* @example
|
|
254
|
+
* ```typescript
|
|
255
|
+
* import { defineConfig } from '@shohojdhara/atomix/config';
|
|
256
|
+
*
|
|
257
|
+
* export default defineConfig({
|
|
258
|
+
* theme: {
|
|
259
|
+
* extend: {
|
|
260
|
+
* colors: {
|
|
261
|
+
* primary: { main: '#7AFFD7' },
|
|
262
|
+
* },
|
|
263
|
+
* },
|
|
264
|
+
* },
|
|
265
|
+
* });
|
|
266
|
+
* ```
|
|
267
|
+
*/
|
|
268
|
+
export function defineConfig(config: AtomixConfig): AtomixConfig {
|
|
269
|
+
return config;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Export loader functions
|
|
273
|
+
export { loadAtomixConfig, resolveConfigPath } from './loader';
|
|
274
|
+
|
|
275
|
+
export default AtomixConfig;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Atomix Config Loader
|
|
3
|
+
*
|
|
4
|
+
* Helper functions to load atomix.config.ts from external projects.
|
|
5
|
+
* Similar to how Tailwind loads tailwind.config.js
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { AtomixConfig } from './index';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Load Atomix configuration from project root
|
|
12
|
+
*
|
|
13
|
+
* Attempts to load atomix.config.ts from the current working directory.
|
|
14
|
+
* Falls back to default config if file doesn't exist.
|
|
15
|
+
*
|
|
16
|
+
* @param options - Loader options
|
|
17
|
+
* @returns Loaded configuration or default
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* import { loadAtomixConfig } from '@shohojdhara/atomix/config';
|
|
22
|
+
*
|
|
23
|
+
* const config = loadAtomixConfig();
|
|
24
|
+
* const theme = createThemeFromConfig(config);
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export function loadAtomixConfig(
|
|
28
|
+
options: {
|
|
29
|
+
/** Custom config path (default: 'atomix.config.ts') */
|
|
30
|
+
configPath?: string;
|
|
31
|
+
/** Whether to throw error if config not found (default: false) */
|
|
32
|
+
required?: boolean;
|
|
33
|
+
} = {}
|
|
34
|
+
): AtomixConfig {
|
|
35
|
+
const { configPath = 'atomix.config.ts', required = false } = options;
|
|
36
|
+
|
|
37
|
+
// Default config
|
|
38
|
+
const defaultConfig: AtomixConfig = {
|
|
39
|
+
prefix: 'atomix',
|
|
40
|
+
theme: {
|
|
41
|
+
extend: {},
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
// In browser environments, config loading is not supported
|
|
46
|
+
if (typeof window !== 'undefined') {
|
|
47
|
+
if (required) {
|
|
48
|
+
throw new Error('Config loading not supported in browser environment');
|
|
49
|
+
}
|
|
50
|
+
return defaultConfig;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Try to load config file
|
|
54
|
+
try {
|
|
55
|
+
// Use dynamic import for ESM compatibility
|
|
56
|
+
const configModule = require(configPath);
|
|
57
|
+
const config = configModule.default || configModule;
|
|
58
|
+
|
|
59
|
+
// Validate it's an AtomixConfig
|
|
60
|
+
if (config && typeof config === 'object') {
|
|
61
|
+
return config as AtomixConfig;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
throw new Error('Invalid config format');
|
|
65
|
+
} catch (error: any) {
|
|
66
|
+
if (required) {
|
|
67
|
+
throw new Error(`Failed to load config from ${configPath}: ${error.message}`);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Return default config if not required
|
|
71
|
+
return defaultConfig;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Resolve config path
|
|
77
|
+
*
|
|
78
|
+
* Finds atomix.config.ts in the project, checking common locations.
|
|
79
|
+
*/
|
|
80
|
+
export function resolveConfigPath(): string | null {
|
|
81
|
+
if (typeof process === 'undefined' || !process.cwd) {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const fs = require('fs');
|
|
86
|
+
const path = require('path');
|
|
87
|
+
|
|
88
|
+
const cwd = process.cwd();
|
|
89
|
+
const possiblePaths = [
|
|
90
|
+
path.join(cwd, 'atomix.config.ts'),
|
|
91
|
+
path.join(cwd, 'atomix.config.js'),
|
|
92
|
+
path.join(cwd, 'atomix.config.mjs'),
|
|
93
|
+
];
|
|
94
|
+
|
|
95
|
+
for (const configPath of possiblePaths) {
|
|
96
|
+
if (fs.existsSync(configPath)) {
|
|
97
|
+
return configPath;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export default loadAtomixConfig;
|
|
105
|
+
|