fetta 1.4.3 → 1.4.4

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.
@@ -122,58 +122,9 @@ function copyKerningStyles(target, styles) {
122
122
  if (value) target.style.setProperty(prop, value);
123
123
  });
124
124
  }
125
- function buildCanvasFontString(styles) {
126
- const fontStyle = styles.fontStyle || "normal";
127
- const fontWeight = styles.fontWeight || "normal";
128
- const fontSize = styles.fontSize || "16px";
129
- const fontFamily = styles.fontFamily || "sans-serif";
130
- return [fontStyle, fontWeight, fontSize, fontFamily].filter(Boolean).join(" ");
131
- }
132
- function applyKerningStylesToCanvas(ctx, styles) {
133
- ctx.font = buildCanvasFontString(styles);
134
- const ctxAny = ctx;
135
- const setIfExists = (prop, value) => {
136
- if (!value || value === "normal") return;
137
- if (prop in ctxAny) ctxAny[prop] = value;
138
- };
139
- setIfExists("fontKerning", styles.getPropertyValue("font-kerning"));
140
- setIfExists("fontVariantLigatures", styles.getPropertyValue("font-variant-ligatures"));
141
- setIfExists("fontFeatureSettings", styles.getPropertyValue("font-feature-settings"));
142
- setIfExists("fontVariationSettings", styles.getPropertyValue("font-variation-settings"));
143
- setIfExists("fontOpticalSizing", styles.getPropertyValue("font-optical-sizing"));
144
- setIfExists("fontSizeAdjust", styles.getPropertyValue("font-size-adjust"));
145
- setIfExists("fontStretch", styles.getPropertyValue("font-stretch"));
146
- setIfExists("fontVariantCaps", styles.getPropertyValue("font-variant-caps"));
147
- setIfExists("fontVariantNumeric", styles.getPropertyValue("font-variant-numeric"));
148
- setIfExists("fontVariantEastAsian", styles.getPropertyValue("font-variant-east-asian"));
149
- setIfExists("fontSynthesis", styles.getPropertyValue("font-synthesis"));
150
- setIfExists("fontSynthesisWeight", styles.getPropertyValue("font-synthesis-weight"));
151
- setIfExists("fontSynthesisStyle", styles.getPropertyValue("font-synthesis-style"));
152
- setIfExists("letterSpacing", styles.getPropertyValue("letter-spacing"));
153
- setIfExists("wordSpacing", styles.getPropertyValue("word-spacing"));
154
- setIfExists("textRendering", styles.getPropertyValue("text-rendering"));
155
- setIfExists("direction", styles.getPropertyValue("direction"));
156
- }
157
125
  function buildKerningStyleKey(styles) {
158
126
  return KERNING_STYLE_PROPS.map((prop) => styles.getPropertyValue(prop)).join("|");
159
127
  }
160
- function shouldUseDomKerning(styles) {
161
- const textTransform = styles.getPropertyValue("text-transform");
162
- if (textTransform && textTransform !== "none") return true;
163
- const fontVariant = styles.getPropertyValue("font-variant");
164
- if (fontVariant && fontVariant !== "normal") return true;
165
- const fontStretch = styles.getPropertyValue("font-stretch");
166
- if (fontStretch && fontStretch !== "normal" && fontStretch !== "100%") return true;
167
- const fontFeatureSettings = styles.getPropertyValue("font-feature-settings");
168
- if (fontFeatureSettings && fontFeatureSettings !== "normal") return true;
169
- const fontVariationSettings = styles.getPropertyValue("font-variation-settings");
170
- if (fontVariationSettings && fontVariationSettings !== "normal") return true;
171
- const fontOpticalSizing = styles.getPropertyValue("font-optical-sizing");
172
- if (fontOpticalSizing && fontOpticalSizing !== "auto") return true;
173
- const fontSizeAdjust = styles.getPropertyValue("font-size-adjust");
174
- if (fontSizeAdjust && fontSizeAdjust !== "none") return true;
175
- return false;
176
- }
177
128
  var isSafariBrowser = null;
178
129
  function isSafari() {
179
130
  if (isSafariBrowser !== null) return isSafariBrowser;
@@ -181,29 +132,6 @@ function isSafari() {
181
132
  isSafariBrowser = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
182
133
  return isSafariBrowser;
183
134
  }
184
- function measureKerningCanvas(styleSource, chars, styles) {
185
- const kerningMap = /* @__PURE__ */ new Map();
186
- if (chars.length < 2) return kerningMap;
187
- const canvas = document.createElement("canvas");
188
- const ctx = canvas.getContext("2d");
189
- if (!ctx) return kerningMap;
190
- const computedStyles = styles != null ? styles : getComputedStyle(styleSource);
191
- applyKerningStylesToCanvas(ctx, computedStyles);
192
- const charWidths = /* @__PURE__ */ new Map();
193
- for (const char of new Set(chars)) {
194
- charWidths.set(char, ctx.measureText(char).width);
195
- }
196
- for (let i = 0; i < chars.length - 1; i++) {
197
- const char1 = chars[i];
198
- const char2 = chars[i + 1];
199
- const pairWidth = ctx.measureText(char1 + char2).width;
200
- const kerning = pairWidth - charWidths.get(char1) - charWidths.get(char2);
201
- if (Math.abs(kerning) > 1e-3) {
202
- kerningMap.set(i + 1, kerning);
203
- }
204
- }
205
- return kerningMap;
206
- }
207
135
  function measureKerningDOM(container, styleSource, chars, styles) {
208
136
  const kerningMap = /* @__PURE__ */ new Map();
209
137
  if (chars.length < 2) return kerningMap;
@@ -242,10 +170,47 @@ function measureKerningDOM(container, styleSource, chars, styles) {
242
170
  container.removeChild(measurer);
243
171
  return kerningMap;
244
172
  }
173
+ function measureKerningRange(container, styleSource, chars, styles) {
174
+ const kerningMap = /* @__PURE__ */ new Map();
175
+ if (chars.length < 2) return kerningMap;
176
+ const measurer = document.createElement("span");
177
+ measurer.style.cssText = "position:absolute;visibility:hidden;white-space:pre;";
178
+ const computedStyles = styles != null ? styles : getComputedStyle(styleSource);
179
+ copyKerningStyles(measurer, computedStyles);
180
+ container.appendChild(measurer);
181
+ const range = document.createRange();
182
+ const measureWidth = () => {
183
+ const textNode = measurer.firstChild;
184
+ if (!textNode) return 0;
185
+ range.selectNodeContents(textNode);
186
+ return range.getBoundingClientRect().width;
187
+ };
188
+ const charWidths = /* @__PURE__ */ new Map();
189
+ for (const char of new Set(chars)) {
190
+ measurer.textContent = char;
191
+ charWidths.set(char, measureWidth());
192
+ }
193
+ for (let i = 0; i < chars.length - 1; i++) {
194
+ const char1 = chars[i];
195
+ const char2 = chars[i + 1];
196
+ measurer.textContent = char1 + char2;
197
+ const kerning = measureWidth() - charWidths.get(char1) - charWidths.get(char2);
198
+ if (Math.abs(kerning) > 1e-3) {
199
+ kerningMap.set(i + 1, kerning);
200
+ }
201
+ }
202
+ range.detach();
203
+ container.removeChild(measurer);
204
+ return kerningMap;
205
+ }
245
206
  function measureKerning(container, styleSource, chars, styles) {
246
207
  if (chars.length < 2) return /* @__PURE__ */ new Map();
208
+ if (!container.isConnected) {
209
+ console.warn("splitText: kerning measurement requires a connected DOM element. Skipping kerning.");
210
+ return /* @__PURE__ */ new Map();
211
+ }
247
212
  const computedStyles = styles != null ? styles : getComputedStyle(styleSource);
248
- return isSafari() || shouldUseDomKerning(computedStyles) ? measureKerningDOM(container, styleSource, chars, computedStyles) : measureKerningCanvas(styleSource, chars, computedStyles);
213
+ return isSafari() ? measureKerningDOM(container, styleSource, chars, computedStyles) : measureKerningRange(container, styleSource, chars, computedStyles);
249
214
  }
250
215
  var srOnlyStylesInjected = false;
251
216
  function injectSrOnlyStyles() {
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- export { splitText } from './chunk-24TCJGUM.js';
1
+ export { splitText } from './chunk-Y4GCLM4K.js';
package/dist/react.js CHANGED
@@ -1,4 +1,4 @@
1
- import { splitText, __spreadProps, __spreadValues, normalizeToPromise } from './chunk-24TCJGUM.js';
1
+ import { splitText, __spreadProps, __spreadValues, normalizeToPromise } from './chunk-Y4GCLM4K.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.4.3",
3
+ "version": "1.4.4",
4
4
  "description": "Text splitting library with kerning compensation for animations",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -80,11 +80,20 @@
80
80
  "keywords": [
81
81
  "text",
82
82
  "split",
83
+ "split-text",
84
+ "splittext",
85
+ "text-animation",
86
+ "text-effect",
83
87
  "animation",
84
88
  "motion",
85
89
  "kerning",
86
90
  "typography",
87
- "gsap"
91
+ "gsap",
92
+ "javascript",
93
+ "typescript",
94
+ "react",
95
+ "web-animation",
96
+ "accessible"
88
97
  ],
89
98
  "license": "MIT",
90
99
  "author": "dimi",