vevet 3.13.0 → 3.14.0

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 (217) hide show
  1. package/lib/cjs/Application/events/PageLoad/index.js +2 -1
  2. package/lib/cjs/Application/events/PageLoad/index.js.map +1 -1
  3. package/lib/cjs/Application/events/Viewport/index.js +16 -27
  4. package/lib/cjs/Application/events/Viewport/index.js.map +1 -1
  5. package/lib/cjs/Application/index.js +0 -18
  6. package/lib/cjs/Application/index.js.map +1 -1
  7. package/lib/cjs/base/Callbacks/index.js +0 -28
  8. package/lib/cjs/base/Callbacks/index.js.map +1 -1
  9. package/lib/cjs/base/Module/index.js +4 -22
  10. package/lib/cjs/base/Module/index.js.map +1 -1
  11. package/lib/cjs/base/MutableProps/index.js +3 -3
  12. package/lib/cjs/base/MutableProps/index.js.map +1 -1
  13. package/lib/cjs/components/BaseTimeline/index.js +15 -14
  14. package/lib/cjs/components/BaseTimeline/index.js.map +1 -1
  15. package/lib/cjs/components/Ctx2D/index.js +2 -2
  16. package/lib/cjs/components/Ctx2D/index.js.map +1 -1
  17. package/lib/cjs/components/CustomCursor/index.js +3 -2
  18. package/lib/cjs/components/CustomCursor/index.js.map +1 -1
  19. package/lib/cjs/components/DraggerBase/index.js +2 -1
  20. package/lib/cjs/components/DraggerBase/index.js.map +1 -1
  21. package/lib/cjs/components/Marquee/index.js +3 -2
  22. package/lib/cjs/components/Marquee/index.js.map +1 -1
  23. package/lib/cjs/components/Preloader/index.js +3 -31
  24. package/lib/cjs/components/Preloader/index.js.map +1 -1
  25. package/lib/cjs/components/ProgressPreloader/index.js +5 -3
  26. package/lib/cjs/components/ProgressPreloader/index.js.map +1 -1
  27. package/lib/cjs/components/ScrollBar/index.js +4 -3
  28. package/lib/cjs/components/ScrollBar/index.js.map +1 -1
  29. package/lib/cjs/components/ScrollView/index.js +3 -2
  30. package/lib/cjs/components/ScrollView/index.js.map +1 -1
  31. package/lib/cjs/components/SectionScrollProgress/index.js +3 -2
  32. package/lib/cjs/components/SectionScrollProgress/index.js.map +1 -1
  33. package/lib/cjs/components/SlideProgress/index.js +15 -4
  34. package/lib/cjs/components/SlideProgress/index.js.map +1 -1
  35. package/lib/cjs/components/SmoothScroll/index.js +2 -1
  36. package/lib/cjs/components/SmoothScroll/index.js.map +1 -1
  37. package/lib/cjs/components/SmoothScrollKeyboardPlugin/index.js +2 -1
  38. package/lib/cjs/components/SmoothScrollKeyboardPlugin/index.js.map +1 -1
  39. package/lib/cjs/components/SplitText/index.js +68 -202
  40. package/lib/cjs/components/SplitText/index.js.map +1 -1
  41. package/lib/cjs/components/SplitText/utils/splitBase.js +31 -0
  42. package/lib/cjs/components/SplitText/utils/splitBase.js.map +1 -0
  43. package/lib/cjs/components/SplitText/utils/wrapLetters.js +30 -0
  44. package/lib/cjs/components/SplitText/utils/wrapLetters.js.map +1 -0
  45. package/lib/cjs/components/SplitText/utils/wrapLines.js +78 -0
  46. package/lib/cjs/components/SplitText/utils/wrapLines.js.map +1 -0
  47. package/lib/cjs/components/SplitText/utils/wrapWords.js +57 -0
  48. package/lib/cjs/components/SplitText/utils/wrapWords.js.map +1 -0
  49. package/lib/cjs/components/Timeline/index.js +7 -7
  50. package/lib/cjs/components/Timeline/index.js.map +1 -1
  51. package/lib/cjs/utils/internal/getApp.js +8 -0
  52. package/lib/cjs/utils/internal/getApp.js.map +1 -0
  53. package/lib/cjs/utils/scroll/scrollTo.js +3 -3
  54. package/lib/cjs/utils/scroll/scrollTo.js.map +1 -1
  55. package/lib/cjs/version.js +1 -1
  56. package/lib/esm/Application/events/PageLoad/index.js +2 -1
  57. package/lib/esm/Application/events/PageLoad/index.js.map +1 -1
  58. package/lib/esm/Application/events/Viewport/index.js +16 -26
  59. package/lib/esm/Application/events/Viewport/index.js.map +1 -1
  60. package/lib/esm/Application/index.js +0 -18
  61. package/lib/esm/Application/index.js.map +1 -1
  62. package/lib/esm/base/Callbacks/index.js +0 -23
  63. package/lib/esm/base/Callbacks/index.js.map +1 -1
  64. package/lib/esm/base/Module/index.js +4 -17
  65. package/lib/esm/base/Module/index.js.map +1 -1
  66. package/lib/esm/base/MutableProps/index.js +3 -3
  67. package/lib/esm/base/MutableProps/index.js.map +1 -1
  68. package/lib/esm/components/BaseTimeline/index.js +16 -15
  69. package/lib/esm/components/BaseTimeline/index.js.map +1 -1
  70. package/lib/esm/components/Ctx2D/index.js +2 -2
  71. package/lib/esm/components/Ctx2D/index.js.map +1 -1
  72. package/lib/esm/components/CustomCursor/index.js +3 -2
  73. package/lib/esm/components/CustomCursor/index.js.map +1 -1
  74. package/lib/esm/components/DraggerBase/index.js +2 -1
  75. package/lib/esm/components/DraggerBase/index.js.map +1 -1
  76. package/lib/esm/components/Marquee/index.js +3 -2
  77. package/lib/esm/components/Marquee/index.js.map +1 -1
  78. package/lib/esm/components/Preloader/index.js +3 -19
  79. package/lib/esm/components/Preloader/index.js.map +1 -1
  80. package/lib/esm/components/ProgressPreloader/index.js +4 -3
  81. package/lib/esm/components/ProgressPreloader/index.js.map +1 -1
  82. package/lib/esm/components/ScrollBar/index.js +4 -3
  83. package/lib/esm/components/ScrollBar/index.js.map +1 -1
  84. package/lib/esm/components/ScrollView/index.js +3 -2
  85. package/lib/esm/components/ScrollView/index.js.map +1 -1
  86. package/lib/esm/components/SectionScrollProgress/index.js +3 -2
  87. package/lib/esm/components/SectionScrollProgress/index.js.map +1 -1
  88. package/lib/esm/components/SlideProgress/index.js +15 -4
  89. package/lib/esm/components/SlideProgress/index.js.map +1 -1
  90. package/lib/esm/components/SmoothScroll/index.js +2 -1
  91. package/lib/esm/components/SmoothScroll/index.js.map +1 -1
  92. package/lib/esm/components/SmoothScrollKeyboardPlugin/index.js +2 -1
  93. package/lib/esm/components/SmoothScrollKeyboardPlugin/index.js.map +1 -1
  94. package/lib/esm/components/SplitText/index.js +55 -193
  95. package/lib/esm/components/SplitText/index.js.map +1 -1
  96. package/lib/esm/components/SplitText/utils/splitBase.js +26 -0
  97. package/lib/esm/components/SplitText/utils/splitBase.js.map +1 -0
  98. package/lib/esm/components/SplitText/utils/wrapLetters.js +25 -0
  99. package/lib/esm/components/SplitText/utils/wrapLetters.js.map +1 -0
  100. package/lib/esm/components/SplitText/utils/wrapLines.js +68 -0
  101. package/lib/esm/components/SplitText/utils/wrapLines.js.map +1 -0
  102. package/lib/esm/components/SplitText/utils/wrapWords.js +43 -0
  103. package/lib/esm/components/SplitText/utils/wrapWords.js.map +1 -0
  104. package/lib/esm/components/Timeline/index.js +7 -7
  105. package/lib/esm/components/Timeline/index.js.map +1 -1
  106. package/lib/esm/utils/internal/getApp.js +4 -0
  107. package/lib/esm/utils/internal/getApp.js.map +1 -0
  108. package/lib/esm/utils/scroll/scrollTo.js +3 -3
  109. package/lib/esm/utils/scroll/scrollTo.js.map +1 -1
  110. package/lib/esm/version.js +1 -1
  111. package/lib/types/Application/events/PageLoad/index.d.ts.map +1 -1
  112. package/lib/types/Application/events/Viewport/index.d.ts +0 -4
  113. package/lib/types/Application/events/Viewport/index.d.ts.map +1 -1
  114. package/lib/types/Application/index.d.ts +0 -2
  115. package/lib/types/Application/index.d.ts.map +1 -1
  116. package/lib/types/Application/types.d.ts +2 -3
  117. package/lib/types/Application/types.d.ts.map +1 -1
  118. package/lib/types/base/Callbacks/index.d.ts +0 -16
  119. package/lib/types/base/Callbacks/index.d.ts.map +1 -1
  120. package/lib/types/base/Module/index.d.ts +1 -6
  121. package/lib/types/base/Module/index.d.ts.map +1 -1
  122. package/lib/types/base/Module/types.d.ts +2 -2
  123. package/lib/types/base/Module/types.d.ts.map +1 -1
  124. package/lib/types/base/MutableProps/index.d.ts +0 -4
  125. package/lib/types/base/MutableProps/index.d.ts.map +1 -1
  126. package/lib/types/base/Plugin/types.d.ts +2 -2
  127. package/lib/types/base/Plugin/types.d.ts.map +1 -1
  128. package/lib/types/components/AnimationFrame/index.d.ts +1 -1
  129. package/lib/types/components/BaseTimeline/index.d.ts +7 -7
  130. package/lib/types/components/BaseTimeline/index.d.ts.map +1 -1
  131. package/lib/types/components/BaseTimeline/types.d.ts +2 -2
  132. package/lib/types/components/BaseTimeline/types.d.ts.map +1 -1
  133. package/lib/types/components/Ctx2D/index.d.ts +1 -1
  134. package/lib/types/components/Ctx2D/index.d.ts.map +1 -1
  135. package/lib/types/components/Ctx2DPrerender/index.d.ts +3 -3
  136. package/lib/types/components/Ctx2DPrerender/index.d.ts.map +1 -1
  137. package/lib/types/components/CustomCursor/index.d.ts +1 -1
  138. package/lib/types/components/CustomCursor/index.d.ts.map +1 -1
  139. package/lib/types/components/DraggerBase/index.d.ts +1 -1
  140. package/lib/types/components/DraggerBase/index.d.ts.map +1 -1
  141. package/lib/types/components/DraggerDirection/index.d.ts +1 -1
  142. package/lib/types/components/DraggerMove/index.d.ts +1 -1
  143. package/lib/types/components/Marquee/index.d.ts +1 -1
  144. package/lib/types/components/Marquee/index.d.ts.map +1 -1
  145. package/lib/types/components/Preloader/index.d.ts +1 -13
  146. package/lib/types/components/Preloader/index.d.ts.map +1 -1
  147. package/lib/types/components/ProgressPreloader/index.d.ts +1 -1
  148. package/lib/types/components/ProgressPreloader/index.d.ts.map +1 -1
  149. package/lib/types/components/ScrollBar/index.d.ts +1 -1
  150. package/lib/types/components/ScrollBar/index.d.ts.map +1 -1
  151. package/lib/types/components/ScrollView/index.d.ts +1 -1
  152. package/lib/types/components/ScrollView/index.d.ts.map +1 -1
  153. package/lib/types/components/SectionScrollProgress/index.d.ts +1 -1
  154. package/lib/types/components/SectionScrollProgress/index.d.ts.map +1 -1
  155. package/lib/types/components/SlideProgress/index.d.ts +3 -1
  156. package/lib/types/components/SlideProgress/index.d.ts.map +1 -1
  157. package/lib/types/components/SmoothScroll/index.d.ts +1 -1
  158. package/lib/types/components/SmoothScroll/index.d.ts.map +1 -1
  159. package/lib/types/components/SmoothScrollDragPlugin/index.d.ts +1 -1
  160. package/lib/types/components/SmoothScrollKeyboardPlugin/index.d.ts +1 -1
  161. package/lib/types/components/SmoothScrollKeyboardPlugin/index.d.ts.map +1 -1
  162. package/lib/types/components/SplitText/index.d.ts +15 -13
  163. package/lib/types/components/SplitText/index.d.ts.map +1 -1
  164. package/lib/types/components/SplitText/types.d.ts +17 -13
  165. package/lib/types/components/SplitText/types.d.ts.map +1 -1
  166. package/lib/types/components/SplitText/utils/splitBase.d.ts +17 -0
  167. package/lib/types/components/SplitText/utils/splitBase.d.ts.map +1 -0
  168. package/lib/types/components/SplitText/utils/wrapLetters.d.ts +12 -0
  169. package/lib/types/components/SplitText/utils/wrapLetters.d.ts.map +1 -0
  170. package/lib/types/components/SplitText/utils/wrapLines.d.ts +17 -0
  171. package/lib/types/components/SplitText/utils/wrapLines.d.ts.map +1 -0
  172. package/lib/types/components/SplitText/utils/wrapWords.d.ts +10 -0
  173. package/lib/types/components/SplitText/utils/wrapWords.d.ts.map +1 -0
  174. package/lib/types/components/Timeline/index.d.ts +2 -2
  175. package/lib/types/utils/internal/getApp.d.ts +2 -0
  176. package/lib/types/utils/internal/getApp.d.ts.map +1 -0
  177. package/lib/types/version.d.ts +1 -1
  178. package/package.json +1 -1
  179. package/src/Application/events/PageLoad/index.ts +2 -1
  180. package/src/Application/events/Viewport/index.ts +17 -29
  181. package/src/Application/index.ts +0 -22
  182. package/src/Application/types.ts +2 -3
  183. package/src/base/Callbacks/index.ts +0 -31
  184. package/src/base/Module/index.ts +4 -22
  185. package/src/base/Module/types.ts +4 -3
  186. package/src/base/MutableProps/index.ts +3 -8
  187. package/src/base/Plugin/types.ts +2 -2
  188. package/src/components/BaseTimeline/index.ts +19 -18
  189. package/src/components/BaseTimeline/stories/Basic.tsx +3 -3
  190. package/src/components/BaseTimeline/stories/Nested.tsx +3 -3
  191. package/src/components/BaseTimeline/types.ts +2 -2
  192. package/src/components/Ctx2D/index.ts +2 -2
  193. package/src/components/CustomCursor/index.ts +3 -2
  194. package/src/components/DraggerBase/index.ts +2 -1
  195. package/src/components/Marquee/index.ts +3 -2
  196. package/src/components/Preloader/index.ts +3 -28
  197. package/src/components/ProgressPreloader/index.ts +4 -3
  198. package/src/components/ScrollBar/index.ts +4 -3
  199. package/src/components/ScrollView/index.ts +3 -2
  200. package/src/components/SectionScrollProgress/index.ts +3 -2
  201. package/src/components/SlideProgress/index.ts +20 -4
  202. package/src/components/SmoothScroll/index.ts +2 -1
  203. package/src/components/SmoothScrollKeyboardPlugin/index.ts +2 -1
  204. package/src/components/SplitText/index.ts +64 -234
  205. package/src/components/SplitText/stories/index.stories.tsx +13 -4
  206. package/src/components/SplitText/stories/index.tsx +26 -8
  207. package/src/components/SplitText/types.ts +17 -14
  208. package/src/components/SplitText/utils/splitBase.ts +49 -0
  209. package/src/components/SplitText/utils/wrapLetters.ts +41 -0
  210. package/src/components/SplitText/utils/wrapLines.ts +98 -0
  211. package/src/components/SplitText/utils/wrapWords.ts +64 -0
  212. package/src/components/Timeline/index.ts +7 -7
  213. package/src/components/Timeline/stories/Basic.tsx +4 -4
  214. package/src/components/Timeline/stories/Nested.tsx +5 -5
  215. package/src/utils/internal/getApp.ts +3 -0
  216. package/src/utils/scroll/scrollTo.ts +3 -3
  217. package/src/version.ts +1 -1
@@ -2,12 +2,17 @@ import { selectOne } from 'vevet-dom';
2
2
  import { Component as ComponentClass } from '@/base/Component';
3
3
  import { NSplitText } from './types';
4
4
  import { onResize } from '@/utils/listeners/onResize';
5
+ import { splitBase } from './utils/splitBase';
6
+ import { wrapLines } from './utils/wrapLines';
7
+ import { getApp } from '@/utils/internal/getApp';
5
8
 
6
9
  export type { NSplitText };
7
10
 
8
11
  /**
9
12
  * Split text into letters, words & lines.
10
13
  * Usually used for text animation.
14
+ *
15
+ * Apply `fontKerning: none` to your container to prevent large layout shifts
11
16
  */
12
17
  export class SplitText<
13
18
  StaticProps extends NSplitText.IStaticProps = NSplitText.IStaticProps,
@@ -20,26 +25,37 @@ export class SplitText<
20
25
  return {
21
26
  ...super._getDefaultProps(),
22
27
  container: `#${this.prefix}`,
23
- textSource: 'innerText',
24
28
  hasLetters: true,
25
29
  hasLines: false,
30
+ letterTag: 'span',
31
+ wordTag: 'span',
32
+ lineTag: 'span',
26
33
  viewportTarget: 'any',
27
34
  resizeDebounce: 0,
28
35
  };
29
36
  }
30
37
 
31
38
  get prefix() {
32
- return `${this.app.prefix}split-text`;
39
+ return `${getApp().prefix}split-text`;
33
40
  }
34
41
 
35
- /** Initial text */
36
- protected _initialText: string;
42
+ get letterClassName() {
43
+ return this.className('__letter');
44
+ }
45
+
46
+ get wordClassName() {
47
+ return this.className('__word');
48
+ }
49
+
50
+ get lineClassName() {
51
+ return this.className('__line');
52
+ }
37
53
 
38
54
  /** Initial HTML content */
39
55
  protected _initialHTML: string;
40
56
 
41
- /** If the text is already split into letters and words */
42
- protected _isBaseSplit: boolean;
57
+ /** If the text is already split into words & letters */
58
+ protected _isBaseSplit = false;
43
59
 
44
60
  /** Text container */
45
61
  protected _container: HTMLElement;
@@ -73,31 +89,23 @@ export class SplitText<
73
89
  return this._lines;
74
90
  }
75
91
 
92
+ /** What `wrapLines` returns */
93
+ protected _lineWrapper?: ReturnType<typeof wrapLines>;
94
+
76
95
  constructor(initialProps: StaticProps & ChangeableProps, canInit = true) {
77
96
  super(initialProps, false);
78
97
 
79
98
  // get text container
80
99
  this._container = selectOne(this.props.container) as HTMLElement;
81
100
  this.toggleClassName(this._container, this.className(''), true);
101
+
102
+ // attributes
82
103
  this._container.translate = false;
83
104
 
84
- // initial html
105
+ // save initial html
85
106
  this._initialHTML = this._container.innerHTML;
86
107
 
87
- // get initial text
88
- const innerText = this._container[this.props.textSource]!.trim();
89
- this._initialText = innerText || 'no rendered text';
90
- this._initialText = this._initialText.replace(/\s+\n/gm, '\n');
91
- this._initialText = this._initialText.replace(
92
- /<br( ?)(\/?)>/gm,
93
- String.fromCharCode(10),
94
- );
95
-
96
- // a11y
97
- this._container.ariaLabel = this._initialText;
98
-
99
108
  // set default vars
100
- this._isBaseSplit = false;
101
109
  this._letters = [];
102
110
  this._words = [];
103
111
  this._lines = [];
@@ -137,16 +145,7 @@ export class SplitText<
137
145
 
138
146
  /** Split the text */
139
147
  public splitText() {
140
- // split into words & letters
141
- if (!this._isBaseSplit) {
142
- this.container.innerHTML = '';
143
-
144
- this._splitIntoWords();
145
- this._splitIntoLetters();
146
- this._appendWords();
147
-
148
- this._isBaseSplit = true;
149
- }
148
+ this._splitBase();
150
149
 
151
150
  // split text into lines
152
151
  if (this.props.hasLines) {
@@ -157,227 +156,58 @@ export class SplitText<
157
156
  this.callbacks.tbt('split', undefined);
158
157
  }
159
158
 
160
- /** Split the text into words */
161
- protected _splitIntoWords() {
162
- const chars = this._initialText.split('');
163
-
164
- // create words
165
- let wordIndex = 0;
166
- chars.forEach((char) => {
167
- const element = document.createElement('span');
168
- element.classList.add(this.className('__word'));
169
- element.setAttribute('aria-hidden', 'true');
170
- element.style.display = 'inline-block';
171
-
172
- // get an existing word or create a base for a new one
173
- const currentWord: NSplitText.IWord = this._words[wordIndex] ?? {
174
- element,
175
- textContent: '',
176
- hasNewLine: false,
177
- letters: [],
178
- };
179
- this._words[wordIndex] = currentWord;
180
-
181
- // get type of the char
182
- const charCode = char.charCodeAt(0);
183
- const isWhitespace = charCode === 32 || charCode === 160;
184
- const isSeparator = [45, 8208, 8211, 8212, 8722].includes(charCode);
185
- const isNewLine = charCode === 10;
186
-
187
- // add whitespace
188
- if (isWhitespace) {
189
- currentWord.whitespace = document.createTextNode(' ');
190
- }
191
-
192
- // add newline
193
- if (isNewLine) {
194
- currentWord.br = document.createElement('br');
195
- }
196
-
197
- // update word states
198
- currentWord.hasNewLine = isNewLine;
199
-
200
- // go to next word if needed
201
- if (isWhitespace || isNewLine) {
202
- wordIndex += 1;
203
-
204
- return;
205
- }
206
-
207
- // update contents
208
- currentWord.textContent += char;
209
- if (!this.props.hasLetters) {
210
- currentWord.element.innerHTML = currentWord.textContent;
211
- }
212
-
213
- // go to next word if needed
214
- if (isSeparator) {
215
- wordIndex += 1;
216
- }
217
- });
218
-
219
- // only filled words
220
- this._words = this._words.filter((word, index) => {
221
- if (word.textContent.length === 0) {
222
- if (index > 0) {
223
- this._words[index - 1].whitespace = word.whitespace;
224
- this._words[index - 1].br = word.br;
225
- }
226
-
227
- return false;
228
- }
229
-
230
- return true;
231
- });
232
-
233
- // add classnames
234
- let prevWord: NSplitText.IWord | undefined;
235
- this._words.forEach((word) => {
236
- if (prevWord && !!prevWord.whitespace) {
237
- word.element.classList.add(this.className('pre-whitespace'));
238
- }
159
+ /** Split base */
160
+ private _splitBase() {
161
+ if (this._isBaseSplit) {
162
+ return;
163
+ }
239
164
 
240
- if (word.whitespace) {
241
- word.element.classList.add(this.className('has-whitespace'));
242
- }
165
+ const { container, letterClassName, wordClassName } = this;
166
+ const { letterTag, wordTag } = this.props;
243
167
 
244
- prevWord = word;
245
- });
246
- }
168
+ this._isBaseSplit = true;
247
169
 
248
- /** Split the text into letters */
249
- protected _splitIntoLetters() {
250
- // check if need to have letters
251
- if (!this.props.hasLetters) {
252
- return;
253
- }
170
+ // split text
254
171
 
255
- // create letters
256
- this._words.forEach((word) => {
257
- const chars = word.textContent.split('');
258
- const wordLetters: NSplitText.ILetter[] = [];
259
-
260
- chars.forEach((char) => {
261
- const element = document.createElement('span');
262
- element.classList.add(this.className('__letter'));
263
- element.setAttribute('aria-hidden', 'true');
264
- element.innerHTML = char;
265
- element.style.display = 'inline-block';
266
-
267
- const letter: NSplitText.ILetter = {
268
- element,
269
- textContent: char,
270
- word,
271
- };
272
-
273
- this._letters.push(letter);
274
- wordLetters.push(letter);
275
- });
276
-
277
- // eslint-disable-next-line no-param-reassign
278
- word.letters = wordLetters;
172
+ const { helper, words, letters } = splitBase({
173
+ container,
174
+ letterClassName,
175
+ wordClassName,
176
+ hasLetters: this.props.hasLetters,
177
+ letterTag,
178
+ wordTag,
279
179
  });
280
180
 
281
- // append letters
282
- this._letters.forEach(({ element, word }) => {
283
- word.element.appendChild(element);
284
- });
285
- }
181
+ // append nodes
286
182
 
287
- /** Append split words to the container */
288
- protected _appendWords() {
289
- this._words.forEach((word) => {
290
- this.container.appendChild(word.element);
183
+ while (container.childNodes[0]) {
184
+ container.childNodes[0].remove();
185
+ }
291
186
 
292
- if (word.whitespace) {
293
- this.container.appendChild(word.whitespace);
294
- }
187
+ while (helper.childNodes[0]) {
188
+ container.appendChild(helper.childNodes[0]);
189
+ }
295
190
 
296
- if (word.br) {
297
- this.container.appendChild(word.br);
298
- }
299
- });
191
+ // update elements
192
+ this._words = words;
193
+ this._letters = letters;
300
194
  }
301
195
 
302
196
  /** Split the text into lines */
303
197
  protected _splitIntoLines() {
304
- // first of all, remove all previous lines
305
- this._removeLines();
306
-
307
- // create lines
308
- let currentLine: NSplitText.ILine | undefined;
309
- let prevOffsetTop = Infinity;
310
- let prevWord: NSplitText.IWord | undefined;
311
-
312
- this.words.forEach((word) => {
313
- // check if need to create a new line
314
- let isNewLine = false;
315
- const top = word.element.offsetTop;
316
-
317
- // check if the previous word contains BR
318
- if (!!prevWord && !!prevWord.br) {
319
- isNewLine = true;
320
- } else {
321
- // otherwise check offset
322
- isNewLine = top !== prevOffsetTop;
323
- }
324
-
325
- // update vars
326
- prevWord = word;
327
- prevOffsetTop = top;
328
-
329
- // create new line
330
- if (isNewLine) {
331
- const element = document.createElement('span');
332
- element.classList.add(this.className('__line'));
333
- element.setAttribute('aria-hidden', 'true');
334
- element.style.display = 'block';
335
-
336
- currentLine = {
337
- element,
338
- textContent: '',
339
- words: [],
340
- };
341
-
342
- this._lines.push(currentLine);
343
- }
344
-
345
- // append words
346
- if (currentLine) {
347
- currentLine.words.push(word);
348
- }
349
- });
350
-
351
- // update lines content
352
- this._lines.forEach((line) => {
353
- // eslint-disable-next-line no-param-reassign
354
- line.textContent = line.words.map((word) => word.textContent).join(' ');
355
- });
356
-
357
- // append lines
358
- this._lines.forEach((line) => {
359
- line.words.forEach((word) => {
360
- line.element.appendChild(word.element);
198
+ const { container, words, lineClassName } = this;
199
+ const { lineTag } = this.props;
361
200
 
362
- if (word.br) {
363
- word.br.remove();
364
- }
201
+ this._lineWrapper?.destroy();
365
202
 
366
- if (word.whitespace) {
367
- line.element.appendChild(word.whitespace);
368
- }
369
- });
370
-
371
- this.container.appendChild(line.element);
203
+ this._lineWrapper = wrapLines({
204
+ container,
205
+ words,
206
+ className: lineClassName,
207
+ tagName: lineTag,
372
208
  });
373
- }
374
-
375
- /** Remove all lines */
376
- protected _removeLines() {
377
- this._lines.forEach(({ element }) => element.remove());
378
- this._lines = [];
379
209
 
380
- this._appendWords();
210
+ this._lines = this._lineWrapper.lines;
381
211
  }
382
212
 
383
213
  /** Destroy the module */
@@ -7,7 +7,7 @@ const meta: Meta<TComponent> = {
7
7
  title: 'Components/SplitText',
8
8
  component: Component,
9
9
  args: {
10
- text: 'Loremipsum-dolor- sit - amet consectetur adipisicing elit. <br>Recusandae totam-<br /> ipsum unde -quaerat magni maiores voluptate quidem laboriosam repellendus libero dolore ut est perferendis dignissimos laborum, quis ad veniam?<br/>Incidunt?',
10
+ text: ' <b>Lorem</b>ipsum dolor <b style="color: red;">sit</b> amet,&nbsp;consectetur adipiscing elit, <span style="color: blue;">sed <b><i>d</i>o</b></span> eius<b>m<i>od</i></b> tempor <i>incididunt ut</i> labore et dolore magna aliqua. <br /> <br /> <button type="button" style="font: inherit; background-color: #ccc;">Ut enim</button> ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. ',
11
11
  },
12
12
  };
13
13
 
@@ -15,14 +15,23 @@ export default meta;
15
15
 
16
16
  export const Default: StoryObj<TComponent> = {};
17
17
 
18
- export const WithLines: StoryObj<TComponent> = {
18
+ export const WithLetters: StoryObj<TComponent> = {
19
19
  args: {
20
- hasLines: true,
20
+ hasLetters: true,
21
+ hasLines: false,
21
22
  },
22
23
  };
23
24
 
24
- export const WithoutLetter: StoryObj<TComponent> = {
25
+ export const WithWords: StoryObj<TComponent> = {
25
26
  args: {
26
27
  hasLetters: false,
28
+ hasLines: false,
29
+ },
30
+ };
31
+
32
+ export const WithLines: StoryObj<TComponent> = {
33
+ args: {
34
+ hasLetters: false,
35
+ hasLines: true,
27
36
  },
28
37
  };
@@ -1,26 +1,44 @@
1
+ /* eslint-disable react/no-danger */
1
2
  import React, { FC, useEffect, useRef } from 'react';
2
- import { SplitText, NSplitText } from '..';
3
+ import { NSplitText } from '../types';
4
+ import { SplitText } from '..';
3
5
 
4
6
  interface IProps extends Omit<NSplitText.IStaticProps, 'parent' | 'container'> {
5
7
  text: string;
6
8
  }
7
9
 
8
- export const Component: FC<IProps> = ({ text, ...props }) => {
10
+ export const Component: FC<IProps> = ({ text, hasLetters, hasLines }) => {
9
11
  const ref = useRef<HTMLHeadingElement>(null);
10
12
 
11
13
  useEffect(() => {
12
- if (!ref.current) {
14
+ const container = ref.current;
15
+ if (!container) {
13
16
  return undefined;
14
17
  }
15
18
 
16
19
  const instance = new SplitText({
17
- ...props,
18
- container: ref.current,
20
+ container,
21
+ hasLetters,
22
+ hasLines,
19
23
  });
20
24
 
21
25
  return () => instance.destroy();
22
- }, [props]);
26
+ }, [hasLetters, hasLines]);
23
27
 
24
- // eslint-disable-next-line react/no-danger
25
- return <h1 ref={ref} dangerouslySetInnerHTML={{ __html: text }} />;
28
+ const style = { fontSize: '30px', fontKerning: 'none' } as any;
29
+
30
+ return (
31
+ <>
32
+ <h1>Vevet</h1>
33
+
34
+ <div ref={ref} style={style} dangerouslySetInnerHTML={{ __html: text }} />
35
+
36
+ <br />
37
+ <br />
38
+
39
+ <h1>Reference text</h1>
40
+
41
+ <div style={style} dangerouslySetInnerHTML={{ __html: text }} />
42
+ </>
43
+ );
26
44
  };
@@ -2,18 +2,11 @@ import { NComponent } from '@/base/Component/types';
2
2
  import { TOnResizeTarget } from '@/utils/listeners/onResize';
3
3
 
4
4
  export namespace NSplitText {
5
- export type TTextSource = 'textContent' | 'innerText' | 'innerHTML';
6
-
7
5
  export interface IStaticProps extends NComponent.IStaticProps {
8
6
  /**
9
7
  * The text container. You may use a CSS selector or the element itself
10
8
  */
11
9
  container: string | Element;
12
- /**
13
- * Text content
14
- * @default 'innerText'
15
- */
16
- textSource?: TTextSource;
17
10
  /**
18
11
  * If need to split text into letters.
19
12
  * @default true
@@ -24,6 +17,21 @@ export namespace NSplitText {
24
17
  * @default false
25
18
  */
26
19
  hasLines?: boolean;
20
+ /**
21
+ * Letter tagName
22
+ * @default `span`
23
+ */
24
+ letterTag?: keyof HTMLElementTagNameMap;
25
+ /**
26
+ * Word tagName
27
+ * @default `span`
28
+ */
29
+ wordTag?: keyof HTMLElementTagNameMap;
30
+ /**
31
+ * Line tagName
32
+ * @default `span`
33
+ */
34
+ lineTag?: keyof HTMLElementTagNameMap;
27
35
  /**
28
36
  * Viewport target
29
37
  * @default 'any'
@@ -44,22 +52,17 @@ export namespace NSplitText {
44
52
 
45
53
  export interface ILine {
46
54
  element: HTMLElement;
47
- textContent: string;
48
55
  words: NSplitText.IWord[];
49
56
  }
50
57
 
51
58
  export interface IWord {
52
59
  element: HTMLElement;
53
- textContent: string;
54
- hasNewLine: boolean;
60
+ text: string;
55
61
  letters: NSplitText.ILetter[];
56
- br?: HTMLBRElement;
57
- whitespace?: Text;
58
62
  }
59
63
 
60
64
  export interface ILetter {
61
65
  element: HTMLElement;
62
- textContent: string;
63
- word: NSplitText.IWord;
66
+ text: string;
64
67
  }
65
68
  }
@@ -0,0 +1,49 @@
1
+ /* eslint-disable no-param-reassign */
2
+ import { NSplitText } from '../types';
3
+ import { wrapLetters } from './wrapLetters';
4
+ import { wrapWords } from './wrapWords';
5
+
6
+ interface IProps {
7
+ container: HTMLElement;
8
+ letterClassName: string;
9
+ wordClassName: string;
10
+ hasLetters: boolean;
11
+ letterTag: keyof HTMLElementTagNameMap;
12
+ wordTag: keyof HTMLElementTagNameMap;
13
+ }
14
+
15
+ /** Split text into letters & words */
16
+ export function splitBase({
17
+ container,
18
+ letterClassName,
19
+ wordClassName,
20
+ hasLetters,
21
+ letterTag,
22
+ wordTag,
23
+ }: IProps) {
24
+ const helper = container.cloneNode(true) as HTMLElement;
25
+
26
+ const words = wrapWords({
27
+ container: helper,
28
+ classname: wordClassName,
29
+ tagName: wordTag,
30
+ });
31
+
32
+ const letters: NSplitText.ILetter[] = [];
33
+
34
+ if (hasLetters) {
35
+ const wrappedLetters = wrapLetters({
36
+ words,
37
+ classname: letterClassName,
38
+ tagName: letterTag,
39
+ });
40
+
41
+ letters.push(...wrappedLetters.letters);
42
+ }
43
+
44
+ return {
45
+ helper,
46
+ words,
47
+ letters,
48
+ };
49
+ }
@@ -0,0 +1,41 @@
1
+ import { NSplitText } from '../types';
2
+
3
+ interface IProps {
4
+ words: NSplitText.IWord[];
5
+ classname: string;
6
+ tagName: keyof HTMLElementTagNameMap;
7
+ }
8
+
9
+ /** Wrap each word inside the container */
10
+ export function wrapLetters({ words, classname, tagName }: IProps) {
11
+ const letters: NSplitText.ILetter[] = [];
12
+
13
+ words.forEach((word) => {
14
+ const textNode = word.element.childNodes[0];
15
+ const text = textNode.textContent;
16
+
17
+ if (!text) {
18
+ return;
19
+ }
20
+
21
+ const slitLetters = text.split('');
22
+
23
+ slitLetters.forEach((splitLetter) => {
24
+ const element = document.createElement(tagName);
25
+ element.style.display = 'inline-block';
26
+ element.classList.add(classname);
27
+ element.appendChild(document.createTextNode(splitLetter));
28
+
29
+ word.element.appendChild(element);
30
+
31
+ const letter: NSplitText.ILetter = { element, text: splitLetter };
32
+
33
+ word.letters.push(letter);
34
+ letters.push(letter);
35
+ });
36
+
37
+ textNode.remove();
38
+ });
39
+
40
+ return { letters };
41
+ }