@dooboostore/dom-render 1.0.100 → 1.0.102
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 +90 -48
- package/components/ComponentBase.d.ts +1 -1
- package/components/ComponentBase.js +54 -35
- package/package.json +2 -2
package/README.MD
CHANGED
|
@@ -6,25 +6,25 @@ DOM-RENDER
|
|
|
6
6
|
* all internal variables are managed by proxy. (DomRenderProxy)
|
|
7
7
|
|
|
8
8
|
# 🚀 Quick start
|
|
9
|
-
```
|
|
10
|
-
|
|
9
|
+
```typescript
|
|
10
|
+
import { DomRender } from '@dooboostore/dom-render';
|
|
11
|
+
|
|
12
|
+
class AppData {
|
|
13
|
+
name: string = 'my name is dom-render';
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const data = new AppData();
|
|
17
|
+
DomRender.run(data, document.querySelector('#app')!);
|
|
11
18
|
```
|
|
12
19
|
```html
|
|
13
20
|
<!DOCTYPE html>
|
|
14
21
|
<html lang="en">
|
|
15
|
-
<head
|
|
22
|
+
<head>
|
|
16
23
|
<meta charset="UTF-8">
|
|
17
24
|
<title>Title</title>
|
|
18
25
|
</head>
|
|
19
26
|
<body id="app">
|
|
20
27
|
${this.name}$
|
|
21
|
-
<script src="https://cdn.jsdelivr.net/npm/dom-render@1.0.86/dist/bundle.js"></script>
|
|
22
|
-
<script>
|
|
23
|
-
let data = {
|
|
24
|
-
name: 'my name is dom-render'
|
|
25
|
-
};
|
|
26
|
-
data = DomRender.run(data , document.querySelector('#app'));
|
|
27
|
-
</script>
|
|
28
28
|
</body>
|
|
29
29
|
</html>
|
|
30
30
|
```
|
|
@@ -42,14 +42,18 @@ ${this.name}$
|
|
|
42
42
|
<body id="app">
|
|
43
43
|
${this.name}$ <!-- outout: <i>my name is dom-render</i> -->
|
|
44
44
|
#{this.name}# <!-- outout text is italic: my name is dom-render -->
|
|
45
|
-
<script>
|
|
46
|
-
let data = {
|
|
47
|
-
name: '<i>my name is dom-render</i>'
|
|
48
|
-
};
|
|
49
|
-
data = DomRender.run(data , document.querySelector('#app'));
|
|
50
|
-
</script>
|
|
51
45
|
</body>
|
|
52
46
|
```
|
|
47
|
+
```typescript
|
|
48
|
+
import { DomRender } from '@dooboostore/dom-render';
|
|
49
|
+
|
|
50
|
+
class AppData {
|
|
51
|
+
name: string = '<i>my name is dom-render</i>';
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const data = new AppData();
|
|
55
|
+
DomRender.run(data, document.querySelector('#app')!);
|
|
56
|
+
```
|
|
53
57
|
</details>
|
|
54
58
|
|
|
55
59
|
# dom-render attributes
|
|
@@ -141,14 +145,18 @@ uuid: rawSetUUID
|
|
|
141
145
|
<body id="app">
|
|
142
146
|
<div dr-if="true">true</div> <!-- render -->
|
|
143
147
|
<div dr-if="this.gender === 'M'">gender: M</div> <!-- No Render -->
|
|
144
|
-
<script>
|
|
145
|
-
let data = {
|
|
146
|
-
gender: 'F',
|
|
147
|
-
};
|
|
148
|
-
data = DomRender.run(data , document.querySelector('#app'));
|
|
149
|
-
</script>
|
|
150
148
|
</body>
|
|
151
149
|
```
|
|
150
|
+
```typescript
|
|
151
|
+
import { DomRender } from '@dooboostore/dom-render';
|
|
152
|
+
|
|
153
|
+
class AppData {
|
|
154
|
+
gender: string = 'F';
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const data = new AppData();
|
|
158
|
+
DomRender.run(data, document.querySelector('#app')!);
|
|
159
|
+
```
|
|
152
160
|
</details>
|
|
153
161
|
|
|
154
162
|
|
|
@@ -396,6 +404,7 @@ You can get a reference to DOM elements within your component's template using t
|
|
|
396
404
|
|
|
397
405
|
- If the decorated property is **not an array type**, it returns the **first** matching element (like `querySelector`).
|
|
398
406
|
- If the decorated property **is an array type** (e.g., `HTMLElement[]`), it returns **all** matching elements as an array (like `querySelectorAll`).
|
|
407
|
+
- When applied to a **method**, the method will be called with the queried element(s) as arguments after the component has been rendered. If the method expects a single element, the first match is passed. If it expects an array, all matches are passed.
|
|
399
408
|
|
|
400
409
|
This is useful for directly accessing and manipulating elements you need to interact with from your component's logic.
|
|
401
410
|
|
|
@@ -428,6 +437,22 @@ export class MyComponent extends ComponentBase<{}> {
|
|
|
428
437
|
});
|
|
429
438
|
}
|
|
430
439
|
}
|
|
440
|
+
|
|
441
|
+
// Method query: This method will be called with the first element matching '.my-button'
|
|
442
|
+
@query('.my-button')
|
|
443
|
+
private setupButton(button: HTMLButtonElement) {
|
|
444
|
+
if (button) {
|
|
445
|
+
button.addEventListener('click', () => alert('Button clicked from method query!'));
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
// Method query: This method will be called with an array of all elements matching 'span.item'
|
|
450
|
+
@query('span.item')
|
|
451
|
+
private processItems(spans: HTMLSpanElement[]) {
|
|
452
|
+
spans.forEach(span => {
|
|
453
|
+
span.style.fontWeight = 'bold';
|
|
454
|
+
});
|
|
455
|
+
}
|
|
431
456
|
}
|
|
432
457
|
```
|
|
433
458
|
|
|
@@ -441,9 +466,11 @@ export class MyComponent extends ComponentBase<{}> {
|
|
|
441
466
|
<li>Item 2</li>
|
|
442
467
|
<li>Item 3</li>
|
|
443
468
|
</ul>
|
|
469
|
+
<button class="my-button">Click Me</button>
|
|
470
|
+
<p><span class="item">First Span</span> <span class="item">Second Span</span></p>
|
|
444
471
|
</div>
|
|
445
472
|
```
|
|
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.
|
|
473
|
+
The `@query` decorator will assign the corresponding element(s) to the decorated property or pass them to the decorated method. 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. For methods, they will only be called if matching elements are found.
|
|
447
474
|
|
|
448
475
|
</details>
|
|
449
476
|
|
|
@@ -637,16 +664,22 @@ dr-value-link: <input type="text" dr-value-link="@this@.office.addr.street"> <br
|
|
|
637
664
|
age: <input name="age">
|
|
638
665
|
<button type="submit">submit</button>
|
|
639
666
|
</form>
|
|
640
|
-
<script>
|
|
641
|
-
let data = {
|
|
642
|
-
form: {},
|
|
643
|
-
submit() {
|
|
644
|
-
console.log(this.form);
|
|
645
|
-
}
|
|
646
|
-
};
|
|
647
|
-
data = DomRender.run(data , document.querySelector('#app'));
|
|
648
|
-
</script>
|
|
649
667
|
</body>
|
|
668
|
+
```
|
|
669
|
+
```typescript
|
|
670
|
+
import { DomRender } from '@dooboostore/dom-render';
|
|
671
|
+
|
|
672
|
+
class FormData {
|
|
673
|
+
form: any = {};
|
|
674
|
+
|
|
675
|
+
submit() {
|
|
676
|
+
console.log(this.form);
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
const data = new FormData();
|
|
681
|
+
DomRender.run(data, document.querySelector('#app')!);
|
|
682
|
+
```
|
|
650
683
|
<!-- 💥 submit call -->
|
|
651
684
|
<!-- console: {name: 'name data', age: 'age data'} -->
|
|
652
685
|
```
|
|
@@ -662,24 +695,33 @@ dr-value-link: <input type="text" dr-value-link="@this@.office.addr.street"> <br
|
|
|
662
695
|
age: <input name="age">
|
|
663
696
|
<button type="submit">submit</button>
|
|
664
697
|
</form>
|
|
665
|
-
<script>
|
|
666
|
-
const form = new FormValidator();
|
|
667
|
-
form.name = new NotEmptyValidator();
|
|
668
|
-
form.age = new NotEmptyValidator();
|
|
669
|
-
let data = {
|
|
670
|
-
form,
|
|
671
|
-
submit() {
|
|
672
|
-
if (this.form.valid()){
|
|
673
|
-
console.log('valid');
|
|
674
|
-
} else {
|
|
675
|
-
console.log('inValid');
|
|
676
|
-
}
|
|
677
|
-
}
|
|
678
|
-
};
|
|
679
|
-
data = DomRender.run(data , document.querySelector('#app'));
|
|
680
|
-
</script>
|
|
681
698
|
</body>
|
|
682
699
|
```
|
|
700
|
+
```typescript
|
|
701
|
+
import { DomRender } from '@dooboostore/dom-render';
|
|
702
|
+
import { FormValidator, NotEmptyValidator } from '@dooboostore/dom-render/validator';
|
|
703
|
+
|
|
704
|
+
class FormDataWithValidator {
|
|
705
|
+
form: FormValidator = new FormValidator();
|
|
706
|
+
|
|
707
|
+
constructor() {
|
|
708
|
+
this.form.name = new NotEmptyValidator();
|
|
709
|
+
this.form.age = new NotEmptyValidator();
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
submit() {
|
|
713
|
+
if (this.form.valid()){
|
|
714
|
+
console.log('valid');
|
|
715
|
+
} else {
|
|
716
|
+
console.log('inValid');
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
const data = new FormDataWithValidator();
|
|
722
|
+
DomRender.run(data, document.querySelector('#app')!);
|
|
723
|
+
```
|
|
724
|
+
```
|
|
683
725
|
validator
|
|
684
726
|
- Validator (abstract)
|
|
685
727
|
- ValidatorArray (abstract)
|
|
@@ -16,7 +16,7 @@ export declare function attribute(options?: {
|
|
|
16
16
|
name?: string;
|
|
17
17
|
converter?: (value: string | null) => any;
|
|
18
18
|
}): PropertyDecorator;
|
|
19
|
-
export declare function query(selector: string):
|
|
19
|
+
export declare function query(selector: string): (target: object, propertyKey: string | symbol, descriptor?: PropertyDescriptor) => void;
|
|
20
20
|
export declare function event(options: {
|
|
21
21
|
query: string;
|
|
22
22
|
name: string;
|
|
@@ -8,32 +8,46 @@ 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
10
|
import 'reflect-metadata';
|
|
11
|
+
import { ElementUtils } from '@dooboostore/core-web/element/ElementUtils';
|
|
11
12
|
import { RawSet } from '../rawsets/RawSet';
|
|
12
13
|
import { DomRenderNoProxy } from '../decorators/DomRenderNoProxy';
|
|
13
14
|
import { ConvertUtils } from '@dooboostore/core/convert/ConvertUtils';
|
|
14
15
|
import * as Render from '../rawsets/Render';
|
|
16
|
+
import { ReflectUtils } from '@dooboostore/core/reflect/ReflectUtils';
|
|
15
17
|
export const ATTRIBUTE_METADATA_KEY = Symbol('attribute');
|
|
16
18
|
export const QUERY_METADATA_KEY = Symbol('query');
|
|
17
19
|
export const EVENT_METADATA_KEY = Symbol('event');
|
|
18
20
|
export function attribute(options) {
|
|
19
21
|
return (target, propertyKey) => {
|
|
20
|
-
|
|
22
|
+
const attributes = ReflectUtils.getMetadata(ATTRIBUTE_METADATA_KEY, target.constructor) || [];
|
|
23
|
+
attributes.push({
|
|
24
|
+
propertyKey: propertyKey,
|
|
25
|
+
name: (options === null || options === void 0 ? void 0 : options.name) || propertyKey,
|
|
26
|
+
converter: options === null || options === void 0 ? void 0 : options.converter
|
|
27
|
+
});
|
|
28
|
+
ReflectUtils.defineMetadata(ATTRIBUTE_METADATA_KEY, attributes, target.constructor);
|
|
21
29
|
};
|
|
22
30
|
}
|
|
23
31
|
export function query(selector) {
|
|
24
|
-
return (target, propertyKey) => {
|
|
25
|
-
|
|
32
|
+
return (target, propertyKey, descriptor) => {
|
|
33
|
+
const queries = ReflectUtils.getMetadata(QUERY_METADATA_KEY, target.constructor) || [];
|
|
34
|
+
queries.push({
|
|
35
|
+
selector: selector,
|
|
36
|
+
propertyKey: propertyKey,
|
|
37
|
+
isMethod: !!descriptor
|
|
38
|
+
});
|
|
39
|
+
ReflectUtils.defineMetadata(QUERY_METADATA_KEY, queries, target.constructor);
|
|
26
40
|
};
|
|
27
41
|
}
|
|
28
42
|
export function event(options) {
|
|
29
43
|
return (target, propertyKey, descriptor) => {
|
|
30
|
-
const events =
|
|
44
|
+
const events = ReflectUtils.getMetadata(EVENT_METADATA_KEY, target.constructor) || [];
|
|
31
45
|
events.push({
|
|
32
46
|
query: options.query,
|
|
33
47
|
eventName: options.name,
|
|
34
48
|
methodName: propertyKey
|
|
35
49
|
});
|
|
36
|
-
|
|
50
|
+
ReflectUtils.defineMetadata(EVENT_METADATA_KEY, events, target.constructor);
|
|
37
51
|
};
|
|
38
52
|
}
|
|
39
53
|
export class ComponentBase {
|
|
@@ -47,25 +61,13 @@ export class ComponentBase {
|
|
|
47
61
|
var _a, _b, _c, _d;
|
|
48
62
|
const e = this.getElement();
|
|
49
63
|
if (e) {
|
|
50
|
-
return
|
|
64
|
+
return ElementUtils.querySelectorAll(e, selector);
|
|
51
65
|
}
|
|
52
66
|
else {
|
|
53
67
|
const startNode = (_b = (_a = this.rawSet) === null || _a === void 0 ? void 0 : _a.point) === null || _b === void 0 ? void 0 : _b.start;
|
|
54
68
|
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
|
|
56
|
-
|
|
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
|
+
if (startNode && endNode) {
|
|
70
|
+
return ElementUtils.querySelectorAll({ start: startNode, end: endNode }, selector);
|
|
69
71
|
}
|
|
70
72
|
}
|
|
71
73
|
return [];
|
|
@@ -171,12 +173,12 @@ export class ComponentBase {
|
|
|
171
173
|
if (other.rawSet) {
|
|
172
174
|
this.setRawSet(other.rawSet);
|
|
173
175
|
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
if (this.equalsAttributeName(name,
|
|
178
|
-
const finalValue =
|
|
179
|
-
this[propertyKey] = finalValue;
|
|
176
|
+
const attributes = ReflectUtils.getMetadata(ATTRIBUTE_METADATA_KEY, this.constructor);
|
|
177
|
+
if (attributes && Array.isArray(attributes)) {
|
|
178
|
+
for (const attrInfo of attributes) {
|
|
179
|
+
if (this.equalsAttributeName(name, attrInfo.name)) {
|
|
180
|
+
const finalValue = attrInfo.converter ? attrInfo.converter(value) : value;
|
|
181
|
+
this[attrInfo.propertyKey] = finalValue;
|
|
180
182
|
}
|
|
181
183
|
}
|
|
182
184
|
}
|
|
@@ -189,21 +191,38 @@ export class ComponentBase {
|
|
|
189
191
|
e.component = this;
|
|
190
192
|
}
|
|
191
193
|
// Handle @query decorators
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
const
|
|
196
|
-
const elements = this._queryElements(
|
|
197
|
-
if (
|
|
198
|
-
this[propertyKey]
|
|
194
|
+
const queries = ReflectUtils.getMetadata(QUERY_METADATA_KEY, this.constructor);
|
|
195
|
+
if (queries && Array.isArray(queries)) {
|
|
196
|
+
for (const queryInfo of queries) {
|
|
197
|
+
const { selector, propertyKey, isMethod } = queryInfo;
|
|
198
|
+
const elements = this._queryElements(selector);
|
|
199
|
+
if (isMethod) {
|
|
200
|
+
const method = this[propertyKey];
|
|
201
|
+
if (typeof method === 'function') {
|
|
202
|
+
const paramTypes = ReflectUtils.getParameterTypes(this, propertyKey);
|
|
203
|
+
if (paramTypes && paramTypes.length > 0 && paramTypes[0] === Array) {
|
|
204
|
+
method.call(this, elements);
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
if (elements.length > 0) {
|
|
208
|
+
method.call(this, elements[0]);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
199
212
|
}
|
|
200
213
|
else {
|
|
201
|
-
|
|
214
|
+
const propertyType = ReflectUtils.getType(this, propertyKey);
|
|
215
|
+
if (propertyType === Array) {
|
|
216
|
+
this[propertyKey] = elements;
|
|
217
|
+
}
|
|
218
|
+
else {
|
|
219
|
+
this[propertyKey] = elements[0] || null;
|
|
220
|
+
}
|
|
202
221
|
}
|
|
203
222
|
}
|
|
204
223
|
}
|
|
205
224
|
// Handle @event decorators
|
|
206
|
-
const events =
|
|
225
|
+
const events = ReflectUtils.getMetadata(EVENT_METADATA_KEY, this.constructor);
|
|
207
226
|
if (events && Array.isArray(events)) {
|
|
208
227
|
for (const eventInfo of events) {
|
|
209
228
|
const elements = this._queryElements(eventInfo.query);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dooboostore/dom-render",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.102",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "html view template engine",
|
|
6
6
|
"keywords": [
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
46
|
"reflect-metadata": "^0.2.2",
|
|
47
|
-
"@dooboostore/core": "1.0.
|
|
47
|
+
"@dooboostore/core": "1.0.8",
|
|
48
48
|
"@dooboostore/core-web": "1.0.1",
|
|
49
49
|
"@dooboostore/core-node": "1.0.1"
|
|
50
50
|
},
|