text-slicer 1.4.1 → 1.5.0-dev.1

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/dist/index.cjs.js CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const p={splitMode:"both",cssVariables:!1,dataAttributes:!1,keepWhitespaceNodes:!0,freezeWordWidths:!1},i=Object.freeze({word:"ts-word",char:"ts-char",whitespace:"ts-whitespace"}),n=" ",u=e=>typeof window>"u"||typeof document>"u"?null:e?typeof e=="string"?document.querySelector(e):e:null,a=e=>{const t=Intl.Segmenter;if(typeof t=="function"){const s=new t("en",{granularity:"grapheme"});return Array.from(s.segment(e),r=>r.segment)}return Array.from(e)},d=e=>e.split(n),h=e=>{const t={};return Object.keys(e).forEach(s=>{const r=e[s];r!==void 0&&(t[s]=r)}),t};exports.CLASSNAMES=i,exports.TextSlicer=class{el;original;opts;callbacks;charIndex;mounted;ro=null;frozen=!1;freezeScheduled=!1;freezeWordWidthsBound=()=>{this.scheduleFreezeNow()};constructor(e={},t){const s=u(e.container);this.el=s,this.original=(r=>!!r&&typeof HTMLElement<"u"&&r instanceof HTMLElement)(s)?s.textContent?.toString()??"":"",this.opts={...p,...h(e)},this.callbacks=t,this.charIndex=0,this.mounted=!1}get metrics(){const e=this.original;return{wordTotal:e.length?d(e).length:0,charTotal:e.length,renderedAt:Date.now()}}init(){this.el&&(this.mounted=!0,this.split())}reinit(e,t){this.el&&(typeof e=="string"&&(this.original=e),t&&(this.opts={...this.opts,...h(t)}),this.split())}clear(){this.el&&(this.unfreezeWordWidths(),this.el.replaceChildren())}split(){if(!this.el)return;this.clear(),this.charIndex=0;const e=this.original,t=document.createDocumentFragment(),s=d(e);this.opts.splitMode==="chars"?this.appendChars(t,e):this.appendWords(t,s),this.el.appendChild(t),this.opts.cssVariables&&(this.el.style.setProperty("--word-total",String(s.length)),this.el.style.setProperty("--char-total",String(e.length))),this.scheduleFreezeWordWidths().then(()=>{this.callbacks?.onAfterRender?.(this.metrics)}).catch(()=>{})}destroy(){this.el&&(this.detachResizeObserver(),this.clear(),this.mounted=!1)}updateOptions(e){this.opts={...this.opts,...h(e)},this.mounted&&this.split()}appendWords(e,t){t.forEach((s,r)=>{if(this.opts.splitMode==="both"){const o=this.createWordSpan(r,s);for(const c of a(s)){const l=this.createCharSpan(c);o.append(l)}e.append(o)}else{const o=this.createWordSpan(r);o.append(document.createTextNode(s)),e.append(o)}r<t.length-1&&e.append(this.createSpaceSpan())})}appendChars(e,t){for(const s of a(t)){const r=this.createCharSpan(s);e.append(r)}}createWordSpan(e,t=""){const s=document.createElement("span");return s.classList.add(i.word),this.opts.dataAttributes&&t&&s.setAttribute("data-word",t),this.opts.cssVariables&&s.style.setProperty("--word-index",String(e)),s}createCharSpan(e){const t=document.createElement("span");return t.textContent=e,this.opts.dataAttributes&&t.setAttribute("data-char",e),e===n?(t.classList.add(i.whitespace),this.opts.keepWhitespaceNodes||(t.textContent=n)):(t.classList.add(i.char),this.opts.cssVariables&&t.style.setProperty("--char-index",String(this.charIndex)),this.charIndex+=1),t}createSpaceSpan(){const e=document.createElement("span");return e.classList.add(i.whitespace),e.textContent=n,e}scheduleFreezeNow(){this.el&&this.opts.freezeWordWidths&&(this.freezeScheduled||(this.freezeScheduled=!0,requestAnimationFrame(()=>{this.freezeScheduled=!1,this.freezeWordWidths()})))}async scheduleFreezeWordWidths(){if(this.el&&this.opts.freezeWordWidths){try{await document.fonts?.ready}catch{}await new Promise(e=>requestAnimationFrame(()=>e())),this.scheduleFreezeNow(),this.attachResizeObserver()}}measureWordWidths(){if(!this.el)return[];const e=this.el.querySelectorAll(`.${i.word}`);return Array.from(e,t=>{t.style.width="",t.style.flex="";const{width:s}=t.getBoundingClientRect();return Math.ceil(s)})}applyWordWidths(e){this.el&&this.el.querySelectorAll(`.${i.word}`).forEach((t,s)=>{const r=e[s]??0;t.style.width=`${r}px`,t.style.flex="0 0 auto"})}freezeWordWidths(){if(!this.el)return;const e=this.measureWordWidths();this.applyWordWidths(e),this.frozen=!0}unfreezeWordWidths(){!this.el||!this.frozen||(this.el.querySelectorAll(`.${i.word}`).forEach(e=>{e.style.width="",e.style.flex=""}),this.frozen=!1)}attachResizeObserver(){if(this.el&&this.opts.freezeWordWidths){if(!this.ro){this.ro=new ResizeObserver(()=>{this.scheduleFreezeNow()});try{this.ro.observe(this.el,{box:"border-box"})}catch{this.ro.observe(this.el)}}window.addEventListener("resize",this.freezeWordWidthsBound,{passive:!0})}}detachResizeObserver(){this.ro&&(this.ro.disconnect(),this.ro=null),window.removeEventListener("resize",this.freezeWordWidthsBound)}};
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const g={splitMode:"both",cssVariables:!1,dataAttributes:!1,keepWhitespaceNodes:!0,lockContainerHeight:!1},h=Object.freeze({word:"ts-word",char:"ts-char",whitespace:"ts-whitespace"}),o=" ",r=()=>typeof window<"u"&&typeof document<"u",c=t=>{const e=Intl.Segmenter;if(typeof e=="function"){const s=new e("en",{granularity:"grapheme"});return Array.from(s.segment(t),i=>i.segment)}return Array.from(t)},l=t=>t.split(o),a=t=>{const e={};return Object.keys(t).forEach(s=>{const i=t[s];i!==void 0&&(e[s]=i)}),e};exports.CLASSNAMES=h,exports.TextSlicer=class{el;original;opts;callbacks;charIndex;mounted;heightLocked=!1;lockedHeightPx=null;constructor(t={},e){const s=(i=t.container,r()&&i?typeof i=="string"?document.querySelector(i):i:null);var i;this.el=s,this.original=(n=>!!n&&typeof HTMLElement<"u"&&n instanceof HTMLElement)(s)?s.textContent?.toString()??"":"",this.opts={...g,...a(t)},this.callbacks=e,this.charIndex=0,this.mounted=!1}get metrics(){const t=this.original;return{wordTotal:t.length?l(t).length:0,charTotal:t.length,renderedAt:Date.now()}}init(){this.el&&(this.mounted=!0,this.opts.lockContainerHeight&&this.freezeHeight(),this.split())}reinit(t,e){if(this.el){if(typeof t=="string"&&(this.original=t),e){const s=this.opts.lockContainerHeight;this.opts={...this.opts,...a(e)},!s&&this.opts.lockContainerHeight?this.freezeHeight():s&&!this.opts.lockContainerHeight&&this.unfreezeHeight()}this.opts.lockContainerHeight&&this.freezeHeight(),this.split()}}clear(){this.el&&this.el.replaceChildren()}split(){if(!this.el)return;this.clear(),this.charIndex=0;const t=this.original,e=document.createDocumentFragment(),s=l(t);this.opts.splitMode==="chars"?this.appendChars(e,t):this.appendWords(e,s),this.el.appendChild(e),this.opts.cssVariables&&(this.el.style.setProperty("--word-total",String(s.length)),this.el.style.setProperty("--char-total",String(t.length))),this.callbacks?.onAfterRender?.(this.metrics)}destroy(){this.el&&(this.clear(),this.mounted=!1,this.unfreezeHeight())}updateOptions(t){const e=this.opts.lockContainerHeight;this.opts={...this.opts,...a(t)},!e&&this.opts.lockContainerHeight?this.freezeHeight():e&&!this.opts.lockContainerHeight&&this.unfreezeHeight(),this.mounted&&this.split()}freezeHeight(){if(!this.el||!r())return;const t=this.el.getBoundingClientRect(),e=Math.max(0,Math.round(t.height));e&&(this.heightLocked&&this.lockedHeightPx===e||(this.el.style.height=`${e}px`,this.el.style.overflow="hidden",this.heightLocked=!0,this.lockedHeightPx=e))}unfreezeHeight(){this.el&&r()&&(this.heightLocked&&(this.el.style.height="",this.el.style.overflow=""),this.heightLocked=!1,this.lockedHeightPx=null)}appendWords(t,e){e.forEach((s,i)=>{if(this.opts.splitMode==="both"){const n=this.createWordSpan(i,s);for(const d of c(s)){const p=this.createCharSpan(d);n.append(p)}t.append(n)}else{const n=this.createWordSpan(i);n.append(document.createTextNode(s)),t.append(n)}i<e.length-1&&t.append(this.createSpaceSpan())})}appendChars(t,e){for(const s of c(e)){const i=this.createCharSpan(s);t.append(i)}}createWordSpan(t,e=""){const s=document.createElement("span");return s.classList.add(h.word),this.opts.dataAttributes&&e&&s.setAttribute("data-word",e),this.opts.cssVariables&&s.style.setProperty("--word-index",String(t)),s}createCharSpan(t){const e=document.createElement("span");return e.textContent=t,this.opts.dataAttributes&&e.setAttribute("data-char",t),t===o?(e.classList.add(h.whitespace),this.opts.keepWhitespaceNodes||(e.textContent=o)):(e.classList.add(h.char),this.opts.cssVariables&&e.style.setProperty("--char-index",String(this.charIndex)),this.charIndex+=1),e}createSpaceSpan(){const t=document.createElement("span");return t.classList.add(h.whitespace),t.textContent=o,t}};
package/dist/index.d.ts CHANGED
@@ -5,7 +5,8 @@ export interface TextSlicerOptions {
5
5
  cssVariables?: boolean;
6
6
  dataAttributes?: boolean;
7
7
  keepWhitespaceNodes?: boolean;
8
- freezeWordWidths?: boolean;
8
+ /** При true фиксирует текущую высоту контейнера при init()/reinit(). */
9
+ lockContainerHeight?: boolean;
9
10
  }
10
11
  export interface TextSlicerMetrics {
11
12
  wordTotal: number;
@@ -27,10 +28,10 @@ export declare class TextSlicer {
27
28
  private callbacks;
28
29
  private charIndex;
29
30
  private mounted;
30
- private ro;
31
- private frozen;
32
- private freezeScheduled;
33
- private freezeWordWidthsBound;
31
+ /** Флаг, зафиксирована ли высота сейчас. */
32
+ private heightLocked;
33
+ /** Последнее зафиксированное значение высоты (в px). */
34
+ private lockedHeightPx;
34
35
  constructor(options?: TextSlicerOptions, callbacks?: TextSlicerCallbacks);
35
36
  get metrics(): TextSlicerMetrics;
36
37
  init(): void;
@@ -39,17 +40,19 @@ export declare class TextSlicer {
39
40
  split(): void;
40
41
  destroy(): void;
41
42
  updateOptions(next: Partial<TextSlicerOptions>): void;
43
+ /**
44
+ * Фиксирует текущую высоту контейнера (в px) и скрывает переполнение.
45
+ * Идемпотентно: повторный вызов без изменения высоты не трогает стили.
46
+ */
47
+ freezeHeight(): void;
48
+ /**
49
+ * Снимает фиксацию высоты контейнера.
50
+ * Возвращает инлайн-стили height/overflow к значениям по умолчанию.
51
+ */
52
+ unfreezeHeight(): void;
42
53
  private appendWords;
43
54
  private appendChars;
44
55
  private createWordSpan;
45
56
  private createCharSpan;
46
57
  private createSpaceSpan;
47
- private scheduleFreezeNow;
48
- private scheduleFreezeWordWidths;
49
- private measureWordWidths;
50
- private applyWordWidths;
51
- private freezeWordWidths;
52
- private unfreezeWordWidths;
53
- private attachResizeObserver;
54
- private detachResizeObserver;
55
58
  }
package/dist/index.es.js CHANGED
@@ -1,155 +1,107 @@
1
- const u = { splitMode: "both", cssVariables: !1, dataAttributes: !1, keepWhitespaceNodes: !0, freezeWordWidths: !1 }, o = Object.freeze({ word: "ts-word", char: "ts-char", whitespace: "ts-whitespace" }), h = " ", f = (r) => typeof window > "u" || typeof document > "u" ? null : r ? typeof r == "string" ? document.querySelector(r) : r : null, d = (r) => {
2
- const e = Intl.Segmenter;
3
- if (typeof e == "function") {
4
- const t = new e("en", { granularity: "grapheme" });
5
- return Array.from(t.segment(r), (s) => s.segment);
6
- }
7
- return Array.from(r);
8
- }, c = (r) => r.split(h), a = (r) => {
9
- const e = {};
10
- return Object.keys(r).forEach((t) => {
11
- const s = r[t];
12
- s !== void 0 && (e[t] = s);
13
- }), e;
1
+ const u = { splitMode: "both", cssVariables: !1, dataAttributes: !1, keepWhitespaceNodes: !0, lockContainerHeight: !1 }, o = Object.freeze({ word: "ts-word", char: "ts-char", whitespace: "ts-whitespace" }), r = " ", a = () => typeof window < "u" && typeof document < "u", l = (n) => {
2
+ const t = Intl.Segmenter;
3
+ if (typeof t == "function") {
4
+ const e = new t("en", { granularity: "grapheme" });
5
+ return Array.from(e.segment(n), (s) => s.segment);
6
+ }
7
+ return Array.from(n);
8
+ }, d = (n) => n.split(r), c = (n) => {
9
+ const t = {};
10
+ return Object.keys(n).forEach((e) => {
11
+ const s = n[e];
12
+ s !== void 0 && (t[e] = s);
13
+ }), t;
14
14
  };
15
- class W {
15
+ class f {
16
16
  el;
17
17
  original;
18
18
  opts;
19
19
  callbacks;
20
20
  charIndex;
21
21
  mounted;
22
- ro = null;
23
- frozen = !1;
24
- freezeScheduled = !1;
25
- freezeWordWidthsBound = () => {
26
- this.scheduleFreezeNow();
27
- };
28
- constructor(e = {}, t) {
29
- const s = f(e.container);
30
- this.el = s, this.original = ((i) => !!i && typeof HTMLElement < "u" && i instanceof HTMLElement)(s) ? s.textContent?.toString() ?? "" : "", this.opts = { ...u, ...a(e) }, this.callbacks = t, this.charIndex = 0, this.mounted = !1;
22
+ heightLocked = !1;
23
+ lockedHeightPx = null;
24
+ constructor(t = {}, e) {
25
+ const s = (i = t.container, a() && i ? typeof i == "string" ? document.querySelector(i) : i : null);
26
+ var i;
27
+ this.el = s, this.original = ((h) => !!h && typeof HTMLElement < "u" && h instanceof HTMLElement)(s) ? s.textContent?.toString() ?? "" : "", this.opts = { ...u, ...c(t) }, this.callbacks = e, this.charIndex = 0, this.mounted = !1;
31
28
  }
32
29
  get metrics() {
33
- const e = this.original;
34
- return { wordTotal: e.length ? c(e).length : 0, charTotal: e.length, renderedAt: Date.now() };
30
+ const t = this.original;
31
+ return { wordTotal: t.length ? d(t).length : 0, charTotal: t.length, renderedAt: Date.now() };
35
32
  }
36
33
  init() {
37
- this.el && (this.mounted = !0, this.split());
34
+ this.el && (this.mounted = !0, this.opts.lockContainerHeight && this.freezeHeight(), this.split());
38
35
  }
39
- reinit(e, t) {
40
- this.el && (typeof e == "string" && (this.original = e), t && (this.opts = { ...this.opts, ...a(t) }), this.split());
36
+ reinit(t, e) {
37
+ if (this.el) {
38
+ if (typeof t == "string" && (this.original = t), e) {
39
+ const s = this.opts.lockContainerHeight;
40
+ this.opts = { ...this.opts, ...c(e) }, !s && this.opts.lockContainerHeight ? this.freezeHeight() : s && !this.opts.lockContainerHeight && this.unfreezeHeight();
41
+ }
42
+ this.opts.lockContainerHeight && this.freezeHeight(), this.split();
43
+ }
41
44
  }
42
45
  clear() {
43
- this.el && (this.unfreezeWordWidths(), this.el.replaceChildren());
46
+ this.el && this.el.replaceChildren();
44
47
  }
45
48
  split() {
46
49
  if (!this.el) return;
47
50
  this.clear(), this.charIndex = 0;
48
- const e = this.original, t = document.createDocumentFragment(), s = c(e);
49
- this.opts.splitMode === "chars" ? this.appendChars(t, e) : this.appendWords(t, s), this.el.appendChild(t), this.opts.cssVariables && (this.el.style.setProperty("--word-total", String(s.length)), this.el.style.setProperty("--char-total", String(e.length))), this.scheduleFreezeWordWidths().then(() => {
50
- this.callbacks?.onAfterRender?.(this.metrics);
51
- }).catch(() => {
52
- });
51
+ const t = this.original, e = document.createDocumentFragment(), s = d(t);
52
+ this.opts.splitMode === "chars" ? this.appendChars(e, t) : this.appendWords(e, s), this.el.appendChild(e), this.opts.cssVariables && (this.el.style.setProperty("--word-total", String(s.length)), this.el.style.setProperty("--char-total", String(t.length))), this.callbacks?.onAfterRender?.(this.metrics);
53
53
  }
54
54
  destroy() {
55
- this.el && (this.detachResizeObserver(), this.clear(), this.mounted = !1);
55
+ this.el && (this.clear(), this.mounted = !1, this.unfreezeHeight());
56
56
  }
57
- updateOptions(e) {
58
- this.opts = { ...this.opts, ...a(e) }, this.mounted && this.split();
57
+ updateOptions(t) {
58
+ const e = this.opts.lockContainerHeight;
59
+ this.opts = { ...this.opts, ...c(t) }, !e && this.opts.lockContainerHeight ? this.freezeHeight() : e && !this.opts.lockContainerHeight && this.unfreezeHeight(), this.mounted && this.split();
59
60
  }
60
- appendWords(e, t) {
61
- t.forEach((s, i) => {
61
+ freezeHeight() {
62
+ if (!this.el || !a()) return;
63
+ const t = this.el.getBoundingClientRect(), e = Math.max(0, Math.round(t.height));
64
+ e && (this.heightLocked && this.lockedHeightPx === e || (this.el.style.height = `${e}px`, this.el.style.overflow = "hidden", this.heightLocked = !0, this.lockedHeightPx = e));
65
+ }
66
+ unfreezeHeight() {
67
+ this.el && a() && (this.heightLocked && (this.el.style.height = "", this.el.style.overflow = ""), this.heightLocked = !1, this.lockedHeightPx = null);
68
+ }
69
+ appendWords(t, e) {
70
+ e.forEach((s, i) => {
62
71
  if (this.opts.splitMode === "both") {
63
- const n = this.createWordSpan(i, s);
64
- for (const l of d(s)) {
65
- const p = this.createCharSpan(l);
66
- n.append(p);
72
+ const h = this.createWordSpan(i, s);
73
+ for (const p of l(s)) {
74
+ const g = this.createCharSpan(p);
75
+ h.append(g);
67
76
  }
68
- e.append(n);
77
+ t.append(h);
69
78
  } else {
70
- const n = this.createWordSpan(i);
71
- n.append(document.createTextNode(s)), e.append(n);
79
+ const h = this.createWordSpan(i);
80
+ h.append(document.createTextNode(s)), t.append(h);
72
81
  }
73
- i < t.length - 1 && e.append(this.createSpaceSpan());
82
+ i < e.length - 1 && t.append(this.createSpaceSpan());
74
83
  });
75
84
  }
76
- appendChars(e, t) {
77
- for (const s of d(t)) {
85
+ appendChars(t, e) {
86
+ for (const s of l(e)) {
78
87
  const i = this.createCharSpan(s);
79
- e.append(i);
88
+ t.append(i);
80
89
  }
81
90
  }
82
- createWordSpan(e, t = "") {
91
+ createWordSpan(t, e = "") {
83
92
  const s = document.createElement("span");
84
- return s.classList.add(o.word), this.opts.dataAttributes && t && s.setAttribute("data-word", t), this.opts.cssVariables && s.style.setProperty("--word-index", String(e)), s;
93
+ return s.classList.add(o.word), this.opts.dataAttributes && e && s.setAttribute("data-word", e), this.opts.cssVariables && s.style.setProperty("--word-index", String(t)), s;
85
94
  }
86
- createCharSpan(e) {
87
- const t = document.createElement("span");
88
- return t.textContent = e, this.opts.dataAttributes && t.setAttribute("data-char", e), e === h ? (t.classList.add(o.whitespace), this.opts.keepWhitespaceNodes || (t.textContent = h)) : (t.classList.add(o.char), this.opts.cssVariables && t.style.setProperty("--char-index", String(this.charIndex)), this.charIndex += 1), t;
89
- }
90
- createSpaceSpan() {
95
+ createCharSpan(t) {
91
96
  const e = document.createElement("span");
92
- return e.classList.add(o.whitespace), e.textContent = h, e;
93
- }
94
- scheduleFreezeNow() {
95
- this.el && this.opts.freezeWordWidths && (this.freezeScheduled || (this.freezeScheduled = !0, requestAnimationFrame(() => {
96
- this.freezeScheduled = !1, this.freezeWordWidths();
97
- })));
98
- }
99
- async scheduleFreezeWordWidths() {
100
- if (this.el && this.opts.freezeWordWidths) {
101
- try {
102
- await document.fonts?.ready;
103
- } catch {
104
- }
105
- await new Promise((e) => requestAnimationFrame(() => e())), this.scheduleFreezeNow(), this.attachResizeObserver();
106
- }
97
+ return e.textContent = t, this.opts.dataAttributes && e.setAttribute("data-char", t), t === r ? (e.classList.add(o.whitespace), this.opts.keepWhitespaceNodes || (e.textContent = r)) : (e.classList.add(o.char), this.opts.cssVariables && e.style.setProperty("--char-index", String(this.charIndex)), this.charIndex += 1), e;
107
98
  }
108
- measureWordWidths() {
109
- if (!this.el) return [];
110
- const e = this.el.querySelectorAll(`.${o.word}`);
111
- return Array.from(e, (t) => {
112
- t.style.width = "", t.style.flex = "";
113
- const { width: s } = t.getBoundingClientRect();
114
- return Math.ceil(s);
115
- });
116
- }
117
- applyWordWidths(e) {
118
- this.el && this.el.querySelectorAll(`.${o.word}`).forEach((t, s) => {
119
- const i = e[s] ?? 0;
120
- t.style.width = `${i}px`, t.style.flex = "0 0 auto";
121
- });
122
- }
123
- freezeWordWidths() {
124
- if (!this.el) return;
125
- const e = this.measureWordWidths();
126
- this.applyWordWidths(e), this.frozen = !0;
127
- }
128
- unfreezeWordWidths() {
129
- !this.el || !this.frozen || (this.el.querySelectorAll(`.${o.word}`).forEach((e) => {
130
- e.style.width = "", e.style.flex = "";
131
- }), this.frozen = !1);
132
- }
133
- attachResizeObserver() {
134
- if (this.el && this.opts.freezeWordWidths) {
135
- if (!this.ro) {
136
- this.ro = new ResizeObserver(() => {
137
- this.scheduleFreezeNow();
138
- });
139
- try {
140
- this.ro.observe(this.el, { box: "border-box" });
141
- } catch {
142
- this.ro.observe(this.el);
143
- }
144
- }
145
- window.addEventListener("resize", this.freezeWordWidthsBound, { passive: !0 });
146
- }
147
- }
148
- detachResizeObserver() {
149
- this.ro && (this.ro.disconnect(), this.ro = null), window.removeEventListener("resize", this.freezeWordWidthsBound);
99
+ createSpaceSpan() {
100
+ const t = document.createElement("span");
101
+ return t.classList.add(o.whitespace), t.textContent = r, t;
150
102
  }
151
103
  }
152
104
  export {
153
105
  o as CLASSNAMES,
154
- W as TextSlicer
106
+ f as TextSlicer
155
107
  };
package/dist/index.umd.js CHANGED
@@ -1 +1 @@
1
- (function(o,n){typeof exports=="object"&&typeof module<"u"?n(exports):typeof define=="function"&&define.amd?define(["exports"],n):n((o=typeof globalThis<"u"?globalThis:o||self).TextSlicer={})})(this,function(o){"use strict";const n={splitMode:"both",cssVariables:!1,dataAttributes:!1,keepWhitespaceNodes:!0,freezeWordWidths:!1},i=Object.freeze({word:"ts-word",char:"ts-char",whitespace:"ts-whitespace"}),d=" ",p=e=>typeof window>"u"||typeof document>"u"?null:e?typeof e=="string"?document.querySelector(e):e:null,c=e=>{const t=Intl.Segmenter;if(typeof t=="function"){const s=new t("en",{granularity:"grapheme"});return Array.from(s.segment(e),r=>r.segment)}return Array.from(e)},l=e=>e.split(d),a=e=>{const t={};return Object.keys(e).forEach(s=>{const r=e[s];r!==void 0&&(t[s]=r)}),t};o.CLASSNAMES=i,o.TextSlicer=class{el;original;opts;callbacks;charIndex;mounted;ro=null;frozen=!1;freezeScheduled=!1;freezeWordWidthsBound=()=>{this.scheduleFreezeNow()};constructor(e={},t){const s=p(e.container);this.el=s,this.original=(r=>!!r&&typeof HTMLElement<"u"&&r instanceof HTMLElement)(s)?s.textContent?.toString()??"":"",this.opts={...n,...a(e)},this.callbacks=t,this.charIndex=0,this.mounted=!1}get metrics(){const e=this.original;return{wordTotal:e.length?l(e).length:0,charTotal:e.length,renderedAt:Date.now()}}init(){this.el&&(this.mounted=!0,this.split())}reinit(e,t){this.el&&(typeof e=="string"&&(this.original=e),t&&(this.opts={...this.opts,...a(t)}),this.split())}clear(){this.el&&(this.unfreezeWordWidths(),this.el.replaceChildren())}split(){if(!this.el)return;this.clear(),this.charIndex=0;const e=this.original,t=document.createDocumentFragment(),s=l(e);this.opts.splitMode==="chars"?this.appendChars(t,e):this.appendWords(t,s),this.el.appendChild(t),this.opts.cssVariables&&(this.el.style.setProperty("--word-total",String(s.length)),this.el.style.setProperty("--char-total",String(e.length))),this.scheduleFreezeWordWidths().then(()=>{this.callbacks?.onAfterRender?.(this.metrics)}).catch(()=>{})}destroy(){this.el&&(this.detachResizeObserver(),this.clear(),this.mounted=!1)}updateOptions(e){this.opts={...this.opts,...a(e)},this.mounted&&this.split()}appendWords(e,t){t.forEach((s,r)=>{if(this.opts.splitMode==="both"){const h=this.createWordSpan(r,s);for(const u of c(s)){const f=this.createCharSpan(u);h.append(f)}e.append(h)}else{const h=this.createWordSpan(r);h.append(document.createTextNode(s)),e.append(h)}r<t.length-1&&e.append(this.createSpaceSpan())})}appendChars(e,t){for(const s of c(t)){const r=this.createCharSpan(s);e.append(r)}}createWordSpan(e,t=""){const s=document.createElement("span");return s.classList.add(i.word),this.opts.dataAttributes&&t&&s.setAttribute("data-word",t),this.opts.cssVariables&&s.style.setProperty("--word-index",String(e)),s}createCharSpan(e){const t=document.createElement("span");return t.textContent=e,this.opts.dataAttributes&&t.setAttribute("data-char",e),e===d?(t.classList.add(i.whitespace),this.opts.keepWhitespaceNodes||(t.textContent=d)):(t.classList.add(i.char),this.opts.cssVariables&&t.style.setProperty("--char-index",String(this.charIndex)),this.charIndex+=1),t}createSpaceSpan(){const e=document.createElement("span");return e.classList.add(i.whitespace),e.textContent=d,e}scheduleFreezeNow(){this.el&&this.opts.freezeWordWidths&&(this.freezeScheduled||(this.freezeScheduled=!0,requestAnimationFrame(()=>{this.freezeScheduled=!1,this.freezeWordWidths()})))}async scheduleFreezeWordWidths(){if(this.el&&this.opts.freezeWordWidths){try{await document.fonts?.ready}catch{}await new Promise(e=>requestAnimationFrame(()=>e())),this.scheduleFreezeNow(),this.attachResizeObserver()}}measureWordWidths(){if(!this.el)return[];const e=this.el.querySelectorAll(`.${i.word}`);return Array.from(e,t=>{t.style.width="",t.style.flex="";const{width:s}=t.getBoundingClientRect();return Math.ceil(s)})}applyWordWidths(e){this.el&&this.el.querySelectorAll(`.${i.word}`).forEach((t,s)=>{const r=e[s]??0;t.style.width=`${r}px`,t.style.flex="0 0 auto"})}freezeWordWidths(){if(!this.el)return;const e=this.measureWordWidths();this.applyWordWidths(e),this.frozen=!0}unfreezeWordWidths(){!this.el||!this.frozen||(this.el.querySelectorAll(`.${i.word}`).forEach(e=>{e.style.width="",e.style.flex=""}),this.frozen=!1)}attachResizeObserver(){if(this.el&&this.opts.freezeWordWidths){if(!this.ro){this.ro=new ResizeObserver(()=>{this.scheduleFreezeNow()});try{this.ro.observe(this.el,{box:"border-box"})}catch{this.ro.observe(this.el)}}window.addEventListener("resize",this.freezeWordWidthsBound,{passive:!0})}}detachResizeObserver(){this.ro&&(this.ro.disconnect(),this.ro=null),window.removeEventListener("resize",this.freezeWordWidthsBound)}},Object.defineProperty(o,Symbol.toStringTag,{value:"Module"})});
1
+ (function(o,h){typeof exports=="object"&&typeof module<"u"?h(exports):typeof define=="function"&&define.amd?define(["exports"],h):h((o=typeof globalThis<"u"?globalThis:o||self).TextSlicer={})})(this,function(o){"use strict";const h={splitMode:"both",cssVariables:!1,dataAttributes:!1,keepWhitespaceNodes:!0,lockContainerHeight:!1},r=Object.freeze({word:"ts-word",char:"ts-char",whitespace:"ts-whitespace"}),a=" ",c=()=>typeof window<"u"&&typeof document<"u",d=t=>{const e=Intl.Segmenter;if(typeof e=="function"){const s=new e("en",{granularity:"grapheme"});return Array.from(s.segment(t),i=>i.segment)}return Array.from(t)},p=t=>t.split(a),l=t=>{const e={};return Object.keys(t).forEach(s=>{const i=t[s];i!==void 0&&(e[s]=i)}),e};o.CLASSNAMES=r,o.TextSlicer=class{el;original;opts;callbacks;charIndex;mounted;heightLocked=!1;lockedHeightPx=null;constructor(t={},e){const s=(i=t.container,c()&&i?typeof i=="string"?document.querySelector(i):i:null);var i;this.el=s,this.original=(n=>!!n&&typeof HTMLElement<"u"&&n instanceof HTMLElement)(s)?s.textContent?.toString()??"":"",this.opts={...h,...l(t)},this.callbacks=e,this.charIndex=0,this.mounted=!1}get metrics(){const t=this.original;return{wordTotal:t.length?p(t).length:0,charTotal:t.length,renderedAt:Date.now()}}init(){this.el&&(this.mounted=!0,this.opts.lockContainerHeight&&this.freezeHeight(),this.split())}reinit(t,e){if(this.el){if(typeof t=="string"&&(this.original=t),e){const s=this.opts.lockContainerHeight;this.opts={...this.opts,...l(e)},!s&&this.opts.lockContainerHeight?this.freezeHeight():s&&!this.opts.lockContainerHeight&&this.unfreezeHeight()}this.opts.lockContainerHeight&&this.freezeHeight(),this.split()}}clear(){this.el&&this.el.replaceChildren()}split(){if(!this.el)return;this.clear(),this.charIndex=0;const t=this.original,e=document.createDocumentFragment(),s=p(t);this.opts.splitMode==="chars"?this.appendChars(e,t):this.appendWords(e,s),this.el.appendChild(e),this.opts.cssVariables&&(this.el.style.setProperty("--word-total",String(s.length)),this.el.style.setProperty("--char-total",String(t.length))),this.callbacks?.onAfterRender?.(this.metrics)}destroy(){this.el&&(this.clear(),this.mounted=!1,this.unfreezeHeight())}updateOptions(t){const e=this.opts.lockContainerHeight;this.opts={...this.opts,...l(t)},!e&&this.opts.lockContainerHeight?this.freezeHeight():e&&!this.opts.lockContainerHeight&&this.unfreezeHeight(),this.mounted&&this.split()}freezeHeight(){if(!this.el||!c())return;const t=this.el.getBoundingClientRect(),e=Math.max(0,Math.round(t.height));e&&(this.heightLocked&&this.lockedHeightPx===e||(this.el.style.height=`${e}px`,this.el.style.overflow="hidden",this.heightLocked=!0,this.lockedHeightPx=e))}unfreezeHeight(){this.el&&c()&&(this.heightLocked&&(this.el.style.height="",this.el.style.overflow=""),this.heightLocked=!1,this.lockedHeightPx=null)}appendWords(t,e){e.forEach((s,i)=>{if(this.opts.splitMode==="both"){const n=this.createWordSpan(i,s);for(const g of d(s)){const u=this.createCharSpan(g);n.append(u)}t.append(n)}else{const n=this.createWordSpan(i);n.append(document.createTextNode(s)),t.append(n)}i<e.length-1&&t.append(this.createSpaceSpan())})}appendChars(t,e){for(const s of d(e)){const i=this.createCharSpan(s);t.append(i)}}createWordSpan(t,e=""){const s=document.createElement("span");return s.classList.add(r.word),this.opts.dataAttributes&&e&&s.setAttribute("data-word",e),this.opts.cssVariables&&s.style.setProperty("--word-index",String(t)),s}createCharSpan(t){const e=document.createElement("span");return e.textContent=t,this.opts.dataAttributes&&e.setAttribute("data-char",t),t===a?(e.classList.add(r.whitespace),this.opts.keepWhitespaceNodes||(e.textContent=a)):(e.classList.add(r.char),this.opts.cssVariables&&e.style.setProperty("--char-index",String(this.charIndex)),this.charIndex+=1),e}createSpaceSpan(){const t=document.createElement("span");return t.classList.add(r.whitespace),t.textContent=a,t}},Object.defineProperty(o,Symbol.toStringTag,{value:"Module"})});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "text-slicer",
3
- "version": "1.4.1",
3
+ "version": "1.5.0-dev.1",
4
4
  "description": "TextSlicer is designed to split text within an HTML element into separate words and/or characters, wrapping each word and/or character in separate span elements.",
5
5
  "author": "ux-ui.pro",
6
6
  "license": "MIT",