ripple 0.2.112 → 0.2.114

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.112",
6
+ "version": "0.2.114",
7
7
  "type": "module",
8
8
  "module": "src/runtime/index-client.js",
9
9
  "main": "src/runtime/index-client.js",
@@ -1306,6 +1306,7 @@ function RipplePlugin(config) {
1306
1306
  }
1307
1307
 
1308
1308
  if (this.value === 'component') {
1309
+ this.awaitPos = 0;
1309
1310
  const node = this.startNode();
1310
1311
  node.type = 'Component';
1311
1312
  node.css = null;
@@ -204,7 +204,7 @@ const visitors = {
204
204
  return {
205
205
  ...node,
206
206
  specifiers: node.specifiers
207
- .filter((spec) => spec.importKind !== 'type')
207
+ .filter((spec) => context.state.to_ts || spec.importKind !== 'type')
208
208
  .map((spec) => context.visit(spec)),
209
209
  };
210
210
  },
@@ -1462,23 +1462,28 @@ function transform_ts_child(node, context) {
1462
1462
  }
1463
1463
  }
1464
1464
 
1465
- const opening_type = b.jsx_id(type);
1465
+ let tracked_type;
1466
+
1467
+ // Make VSCode happy about tracked components, as they're lowercase in the AST
1468
+ // so VSCode doesn't bother tracking them as components, so this fixes that
1469
+ if (node.id.tracked) {
1470
+ tracked_type = state.scope.generate(type[0].toUpperCase() + type.slice(1));
1471
+ state.init.push(
1472
+ b.const(tracked_type, b.member(node.id, b.literal('#v'), true)),
1473
+ );
1474
+ }
1475
+
1476
+ const opening_type = tracked_type
1477
+ ? b.jsx_id(tracked_type)
1478
+ : b.jsx_id(type);
1466
1479
  opening_type.loc = node.id.loc;
1467
1480
 
1468
1481
  let closing_type = undefined;
1469
1482
 
1470
1483
  if (!node.selfClosing) {
1471
- closing_type = b.jsx_id(type);
1472
- closing_type.loc = {
1473
- start: {
1474
- line: node.loc.end.line,
1475
- column: node.loc.end.column - type.length - 1,
1476
- },
1477
- end: {
1478
- line: node.loc.end.line,
1479
- column: node.loc.end.column - 1,
1480
- },
1481
- };
1484
+ closing_type = tracked_type
1485
+ ? b.jsx_id(tracked_type)
1486
+ : b.jsx_id(type);
1482
1487
  }
1483
1488
 
1484
1489
  state.init.push(
@@ -119,7 +119,7 @@ export function convert_source_map_to_mappings(ast, source, generated_code) {
119
119
  visit(specifier);
120
120
  }
121
121
  }
122
- // Skip source (just a string literal)
122
+ visit(node.source);
123
123
  return;
124
124
  } else if (node.type === 'ImportSpecifier') {
125
125
  // If local and imported are the same, only visit local to avoid duplicates
@@ -1,10 +1,10 @@
1
- /** @import { Block, Derived, Tracked } from '#client' */
1
+ /** @import { Block, Tracked } from '#client' */
2
2
 
3
- import { destroy_block, root } from './internal/client/blocks.js';
4
- import { handle_root_events } from './internal/client/events.js';
3
+ import { destroy_block, effect, render, root } from './internal/client/blocks.js';
4
+ import { handle_root_events, on } from './internal/client/events.js';
5
5
  import { init_operations } from './internal/client/operations.js';
6
- import { active_block, tracked, derived } from './internal/client/runtime.js';
7
- import { create_anchor } from './internal/client/utils.js';
6
+ import { active_block, get, set, tick } from './internal/client/runtime.js';
7
+ import { create_anchor, is_array, is_tracked_object } from './internal/client/utils.js';
8
8
  import { remove_ssr_css } from './internal/client/css.js';
9
9
 
10
10
  // Re-export JSX runtime functions for jsxImportSource: "ripple"
@@ -77,3 +77,183 @@ export { Portal } from './internal/client/portal.js';
77
77
  export { ref_prop as createRefKey } from './internal/client/runtime.js';
78
78
 
79
79
  export { on } from './internal/client/events.js';
80
+
81
+ /**
82
+ * @param {string} value
83
+ */
84
+ function to_number(value) {
85
+ return value === '' ? null : +value;
86
+ }
87
+
88
+ /**
89
+ * @param {HTMLInputElement} input
90
+ */
91
+ function is_numberlike_input(input) {
92
+ var type = input.type;
93
+ return type === 'number' || type === 'range';
94
+ }
95
+
96
+ /** @param {HTMLOptionElement} option */
97
+ function get_option_value(option) {
98
+ return option.value;
99
+ }
100
+
101
+ /**
102
+ * Selects the correct option(s) (depending on whether this is a multiple select)
103
+ * @template V
104
+ * @param {HTMLSelectElement} select
105
+ * @param {V} value
106
+ * @param {boolean} mounting
107
+ */
108
+ function select_option(select, value, mounting = false) {
109
+ if (select.multiple) {
110
+ // If value is null or undefined, keep the selection as is
111
+ if (value == undefined) {
112
+ return;
113
+ }
114
+
115
+ // If not an array, warn and keep the selection as is
116
+ if (!is_array(value)) {
117
+ // TODO
118
+ }
119
+
120
+ // Otherwise, update the selection
121
+ for (var option of select.options) {
122
+ option.selected = /** @type {string[]} */ (value).includes(get_option_value(option));
123
+ }
124
+
125
+ return;
126
+ }
127
+
128
+ for (option of select.options) {
129
+ var option_value = get_option_value(option);
130
+ if (option_value === value) {
131
+ option.selected = true;
132
+ return;
133
+ }
134
+ }
135
+
136
+ if (!mounting || value !== undefined) {
137
+ select.selectedIndex = -1; // no option should be selected
138
+ }
139
+ }
140
+
141
+ /**
142
+ * @param {unknown} maybe_tracked
143
+ * @returns {(node: HTMLInputElement | HTMLSelectElement) => void}
144
+ */
145
+ export function bindValue(maybe_tracked) {
146
+ if (!is_tracked_object(maybe_tracked)) {
147
+ throw new TypeError('bindValue() argument is not a tracked object');
148
+ }
149
+
150
+ var block = /** @type {Block} */ (active_block);
151
+ var tracked = /** @type {Tracked} */ (maybe_tracked);
152
+
153
+ return (node) => {
154
+ var clear_event;
155
+
156
+ if (node.tagName === 'SELECT') {
157
+ var select = /** @type {HTMLSelectElement} */ (node);
158
+ var mounting = true;
159
+
160
+ clear_event = on(select, 'change', async () => {
161
+ var query = ':checked';
162
+ /** @type {unknown} */
163
+ var value;
164
+
165
+ if (select.multiple) {
166
+ value = [].map.call(select.querySelectorAll(query), get_option_value);
167
+ } else {
168
+ /** @type {HTMLOptionElement | null} */
169
+ var selected_option =
170
+ select.querySelector(query) ??
171
+ // will fall back to first non-disabled option if no option is selected
172
+ select.querySelector('option:not([disabled])');
173
+ value = selected_option && get_option_value(selected_option);
174
+ }
175
+
176
+ set(tracked, value, block);
177
+ });
178
+
179
+ effect(() => {
180
+ var value = get(tracked);
181
+ select_option(select, value, mounting);
182
+
183
+ // Mounting and value undefined -> take selection from dom
184
+ if (mounting && value === undefined) {
185
+ /** @type {HTMLOptionElement | null} */
186
+ var selected_option = select.querySelector(':checked');
187
+ if (selected_option !== null) {
188
+ value = get_option_value(selected_option);
189
+ set(tracked, value, block);
190
+ }
191
+ }
192
+
193
+ mounting = false;
194
+ });
195
+ } else {
196
+ var input = /** @type {HTMLInputElement} */ (node);
197
+
198
+ clear_event = on(input, 'input', async () => {
199
+ /** @type {any} */
200
+ var value = input.value;
201
+ value = is_numberlike_input(input) ? to_number(value) : value;
202
+ set(tracked, value, block);
203
+
204
+ await tick();
205
+
206
+ if (value !== (value = get(tracked))) {
207
+ var start = input.selectionStart;
208
+ var end = input.selectionEnd;
209
+ input.value = value ?? '';
210
+
211
+ // Restore selection
212
+ if (end !== null) {
213
+ input.selectionStart = start;
214
+ input.selectionEnd = Math.min(end, input.value.length);
215
+ }
216
+ }
217
+ });
218
+
219
+ render(() => {
220
+ var value = get(tracked);
221
+
222
+ if (is_numberlike_input(input) && value === to_number(input.value)) {
223
+ return;
224
+ }
225
+
226
+ if (input.type === 'date' && !value && !input.value) {
227
+ return;
228
+ }
229
+
230
+ if (value !== input.value) {
231
+ input.value = value ?? '';
232
+ }
233
+ });
234
+
235
+ return clear_event;
236
+ }
237
+ };
238
+ }
239
+
240
+ /**
241
+ * @param {unknown} maybe_tracked
242
+ * @returns {(node: HTMLInputElement) => void}
243
+ */
244
+ export function bindChecked(maybe_tracked) {
245
+ if (!is_tracked_object(maybe_tracked)) {
246
+ throw new TypeError('bindChecked() argument is not a tracked object');
247
+ }
248
+
249
+ const block = /** @type {any} */ (active_block);
250
+ const tracked = /** @type {Tracked<any>} */ (maybe_tracked);
251
+
252
+ return (input) => {
253
+ const clear_event = on(input, 'change', () => {
254
+ set(tracked, input.checked, block);
255
+ });
256
+
257
+ return clear_event;
258
+ };
259
+ }
@@ -269,16 +269,16 @@ var empty_get_set = { get: undefined, set: undefined };
269
269
  /**
270
270
  *
271
271
  * @param {any} v
272
- * @param {Block} b
272
+ * @param {Block} block
273
273
  * @param {(value: any) => any} [get]
274
274
  * @param {(next: any, prev: any) => any} [set]
275
275
  * @returns {Tracked}
276
276
  */
277
- export function tracked(v, b, get, set) {
277
+ export function tracked(v, block, get, set) {
278
278
  // TODO: now we expose tracked, we should likely block access in DEV somehow
279
279
  return {
280
280
  a: get || set ? { get, set } : empty_get_set,
281
- b,
281
+ b: block || active_block,
282
282
  c: 0,
283
283
  f: TRACKED,
284
284
  v,
@@ -295,7 +295,7 @@ export function tracked(v, b, get, set) {
295
295
  export function derived(fn, block, get, set) {
296
296
  return {
297
297
  a: get || set ? { get, set } : empty_get_set,
298
- b: block,
298
+ b: block || active_block,
299
299
  blocks: null,
300
300
  c: 0,
301
301
  co: active_component,
@@ -17,12 +17,12 @@ export type Dependency = {
17
17
  n: null | Dependency;
18
18
  };
19
19
 
20
- export type Tracked = {
20
+ export type Tracked<V = any> = {
21
21
  a: { get?: Function, set?: Function };
22
22
  b: Block;
23
23
  c: number;
24
24
  f: number;
25
- v: any;
25
+ v: V;
26
26
  };
27
27
 
28
28
  export type Derived = {