fetta 1.3.3 → 1.3.5

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/README.md CHANGED
@@ -85,6 +85,7 @@ const result = splitText(element, options);
85
85
  | `onSplit` | `function` | — | Callback after initial split |
86
86
  | `revertOnComplete` | `boolean` | `false` | Auto-revert when animation completes |
87
87
  | `propIndex` | `boolean` | `false` | Add CSS custom properties: `--char-index`, `--word-index`, `--line-index` |
88
+ | `disableKerning` | `boolean` | `false` | Skip kerning compensation (no margin adjustments) |
88
89
 
89
90
  #### Return Value
90
91
 
@@ -110,7 +111,7 @@ import { SplitText } from 'fetta/react';
110
111
  | `children` | `ReactElement` | — | Single element to split |
111
112
  | `onSplit` | `function` | — | Called after text is split |
112
113
  | `onResize` | `function` | — | Called on autoSplit re-split |
113
- | `options` | `object` | — | Split options (type, classes, mask, propIndex) |
114
+ | `options` | `object` | — | Split options (type, classes, mask, propIndex, disableKerning) |
114
115
  | `autoSplit` | `boolean` | `false` | Re-split on container resize |
115
116
  | `revertOnComplete` | `boolean` | `false` | Revert after animation completes |
116
117
  | `inView` | `boolean \| InViewOptions` | `false` | Enable viewport detection |
@@ -33,6 +33,10 @@ var BREAK_CHARS = /* @__PURE__ */ new Set([
33
33
  "\u2015"
34
34
  // horizontal bar (U+2015)
35
35
  ]);
36
+ var CONTEXTUAL_SCRIPT_REGEX = /[\u0600-\u06FF\u0750-\u077F\u08A0-\u08FF\uFB50-\uFDFF\uFE70-\uFEFF\u0590-\u05FF\uFB1D-\uFB4F\u0E00-\u0E7F\u0900-\u097F\u0980-\u09FF\u0A00-\u0A7F\u0A80-\u0AFF\u0B00-\u0B7F\u0B80-\u0BFF\u0C00-\u0C7F\u0C80-\u0CFF\u0D00-\u0D7F]/;
37
+ function hasContextualScript(chars) {
38
+ return chars.some((char) => CONTEXTUAL_SCRIPT_REGEX.test(char));
39
+ }
36
40
  var INLINE_ELEMENTS = /* @__PURE__ */ new Set([
37
41
  "a",
38
42
  "abbr",
@@ -580,10 +584,12 @@ function performSplit(element, measuredWords, charClass, wordClass, lineClass, s
580
584
  i++;
581
585
  }
582
586
  }
583
- if (splitChars && allWords.length > 0) {
587
+ if (!(options == null ? void 0 : options.disableKerning) && splitChars && allWords.length > 0) {
584
588
  for (const wordSpan of allWords) {
585
589
  const wordChars = Array.from(wordSpan.querySelectorAll(`.${charClass}`));
586
590
  if (wordChars.length < 2) continue;
591
+ const charStringsForCheck = wordChars.map((c) => c.textContent || "");
592
+ if (hasContextualScript(charStringsForCheck)) continue;
587
593
  const styleGroups = [];
588
594
  const firstCharStyles = getComputedStyle(wordChars[0]);
589
595
  let currentKey = buildKerningStyleKey(firstCharStyles);
@@ -630,6 +636,7 @@ function performSplit(element, measuredWords, charClass, wordClass, lineClass, s
630
636
  const lastChar = lastCharSpan.textContent || "";
631
637
  const firstChar = firstCharSpan.textContent || "";
632
638
  if (!lastChar || !firstChar) continue;
639
+ if (hasContextualScript([lastChar, firstChar])) continue;
633
640
  const styles = getComputedStyle(firstCharSpan);
634
641
  const kerningMap = measureKerning(element, firstCharSpan, [lastChar, " ", firstChar], styles);
635
642
  let totalKerning = 0;
@@ -640,7 +647,7 @@ function performSplit(element, measuredWords, charClass, wordClass, lineClass, s
640
647
  targetElement.style.marginLeft = `${totalKerning}px`;
641
648
  }
642
649
  }
643
- } else if (splitWords && allWords.length > 1) {
650
+ } else if (!(options == null ? void 0 : options.disableKerning) && splitWords && allWords.length > 1) {
644
651
  for (let wordIdx = 1; wordIdx < allWords.length; wordIdx++) {
645
652
  if (noSpaceBeforeSet.has(allWords[wordIdx])) continue;
646
653
  const prevWord = allWords[wordIdx - 1];
@@ -650,6 +657,7 @@ function performSplit(element, measuredWords, charClass, wordClass, lineClass, s
650
657
  if (!prevText || !currText) continue;
651
658
  const lastChar = prevText[prevText.length - 1];
652
659
  const firstChar = currText[0];
660
+ if (hasContextualScript([lastChar, firstChar])) continue;
653
661
  const styles = getComputedStyle(currWord);
654
662
  const kerningMap = measureKerning(element, currWord, [lastChar, " ", firstChar], styles);
655
663
  let totalKerning = 0;
@@ -805,7 +813,8 @@ function splitText(element, {
805
813
  onResize,
806
814
  onSplit,
807
815
  revertOnComplete = false,
808
- propIndex = false
816
+ propIndex = false,
817
+ disableKerning = false
809
818
  } = {}) {
810
819
  var _a;
811
820
  if (!(element instanceof HTMLElement)) {
@@ -856,7 +865,7 @@ function splitText(element, {
856
865
  splitChars,
857
866
  splitWords,
858
867
  splitLines,
859
- { propIndex, mask, ariaHidden: !trackAncestors }
868
+ { propIndex, mask, ariaHidden: !trackAncestors, disableKerning }
860
869
  );
861
870
  currentChars = chars;
862
871
  currentWords = words;
@@ -931,7 +940,7 @@ function splitText(element, {
931
940
  splitChars,
932
941
  splitWords,
933
942
  splitLines,
934
- { propIndex, mask, ariaHidden: !trackAncestors }
943
+ { propIndex, mask, ariaHidden: !trackAncestors, disableKerning }
935
944
  );
936
945
  currentChars = result.chars;
937
946
  currentWords = result.words;
package/dist/index.d.ts CHANGED
@@ -42,6 +42,10 @@ interface SplitTextOptions {
42
42
  revertOnComplete?: boolean;
43
43
  /** Add CSS custom properties (--char-index, --word-index, --line-index) */
44
44
  propIndex?: boolean;
45
+ /** Skip kerning compensation (no margin adjustments applied).
46
+ * Kerning is naturally lost when splitting into inline-block spans.
47
+ * Use this if you prefer no compensation over imperfect Safari compensation. */
48
+ disableKerning?: boolean;
45
49
  }
46
50
  /**
47
51
  * Result returned by splitText containing arrays of split elements and a revert function.
@@ -108,6 +112,6 @@ interface SplitTextResult {
108
112
  * });
109
113
  * ```
110
114
  */
111
- declare function splitText(element: HTMLElement, { type, charClass, wordClass, lineClass, mask, autoSplit, onResize, onSplit, revertOnComplete, propIndex, }?: SplitTextOptions): SplitTextResult;
115
+ declare function splitText(element: HTMLElement, { type, charClass, wordClass, lineClass, mask, autoSplit, onResize, onSplit, revertOnComplete, propIndex, disableKerning, }?: SplitTextOptions): SplitTextResult;
112
116
 
113
117
  export { type SplitTextOptions, type SplitTextResult, splitText };
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- export { splitText } from './chunk-RJ7GQ53Y.js';
1
+ export { splitText } from './chunk-HQLYE4Q5.js';
package/dist/react.d.ts CHANGED
@@ -10,6 +10,10 @@ interface SplitTextOptions {
10
10
  /** Apply overflow mask wrapper to elements for reveal animations */
11
11
  mask?: "lines" | "words" | "chars";
12
12
  propIndex?: boolean;
13
+ /** Skip kerning compensation (no margin adjustments applied).
14
+ * Kerning is naturally lost when splitting into inline-block spans.
15
+ * Use this if you prefer no compensation over imperfect Safari compensation. */
16
+ disableKerning?: boolean;
13
17
  }
14
18
  interface InViewOptions {
15
19
  /** How much of the element must be visible (0-1). Default: 0 */
package/dist/react.js CHANGED
@@ -1,4 +1,4 @@
1
- import { splitText, __spreadProps, __spreadValues, normalizeToPromise } from './chunk-RJ7GQ53Y.js';
1
+ import { splitText, __spreadProps, __spreadValues, normalizeToPromise } from './chunk-HQLYE4Q5.js';
2
2
  import { forwardRef, useRef, useCallback, useState, useLayoutEffect, useEffect, isValidElement, cloneElement } from 'react';
3
3
  import { jsx } from 'react/jsx-runtime';
4
4
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fetta",
3
- "version": "1.3.3",
3
+ "version": "1.3.5",
4
4
  "description": "Text splitting library with kerning compensation for animations",
5
5
  "type": "module",
6
6
  "sideEffects": false,