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,252 @@
|
|
|
1
|
+
import { describe, test, expect } from 'bun:test';
|
|
2
|
+
import {
|
|
3
|
+
CSS_PROPERTIES,
|
|
4
|
+
CSS_PROPERTIES_DEFINITION,
|
|
5
|
+
filterCSSProperties,
|
|
6
|
+
getPropertyValues,
|
|
7
|
+
filterPropertyValues,
|
|
8
|
+
getPropertyType
|
|
9
|
+
} from './cssProperties';
|
|
10
|
+
|
|
11
|
+
describe('cssProperties', () => {
|
|
12
|
+
describe('CSS_PROPERTIES', () => {
|
|
13
|
+
test('is an array of property names', () => {
|
|
14
|
+
expect(Array.isArray(CSS_PROPERTIES)).toBe(true);
|
|
15
|
+
expect(CSS_PROPERTIES.length).toBeGreaterThan(0);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test('contains common CSS properties', () => {
|
|
19
|
+
expect(CSS_PROPERTIES).toContain('display');
|
|
20
|
+
expect(CSS_PROPERTIES).toContain('flexDirection');
|
|
21
|
+
expect(CSS_PROPERTIES).toContain('padding');
|
|
22
|
+
expect(CSS_PROPERTIES).toContain('backgroundColor');
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
describe('CSS_PROPERTIES_DEFINITION', () => {
|
|
27
|
+
test('contains display property with values', () => {
|
|
28
|
+
expect(CSS_PROPERTIES_DEFINITION.display).toBeDefined();
|
|
29
|
+
expect(CSS_PROPERTIES_DEFINITION.display.values).toContain('flex');
|
|
30
|
+
expect(CSS_PROPERTIES_DEFINITION.display.type).toBe('select');
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test('contains position property', () => {
|
|
34
|
+
expect(CSS_PROPERTIES_DEFINITION.position).toBeDefined();
|
|
35
|
+
expect(CSS_PROPERTIES_DEFINITION.position.values).toContain('relative');
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test('contains flexbox properties', () => {
|
|
39
|
+
expect(CSS_PROPERTIES_DEFINITION.flexDirection).toBeDefined();
|
|
40
|
+
expect(CSS_PROPERTIES_DEFINITION.justifyContent).toBeDefined();
|
|
41
|
+
expect(CSS_PROPERTIES_DEFINITION.alignItems).toBeDefined();
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test('contains dimension properties', () => {
|
|
45
|
+
expect(CSS_PROPERTIES_DEFINITION.width).toBeDefined();
|
|
46
|
+
expect(CSS_PROPERTIES_DEFINITION.height).toBeDefined();
|
|
47
|
+
expect(CSS_PROPERTIES_DEFINITION.width.type).toBe('string');
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test('contains cursor property with values', () => {
|
|
51
|
+
expect(CSS_PROPERTIES_DEFINITION.cursor.values).toContain('pointer');
|
|
52
|
+
expect(CSS_PROPERTIES_DEFINITION.cursor.values).toContain('default');
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
describe('filterCSSProperties', () => {
|
|
57
|
+
test('returns first 15 properties for empty input', () => {
|
|
58
|
+
const result = filterCSSProperties('');
|
|
59
|
+
expect(result.length).toBe(15);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test('filters properties by exact prefix match', () => {
|
|
63
|
+
const result = filterCSSProperties('flex');
|
|
64
|
+
expect(result.every(prop => prop.toLowerCase().startsWith('flex'))).toBe(true);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test('filters properties case-insensitively', () => {
|
|
68
|
+
const result = filterCSSProperties('FLEX');
|
|
69
|
+
expect(result.length).toBeGreaterThan(0);
|
|
70
|
+
expect(result.every(prop => prop.toLowerCase().startsWith('flex'))).toBe(true);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test('supports camelCase abbreviation matching', () => {
|
|
74
|
+
const result = filterCSSProperties('bC');
|
|
75
|
+
expect(result).toContain('backgroundColor');
|
|
76
|
+
expect(result).toContain('borderColor');
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test('prioritizes startsWith matches over abbreviation matches', () => {
|
|
80
|
+
const result = filterCSSProperties('b');
|
|
81
|
+
const startsWithB = result.filter(p => p.toLowerCase().startsWith('b'));
|
|
82
|
+
expect(startsWithB.length).toBeGreaterThan(0);
|
|
83
|
+
// startsWith matches should come first
|
|
84
|
+
expect(result.indexOf('border')).toBeLessThan(result.indexOf('backgroundColor') || Infinity);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test('returns empty array for non-matching input', () => {
|
|
88
|
+
const result = filterCSSProperties('zzzzz');
|
|
89
|
+
expect(result.length).toBe(0);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test('handles single character input', () => {
|
|
93
|
+
const result = filterCSSProperties('p');
|
|
94
|
+
expect(result.some(prop => prop.toLowerCase().startsWith('p'))).toBe(true);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test('trims whitespace from input', () => {
|
|
98
|
+
const result = filterCSSProperties(' flex ');
|
|
99
|
+
expect(result.length).toBeGreaterThan(0);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test('matches abbreviation with multiple capitals', () => {
|
|
103
|
+
const result = filterCSSProperties('fD');
|
|
104
|
+
expect(result).toContain('flexDirection');
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
test('matches abbreviation case-insensitively', () => {
|
|
108
|
+
const result = filterCSSProperties('fd');
|
|
109
|
+
expect(result).toContain('flexDirection');
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
describe('getPropertyValues', () => {
|
|
114
|
+
test('returns values for display property', () => {
|
|
115
|
+
const values = getPropertyValues('display');
|
|
116
|
+
expect(values).toContain('flex');
|
|
117
|
+
expect(values).toContain('grid');
|
|
118
|
+
expect(values).toContain('block');
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
test('returns values for position property', () => {
|
|
122
|
+
const values = getPropertyValues('position');
|
|
123
|
+
expect(values).toContain('relative');
|
|
124
|
+
expect(values).toContain('absolute');
|
|
125
|
+
expect(values).toContain('fixed');
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test('returns empty array for property without predefined values', () => {
|
|
129
|
+
const values = getPropertyValues('width');
|
|
130
|
+
expect(values).toEqual([]);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
test('returns empty array for non-existent property', () => {
|
|
134
|
+
const values = getPropertyValues('nonExistentProperty');
|
|
135
|
+
expect(values).toEqual([]);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
test('returns values for flexDirection', () => {
|
|
139
|
+
const values = getPropertyValues('flexDirection');
|
|
140
|
+
expect(values).toContain('row');
|
|
141
|
+
expect(values).toContain('column');
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
test('returns values for cursor', () => {
|
|
145
|
+
const values = getPropertyValues('cursor');
|
|
146
|
+
expect(values).toContain('pointer');
|
|
147
|
+
expect(values).toContain('default');
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
test('returns array copy (not reference)', () => {
|
|
151
|
+
const values1 = getPropertyValues('display');
|
|
152
|
+
const values2 = getPropertyValues('display');
|
|
153
|
+
expect(values1).toEqual(values2);
|
|
154
|
+
expect(values1).not.toBe(values2);
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
describe('filterPropertyValues', () => {
|
|
159
|
+
test('returns all values when input is empty', () => {
|
|
160
|
+
const values = filterPropertyValues('display', '');
|
|
161
|
+
expect(values).toContain('flex');
|
|
162
|
+
expect(values).toContain('grid');
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
test('filters values by substring match', () => {
|
|
166
|
+
const values = filterPropertyValues('display', 'flex');
|
|
167
|
+
expect(values).toContain('flex');
|
|
168
|
+
expect(values).toContain('inline-flex');
|
|
169
|
+
expect(values).not.toContain('grid');
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
test('filters case-insensitively', () => {
|
|
173
|
+
const values = filterPropertyValues('display', 'FLEX');
|
|
174
|
+
expect(values).toContain('flex');
|
|
175
|
+
expect(values).toContain('inline-flex');
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
test('returns empty array for property without values', () => {
|
|
179
|
+
const values = filterPropertyValues('width', 'auto');
|
|
180
|
+
expect(values).toEqual([]);
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
test('returns empty array for non-matching input', () => {
|
|
184
|
+
const values = filterPropertyValues('display', 'zzzzz');
|
|
185
|
+
expect(values).toEqual([]);
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
test('handles whitespace in input', () => {
|
|
189
|
+
const values = filterPropertyValues('display', ' flex ');
|
|
190
|
+
expect(values.length).toBeGreaterThan(0);
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
test('filters position values', () => {
|
|
194
|
+
const values = filterPropertyValues('position', 'relative');
|
|
195
|
+
expect(values).toContain('relative');
|
|
196
|
+
expect(values).not.toContain('absolute');
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
test('filters flexDirection values', () => {
|
|
200
|
+
const values = filterPropertyValues('flexDirection', 'row');
|
|
201
|
+
expect(values).toContain('row');
|
|
202
|
+
expect(values).toContain('row-reverse');
|
|
203
|
+
expect(values).not.toContain('column');
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
test('handles partial matches', () => {
|
|
207
|
+
const values = filterPropertyValues('justifyContent', 'space');
|
|
208
|
+
expect(values).toContain('space-between');
|
|
209
|
+
expect(values).toContain('space-around');
|
|
210
|
+
expect(values).toContain('space-evenly');
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
test('returns empty for non-existent property', () => {
|
|
214
|
+
const values = filterPropertyValues('nonExistent', 'test');
|
|
215
|
+
expect(values).toEqual([]);
|
|
216
|
+
});
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
describe('getPropertyType', () => {
|
|
220
|
+
test('returns select type for display', () => {
|
|
221
|
+
expect(getPropertyType('display')).toBe('select');
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
test('returns string type for width', () => {
|
|
225
|
+
expect(getPropertyType('width')).toBe('string');
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
test('returns number type for zIndex', () => {
|
|
229
|
+
expect(getPropertyType('zIndex')).toBe('number');
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
test('returns number type for opacity', () => {
|
|
233
|
+
expect(getPropertyType('opacity')).toBe('number');
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
test('returns select type for cursor', () => {
|
|
237
|
+
expect(getPropertyType('cursor')).toBe('select');
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
test('returns undefined for non-existent property', () => {
|
|
241
|
+
expect(getPropertyType('nonExistent')).toBeUndefined();
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
test('returns select type for flexDirection', () => {
|
|
245
|
+
expect(getPropertyType('flexDirection')).toBe('select');
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
test('returns string type for padding', () => {
|
|
249
|
+
expect(getPropertyType('padding')).toBe('string');
|
|
250
|
+
});
|
|
251
|
+
});
|
|
252
|
+
});
|
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CSS Property Definition structure
|
|
3
|
+
*/
|
|
4
|
+
interface CSSPropertyDefinition {
|
|
5
|
+
values?: readonly string[];
|
|
6
|
+
type?: 'string' | 'select' | 'boolean' | 'number';
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* CSS Properties with their valid values and metadata
|
|
11
|
+
* Used for autocomplete suggestions for both property names and values
|
|
12
|
+
*/
|
|
13
|
+
export const CSS_PROPERTIES_DEFINITION: Record<string, CSSPropertyDefinition> = {
|
|
14
|
+
// Display & Layout
|
|
15
|
+
display: {
|
|
16
|
+
values: ['block', 'flex', 'grid', 'inline', 'inline-block', 'inline-flex', 'inline-grid', 'none'],
|
|
17
|
+
type: 'select',
|
|
18
|
+
},
|
|
19
|
+
position: {
|
|
20
|
+
values: ['static', 'relative', 'absolute', 'fixed', 'sticky'],
|
|
21
|
+
type: 'select',
|
|
22
|
+
},
|
|
23
|
+
top: { type: 'string' },
|
|
24
|
+
right: { type: 'string' },
|
|
25
|
+
bottom: { type: 'string' },
|
|
26
|
+
left: { type: 'string' },
|
|
27
|
+
zIndex: { type: 'number' },
|
|
28
|
+
|
|
29
|
+
// Dimensions
|
|
30
|
+
width: { type: 'string' },
|
|
31
|
+
height: { type: 'string' },
|
|
32
|
+
minWidth: { type: 'string' },
|
|
33
|
+
maxWidth: { type: 'string' },
|
|
34
|
+
minHeight: { type: 'string' },
|
|
35
|
+
maxHeight: { type: 'string' },
|
|
36
|
+
|
|
37
|
+
// Spacing
|
|
38
|
+
margin: { type: 'string' },
|
|
39
|
+
marginTop: { type: 'string' },
|
|
40
|
+
marginRight: { type: 'string' },
|
|
41
|
+
marginBottom: { type: 'string' },
|
|
42
|
+
marginLeft: { type: 'string' },
|
|
43
|
+
padding: { type: 'string' },
|
|
44
|
+
paddingTop: { type: 'string' },
|
|
45
|
+
paddingRight: { type: 'string' },
|
|
46
|
+
paddingBottom: { type: 'string' },
|
|
47
|
+
paddingLeft: { type: 'string' },
|
|
48
|
+
gap: { type: 'string' },
|
|
49
|
+
rowGap: { type: 'string' },
|
|
50
|
+
columnGap: { type: 'string' },
|
|
51
|
+
|
|
52
|
+
// Borders
|
|
53
|
+
border: { type: 'string' },
|
|
54
|
+
borderWidth: { type: 'string' },
|
|
55
|
+
borderStyle: {
|
|
56
|
+
values: ['solid', 'dashed', 'dotted', 'double', 'groove', 'ridge', 'inset', 'outset', 'none'],
|
|
57
|
+
type: 'select',
|
|
58
|
+
},
|
|
59
|
+
borderColor: { type: 'string' },
|
|
60
|
+
borderTop: { type: 'string' },
|
|
61
|
+
borderRight: { type: 'string' },
|
|
62
|
+
borderBottom: { type: 'string' },
|
|
63
|
+
borderLeft: { type: 'string' },
|
|
64
|
+
borderRadius: { type: 'string' },
|
|
65
|
+
borderTopLeftRadius: { type: 'string' },
|
|
66
|
+
borderTopRightRadius: { type: 'string' },
|
|
67
|
+
borderBottomLeftRadius: { type: 'string' },
|
|
68
|
+
borderBottomRightRadius: { type: 'string' },
|
|
69
|
+
|
|
70
|
+
// Background & Colors
|
|
71
|
+
background: { type: 'string' },
|
|
72
|
+
backgroundColor: { type: 'string' },
|
|
73
|
+
backgroundImage: { type: 'string' },
|
|
74
|
+
backgroundSize: {
|
|
75
|
+
values: ['auto', 'cover', 'contain'],
|
|
76
|
+
type: 'select',
|
|
77
|
+
},
|
|
78
|
+
backgroundPosition: {
|
|
79
|
+
values: ['top', 'bottom', 'left', 'right', 'center'],
|
|
80
|
+
type: 'select',
|
|
81
|
+
},
|
|
82
|
+
backgroundRepeat: {
|
|
83
|
+
values: ['repeat', 'repeat-x', 'repeat-y', 'no-repeat'],
|
|
84
|
+
type: 'select',
|
|
85
|
+
},
|
|
86
|
+
color: { type: 'string' },
|
|
87
|
+
opacity: { type: 'number' },
|
|
88
|
+
|
|
89
|
+
// Flexbox
|
|
90
|
+
flex: { type: 'string' },
|
|
91
|
+
flexDirection: {
|
|
92
|
+
values: ['row', 'column', 'row-reverse', 'column-reverse'],
|
|
93
|
+
type: 'select',
|
|
94
|
+
},
|
|
95
|
+
flexWrap: {
|
|
96
|
+
values: ['nowrap', 'wrap', 'wrap-reverse'],
|
|
97
|
+
type: 'select',
|
|
98
|
+
},
|
|
99
|
+
flexFlow: { type: 'string' },
|
|
100
|
+
justifyContent: {
|
|
101
|
+
values: ['flex-start', 'flex-end', 'center', 'space-between', 'space-around', 'space-evenly'],
|
|
102
|
+
type: 'select',
|
|
103
|
+
},
|
|
104
|
+
alignItems: {
|
|
105
|
+
values: ['flex-start', 'flex-end', 'center', 'stretch', 'baseline'],
|
|
106
|
+
type: 'select',
|
|
107
|
+
},
|
|
108
|
+
alignContent: {
|
|
109
|
+
values: ['flex-start', 'flex-end', 'center', 'space-between', 'space-around', 'stretch'],
|
|
110
|
+
type: 'select',
|
|
111
|
+
},
|
|
112
|
+
flexGrow: { type: 'number' },
|
|
113
|
+
flexShrink: { type: 'number' },
|
|
114
|
+
flexBasis: { type: 'string' },
|
|
115
|
+
order: { type: 'number' },
|
|
116
|
+
|
|
117
|
+
// Grid
|
|
118
|
+
grid: { type: 'string' },
|
|
119
|
+
gridTemplateColumns: { type: 'string' },
|
|
120
|
+
gridTemplateRows: { type: 'string' },
|
|
121
|
+
gridGap: { type: 'string' },
|
|
122
|
+
gridColumn: { type: 'string' },
|
|
123
|
+
gridRow: { type: 'string' },
|
|
124
|
+
gridAutoFlow: {
|
|
125
|
+
values: ['row', 'column', 'row dense', 'column dense'],
|
|
126
|
+
type: 'select',
|
|
127
|
+
},
|
|
128
|
+
|
|
129
|
+
// Text & Font
|
|
130
|
+
fontSize: { type: 'string' },
|
|
131
|
+
fontWeight: {
|
|
132
|
+
values: ['100', '200', '300', '400', '500', '600', '700', '800', '900', 'normal', 'bold'],
|
|
133
|
+
type: 'select',
|
|
134
|
+
},
|
|
135
|
+
fontFamily: { type: 'string' },
|
|
136
|
+
fontStyle: {
|
|
137
|
+
values: ['normal', 'italic', 'oblique'],
|
|
138
|
+
type: 'select',
|
|
139
|
+
},
|
|
140
|
+
lineHeight: { type: 'string' },
|
|
141
|
+
textAlign: {
|
|
142
|
+
values: ['left', 'right', 'center', 'justify', 'start', 'end'],
|
|
143
|
+
type: 'select',
|
|
144
|
+
},
|
|
145
|
+
textDecoration: {
|
|
146
|
+
values: ['none', 'underline', 'overline', 'line-through'],
|
|
147
|
+
type: 'select',
|
|
148
|
+
},
|
|
149
|
+
textTransform: {
|
|
150
|
+
values: ['none', 'capitalize', 'uppercase', 'lowercase'],
|
|
151
|
+
type: 'select',
|
|
152
|
+
},
|
|
153
|
+
letterSpacing: { type: 'string' },
|
|
154
|
+
wordSpacing: { type: 'string' },
|
|
155
|
+
|
|
156
|
+
// Box Shadow & Effects
|
|
157
|
+
boxShadow: { type: 'string' },
|
|
158
|
+
textShadow: { type: 'string' },
|
|
159
|
+
filter: { type: 'string' },
|
|
160
|
+
transform: { type: 'string' },
|
|
161
|
+
transition: { type: 'string' },
|
|
162
|
+
animation: { type: 'string' },
|
|
163
|
+
|
|
164
|
+
// Overflow & Content
|
|
165
|
+
overflow: {
|
|
166
|
+
values: ['visible', 'hidden', 'scroll', 'auto'],
|
|
167
|
+
type: 'select',
|
|
168
|
+
},
|
|
169
|
+
overflowX: {
|
|
170
|
+
values: ['visible', 'hidden', 'scroll', 'auto'],
|
|
171
|
+
type: 'select',
|
|
172
|
+
},
|
|
173
|
+
overflowY: {
|
|
174
|
+
values: ['visible', 'hidden', 'scroll', 'auto'],
|
|
175
|
+
type: 'select',
|
|
176
|
+
},
|
|
177
|
+
whiteSpace: {
|
|
178
|
+
values: ['normal', 'nowrap', 'pre', 'pre-wrap', 'pre-line'],
|
|
179
|
+
type: 'select',
|
|
180
|
+
},
|
|
181
|
+
textOverflow: {
|
|
182
|
+
values: ['clip', 'ellipsis'],
|
|
183
|
+
type: 'select',
|
|
184
|
+
},
|
|
185
|
+
visibility: {
|
|
186
|
+
values: ['visible', 'hidden', 'collapse'],
|
|
187
|
+
type: 'select',
|
|
188
|
+
},
|
|
189
|
+
content: { type: 'string' },
|
|
190
|
+
|
|
191
|
+
// Cursor & Interaction
|
|
192
|
+
cursor: {
|
|
193
|
+
values: ['auto', 'default', 'pointer', 'wait', 'text', 'move', 'not-allowed', 'help'],
|
|
194
|
+
type: 'select',
|
|
195
|
+
},
|
|
196
|
+
pointerEvents: {
|
|
197
|
+
values: ['auto', 'none'],
|
|
198
|
+
type: 'select',
|
|
199
|
+
},
|
|
200
|
+
userSelect: {
|
|
201
|
+
values: ['auto', 'none', 'text', 'all'],
|
|
202
|
+
type: 'select',
|
|
203
|
+
},
|
|
204
|
+
|
|
205
|
+
// Miscellaneous
|
|
206
|
+
float: {
|
|
207
|
+
values: ['left', 'right', 'none'],
|
|
208
|
+
type: 'select',
|
|
209
|
+
},
|
|
210
|
+
clear: {
|
|
211
|
+
values: ['left', 'right', 'both', 'none'],
|
|
212
|
+
type: 'select',
|
|
213
|
+
},
|
|
214
|
+
boxSizing: {
|
|
215
|
+
values: ['content-box', 'border-box'],
|
|
216
|
+
type: 'select',
|
|
217
|
+
},
|
|
218
|
+
objectFit: {
|
|
219
|
+
values: ['fill', 'contain', 'cover', 'scale-down'],
|
|
220
|
+
type: 'select',
|
|
221
|
+
},
|
|
222
|
+
objectPosition: {
|
|
223
|
+
values: ['top', 'bottom', 'left', 'right', 'center'],
|
|
224
|
+
type: 'select',
|
|
225
|
+
},
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Common CSS properties for autocomplete suggestions (camelCase for React/JS)
|
|
230
|
+
* Derived from CSS_PROPERTIES_DEFINITION keys
|
|
231
|
+
*/
|
|
232
|
+
export const CSS_PROPERTIES = Object.keys(CSS_PROPERTIES_DEFINITION);
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Check if property matches the abbreviation pattern
|
|
236
|
+
* For example, "bC" matches "backgroundColor" (b→b, C→C capital letter)
|
|
237
|
+
*/
|
|
238
|
+
function matchesAbbreviation(property: string, input: string): boolean {
|
|
239
|
+
if (!input) return false;
|
|
240
|
+
|
|
241
|
+
let propertyIndex = 0;
|
|
242
|
+
|
|
243
|
+
for (let inputIndex = 0; inputIndex < input.length; inputIndex++) {
|
|
244
|
+
const inputChar = input[inputIndex];
|
|
245
|
+
let found = false;
|
|
246
|
+
|
|
247
|
+
// Search from current position to end of property for a match at a word boundary
|
|
248
|
+
while (propertyIndex < property.length) {
|
|
249
|
+
const propChar = property[propertyIndex];
|
|
250
|
+
// Word boundary = first character OR capital letter
|
|
251
|
+
const isWordBoundary = propertyIndex === 0 ||
|
|
252
|
+
(propChar === propChar.toUpperCase() && propChar !== propChar.toLowerCase());
|
|
253
|
+
|
|
254
|
+
if (isWordBoundary && inputChar.toLowerCase() === propChar.toLowerCase()) {
|
|
255
|
+
// Match found at word boundary
|
|
256
|
+
found = true;
|
|
257
|
+
propertyIndex++; // Move past this character for next search
|
|
258
|
+
break;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
propertyIndex++;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
if (!found) {
|
|
265
|
+
return false; // Couldn't find this input character at a word boundary
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
return true;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Filter CSS properties based on input value
|
|
274
|
+
* Supports both startsWith matching and camelCase abbreviation matching
|
|
275
|
+
*/
|
|
276
|
+
export function filterCSSProperties(input: string): string[] {
|
|
277
|
+
const normalizedInput = input.trim();
|
|
278
|
+
if (!normalizedInput) {
|
|
279
|
+
return CSS_PROPERTIES.slice(0, 15); // Show first 15 by default
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// First, try startsWith matching (exact prefix match)
|
|
283
|
+
const startsWithMatches = CSS_PROPERTIES.filter(prop =>
|
|
284
|
+
prop.toLowerCase().startsWith(normalizedInput.toLowerCase())
|
|
285
|
+
);
|
|
286
|
+
|
|
287
|
+
// Then, try abbreviation/fuzzy matching
|
|
288
|
+
const abbreviationMatches = CSS_PROPERTIES.filter(prop =>
|
|
289
|
+
matchesAbbreviation(prop, normalizedInput) &&
|
|
290
|
+
!startsWithMatches.includes(prop) // Don't duplicate startsWith matches
|
|
291
|
+
);
|
|
292
|
+
|
|
293
|
+
// Combine results: exact matches first, then fuzzy matches
|
|
294
|
+
return [...startsWithMatches, ...abbreviationMatches];
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Get available values for a specific CSS property
|
|
299
|
+
* @param propertyName - The CSS property name (e.g., 'flexDirection')
|
|
300
|
+
* @returns Array of valid values for the property, or empty array if property has no predefined values
|
|
301
|
+
*/
|
|
302
|
+
export function getPropertyValues(propertyName: string): string[] {
|
|
303
|
+
const values = CSS_PROPERTIES_DEFINITION[propertyName]?.values;
|
|
304
|
+
return values ? Array.from(values) : [];
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Filter values for a specific CSS property based on input
|
|
309
|
+
* Supports substring matching and returns values that contain the input
|
|
310
|
+
* @param propertyName - The CSS property name (e.g., 'flexDirection')
|
|
311
|
+
* @param input - The user input to filter values by
|
|
312
|
+
* @returns Filtered array of valid values for the property
|
|
313
|
+
*/
|
|
314
|
+
export function filterPropertyValues(propertyName: string, input: string): string[] {
|
|
315
|
+
const values = getPropertyValues(propertyName);
|
|
316
|
+
if (!values.length) {
|
|
317
|
+
return []; // No predefined values for this property
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
const normalizedInput = input.trim().toLowerCase();
|
|
321
|
+
if (!normalizedInput) {
|
|
322
|
+
return values; // Return all values if no input
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// Filter values that contain the input (substring match)
|
|
326
|
+
return values.filter(value =>
|
|
327
|
+
value.toLowerCase().includes(normalizedInput)
|
|
328
|
+
);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Get the property type for a CSS property
|
|
333
|
+
* @param propertyName - The CSS property name
|
|
334
|
+
* @returns The property type ('string' | 'select' | 'boolean' | 'number') or undefined
|
|
335
|
+
*/
|
|
336
|
+
export function getPropertyType(propertyName: string): 'string' | 'select' | 'boolean' | 'number' | undefined {
|
|
337
|
+
return CSS_PROPERTIES_DEFINITION[propertyName]?.type;
|
|
338
|
+
}
|