@furystack/shades 11.0.35 → 11.1.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/CHANGELOG.md +46 -0
- package/README.md +86 -0
- package/esm/compile-route.spec.d.ts +2 -0
- package/esm/compile-route.spec.d.ts.map +1 -0
- package/esm/compile-route.spec.js +34 -0
- package/esm/compile-route.spec.js.map +1 -0
- package/esm/components/route-link.d.ts.map +1 -1
- package/esm/components/route-link.js +4 -5
- package/esm/components/route-link.js.map +1 -1
- package/esm/components/route-link.spec.js +1 -1
- package/esm/components/route-link.spec.js.map +1 -1
- package/esm/css-generator.d.ts +50 -0
- package/esm/css-generator.d.ts.map +1 -0
- package/esm/css-generator.js +107 -0
- package/esm/css-generator.js.map +1 -0
- package/esm/css-generator.spec.d.ts +2 -0
- package/esm/css-generator.spec.d.ts.map +1 -0
- package/esm/css-generator.spec.js +162 -0
- package/esm/css-generator.spec.js.map +1 -0
- package/esm/index.d.ts +2 -0
- package/esm/index.d.ts.map +1 -1
- package/esm/index.js +2 -0
- package/esm/index.js.map +1 -1
- package/esm/models/css-object.d.ts +33 -0
- package/esm/models/css-object.d.ts.map +1 -0
- package/esm/models/css-object.js +2 -0
- package/esm/models/css-object.js.map +1 -0
- package/esm/models/index.d.ts +1 -0
- package/esm/models/index.d.ts.map +1 -1
- package/esm/models/index.js +1 -0
- package/esm/models/index.js.map +1 -1
- package/esm/shade.d.ts +18 -2
- package/esm/shade.d.ts.map +1 -1
- package/esm/shade.js +8 -0
- package/esm/shade.js.map +1 -1
- package/esm/shade.spec.d.ts +2 -0
- package/esm/shade.spec.d.ts.map +1 -0
- package/esm/shade.spec.js +197 -0
- package/esm/shade.spec.js.map +1 -0
- package/esm/style-manager.d.ts +65 -0
- package/esm/style-manager.d.ts.map +1 -0
- package/esm/style-manager.js +95 -0
- package/esm/style-manager.js.map +1 -0
- package/esm/style-manager.spec.d.ts +2 -0
- package/esm/style-manager.spec.d.ts.map +1 -0
- package/esm/style-manager.spec.js +179 -0
- package/esm/style-manager.spec.js.map +1 -0
- package/esm/styled-element.spec.d.ts +2 -0
- package/esm/styled-element.spec.d.ts.map +1 -0
- package/esm/styled-element.spec.js +86 -0
- package/esm/styled-element.spec.js.map +1 -0
- package/esm/styled-shade.spec.d.ts +2 -0
- package/esm/styled-shade.spec.d.ts.map +1 -0
- package/esm/styled-shade.spec.js +66 -0
- package/esm/styled-shade.spec.js.map +1 -0
- package/package.json +1 -1
- package/src/compile-route.spec.ts +39 -0
- package/src/components/route-link.spec.tsx +1 -1
- package/src/components/route-link.tsx +4 -5
- package/src/css-generator.spec.ts +183 -0
- package/src/css-generator.ts +117 -0
- package/src/index.ts +2 -0
- package/src/models/css-object.ts +34 -0
- package/src/models/index.ts +1 -0
- package/src/shade.spec.tsx +238 -0
- package/src/shade.ts +29 -2
- package/src/style-manager.spec.ts +229 -0
- package/src/style-manager.ts +104 -0
- package/src/styled-element.spec.tsx +117 -0
- package/src/styled-shade.spec.ts +86 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,51 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [11.1.0] - 2026-02-01
|
|
4
|
+
|
|
5
|
+
### ✨ Features
|
|
6
|
+
|
|
7
|
+
### CSS Property for Component-Level Styling
|
|
8
|
+
|
|
9
|
+
Added a new `css` property to `Shade()` components that enables defining component-level styles with support for pseudo-selectors and nested selectors. This provides a cleaner alternative to using `useState` for hover/focus states.
|
|
10
|
+
|
|
11
|
+
**Key benefits:**
|
|
12
|
+
|
|
13
|
+
- Define `:hover`, `:active`, `:focus`, and `:disabled` states declaratively
|
|
14
|
+
- Support for nested selectors (e.g., `& .className`, `& > div`)
|
|
15
|
+
- Styles are injected as a shared stylesheet, reducing DOM overhead
|
|
16
|
+
- Type-safe with the new `CSSObject` type
|
|
17
|
+
|
|
18
|
+
**Usage:**
|
|
19
|
+
|
|
20
|
+
```typescript
|
|
21
|
+
const Button = Shade({
|
|
22
|
+
shadowDomName: 'my-button',
|
|
23
|
+
css: {
|
|
24
|
+
padding: '12px 24px',
|
|
25
|
+
backgroundColor: 'blue',
|
|
26
|
+
cursor: 'pointer',
|
|
27
|
+
'&:hover': { backgroundColor: 'darkblue' },
|
|
28
|
+
'&:disabled': { opacity: '0.5', cursor: 'not-allowed' },
|
|
29
|
+
'& .icon': { marginRight: '8px' }
|
|
30
|
+
},
|
|
31
|
+
render: ({ children }) => <button>{children}</button>
|
|
32
|
+
})
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
- Added `CSSObject` type - type definition for CSS styles with nested selector support
|
|
36
|
+
- Added `StyleManager` - singleton that manages CSS injection and deduplication for components
|
|
37
|
+
- Added `generateCSS()` - utility function to convert `CSSObject` to CSS strings
|
|
38
|
+
- Extended support for customized built-in elements (e.g., `a[is="my-link"]` selectors)
|
|
39
|
+
|
|
40
|
+
### 📚 Documentation
|
|
41
|
+
|
|
42
|
+
- Updated README with documentation for the new `css` property and styling patterns
|
|
43
|
+
|
|
44
|
+
### 🧪 Tests
|
|
45
|
+
|
|
46
|
+
- Added tests for `generateCSS()` covering camelCase conversion, pseudo-selectors, and nested selectors
|
|
47
|
+
- Added tests for `StyleManager` covering component registration, deduplication, and customized built-in elements
|
|
48
|
+
|
|
3
49
|
## [11.0.35] - 2026-01-26
|
|
4
50
|
|
|
5
51
|
### 🔧 Chores
|
package/README.md
CHANGED
|
@@ -24,6 +24,92 @@ A shade (component) can be constructed from the following properties:
|
|
|
24
24
|
- `constructed: (options: RenderOptions)=>void` – Optional callback executed after component construction. It can return a cleanup method (e.g., free up resources, dispose value observers, etc.).
|
|
25
25
|
- `onAttach: (options: RenderOptions)=>void` – Executed when the component is attached to the DOM.
|
|
26
26
|
- `onDetach: (options: RenderOptions)=>void` – Executed when the component is detached from the DOM.
|
|
27
|
+
- `style` – Optional inline styles applied to each component instance. Use for per-instance overrides.
|
|
28
|
+
- `css` – Optional CSS styles injected as a stylesheet during component registration. Supports pseudo-selectors and nested selectors.
|
|
29
|
+
|
|
30
|
+
### Styling
|
|
31
|
+
|
|
32
|
+
Shades provides two complementary approaches to styling components:
|
|
33
|
+
|
|
34
|
+
#### `style` Property (Inline Styles)
|
|
35
|
+
|
|
36
|
+
The `style` property applies inline styles to each component instance. Use this for:
|
|
37
|
+
|
|
38
|
+
- Per-instance style overrides
|
|
39
|
+
- Dynamic styles that change based on props/state
|
|
40
|
+
- Quick prototyping
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
const MyComponent = Shade({
|
|
44
|
+
shadowDomName: 'my-component',
|
|
45
|
+
style: {
|
|
46
|
+
display: 'flex',
|
|
47
|
+
padding: '16px',
|
|
48
|
+
},
|
|
49
|
+
render: () => <div>Content</div>,
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
// Override styles on specific instances
|
|
53
|
+
<MyComponent style={{ marginTop: '20px' }} />
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
#### `css` Property (Stylesheet Injection)
|
|
57
|
+
|
|
58
|
+
The `css` property injects CSS rules into a stylesheet once per component type. Use this for:
|
|
59
|
+
|
|
60
|
+
- Component-level default styles
|
|
61
|
+
- Pseudo-selectors (`:hover`, `:active`, `:focus`, `:disabled`, etc.)
|
|
62
|
+
- Nested selectors (child elements, class names)
|
|
63
|
+
- Better performance (styles injected once, not per-instance)
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
const Button = Shade({
|
|
67
|
+
shadowDomName: 'my-button',
|
|
68
|
+
css: {
|
|
69
|
+
padding: '12px 24px',
|
|
70
|
+
backgroundColor: 'blue',
|
|
71
|
+
color: 'white',
|
|
72
|
+
border: 'none',
|
|
73
|
+
cursor: 'pointer',
|
|
74
|
+
transition: 'all 0.2s ease',
|
|
75
|
+
|
|
76
|
+
'&:hover': {
|
|
77
|
+
backgroundColor: 'darkblue',
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
'&:active': {
|
|
81
|
+
transform: 'scale(0.98)',
|
|
82
|
+
},
|
|
83
|
+
|
|
84
|
+
'&:disabled': {
|
|
85
|
+
opacity: '0.5',
|
|
86
|
+
cursor: 'not-allowed',
|
|
87
|
+
},
|
|
88
|
+
|
|
89
|
+
'& .icon': {
|
|
90
|
+
marginRight: '8px',
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
render: ({ props }) => (
|
|
94
|
+
<button disabled={props.disabled}>
|
|
95
|
+
{props.icon && <span className="icon">{props.icon}</span>}
|
|
96
|
+
{props.children}
|
|
97
|
+
</button>
|
|
98
|
+
),
|
|
99
|
+
})
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
#### When to Use Which
|
|
103
|
+
|
|
104
|
+
| Use Case | `style` | `css` |
|
|
105
|
+
| ------------------------- | ------- | ----- |
|
|
106
|
+
| Hover/focus/active states | ❌ | ✅ |
|
|
107
|
+
| Per-instance overrides | ✅ | ❌ |
|
|
108
|
+
| Nested element styling | ❌ | ✅ |
|
|
109
|
+
| Dynamic values from props | ✅ | ❌ |
|
|
110
|
+
| Component defaults | ⚠️ | ✅ |
|
|
111
|
+
|
|
112
|
+
Both properties can be used together. Inline `style` will override `css` due to CSS specificity rules.
|
|
27
113
|
|
|
28
114
|
### Render Options
|
|
29
115
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compile-route.spec.d.ts","sourceRoot":"","sources":["../src/compile-route.spec.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { compileRoute } from './compile-route.js';
|
|
3
|
+
describe('compile-route', () => {
|
|
4
|
+
describe('compileRoute', () => {
|
|
5
|
+
it('should compile route with string params', () => {
|
|
6
|
+
const result = compileRoute('/users/:id', { id: 'abc123' });
|
|
7
|
+
expect(result).toBe('/users/abc123');
|
|
8
|
+
});
|
|
9
|
+
it('should compile route with numeric params (stringified)', () => {
|
|
10
|
+
const result = compileRoute('/users/:id', { id: 42 });
|
|
11
|
+
expect(result).toBe('/users/42');
|
|
12
|
+
});
|
|
13
|
+
it('should compile route with multiple params', () => {
|
|
14
|
+
const result = compileRoute('/users/:userId/posts/:postId', {
|
|
15
|
+
userId: 'user1',
|
|
16
|
+
postId: 'post2',
|
|
17
|
+
});
|
|
18
|
+
expect(result).toBe('/users/user1/posts/post2');
|
|
19
|
+
});
|
|
20
|
+
it('should handle empty params object for static routes', () => {
|
|
21
|
+
const result = compileRoute('/home', {});
|
|
22
|
+
expect(result).toBe('/home');
|
|
23
|
+
});
|
|
24
|
+
it('should handle params with special characters', () => {
|
|
25
|
+
const result = compileRoute('/search/:query', { query: 'hello-world' });
|
|
26
|
+
expect(result).toBe('/search/hello-world');
|
|
27
|
+
});
|
|
28
|
+
it('should handle boolean params (stringified)', () => {
|
|
29
|
+
const result = compileRoute('/feature/:enabled', { enabled: true });
|
|
30
|
+
expect(result).toBe('/feature/true');
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
//# sourceMappingURL=compile-route.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compile-route.spec.js","sourceRoot":"","sources":["../src/compile-route.spec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAA;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAEjD,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;IAC7B,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;QAC5B,EAAE,CAAC,yCAAyC,EAAE,GAAG,EAAE;YACjD,MAAM,MAAM,GAAG,YAAY,CAAC,YAAY,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAA;YAC3D,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,CAAA;QACtC,CAAC,CAAC,CAAA;QAEF,EAAE,CAAC,wDAAwD,EAAE,GAAG,EAAE;YAChE,MAAM,MAAM,GAAG,YAAY,CAAC,YAAY,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAA;YACrD,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAA;QAClC,CAAC,CAAC,CAAA;QAEF,EAAE,CAAC,2CAA2C,EAAE,GAAG,EAAE;YACnD,MAAM,MAAM,GAAG,YAAY,CAAC,8BAA8B,EAAE;gBAC1D,MAAM,EAAE,OAAO;gBACf,MAAM,EAAE,OAAO;aAChB,CAAC,CAAA;YACF,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAA;QACjD,CAAC,CAAC,CAAA;QAEF,EAAE,CAAC,qDAAqD,EAAE,GAAG,EAAE;YAC7D,MAAM,MAAM,GAAG,YAAY,CAAC,OAAO,EAAE,EAAE,CAAC,CAAA;YACxC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QAC9B,CAAC,CAAC,CAAA;QAEF,EAAE,CAAC,8CAA8C,EAAE,GAAG,EAAE;YACtD,MAAM,MAAM,GAAG,YAAY,CAAC,gBAAgB,EAAE,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC,CAAA;YACvE,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAA;QAC5C,CAAC,CAAC,CAAA;QAEF,EAAE,CAAC,4CAA4C,EAAE,GAAG,EAAE;YACpD,MAAM,MAAM,GAAG,YAAY,CAAC,mBAAmB,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAA;YACnE,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,CAAA;QACtC,CAAC,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"route-link.d.ts","sourceRoot":"","sources":["../../src/components/route-link.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAA;AAIlE,MAAM,MAAM,cAAc,GAAG,cAAc,CAAC,IAAI,CAAC,iBAAiB,EAAE,SAAS,CAAC,CAAC,CAAA;AAE/E,eAAO,MAAM,SAAS;;
|
|
1
|
+
{"version":3,"file":"route-link.d.ts","sourceRoot":"","sources":["../../src/components/route-link.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAA;AAIlE,MAAM,MAAM,cAAc,GAAG,cAAc,CAAC,IAAI,CAAC,iBAAiB,EAAE,SAAS,CAAC,CAAC,CAAA;AAE/E,eAAO,MAAM,SAAS;;sGAmBpB,CAAA"}
|
|
@@ -5,14 +5,13 @@ export const RouteLink = Shade({
|
|
|
5
5
|
shadowDomName: 'route-link',
|
|
6
6
|
elementBase: HTMLAnchorElement,
|
|
7
7
|
elementBaseName: 'a',
|
|
8
|
+
css: {
|
|
9
|
+
color: 'inherit',
|
|
10
|
+
textDecoration: 'inherit',
|
|
11
|
+
},
|
|
8
12
|
render: ({ children, props, injector, element }) => {
|
|
9
13
|
attachProps(element, {
|
|
10
14
|
...props,
|
|
11
|
-
style: {
|
|
12
|
-
color: 'inherit',
|
|
13
|
-
textDecoration: 'inherit',
|
|
14
|
-
...props.style,
|
|
15
|
-
},
|
|
16
15
|
onclick: (ev) => {
|
|
17
16
|
ev.preventDefault();
|
|
18
17
|
history.pushState('', props.title || '', props.href);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"route-link.js","sourceRoot":"","sources":["../../src/components/route-link.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AAEnC,OAAO,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAA;AACjE,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAIpE,MAAM,CAAC,MAAM,SAAS,GAAG,KAAK,CAAiB;IAC7C,aAAa,EAAE,YAAY;IAC3B,WAAW,EAAE,iBAAiB;IAC9B,eAAe,EAAE,GAAG;IACpB,MAAM,EAAE,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,EAAE;QACjD,WAAW,CAAC,OAAO,EAAE;YACnB,GAAG,KAAK;YACR,
|
|
1
|
+
{"version":3,"file":"route-link.js","sourceRoot":"","sources":["../../src/components/route-link.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AAEnC,OAAO,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAA;AACjE,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAIpE,MAAM,CAAC,MAAM,SAAS,GAAG,KAAK,CAAiB;IAC7C,aAAa,EAAE,YAAY;IAC3B,WAAW,EAAE,iBAAiB;IAC9B,eAAe,EAAE,GAAG;IACpB,GAAG,EAAE;QACH,KAAK,EAAE,SAAS;QAChB,cAAc,EAAE,SAAS;KAC1B;IACD,MAAM,EAAE,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,EAAE;QACjD,WAAW,CAAC,OAAO,EAAE;YACnB,GAAG,KAAK;YACR,OAAO,EAAE,CAAC,EAAc,EAAE,EAAE;gBAC1B,EAAE,CAAC,cAAc,EAAE,CAAA;gBACnB,OAAO,CAAC,SAAS,CAAC,EAAE,EAAE,KAAK,CAAC,KAAK,IAAI,EAAE,EAAE,KAAK,CAAC,IAAI,CAAC,CAAA;gBACpD,QAAQ,CAAC,WAAW,CAAC,eAAe,CAAC,CAAC,WAAW,EAAE,CAAA;YACrD,CAAC;SACF,CAAC,CAAA;QACF,OAAO,uCAAG,QAAQ,CAAI,CAAA;IACxB,CAAC;CACF,CAAC,CAAA"}
|
|
@@ -21,7 +21,7 @@ describe('RouteLink', () => {
|
|
|
21
21
|
rootElement,
|
|
22
22
|
jsxElement: (createComponent(RouteLink, { id: "route", href: "/subroute" }, "Link")),
|
|
23
23
|
});
|
|
24
|
-
expect(document.body.innerHTML).toMatchInlineSnapshot(`"<div id="root"><a is="route-link" id="route" href="/subroute"
|
|
24
|
+
expect(document.body.innerHTML).toMatchInlineSnapshot(`"<div id="root"><a is="route-link" id="route" href="/subroute">Link</a></div>"`);
|
|
25
25
|
expect(onRouteChange).not.toBeCalled();
|
|
26
26
|
document.getElementById('route')?.click();
|
|
27
27
|
expect(onRouteChange).toBeCalledTimes(1);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"route-link.spec.js","sourceRoot":"","sources":["../../src/components/route-link.spec.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAC5C,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAA;AACxE,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAA;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAA;AACjE,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AACvD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAE3C,QAAQ,CAAC,WAAW,EAAE,GAAG,EAAE;IACzB,UAAU,CAAC,GAAG,EAAE;QACd,QAAQ,CAAC,IAAI,CAAC,SAAS,GAAG,uBAAuB,CAAA;IACnD,CAAC,CAAC,CAAA;IACF,SAAS,CAAC,GAAG,EAAE;QACb,QAAQ,CAAC,IAAI,CAAC,SAAS,GAAG,EAAE,CAAA;IAC9B,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,8CAA8C,EAAE,KAAK,IAAI,EAAE;QAC5D,MAAM,QAAQ,GAAG,IAAI,QAAQ,EAAE,CAAA;QAC/B,MAAM,WAAW,GAAG,QAAQ,CAAC,cAAc,CAAC,MAAM,CAAmB,CAAA;QAErE,MAAM,aAAa,GAAG,EAAE,CAAC,EAAE,EAAE,CAAA;QAE7B,QAAQ,CAAC,WAAW,CAAC,eAAe,CAAC,CAAC,qBAAqB,CAAC,SAAS,CAAC,aAAa,CAAC,CAAA;QAEpF,mBAAmB,CAAC;YAClB,QAAQ;YACR,WAAW;YACX,UAAU,EAAE,CACV,gBAAC,SAAS,IAAC,EAAE,EAAC,OAAO,EAAC,IAAI,EAAC,WAAW,WAE1B,CACb;SACF,CAAC,CAAA;QACF,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,qBAAqB,CACnD,
|
|
1
|
+
{"version":3,"file":"route-link.spec.js","sourceRoot":"","sources":["../../src/components/route-link.spec.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAC5C,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAA;AACxE,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAA;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAA;AACjE,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AACvD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAE3C,QAAQ,CAAC,WAAW,EAAE,GAAG,EAAE;IACzB,UAAU,CAAC,GAAG,EAAE;QACd,QAAQ,CAAC,IAAI,CAAC,SAAS,GAAG,uBAAuB,CAAA;IACnD,CAAC,CAAC,CAAA;IACF,SAAS,CAAC,GAAG,EAAE;QACb,QAAQ,CAAC,IAAI,CAAC,SAAS,GAAG,EAAE,CAAA;IAC9B,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,8CAA8C,EAAE,KAAK,IAAI,EAAE;QAC5D,MAAM,QAAQ,GAAG,IAAI,QAAQ,EAAE,CAAA;QAC/B,MAAM,WAAW,GAAG,QAAQ,CAAC,cAAc,CAAC,MAAM,CAAmB,CAAA;QAErE,MAAM,aAAa,GAAG,EAAE,CAAC,EAAE,EAAE,CAAA;QAE7B,QAAQ,CAAC,WAAW,CAAC,eAAe,CAAC,CAAC,qBAAqB,CAAC,SAAS,CAAC,aAAa,CAAC,CAAA;QAEpF,mBAAmB,CAAC;YAClB,QAAQ;YACR,WAAW;YACX,UAAU,EAAE,CACV,gBAAC,SAAS,IAAC,EAAE,EAAC,OAAO,EAAC,IAAI,EAAC,WAAW,WAE1B,CACb;SACF,CAAC,CAAA;QACF,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,qBAAqB,CACnD,gFAAgF,CACjF,CAAA;QACD,MAAM,CAAC,aAAa,CAAC,CAAC,GAAG,CAAC,UAAU,EAAE,CAAA;QACtC,QAAQ,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,CAAA;QACzC,MAAM,CAAC,aAAa,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAA;IAC1C,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { CSSObject, CSSProperties } from './models/css-object.js';
|
|
2
|
+
/**
|
|
3
|
+
* Converts a camelCase string to kebab-case
|
|
4
|
+
* @param str - The camelCase string to convert
|
|
5
|
+
* @returns The kebab-case string
|
|
6
|
+
* @example
|
|
7
|
+
* camelToKebab('backgroundColor') // 'background-color'
|
|
8
|
+
* camelToKebab('fontSize') // 'font-size'
|
|
9
|
+
*/
|
|
10
|
+
export declare const camelToKebab: (str: string) => string;
|
|
11
|
+
/**
|
|
12
|
+
* Checks if a key is a selector key (starts with '&')
|
|
13
|
+
* @param key - The key to check
|
|
14
|
+
* @returns True if the key is a selector key
|
|
15
|
+
*/
|
|
16
|
+
export declare const isSelectorKey: (key: string) => boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Converts CSS properties to a CSS declaration string
|
|
19
|
+
* @param properties - The CSS properties object
|
|
20
|
+
* @returns A CSS declaration string (e.g., "color: red; padding: 10px;")
|
|
21
|
+
*/
|
|
22
|
+
export declare const propertiesToCSSString: (properties: CSSProperties) => string;
|
|
23
|
+
/**
|
|
24
|
+
* Generates a CSS rule string from a selector and properties
|
|
25
|
+
* @param selector - The CSS selector
|
|
26
|
+
* @param properties - The CSS properties object
|
|
27
|
+
* @returns A complete CSS rule string (e.g., "selector { color: red; }")
|
|
28
|
+
*/
|
|
29
|
+
export declare const generateCSSRule: (selector: string, properties: CSSProperties) => string;
|
|
30
|
+
/**
|
|
31
|
+
* Generates complete CSS from a CSSObject for a given component selector
|
|
32
|
+
* @param selector - The base selector (typically the shadowDomName)
|
|
33
|
+
* @param cssObject - The CSSObject containing styles and nested selectors
|
|
34
|
+
* @returns A complete CSS string with all rules
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```typescript
|
|
38
|
+
* generateCSS('my-component', {
|
|
39
|
+
* color: 'red',
|
|
40
|
+
* '&:hover': { color: 'blue' },
|
|
41
|
+
* '& .inner': { fontWeight: 'bold' }
|
|
42
|
+
* })
|
|
43
|
+
* // Returns:
|
|
44
|
+
* // "my-component { color: red; }
|
|
45
|
+
* // my-component:hover { color: blue; }
|
|
46
|
+
* // my-component .inner { font-weight: bold; }"
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
export declare const generateCSS: (selector: string, cssObject: CSSObject) => string;
|
|
50
|
+
//# sourceMappingURL=css-generator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"css-generator.d.ts","sourceRoot":"","sources":["../src/css-generator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAA;AAEtE;;;;;;;GAOG;AACH,eAAO,MAAM,YAAY,GAAI,KAAK,MAAM,KAAG,MAE1C,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,aAAa,GAAI,KAAK,MAAM,KAAG,OAE3C,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,GAAI,YAAY,aAAa,KAAG,MAajE,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,eAAe,GAAI,UAAU,MAAM,EAAE,YAAY,aAAa,KAAG,MAM7E,CAAA;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,WAAW,GAAI,UAAU,MAAM,EAAE,WAAW,SAAS,KAAG,MAwCpE,CAAA"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Converts a camelCase string to kebab-case
|
|
3
|
+
* @param str - The camelCase string to convert
|
|
4
|
+
* @returns The kebab-case string
|
|
5
|
+
* @example
|
|
6
|
+
* camelToKebab('backgroundColor') // 'background-color'
|
|
7
|
+
* camelToKebab('fontSize') // 'font-size'
|
|
8
|
+
*/
|
|
9
|
+
export const camelToKebab = (str) => {
|
|
10
|
+
return str.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`);
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Checks if a key is a selector key (starts with '&')
|
|
14
|
+
* @param key - The key to check
|
|
15
|
+
* @returns True if the key is a selector key
|
|
16
|
+
*/
|
|
17
|
+
export const isSelectorKey = (key) => {
|
|
18
|
+
return key.startsWith('&');
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Converts CSS properties to a CSS declaration string
|
|
22
|
+
* @param properties - The CSS properties object
|
|
23
|
+
* @returns A CSS declaration string (e.g., "color: red; padding: 10px;")
|
|
24
|
+
*/
|
|
25
|
+
export const propertiesToCSSString = (properties) => {
|
|
26
|
+
const declarations = [];
|
|
27
|
+
for (const key in properties) {
|
|
28
|
+
if (Object.prototype.hasOwnProperty.call(properties, key) && !isSelectorKey(key)) {
|
|
29
|
+
const value = properties[key];
|
|
30
|
+
if (value !== undefined && value !== null && value !== '' && typeof value === 'string') {
|
|
31
|
+
declarations.push(`${camelToKebab(key)}: ${value}`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return declarations.join('; ');
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Generates a CSS rule string from a selector and properties
|
|
39
|
+
* @param selector - The CSS selector
|
|
40
|
+
* @param properties - The CSS properties object
|
|
41
|
+
* @returns A complete CSS rule string (e.g., "selector { color: red; }")
|
|
42
|
+
*/
|
|
43
|
+
export const generateCSSRule = (selector, properties) => {
|
|
44
|
+
const cssString = propertiesToCSSString(properties);
|
|
45
|
+
if (!cssString) {
|
|
46
|
+
return '';
|
|
47
|
+
}
|
|
48
|
+
return `${selector} { ${cssString}; }`;
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* Generates complete CSS from a CSSObject for a given component selector
|
|
52
|
+
* @param selector - The base selector (typically the shadowDomName)
|
|
53
|
+
* @param cssObject - The CSSObject containing styles and nested selectors
|
|
54
|
+
* @returns A complete CSS string with all rules
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* ```typescript
|
|
58
|
+
* generateCSS('my-component', {
|
|
59
|
+
* color: 'red',
|
|
60
|
+
* '&:hover': { color: 'blue' },
|
|
61
|
+
* '& .inner': { fontWeight: 'bold' }
|
|
62
|
+
* })
|
|
63
|
+
* // Returns:
|
|
64
|
+
* // "my-component { color: red; }
|
|
65
|
+
* // my-component:hover { color: blue; }
|
|
66
|
+
* // my-component .inner { font-weight: bold; }"
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
69
|
+
export const generateCSS = (selector, cssObject) => {
|
|
70
|
+
const rules = [];
|
|
71
|
+
// Extract base properties (non-selector keys)
|
|
72
|
+
const baseProperties = {};
|
|
73
|
+
const selectorRules = [];
|
|
74
|
+
for (const key in cssObject) {
|
|
75
|
+
if (Object.prototype.hasOwnProperty.call(cssObject, key)) {
|
|
76
|
+
if (isSelectorKey(key)) {
|
|
77
|
+
const properties = cssObject[key];
|
|
78
|
+
if (properties && typeof properties === 'object') {
|
|
79
|
+
selectorRules.push({ selectorKey: key, properties: properties });
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
const value = cssObject[key];
|
|
84
|
+
if (typeof value !== 'object') {
|
|
85
|
+
;
|
|
86
|
+
baseProperties[key] = value;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
// Generate base rule
|
|
92
|
+
const baseRule = generateCSSRule(selector, baseProperties);
|
|
93
|
+
if (baseRule) {
|
|
94
|
+
rules.push(baseRule);
|
|
95
|
+
}
|
|
96
|
+
// Generate selector rules
|
|
97
|
+
for (const { selectorKey, properties } of selectorRules) {
|
|
98
|
+
// Replace '&' with the base selector
|
|
99
|
+
const fullSelector = selectorKey.replace(/&/g, selector);
|
|
100
|
+
const rule = generateCSSRule(fullSelector, properties);
|
|
101
|
+
if (rule) {
|
|
102
|
+
rules.push(rule);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return rules.join('\n');
|
|
106
|
+
};
|
|
107
|
+
//# sourceMappingURL=css-generator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"css-generator.js","sourceRoot":"","sources":["../src/css-generator.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,GAAW,EAAU,EAAE;IAClD,OAAO,GAAG,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC,CAAA;AACpE,CAAC,CAAA;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,GAAW,EAAW,EAAE;IACpD,OAAO,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAA;AAC5B,CAAC,CAAA;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,UAAyB,EAAU,EAAE;IACzE,MAAM,YAAY,GAAa,EAAE,CAAA;IAEjC,KAAK,MAAM,GAAG,IAAI,UAAU,EAAE,CAAC;QAC7B,IAAI,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,UAAU,EAAE,GAAG,CAAC,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,EAAE,CAAC;YACjF,MAAM,KAAK,GAAG,UAAU,CAAC,GAA0B,CAAC,CAAA;YACpD,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,EAAE,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;gBACvF,YAAY,CAAC,IAAI,CAAC,GAAG,YAAY,CAAC,GAAG,CAAC,KAAK,KAAK,EAAE,CAAC,CAAA;YACrD,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AAChC,CAAC,CAAA;AAED;;;;;GAKG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,QAAgB,EAAE,UAAyB,EAAU,EAAE;IACrF,MAAM,SAAS,GAAG,qBAAqB,CAAC,UAAU,CAAC,CAAA;IACnD,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,OAAO,EAAE,CAAA;IACX,CAAC;IACD,OAAO,GAAG,QAAQ,MAAM,SAAS,KAAK,CAAA;AACxC,CAAC,CAAA;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,QAAgB,EAAE,SAAoB,EAAU,EAAE;IAC5E,MAAM,KAAK,GAAa,EAAE,CAAA;IAE1B,8CAA8C;IAC9C,MAAM,cAAc,GAAkB,EAAE,CAAA;IACxC,MAAM,aAAa,GAA8D,EAAE,CAAA;IAEnF,KAAK,MAAM,GAAG,IAAI,SAAS,EAAE,CAAC;QAC5B,IAAI,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,CAAC,EAAE,CAAC;YACzD,IAAI,aAAa,CAAC,GAAG,CAAC,EAAE,CAAC;gBACvB,MAAM,UAAU,GAAG,SAAS,CAAC,GAAsB,CAAC,CAAA;gBACpD,IAAI,UAAU,IAAI,OAAO,UAAU,KAAK,QAAQ,EAAE,CAAC;oBACjD,aAAa,CAAC,IAAI,CAAC,EAAE,WAAW,EAAE,GAAG,EAAE,UAAU,EAAE,UAA2B,EAAE,CAAC,CAAA;gBACnF,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,MAAM,KAAK,GAAG,SAAS,CAAC,GAAsB,CAAC,CAAA;gBAC/C,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;oBAC9B,CAAC;oBAAC,cAA0C,CAAC,GAAG,CAAC,GAAG,KAAK,CAAA;gBAC3D,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,qBAAqB;IACrB,MAAM,QAAQ,GAAG,eAAe,CAAC,QAAQ,EAAE,cAAc,CAAC,CAAA;IAC1D,IAAI,QAAQ,EAAE,CAAC;QACb,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;IACtB,CAAC;IAED,0BAA0B;IAC1B,KAAK,MAAM,EAAE,WAAW,EAAE,UAAU,EAAE,IAAI,aAAa,EAAE,CAAC;QACxD,qCAAqC;QACrC,MAAM,YAAY,GAAG,WAAW,CAAC,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAA;QACxD,MAAM,IAAI,GAAG,eAAe,CAAC,YAAY,EAAE,UAAU,CAAC,CAAA;QACtD,IAAI,IAAI,EAAE,CAAC;YACT,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAClB,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACzB,CAAC,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"css-generator.spec.d.ts","sourceRoot":"","sources":["../src/css-generator.spec.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { camelToKebab, generateCSS, generateCSSRule, isSelectorKey, propertiesToCSSString } from './css-generator.js';
|
|
3
|
+
describe('css-generator', () => {
|
|
4
|
+
describe('camelToKebab', () => {
|
|
5
|
+
it('should convert camelCase to kebab-case', () => {
|
|
6
|
+
expect(camelToKebab('backgroundColor')).toBe('background-color');
|
|
7
|
+
expect(camelToKebab('fontSize')).toBe('font-size');
|
|
8
|
+
expect(camelToKebab('borderTopLeftRadius')).toBe('border-top-left-radius');
|
|
9
|
+
});
|
|
10
|
+
it('should handle single word properties', () => {
|
|
11
|
+
expect(camelToKebab('color')).toBe('color');
|
|
12
|
+
expect(camelToKebab('margin')).toBe('margin');
|
|
13
|
+
});
|
|
14
|
+
it('should handle empty string', () => {
|
|
15
|
+
expect(camelToKebab('')).toBe('');
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
describe('isSelectorKey', () => {
|
|
19
|
+
it('should return true for selector keys starting with &', () => {
|
|
20
|
+
expect(isSelectorKey('&:hover')).toBe(true);
|
|
21
|
+
expect(isSelectorKey('&:active')).toBe(true);
|
|
22
|
+
expect(isSelectorKey('& .className')).toBe(true);
|
|
23
|
+
expect(isSelectorKey('& > div')).toBe(true);
|
|
24
|
+
});
|
|
25
|
+
it('should return false for regular CSS property keys', () => {
|
|
26
|
+
expect(isSelectorKey('color')).toBe(false);
|
|
27
|
+
expect(isSelectorKey('backgroundColor')).toBe(false);
|
|
28
|
+
expect(isSelectorKey('fontSize')).toBe(false);
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
describe('propertiesToCSSString', () => {
|
|
32
|
+
it('should convert CSS properties object to CSS string', () => {
|
|
33
|
+
const result = propertiesToCSSString({
|
|
34
|
+
color: 'red',
|
|
35
|
+
backgroundColor: 'blue',
|
|
36
|
+
});
|
|
37
|
+
expect(result).toBe('color: red; background-color: blue');
|
|
38
|
+
});
|
|
39
|
+
it('should skip undefined and null values', () => {
|
|
40
|
+
const result = propertiesToCSSString({
|
|
41
|
+
color: 'red',
|
|
42
|
+
backgroundColor: undefined,
|
|
43
|
+
});
|
|
44
|
+
expect(result).toBe('color: red');
|
|
45
|
+
});
|
|
46
|
+
it('should skip empty string values', () => {
|
|
47
|
+
const result = propertiesToCSSString({
|
|
48
|
+
color: 'red',
|
|
49
|
+
backgroundColor: '',
|
|
50
|
+
});
|
|
51
|
+
expect(result).toBe('color: red');
|
|
52
|
+
});
|
|
53
|
+
it('should return empty string for empty object', () => {
|
|
54
|
+
const result = propertiesToCSSString({});
|
|
55
|
+
expect(result).toBe('');
|
|
56
|
+
});
|
|
57
|
+
it('should ignore selector keys', () => {
|
|
58
|
+
// Type assertion needed to test mixed object with selectors
|
|
59
|
+
const mixedObject = {
|
|
60
|
+
color: 'red',
|
|
61
|
+
'&:hover': { color: 'blue' },
|
|
62
|
+
};
|
|
63
|
+
const result = propertiesToCSSString(mixedObject);
|
|
64
|
+
expect(result).toBe('color: red');
|
|
65
|
+
});
|
|
66
|
+
it('should filter out non-string values', () => {
|
|
67
|
+
// Type assertion to test edge case with non-string values
|
|
68
|
+
const mixedObject = {
|
|
69
|
+
color: 'red',
|
|
70
|
+
opacity: 0.5, // number - should be filtered
|
|
71
|
+
display: 'flex',
|
|
72
|
+
hidden: true, // boolean - should be filtered
|
|
73
|
+
};
|
|
74
|
+
const result = propertiesToCSSString(mixedObject);
|
|
75
|
+
expect(result).toBe('color: red; display: flex');
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
describe('generateCSSRule', () => {
|
|
79
|
+
it('should generate a complete CSS rule', () => {
|
|
80
|
+
const result = generateCSSRule('my-component', {
|
|
81
|
+
color: 'red',
|
|
82
|
+
padding: '10px',
|
|
83
|
+
});
|
|
84
|
+
expect(result).toBe('my-component { color: red; padding: 10px; }');
|
|
85
|
+
});
|
|
86
|
+
it('should return empty string for empty properties', () => {
|
|
87
|
+
const result = generateCSSRule('my-component', {});
|
|
88
|
+
expect(result).toBe('');
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
describe('generateCSS', () => {
|
|
92
|
+
it('should generate CSS for base properties only', () => {
|
|
93
|
+
const result = generateCSS('my-component', {
|
|
94
|
+
color: 'red',
|
|
95
|
+
padding: '10px',
|
|
96
|
+
});
|
|
97
|
+
expect(result).toBe('my-component { color: red; padding: 10px; }');
|
|
98
|
+
});
|
|
99
|
+
it('should generate CSS with pseudo-selectors', () => {
|
|
100
|
+
const result = generateCSS('my-component', {
|
|
101
|
+
color: 'red',
|
|
102
|
+
'&:hover': { color: 'blue' },
|
|
103
|
+
});
|
|
104
|
+
expect(result).toContain('my-component { color: red; }');
|
|
105
|
+
expect(result).toContain('my-component:hover { color: blue; }');
|
|
106
|
+
});
|
|
107
|
+
it('should generate CSS with nested class selectors', () => {
|
|
108
|
+
const result = generateCSS('my-component', {
|
|
109
|
+
padding: '10px',
|
|
110
|
+
'& .inner': { fontWeight: 'bold' },
|
|
111
|
+
});
|
|
112
|
+
expect(result).toContain('my-component { padding: 10px; }');
|
|
113
|
+
expect(result).toContain('my-component .inner { font-weight: bold; }');
|
|
114
|
+
});
|
|
115
|
+
it('should generate CSS with child selectors', () => {
|
|
116
|
+
const result = generateCSS('my-component', {
|
|
117
|
+
display: 'flex',
|
|
118
|
+
'& > div': { margin: '5px' },
|
|
119
|
+
});
|
|
120
|
+
expect(result).toContain('my-component { display: flex; }');
|
|
121
|
+
expect(result).toContain('my-component > div { margin: 5px; }');
|
|
122
|
+
});
|
|
123
|
+
it('should handle multiple pseudo-selectors', () => {
|
|
124
|
+
const result = generateCSS('my-button', {
|
|
125
|
+
backgroundColor: 'blue',
|
|
126
|
+
'&:hover': { backgroundColor: 'darkblue' },
|
|
127
|
+
'&:active': { backgroundColor: 'navy' },
|
|
128
|
+
'&:disabled': { opacity: '0.5' },
|
|
129
|
+
});
|
|
130
|
+
expect(result).toContain('my-button { background-color: blue; }');
|
|
131
|
+
expect(result).toContain('my-button:hover { background-color: darkblue; }');
|
|
132
|
+
expect(result).toContain('my-button:active { background-color: navy; }');
|
|
133
|
+
expect(result).toContain('my-button:disabled { opacity: 0.5; }');
|
|
134
|
+
});
|
|
135
|
+
it('should handle empty css object', () => {
|
|
136
|
+
const result = generateCSS('my-component', {});
|
|
137
|
+
expect(result).toBe('');
|
|
138
|
+
});
|
|
139
|
+
it('should handle css object with only selectors', () => {
|
|
140
|
+
const result = generateCSS('my-component', {
|
|
141
|
+
'&:hover': { color: 'blue' },
|
|
142
|
+
});
|
|
143
|
+
expect(result).toBe('my-component:hover { color: blue; }');
|
|
144
|
+
});
|
|
145
|
+
it('should skip selector keys with non-object values', () => {
|
|
146
|
+
// Type assertion to test edge case with invalid selector values
|
|
147
|
+
const cssObject = {
|
|
148
|
+
color: 'red',
|
|
149
|
+
'&:hover': 'invalid', // string instead of object - should be skipped
|
|
150
|
+
'&:active': null, // null - should be skipped
|
|
151
|
+
'&:focus': { backgroundColor: 'blue' }, // valid - should be included
|
|
152
|
+
};
|
|
153
|
+
const result = generateCSS('my-component', cssObject);
|
|
154
|
+
expect(result).toContain('my-component { color: red; }');
|
|
155
|
+
expect(result).toContain('my-component:focus { background-color: blue; }');
|
|
156
|
+
expect(result).not.toContain('invalid');
|
|
157
|
+
expect(result).not.toContain(':hover');
|
|
158
|
+
expect(result).not.toContain(':active');
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
//# sourceMappingURL=css-generator.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"css-generator.spec.js","sourceRoot":"","sources":["../src/css-generator.spec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAA;AAC7C,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,eAAe,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAA;AAGrH,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;IAC7B,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;QAC5B,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;YAChD,MAAM,CAAC,YAAY,CAAC,iBAAiB,CAAC,CAAC,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAA;YAChE,MAAM,CAAC,YAAY,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAA;YAClD,MAAM,CAAC,YAAY,CAAC,qBAAqB,CAAC,CAAC,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAA;QAC5E,CAAC,CAAC,CAAA;QAEF,EAAE,CAAC,sCAAsC,EAAE,GAAG,EAAE;YAC9C,MAAM,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;YAC3C,MAAM,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;QAC/C,CAAC,CAAC,CAAA;QAEF,EAAE,CAAC,4BAA4B,EAAE,GAAG,EAAE;YACpC,MAAM,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;QACnC,CAAC,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;QAC7B,EAAE,CAAC,sDAAsD,EAAE,GAAG,EAAE;YAC9D,MAAM,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;YAC3C,MAAM,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;YAC5C,MAAM,CAAC,aAAa,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;YAChD,MAAM,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAC7C,CAAC,CAAC,CAAA;QAEF,EAAE,CAAC,mDAAmD,EAAE,GAAG,EAAE;YAC3D,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;YAC1C,MAAM,CAAC,aAAa,CAAC,iBAAiB,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;YACpD,MAAM,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QAC/C,CAAC,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,QAAQ,CAAC,uBAAuB,EAAE,GAAG,EAAE;QACrC,EAAE,CAAC,oDAAoD,EAAE,GAAG,EAAE;YAC5D,MAAM,MAAM,GAAG,qBAAqB,CAAC;gBACnC,KAAK,EAAE,KAAK;gBACZ,eAAe,EAAE,MAAM;aACxB,CAAC,CAAA;YACF,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,oCAAoC,CAAC,CAAA;QAC3D,CAAC,CAAC,CAAA;QAEF,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;YAC/C,MAAM,MAAM,GAAG,qBAAqB,CAAC;gBACnC,KAAK,EAAE,KAAK;gBACZ,eAAe,EAAE,SAAS;aAC3B,CAAC,CAAA;YACF,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAA;QACnC,CAAC,CAAC,CAAA;QAEF,EAAE,CAAC,iCAAiC,EAAE,GAAG,EAAE;YACzC,MAAM,MAAM,GAAG,qBAAqB,CAAC;gBACnC,KAAK,EAAE,KAAK;gBACZ,eAAe,EAAE,EAAE;aACpB,CAAC,CAAA;YACF,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAA;QACnC,CAAC,CAAC,CAAA;QAEF,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;YACrD,MAAM,MAAM,GAAG,qBAAqB,CAAC,EAAE,CAAC,CAAA;YACxC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;QACzB,CAAC,CAAC,CAAA;QAEF,EAAE,CAAC,6BAA6B,EAAE,GAAG,EAAE;YACrC,4DAA4D;YAC5D,MAAM,WAAW,GAAG;gBAClB,KAAK,EAAE,KAAK;gBACZ,SAAS,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE;aAC7B,CAAA;YACD,MAAM,MAAM,GAAG,qBAAqB,CAAC,WAAuC,CAAC,CAAA;YAC7E,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAA;QACnC,CAAC,CAAC,CAAA;QAEF,EAAE,CAAC,qCAAqC,EAAE,GAAG,EAAE;YAC7C,0DAA0D;YAC1D,MAAM,WAAW,GAAG;gBAClB,KAAK,EAAE,KAAK;gBACZ,OAAO,EAAE,GAAG,EAAE,8BAA8B;gBAC5C,OAAO,EAAE,MAAM;gBACf,MAAM,EAAE,IAAI,EAAE,+BAA+B;aAC9C,CAAA;YACD,MAAM,MAAM,GAAG,qBAAqB,CAAC,WAAuC,CAAC,CAAA;YAC7E,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAA;QAClD,CAAC,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,QAAQ,CAAC,iBAAiB,EAAE,GAAG,EAAE;QAC/B,EAAE,CAAC,qCAAqC,EAAE,GAAG,EAAE;YAC7C,MAAM,MAAM,GAAG,eAAe,CAAC,cAAc,EAAE;gBAC7C,KAAK,EAAE,KAAK;gBACZ,OAAO,EAAE,MAAM;aAChB,CAAC,CAAA;YACF,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,6CAA6C,CAAC,CAAA;QACpE,CAAC,CAAC,CAAA;QAEF,EAAE,CAAC,iDAAiD,EAAE,GAAG,EAAE;YACzD,MAAM,MAAM,GAAG,eAAe,CAAC,cAAc,EAAE,EAAE,CAAC,CAAA;YAClD,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;QACzB,CAAC,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,QAAQ,CAAC,aAAa,EAAE,GAAG,EAAE;QAC3B,EAAE,CAAC,8CAA8C,EAAE,GAAG,EAAE;YACtD,MAAM,MAAM,GAAG,WAAW,CAAC,cAAc,EAAE;gBACzC,KAAK,EAAE,KAAK;gBACZ,OAAO,EAAE,MAAM;aAChB,CAAC,CAAA;YACF,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,6CAA6C,CAAC,CAAA;QACpE,CAAC,CAAC,CAAA;QAEF,EAAE,CAAC,2CAA2C,EAAE,GAAG,EAAE;YACnD,MAAM,MAAM,GAAG,WAAW,CAAC,cAAc,EAAE;gBACzC,KAAK,EAAE,KAAK;gBACZ,SAAS,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE;aAC7B,CAAC,CAAA;YACF,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,8BAA8B,CAAC,CAAA;YACxD,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,qCAAqC,CAAC,CAAA;QACjE,CAAC,CAAC,CAAA;QAEF,EAAE,CAAC,iDAAiD,EAAE,GAAG,EAAE;YACzD,MAAM,MAAM,GAAG,WAAW,CAAC,cAAc,EAAE;gBACzC,OAAO,EAAE,MAAM;gBACf,UAAU,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE;aACnC,CAAC,CAAA;YACF,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,iCAAiC,CAAC,CAAA;YAC3D,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,4CAA4C,CAAC,CAAA;QACxE,CAAC,CAAC,CAAA;QAEF,EAAE,CAAC,0CAA0C,EAAE,GAAG,EAAE;YAClD,MAAM,MAAM,GAAG,WAAW,CAAC,cAAc,EAAE;gBACzC,OAAO,EAAE,MAAM;gBACf,SAAS,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE;aAC7B,CAAC,CAAA;YACF,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,iCAAiC,CAAC,CAAA;YAC3D,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,qCAAqC,CAAC,CAAA;QACjE,CAAC,CAAC,CAAA;QAEF,EAAE,CAAC,yCAAyC,EAAE,GAAG,EAAE;YACjD,MAAM,MAAM,GAAG,WAAW,CAAC,WAAW,EAAE;gBACtC,eAAe,EAAE,MAAM;gBACvB,SAAS,EAAE,EAAE,eAAe,EAAE,UAAU,EAAE;gBAC1C,UAAU,EAAE,EAAE,eAAe,EAAE,MAAM,EAAE;gBACvC,YAAY,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE;aACjC,CAAC,CAAA;YACF,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,uCAAuC,CAAC,CAAA;YACjE,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,iDAAiD,CAAC,CAAA;YAC3E,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,8CAA8C,CAAC,CAAA;YACxE,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,sCAAsC,CAAC,CAAA;QAClE,CAAC,CAAC,CAAA;QAEF,EAAE,CAAC,gCAAgC,EAAE,GAAG,EAAE;YACxC,MAAM,MAAM,GAAG,WAAW,CAAC,cAAc,EAAE,EAAE,CAAC,CAAA;YAC9C,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;QACzB,CAAC,CAAC,CAAA;QAEF,EAAE,CAAC,8CAA8C,EAAE,GAAG,EAAE;YACtD,MAAM,MAAM,GAAG,WAAW,CAAC,cAAc,EAAE;gBACzC,SAAS,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE;aAC7B,CAAC,CAAA;YACF,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,qCAAqC,CAAC,CAAA;QAC5D,CAAC,CAAC,CAAA;QAEF,EAAE,CAAC,kDAAkD,EAAE,GAAG,EAAE;YAC1D,gEAAgE;YAChE,MAAM,SAAS,GAAG;gBAChB,KAAK,EAAE,KAAK;gBACZ,SAAS,EAAE,SAAS,EAAE,+CAA+C;gBACrE,UAAU,EAAE,IAAI,EAAE,2BAA2B;gBAC7C,SAAS,EAAE,EAAE,eAAe,EAAE,MAAM,EAAE,EAAE,6BAA6B;aACtE,CAAA;YACD,MAAM,MAAM,GAAG,WAAW,CAAC,cAAc,EAAE,SAAyD,CAAC,CAAA;YACrG,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,8BAA8B,CAAC,CAAA;YACxD,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,gDAAgD,CAAC,CAAA;YAC1E,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,SAAS,CAAC,CAAA;YACvC,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAA;YACtC,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,SAAS,CAAC,CAAA;QACzC,CAAC,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}
|
package/esm/index.d.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
export * from './compile-route.js';
|
|
2
2
|
export * from './components/index.js';
|
|
3
|
+
export * from './css-generator.js';
|
|
3
4
|
export * from './initialize.js';
|
|
4
5
|
export * from './models/index.js';
|
|
5
6
|
export * from './services/index.js';
|
|
6
7
|
export * from './shade-component.js';
|
|
7
8
|
export * from './shade.js';
|
|
9
|
+
export * from './style-manager.js';
|
|
8
10
|
export * from './styled-element.js';
|
|
9
11
|
export * from './styled-shade.js';
|
|
10
12
|
import './jsx.js';
|
package/esm/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAA;AAClC,cAAc,uBAAuB,CAAA;AACrC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,mBAAmB,CAAA;AACjC,cAAc,qBAAqB,CAAA;AACnC,cAAc,sBAAsB,CAAA;AACpC,cAAc,YAAY,CAAA;AAC1B,cAAc,qBAAqB,CAAA;AACnC,cAAc,mBAAmB,CAAA;AACjC,OAAO,UAAU,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAA;AAClC,cAAc,uBAAuB,CAAA;AACrC,cAAc,oBAAoB,CAAA;AAClC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,mBAAmB,CAAA;AACjC,cAAc,qBAAqB,CAAA;AACnC,cAAc,sBAAsB,CAAA;AACpC,cAAc,YAAY,CAAA;AAC1B,cAAc,oBAAoB,CAAA;AAClC,cAAc,qBAAqB,CAAA;AACnC,cAAc,mBAAmB,CAAA;AACjC,OAAO,UAAU,CAAA"}
|
package/esm/index.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
export * from './compile-route.js';
|
|
2
2
|
export * from './components/index.js';
|
|
3
|
+
export * from './css-generator.js';
|
|
3
4
|
export * from './initialize.js';
|
|
4
5
|
export * from './models/index.js';
|
|
5
6
|
export * from './services/index.js';
|
|
6
7
|
export * from './shade-component.js';
|
|
7
8
|
export * from './shade.js';
|
|
9
|
+
export * from './style-manager.js';
|
|
8
10
|
export * from './styled-element.js';
|
|
9
11
|
export * from './styled-shade.js';
|
|
10
12
|
import './jsx.js';
|
package/esm/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAA;AAClC,cAAc,uBAAuB,CAAA;AACrC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,mBAAmB,CAAA;AACjC,cAAc,qBAAqB,CAAA;AACnC,cAAc,sBAAsB,CAAA;AACpC,cAAc,YAAY,CAAA;AAC1B,cAAc,qBAAqB,CAAA;AACnC,cAAc,mBAAmB,CAAA;AACjC,OAAO,UAAU,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAA;AAClC,cAAc,uBAAuB,CAAA;AACrC,cAAc,oBAAoB,CAAA;AAClC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,mBAAmB,CAAA;AACjC,cAAc,qBAAqB,CAAA;AACnC,cAAc,sBAAsB,CAAA;AACpC,cAAc,YAAY,CAAA;AAC1B,cAAc,oBAAoB,CAAA;AAClC,cAAc,qBAAqB,CAAA;AACnC,cAAc,mBAAmB,CAAA;AACjC,OAAO,UAAU,CAAA"}
|