torph 0.0.4 → 0.0.6

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
@@ -17,16 +17,19 @@ yarn add torph
17
17
  ### React
18
18
 
19
19
  ```tsx
20
- import { TextMorph } from 'torph/react';
20
+ import { TextMorph } from "torph/react";
21
21
 
22
22
  function App() {
23
- const [text, setText] = useState('Hello World');
23
+ const [text, setText] = useState("Hello World");
24
24
 
25
25
  return (
26
26
  <TextMorph
27
27
  duration={400}
28
28
  ease="cubic-bezier(0.19, 1, 0.22, 1)"
29
29
  locale="en"
30
+ onAnimationComplete={() => console.log("Animation done!")}
31
+ className="my-text"
32
+ as="h1"
30
33
  >
31
34
  {text}
32
35
  </TextMorph>
@@ -37,7 +40,7 @@ function App() {
37
40
  #### React Hook
38
41
 
39
42
  ```tsx
40
- import { useTextMorph } from 'torph/react';
43
+ import { useTextMorph } from "torph/react";
41
44
 
42
45
  function CustomComponent() {
43
46
  const { ref, update } = useTextMorph({
@@ -46,7 +49,7 @@ function CustomComponent() {
46
49
  });
47
50
 
48
51
  useEffect(() => {
49
- update('Hello World');
52
+ update("Hello World");
50
53
  }, []);
51
54
 
52
55
  return <div ref={ref} />;
@@ -57,10 +60,14 @@ function CustomComponent() {
57
60
 
58
61
  ```vue
59
62
  <script setup>
60
- import { ref } from 'vue';
61
- import { TextMorph } from 'torph/vue';
63
+ import { ref } from "vue";
64
+ import { TextMorph } from "torph/vue";
62
65
 
63
- const text = ref('Hello World');
66
+ const text = ref("Hello World");
67
+
68
+ const handleComplete = () => {
69
+ console.log("Animation done!");
70
+ };
64
71
  </script>
65
72
 
66
73
  <template>
@@ -69,6 +76,9 @@ const text = ref('Hello World');
69
76
  :duration="400"
70
77
  ease="cubic-bezier(0.19, 1, 0.22, 1)"
71
78
  locale="en"
79
+ :onAnimationComplete="handleComplete"
80
+ class="my-text"
81
+ as="h1"
72
82
  />
73
83
  </template>
74
84
  ```
@@ -78,8 +88,12 @@ const text = ref('Hello World');
78
88
  ```svelte
79
89
  <script>
80
90
  import { TextMorph } from 'torph/svelte';
81
-
82
- let text = 'Hello World';
91
+
92
+ let text = $state('Hello World');
93
+
94
+ const handleComplete = () => {
95
+ console.log('Animation done!');
96
+ };
83
97
  </script>
84
98
 
85
99
  <TextMorph
@@ -87,22 +101,27 @@ const text = ref('Hello World');
87
101
  duration={400}
88
102
  ease="cubic-bezier(0.19, 1, 0.22, 1)"
89
103
  locale="en"
104
+ onAnimationComplete={handleComplete}
105
+ class="my-text"
106
+ as="h1"
90
107
  />
91
108
  ```
92
109
 
93
110
  ### Vanilla JS
94
111
 
95
112
  ```js
96
- import { TextMorph } from 'torph';
113
+ import { TextMorph } from "torph";
97
114
 
98
115
  const morph = new TextMorph({
99
- element: document.getElementById('morph'),
116
+ element: document.getElementById("morph"),
100
117
  duration: 400,
101
- ease: 'cubic-bezier(0.19, 1, 0.22, 1)',
102
- locale: 'en',
118
+ ease: "cubic-bezier(0.19, 1, 0.22, 1)",
119
+ locale: "en",
120
+ onAnimationStart: () => console.log("Starting..."),
121
+ onAnimationComplete: () => console.log("Done!"),
103
122
  });
104
123
 
105
- morph.update('Hello World');
124
+ morph.update("Hello World");
106
125
  ```
107
126
 
108
127
  ## API
@@ -116,6 +135,13 @@ All components accept the following props/options:
116
135
  - `ease?: string` - CSS easing function (default: "cubic-bezier(0.19, 1, 0.22, 1)")
117
136
  - `locale?: Intl.LocalesArgument` - Locale for text segmentation (default: "en")
118
137
  - `debug?: boolean` - Enable debug mode with visual indicators
138
+ - `disabled?: boolean` - Disable all morphing animations (default: false)
139
+ - `respectReducedMotion?: boolean` - Respect user's prefers-reduced-motion setting (default: true)
140
+ - `onAnimationStart?: () => void` - Callback fired when animation begins
141
+ - `onAnimationComplete?: () => void` - Callback fired when animation completes
142
+ - `className?: string` - CSS class name (React/Vue: `class`)
143
+ - `style?: object | string` - Inline styles
144
+ - `as?: string` - HTML element type (default: "div")
119
145
 
120
146
  ## Found this useful?
121
147
 
package/dist/.DS_Store CHANGED
Binary file
@@ -0,0 +1,60 @@
1
+ <template>
2
+ <component
3
+ :is="as"
4
+ ref="containerRef"
5
+ :class="props.class"
6
+ :style="props.style"
7
+ ></component>
8
+ </template>
9
+
10
+ <script setup lang="ts">
11
+ import { ref, onMounted, onUnmounted, watch } from "vue";
12
+ import {
13
+ DEFAULT_AS,
14
+ DEFAULT_TEXT_MORPH_OPTIONS,
15
+ TextMorph as Morph,
16
+ } from "../lib/text-morph";
17
+ import type { TextMorphProps } from "./types";
18
+
19
+ const props = withDefaults(defineProps<TextMorphProps>(), {
20
+ locale: DEFAULT_TEXT_MORPH_OPTIONS.locale,
21
+ duration: DEFAULT_TEXT_MORPH_OPTIONS.duration,
22
+ ease: DEFAULT_TEXT_MORPH_OPTIONS.ease,
23
+ scale: DEFAULT_TEXT_MORPH_OPTIONS.scale,
24
+ disabled: DEFAULT_TEXT_MORPH_OPTIONS.disabled,
25
+ respectReducedMotion: DEFAULT_TEXT_MORPH_OPTIONS.respectReducedMotion,
26
+ as: DEFAULT_AS,
27
+ });
28
+
29
+ const containerRef = ref<HTMLElement | null>(null);
30
+ let morphInstance: Morph | null = null;
31
+
32
+ onMounted(() => {
33
+ if (containerRef.value) {
34
+ morphInstance = new Morph({
35
+ element: containerRef.value,
36
+ locale: props.locale,
37
+ duration: props.duration,
38
+ ease: props.ease,
39
+ debug: props.debug,
40
+ scale: props.scale,
41
+ disabled: props.disabled,
42
+ respectReducedMotion: props.respectReducedMotion,
43
+ onAnimationStart: props.onAnimationStart,
44
+ onAnimationComplete: props.onAnimationComplete,
45
+ });
46
+ morphInstance.update(props.text);
47
+ }
48
+ });
49
+
50
+ onUnmounted(() => {
51
+ morphInstance?.destroy();
52
+ });
53
+
54
+ watch(
55
+ () => props.text,
56
+ (newText) => {
57
+ morphInstance?.update(newText);
58
+ },
59
+ );
60
+ </script>
package/dist/index.d.mts CHANGED
@@ -1,7 +1,26 @@
1
- import { T as TextMorphOptions } from './types-CsvRfPun.mjs';
2
-
3
- var version = "0.0.4";
1
+ interface TextMorphOptions {
2
+ debug?: boolean;
3
+ element: HTMLElement;
4
+ locale?: Intl.LocalesArgument;
5
+ scale?: boolean;
6
+ duration?: number;
7
+ ease?: string;
8
+ disabled?: boolean;
9
+ respectReducedMotion?: boolean;
10
+ onAnimationStart?: () => void;
11
+ onAnimationComplete?: () => void;
12
+ }
4
13
 
14
+ declare const DEFAULT_AS = "span";
15
+ declare const DEFAULT_TEXT_MORPH_OPTIONS: {
16
+ readonly debug: false;
17
+ readonly locale: "en";
18
+ readonly duration: 400;
19
+ readonly scale: true;
20
+ readonly ease: "cubic-bezier(0.19, 1, 0.22, 1)";
21
+ readonly disabled: false;
22
+ readonly respectReducedMotion: true;
23
+ };
5
24
  declare class TextMorph {
6
25
  private element;
7
26
  private options;
@@ -9,9 +28,13 @@ declare class TextMorph {
9
28
  private currentMeasures;
10
29
  private prevMeasures;
11
30
  private isInitialRender;
31
+ private prefersReducedMotion;
32
+ private mediaQuery?;
12
33
  static styleEl: HTMLStyleElement;
13
34
  constructor(options: TextMorphOptions);
14
35
  destroy(): void;
36
+ private handleMediaQueryChange;
37
+ private isDisabled;
15
38
  update(value: HTMLElement | string): void;
16
39
  private createTextGroup;
17
40
  private measure;
@@ -19,8 +42,8 @@ declare class TextMorph {
19
42
  private addStyles;
20
43
  private removeStyles;
21
44
  private blocks;
22
- private getUnscaledBoundingClientRect;
45
+ private blocksFallback;
23
46
  private log;
24
47
  }
25
48
 
26
- export { TextMorph, TextMorphOptions, version };
49
+ export { DEFAULT_AS, DEFAULT_TEXT_MORPH_OPTIONS, TextMorph, type TextMorphOptions };
package/dist/index.d.ts CHANGED
@@ -1,7 +1,26 @@
1
- import { T as TextMorphOptions } from './types-CsvRfPun.js';
2
-
3
- var version = "0.0.4";
1
+ interface TextMorphOptions {
2
+ debug?: boolean;
3
+ element: HTMLElement;
4
+ locale?: Intl.LocalesArgument;
5
+ scale?: boolean;
6
+ duration?: number;
7
+ ease?: string;
8
+ disabled?: boolean;
9
+ respectReducedMotion?: boolean;
10
+ onAnimationStart?: () => void;
11
+ onAnimationComplete?: () => void;
12
+ }
4
13
 
14
+ declare const DEFAULT_AS = "span";
15
+ declare const DEFAULT_TEXT_MORPH_OPTIONS: {
16
+ readonly debug: false;
17
+ readonly locale: "en";
18
+ readonly duration: 400;
19
+ readonly scale: true;
20
+ readonly ease: "cubic-bezier(0.19, 1, 0.22, 1)";
21
+ readonly disabled: false;
22
+ readonly respectReducedMotion: true;
23
+ };
5
24
  declare class TextMorph {
6
25
  private element;
7
26
  private options;
@@ -9,9 +28,13 @@ declare class TextMorph {
9
28
  private currentMeasures;
10
29
  private prevMeasures;
11
30
  private isInitialRender;
31
+ private prefersReducedMotion;
32
+ private mediaQuery?;
12
33
  static styleEl: HTMLStyleElement;
13
34
  constructor(options: TextMorphOptions);
14
35
  destroy(): void;
36
+ private handleMediaQueryChange;
37
+ private isDisabled;
15
38
  update(value: HTMLElement | string): void;
16
39
  private createTextGroup;
17
40
  private measure;
@@ -19,8 +42,8 @@ declare class TextMorph {
19
42
  private addStyles;
20
43
  private removeStyles;
21
44
  private blocks;
22
- private getUnscaledBoundingClientRect;
45
+ private blocksFallback;
23
46
  private log;
24
47
  }
25
48
 
26
- export { TextMorph, TextMorphOptions, version };
49
+ export { DEFAULT_AS, DEFAULT_TEXT_MORPH_OPTIONS, TextMorph, type TextMorphOptions };
package/dist/index.js CHANGED
@@ -1,10 +1,10 @@
1
- "use client";
2
- "use strict";var M=Object.defineProperty;var L=Object.getOwnPropertyDescriptor;var R=Object.getOwnPropertyNames;var k=Object.prototype.hasOwnProperty;var C=(o,e)=>{for(var t in e)M(o,t,{get:e[t],enumerable:!0})},j=(o,e,t,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of R(e))!k.call(o,i)&&i!==t&&M(o,i,{get:()=>e[i],enumerable:!(n=L(e,i))||n.enumerable});return o};var I=o=>j(M({},"__esModule",{value:!0}),o);var O={};C(O,{TextMorph:()=>x,version:()=>w});module.exports=I(O);var w="0.0.4";var x=class o{element;options={};data;currentMeasures={};prevMeasures={};isInitialRender=!0;static styleEl;constructor(e){this.options={locale:"en",duration:400,ease:"cubic-bezier(0.19, 1, 0.22, 1)",...e},this.element=e.element,this.element.setAttribute("torph-root",""),this.element.style.transitionDuration=`${this.options.duration}ms`,this.element.style.transitionTimingFunction=this.options.ease,e.debug&&this.element.setAttribute("torph-debug",""),this.data=this.element.innerHTML,this.addStyles()}destroy(){this.element.getAnimations().forEach(e=>e.cancel()),this.element.removeAttribute("torph-root"),this.element.removeAttribute("torph-debug"),this.removeStyles()}update(e){if(e!==this.data){if(this.data=e,this.data instanceof HTMLElement)throw new Error("HTMLElement not yet supported");this.createTextGroup(this.data,this.element)}}createTextGroup(e,t){let n=t.offsetWidth,i=t.offsetHeight,r=e.includes(" "),g=new Intl.Segmenter(this.options.locale,{granularity:r?"word":"grapheme"}).segment(e)[Symbol.iterator](),m=this.blocks(g);this.prevMeasures=this.measure();let h=Array.from(t.children),d=new Set(m.map(s=>s.id)),p=h.filter(s=>!d.has(s.getAttribute("torph-id"))&&!s.hasAttribute("torph-exiting")),u=this.getUnscaledBoundingClientRect(t);if(p.forEach(s=>{let a=this.getUnscaledBoundingClientRect(s);s.setAttribute("torph-exiting",""),s.style.position="absolute",s.style.pointerEvents="none",s.style.left=`${a.left-u.left}px`,s.style.top=`${a.top-u.top}px`,s.style.width=`${a.width}px`,s.style.height=`${a.height}px`}),h.forEach(s=>{let a=s.getAttribute("torph-id");d.has(a)&&s.remove()}),m.forEach(s=>{let a=document.createElement("span");a.setAttribute("torph-item",""),a.setAttribute("torph-id",s.id),a.textContent=s.string,t.appendChild(a)}),this.currentMeasures=this.measure(),this.updateStyles(),p.forEach(s=>{if(this.isInitialRender){s.remove();return}let a=s.getAttribute("torph-id"),v=this.prevMeasures[a],E=Array.from(t.children).find(b=>{let S=b.getBoundingClientRect(),$=s.getBoundingClientRect();return Math.abs(S.left-$.left)<40}),f=E?this.currentMeasures[E.getAttribute("torph-id")]:v,A=(f?f.x-(v?.x||0):0)*.5,T=(f?f.y-(v?.y||0):0)*.5;s.getAnimations().forEach(b=>b.cancel());let H=s.animate({transform:`translate(${A}px, ${T}px) scale(0.95)`,opacity:0,offset:1},{duration:this.options.duration,easing:this.options.ease,fill:"both"});H.onfinish=()=>s.remove()}),this.isInitialRender){this.isInitialRender=!1,t.style.width="auto",t.style.height="auto";return}if(n===0||i===0)return;t.style.width="auto",t.style.height="auto",t.offsetWidth;let c=t.offsetWidth,y=t.offsetHeight;t.style.width=`${n}px`,t.style.height=`${i}px`,t.offsetWidth,t.style.width=`${c}px`,t.style.height=`${y}px`,setTimeout(()=>{t.style.width="auto",t.style.height="auto"},this.options.duration)}measure(){let e=Array.from(this.element.children),t={};return e.forEach((n,i)=>{if(n.hasAttribute("torph-exiting"))return;let r=n.getAttribute("torph-id")||`child-${i}`;t[r]={x:n.offsetLeft,y:n.offsetTop}}),t}updateStyles(){if(this.isInitialRender)return;Array.from(this.element.children).forEach((t,n)=>{if(t.hasAttribute("torph-exiting"))return;let i=t.getAttribute("torph-id")||`child-${n}`,r=this.prevMeasures[i],l=this.currentMeasures[i],g=l?.x||0,m=l?.y||0,h=r?r?.x-g:0,d=r?r?.y-m:0,p=!r;t.getAnimations().forEach(u=>u.cancel()),t.animate({transform:`translate(${h}px, ${d}px) scale(${p?.95:1})`,opacity:p?0:1,offset:0},{duration:this.options.duration,easing:this.options.ease,delay:p?this.options.duration*.2:0,fill:"both"})})}addStyles(){if(o.styleEl)return;let e=document.createElement("style");e.dataset.torph="true",e.innerHTML=`
1
+ 'use strict';var T="span",A={debug:false,locale:"en",duration:400,scale:true,ease:"cubic-bezier(0.19, 1, 0.22, 1)",disabled:false,respectReducedMotion:true},b=class g{element;options={};data;currentMeasures={};prevMeasures={};isInitialRender=true;prefersReducedMotion=false;mediaQuery;static styleEl;constructor(e){this.options={...A,...e},this.element=e.element,typeof window<"u"&&this.options.respectReducedMotion&&(this.mediaQuery=window.matchMedia("(prefers-reduced-motion: reduce)"),this.prefersReducedMotion=this.mediaQuery.matches,this.mediaQuery.addEventListener("change",this.handleMediaQueryChange)),this.isDisabled()||(this.element.setAttribute("torph-root",""),this.element.style.transitionDuration=`${this.options.duration}ms`,this.element.style.transitionTimingFunction=this.options.ease,e.debug&&this.element.setAttribute("torph-debug","")),this.data="",this.isDisabled()||this.addStyles();}destroy(){this.mediaQuery&&this.mediaQuery.removeEventListener("change",this.handleMediaQueryChange),this.element.getAnimations().forEach(e=>e.cancel()),this.element.removeAttribute("torph-root"),this.element.removeAttribute("torph-debug"),this.removeStyles();}handleMediaQueryChange=e=>{this.prefersReducedMotion=e.matches;};isDisabled(){return !!(this.options.disabled||this.options.respectReducedMotion&&this.prefersReducedMotion)}update(e){if(e!==this.data){if(this.data=e,this.isDisabled()){typeof e=="string"&&(this.element.textContent=e);return}if(this.data instanceof HTMLElement)throw new Error("HTMLElement not yet supported");this.options.onAnimationStart&&!this.isInitialRender&&this.options.onAnimationStart(),this.createTextGroup(this.data,this.element);}}createTextGroup(e,s){let h=s.offsetWidth,i=s.offsetHeight,a=e.includes(" "),p;if(typeof Intl.Segmenter<"u"){let r=new Intl.Segmenter(this.options.locale,{granularity:a?"word":"grapheme"}).segment(e)[Symbol.iterator]();p=this.blocks(r);}else p=this.blocksFallback(e,a);this.prevMeasures=this.measure();let l=Array.from(s.children),d=new Set(p.map(t=>t.id)),c=l.filter(t=>!d.has(t.getAttribute("torph-id"))&&!t.hasAttribute("torph-exiting")),m=new Set(c),y=new Map;for(let t=0;t<l.length;t++){let r=l[t];if(!m.has(r))continue;let o=null;for(let n=t+1;n<l.length;n++){let u=l[n].getAttribute("torph-id");if(d.has(u)&&!m.has(l[n])){o=u;break}}if(!o)for(let n=t-1;n>=0;n--){let u=l[n].getAttribute("torph-id");if(d.has(u)&&!m.has(l[n])){o=u;break}}y.set(r,o);}let x=c.map(t=>(t.getAnimations().forEach(r=>r.cancel()),{left:t.offsetLeft,top:t.offsetTop,width:t.offsetWidth,height:t.offsetHeight}));if(c.forEach((t,r)=>{let o=x[r];t.setAttribute("torph-exiting",""),t.style.position="absolute",t.style.pointerEvents="none",t.style.left=`${o.left}px`,t.style.top=`${o.top}px`,t.style.width=`${o.width}px`,t.style.height=`${o.height}px`;}),l.forEach(t=>{let r=t.getAttribute("torph-id");d.has(r)&&t.remove();}),Array.from(s.childNodes).forEach(t=>{t.nodeType===Node.TEXT_NODE&&t.remove();}),p.forEach(t=>{let r=document.createElement("span");r.setAttribute("torph-item",""),r.setAttribute("torph-id",t.id),r.textContent=t.string,s.appendChild(r);}),this.currentMeasures=this.measure(),this.updateStyles(p),c.forEach(t=>{if(this.isInitialRender){t.remove();return}let r=y.get(t),o=0,n=0;if(r&&this.prevMeasures[r]&&this.currentMeasures[r]){let E=this.prevMeasures[r],v=this.currentMeasures[r];o=v.x-E.x,n=v.y-E.y;}t.animate({transform:this.options.scale?`translate(${o}px, ${n}px) scale(0.95)`:`translate(${o}px, ${n}px)`,offset:1},{duration:this.options.duration,easing:this.options.ease,fill:"both"});let u=t.animate({opacity:0,offset:1},{duration:this.options.duration*.25,easing:"linear",fill:"both"});u.onfinish=()=>t.remove();}),this.isInitialRender){this.isInitialRender=false,s.style.width="auto",s.style.height="auto";return}if(h===0||i===0)return;s.style.width="auto",s.style.height="auto",s.offsetWidth;let f=s.offsetWidth,M=s.offsetHeight;s.style.width=`${h}px`,s.style.height=`${i}px`,s.offsetWidth,s.style.width=`${f}px`,s.style.height=`${M}px`,setTimeout(()=>{s.style.width="auto",s.style.height="auto",this.options.onAnimationComplete&&this.options.onAnimationComplete();},this.options.duration);}measure(){let e=Array.from(this.element.children),s={};return e.forEach((h,i)=>{if(h.hasAttribute("torph-exiting"))return;let a=h.getAttribute("torph-id")||`child-${i}`;s[a]={x:h.offsetLeft,y:h.offsetTop};}),s}updateStyles(e){if(this.isInitialRender)return;let s=Array.from(this.element.children),h=new Set(e.map(i=>i.id).filter(i=>this.prevMeasures[i]));s.forEach((i,a)=>{if(i.hasAttribute("torph-exiting"))return;let p=i.getAttribute("torph-id")||`child-${a}`,l=this.prevMeasures[p],d=this.currentMeasures[p],c=d?.x||0,m=d?.y||0,y=l?l.x-c:0,x=l?l.y-m:0,f=!l;if(f){let r=e.findIndex(n=>n.id===p),o=null;for(let n=r-1;n>=0;n--)if(h.has(e[n].id)){o=e[n].id;break}if(!o){for(let n=r+1;n<e.length;n++)if(h.has(e[n].id)){o=e[n].id;break}}if(o){let n=this.prevMeasures[o],u=this.currentMeasures[o];y=n.x-u.x,x=n.y-u.y;}}i.getAnimations().forEach(r=>r.cancel()),i.animate({transform:`translate(${y}px, ${x}px) scale(${f?.95:1})`,offset:0},{duration:this.options.duration,easing:this.options.ease,fill:"both"});let M=f?this.options.duration*.25:0,t=f?this.options.duration*.25:0;i.animate({opacity:f?0:1,offset:0},{duration:M,delay:t,easing:"linear",fill:"both"});});}addStyles(){if(g.styleEl)return;let e=document.createElement("style");e.dataset.torph="true",e.innerHTML=`
3
2
  [torph-root] {
4
- display: inline-flex; /* TODO: remove for multi-line support */
3
+ display: inline-flex;
5
4
  position: relative;
6
5
  will-change: width, height;
7
6
  transition-property: width, height;
7
+ white-space: nowrap;
8
8
  }
9
9
 
10
10
  [torph-item] {
@@ -13,7 +13,7 @@
13
13
  transform: none;
14
14
  opacity: 1;
15
15
  }
16
-
16
+
17
17
  [torph-root][torph-debug] {
18
18
  outline:2px solid magenta;
19
19
  [torph-item] {
@@ -21,5 +21,5 @@
21
21
  outline-offset: -4px;
22
22
  }
23
23
  }
24
- `,document.head.appendChild(e),o.styleEl=e}removeStyles(){o.styleEl&&(o.styleEl.remove(),o.styleEl=void 0)}blocks(e){return Array.from(e).reduce((n,i)=>i.segment===" "?[...n,{id:`space-${i.index}`,string:"\xA0"}]:n.find(l=>l.string===i.segment)?[...n,{id:`${i.segment}-${i.index}`,string:i.segment}]:[...n,{id:i.segment,string:i.segment}],[])}getUnscaledBoundingClientRect(e){let t=e.getBoundingClientRect(),i=window.getComputedStyle(e).transform,r=1,l=1,g=/matrix\(([^)]+)\)/,m=i.match(g);if(m){let c=m[1]?.split(",").map(Number);c&&c?.length>=4&&(r=c[0],l=c[3])}else{let c=i.match(/scaleX\(([^)]+)\)/),y=i.match(/scaleY\(([^)]+)\)/);c&&(r=parseFloat(c[1])),y&&(l=parseFloat(y[1]))}let h=t.width/r,d=t.height/l,p=t.x+(t.width-h)/2,u=t.y+(t.height-d)/2;return{x:p,y:u,width:h,height:d,top:u,right:p+h,bottom:u+d,left:p}}log(...e){this.options.debug&&console.log("[TextMorph]",...e)}};0&&(module.exports={TextMorph,version});
24
+ `,document.head.appendChild(e),g.styleEl=e;}removeStyles(){g.styleEl&&(g.styleEl.remove(),g.styleEl=void 0);}blocks(e){return Array.from(e).reduce((h,i)=>i.segment===" "?[...h,{id:`space-${i.index}`,string:"\xA0"}]:h.find(p=>p.string===i.segment)?[...h,{id:`${i.segment}-${i.index}`,string:i.segment}]:[...h,{id:i.segment,string:i.segment}],[])}blocksFallback(e,s){let h=s?e.split(" "):e.split(""),i=[];return s?h.forEach((a,p)=>{p>0&&i.push({id:`space-${p}`,string:"\xA0"}),i.find(d=>d.string===a)?i.push({id:`${a}-${p}`,string:a}):i.push({id:a,string:a});}):h.forEach((a,p)=>{i.find(d=>d.string===a)?i.push({id:`${a}-${p}`,string:a}):i.push({id:a,string:a});}),i}log(...e){this.options.debug&&console.log("[TextMorph]",...e);}};exports.DEFAULT_AS=T;exports.DEFAULT_TEXT_MORPH_OPTIONS=A;exports.TextMorph=b;//# sourceMappingURL=index.js.map
25
25
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../package.json","../src/lib/text-morph/index.ts"],"sourcesContent":["export { version } from \"./../package.json\";\n\nexport { TextMorph } from \"./lib/text-morph\";\nexport type { TextMorphOptions } from \"./lib/text-morph/types\";\n","{\n \"name\": \"torph\",\n \"version\": \"0.0.4\",\n \"description\": \"Dependency-free animated text component.\",\n \"author\": \"Lochie Axon\",\n \"license\": \"MIT\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/lochie/torph.git\",\n \"directory\": \"packages/torph\"\n },\n \"types\": \"dist/index.d.ts\",\n \"exports\": {\n \".\": {\n \"types\": \"./dist/index.d.ts\",\n \"import\": \"./dist/index.mjs\",\n \"require\": \"./dist/index.js\"\n },\n \"./react\": {\n \"types\": \"./dist/react/index.d.ts\",\n \"import\": \"./dist/react/index.mjs\",\n \"require\": \"./dist/react/index.js\"\n },\n \"./vue\": {\n \"types\": \"./dist/vue/index.d.ts\",\n \"import\": \"./dist/vue/index.mjs\",\n \"require\": \"./dist/vue/index.js\"\n },\n \"./svelte\": {\n \"types\": \"./dist/svelte/index.d.ts\",\n \"import\": \"./dist/svelte/index.mjs\",\n \"require\": \"./dist/svelte/index.js\"\n }\n },\n \"publishConfig\": {\n \"access\": \"public\"\n },\n \"scripts\": {\n \"build\": \"tsup\",\n \"dev\": \"tsup --watch\",\n \"lint\": \"eslint -c .eslintrc.cjs ./src/**/*.{ts,tsx}\",\n \"lint:fix\": \"eslint --fix -c .eslintrc.cjs ./src/**/*.{ts,tsx}\",\n \"pre-commit\": \"lint-staged\"\n },\n \"keywords\": [\n \"react\",\n \"vue\",\n \"svelte\",\n \"text\",\n \"animation\",\n \"morph\"\n ],\n \"bugs\": {\n \"url\": \"https://github.com/lochie/torph/issues\"\n },\n \"files\": [\n \"dist/*\"\n ],\n \"peerDependencies\": {\n \"react\": \">=18\",\n \"react-dom\": \">=18\",\n \"vue\": \">=3\",\n \"svelte\": \">=4\"\n },\n \"peerDependenciesMeta\": {\n \"react\": {\n \"optional\": true\n },\n \"react-dom\": {\n \"optional\": true\n },\n \"vue\": {\n \"optional\": true\n },\n \"svelte\": {\n \"optional\": true\n }\n },\n \"devDependencies\": {\n \"@types/react\": \"^18.2.15\",\n \"@typescript-eslint/eslint-plugin\": \"^6.8.0\",\n \"@typescript-eslint/parser\": \"^6.8.0\",\n \"eslint\": \"^9.36.0\",\n \"eslint-config-airbnb\": \"^19.0.4\",\n \"eslint-config-airbnb-typescript\": \"^17.1.0\",\n \"eslint-config-prettier\": \"^9.0.0\",\n \"eslint-plugin-import\": \"^2.28.1\",\n \"eslint-plugin-jsx-a11y\": \"^6.7.1\",\n \"eslint-plugin-prettier\": \"^5.0.1\",\n \"eslint-plugin-react\": \"^7.33.2\",\n \"eslint-plugin-react-hooks\": \"^4.6.0\",\n \"prettier\": \"^3.0.3\",\n \"react\": \"^18.2.0\",\n \"react-dom\": \"^18.2.0\",\n \"svelte\": \"^4.0.0\",\n \"tsup\": \"^8.5.0\",\n \"typescript\": \"^5.9.3\",\n \"vue\": \"^3.3.0\"\n }\n}\n","import type { TextMorphOptions } from \"./types\";\n\nexport type { TextMorphOptions } from \"./types\";\n\ntype Block = {\n id: string;\n string: string;\n};\ntype Measures = {\n [key: string]: { x: number; y: number };\n};\n\nexport class TextMorph {\n private element: HTMLElement;\n private options: Omit<TextMorphOptions, \"element\"> = {};\n\n private data: HTMLElement | string;\n\n private currentMeasures: Measures = {};\n private prevMeasures: Measures = {};\n private isInitialRender = true;\n\n static styleEl: HTMLStyleElement;\n\n constructor(options: TextMorphOptions) {\n this.options = {\n locale: \"en\",\n duration: 400,\n ease: \"cubic-bezier(0.19, 1, 0.22, 1)\",\n ...options,\n };\n\n this.element = options.element;\n this.element.setAttribute(\"torph-root\", \"\");\n this.element.style.transitionDuration = `${this.options.duration}ms`;\n this.element.style.transitionTimingFunction = this.options.ease!;\n\n if (options.debug) this.element.setAttribute(\"torph-debug\", \"\");\n\n this.data = this.element.innerHTML;\n\n this.addStyles();\n }\n\n destroy() {\n this.element.getAnimations().forEach((anim) => anim.cancel());\n this.element.removeAttribute(\"torph-root\");\n this.element.removeAttribute(\"torph-debug\");\n this.removeStyles();\n }\n\n update(value: HTMLElement | string) {\n if (value === this.data) return;\n this.data = value;\n\n if (this.data instanceof HTMLElement) {\n // TODO: handle HTMLElement case\n throw new Error(\"HTMLElement not yet supported\");\n } else {\n this.createTextGroup(this.data, this.element);\n }\n }\n\n private createTextGroup(value: string, element: HTMLElement) {\n const oldWidth = element.offsetWidth;\n const oldHeight = element.offsetHeight;\n\n const byWord = value.includes(\" \");\n const segmenter = new Intl.Segmenter(this.options.locale, {\n granularity: byWord ? \"word\" : \"grapheme\",\n });\n const iterator = segmenter.segment(value)[Symbol.iterator]();\n const blocks = this.blocks(iterator);\n\n this.prevMeasures = this.measure();\n const oldChildren = Array.from(element.children) as HTMLElement[];\n const newIds = new Set(blocks.map((b) => b.id));\n\n const exiting = oldChildren.filter(\n (child) =>\n !newIds.has(child.getAttribute(\"torph-id\") as string) &&\n !child.hasAttribute(\"torph-exiting\"),\n );\n\n const parentRect = this.getUnscaledBoundingClientRect(element);\n exiting.forEach((child) => {\n const rect = this.getUnscaledBoundingClientRect(child);\n child.setAttribute(\"torph-exiting\", \"\");\n child.style.position = \"absolute\";\n child.style.pointerEvents = \"none\";\n child.style.left = `${rect.left - parentRect.left}px`;\n child.style.top = `${rect.top - parentRect.top}px`;\n child.style.width = `${rect.width}px`;\n child.style.height = `${rect.height}px`;\n });\n\n oldChildren.forEach((child) => {\n const id = child.getAttribute(\"torph-id\") as string;\n if (newIds.has(id)) child.remove();\n });\n\n blocks.forEach((block) => {\n const span = document.createElement(\"span\");\n span.setAttribute(\"torph-item\", \"\");\n span.setAttribute(\"torph-id\", block.id);\n span.textContent = block.string;\n element.appendChild(span);\n });\n\n this.currentMeasures = this.measure();\n this.updateStyles();\n\n exiting.forEach((child) => {\n if (this.isInitialRender) {\n child.remove();\n return;\n }\n\n const id = child.getAttribute(\"torph-id\")!;\n\n const prev = this.prevMeasures[id];\n\n const siblings = Array.from(element.children) as HTMLElement[];\n const nearest = siblings.find((s) => {\n const sRect = s.getBoundingClientRect();\n const cRect = child.getBoundingClientRect();\n return Math.abs(sRect.left - cRect.left) < 40;\n });\n\n const nextPos = nearest\n ? this.currentMeasures[nearest.getAttribute(\"torph-id\")!]\n : prev;\n\n const dx = (nextPos ? nextPos.x - (prev?.x || 0) : 0) * 0.5;\n const dy = (nextPos ? nextPos.y - (prev?.y || 0) : 0) * 0.5;\n\n child.getAnimations().forEach((a) => a.cancel());\n const animation: Animation = child.animate(\n {\n transform: `translate(${dx}px, ${dy}px) scale(0.95)`,\n opacity: 0,\n offset: 1,\n },\n {\n duration: this.options.duration,\n easing: this.options.ease,\n fill: \"both\",\n },\n );\n animation.onfinish = () => child.remove();\n });\n\n if (this.isInitialRender) {\n this.isInitialRender = false;\n element.style.width = \"auto\";\n element.style.height = \"auto\";\n return;\n }\n\n if (oldWidth === 0 || oldHeight === 0) return;\n\n element.style.width = \"auto\";\n element.style.height = \"auto\";\n void element.offsetWidth; // force reflow\n\n const newWidth = element.offsetWidth;\n const newHeight = element.offsetHeight;\n\n element.style.width = `${oldWidth}px`;\n element.style.height = `${oldHeight}px`;\n void element.offsetWidth; // force reflow\n\n element.style.width = `${newWidth}px`;\n element.style.height = `${newHeight}px`;\n\n // TODO: move to `transitionend` event listener\n setTimeout(() => {\n element.style.width = \"auto\";\n element.style.height = \"auto\";\n }, this.options.duration);\n }\n\n private measure() {\n const children = Array.from(this.element.children) as HTMLElement[];\n const measures: Measures = {};\n\n children.forEach((child, index) => {\n if (child.hasAttribute(\"torph-exiting\")) return;\n const key = child.getAttribute(\"torph-id\") || `child-${index}`;\n measures[key] = {\n x: child.offsetLeft,\n y: child.offsetTop,\n };\n });\n\n return measures;\n }\n\n private updateStyles() {\n if (this.isInitialRender) return;\n\n const children = Array.from(this.element.children) as HTMLElement[];\n\n children.forEach((child, index) => {\n if (child.hasAttribute(\"torph-exiting\")) return;\n const key = child.getAttribute(\"torph-id\") || `child-${index}`;\n const prev = this.prevMeasures[key];\n const current = this.currentMeasures[key];\n\n const cx = current?.x || 0;\n const cy = current?.y || 0;\n\n const deltaX = prev ? prev?.x - cx : 0;\n const deltaY = prev ? prev?.y - cy : 0;\n const isNew = !prev;\n\n child.getAnimations().forEach((a) => a.cancel());\n child.animate(\n {\n transform: `translate(${deltaX}px, ${deltaY}px) scale(${isNew ? 0.95 : 1})`,\n opacity: isNew ? 0 : 1,\n offset: 0,\n },\n {\n duration: this.options.duration,\n easing: this.options.ease,\n delay: isNew ? this.options.duration! * 0.2 : 0,\n fill: \"both\",\n },\n );\n });\n }\n\n private addStyles() {\n if (TextMorph.styleEl) return;\n\n const style = document.createElement(\"style\");\n style.dataset.torph = \"true\";\n style.innerHTML = `\n[torph-root] {\n display: inline-flex; /* TODO: remove for multi-line support */\n position: relative;\n will-change: width, height;\n transition-property: width, height;\n}\n\n[torph-item] {\n display: inline-block;\n will-change: opacity, transform;\n transform: none;\n opacity: 1;\n}\n \n[torph-root][torph-debug] {\n outline:2px solid magenta;\n [torph-item] {\n outline:2px solid cyan;\n outline-offset: -4px;\n }\n}\n `;\n document.head.appendChild(style);\n TextMorph.styleEl = style;\n }\n\n private removeStyles() {\n if (TextMorph.styleEl) {\n TextMorph.styleEl.remove();\n TextMorph.styleEl = undefined!;\n }\n }\n\n // utils\n\n private blocks(iterator: Intl.SegmentIterator<Intl.SegmentData>) {\n const uniqueStrings: Block[] = Array.from(iterator).reduce(\n (acc, string) => {\n if (string.segment === \" \") {\n return [...acc, { id: `space-${string.index}`, string: \"\\u00A0\" }];\n }\n\n const existingString = acc.find((x) => x.string === string.segment);\n if (existingString) {\n return [\n ...acc,\n { id: `${string.segment}-${string.index}`, string: string.segment },\n ];\n }\n\n return [\n ...acc,\n {\n id: string.segment,\n string: string.segment,\n },\n ];\n },\n [] as Block[],\n );\n\n return uniqueStrings;\n }\n\n private getUnscaledBoundingClientRect(element: HTMLElement) {\n const scaledRect = element.getBoundingClientRect();\n const computedStyle = window.getComputedStyle(element);\n const transform = computedStyle.transform;\n\n let scaleX = 1;\n let scaleY = 1;\n\n const matrixRegex = /matrix\\(([^)]+)\\)/;\n const match = transform.match(matrixRegex);\n\n if (match) {\n const values = match[1]?.split(\",\").map(Number);\n if (values && values?.length >= 4) {\n scaleX = values[0]!;\n scaleY = values[3]!;\n }\n } else {\n const scaleXMatch = transform.match(/scaleX\\(([^)]+)\\)/);\n const scaleYMatch = transform.match(/scaleY\\(([^)]+)\\)/);\n if (scaleXMatch) scaleX = parseFloat(scaleXMatch[1]!);\n if (scaleYMatch) scaleY = parseFloat(scaleYMatch[1]!);\n }\n\n const unscaledWidth = scaledRect.width / scaleX;\n const unscaledHeight = scaledRect.height / scaleY;\n\n const unscaledX = scaledRect.x + (scaledRect.width - unscaledWidth) / 2;\n const unscaledY = scaledRect.y + (scaledRect.height - unscaledHeight) / 2;\n\n return {\n x: unscaledX,\n y: unscaledY,\n width: unscaledWidth,\n height: unscaledHeight,\n top: unscaledY,\n right: unscaledX + unscaledWidth,\n bottom: unscaledY + unscaledHeight,\n left: unscaledX,\n };\n }\n\n private log(...args: any[]) {\n if (this.options.debug) console.log(\"[TextMorph]\", ...args);\n }\n}\n"],"mappings":"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","names":["src_exports","__export","TextMorph","version","__toCommonJS","version","TextMorph","_TextMorph","options","anim","value","element","oldWidth","oldHeight","byWord","iterator","blocks","oldChildren","newIds","b","exiting","child","parentRect","rect","id","block","span","prev","nearest","s","sRect","cRect","nextPos","dx","dy","a","animation","newWidth","newHeight","children","measures","index","key","current","cx","cy","deltaX","deltaY","isNew","style","acc","string","x","scaledRect","transform","scaleX","scaleY","matrixRegex","match","values","scaleXMatch","scaleYMatch","unscaledWidth","unscaledHeight","unscaledX","unscaledY","args"]}
1
+ {"version":3,"sources":["../src/lib/text-morph/index.ts"],"names":["DEFAULT_AS","DEFAULT_TEXT_MORPH_OPTIONS","TextMorph","_TextMorph","options","anim","event","value","element","oldWidth","oldHeight","byWord","blocks","iterator","oldChildren","newIds","b","exiting","child","exitingSet","exitingAnchorId","i","anchor","j","siblingId","exitPositions","a","pos","id","node","block","span","anchorId","dx","dy","anchorPrev","anchorCurr","animation","newWidth","newHeight","children","measures","index","key","persistentIds","prev","current","cx","cy","deltaX","deltaY","isNew","blockIndex","duration","delay","style","acc","string","x","segments","segment","args"],"mappings":"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","file":"index.js","sourcesContent":["import type { TextMorphOptions } from \"./types\";\n\nexport type { TextMorphOptions } from \"./types\";\n\nexport const DEFAULT_AS = \"span\";\nexport const DEFAULT_TEXT_MORPH_OPTIONS = {\n debug: false,\n locale: \"en\",\n duration: 400,\n scale: true,\n ease: \"cubic-bezier(0.19, 1, 0.22, 1)\",\n disabled: false,\n respectReducedMotion: true,\n} as const satisfies Omit<TextMorphOptions, \"element\">;\n\ntype Block = {\n id: string;\n string: string;\n};\ntype Measures = {\n [key: string]: { x: number; y: number };\n};\n\nexport class TextMorph {\n private element: HTMLElement;\n private options: Omit<TextMorphOptions, \"element\"> = {};\n\n private data: HTMLElement | string;\n\n private currentMeasures: Measures = {};\n private prevMeasures: Measures = {};\n private isInitialRender = true;\n private prefersReducedMotion = false;\n private mediaQuery?: MediaQueryList;\n\n static styleEl: HTMLStyleElement;\n\n constructor(options: TextMorphOptions) {\n this.options = {\n ...DEFAULT_TEXT_MORPH_OPTIONS,\n ...options,\n };\n\n this.element = options.element;\n\n // reduced motion detection\n if (typeof window !== \"undefined\" && this.options.respectReducedMotion) {\n this.mediaQuery = window.matchMedia(\"(prefers-reduced-motion: reduce)\");\n this.prefersReducedMotion = this.mediaQuery.matches;\n this.mediaQuery.addEventListener(\"change\", this.handleMediaQueryChange);\n }\n\n if (!this.isDisabled()) {\n this.element.setAttribute(\"torph-root\", \"\");\n this.element.style.transitionDuration = `${this.options.duration}ms`;\n this.element.style.transitionTimingFunction = this.options.ease!;\n\n if (options.debug) this.element.setAttribute(\"torph-debug\", \"\");\n }\n\n this.data = \"\";\n if (!this.isDisabled()) {\n this.addStyles();\n }\n }\n\n destroy() {\n if (this.mediaQuery) {\n this.mediaQuery.removeEventListener(\n \"change\",\n this.handleMediaQueryChange,\n );\n }\n this.element.getAnimations().forEach((anim) => anim.cancel());\n this.element.removeAttribute(\"torph-root\");\n this.element.removeAttribute(\"torph-debug\");\n this.removeStyles();\n }\n\n private handleMediaQueryChange = (event: MediaQueryListEvent) => {\n this.prefersReducedMotion = event.matches;\n };\n\n private isDisabled(): boolean {\n return Boolean(\n this.options.disabled ||\n (this.options.respectReducedMotion && this.prefersReducedMotion),\n );\n }\n\n update(value: HTMLElement | string) {\n if (value === this.data) return;\n this.data = value;\n\n if (this.isDisabled()) {\n if (typeof value === \"string\") {\n this.element.textContent = value;\n }\n return;\n }\n\n if (this.data instanceof HTMLElement) {\n // TODO: handle HTMLElement case\n throw new Error(\"HTMLElement not yet supported\");\n } else {\n if (this.options.onAnimationStart && !this.isInitialRender) {\n this.options.onAnimationStart();\n }\n this.createTextGroup(this.data, this.element);\n }\n }\n\n private createTextGroup(value: string, element: HTMLElement) {\n const oldWidth = element.offsetWidth;\n const oldHeight = element.offsetHeight;\n\n const byWord = value.includes(\" \");\n let blocks: Block[];\n\n if (typeof Intl.Segmenter !== \"undefined\") {\n const segmenter = new Intl.Segmenter(this.options.locale, {\n granularity: byWord ? \"word\" : \"grapheme\",\n });\n const iterator = segmenter.segment(value)[Symbol.iterator]();\n blocks = this.blocks(iterator);\n } else {\n // Fallback for browsers without Intl.Segmenter\n blocks = this.blocksFallback(value, byWord);\n }\n\n this.prevMeasures = this.measure();\n const oldChildren = Array.from(element.children) as HTMLElement[];\n const newIds = new Set(blocks.map((b) => b.id));\n\n const exiting = oldChildren.filter(\n (child) =>\n !newIds.has(child.getAttribute(\"torph-id\") as string) &&\n !child.hasAttribute(\"torph-exiting\"),\n );\n\n // For each exiting char, find the nearest persistent neighbor in old order\n // so we can make it follow that neighbor's FLIP movement\n const exitingSet = new Set(exiting);\n const exitingAnchorId = new Map<HTMLElement, string | null>();\n for (let i = 0; i < oldChildren.length; i++) {\n const child = oldChildren[i]!;\n if (!exitingSet.has(child)) continue;\n\n // Look forward for nearest persistent char\n let anchor: string | null = null;\n for (let j = i + 1; j < oldChildren.length; j++) {\n const siblingId = oldChildren[j]!.getAttribute(\"torph-id\") as string;\n if (newIds.has(siblingId) && !exitingSet.has(oldChildren[j]!)) {\n anchor = siblingId;\n break;\n }\n }\n // If none forward, look backward\n if (!anchor) {\n for (let j = i - 1; j >= 0; j--) {\n const siblingId = oldChildren[j]!.getAttribute(\"torph-id\") as string;\n if (newIds.has(siblingId) && !exitingSet.has(oldChildren[j]!)) {\n anchor = siblingId;\n break;\n }\n }\n }\n exitingAnchorId.set(child, anchor);\n }\n\n // Two-pass: read all positions before modifying any element,\n // since setting position:absolute removes from flow and shifts siblings\n const exitPositions = exiting.map((child) => {\n child.getAnimations().forEach((a) => a.cancel());\n return {\n left: child.offsetLeft,\n top: child.offsetTop,\n width: child.offsetWidth,\n height: child.offsetHeight,\n };\n });\n exiting.forEach((child, i) => {\n const pos = exitPositions[i]!;\n child.setAttribute(\"torph-exiting\", \"\");\n child.style.position = \"absolute\";\n child.style.pointerEvents = \"none\";\n child.style.left = `${pos.left}px`;\n child.style.top = `${pos.top}px`;\n child.style.width = `${pos.width}px`;\n child.style.height = `${pos.height}px`;\n });\n\n oldChildren.forEach((child) => {\n const id = child.getAttribute(\"torph-id\") as string;\n if (newIds.has(id)) child.remove();\n });\n\n // Disabled-mode updates set plain text via textContent; remove that text node\n // before appending torph items so old content is not duplicated.\n Array.from(element.childNodes).forEach((node) => {\n if (node.nodeType === Node.TEXT_NODE) {\n node.remove();\n }\n });\n\n blocks.forEach((block) => {\n const span = document.createElement(\"span\");\n span.setAttribute(\"torph-item\", \"\");\n span.setAttribute(\"torph-id\", block.id);\n span.textContent = block.string;\n element.appendChild(span);\n });\n\n this.currentMeasures = this.measure();\n this.updateStyles(blocks);\n\n exiting.forEach((child) => {\n if (this.isInitialRender) {\n child.remove();\n return;\n }\n\n // Find the anchor neighbor's FLIP delta so we move in sync with it\n const anchorId = exitingAnchorId.get(child);\n let dx = 0;\n let dy = 0;\n\n if (\n anchorId &&\n this.prevMeasures[anchorId] &&\n this.currentMeasures[anchorId]\n ) {\n const anchorPrev = this.prevMeasures[anchorId]!;\n const anchorCurr = this.currentMeasures[anchorId]!;\n dx = anchorCurr.x - anchorPrev.x;\n dy = anchorCurr.y - anchorPrev.y;\n }\n\n child.animate(\n {\n transform: this.options.scale\n ? `translate(${dx}px, ${dy}px) scale(0.95)`\n : `translate(${dx}px, ${dy}px)`,\n offset: 1,\n },\n {\n duration: this.options.duration,\n easing: this.options.ease,\n fill: \"both\",\n },\n );\n const animation: Animation = child.animate(\n {\n opacity: 0,\n offset: 1,\n },\n {\n duration: this.options.duration! * 0.25,\n easing: \"linear\",\n fill: \"both\",\n },\n );\n animation.onfinish = () => child.remove();\n });\n\n if (this.isInitialRender) {\n this.isInitialRender = false;\n element.style.width = \"auto\";\n element.style.height = \"auto\";\n return;\n }\n\n if (oldWidth === 0 || oldHeight === 0) return;\n\n element.style.width = \"auto\";\n element.style.height = \"auto\";\n void element.offsetWidth; // force reflow\n\n const newWidth = element.offsetWidth;\n const newHeight = element.offsetHeight;\n\n element.style.width = `${oldWidth}px`;\n element.style.height = `${oldHeight}px`;\n void element.offsetWidth; // force reflow\n\n element.style.width = `${newWidth}px`;\n element.style.height = `${newHeight}px`;\n\n // TODO: move to `transitionend` event listener\n setTimeout(() => {\n element.style.width = \"auto\";\n element.style.height = \"auto\";\n if (this.options.onAnimationComplete) {\n this.options.onAnimationComplete();\n }\n }, this.options.duration);\n }\n\n private measure() {\n const children = Array.from(this.element.children) as HTMLElement[];\n const measures: Measures = {};\n\n children.forEach((child, index) => {\n if (child.hasAttribute(\"torph-exiting\")) return;\n const key = child.getAttribute(\"torph-id\") || `child-${index}`;\n measures[key] = {\n x: child.offsetLeft,\n y: child.offsetTop,\n };\n });\n\n return measures;\n }\n\n private updateStyles(blocks: Block[]) {\n if (this.isInitialRender) return;\n\n const children = Array.from(this.element.children) as HTMLElement[];\n\n const persistentIds = new Set(\n blocks.map((b) => b.id).filter((id) => this.prevMeasures[id]),\n );\n\n children.forEach((child, index) => {\n if (child.hasAttribute(\"torph-exiting\")) return;\n const key = child.getAttribute(\"torph-id\") || `child-${index}`;\n const prev = this.prevMeasures[key];\n const current = this.currentMeasures[key];\n\n const cx = current?.x || 0;\n const cy = current?.y || 0;\n\n let deltaX = prev ? prev.x - cx : 0;\n let deltaY = prev ? prev.y - cy : 0;\n const isNew = !prev;\n\n // For new chars, use the nearest persistent neighbor's FLIP delta\n // so all new chars get the same consistent offset\n if (isNew) {\n const blockIndex = blocks.findIndex((b) => b.id === key);\n let anchorId: string | null = null;\n\n for (let j = blockIndex - 1; j >= 0; j--) {\n if (persistentIds.has(blocks[j]!.id)) {\n anchorId = blocks[j]!.id;\n break;\n }\n }\n if (!anchorId) {\n for (let j = blockIndex + 1; j < blocks.length; j++) {\n if (persistentIds.has(blocks[j]!.id)) {\n anchorId = blocks[j]!.id;\n break;\n }\n }\n }\n\n if (anchorId) {\n const anchorPrev = this.prevMeasures[anchorId]!;\n const anchorCurr = this.currentMeasures[anchorId]!;\n deltaX = anchorPrev.x - anchorCurr.x;\n deltaY = anchorPrev.y - anchorCurr.y;\n }\n }\n\n child.getAnimations().forEach((a) => a.cancel());\n child.animate(\n {\n transform: `translate(${deltaX}px, ${deltaY}px) scale(${isNew ? 0.95 : 1})`,\n offset: 0,\n },\n {\n duration: this.options.duration,\n easing: this.options.ease,\n fill: \"both\",\n },\n );\n const duration = isNew ? this.options.duration! * 0.25 : 0;\n const delay = isNew ? this.options.duration! * 0.25 : 0;\n child.animate(\n {\n opacity: isNew ? 0 : 1,\n offset: 0,\n },\n {\n duration: duration,\n delay: delay,\n easing: \"linear\",\n fill: \"both\",\n },\n );\n });\n }\n\n private addStyles() {\n if (TextMorph.styleEl) return;\n\n const style = document.createElement(\"style\");\n style.dataset.torph = \"true\";\n style.innerHTML = `\n[torph-root] {\n display: inline-flex;\n position: relative;\n will-change: width, height;\n transition-property: width, height;\n white-space: nowrap;\n}\n\n[torph-item] {\n display: inline-block;\n will-change: opacity, transform;\n transform: none;\n opacity: 1;\n}\n\n[torph-root][torph-debug] {\n outline:2px solid magenta;\n [torph-item] {\n outline:2px solid cyan;\n outline-offset: -4px;\n }\n}\n `;\n document.head.appendChild(style);\n TextMorph.styleEl = style;\n }\n\n private removeStyles() {\n if (TextMorph.styleEl) {\n TextMorph.styleEl.remove();\n TextMorph.styleEl = undefined!;\n }\n }\n\n // utils\n\n private blocks(iterator: Intl.SegmentIterator<Intl.SegmentData>) {\n const uniqueStrings: Block[] = Array.from(iterator).reduce(\n (acc, string) => {\n if (string.segment === \" \") {\n return [...acc, { id: `space-${string.index}`, string: \"\\u00A0\" }];\n }\n\n const existingString = acc.find((x) => x.string === string.segment);\n if (existingString) {\n return [\n ...acc,\n { id: `${string.segment}-${string.index}`, string: string.segment },\n ];\n }\n\n return [\n ...acc,\n {\n id: string.segment,\n string: string.segment,\n },\n ];\n },\n [] as Block[],\n );\n\n return uniqueStrings;\n }\n\n private blocksFallback(value: string, byWord: boolean): Block[] {\n const segments = byWord ? value.split(\" \") : value.split(\"\");\n const blocks: Block[] = [];\n\n if (byWord) {\n segments.forEach((segment, index) => {\n if (index > 0) {\n blocks.push({ id: `space-${index}`, string: \"\\u00A0\" });\n }\n const existing = blocks.find((x) => x.string === segment);\n if (existing) {\n blocks.push({ id: `${segment}-${index}`, string: segment });\n } else {\n blocks.push({ id: segment, string: segment });\n }\n });\n } else {\n segments.forEach((segment, index) => {\n const existing = blocks.find((x) => x.string === segment);\n if (existing) {\n blocks.push({ id: `${segment}-${index}`, string: segment });\n } else {\n blocks.push({ id: segment, string: segment });\n }\n });\n }\n\n return blocks;\n }\n\n private log(...args: any[]) {\n if (this.options.debug) console.log(\"[TextMorph]\", ...args);\n }\n}\n"]}
package/dist/index.mjs CHANGED
@@ -1,3 +1,25 @@
1
- "use client";
2
- import{a as e}from"./chunk-4ZXQ2BQA.mjs";var i="0.0.4";export{e as TextMorph,i as version};
1
+ var T="span",A={debug:false,locale:"en",duration:400,scale:true,ease:"cubic-bezier(0.19, 1, 0.22, 1)",disabled:false,respectReducedMotion:true},b=class g{element;options={};data;currentMeasures={};prevMeasures={};isInitialRender=true;prefersReducedMotion=false;mediaQuery;static styleEl;constructor(e){this.options={...A,...e},this.element=e.element,typeof window<"u"&&this.options.respectReducedMotion&&(this.mediaQuery=window.matchMedia("(prefers-reduced-motion: reduce)"),this.prefersReducedMotion=this.mediaQuery.matches,this.mediaQuery.addEventListener("change",this.handleMediaQueryChange)),this.isDisabled()||(this.element.setAttribute("torph-root",""),this.element.style.transitionDuration=`${this.options.duration}ms`,this.element.style.transitionTimingFunction=this.options.ease,e.debug&&this.element.setAttribute("torph-debug","")),this.data="",this.isDisabled()||this.addStyles();}destroy(){this.mediaQuery&&this.mediaQuery.removeEventListener("change",this.handleMediaQueryChange),this.element.getAnimations().forEach(e=>e.cancel()),this.element.removeAttribute("torph-root"),this.element.removeAttribute("torph-debug"),this.removeStyles();}handleMediaQueryChange=e=>{this.prefersReducedMotion=e.matches;};isDisabled(){return !!(this.options.disabled||this.options.respectReducedMotion&&this.prefersReducedMotion)}update(e){if(e!==this.data){if(this.data=e,this.isDisabled()){typeof e=="string"&&(this.element.textContent=e);return}if(this.data instanceof HTMLElement)throw new Error("HTMLElement not yet supported");this.options.onAnimationStart&&!this.isInitialRender&&this.options.onAnimationStart(),this.createTextGroup(this.data,this.element);}}createTextGroup(e,s){let h=s.offsetWidth,i=s.offsetHeight,a=e.includes(" "),p;if(typeof Intl.Segmenter<"u"){let r=new Intl.Segmenter(this.options.locale,{granularity:a?"word":"grapheme"}).segment(e)[Symbol.iterator]();p=this.blocks(r);}else p=this.blocksFallback(e,a);this.prevMeasures=this.measure();let l=Array.from(s.children),d=new Set(p.map(t=>t.id)),c=l.filter(t=>!d.has(t.getAttribute("torph-id"))&&!t.hasAttribute("torph-exiting")),m=new Set(c),y=new Map;for(let t=0;t<l.length;t++){let r=l[t];if(!m.has(r))continue;let o=null;for(let n=t+1;n<l.length;n++){let u=l[n].getAttribute("torph-id");if(d.has(u)&&!m.has(l[n])){o=u;break}}if(!o)for(let n=t-1;n>=0;n--){let u=l[n].getAttribute("torph-id");if(d.has(u)&&!m.has(l[n])){o=u;break}}y.set(r,o);}let x=c.map(t=>(t.getAnimations().forEach(r=>r.cancel()),{left:t.offsetLeft,top:t.offsetTop,width:t.offsetWidth,height:t.offsetHeight}));if(c.forEach((t,r)=>{let o=x[r];t.setAttribute("torph-exiting",""),t.style.position="absolute",t.style.pointerEvents="none",t.style.left=`${o.left}px`,t.style.top=`${o.top}px`,t.style.width=`${o.width}px`,t.style.height=`${o.height}px`;}),l.forEach(t=>{let r=t.getAttribute("torph-id");d.has(r)&&t.remove();}),Array.from(s.childNodes).forEach(t=>{t.nodeType===Node.TEXT_NODE&&t.remove();}),p.forEach(t=>{let r=document.createElement("span");r.setAttribute("torph-item",""),r.setAttribute("torph-id",t.id),r.textContent=t.string,s.appendChild(r);}),this.currentMeasures=this.measure(),this.updateStyles(p),c.forEach(t=>{if(this.isInitialRender){t.remove();return}let r=y.get(t),o=0,n=0;if(r&&this.prevMeasures[r]&&this.currentMeasures[r]){let E=this.prevMeasures[r],v=this.currentMeasures[r];o=v.x-E.x,n=v.y-E.y;}t.animate({transform:this.options.scale?`translate(${o}px, ${n}px) scale(0.95)`:`translate(${o}px, ${n}px)`,offset:1},{duration:this.options.duration,easing:this.options.ease,fill:"both"});let u=t.animate({opacity:0,offset:1},{duration:this.options.duration*.25,easing:"linear",fill:"both"});u.onfinish=()=>t.remove();}),this.isInitialRender){this.isInitialRender=false,s.style.width="auto",s.style.height="auto";return}if(h===0||i===0)return;s.style.width="auto",s.style.height="auto",s.offsetWidth;let f=s.offsetWidth,M=s.offsetHeight;s.style.width=`${h}px`,s.style.height=`${i}px`,s.offsetWidth,s.style.width=`${f}px`,s.style.height=`${M}px`,setTimeout(()=>{s.style.width="auto",s.style.height="auto",this.options.onAnimationComplete&&this.options.onAnimationComplete();},this.options.duration);}measure(){let e=Array.from(this.element.children),s={};return e.forEach((h,i)=>{if(h.hasAttribute("torph-exiting"))return;let a=h.getAttribute("torph-id")||`child-${i}`;s[a]={x:h.offsetLeft,y:h.offsetTop};}),s}updateStyles(e){if(this.isInitialRender)return;let s=Array.from(this.element.children),h=new Set(e.map(i=>i.id).filter(i=>this.prevMeasures[i]));s.forEach((i,a)=>{if(i.hasAttribute("torph-exiting"))return;let p=i.getAttribute("torph-id")||`child-${a}`,l=this.prevMeasures[p],d=this.currentMeasures[p],c=d?.x||0,m=d?.y||0,y=l?l.x-c:0,x=l?l.y-m:0,f=!l;if(f){let r=e.findIndex(n=>n.id===p),o=null;for(let n=r-1;n>=0;n--)if(h.has(e[n].id)){o=e[n].id;break}if(!o){for(let n=r+1;n<e.length;n++)if(h.has(e[n].id)){o=e[n].id;break}}if(o){let n=this.prevMeasures[o],u=this.currentMeasures[o];y=n.x-u.x,x=n.y-u.y;}}i.getAnimations().forEach(r=>r.cancel()),i.animate({transform:`translate(${y}px, ${x}px) scale(${f?.95:1})`,offset:0},{duration:this.options.duration,easing:this.options.ease,fill:"both"});let M=f?this.options.duration*.25:0,t=f?this.options.duration*.25:0;i.animate({opacity:f?0:1,offset:0},{duration:M,delay:t,easing:"linear",fill:"both"});});}addStyles(){if(g.styleEl)return;let e=document.createElement("style");e.dataset.torph="true",e.innerHTML=`
2
+ [torph-root] {
3
+ display: inline-flex;
4
+ position: relative;
5
+ will-change: width, height;
6
+ transition-property: width, height;
7
+ white-space: nowrap;
8
+ }
9
+
10
+ [torph-item] {
11
+ display: inline-block;
12
+ will-change: opacity, transform;
13
+ transform: none;
14
+ opacity: 1;
15
+ }
16
+
17
+ [torph-root][torph-debug] {
18
+ outline:2px solid magenta;
19
+ [torph-item] {
20
+ outline:2px solid cyan;
21
+ outline-offset: -4px;
22
+ }
23
+ }
24
+ `,document.head.appendChild(e),g.styleEl=e;}removeStyles(){g.styleEl&&(g.styleEl.remove(),g.styleEl=void 0);}blocks(e){return Array.from(e).reduce((h,i)=>i.segment===" "?[...h,{id:`space-${i.index}`,string:"\xA0"}]:h.find(p=>p.string===i.segment)?[...h,{id:`${i.segment}-${i.index}`,string:i.segment}]:[...h,{id:i.segment,string:i.segment}],[])}blocksFallback(e,s){let h=s?e.split(" "):e.split(""),i=[];return s?h.forEach((a,p)=>{p>0&&i.push({id:`space-${p}`,string:"\xA0"}),i.find(d=>d.string===a)?i.push({id:`${a}-${p}`,string:a}):i.push({id:a,string:a});}):h.forEach((a,p)=>{i.find(d=>d.string===a)?i.push({id:`${a}-${p}`,string:a}):i.push({id:a,string:a});}),i}log(...e){this.options.debug&&console.log("[TextMorph]",...e);}};export{T as DEFAULT_AS,A as DEFAULT_TEXT_MORPH_OPTIONS,b as TextMorph};//# sourceMappingURL=index.mjs.map
3
25
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../package.json"],"sourcesContent":["{\n \"name\": \"torph\",\n \"version\": \"0.0.4\",\n \"description\": \"Dependency-free animated text component.\",\n \"author\": \"Lochie Axon\",\n \"license\": \"MIT\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/lochie/torph.git\",\n \"directory\": \"packages/torph\"\n },\n \"types\": \"dist/index.d.ts\",\n \"exports\": {\n \".\": {\n \"types\": \"./dist/index.d.ts\",\n \"import\": \"./dist/index.mjs\",\n \"require\": \"./dist/index.js\"\n },\n \"./react\": {\n \"types\": \"./dist/react/index.d.ts\",\n \"import\": \"./dist/react/index.mjs\",\n \"require\": \"./dist/react/index.js\"\n },\n \"./vue\": {\n \"types\": \"./dist/vue/index.d.ts\",\n \"import\": \"./dist/vue/index.mjs\",\n \"require\": \"./dist/vue/index.js\"\n },\n \"./svelte\": {\n \"types\": \"./dist/svelte/index.d.ts\",\n \"import\": \"./dist/svelte/index.mjs\",\n \"require\": \"./dist/svelte/index.js\"\n }\n },\n \"publishConfig\": {\n \"access\": \"public\"\n },\n \"scripts\": {\n \"build\": \"tsup\",\n \"dev\": \"tsup --watch\",\n \"lint\": \"eslint -c .eslintrc.cjs ./src/**/*.{ts,tsx}\",\n \"lint:fix\": \"eslint --fix -c .eslintrc.cjs ./src/**/*.{ts,tsx}\",\n \"pre-commit\": \"lint-staged\"\n },\n \"keywords\": [\n \"react\",\n \"vue\",\n \"svelte\",\n \"text\",\n \"animation\",\n \"morph\"\n ],\n \"bugs\": {\n \"url\": \"https://github.com/lochie/torph/issues\"\n },\n \"files\": [\n \"dist/*\"\n ],\n \"peerDependencies\": {\n \"react\": \">=18\",\n \"react-dom\": \">=18\",\n \"vue\": \">=3\",\n \"svelte\": \">=4\"\n },\n \"peerDependenciesMeta\": {\n \"react\": {\n \"optional\": true\n },\n \"react-dom\": {\n \"optional\": true\n },\n \"vue\": {\n \"optional\": true\n },\n \"svelte\": {\n \"optional\": true\n }\n },\n \"devDependencies\": {\n \"@types/react\": \"^18.2.15\",\n \"@typescript-eslint/eslint-plugin\": \"^6.8.0\",\n \"@typescript-eslint/parser\": \"^6.8.0\",\n \"eslint\": \"^9.36.0\",\n \"eslint-config-airbnb\": \"^19.0.4\",\n \"eslint-config-airbnb-typescript\": \"^17.1.0\",\n \"eslint-config-prettier\": \"^9.0.0\",\n \"eslint-plugin-import\": \"^2.28.1\",\n \"eslint-plugin-jsx-a11y\": \"^6.7.1\",\n \"eslint-plugin-prettier\": \"^5.0.1\",\n \"eslint-plugin-react\": \"^7.33.2\",\n \"eslint-plugin-react-hooks\": \"^4.6.0\",\n \"prettier\": \"^3.0.3\",\n \"react\": \"^18.2.0\",\n \"react-dom\": \"^18.2.0\",\n \"svelte\": \"^4.0.0\",\n \"tsup\": \"^8.5.0\",\n \"typescript\": \"^5.9.3\",\n \"vue\": \"^3.3.0\"\n }\n}\n"],"mappings":";yCAEE,IAAAA,EAAW","names":["version"]}
1
+ {"version":3,"sources":["../src/lib/text-morph/index.ts"],"names":["DEFAULT_AS","DEFAULT_TEXT_MORPH_OPTIONS","TextMorph","_TextMorph","options","anim","event","value","element","oldWidth","oldHeight","byWord","blocks","iterator","oldChildren","newIds","b","exiting","child","exitingSet","exitingAnchorId","i","anchor","j","siblingId","exitPositions","a","pos","id","node","block","span","anchorId","dx","dy","anchorPrev","anchorCurr","animation","newWidth","newHeight","children","measures","index","key","persistentIds","prev","current","cx","cy","deltaX","deltaY","isNew","blockIndex","duration","delay","style","acc","string","x","segments","segment","args"],"mappings":"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","file":"index.mjs","sourcesContent":["import type { TextMorphOptions } from \"./types\";\n\nexport type { TextMorphOptions } from \"./types\";\n\nexport const DEFAULT_AS = \"span\";\nexport const DEFAULT_TEXT_MORPH_OPTIONS = {\n debug: false,\n locale: \"en\",\n duration: 400,\n scale: true,\n ease: \"cubic-bezier(0.19, 1, 0.22, 1)\",\n disabled: false,\n respectReducedMotion: true,\n} as const satisfies Omit<TextMorphOptions, \"element\">;\n\ntype Block = {\n id: string;\n string: string;\n};\ntype Measures = {\n [key: string]: { x: number; y: number };\n};\n\nexport class TextMorph {\n private element: HTMLElement;\n private options: Omit<TextMorphOptions, \"element\"> = {};\n\n private data: HTMLElement | string;\n\n private currentMeasures: Measures = {};\n private prevMeasures: Measures = {};\n private isInitialRender = true;\n private prefersReducedMotion = false;\n private mediaQuery?: MediaQueryList;\n\n static styleEl: HTMLStyleElement;\n\n constructor(options: TextMorphOptions) {\n this.options = {\n ...DEFAULT_TEXT_MORPH_OPTIONS,\n ...options,\n };\n\n this.element = options.element;\n\n // reduced motion detection\n if (typeof window !== \"undefined\" && this.options.respectReducedMotion) {\n this.mediaQuery = window.matchMedia(\"(prefers-reduced-motion: reduce)\");\n this.prefersReducedMotion = this.mediaQuery.matches;\n this.mediaQuery.addEventListener(\"change\", this.handleMediaQueryChange);\n }\n\n if (!this.isDisabled()) {\n this.element.setAttribute(\"torph-root\", \"\");\n this.element.style.transitionDuration = `${this.options.duration}ms`;\n this.element.style.transitionTimingFunction = this.options.ease!;\n\n if (options.debug) this.element.setAttribute(\"torph-debug\", \"\");\n }\n\n this.data = \"\";\n if (!this.isDisabled()) {\n this.addStyles();\n }\n }\n\n destroy() {\n if (this.mediaQuery) {\n this.mediaQuery.removeEventListener(\n \"change\",\n this.handleMediaQueryChange,\n );\n }\n this.element.getAnimations().forEach((anim) => anim.cancel());\n this.element.removeAttribute(\"torph-root\");\n this.element.removeAttribute(\"torph-debug\");\n this.removeStyles();\n }\n\n private handleMediaQueryChange = (event: MediaQueryListEvent) => {\n this.prefersReducedMotion = event.matches;\n };\n\n private isDisabled(): boolean {\n return Boolean(\n this.options.disabled ||\n (this.options.respectReducedMotion && this.prefersReducedMotion),\n );\n }\n\n update(value: HTMLElement | string) {\n if (value === this.data) return;\n this.data = value;\n\n if (this.isDisabled()) {\n if (typeof value === \"string\") {\n this.element.textContent = value;\n }\n return;\n }\n\n if (this.data instanceof HTMLElement) {\n // TODO: handle HTMLElement case\n throw new Error(\"HTMLElement not yet supported\");\n } else {\n if (this.options.onAnimationStart && !this.isInitialRender) {\n this.options.onAnimationStart();\n }\n this.createTextGroup(this.data, this.element);\n }\n }\n\n private createTextGroup(value: string, element: HTMLElement) {\n const oldWidth = element.offsetWidth;\n const oldHeight = element.offsetHeight;\n\n const byWord = value.includes(\" \");\n let blocks: Block[];\n\n if (typeof Intl.Segmenter !== \"undefined\") {\n const segmenter = new Intl.Segmenter(this.options.locale, {\n granularity: byWord ? \"word\" : \"grapheme\",\n });\n const iterator = segmenter.segment(value)[Symbol.iterator]();\n blocks = this.blocks(iterator);\n } else {\n // Fallback for browsers without Intl.Segmenter\n blocks = this.blocksFallback(value, byWord);\n }\n\n this.prevMeasures = this.measure();\n const oldChildren = Array.from(element.children) as HTMLElement[];\n const newIds = new Set(blocks.map((b) => b.id));\n\n const exiting = oldChildren.filter(\n (child) =>\n !newIds.has(child.getAttribute(\"torph-id\") as string) &&\n !child.hasAttribute(\"torph-exiting\"),\n );\n\n // For each exiting char, find the nearest persistent neighbor in old order\n // so we can make it follow that neighbor's FLIP movement\n const exitingSet = new Set(exiting);\n const exitingAnchorId = new Map<HTMLElement, string | null>();\n for (let i = 0; i < oldChildren.length; i++) {\n const child = oldChildren[i]!;\n if (!exitingSet.has(child)) continue;\n\n // Look forward for nearest persistent char\n let anchor: string | null = null;\n for (let j = i + 1; j < oldChildren.length; j++) {\n const siblingId = oldChildren[j]!.getAttribute(\"torph-id\") as string;\n if (newIds.has(siblingId) && !exitingSet.has(oldChildren[j]!)) {\n anchor = siblingId;\n break;\n }\n }\n // If none forward, look backward\n if (!anchor) {\n for (let j = i - 1; j >= 0; j--) {\n const siblingId = oldChildren[j]!.getAttribute(\"torph-id\") as string;\n if (newIds.has(siblingId) && !exitingSet.has(oldChildren[j]!)) {\n anchor = siblingId;\n break;\n }\n }\n }\n exitingAnchorId.set(child, anchor);\n }\n\n // Two-pass: read all positions before modifying any element,\n // since setting position:absolute removes from flow and shifts siblings\n const exitPositions = exiting.map((child) => {\n child.getAnimations().forEach((a) => a.cancel());\n return {\n left: child.offsetLeft,\n top: child.offsetTop,\n width: child.offsetWidth,\n height: child.offsetHeight,\n };\n });\n exiting.forEach((child, i) => {\n const pos = exitPositions[i]!;\n child.setAttribute(\"torph-exiting\", \"\");\n child.style.position = \"absolute\";\n child.style.pointerEvents = \"none\";\n child.style.left = `${pos.left}px`;\n child.style.top = `${pos.top}px`;\n child.style.width = `${pos.width}px`;\n child.style.height = `${pos.height}px`;\n });\n\n oldChildren.forEach((child) => {\n const id = child.getAttribute(\"torph-id\") as string;\n if (newIds.has(id)) child.remove();\n });\n\n // Disabled-mode updates set plain text via textContent; remove that text node\n // before appending torph items so old content is not duplicated.\n Array.from(element.childNodes).forEach((node) => {\n if (node.nodeType === Node.TEXT_NODE) {\n node.remove();\n }\n });\n\n blocks.forEach((block) => {\n const span = document.createElement(\"span\");\n span.setAttribute(\"torph-item\", \"\");\n span.setAttribute(\"torph-id\", block.id);\n span.textContent = block.string;\n element.appendChild(span);\n });\n\n this.currentMeasures = this.measure();\n this.updateStyles(blocks);\n\n exiting.forEach((child) => {\n if (this.isInitialRender) {\n child.remove();\n return;\n }\n\n // Find the anchor neighbor's FLIP delta so we move in sync with it\n const anchorId = exitingAnchorId.get(child);\n let dx = 0;\n let dy = 0;\n\n if (\n anchorId &&\n this.prevMeasures[anchorId] &&\n this.currentMeasures[anchorId]\n ) {\n const anchorPrev = this.prevMeasures[anchorId]!;\n const anchorCurr = this.currentMeasures[anchorId]!;\n dx = anchorCurr.x - anchorPrev.x;\n dy = anchorCurr.y - anchorPrev.y;\n }\n\n child.animate(\n {\n transform: this.options.scale\n ? `translate(${dx}px, ${dy}px) scale(0.95)`\n : `translate(${dx}px, ${dy}px)`,\n offset: 1,\n },\n {\n duration: this.options.duration,\n easing: this.options.ease,\n fill: \"both\",\n },\n );\n const animation: Animation = child.animate(\n {\n opacity: 0,\n offset: 1,\n },\n {\n duration: this.options.duration! * 0.25,\n easing: \"linear\",\n fill: \"both\",\n },\n );\n animation.onfinish = () => child.remove();\n });\n\n if (this.isInitialRender) {\n this.isInitialRender = false;\n element.style.width = \"auto\";\n element.style.height = \"auto\";\n return;\n }\n\n if (oldWidth === 0 || oldHeight === 0) return;\n\n element.style.width = \"auto\";\n element.style.height = \"auto\";\n void element.offsetWidth; // force reflow\n\n const newWidth = element.offsetWidth;\n const newHeight = element.offsetHeight;\n\n element.style.width = `${oldWidth}px`;\n element.style.height = `${oldHeight}px`;\n void element.offsetWidth; // force reflow\n\n element.style.width = `${newWidth}px`;\n element.style.height = `${newHeight}px`;\n\n // TODO: move to `transitionend` event listener\n setTimeout(() => {\n element.style.width = \"auto\";\n element.style.height = \"auto\";\n if (this.options.onAnimationComplete) {\n this.options.onAnimationComplete();\n }\n }, this.options.duration);\n }\n\n private measure() {\n const children = Array.from(this.element.children) as HTMLElement[];\n const measures: Measures = {};\n\n children.forEach((child, index) => {\n if (child.hasAttribute(\"torph-exiting\")) return;\n const key = child.getAttribute(\"torph-id\") || `child-${index}`;\n measures[key] = {\n x: child.offsetLeft,\n y: child.offsetTop,\n };\n });\n\n return measures;\n }\n\n private updateStyles(blocks: Block[]) {\n if (this.isInitialRender) return;\n\n const children = Array.from(this.element.children) as HTMLElement[];\n\n const persistentIds = new Set(\n blocks.map((b) => b.id).filter((id) => this.prevMeasures[id]),\n );\n\n children.forEach((child, index) => {\n if (child.hasAttribute(\"torph-exiting\")) return;\n const key = child.getAttribute(\"torph-id\") || `child-${index}`;\n const prev = this.prevMeasures[key];\n const current = this.currentMeasures[key];\n\n const cx = current?.x || 0;\n const cy = current?.y || 0;\n\n let deltaX = prev ? prev.x - cx : 0;\n let deltaY = prev ? prev.y - cy : 0;\n const isNew = !prev;\n\n // For new chars, use the nearest persistent neighbor's FLIP delta\n // so all new chars get the same consistent offset\n if (isNew) {\n const blockIndex = blocks.findIndex((b) => b.id === key);\n let anchorId: string | null = null;\n\n for (let j = blockIndex - 1; j >= 0; j--) {\n if (persistentIds.has(blocks[j]!.id)) {\n anchorId = blocks[j]!.id;\n break;\n }\n }\n if (!anchorId) {\n for (let j = blockIndex + 1; j < blocks.length; j++) {\n if (persistentIds.has(blocks[j]!.id)) {\n anchorId = blocks[j]!.id;\n break;\n }\n }\n }\n\n if (anchorId) {\n const anchorPrev = this.prevMeasures[anchorId]!;\n const anchorCurr = this.currentMeasures[anchorId]!;\n deltaX = anchorPrev.x - anchorCurr.x;\n deltaY = anchorPrev.y - anchorCurr.y;\n }\n }\n\n child.getAnimations().forEach((a) => a.cancel());\n child.animate(\n {\n transform: `translate(${deltaX}px, ${deltaY}px) scale(${isNew ? 0.95 : 1})`,\n offset: 0,\n },\n {\n duration: this.options.duration,\n easing: this.options.ease,\n fill: \"both\",\n },\n );\n const duration = isNew ? this.options.duration! * 0.25 : 0;\n const delay = isNew ? this.options.duration! * 0.25 : 0;\n child.animate(\n {\n opacity: isNew ? 0 : 1,\n offset: 0,\n },\n {\n duration: duration,\n delay: delay,\n easing: \"linear\",\n fill: \"both\",\n },\n );\n });\n }\n\n private addStyles() {\n if (TextMorph.styleEl) return;\n\n const style = document.createElement(\"style\");\n style.dataset.torph = \"true\";\n style.innerHTML = `\n[torph-root] {\n display: inline-flex;\n position: relative;\n will-change: width, height;\n transition-property: width, height;\n white-space: nowrap;\n}\n\n[torph-item] {\n display: inline-block;\n will-change: opacity, transform;\n transform: none;\n opacity: 1;\n}\n\n[torph-root][torph-debug] {\n outline:2px solid magenta;\n [torph-item] {\n outline:2px solid cyan;\n outline-offset: -4px;\n }\n}\n `;\n document.head.appendChild(style);\n TextMorph.styleEl = style;\n }\n\n private removeStyles() {\n if (TextMorph.styleEl) {\n TextMorph.styleEl.remove();\n TextMorph.styleEl = undefined!;\n }\n }\n\n // utils\n\n private blocks(iterator: Intl.SegmentIterator<Intl.SegmentData>) {\n const uniqueStrings: Block[] = Array.from(iterator).reduce(\n (acc, string) => {\n if (string.segment === \" \") {\n return [...acc, { id: `space-${string.index}`, string: \"\\u00A0\" }];\n }\n\n const existingString = acc.find((x) => x.string === string.segment);\n if (existingString) {\n return [\n ...acc,\n { id: `${string.segment}-${string.index}`, string: string.segment },\n ];\n }\n\n return [\n ...acc,\n {\n id: string.segment,\n string: string.segment,\n },\n ];\n },\n [] as Block[],\n );\n\n return uniqueStrings;\n }\n\n private blocksFallback(value: string, byWord: boolean): Block[] {\n const segments = byWord ? value.split(\" \") : value.split(\"\");\n const blocks: Block[] = [];\n\n if (byWord) {\n segments.forEach((segment, index) => {\n if (index > 0) {\n blocks.push({ id: `space-${index}`, string: \"\\u00A0\" });\n }\n const existing = blocks.find((x) => x.string === segment);\n if (existing) {\n blocks.push({ id: `${segment}-${index}`, string: segment });\n } else {\n blocks.push({ id: segment, string: segment });\n }\n });\n } else {\n segments.forEach((segment, index) => {\n const existing = blocks.find((x) => x.string === segment);\n if (existing) {\n blocks.push({ id: `${segment}-${index}`, string: segment });\n } else {\n blocks.push({ id: segment, string: segment });\n }\n });\n }\n\n return blocks;\n }\n\n private log(...args: any[]) {\n if (this.options.debug) console.log(\"[TextMorph]\", ...args);\n }\n}\n"]}
@@ -1,10 +1,25 @@
1
1
  import React from 'react';
2
- import { T as TextMorphOptions } from '../types-CsvRfPun.mjs';
2
+
3
+ interface TextMorphOptions {
4
+ debug?: boolean;
5
+ element: HTMLElement;
6
+ locale?: Intl.LocalesArgument;
7
+ scale?: boolean;
8
+ duration?: number;
9
+ ease?: string;
10
+ disabled?: boolean;
11
+ respectReducedMotion?: boolean;
12
+ onAnimationStart?: () => void;
13
+ onAnimationComplete?: () => void;
14
+ }
3
15
 
4
16
  type TextMorphProps = Omit<TextMorphOptions, "element"> & {
5
- children: string;
17
+ children: React.ReactNode;
18
+ className?: string;
19
+ style?: React.CSSProperties;
20
+ as?: React.ElementType;
6
21
  };
7
- declare const TextMorph: ({ children, ...props }: TextMorphProps) => React.JSX.Element;
22
+ declare const TextMorph: ({ children, className, style, as, ...props }: TextMorphProps) => React.JSX.Element;
8
23
  declare function useTextMorph(props: Omit<TextMorphOptions, "element">): {
9
24
  ref: React.MutableRefObject<HTMLDivElement | null>;
10
25
  update: (text: string) => void;