@shohojdhara/atomix 0.3.4 → 0.3.6
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 +101 -199
- package/atomix.config.ts +241 -0
- package/dist/atomix.css +269 -189
- package/dist/atomix.css.map +1 -0
- package/dist/atomix.min.css +15179 -11
- package/dist/atomix.min.css.map +1 -0
- package/dist/charts.d.ts +1929 -0
- package/dist/charts.js +6477 -0
- package/dist/charts.js.map +1 -0
- package/dist/core.d.ts +1289 -0
- package/dist/core.js +3373 -0
- package/dist/core.js.map +1 -0
- package/dist/forms.d.ts +1085 -0
- package/dist/forms.js +2466 -0
- package/dist/forms.js.map +1 -0
- package/dist/heavy.d.ts +636 -0
- package/dist/heavy.js +4566 -0
- package/dist/heavy.js.map +1 -0
- package/dist/index.d.ts +5171 -4792
- package/dist/index.esm.js +6098 -4563
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +6291 -4747
- 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.d.ts +300 -0
- package/dist/layout.js +336 -0
- package/dist/layout.js.map +1 -0
- package/dist/theme.d.ts +2122 -0
- package/dist/theme.js +6084 -0
- package/dist/theme.js.map +1 -0
- package/package.json +59 -27
- package/scripts/atomix-cli.js +544 -16
- package/scripts/cli/__tests__/cli-commands.test.js +204 -0
- package/scripts/cli/__tests__/utils.test.js +201 -0
- package/scripts/cli/__tests__/vitest.config.js +26 -0
- package/scripts/cli/interactive-init.js +1 -1
- package/scripts/cli/token-manager.js +32 -7
- package/scripts/cli/utils.js +347 -0
- package/src/components/Accordion/Accordion.stories.tsx +50 -17
- package/src/components/Accordion/Accordion.tsx +5 -54
- package/src/components/Accordion/index.ts +1 -1
- package/src/components/AtomixGlass/AtomixGlass.tsx +65 -31
- package/src/components/AtomixGlass/AtomixGlassContainer.tsx +11 -4
- package/src/components/AtomixGlass/stories/AtomixGlass.stories.tsx +1 -32
- package/src/components/AtomixGlass/stories/Examples.stories.tsx +2 -2
- package/src/components/AtomixGlass/stories/shared-components.tsx +0 -31
- package/src/components/Avatar/Avatar.stories.tsx +7 -0
- package/src/components/Avatar/Avatar.tsx +3 -3
- package/src/components/Badge/Badge.stories.tsx +91 -13
- package/src/components/Badge/Badge.tsx +3 -3
- package/src/components/Block/Block.stories.tsx +7 -23
- package/src/components/Breadcrumb/Breadcrumb.stories.tsx +7 -0
- package/src/components/Breadcrumb/Breadcrumb.tsx +3 -3
- package/src/components/Button/Button.stories.tsx +141 -22
- package/src/components/Button/ButtonGroup.stories.tsx +315 -0
- package/src/components/Button/ButtonGroup.tsx +67 -0
- package/src/components/Button/index.ts +2 -0
- package/src/components/Callout/Callout.stories.tsx +8 -6
- package/src/components/Card/Card.stories.tsx +82 -28
- package/src/components/Card/ElevationCard.tsx +1 -1
- package/src/components/Chart/AnimatedChart.tsx +19 -18
- package/src/components/Chart/AreaChart.tsx +5 -2
- package/src/components/Chart/BarChart.tsx +1 -1
- package/src/components/Chart/BubbleChart.tsx +6 -6
- package/src/components/Chart/CandlestickChart.tsx +0 -1
- package/src/components/Chart/Chart.stories.tsx +5 -7
- package/src/components/Chart/Chart.tsx +0 -16
- package/src/components/Chart/ChartRenderer.tsx +1 -1
- package/src/components/Chart/ChartToolbar.tsx +1 -0
- package/src/components/Chart/DonutChart.tsx +0 -1
- package/src/components/Chart/FunnelChart.tsx +1 -2
- package/src/components/Chart/GaugeChart.tsx +0 -1
- package/src/components/Chart/HeatmapChart.tsx +0 -1
- package/src/components/Chart/LineChart.tsx +0 -1
- package/src/components/Chart/MultiAxisChart.tsx +0 -1
- package/src/components/Chart/PieChart.tsx +0 -1
- package/src/components/Chart/RadarChart.tsx +19 -13
- package/src/components/Chart/ScatterChart.tsx +3 -4
- package/src/components/Chart/TreemapChart.tsx +2 -1
- package/src/components/Chart/WaterfallChart.tsx +0 -2
- package/src/components/Chart/types.ts +12 -2
- package/src/components/Chart/utils.ts +4 -3
- package/src/components/ColorModeToggle/ColorModeToggle.stories.tsx +7 -0
- package/src/components/DataTable/DataTable.stories.tsx +23 -16
- package/src/components/DataTable/DataTable.tsx +3 -3
- package/src/components/DatePicker/DatePicker.stories.tsx +27 -19
- package/src/components/Dropdown/Dropdown.stories.tsx +11 -19
- package/src/components/Dropdown/Dropdown.tsx +12 -9
- package/src/components/EdgePanel/EdgePanel.stories.tsx +1 -0
- package/src/components/Footer/Footer.stories.tsx +8 -6
- package/src/components/Footer/FooterLink.tsx +9 -2
- package/src/components/Footer/FooterSection.tsx +3 -3
- package/src/components/Form/Checkbox.stories.tsx +7 -0
- package/src/components/Form/Checkbox.tsx +3 -3
- package/src/components/Form/Form.stories.tsx +7 -0
- package/src/components/Form/FormGroup.stories.tsx +9 -1
- package/src/components/Form/Input.stories.tsx +69 -16
- package/src/components/Form/Input.tsx +4 -2
- package/src/components/Form/Radio.stories.tsx +9 -1
- package/src/components/Form/Radio.tsx +3 -3
- package/src/components/Form/Select.stories.tsx +9 -1
- package/src/components/Form/Select.tsx +3 -3
- package/src/components/Form/Textarea.stories.tsx +10 -2
- package/src/components/Form/Textarea.tsx +4 -2
- package/src/components/Hero/Hero.stories.tsx +7 -0
- package/src/components/List/List.stories.tsx +10 -3
- package/src/components/List/List.tsx +3 -3
- package/src/components/List/ListGroup.tsx +3 -1
- package/src/components/Messages/Messages.stories.tsx +8 -7
- package/src/components/Modal/Modal.stories.tsx +17 -6
- package/src/components/Modal/Modal.tsx +3 -3
- package/src/components/Navigation/Menu/MegaMenu.tsx +9 -3
- package/src/components/Navigation/Menu/Menu.stories.tsx +7 -0
- package/src/components/Navigation/Menu/Menu.tsx +9 -3
- package/src/components/Navigation/Nav/Nav.stories.tsx +7 -0
- package/src/components/Navigation/Navbar/Navbar.stories.tsx +1 -0
- package/src/components/Navigation/SideMenu/SideMenu.stories.tsx +1 -1
- package/src/components/Pagination/Pagination.stories.tsx +188 -111
- package/src/components/Pagination/Pagination.tsx +88 -7
- package/src/components/PhotoViewer/PhotoViewer.stories.tsx +10 -5
- package/src/components/PhotoViewer/PhotoViewerImage.tsx +2 -2
- package/src/components/Popover/Popover.stories.tsx +191 -115
- package/src/components/Popover/Popover.tsx +4 -4
- package/src/components/ProductReview/ProductReview.stories.tsx +80 -58
- package/src/components/Progress/Progress.stories.tsx +79 -49
- package/src/components/Progress/Progress.tsx +6 -2
- package/src/components/Rating/Rating.stories.tsx +109 -84
- package/src/components/Rating/Rating.tsx +5 -2
- package/src/components/River/River.stories.tsx +194 -114
- package/src/components/SectionIntro/SectionIntro.stories.tsx +19 -9
- package/src/components/Slider/Slider.stories.tsx +7 -0
- package/src/components/Slider/Slider.tsx +10 -9
- package/src/components/Spinner/Spinner.stories.tsx +15 -11
- package/src/components/Spinner/Spinner.tsx +3 -3
- package/src/components/Steps/Steps.stories.tsx +132 -98
- package/src/components/Tabs/Tabs.stories.tsx +163 -112
- package/src/components/Tabs/Tabs.tsx +3 -3
- package/src/components/Testimonial/Testimonial.stories.tsx +114 -68
- package/src/components/Todo/Todo.stories.tsx +38 -12
- package/src/components/Toggle/Toggle.stories.tsx +61 -28
- package/src/components/Tooltip/Tooltip.stories.tsx +318 -200
- package/src/components/Tooltip/Tooltip.tsx +3 -3
- package/src/components/Upload/Upload.stories.tsx +122 -84
- package/src/components/VideoPlayer/VideoPlayer.stories.tsx +7 -24
- package/src/components/index.ts +6 -2
- package/src/layouts/MasonryGrid/MasonryGrid.tsx +2 -2
- package/src/lib/composables/useAtomixGlass.ts +2 -3
- package/src/lib/composables/useChartPerformance.ts +102 -78
- package/src/lib/composables/useChartScale.ts +10 -0
- package/src/lib/composables/useHero.ts +9 -2
- package/src/lib/composables/useHeroBackgroundSlider.ts +5 -3
- package/src/lib/composables/useNavbar.ts +0 -10
- package/src/lib/composables/useSideMenu.ts +1 -0
- package/src/lib/composables/useVideoPlayer.ts +3 -2
- package/src/lib/config/loader.ts +57 -14
- package/src/lib/constants/components.ts +10 -0
- package/src/lib/hooks/index.ts +0 -1
- package/src/lib/hooks/useComponentCustomization.ts +11 -15
- package/src/lib/hooks/usePerformanceMonitor.ts +149 -0
- package/src/lib/patterns/index.ts +2 -2
- package/src/lib/patterns/slots.tsx +2 -2
- package/src/lib/theme/README.md +174 -0
- package/src/lib/theme/adapters/index.ts +31 -0
- package/src/lib/theme/adapters/themeAdapter.ts +287 -0
- package/src/lib/theme/config/__tests__/configLoader.test.ts +207 -0
- package/src/lib/theme/config/configLoader.ts +254 -0
- package/src/lib/theme/config/loader.ts +37 -48
- package/src/lib/theme/config/types.ts +2 -2
- package/src/lib/theme/config/validator.ts +15 -91
- package/src/lib/theme/{constants.ts → constants/constants.ts} +0 -18
- package/src/lib/theme/constants/index.ts +8 -0
- package/src/lib/theme/core/ThemeRegistry.ts +19 -6
- package/src/lib/theme/core/__tests__/createTheme.test.ts +132 -0
- package/src/lib/theme/core/composeTheme.ts +155 -0
- package/src/lib/theme/core/createTheme.ts +94 -0
- package/src/lib/theme/{createTheme.ts → core/createThemeObject.ts} +10 -6
- package/src/lib/theme/core/index.ts +5 -19
- package/src/lib/theme/devtools/Comparator.tsx +346 -22
- package/src/lib/theme/devtools/IMPROVEMENTS.md +139 -38
- package/src/lib/theme/devtools/Inspector.tsx +335 -51
- package/src/lib/theme/devtools/LiveEditor.tsx +489 -112
- package/src/lib/theme/devtools/Preview.tsx +471 -221
- package/src/lib/theme/{core → devtools}/ThemeValidator.ts +6 -3
- package/src/lib/theme/devtools/index.ts +14 -4
- package/src/lib/theme/devtools/useHistory.ts +130 -0
- package/src/lib/theme/errors/index.ts +12 -0
- package/src/lib/theme/generators/cssFile.ts +79 -0
- package/src/lib/theme/generators/generateCSS.ts +89 -0
- package/src/lib/theme/{generateCSSVariables.ts → generators/generateCSSVariables.ts} +4 -14
- package/src/lib/theme/generators/index.ts +19 -0
- package/src/lib/theme/i18n/rtl.ts +7 -7
- package/src/lib/theme/index.ts +120 -15
- package/src/lib/theme/runtime/ThemeApplicator.ts +53 -95
- package/src/lib/theme/{ThemeContext.tsx → runtime/ThemeContext.tsx} +1 -1
- package/src/lib/theme/runtime/ThemeErrorBoundary.tsx +4 -4
- package/src/lib/theme/runtime/ThemeProvider.tsx +456 -179
- package/src/lib/theme/runtime/index.ts +1 -2
- package/src/lib/theme/runtime/useTheme.ts +1 -2
- package/src/lib/theme/test/testTheme.ts +385 -0
- package/src/lib/theme/tokens/index.ts +12 -0
- package/src/lib/theme/tokens/tokens.ts +721 -0
- package/src/lib/theme/types.ts +6 -42
- package/src/lib/theme/{utils.ts → utils/domUtils.ts} +2 -2
- package/src/lib/theme/utils/index.ts +11 -0
- package/src/lib/theme/utils/injectCSS.ts +90 -0
- package/src/lib/theme/utils/themeHelpers.ts +78 -0
- package/src/lib/theme/{themeUtils.ts → utils/themeUtils.ts} +1 -1
- package/src/lib/theme-tools.ts +8 -9
- package/src/lib/types/components.ts +93 -34
- package/src/lib/types/partProps.ts +0 -16
- package/src/lib/utils/componentUtils.ts +1 -1
- package/src/lib/utils/fontPreloader.ts +148 -0
- package/src/lib/utils/index.ts +11 -0
- package/src/lib/utils/memoryMonitor.ts +189 -0
- package/src/styles/01-settings/_settings.design-tokens.scss +4 -1
- package/src/styles/01-settings/_settings.fonts.scss +2 -5
- package/src/styles/02-tools/_tools.button.scss +66 -79
- package/src/styles/06-components/_components.atomix-glass.scss +13 -3
- package/src/styles/06-components/_components.navbar.scss +0 -6
- package/src/styles/06-components/_components.pagination.scss +88 -0
- package/scripts/build-themes.js +0 -208
- package/scripts/sync-theme-config.js +0 -309
- package/src/components/AtomixGlass/atomixGLass.old.tsx +0 -1263
- package/src/lib/theme/composeTheme.ts +0 -370
- package/src/lib/theme/core/ThemeCache.ts +0 -283
- package/src/lib/theme/core/ThemeEngine.test.ts +0 -146
- package/src/lib/theme/core/ThemeEngine.ts +0 -657
- package/src/lib/theme/createThemeFromConfig.ts +0 -132
- package/src/lib/theme/devtools/CLI.ts +0 -364
- package/src/lib/theme/runtime/ThemeManager.test.ts +0 -192
- package/src/lib/theme/runtime/ThemeManager.ts +0 -442
- package/src/styles/03-generic/_generated-root.css +0 -5
- package/src/themes/README.md +0 -442
- package/src/themes/themes.config.js +0 -35
- /package/src/lib/theme/{cssVariableMapper.ts → adapters/cssVariableMapper.ts} +0 -0
- /package/src/lib/theme/{errors.ts → errors/errors.ts} +0 -0
|
@@ -18,6 +18,11 @@ export function useChartScale(
|
|
|
18
18
|
return {
|
|
19
19
|
xScale: () => padding.left,
|
|
20
20
|
yScale: () => padding.top + innerHeight,
|
|
21
|
+
minValue: 0,
|
|
22
|
+
maxValue: 0,
|
|
23
|
+
valueRange: 0,
|
|
24
|
+
innerWidth,
|
|
25
|
+
innerHeight,
|
|
21
26
|
width,
|
|
22
27
|
height,
|
|
23
28
|
padding,
|
|
@@ -40,6 +45,11 @@ export function useChartScale(
|
|
|
40
45
|
return {
|
|
41
46
|
xScale,
|
|
42
47
|
yScale,
|
|
48
|
+
minValue,
|
|
49
|
+
maxValue,
|
|
50
|
+
valueRange,
|
|
51
|
+
innerWidth,
|
|
52
|
+
innerHeight,
|
|
43
53
|
width,
|
|
44
54
|
height,
|
|
45
55
|
padding,
|
|
@@ -97,10 +97,17 @@ export function useHero(initialProps?: Partial<HeroProps>): UseHeroResult {
|
|
|
97
97
|
const hasBackgroundSlider = !!defaultProps.backgroundSlider;
|
|
98
98
|
|
|
99
99
|
/**
|
|
100
|
-
* Initialize background slider hook
|
|
100
|
+
* Initialize background slider hook - always call hook, conditionally use result
|
|
101
101
|
*/
|
|
102
|
+
const backgroundSliderResult = useHeroBackgroundSlider(
|
|
103
|
+
defaultProps.backgroundSlider || {
|
|
104
|
+
slides: [],
|
|
105
|
+
autoplay: { delay: 5000, pauseOnHover: true }
|
|
106
|
+
}
|
|
107
|
+
);
|
|
108
|
+
|
|
102
109
|
const backgroundSlider = hasBackgroundSlider && defaultProps.backgroundSlider
|
|
103
|
-
?
|
|
110
|
+
? backgroundSliderResult
|
|
104
111
|
: undefined;
|
|
105
112
|
|
|
106
113
|
/**
|
|
@@ -89,8 +89,9 @@ export function useHeroBackgroundSlider(
|
|
|
89
89
|
|
|
90
90
|
// Play video if it's a video slide
|
|
91
91
|
const currentVideo = videoRefs[nextIndex]?.current;
|
|
92
|
-
|
|
93
|
-
|
|
92
|
+
const nextSlide = slides[nextIndex];
|
|
93
|
+
if (currentVideo && nextSlide && nextSlide.type === 'video') {
|
|
94
|
+
const videoOptions = nextSlide.videoOptions || {};
|
|
94
95
|
if (videoOptions.autoplay !== false) {
|
|
95
96
|
currentVideo.play().catch(() => {
|
|
96
97
|
// Ignore autoplay errors
|
|
@@ -100,7 +101,8 @@ export function useHeroBackgroundSlider(
|
|
|
100
101
|
|
|
101
102
|
// Pause previous video if it exists
|
|
102
103
|
const prevVideo = videoRefs[currentIndex]?.current;
|
|
103
|
-
|
|
104
|
+
const currentSlide = slides[currentIndex];
|
|
105
|
+
if (prevVideo && currentSlide && currentSlide.type === 'video') {
|
|
104
106
|
prevVideo.pause();
|
|
105
107
|
}
|
|
106
108
|
|
|
@@ -269,20 +269,10 @@ export function useNavDropdown(initialProps?: Partial<NavDropdownProps>) {
|
|
|
269
269
|
return isFixedBottom ? 'CaretUp' : 'CaretDown';
|
|
270
270
|
};
|
|
271
271
|
|
|
272
|
-
// Keeping this for backward compatibility
|
|
273
|
-
const getIconClass = (isMegaMenu: boolean = false): string => {
|
|
274
|
-
console.warn(
|
|
275
|
-
'getIconClass is deprecated. Please use the Icon component directly with the getIconName function.'
|
|
276
|
-
);
|
|
277
|
-
const isFixedBottom = isInFixedBottomNavbar();
|
|
278
|
-
return `c-nav__icon ${isFixedBottom ? 'icon-lux-caret-up' : 'icon-lux-caret-down'}`;
|
|
279
|
-
};
|
|
280
|
-
|
|
281
272
|
return {
|
|
282
273
|
defaultProps,
|
|
283
274
|
generateDropdownMenuClass,
|
|
284
275
|
isInFixedBottomNavbar,
|
|
285
|
-
getIconClass,
|
|
286
276
|
getIconName,
|
|
287
277
|
};
|
|
288
278
|
}
|
|
@@ -60,6 +60,7 @@ export function useSideMenu(initialProps?: Partial<SideMenuProps>) {
|
|
|
60
60
|
} else if (!shouldCollapse && wrapperRef.current) {
|
|
61
61
|
wrapperRef.current.style.height = 'auto';
|
|
62
62
|
}
|
|
63
|
+
return undefined;
|
|
63
64
|
}, []); // Only run on mount
|
|
64
65
|
|
|
65
66
|
// Handle responsive behavior - vertical collapse for both mobile and desktop
|
|
@@ -41,18 +41,19 @@ export function useVideoPlayer({
|
|
|
41
41
|
const [currentQuality, setCurrentQuality] = useState<VideoQuality | null>(quality?.[0] || null);
|
|
42
42
|
const [showControls, setShowControls] = useState(true);
|
|
43
43
|
|
|
44
|
-
const controlsTimeoutRef = useRef<NodeJS.Timeout>(null);
|
|
44
|
+
const controlsTimeoutRef = useRef<NodeJS.Timeout | null>(null);
|
|
45
45
|
|
|
46
46
|
const resetControlsTimeout = useCallback(() => {
|
|
47
47
|
if (controlsTimeoutRef.current) {
|
|
48
48
|
clearTimeout(controlsTimeoutRef.current);
|
|
49
49
|
}
|
|
50
50
|
setShowControls(true);
|
|
51
|
-
|
|
51
|
+
const timeout = setTimeout(() => {
|
|
52
52
|
if (isPlaying) {
|
|
53
53
|
setShowControls(false);
|
|
54
54
|
}
|
|
55
55
|
}, 3000);
|
|
56
|
+
controlsTimeoutRef.current = timeout;
|
|
56
57
|
}, [isPlaying]);
|
|
57
58
|
|
|
58
59
|
const play = useCallback(async () => {
|
package/src/lib/config/loader.ts
CHANGED
|
@@ -19,9 +19,10 @@ import type { AtomixConfig } from './index';
|
|
|
19
19
|
* @example
|
|
20
20
|
* ```typescript
|
|
21
21
|
* import { loadAtomixConfig } from '@shohojdhara/atomix/config';
|
|
22
|
+
* import { createTheme } from '@shohojdhara/atomix/theme';
|
|
22
23
|
*
|
|
23
24
|
* const config = loadAtomixConfig();
|
|
24
|
-
* const theme =
|
|
25
|
+
* const theme = createTheme(config.theme?.tokens || {});
|
|
25
26
|
* ```
|
|
26
27
|
*/
|
|
27
28
|
export function loadAtomixConfig(
|
|
@@ -76,26 +77,68 @@ export function loadAtomixConfig(
|
|
|
76
77
|
* Resolve config path
|
|
77
78
|
*
|
|
78
79
|
* Finds atomix.config.ts in the project, checking common locations.
|
|
80
|
+
* Returns null in browser environments where file system access is not available.
|
|
81
|
+
*
|
|
82
|
+
* This function is designed to work in Node.js environments only.
|
|
83
|
+
* In browser builds, it will always return null without attempting to access Node.js modules.
|
|
84
|
+
*
|
|
85
|
+
* @internal This function uses Node.js modules and should not be called in browser environments.
|
|
79
86
|
*/
|
|
80
87
|
export function resolveConfigPath(): string | null {
|
|
81
|
-
|
|
88
|
+
// Early return for browser environments - prevents any Node.js module access
|
|
89
|
+
// This check happens before any require() calls, preventing bundlers from analyzing them
|
|
90
|
+
if (typeof window !== 'undefined' || typeof process === 'undefined' || !process.cwd) {
|
|
82
91
|
return null;
|
|
83
92
|
}
|
|
84
93
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
+
// Only attempt to load Node.js modules in Node.js runtime
|
|
95
|
+
// Use a lazy-loading pattern that prevents static analysis by bundlers
|
|
96
|
+
try {
|
|
97
|
+
// Create a function that only executes in Node.js runtime
|
|
98
|
+
// Use string-based module names to prevent static analysis by bundlers
|
|
99
|
+
const loadNodeModules = () => {
|
|
100
|
+
// These requires are only executed at runtime in Node.js environments
|
|
101
|
+
// They are marked as external in Rollup config and should not be bundled
|
|
102
|
+
// Using string concatenation and computed property access to prevent static analysis
|
|
103
|
+
if (typeof require === 'undefined') {
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Use a try-catch wrapper to safely access require
|
|
108
|
+
try {
|
|
109
|
+
// Build module names dynamically to prevent static analysis
|
|
110
|
+
const moduleNames: [string, string] = ['f' + 's', 'p' + 'a' + 't' + 'h'];
|
|
111
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires
|
|
112
|
+
const fs = require(moduleNames[0]);
|
|
113
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires
|
|
114
|
+
const path = require(moduleNames[1]);
|
|
115
|
+
return { fs, path };
|
|
116
|
+
} catch {
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
};
|
|
94
120
|
|
|
95
|
-
|
|
96
|
-
if (
|
|
97
|
-
return
|
|
121
|
+
const modules = loadNodeModules();
|
|
122
|
+
if (!modules) {
|
|
123
|
+
return null;
|
|
98
124
|
}
|
|
125
|
+
|
|
126
|
+
const { fs, path } = modules;
|
|
127
|
+
const cwd = process.cwd();
|
|
128
|
+
const possiblePaths = [
|
|
129
|
+
path.join(cwd, 'atomix.config.ts'),
|
|
130
|
+
path.join(cwd, 'atomix.config.js'),
|
|
131
|
+
path.join(cwd, 'atomix.config.mjs'),
|
|
132
|
+
];
|
|
133
|
+
|
|
134
|
+
for (const configPath of possiblePaths) {
|
|
135
|
+
if (fs.existsSync(configPath)) {
|
|
136
|
+
return configPath;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
} catch (error) {
|
|
140
|
+
// Silently fail in browser environments or when modules are unavailable
|
|
141
|
+
return null;
|
|
99
142
|
}
|
|
100
143
|
|
|
101
144
|
return null;
|
|
@@ -48,6 +48,16 @@ export const BUTTON = {
|
|
|
48
48
|
},
|
|
49
49
|
};
|
|
50
50
|
|
|
51
|
+
/**
|
|
52
|
+
* ButtonGroup-specific constants
|
|
53
|
+
*/
|
|
54
|
+
export const BUTTON_GROUP = {
|
|
55
|
+
BASE_CLASS: 'c-btn-group',
|
|
56
|
+
CLASSES: {
|
|
57
|
+
BASE: 'c-btn-group',
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
|
|
51
61
|
/**
|
|
52
62
|
* Callout-specific constants
|
|
53
63
|
*/
|
package/src/lib/hooks/index.ts
CHANGED
|
@@ -9,8 +9,9 @@ import { useMemo } from 'react';
|
|
|
9
9
|
import { useTheme } from '../theme/runtime/useTheme';
|
|
10
10
|
import type { ComponentPartsMap } from '../types/partProps';
|
|
11
11
|
import type { ComponentCSSVariables } from '../constants/cssVariables';
|
|
12
|
-
import { mergeCSSVars } from '../theme/cssVariableMapper';
|
|
12
|
+
import { mergeCSSVars } from '../theme/adapters/cssVariableMapper';
|
|
13
13
|
import { mergePartStyles } from '../types/partProps';
|
|
14
|
+
import { mergeClassNames } from '../utils/componentUtils';
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
17
|
* Component names that support customization
|
|
@@ -22,7 +23,9 @@ export type ComponentName = keyof ComponentPartsMap;
|
|
|
22
23
|
*/
|
|
23
24
|
export interface CustomizableComponentProps<T extends ComponentName> {
|
|
24
25
|
/** CSS variable overrides */
|
|
25
|
-
cssVars?:
|
|
26
|
+
cssVars?: T extends keyof ComponentCSSVariables
|
|
27
|
+
? Partial<Record<ComponentCSSVariables[T], string | number>>
|
|
28
|
+
: Record<string, string | number>;
|
|
26
29
|
/** Part-based styling */
|
|
27
30
|
parts?: ComponentPartsMap[T];
|
|
28
31
|
/** Additional className */
|
|
@@ -72,17 +75,17 @@ export function useComponentCustomization<T extends ComponentName>(
|
|
|
72
75
|
|
|
73
76
|
// Merge CSS variables
|
|
74
77
|
const cssVars = useMemo(() => {
|
|
75
|
-
const themeVars = theme?.components?.[component]?.cssVars || {};
|
|
78
|
+
const themeVars = (theme as any)?.components?.[component]?.cssVars || {};
|
|
76
79
|
const propVars = props.cssVars || {};
|
|
77
80
|
return mergeCSSVars(themeVars, propVars as any);
|
|
78
81
|
}, [theme, component, props.cssVars]);
|
|
79
82
|
|
|
80
83
|
// Merge parts
|
|
81
84
|
const parts = useMemo(() => {
|
|
82
|
-
const themeParts = theme?.components?.[component]?.parts || {};
|
|
83
|
-
const propParts = props.parts || {}
|
|
85
|
+
const themeParts = (theme as any)?.components?.[component]?.parts || {};
|
|
86
|
+
const propParts = (props.parts || {}) as Record<string, any>;
|
|
84
87
|
|
|
85
|
-
const merged: any = {};
|
|
88
|
+
const merged: Record<string, any> = {};
|
|
86
89
|
const allPartNames = new Set([
|
|
87
90
|
...Object.keys(themeParts),
|
|
88
91
|
...Object.keys(propParts),
|
|
@@ -100,7 +103,7 @@ export function useComponentCustomization<T extends ComponentName>(
|
|
|
100
103
|
|
|
101
104
|
// Merge className
|
|
102
105
|
const className = useMemo(() => {
|
|
103
|
-
const themeClassName = theme?.components?.[component]?.className || '';
|
|
106
|
+
const themeClassName = (theme as any)?.components?.[component]?.className || '';
|
|
104
107
|
const propClassName = props.className || '';
|
|
105
108
|
return [themeClassName, propClassName].filter(Boolean).join(' ');
|
|
106
109
|
}, [theme, component, props.className]);
|
|
@@ -136,7 +139,7 @@ export function useComponentDefaultProps<T extends ComponentName>(
|
|
|
136
139
|
const { theme } = useTheme();
|
|
137
140
|
|
|
138
141
|
return useMemo(() => {
|
|
139
|
-
return theme?.components?.[component]?.defaultProps || {};
|
|
142
|
+
return (theme as any)?.components?.[component]?.defaultProps || {};
|
|
140
143
|
}, [theme, component]);
|
|
141
144
|
}
|
|
142
145
|
|
|
@@ -152,13 +155,6 @@ export function useMergedProps<T extends Record<string, any>>(
|
|
|
152
155
|
}, [defaultProps, props]);
|
|
153
156
|
}
|
|
154
157
|
|
|
155
|
-
/**
|
|
156
|
-
* Utility to create className from parts
|
|
157
|
-
*/
|
|
158
|
-
export function mergeClassNames(...classNames: Array<string | undefined | null | false>): string {
|
|
159
|
-
return classNames.filter(Boolean).join(' ');
|
|
160
|
-
}
|
|
161
|
-
|
|
162
158
|
/**
|
|
163
159
|
* Utility to apply CSS variables to style object
|
|
164
160
|
*/
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Performance Monitoring Hook
|
|
3
|
+
*
|
|
4
|
+
* Tracks component render times and re-render counts
|
|
5
|
+
* for performance analysis and optimization
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { useEffect, useRef } from 'react';
|
|
9
|
+
|
|
10
|
+
export interface PerformanceMetrics {
|
|
11
|
+
/**
|
|
12
|
+
* Component name
|
|
13
|
+
*/
|
|
14
|
+
componentName: string;
|
|
15
|
+
/**
|
|
16
|
+
* Number of renders
|
|
17
|
+
*/
|
|
18
|
+
renderCount: number;
|
|
19
|
+
/**
|
|
20
|
+
* Average render time in milliseconds
|
|
21
|
+
*/
|
|
22
|
+
averageRenderTime: number;
|
|
23
|
+
/**
|
|
24
|
+
* Total render time in milliseconds
|
|
25
|
+
*/
|
|
26
|
+
totalRenderTime: number;
|
|
27
|
+
/**
|
|
28
|
+
* Maximum render time in milliseconds
|
|
29
|
+
*/
|
|
30
|
+
maxRenderTime: number;
|
|
31
|
+
/**
|
|
32
|
+
* Minimum render time in milliseconds
|
|
33
|
+
*/
|
|
34
|
+
minRenderTime: number;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Options for performance monitoring
|
|
39
|
+
*/
|
|
40
|
+
export interface UsePerformanceMonitorOptions {
|
|
41
|
+
/**
|
|
42
|
+
* Component name to track
|
|
43
|
+
*/
|
|
44
|
+
componentName: string;
|
|
45
|
+
/**
|
|
46
|
+
* Whether to log metrics to console (development only)
|
|
47
|
+
*/
|
|
48
|
+
logToConsole?: boolean;
|
|
49
|
+
/**
|
|
50
|
+
* Threshold in milliseconds to warn about slow renders
|
|
51
|
+
*/
|
|
52
|
+
warnThreshold?: number;
|
|
53
|
+
/**
|
|
54
|
+
* Callback to report metrics (e.g., to analytics)
|
|
55
|
+
*/
|
|
56
|
+
onMetrics?: (metrics: PerformanceMetrics) => void;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Hook to monitor component performance
|
|
61
|
+
*
|
|
62
|
+
* @param options - Performance monitoring options
|
|
63
|
+
* @returns Performance metrics
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* ```tsx
|
|
67
|
+
* function MyComponent() {
|
|
68
|
+
* usePerformanceMonitor({
|
|
69
|
+
* componentName: 'MyComponent',
|
|
70
|
+
* warnThreshold: 16, // Warn if render takes > 16ms (1 frame)
|
|
71
|
+
* });
|
|
72
|
+
*
|
|
73
|
+
* return <div>Content</div>;
|
|
74
|
+
* }
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
77
|
+
export function usePerformanceMonitor(options: UsePerformanceMonitorOptions) {
|
|
78
|
+
const {
|
|
79
|
+
componentName,
|
|
80
|
+
logToConsole = process.env.NODE_ENV === 'development',
|
|
81
|
+
warnThreshold = 16,
|
|
82
|
+
onMetrics,
|
|
83
|
+
} = options;
|
|
84
|
+
|
|
85
|
+
const metricsRef = useRef<PerformanceMetrics>({
|
|
86
|
+
componentName,
|
|
87
|
+
renderCount: 0,
|
|
88
|
+
averageRenderTime: 0,
|
|
89
|
+
totalRenderTime: 0,
|
|
90
|
+
maxRenderTime: 0,
|
|
91
|
+
minRenderTime: Infinity,
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
const renderStartRef = useRef<number>(0);
|
|
95
|
+
|
|
96
|
+
useEffect(() => {
|
|
97
|
+
// Start timing the render
|
|
98
|
+
renderStartRef.current = performance.now();
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
useEffect(() => {
|
|
102
|
+
// Calculate render time
|
|
103
|
+
const renderTime = performance.now() - renderStartRef.current;
|
|
104
|
+
const metrics = metricsRef.current;
|
|
105
|
+
|
|
106
|
+
// Update metrics
|
|
107
|
+
metrics.renderCount += 1;
|
|
108
|
+
metrics.totalRenderTime += renderTime;
|
|
109
|
+
metrics.averageRenderTime = metrics.totalRenderTime / metrics.renderCount;
|
|
110
|
+
metrics.maxRenderTime = Math.max(metrics.maxRenderTime, renderTime);
|
|
111
|
+
metrics.minRenderTime = Math.min(metrics.minRenderTime, renderTime);
|
|
112
|
+
|
|
113
|
+
// Warn if render is slow
|
|
114
|
+
if (renderTime > warnThreshold && logToConsole) {
|
|
115
|
+
console.warn(
|
|
116
|
+
`[Performance] ${componentName} render took ${renderTime.toFixed(2)}ms ` +
|
|
117
|
+
`(threshold: ${warnThreshold}ms)`
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Log metrics in development
|
|
122
|
+
if (logToConsole && metrics.renderCount % 10 === 0) {
|
|
123
|
+
console.log(`[Performance] ${componentName} metrics:`, {
|
|
124
|
+
renderCount: metrics.renderCount,
|
|
125
|
+
averageRenderTime: metrics.averageRenderTime.toFixed(2) + 'ms',
|
|
126
|
+
maxRenderTime: metrics.maxRenderTime.toFixed(2) + 'ms',
|
|
127
|
+
minRenderTime: metrics.minRenderTime.toFixed(2) + 'ms',
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Report metrics via callback
|
|
132
|
+
if (onMetrics) {
|
|
133
|
+
onMetrics({ ...metrics });
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
return metricsRef.current;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Get all performance metrics for all monitored components
|
|
142
|
+
* (useful for debugging and analytics)
|
|
143
|
+
*/
|
|
144
|
+
export function getPerformanceMetrics(): PerformanceMetrics[] {
|
|
145
|
+
// This would need to be implemented with a global store
|
|
146
|
+
// For now, this is a placeholder
|
|
147
|
+
return [];
|
|
148
|
+
}
|
|
149
|
+
|
|
@@ -11,7 +11,7 @@ export {
|
|
|
11
11
|
createSlotComponent,
|
|
12
12
|
useSlot,
|
|
13
13
|
createSlotProps,
|
|
14
|
-
} from './slots
|
|
14
|
+
} from './slots';
|
|
15
15
|
|
|
16
16
|
export type {
|
|
17
17
|
SlotProps,
|
|
@@ -32,4 +32,4 @@ export type {
|
|
|
32
32
|
DropdownRootSlotProps,
|
|
33
33
|
DropdownToggleSlotProps,
|
|
34
34
|
DropdownMenuSlotProps,
|
|
35
|
-
} from './slots
|
|
35
|
+
} from './slots';
|
|
@@ -59,7 +59,7 @@ export function renderSlot<T>(
|
|
|
59
59
|
// Priority 2: component
|
|
60
60
|
if (slotObj.component) {
|
|
61
61
|
const Component = slotObj.component;
|
|
62
|
-
return <Component {...props} />;
|
|
62
|
+
return <Component {...(props as any)} />;
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
// Priority 3: children
|
|
@@ -133,7 +133,7 @@ export function createSlotComponent<T>(
|
|
|
133
133
|
return <Element {...(props as any)}>{children}</Element>;
|
|
134
134
|
}
|
|
135
135
|
|
|
136
|
-
const Component = defaultElement
|
|
136
|
+
const Component = defaultElement as React.ComponentType<any>;
|
|
137
137
|
return <Component {...slotProps}>{children}</Component>;
|
|
138
138
|
};
|
|
139
139
|
}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# Theme System File Organization
|
|
2
|
+
|
|
3
|
+
## Directory Structure
|
|
4
|
+
|
|
5
|
+
The theme system is organized following senior developer best practices with clear separation of concerns and logical grouping:
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
theme/
|
|
9
|
+
├── index.ts # Main public API exports
|
|
10
|
+
├── README.md # This file
|
|
11
|
+
│
|
|
12
|
+
├── core/ # Core theme engine
|
|
13
|
+
│ ├── index.ts
|
|
14
|
+
│ ├── createTheme.ts # Main createTheme function (unified)
|
|
15
|
+
│ ├── createThemeObject.ts # Theme object creation
|
|
16
|
+
│ ├── composeTheme.ts # Theme composition and merging
|
|
17
|
+
│ └── ThemeRegistry.ts # Theme registration and discovery
|
|
18
|
+
│
|
|
19
|
+
├── adapters/ # Adapters and converters
|
|
20
|
+
│ ├── index.ts
|
|
21
|
+
│ ├── themeAdapter.ts # Theme ↔ DesignTokens conversion
|
|
22
|
+
│ └── cssVariableMapper.ts # CSS variable mapping utilities
|
|
23
|
+
│
|
|
24
|
+
├── generators/ # Code generation
|
|
25
|
+
│ ├── index.ts
|
|
26
|
+
│ ├── generateCSS.ts # CSS variable generation (simple)
|
|
27
|
+
│ ├── generateCSSVariables.ts # CSS variable generation (advanced)
|
|
28
|
+
│ └── cssFile.ts # File operations (save CSS to disk)
|
|
29
|
+
│
|
|
30
|
+
├── runtime/ # React runtime components
|
|
31
|
+
│ ├── index.ts
|
|
32
|
+
│ ├── ThemeProvider.tsx # Main theme provider component
|
|
33
|
+
│ ├── ThemeApplicator.ts # Theme application logic
|
|
34
|
+
│ ├── ThemeErrorBoundary.tsx # Error boundary for themes
|
|
35
|
+
│ ├── ThemeContext.tsx # React context for theme management
|
|
36
|
+
│ └── useTheme.ts # React hook for theme access
|
|
37
|
+
│
|
|
38
|
+
├── config/ # Configuration
|
|
39
|
+
│ ├── index.ts
|
|
40
|
+
│ ├── loader.ts # Config file loader
|
|
41
|
+
│ ├── configLoader.ts # Config loading from atomix.config.ts
|
|
42
|
+
│ ├── types.ts # Config type definitions
|
|
43
|
+
│ └── validator.ts # Config validation
|
|
44
|
+
│
|
|
45
|
+
├── utils/ # Utilities
|
|
46
|
+
│ ├── index.ts
|
|
47
|
+
│ ├── themeHelpers.ts # Type guards and DesignTokens utilities
|
|
48
|
+
│ ├── themeUtils.ts # Theme value manipulation (colors, spacing)
|
|
49
|
+
│ ├── domUtils.ts # DOM/browser utilities
|
|
50
|
+
│ └── injectCSS.ts # CSS injection utilities
|
|
51
|
+
│
|
|
52
|
+
├── tokens/ # Design tokens
|
|
53
|
+
│ ├── index.ts
|
|
54
|
+
│ └── tokens.ts # Design tokens definitions
|
|
55
|
+
│
|
|
56
|
+
├── constants/ # Constants
|
|
57
|
+
│ ├── index.ts
|
|
58
|
+
│ └── constants.ts # System constants and default values
|
|
59
|
+
│
|
|
60
|
+
├── errors/ # Error handling
|
|
61
|
+
│ ├── index.ts
|
|
62
|
+
│ └── errors.ts # Error classes and error handling
|
|
63
|
+
│
|
|
64
|
+
├── devtools/ # Development tools
|
|
65
|
+
│ ├── index.ts
|
|
66
|
+
│ ├── CLI.ts # Command-line interface
|
|
67
|
+
│ ├── Comparator.tsx # Theme comparison
|
|
68
|
+
│ ├── Inspector.tsx # Theme inspector
|
|
69
|
+
│ ├── LiveEditor.tsx # Live theme editor
|
|
70
|
+
│ ├── Preview.tsx # Theme preview
|
|
71
|
+
│ ├── ThemeValidator.tsx # Theme validation component
|
|
72
|
+
│ └── README.md # DevTools documentation
|
|
73
|
+
│
|
|
74
|
+
├── i18n/ # Internationalization
|
|
75
|
+
│ ├── index.ts
|
|
76
|
+
│ └── rtl.ts # Right-to-left language support
|
|
77
|
+
│
|
|
78
|
+
└── types.ts # TypeScript type definitions (root level)
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Naming Conventions
|
|
82
|
+
|
|
83
|
+
### Files and Directories
|
|
84
|
+
|
|
85
|
+
1. **React Components**: Always use PascalCase (e.g., `ThemeProvider.tsx`)
|
|
86
|
+
2. **Feature Modules**: Use camelCase with descriptive names (e.g., `themeAdapter.ts`)
|
|
87
|
+
3. **Base Utilities**: Use lowercase for foundational utilities (e.g., `domUtils.ts`, `types.ts`)
|
|
88
|
+
4. **Directories**: Use lowercase, plural for collections (e.g., `utils/`, `adapters/`)
|
|
89
|
+
5. **Index Files**: Each subdirectory has an `index.ts` for clean exports
|
|
90
|
+
|
|
91
|
+
### Import Patterns
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
// ✅ CORRECT - Import from organized directories
|
|
95
|
+
import { createTheme } from './core';
|
|
96
|
+
import { themeToDesignTokens } from './adapters';
|
|
97
|
+
import { generateCSSVariables } from './generators';
|
|
98
|
+
import { isDesignTokens } from './utils/themeHelpers';
|
|
99
|
+
|
|
100
|
+
// ❌ INCORRECT - Direct imports from root (old structure)
|
|
101
|
+
import { createTheme } from './core';
|
|
102
|
+
import { themeToDesignTokens } from './themeAdapter';
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## File Organization Principles
|
|
106
|
+
|
|
107
|
+
1. **Separation of Concerns**: Each directory has a single, well-defined responsibility
|
|
108
|
+
2. **Logical Grouping**: Related functionality is grouped together
|
|
109
|
+
3. **Clear Entry Points**: Each directory has an `index.ts` for clean exports
|
|
110
|
+
4. **Consistent Naming**: Follow established naming conventions throughout
|
|
111
|
+
5. **Maintainability**: Structure makes it easy to find and modify code
|
|
112
|
+
|
|
113
|
+
## Directory Responsibilities
|
|
114
|
+
|
|
115
|
+
### `/core` - Core Theme Engine
|
|
116
|
+
- Theme creation and composition
|
|
117
|
+
- Theme registry and discovery
|
|
118
|
+
- Core theme logic
|
|
119
|
+
|
|
120
|
+
### `/adapters` - Adapters and Converters
|
|
121
|
+
- Convert between Theme objects and DesignTokens
|
|
122
|
+
- CSS variable mapping utilities
|
|
123
|
+
- Format transformations
|
|
124
|
+
|
|
125
|
+
### `/generators` - Code Generation
|
|
126
|
+
- CSS variable generation
|
|
127
|
+
- File operations (save CSS to disk)
|
|
128
|
+
- Code generation utilities
|
|
129
|
+
|
|
130
|
+
### `/runtime` - React Runtime
|
|
131
|
+
- React components (ThemeProvider, ThemeErrorBoundary)
|
|
132
|
+
- React hooks (useTheme)
|
|
133
|
+
- Theme application logic
|
|
134
|
+
- React context
|
|
135
|
+
|
|
136
|
+
### `/config` - Configuration
|
|
137
|
+
- Config file loading
|
|
138
|
+
- Config validation
|
|
139
|
+
- Configuration types
|
|
140
|
+
|
|
141
|
+
### `/utils` - Utilities
|
|
142
|
+
- Theme helpers and type guards
|
|
143
|
+
- Theme value manipulation
|
|
144
|
+
- DOM/browser utilities
|
|
145
|
+
- CSS injection
|
|
146
|
+
|
|
147
|
+
### `/tokens` - Design Tokens
|
|
148
|
+
- Design token definitions
|
|
149
|
+
- Token creation utilities
|
|
150
|
+
|
|
151
|
+
### `/constants` - Constants
|
|
152
|
+
- System constants
|
|
153
|
+
- Default values
|
|
154
|
+
|
|
155
|
+
### `/errors` - Error Handling
|
|
156
|
+
- Error classes
|
|
157
|
+
- Error handling utilities
|
|
158
|
+
|
|
159
|
+
### `/devtools` - Development Tools
|
|
160
|
+
- Development and debugging tools
|
|
161
|
+
- Theme inspection and validation
|
|
162
|
+
- Live editing capabilities
|
|
163
|
+
|
|
164
|
+
### `/i18n` - Internationalization
|
|
165
|
+
- RTL (Right-to-Left) support
|
|
166
|
+
- Internationalization utilities
|
|
167
|
+
|
|
168
|
+
## Best Practices
|
|
169
|
+
|
|
170
|
+
1. **Always use index files**: Import from directory index files when possible
|
|
171
|
+
2. **Group related functionality**: Keep related code together
|
|
172
|
+
3. **Maintain clear boundaries**: Don't mix concerns between directories
|
|
173
|
+
4. **Follow naming conventions**: Consistency is key
|
|
174
|
+
5. **Document changes**: Update this README when structure changes
|