ripple 0.2.170 → 0.2.172
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 +2 -2
- package/src/compiler/phases/1-parse/index.js +10 -0
- package/src/compiler/phases/3-transform/client/index.js +47 -18
- package/src/runtime/index-client.js +4 -0
- package/src/runtime/internal/client/bindings.js +89 -0
- package/src/runtime/internal/client/events.js +30 -3
- package/src/runtime/internal/client/render.js +24 -22
- package/tests/client/events.test.ripple +562 -0
- package/tests/client/input-value.test.ripple +1272 -1
- package/types/index.d.ts +8 -0
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.172",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"module": "src/runtime/index-client.js",
|
|
9
9
|
"main": "src/runtime/index-client.js",
|
|
@@ -81,6 +81,6 @@
|
|
|
81
81
|
"typescript": "^5.9.2"
|
|
82
82
|
},
|
|
83
83
|
"peerDependencies": {
|
|
84
|
-
"ripple": "0.2.
|
|
84
|
+
"ripple": "0.2.172"
|
|
85
85
|
}
|
|
86
86
|
}
|
|
@@ -950,6 +950,16 @@ function RipplePlugin(config) {
|
|
|
950
950
|
return this.finishNode(node, isForIn ? 'ForInStatement' : 'ForOfStatement');
|
|
951
951
|
}
|
|
952
952
|
|
|
953
|
+
checkUnreserved(ref) {
|
|
954
|
+
if (ref.name === 'component') {
|
|
955
|
+
this.raise(
|
|
956
|
+
ref.start,
|
|
957
|
+
'"component" is a Ripple keyword and cannot be used as an identifier',
|
|
958
|
+
);
|
|
959
|
+
}
|
|
960
|
+
return super.checkUnreserved(ref);
|
|
961
|
+
}
|
|
962
|
+
|
|
953
963
|
shouldParseExportStatement() {
|
|
954
964
|
if (super.shouldParseExportStatement()) {
|
|
955
965
|
return true;
|
|
@@ -134,7 +134,7 @@ function visit_head_element(node, context) {
|
|
|
134
134
|
}
|
|
135
135
|
|
|
136
136
|
function apply_updates(init, update, state) {
|
|
137
|
-
if (update.length === 1) {
|
|
137
|
+
if (update.length === 1 && !update[0].needsPrevTracking) {
|
|
138
138
|
init.push(
|
|
139
139
|
b.stmt(
|
|
140
140
|
b.call(
|
|
@@ -185,18 +185,27 @@ function apply_updates(init, update, state) {
|
|
|
185
185
|
b.var('__' + key, u.expression),
|
|
186
186
|
b.if(
|
|
187
187
|
b.binary('!==', b.member(b.id('__prev'), b.id(key)), b.id('__' + key)),
|
|
188
|
-
b.block(
|
|
189
|
-
u.
|
|
190
|
-
|
|
188
|
+
b.block(
|
|
189
|
+
u.needsPrevTracking
|
|
190
|
+
? [
|
|
191
|
+
u.operation(b.id('__' + key), b.member(b.id('__prev'), b.id(key))),
|
|
192
|
+
b.stmt(
|
|
193
|
+
b.assignment('=', b.member(b.id('__prev'), b.id(key)), b.id('__' + key)),
|
|
194
|
+
),
|
|
195
|
+
]
|
|
196
|
+
: [
|
|
197
|
+
u.operation(
|
|
198
|
+
b.assignment('=', b.member(b.id('__prev'), b.id(key)), b.id('__' + key)),
|
|
199
|
+
),
|
|
200
|
+
],
|
|
201
|
+
),
|
|
191
202
|
),
|
|
192
203
|
);
|
|
193
204
|
index++;
|
|
194
205
|
} else {
|
|
195
206
|
const key = index_to_key(index);
|
|
196
207
|
/** @type {Array<Statement>} */
|
|
197
|
-
const if_body = [
|
|
198
|
-
b.stmt(b.assignment('=', b.member(b.id('__prev'), b.id(key)), b.id('__' + key))),
|
|
199
|
-
];
|
|
208
|
+
const if_body = [];
|
|
200
209
|
initial.push(b.prop('init', b.id(key), updates[0].initial));
|
|
201
210
|
render_statements.push(
|
|
202
211
|
b.var('__' + key, updates[0].expression),
|
|
@@ -207,14 +216,22 @@ function apply_updates(init, update, state) {
|
|
|
207
216
|
);
|
|
208
217
|
for (const u of updates) {
|
|
209
218
|
index_map.set(u.operation, key);
|
|
210
|
-
if_body.push(
|
|
219
|
+
if_body.push(
|
|
220
|
+
u.needsPrevTracking
|
|
221
|
+
? u.operation(b.id('__' + key), b.member(b.id('__prev'), b.id(key)))
|
|
222
|
+
: u.operation(b.id('__' + key)),
|
|
223
|
+
);
|
|
211
224
|
index++;
|
|
212
225
|
}
|
|
226
|
+
// Update prev after all operations
|
|
227
|
+
if_body.push(
|
|
228
|
+
b.stmt(b.assignment('=', b.member(b.id('__prev'), b.id(key)), b.id('__' + key))),
|
|
229
|
+
);
|
|
213
230
|
}
|
|
214
231
|
}
|
|
215
232
|
|
|
216
233
|
for (const u of update) {
|
|
217
|
-
if (!u.initial) {
|
|
234
|
+
if (!u.initial && !u.needsPrevTracking) {
|
|
218
235
|
render_statements.push(u.operation);
|
|
219
236
|
}
|
|
220
237
|
}
|
|
@@ -879,7 +896,6 @@ const visitors = {
|
|
|
879
896
|
if (is_dom_element) {
|
|
880
897
|
let class_attribute = null;
|
|
881
898
|
let style_attribute = null;
|
|
882
|
-
/** @type {Array<Statement>} */
|
|
883
899
|
const local_updates = [];
|
|
884
900
|
const is_void = is_void_element(node.id.name);
|
|
885
901
|
|
|
@@ -954,7 +970,7 @@ const visitors = {
|
|
|
954
970
|
const metadata = { tracking: false, await: false };
|
|
955
971
|
const expression = visit(attr.value, { ...state, metadata });
|
|
956
972
|
|
|
957
|
-
if (
|
|
973
|
+
if (metadata.tracking) {
|
|
958
974
|
local_updates.push({
|
|
959
975
|
operation: b.stmt(b.call('_$_.set_checked', id, expression)),
|
|
960
976
|
});
|
|
@@ -1093,14 +1109,27 @@ const visitors = {
|
|
|
1093
1109
|
const expression = visit(style_attribute.value, { ...state, metadata });
|
|
1094
1110
|
|
|
1095
1111
|
if (metadata.tracking) {
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1112
|
+
if (
|
|
1113
|
+
style_attribute.value.type === 'Literal' ||
|
|
1114
|
+
style_attribute.value.type === 'TemplateLiteral'
|
|
1115
|
+
) {
|
|
1116
|
+
// Doesn't need prev tracking
|
|
1117
|
+
local_updates.push({
|
|
1118
|
+
operation: b.stmt(b.call('_$_.set_style', id, expression, b.void0)),
|
|
1119
|
+
});
|
|
1120
|
+
} else {
|
|
1121
|
+
// Object or unknown - needs prev tracking
|
|
1122
|
+
local_updates.push({
|
|
1123
|
+
operation: (new_value, prev_value) =>
|
|
1124
|
+
b.stmt(b.call('_$_.set_style', id, new_value, prev_value)),
|
|
1125
|
+
identity: style_attribute.value,
|
|
1126
|
+
expression,
|
|
1127
|
+
initial: b.void0,
|
|
1128
|
+
needsPrevTracking: true,
|
|
1129
|
+
});
|
|
1130
|
+
}
|
|
1102
1131
|
} else {
|
|
1103
|
-
state.init.push(b.stmt(b.call('_$_.set_style', id, expression)));
|
|
1132
|
+
state.init.push(b.stmt(b.call('_$_.set_style', id, expression, b.void0)));
|
|
1104
1133
|
}
|
|
1105
1134
|
}
|
|
1106
1135
|
}
|
|
@@ -81,14 +81,18 @@ export { on } from './internal/client/events.js';
|
|
|
81
81
|
export {
|
|
82
82
|
bindValue,
|
|
83
83
|
bindChecked,
|
|
84
|
+
bindGroup,
|
|
84
85
|
bindClientWidth,
|
|
85
86
|
bindClientHeight,
|
|
86
87
|
bindContentRect,
|
|
87
88
|
bindContentBoxSize,
|
|
88
89
|
bindBorderBoxSize,
|
|
89
90
|
bindDevicePixelContentBoxSize,
|
|
91
|
+
bindIndeterminate,
|
|
90
92
|
bindInnerHTML,
|
|
91
93
|
bindInnerText,
|
|
92
94
|
bindTextContent,
|
|
93
95
|
bindNode,
|
|
96
|
+
bindOffsetWidth,
|
|
97
|
+
bindOffsetHeight,
|
|
94
98
|
} from './internal/client/bindings.js';
|
|
@@ -263,6 +263,95 @@ export function bindChecked(maybe_tracked) {
|
|
|
263
263
|
set(tracked, input.checked);
|
|
264
264
|
});
|
|
265
265
|
|
|
266
|
+
effect(() => {
|
|
267
|
+
var value = get(tracked);
|
|
268
|
+
input.checked = Boolean(value);
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
return clear_event;
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* @param {unknown} maybe_tracked
|
|
277
|
+
* @returns {(node: HTMLInputElement) => void}
|
|
278
|
+
*/
|
|
279
|
+
export function bindIndeterminate(maybe_tracked) {
|
|
280
|
+
if (!is_tracked_object(maybe_tracked)) {
|
|
281
|
+
throw not_tracked_type_error('bindIndeterminate()');
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const tracked = /** @type {Tracked} */ (maybe_tracked);
|
|
285
|
+
|
|
286
|
+
return (input) => {
|
|
287
|
+
const clear_event = on(input, 'change', () => {
|
|
288
|
+
set(tracked, input.indeterminate);
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
effect(() => {
|
|
292
|
+
var value = get(tracked);
|
|
293
|
+
input.indeterminate = Boolean(value);
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
return clear_event;
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* @param {unknown} maybe_tracked
|
|
302
|
+
* @returns {(node: HTMLInputElement) => void}
|
|
303
|
+
*/
|
|
304
|
+
export function bindGroup(maybe_tracked) {
|
|
305
|
+
if (!is_tracked_object(maybe_tracked)) {
|
|
306
|
+
throw not_tracked_type_error('bindGroup()');
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
var tracked = /** @type {Tracked} */ (maybe_tracked);
|
|
310
|
+
|
|
311
|
+
return (input) => {
|
|
312
|
+
var is_checkbox = input.getAttribute('type') === 'checkbox';
|
|
313
|
+
|
|
314
|
+
// Store the input's value
|
|
315
|
+
// @ts-ignore
|
|
316
|
+
input.__value = input.value;
|
|
317
|
+
|
|
318
|
+
var clear_event = on(input, 'change', () => {
|
|
319
|
+
// @ts-ignore
|
|
320
|
+
var value = input.__value;
|
|
321
|
+
|
|
322
|
+
if (is_checkbox) {
|
|
323
|
+
/** @type {Array<any>} */
|
|
324
|
+
var current_value = get(tracked) || [];
|
|
325
|
+
|
|
326
|
+
if (input.checked) {
|
|
327
|
+
// Add if not already present
|
|
328
|
+
if (!current_value.includes(value)) {
|
|
329
|
+
value = [...current_value, value];
|
|
330
|
+
} else {
|
|
331
|
+
value = current_value;
|
|
332
|
+
}
|
|
333
|
+
} else {
|
|
334
|
+
// Remove the value
|
|
335
|
+
value = current_value.filter((v) => v !== value);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
set(tracked, value);
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
effect(() => {
|
|
343
|
+
var value = get(tracked);
|
|
344
|
+
|
|
345
|
+
if (is_checkbox) {
|
|
346
|
+
value = value || [];
|
|
347
|
+
// @ts-ignore
|
|
348
|
+
input.checked = value.includes(input.__value);
|
|
349
|
+
} else {
|
|
350
|
+
// @ts-ignore
|
|
351
|
+
input.checked = value === input.__value;
|
|
352
|
+
}
|
|
353
|
+
});
|
|
354
|
+
|
|
266
355
|
return clear_event;
|
|
267
356
|
};
|
|
268
357
|
}
|
|
@@ -163,7 +163,13 @@ export function handle_event_propagation(event) {
|
|
|
163
163
|
var delegated = current_target['__' + event_name];
|
|
164
164
|
|
|
165
165
|
if (delegated !== undefined && !(/** @type {any} */ (current_target).disabled)) {
|
|
166
|
-
delegated
|
|
166
|
+
if (is_array(delegated)) {
|
|
167
|
+
for (var i = 0; i < delegated.length; i++) {
|
|
168
|
+
delegated[i].call(current_target, event);
|
|
169
|
+
}
|
|
170
|
+
} else {
|
|
171
|
+
delegated.call(current_target, event);
|
|
172
|
+
}
|
|
167
173
|
}
|
|
168
174
|
} catch (error) {
|
|
169
175
|
if (throw_error) {
|
|
@@ -235,10 +241,31 @@ function create_event(event_name, dom, handler, options) {
|
|
|
235
241
|
|
|
236
242
|
if (is_delegated) {
|
|
237
243
|
var prop = '__' + event_name;
|
|
238
|
-
/** @type {DelegatedEventTarget} */ (dom)
|
|
244
|
+
var target = /** @type {DelegatedEventTarget} */ (dom);
|
|
245
|
+
var current = target[prop];
|
|
246
|
+
|
|
247
|
+
if (current === undefined) {
|
|
248
|
+
target[prop] = handler;
|
|
249
|
+
} else if (is_array(current)) {
|
|
250
|
+
if (!current.includes(handler)) {
|
|
251
|
+
current.push(handler);
|
|
252
|
+
}
|
|
253
|
+
} else {
|
|
254
|
+
if (current !== handler) {
|
|
255
|
+
target[prop] = [current, handler];
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
239
259
|
delegate([event_name]);
|
|
240
260
|
return () => {
|
|
241
|
-
|
|
261
|
+
var handlers = target[prop];
|
|
262
|
+
if (is_array(handlers)) {
|
|
263
|
+
var filtered = handlers.filter((h) => h !== handler);
|
|
264
|
+
target[prop] =
|
|
265
|
+
filtered.length === 0 ? undefined : filtered.length === 1 ? filtered[0] : filtered;
|
|
266
|
+
} else {
|
|
267
|
+
target[prop] = undefined;
|
|
268
|
+
}
|
|
242
269
|
};
|
|
243
270
|
}
|
|
244
271
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** @import { Block } from '#client' */
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { destroy_block, ref } from './blocks.js';
|
|
4
4
|
import { REF_PROP } from './constants.js';
|
|
5
5
|
import {
|
|
6
6
|
get_descriptors,
|
|
@@ -66,13 +66,14 @@ function get_setters(element) {
|
|
|
66
66
|
/**
|
|
67
67
|
* @param {Element} element
|
|
68
68
|
* @param {any} value
|
|
69
|
+
* @param {Record<string, string> | undefined} prev
|
|
69
70
|
* @returns {void}
|
|
70
71
|
*/
|
|
71
|
-
export function set_style(element, value) {
|
|
72
|
+
export function set_style(element, value, prev = {}) {
|
|
72
73
|
if (value == null) {
|
|
73
74
|
element.removeAttribute('style');
|
|
74
75
|
} else if (typeof value !== 'string') {
|
|
75
|
-
apply_styles(/** @type {HTMLElement} */ (element), value);
|
|
76
|
+
apply_styles(/** @type {HTMLElement} */ (element), value, prev);
|
|
76
77
|
} else {
|
|
77
78
|
// @ts-ignore
|
|
78
79
|
element.style.cssText = value;
|
|
@@ -97,27 +98,27 @@ export function set_attribute(element, attribute, value) {
|
|
|
97
98
|
|
|
98
99
|
/**
|
|
99
100
|
* @param {HTMLElement} element
|
|
100
|
-
* @param {
|
|
101
|
+
* @param {Record<string, string | number>} new_styles
|
|
102
|
+
* @param {Record<string, string>} prev
|
|
101
103
|
*/
|
|
102
|
-
|
|
104
|
+
function apply_styles(element, new_styles, prev) {
|
|
103
105
|
const style = element.style;
|
|
104
|
-
const new_properties = new Set();
|
|
105
106
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
const
|
|
107
|
+
// Apply new styles
|
|
108
|
+
for (const key in new_styles) {
|
|
109
|
+
const css_prop = normalize_css_property_name(key);
|
|
110
|
+
const value = String(new_styles[key]);
|
|
109
111
|
|
|
110
|
-
if (
|
|
111
|
-
style.setProperty(
|
|
112
|
+
if (!(key in prev) || prev[key] !== value) {
|
|
113
|
+
style.setProperty(css_prop, value);
|
|
112
114
|
}
|
|
113
|
-
|
|
114
|
-
new_properties.add(normalized_property);
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
if (!
|
|
120
|
-
|
|
117
|
+
// Remove properties that were in prev but not in new_styles
|
|
118
|
+
for (const key in prev) {
|
|
119
|
+
if (!(key in new_styles)) {
|
|
120
|
+
const css_prop = normalize_css_property_name(key);
|
|
121
|
+
style.removeProperty(css_prop);
|
|
121
122
|
}
|
|
122
123
|
}
|
|
123
124
|
}
|
|
@@ -128,13 +129,14 @@ export function apply_styles(element, new_styles) {
|
|
|
128
129
|
* @param {string} key
|
|
129
130
|
* @param {any} value
|
|
130
131
|
* @param {Record<string, (() => void) | undefined>} remove_listeners
|
|
132
|
+
* @param {Record<string, any>} prev
|
|
131
133
|
*/
|
|
132
|
-
function set_attribute_helper(element, key, value, remove_listeners) {
|
|
134
|
+
function set_attribute_helper(element, key, value, remove_listeners, prev) {
|
|
133
135
|
if (key === 'class') {
|
|
134
136
|
const is_html = element.namespaceURI === 'http://www.w3.org/1999/xhtml';
|
|
135
137
|
set_class(/** @type {HTMLElement} */ (element), value, undefined, is_html);
|
|
136
138
|
} else if (key === 'style') {
|
|
137
|
-
set_style(
|
|
139
|
+
set_style(element, value, prev.style);
|
|
138
140
|
} else if (key === '#class') {
|
|
139
141
|
// Special case for static class when spreading props
|
|
140
142
|
element.classList.add(value);
|
|
@@ -167,7 +169,7 @@ export function set_class(dom, value, hash, is_html = true) {
|
|
|
167
169
|
: clsx([value, hash]);
|
|
168
170
|
|
|
169
171
|
// Removing the attribute when the value is only an empty string causes
|
|
170
|
-
//
|
|
172
|
+
// performance issues vs simply making the className an empty string. So
|
|
171
173
|
// we should only remove the class if the the value is nullish.
|
|
172
174
|
if (value == null && hash === undefined) {
|
|
173
175
|
dom.removeAttribute('class');
|
|
@@ -290,7 +292,7 @@ export function apply_element_spread(element, fn) {
|
|
|
290
292
|
if (key === '#class') {
|
|
291
293
|
continue;
|
|
292
294
|
}
|
|
293
|
-
set_attribute_helper(element, key, null, remove_listeners);
|
|
295
|
+
set_attribute_helper(element, key, null, remove_listeners, prev);
|
|
294
296
|
}
|
|
295
297
|
}
|
|
296
298
|
|
|
@@ -309,7 +311,7 @@ export function apply_element_spread(element, fn) {
|
|
|
309
311
|
continue;
|
|
310
312
|
}
|
|
311
313
|
|
|
312
|
-
set_attribute_helper(element, key, value, remove_listeners);
|
|
314
|
+
set_attribute_helper(element, key, value, remove_listeners, prev);
|
|
313
315
|
}
|
|
314
316
|
prev = current;
|
|
315
317
|
};
|