rumious 2.0.2 → 2.1.1
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/dist/app/app.js +20 -0
- package/dist/app/index.js +1 -0
- package/dist/component/component.js +38 -0
- package/dist/component/element.js +50 -0
- package/dist/component/index.js +2 -0
- package/dist/context/context.d.ts +11 -0
- package/dist/context/index.d.ts +1 -0
- package/dist/global.js +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +90 -1
- package/dist/jsx/component.js +6 -0
- package/dist/jsx/dynamic.js +65 -0
- package/dist/jsx/element.js +50 -0
- package/dist/jsx/index.js +4 -0
- package/dist/jsx/template.js +281 -0
- package/dist/module/index.js +2 -0
- package/dist/ref/index.js +1 -0
- package/dist/ref/ref.js +147 -0
- package/dist/render/context.js +9 -0
- package/dist/render/index.js +4 -0
- package/dist/render/list.js +96 -0
- package/dist/render/render.js +17 -0
- package/dist/render/view.js +76 -0
- package/dist/state/index.d.ts +1 -0
- package/dist/state/index.js +3 -0
- package/dist/state/list.js +79 -0
- package/dist/state/reactor.js +53 -0
- package/dist/state/state.d.ts +1 -0
- package/dist/state/state.js +53 -0
- package/dist/state/store.d.ts +14 -0
- package/dist/state/store.js +17 -0
- package/dist/types/component.js +1 -0
- package/dist/types/index.js +3 -0
- package/dist/types/state.js +1 -0
- package/dist/types/template.js +1 -0
- package/dist/utils/checker.js +3 -0
- package/package.json +1 -1
package/dist/app/app.js
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
import { RumiousRenderContext, render } from '../render/index.js';
|
2
|
+
export class RumiousApp {
|
3
|
+
config;
|
4
|
+
modules = [];
|
5
|
+
context = new RumiousRenderContext(this, this);
|
6
|
+
constructor(config) {
|
7
|
+
this.config = config;
|
8
|
+
}
|
9
|
+
addModule(ModuleClass, options) {
|
10
|
+
const instance = ModuleClass.init(this, options);
|
11
|
+
this.modules.push(instance);
|
12
|
+
return instance;
|
13
|
+
}
|
14
|
+
render(content) {
|
15
|
+
render(content, this.config.root, this.context);
|
16
|
+
}
|
17
|
+
}
|
18
|
+
export function createApp(config) {
|
19
|
+
return new RumiousApp(config);
|
20
|
+
}
|
@@ -0,0 +1 @@
|
|
1
|
+
export * from './app.js';
|
@@ -0,0 +1,38 @@
|
|
1
|
+
import { RumiousRenderContext, render, createViewControl } from '../render/index.js';
|
2
|
+
export class RumiousComponent {
|
3
|
+
props;
|
4
|
+
app;
|
5
|
+
element;
|
6
|
+
context;
|
7
|
+
slot = null;
|
8
|
+
static tagName = 'rumious-component';
|
9
|
+
constructor() { }
|
10
|
+
createViewControl() {
|
11
|
+
return createViewControl();
|
12
|
+
}
|
13
|
+
mountTo(template, target) {
|
14
|
+
return render(template, target, this.context);
|
15
|
+
}
|
16
|
+
prepare(props, context, element) {
|
17
|
+
this.app = context.app;
|
18
|
+
this.element = element;
|
19
|
+
this.props = props;
|
20
|
+
this.context = new RumiousRenderContext(context.app, this);
|
21
|
+
}
|
22
|
+
template() {
|
23
|
+
throw new Error(`RumiousRenderError: Cannot render empty component !`);
|
24
|
+
}
|
25
|
+
requestRender() {
|
26
|
+
let template = this.template();
|
27
|
+
render(template, this.element, this.context);
|
28
|
+
}
|
29
|
+
remove() {
|
30
|
+
this.element.remove();
|
31
|
+
}
|
32
|
+
onCreate() { }
|
33
|
+
onRender() { }
|
34
|
+
onDestroy() { }
|
35
|
+
beforeRender() { }
|
36
|
+
}
|
37
|
+
export class Fragment extends RumiousComponent {
|
38
|
+
}
|
@@ -0,0 +1,50 @@
|
|
1
|
+
export class RumiousComponentElement extends HTMLElement {
|
2
|
+
component;
|
3
|
+
props;
|
4
|
+
context;
|
5
|
+
instance;
|
6
|
+
slotTempl = null;
|
7
|
+
constructor() {
|
8
|
+
super();
|
9
|
+
}
|
10
|
+
setContext(context) {
|
11
|
+
this.context = context;
|
12
|
+
}
|
13
|
+
async connectedCallback() {
|
14
|
+
let instance = new this.component();
|
15
|
+
this.instance = instance;
|
16
|
+
this.instance.slot = this.slotTempl;
|
17
|
+
instance.prepare(this.props, this.context, this);
|
18
|
+
instance.onCreate();
|
19
|
+
await instance.beforeRender();
|
20
|
+
instance.requestRender();
|
21
|
+
instance.onRender();
|
22
|
+
}
|
23
|
+
disconnectedCallback() {
|
24
|
+
this.instance.onDestroy();
|
25
|
+
}
|
26
|
+
setSlot(templ) {
|
27
|
+
this.slotTempl = templ;
|
28
|
+
}
|
29
|
+
}
|
30
|
+
export function createComponentElement(context, component, props) {
|
31
|
+
if (!window.customElements.get(component.tagName)) {
|
32
|
+
window.customElements.define(component.tagName, class extends RumiousComponentElement {
|
33
|
+
});
|
34
|
+
}
|
35
|
+
const element = document.createElement(component.tagName);
|
36
|
+
element.component = component;
|
37
|
+
element.props = props;
|
38
|
+
element.context = context;
|
39
|
+
return [element];
|
40
|
+
}
|
41
|
+
export function renderComponent(component, props) {
|
42
|
+
if (!window.customElements.get(component.tagName)) {
|
43
|
+
window.customElements.define(component.tagName, class extends RumiousComponentElement {
|
44
|
+
});
|
45
|
+
}
|
46
|
+
const element = document.createElement(component.tagName);
|
47
|
+
element.component = component;
|
48
|
+
element.props = props;
|
49
|
+
return element;
|
50
|
+
}
|
@@ -0,0 +1,11 @@
|
|
1
|
+
export declare class RumiousContext<T extends {}> {
|
2
|
+
values: T;
|
3
|
+
events: Record<string, Set<(...args: any[]) => void>>;
|
4
|
+
constructor(values: T);
|
5
|
+
set<K extends keyof T>(key: K, value: T[K]): void;
|
6
|
+
get<K extends keyof T>(key: K): T[K];
|
7
|
+
emit(event: string, ...args: any[]): void;
|
8
|
+
on(event: string, fn: (...args: any[]) => void): void;
|
9
|
+
off(event: string, fn?: (...args: any[]) => void): void;
|
10
|
+
}
|
11
|
+
export declare function createContext<T extends {}>(values: T): RumiousContext<T>;
|
@@ -0,0 +1 @@
|
|
1
|
+
export * from "./context.js";
|
package/dist/global.js
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
@@ -303,6 +303,9 @@ class RumiousState {
|
|
303
303
|
toJSON() {
|
304
304
|
return JSON.stringify(this.value);
|
305
305
|
}
|
306
|
+
equal(value) {
|
307
|
+
return value === this.value;
|
308
|
+
}
|
306
309
|
trigger() {
|
307
310
|
this.reactor?.notify({
|
308
311
|
type: 'set',
|
@@ -402,6 +405,45 @@ function createListState(values) {
|
|
402
405
|
return new RumiousListState(values);
|
403
406
|
}
|
404
407
|
|
408
|
+
class RumiousStore {
|
409
|
+
value;
|
410
|
+
states = {};
|
411
|
+
constructor(value) {
|
412
|
+
this.value = value;
|
413
|
+
for (const key in value) {
|
414
|
+
this.states[key] = createState(value[key]);
|
415
|
+
}
|
416
|
+
}
|
417
|
+
get(key) {
|
418
|
+
return this.states[key];
|
419
|
+
}
|
420
|
+
set(value) {
|
421
|
+
this.value = value;
|
422
|
+
for (const key in value) {
|
423
|
+
if (this.states[key]) {
|
424
|
+
this.states[key].value = value[key];
|
425
|
+
}
|
426
|
+
else {
|
427
|
+
this.states[key] = createState(value[key]);
|
428
|
+
}
|
429
|
+
}
|
430
|
+
}
|
431
|
+
map(fn) {
|
432
|
+
const results = [];
|
433
|
+
for (const key in this.states) {
|
434
|
+
results.push(fn(this.states[key], key));
|
435
|
+
}
|
436
|
+
return results;
|
437
|
+
}
|
438
|
+
remove(key) {
|
439
|
+
delete this.states[key];
|
440
|
+
delete this.value[key];
|
441
|
+
}
|
442
|
+
}
|
443
|
+
function createStore(value) {
|
444
|
+
return new RumiousStore(value);
|
445
|
+
}
|
446
|
+
|
405
447
|
class RumiousComponent {
|
406
448
|
props;
|
407
449
|
app;
|
@@ -868,6 +910,12 @@ const directives = {
|
|
868
910
|
element.value = String(state.get());
|
869
911
|
};
|
870
912
|
break;
|
913
|
+
case 'show':
|
914
|
+
reactive = () => element.style.display = state.get() ? '' : 'none';
|
915
|
+
break;
|
916
|
+
case 'hide':
|
917
|
+
reactive = () => element.style.display = state.get() ? 'none' : '';
|
918
|
+
break;
|
871
919
|
default:
|
872
920
|
throw new Error(`Unknown bind directive modifier: ${modifier}`);
|
873
921
|
}
|
@@ -1038,6 +1086,47 @@ function createComponent(root, context, component, props) {
|
|
1038
1086
|
class RumiousModule {
|
1039
1087
|
}
|
1040
1088
|
|
1089
|
+
class RumiousContext {
|
1090
|
+
values;
|
1091
|
+
events = {};
|
1092
|
+
constructor(values) {
|
1093
|
+
this.values = values;
|
1094
|
+
}
|
1095
|
+
set(key, value) {
|
1096
|
+
this.values[key] = value;
|
1097
|
+
}
|
1098
|
+
get(key) {
|
1099
|
+
return this.values[key];
|
1100
|
+
}
|
1101
|
+
emit(event, ...args) {
|
1102
|
+
const listeners = this.events[event];
|
1103
|
+
if (listeners) {
|
1104
|
+
for (const fn of listeners) {
|
1105
|
+
fn(...args);
|
1106
|
+
}
|
1107
|
+
}
|
1108
|
+
}
|
1109
|
+
on(event, fn) {
|
1110
|
+
if (!this.events[event]) {
|
1111
|
+
this.events[event] = new Set();
|
1112
|
+
}
|
1113
|
+
this.events[event].add(fn);
|
1114
|
+
}
|
1115
|
+
off(event, fn) {
|
1116
|
+
if (!this.events[event])
|
1117
|
+
return;
|
1118
|
+
if (fn) {
|
1119
|
+
this.events[event].delete(fn);
|
1120
|
+
}
|
1121
|
+
else {
|
1122
|
+
delete this.events[event];
|
1123
|
+
}
|
1124
|
+
}
|
1125
|
+
}
|
1126
|
+
function createContext(values) {
|
1127
|
+
return new RumiousContext(values);
|
1128
|
+
}
|
1129
|
+
|
1041
1130
|
const __version__ = "2.x";
|
1042
1131
|
|
1043
|
-
export { Fragment, RumiousApp, RumiousComponent, RumiousComponentElement, RumiousListState, RumiousModule, RumiousPagination, RumiousRef, RumiousRenderContext, RumiousState, RumiousViewControl, __version__, appendChild, createApp, createComponent, createComponentElement, createDynamicValue, createEvent, createListState, createRef, createState, createTemplate, createViewControl, delegateEvents, directives, element, html, render, renderComponent, renderFrag, replaceNode, unwatch, watch };
|
1132
|
+
export { Fragment, RumiousApp, RumiousComponent, RumiousComponentElement, RumiousContext, RumiousListState, RumiousModule, RumiousPagination, RumiousRef, RumiousRenderContext, RumiousState, RumiousStore, RumiousViewControl, __version__, appendChild, createApp, createComponent, createComponentElement, createContext, createDynamicValue, createEvent, createListState, createRef, createState, createStore, createTemplate, createViewControl, delegateEvents, directives, element, html, render, renderComponent, renderFrag, replaceNode, unwatch, watch };
|
@@ -0,0 +1,65 @@
|
|
1
|
+
import { isTemplate } from '../utils/checker.js';
|
2
|
+
import { RumiousState } from '../state/index.js';
|
3
|
+
import { RumiousComponentElement } from '../component/index.js';
|
4
|
+
function handleReactiveNode(node, value, context) {
|
5
|
+
let currentNode = node;
|
6
|
+
const update = () => {
|
7
|
+
if (!document.contains(currentNode) && value.reactor) {
|
8
|
+
value.reactor.removeBinding(update);
|
9
|
+
return;
|
10
|
+
}
|
11
|
+
const newNode = value.value;
|
12
|
+
if (newNode instanceof RumiousComponentElement) {
|
13
|
+
newNode.setContext(context);
|
14
|
+
}
|
15
|
+
currentNode.parentNode?.replaceChild(newNode, currentNode);
|
16
|
+
currentNode = newNode;
|
17
|
+
};
|
18
|
+
context.onRendered.push(() => {
|
19
|
+
update();
|
20
|
+
if (!value.reactor)
|
21
|
+
return;
|
22
|
+
value.reactor.addBinding(update);
|
23
|
+
});
|
24
|
+
return node;
|
25
|
+
}
|
26
|
+
function isPrimitive(value) {
|
27
|
+
return value === null || (typeof value !== 'object' && typeof value !== 'function');
|
28
|
+
}
|
29
|
+
export function createDynamicValue(context, value) {
|
30
|
+
if (Array.isArray(value)) {
|
31
|
+
const fragment = document.createDocumentFragment();
|
32
|
+
for (const item of value) {
|
33
|
+
if (isTemplate(item)) {
|
34
|
+
const rendered = item(document.createDocumentFragment(), context);
|
35
|
+
fragment.appendChild(rendered);
|
36
|
+
}
|
37
|
+
else if (isPrimitive(item)) {
|
38
|
+
if (item !== null && item !== undefined && item !== false) {
|
39
|
+
fragment.appendChild(document.createTextNode(String(item)));
|
40
|
+
}
|
41
|
+
}
|
42
|
+
}
|
43
|
+
return fragment;
|
44
|
+
}
|
45
|
+
if (isTemplate(value)) {
|
46
|
+
return value(document.createDocumentFragment(), context);
|
47
|
+
}
|
48
|
+
if (value instanceof RumiousState && value.value instanceof Node) {
|
49
|
+
return handleReactiveNode(document.createTextNode(''), value, context);
|
50
|
+
}
|
51
|
+
if (value instanceof RumiousState && value.reactor) {
|
52
|
+
let node = document.createTextNode('');
|
53
|
+
context.onRendered.push(() => {
|
54
|
+
node.textContent = String(value.get());
|
55
|
+
if (!value.reactor)
|
56
|
+
return;
|
57
|
+
value.reactor.addBinding((commit) => node.textContent = String(commit.state.get()));
|
58
|
+
});
|
59
|
+
return node;
|
60
|
+
}
|
61
|
+
if (isPrimitive(value) && value !== null && value !== undefined && value !== false) {
|
62
|
+
return document.createTextNode(String(value));
|
63
|
+
}
|
64
|
+
return document.createTextNode('');
|
65
|
+
}
|
@@ -0,0 +1,50 @@
|
|
1
|
+
export function appendChild(parent, node) {
|
2
|
+
if (typeof node === 'string')
|
3
|
+
parent.appendChild(document.createTextNode(node));
|
4
|
+
else
|
5
|
+
parent.appendChild(node);
|
6
|
+
}
|
7
|
+
export function element(parent, tagName, attrs) {
|
8
|
+
const el = document.createElement(tagName);
|
9
|
+
if (attrs) {
|
10
|
+
for (let key in attrs) {
|
11
|
+
el.setAttribute(key, attrs[key]);
|
12
|
+
}
|
13
|
+
}
|
14
|
+
parent.appendChild(el);
|
15
|
+
return el;
|
16
|
+
}
|
17
|
+
export function replaceNode(oldNode, newNode) {
|
18
|
+
const parent = oldNode.parentNode;
|
19
|
+
if (parent) {
|
20
|
+
parent.replaceChild(newNode, oldNode);
|
21
|
+
}
|
22
|
+
else {
|
23
|
+
console.warn('replaceNode: oldNode has no parent. Cannot replace.');
|
24
|
+
}
|
25
|
+
}
|
26
|
+
export function createEvent(target, name, callback) {
|
27
|
+
if (!target.__rumiousEvents) {
|
28
|
+
target.__rumiousEvents = {};
|
29
|
+
}
|
30
|
+
target.__rumiousEvents[name] = callback;
|
31
|
+
}
|
32
|
+
function triggerEvent(name, event) {
|
33
|
+
const path = (event.composedPath?.() ?? [event.target]);
|
34
|
+
for (const target of path) {
|
35
|
+
if (target instanceof HTMLElement &&
|
36
|
+
'__rumiousEvents' in target) {
|
37
|
+
const element = target;
|
38
|
+
const handler = element.__rumiousEvents?.[name];
|
39
|
+
if (handler) {
|
40
|
+
handler(event);
|
41
|
+
break;
|
42
|
+
}
|
43
|
+
}
|
44
|
+
}
|
45
|
+
}
|
46
|
+
export function delegateEvents(events) {
|
47
|
+
for (const name of events) {
|
48
|
+
window.addEventListener(name, (e) => triggerEvent(name, e));
|
49
|
+
}
|
50
|
+
}
|
@@ -0,0 +1,281 @@
|
|
1
|
+
import { RumiousRenderContext, renderFrag } from '../render/index.js';
|
2
|
+
import { RumiousRef } from '../ref/index.js';
|
3
|
+
import { createEvent } from './element.js';
|
4
|
+
export function createTemplate(fn) {
|
5
|
+
return Object.assign(fn, {
|
6
|
+
__isTemplate: true
|
7
|
+
});
|
8
|
+
}
|
9
|
+
export function html(h) {
|
10
|
+
let template = document.createElement('template');
|
11
|
+
template.innerHTML = h;
|
12
|
+
return template.content.cloneNode(true);
|
13
|
+
}
|
14
|
+
export const directives = {
|
15
|
+
ref(context, modifier, target, value) {
|
16
|
+
if (value instanceof RumiousRef) {
|
17
|
+
value.setTarget(target);
|
18
|
+
}
|
19
|
+
else {
|
20
|
+
throw new Error("Cannot setup element reference for non-RumiousRef object !");
|
21
|
+
}
|
22
|
+
},
|
23
|
+
model(context, modifier, element, state) {
|
24
|
+
const tag = element.tagName, type = element.type;
|
25
|
+
if (tag === "TEXTAREA") {
|
26
|
+
element.addEventListener("input", () => state.set(element.value));
|
27
|
+
}
|
28
|
+
else if (tag === "SELECT") {
|
29
|
+
element.addEventListener("change", () => {
|
30
|
+
const s = element;
|
31
|
+
state.set(s.multiple ? Array.from(s.selectedOptions).map(o => o.value) : s.value);
|
32
|
+
});
|
33
|
+
}
|
34
|
+
else if (tag === "INPUT") {
|
35
|
+
if (type === "checkbox") {
|
36
|
+
element.addEventListener("change", () => state.set(element.checked));
|
37
|
+
}
|
38
|
+
else if (type === "radio") {
|
39
|
+
element.addEventListener("change", () => {
|
40
|
+
const i = element;
|
41
|
+
if (i.checked)
|
42
|
+
state.set(i.value);
|
43
|
+
});
|
44
|
+
}
|
45
|
+
else if (type === "file") {
|
46
|
+
element.addEventListener("change", () => {
|
47
|
+
const f = element.files;
|
48
|
+
state.set(element.hasAttribute("multiple") ? f : f?.[0] ?? null);
|
49
|
+
});
|
50
|
+
}
|
51
|
+
else {
|
52
|
+
element.addEventListener("input", () => {
|
53
|
+
const val = element.value;
|
54
|
+
state.set(type === "number" ? (val === "" ? null : +val) : val);
|
55
|
+
});
|
56
|
+
}
|
57
|
+
}
|
58
|
+
},
|
59
|
+
on(context, event, element, callback) {
|
60
|
+
createEvent(element, event, callback);
|
61
|
+
},
|
62
|
+
bind(context, modifier, element, state) {
|
63
|
+
let reactive = () => { };
|
64
|
+
switch (modifier) {
|
65
|
+
case 'text':
|
66
|
+
reactive = () => { element.textContent = String(state.get()); };
|
67
|
+
break;
|
68
|
+
case 'html':
|
69
|
+
reactive = () => { element.innerHTML = String(state.get()); };
|
70
|
+
break;
|
71
|
+
case 'style':
|
72
|
+
reactive = () => {
|
73
|
+
const styles = state.get();
|
74
|
+
if (typeof styles === 'string') {
|
75
|
+
element.setAttribute('style', styles);
|
76
|
+
}
|
77
|
+
else if (typeof styles === 'object') {
|
78
|
+
Object.assign(element.style, styles);
|
79
|
+
}
|
80
|
+
};
|
81
|
+
break;
|
82
|
+
case 'class':
|
83
|
+
reactive = () => {
|
84
|
+
const cls = state.get();
|
85
|
+
if (typeof cls === 'string')
|
86
|
+
element.className = cls;
|
87
|
+
else if (Array.isArray(cls))
|
88
|
+
element.className = cls.join(' ');
|
89
|
+
else if (typeof cls === 'object') {
|
90
|
+
element.className = Object.entries(cls)
|
91
|
+
.filter(([_, active]) => active)
|
92
|
+
.map(([name]) => name)
|
93
|
+
.join(' ');
|
94
|
+
}
|
95
|
+
};
|
96
|
+
break;
|
97
|
+
case 'disabled':
|
98
|
+
reactive = () => {
|
99
|
+
if ('disabled' in element)
|
100
|
+
element.disabled = Boolean(state.get());
|
101
|
+
};
|
102
|
+
break;
|
103
|
+
case 'checked':
|
104
|
+
reactive = () => {
|
105
|
+
if (element instanceof HTMLInputElement || element instanceof HTMLInputElement) {
|
106
|
+
element.checked = Boolean(state.get());
|
107
|
+
}
|
108
|
+
};
|
109
|
+
break;
|
110
|
+
case 'value':
|
111
|
+
reactive = () => {
|
112
|
+
if ('value' in element)
|
113
|
+
element.value = String(state.get());
|
114
|
+
};
|
115
|
+
break;
|
116
|
+
case 'show':
|
117
|
+
reactive = () => element.style.display = state.get() ? '' : 'none';
|
118
|
+
break;
|
119
|
+
case 'hide':
|
120
|
+
reactive = () => element.style.display = state.get() ? 'none' : '';
|
121
|
+
break;
|
122
|
+
default:
|
123
|
+
throw new Error(`Unknown bind directive modifier: ${modifier}`);
|
124
|
+
}
|
125
|
+
function onStateChange(commit) {
|
126
|
+
if (!document.contains(element) && state.reactor) {
|
127
|
+
state.reactor.removeInternalBinding(onStateChange);
|
128
|
+
return;
|
129
|
+
}
|
130
|
+
reactive();
|
131
|
+
}
|
132
|
+
context.onRendered.push(() => {
|
133
|
+
reactive();
|
134
|
+
if (!state.reactor)
|
135
|
+
return;
|
136
|
+
state.reactor.addInternalBinding(onStateChange);
|
137
|
+
});
|
138
|
+
},
|
139
|
+
attr(context, attrName, element, state) {
|
140
|
+
function onStateChange(commit) {
|
141
|
+
if (!document.contains(element) && state.reactor) {
|
142
|
+
state.reactor.removeInternalBinding(onStateChange);
|
143
|
+
return;
|
144
|
+
}
|
145
|
+
element.setAttribute(attrName, String(state.get()));
|
146
|
+
}
|
147
|
+
context.onRendered.push(() => {
|
148
|
+
onStateChange();
|
149
|
+
if (!state.reactor)
|
150
|
+
return;
|
151
|
+
state.reactor.addInternalBinding(onStateChange);
|
152
|
+
});
|
153
|
+
},
|
154
|
+
prop(context, name, element, state) {
|
155
|
+
function onStateChange(commit) {
|
156
|
+
if (!document.contains(element) && state.reactor) {
|
157
|
+
state.reactor.removeInternalBinding(onStateChange);
|
158
|
+
return;
|
159
|
+
}
|
160
|
+
element[name] = state.get();
|
161
|
+
}
|
162
|
+
context.onRendered.push(() => {
|
163
|
+
onStateChange();
|
164
|
+
if (!state.reactor)
|
165
|
+
return;
|
166
|
+
state.reactor.addInternalBinding(onStateChange);
|
167
|
+
});
|
168
|
+
},
|
169
|
+
html(context, modifier, element, state) {
|
170
|
+
function onStateChange(commit) {
|
171
|
+
if (!document.contains(element) && state.reactor) {
|
172
|
+
state.reactor.removeInternalBinding(onStateChange);
|
173
|
+
return;
|
174
|
+
}
|
175
|
+
element.innerHTML = String(state.get());
|
176
|
+
}
|
177
|
+
context.onRendered.push(() => {
|
178
|
+
onStateChange();
|
179
|
+
if (!state.reactor)
|
180
|
+
return;
|
181
|
+
state.reactor.addInternalBinding(onStateChange);
|
182
|
+
});
|
183
|
+
},
|
184
|
+
show(context, modifier, element, state) {
|
185
|
+
function onStateChange(commit) {
|
186
|
+
if (!document.contains(element) && state.reactor) {
|
187
|
+
state.reactor.removeInternalBinding(onStateChange);
|
188
|
+
return;
|
189
|
+
}
|
190
|
+
element.style.display = Boolean(state.get()) ? 'block' : 'none';
|
191
|
+
}
|
192
|
+
context.onRendered.push(() => {
|
193
|
+
onStateChange();
|
194
|
+
if (!state.reactor)
|
195
|
+
return;
|
196
|
+
state.reactor.addInternalBinding(onStateChange);
|
197
|
+
});
|
198
|
+
},
|
199
|
+
hide(context, modifier, element, state) {
|
200
|
+
function onStateChange(commit) {
|
201
|
+
if (!document.contains(element) && state.reactor) {
|
202
|
+
state.reactor.removeInternalBinding(onStateChange);
|
203
|
+
return;
|
204
|
+
}
|
205
|
+
element.style.display = !Boolean(state.get()) ? 'block' : 'none';
|
206
|
+
}
|
207
|
+
context.onRendered.push(() => {
|
208
|
+
onStateChange();
|
209
|
+
if (!state.reactor)
|
210
|
+
return;
|
211
|
+
state.reactor.addInternalBinding(onStateChange);
|
212
|
+
});
|
213
|
+
},
|
214
|
+
each(context, modifier, element, configs) {
|
215
|
+
context = new RumiousRenderContext(context.app, context.target);
|
216
|
+
const keyToNode = new Map();
|
217
|
+
const nodeOrder = [];
|
218
|
+
for (const item of configs.value.value) {
|
219
|
+
const key = configs.key(item);
|
220
|
+
const templ = renderFrag(configs.templ(item, key), context);
|
221
|
+
const dom = templ.childNodes[0];
|
222
|
+
keyToNode.set(key, dom);
|
223
|
+
nodeOrder.push(key);
|
224
|
+
element.appendChild(dom);
|
225
|
+
}
|
226
|
+
if (!configs.value.reactor)
|
227
|
+
return;
|
228
|
+
configs.value.reactor.addInternalBinding((commit) => {
|
229
|
+
const value = commit.value;
|
230
|
+
const key = configs.key(value);
|
231
|
+
if (commit.type === 'remove') {
|
232
|
+
const oldDom = keyToNode.get(key);
|
233
|
+
if (oldDom) {
|
234
|
+
element.removeChild(oldDom);
|
235
|
+
keyToNode.delete(key);
|
236
|
+
const index = nodeOrder.indexOf(key);
|
237
|
+
if (index !== -1)
|
238
|
+
nodeOrder.splice(index, 1);
|
239
|
+
}
|
240
|
+
return;
|
241
|
+
}
|
242
|
+
const templ = renderFrag(configs.templ(value, key), context);
|
243
|
+
const dom = templ.childNodes[0];
|
244
|
+
switch (commit.type) {
|
245
|
+
case 'append':
|
246
|
+
keyToNode.set(key, dom);
|
247
|
+
nodeOrder.push(key);
|
248
|
+
element.appendChild(dom);
|
249
|
+
break;
|
250
|
+
case 'prepend':
|
251
|
+
keyToNode.set(key, dom);
|
252
|
+
nodeOrder.unshift(key);
|
253
|
+
element.prepend(dom);
|
254
|
+
break;
|
255
|
+
case 'update': {
|
256
|
+
const oldDom = keyToNode.get(key);
|
257
|
+
if (oldDom) {
|
258
|
+
keyToNode.set(key, dom);
|
259
|
+
element.replaceChild(dom, oldDom);
|
260
|
+
}
|
261
|
+
break;
|
262
|
+
}
|
263
|
+
case 'insert': {
|
264
|
+
const index = commit.key;
|
265
|
+
const anchorKey = nodeOrder[index];
|
266
|
+
const anchorNode = keyToNode.get(anchorKey) ?? null;
|
267
|
+
keyToNode.set(key, dom);
|
268
|
+
nodeOrder.splice(index, 0, key);
|
269
|
+
element.insertBefore(dom, anchorNode);
|
270
|
+
break;
|
271
|
+
}
|
272
|
+
}
|
273
|
+
});
|
274
|
+
},
|
275
|
+
view(context, modifier, element, configs) {
|
276
|
+
configs.addTarget({
|
277
|
+
element,
|
278
|
+
context
|
279
|
+
});
|
280
|
+
}
|
281
|
+
};
|
@@ -0,0 +1 @@
|
|
1
|
+
export * from './ref.js';
|
package/dist/ref/ref.js
ADDED
@@ -0,0 +1,147 @@
|
|
1
|
+
import { RumiousComponentElement } from '../component/index.js';
|
2
|
+
export class RumiousRef {
|
3
|
+
element;
|
4
|
+
_mounted = false;
|
5
|
+
_onMountCallbacks = [];
|
6
|
+
_onUnmountCallbacks = [];
|
7
|
+
constructor() { }
|
8
|
+
setTarget(element) {
|
9
|
+
this.element = element;
|
10
|
+
this._mounted = true;
|
11
|
+
for (const cb of this._onMountCallbacks) {
|
12
|
+
cb(element);
|
13
|
+
}
|
14
|
+
}
|
15
|
+
reset() {
|
16
|
+
if (this._mounted) {
|
17
|
+
for (const cb of this._onUnmountCallbacks) {
|
18
|
+
cb();
|
19
|
+
}
|
20
|
+
}
|
21
|
+
this.element = undefined;
|
22
|
+
this._mounted = false;
|
23
|
+
}
|
24
|
+
get() {
|
25
|
+
return this._mounted ? this.element : undefined;
|
26
|
+
}
|
27
|
+
isMounted() {
|
28
|
+
return this._mounted;
|
29
|
+
}
|
30
|
+
has() {
|
31
|
+
return this.isMounted();
|
32
|
+
}
|
33
|
+
onMount(cb) {
|
34
|
+
this._onMountCallbacks.push(cb);
|
35
|
+
if (this._mounted)
|
36
|
+
cb(this.element);
|
37
|
+
}
|
38
|
+
onUnmount(cb) {
|
39
|
+
this._onUnmountCallbacks.push(cb);
|
40
|
+
}
|
41
|
+
toString() {
|
42
|
+
return `[RumiousRef ${this._mounted ? "mounted" : "not mounted"}]`;
|
43
|
+
}
|
44
|
+
focus() {
|
45
|
+
this.assertMounted();
|
46
|
+
this.element.focus();
|
47
|
+
}
|
48
|
+
addClass(className) {
|
49
|
+
this.assertMounted();
|
50
|
+
this.element.classList.add(className);
|
51
|
+
}
|
52
|
+
removeClass(className) {
|
53
|
+
this.assertMounted();
|
54
|
+
this.element.classList.remove(className);
|
55
|
+
}
|
56
|
+
toggleClass(className) {
|
57
|
+
this.assertMounted();
|
58
|
+
this.element.classList.toggle(className);
|
59
|
+
}
|
60
|
+
setAttr(key, value) {
|
61
|
+
this.assertMounted();
|
62
|
+
this.element.setAttribute(key, value);
|
63
|
+
}
|
64
|
+
removeAttr(key) {
|
65
|
+
this.assertMounted();
|
66
|
+
this.element.removeAttribute(key);
|
67
|
+
}
|
68
|
+
query(selector) {
|
69
|
+
this.assertMounted();
|
70
|
+
return this.element.querySelector(selector);
|
71
|
+
}
|
72
|
+
queryAll(selector) {
|
73
|
+
this.assertMounted();
|
74
|
+
return this.element.querySelectorAll(selector);
|
75
|
+
}
|
76
|
+
get value() {
|
77
|
+
this.assertMounted();
|
78
|
+
return 'value' in this.element ? this.element.value : undefined;
|
79
|
+
}
|
80
|
+
set value(val) {
|
81
|
+
this.assertMounted();
|
82
|
+
if ('value' in this.element) {
|
83
|
+
this.element.value = val;
|
84
|
+
}
|
85
|
+
else {
|
86
|
+
throw new Error("RumiousRefError: Element has no 'value' property.");
|
87
|
+
}
|
88
|
+
}
|
89
|
+
get text() {
|
90
|
+
this.assertMounted();
|
91
|
+
return this.element.textContent;
|
92
|
+
}
|
93
|
+
set text(val) {
|
94
|
+
this.assertMounted();
|
95
|
+
this.element.textContent = val;
|
96
|
+
}
|
97
|
+
get html() {
|
98
|
+
this.assertMounted();
|
99
|
+
return this.element.innerHTML;
|
100
|
+
}
|
101
|
+
set html(val) {
|
102
|
+
this.assertMounted();
|
103
|
+
this.element.innerHTML = val;
|
104
|
+
}
|
105
|
+
get checked() {
|
106
|
+
this.assertMounted();
|
107
|
+
return 'checked' in this.element ? Boolean(this.element.checked) : false;
|
108
|
+
}
|
109
|
+
set checked(val) {
|
110
|
+
this.assertMounted();
|
111
|
+
if ('checked' in this.element) {
|
112
|
+
this.element.checked = val;
|
113
|
+
}
|
114
|
+
else {
|
115
|
+
throw new Error("RumiousRefError: Element has no 'checked' property.");
|
116
|
+
}
|
117
|
+
}
|
118
|
+
get disabled() {
|
119
|
+
this.assertMounted();
|
120
|
+
return 'disabled' in this.element ? Boolean(this.element.disabled) : false;
|
121
|
+
}
|
122
|
+
set disabled(val) {
|
123
|
+
this.assertMounted();
|
124
|
+
if ('disabled' in this.element) {
|
125
|
+
this.element.disabled = val;
|
126
|
+
}
|
127
|
+
else {
|
128
|
+
throw new Error("RumiousRefError: Element has no 'disabled' property.");
|
129
|
+
}
|
130
|
+
}
|
131
|
+
get component() {
|
132
|
+
if (this.element instanceof RumiousComponentElement) {
|
133
|
+
return this.element.instance;
|
134
|
+
}
|
135
|
+
else {
|
136
|
+
return null;
|
137
|
+
}
|
138
|
+
}
|
139
|
+
assertMounted() {
|
140
|
+
if (!this._mounted) {
|
141
|
+
throw new Error("RumiousRefError: Element is not mounted.");
|
142
|
+
}
|
143
|
+
}
|
144
|
+
}
|
145
|
+
export function createRef() {
|
146
|
+
return new RumiousRef();
|
147
|
+
}
|
@@ -0,0 +1,96 @@
|
|
1
|
+
export class RumiousPagination {
|
2
|
+
view;
|
3
|
+
data;
|
4
|
+
templFn;
|
5
|
+
keyFn;
|
6
|
+
currentPage = 0;
|
7
|
+
limit = 50;
|
8
|
+
pos = [0, 0];
|
9
|
+
constructor(view, data, templFn, keyFn) {
|
10
|
+
this.view = view;
|
11
|
+
this.data = data;
|
12
|
+
this.templFn = templFn;
|
13
|
+
this.keyFn = keyFn;
|
14
|
+
}
|
15
|
+
show() {
|
16
|
+
let [start, end] = this.calcPos();
|
17
|
+
let list = this.data.value.slice(start, end);
|
18
|
+
this.pos = [start, end];
|
19
|
+
for (let data of list) {
|
20
|
+
let key = this.keyFn(data);
|
21
|
+
let templ = this.templFn(data);
|
22
|
+
this.view.addChild(templ);
|
23
|
+
}
|
24
|
+
if (!this.data.reactor)
|
25
|
+
return;
|
26
|
+
this.data.reactor.addInternalBinding(this.onDataChange.bind(this));
|
27
|
+
}
|
28
|
+
calcPos() {
|
29
|
+
const size = this.data.value.length;
|
30
|
+
const totalPage = Math.ceil(size / this.limit);
|
31
|
+
const currentPage = Math.max(0, Math.min(this.currentPage, totalPage - 1));
|
32
|
+
const start = currentPage * this.limit;
|
33
|
+
const end = Math.min(start + this.limit, size);
|
34
|
+
return [start, end];
|
35
|
+
}
|
36
|
+
onDataChange(commit) {
|
37
|
+
const [start, end] = this.calcPos();
|
38
|
+
const total = this.data.value.length;
|
39
|
+
const { type, key, value } = commit;
|
40
|
+
if (type === 'set') {
|
41
|
+
this.view.emptyAll();
|
42
|
+
this.show();
|
43
|
+
return;
|
44
|
+
}
|
45
|
+
if (typeof key === 'number' && key < start) {
|
46
|
+
this.view.emptyAll();
|
47
|
+
this.show();
|
48
|
+
return;
|
49
|
+
}
|
50
|
+
if (typeof key === 'number' && key >= start && key < end) {
|
51
|
+
const indexInView = key - start;
|
52
|
+
switch (type) {
|
53
|
+
case 'update': {
|
54
|
+
const item = this.data.value[key];
|
55
|
+
const templ = this.templFn(item);
|
56
|
+
this.view.updateChild(indexInView, templ);
|
57
|
+
break;
|
58
|
+
}
|
59
|
+
case 'remove': {
|
60
|
+
this.view.removeChild(indexInView);
|
61
|
+
const extraIndex = end - 1;
|
62
|
+
if (extraIndex < total) {
|
63
|
+
const extraItem = this.data.value[extraIndex];
|
64
|
+
const extraTemplate = this.templFn(extraItem);
|
65
|
+
this.view.addChild(extraTemplate);
|
66
|
+
}
|
67
|
+
break;
|
68
|
+
}
|
69
|
+
case 'insert':
|
70
|
+
case 'prepend': {
|
71
|
+
const item = this.data.value[key];
|
72
|
+
const templ = this.templFn(item);
|
73
|
+
this.view.addChild(templ, true);
|
74
|
+
const currentViewSize = end - start + 1;
|
75
|
+
if (currentViewSize > this.limit) {
|
76
|
+
this.view.removeChild(this.limit);
|
77
|
+
}
|
78
|
+
break;
|
79
|
+
}
|
80
|
+
case 'append': {
|
81
|
+
if (key < start + this.limit) {
|
82
|
+
const item = this.data.value[key];
|
83
|
+
const templ = this.templFn(item);
|
84
|
+
this.view.addChild(templ);
|
85
|
+
const currentViewSize = end - start + 1;
|
86
|
+
if (currentViewSize > this.limit) {
|
87
|
+
this.view.removeChild(0);
|
88
|
+
}
|
89
|
+
}
|
90
|
+
break;
|
91
|
+
}
|
92
|
+
}
|
93
|
+
return;
|
94
|
+
}
|
95
|
+
}
|
96
|
+
}
|
@@ -0,0 +1,17 @@
|
|
1
|
+
export function render(content, container, context) {
|
2
|
+
context.onRendered = [];
|
3
|
+
let result = content(container, context);
|
4
|
+
for (var i = 0; i < context.onRendered.length; i++) {
|
5
|
+
context.onRendered[i]();
|
6
|
+
}
|
7
|
+
return result;
|
8
|
+
}
|
9
|
+
export function renderFrag(content, context) {
|
10
|
+
let container = document.createDocumentFragment();
|
11
|
+
context.onRendered = [];
|
12
|
+
let result = content(container, context);
|
13
|
+
for (var i = 0; i < context.onRendered.length; i++) {
|
14
|
+
context.onRendered[i]();
|
15
|
+
}
|
16
|
+
return result;
|
17
|
+
}
|
@@ -0,0 +1,76 @@
|
|
1
|
+
import { render, renderFrag } from './render.js';
|
2
|
+
export class RumiousViewControl {
|
3
|
+
targets = [];
|
4
|
+
constructor() { }
|
5
|
+
addTarget(target) {
|
6
|
+
this.targets.push(target);
|
7
|
+
}
|
8
|
+
setView(template) {
|
9
|
+
const targets = this.targets;
|
10
|
+
if (targets.length === 0) {
|
11
|
+
throw new Error(`RumiousRenderError: No target assigned to ViewControl`);
|
12
|
+
}
|
13
|
+
for (let i = 0; i < targets.length; i++) {
|
14
|
+
render(template, targets[i].element, targets[i].context);
|
15
|
+
}
|
16
|
+
}
|
17
|
+
removeChild(index) {
|
18
|
+
for (let i = 0; i < this.targets.length; i++) {
|
19
|
+
let parent = this.targets[i].element.parentElement;
|
20
|
+
if (!parent)
|
21
|
+
return;
|
22
|
+
let element = parent.children[index];
|
23
|
+
if (element)
|
24
|
+
parent.removeChild(element);
|
25
|
+
}
|
26
|
+
}
|
27
|
+
addChild(template, prepend = false) {
|
28
|
+
const targets = this.targets;
|
29
|
+
if (targets.length === 0) {
|
30
|
+
throw new Error(`RumiousRenderError: No target assigned to ViewControl`);
|
31
|
+
}
|
32
|
+
for (let i = 0; i < targets.length; i++) {
|
33
|
+
let templ = renderFrag(template, targets[i].context);
|
34
|
+
if (!prepend)
|
35
|
+
targets[i].element.appendChild(templ);
|
36
|
+
else
|
37
|
+
targets[i].element.prepend(templ);
|
38
|
+
}
|
39
|
+
}
|
40
|
+
each(callback) {
|
41
|
+
for (let target of this.targets) {
|
42
|
+
callback(target);
|
43
|
+
}
|
44
|
+
}
|
45
|
+
emptyAll() {
|
46
|
+
const targets = this.targets;
|
47
|
+
for (let i = 0; i < targets.length; i++) {
|
48
|
+
targets[i].element.textContent = '';
|
49
|
+
}
|
50
|
+
}
|
51
|
+
empty(target) {
|
52
|
+
const targets = this.targets;
|
53
|
+
for (let i = 0; i < targets.length; i++) {
|
54
|
+
if (targets[i].element === target) {
|
55
|
+
target.textContent = '';
|
56
|
+
return;
|
57
|
+
}
|
58
|
+
}
|
59
|
+
}
|
60
|
+
updateChild(index, template) {
|
61
|
+
for (let i = 0; i < this.targets.length; i++) {
|
62
|
+
const { element, context } = this.targets[i];
|
63
|
+
const parent = element.parentElement;
|
64
|
+
if (!parent)
|
65
|
+
continue;
|
66
|
+
const oldChild = parent.children[index];
|
67
|
+
const newNode = renderFrag(template, context);
|
68
|
+
if (oldChild) {
|
69
|
+
parent.replaceChild(newNode, oldChild);
|
70
|
+
}
|
71
|
+
}
|
72
|
+
}
|
73
|
+
}
|
74
|
+
export function createViewControl() {
|
75
|
+
return new RumiousViewControl();
|
76
|
+
}
|
package/dist/state/index.d.ts
CHANGED
@@ -0,0 +1,79 @@
|
|
1
|
+
import { RumiousState } from './state.js';
|
2
|
+
export class RumiousListState extends RumiousState {
|
3
|
+
constructor(value = [], reactor) {
|
4
|
+
super(value, reactor);
|
5
|
+
}
|
6
|
+
append(value) {
|
7
|
+
this.value.push(value);
|
8
|
+
this.reactor?.notify({
|
9
|
+
type: 'append',
|
10
|
+
state: this,
|
11
|
+
key: this.value.length - 1,
|
12
|
+
value
|
13
|
+
});
|
14
|
+
}
|
15
|
+
prepend(value) {
|
16
|
+
this.value.unshift(value);
|
17
|
+
this.reactor?.notify({
|
18
|
+
type: 'prepend',
|
19
|
+
state: this,
|
20
|
+
key: 0,
|
21
|
+
value
|
22
|
+
});
|
23
|
+
}
|
24
|
+
insert(pos, value) {
|
25
|
+
this.value.splice(pos, 0, value);
|
26
|
+
this.reactor?.notify({
|
27
|
+
type: 'insert',
|
28
|
+
state: this,
|
29
|
+
value,
|
30
|
+
key: pos
|
31
|
+
});
|
32
|
+
}
|
33
|
+
updateAt(pos, value) {
|
34
|
+
this.value[pos] = value;
|
35
|
+
this.reactor?.notify({
|
36
|
+
type: 'update',
|
37
|
+
state: this,
|
38
|
+
value,
|
39
|
+
key: pos
|
40
|
+
});
|
41
|
+
}
|
42
|
+
remove(pos) {
|
43
|
+
let currentValue = this.value[pos];
|
44
|
+
this.value.splice(pos, 1);
|
45
|
+
this.reactor?.notify({
|
46
|
+
type: 'remove',
|
47
|
+
state: this,
|
48
|
+
value: currentValue,
|
49
|
+
key: pos
|
50
|
+
});
|
51
|
+
}
|
52
|
+
clear() {
|
53
|
+
this.value.length = 0;
|
54
|
+
this.reactor?.notify({
|
55
|
+
type: 'set',
|
56
|
+
state: this,
|
57
|
+
value: []
|
58
|
+
});
|
59
|
+
}
|
60
|
+
reverse() {
|
61
|
+
this.value.reverse();
|
62
|
+
this.reactor?.notify({
|
63
|
+
type: 'set',
|
64
|
+
state: this,
|
65
|
+
value: this.value
|
66
|
+
});
|
67
|
+
}
|
68
|
+
filter(predicate) {
|
69
|
+
this.value = this.value.filter(predicate);
|
70
|
+
this.reactor?.notify({
|
71
|
+
type: 'set',
|
72
|
+
state: this,
|
73
|
+
value: this.value
|
74
|
+
});
|
75
|
+
}
|
76
|
+
}
|
77
|
+
export function createListState(values) {
|
78
|
+
return new RumiousListState(values);
|
79
|
+
}
|
@@ -0,0 +1,53 @@
|
|
1
|
+
export class RumiousReactor {
|
2
|
+
target;
|
3
|
+
bindings = [];
|
4
|
+
internal = [];
|
5
|
+
isUIBatch = true;
|
6
|
+
scheduled = false;
|
7
|
+
queuedCommits = [];
|
8
|
+
constructor(target) {
|
9
|
+
this.target = target;
|
10
|
+
}
|
11
|
+
addBinding(binding) {
|
12
|
+
this.bindings.push(binding);
|
13
|
+
}
|
14
|
+
removeBinding(binding) {
|
15
|
+
this.bindings = this.bindings.filter(b => b !== binding);
|
16
|
+
}
|
17
|
+
addInternalBinding(binding) {
|
18
|
+
this.internal.push(binding);
|
19
|
+
}
|
20
|
+
removeInternalBinding(binding) {
|
21
|
+
this.internal = this.internal.filter(b => b !== binding);
|
22
|
+
}
|
23
|
+
notify(commit) {
|
24
|
+
for (const binding of this.bindings) {
|
25
|
+
binding(commit);
|
26
|
+
}
|
27
|
+
if (this.isUIBatch) {
|
28
|
+
this.scheduleInternalUpdate(commit);
|
29
|
+
}
|
30
|
+
else {
|
31
|
+
for (const binding of this.internal) {
|
32
|
+
binding(commit);
|
33
|
+
}
|
34
|
+
}
|
35
|
+
}
|
36
|
+
scheduleInternalUpdate(commit) {
|
37
|
+
this.queuedCommits.push(commit);
|
38
|
+
if (!this.scheduled) {
|
39
|
+
this.scheduled = true;
|
40
|
+
queueMicrotask(() => {
|
41
|
+
this.flushInternal();
|
42
|
+
});
|
43
|
+
}
|
44
|
+
}
|
45
|
+
flushInternal() {
|
46
|
+
const lastCommit = this.queuedCommits[this.queuedCommits.length - 1];
|
47
|
+
for (const binding of this.internal) {
|
48
|
+
binding(lastCommit); // chỉ gửi commit cuối cùng
|
49
|
+
}
|
50
|
+
this.queuedCommits = [];
|
51
|
+
this.scheduled = false;
|
52
|
+
}
|
53
|
+
}
|
package/dist/state/state.d.ts
CHANGED
@@ -0,0 +1,53 @@
|
|
1
|
+
import { RumiousReactor } from './reactor.js';
|
2
|
+
export class RumiousState {
|
3
|
+
value;
|
4
|
+
reactor;
|
5
|
+
constructor(value, reactor) {
|
6
|
+
this.value = value;
|
7
|
+
this.reactor = reactor;
|
8
|
+
if (!this.reactor) {
|
9
|
+
this.reactor = new RumiousReactor(this);
|
10
|
+
}
|
11
|
+
}
|
12
|
+
set(value) {
|
13
|
+
this.value = value;
|
14
|
+
this.reactor?.notify({
|
15
|
+
type: 'set',
|
16
|
+
value: value,
|
17
|
+
state: this
|
18
|
+
});
|
19
|
+
}
|
20
|
+
get() {
|
21
|
+
return this.value;
|
22
|
+
}
|
23
|
+
slientUpdate(value) {
|
24
|
+
this.value = value;
|
25
|
+
}
|
26
|
+
update(updater) {
|
27
|
+
this.set(updater(this.value));
|
28
|
+
}
|
29
|
+
toJSON() {
|
30
|
+
return JSON.stringify(this.value);
|
31
|
+
}
|
32
|
+
equal(value) {
|
33
|
+
return value === this.value;
|
34
|
+
}
|
35
|
+
trigger() {
|
36
|
+
this.reactor?.notify({
|
37
|
+
type: 'set',
|
38
|
+
value: this.value,
|
39
|
+
state: this
|
40
|
+
});
|
41
|
+
}
|
42
|
+
}
|
43
|
+
export function createState(value) {
|
44
|
+
return new RumiousState(value);
|
45
|
+
}
|
46
|
+
export function watch(state, callback) {
|
47
|
+
if (state.reactor)
|
48
|
+
state.reactor.addBinding(callback);
|
49
|
+
}
|
50
|
+
export function unwatch(state, callback) {
|
51
|
+
if (state.reactor)
|
52
|
+
state.reactor.removeBinding(callback);
|
53
|
+
}
|
@@ -0,0 +1,14 @@
|
|
1
|
+
import { RumiousState } from './state.js';
|
2
|
+
export type RumiousStoreReactiveMap<T> = {
|
3
|
+
[K in keyof T]: RumiousState<T[K]>;
|
4
|
+
};
|
5
|
+
export declare class RumiousStore<T extends {}> {
|
6
|
+
value: T;
|
7
|
+
states: RumiousStoreReactiveMap<T>;
|
8
|
+
constructor(value: T);
|
9
|
+
get<K extends keyof T>(key: K): RumiousState<T[K]>;
|
10
|
+
set(value: T): void;
|
11
|
+
map<U>(fn: <K extends keyof T>(state: RumiousState<T[K]>, key: K) => U): U[];
|
12
|
+
remove<K extends keyof T>(key: K): void;
|
13
|
+
}
|
14
|
+
export declare function createStore<T extends {}>(value: T): RumiousStore<T>;
|
@@ -0,0 +1,17 @@
|
|
1
|
+
import { RumiousState, createState } from './state.js';
|
2
|
+
T;
|
3
|
+
RumiousState;
|
4
|
+
;
|
5
|
+
export class RumiousStore {
|
6
|
+
value;
|
7
|
+
map = {};
|
8
|
+
constructor(value) {
|
9
|
+
this.value = value;
|
10
|
+
for (const key in value) {
|
11
|
+
this.map[key] = createState(value[key]);
|
12
|
+
}
|
13
|
+
}
|
14
|
+
}
|
15
|
+
export function createStore(value) {
|
16
|
+
return new RumiousStore(value);
|
17
|
+
}
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|