ripple 0.2.149 → 0.2.151

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.149",
6
+ "version": "0.2.151",
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.149"
84
+ "ripple": "0.2.151"
85
85
  }
86
86
  }
@@ -32,9 +32,11 @@ export function jsxs(
32
32
  */
33
33
  export function Fragment(props: { children?: any }): void;
34
34
 
35
+ export type ClassValue = string | import('clsx').ClassArray | import('clsx').ClassDictionary;
36
+
35
37
  // Base HTML attributes
36
38
  interface HTMLAttributes {
37
- class?: string;
39
+ class?: ClassValue | undefined | null;
38
40
  className?: string;
39
41
  id?: string;
40
42
  style?: string | Record<string, string | number>;
@@ -1,12 +1,13 @@
1
- /** @import { Block, Component } from '#client' */
1
+ /** @import { Block } from '#client' */
2
2
 
3
3
  import { branch, destroy_block, render, render_spread } from './blocks.js';
4
4
  import { COMPOSITE_BLOCK, NAMESPACE_URI, DEFAULT_NAMESPACE } from './constants.js';
5
- import { active_block, active_namespace } from './runtime.js';
5
+ import { active_block, active_namespace, with_ns } from './runtime.js';
6
+ import { top_element_to_ns } from './utils.js';
6
7
 
7
8
  /**
8
9
  * @typedef {((anchor: Node, props: Record<string, any>, block: Block | null) => void)} ComponentFunction
9
- * @param {() => ComponentFunction | keyof HTMLElementTagNameMap} get_component
10
+ * @param {() => ComponentFunction | keyof HTMLElementTagNameMap | keyof SVGElementTagNameMap | keyof MathMLElementTagNameMap} get_component
10
11
  * @param {Node} node
11
12
  * @param {Record<string, any>} props
12
13
  * @returns {void}
@@ -33,7 +34,7 @@ export function composite(get_component, node, props) {
33
34
  });
34
35
  } else {
35
36
  // Custom element
36
- b = branch(() => {
37
+ var run = () => {
37
38
  var block = /** @type {Block} */ (active_block);
38
39
 
39
40
  var element =
@@ -61,7 +62,16 @@ export function composite(get_component, node, props) {
61
62
 
62
63
  props?.children?.(child_anchor, {}, block);
63
64
  }
64
- });
65
+ };
66
+
67
+ const ns = top_element_to_ns(component, active_namespace);
68
+
69
+ if (ns !== active_namespace) {
70
+ // support top-level dynamic element svg/math <@tag />
71
+ b = branch(() => with_ns(ns, run));
72
+ } else {
73
+ b = branch(run);
74
+ }
65
75
  }
66
76
  },
67
77
  null,
@@ -50,7 +50,7 @@ export let active_reaction = null;
50
50
  export let active_scope = null;
51
51
  /** @type {null | Component} */
52
52
  export let active_component = null;
53
- /** @type {keyof NAMESPACE_URI} */
53
+ /** @type {keyof typeof NAMESPACE_URI} */
54
54
  export let active_namespace = DEFAULT_NAMESPACE;
55
55
  /** @type {boolean} */
56
56
  export let is_mutating_allowed = true;
@@ -1170,7 +1170,7 @@ export function pop_component() {
1170
1170
  /**
1171
1171
  * @template T
1172
1172
  * @param {() => T} fn
1173
- * @param {keyof NAMESPACE_URI} namespace
1173
+ * @param {keyof typeof NAMESPACE_URI} namespace
1174
1174
  * @returns {T}
1175
1175
  */
1176
1176
  export function with_ns(namespace, fn) {
@@ -1,3 +1,5 @@
1
+ /** @import { NAMESPACE_URI } from './constants.js' */
2
+
1
3
  /** @type {typeof Object.getOwnPropertyDescriptor} */
2
4
  export var get_descriptor = Object.getOwnPropertyDescriptor;
3
5
  /** @type {typeof Object.getOwnPropertyDescriptors} */
@@ -43,3 +45,19 @@ export function create_anchor() {
43
45
  export function is_tracked_object(v) {
44
46
  return typeof v === 'object' && v !== null && typeof (/** @type {any} */ (v).f) === 'number';
45
47
  }
48
+
49
+ /**
50
+ * Converts a tag name to its corresponding namespace.
51
+ * @param {keyof SVGElementTagNameMap | keyof MathMLElementTagNameMap | keyof HTMLElementTagNameMap} element
52
+ * @param {keyof typeof NAMESPACE_URI} current_namespace
53
+ * @returns {keyof typeof NAMESPACE_URI}
54
+ */
55
+ export function top_element_to_ns(element, current_namespace) {
56
+ if (element === 'svg') {
57
+ return 'svg';
58
+ } else if (element === 'math') {
59
+ return 'mathml';
60
+ } else {
61
+ return current_namespace;
62
+ }
63
+ }
@@ -331,6 +331,35 @@ describe('SVG namespace handling', () => {
331
331
  expect(polygon.namespaceURI).toBe('http://www.w3.org/2000/svg');
332
332
  });
333
333
 
334
+ it('should render SVG as a dynamic top element with any dynamic children elements', () => {
335
+ component SVG({ children }) {
336
+ const tag = track('svg');
337
+ <@tag width={100} height={50} fill="red" viewBox="0 0 30 10" preserveAspectRatio="none">
338
+ <children />
339
+ </@tag>
340
+ }
341
+
342
+ component Polygon({ points }) {
343
+ let dynTag = track('polygon');
344
+ <@dynTag {points} />
345
+ }
346
+
347
+ component App() {
348
+ let Component = track(() => Polygon);
349
+ <SVG>
350
+ <@Component points="0,0 30,0 15,10" />
351
+ </SVG>
352
+ }
353
+
354
+ render(App);
355
+
356
+ const svg = container.querySelector('svg');
357
+ const polygon = container.querySelector('polygon');
358
+
359
+ expect(svg.namespaceURI).toBe('http://www.w3.org/2000/svg');
360
+ expect(polygon.namespaceURI).toBe('http://www.w3.org/2000/svg');
361
+ });
362
+
334
363
  it('should compare static vs dynamic SVG rendering (original problem case)', () => {
335
364
  component App() {
336
365
  const d = [