rask-ui 0.2.0 → 0.2.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.
@@ -46,6 +46,7 @@ describe("Class Property Support", () => {
46
46
  render(jsx("div", { class: {} }), container);
47
47
  const div = container.querySelector("div");
48
48
  expect(div?.className).toBe("");
49
+ expect(div?.hasAttribute("class")).toBe(false);
49
50
  });
50
51
  it("should handle all false object notation", () => {
51
52
  const container = document.createElement("div");
@@ -57,6 +58,7 @@ describe("Class Property Support", () => {
57
58
  }), container);
58
59
  const div = container.querySelector("div");
59
60
  expect(div?.className).toBe("");
61
+ expect(div?.hasAttribute("class")).toBe(false);
60
62
  });
61
63
  it("should update classes when object notation changes", async () => {
62
64
  const container = document.createElement("div");
@@ -124,6 +126,46 @@ describe("Class Property Support", () => {
124
126
  render(jsx("div", { class: undefined }), container);
125
127
  const div = container.querySelector("div");
126
128
  expect(div?.className).toBe("");
129
+ expect(div?.hasAttribute("class")).toBe(false);
130
+ });
131
+ it("should remove class attribute when empty string is provided", () => {
132
+ const container = document.createElement("div");
133
+ render(jsx("div", { class: "" }), container);
134
+ const div = container.querySelector("div");
135
+ expect(div?.hasAttribute("class")).toBe(false);
136
+ });
137
+ it("should remove class attribute when null is provided", () => {
138
+ const container = document.createElement("div");
139
+ render(jsx("div", { class: null }), container);
140
+ const div = container.querySelector("div");
141
+ expect(div?.hasAttribute("class")).toBe(false);
142
+ });
143
+ it("should remove class attribute when object notation results in empty string", () => {
144
+ const container = document.createElement("div");
145
+ render(jsx("div", {
146
+ class: {
147
+ active: false,
148
+ visible: false,
149
+ },
150
+ }), container);
151
+ const div = container.querySelector("div");
152
+ expect(div?.hasAttribute("class")).toBe(false);
153
+ });
154
+ it("should remove class attribute when updating from non-empty to empty string", async () => {
155
+ const container = document.createElement("div");
156
+ let stateFn;
157
+ const App = () => {
158
+ const state = createState({ className: "initial" });
159
+ stateFn = state;
160
+ return () => jsx("div", { class: state.className });
161
+ };
162
+ render(jsx(App, {}), container);
163
+ const div = container.querySelector("div");
164
+ expect(div?.className).toBe("initial");
165
+ expect(div?.hasAttribute("class")).toBe(true);
166
+ stateFn.className = "";
167
+ await new Promise((resolve) => setTimeout(resolve, 0));
168
+ expect(div?.hasAttribute("class")).toBe(false);
127
169
  });
128
170
  it("should handle dynamic string class updates", async () => {
129
171
  const container = document.createElement("div");
@@ -1,5 +1,6 @@
1
1
  import { describe, it, expect } from "vitest";
2
2
  import { jsx, render } from "../vdom";
3
+ import { createState } from "../createState";
3
4
  describe("Component Props", () => {
4
5
  it("should pass string props to component", () => {
5
6
  const container = document.createElement("div");
@@ -75,14 +76,259 @@ describe("Component Props", () => {
75
76
  render(jsx(MyComponent, { message: "Custom message" }), container2);
76
77
  expect(container2.children[0].textContent).toBe("Custom message");
77
78
  });
78
- it("should update when props change", () => {
79
+ it("should pass props from parent to child component", () => {
79
80
  const container = document.createElement("div");
80
- const MyComponent = (props) => {
81
+ const ChildComponent = (props) => {
81
82
  return () => jsx("div", { children: props.message });
82
83
  };
83
- render(jsx(MyComponent, { message: "Initial" }), container);
84
- expect(container.children[0].textContent).toBe("Initial");
85
- // Simulate props update
86
- // The implementation will handle prop updates through patch
84
+ const ParentComponent = (props) => {
85
+ return () => jsx("div", {
86
+ children: jsx(ChildComponent, { message: props.text }),
87
+ });
88
+ };
89
+ render(jsx(ParentComponent, { text: "Hello from parent" }), container);
90
+ expect(container.children[0].children[0].textContent).toBe("Hello from parent");
91
+ });
92
+ it("should pass props through multiple levels of nesting", () => {
93
+ const container = document.createElement("div");
94
+ const GrandchildComponent = (props) => {
95
+ return () => jsx("span", { children: props.value });
96
+ };
97
+ const ChildComponent = (props) => {
98
+ return () => jsx("div", {
99
+ children: jsx(GrandchildComponent, { value: props.data }),
100
+ });
101
+ };
102
+ const ParentComponent = (props) => {
103
+ return () => jsx("div", {
104
+ children: jsx(ChildComponent, { data: props.info }),
105
+ });
106
+ };
107
+ render(jsx(ParentComponent, { info: "Deep nested value" }), container);
108
+ const span = container.querySelector("span");
109
+ expect(span?.textContent).toBe("Deep nested value");
110
+ });
111
+ it("should update nested component when parent props change", async () => {
112
+ const container = document.createElement("div");
113
+ const ChildComponent = (props) => {
114
+ return () => jsx("div", { children: String(props.count) });
115
+ };
116
+ const ParentComponent = () => {
117
+ const state = createState({ value: 5 });
118
+ return () => jsx("div", {
119
+ children: [
120
+ jsx(ChildComponent, { count: state.value }),
121
+ jsx("button", {
122
+ onclick: () => (state.value = 10),
123
+ children: "Update",
124
+ }),
125
+ ],
126
+ });
127
+ };
128
+ render(jsx(ParentComponent, {}), container);
129
+ expect(container.children[0].children[0].textContent).toBe("5");
130
+ // Trigger update
131
+ container.querySelector("button").click();
132
+ await new Promise((resolve) => setTimeout(resolve, 0));
133
+ expect(container.children[0].children[0].textContent).toBe("10");
134
+ });
135
+ it("should correctly update props in nested components with state changes", async () => {
136
+ const container = document.createElement("div");
137
+ const ChildComponent = (props) => {
138
+ return () => jsx("div", { children: `${props.message}: ${props.count}` });
139
+ };
140
+ const ParentComponent = () => {
141
+ const state = createState({ count: 0 });
142
+ return () => jsx("div", {
143
+ children: [
144
+ jsx(ChildComponent, { message: "Count", count: state.count }),
145
+ jsx("button", {
146
+ onclick: () => state.count++,
147
+ children: "Increment",
148
+ }),
149
+ ],
150
+ });
151
+ };
152
+ render(jsx(ParentComponent, {}), container);
153
+ // Check initial state
154
+ const childDiv = container.querySelector(":scope > div > div");
155
+ expect(childDiv.textContent).toBe("Count: 0");
156
+ // Simulate button click
157
+ const button = container.querySelector("button");
158
+ button.click();
159
+ await new Promise((resolve) => setTimeout(resolve, 0));
160
+ // Check updated state - THIS is where the bug would show
161
+ // If old props are used instead of new props, this will fail
162
+ expect(childDiv.textContent).toBe("Count: 1");
163
+ });
164
+ it("should pass updated object props to nested components", async () => {
165
+ const container = document.createElement("div");
166
+ const ChildComponent = (props) => {
167
+ return () => jsx("div", { children: `${props.user.name} - ${props.user.age}` });
168
+ };
169
+ const ParentComponent = () => {
170
+ const state = createState({
171
+ userData: { name: "Alice", age: 25 },
172
+ });
173
+ return () => jsx("div", {
174
+ children: [
175
+ jsx(ChildComponent, { user: state.userData }),
176
+ jsx("button", {
177
+ onclick: () => {
178
+ state.userData.name = "Bob";
179
+ state.userData.age = 30;
180
+ },
181
+ children: "Update",
182
+ }),
183
+ ],
184
+ });
185
+ };
186
+ render(jsx(ParentComponent, {}), container);
187
+ expect(container.children[0].children[0].textContent).toBe("Alice - 25");
188
+ // Trigger update
189
+ container.querySelector("button").click();
190
+ await new Promise((resolve) => setTimeout(resolve, 0));
191
+ expect(container.children[0].children[0].textContent).toBe("Bob - 30");
192
+ });
193
+ it("should correctly update props in deeply nested components on state change", async () => {
194
+ const container = document.createElement("div");
195
+ const DeepChild = (props) => {
196
+ return () => jsx("span", { children: String(props.value) });
197
+ };
198
+ const MiddleChild = (props) => {
199
+ return () => jsx("div", {
200
+ children: jsx(DeepChild, { value: props.data }),
201
+ });
202
+ };
203
+ const ParentComponent = () => {
204
+ const state = createState({ value: 100 });
205
+ return () => jsx("div", {
206
+ children: [
207
+ jsx(MiddleChild, { data: state.value }),
208
+ jsx("button", {
209
+ onclick: () => (state.value += 50),
210
+ children: "Update",
211
+ }),
212
+ ],
213
+ });
214
+ };
215
+ render(jsx(ParentComponent, {}), container);
216
+ // Check initial state
217
+ const span = container.querySelector("span");
218
+ expect(span.textContent).toBe("100");
219
+ // Trigger update
220
+ const button = container.querySelector("button");
221
+ button.click();
222
+ await new Promise((resolve) => setTimeout(resolve, 0));
223
+ // THIS is the critical test - ensures new props propagate correctly
224
+ expect(span.textContent).toBe("150");
225
+ });
226
+ it("should handle multiple nested components receiving different updated props", async () => {
227
+ const container = document.createElement("div");
228
+ const ChildA = (props) => {
229
+ return () => jsx("div", { class: "child-a", children: props.value });
230
+ };
231
+ const ChildB = (props) => {
232
+ return () => jsx("div", { class: "child-b", children: props.value });
233
+ };
234
+ const ParentComponent = () => {
235
+ const state = createState({ valueA: "A1", valueB: "B1" });
236
+ return () => jsx("div", {
237
+ children: [
238
+ jsx(ChildA, { value: state.valueA }),
239
+ jsx(ChildB, { value: state.valueB }),
240
+ jsx("button", {
241
+ class: "btn-a",
242
+ onclick: () => (state.valueA = "A2"),
243
+ children: "Update A",
244
+ }),
245
+ jsx("button", {
246
+ class: "btn-b",
247
+ onclick: () => (state.valueB = "B2"),
248
+ children: "Update B",
249
+ }),
250
+ ],
251
+ });
252
+ };
253
+ render(jsx(ParentComponent, {}), container);
254
+ const childA = container.querySelector(".child-a");
255
+ const childB = container.querySelector(".child-b");
256
+ expect(childA.textContent).toBe("A1");
257
+ expect(childB.textContent).toBe("B1");
258
+ // Update only A
259
+ container.querySelector(".btn-a").click();
260
+ await new Promise((resolve) => setTimeout(resolve, 0));
261
+ expect(childA.textContent).toBe("A2");
262
+ expect(childB.textContent).toBe("B1"); // B should remain unchanged
263
+ // Update only B
264
+ container.querySelector(".btn-b").click();
265
+ await new Promise((resolve) => setTimeout(resolve, 0));
266
+ expect(childA.textContent).toBe("A2"); // A should remain unchanged
267
+ expect(childB.textContent).toBe("B2");
268
+ });
269
+ it("should pass array of props to nested components and update correctly", async () => {
270
+ const container = document.createElement("div");
271
+ const ItemComponent = (props) => {
272
+ return () => jsx("li", { children: `${props.index}: ${props.item}` });
273
+ };
274
+ const ListComponent = (props) => {
275
+ return () => jsx("ul", {
276
+ children: props.items.map((item, index) => jsx(ItemComponent, { item, index, key: index })),
277
+ });
278
+ };
279
+ const ParentComponent = () => {
280
+ const state = createState({ items: ["apple", "banana"] });
281
+ return () => jsx("div", {
282
+ children: [
283
+ jsx(ListComponent, { items: state.items }),
284
+ jsx("button", {
285
+ onclick: () => state.items.push("cherry"),
286
+ children: "Add",
287
+ }),
288
+ ],
289
+ });
290
+ };
291
+ render(jsx(ParentComponent, {}), container);
292
+ const ul = container.querySelector("ul");
293
+ expect(ul.children.length).toBe(2);
294
+ expect(ul.children[0].textContent).toBe("0: apple");
295
+ expect(ul.children[1].textContent).toBe("1: banana");
296
+ // Add item
297
+ container.querySelector("button").click();
298
+ await new Promise((resolve) => setTimeout(resolve, 0));
299
+ expect(ul.children.length).toBe(3);
300
+ expect(ul.children[2].textContent).toBe("2: cherry");
301
+ });
302
+ it("should maintain correct prop values when sibling components update", async () => {
303
+ const container = document.createElement("div");
304
+ const DisplayComponent = (props) => {
305
+ return () => jsx("div", {
306
+ class: props.label.toLowerCase().replace(/\s/g, "-"),
307
+ children: `${props.label}: ${props.value}`,
308
+ });
309
+ };
310
+ const ParentComponent = () => {
311
+ const state = createState({ countA: 0, countB: 100 });
312
+ return () => jsx("div", {
313
+ children: [
314
+ jsx(DisplayComponent, { label: "Counter A", value: state.countA }),
315
+ jsx(DisplayComponent, { label: "Counter B", value: state.countB }),
316
+ jsx("button", {
317
+ class: "update-a",
318
+ onclick: () => state.countA++,
319
+ children: "Inc A",
320
+ }),
321
+ ],
322
+ });
323
+ };
324
+ render(jsx(ParentComponent, {}), container);
325
+ const displayA = container.querySelector(".counter-a");
326
+ const displayB = container.querySelector(".counter-b");
327
+ // Update A
328
+ container.querySelector(".update-a").click();
329
+ await new Promise((resolve) => setTimeout(resolve, 0));
330
+ // Ensure A updated and B kept its value (didn't get stale/wrong props)
331
+ expect(displayA?.textContent).toContain("1");
332
+ expect(displayB?.textContent).toContain("100");
87
333
  });
88
334
  });
@@ -11,7 +11,7 @@ describe("Component State", () => {
11
11
  render(jsx(MyComponent, {}), container);
12
12
  expect(container.children[0].textContent).toBe("0");
13
13
  });
14
- it("should update state when value changes", () => {
14
+ it("should update state when value changes", async () => {
15
15
  const container = document.createElement("div");
16
16
  let stateFn;
17
17
  const MyComponent = () => {
@@ -20,9 +20,10 @@ describe("Component State", () => {
20
20
  return () => jsx("div", { children: String(state.count) });
21
21
  };
22
22
  render(jsx(MyComponent, {}), container);
23
+ expect(container.children[0].textContent).toBe("0");
23
24
  stateFn.count = 5;
24
- // After state update, component should re-render
25
- // The implementation will handle this
25
+ await new Promise((resolve) => setTimeout(resolve, 0));
26
+ expect(container.children[0].textContent).toBe("5");
26
27
  });
27
28
  it("should support multiple state values", () => {
28
29
  const container = document.createElement("div");
@@ -40,7 +41,7 @@ describe("Component State", () => {
40
41
  expect(div.children[0].textContent).toBe("0");
41
42
  expect(div.children[1].textContent).toBe("John");
42
43
  });
43
- it("should support incremental state updates", () => {
44
+ it("should support incremental state updates", async () => {
44
45
  const container = document.createElement("div");
45
46
  let stateFn;
46
47
  const MyComponent = () => {
@@ -49,11 +50,12 @@ describe("Component State", () => {
49
50
  return () => jsx("div", { children: String(state.count) });
50
51
  };
51
52
  render(jsx(MyComponent, {}), container);
53
+ expect(container.children[0].textContent).toBe("0");
52
54
  stateFn.count = stateFn.count + 1;
53
- // After state update, component should re-render
54
- // The implementation will handle this
55
+ await new Promise((resolve) => setTimeout(resolve, 0));
56
+ expect(container.children[0].textContent).toBe("1");
55
57
  });
56
- it("should preserve state between re-renders", () => {
58
+ it("should preserve state between re-renders", async () => {
57
59
  const container = document.createElement("div");
58
60
  let stateFn;
59
61
  const MyComponent = () => {
@@ -62,11 +64,12 @@ describe("Component State", () => {
62
64
  return () => jsx("div", { children: String(state.count) });
63
65
  };
64
66
  render(jsx(MyComponent, {}), container);
67
+ expect(container.children[0].textContent).toBe("0");
65
68
  stateFn.count = 1;
66
69
  stateFn.count = 2;
67
70
  stateFn.count = 3;
68
- // State should be preserved across multiple updates
69
- // The implementation will handle this
71
+ await new Promise((resolve) => setTimeout(resolve, 0));
72
+ expect(container.children[0].textContent).toBe("3");
70
73
  });
71
74
  it("should support nested objects as state", () => {
72
75
  const container = document.createElement("div");
@@ -94,7 +97,7 @@ describe("Component State", () => {
94
97
  expect(ul.children[1].textContent).toBe("2");
95
98
  expect(ul.children[2].textContent).toBe("3");
96
99
  });
97
- it("should update nested state properties", () => {
100
+ it("should update nested state properties", async () => {
98
101
  const container = document.createElement("div");
99
102
  let stateFn;
100
103
  const MyComponent = () => {
@@ -108,10 +111,10 @@ describe("Component State", () => {
108
111
  expect(container.children[0].textContent).toBe("Alice is 25");
109
112
  stateFn.user.name = "Bob";
110
113
  stateFn.user.age = 30;
111
- // After nested state update, component should re-render
112
- // The implementation will handle this
114
+ await new Promise((resolve) => setTimeout(resolve, 0));
115
+ expect(container.children[0].textContent).toBe("Bob is 30");
113
116
  });
114
- it("should support array mutations", () => {
117
+ it("should support array mutations", async () => {
115
118
  const container = document.createElement("div");
116
119
  let stateFn;
117
120
  const MyComponent = () => {
@@ -122,8 +125,11 @@ describe("Component State", () => {
122
125
  });
123
126
  };
124
127
  render(jsx(MyComponent, {}), container);
128
+ const ul = container.children[0];
129
+ expect(ul.children).toHaveLength(3);
125
130
  stateFn.items.push(4);
126
- // After array mutation, component should re-render
127
- // The implementation will handle this
131
+ await new Promise((resolve) => setTimeout(resolve, 0));
132
+ expect(ul.children).toHaveLength(4);
133
+ expect(ul.children[3].textContent).toBe("4");
128
134
  });
129
135
  });
@@ -27,7 +27,7 @@ describe("createState", () => {
27
27
  state.items.push(4);
28
28
  expect(state.items).toEqual([1, 2, 3, 4]);
29
29
  });
30
- it("should track property access in observers", () => {
30
+ it("should track property access in observers", async () => {
31
31
  const state = createState({ count: 0 });
32
32
  let renderCount = 0;
33
33
  const observer = new Observer(() => {
@@ -42,13 +42,9 @@ describe("createState", () => {
42
42
  const value = state.count; // Track
43
43
  dispose2(); // Stop observing, subscriptions are now active
44
44
  state.count = 1;
45
- // Wait for microtask
46
- return new Promise((resolve) => {
47
- queueMicrotask(() => {
48
- expect(renderCount).toBeGreaterThan(0);
49
- resolve(undefined);
50
- });
51
- });
45
+ // Wait for microtasks to complete
46
+ await new Promise((resolve) => setTimeout(resolve, 0));
47
+ expect(renderCount).toBeGreaterThan(0);
52
48
  });
53
49
  it("should handle property deletion", () => {
54
50
  const state = createState({ count: 0, temp: "value" });
@@ -67,7 +63,7 @@ describe("createState", () => {
67
63
  const state = createState({ [sym]: "value" });
68
64
  expect(state[sym]).toBe("value");
69
65
  });
70
- it("should notify observers only on actual changes", () => {
66
+ it("should notify observers only on actual changes", async () => {
71
67
  const state = createState({ count: 0 });
72
68
  let notifyCount = 0;
73
69
  const observer = new Observer(() => {
@@ -78,13 +74,9 @@ describe("createState", () => {
78
74
  dispose();
79
75
  state.count = 0; // Same value - should still notify per current implementation
80
76
  state.count = 0;
81
- return new Promise((resolve) => {
82
- queueMicrotask(() => {
83
- // The implementation notifies even for same value, except for optimization cases
84
- observer.dispose();
85
- resolve(undefined);
86
- });
87
- });
77
+ await new Promise((resolve) => setTimeout(resolve, 0));
78
+ // The implementation notifies even for same value, except for optimization cases
79
+ observer.dispose();
88
80
  });
89
81
  it("should handle deeply nested objects", () => {
90
82
  const state = createState({
@@ -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;IAuH7B,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;IAyH7B,KAAK,CAAC,OAAO,EAAE,cAAc;IAW7B,OAAO;CAcR"}
@@ -164,7 +164,7 @@ export class ComponentVNode extends AbstractVNode {
164
164
  this.root?.setAsCurrent();
165
165
  this.root?.pushComponent(this.instance);
166
166
  for (const prop in newNode.props) {
167
- this.instance.reactiveProps[prop] = this.props[prop];
167
+ this.instance.reactiveProps[prop] = newNode.props[prop];
168
168
  }
169
169
  this.root?.popComponent();
170
170
  this.root?.clearCurrent();
@@ -1 +1 @@
1
- {"version":3,"file":"dom-utils.d.ts","sourceRoot":"","sources":["../../src/vdom/dom-utils.ts"],"names":[],"mappings":"AAAA,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,WAAW,EACnB,WAAW,EAAE,IAAI,GAAG,IAAI,EAAE,QAO3B;AAED,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,IAAI,GAAG,IAAI,EAAE,GAAG,IAAI,CAc3D;AAGD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,QAQtE;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,QAE3E;AAED,wBAAgB,cAAc,CAC5B,GAAG,EAAE,WAAW,EAChB,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,GAAG,IAAI,QAOrB;AAED,wBAAgB,eAAe,CAC7B,GAAG,EAAE,WAAW,EAChB,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,QAe/C;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEjD;AAED,wBAAgB,eAAe,CAC7B,GAAG,EAAE,WAAW,EAChB,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GAAG,SAAS,QAkB3D"}
1
+ {"version":3,"file":"dom-utils.d.ts","sourceRoot":"","sources":["../../src/vdom/dom-utils.ts"],"names":[],"mappings":"AAAA,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,WAAW,EACnB,WAAW,EAAE,IAAI,GAAG,IAAI,EAAE,QAO3B;AAED,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,IAAI,GAAG,IAAI,EAAE,GAAG,IAAI,CAc3D;AAGD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,QAQtE;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,QAE3E;AAED,wBAAgB,cAAc,CAC5B,GAAG,EAAE,WAAW,EAChB,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,GAAG,IAAI,QAOrB;AAED,wBAAgB,eAAe,CAC7B,GAAG,EAAE,WAAW,EAChB,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,QAe/C;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEjD;AAED,wBAAgB,eAAe,CAC7B,GAAG,EAAE,WAAW,EAChB,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GAAG,SAAS,QA0B3D"}
@@ -59,16 +59,26 @@ export function isEventProp(name) {
59
59
  }
60
60
  export function setElementClass(elm, value) {
61
61
  if (value === null || value === undefined) {
62
- elm.className = "";
62
+ elm.removeAttribute("class");
63
63
  return;
64
64
  }
65
65
  if (typeof value === "string") {
66
- elm.className = value;
66
+ if (value === "") {
67
+ elm.removeAttribute("class");
68
+ }
69
+ else {
70
+ elm.className = value;
71
+ }
67
72
  return;
68
73
  }
69
74
  // Handle object notation: { "class-name": true, "other-class": false }
70
75
  const classes = Object.keys(value)
71
76
  .filter((key) => value[key])
72
77
  .join(" ");
73
- elm.className = classes;
78
+ if (classes === "") {
79
+ elm.removeAttribute("class");
80
+ }
81
+ else {
82
+ elm.className = classes;
83
+ }
74
84
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rask-ui",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -19,10 +19,11 @@
19
19
  }
20
20
  },
21
21
  "files": [
22
- "dist"
22
+ "dist",
23
+ "README.md"
23
24
  ],
24
25
  "scripts": {
25
- "build": "tsc && cp src/jsx.d.ts dist/",
26
+ "build": "tsc && cp src/jsx.d.ts dist/ && cp ../../README.md .",
26
27
  "dev": "tsc --watch",
27
28
  "test": "vitest",
28
29
  "test:ui": "vitest --ui",