From 8dc935d0b86eaf8bf9cf3c01af8e72681d076f3b Mon Sep 17 00:00:00 2001
From: zubiden <19638254+zubiden@users.noreply.github.com>
Date: Tue, 21 Jan 2025 18:21:10 +0100
Subject: [PATCH] SVG: Allow setting filters using JSX (#5449)
---
src/components/main/Main.tsx | 2 +
src/components/main/SvgController.tsx | 45 +++++
.../visualEffects/SnapEffectContainer.tsx | 171 +++++++-----------
.../main/visualEffects/WaveContainer.tsx | 37 ++--
src/config.ts | 2 +
.../{useColorFilter.ts => useColorFilter.tsx} | 26 ++-
src/lib/teact/teact-dom.ts | 58 +++---
src/util/element/jsxToHtml.ts | 12 ++
src/util/svgController.ts | 32 ----
9 files changed, 184 insertions(+), 201 deletions(-)
create mode 100644 src/components/main/SvgController.tsx
rename src/hooks/stickers/{useColorFilter.ts => useColorFilter.tsx} (68%)
create mode 100644 src/util/element/jsxToHtml.ts
delete mode 100644 src/util/svgController.ts
diff --git a/src/components/main/Main.tsx b/src/components/main/Main.tsx
index b212045e6..4ac2680fa 100644
--- a/src/components/main/Main.tsx
+++ b/src/components/main/Main.tsx
@@ -86,6 +86,7 @@ import GiveawayModal from './premium/GiveawayModal.async';
import PremiumMainModal from './premium/PremiumMainModal.async';
import StarsGiftingPickerModal from './premium/StarsGiftingPickerModal.async';
import SafeLinkModal from './SafeLinkModal.async';
+import SvgController from './SvgController';
import ConfettiContainer from './visualEffects/ConfettiContainer';
import SnapEffectContainer from './visualEffects/SnapEffectContainer';
import WaveContainer from './visualEffects/WaveContainer';
@@ -587,6 +588,7 @@ const Main = ({
+
);
};
diff --git a/src/components/main/SvgController.tsx b/src/components/main/SvgController.tsx
new file mode 100644
index 000000000..725c77aa6
--- /dev/null
+++ b/src/components/main/SvgController.tsx
@@ -0,0 +1,45 @@
+import React, { memo, useEffect } from '../../lib/teact/teact';
+
+import { createCallbackManager } from '../../util/callbacks';
+import generateUniqueId from '../../util/generateUniqueId';
+
+import useForceUpdate from '../../hooks/useForceUpdate';
+
+import Portal from '../ui/Portal';
+
+const DEFINITION_MAP = new Map();
+const CALLBACK_MANAGER = createCallbackManager();
+
+const SvgController = () => {
+ const forceUpdate = useForceUpdate();
+
+ useEffect(() => {
+ return CALLBACK_MANAGER.addCallback(forceUpdate);
+ }, []);
+
+ return (
+
+
+
+ );
+};
+
+export default memo(SvgController);
+
+export function addSvgDefinition(element: React.ReactElement, id?: string) {
+ id ??= generateUniqueId();
+ element.props.id = id;
+
+ DEFINITION_MAP.set(element.props.id, element);
+ CALLBACK_MANAGER.runCallbacks();
+ return id;
+}
+
+export function removeSvgDefinition(id: string) {
+ DEFINITION_MAP.delete(id);
+ CALLBACK_MANAGER.runCallbacks();
+}
diff --git a/src/components/main/visualEffects/SnapEffectContainer.tsx b/src/components/main/visualEffects/SnapEffectContainer.tsx
index a960f9a49..4a811f611 100644
--- a/src/components/main/visualEffects/SnapEffectContainer.tsx
+++ b/src/components/main/visualEffects/SnapEffectContainer.tsx
@@ -1,10 +1,10 @@
import React, { memo } from '../../../lib/teact/teact';
import { getGlobal } from '../../../global';
-import { SNAP_EFFECT_CONTAINER_ID, SNAP_EFFECT_ID } from '../../../config';
+import { SNAP_EFFECT_CONTAINER_ID, SNAP_EFFECT_ID, SVG_NAMESPACE } from '../../../config';
import { selectCanAnimateSnapEffect } from '../../../global/selectors';
+import jsxToHtml from '../../../util/element/jsxToHtml';
import generateUniqueId from '../../../util/generateUniqueId';
-import { SVG_NAMESPACE } from '../../../util/svgController';
import styles from './SnapEffectContainer.module.scss';
@@ -39,29 +39,25 @@ export function animateSnap(element: HTMLElement) {
const seed = Math.floor(Date.now() / 1000);
const filterId = `${SNAP_EFFECT_ID}-${generateUniqueId()}`;
- const svg = document.createElementNS(SVG_NAMESPACE, 'svg');
- svg.setAttribute('class', styles.ghost);
- svg.setAttribute('width', `${width}px`);
- svg.setAttribute('height', `${height}px`);
- svg.setAttribute('style', `left: ${x}px; top: ${y}px;`);
- svg.setAttribute('viewBox', `0 0 ${width} ${height}`);
+ const component = (
+
+ );
- const defs = document.createElementNS(SVG_NAMESPACE, 'defs');
- svg.appendChild(defs);
-
- const filter = createFilter(Math.min(width, height), seed);
- filter.setAttribute('id', filterId);
- defs.appendChild(filter);
-
- const g = document.createElementNS(SVG_NAMESPACE, 'g');
- g.setAttribute('filter', `url(#${filterId})`);
- svg.appendChild(g);
-
- const foreignObject = document.createElementNS(SVG_NAMESPACE, 'foreignObject');
- foreignObject.setAttribute('class', styles.elementContainer);
- foreignObject.setAttribute('width', `${width}px`);
- foreignObject.setAttribute('height', `${height}px`);
- g.appendChild(foreignObject);
+ const svg = jsxToHtml(component)[0] as HTMLElement;
+ const foreignObject = svg.querySelector('foreignObject')!;
const computedStyle = window.getComputedStyle(element);
const clone = element.cloneNode(true) as HTMLElement;
@@ -83,90 +79,45 @@ export function animateSnap(element: HTMLElement) {
return true;
}
-function createFilter(smallestSide: number, baseSeed: number = 42) {
- const filter = document.createElementNS(SVG_NAMESPACE, 'filter');
- filter.setAttribute('x', '-150%');
- filter.setAttribute('y', '-150%');
- filter.setAttribute('width', '400%');
- filter.setAttribute('height', '400%');
- filter.setAttribute('color-interpolation-filters', 'sRGB');
-
- const feTurbulence = document.createElementNS(SVG_NAMESPACE, 'feTurbulence');
- feTurbulence.setAttribute('type', 'fractalNoise');
- feTurbulence.setAttribute('baseFrequency', '0.5');
- feTurbulence.setAttribute('numOctaves', '1');
- feTurbulence.setAttribute('result', 'dustNoise');
- feTurbulence.setAttribute('seed', baseSeed.toString());
- filter.appendChild(feTurbulence);
-
- const feComponentTransfer = document.createElementNS(SVG_NAMESPACE, 'feComponentTransfer');
- feComponentTransfer.setAttribute('in', 'dustNoise');
- feComponentTransfer.setAttribute('result', 'dustNoiseMask');
- filter.appendChild(feComponentTransfer);
-
- const feFuncA = document.createElementNS(SVG_NAMESPACE, 'feFuncA');
- feFuncA.setAttribute('type', 'linear');
- feFuncA.setAttribute('slope', '5');
- feFuncA.setAttribute('intercept', '0');
- feComponentTransfer.appendChild(feFuncA);
-
- const feFuncAAnimate = document.createElementNS(SVG_NAMESPACE, 'animate');
- feFuncAAnimate.setAttribute('attributeName', 'slope');
- feFuncAAnimate.setAttribute('values', '5; 2; 1; 0');
- feFuncAAnimate.setAttribute('dur', `${DURATION}ms`);
- feFuncAAnimate.setAttribute('fill', 'freeze');
- feFuncA.appendChild(feFuncAAnimate);
-
- const feComposite = document.createElementNS(SVG_NAMESPACE, 'feComposite');
- feComposite.setAttribute('in', 'SourceGraphic');
- feComposite.setAttribute('in2', 'dustNoiseMask');
- feComposite.setAttribute('operator', 'in');
- feComposite.setAttribute('result', 'dustySource');
- filter.appendChild(feComposite);
-
- const feTurbulence2 = document.createElementNS(SVG_NAMESPACE, 'feTurbulence');
- feTurbulence2.setAttribute('type', 'fractalNoise');
- feTurbulence2.setAttribute('baseFrequency', '0.015');
- feTurbulence2.setAttribute('numOctaves', '1');
- feTurbulence2.setAttribute('result', 'displacementNoice1');
- feTurbulence2.setAttribute('seed', (baseSeed + 1).toString());
- filter.appendChild(feTurbulence2);
-
- const feTurbulence3 = document.createElementNS(SVG_NAMESPACE, 'feTurbulence');
- feTurbulence3.setAttribute('type', 'fractalNoise');
- feTurbulence3.setAttribute('baseFrequency', '1');
- feTurbulence3.setAttribute('numOctaves', '2');
- feTurbulence3.setAttribute('result', 'displacementNoice2');
- feTurbulence3.setAttribute('seed', (baseSeed + 2).toString());
- filter.appendChild(feTurbulence3);
-
- const feMerge = document.createElementNS(SVG_NAMESPACE, 'feMerge');
- feMerge.setAttribute('result', 'combinedNoise');
- filter.appendChild(feMerge);
-
- const feMergeNode1 = document.createElementNS(SVG_NAMESPACE, 'feMergeNode');
- feMergeNode1.setAttribute('in', 'displacementNoice1');
- feMerge.appendChild(feMergeNode1);
-
- const feMergeNode2 = document.createElementNS(SVG_NAMESPACE, 'feMergeNode');
- feMergeNode2.setAttribute('in', 'displacementNoice2');
- feMerge.appendChild(feMergeNode2);
-
- const feDisplacementMap = document.createElementNS(SVG_NAMESPACE, 'feDisplacementMap');
- feDisplacementMap.setAttribute('in', 'dustySource');
- feDisplacementMap.setAttribute('in2', 'combinedNoise');
- feDisplacementMap.setAttribute('scale', '0');
-
- feDisplacementMap.setAttribute('xChannelSelector', 'R');
- feDisplacementMap.setAttribute('yChannelSelector', 'G');
- filter.appendChild(feDisplacementMap);
-
- const feDisplacementMapAnimate = document.createElementNS(SVG_NAMESPACE, 'animate');
- feDisplacementMapAnimate.setAttribute('attributeName', 'scale');
- feDisplacementMapAnimate.setAttribute('values', `0; ${smallestSide * 3}`);
- feDisplacementMapAnimate.setAttribute('dur', `${DURATION}ms`);
- feDisplacementMapAnimate.setAttribute('fill', 'freeze');
- feDisplacementMap.appendChild(feDisplacementMapAnimate);
-
- return filter;
+function createFilter(id: string, smallestSide: number, baseSeed: number = 42) {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
}
diff --git a/src/components/main/visualEffects/WaveContainer.tsx b/src/components/main/visualEffects/WaveContainer.tsx
index 469f9254d..15623c1ac 100644
--- a/src/components/main/visualEffects/WaveContainer.tsx
+++ b/src/components/main/visualEffects/WaveContainer.tsx
@@ -5,14 +5,16 @@ import { withGlobal } from '../../../global';
import type { TabState } from '../../../global/types';
+import { SVG_NAMESPACE } from '../../../config';
import { selectTabState } from '../../../global/selectors';
import buildClassName from '../../../util/buildClassName';
import buildStyle from '../../../util/buildStyle';
-import { addSvgDefinition, removeSvgDefinition, SVG_NAMESPACE } from '../../../util/svgController';
import windowSize from '../../../util/windowSize';
import useLastCallback from '../../../hooks/useLastCallback';
+import { addSvgDefinition, removeSvgDefinition } from '../SvgController';
+
import styles from './WaveContainer.module.scss';
import waveRipple from '../../../assets/wave_ripple.svg';
@@ -62,26 +64,19 @@ const WaveContainer = ({ waveInfo }: StateProps) => {
}, [waveInfo]);
useEffect(() => {
- const filter = document.createElementNS(SVG_NAMESPACE, 'filter');
- filter.setAttribute('x', '0');
- filter.setAttribute('y', '0');
- filter.setAttribute('width', '1');
- filter.setAttribute('height', '1');
- filter.setAttribute('color-interpolation-filters', 'sRGB');
- addSvgDefinition(filter, FILTER_ID);
-
- const feImage = document.createElementNS(SVG_NAMESPACE, 'feImage');
- feImage.setAttribute('href', waveRipple);
- feImage.setAttribute('result', 'waveImage');
- filter.appendChild(feImage);
-
- const feDisplacementMap = document.createElementNS(SVG_NAMESPACE, 'feDisplacementMap');
- feDisplacementMap.setAttribute('in', 'SourceGraphic');
- feDisplacementMap.setAttribute('in2', 'waveImage');
- feDisplacementMap.setAttribute('scale', FILTER_SCALE);
- feDisplacementMap.setAttribute('xChannelSelector', 'R');
- feDisplacementMap.setAttribute('yChannelSelector', 'B');
- filter.appendChild(feDisplacementMap);
+ addSvgDefinition(
+
+
+
+ ,
+ FILTER_ID,
+ );
return () => {
removeSvgDefinition(FILTER_ID);
diff --git a/src/config.ts b/src/config.ts
index 306a54007..6bb314f42 100644
--- a/src/config.ts
+++ b/src/config.ts
@@ -227,6 +227,8 @@ export const SLIDE_TRANSITION_DURATION = 450;
export const BIRTHDAY_NUMBERS_SET = 'FestiveFontEmoji';
export const RESTRICTED_EMOJI_SET = 'RestrictedEmoji';
+export const SVG_NAMESPACE = 'http://www.w3.org/2000/svg';
+
export const VIDEO_WEBM_TYPE = 'video/webm';
export const GIF_MIME_TYPE = 'image/gif';
diff --git a/src/hooks/stickers/useColorFilter.ts b/src/hooks/stickers/useColorFilter.tsx
similarity index 68%
rename from src/hooks/stickers/useColorFilter.ts
rename to src/hooks/stickers/useColorFilter.tsx
index 34e0901ff..e4ec3075e 100644
--- a/src/hooks/stickers/useColorFilter.ts
+++ b/src/hooks/stickers/useColorFilter.tsx
@@ -1,8 +1,10 @@
-import { useEffect } from '../../lib/teact/teact';
+import React, { useEffect } from '../../lib/teact/teact';
-import { addSvgDefinition, removeSvgDefinition, SVG_NAMESPACE } from '../../util/svgController';
+import { SVG_NAMESPACE } from '../../config';
import { hexToRgb } from '../../util/switchTheme';
+import { addSvgDefinition, removeSvgDefinition } from '../../components/main/SvgController';
+
const SVG_MAP = new Map();
class SvgColorFilter {
@@ -13,20 +15,16 @@ class SvgColorFilter {
constructor(public color: string) {
this.filterId = `color-filter-${color.slice(1)}`;
- const filter = document.createElementNS(SVG_NAMESPACE, 'filter');
- filter.setAttribute('color-interpolation-filters', 'sRGB');
- addSvgDefinition(filter, this.filterId);
-
- const feColorMatrix = document.createElementNS(SVG_NAMESPACE, 'feColorMatrix');
- feColorMatrix.setAttribute('type', 'matrix');
-
const rgbColor = hexToRgb(color);
- feColorMatrix.setAttribute(
- 'values',
- `0 0 0 0 ${rgbColor.r / 255} 0 0 0 0 ${rgbColor.g / 255} 0 0 0 0 ${rgbColor.b / 255} 0 0 0 1 0`,
+ addSvgDefinition(
+
+
+ ,
+ this.filterId,
);
-
- filter.appendChild(feColorMatrix);
}
public getFilterId() {
diff --git a/src/lib/teact/teact-dom.ts b/src/lib/teact/teact-dom.ts
index 33e1a08e3..ddd69aa4b 100644
--- a/src/lib/teact/teact-dom.ts
+++ b/src/lib/teact/teact-dom.ts
@@ -39,6 +39,9 @@ type CurrentContext = Record>;
type DOMElement = HTMLElement | SVGElement;
+const SVG_NAMESPACE = 'http://www.w3.org/2000/svg';
+const HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';
+
const FILTERED_ATTRIBUTES = new Set(['key', 'ref', 'teactFastList', 'teactOrderKey']);
const HTML_ATTRIBUTES = new Set(['dir', 'role', 'form']);
const CONTROLLABLE_TAGS = ['INPUT', 'TEXTAREA', 'SELECT'];
@@ -89,11 +92,11 @@ function renderWithVirtual(
nextSibling?: ChildNode;
forceMoveToEnd?: boolean;
fragment?: DocumentFragment;
- isSvg?: true;
+ namespace?: string;
} = {},
): T {
const { skipComponentUpdate, fragment } = options;
- let { nextSibling, isSvg } = options;
+ let { nextSibling, namespace } = options;
const isCurrentComponent = $current?.type === VirtualType.Component;
const isNewComponent = $new?.type === VirtualType.Component;
@@ -102,8 +105,9 @@ function renderWithVirtual(
const isCurrentFragment = !isCurrentComponent && $current?.type === VirtualType.Fragment;
const isNewFragment = !isNewComponent && $new?.type === VirtualType.Fragment;
- if (!isSvg && $new?.type === VirtualType.Tag && $new.tag === 'svg') {
- isSvg = true;
+ if ($new?.type === VirtualType.Tag) {
+ if ($new.tag === 'svg') namespace = SVG_NAMESPACE;
+ if ($new.props.xmlns) namespace = $new.props.xmlns;
}
if (
@@ -144,7 +148,7 @@ function renderWithVirtual(
}
mountChildren(parentEl, $new as VirtualElementComponent | VirtualElementFragment, currentContext, {
- nextSibling, fragment, isSvg,
+ nextSibling, fragment, namespace,
});
} else {
const canSetTextContent = !fragment
@@ -157,7 +161,7 @@ function renderWithVirtual(
parentEl.textContent = $newAsReal.value;
$newAsReal.target = parentEl.firstChild!;
} else {
- const node = createNode($newAsReal, currentContext, isSvg);
+ const node = createNode($newAsReal, currentContext, namespace);
$newAsReal.target = node;
insertBefore(fragment || parentEl, node, nextSibling);
@@ -184,10 +188,10 @@ function renderWithVirtual(
remount(parentEl, $current, currentContext, undefined);
mountChildren(parentEl, $new as VirtualElementComponent | VirtualElementFragment, currentContext, {
- nextSibling, fragment, isSvg,
+ nextSibling, fragment, namespace,
});
} else {
- const node = createNode($newAsReal, currentContext, isSvg);
+ const node = createNode($newAsReal, currentContext, namespace);
$newAsReal.target = node;
remount(parentEl, $current, currentContext, node, nextSibling);
@@ -226,8 +230,10 @@ function renderWithVirtual(
insertBefore(parentEl, currentTarget, nextSibling);
}
- updateAttributes($current, $newAsTag, currentTarget as DOMElement, isSvg);
- renderChildren($current, $newAsTag, currentContext, currentTarget as DOMElement, undefined, undefined, isSvg);
+ updateAttributes($current, $newAsTag, currentTarget as DOMElement, namespace);
+ renderChildren(
+ $current, $newAsTag, currentContext, currentTarget as DOMElement, undefined, undefined, namespace,
+ );
}
}
}
@@ -290,7 +296,7 @@ function mountChildren(
options: {
nextSibling?: ChildNode;
fragment?: DocumentFragment;
- isSvg?: true;
+ namespace?: string;
},
) {
const { children } = $element;
@@ -311,7 +317,7 @@ function unmountChildren(
}
}
-function createNode($element: VirtualElementReal, currentContext: CurrentContext, isSvg?: true): Node {
+function createNode($element: VirtualElementReal, currentContext: CurrentContext, namespace = HTML_NAMESPACE): Node {
if ($element.type === VirtualType.Empty) {
return document.createTextNode('');
}
@@ -321,7 +327,7 @@ function createNode($element: VirtualElementReal, currentContext: CurrentContext
}
const { tag, props, children } = $element;
- const element = isSvg ? document.createElementNS('http://www.w3.org/2000/svg', tag) : document.createElement(tag);
+ const element = document.createElementNS(namespace, tag) as DOMElement;
processControlled(tag, props);
@@ -330,7 +336,7 @@ function createNode($element: VirtualElementReal, currentContext: CurrentContext
if (!props.hasOwnProperty(key)) continue;
if (props[key] !== undefined) {
- setAttribute(element, key, props[key], isSvg);
+ setAttribute(element, key, props[key], namespace);
}
}
@@ -338,7 +344,7 @@ function createNode($element: VirtualElementReal, currentContext: CurrentContext
for (let i = 0, l = children.length; i < l; i++) {
const $child = children[i];
- const $renderedChild = renderWithVirtual(element, undefined, $child, $element, currentContext, i, { isSvg });
+ const $renderedChild = renderWithVirtual(element, undefined, $child, $element, currentContext, i, { namespace });
if ($renderedChild !== $child) {
children[i] = $renderedChild;
}
@@ -424,7 +430,7 @@ function renderChildren(
currentEl: DOMElement,
nextSibling?: ChildNode,
forceMoveToEnd = false,
- isSvg?: true,
+ namespace?: string,
) {
if (DEBUG) {
DEBUG_checkKeyUniqueness($new.children);
@@ -456,7 +462,7 @@ function renderChildren(
$new,
currentContext,
i,
- i >= currentChildrenLength ? { fragment, isSvg } : { nextSibling, forceMoveToEnd, isSvg },
+ i >= currentChildrenLength ? { fragment, namespace } : { nextSibling, forceMoveToEnd, namespace },
);
if ($renderedChild && $renderedChild !== newChildren[i]) {
@@ -691,7 +697,9 @@ function processUncontrolledOnMount(element: DOMElement, props: AnyLiteral) {
}
}
-function updateAttributes($current: VirtualElementTag, $new: VirtualElementTag, element: DOMElement, isSvg?: true) {
+function updateAttributes(
+ $current: VirtualElementTag, $new: VirtualElementTag, element: DOMElement, namespace?: string,
+) {
processControlled(element.tagName, $new.props);
const currentEntries = Object.entries($current.props);
@@ -715,14 +723,14 @@ function updateAttributes($current: VirtualElementTag, $new: VirtualElementTag,
const currentValue = $current.props[key];
if (newValue !== undefined && newValue !== currentValue) {
- setAttribute(element, key, newValue, isSvg);
+ setAttribute(element, key, newValue, namespace);
}
}
}
-function setAttribute(element: DOMElement, key: string, value: any, isSvg?: true) {
+function setAttribute(element: DOMElement, key: string, value: any, namespace?: string) {
if (key === 'className') {
- updateClassName(element, value, isSvg);
+ updateClassName(element, value, namespace);
} else if (key === 'value') {
const inputEl = element as HTMLInputElement;
@@ -749,7 +757,9 @@ function setAttribute(element: DOMElement, key: string, value: any, isSvg?: true
element.innerHTML = value.__html;
} else if (key.startsWith('on')) {
addEventListener(element, key, value, key.endsWith('Capture'));
- } else if (isSvg || key.startsWith('data-') || key.startsWith('aria-') || HTML_ATTRIBUTES.has(key)) {
+ } else if (
+ namespace === SVG_NAMESPACE || key.startsWith('data-') || key.startsWith('aria-') || HTML_ATTRIBUTES.has(key)
+ ) {
element.setAttribute(key, value);
} else if (!FILTERED_ATTRIBUTES.has(key)) {
(element as any)[MAPPED_ATTRIBUTES[key] || key] = value;
@@ -772,8 +782,8 @@ function removeAttribute(element: DOMElement, key: string, value: any) {
}
}
-function updateClassName(element: DOMElement, value: string, isSvg?: true) {
- if (isSvg) {
+function updateClassName(element: DOMElement, value: string, namespace?: string) {
+ if (namespace === SVG_NAMESPACE) {
element.setAttribute('class', value);
return;
}
diff --git a/src/util/element/jsxToHtml.ts b/src/util/element/jsxToHtml.ts
new file mode 100644
index 000000000..c5d05146f
--- /dev/null
+++ b/src/util/element/jsxToHtml.ts
@@ -0,0 +1,12 @@
+import type { VirtualElement } from '../../lib/teact/teact';
+import TeactDOM from '../../lib/teact/teact-dom';
+
+export default function jsxToHtml(jsx: VirtualElement) {
+ const fragment = document.createElement('div');
+ TeactDOM.render(jsx, fragment);
+
+ const children = Array.from(fragment.children);
+ TeactDOM.render(undefined, fragment);
+
+ return children;
+}
diff --git a/src/util/svgController.ts b/src/util/svgController.ts
deleted file mode 100644
index 927f6b28d..000000000
--- a/src/util/svgController.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-import generateUniqueId from './generateUniqueId';
-
-export const SVG_NAMESPACE = 'http://www.w3.org/2000/svg';
-
-const CONTAINER = document.createElementNS(SVG_NAMESPACE, 'svg');
-CONTAINER.setAttribute('width', '0');
-CONTAINER.setAttribute('height', '0');
-CONTAINER.setAttribute('viewBox', '0 0 1 1');
-CONTAINER.classList.add('svg-definitions');
-document.body.appendChild(CONTAINER);
-
-const DEFS = document.createElementNS(SVG_NAMESPACE, 'defs');
-CONTAINER.appendChild(DEFS);
-
-const DEFINITION_MAP = new Map();
-
-export function addSvgDefinition(element: SVGElement, id?: string) {
- id ??= generateUniqueId();
- element.id = id;
-
- DEFS.appendChild(element);
- DEFINITION_MAP.set(element.id, element);
- return id;
-}
-
-export function removeSvgDefinition(id: string) {
- const element = DEFINITION_MAP.get(id);
- if (element) {
- element.remove();
- DEFINITION_MAP.delete(id);
- }
-}