rask-ui 0.2.1 → 0.2.2

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.
@@ -5,11 +5,10 @@ export declare class Signal {
5
5
  notify(): void;
6
6
  }
7
7
  export declare class Observer {
8
- private _isDisposed;
8
+ isDisposed: boolean;
9
9
  private signalDisposers;
10
10
  private clearSignals;
11
11
  private onNotify;
12
- private isQueued;
13
12
  constructor(onNotify: () => void);
14
13
  subscribeSignal(signal: Signal): void;
15
14
  observe(): () => void;
@@ -1 +1 @@
1
- {"version":3,"file":"observation.d.ts","sourceRoot":"","sources":["../src/observation.ts"],"names":[],"mappings":"AAEA,wBAAgB,kBAAkB,aAEjC;AAED,qBAAa,MAAM;IACjB,OAAO,CAAC,WAAW,CAAyB;IAC5C,SAAS,CAAC,EAAE,EAAE,MAAM,IAAI;IAOxB,MAAM;CAGP;AAED,qBAAa,QAAQ;IACnB,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,eAAe,CAAyB;IAChD,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,QAAQ,CAAa;IAC7B,OAAO,CAAC,QAAQ,CAAS;gBACb,QAAQ,EAAE,MAAM,IAAI;IAmBhC,eAAe,CAAC,MAAM,EAAE,MAAM;IAG9B,OAAO;IAOP,OAAO;CAIR"}
1
+ {"version":3,"file":"observation.d.ts","sourceRoot":"","sources":["../src/observation.ts"],"names":[],"mappings":"AAEA,wBAAgB,kBAAkB,aAEjC;AAED,qBAAa,MAAM;IACjB,OAAO,CAAC,WAAW,CAAyB;IAC5C,SAAS,CAAC,EAAE,EAAE,MAAM,IAAI;IAOxB,MAAM;CAGP;AAED,qBAAa,QAAQ;IACnB,UAAU,UAAS;IACnB,OAAO,CAAC,eAAe,CAAyB;IAChD,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,QAAQ,CAAa;gBACjB,QAAQ,EAAE,MAAM,IAAI;IAGhC,eAAe,CAAC,MAAM,EAAE,MAAM;IAG9B,OAAO;IAOP,OAAO;CAIR"}
@@ -15,28 +15,15 @@ export class Signal {
15
15
  }
16
16
  }
17
17
  export class Observer {
18
- _isDisposed = false;
18
+ isDisposed = false;
19
19
  signalDisposers = new Set();
20
20
  clearSignals() {
21
21
  this.signalDisposers.forEach((dispose) => dispose());
22
22
  this.signalDisposers.clear();
23
23
  }
24
24
  onNotify;
25
- isQueued = false;
26
25
  constructor(onNotify) {
27
- this.onNotify = () => {
28
- if (this.isQueued) {
29
- return;
30
- }
31
- queueMicrotask(() => {
32
- this.isQueued = false;
33
- if (this._isDisposed) {
34
- return;
35
- }
36
- onNotify();
37
- });
38
- this.isQueued = true;
39
- };
26
+ this.onNotify = onNotify;
40
27
  }
41
28
  subscribeSignal(signal) {
42
29
  this.signalDisposers.add(signal.subscribe(this.onNotify));
@@ -50,6 +37,6 @@ export class Observer {
50
37
  }
51
38
  dispose() {
52
39
  this.clearSignals();
53
- this._isDisposed = true;
40
+ this.isDisposed = true;
54
41
  }
55
42
  }
@@ -39,26 +39,6 @@ describe("Signal", () => {
39
39
  });
40
40
  });
41
41
  describe("Observer", () => {
42
- it("should queue notifications in microtasks", async () => {
43
- let callCount = 0;
44
- const observer = new Observer(() => {
45
- callCount++;
46
- });
47
- const signal = new Signal();
48
- const dispose = observer.observe();
49
- observer.subscribeSignal(signal);
50
- dispose();
51
- // Trigger multiple notifications
52
- signal.notify();
53
- signal.notify();
54
- signal.notify();
55
- // Should not be called synchronously
56
- expect(callCount).toBe(0);
57
- // Wait for microtask
58
- await new Promise((resolve) => queueMicrotask(() => resolve()));
59
- // Should be called only once due to queuing
60
- expect(callCount).toBe(1);
61
- });
62
42
  it("should track signals during observation", () => {
63
43
  const callback = vi.fn();
64
44
  const observer = new Observer(callback);
@@ -130,21 +110,4 @@ describe("Observer", () => {
130
110
  dispose1();
131
111
  expect(getCurrentObserver()).toBeUndefined();
132
112
  });
133
- it("should prevent duplicate notifications while queued", async () => {
134
- let callCount = 0;
135
- const observer = new Observer(() => {
136
- callCount++;
137
- });
138
- const signal = new Signal();
139
- const dispose = observer.observe();
140
- observer.subscribeSignal(signal);
141
- dispose();
142
- // Rapid-fire notifications
143
- for (let i = 0; i < 100; i++) {
144
- signal.notify();
145
- }
146
- await new Promise((resolve) => queueMicrotask(() => resolve()));
147
- // Should only be called once
148
- expect(callCount).toBe(1);
149
- });
150
113
  });
@@ -1 +1 @@
1
- {"version":3,"file":"ComponentVNode.d.ts","sourceRoot":"","sources":["../../src/vdom/ComponentVNode.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,QAAQ,EAAU,MAAM,gBAAgB,CAAC;AACtE,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAGhD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAGvC,MAAM,MAAM,cAAc,GACtB,KAAK,GACL,MAAM,GACN,IAAI,GACJ,MAAM,GACN,SAAS,GACT,OAAO,CAAC;AACZ,MAAM,MAAM,iBAAiB,GAAG,cAAc,GAAG,cAAc,EAAE,CAAC;AAElE;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,MAAM,SAAS,CAAC,CAAC,SAAS,KAAK,IACjC,CAAC,CAAC,KAAK,EAAE,CAAC,KAAK,MAAM,iBAAiB,CAAC,GACvC,CAAC,MAAM,MAAM,iBAAiB,CAAC,CAAC;AAEpC,MAAM,MAAM,iBAAiB,GAAG;IAC9B,MAAM,CAAC,EAAE,KAAK,CAAC;IACf,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACtC,QAAQ,EAAE,KAAK,CAAC,MAAM,IAAI,CAAC,CAAC;IAC5B,UAAU,EAAE,KAAK,CAAC,MAAM,IAAI,CAAC,CAAC;IAC9B,QAAQ,EAAE,QAAQ,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,OAAO,CAAC;IACf,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;CACnC,CAAC;AAKF,wBAAgB,mBAAmB,sBAYlC;AAED,wBAAgB,OAAO,CAAC,EAAE,EAAE,MAAM,IAAI,QAYrC;AAED,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,IAAI,QAYvC;AAED,qBAAa,cAAe,SAAQ,aAAa;IAC/C,SAAS,EAAE,SAAS,CAAC,GAAG,CAAC,CAAC;IAC1B,KAAK,EAAE,KAAK,CAAC;IAEb,QAAQ,EAAE,KAAK,EAAE,CAAM;IACvB,QAAQ,CAAC,EAAE,iBAAiB,CAAC;gBAE3B,SAAS,EAAE,SAAS,CAAC,GAAG,CAAC,EACzB,KAAK,EAAE,KAAK,EACZ,QAAQ,EAAE,KAAK,EAAE,EACjB,GAAG,CAAC,EAAE,MAAM;IAWd,QAAQ,IAAI,IAAI;IAGhB,KAAK,CAAC,MAAM,CAAC,EAAE,KAAK,GAAG,IAAI,EAAE;IAyH7B,KAAK,CAAC,OAAO,EAAE,cAAc;IAW7B,OAAO;CAcR"}
1
+ {"version":3,"file":"ComponentVNode.d.ts","sourceRoot":"","sources":["../../src/vdom/ComponentVNode.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,QAAQ,EAAU,MAAM,gBAAgB,CAAC;AACtE,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAGhD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAGvC,MAAM,MAAM,cAAc,GACtB,KAAK,GACL,MAAM,GACN,IAAI,GACJ,MAAM,GACN,SAAS,GACT,OAAO,CAAC;AACZ,MAAM,MAAM,iBAAiB,GAAG,cAAc,GAAG,cAAc,EAAE,CAAC;AAElE;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,MAAM,SAAS,CAAC,CAAC,SAAS,KAAK,IACjC,CAAC,CAAC,KAAK,EAAE,CAAC,KAAK,MAAM,iBAAiB,CAAC,GACvC,CAAC,MAAM,MAAM,iBAAiB,CAAC,CAAC;AAEpC,MAAM,MAAM,iBAAiB,GAAG;IAC9B,MAAM,CAAC,EAAE,KAAK,CAAC;IACf,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACtC,QAAQ,EAAE,KAAK,CAAC,MAAM,IAAI,CAAC,CAAC;IAC5B,UAAU,EAAE,KAAK,CAAC,MAAM,IAAI,CAAC,CAAC;IAC9B,QAAQ,EAAE,QAAQ,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,OAAO,CAAC;IACf,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;CACnC,CAAC;AAKF,wBAAgB,mBAAmB,sBAYlC;AAED,wBAAgB,OAAO,CAAC,EAAE,EAAE,MAAM,IAAI,QAYrC;AAED,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,IAAI,QAYvC;AAED,qBAAa,cAAe,SAAQ,aAAa;IAC/C,SAAS,EAAE,SAAS,CAAC,GAAG,CAAC,CAAC;IAC1B,KAAK,EAAE,KAAK,CAAC;IAEb,QAAQ,EAAE,KAAK,EAAE,CAAM;IACvB,QAAQ,CAAC,EAAE,iBAAiB,CAAC;gBAE3B,SAAS,EAAE,SAAS,CAAC,GAAG,CAAC,EACzB,KAAK,EAAE,KAAK,EACZ,QAAQ,EAAE,KAAK,EAAE,EACjB,GAAG,CAAC,EAAE,MAAM;IAWd,QAAQ,IAAI,IAAI;IAGhB,KAAK,CAAC,MAAM,CAAC,EAAE,KAAK,GAAG,IAAI,EAAE;IAyI7B,KAAK,CAAC,OAAO,EAAE,cAAc;IAW7B,OAAO;CAcR"}
@@ -78,28 +78,38 @@ export class ComponentVNode extends AbstractVNode {
78
78
  }
79
79
  return normalizeChildren(renderResult);
80
80
  };
81
+ let isObserverQueued = false;
81
82
  const instance = (this.instance = {
82
83
  parent,
83
84
  contexts: null,
84
85
  onCleanups: [],
85
86
  onMounts: [],
86
87
  observer: new Observer(() => {
87
- this.root?.setAsCurrent();
88
- const newChildren = executeRender();
89
- const prevChildren = this.children;
90
- this.children = this.patchChildren(newChildren);
91
- // Typically components return a single element, which does
92
- // not require the parent to apply elements to the DOM again
93
- const canSelfUpdate = prevChildren.length === 1 &&
94
- this.children.length === 1 &&
95
- prevChildren[0] instanceof ElementVNode &&
96
- this.children[0] instanceof ElementVNode &&
97
- this.canPatch(prevChildren[0], this.children[0]);
98
- if (!canSelfUpdate) {
99
- this.parent?.rerender();
88
+ if (isObserverQueued) {
89
+ return;
100
90
  }
101
- this.root?.flushLifecycle();
102
- this.root?.clearCurrent();
91
+ isObserverQueued = true;
92
+ this.root?.queueObserver(() => {
93
+ if (instance.observer.isDisposed) {
94
+ return;
95
+ }
96
+ isObserverQueued = false;
97
+ this.root?.setAsCurrent();
98
+ const newChildren = executeRender();
99
+ const prevChildren = this.children;
100
+ this.children = this.patchChildren(newChildren);
101
+ // Typically components return a single element, which does
102
+ // not require the parent to apply elements to the DOM again
103
+ const canSelfUpdate = prevChildren.length === 1 &&
104
+ this.children.length === 1 &&
105
+ prevChildren[0] instanceof ElementVNode &&
106
+ this.children[0] instanceof ElementVNode &&
107
+ this.canPatch(prevChildren[0], this.children[0]);
108
+ if (!canSelfUpdate) {
109
+ this.parent?.rerender();
110
+ }
111
+ this.root?.clearCurrent();
112
+ });
103
113
  }),
104
114
  reactiveProps: createReactiveProps(this.props),
105
115
  get error() {
@@ -20,5 +20,11 @@ export declare class ElementVNode extends AbstractVNode {
20
20
  private setProp;
21
21
  private patchProps;
22
22
  private addEventListener;
23
+ /**
24
+ * Intelligently sync DOM to match children VNode order.
25
+ * Only performs DOM operations when elements are out of position.
26
+ * This is used by both patch() and rerender() to efficiently update children.
27
+ */
28
+ private syncDOMChildren;
23
29
  }
24
30
  //# sourceMappingURL=ElementVNode.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ElementVNode.d.ts","sourceRoot":"","sources":["../../src/vdom/ElementVNode.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAGhD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAUvC,qBAAa,YAAa,SAAQ,aAAa;IAC7C,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,KAAK,CAAC;IACb,QAAQ,EAAE,KAAK,EAAE,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,GAAG,CAAC,CAA0D;IACtE,OAAO,CAAC,cAAc,CAAC,CAA6B;gBAElD,GAAG,EAAE,MAAM,EACX,EAAE,GAAG,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,EACxB,QAAQ,EAAE,KAAK,EAAE,EACjB,GAAG,CAAC,EAAE,MAAM;IASd,QAAQ,IAAI,IAAI;IAOhB,KAAK,CAAC,MAAM,CAAC,EAAE,KAAK,GAAG,IAAI;IAkC3B;;;;OAIG;IACH,KAAK,CAAC,OAAO,EAAE,YAAY;IAW3B,OAAO;IAYP,OAAO,CAAC,OAAO,CAoCb;IACF,OAAO,CAAC,UAAU;IAGlB,OAAO,CAAC,gBAAgB;CAgBzB"}
1
+ {"version":3,"file":"ElementVNode.d.ts","sourceRoot":"","sources":["../../src/vdom/ElementVNode.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAGhD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAUvC,qBAAa,YAAa,SAAQ,aAAa;IAC7C,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,KAAK,CAAC;IACb,QAAQ,EAAE,KAAK,EAAE,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,GAAG,CAAC,CAA0D;IACtE,OAAO,CAAC,cAAc,CAAC,CAA6B;gBAElD,GAAG,EAAE,MAAM,EACX,EAAE,GAAG,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,EACxB,QAAQ,EAAE,KAAK,EAAE,EACjB,GAAG,CAAC,EAAE,MAAM;IASd,QAAQ,IAAI,IAAI;IAGhB,KAAK,CAAC,MAAM,CAAC,EAAE,KAAK,GAAG,IAAI;IAkC3B;;;;OAIG;IACH,KAAK,CAAC,OAAO,EAAE,YAAY;IAM3B,OAAO;IAYP,OAAO,CAAC,OAAO,CAoCb;IACF,OAAO,CAAC,UAAU;IAGlB,OAAO,CAAC,gBAAgB;IAgBxB;;;;OAIG;IACH,OAAO,CAAC,eAAe;CAyBxB"}
@@ -18,10 +18,7 @@ export class ElementVNode extends AbstractVNode {
18
18
  this.ref = ref;
19
19
  }
20
20
  rerender() {
21
- const childrenElms = this.children
22
- .map((child) => child.getElements())
23
- .flat();
24
- this.elm.replaceChildren(...childrenElms);
21
+ this.syncDOMChildren();
25
22
  }
26
23
  mount(parent) {
27
24
  this.parent = parent;
@@ -61,10 +58,7 @@ export class ElementVNode extends AbstractVNode {
61
58
  this.patchProps(newNode.props);
62
59
  this.props = newNode.props;
63
60
  this.children = this.patchChildren(newNode.children);
64
- const childrenElms = this.children
65
- .map((child) => child.getElements())
66
- .flat();
67
- this.elm.replaceChildren(...childrenElms);
61
+ this.syncDOMChildren();
68
62
  }
69
63
  unmount() {
70
64
  this.children.forEach((child) => child.unmount());
@@ -123,4 +117,32 @@ export class ElementVNode extends AbstractVNode {
123
117
  delete this.eventListeners[type];
124
118
  }
125
119
  }
120
+ /**
121
+ * Intelligently sync DOM to match children VNode order.
122
+ * Only performs DOM operations when elements are out of position.
123
+ * This is used by both patch() and rerender() to efficiently update children.
124
+ */
125
+ syncDOMChildren() {
126
+ const elm = this.elm;
127
+ let currentDomChild = elm.firstChild;
128
+ for (const child of this.children) {
129
+ const childNodes = child.getElements();
130
+ for (const node of childNodes) {
131
+ if (currentDomChild === node) {
132
+ // Already in correct position, advance pointer
133
+ currentDomChild = currentDomChild.nextSibling;
134
+ }
135
+ else {
136
+ // Insert (or move if it exists elsewhere in DOM)
137
+ elm.insertBefore(node, currentDomChild);
138
+ }
139
+ }
140
+ }
141
+ // Remove any leftover nodes (shouldn't happen if unmount works correctly)
142
+ while (currentDomChild) {
143
+ const next = currentDomChild.nextSibling;
144
+ elm.removeChild(currentDomChild);
145
+ currentDomChild = next;
146
+ }
147
+ }
126
148
  }
@@ -6,9 +6,12 @@ export declare class RootVNode extends AbstractVNode {
6
6
  children: VNode[];
7
7
  componentStack: ComponentInstance[];
8
8
  private lifecycleQueue;
9
+ private hasPendingMicroTask;
10
+ private pendingObservers;
9
11
  constructor(rootNode: VNode, container: HTMLElement);
10
12
  queueMount(cb: () => void): void;
11
13
  queueUnmount(cb: () => void): void;
14
+ queueObserver(cb: () => void): void;
12
15
  flushLifecycle(): void;
13
16
  pushComponent(instance: ComponentInstance): void;
14
17
  popComponent(): void;
@@ -1 +1 @@
1
- {"version":3,"file":"RootVNode.d.ts","sourceRoot":"","sources":["../../src/vdom/RootVNode.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAIrD,eAAO,IAAI,WAAW,EAAE,SAAS,GAAG,SAAS,CAAC;AAE9C,qBAAa,SAAU,SAAQ,aAAa;IAC1C,QAAQ,EAAE,KAAK,EAAE,CAAC;IAClB,cAAc,EAAE,iBAAiB,EAAE,CAAM;IACzC,OAAO,CAAC,cAAc,CAGpB;gBAEU,QAAQ,EAAE,KAAK,EAAE,SAAS,EAAE,WAAW;IAMnD,UAAU,CAAC,EAAE,EAAE,MAAM,IAAI;IAIzB,YAAY,CAAC,EAAE,EAAE,MAAM,IAAI;IAI3B,cAAc;IAOd,aAAa,CAAC,QAAQ,EAAE,iBAAiB;IAIzC,YAAY;IAIZ,YAAY;IAIZ,YAAY;IAMZ,KAAK,IAAI,IAAI,GAAG,IAAI,EAAE;IAGtB,KAAK,IAAI,IAAI;IACb,QAAQ,IAAI,IAAI;IAShB,OAAO,IAAI,IAAI;CAChB"}
1
+ {"version":3,"file":"RootVNode.d.ts","sourceRoot":"","sources":["../../src/vdom/RootVNode.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAIrD,eAAO,IAAI,WAAW,EAAE,SAAS,GAAG,SAAS,CAAC;AAE9C,qBAAa,SAAU,SAAQ,aAAa;IAC1C,QAAQ,EAAE,KAAK,EAAE,CAAC;IAClB,cAAc,EAAE,iBAAiB,EAAE,CAAM;IACzC,OAAO,CAAC,cAAc,CAGpB;IACF,OAAO,CAAC,mBAAmB,CAAkB;IAC7C,OAAO,CAAC,gBAAgB,CAAyB;gBAErC,QAAQ,EAAE,KAAK,EAAE,SAAS,EAAE,WAAW;IAMnD,UAAU,CAAC,EAAE,EAAE,MAAM,IAAI;IAIzB,YAAY,CAAC,EAAE,EAAE,MAAM,IAAI;IAG3B,aAAa,CAAC,EAAE,EAAE,MAAM,IAAI;IAa5B,cAAc;IAOd,aAAa,CAAC,QAAQ,EAAE,iBAAiB;IAIzC,YAAY;IAIZ,YAAY;IAIZ,YAAY;IAMZ,KAAK,IAAI,IAAI,GAAG,IAAI,EAAE;IAGtB,KAAK,IAAI,IAAI;IACb,QAAQ,IAAI,IAAI;IAShB,OAAO,IAAI,IAAI;CAChB"}
@@ -9,6 +9,8 @@ export class RootVNode extends AbstractVNode {
9
9
  toMount: [],
10
10
  toUnmount: [],
11
11
  };
12
+ hasPendingMicroTask = false;
13
+ pendingObservers = [];
12
14
  constructor(rootNode, container) {
13
15
  super();
14
16
  this.elm = container;
@@ -20,6 +22,19 @@ export class RootVNode extends AbstractVNode {
20
22
  queueUnmount(cb) {
21
23
  this.lifecycleQueue.toUnmount.push(cb);
22
24
  }
25
+ queueObserver(cb) {
26
+ this.pendingObservers.push(cb);
27
+ if (!this.hasPendingMicroTask) {
28
+ this.hasPendingMicroTask = true;
29
+ queueMicrotask(() => {
30
+ this.hasPendingMicroTask = false;
31
+ const pendingObservers = this.pendingObservers;
32
+ this.pendingObservers = [];
33
+ pendingObservers.forEach((pendingObserver) => pendingObserver());
34
+ this.flushLifecycle();
35
+ });
36
+ }
37
+ }
23
38
  flushLifecycle() {
24
39
  this.lifecycleQueue.toUnmount.forEach((cb) => cb());
25
40
  this.lifecycleQueue.toUnmount = [];
@@ -1 +1 @@
1
- {"version":3,"file":"TextVNode.d.ts","sourceRoot":"","sources":["../../src/vdom/TextVNode.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEhD,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAEhC,qBAAa,SAAU,SAAQ,aAAa;IAC1C,IAAI,EAAE,MAAM,CAAC;gBACD,IAAI,EAAE,MAAM;IAIxB,KAAK,CAAC,MAAM,CAAC,EAAE,KAAK,GAAG,IAAI;IAe3B,KAAK,CAAC,OAAO,EAAE,SAAS;IAIxB,QAAQ,IAAI,IAAI;IAChB,OAAO;CAMR"}
1
+ {"version":3,"file":"TextVNode.d.ts","sourceRoot":"","sources":["../../src/vdom/TextVNode.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEhD,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAEhC,qBAAa,SAAU,SAAQ,aAAa;IAC1C,IAAI,EAAE,MAAM,CAAC;gBACD,IAAI,EAAE,MAAM;IAIxB,KAAK,CAAC,MAAM,CAAC,EAAE,KAAK,GAAG,IAAI;IAe3B,KAAK,CAAC,OAAO,EAAE,SAAS;IAQxB,QAAQ,IAAI,IAAI;IAChB,OAAO;CAMR"}
@@ -19,6 +19,9 @@ export class TextVNode extends AbstractVNode {
19
19
  return textNode;
20
20
  }
21
21
  patch(newNode) {
22
+ if (newNode.text === this.text) {
23
+ return;
24
+ }
22
25
  this.text = newNode.text;
23
26
  this.elm.textContent = this.text;
24
27
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rask-ui",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",