ripple 0.2.16 → 0.2.18

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/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "description": "Ripple is a TypeScript UI framework for the web",
4
4
  "license": "MIT",
5
5
  "author": "Dominic Gannaway",
6
- "version": "0.2.16",
6
+ "version": "0.2.18",
7
7
  "type": "module",
8
8
  "module": "src/runtime/index.js",
9
9
  "main": "src/runtime/index.js",
@@ -115,7 +115,13 @@ const visitors = {
115
115
  if (!context.state.to_ts && node.importKind === 'type') {
116
116
  return b.empty;
117
117
  }
118
- return context.next();
118
+
119
+ return {
120
+ ...node,
121
+ specifiers: node.specifiers
122
+ .filter((spec) => spec.importKind !== 'type')
123
+ .map((spec) => context.visit(spec.local)),
124
+ };
119
125
  },
120
126
 
121
127
  CallExpression(node, context) {
@@ -40,6 +40,8 @@ export { flush_sync as flushSync, untrack, deferred } from './internal/client/ru
40
40
 
41
41
  export { RippleArray } from './array.js';
42
42
 
43
+ export { RippleSet } from './set.js';
44
+
43
45
  export { keyed } from './internal/client/for.js';
44
46
 
45
47
  export { user_effect as effect } from './internal/client/blocks.js';
@@ -0,0 +1,179 @@
1
+ import { get, increment, scope, set, tracked } from './internal/client/runtime.js';
2
+
3
+ const introspect_methods = [
4
+ 'entries',
5
+ 'forEach',
6
+ 'keys',
7
+ 'values',
8
+ Symbol.iterator
9
+ ];
10
+
11
+ const compare_other_methods = [
12
+ 'isDisjointFrom',
13
+ 'isSubsetOf',
14
+ 'isSupersetOf',
15
+ ];
16
+
17
+ const new_other_methods = [
18
+ 'difference',
19
+ 'intersection',
20
+ 'symmetricDifference',
21
+ 'union',
22
+ ];
23
+
24
+ let init = false;
25
+
26
+ export class RippleSet extends Set {
27
+ #tracked_size;
28
+ #tracked_items = new Map();
29
+
30
+ constructor(iterable) {
31
+ super();
32
+
33
+ var block = scope();
34
+
35
+ if (iterable) {
36
+ for (var item of iterable) {
37
+ super.add(item);
38
+ this.#tracked_items.set(item, tracked(0, block));
39
+ }
40
+ }
41
+
42
+ this.#tracked_size = tracked(this.size, block);
43
+
44
+ if (!init) {
45
+ init = true;
46
+ this.#init();
47
+ }
48
+ }
49
+
50
+ #init() {
51
+ var proto = RippleSet.prototype;
52
+ var set_proto = Set.prototype;
53
+
54
+ for (const method of introspect_methods) {
55
+ if (!(method in set_proto)) {
56
+ continue;
57
+ }
58
+
59
+ proto[method] = function (...v) {
60
+ this.$size;
61
+
62
+ return set_proto[method].apply(this, v);
63
+ };
64
+ }
65
+
66
+ for (const method of compare_other_methods) {
67
+ if (!(method in set_proto)) {
68
+ continue;
69
+ }
70
+
71
+ proto[method] = function (other, ...v) {
72
+ this.$size;
73
+
74
+ if (other instanceof RippleSet) {
75
+ other.$size;
76
+ }
77
+
78
+ return set_proto[method].apply(this, [other, ...v]);
79
+ };
80
+ }
81
+
82
+ for (const method of new_other_methods) {
83
+ if (!(method in set_proto)) {
84
+ continue;
85
+ }
86
+
87
+ proto[method] = function (other, ...v) {
88
+ this.$size;
89
+
90
+ if (other instanceof RippleSet) {
91
+ other.$size;
92
+ }
93
+
94
+ return new RippleSet(
95
+ set_proto[method].apply(this, [other, ...v])
96
+ );
97
+ };
98
+ }
99
+ }
100
+
101
+ add(value) {
102
+ var block = scope();
103
+
104
+ if (!super.has(value)) {
105
+ super.add(value);
106
+ this.#tracked_items.set(value, tracked(0, block));
107
+ set(this.#tracked_size, this.size, block);
108
+ }
109
+
110
+ return this;
111
+ }
112
+
113
+ delete(value) {
114
+ var block = scope();
115
+
116
+ if (super.has(value)) {
117
+ super.delete(value);
118
+ var t = this.#tracked_items.get(value);
119
+
120
+ if (t) {
121
+ increment(t, block);
122
+ }
123
+
124
+ this.#tracked_items.delete(value);
125
+ set(this.#tracked_size, this.size, block);
126
+
127
+ return true;
128
+ }
129
+
130
+ return false;
131
+ }
132
+
133
+ has(value) {
134
+ var block = scope();
135
+ var has = super.has(value);
136
+ var tracked_items = this.#tracked_items;
137
+ var t = tracked_items.get(value);
138
+
139
+ if (t === undefined) {
140
+ if (!has) {
141
+ // If the value doesn't exist, track the size in case it's added later
142
+ // but don't create tracked entries willy-nilly to track all possible values
143
+ this.$size;
144
+
145
+ return false;
146
+ }
147
+
148
+ t = tracked(0, block);
149
+ tracked_items.set(value, t);
150
+ }
151
+
152
+ get(t);
153
+ return has;
154
+ }
155
+
156
+ clear() {
157
+ var block = scope();
158
+
159
+ if (this.size > 0) {
160
+ for (var [value, t] of this.#tracked_items) {
161
+ increment(t, block);
162
+ }
163
+
164
+ super.clear();
165
+ this.#tracked_items.clear();
166
+ set(this.#tracked_size, 0, block);
167
+ }
168
+ }
169
+
170
+ get $size() {
171
+ return get(this.#tracked_size);
172
+ }
173
+
174
+ toJSON() {
175
+ this.$size;
176
+
177
+ return [...this];
178
+ }
179
+ }
@@ -0,0 +1,99 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
+ import { mount, flushSync, RippleSet } from 'ripple';
3
+
4
+ describe('RippleSet', () => {
5
+ let container;
6
+
7
+ function render(component) {
8
+ mount(component, {
9
+ target: container
10
+ });
11
+ }
12
+
13
+ beforeEach(() => {
14
+ container = document.createElement('div');
15
+ document.body.appendChild(container);
16
+ });
17
+
18
+ afterEach(() => {
19
+ document.body.removeChild(container);
20
+ container = null;
21
+ });
22
+
23
+ it('handles add and delete operations', () => {
24
+ component SetTest() {
25
+ let items = new RippleSet([1, 2, 3]);
26
+
27
+ <button onClick={() => items.add(4)}>{'add'}</button>
28
+ <button onClick={() => items.delete(2)}>{'delete'}</button>
29
+ <Child items={items} />
30
+ }
31
+
32
+ component Child({ items }) {
33
+ <pre>{JSON.stringify(items)}</pre>
34
+ <pre>{items.$size}</pre>
35
+ }
36
+
37
+ render(SetTest);
38
+
39
+ const addButton = container.querySelectorAll('button')[0];
40
+ const deleteButton = container.querySelectorAll('button')[1];
41
+
42
+ addButton.click();
43
+ flushSync();
44
+
45
+ expect(container.querySelectorAll('pre')[0].textContent).toBe('[1,2,3,4]');
46
+ expect(container.querySelectorAll('pre')[1].textContent).toBe('4');
47
+
48
+ deleteButton.click();
49
+ flushSync();
50
+
51
+ expect(container.querySelectorAll('pre')[0].textContent).toBe('[1,3,4]');
52
+ expect(container.querySelectorAll('pre')[1].textContent).toBe('3');
53
+ });
54
+
55
+ it('handles clear operation', () => {
56
+ component SetTest() {
57
+ let items = new RippleSet([1, 2, 3]);
58
+
59
+ <button onClick={() => items.clear()}>{'clear'}</button>
60
+ <Child items={items} />
61
+ }
62
+
63
+ component Child({ items }) {
64
+ <pre>{JSON.stringify(items)}</pre>
65
+ <pre>{items.$size}</pre>
66
+ }
67
+
68
+ render(SetTest);
69
+
70
+ const clearButton = container.querySelector('button');
71
+
72
+ clearButton.click();
73
+ flushSync();
74
+
75
+ expect(container.querySelectorAll('pre')[0].textContent).toBe('[]');
76
+ expect(container.querySelectorAll('pre')[1].textContent).toBe('0');
77
+ });
78
+
79
+ it('handles has operation', () => {
80
+ component SetTest() {
81
+ let items = new RippleSet([1, 2, 3]);
82
+ let $hasValue = items.has(2);
83
+
84
+ <button onClick={() => items.delete(2)}>{'delete'}</button>
85
+ <pre>{JSON.stringify($hasValue)}</pre>
86
+ }
87
+
88
+ render(SetTest);
89
+
90
+ expect(container.querySelectorAll('pre')[0].textContent).toBe('true');
91
+
92
+ const deleteButton = container.querySelectorAll('button')[0];
93
+
94
+ deleteButton.click();
95
+ flushSync();
96
+
97
+ expect(container.querySelectorAll('pre')[0].textContent).toBe('false');
98
+ });
99
+ });
package/types/index.d.ts CHANGED
@@ -38,3 +38,15 @@ export type Context<T> = {
38
38
  };
39
39
 
40
40
  export declare function createContext<T>(initialValue: T): Context<T>;
41
+
42
+ export class RippleSet<T> extends Set<T> {
43
+ readonly $size: number;
44
+ isDisjointFrom(other: RippleSet<T> | Set<T>): boolean;
45
+ isSubsetOf(other: RippleSet<T> | Set<T>): boolean;
46
+ isSupersetOf(other: RippleSet<T> | Set<T>): boolean;
47
+ difference(other: RippleSet<T> | Set<T>): RippleSet<T>;
48
+ intersection(other: RippleSet<T> | Set<T>): RippleSet<T>;
49
+ symmetricDifference(other: RippleSet<T> | Set<T>): RippleSet<T>;
50
+ union(other: RippleSet<T> | Set<T>): RippleSet<T>;
51
+ toJSON(): T[];
52
+ }