@dooboostore/dom-render 1.0.99 β 1.0.100
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.MD +172 -0
- package/components/ComponentBase.d.ts +17 -0
- package/components/ComponentBase.js +116 -77
- package/package.json +2 -2
- package/rawsets/RawSet.js +31 -27
- package/css/parse/index.d.ts +0 -49
- package/css/parse/index.js +0 -498
- package/css/stringify/compiler.d.ts +0 -34
- package/css/stringify/compiler.js +0 -36
- package/css/stringify/compress.d.ts +0 -104
- package/css/stringify/compress.js +0 -147
- package/css/stringify/identity.d.ts +0 -109
- package/css/stringify/identity.js +0 -183
- package/css/stringify/index.d.ts +0 -24
- package/css/stringify/index.js +0 -27
- package/css/stringify/types.d.ts +0 -15
- package/css/stringify/types.js +0 -2
- package/css/types.d.ts +0 -10
- package/css/types.js +0 -2
package/README.MD
CHANGED
|
@@ -326,6 +326,178 @@ class data {
|
|
|
326
326
|
</details>
|
|
327
327
|
|
|
328
328
|
|
|
329
|
+
## Component Property binding with `@attribute` decorator
|
|
330
|
+
<details>
|
|
331
|
+
<summary>@attribute<strong>π»(click)</strong></summary>
|
|
332
|
+
|
|
333
|
+
You can bind component properties directly to HTML attributes using the `@attribute` decorator. When the attribute on the custom element changes, the corresponding property in the component class instance is automatically updated.
|
|
334
|
+
|
|
335
|
+
This requires your component to extend `ComponentBase`.
|
|
336
|
+
|
|
337
|
+
### Usage
|
|
338
|
+
|
|
339
|
+
The `@attribute` decorator options:
|
|
340
|
+
|
|
341
|
+
* `name: string`: Binds the property to a specifically named attribute. If not provided, the property name is used.
|
|
342
|
+
* `converter: (value: string | null) => any`: A function to convert the string attribute value to another type.
|
|
343
|
+
|
|
344
|
+
### Example
|
|
345
|
+
|
|
346
|
+
**Component (`MyComponent.ts`)**
|
|
347
|
+
```typescript
|
|
348
|
+
import { ComponentBase, attribute } from '@dooboostore/dom-render';
|
|
349
|
+
|
|
350
|
+
// Assuming MyComponent is registered with the tag 'my-component'
|
|
351
|
+
export class MyComponent extends ComponentBase<{ 'title': string, 'show-icon': string, 'created-at': string }> {
|
|
352
|
+
@attribute
|
|
353
|
+
title: string = 'Default Title';
|
|
354
|
+
|
|
355
|
+
@attribute({
|
|
356
|
+
name: 'show-icon',
|
|
357
|
+
converter: (val) => val === 'true'
|
|
358
|
+
})
|
|
359
|
+
showIcon: boolean; // The converter handles string -> boolean
|
|
360
|
+
|
|
361
|
+
@attribute({
|
|
362
|
+
name: 'created-at',
|
|
363
|
+
converter: (val) => val ? new Date(val) : null
|
|
364
|
+
})
|
|
365
|
+
createdAt: Date | null;
|
|
366
|
+
}
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
**Template (`index.html`)**
|
|
370
|
+
```html
|
|
371
|
+
<my-component
|
|
372
|
+
title="Hello World!"
|
|
373
|
+
show-icon="true"
|
|
374
|
+
created-at="2025-08-30T12:00:00Z">
|
|
375
|
+
</my-component>
|
|
376
|
+
|
|
377
|
+
<script>
|
|
378
|
+
// After some time, change the attribute
|
|
379
|
+
setTimeout(() => {
|
|
380
|
+
const comp = document.querySelector('my-component');
|
|
381
|
+
comp.setAttribute('title', 'New Title!');
|
|
382
|
+
comp.setAttribute('show-icon', 'false');
|
|
383
|
+
comp.setAttribute('created-at', new Date().toISOString());
|
|
384
|
+
}, 2000);
|
|
385
|
+
</script>
|
|
386
|
+
```
|
|
387
|
+
When attributes change, the `converter` function will be executed before the value is assigned to the property. For `showIcon`, the string `"true"` or `"false"` will be converted to a boolean. For `createdAt`, the date string will be converted to a `Date` object.
|
|
388
|
+
|
|
389
|
+
</details>
|
|
390
|
+
|
|
391
|
+
## Element Query with `@query` decorator
|
|
392
|
+
<details>
|
|
393
|
+
<summary>@query<strong>π»(click)</strong></summary>
|
|
394
|
+
|
|
395
|
+
You can get a reference to DOM elements within your component's template using the `@query` decorator. It takes a CSS selector as an argument.
|
|
396
|
+
|
|
397
|
+
- If the decorated property is **not an array type**, it returns the **first** matching element (like `querySelector`).
|
|
398
|
+
- If the decorated property **is an array type** (e.g., `HTMLElement[]`), it returns **all** matching elements as an array (like `querySelectorAll`).
|
|
399
|
+
|
|
400
|
+
This is useful for directly accessing and manipulating elements you need to interact with from your component's logic.
|
|
401
|
+
|
|
402
|
+
**Note:** This feature relies on TypeScript's decorator metadata (`emitDecoratorMetadata: true` in `tsconfig.json`).
|
|
403
|
+
|
|
404
|
+
### Usage Example
|
|
405
|
+
|
|
406
|
+
```typescript
|
|
407
|
+
import { ComponentBase, query } from '@dooboostore/dom-render';
|
|
408
|
+
|
|
409
|
+
// Assuming MyComponent is rendered with the template below
|
|
410
|
+
export class MyComponent extends ComponentBase<{}> {
|
|
411
|
+
// Gets the first element matching the selector
|
|
412
|
+
@query('h3.title')
|
|
413
|
+
private titleEl: HTMLHeadingElement;
|
|
414
|
+
|
|
415
|
+
// Gets all elements matching the selector as an array
|
|
416
|
+
@query('ul.item-list > li')
|
|
417
|
+
private items: HTMLLIElement[];
|
|
418
|
+
|
|
419
|
+
onInitRender() {
|
|
420
|
+
// The elements are available after the component has been rendered.
|
|
421
|
+
if (this.titleEl) {
|
|
422
|
+
this.titleEl.style.color = 'blue';
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
if (this.items && this.items.length > 0) {
|
|
426
|
+
this.items.forEach((item, index) => {
|
|
427
|
+
item.textContent = `Item ${index + 1} (updated)`;
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
### Template
|
|
435
|
+
```html
|
|
436
|
+
<!-- Template for MyComponent -->
|
|
437
|
+
<div>
|
|
438
|
+
<h3 class="title">Original Title</h3>
|
|
439
|
+
<ul class="item-list">
|
|
440
|
+
<li>Item 1</li>
|
|
441
|
+
<li>Item 2</li>
|
|
442
|
+
<li>Item 3</li>
|
|
443
|
+
</ul>
|
|
444
|
+
</div>
|
|
445
|
+
```
|
|
446
|
+
The `@query` decorator will assign the corresponding element(s) to the decorated property. The property will be `null`/`undefined` (for single query) or an empty array (for multiple query) if no elements are found or before the component is rendered.
|
|
447
|
+
|
|
448
|
+
</details>
|
|
449
|
+
|
|
450
|
+
</details>
|
|
451
|
+
|
|
452
|
+
## DOM Event Binding with `@event` decorator
|
|
453
|
+
<details>
|
|
454
|
+
<summary>@event<strong>π»(click)</strong></summary>
|
|
455
|
+
|
|
456
|
+
You can bind component methods directly to DOM events on elements within your component's template using the `@event` decorator.
|
|
457
|
+
|
|
458
|
+
The decorator takes an options object with two properties:
|
|
459
|
+
- `query: string`: A CSS selector to find the target element(s).
|
|
460
|
+
- `name: string`: The name of the DOM event to listen for (e.g., `'click'`, `'input'`).
|
|
461
|
+
|
|
462
|
+
The framework will automatically add the event listener when the component is initialized and remove it when the component is destroyed, preventing memory leaks.
|
|
463
|
+
|
|
464
|
+
### Usage Example
|
|
465
|
+
|
|
466
|
+
```typescript
|
|
467
|
+
import { ComponentBase, event } from '@dooboostore/dom-render';
|
|
468
|
+
|
|
469
|
+
// Assuming MyComponent is rendered with the template below
|
|
470
|
+
export class MyComponent extends ComponentBase<{}> {
|
|
471
|
+
|
|
472
|
+
// This method will be called when any element matching 'button.action' is clicked.
|
|
473
|
+
@event({ query: 'button.action', name: 'click' })
|
|
474
|
+
onActionClick(e: MouseEvent) {
|
|
475
|
+
console.log('Action button clicked!', e.target);
|
|
476
|
+
alert('Action!');
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// This method will be called for the 'input' event on all text inputs.
|
|
480
|
+
@event({ query: 'input[type="text"]', name: 'input' })
|
|
481
|
+
onTextInput(e: Event) {
|
|
482
|
+
const target = e.target as HTMLInputElement;
|
|
483
|
+
console.log(`Text changed in ${target.id}: ${target.value}`);
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
### Template
|
|
489
|
+
```html
|
|
490
|
+
<!-- Template for MyComponent -->
|
|
491
|
+
<div>
|
|
492
|
+
<input type="text" id="firstname" placeholder="First Name">
|
|
493
|
+
<input type="text" id="lastname" placeholder="Last Name">
|
|
494
|
+
<button class="action">Submit</button>
|
|
495
|
+
</div>
|
|
496
|
+
```
|
|
497
|
+
In this example, when the "Submit" button is clicked, the `onActionClick` method is executed. When the user types in either of the text inputs, the `onTextInput` method is executed for each keystroke.
|
|
498
|
+
|
|
499
|
+
</details>
|
|
500
|
+
|
|
329
501
|
## nearThis
|
|
330
502
|
```html
|
|
331
503
|
<div dr-this="@this@.dictionary"> (${@nearThis@.name}$) ${$nearThis.name}$ </div>
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
1
2
|
import { OnChangeAttrRender, OtherData } from '../lifecycle/OnChangeAttrRender';
|
|
2
3
|
import { OnCreateRenderData, OnCreateRenderDataParams } from '../lifecycle/OnCreateRenderData';
|
|
3
4
|
import { RawSet } from '../rawsets/RawSet';
|
|
@@ -8,14 +9,29 @@ import { OnDestroyRender, OnDestroyRenderParams } from '../lifecycle/OnDestroyRe
|
|
|
8
9
|
import { OnDrThisUnBind } from '../lifecycle/dr-this/OnDrThisUnBind';
|
|
9
10
|
import { OnDrThisBind } from '../lifecycle/dr-this/OnDrThisBind';
|
|
10
11
|
import * as Render from '../rawsets/Render';
|
|
12
|
+
export declare const ATTRIBUTE_METADATA_KEY: unique symbol;
|
|
13
|
+
export declare const QUERY_METADATA_KEY: unique symbol;
|
|
14
|
+
export declare const EVENT_METADATA_KEY: unique symbol;
|
|
15
|
+
export declare function attribute(options?: {
|
|
16
|
+
name?: string;
|
|
17
|
+
converter?: (value: string | null) => any;
|
|
18
|
+
}): PropertyDecorator;
|
|
19
|
+
export declare function query(selector: string): PropertyDecorator;
|
|
20
|
+
export declare function event(options: {
|
|
21
|
+
query: string;
|
|
22
|
+
name: string;
|
|
23
|
+
}): MethodDecorator;
|
|
11
24
|
export declare abstract class ComponentBase<T> implements OnChangeAttrRender, OnCreateRenderData, OnCreatedThisChild, OnDrThisBind, OnDrThisUnBind, OnInitRender, OnDestroyRender {
|
|
12
25
|
private config?;
|
|
13
26
|
private _rawSet?;
|
|
14
27
|
private _children;
|
|
15
28
|
private _render;
|
|
16
29
|
private _attribute?;
|
|
30
|
+
private _boundEventListeners;
|
|
17
31
|
get render(): Render.Render | undefined;
|
|
18
32
|
get rawSet(): RawSet | undefined;
|
|
33
|
+
private _queryElements;
|
|
34
|
+
private _cleanupEventListeners;
|
|
19
35
|
getAttribute<K extends keyof T>(name: K, attribute?: T): T[K] | null;
|
|
20
36
|
hasAttribute(name: keyof T): boolean;
|
|
21
37
|
equalsAttributeName(n: string, name: keyof T): boolean;
|
|
@@ -29,6 +45,7 @@ export declare abstract class ComponentBase<T> implements OnChangeAttrRender, On
|
|
|
29
45
|
constructor(config?: {
|
|
30
46
|
onlyParentType: ConstructorType<any>[] | ConstructorType<any>;
|
|
31
47
|
});
|
|
48
|
+
private setRawSet;
|
|
32
49
|
setAttribute(attribute: T): void;
|
|
33
50
|
onCreatedThisChild(child: any, data: OnCreateRenderDataParams): void;
|
|
34
51
|
onCreateRenderData(data: OnCreateRenderDataParams): void;
|
|
@@ -7,18 +7,75 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
7
7
|
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
8
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
9
|
};
|
|
10
|
+
import 'reflect-metadata';
|
|
10
11
|
import { RawSet } from '../rawsets/RawSet';
|
|
11
12
|
import { DomRenderNoProxy } from '../decorators/DomRenderNoProxy';
|
|
12
13
|
import { ConvertUtils } from '@dooboostore/core/convert/ConvertUtils';
|
|
13
14
|
import * as Render from '../rawsets/Render';
|
|
15
|
+
export const ATTRIBUTE_METADATA_KEY = Symbol('attribute');
|
|
16
|
+
export const QUERY_METADATA_KEY = Symbol('query');
|
|
17
|
+
export const EVENT_METADATA_KEY = Symbol('event');
|
|
18
|
+
export function attribute(options) {
|
|
19
|
+
return (target, propertyKey) => {
|
|
20
|
+
Reflect.defineMetadata(ATTRIBUTE_METADATA_KEY, Object.assign(Object.assign({}, options), { name: (options === null || options === void 0 ? void 0 : options.name) || propertyKey }), target, propertyKey);
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export function query(selector) {
|
|
24
|
+
return (target, propertyKey) => {
|
|
25
|
+
Reflect.defineMetadata(QUERY_METADATA_KEY, { selector }, target, propertyKey);
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
export function event(options) {
|
|
29
|
+
return (target, propertyKey, descriptor) => {
|
|
30
|
+
const events = Reflect.getMetadata(EVENT_METADATA_KEY, target.constructor) || [];
|
|
31
|
+
events.push({
|
|
32
|
+
query: options.query,
|
|
33
|
+
eventName: options.name,
|
|
34
|
+
methodName: propertyKey
|
|
35
|
+
});
|
|
36
|
+
Reflect.defineMetadata(EVENT_METADATA_KEY, events, target.constructor);
|
|
37
|
+
};
|
|
38
|
+
}
|
|
14
39
|
export class ComponentBase {
|
|
15
|
-
// private attributeData?: FieldUnionType<T, null>;
|
|
16
40
|
get render() {
|
|
17
41
|
return this._render;
|
|
18
42
|
}
|
|
19
43
|
get rawSet() {
|
|
20
44
|
return this._rawSet;
|
|
21
45
|
}
|
|
46
|
+
_queryElements(selector) {
|
|
47
|
+
var _a, _b, _c, _d;
|
|
48
|
+
const e = this.getElement();
|
|
49
|
+
if (e) {
|
|
50
|
+
return Array.from(e.querySelectorAll(selector));
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
const startNode = (_b = (_a = this.rawSet) === null || _a === void 0 ? void 0 : _a.point) === null || _b === void 0 ? void 0 : _b.start;
|
|
54
|
+
const endNode = (_d = (_c = this.rawSet) === null || _c === void 0 ? void 0 : _c.point) === null || _d === void 0 ? void 0 : _d.end;
|
|
55
|
+
if (startNode && endNode && startNode.parentNode === endNode.parentNode) {
|
|
56
|
+
const elements = [];
|
|
57
|
+
let currentNode = startNode.nextSibling;
|
|
58
|
+
while (currentNode && currentNode !== endNode) {
|
|
59
|
+
if (currentNode.nodeType === Node.ELEMENT_NODE) {
|
|
60
|
+
const currentElement = currentNode;
|
|
61
|
+
if (currentElement.matches(selector)) {
|
|
62
|
+
elements.push(currentElement);
|
|
63
|
+
}
|
|
64
|
+
elements.push(...Array.from(currentElement.querySelectorAll(selector)));
|
|
65
|
+
}
|
|
66
|
+
currentNode = currentNode.nextSibling;
|
|
67
|
+
}
|
|
68
|
+
return elements;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return [];
|
|
72
|
+
}
|
|
73
|
+
_cleanupEventListeners() {
|
|
74
|
+
for (const { element, eventName, listener } of this._boundEventListeners) {
|
|
75
|
+
element.removeEventListener(eventName, listener);
|
|
76
|
+
}
|
|
77
|
+
this._boundEventListeners = [];
|
|
78
|
+
}
|
|
22
79
|
getAttribute(name, attribute = this._attribute) {
|
|
23
80
|
const target = attribute;
|
|
24
81
|
if (!target) {
|
|
@@ -47,35 +104,6 @@ export class ComponentBase {
|
|
|
47
104
|
}
|
|
48
105
|
return null;
|
|
49
106
|
}
|
|
50
|
-
// get attribute(): T | undefined {
|
|
51
|
-
// const handler = {
|
|
52
|
-
// get(target: any, prop: string) {
|
|
53
|
-
// let p = prop;
|
|
54
|
-
// if (p in target) {
|
|
55
|
-
// return target[p];
|
|
56
|
-
// }
|
|
57
|
-
// p = ConvertUtils.snakeToCamelCase(prop);
|
|
58
|
-
// if (p in target) {
|
|
59
|
-
// return target[p];
|
|
60
|
-
// }
|
|
61
|
-
// p = ConvertUtils.camelToSnakeCase(prop);
|
|
62
|
-
// if (p in target) {
|
|
63
|
-
// return target[p];
|
|
64
|
-
// }
|
|
65
|
-
// p = prop.toUpperCase()
|
|
66
|
-
// if (p in target) {
|
|
67
|
-
// return target[p];
|
|
68
|
-
// }
|
|
69
|
-
// p = prop.toLowerCase()
|
|
70
|
-
// if (p in target) {
|
|
71
|
-
// return target[p];
|
|
72
|
-
// }
|
|
73
|
-
// return null;
|
|
74
|
-
// }
|
|
75
|
-
// };
|
|
76
|
-
// const obj = new Proxy(this._attribute, handler);
|
|
77
|
-
// return obj;
|
|
78
|
-
// }
|
|
79
107
|
hasAttribute(name) {
|
|
80
108
|
return this.getAttribute(name) !== null;
|
|
81
109
|
}
|
|
@@ -86,15 +114,6 @@ export class ComponentBase {
|
|
|
86
114
|
n.toUpperCase() === name.toUpperCase() ||
|
|
87
115
|
n.toLowerCase() === name.toLowerCase();
|
|
88
116
|
}
|
|
89
|
-
// attribute(name: keyof T, {attribute = this._attribute}: {equals?: (data: T[keyof T]) => void, notEquals?: () => void,attribute?: T} = {}) {
|
|
90
|
-
// const data = this.getAttribute(name, attribute)
|
|
91
|
-
// return n === name ||
|
|
92
|
-
// ConvertUtils.snakeToCamelCase(n) === name ||
|
|
93
|
-
// ConvertUtils.camelToSnakeCase(n) === name ||
|
|
94
|
-
// n.toUpperCase() === (name as string).toUpperCase() ||
|
|
95
|
-
// n.toLowerCase() === (name as string).toLowerCase()
|
|
96
|
-
// ;
|
|
97
|
-
// }
|
|
98
117
|
get uuid() {
|
|
99
118
|
var _a;
|
|
100
119
|
return (_a = this._rawSet) === null || _a === void 0 ? void 0 : _a.uuid;
|
|
@@ -113,82 +132,98 @@ export class ComponentBase {
|
|
|
113
132
|
var _a;
|
|
114
133
|
return (_a = this.render) === null || _a === void 0 ? void 0 : _a.parentThis;
|
|
115
134
|
}
|
|
116
|
-
// ꡬν μκ·Έλμ² (μ€λ²λ‘λ μκ·Έλμ²λ³΄λ€ λ μΌλ°μ μ΄μ΄μΌ ν¨)
|
|
117
135
|
getChildren(typeOrTypes) {
|
|
118
136
|
const targets = Array.isArray(typeOrTypes) ? typeOrTypes : [typeOrTypes];
|
|
119
|
-
// filterμ κ²°κ³Όλ νμ λ°°μ΄μ΄λ―λ‘ `?? []`λ νμ μμ΅λλ€.
|
|
120
|
-
// filterκ° μ무κ²λ μ°Ύμ§ λͺ»νλ©΄ λΉ λ°°μ΄μ λ°νν©λλ€.
|
|
121
137
|
return this._children.filter(child => targets.some(targetConstructor => child instanceof targetConstructor));
|
|
122
138
|
}
|
|
123
|
-
// getChildren<T extends ConstructorType<any>[] | ConstructorType<any>>(
|
|
124
|
-
// type: T
|
|
125
|
-
// ): T extends (infer U)[] ? InstanceType<U>[] : T extends ConstructorType<infer V> ? V[] : never {
|
|
126
|
-
// const target = Array.isArray(type) ? type : [type];
|
|
127
|
-
// return this._children.filter(it => target.some(t => it instanceof t)) ?? [];
|
|
128
|
-
// }
|
|
129
|
-
// getChildren<T>(type: ConstructorType<T> | ConstructorType<T>[]): T[] {
|
|
130
|
-
// const target = Array.isArray(type) ? type : [type];
|
|
131
|
-
// return this._children.filter(it => target.some(t => it instanceof t)) ?? [];
|
|
132
|
-
// }
|
|
133
|
-
/*
|
|
134
|
-
getChildren<T>(type: ConstructorType<T>): T[] {
|
|
135
|
-
return this._children.filter(it => it instanceof type) ?? [];
|
|
136
|
-
}
|
|
137
|
-
*/
|
|
138
139
|
constructor(config) {
|
|
139
140
|
this.config = config;
|
|
140
141
|
this._children = [];
|
|
142
|
+
this._boundEventListeners = [];
|
|
143
|
+
}
|
|
144
|
+
setRawSet(rawSet) {
|
|
145
|
+
var _a, _b;
|
|
146
|
+
this._rawSet = rawSet;
|
|
147
|
+
if (this._rawSet) {
|
|
148
|
+
this.setAttribute((_b = (_a = this._rawSet.dataSet) === null || _a === void 0 ? void 0 : _a.render) === null || _b === void 0 ? void 0 : _b.attribute);
|
|
149
|
+
}
|
|
141
150
|
}
|
|
142
151
|
setAttribute(attribute) {
|
|
143
152
|
this._attribute = attribute;
|
|
144
|
-
// this.attributeData = this._attribute;
|
|
145
153
|
}
|
|
146
154
|
onCreatedThisChild(child, data) {
|
|
147
155
|
this._children.push(child);
|
|
148
|
-
// this._rawSet = data.render?.rawSet;
|
|
149
|
-
// this._attribute = data.render?.attribute as T;
|
|
150
156
|
}
|
|
151
157
|
onCreateRenderData(data) {
|
|
152
|
-
var _a, _b, _c, _d, _e, _f
|
|
153
|
-
this._rawSet = (_a = data.render) === null || _a === void 0 ? void 0 : _a.rawSet;
|
|
154
|
-
this.setAttribute((_b = data.render) === null || _b === void 0 ? void 0 : _b.attribute);
|
|
158
|
+
var _a, _b, _c, _d, _e, _f;
|
|
155
159
|
this._render = data.render;
|
|
156
|
-
if ((
|
|
157
|
-
|
|
160
|
+
if ((_a = data.render) === null || _a === void 0 ? void 0 : _a.rawSet) {
|
|
161
|
+
this.setRawSet(data.render.rawSet);
|
|
162
|
+
}
|
|
163
|
+
if ((_b = this.config) === null || _b === void 0 ? void 0 : _b.onlyParentType) {
|
|
164
|
+
const isOk = Array.isArray((_c = this.config) === null || _c === void 0 ? void 0 : _c.onlyParentType) ? (_d = this.config) === null || _d === void 0 ? void 0 : _d.onlyParentType : [(_e = this.config) === null || _e === void 0 ? void 0 : _e.onlyParentType].some(it => { var _a; return ((_a = data.render) === null || _a === void 0 ? void 0 : _a.parentThis) instanceof it; });
|
|
158
165
|
if (!isOk) {
|
|
159
|
-
throw new Error('only my parentThis ' + ((
|
|
166
|
+
throw new Error('only my parentThis ' + ((_f = this.config) === null || _f === void 0 ? void 0 : _f.onlyParentType));
|
|
160
167
|
}
|
|
161
168
|
}
|
|
162
169
|
}
|
|
163
170
|
onChangeAttrRender(name, value, other) {
|
|
164
|
-
var _a, _b;
|
|
165
171
|
if (other.rawSet) {
|
|
166
|
-
this.
|
|
167
|
-
|
|
168
|
-
|
|
172
|
+
this.setRawSet(other.rawSet);
|
|
173
|
+
}
|
|
174
|
+
for (const propertyKey in this) {
|
|
175
|
+
const metadata = Reflect.getMetadata(ATTRIBUTE_METADATA_KEY, this, propertyKey);
|
|
176
|
+
if (metadata && metadata.name) {
|
|
177
|
+
if (this.equalsAttributeName(name, metadata.name)) {
|
|
178
|
+
const finalValue = metadata.converter ? metadata.converter(value) : value;
|
|
179
|
+
this[propertyKey] = finalValue;
|
|
180
|
+
}
|
|
169
181
|
}
|
|
170
182
|
}
|
|
171
|
-
// this.changeAttribute?.(this._attribute);
|
|
172
183
|
}
|
|
173
184
|
onInitRender(param, rawSet) {
|
|
174
|
-
var _a, _b;
|
|
175
185
|
if (rawSet) {
|
|
176
|
-
this.
|
|
177
|
-
if (this._rawSet) {
|
|
178
|
-
this.setAttribute((_b = (_a = rawSet.dataSet) === null || _a === void 0 ? void 0 : _a.render) === null || _b === void 0 ? void 0 : _b.attribute);
|
|
179
|
-
}
|
|
186
|
+
this.setRawSet(rawSet);
|
|
180
187
|
const e = this.getElement();
|
|
181
188
|
if (e) {
|
|
182
189
|
e.component = this;
|
|
183
190
|
}
|
|
191
|
+
// Handle @query decorators
|
|
192
|
+
for (const propertyKey in this) {
|
|
193
|
+
const metadata = Reflect.getMetadata(QUERY_METADATA_KEY, this, propertyKey);
|
|
194
|
+
if (metadata && metadata.selector) {
|
|
195
|
+
const propertyType = Reflect.getMetadata("design:type", this, propertyKey);
|
|
196
|
+
const elements = this._queryElements(metadata.selector);
|
|
197
|
+
if (propertyType === Array) {
|
|
198
|
+
this[propertyKey] = elements;
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
this[propertyKey] = elements[0] || null;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
// Handle @event decorators
|
|
206
|
+
const events = Reflect.getMetadata(EVENT_METADATA_KEY, this.constructor);
|
|
207
|
+
if (events && Array.isArray(events)) {
|
|
208
|
+
for (const eventInfo of events) {
|
|
209
|
+
const elements = this._queryElements(eventInfo.query);
|
|
210
|
+
elements.forEach(element => {
|
|
211
|
+
const listener = this[eventInfo.methodName].bind(this);
|
|
212
|
+
element.addEventListener(eventInfo.eventName, listener);
|
|
213
|
+
this._boundEventListeners.push({ element, eventName: eventInfo.eventName, listener });
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
}
|
|
184
217
|
}
|
|
185
218
|
}
|
|
186
219
|
onDrThisBind() {
|
|
220
|
+
// Logic moved to onInitRender for robustness
|
|
187
221
|
}
|
|
188
222
|
onDrThisUnBind() {
|
|
223
|
+
this._cleanupEventListeners();
|
|
189
224
|
}
|
|
190
225
|
onDestroyRender(data) {
|
|
191
|
-
|
|
226
|
+
this._cleanupEventListeners();
|
|
192
227
|
}
|
|
193
228
|
}
|
|
194
229
|
__decorate([
|
|
@@ -207,4 +242,8 @@ __decorate([
|
|
|
207
242
|
DomRenderNoProxy,
|
|
208
243
|
__metadata("design:type", Object)
|
|
209
244
|
], ComponentBase.prototype, "_attribute", void 0);
|
|
245
|
+
__decorate([
|
|
246
|
+
DomRenderNoProxy,
|
|
247
|
+
__metadata("design:type", Array)
|
|
248
|
+
], ComponentBase.prototype, "_boundEventListeners", void 0);
|
|
210
249
|
//# sourceMappingURL=ComponentBase.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dooboostore/dom-render",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.100",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "html view template engine",
|
|
6
6
|
"keywords": [
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
"ts-node-dev": "^2.0.0",
|
|
67
67
|
"tslib": "^2.8.1",
|
|
68
68
|
"@types/node": "^20",
|
|
69
|
-
"typescript": "^5.
|
|
69
|
+
"typescript": "^5.9.2",
|
|
70
70
|
"webpack": "^5.98.0",
|
|
71
71
|
"webpack-cli": "^5.1.4",
|
|
72
72
|
"webpack-dev-server": "^5.2.0"
|
package/rawsets/RawSet.js
CHANGED
|
@@ -32,8 +32,7 @@ import { DestroyOptionType } from './DestroyOptionType';
|
|
|
32
32
|
import { RawSetType } from './RawSetType';
|
|
33
33
|
import { DrThisProperty } from '../operators/DrThisProperty';
|
|
34
34
|
import { RawSetOperatorType } from './RawSetOperatorType';
|
|
35
|
-
import
|
|
36
|
-
import cssStringify from '../css/stringify';
|
|
35
|
+
import { CssParser, isStyleRule, isAtRule } from '@dooboostore/core/parser/css/CssParser';
|
|
37
36
|
import { isOnCreateRenderData } from '../lifecycle/OnCreateRenderData';
|
|
38
37
|
import { isOnCreateRender } from '../lifecycle/OnCreateRender';
|
|
39
38
|
import { isOnChangeAttrRender } from '../lifecycle/OnChangeAttrRender';
|
|
@@ -390,7 +389,6 @@ export class RawSet {
|
|
|
390
389
|
}
|
|
391
390
|
}
|
|
392
391
|
static generateStyleTransform(styleBody, componentKey, styleTagWrap = true) {
|
|
393
|
-
var _a;
|
|
394
392
|
// console.log('style!!!!!!!!!!!!!!!!1', styleBody, componentKey, styleTagWrap);
|
|
395
393
|
if (Array.isArray(styleBody)) {
|
|
396
394
|
styleBody = styleBody.join('\n');
|
|
@@ -400,37 +398,43 @@ export class RawSet {
|
|
|
400
398
|
const end = `#${componentKey}-end`;
|
|
401
399
|
// μ΄κ±°λνλμ§ κΉλ¨Ήμλ€ λμμ§??..
|
|
402
400
|
// λμ μΌλ‘ μ²λ¦¬νλκ²κ°μλ°.. css μμλ λμ μΌλ‘ μ²λ¦¬νλκ±Έ μ§ν₯ ν΄μΌλ κ²κ°μλ° μ΄κΈ°λ₯ deprecated ν μ§ κ³ λ―Όμ€
|
|
403
|
-
const before = StringUtils.regexExecArrayReplace(styleBody, /(\$\{.*?\}\$)/g, (data) => {
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
});
|
|
401
|
+
// const before = StringUtils.regexExecArrayReplace(styleBody, /(\$\{.*?\}\$)/g, (data) => {
|
|
402
|
+
// // console.log('λμ ?', `var(--domrender-${data[0]})`)
|
|
403
|
+
// return `var(--domrender-${data[0]})`;
|
|
404
|
+
// });
|
|
407
405
|
// const before = styleBody;
|
|
408
406
|
// console.log('before@@@@@@@@@@@', before)
|
|
409
|
-
const
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
407
|
+
const cssParser = new CssParser(styleBody);
|
|
408
|
+
const processRule = (rule) => {
|
|
409
|
+
if (isStyleRule(rule)) {
|
|
410
|
+
const isRoot = rule.selector.includes(':root');
|
|
411
|
+
if (!isRoot) {
|
|
412
|
+
// Handle comma-separated selectors
|
|
413
|
+
const selectors = rule.selector.split(',').map(s => s.trim());
|
|
414
|
+
const transformedSelectors = selectors.map(selector => this.generateRuleSelector(start, end, selector));
|
|
415
|
+
rule.selector = transformedSelectors.join(', ');
|
|
416
|
+
}
|
|
418
417
|
}
|
|
419
|
-
else if (rule
|
|
420
|
-
(
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
418
|
+
else if (isAtRule(rule)) {
|
|
419
|
+
if (rule.name === 'media') {
|
|
420
|
+
const hasRoot = rule.children.some(childRule => isStyleRule(childRule) && childRule.selector.includes(':root'));
|
|
421
|
+
if (!hasRoot) {
|
|
422
|
+
rule.children.forEach(childRule => {
|
|
423
|
+
if (isStyleRule(childRule)) {
|
|
424
|
+
const selectors = childRule.selector.split(',').map(s => s.trim());
|
|
425
|
+
const transformedSelectors = selectors.map(selector => this.generateRuleSelector(start, end, selector));
|
|
426
|
+
childRule.selector = transformedSelectors.join(', ');
|
|
427
|
+
}
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
}
|
|
427
431
|
}
|
|
428
|
-
}
|
|
429
|
-
|
|
432
|
+
};
|
|
433
|
+
cssParser.rules.forEach(processRule);
|
|
434
|
+
let after = cssParser.stringify();
|
|
430
435
|
// let after = StringUtils.regexExecArrayReplace(stringify as string, /(var\(--domrender-(\$\{.*?\}\$)?\))/g, (data) => {
|
|
431
436
|
// return data[2];
|
|
432
437
|
// });
|
|
433
|
-
let after = stringify;
|
|
434
438
|
// console.log('after@@@@@@@@@@@', after)
|
|
435
439
|
// μ¬κΈ°μ μ μΈ selector μ체μλ μ²λ¦¬κ°λ₯νλλ‘ νλ€ 20250518
|
|
436
440
|
after = after.replaceAll('/*$', '${');
|
package/css/parse/index.d.ts
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
export interface Position {
|
|
2
|
-
start: {
|
|
3
|
-
line: number;
|
|
4
|
-
column: number;
|
|
5
|
-
};
|
|
6
|
-
end: {
|
|
7
|
-
line: number;
|
|
8
|
-
column: number;
|
|
9
|
-
};
|
|
10
|
-
source?: string;
|
|
11
|
-
content: string;
|
|
12
|
-
}
|
|
13
|
-
export interface ParseOptions {
|
|
14
|
-
source?: string;
|
|
15
|
-
silent?: boolean;
|
|
16
|
-
}
|
|
17
|
-
export interface StyleSheet {
|
|
18
|
-
type: 'stylesheet';
|
|
19
|
-
stylesheet: {
|
|
20
|
-
source?: string;
|
|
21
|
-
rules: Rule[];
|
|
22
|
-
parsingErrors: Error[];
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
export interface Rule {
|
|
26
|
-
type: string;
|
|
27
|
-
selectors?: string[];
|
|
28
|
-
declarations?: Declaration[];
|
|
29
|
-
values?: string[];
|
|
30
|
-
name?: string;
|
|
31
|
-
vendor?: string;
|
|
32
|
-
keyframes?: Rule[];
|
|
33
|
-
media?: string;
|
|
34
|
-
rules?: Rule[];
|
|
35
|
-
supports?: string;
|
|
36
|
-
document?: string;
|
|
37
|
-
comment?: string;
|
|
38
|
-
position?: Position;
|
|
39
|
-
[key: string]: any;
|
|
40
|
-
}
|
|
41
|
-
interface Declaration {
|
|
42
|
-
type: 'declaration';
|
|
43
|
-
property: string;
|
|
44
|
-
value: string;
|
|
45
|
-
position?: Position;
|
|
46
|
-
}
|
|
47
|
-
export default function parse(css: string, options?: ParseOptions): StyleSheet;
|
|
48
|
-
export {};
|
|
49
|
-
//# sourceMappingURL=index.d.ts.map
|