ripple 0.2.31 → 0.2.33

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.31",
6
+ "version": "0.2.33",
7
7
  "type": "module",
8
8
  "module": "src/runtime/index.js",
9
9
  "main": "src/runtime/index.js",
@@ -398,6 +398,7 @@ const visitors = {
398
398
 
399
399
  if (is_dom_element) {
400
400
  let class_attribute = null;
401
+ const local_updates = [];
401
402
 
402
403
  state.template.push(`<${node.id.name}`);
403
404
 
@@ -422,7 +423,7 @@ const visitors = {
422
423
  const expression = visit(attr.value, state);
423
424
 
424
425
  if (name === '$value') {
425
- state.update.push(b.stmt(b.call('$.set_value', id, expression)));
426
+ local_updates.push(b.stmt(b.call('$.set_value', id, expression)));
426
427
  } else {
427
428
  state.init.push(b.stmt(b.call('$.set_value', id, expression)));
428
429
  }
@@ -435,7 +436,7 @@ const visitors = {
435
436
  const expression = visit(attr.value, state);
436
437
 
437
438
  if (name === '$checked') {
438
- state.update.push(b.stmt(b.call('$.set_checked', id, expression)));
439
+ local_updates.push(b.stmt(b.call('$.set_checked', id, expression)));
439
440
  } else {
440
441
  state.init.push(b.stmt(b.call('$.set_checked', id, expression)));
441
442
  }
@@ -447,7 +448,7 @@ const visitors = {
447
448
  const expression = visit(attr.value, state);
448
449
 
449
450
  if (name === '$selected') {
450
- state.update.push(b.stmt(b.call('$.set_selected', id, expression)));
451
+ local_updates.push(b.stmt(b.call('$.set_selected', id, expression)));
451
452
  } else {
452
453
  state.init.push(b.stmt(b.call('$.set_selected', id, expression)));
453
454
  }
@@ -523,9 +524,9 @@ const visitors = {
523
524
  const expression = visit(attr.value, state);
524
525
 
525
526
  if (is_dom_property(attribute)) {
526
- state.update.push(b.stmt(b.assignment('=', b.member(id, attribute), expression)));
527
+ local_updates.push(b.stmt(b.assignment('=', b.member(id, attribute), expression)));
527
528
  } else {
528
- state.update.push(
529
+ local_updates.push(
529
530
  b.stmt(b.call('$.set_attribute', id, b.literal(attribute), expression)),
530
531
  );
531
532
  }
@@ -566,7 +567,7 @@ const visitors = {
566
567
  }
567
568
 
568
569
  if (class_attribute.name.name === '$class') {
569
- state.update.push(b.stmt(b.call('$.set_class', id, expression)));
570
+ local_updates.push(b.stmt(b.call('$.set_class', id, expression)));
570
571
  } else {
571
572
  state.init.push(b.stmt(b.call('$.set_class', id, expression)));
572
573
  }
@@ -591,6 +592,8 @@ const visitors = {
591
592
 
592
593
  transform_children(node.children, { visit, state: { ...state, init, update }, root: false });
593
594
 
595
+ update.push(...local_updates);
596
+
594
597
  if (init.length > 0) {
595
598
  state.init.push(b.block(init));
596
599
  }
@@ -1581,7 +1584,7 @@ function transform_children(children, { visit, state, root }) {
1581
1584
  state.update.push(b.stmt(b.call('$.set_text', id, expression)));
1582
1585
  } else if (normalized.length === 1) {
1583
1586
  if (expression.type === 'Literal') {
1584
- state.template.push(expression.value);
1587
+ state.template.push(escape_html(expression.value));
1585
1588
  } else {
1586
1589
  const id = state.flush_node();
1587
1590
  state.init.push(
@@ -570,7 +570,7 @@ export function build_assignment(operator, left, right, context) {
570
570
  const ATTR_REGEX = /[&"<]/g;
571
571
  const CONTENT_REGEX = /[&<]/g;
572
572
 
573
- export function escape_html(value, is_attr) {
573
+ export function escape_html(value, is_attr = false) {
574
574
  const str = String(value ?? '');
575
575
 
576
576
  const pattern = is_attr ? ATTR_REGEX : CONTENT_REGEX;
@@ -43,6 +43,7 @@ export {
43
43
  untrack,
44
44
  use_prop,
45
45
  fallback,
46
+ exclude_from_object,
46
47
  } from './runtime.js';
47
48
 
48
49
  export { for_block as for } from './for.js';
@@ -1182,3 +1182,17 @@ export function use_prop() {
1182
1182
  export function fallback(value, fallback) {
1183
1183
  return value === undefined ? fallback : value;
1184
1184
  }
1185
+
1186
+ /**
1187
+ * @param {Record<string, unknown>} obj
1188
+ * @param {string[]} keys
1189
+ * @returns {Record<string, unknown>}
1190
+ */
1191
+ export function exclude_from_object(obj, keys) {
1192
+ obj = { ...obj };
1193
+ let key;
1194
+ for (key of keys) {
1195
+ delete obj[key];
1196
+ }
1197
+ return obj;
1198
+ }
@@ -811,4 +811,25 @@ describe('basic', () => {
811
811
  flushSync();
812
812
  expect(greetingP.textContent).toBe('Hello, User!');
813
813
  });
814
+
815
+ it('renders with reactive attributes with nested reactive attributes', () => {
816
+ component App() {
817
+ let $value = 'parent-class';
818
+
819
+ <p $class={$value}>{'Colored parent value'}</p>
820
+
821
+ <div>
822
+ let $nested = 'nested-class';
823
+
824
+ <p $class={$nested}>{'Colored nested value'}</p>
825
+ </div>
826
+ }
827
+
828
+ render(App);
829
+
830
+ const paragraphs = container.querySelectorAll('p');
831
+
832
+ expect(paragraphs[0].className).toBe('parent-class');
833
+ expect(paragraphs[1].className).toBe('nested-class');
834
+ });
814
835
  });