ripple 0.2.203 → 0.2.204
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 an elegant TypeScript UI framework",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Dominic Gannaway",
|
|
6
|
-
"version": "0.2.
|
|
6
|
+
"version": "0.2.204",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"module": "src/runtime/index-client.js",
|
|
9
9
|
"main": "src/runtime/index-client.js",
|
|
@@ -97,6 +97,6 @@
|
|
|
97
97
|
"vscode-languageserver-types": "^3.17.5"
|
|
98
98
|
},
|
|
99
99
|
"peerDependencies": {
|
|
100
|
-
"ripple": "0.2.
|
|
100
|
+
"ripple": "0.2.204"
|
|
101
101
|
}
|
|
102
102
|
}
|
|
@@ -3684,6 +3684,16 @@ function create_tsx_with_typescript_support(comments) {
|
|
|
3684
3684
|
context.visit(node.value.body);
|
|
3685
3685
|
}
|
|
3686
3686
|
},
|
|
3687
|
+
TSAsExpression(node, context) {
|
|
3688
|
+
if (!node.loc) {
|
|
3689
|
+
base_tsx.TSAsExpression?.(node, context);
|
|
3690
|
+
return;
|
|
3691
|
+
}
|
|
3692
|
+
const loc = /** @type {AST.SourceLocation} */ (node.loc);
|
|
3693
|
+
context.location(loc.start.line, loc.start.column);
|
|
3694
|
+
base_tsx.TSAsExpression?.(node, context);
|
|
3695
|
+
context.location(loc.end.line, loc.end.column);
|
|
3696
|
+
},
|
|
3687
3697
|
TSObjectKeyword(node, context) {
|
|
3688
3698
|
if (node.loc) {
|
|
3689
3699
|
context.location(node.loc.start.line, node.loc.start.column);
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
import { effect, render } from './blocks.js';
|
|
11
11
|
import { on } from './events.js';
|
|
12
|
-
import { get, set
|
|
12
|
+
import { get, set } from './runtime.js';
|
|
13
13
|
import { is_array, is_tracked_object } from './utils.js';
|
|
14
14
|
|
|
15
15
|
/**
|
|
@@ -246,26 +246,15 @@ export function bindValue(maybe_tracked, set_func = undefined) {
|
|
|
246
246
|
});
|
|
247
247
|
} else {
|
|
248
248
|
var input = /** @type {HTMLInputElement} */ (node);
|
|
249
|
+
var selection_restore_needed = false;
|
|
249
250
|
|
|
250
|
-
clear_event = on(input, 'input',
|
|
251
|
+
clear_event = on(input, 'input', () => {
|
|
252
|
+
selection_restore_needed = true;
|
|
251
253
|
/** @type {any} */
|
|
252
254
|
var value = input.value;
|
|
253
255
|
value = is_numberlike_input(input) ? to_number(value) : value;
|
|
256
|
+
// the setter will schedule a microtask and the render block below will run
|
|
254
257
|
setter(value);
|
|
255
|
-
|
|
256
|
-
await tick();
|
|
257
|
-
|
|
258
|
-
if (value !== getter()) {
|
|
259
|
-
var start = input.selectionStart;
|
|
260
|
-
var end = input.selectionEnd;
|
|
261
|
-
input.value = value ?? '';
|
|
262
|
-
|
|
263
|
-
// Restore selection
|
|
264
|
-
if (end !== null) {
|
|
265
|
-
input.selectionStart = start;
|
|
266
|
-
input.selectionEnd = Math.min(end, input.value.length);
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
258
|
});
|
|
270
259
|
|
|
271
260
|
render(() => {
|
|
@@ -280,7 +269,23 @@ export function bindValue(maybe_tracked, set_func = undefined) {
|
|
|
280
269
|
}
|
|
281
270
|
|
|
282
271
|
if (value !== input.value) {
|
|
283
|
-
|
|
272
|
+
if (selection_restore_needed) {
|
|
273
|
+
var start = input.selectionStart;
|
|
274
|
+
var end = input.selectionEnd;
|
|
275
|
+
|
|
276
|
+
input.value = value ?? '';
|
|
277
|
+
|
|
278
|
+
// Restore selection
|
|
279
|
+
if (end !== null && start !== null) {
|
|
280
|
+
end = Math.min(end, input.value.length);
|
|
281
|
+
start = Math.min(start, end);
|
|
282
|
+
input.selectionStart = start;
|
|
283
|
+
input.selectionEnd = end;
|
|
284
|
+
}
|
|
285
|
+
selection_restore_needed = false;
|
|
286
|
+
} else {
|
|
287
|
+
input.value = value ?? '';
|
|
288
|
+
}
|
|
284
289
|
}
|
|
285
290
|
});
|
|
286
291
|
|
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
flushSync,
|
|
3
3
|
track,
|
|
4
4
|
effect,
|
|
5
|
+
untrack,
|
|
5
6
|
TrackedObject,
|
|
6
7
|
bindValue,
|
|
7
8
|
bindChecked,
|
|
@@ -239,7 +240,7 @@ describe('use value()', () => {
|
|
|
239
240
|
});
|
|
240
241
|
|
|
241
242
|
it('should update checked on input', () => {
|
|
242
|
-
const logs: string[] = [];
|
|
243
|
+
const logs: (string | boolean)[] = [];
|
|
243
244
|
|
|
244
245
|
component App() {
|
|
245
246
|
const value = track(false);
|
|
@@ -335,7 +336,7 @@ describe('use value()', () => {
|
|
|
335
336
|
});
|
|
336
337
|
|
|
337
338
|
it('should update indeterminate on input', () => {
|
|
338
|
-
const logs: string[] = [];
|
|
339
|
+
const logs: (string | boolean)[] = [];
|
|
339
340
|
|
|
340
341
|
component App() {
|
|
341
342
|
const value = track(false);
|
|
@@ -2533,6 +2534,305 @@ describe('bindNode', () => {
|
|
|
2533
2534
|
|
|
2534
2535
|
expect(input.value).toBe('Set by ref');
|
|
2535
2536
|
});
|
|
2537
|
+
|
|
2538
|
+
it('should accurately reflect values mutated through a tracked setter', () => {
|
|
2539
|
+
component App() {
|
|
2540
|
+
let value = track(
|
|
2541
|
+
'',
|
|
2542
|
+
(val) => {
|
|
2543
|
+
return val;
|
|
2544
|
+
},
|
|
2545
|
+
(next) => {
|
|
2546
|
+
if (next.includes('c')) {
|
|
2547
|
+
next = next.replace(/c/g, '');
|
|
2548
|
+
}
|
|
2549
|
+
return next;
|
|
2550
|
+
},
|
|
2551
|
+
);
|
|
2552
|
+
|
|
2553
|
+
<input type="text" {ref bindValue(value)} />
|
|
2554
|
+
<div>{@value}</div>
|
|
2555
|
+
}
|
|
2556
|
+
|
|
2557
|
+
render(App);
|
|
2558
|
+
flushSync();
|
|
2559
|
+
|
|
2560
|
+
const input = container.querySelector('input') as HTMLInputElement;
|
|
2561
|
+
const div = container.querySelector('div') as HTMLDivElement;
|
|
2562
|
+
|
|
2563
|
+
expect(input.value).toBe('');
|
|
2564
|
+
expect(div.textContent).toBe('');
|
|
2565
|
+
|
|
2566
|
+
input.value = 'abc';
|
|
2567
|
+
input.dispatchEvent(new Event('input', { bubbles: true }));
|
|
2568
|
+
flushSync();
|
|
2569
|
+
|
|
2570
|
+
expect(input.value).toBe('ab');
|
|
2571
|
+
expect(div.textContent).toBe('ab');
|
|
2572
|
+
});
|
|
2573
|
+
|
|
2574
|
+
it('should accurately reflect values when a getter modifies value', () => {
|
|
2575
|
+
component App() {
|
|
2576
|
+
let value = track(
|
|
2577
|
+
'',
|
|
2578
|
+
(val) => {
|
|
2579
|
+
if (val.includes('c')) {
|
|
2580
|
+
val = val.replace(/c/g, '');
|
|
2581
|
+
}
|
|
2582
|
+
return val;
|
|
2583
|
+
},
|
|
2584
|
+
(next) => {
|
|
2585
|
+
return next;
|
|
2586
|
+
},
|
|
2587
|
+
);
|
|
2588
|
+
|
|
2589
|
+
<input type="text" {ref bindValue(value)} />
|
|
2590
|
+
<div>{@value}</div>
|
|
2591
|
+
}
|
|
2592
|
+
|
|
2593
|
+
render(App);
|
|
2594
|
+
flushSync();
|
|
2595
|
+
|
|
2596
|
+
const input = container.querySelector('input') as HTMLInputElement;
|
|
2597
|
+
const div = container.querySelector('div') as HTMLDivElement;
|
|
2598
|
+
|
|
2599
|
+
expect(input.value).toBe('');
|
|
2600
|
+
expect(div.textContent).toBe('');
|
|
2601
|
+
|
|
2602
|
+
input.value = 'abc';
|
|
2603
|
+
input.dispatchEvent(new Event('input', { bubbles: true }));
|
|
2604
|
+
flushSync();
|
|
2605
|
+
|
|
2606
|
+
expect(input.value).toBe('ab');
|
|
2607
|
+
expect(div.textContent).toBe('ab');
|
|
2608
|
+
});
|
|
2609
|
+
|
|
2610
|
+
it('should always prefer what getter returns even if setter mutates next', () => {
|
|
2611
|
+
component App() {
|
|
2612
|
+
let value = track(
|
|
2613
|
+
'',
|
|
2614
|
+
(val) => {
|
|
2615
|
+
return val.replace(/[c,b]+/g, '');
|
|
2616
|
+
},
|
|
2617
|
+
(next) => {
|
|
2618
|
+
if (next.includes('c')) {
|
|
2619
|
+
next = next.replace(/c/g, '');
|
|
2620
|
+
}
|
|
2621
|
+
return next;
|
|
2622
|
+
},
|
|
2623
|
+
);
|
|
2624
|
+
|
|
2625
|
+
<input type="text" {ref bindValue(value)} />
|
|
2626
|
+
<div>{@value}</div>
|
|
2627
|
+
}
|
|
2628
|
+
|
|
2629
|
+
render(App);
|
|
2630
|
+
flushSync();
|
|
2631
|
+
|
|
2632
|
+
const input = container.querySelector('input') as HTMLInputElement;
|
|
2633
|
+
const div = container.querySelector('div') as HTMLDivElement;
|
|
2634
|
+
|
|
2635
|
+
expect(input.value).toBe('');
|
|
2636
|
+
expect(div.textContent).toBe('');
|
|
2637
|
+
|
|
2638
|
+
input.value = 'abc';
|
|
2639
|
+
input.dispatchEvent(new Event('input', { bubbles: true }));
|
|
2640
|
+
flushSync();
|
|
2641
|
+
|
|
2642
|
+
expect(input.value).toBe('a');
|
|
2643
|
+
expect(div.textContent).toBe('a');
|
|
2644
|
+
});
|
|
2645
|
+
|
|
2646
|
+
it(
|
|
2647
|
+
'should accurately reflect values mutated through an effect even after a setter mutation',
|
|
2648
|
+
() => {
|
|
2649
|
+
component App() {
|
|
2650
|
+
let value = track(
|
|
2651
|
+
'',
|
|
2652
|
+
(val) => {
|
|
2653
|
+
return val;
|
|
2654
|
+
},
|
|
2655
|
+
(next) => {
|
|
2656
|
+
if (next.includes('c')) {
|
|
2657
|
+
next = next.replace(/c/g, '');
|
|
2658
|
+
}
|
|
2659
|
+
return next;
|
|
2660
|
+
},
|
|
2661
|
+
);
|
|
2662
|
+
|
|
2663
|
+
effect(() => {
|
|
2664
|
+
@value;
|
|
2665
|
+
|
|
2666
|
+
untrack(() => {
|
|
2667
|
+
if (@value.includes('a')) {
|
|
2668
|
+
@value = @value.replace(/a/g, '');
|
|
2669
|
+
}
|
|
2670
|
+
});
|
|
2671
|
+
});
|
|
2672
|
+
<input type="text" {ref bindValue(value)} />
|
|
2673
|
+
<div>{@value}</div>
|
|
2674
|
+
}
|
|
2675
|
+
|
|
2676
|
+
render(App);
|
|
2677
|
+
flushSync();
|
|
2678
|
+
|
|
2679
|
+
const input = container.querySelector('input') as HTMLInputElement;
|
|
2680
|
+
const div = container.querySelector('div') as HTMLDivElement;
|
|
2681
|
+
|
|
2682
|
+
expect(input.value).toBe('');
|
|
2683
|
+
expect(div.textContent).toBe('');
|
|
2684
|
+
|
|
2685
|
+
input.value = 'abc';
|
|
2686
|
+
input.dispatchEvent(new Event('input', { bubbles: true }));
|
|
2687
|
+
flushSync();
|
|
2688
|
+
|
|
2689
|
+
expect(input.value).toBe('b');
|
|
2690
|
+
expect(div.textContent).toBe('b');
|
|
2691
|
+
},
|
|
2692
|
+
);
|
|
2693
|
+
|
|
2694
|
+
it('should accurately reflect values mutated through a tracked setter via bind accessors', () => {
|
|
2695
|
+
component App() {
|
|
2696
|
+
let value = track('');
|
|
2697
|
+
const value_accessors = [
|
|
2698
|
+
() => {
|
|
2699
|
+
return @value;
|
|
2700
|
+
},
|
|
2701
|
+
(v: string) => {
|
|
2702
|
+
if (v.includes('c')) {
|
|
2703
|
+
v = v.replace(/c/g, '');
|
|
2704
|
+
}
|
|
2705
|
+
@value = v;
|
|
2706
|
+
},
|
|
2707
|
+
];
|
|
2708
|
+
|
|
2709
|
+
<input type="text" {ref bindValue(...value_accessors)} />
|
|
2710
|
+
}
|
|
2711
|
+
|
|
2712
|
+
render(App);
|
|
2713
|
+
flushSync();
|
|
2714
|
+
|
|
2715
|
+
const input = container.querySelector('input') as HTMLInputElement;
|
|
2716
|
+
|
|
2717
|
+
expect(input.value).toBe('');
|
|
2718
|
+
|
|
2719
|
+
input.value = 'abc';
|
|
2720
|
+
input.dispatchEvent(new Event('input', { bubbles: true }));
|
|
2721
|
+
flushSync();
|
|
2722
|
+
|
|
2723
|
+
expect(input.value).toBe('ab');
|
|
2724
|
+
});
|
|
2725
|
+
|
|
2726
|
+
it('should prefer what getter returns via bind accessors', () => {
|
|
2727
|
+
component App() {
|
|
2728
|
+
let value = track('');
|
|
2729
|
+
const value_accessors = [
|
|
2730
|
+
() => {
|
|
2731
|
+
if (@value.includes('c')) {
|
|
2732
|
+
return @value.replace(/c/g, '');
|
|
2733
|
+
}
|
|
2734
|
+
return @value;
|
|
2735
|
+
},
|
|
2736
|
+
(v: string) => {
|
|
2737
|
+
@value = v;
|
|
2738
|
+
},
|
|
2739
|
+
];
|
|
2740
|
+
|
|
2741
|
+
<input type="text" {ref bindValue(...value_accessors)} />
|
|
2742
|
+
}
|
|
2743
|
+
|
|
2744
|
+
render(App);
|
|
2745
|
+
flushSync();
|
|
2746
|
+
|
|
2747
|
+
const input = container.querySelector('input') as HTMLInputElement;
|
|
2748
|
+
|
|
2749
|
+
expect(input.value).toBe('');
|
|
2750
|
+
|
|
2751
|
+
input.value = 'abc';
|
|
2752
|
+
input.dispatchEvent(new Event('input', { bubbles: true }));
|
|
2753
|
+
flushSync();
|
|
2754
|
+
|
|
2755
|
+
expect(input.value).toBe('ab');
|
|
2756
|
+
});
|
|
2757
|
+
|
|
2758
|
+
it(
|
|
2759
|
+
'should always prefer what getter returns even if setter mutates next via bind accessors',
|
|
2760
|
+
() => {
|
|
2761
|
+
component App() {
|
|
2762
|
+
let value = track('');
|
|
2763
|
+
const value_accessors = [
|
|
2764
|
+
() => {
|
|
2765
|
+
return @value.replace(/[c,b]+/g, '');
|
|
2766
|
+
},
|
|
2767
|
+
(v: string) => {
|
|
2768
|
+
if (v.includes('c')) {
|
|
2769
|
+
v = v.replace(/c/g, '');
|
|
2770
|
+
}
|
|
2771
|
+
@value = v;
|
|
2772
|
+
},
|
|
2773
|
+
];
|
|
2774
|
+
|
|
2775
|
+
<input type="text" {ref bindValue(...value_accessors)} />
|
|
2776
|
+
}
|
|
2777
|
+
|
|
2778
|
+
render(App);
|
|
2779
|
+
flushSync();
|
|
2780
|
+
|
|
2781
|
+
const input = container.querySelector('input') as HTMLInputElement;
|
|
2782
|
+
|
|
2783
|
+
expect(input.value).toBe('');
|
|
2784
|
+
|
|
2785
|
+
input.value = 'abc';
|
|
2786
|
+
input.dispatchEvent(new Event('input', { bubbles: true }));
|
|
2787
|
+
flushSync();
|
|
2788
|
+
|
|
2789
|
+
expect(input.value).toBe('a');
|
|
2790
|
+
},
|
|
2791
|
+
);
|
|
2792
|
+
|
|
2793
|
+
it(
|
|
2794
|
+
'should accurately reflect values mutated through an effect even after a setter mutation via bind accessors',
|
|
2795
|
+
() => {
|
|
2796
|
+
component App() {
|
|
2797
|
+
let value = track('');
|
|
2798
|
+
const value_accessors = [
|
|
2799
|
+
() => {
|
|
2800
|
+
return @value;
|
|
2801
|
+
},
|
|
2802
|
+
(v: string) => {
|
|
2803
|
+
if (v.includes('c')) {
|
|
2804
|
+
v = v.replace(/c/g, '');
|
|
2805
|
+
}
|
|
2806
|
+
@value = v;
|
|
2807
|
+
},
|
|
2808
|
+
];
|
|
2809
|
+
|
|
2810
|
+
effect(() => {
|
|
2811
|
+
@value;
|
|
2812
|
+
|
|
2813
|
+
untrack(() => {
|
|
2814
|
+
if (@value.includes('a')) {
|
|
2815
|
+
@value = @value.replace(/a/g, '');
|
|
2816
|
+
}
|
|
2817
|
+
});
|
|
2818
|
+
});
|
|
2819
|
+
<input type="text" {ref bindValue(...value_accessors)} />
|
|
2820
|
+
}
|
|
2821
|
+
|
|
2822
|
+
render(App);
|
|
2823
|
+
flushSync();
|
|
2824
|
+
|
|
2825
|
+
const input = container.querySelector('input') as HTMLInputElement;
|
|
2826
|
+
|
|
2827
|
+
expect(input.value).toBe('');
|
|
2828
|
+
|
|
2829
|
+
input.value = 'abc';
|
|
2830
|
+
input.dispatchEvent(new Event('input', { bubbles: true }));
|
|
2831
|
+
flushSync();
|
|
2832
|
+
|
|
2833
|
+
expect(input.value).toBe('b');
|
|
2834
|
+
},
|
|
2835
|
+
);
|
|
2536
2836
|
});
|
|
2537
2837
|
|
|
2538
2838
|
describe('bindFiles', () => {
|