meno-core 1.0.0
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/bin/cli.ts +281 -0
- package/build-static.ts +298 -0
- package/bunfig.toml +39 -0
- package/entries/client-router.tsx +111 -0
- package/entries/server-router.tsx +71 -0
- package/lib/client/ClientInitializer.test.ts +9 -0
- package/lib/client/ClientInitializer.test.ts.skip +92 -0
- package/lib/client/ClientInitializer.ts +60 -0
- package/lib/client/ErrorBoundary.test.tsx +595 -0
- package/lib/client/ErrorBoundary.tsx +230 -0
- package/lib/client/componentRegistry.test.ts +165 -0
- package/lib/client/componentRegistry.ts +18 -0
- package/lib/client/contexts/ThemeContext.tsx +73 -0
- package/lib/client/core/ComponentBuilder.test.ts +677 -0
- package/lib/client/core/ComponentBuilder.ts +660 -0
- package/lib/client/core/ComponentRenderer.test.tsx +176 -0
- package/lib/client/core/ComponentRenderer.tsx +83 -0
- package/lib/client/core/cmsTemplateProcessor.ts +129 -0
- package/lib/client/elementRegistry.ts +81 -0
- package/lib/client/hmr/HMRManager.tsx +179 -0
- package/lib/client/hmr/index.ts +5 -0
- package/lib/client/hmrWebSocket.test.ts +9 -0
- package/lib/client/hmrWebSocket.ts +250 -0
- package/lib/client/hooks/useColorVariables.test.ts +166 -0
- package/lib/client/hooks/useColorVariables.ts +249 -0
- package/lib/client/hooks/usePropertyAutocomplete.test.ts +9 -0
- package/lib/client/hooks/usePropertyAutocomplete.ts +40 -0
- package/lib/client/hydration/HydrationUtils.test.ts +154 -0
- package/lib/client/hydration/HydrationUtils.ts +35 -0
- package/lib/client/i18nConfigService.test.ts +74 -0
- package/lib/client/i18nConfigService.ts +78 -0
- package/lib/client/index.ts +56 -0
- package/lib/client/navigation.test.ts +441 -0
- package/lib/client/navigation.ts +23 -0
- package/lib/client/responsiveStyleResolver.test.ts +491 -0
- package/lib/client/responsiveStyleResolver.ts +184 -0
- package/lib/client/routing/RouteLoader.test.ts +635 -0
- package/lib/client/routing/RouteLoader.ts +347 -0
- package/lib/client/routing/Router.tsx +382 -0
- package/lib/client/scripts/ScriptExecutor.test.ts +489 -0
- package/lib/client/scripts/ScriptExecutor.ts +171 -0
- package/lib/client/scripts/formHandler.ts +103 -0
- package/lib/client/styleProcessor.test.ts +126 -0
- package/lib/client/styleProcessor.ts +92 -0
- package/lib/client/styles/StyleInjector.test.ts +354 -0
- package/lib/client/styles/StyleInjector.ts +154 -0
- package/lib/client/templateEngine.test.ts +660 -0
- package/lib/client/templateEngine.ts +667 -0
- package/lib/client/theme.test.ts +173 -0
- package/lib/client/theme.ts +159 -0
- package/lib/client/utils/toast.ts +46 -0
- package/lib/server/createServer.ts +170 -0
- package/lib/server/cssGenerator.test.ts +172 -0
- package/lib/server/cssGenerator.ts +58 -0
- package/lib/server/fileWatcher.ts +134 -0
- package/lib/server/index.ts +55 -0
- package/lib/server/jsonLoader.test.ts +103 -0
- package/lib/server/jsonLoader.ts +350 -0
- package/lib/server/middleware/cors.test.ts +177 -0
- package/lib/server/middleware/cors.ts +69 -0
- package/lib/server/middleware/errorHandler.test.ts +208 -0
- package/lib/server/middleware/errorHandler.ts +63 -0
- package/lib/server/middleware/index.ts +9 -0
- package/lib/server/middleware/logger.test.ts +233 -0
- package/lib/server/middleware/logger.ts +99 -0
- package/lib/server/pageCache.test.ts +167 -0
- package/lib/server/pageCache.ts +97 -0
- package/lib/server/projectContext.ts +51 -0
- package/lib/server/providers/fileSystemCMSProvider.test.ts +292 -0
- package/lib/server/providers/fileSystemCMSProvider.ts +227 -0
- package/lib/server/providers/fileSystemPageProvider.ts +83 -0
- package/lib/server/routes/api/cms.test.ts +177 -0
- package/lib/server/routes/api/cms.ts +82 -0
- package/lib/server/routes/api/colors.ts +59 -0
- package/lib/server/routes/api/components.ts +70 -0
- package/lib/server/routes/api/config.test.ts +9 -0
- package/lib/server/routes/api/config.ts +28 -0
- package/lib/server/routes/api/core-routes.ts +182 -0
- package/lib/server/routes/api/functions.ts +170 -0
- package/lib/server/routes/api/index.ts +69 -0
- package/lib/server/routes/api/pages.ts +95 -0
- package/lib/server/routes/api/shared.test.ts +81 -0
- package/lib/server/routes/api/shared.ts +31 -0
- package/lib/server/routes/editor.test.ts +9 -0
- package/lib/server/routes/index.ts +104 -0
- package/lib/server/routes/pages.ts +161 -0
- package/lib/server/routes/static.ts +107 -0
- package/lib/server/services/ColorService.ts +193 -0
- package/lib/server/services/cmsService.test.ts +388 -0
- package/lib/server/services/cmsService.ts +296 -0
- package/lib/server/services/componentService.test.ts +276 -0
- package/lib/server/services/componentService.ts +346 -0
- package/lib/server/services/configService.ts +156 -0
- package/lib/server/services/fileWatcherService.ts +67 -0
- package/lib/server/services/index.ts +10 -0
- package/lib/server/services/pageService.test.ts +258 -0
- package/lib/server/services/pageService.ts +240 -0
- package/lib/server/ssrRenderer.test.ts +1005 -0
- package/lib/server/ssrRenderer.ts +878 -0
- package/lib/server/utilityClassGenerator.ts +11 -0
- package/lib/server/utils/index.ts +5 -0
- package/lib/server/utils/jsonLineMapper.test.ts +100 -0
- package/lib/server/utils/jsonLineMapper.ts +166 -0
- package/lib/server/validateStyleCoverage.test.ts +9 -0
- package/lib/server/validateStyleCoverage.ts +167 -0
- package/lib/server/websocketManager.test.ts +9 -0
- package/lib/server/websocketManager.ts +95 -0
- package/lib/shared/attributeNodeUtils.test.ts +152 -0
- package/lib/shared/attributeNodeUtils.ts +50 -0
- package/lib/shared/breakpoints.test.ts +166 -0
- package/lib/shared/breakpoints.ts +65 -0
- package/lib/shared/colorProperties.test.ts +111 -0
- package/lib/shared/colorProperties.ts +40 -0
- package/lib/shared/colorVariableUtils.test.ts +319 -0
- package/lib/shared/colorVariableUtils.ts +97 -0
- package/lib/shared/constants.test.ts +175 -0
- package/lib/shared/constants.ts +116 -0
- package/lib/shared/cssGeneration.ts +481 -0
- package/lib/shared/cssProperties.test.ts +252 -0
- package/lib/shared/cssProperties.ts +338 -0
- package/lib/shared/elementUtils.test.ts +245 -0
- package/lib/shared/elementUtils.ts +90 -0
- package/lib/shared/fontLoader.ts +97 -0
- package/lib/shared/i18n.test.ts +313 -0
- package/lib/shared/i18n.ts +286 -0
- package/lib/shared/index.ts +50 -0
- package/lib/shared/interfaces/contentProvider.test.ts +9 -0
- package/lib/shared/interfaces/contentProvider.ts +121 -0
- package/lib/shared/nodeUtils.test.ts +320 -0
- package/lib/shared/nodeUtils.ts +220 -0
- package/lib/shared/pathArrayUtils.test.ts +315 -0
- package/lib/shared/pathArrayUtils.ts +17 -0
- package/lib/shared/pathUtils.test.ts +260 -0
- package/lib/shared/pathUtils.ts +244 -0
- package/lib/shared/paths/Path.test.ts +74 -0
- package/lib/shared/paths/Path.ts +23 -0
- package/lib/shared/paths/PathConverter.test.ts +232 -0
- package/lib/shared/paths/PathConverter.ts +141 -0
- package/lib/shared/paths/PathUtils.ts +290 -0
- package/lib/shared/paths/PathValidator.test.ts +193 -0
- package/lib/shared/paths/PathValidator.ts +53 -0
- package/lib/shared/paths/index.ts +48 -0
- package/lib/shared/propResolver.test.ts +639 -0
- package/lib/shared/propResolver.ts +124 -0
- package/lib/shared/registry/BaseNodeTypeRegistry.test.ts +190 -0
- package/lib/shared/registry/BaseNodeTypeRegistry.ts +200 -0
- package/lib/shared/registry/ClientNodeTypeRegistry.ts +34 -0
- package/lib/shared/registry/ClientRegistry.test.ts +26 -0
- package/lib/shared/registry/ClientRegistry.ts +15 -0
- package/lib/shared/registry/ComponentRegistry.test.ts +293 -0
- package/lib/shared/registry/ComponentRegistry.ts +100 -0
- package/lib/shared/registry/NodeTypeDefinition.ts +198 -0
- package/lib/shared/registry/NodeTypeManager.ts +94 -0
- package/lib/shared/registry/RegistryManager.test.ts +58 -0
- package/lib/shared/registry/RegistryManager.ts +60 -0
- package/lib/shared/registry/SSRNodeTypeRegistry.ts +33 -0
- package/lib/shared/registry/SSRRegistry.test.ts +26 -0
- package/lib/shared/registry/SSRRegistry.ts +15 -0
- package/lib/shared/registry/createNodeType.ts +175 -0
- package/lib/shared/registry/defineNodeType.ts +73 -0
- package/lib/shared/registry/fieldPresets.ts +109 -0
- package/lib/shared/registry/index.ts +50 -0
- package/lib/shared/registry/nodeTypes/ComponentInstanceNodeType.ts +71 -0
- package/lib/shared/registry/nodeTypes/EmbedNodeType.ts +61 -0
- package/lib/shared/registry/nodeTypes/HtmlNodeType.ts +88 -0
- package/lib/shared/registry/nodeTypes/LocaleListNodeType.ts +66 -0
- package/lib/shared/registry/nodeTypes/ObjectLinkNodeType.ts +75 -0
- package/lib/shared/registry/nodeTypes/SlotMarkerType.ts +49 -0
- package/lib/shared/registry/nodeTypes/TextNodeType.ts +52 -0
- package/lib/shared/registry/nodeTypes/index.ts +75 -0
- package/lib/shared/responsiveScaling.test.ts +268 -0
- package/lib/shared/responsiveScaling.ts +194 -0
- package/lib/shared/responsiveStyleUtils.test.ts +300 -0
- package/lib/shared/responsiveStyleUtils.ts +139 -0
- package/lib/shared/slugTranslator.test.ts +325 -0
- package/lib/shared/slugTranslator.ts +177 -0
- package/lib/shared/styleNodeUtils.test.ts +132 -0
- package/lib/shared/styleNodeUtils.ts +102 -0
- package/lib/shared/styleUtils.test.ts +238 -0
- package/lib/shared/styleUtils.ts +63 -0
- package/lib/shared/themeDefaults.test.ts +113 -0
- package/lib/shared/themeDefaults.ts +103 -0
- package/lib/shared/tree/PathBuilder.ts +383 -0
- package/lib/shared/treePathUtils.test.ts +539 -0
- package/lib/shared/treePathUtils.ts +339 -0
- package/lib/shared/types/api.ts +58 -0
- package/lib/shared/types/cms.ts +95 -0
- package/lib/shared/types/colors.ts +45 -0
- package/lib/shared/types/components.ts +121 -0
- package/lib/shared/types/errors.test.ts +103 -0
- package/lib/shared/types/errors.ts +69 -0
- package/lib/shared/types/index.ts +96 -0
- package/lib/shared/types/nodes.ts +20 -0
- package/lib/shared/types/rendering.ts +61 -0
- package/lib/shared/types/styles.ts +38 -0
- package/lib/shared/types.ts +11 -0
- package/lib/shared/utilityClassConfig.ts +287 -0
- package/lib/shared/utilityClassMapper.test.ts +140 -0
- package/lib/shared/utilityClassMapper.ts +229 -0
- package/lib/shared/utils/fileUtils.test.ts +99 -0
- package/lib/shared/utils/fileUtils.ts +56 -0
- package/lib/shared/utils.test.ts +261 -0
- package/lib/shared/utils.ts +84 -0
- package/lib/shared/validation/index.ts +7 -0
- package/lib/shared/validation/propValidator.test.ts +178 -0
- package/lib/shared/validation/propValidator.ts +238 -0
- package/lib/shared/validation/schemas.test.ts +177 -0
- package/lib/shared/validation/schemas.ts +401 -0
- package/lib/shared/validation/validators.test.ts +109 -0
- package/lib/shared/validation/validators.ts +304 -0
- package/lib/test-utils/dom-setup.ts +55 -0
- package/lib/test-utils/factories/ConsoleMockFactory.ts +200 -0
- package/lib/test-utils/factories/DomMockFactory.ts +487 -0
- package/lib/test-utils/factories/EventMockFactory.ts +244 -0
- package/lib/test-utils/factories/FetchMockFactory.ts +210 -0
- package/lib/test-utils/factories/ServerMockFactory.ts +223 -0
- package/lib/test-utils/factories/StoreMockFactory.ts +370 -0
- package/lib/test-utils/factories/index.ts +11 -0
- package/lib/test-utils/fixtures.ts +134 -0
- package/lib/test-utils/helpers/asyncHelpers.test.ts +112 -0
- package/lib/test-utils/helpers/asyncHelpers.ts +196 -0
- package/lib/test-utils/helpers/index.ts +6 -0
- package/lib/test-utils/helpers.test.ts +73 -0
- package/lib/test-utils/helpers.ts +90 -0
- package/lib/test-utils/index.ts +17 -0
- package/lib/test-utils/mockFactories.ts +92 -0
- package/lib/test-utils/mocks.ts +341 -0
- package/package.json +38 -0
- package/templates/index-router.html +34 -0
- package/tsconfig.json +14 -0
- package/vite.config.ts +43 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utilities for working with styles in ComponentNode structures
|
|
3
|
+
* Provides helper functions for applying and merging styles based on node type
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { ComponentNode, HtmlNode, ComponentInstanceNode, StyleValue, ResponsiveStyleObject } from './types';
|
|
7
|
+
import { isComponentNode, isHtmlNode, isEmbedNode } from './nodeUtils';
|
|
8
|
+
import { isResponsiveStyle } from './styleUtils';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Apply styles to a ComponentNode based on its type
|
|
12
|
+
* - HTML nodes: styles go at top level (node.style)
|
|
13
|
+
* - Component instances: styles go in props.style
|
|
14
|
+
*/
|
|
15
|
+
export function applyStylesToNode(
|
|
16
|
+
node: ComponentNode,
|
|
17
|
+
styles: StyleValue | Record<string, string | number> | null | undefined,
|
|
18
|
+
viewportWidth?: number
|
|
19
|
+
): ComponentNode {
|
|
20
|
+
if (!styles) return node;
|
|
21
|
+
|
|
22
|
+
if (isComponentNode(node)) {
|
|
23
|
+
// Component instance: put styles in props.style
|
|
24
|
+
if (!node.props) {
|
|
25
|
+
node.props = {};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
node.props.style = { ...(node.props.style || {}), ...styles };
|
|
29
|
+
} else if (isHtmlNode(node) || isEmbedNode(node)) {
|
|
30
|
+
// HTML node and Embed node: put styles at top level
|
|
31
|
+
node.style = styles as StyleValue;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return node;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Merge instance styles with structure styles for a ComponentNode
|
|
39
|
+
* Instance styles override structure styles
|
|
40
|
+
*/
|
|
41
|
+
export function mergeNodeStyles(
|
|
42
|
+
node: ComponentNode,
|
|
43
|
+
instanceStyles: StyleValue | Record<string, string | number> | null | undefined,
|
|
44
|
+
viewportWidth?: number
|
|
45
|
+
): ComponentNode {
|
|
46
|
+
if (!instanceStyles) return node;
|
|
47
|
+
|
|
48
|
+
if (isHtmlNode(node) || isEmbedNode(node)) {
|
|
49
|
+
// For HTML nodes and Embed nodes: merge instance styles with existing top-level styles
|
|
50
|
+
const existingStyle = node.style;
|
|
51
|
+
if (existingStyle && typeof existingStyle === 'object') {
|
|
52
|
+
node.style = {
|
|
53
|
+
...(existingStyle as Record<string, any>),
|
|
54
|
+
...(instanceStyles as Record<string, any>)
|
|
55
|
+
} as StyleValue;
|
|
56
|
+
} else {
|
|
57
|
+
node.style = instanceStyles as StyleValue;
|
|
58
|
+
}
|
|
59
|
+
} else if (isComponentNode(node)) {
|
|
60
|
+
// For component instances: merge into props.style
|
|
61
|
+
if (!node.props) {
|
|
62
|
+
node.props = {};
|
|
63
|
+
}
|
|
64
|
+
const existingStyle = node.props.style;
|
|
65
|
+
if (existingStyle && typeof existingStyle === 'object') {
|
|
66
|
+
node.props.style = {
|
|
67
|
+
...(existingStyle as Record<string, any>),
|
|
68
|
+
...(instanceStyles as Record<string, any>)
|
|
69
|
+
};
|
|
70
|
+
} else {
|
|
71
|
+
node.props.style = instanceStyles;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return node;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Extract styles from a ComponentNode based on its type
|
|
80
|
+
* Returns a flat style object ready for React props
|
|
81
|
+
*/
|
|
82
|
+
export function extractStylesFromNode(
|
|
83
|
+
node: ComponentNode,
|
|
84
|
+
viewportWidth?: number
|
|
85
|
+
): Record<string, string | number> {
|
|
86
|
+
if (isComponentNode(node)) {
|
|
87
|
+
// Component instance: styles are in props.style
|
|
88
|
+
const style = node.props?.style;
|
|
89
|
+
if (!style) return {};
|
|
90
|
+
|
|
91
|
+
return style as Record<string, string | number>;
|
|
92
|
+
} else if (isHtmlNode(node) || isEmbedNode(node)) {
|
|
93
|
+
// HTML node and Embed node: styles are at top level
|
|
94
|
+
const style = node.style;
|
|
95
|
+
if (!style) return {};
|
|
96
|
+
|
|
97
|
+
return style as Record<string, string | number>;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return {};
|
|
101
|
+
}
|
|
102
|
+
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import { describe, test, expect } from 'bun:test';
|
|
2
|
+
import { isResponsiveStyle, isStyleObject, isStyleValue } from './styleUtils';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* styleUtils Tests
|
|
6
|
+
* Tests style type guards and utilities
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
describe('styleUtils', () => {
|
|
10
|
+
describe('isResponsiveStyle', () => {
|
|
11
|
+
test('should return true for object with base key', () => {
|
|
12
|
+
expect(isResponsiveStyle({ base: { color: 'red' } })).toBe(true);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
test('should return true for object with tablet key', () => {
|
|
16
|
+
expect(isResponsiveStyle({ tablet: { color: 'blue' } })).toBe(true);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test('should return true for object with mobile key', () => {
|
|
20
|
+
expect(isResponsiveStyle({ mobile: { color: 'green' } })).toBe(true);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test('should return true for object with multiple responsive keys', () => {
|
|
24
|
+
expect(isResponsiveStyle({
|
|
25
|
+
base: { color: 'red' },
|
|
26
|
+
tablet: { color: 'blue' },
|
|
27
|
+
mobile: { color: 'green' },
|
|
28
|
+
})).toBe(true);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test('should return false for regular style object', () => {
|
|
32
|
+
expect(isResponsiveStyle({ color: 'red', fontSize: '16px' })).toBe(false);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test('should return false for null', () => {
|
|
36
|
+
expect(isResponsiveStyle(null)).toBe(false);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test('should return false for undefined', () => {
|
|
40
|
+
expect(isResponsiveStyle(undefined)).toBe(false);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test('should return false for string', () => {
|
|
44
|
+
expect(isResponsiveStyle('red')).toBe(false);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test('should return false for number', () => {
|
|
48
|
+
expect(isResponsiveStyle(16)).toBe(false);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test('should return false for array', () => {
|
|
52
|
+
expect(isResponsiveStyle([])).toBe(false);
|
|
53
|
+
expect(isResponsiveStyle([{ color: 'red' }])).toBe(false);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test('should return false for empty object', () => {
|
|
57
|
+
expect(isResponsiveStyle({})).toBe(false);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test('should handle object with base key and other properties', () => {
|
|
61
|
+
expect(isResponsiveStyle({
|
|
62
|
+
base: { color: 'red' },
|
|
63
|
+
customProp: 'value',
|
|
64
|
+
})).toBe(true);
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
describe('isStyleObject', () => {
|
|
69
|
+
test('should return true for simple style object', () => {
|
|
70
|
+
expect(isStyleObject({ color: 'red', fontSize: '16px' })).toBe(true);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test('should return true for style object with string values', () => {
|
|
74
|
+
expect(isStyleObject({
|
|
75
|
+
color: 'red',
|
|
76
|
+
backgroundColor: 'blue',
|
|
77
|
+
padding: '10px',
|
|
78
|
+
})).toBe(true);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test('should return true for style object with number values', () => {
|
|
82
|
+
expect(isStyleObject({
|
|
83
|
+
width: 100,
|
|
84
|
+
height: 200,
|
|
85
|
+
opacity: 0.5,
|
|
86
|
+
})).toBe(true);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test('should return true for style object with mixed string and number', () => {
|
|
90
|
+
expect(isStyleObject({
|
|
91
|
+
color: 'red',
|
|
92
|
+
width: 100,
|
|
93
|
+
fontSize: '16px',
|
|
94
|
+
opacity: 0.8,
|
|
95
|
+
})).toBe(true);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test('should return true for style object with _mapping property', () => {
|
|
99
|
+
expect(isStyleObject({
|
|
100
|
+
color: { _mapping: 'theme.colors.primary' },
|
|
101
|
+
})).toBe(true);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test('should return false for responsive style object', () => {
|
|
105
|
+
expect(isStyleObject({
|
|
106
|
+
base: { color: 'red' },
|
|
107
|
+
tablet: { color: 'blue' },
|
|
108
|
+
})).toBe(false);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
test('should return false for object with only base key', () => {
|
|
112
|
+
expect(isStyleObject({ base: { color: 'red' } })).toBe(false);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
test('should return false for null', () => {
|
|
116
|
+
expect(isStyleObject(null)).toBe(false);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
test('should return false for undefined', () => {
|
|
120
|
+
expect(isStyleObject(undefined)).toBe(false);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
test('should return false for string', () => {
|
|
124
|
+
expect(isStyleObject('red')).toBe(false);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
test('should return false for number', () => {
|
|
128
|
+
expect(isStyleObject(100)).toBe(false);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
test('should return false for array', () => {
|
|
132
|
+
expect(isStyleObject([])).toBe(false);
|
|
133
|
+
expect(isStyleObject([{ color: 'red' }])).toBe(false);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
test('should return false for empty object', () => {
|
|
137
|
+
expect(isStyleObject({})).toBe(true); // Empty object is technically a valid StyleObject
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
test('should return false for object with invalid value types', () => {
|
|
141
|
+
expect(isStyleObject({
|
|
142
|
+
color: 'red',
|
|
143
|
+
invalid: [],
|
|
144
|
+
})).toBe(false);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
test('should return false for object with nested objects without _mapping', () => {
|
|
148
|
+
expect(isStyleObject({
|
|
149
|
+
color: 'red',
|
|
150
|
+
nested: { prop: 'value' },
|
|
151
|
+
})).toBe(false);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
test('should handle complex StyleMapping objects', () => {
|
|
155
|
+
expect(isStyleObject({
|
|
156
|
+
color: { _mapping: 'colors.primary' },
|
|
157
|
+
fontSize: '16px',
|
|
158
|
+
width: 100,
|
|
159
|
+
})).toBe(true);
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
describe('isStyleValue', () => {
|
|
164
|
+
test('should return true for simple style object', () => {
|
|
165
|
+
expect(isStyleValue({ color: 'red', fontSize: '16px' })).toBe(true);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
test('should return true for responsive style object', () => {
|
|
169
|
+
expect(isStyleValue({
|
|
170
|
+
base: { color: 'red' },
|
|
171
|
+
tablet: { color: 'blue' },
|
|
172
|
+
})).toBe(true);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
test('should return true for object with base key', () => {
|
|
176
|
+
expect(isStyleValue({ base: { color: 'red' } })).toBe(true);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
test('should return true for object with tablet key', () => {
|
|
180
|
+
expect(isStyleValue({ tablet: { color: 'blue' } })).toBe(true);
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
test('should return true for object with mobile key', () => {
|
|
184
|
+
expect(isStyleValue({ mobile: { color: 'green' } })).toBe(true);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
test('should return true for style object with numbers', () => {
|
|
188
|
+
expect(isStyleValue({ width: 100, height: 200 })).toBe(true);
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
test('should return true for style object with _mapping', () => {
|
|
192
|
+
expect(isStyleValue({ color: { _mapping: 'theme.primary' } })).toBe(true);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
test('should return false for null', () => {
|
|
196
|
+
expect(isStyleValue(null)).toBe(false);
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
test('should return false for undefined', () => {
|
|
200
|
+
expect(isStyleValue(undefined)).toBe(false);
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
test('should return false for string', () => {
|
|
204
|
+
expect(isStyleValue('red')).toBe(false);
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
test('should return false for number', () => {
|
|
208
|
+
expect(isStyleValue(100)).toBe(false);
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
test('should return false for array', () => {
|
|
212
|
+
expect(isStyleValue([])).toBe(false);
|
|
213
|
+
expect(isStyleValue([{ color: 'red' }])).toBe(false);
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
test('should return true for empty object', () => {
|
|
217
|
+
expect(isStyleValue({})).toBe(true);
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
test('should handle complex responsive styles', () => {
|
|
221
|
+
expect(isStyleValue({
|
|
222
|
+
base: { color: 'red', fontSize: '18px' },
|
|
223
|
+
tablet: { color: 'blue', fontSize: '16px' },
|
|
224
|
+
mobile: { color: 'green', fontSize: '14px' },
|
|
225
|
+
})).toBe(true);
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
test('should handle complex style objects', () => {
|
|
229
|
+
expect(isStyleValue({
|
|
230
|
+
color: 'red',
|
|
231
|
+
fontSize: '16px',
|
|
232
|
+
width: 100,
|
|
233
|
+
height: 200,
|
|
234
|
+
opacity: 0.8,
|
|
235
|
+
})).toBe(true);
|
|
236
|
+
});
|
|
237
|
+
});
|
|
238
|
+
});
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared Style Utilities
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { ResponsiveStyleObject, StyleValue, StyleObject } from './types';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Check if a style value is a responsive style object
|
|
9
|
+
*/
|
|
10
|
+
export function isResponsiveStyle(style: StyleValue): style is ResponsiveStyleObject {
|
|
11
|
+
if (!style || typeof style !== 'object' || Array.isArray(style)) {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Check if it has base, tablet, or mobile keys (indicating responsive format)
|
|
16
|
+
return 'base' in style || 'tablet' in style || 'mobile' in style;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Type guard to check if a value is a StyleObject
|
|
21
|
+
*/
|
|
22
|
+
export function isStyleObject(style: unknown): style is StyleObject {
|
|
23
|
+
if (!style || typeof style !== 'object' || Array.isArray(style)) {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// StyleObject is a record of string keys to string | number | StyleMapping values
|
|
28
|
+
// Check that it's not a ResponsiveStyleObject (which has base/tablet/mobile keys)
|
|
29
|
+
// Use proper type narrowing instead of assertion
|
|
30
|
+
if (!('base' in style) && !('tablet' in style) && !('mobile' in style)) {
|
|
31
|
+
// All values should be string, number, or objects with _mapping property
|
|
32
|
+
const obj = style as Record<string, unknown>;
|
|
33
|
+
return Object.values(obj).every(value => {
|
|
34
|
+
if (typeof value === 'string' || typeof value === 'number') {
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
38
|
+
return '_mapping' in value; // StyleMapping
|
|
39
|
+
}
|
|
40
|
+
return false;
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return false; // This is a ResponsiveStyleObject, not a StyleObject
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Type guard to check if a value is a StyleValue (either StyleObject or ResponsiveStyleObject)
|
|
49
|
+
*/
|
|
50
|
+
export function isStyleValue(style: unknown): style is StyleValue {
|
|
51
|
+
if (!style || typeof style !== 'object' || Array.isArray(style)) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Check if it's a ResponsiveStyleObject
|
|
56
|
+
if (isResponsiveStyle(style)) {
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Check if it's a StyleObject
|
|
61
|
+
return isStyleObject(style);
|
|
62
|
+
}
|
|
63
|
+
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { describe, test, expect } from 'bun:test';
|
|
2
|
+
import {
|
|
3
|
+
DEFAULT_PROPS_PANEL_SPACING,
|
|
4
|
+
DEFAULT_INPUT_STYLE,
|
|
5
|
+
DEFAULT_TEXTAREA_STYLE,
|
|
6
|
+
DEFAULT_PROPS_PANEL_COLORS,
|
|
7
|
+
type PropsPanelColors,
|
|
8
|
+
type PropsPanelSpacing,
|
|
9
|
+
type PropsPanelInputStyle,
|
|
10
|
+
} from './themeDefaults';
|
|
11
|
+
|
|
12
|
+
describe('themeDefaults', () => {
|
|
13
|
+
describe('DEFAULT_PROPS_PANEL_SPACING', () => {
|
|
14
|
+
test('should have tight spacing of 2px', () => {
|
|
15
|
+
expect(DEFAULT_PROPS_PANEL_SPACING.tight).toBe('2px');
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test('should have normal spacing of 4px', () => {
|
|
19
|
+
expect(DEFAULT_PROPS_PANEL_SPACING.normal).toBe('4px');
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test('should have loose spacing of 8px', () => {
|
|
23
|
+
expect(DEFAULT_PROPS_PANEL_SPACING.loose).toBe('8px');
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
describe('DEFAULT_INPUT_STYLE', () => {
|
|
28
|
+
test('should have Consolas monospace font', () => {
|
|
29
|
+
expect(DEFAULT_INPUT_STYLE.fontFamily).toBe('Consolas, monospace');
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test('should have 12px font size', () => {
|
|
33
|
+
expect(DEFAULT_INPUT_STYLE.fontSize).toBe('12px');
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test('should have 4px 6px padding', () => {
|
|
37
|
+
expect(DEFAULT_INPUT_STYLE.padding).toBe('4px 6px');
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test('should have 3px border radius', () => {
|
|
41
|
+
expect(DEFAULT_INPUT_STYLE.borderRadius).toBe('3px');
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test('should have no outline', () => {
|
|
45
|
+
expect(DEFAULT_INPUT_STYLE.outline).toBe('none');
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
describe('DEFAULT_TEXTAREA_STYLE', () => {
|
|
50
|
+
test('should inherit font family', () => {
|
|
51
|
+
expect(DEFAULT_TEXTAREA_STYLE.fontFamily).toBe('inherit');
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test('should have 13px font size', () => {
|
|
55
|
+
expect(DEFAULT_TEXTAREA_STYLE.fontSize).toBe('13px');
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test('should have 4px 6px padding', () => {
|
|
59
|
+
expect(DEFAULT_TEXTAREA_STYLE.padding).toBe('4px 6px');
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test('should have 3px border radius', () => {
|
|
63
|
+
expect(DEFAULT_TEXTAREA_STYLE.borderRadius).toBe('3px');
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test('should have no outline', () => {
|
|
67
|
+
expect(DEFAULT_TEXTAREA_STYLE.outline).toBe('none');
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
describe('DEFAULT_PROPS_PANEL_COLORS', () => {
|
|
72
|
+
test('should have dark theme background colors', () => {
|
|
73
|
+
expect(DEFAULT_PROPS_PANEL_COLORS.background).toBe('#1e1e1e');
|
|
74
|
+
expect(DEFAULT_PROPS_PANEL_COLORS.backgroundSecondary).toBe('#2d2d2d');
|
|
75
|
+
expect(DEFAULT_PROPS_PANEL_COLORS.backgroundTertiary).toBe('#252525');
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test('should have border colors', () => {
|
|
79
|
+
expect(DEFAULT_PROPS_PANEL_COLORS.border).toBe('#333333');
|
|
80
|
+
expect(DEFAULT_PROPS_PANEL_COLORS.borderSecondary).toBe('#444444');
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test('should have text colors', () => {
|
|
84
|
+
expect(DEFAULT_PROPS_PANEL_COLORS.text).toBe('#cccccc');
|
|
85
|
+
expect(DEFAULT_PROPS_PANEL_COLORS.textSecondary).toBe('#cccccc');
|
|
86
|
+
expect(DEFAULT_PROPS_PANEL_COLORS.textMuted).toBe('#888888');
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test('should have code syntax colors', () => {
|
|
90
|
+
expect(DEFAULT_PROPS_PANEL_COLORS.codeString).toBe('#ce9178');
|
|
91
|
+
expect(DEFAULT_PROPS_PANEL_COLORS.codeNumber).toBe('#b5cea8');
|
|
92
|
+
expect(DEFAULT_PROPS_PANEL_COLORS.codeKey).toBe('#9cdcfe');
|
|
93
|
+
expect(DEFAULT_PROPS_PANEL_COLORS.codeType).toBe('#4ec9b0');
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test('should have button colors', () => {
|
|
97
|
+
expect(DEFAULT_PROPS_PANEL_COLORS.buttonPrimary).toBe('#007acc');
|
|
98
|
+
expect(DEFAULT_PROPS_PANEL_COLORS.buttonPrimaryHover).toBe('#0098ff');
|
|
99
|
+
expect(DEFAULT_PROPS_PANEL_COLORS.buttonSecondary).toBe('#007acc');
|
|
100
|
+
expect(DEFAULT_PROPS_PANEL_COLORS.buttonDanger).toBe('#f48771');
|
|
101
|
+
expect(DEFAULT_PROPS_PANEL_COLORS.buttonDangerHover).toBe('#ff6b6b');
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test('should have input colors', () => {
|
|
105
|
+
expect(DEFAULT_PROPS_PANEL_COLORS.inputBackground).toBe('#1e1e1e');
|
|
106
|
+
expect(DEFAULT_PROPS_PANEL_COLORS.inputBorder).toBe('#444444');
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
test('should have hover background color', () => {
|
|
110
|
+
expect(DEFAULT_PROPS_PANEL_COLORS.hoverBackground).toBe('#2a2d2e');
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
});
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared theme color defaults
|
|
3
|
+
* Used by PropsPanel and StyleEditor to avoid duplication
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export interface PropsPanelColors {
|
|
7
|
+
background: string;
|
|
8
|
+
backgroundSecondary: string;
|
|
9
|
+
backgroundTertiary: string;
|
|
10
|
+
border: string;
|
|
11
|
+
borderSecondary: string;
|
|
12
|
+
text: string;
|
|
13
|
+
textSecondary: string;
|
|
14
|
+
textMuted: string;
|
|
15
|
+
codeString: string;
|
|
16
|
+
codeNumber: string;
|
|
17
|
+
codeKey: string;
|
|
18
|
+
codeType: string;
|
|
19
|
+
buttonPrimary: string;
|
|
20
|
+
buttonPrimaryHover: string;
|
|
21
|
+
buttonSecondary: string;
|
|
22
|
+
buttonDanger: string;
|
|
23
|
+
buttonDangerHover: string;
|
|
24
|
+
inputBackground: string;
|
|
25
|
+
inputBorder: string;
|
|
26
|
+
hoverBackground: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Spacing constants for props panel components
|
|
31
|
+
*/
|
|
32
|
+
export interface PropsPanelSpacing {
|
|
33
|
+
tight: string; // 2px - for label:colon:input gaps
|
|
34
|
+
normal: string; // 4px - for standard gaps
|
|
35
|
+
loose: string; // 8px - for larger gaps
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export const DEFAULT_PROPS_PANEL_SPACING: PropsPanelSpacing = {
|
|
39
|
+
tight: '2px',
|
|
40
|
+
normal: '4px',
|
|
41
|
+
loose: '8px',
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Base input style properties
|
|
46
|
+
*/
|
|
47
|
+
export interface PropsPanelInputStyle {
|
|
48
|
+
fontFamily: string;
|
|
49
|
+
fontSize: string;
|
|
50
|
+
padding: string;
|
|
51
|
+
borderRadius: string;
|
|
52
|
+
outline: string;
|
|
53
|
+
border?: string;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Base input style defaults
|
|
58
|
+
*/
|
|
59
|
+
export const DEFAULT_INPUT_STYLE: Omit<PropsPanelInputStyle, 'border' | 'color' | 'background'> = {
|
|
60
|
+
fontFamily: 'Consolas, monospace',
|
|
61
|
+
fontSize: '12px',
|
|
62
|
+
padding: '4px 6px',
|
|
63
|
+
borderRadius: '3px',
|
|
64
|
+
outline: 'none',
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Base textarea style defaults
|
|
69
|
+
*/
|
|
70
|
+
export const DEFAULT_TEXTAREA_STYLE: Omit<PropsPanelInputStyle, 'border' | 'color' | 'background'> = {
|
|
71
|
+
fontFamily: 'inherit',
|
|
72
|
+
fontSize: '13px',
|
|
73
|
+
padding: '4px 6px',
|
|
74
|
+
borderRadius: '3px',
|
|
75
|
+
outline: 'none',
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Default dark theme colors for props panel
|
|
80
|
+
*/
|
|
81
|
+
export const DEFAULT_PROPS_PANEL_COLORS: PropsPanelColors = {
|
|
82
|
+
background: '#1e1e1e',
|
|
83
|
+
backgroundSecondary: '#2d2d2d',
|
|
84
|
+
backgroundTertiary: '#252525',
|
|
85
|
+
border: '#333333',
|
|
86
|
+
borderSecondary: '#444444',
|
|
87
|
+
text: '#cccccc',
|
|
88
|
+
textSecondary: '#cccccc',
|
|
89
|
+
textMuted: '#888888',
|
|
90
|
+
codeString: '#ce9178',
|
|
91
|
+
codeNumber: '#b5cea8',
|
|
92
|
+
codeKey: '#9cdcfe',
|
|
93
|
+
codeType: '#4ec9b0',
|
|
94
|
+
buttonPrimary: '#007acc',
|
|
95
|
+
buttonPrimaryHover: '#0098ff',
|
|
96
|
+
buttonSecondary: '#007acc',
|
|
97
|
+
buttonDanger: '#f48771',
|
|
98
|
+
buttonDangerHover: '#ff6b6b',
|
|
99
|
+
inputBackground: '#1e1e1e',
|
|
100
|
+
inputBorder: '#444444',
|
|
101
|
+
hoverBackground: '#2a2d2e',
|
|
102
|
+
};
|
|
103
|
+
|