@dooboostore/dom-render 1.0.98 β†’ 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 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, _g;
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 ((_c = this.config) === null || _c === void 0 ? void 0 : _c.onlyParentType) {
157
- const isOk = Array.isArray((_d = this.config) === null || _d === void 0 ? void 0 : _d.onlyParentType) ? (_e = this.config) === null || _e === void 0 ? void 0 : _e.onlyParentType : [(_f = this.config) === null || _f === void 0 ? void 0 : _f.onlyParentType].some(it => { var _a; return ((_a = data.render) === null || _a === void 0 ? void 0 : _a.parentThis) instanceof it; });
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 ' + ((_g = this.config) === null || _g === void 0 ? void 0 : _g.onlyParentType));
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._rawSet = other.rawSet;
167
- if (this._rawSet) {
168
- this.setAttribute((_b = (_a = this._rawSet.dataSet) === null || _a === void 0 ? void 0 : _a.render) === null || _b === void 0 ? void 0 : _b.attribute);
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._rawSet = rawSet;
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
- // console.log('ComponentBase---onDestroyRender----')
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
@@ -4,7 +4,7 @@ import { RawSet } from '../../rawsets/RawSet';
4
4
  import { OtherData } from '../../lifecycle/OnChangeAttrRender';
5
5
  import { OnInitRender } from '../../lifecycle/OnInitRender';
6
6
  import { OnCreateRenderDataParams } from '../../lifecycle/OnCreateRenderData';
7
- import { OnCreateRender } from 'lifecycle/OnCreateRender';
7
+ import { OnCreateRender } from '../../lifecycle/OnCreateRender';
8
8
  export declare namespace Details {
9
9
  const selector = "dr-details";
10
10
  type Attribute = {
@@ -2,8 +2,8 @@ import { ComponentBase } from '../ComponentBase';
2
2
  import { RunConfig } from '../../DomRender';
3
3
  import { RawSet } from '../../rawsets/RawSet';
4
4
  import { OtherData } from '../../lifecycle/OnChangeAttrRender';
5
- import { OnCreateRender } from 'lifecycle/OnCreateRender';
6
- import { OnDestroyRender, OnDestroyRenderParams } from 'lifecycle/OnDestroyRender';
5
+ import { OnCreateRender } from '../../lifecycle/OnCreateRender';
6
+ import { OnDestroyRender, OnDestroyRenderParams } from '../../lifecycle/OnDestroyRender';
7
7
  export declare namespace Input {
8
8
  const selector = "dr-input";
9
9
  type Attribute = {
@@ -2,7 +2,7 @@ import { ComponentBase } from '../ComponentBase';
2
2
  import { RunConfig } from '../../DomRender';
3
3
  import { RawSet } from '../../rawsets/RawSet';
4
4
  import { OtherData } from '../../lifecycle/OnChangeAttrRender';
5
- import { OnDestroyRenderParams } from 'lifecycle/OnDestroyRender';
5
+ import { OnDestroyRenderParams } from '../../lifecycle/OnDestroyRender';
6
6
  export declare namespace Timer {
7
7
  const selector = "dr-timer";
8
8
  type Attribute = {
package/package.json CHANGED
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "name": "@dooboostore/dom-render",
3
- "version": "1.0.98",
4
- "main": "dist/index.js",
3
+ "version": "1.0.100",
5
4
  "license": "MIT",
6
5
  "description": "html view template engine",
7
6
  "keywords": [
@@ -17,6 +16,24 @@
17
16
  "framwork",
18
17
  "node"
19
18
  ],
19
+ "author": "dooboostore <dooboostore@gmail.com> (https://github.com/dooboostore)",
20
+ "contributors": [
21
+ "sewoo <sewooimda@nate.com> (https://github.com/sewoo)",
22
+ "SeoDH <adonis_84@naver.com> (https://github.com/SeoDH)",
23
+ "tkyung (https://github.com/tkyungDev)",
24
+ "Hoil-Gang (https://github.com/khl1154)",
25
+ "hwangsero (https://github.com/hwangsero)"
26
+ ],
27
+ "homepage": "https://github.com/dooboostore-develop/packages",
28
+ "bugs": {
29
+ "url": "https://github.com/dooboostore-develop/packages",
30
+ "email": "dooboostore@gmail.com"
31
+ },
32
+ "repository": {
33
+ "type": "git",
34
+ "url": "https://github.com/dooboostore-develop/packages"
35
+ },
36
+ "main": "dist/index.js",
20
37
  "types": "dist/index.d.ts",
21
38
  "files": [
22
39
  "**/*.js",
@@ -27,9 +44,9 @@
27
44
  },
28
45
  "dependencies": {
29
46
  "reflect-metadata": "^0.2.2",
30
- "@dooboostore/core": "1.0.5",
31
- "@dooboostore/core-node": "1.0.1",
32
- "@dooboostore/core-web": "1.0.0"
47
+ "@dooboostore/core": "1.0.7",
48
+ "@dooboostore/core-web": "1.0.1",
49
+ "@dooboostore/core-node": "1.0.1"
33
50
  },
34
51
  "devDependencies": {
35
52
  "@babel/core": "^7.24.0",
@@ -49,7 +66,7 @@
49
66
  "ts-node-dev": "^2.0.0",
50
67
  "tslib": "^2.8.1",
51
68
  "@types/node": "^20",
52
- "typescript": "^5.8.2",
69
+ "typescript": "^5.9.2",
53
70
  "webpack": "^5.98.0",
54
71
  "webpack-cli": "^5.1.4",
55
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 cssParse from '../css/parse';
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
- // console.log('동적?', `var(--domrender-${data[0]})`)
405
- return `var(--domrender-${data[0]})`;
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 cssobject = cssParse(before);
410
- (_a = cssobject.stylesheet) === null || _a === void 0 ? void 0 : _a.rules.forEach((rule) => {
411
- var _a, _b, _c;
412
- // console.log('--------cssParse', rule)
413
- const isRoot = (_a = rule.selectors) === null || _a === void 0 ? void 0 : _a.find(it => it.startsWith(':root'));
414
- if (rule.type === 'rule' && !isRoot) { // && !!isRoot
415
- rule.selectors = (_b = rule.selectors) === null || _b === void 0 ? void 0 : _b.map(sit => {
416
- return this.generateRuleSelector(start, end, sit);
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.type === 'media' && !isRoot) {
420
- (_c = rule.rules) === null || _c === void 0 ? void 0 : _c.forEach((mediaRule) => {
421
- var _a;
422
- mediaRule.selectors = (_a = mediaRule.selectors) === null || _a === void 0 ? void 0 : _a.map(sit => {
423
- return this.generateRuleSelector(start, end, sit);
424
- });
425
- // console.log('--------', mediaRule, mediaRule.selectors)
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
- const stringify = cssStringify(cssobject);
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('/*$', '${');