@twick/visualizer 0.14.0 → 0.14.3

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.
Files changed (68) hide show
  1. package/.eslintrc.json +20 -20
  2. package/README.md +113 -13
  3. package/package.json +14 -14
  4. package/package.json.bak +34 -0
  5. package/src/animations/blur.tsx +96 -60
  6. package/src/animations/breathe.tsx +95 -60
  7. package/src/animations/fade.tsx +173 -60
  8. package/src/animations/index.ts +12 -0
  9. package/src/animations/photo-rise.tsx +103 -66
  10. package/src/animations/photo-zoom.tsx +109 -73
  11. package/src/animations/rise.tsx +157 -118
  12. package/src/animations/succession.tsx +112 -77
  13. package/src/components/frame-effects.tsx +188 -188
  14. package/src/components/track.tsx +237 -232
  15. package/src/controllers/animation.controller.ts +38 -38
  16. package/src/controllers/element.controller.ts +42 -42
  17. package/src/controllers/frame-effect.controller.tsx +29 -29
  18. package/src/controllers/text-effect.controller.ts +32 -32
  19. package/src/elements/audio.element.tsx +79 -17
  20. package/src/elements/caption.element.tsx +169 -87
  21. package/src/elements/circle.element.tsx +88 -20
  22. package/src/elements/icon.element.tsx +88 -20
  23. package/src/elements/image.element.tsx +134 -55
  24. package/src/elements/index.ts +14 -0
  25. package/src/elements/rect.element.tsx +92 -22
  26. package/src/elements/scene.element.tsx +97 -29
  27. package/src/elements/text.element.tsx +101 -27
  28. package/src/elements/video.element.tsx +274 -56
  29. package/src/frame-effects/circle.frame.tsx +168 -103
  30. package/src/frame-effects/index.ts +7 -0
  31. package/src/frame-effects/rect.frame.tsx +198 -103
  32. package/src/global.css +11 -11
  33. package/src/helpers/caption.utils.ts +29 -29
  34. package/src/helpers/constants.ts +162 -158
  35. package/src/helpers/element.utils.ts +331 -239
  36. package/src/helpers/event.utils.ts +21 -0
  37. package/src/helpers/filters.ts +127 -127
  38. package/src/helpers/log.utils.ts +55 -29
  39. package/src/helpers/timing.utils.ts +109 -109
  40. package/src/helpers/types.ts +361 -241
  41. package/src/helpers/utils.ts +36 -19
  42. package/src/index.ts +196 -6
  43. package/src/live.tsx +16 -16
  44. package/src/project.ts +6 -6
  45. package/src/sample.ts +247 -247
  46. package/src/text-effects/elastic.tsx +70 -39
  47. package/src/text-effects/erase.tsx +91 -58
  48. package/src/text-effects/index.ts +9 -0
  49. package/src/text-effects/stream-word.tsx +94 -60
  50. package/src/text-effects/typewriter.tsx +93 -59
  51. package/src/visualizer-grouped.ts +83 -0
  52. package/src/visualizer.tsx +182 -78
  53. package/tsconfig.json +11 -11
  54. package/typedoc.json +19 -14
  55. package/vite.config.ts +15 -15
  56. package/.turbo/turbo-build.log +0 -19
  57. package/.turbo/turbo-docs.log +0 -7
  58. package/LICENSE +0 -197
  59. package/dist/mp4.wasm +0 -0
  60. package/dist/project.css +0 -1
  61. package/dist/project.js +0 -145
  62. package/docs/.nojekyll +0 -1
  63. package/docs/README.md +0 -13
  64. package/docs/interfaces/Animation.md +0 -47
  65. package/docs/interfaces/Element.md +0 -47
  66. package/docs/interfaces/FrameEffectPlugin.md +0 -47
  67. package/docs/interfaces/TextEffect.md +0 -47
  68. package/docs/modules.md +0 -535
@@ -1,30 +1,30 @@
1
-
2
- import { CircleFrameEffect } from "../frame-effects/circle.frame";
3
- import { RectFrameEffect } from "../frame-effects/rect.frame";
4
- import { FrameEffectPlugin } from "../helpers/types";
5
-
6
- export class FrameEffectController {
7
- private frameEffects: Map<string, FrameEffectPlugin> = new Map();
8
-
9
- register(frameEffect: FrameEffectPlugin) {
10
- this.frameEffects.set(frameEffect.name, frameEffect);
11
- }
12
-
13
- get(name: string): FrameEffectPlugin | undefined {
14
- return this.frameEffects.get(name);
15
- }
16
-
17
- list(): string[] {
18
- return Array.from(this.frameEffects.keys());
19
- }
20
- }
21
-
22
- export const registerFrameEffects = () => {
23
- frameEffectController.register(CircleFrameEffect);
24
- frameEffectController.register(RectFrameEffect);
25
- }
26
-
27
- const frameEffectController = new FrameEffectController();
28
- registerFrameEffects();
29
-
1
+
2
+ import { CircleFrameEffect } from "../frame-effects/circle.frame";
3
+ import { RectFrameEffect } from "../frame-effects/rect.frame";
4
+ import { FrameEffectPlugin } from "../helpers/types";
5
+
6
+ export class FrameEffectController {
7
+ private frameEffects: Map<string, FrameEffectPlugin> = new Map();
8
+
9
+ register(frameEffect: FrameEffectPlugin) {
10
+ this.frameEffects.set(frameEffect.name, frameEffect);
11
+ }
12
+
13
+ get(name: string): FrameEffectPlugin | undefined {
14
+ return this.frameEffects.get(name);
15
+ }
16
+
17
+ list(): string[] {
18
+ return Array.from(this.frameEffects.keys());
19
+ }
20
+ }
21
+
22
+ export const registerFrameEffects = () => {
23
+ frameEffectController.register(CircleFrameEffect);
24
+ frameEffectController.register(RectFrameEffect);
25
+ }
26
+
27
+ const frameEffectController = new FrameEffectController();
28
+ registerFrameEffects();
29
+
30
30
  export default frameEffectController;
@@ -1,33 +1,33 @@
1
- import { ElasticEffect } from "../text-effects/elastic";
2
- import { EraseEffect } from "../text-effects/erase";
3
- import { StreamWordEffect } from "../text-effects/stream-word";
4
- import { TypewriterEffect } from "../text-effects/typewriter";
5
- import { TextEffect } from "../helpers/types";
6
-
7
- export class TextEffectController {
8
- private effects: Map<string, TextEffect> = new Map();
9
-
10
- register(effect: TextEffect) {
11
- this.effects.set(effect.name, effect);
12
- }
13
-
14
- get(name: string): TextEffect | undefined {
15
- return this.effects.get(name);
16
- }
17
-
18
- list(): string[] {
19
- return Array.from(this.effects.keys());
20
- }
21
- }
22
-
23
- export const registerTextEffects = () => {
24
- textEffectController.register(TypewriterEffect);
25
- textEffectController.register(StreamWordEffect);
26
- textEffectController.register(EraseEffect);
27
- textEffectController.register(ElasticEffect);
28
- }
29
-
30
- const textEffectController = new TextEffectController();
31
- registerTextEffects();
32
-
1
+ import { ElasticEffect } from "../text-effects/elastic";
2
+ import { EraseEffect } from "../text-effects/erase";
3
+ import { StreamWordEffect } from "../text-effects/stream-word";
4
+ import { TypewriterEffect } from "../text-effects/typewriter";
5
+ import { TextEffect } from "../helpers/types";
6
+
7
+ export class TextEffectController {
8
+ private effects: Map<string, TextEffect> = new Map();
9
+
10
+ register(effect: TextEffect) {
11
+ this.effects.set(effect.name, effect);
12
+ }
13
+
14
+ get(name: string): TextEffect | undefined {
15
+ return this.effects.get(name);
16
+ }
17
+
18
+ list(): string[] {
19
+ return Array.from(this.effects.keys());
20
+ }
21
+ }
22
+
23
+ export const registerTextEffects = () => {
24
+ textEffectController.register(TypewriterEffect);
25
+ textEffectController.register(StreamWordEffect);
26
+ textEffectController.register(EraseEffect);
27
+ textEffectController.register(ElasticEffect);
28
+ }
29
+
30
+ const textEffectController = new TextEffectController();
31
+ registerTextEffects();
32
+
33
33
  export default textEffectController;
@@ -1,17 +1,79 @@
1
- import { ElementParams } from "../helpers/types";
2
- import { createRef, waitFor } from "@twick/core";
3
- import { Audio } from "@twick/2d";
4
-
5
- export const AudioElement = {
6
- name: "audio",
7
- *create({ containerRef, element, view }: ElementParams) {
8
- const elementRef = createRef<any>();
9
- yield* waitFor(element?.s);
10
- yield containerRef().add(
11
- <Audio ref={elementRef} key={element.id} {...element.props} />
12
- );
13
- yield* waitFor(Math.max(0, element.e - element.s));
14
- yield elementRef().playing(false);
15
- yield elementRef().remove();
16
- },
17
- };
1
+ import { ElementParams } from "../helpers/types";
2
+ import { all, createRef, waitFor } from "@twick/core";
3
+ import { Audio } from "@twick/2d";
4
+ import { addAnimation } from "../helpers/element.utils";
5
+ import { logger } from "../helpers/log.utils";
6
+
7
+ /**
8
+ * @group AudioElement
9
+ * AudioElement creates and manages audio content in the visualizer scene.
10
+ * Handles audio playback, timing, and synchronization for background music,
11
+ * sound effects, and audio narration in video presentations.
12
+ *
13
+ * Features:
14
+ * - Audio playback with start/end timing control
15
+ * - Automatic play/pause management
16
+ * - Resource cleanup and memory management
17
+ * - Synchronization with visual elements
18
+ *
19
+ * @param containerRef - Reference to the container element
20
+ * @param element - Audio element configuration and properties
21
+ * @param view - The main scene view for rendering
22
+ *
23
+ * @example
24
+ * ```js
25
+ * // Basic audio element
26
+ * {
27
+ * id: "background-music",
28
+ * type: "audio",
29
+ * s: 0,
30
+ * e: 30,
31
+ * props: {
32
+ * src: "music.mp3",
33
+ * volume: 0.7
34
+ * }
35
+ * }
36
+ *
37
+ * // Sound effect with timing
38
+ * {
39
+ * id: "sound-effect",
40
+ * type: "audio",
41
+ * s: 5,
42
+ * e: 8,
43
+ * props: {
44
+ * src: "effect.wav",
45
+ * volume: 1.0
46
+ * }
47
+ * }
48
+ * ```
49
+ */
50
+ export const AudioElement = {
51
+ name: "audio",
52
+
53
+ /**
54
+ * Generator function that creates and manages audio elements in the scene.
55
+ * Handles audio creation, playback control, and cleanup.
56
+ *
57
+ * @param params - Element parameters including container reference, element config, and view
58
+ * @returns Generator that controls the audio element lifecycle
59
+ *
60
+ * @example
61
+ * ```js
62
+ * yield* AudioElement.create({
63
+ * containerRef: mainContainer,
64
+ * element: audioConfig,
65
+ * view: sceneView
66
+ * });
67
+ * ```
68
+ */
69
+ *create({ containerRef, element, view }: ElementParams) {
70
+ const elementRef = createRef<any>();
71
+ yield* waitFor(element?.s);
72
+ yield containerRef().add(
73
+ <Audio ref={elementRef} key={element.id} play={true} {...element.props} />
74
+ );
75
+ yield* waitFor(Math.max(0, element.e - element.s));
76
+ yield elementRef().play(false);
77
+ yield elementRef().remove();
78
+ },
79
+ };
@@ -1,87 +1,169 @@
1
- import { CaptionProps, ElementParams } from "../helpers/types";
2
- import { Color, createRef, Reference, waitFor } from "@twick/core";
3
- import { Rect, Txt } from "@twick/2d";
4
- import { splitPhraseTiming } from "../helpers/caption.utils";
5
- import { TRANSPARENT_COLOR } from "../helpers/constants";
6
- import { hexToRGB } from "../helpers/utils";
7
-
8
- export const CaptionElement = {
9
- name: "caption",
10
- *create({ containerRef, caption }: ElementParams) {
11
- const words = splitPhraseTiming(caption);
12
- let phraseStart = 0;
13
- if (words?.length) {
14
- phraseStart = words[0].s;
15
- }
16
- let wordsState: {
17
- refs: Array<{ bgRef?: Reference<any>; textRef: Reference<any> }>;
18
- props: CaptionProps[];
19
- idx: number;
20
- prevTime: number;
21
- } = {
22
- refs: [],
23
- props: [],
24
- idx: 0,
25
- prevTime: phraseStart,
26
- };
27
- for (const word of words) {
28
- wordsState.props.push(caption.props);
29
- const textRef = createRef<Txt>();
30
- const captionProps = caption.props;
31
- containerRef().add(
32
- <Txt ref={textRef} {...captionProps} text={word.t} opacity={0} />
33
- );
34
- if (caption.capStyle == "highlight_bg") {
35
- const bgContainerRef = createRef();
36
- const childTextRef = createRef();
37
- const _color = new Color({...hexToRGB(captionProps.colors.background), a: captionProps?.bgOpacity ?? 1});
38
- containerRef().add(
39
- <Rect
40
- ref={bgContainerRef}
41
- fill={_color}
42
- width={textRef().width() + (captionProps.bgOffsetWidth ?? 30)}
43
- height={textRef().height() + (captionProps.bgOffsetHeight ?? 10)}
44
- margin={captionProps.bgMargin ?? [0, -5]}
45
- radius={captionProps.bgRadius ?? 10}
46
- padding={captionProps.bgPadding ?? [0, 15]}
47
- opacity={0}
48
- alignItems={"center"}
49
- justifyContent={"center"}
50
- layout
51
- >
52
- <Txt ref={childTextRef} {...captionProps} text={word.t} />
53
- </Rect>
54
- );
55
- textRef().remove();
56
- wordsState.refs.push({
57
- bgRef: bgContainerRef,
58
- textRef: childTextRef,
59
- });
60
- } else {
61
- wordsState.refs.push({
62
- textRef: textRef,
63
- });
64
- }
65
- wordsState.prevTime = word.e;
66
- wordsState.idx = wordsState.idx + 1;
67
- }
68
-
69
- wordsState.prevTime = phraseStart;
70
- wordsState.idx = 0;
71
-
72
- for (const word of words) {
73
- if (caption.capStyle == "highlight_bg") {
74
- yield* wordsState.refs[wordsState.idx]?.bgRef?.().opacity(1, 0);
75
- yield* waitFor(Math.max(0, word.e - word.s));
76
- yield* wordsState.refs[wordsState.idx]
77
- ?.bgRef?.()
78
- .fill(TRANSPARENT_COLOR, 0);
79
- } else {
80
- yield* wordsState.refs[wordsState.idx]?.textRef?.().opacity(1, 0);
81
- yield* waitFor(Math.max(0, word.e - word.s));
82
- }
83
- wordsState.prevTime = word.e;
84
- wordsState.idx = wordsState.idx + 1;
85
- }
86
- },
87
- };
1
+ import { CaptionProps, ElementParams } from "../helpers/types";
2
+ import { Color, createRef, Reference, waitFor } from "@twick/core";
3
+ import { Rect, Txt } from "@twick/2d";
4
+ import { splitPhraseTiming } from "../helpers/caption.utils";
5
+ import { TRANSPARENT_COLOR } from "../helpers/constants";
6
+ import { hexToRGB } from "../helpers/utils";
7
+
8
+ /**
9
+ * @group CaptionElement
10
+ * CaptionElement creates and manages styled text overlays in the visualizer scene.
11
+ * Handles caption rendering, text effects, background styling, and timing
12
+ * for professional video presentations and content creation.
13
+ *
14
+ * Features:
15
+ * - Styled text with custom fonts, colors, and backgrounds
16
+ * - Word-by-word timing and animation
17
+ * - Background highlighting and styling options
18
+ * - Text effects and animations
19
+ * - Automatic timing and synchronization
20
+ *
21
+ * @param containerRef - Reference to the container element
22
+ * @param caption - Caption configuration including text, styling, and timing
23
+ *
24
+ * @example
25
+ * ```js
26
+ * // Basic caption
27
+ * {
28
+ * id: "welcome-caption",
29
+ * type: "caption",
30
+ * s: 2,
31
+ * e: 8,
32
+ * t: "Welcome to our presentation!",
33
+ * props: {
34
+ * colors: {
35
+ * text: "#ffffff",
36
+ * background: "rgba(0,0,0,0.7)"
37
+ * },
38
+ * font: {
39
+ * family: "Arial",
40
+ * size: 48,
41
+ * weight: 600
42
+ * }
43
+ * }
44
+ * }
45
+ *
46
+ * // Caption with background highlighting
47
+ * {
48
+ * id: "highlighted-caption",
49
+ * type: "caption",
50
+ * s: 3,
51
+ * e: 10,
52
+ * t: "Important information",
53
+ * capStyle: "highlight_bg",
54
+ * props: {
55
+ * colors: {
56
+ * text: "#ffffff",
57
+ * background: "rgba(255,0,0,0.8)"
58
+ * },
59
+ * font: {
60
+ * family: "Helvetica",
61
+ * size: 36,
62
+ * weight: 700
63
+ * },
64
+ * bgOpacity: 0.9,
65
+ * bgOffsetWidth: 20,
66
+ * bgOffsetHeight: 10,
67
+ * bgMargin: [10, 5],
68
+ * bgRadius: 15,
69
+ * bgPadding: [20, 15]
70
+ * }
71
+ * }
72
+ * ```
73
+ */
74
+ export const CaptionElement = {
75
+ name: "caption",
76
+
77
+ /**
78
+ * Generator function that creates and manages caption elements in the scene.
79
+ * Handles caption creation, word timing, styling, and text effects.
80
+ *
81
+ * @param params - Element parameters including container reference and caption config
82
+ * @returns Generator that controls the caption element lifecycle
83
+ *
84
+ * @example
85
+ * ```js
86
+ * yield* CaptionElement.create({
87
+ * containerRef: mainContainer,
88
+ * caption: captionConfig
89
+ * });
90
+ * ```
91
+ */
92
+ *create({ containerRef, caption }: ElementParams) {
93
+ const words = splitPhraseTiming(caption);
94
+ let phraseStart = 0;
95
+ if (words?.length) {
96
+ phraseStart = words[0].s;
97
+ }
98
+ let wordsState: {
99
+ refs: Array<{ bgRef?: Reference<any>; textRef: Reference<any> }>;
100
+ props: CaptionProps[];
101
+ idx: number;
102
+ prevTime: number;
103
+ } = {
104
+ refs: [],
105
+ props: [],
106
+ idx: 0,
107
+ prevTime: phraseStart,
108
+ };
109
+ for (const word of words) {
110
+ wordsState.props.push(caption.props);
111
+ const textRef = createRef<Txt>();
112
+ const captionProps = caption.props;
113
+ containerRef().add(
114
+ <Txt ref={textRef} {...captionProps} text={word.t} opacity={0} />
115
+ );
116
+ if (caption.capStyle == "highlight_bg") {
117
+ const bgContainerRef = createRef();
118
+ const childTextRef = createRef();
119
+ const _color = new Color({...hexToRGB(captionProps.colors.background), a: captionProps?.bgOpacity ?? 1});
120
+ containerRef().add(
121
+ <Rect
122
+ ref={bgContainerRef}
123
+ fill={_color}
124
+ width={textRef().width() + (captionProps.bgOffsetWidth ?? 30)}
125
+ height={textRef().height() + (captionProps.bgOffsetHeight ?? 10)}
126
+ margin={captionProps.bgMargin ?? [0, -5]}
127
+ radius={captionProps.bgRadius ?? 10}
128
+ padding={captionProps.bgPadding ?? [0, 15]}
129
+ opacity={0}
130
+ alignItems={"center"}
131
+ justifyContent={"center"}
132
+ layout
133
+ >
134
+ <Txt ref={childTextRef} {...captionProps} text={word.t} />
135
+ </Rect>
136
+ );
137
+ textRef().remove();
138
+ wordsState.refs.push({
139
+ bgRef: bgContainerRef,
140
+ textRef: childTextRef,
141
+ });
142
+ } else {
143
+ wordsState.refs.push({
144
+ textRef: textRef,
145
+ });
146
+ }
147
+ wordsState.prevTime = word.e;
148
+ wordsState.idx = wordsState.idx + 1;
149
+ }
150
+
151
+ wordsState.prevTime = phraseStart;
152
+ wordsState.idx = 0;
153
+
154
+ for (const word of words) {
155
+ if (caption.capStyle == "highlight_bg") {
156
+ yield* wordsState.refs[wordsState.idx]?.bgRef?.().opacity(1, 0);
157
+ yield* waitFor(Math.max(0, word.e - word.s));
158
+ yield* wordsState.refs[wordsState.idx]
159
+ ?.bgRef?.()
160
+ .fill(TRANSPARENT_COLOR, 0);
161
+ } else {
162
+ yield* wordsState.refs[wordsState.idx]?.textRef?.().opacity(1, 0);
163
+ yield* waitFor(Math.max(0, word.e - word.s));
164
+ }
165
+ wordsState.prevTime = word.e;
166
+ wordsState.idx = wordsState.idx + 1;
167
+ }
168
+ },
169
+ };
@@ -1,20 +1,88 @@
1
- import { ElementParams } from "../helpers/types";
2
- import { all, createRef, waitFor } from "@twick/core";
3
- import { Circle } from "@twick/2d";
4
- import { addAnimation } from "../helpers/element.utils";
5
-
6
- export const CircleElement = {
7
- name: "circle",
8
- *create({ containerRef, element, view }: ElementParams) {
9
- const elementRef = createRef<any>();
10
- yield* waitFor(element?.s);
11
- yield containerRef().add(
12
- <Circle ref={elementRef} key={element.id} {...element.props} />
13
- );
14
- yield* all(
15
- addAnimation({ elementRef: elementRef, element: element, view }),
16
- waitFor(Math.max(0, element.e - element.s))
17
- );
18
- yield elementRef().remove();
19
- },
20
- };
1
+ import { ElementParams } from "../helpers/types";
2
+ import { all, createRef, waitFor } from "@twick/core";
3
+ import { Circle } from "@twick/2d";
4
+ import { addAnimation } from "../helpers/element.utils";
5
+
6
+ /**
7
+ * @group CircleElement
8
+ * CircleElement creates and manages circular shape elements in the visualizer scene.
9
+ * Handles circle rendering, styling, and animations for UI elements and
10
+ * visual design components.
11
+ *
12
+ * Features:
13
+ * - Circle rendering with custom styling
14
+ * - Radius and position control
15
+ * - Color and border customization
16
+ * - Animation support
17
+ *
18
+ * @param containerRef - Reference to the container element
19
+ * @param element - Circle element configuration and properties
20
+ * @param view - The main scene view for rendering
21
+ *
22
+ * @example
23
+ * ```js
24
+ * // Basic circle element
25
+ * {
26
+ * id: "background-circle",
27
+ * type: "circle",
28
+ * s: 0,
29
+ * e: 20,
30
+ * props: {
31
+ * radius: 100,
32
+ * fill: "#000000",
33
+ * opacity: 0.5
34
+ * }
35
+ * }
36
+ *
37
+ * // Circle with animation
38
+ * {
39
+ * id: "animated-circle",
40
+ * type: "circle",
41
+ * s: 2,
42
+ * e: 15,
43
+ * props: {
44
+ * radius: 50,
45
+ * fill: "#ff0000",
46
+ * stroke: "#ffffff",
47
+ * strokeWidth: 2
48
+ * },
49
+ * animation: {
50
+ * name: "fade",
51
+ * animate: "enter",
52
+ * duration: 2
53
+ * }
54
+ * }
55
+ * ```
56
+ */
57
+ export const CircleElement = {
58
+ name: "circle",
59
+
60
+ /**
61
+ * Generator function that creates and manages circle elements in the scene.
62
+ * Handles circle creation, styling, and cleanup.
63
+ *
64
+ * @param params - Element parameters including container reference, element config, and view
65
+ * @returns Generator that controls the circle element lifecycle
66
+ *
67
+ * @example
68
+ * ```js
69
+ * yield* CircleElement.create({
70
+ * containerRef: mainContainer,
71
+ * element: circleConfig,
72
+ * view: sceneView
73
+ * });
74
+ * ```
75
+ */
76
+ *create({ containerRef, element, view }: ElementParams) {
77
+ const elementRef = createRef<any>();
78
+ yield* waitFor(element?.s);
79
+ yield containerRef().add(
80
+ <Circle ref={elementRef} key={element.id} {...element.props} />
81
+ );
82
+ yield* all(
83
+ addAnimation({ elementRef: elementRef, element: element, view }),
84
+ waitFor(Math.max(0, element.e - element.s))
85
+ );
86
+ yield elementRef().remove();
87
+ },
88
+ };