ripple 0.3.7 → 0.3.9

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.
Files changed (119) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/package.json +2 -2
  3. package/src/compiler/phases/1-parse/index.js +48 -349
  4. package/src/compiler/phases/2-analyze/index.js +343 -52
  5. package/src/compiler/phases/3-transform/client/index.js +28 -160
  6. package/src/compiler/phases/3-transform/segments.js +0 -7
  7. package/src/compiler/phases/3-transform/server/index.js +31 -154
  8. package/src/compiler/types/acorn.d.ts +1 -1
  9. package/src/compiler/types/estree.d.ts +1 -1
  10. package/src/compiler/types/import.d.ts +0 -2
  11. package/src/compiler/types/index.d.ts +5 -17
  12. package/src/compiler/types/parse.d.ts +1 -17
  13. package/src/compiler/utils.js +53 -20
  14. package/src/runtime/index-client.js +2 -13
  15. package/src/runtime/index-server.js +2 -2
  16. package/src/runtime/internal/client/bindings.js +3 -1
  17. package/src/runtime/internal/client/composite.js +3 -2
  18. package/src/runtime/internal/client/events.js +1 -1
  19. package/src/runtime/internal/client/head.js +3 -4
  20. package/src/runtime/internal/client/index.js +0 -1
  21. package/src/runtime/internal/client/runtime.js +0 -52
  22. package/src/runtime/internal/server/index.js +31 -55
  23. package/tests/client/array/array.copy-within.test.ripple +12 -12
  24. package/tests/client/array/array.derived.test.ripple +46 -46
  25. package/tests/client/array/array.iteration.test.ripple +10 -10
  26. package/tests/client/array/array.mutations.test.ripple +20 -20
  27. package/tests/client/array/array.to-methods.test.ripple +6 -6
  28. package/tests/client/async-suspend.test.ripple +5 -5
  29. package/tests/client/basic/basic.attributes.test.ripple +81 -81
  30. package/tests/client/basic/basic.collections.test.ripple +9 -9
  31. package/tests/client/basic/basic.components.test.ripple +28 -28
  32. package/tests/client/basic/basic.errors.test.ripple +46 -18
  33. package/tests/client/basic/basic.events.test.ripple +37 -37
  34. package/tests/client/basic/basic.get-set.test.ripple +6 -6
  35. package/tests/client/basic/basic.reactivity.test.ripple +58 -203
  36. package/tests/client/basic/basic.rendering.test.ripple +19 -19
  37. package/tests/client/basic/basic.utilities.test.ripple +3 -3
  38. package/tests/client/boundaries.test.ripple +12 -12
  39. package/tests/client/compiler/__snapshots__/compiler.assignments.test.ripple.snap +5 -5
  40. package/tests/client/compiler/compiler.assignments.test.ripple +19 -19
  41. package/tests/client/compiler/compiler.basic.test.ripple +46 -27
  42. package/tests/client/compiler/compiler.tracked-access.test.ripple +2 -2
  43. package/tests/client/composite/composite.dynamic-components.test.ripple +9 -9
  44. package/tests/client/composite/composite.props.test.ripple +14 -16
  45. package/tests/client/composite/composite.reactivity.test.ripple +69 -70
  46. package/tests/client/composite/composite.render.test.ripple +3 -3
  47. package/tests/client/computed-properties.test.ripple +4 -4
  48. package/tests/client/date.test.ripple +42 -42
  49. package/tests/client/dynamic-elements.test.ripple +44 -45
  50. package/tests/client/events.test.ripple +70 -70
  51. package/tests/client/for.test.ripple +25 -25
  52. package/tests/client/head.test.ripple +19 -19
  53. package/tests/client/html.test.ripple +3 -3
  54. package/tests/client/input-value.test.ripple +84 -84
  55. package/tests/client/lazy-destructuring.test.ripple +138 -26
  56. package/tests/client/map.test.ripple +16 -16
  57. package/tests/client/media-query.test.ripple +7 -7
  58. package/tests/client/portal.test.ripple +11 -11
  59. package/tests/client/ref.test.ripple +4 -4
  60. package/tests/client/return.test.ripple +52 -52
  61. package/tests/client/set.test.ripple +6 -6
  62. package/tests/client/svg.test.ripple +5 -5
  63. package/tests/client/switch.test.ripple +44 -44
  64. package/tests/client/try.test.ripple +5 -5
  65. package/tests/client/url/url.derived.test.ripple +6 -6
  66. package/tests/client/url-search-params/url-search-params.derived.test.ripple +8 -8
  67. package/tests/client/url-search-params/url-search-params.iteration.test.ripple +10 -10
  68. package/tests/client/url-search-params/url-search-params.mutation.test.ripple +10 -10
  69. package/tests/client/url-search-params/url-search-params.retrieval.test.ripple +18 -18
  70. package/tests/client/url-search-params/url-search-params.serialization.test.ripple +2 -2
  71. package/tests/hydration/compiled/client/events.js +25 -25
  72. package/tests/hydration/compiled/client/for.js +70 -66
  73. package/tests/hydration/compiled/client/head.js +25 -25
  74. package/tests/hydration/compiled/client/hmr.js +2 -2
  75. package/tests/hydration/compiled/client/html.js +3 -3
  76. package/tests/hydration/compiled/client/if-children.js +24 -24
  77. package/tests/hydration/compiled/client/if.js +18 -18
  78. package/tests/hydration/compiled/client/mixed-control-flow.js +9 -9
  79. package/tests/hydration/compiled/client/portal.js +3 -3
  80. package/tests/hydration/compiled/client/reactivity.js +16 -16
  81. package/tests/hydration/compiled/client/return.js +40 -40
  82. package/tests/hydration/compiled/client/switch.js +12 -12
  83. package/tests/hydration/compiled/server/events.js +19 -19
  84. package/tests/hydration/compiled/server/for.js +41 -41
  85. package/tests/hydration/compiled/server/head.js +26 -26
  86. package/tests/hydration/compiled/server/hmr.js +2 -2
  87. package/tests/hydration/compiled/server/html.js +2 -2
  88. package/tests/hydration/compiled/server/if-children.js +16 -16
  89. package/tests/hydration/compiled/server/if.js +11 -11
  90. package/tests/hydration/compiled/server/mixed-control-flow.js +6 -6
  91. package/tests/hydration/compiled/server/portal.js +2 -2
  92. package/tests/hydration/compiled/server/reactivity.js +16 -16
  93. package/tests/hydration/compiled/server/return.js +25 -25
  94. package/tests/hydration/compiled/server/switch.js +8 -8
  95. package/tests/hydration/components/events.ripple +25 -25
  96. package/tests/hydration/components/for.ripple +66 -66
  97. package/tests/hydration/components/head.ripple +16 -16
  98. package/tests/hydration/components/hmr.ripple +2 -2
  99. package/tests/hydration/components/html.ripple +3 -3
  100. package/tests/hydration/components/if-children.ripple +24 -24
  101. package/tests/hydration/components/if.ripple +18 -18
  102. package/tests/hydration/components/mixed-control-flow.ripple +9 -9
  103. package/tests/hydration/components/portal.ripple +3 -3
  104. package/tests/hydration/components/reactivity.ripple +16 -16
  105. package/tests/hydration/components/return.ripple +40 -40
  106. package/tests/hydration/components/switch.ripple +20 -20
  107. package/tests/server/await.test.ripple +3 -3
  108. package/tests/server/basic.attributes.test.ripple +34 -34
  109. package/tests/server/basic.components.test.ripple +10 -10
  110. package/tests/server/basic.test.ripple +38 -40
  111. package/tests/server/compiler.test.ripple +22 -0
  112. package/tests/server/composite.props.test.ripple +12 -14
  113. package/tests/server/dynamic-elements.test.ripple +15 -15
  114. package/tests/server/head.test.ripple +11 -11
  115. package/tests/server/lazy-destructuring.test.ripple +92 -13
  116. package/tsconfig.typecheck.json +4 -0
  117. package/types/index.d.ts +0 -19
  118. package/tests/client/__snapshots__/tracked-expression.test.ripple.snap +0 -34
  119. package/tests/client/tracked-expression.test.ripple +0 -26
@@ -1,4 +1,4 @@
1
- import { flushSync, track, trackSplit } from 'ripple';
1
+ import { flushSync, track } from 'ripple';
2
2
 
3
3
  describe('lazy destructuring', () => {
4
4
  it('supports tracked value getter and setter', () => {
@@ -29,14 +29,14 @@ describe('lazy destructuring', () => {
29
29
  }
30
30
 
31
31
  component Test() {
32
- let a = track(1);
33
- let b = track('hello');
32
+ let &[a] = track(1);
33
+ let &[b] = track('hello');
34
34
 
35
- <Inner {@a} {@b} />
35
+ <Inner {a} {b} />
36
36
  <button
37
37
  onClick={() => {
38
- @a = 2;
39
- @b = 'world';
38
+ a = 2;
39
+ b = 'world';
40
40
  }}
41
41
  >
42
42
  {'update'}
@@ -56,14 +56,14 @@ describe('lazy destructuring', () => {
56
56
  }
57
57
 
58
58
  component Test() {
59
- let first = track(10);
60
- let second = track(20);
59
+ let &[first] = track(10);
60
+ let &[second] = track(20);
61
61
 
62
- <Inner {@first} {@second} />
62
+ <Inner {first} {second} />
63
63
  <button
64
64
  onClick={() => {
65
- @first = 30;
66
- @second = 40;
65
+ first = 30;
66
+ second = 40;
67
67
  }}
68
68
  >
69
69
  {'update'}
@@ -107,12 +107,12 @@ describe('lazy destructuring', () => {
107
107
  }
108
108
 
109
109
  component Test() {
110
- let count = track(0);
110
+ let &[count] = track(0);
111
111
 
112
- <Inner {@count} />
112
+ <Inner {count} />
113
113
  <button
114
114
  onClick={() => {
115
- @count++;
115
+ count++;
116
116
  }}
117
117
  >
118
118
  {'increment'}
@@ -126,6 +126,38 @@ describe('lazy destructuring', () => {
126
126
  expect(container.querySelector('pre')!.textContent).toBe('1');
127
127
  });
128
128
 
129
+ it('supports nested lazy destructuring in non-lazy component params', () => {
130
+ component Inner({ something: &[first, second] }) {
131
+ first = second.value + 1;
132
+ <pre>{`${first}-${second.value}`}</pre>
133
+ }
134
+
135
+ component Test() {
136
+ <Inner something={track(1)} />
137
+ }
138
+
139
+ render(Test);
140
+ expect(container.querySelector('pre')!.textContent).toBe('2-2');
141
+ });
142
+
143
+ it(
144
+ 'preserves lazy getter/setter behavior for nested rest destructuring in non-lazy component params',
145
+ () => {
146
+ component Inner({ values: [head, ...&{ length: rest_length, 0: first_rest }] }) {
147
+ const before = `${first_rest?.value ?? 'nil'}-${rest_length}`;
148
+ rest_length = 0;
149
+ <pre>{`${head}-${before}-${first_rest?.value ?? 'nil'}-${rest_length}`}</pre>
150
+ }
151
+
152
+ component Test() {
153
+ <Inner values={[10, track(20), track(30)]} />
154
+ }
155
+
156
+ render(Test);
157
+ expect(container.querySelector('pre')!.textContent).toBe('10-20-2-nil-0');
158
+ },
159
+ );
160
+
129
161
  it('supports lazy destructuring in function params', () => {
130
162
  component Test() {
131
163
  function getInfo(&{ x, y }: { x: number; y: number }) {
@@ -139,6 +171,42 @@ describe('lazy destructuring', () => {
139
171
  expect(container.querySelector('pre')!.textContent).toBe('10');
140
172
  });
141
173
 
174
+ it('supports nested lazy destructuring in non-lazy function params', () => {
175
+ component Test() {
176
+ const something = track(1);
177
+
178
+ function getInfo({ something: &[first, second] }) {
179
+ first = second.value + 1;
180
+ return `${first}-${second.value}`;
181
+ }
182
+
183
+ const result = getInfo({ something });
184
+ <pre>{result}</pre>
185
+ }
186
+
187
+ render(Test);
188
+ expect(container.querySelector('pre')!.textContent).toBe('2-2');
189
+ });
190
+
191
+ it(
192
+ 'preserves lazy getter/setter behavior for nested rest destructuring in non-lazy function params',
193
+ () => {
194
+ component Test() {
195
+ function summarize({ values: [head, ...&{ length: rest_length, 0: first_rest }] }) {
196
+ const before = `${first_rest?.value ?? 'nil'}-${rest_length}`;
197
+ rest_length = 0;
198
+ return `${head}-${before}-${first_rest?.value ?? 'nil'}-${rest_length}`;
199
+ }
200
+
201
+ const result = summarize({ values: [5, track(6), track(7)] });
202
+ <pre>{result}</pre>
203
+ }
204
+
205
+ render(Test);
206
+ expect(container.querySelector('pre')!.textContent).toBe('5-6-2-nil-0');
207
+ },
208
+ );
209
+
142
210
  it('supports let lazy destructuring with assignment writeback', () => {
143
211
  component Test() {
144
212
  const obj = { a: 1, b: 2 };
@@ -205,21 +273,10 @@ describe('lazy destructuring', () => {
205
273
  expect(container.querySelector('pre')!.textContent).toBe('1-99');
206
274
  });
207
275
 
208
- it('does not apply the track tuple fast-path to trackSplit lazy arrays', () => {
209
- component Test() {
210
- const source = { a: 1, b: 2, c: 3 };
211
- let &[a, b, rest] = trackSplit(source, ['a', 'b']);
212
- <pre>{`${@a}-${@b}-${@rest.c}`}</pre>
213
- }
214
-
215
- render(Test);
216
- expect(container.querySelector('pre')!.textContent).toBe('1-2-3');
217
- });
218
-
219
276
  it('supports rest destructuring from iterable array-like tracked values', () => {
220
277
  component Test() {
221
278
  let &[value, ...rest] = track(0);
222
- <pre>{`${value}-${@rest.length}-${@rest[0] === value}`}</pre>
279
+ <pre>{`${value}-${rest.length}-${rest[0] === value}`}</pre>
223
280
  }
224
281
 
225
282
  render(Test);
@@ -260,4 +317,59 @@ describe('lazy destructuring', () => {
260
317
  render(Test);
261
318
  expect(container.querySelector('pre')!.textContent).toBe('Alice-30');
262
319
  });
320
+
321
+ it('supports standalone lazy array destructuring with track()', () => {
322
+ component Test() {
323
+ let count;
324
+ &[count] = track(0);
325
+ <div>{count}</div>
326
+ <button
327
+ onClick={() => {
328
+ count++;
329
+ }}
330
+ >
331
+ {'inc'}
332
+ </button>
333
+ }
334
+
335
+ render(Test);
336
+ expect(container.querySelector('div')!.textContent).toBe('0');
337
+ container.querySelector('button')!.click();
338
+ flushSync();
339
+ expect(container.querySelector('div')!.textContent).toBe('1');
340
+ });
341
+
342
+ it('supports standalone lazy array destructuring with second element', () => {
343
+ component Test() {
344
+ let value;
345
+ let tracked;
346
+ &[value, tracked] = track(42);
347
+ <div>{value}</div>
348
+ <button
349
+ onClick={() => {
350
+ value = 100;
351
+ }}
352
+ >
353
+ {'set'}
354
+ </button>
355
+ }
356
+
357
+ render(Test);
358
+ expect(container.querySelector('div')!.textContent).toBe('42');
359
+ container.querySelector('button')!.click();
360
+ flushSync();
361
+ expect(container.querySelector('div')!.textContent).toBe('100');
362
+ });
363
+
364
+ it('supports standalone lazy object destructuring', () => {
365
+ component Test() {
366
+ let a;
367
+ let b;
368
+ &{ a, b } = { a: 10, b: 20 };
369
+ <pre>{`${a}-${b}`}</pre>
370
+ }
371
+
372
+ render(Test);
373
+ expect(container.querySelector('pre')!.textContent).toBe('10-20');
374
+ });
263
375
  });
@@ -4,16 +4,16 @@ describe('RippleMap', () => {
4
4
  it('handles set with update and delete operations with a reactive size var', () => {
5
5
  component MapTest() {
6
6
  let map = RippleMap([['a', 1], ['b', 2], ['c', 3]]);
7
- let value = track(() => map.get('a'));
8
- let size = track(() => map.size);
7
+ let &[value] = track(() => map.get('a'));
8
+ let &[size] = track(() => map.size);
9
9
 
10
10
  <button onClick={() => map.set('d', 4)}>{'set'}</button>
11
11
  <button onClick={() => map.delete('b')}>{'delete'}</button>
12
12
  <button onClick={() => map.set('a', 5)}>{'update'}</button>
13
13
 
14
14
  <pre>{map.get('d')}</pre>
15
- <pre>{@size}</pre>
16
- <pre>{@value}</pre>
15
+ <pre>{size}</pre>
16
+ <pre>{value}</pre>
17
17
  }
18
18
 
19
19
  render(MapTest);
@@ -61,10 +61,10 @@ describe('RippleMap', () => {
61
61
  it('handles has operation and tracks reactive $has', () => {
62
62
  component MapTest() {
63
63
  let map = RippleMap([['a', 1], ['b', 2], ['c', 3]]);
64
- let has = track(() => map.has('b'));
64
+ let &[has] = track(() => map.has('b'));
65
65
 
66
66
  <button onClick={() => map.delete('b')}>{'delete'}</button>
67
- <pre>{@has}</pre>
67
+ <pre>{has}</pre>
68
68
  }
69
69
 
70
70
  render(MapTest);
@@ -81,15 +81,15 @@ describe('RippleMap', () => {
81
81
  it('handles reactivity of keys, values, and entries', () => {
82
82
  component MapTest() {
83
83
  let map = RippleMap([['x', 10], ['y', 20]]);
84
- let keys = track(() => Array.from(map.keys()));
85
- let values = track(() => Array.from(map.values()));
86
- let entries = track(() => Array.from(map.entries()));
84
+ let &[keys] = track(() => Array.from(map.keys()));
85
+ let &[values] = track(() => Array.from(map.values()));
86
+ let &[entries] = track(() => Array.from(map.entries()));
87
87
 
88
88
  <button onClick={() => map.delete('x')}>{'delete'}</button>
89
89
 
90
- <pre>{JSON.stringify(@keys)}</pre>
91
- <pre>{JSON.stringify(@values)}</pre>
92
- <pre>{JSON.stringify(@entries)}</pre>
90
+ <pre>{JSON.stringify(keys)}</pre>
91
+ <pre>{JSON.stringify(values)}</pre>
92
+ <pre>{JSON.stringify(entries)}</pre>
93
93
  }
94
94
 
95
95
  render(MapTest);
@@ -142,11 +142,11 @@ describe('RippleMap', () => {
142
142
  it('creates RippleMap with initial entries using RippleMap() shorthand syntax', () => {
143
143
  component MapTest() {
144
144
  let map = RippleMap([['a', 1], ['b', 2], ['c', 3]]);
145
- let value = track(() => map.get('b'));
145
+ let &[value] = track(() => map.get('b'));
146
146
 
147
147
  <button onClick={() => map.set('b', 10)}>{'update'}</button>
148
148
  <pre>{map.size}</pre>
149
- <pre>{@value}</pre>
149
+ <pre>{value}</pre>
150
150
  }
151
151
 
152
152
  render(MapTest);
@@ -164,13 +164,13 @@ describe('RippleMap', () => {
164
164
  it('handles all operations with RippleMap() shorthand syntax', () => {
165
165
  component MapTest() {
166
166
  let map = RippleMap([['x', 100], ['y', 200]]);
167
- let keys = track(() => Array.from(map.keys()));
167
+ let &[keys] = track(() => Array.from(map.keys()));
168
168
 
169
169
  <button onClick={() => map.set('z', 300)}>{'add'}</button>
170
170
  <button onClick={() => map.delete('x')}>{'delete'}</button>
171
171
  <button onClick={() => map.clear()}>{'clear'}</button>
172
172
 
173
- <pre>{JSON.stringify(@keys)}</pre>
173
+ <pre>{JSON.stringify(keys)}</pre>
174
174
  <pre>{map.size}</pre>
175
175
  }
176
176
 
@@ -51,10 +51,10 @@ describe('MediaQuery', () => {
51
51
  const media = '(min-width: 600px)';
52
52
 
53
53
  component App() {
54
- const medium = MediaQuery(media);
54
+ let &[medium] = MediaQuery(media);
55
55
 
56
56
  <div>
57
- <p>{@medium}</p>
57
+ <p>{medium}</p>
58
58
  </div>
59
59
  }
60
60
 
@@ -86,20 +86,20 @@ describe('MediaQuery', () => {
86
86
  const media = '(min-width: 600px)';
87
87
 
88
88
  component App() {
89
- let show = track(true);
89
+ let &[show] = track(true);
90
90
 
91
- if (@show) {
91
+ if (show) {
92
92
  <Child />
93
93
  }
94
94
 
95
- <button onClick={() => (@show = !@show)}>{'Toggle Child'}</button>
95
+ <button onClick={() => (show = !show)}>{'Toggle Child'}</button>
96
96
  }
97
97
 
98
98
  component Child() {
99
- const medium = MediaQuery(media);
99
+ let &[medium] = MediaQuery(media);
100
100
 
101
101
  <div>
102
- <p>{@medium}</p>
102
+ <p>{medium}</p>
103
103
  </div>
104
104
  }
105
105
 
@@ -46,15 +46,15 @@ describe('Portal', () => {
46
46
 
47
47
  it('cleans up portal content when destroyed via conditional rendering', () => {
48
48
  component TestPortal() {
49
- let open = track(true);
49
+ let &[open] = track(true);
50
50
 
51
- if (@open) {
51
+ if (open) {
52
52
  <Portal target={document.body}>
53
53
  <div class="test-portal">{'Conditional content'}</div>
54
54
  </Portal>
55
55
  }
56
56
 
57
- <button onClick={() => (@open = false)}>{'Close'}</button>
57
+ <button onClick={() => (open = false)}>{'Close'}</button>
58
58
  }
59
59
 
60
60
  render(TestPortal);
@@ -72,19 +72,19 @@ describe('Portal', () => {
72
72
 
73
73
  it('opens and closes portal via conditional rendering', () => {
74
74
  component TestPortal() {
75
- let open = track(false);
75
+ let &[open] = track(false);
76
76
 
77
- if (@open) {
77
+ if (open) {
78
78
  <Portal target={document.body}>
79
79
  <div class="test-portal">
80
80
  {'Content'}
81
- <button onClick={() => (@open = false)}>{'Close'}</button>
81
+ <button onClick={() => (open = false)}>{'Close'}</button>
82
82
  </div>
83
83
  </Portal>
84
84
  }
85
85
 
86
- if (!@open) {
87
- <button onClick={() => (@open = true)}>{'Open'}</button>
86
+ if (!open) {
87
+ <button onClick={() => (open = true)}>{'Open'}</button>
88
88
  }
89
89
  }
90
90
 
@@ -138,13 +138,13 @@ describe('Portal', () => {
138
138
 
139
139
  it('handles portal with reactive content', () => {
140
140
  component TestReactivePortal() {
141
- let count = track(0);
141
+ let &[count] = track(0);
142
142
 
143
143
  <Portal target={document.body}>
144
144
  <div class="test-portal">
145
145
  {'Count: '}
146
- {String(@count)}
147
- <button onClick={() => @count++}>{'Increment'}</button>
146
+ {String(count)}
147
+ <button onClick={() => count++}>{'Increment'}</button>
148
148
  </div>
149
149
  </Portal>
150
150
  }
@@ -50,7 +50,7 @@ describe('refs', () => {
50
50
  let logs: string[] = [];
51
51
 
52
52
  component App() {
53
- let value = track('test');
53
+ let &[value] = track('test');
54
54
 
55
55
  function inputRef(node: HTMLInputElement) {
56
56
  logs.push('ref called');
@@ -58,7 +58,7 @@ describe('refs', () => {
58
58
 
59
59
  const props = {
60
60
  id: 'example',
61
- @value,
61
+ value,
62
62
  [createRefKey()]: inputRef,
63
63
  };
64
64
 
@@ -81,7 +81,7 @@ describe('refs', () => {
81
81
  let logs: string[] = [];
82
82
 
83
83
  component App() {
84
- let value = track('test');
84
+ let &[value] = track('test');
85
85
 
86
86
  function inputRef(node: HTMLInputElement) {
87
87
  logs.push('ref called');
@@ -89,7 +89,7 @@ describe('refs', () => {
89
89
 
90
90
  const props = {
91
91
  id: 'example',
92
- @value,
92
+ value,
93
93
  };
94
94
 
95
95
  <input type="text" {ref inputRef} {...props} />