@reidelsaltres/pureper 0.1.160 → 0.1.163
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/out/foundation/Triplet.d.ts.map +1 -1
- package/out/foundation/Triplet.js +4 -5
- package/out/foundation/Triplet.js.map +1 -1
- package/out/foundation/component_api/Component.d.ts +2 -2
- package/out/foundation/component_api/Component.d.ts.map +1 -1
- package/out/foundation/component_api/Component.js.map +1 -1
- package/out/foundation/component_api/UniHtml.d.ts +4 -10
- package/out/foundation/component_api/UniHtml.d.ts.map +1 -1
- package/out/foundation/component_api/UniHtml.js +7 -15
- package/out/foundation/component_api/UniHtml.js.map +1 -1
- package/out/foundation/engine/Rule.d.ts +2 -0
- package/out/foundation/engine/Rule.d.ts.map +1 -1
- package/out/foundation/engine/Rule.js.map +1 -1
- package/out/foundation/engine/Scope.d.ts +5 -1
- package/out/foundation/engine/Scope.d.ts.map +1 -1
- package/out/foundation/engine/Scope.js +12 -3
- package/out/foundation/engine/Scope.js.map +1 -1
- package/out/foundation/engine/TemplateEngine.d.ts +17 -0
- package/out/foundation/engine/TemplateEngine.d.ts.map +1 -1
- package/out/foundation/engine/TemplateEngine.js +51 -3
- package/out/foundation/engine/TemplateEngine.js.map +1 -1
- package/out/foundation/engine/TemplateInstance.d.ts +167 -47
- package/out/foundation/engine/TemplateInstance.d.ts.map +1 -1
- package/out/foundation/engine/TemplateInstance.js +500 -118
- package/out/foundation/engine/TemplateInstance.js.map +1 -1
- package/out/foundation/engine/TemplateInstance.old.d.ts +219 -0
- package/out/foundation/engine/TemplateInstance.old.d.ts.map +1 -0
- package/out/foundation/engine/TemplateInstance.old.js +487 -0
- package/out/foundation/engine/TemplateInstance.old.js.map +1 -0
- package/out/foundation/engine/rules/attribute/RefRule.d.ts.map +1 -1
- package/out/foundation/engine/rules/attribute/RefRule.js +7 -1
- package/out/foundation/engine/rules/attribute/RefRule.js.map +1 -1
- package/out/index.d.ts +0 -2
- package/out/index.d.ts.map +1 -1
- package/out/index.js +0 -2
- package/out/index.js.map +1 -1
- package/package.json +1 -1
- package/src/foundation/Triplet.ts +6 -6
- package/src/foundation/component_api/Component.ts +2 -1
- package/src/foundation/component_api/UniHtml.ts +12 -22
- package/src/foundation/engine/Rule.ts +2 -0
- package/src/foundation/engine/Scope.ts +13 -3
- package/src/foundation/engine/TemplateEngine.ts +79 -4
- package/src/foundation/engine/TemplateInstance.md +110 -0
- package/src/foundation/engine/TemplateInstance.old.ts +673 -0
- package/src/foundation/engine/TemplateInstance.ts +635 -147
- package/src/foundation/engine/rules/attribute/RefRule.ts +8 -1
- package/src/index.ts +0 -6
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* Designed to replace legacy Page and Component base classes.
|
|
7
7
|
*/
|
|
8
8
|
import IElementHolder from "../api/ElementHolder.js";
|
|
9
|
+
import { TemplateInstance } from "../engine/TemplateEngine.js";
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
/**
|
|
@@ -13,7 +14,6 @@ import IElementHolder from "../api/ElementHolder.js";
|
|
|
13
14
|
* Use static factory methods for instantiation.
|
|
14
15
|
*/
|
|
15
16
|
export default class UniHtml {
|
|
16
|
-
|
|
17
17
|
/**
|
|
18
18
|
* Unified component lifecycle entrypoint.
|
|
19
19
|
* Loads HTML, then calls preLoadJS, render, and postLoadJS hooks in order.
|
|
@@ -21,30 +21,27 @@ export default class UniHtml {
|
|
|
21
21
|
*/
|
|
22
22
|
public async load(element: HTMLElement | ShadowRoot): Promise<void> {;
|
|
23
23
|
await this.preInit();
|
|
24
|
-
const preHtml:
|
|
25
|
-
const html:
|
|
24
|
+
const preHtml: TemplateInstance = await this._init();
|
|
25
|
+
const html: TemplateInstance = await this._postInit(preHtml);
|
|
26
26
|
|
|
27
|
-
const localRoot = html;
|
|
28
|
-
|
|
29
|
-
const holder : IElementHolder = { element: localRoot };
|
|
30
27
|
|
|
31
28
|
// ВАЖНО: preLoad() вызывается ДО монтирования в DOM/Shadow DOM.
|
|
32
29
|
// Для компонентов (UniHtmlComponent) на этом этапе ещё нельзя полагаться на this.shadowRoot —
|
|
33
30
|
// используйте переданный localRoot для подготовки DOM, данных и навешивания обработчиков.
|
|
34
31
|
// Это предпочтительный этап инициализации для компонентов.
|
|
35
|
-
await this.preLoad(
|
|
32
|
+
await this.preLoad(html);
|
|
36
33
|
// render() отвечает за помещение содержимого из localRoot в конечную цель (renderTarget).
|
|
37
34
|
// В UniHtmlComponent.render() после вызова базового render() происходит добавление wrapper в shadowRoot.
|
|
38
|
-
await this.render(
|
|
35
|
+
await this.render(html, element);
|
|
39
36
|
// postLoad() вызывается ПОСЛЕ render(). Для компонентов к этому моменту содержимое уже добавлено
|
|
40
37
|
// внутрь shadowRoot, и можно безопасно работать с this.shadowRoot, измерениями layout и т.п.
|
|
41
|
-
await this.postLoad(
|
|
38
|
+
await this.postLoad(html);
|
|
42
39
|
}
|
|
43
40
|
|
|
44
|
-
private async _postInit(html:
|
|
41
|
+
private async _postInit(html: TemplateInstance): Promise<TemplateInstance> {
|
|
45
42
|
throw new Error("Method not implemented.");
|
|
46
43
|
}
|
|
47
|
-
private async _init(): Promise<
|
|
44
|
+
private async _init(): Promise<TemplateInstance> {
|
|
48
45
|
throw new Error("Method not implemented.");
|
|
49
46
|
}
|
|
50
47
|
|
|
@@ -55,34 +52,27 @@ export default class UniHtml {
|
|
|
55
52
|
* РЕКОМЕНДАЦИЯ: предпочитайте выполнять основную подготовку, поиск элементов, навешивание обработчиков
|
|
56
53
|
* на узлы из localRoot именно здесь; затем render() вставит их в целевой контейнер/теневой DOM.
|
|
57
54
|
*/
|
|
58
|
-
protected async preLoad(
|
|
55
|
+
protected async preLoad(template: TemplateInstance) { }
|
|
59
56
|
/**
|
|
60
57
|
* Hook after rendering (e.g., event binding).
|
|
61
58
|
* Для компонентов вызывается после того, как содержимое вставлено в shadowRoot (см. UniHtmlComponent.render()).
|
|
62
59
|
* Используйте этот этап только когда необходим доступ к реально смонтированному DOM (layout/measurements,
|
|
63
60
|
* интеграции, требующие присутствия в документе). В остальных случаях предпочитайте preLoad().
|
|
64
61
|
*/
|
|
65
|
-
protected async postLoad(
|
|
62
|
+
protected async postLoad(template: TemplateInstance) { }
|
|
66
63
|
/**
|
|
67
64
|
* Main rendering step. By default, simply inserts HTML into the container.
|
|
68
65
|
* Override in subclasses for custom rendering logic.
|
|
69
66
|
* @param element Target container
|
|
70
67
|
* @param html HTML content
|
|
71
68
|
*/
|
|
72
|
-
protected async render(
|
|
69
|
+
protected async render(template: TemplateInstance, renderTarget: HTMLElement | DocumentFragment): Promise<void> {
|
|
73
70
|
// Clear renderTarget
|
|
74
71
|
while (renderTarget.firstChild) {
|
|
75
72
|
renderTarget.removeChild(renderTarget.firstChild);
|
|
76
73
|
}
|
|
77
|
-
|
|
78
|
-
// Move all children from holder.element to renderTarget
|
|
79
|
-
const children = Array.from(holder.element.childNodes);
|
|
80
|
-
for (const child of children) {
|
|
81
|
-
renderTarget.appendChild(child);
|
|
82
|
-
}
|
|
74
|
+
template.bind(renderTarget as any);
|
|
83
75
|
|
|
84
|
-
// Update holder to point to renderTarget (now contains the content)
|
|
85
|
-
(holder as { element: HTMLElement | DocumentFragment }).element = renderTarget;
|
|
86
76
|
return Promise.resolve();
|
|
87
77
|
}
|
|
88
78
|
}
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
export default class Scope {
|
|
6
6
|
private variables: Record<string, any> = {};
|
|
7
7
|
private readonly debugWarnings: boolean;
|
|
8
|
+
/** Оригинальный объект для синхронизации refs */
|
|
9
|
+
private originalSource: object | null = null;
|
|
8
10
|
|
|
9
11
|
constructor(options?: { debugWarnings?: boolean }) {
|
|
10
12
|
this.debugWarnings = options?.debugWarnings ?? true;
|
|
@@ -19,12 +21,18 @@ export default class Scope {
|
|
|
19
21
|
|
|
20
22
|
/**
|
|
21
23
|
* Установить переменную в Scope
|
|
24
|
+
* @param syncToOriginal - синхронизировать с оригинальным объектом (по умолчанию true)
|
|
22
25
|
*/
|
|
23
|
-
public set(key: string, value: any): void {
|
|
24
|
-
if (this.debugWarnings && key in this.variables) {
|
|
25
|
-
|
|
26
|
+
public set(key: string, value: any, syncToOriginal: boolean = true): void {
|
|
27
|
+
if (this.debugWarnings && key in this.variables && this.variables[key] !== undefined) {
|
|
28
|
+
// Не предупреждаем при установке ref (значение было undefined)
|
|
26
29
|
}
|
|
27
30
|
this.variables[key] = value;
|
|
31
|
+
|
|
32
|
+
// Синхронизируем с оригинальным объектом если он есть
|
|
33
|
+
if (syncToOriginal && this.originalSource) {
|
|
34
|
+
(this.originalSource as any)[key] = value;
|
|
35
|
+
}
|
|
28
36
|
}
|
|
29
37
|
|
|
30
38
|
/**
|
|
@@ -146,9 +154,11 @@ export default class Scope {
|
|
|
146
154
|
|
|
147
155
|
/**
|
|
148
156
|
* Статический метод для создания Scope из объекта
|
|
157
|
+
* Сохраняет ссылку на оригинальный объект для синхронизации refs
|
|
149
158
|
*/
|
|
150
159
|
public static from(source: object | Record<string, any>, options?: { debugWarnings?: boolean }): Scope {
|
|
151
160
|
const scope = new Scope(options);
|
|
161
|
+
scope.originalSource = source;
|
|
152
162
|
scope.merge(source);
|
|
153
163
|
return scope;
|
|
154
164
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import Scope from './Scope.js';
|
|
2
2
|
import Expression from './Expression.js';
|
|
3
3
|
import EscapeHandler from './EscapeHandler.js';
|
|
4
|
-
import TemplateInstance, { TemplateSection } from './TemplateInstance.js';
|
|
4
|
+
import TemplateInstance, { TemplateSection, FragmentBinding } from './TemplateInstance.js';
|
|
5
5
|
import Rule, { RuleMatch, RuleResult, SyntaxRule, AttributeRule } from './Rule.js';
|
|
6
6
|
import Observable from '../api/Observer.js';
|
|
7
7
|
|
|
@@ -30,6 +30,8 @@ export interface ProcessResult {
|
|
|
30
30
|
output: string;
|
|
31
31
|
observables: Observable<any>[];
|
|
32
32
|
sections: TemplateSection[];
|
|
33
|
+
/** ID созданного фрагмента (если был создан) */
|
|
34
|
+
fragmentId?: string;
|
|
33
35
|
}
|
|
34
36
|
|
|
35
37
|
/**
|
|
@@ -97,10 +99,15 @@ export default class TemplateEngine {
|
|
|
97
99
|
* Обработать шаблон и вернуть TemplateInstance
|
|
98
100
|
*/
|
|
99
101
|
public parse(template: string): TemplateInstance {
|
|
100
|
-
const
|
|
101
|
-
const
|
|
102
|
+
const templateInstance = new TemplateInstance(this.scope);
|
|
103
|
+
const result = this.processTemplateWithFragments(template, this.scope, templateInstance, null);
|
|
102
104
|
|
|
103
|
-
//
|
|
105
|
+
// Устанавливаем корневой фрагмент
|
|
106
|
+
if (result.fragmentId) {
|
|
107
|
+
templateInstance.setRootFragment(result.fragmentId);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Add sections and track observables
|
|
104
111
|
for (const section of result.sections) {
|
|
105
112
|
templateInstance.addSection(section);
|
|
106
113
|
|
|
@@ -115,6 +122,31 @@ export default class TemplateEngine {
|
|
|
115
122
|
return templateInstance;
|
|
116
123
|
}
|
|
117
124
|
|
|
125
|
+
/**
|
|
126
|
+
* Обработать шаблон с созданием фрагментов
|
|
127
|
+
*/
|
|
128
|
+
private processTemplateWithFragments(
|
|
129
|
+
template: string,
|
|
130
|
+
scope: Scope,
|
|
131
|
+
instance: TemplateInstance,
|
|
132
|
+
parentFragmentId: string | null
|
|
133
|
+
): ProcessResult {
|
|
134
|
+
const result = this.processTemplate(template, scope);
|
|
135
|
+
|
|
136
|
+
// Создаём фрагмент для этого результата
|
|
137
|
+
const fragmentId = instance.createFragment(
|
|
138
|
+
result.output,
|
|
139
|
+
template,
|
|
140
|
+
result.sections,
|
|
141
|
+
parentFragmentId
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
return {
|
|
145
|
+
...result,
|
|
146
|
+
fragmentId
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
118
150
|
/**
|
|
119
151
|
* Обработать шаблон (внутренний метод, используется Rule для рекурсии)
|
|
120
152
|
*/
|
|
@@ -219,6 +251,49 @@ export default class TemplateEngine {
|
|
|
219
251
|
.replace(/@injection\[(head|tail)\]\s*=\s*["'][^"']*["']/gi, '');
|
|
220
252
|
}
|
|
221
253
|
|
|
254
|
+
/**
|
|
255
|
+
* Добавить новый шаблон в существующий TemplateInstance.
|
|
256
|
+
* Обрабатывает шаблон и добавляет результат как новый фрагмент.
|
|
257
|
+
* Если instance привязан к контейнерам, DOM обновится автоматически.
|
|
258
|
+
*
|
|
259
|
+
* @param instance - существующий TemplateInstance
|
|
260
|
+
* @param template - новый шаблон для добавления
|
|
261
|
+
* @param customScope - опциональный scope для нового шаблона
|
|
262
|
+
* @returns ID созданного фрагмента
|
|
263
|
+
*/
|
|
264
|
+
public appendTemplate(instance: TemplateInstance, template: string, customScope?: Scope | object): string {
|
|
265
|
+
const scope = customScope
|
|
266
|
+
? (customScope instanceof Scope ? customScope : Scope.from(customScope))
|
|
267
|
+
: instance.getScope();
|
|
268
|
+
|
|
269
|
+
// Обрабатываем шаблон
|
|
270
|
+
const result = this.processTemplate(template, scope);
|
|
271
|
+
|
|
272
|
+
// Создаём фрагмент
|
|
273
|
+
const fragmentId = instance.createFragment(
|
|
274
|
+
result.output,
|
|
275
|
+
template,
|
|
276
|
+
result.sections,
|
|
277
|
+
null // без родителя - это отдельный фрагмент
|
|
278
|
+
);
|
|
279
|
+
|
|
280
|
+
// Добавляем секции и отслеживаем Observable
|
|
281
|
+
for (const section of result.sections) {
|
|
282
|
+
instance.addSection(section);
|
|
283
|
+
|
|
284
|
+
for (const observable of section.result.observables || []) {
|
|
285
|
+
instance.trackObservable(observable, section, (s) => {
|
|
286
|
+
return this.processTemplate(s.sourceTemplate, scope);
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// Вставляем в привязанные контейнеры
|
|
292
|
+
instance.insertAppendedFragment(fragmentId);
|
|
293
|
+
|
|
294
|
+
return fragmentId;
|
|
295
|
+
}
|
|
296
|
+
|
|
222
297
|
/**
|
|
223
298
|
* Статический метод для быстрой обработки
|
|
224
299
|
*/
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
Проанализируй текущий код TemplateInstance, Переделай TemplateInstance
|
|
2
|
+
|
|
3
|
+
# Предположительный пример структуры и работы
|
|
4
|
+
|
|
5
|
+
Должен содержать множество различных мелких фрагментов, и при обновлении rebuild-ить только те на которые изменения повлияли, т.е
|
|
6
|
+
Если:
|
|
7
|
+
<div>@(observable1)</div>
|
|
8
|
+
<div>@(observable2)</div>
|
|
9
|
+
|
|
10
|
+
должно создатся 2 фрагмента
|
|
11
|
+
1. <div>@(observable1)</div> - пересоздастся только при изменении observable1, изменения observable2 не повлияют на него
|
|
12
|
+
2. <div>@(observable2)</div> - пересоздастся только при изменении observable2, изменения observable1 не повлияют на него
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
Если:
|
|
17
|
+
<div>@(observable1) и @(observable2)</div> то изменения observable1 и изменение observable2 должны влиять
|
|
18
|
+
|
|
19
|
+
здесь 1 фрагмент:
|
|
20
|
+
1. <div>@(observable1) и @(observable2)</div> #id1
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
Если:
|
|
25
|
+
<div @[ref]="'outer'">
|
|
26
|
+
<span>@(observable1)</span>
|
|
27
|
+
<span>@(observable2)</span>
|
|
28
|
+
@for (observableVar in observableCollection) {
|
|
29
|
+
<span>@(observableVar)</span>
|
|
30
|
+
}
|
|
31
|
+
</div>
|
|
32
|
+
|
|
33
|
+
здесь 5+ фрагментов:
|
|
34
|
+
1. <div @[ref]="'outer'"> #id1
|
|
35
|
+
#id2
|
|
36
|
+
#id3
|
|
37
|
+
#id4
|
|
38
|
+
</div>
|
|
39
|
+
2. <span>@(observable1)</span> #id2
|
|
40
|
+
3. <span>@(observable2)</span> #id3
|
|
41
|
+
4. *RuleFragment* @for (observableVar in observableCollection) { #id5 } #id4
|
|
42
|
+
5+ <span>@(observableVar)</span> #id5
|
|
43
|
+
|
|
44
|
+
3 отдельных фрагмента (#id5-a, #id5-b, #id5-c), каждый обновляется независимо
|
|
45
|
+
|
|
46
|
+
Если изменится observable1 то пересоздастся должен только #id2
|
|
47
|
+
Если изменится observableCollection то пересоздастся должен #id4 и соответсвенно это коснётся и вложенного в него #id5
|
|
48
|
+
Измеения в var в #id5 должно пересоздать только #id5
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
Если Scope { observable1 = new Observable(1),
|
|
52
|
+
observable1 = new Observable(2),
|
|
53
|
+
observableCollection = new Observable([55,56,57]) }
|
|
54
|
+
|
|
55
|
+
то результат будет выглядеть так:
|
|
56
|
+
|
|
57
|
+
<div id="'outer'">
|
|
58
|
+
<span>1</span>
|
|
59
|
+
<span>2</span>
|
|
60
|
+
<span>55</span>
|
|
61
|
+
<span>56</span>
|
|
62
|
+
<span>57</span>
|
|
63
|
+
</div>
|
|
64
|
+
|
|
65
|
+
но при изменении observable1.setObject(999) то результат станет:
|
|
66
|
+
|
|
67
|
+
<div id="'outer'">
|
|
68
|
+
<span>999</span>
|
|
69
|
+
<span>2</span>
|
|
70
|
+
<span>55</span>
|
|
71
|
+
<span>56</span>
|
|
72
|
+
<span>57</span>
|
|
73
|
+
</div>
|
|
74
|
+
|
|
75
|
+
если TemplateInstance имеет биндинг с контейнером, то аналогичные изменения должны происходить и в контейнере
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
изменения observable1 повлияет только на #id1
|
|
82
|
+
изменения observabıe2 повлияет только на #id2
|
|
83
|
+
|
|
84
|
+
TemplateInstance должен иметь эмитить ивенты на которые можно подписатся
|
|
85
|
+
onFragmentChange(event: FragmentChangeEvent)
|
|
86
|
+
|
|
87
|
+
должен иметь методы
|
|
88
|
+
bindRefs() - должен создавать в Scope переменные из @[ref] правила
|
|
89
|
+
unbindRefs() - должен удалять из Scope переменные из @[ref] правила (приравнивать к null)
|
|
90
|
+
|
|
91
|
+
bindEvents() - должен добавлять обработчик событий к html-element из @on[event] правила
|
|
92
|
+
unbindEvents() - должен удалять обработчик событий из html-element по @on[event] правилу
|
|
93
|
+
|
|
94
|
+
bind(container) - должен связывать внутреннюю DOM структуру с container, должен вставлять DOM в container
|
|
95
|
+
unbind(container) - должен удалять связь внутренней DOM структуры с container, оставляет элементы в container, но при изменении в TemplateInstance container уже не должен меняется
|
|
96
|
+
|
|
97
|
+
Если существует binding с container то изменения внутри TemplateInstance должны так же отражатся и на container,
|
|
98
|
+
удаление и добавление ивентов, динамическое обновление и пересоздание фагментов,
|
|
99
|
+
|
|
100
|
+
TemplateInstance может иметь множество контейнеров binding-ов
|
|
101
|
+
|
|
102
|
+
bind(container) - должен автоматически вызывать bindRefs,bindEvents
|
|
103
|
+
unbind(container) - должен автоматически вызывать unbindRefs,unbindEvents
|
|
104
|
+
|
|
105
|
+
# Уточнения
|
|
106
|
+
1. Архитектурная проблема — TemplateEngine не создаёт фрагменты:
|
|
107
|
+
Сейчас TemplateEngine.parse() возвращает один общий результат. Разбиение на фрагменты по Observable не происходит. Нужно менять логику парсинга. Изменения TemplateEngine приемлимы если требуются
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
|