situs-kit 0.1.8 → 0.1.9

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
@@ -8,125 +8,11 @@ A zero-dependency creative developer toolkit
8
8
  npm install situs-kit
9
9
  ```
10
10
 
11
- ## SplitText
11
+ ## Modules
12
12
 
13
- Split text into characters, words, and lines while preserving inline HTML elements like `<a>`, `<em>`, `<strong>`, and `<u>`.
14
-
15
- ### Basic Usage
16
-
17
- ```ts
18
- import { SplitText } from "situs-kit/split-text";
19
-
20
- const split = new SplitText("#my-text", {
21
- type: ["chars", "words", "lines"],
22
- });
23
-
24
- split.chars; // HTMLElement[]
25
- split.words; // HTMLElement[]
26
- split.lines; // HTMLElement[]
27
- ```
28
-
29
- ### Options
30
-
31
- ```ts
32
- new SplitText(element, {
33
- type: ["chars", "words", "lines"], // which levels to split
34
- tag: "span", // wrapper element tag
35
- mask: ["chars", "lines"], // which levels to mask
36
- resize: true, // auto-reflow lines on resize
37
- style: {
38
- chars: { color: "red" },
39
- words: {},
40
- lines: {},
41
- mask: {},
42
- },
43
- class: {
44
- chars: "my-char",
45
- words: "my-word",
46
- lines: "my-line",
47
- mask: "my-mask",
48
- },
49
- });
50
- ```
51
-
52
- | Option | Type | Default | Description |
53
- |--------|------|---------|-------------|
54
- | `type` | `string \| string[]` | `["chars", "words", "lines"]` | Split granularity |
55
- | `tag` | `string` | `"span"` | Wrapper element tag |
56
- | `mask` | `boolean \| SplitType[]` | `false` | Create overflow-hidden wrappers per split type |
57
- | `resize` | `boolean` | `true` | Auto-reflow lines on window resize |
58
- | `style` | `object` | — | Inline styles per split type |
59
- | `class` | `object` | — | CSS classes per split type |
60
-
61
- ### Properties
62
-
63
- | Property | Type | Description |
64
- |----------|------|-------------|
65
- | `dom` | `HTMLElement` | The original element |
66
- | `chars` | `HTMLElement[]` | Character elements |
67
- | `words` | `HTMLElement[]` | Word elements |
68
- | `lines` | `HTMLElement[]` | Line elements |
69
- | `masks` | `{ chars, words, lines }` | Mask wrapper elements |
70
-
71
- ### Methods
72
-
73
- #### `reflow()`
74
-
75
- Recalculate line groupings without re-splitting. Called automatically on resize.
76
-
77
- ```ts
78
- split.reflow();
79
- ```
80
-
81
- #### `revert()`
82
-
83
- Restore the element to its original HTML.
84
-
85
- ```ts
86
- split.revert();
87
- ```
88
-
89
- #### `destroy()`
90
-
91
- Remove resize observers and event listeners.
92
-
93
- ```ts
94
- split.destroy();
95
- ```
96
-
97
- ### Styling with CSS
98
-
99
- Split elements have data attributes for easy targeting:
100
-
101
- ```css
102
- [data-char] { display: inline-block; }
103
- [data-word] { display: inline-block; }
104
- [data-line] { display: block; }
105
- ```
106
-
107
- ### HTML Preservation
108
-
109
- Inline elements like `<a>`, `<em>`, `<strong>`, `<u>`, `<i>`, and `<b>` are preserved and cloned around split elements. Opaque elements like `<svg>`, `<img>`, and `<br>` are treated as single units.
110
-
111
- ```html
112
- <!-- Input -->
113
- <p id="text">Hello <strong>world</strong></p>
114
-
115
- <!-- After split, <strong> wraps are preserved -->
116
- ```
117
-
118
- ### Masking
119
-
120
- Masks wrap split elements in `overflow: hidden` containers, useful for reveal animations. Like `type`, you specify which split levels to mask:
121
-
122
- ```ts
123
- const split = new SplitText("#text", {
124
- type: ["words", "lines"],
125
- mask: ["lines"], // only mask lines
126
- });
127
-
128
- split.masks.lines; // HTMLElement[] - overflow:hidden wrappers
129
- ```
13
+ - [SplitText](./docs/split-text.md) — Split text into characters, words, and lines with HTML preservation
14
+ - [SmoothScroll](./docs/smooth-scroll.md) — Lerp-based smooth scroll with native touch/mobile support
15
+ - [Ticker](./docs/ticker.md) — Shared RAF loop manager for frame-synced animations
130
16
 
131
17
  ## License
132
18
 
@@ -0,0 +1,2 @@
1
+ function R(j,k){return typeof globalThis.getComputedStyle==="function"?globalThis.getComputedStyle(j,k??null):null}var K=["whe","mou","tou","poi"];function J(j){return K.includes(j.slice(0,3))}function L(j,k){if(!k)return J(j)?{passive:!1}:!1;if(typeof k==="object"&&k.passive===void 0&&J(j))return{...k,passive:!1};return k}function M(j){if(!j)return!1;if(typeof j==="object")return j.capture??!1;return j}function V(j,k,w,B,D){let E=k==="a"?"addEventListener":"removeEventListener",G=k==="a"?L(w,D):M(D);if(typeof j==="string"){let H=Q(j);for(let z=0;z<H.length;z++)H[z][E](w,B,G)}else j[E](w,B,G)}function W(j,k){let w=k??document;return typeof w.querySelector==="function"?w.querySelector(j):null}function Q(j,k){let w=k??document;return typeof w.querySelectorAll==="function"?Array.from(w.querySelectorAll(j)):[]}
2
+ export{R as c,V as d,W as e,Q as f};
@@ -0,0 +1,2 @@
1
+ var J=0,G=0;function N(){return G}var v=[],x=0,D=!0,y=0,E=!1;function H(q){if(D){let j=q-y;y=q,G=j/16.666666666666668;for(let B=x-1;B>=0;B--){let A=v[B];if(!A)continue;A.sT||=q;let I=q-A.sT;A.cb(I)}}requestAnimationFrame(H)}function K(){if(document.hidden)D=!1;else{let q=performance.now()-y;y=performance.now();for(let j=x-1;j>=0;j--)v[j].sT+=q;D=!0}}function L(){if(E)return;if(E=!0,y=performance.now(),typeof document<"u")document.addEventListener("visibilitychange",K);requestAnimationFrame(H)}var M={add(q){L();let j=J++;return v.push({id:j,cb:q,sT:0}),x++,()=>M.remove(q)},remove(q){for(let j=x-1;j>=0;j--)if(v[j].cb===q)v.splice(j,1),x--}};
2
+ export{N as g,M as h};
@@ -0,0 +1,2 @@
1
+ import{c as C,e as c,f as w}from"./chunk-0t3a7kj5.js";var I=1,x=2,K=4,_="inline-block",h="nowrap",f="baseline",u="0px",E="block",m="maskChar",g="maskWord",n="maskLine",o=/^\s+$/,l=/(\s+)/,t=",div,span,svg,img,br,hr,canvas,video,audio,iframe,input,textarea,select,button,picture,figure,",p={u:"underline",ins:"underline",s:"line-through",strike:"line-through",del:"line-through"};class a{dom;chars;words;lines;masks;_n;_k;_g;_j;_f=[];_o=new Map;_i=null;_h=0;_l=!1;_c="";_d;_e;_b;_a=null;constructor(z,F={}){let J;if(typeof z==="string"){let $=w(z);if($.length>1)J=$;else{let Q=$[0]??c(z);if(!Q)throw Error("SplitText: element not found");J=Q}}else J=z;if(Array.isArray(J)||typeof J==="object"&&J!==null&&"length"in J&&typeof J.item==="function"){let $=Array.from(J);if($.length===0)throw Error("SplitText: empty element array");this._a=$.map((Q)=>new a(Q,F)),this.dom=$,this.chars=this._a.flatMap((Q)=>Q.chars),this.words=this._a.flatMap((Q)=>Q.words),this.lines=this._a.flatMap((Q)=>Q.lines),this.masks={chars:this._a.flatMap((Q)=>Q.masks.chars),words:this._a.flatMap((Q)=>Q.masks.words),lines:this._a.flatMap((Q)=>Q.masks.lines)},this._n="",this._k=0,this._g=0,this._j="";return}let U=J;this._b=U,this.dom=U,this._n=U.innerHTML,this.chars=[],this.words=[],this.lines=[],this.masks={chars:[],words:[],lines:[]};let G=F.type?Array.isArray(F.type)?F.type:[F.type]:["chars","words","lines"],D=0;for(let $ of G)if($==="chars")D|=I;else if($==="words")D|=x;else if($==="lines")D|=K;this._k=D,this._j=F.tag??"span";let Z=0;if(F.mask===!0)Z=D;else if(Array.isArray(F.mask)){for(let $ of F.mask)if($==="chars")Z|=I;else if($==="words")Z|=x;else if($==="lines")Z|=K}if(this._g=Z,this._d=F.style,this._e=F.class,this._w(),(F.resize??!0)&&D&K)this._x()}_p(z,F){let J=document.createElement(this._j);if(z)J.dataset[z]="";if(J.style.display=_,F!==void 0)J.textContent=F;return J}_s(z,F){let J=document.createElement(this._j);if(J.dataset[F]="",J.style.overflow="hidden",J.style.display=z,this._d?.mask)Object.assign(J.style,this._d.mask);if(this._e?.mask)J.classList.add(this._e.mask);return J}_r(z,F,J,q,U){if(this._g&F){let G=this._s(J,q);return G.appendChild(z),U.push(G),G}return z}_m(z,F,J,q){if(J){let{outer:U,inner:G}=J();if(G.appendChild(F),q)this._o.set(q,U);z.appendChild(U)}else z.appendChild(F)}_q(z,F,J){if(z.style.textDecoration=F,J)z.style.textDecorationColor=J}_w(){this._l=!0;let z=this._b,F=C(z),J=F?parseFloat(F.letterSpacing)||0:0;if(this._c=F?F.textIndent:"",this._c&&this._c!==u)z.style.textIndent="0";let q=this._k,U=!!(q&I),G=!!(q&x),D=!!(q&K),Z=G||D,$=!!(this._g&I),Q=new Map,H=(M)=>{for(let N of Array.from(M.childNodes))if(N.nodeType===1){let O=N.tagName.toLowerCase();if(p[O]){let v=C(N)?.textDecorationColor;if(v)Q.set(N,v)}H(N)}};H(z);let Y=Array.from(z.childNodes),T=document.createDocumentFragment();z.replaceChildren();let k=(M,N,O,v)=>{if(M.nodeType===3){let X=(M.textContent??"").split(l);for(let j of X){if(!j)continue;if(o.test(j)){T.appendChild(document.createTextNode(j));continue}let V=Z?this._p(G?"word":""):null;if(V){if(V.style.whiteSpace=h,O&&!U)this._q(V,O,v)}if(U){let b=[...j],d=this.chars.length;for(let P=0;P<b.length;P++){let R=this._p("char",b[P]);if(J&&P<b.length-1)R.style.marginRight=`${J}px`;if(O)this._q(R,O,v);if(this.chars.push(R),this._d?.chars)Object.assign(R.style,this._d.chars);if(this._e?.chars)R.classList.add(this._e.chars);let y=$?this._r(R,I,_,m,this.masks.chars):R;if(V)V.appendChild(y)}if(!V){let P=N?N():null,R=P?P.inner:T;for(let y=d;y<this.chars.length;y++){let S=this.chars[y];R.appendChild($&&S.parentElement?.dataset?.[m]!==void 0?S.parentElement:S)}if(P)T.appendChild(P.outer)}}else if(V){if(V.textContent=j,J)V.style.letterSpacing=`${J}px`}if(V){if(G){if(this.words.push(V),this._d?.words)Object.assign(V.style,this._d.words);if(this._e?.words)V.classList.add(this._e.words)}this._f.push(V);let b=this._g&x?this._r(V,x,_,g,this.masks.words):V;this._m(T,b,N,V)}else if(!U)this._m(T,document.createTextNode(j),N)}return}if(M.nodeType!==1)return;let B=M,A=B.tagName.toLowerCase();if(t.includes(","+A+",")){let X=B.cloneNode(!0);if(O)this._q(X,O,v);let j=C(B)?.verticalAlign;if(j&&j!==f)X.style.verticalAlign=j;if(Z){let V=this._p(G?"word":"");if(V.style.whiteSpace=h,j&&j!==f)V.style.verticalAlign=j;if(V.appendChild(X),G){if(this.words.push(V),this._d?.words)Object.assign(V.style,this._d.words);if(this._e?.words)V.classList.add(this._e.words)}this._f.push(V);let b=this._g&x?this._r(V,x,_,g,this.masks.words):V;this._m(T,b,N,V)}else this._m(T,X,N);return}let r=()=>{let X=B.cloneNode(!1);if(N){let{outer:j,inner:V}=N();return V.appendChild(X),{outer:j,inner:X}}return{outer:X,inner:X}},L=p[A],i=L?O?`${O} ${L}`:L:O,s=L?Q.get(B)||v:v;for(let X of Array.from(B.childNodes))k(X,r,i,s)};for(let M of Y)k(M,null,"","");z.appendChild(T);let W=!!(this._c&&this._c!==u);if(D)this._y(W);else if(W){let M=G?this.words[0]:U?this.chars[0]:null;if(M)M.style.marginLeft=this._c,z.style.textIndent="0"}this._l=!1}_y(z){if(z&&this._f[0])this._f[0].style.marginLeft=this._c;this._t();let F=[];if(typeof document.fonts?.status==="string"&&document.fonts.status!=="loaded")F.push(document.fonts.ready);let J=this._b.querySelectorAll("img");for(let q=0;q<J.length;q++){let U=J[q];if(!U.complete)F.push(new Promise((G)=>{U.addEventListener("load",G,{once:!0}),U.addEventListener("error",G,{once:!0})}))}if(F.length){let q=this._b.offsetHeight;Promise.all(F).then(()=>{if(this._b.offsetHeight!==q)this.reflow()})}}_u(z){return this._o.get(z)??z}_t(){let z=this._f;if(!z.length)return;let F=z[0].offsetTop,J=F+z[0].offsetHeight,q=[],U=[];for(let $ of z){let Q=$.offsetTop,H=Q+$.offsetHeight;if(Q<J&&H>F)F=Math.min(F,Q),J=Math.max(J,H);else U.push(q),q=[],F=Q,J=H;q.push($)}if(q.length)U.push(q);let G=!!(this._c&&this._c!==u);if(G&&z[0])z[0].style.marginLeft="";let D=!!(this._g&K),Z=document.createDocumentFragment();for(let $=0;$<U.length;$++){let Q=U[$],H=document.createElement(this._j);if(H.dataset.line="",H.style.display=E,$===0&&G)H.style.paddingLeft=this._c;for(let Y=0;Y<Q.length;Y++){if(Y>0)H.appendChild(document.createTextNode(" "));H.appendChild(this._u(Q[Y]))}if(this.lines.push(H),this._d?.lines)Object.assign(H.style,this._d.lines);if(this._e?.lines)H.classList.add(this._e.lines);if(D){let Y=this._s(E,n);Y.appendChild(H),this.masks.lines.push(Y),Z.appendChild(Y)}else Z.appendChild(H)}this._b.replaceChildren(Z)}_x(){let z=this._b.offsetParent?this._b.offsetParent.offsetWidth:window.innerWidth,F=()=>{if(this._l)return;let q=this._b.offsetParent,U=q?q.offsetWidth:window.innerWidth;if(U===z)return;if(z=U,this._h)cancelAnimationFrame(this._h);this._h=requestAnimationFrame(()=>this.reflow())};this._i=new ResizeObserver(F);let J=this._b.offsetParent??this._b.parentElement;if(J)this._i.observe(J);else this._i.observe(this._b)}reflow(){if(this._a){this._a.forEach((F)=>F.reflow()),this._v();return}if(!(this._k&K)||this._l)return;let z=document.createDocumentFragment();for(let F=0;F<this._f.length;F++){if(F>0)z.appendChild(document.createTextNode(" "));z.appendChild(this._u(this._f[F]))}if(this._b.replaceChildren(z),this.lines.length=0,this.masks.lines.length=0,this._c&&this._c!==u&&this._f[0])this._f[0].style.marginLeft=this._c;this._t()}revert(){if(this._a){this._a.forEach((z)=>z.revert()),this._v();return}this.destroy(),this._b.innerHTML=this._n,this.chars.length=0,this.words.length=0,this._f.length=0,this._o.clear(),this.lines.length=0,this.masks.chars.length=0,this.masks.words.length=0,this.masks.lines.length=0}destroy(){if(this._a){this._a.forEach((z)=>z.destroy());return}if(this._h)cancelAnimationFrame(this._h),this._h=0;if(this._i)this._i.disconnect(),this._i=null}_v(){if(!this._a)return;this.chars.length=0,this.words.length=0,this.lines.length=0,this.masks.chars.length=0,this.masks.words.length=0,this.masks.lines.length=0;for(let z of this._a)this.chars.push(...z.chars),this.words.push(...z.words),this.lines.push(...z.lines),this.masks.chars.push(...z.masks.chars),this.masks.words.push(...z.masks.words),this.masks.lines.push(...z.masks.lines)}}
2
+ export{a};
@@ -0,0 +1,2 @@
1
+ import{d as N,e as P}from"./chunk-0t3a7kj5.js";import{g as Z,h as Q}from"./chunk-qjsknche.js";function O(j,z,B){return Math.min(Math.max(j,z),B)}function $(j,z,B){return j+(z-j)*B}function X(j,z,B){let G=z-j;return G===0?0:O((B-j)/G,0,1)}function Y(j,z,B,G){let H=1-Math.exp(-B*G);return $(j,z,H)}var J=0.09;class y{_d;_f;_c;_x;_e;_y;_o=!1;_q=!1;_m=new Map;_a=0;_b=0;_k=0;_l=1;_p=!1;_L={current:0,target:0,velocity:0,direction:1,max:0,scrolling:!1};_g=!1;_w=0;_r=0;_E=0;_h=0;_s=null;_i=J;_j=null;_z;_A;_B;_t;constructor(j={}){let z=j.wrapper??window;this._d=typeof z==="string"?P(z)??window:z,this._f=this._d===window,this._c=(j.direction??"vertical")==="vertical",this._x=j.duration??0,this._e=j.ease??J,this._y=j.prevent??!this._f,this._z=this._H.bind(this),this._A=this._I.bind(this),this._B=this._J.bind(this),this._t=()=>this._K();let B=this._f?this._c?window.scrollY:window.scrollX:this._c?this._d.scrollTop:this._d.scrollLeft;this._a=B,this._b=B,this.start()}get scroll(){let j=this._L;return j.current=this._a,j.target=this._b,j.velocity=this._k,j.direction=this._l,j.max=this._u(),j.scrolling=this._p,j}_u(){if(this._f)return this._c?document.documentElement.scrollHeight-window.innerHeight:document.documentElement.scrollWidth-window.innerWidth;let j=this._d;return this._c?j.scrollHeight-j.clientHeight:j.scrollWidth-j.clientWidth}_v(j){return O(j,0,this._u())}on(j,z){let B=this._m.get(j);if(!B)B=new Set,this._m.set(j,B);B.add(z)}off(j,z){this._m.get(j)?.delete(z)}_D(j){let z=this._m.get(j);if(!z)return;let B=this.scroll;for(let G of z)G(B)}_n(){if(this._q)return;this._q=!0,Q.add(this._t)}_G(){if(!this._q)return;this._q=!1,Q.remove(this._t)}to(j,z={}){let B;if(typeof j==="number")B=j;else{let K=typeof j==="string"?P(j):j;if(!K)return;let M=K.getBoundingClientRect();if(this._f)B=(this._c?M.top:M.left)+this._a;else{let V=this._d.getBoundingClientRect();B=(this._c?M.top-V.top:M.left-V.left)+this._a}}if(B+=z.offset??0,B=this._v(B),z.immediate){this._a=B,this._b=B,this._g=!0,this._C(),this._D("scroll"),this._g=!1,z.onComplete?.();return}this._g=!0,this._n();let G=z.ease??this._e,H=z.duration??this._x;if(typeof G==="number")this._h=0,this._b=B,this._i=G,this._j=z.onComplete??null;else if(H>0)this._w=this._a,this._r=B,this._E=performance.now(),this._h=H,this._i=G,this._s=z.onComplete??null,this._j=null;else this._h=0,this._b=B,this._i=typeof this._e==="number"?this._e:J,this._j=z.onComplete??null}_F(j){let z=this._f?window:this._d;N(z,j,"wheel",this._z),N(z,j,"scroll",this._A,{passive:!0}),N(window,j,"keydown",this._B)}start(){if(this._o)return;this._o=!0,this._F("a")}stop(){if(!this._o)return;this._o=!1,this._F("r"),this._G()}destroy(){this.stop(),this._m.clear()}_H(j){if(j.preventDefault(),this._y)j.stopPropagation();let z=this._c?j.deltaY:j.deltaX;if(j.deltaMode===1)z*=0.833333;else if(j.deltaMode===2)z*=0.555556;this._b=this._v(this._b+z),this._h=0,this._i=typeof this._e==="number"?this._e:J,this._g=!0,this._n(),this._l=z>0?1:-1}_I(){if(this._g)return;let j=this._f?this._c?window.scrollY:window.scrollX:this._c?this._d.scrollTop:this._d.scrollLeft;this._a=j,this._b=j}_J(j){if(!this._f){let G=document.activeElement;if(G!==this._d&&!this._d.contains(G))return}let z=0,B=this._c?this._f?window.innerHeight:this._d.clientHeight:this._f?window.innerWidth:this._d.clientWidth;switch(j.key){case"ArrowDown":z=100;break;case"ArrowUp":z=-100;break;case"ArrowRight":if(!this._c)z=100;break;case"ArrowLeft":if(!this._c)z=-100;break;case" ":z=j.shiftKey?-B:B;break;case"PageDown":z=B;break;case"PageUp":z=-B;break;case"Home":this._b=0,this._h=0,this._i=typeof this._e==="number"?this._e:J,this._g=!0,this._n();return;case"End":this._b=this._u(),this._h=0,this._i=typeof this._e==="number"?this._e:J,this._g=!0,this._n();return;default:return}if(z)j.preventDefault(),this._b=this._v(this._b+z),this._h=0,this._i=typeof this._e==="number"?this._e:J,this._g=!0,this._n(),this._l=z>0?1:-1}_C(){if(this._f)if(this._c)window.scrollTo(0,this._a);else window.scrollTo(this._a,0);else{let j=this._d;if(this._c)j.scrollTop=this._a;else j.scrollLeft=this._a}}_K(){if(!this._o)return;let j=this._a,z=!1;if(this._g){if(this._h>0){let G=performance.now()-this._E,H=X(0,this._h,G),K=this._i(H);if(this._a=this._w+(this._r-this._w)*K,this._b=this._r,H>=1)this._a=this._r,this._h=0,z=!0}else{let G=this._i;if(this._a=Y(this._a,this._b,G,Z()),Math.abs(this._b-this._a)<0.5)this._a=this._b,z=!0}this._C()}this._k=this._a-j;let B=this._p;if(this._p=Math.abs(this._k)>0.01,this._k>0)this._l=1;else if(this._k<0)this._l=-1;if(this._p||B)this._D("scroll");if(z){if(this._g=!1,this._G(),this._s){let G=this._s;this._s=null,G()}if(this._j){let G=this._j;this._j=null,G()}}}}
2
+ export{y as b};
@@ -1,2 +1,75 @@
1
- /** Shorthand for `getComputedStyle`. Returns `null` when unavailable (e.g. SSR). */
1
+ /**
2
+ * Shorthand for `getComputedStyle`.
3
+ * @param el - The element to compute styles for.
4
+ * @param pseudo - Optional pseudo-element selector (e.g. `"::before"`).
5
+ * @returns The computed style declaration, or `null` when unavailable (e.g. SSR).
6
+ */
2
7
  export declare function gCS(el: Element, pseudo?: string | null): CSSStyleDeclaration | null;
8
+ /** A scope for DOM queries — defaults to `document` when `null` or `undefined`. */
9
+ type Scope = Document | Element | HTMLElement | null | undefined;
10
+ /**
11
+ * Shorthand DOM element getters.
12
+ *
13
+ * @example
14
+ * ```ts
15
+ * Get.i("app"); // document.getElementById("app")
16
+ * Get.c("card", wrapper); // wrapper.getElementsByClassName("card")
17
+ * Get.t("img"); // document.getElementsByTagName("img")
18
+ * ```
19
+ */
20
+ export declare const Get: {
21
+ /**
22
+ * Get an element by its ID.
23
+ * @param id - The element ID (without `#`).
24
+ * @param scope - Optional parent scope.
25
+ */
26
+ i: (id: string, scope?: Scope) => HTMLElement;
27
+ /**
28
+ * Get elements by class name.
29
+ * @param className - The class name to match.
30
+ * @param scope - Optional parent scope.
31
+ */
32
+ c: (className: string, scope?: Scope) => HTMLElement[];
33
+ /**
34
+ * Get elements by tag name.
35
+ * @param tagName - The HTML tag to match.
36
+ * @param scope - Optional parent scope.
37
+ */
38
+ t: (tagName: string, scope?: Scope) => HTMLElement[] | HTMLCollectionOf<HTMLElement>;
39
+ };
40
+ type ListenerScope = Element | HTMLElement | Document | Window;
41
+ /**
42
+ * Add or remove event listeners on one or more targets.
43
+ *
44
+ * Input events (`wheel`, `mouse*`, `touch*`, `pointer*`) default to
45
+ * `{ passive: false }` when no options are provided.
46
+ *
47
+ * @param target - A CSS selector, element, `document`, or `window`.
48
+ * @param action - `"a"` to add, `"r"` to remove.
49
+ * @param type - Event type (e.g. `"click"`, `"wheel"`).
50
+ * @param handler - The event handler.
51
+ * @param opts - Optional `AddEventListenerOptions`.
52
+ *
53
+ * @example
54
+ * ```ts
55
+ * Listen(window, "a", "scroll", onScroll, { passive: true });
56
+ * Listen(".card", "a", "click", onClick);
57
+ * Listen(el, "r", "wheel", onWheel);
58
+ * ```
59
+ */
60
+ export declare function Listen(target: string | ListenerScope, action: "a" | "r", type: string, handler: EventListenerOrEventListenerObject | ((e: any) => void), opts?: AddEventListenerOptions): void;
61
+ /**
62
+ * `querySelector` shorthand — returns a single matching element or `null`.
63
+ * @param selector - A CSS selector string.
64
+ * @param scope - Optional parent scope (defaults to `document`).
65
+ * @returns The first matching element, or `null`.
66
+ */
67
+ export declare function qS<T extends HTMLElement = HTMLElement>(selector: string, scope?: Scope): T | null;
68
+ /**
69
+ * `querySelectorAll` shorthand — returns an array of matching elements.
70
+ * @param selector - A CSS selector string.
71
+ * @param scope - Optional parent scope (defaults to `document`).
72
+ * @returns An array of matching elements (empty if unavailable).
73
+ */
74
+ export declare function qSA<T extends HTMLElement = HTMLElement>(selector: string, scope?: Scope): T[];
75
+ export {};
package/dist/index.d.ts CHANGED
@@ -1 +1,3 @@
1
1
  export { SplitText, type SplitTextOptions, type SplitType, } from "./split-text/index.ts";
2
+ export { SmoothScroll, type SmoothScrollOptions, type ScrollToOptions, type ScrollState, } from "./smooth-scroll/index.ts";
3
+ export { Ticker, getDeltaFrame, type TickerCallback } from "./ticker/index.ts";
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- import{a as b}from"./chunk-8pnv9yxr.js";export{b as SplitText};
1
+ import{a as t}from"./chunk-se6yyp59.js";import{b as p}from"./chunk-xcfcqftx.js";import"./chunk-0t3a7kj5.js";import{g as e,h as o}from"./chunk-qjsknche.js";export{e as getDeltaFrame,o as Ticker,t as SplitText,p as SmoothScroll};
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Round a number to a given precision.
3
+ * @param v - The value to round.
4
+ * @param r - Decimal places (defaults to 2 when omitted).
5
+ * @returns The rounded value.
6
+ */
7
+ export declare const round: (v: number, r?: number) => number;
8
+ /**
9
+ * Clamp a value between a minimum and maximum.
10
+ * @param value - The value to clamp.
11
+ * @param min - Lower bound.
12
+ * @param max - Upper bound.
13
+ * @returns The clamped value.
14
+ */
15
+ export declare function clamp(value: number, min: number, max: number): number;
16
+ /**
17
+ * Linearly interpolate between two values.
18
+ * @param start - Start value (returned when `t = 0`).
19
+ * @param end - End value (returned when `t = 1`).
20
+ * @param t - Interpolation factor, typically in `[0, 1]`.
21
+ * @returns The interpolated value.
22
+ */
23
+ export declare function lerp(start: number, end: number, t: number): number;
24
+ /**
25
+ * Damp a value towards a target using an exponential ease-out.
26
+ * @param current - Current value.
27
+ * @param target - Target value.
28
+ * @param lambda - Damping factor (higher values = faster).
29
+ * @param dt - Delta time (usually in seconds).
30
+ * @returns The dampened value.
31
+ */
32
+ /**
33
+ * Inverse linear interpolation — returns where `v` sits between `a` and `b` as a `[0, 1]` factor.
34
+ * @param a - Start value.
35
+ * @param b - End value.
36
+ * @param v - The value to map.
37
+ * @returns Clamped factor in `[0, 1]`.
38
+ */
39
+ export declare function iLerp(a: number, b: number, v: number): number;
40
+ export declare function damp(current: number, target: number, lambda: number, dt: number): number;
@@ -0,0 +1,83 @@
1
+ export interface SmoothScrollOptions {
2
+ wrapper?: Window | HTMLElement | string;
3
+ direction?: "vertical" | "horizontal";
4
+ duration?: number;
5
+ ease?: number | ((t: number) => number);
6
+ prevent?: boolean;
7
+ }
8
+ export interface ScrollToOptions {
9
+ offset?: number;
10
+ duration?: number;
11
+ ease?: number | ((t: number) => number);
12
+ immediate?: boolean;
13
+ onComplete?: () => void;
14
+ }
15
+ export interface ScrollState {
16
+ current: number;
17
+ target: number;
18
+ velocity: number;
19
+ direction: 1 | -1;
20
+ max: number;
21
+ scrolling: boolean;
22
+ }
23
+ type EventCallback = (scroll: ScrollState) => void;
24
+ export declare class SmoothScroll {
25
+ /** @internal */ _wrapper: Window | HTMLElement;
26
+ /** @internal */ _isW: boolean;
27
+ /** @internal */ _vertical: boolean;
28
+ /** @internal */ _duration: number;
29
+ /** @internal */ _ease: number | ((t: number) => number);
30
+ /** @internal */ _preventParent: boolean;
31
+ /** @internal */ _listening: boolean;
32
+ /** @internal */ _ticking: boolean;
33
+ /** @internal */ _listeners: Map<string, Set<EventCallback>>;
34
+ /** @internal */ _current: number;
35
+ /** @internal */ _target: number;
36
+ /** @internal */ _velocity: number;
37
+ /** @internal */ _dir: 1 | -1;
38
+ /** @internal */ _scrolling: boolean;
39
+ /** @internal */ _state: ScrollState;
40
+ /** @internal */ _driving: boolean;
41
+ /** @internal */ _aFrom: number;
42
+ /** @internal */ _aTo: number;
43
+ /** @internal */ _aStart: number;
44
+ /** @internal */ _aDur: number;
45
+ /** @internal */ _aCb: (() => void) | null;
46
+ /** @internal */ _aEase: number | ((t: number) => number);
47
+ /** @internal */ _aComplete: (() => void) | null;
48
+ /** @internal */ _onWheelBound: (e: WheelEvent) => void;
49
+ /** @internal */ _onScrollBound: () => void;
50
+ /** @internal */ _onKeyDownBound: (e: KeyboardEvent) => void;
51
+ /** @internal */ _tickBound: () => void;
52
+ constructor(options?: SmoothScrollOptions);
53
+ get scroll(): ScrollState;
54
+ /** @internal */
55
+ _max(): number;
56
+ /** @internal */
57
+ _clamp(v: number): number;
58
+ on(event: string, callback: EventCallback): void;
59
+ off(event: string, callback: EventCallback): void;
60
+ /** @internal */
61
+ _emit(event: string): void;
62
+ /** @internal — add ticker if not already ticking */
63
+ _startTick(): void;
64
+ /** @internal — remove ticker */
65
+ _stopTick(): void;
66
+ to(target: number | string | HTMLElement, options?: ScrollToOptions): void;
67
+ /** @internal */
68
+ _listen(action: "a" | "r"): void;
69
+ start(): void;
70
+ stop(): void;
71
+ destroy(): void;
72
+ /** @internal — wheel: intercepted for smooth damp */
73
+ _onWheel(e: WheelEvent): void;
74
+ /** @internal — native scroll event: sync when not driving (touch, programmatic, etc.) */
75
+ _onNativeScroll(): void;
76
+ /** @internal */
77
+ _onKeyDown(e: KeyboardEvent): void;
78
+ /** @internal */
79
+ _applyScroll(): void;
80
+ /** @internal */
81
+ _tick(): void;
82
+ }
83
+ export {};
@@ -0,0 +1 @@
1
+ import{b as a}from"../chunk-xcfcqftx.js";import"../chunk-0t3a7kj5.js";import"../chunk-qjsknche.js";export{a as SmoothScroll};
@@ -47,7 +47,7 @@ export declare class SplitText {
47
47
  /** @internal */ _classes: SplitTextOptions["class"];
48
48
  /** @internal */ _el: HTMLElement;
49
49
  /** @internal */ _instances: SplitText[] | null;
50
- constructor(element: HTMLElement | HTMLElement[] | ArrayLike<HTMLElement> | string, options?: SplitTextOptions);
50
+ constructor(target: HTMLElement | HTMLElement[] | ArrayLike<HTMLElement> | string, options?: SplitTextOptions);
51
51
  /** @internal — create a styled inline-block element. */
52
52
  _makeEl(dataAttr: string, text?: string): HTMLElement;
53
53
  /** @internal — create a mask wrapper element. */
@@ -1 +1 @@
1
- import{a}from"../chunk-8pnv9yxr.js";export{a as SplitText};
1
+ import{a}from"../chunk-se6yyp59.js";import"../chunk-0t3a7kj5.js";export{a as SplitText};
@@ -0,0 +1,9 @@
1
+ export type TickerCallback = (elapsed: number) => void;
2
+ /**
3
+ * Returns the normalized delta frame (1.0 = perfect 60fps frame).
4
+ */
5
+ export declare function getDeltaFrame(): number;
6
+ export declare const Ticker: {
7
+ add(cb: TickerCallback): () => void;
8
+ remove(cb: TickerCallback): void;
9
+ };
@@ -0,0 +1 @@
1
+ import{g as a,h as b}from"../chunk-qjsknche.js";export{a as getDeltaFrame,b as Ticker};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "situs-kit",
3
- "version": "0.1.8",
3
+ "version": "0.1.9",
4
4
  "description": "A creative developer helper library",
5
5
  "license": "MIT",
6
6
  "module": "./dist/index.js",
@@ -15,6 +15,14 @@
15
15
  "./split-text": {
16
16
  "types": "./dist/split-text/index.d.ts",
17
17
  "import": "./dist/split-text/index.js"
18
+ },
19
+ "./smooth-scroll": {
20
+ "types": "./dist/smooth-scroll/index.d.ts",
21
+ "import": "./dist/smooth-scroll/index.js"
22
+ },
23
+ "./ticker": {
24
+ "types": "./dist/ticker/index.d.ts",
25
+ "import": "./dist/ticker/index.js"
18
26
  }
19
27
  },
20
28
  "files": [
@@ -1,2 +0,0 @@
1
- function C(q,z){return typeof globalThis.getComputedStyle==="function"?globalThis.getComputedStyle(q,z??null):null}var B=1,P=2,y=4,I="inline-block",h="nowrap",f="baseline",L="0px",g="block",E="maskChar",p="maskWord",s="maskLine",o=/^\s+$/,n=/(\s+)/,d=",div,span,svg,img,br,hr,canvas,video,audio,iframe,input,textarea,select,button,picture,figure,",r={u:"underline",ins:"underline",s:"line-through",strike:"line-through",del:"line-through"};class a{dom;chars;words;lines;masks;_n;_k;_g;_j;_f=[];_o=new Map;_i=null;_h=0;_l=!1;_c="";_d;_e;_b;_a=null;constructor(q,z={}){if(typeof q==="string"){let U=document.querySelectorAll?document.querySelectorAll(q):null;if(U&&U.length>1)q=Array.from(U);else{let V=U?U[0]:document.querySelector(q);if(!V)throw Error("SplitText: element not found");q=V}}if(Array.isArray(q)||typeof q==="object"&&q!==null&&"length"in q&&typeof q.item==="function"){let U=Array.from(q);if(U.length===0)throw Error("SplitText: empty element array");this._a=U.map((V)=>new a(V,z)),this.dom=U,this.chars=this._a.flatMap((V)=>V.chars),this.words=this._a.flatMap((V)=>V.words),this.lines=this._a.flatMap((V)=>V.lines),this.masks={chars:this._a.flatMap((V)=>V.masks.chars),words:this._a.flatMap((V)=>V.masks.words),lines:this._a.flatMap((V)=>V.masks.lines)},this._n="",this._k=0,this._g=0,this._j="";return}let $=q;this._b=$,this.dom=$,this._n=$.innerHTML,this.chars=[],this.words=[],this.lines=[],this.masks={chars:[],words:[],lines:[]};let G=z.type?Array.isArray(z.type)?z.type:[z.type]:["chars","words","lines"],Q=0;for(let U of G)if(U==="chars")Q|=B;else if(U==="words")Q|=P;else if(U==="lines")Q|=y;this._k=Q,this._j=z.tag??"span";let T=0;if(z.mask===!0)T=Q;else if(Array.isArray(z.mask)){for(let U of z.mask)if(U==="chars")T|=B;else if(U==="words")T|=P;else if(U==="lines")T|=y}if(this._g=T,this._d=z.style,this._e=z.class,this._w(),(z.resize??!0)&&Q&y)this._x()}_p(q,z){let F=document.createElement(this._j);if(q)F.dataset[q]="";if(F.style.display=I,z!==void 0)F.textContent=z;return F}_s(q,z){let F=document.createElement(this._j);if(F.dataset[z]="",F.style.overflow="hidden",F.style.display=q,this._d?.mask)Object.assign(F.style,this._d.mask);if(this._e?.mask)F.classList.add(this._e.mask);return F}_r(q,z,F,$,G){if(this._g&z){let Q=this._s(F,$);return Q.appendChild(q),G.push(Q),Q}return q}_m(q,z,F,$){if(F){let{outer:G,inner:Q}=F();if(Q.appendChild(z),$)this._o.set($,G);q.appendChild(G)}else q.appendChild(z)}_q(q,z,F){if(q.style.textDecoration=z,F)q.style.textDecorationColor=F}_w(){this._l=!0;let q=this._b,z=C(q),F=z?parseFloat(z.letterSpacing)||0:0;if(this._c=z?z.textIndent:"",this._c&&this._c!==L)q.style.textIndent="0";let $=this._k,G=!!($&B),Q=!!($&P),T=!!($&y),U=Q||T,V=!!(this._g&B),X=new Map,H=(N)=>{for(let u of Array.from(N.childNodes))if(u.nodeType===1){let R=u.tagName.toLowerCase();if(r[R]){let Y=C(u)?.textDecorationColor;if(Y)X.set(u,Y)}H(u)}};H(q);let Z=Array.from(q.childNodes),O=document.createDocumentFragment();q.replaceChildren();let k=(N,u,R,Y)=>{if(N.nodeType===3){let D=(N.textContent??"").split(n);for(let j of D){if(!j)continue;if(o.test(j)){O.appendChild(document.createTextNode(j));continue}let J=U?this._p(Q?"word":""):null;if(J){if(J.style.whiteSpace=h,R&&!G)this._q(J,R,Y)}if(G){let v=[...j],i=this.chars.length;for(let b=0;b<v.length;b++){let M=this._p("char",v[b]);if(F&&b<v.length-1)M.style.marginRight=`${F}px`;if(R)this._q(M,R,Y);if(this.chars.push(M),this._d?.chars)Object.assign(M.style,this._d.chars);if(this._e?.chars)M.classList.add(this._e.chars);let S=V?this._r(M,B,I,E,this.masks.chars):M;if(J)J.appendChild(S)}if(!J){let b=u?u():null,M=b?b.inner:O;for(let S=i;S<this.chars.length;S++){let _=this.chars[S];M.appendChild(V&&_.parentElement?.dataset?.[E]!==void 0?_.parentElement:_)}if(b)O.appendChild(b.outer)}}else if(J){if(J.textContent=j,F)J.style.letterSpacing=`${F}px`}if(J){if(Q){if(this.words.push(J),this._d?.words)Object.assign(J.style,this._d.words);if(this._e?.words)J.classList.add(this._e.words)}this._f.push(J);let v=this._g&P?this._r(J,P,I,p,this.masks.words):J;this._m(O,v,u,J)}else if(!G)this._m(O,document.createTextNode(j),u)}return}if(N.nodeType!==1)return;let x=N,W=x.tagName.toLowerCase();if(d.includes(","+W+",")){let D=x.cloneNode(!0);if(R)this._q(D,R,Y);let j=C(x)?.verticalAlign;if(j&&j!==f)D.style.verticalAlign=j;if(U){let J=this._p(Q?"word":"");if(J.style.whiteSpace=h,j&&j!==f)J.style.verticalAlign=j;if(J.appendChild(D),Q){if(this.words.push(J),this._d?.words)Object.assign(J.style,this._d.words);if(this._e?.words)J.classList.add(this._e.words)}this._f.push(J);let v=this._g&P?this._r(J,P,I,p,this.masks.words):J;this._m(O,v,u,J)}else this._m(O,D,u);return}let m=()=>{let D=x.cloneNode(!1);if(u){let{outer:j,inner:J}=u();return J.appendChild(D),{outer:j,inner:D}}return{outer:D,inner:D}},K=r[W],c=K?R?`${R} ${K}`:K:R,w=K?X.get(x)||Y:Y;for(let D of Array.from(x.childNodes))k(D,m,c,w)};for(let N of Z)k(N,null,"","");q.appendChild(O);let A=!!(this._c&&this._c!==L);if(T)this._y(A);else if(A){let N=Q?this.words[0]:G?this.chars[0]:null;if(N)N.style.marginLeft=this._c,q.style.textIndent="0"}this._l=!1}_y(q){if(q&&this._f[0])this._f[0].style.marginLeft=this._c;this._t();let z=[];if(typeof document.fonts?.status==="string"&&document.fonts.status!=="loaded")z.push(document.fonts.ready);let F=this._b.querySelectorAll("img");for(let $=0;$<F.length;$++){let G=F[$];if(!G.complete)z.push(new Promise((Q)=>{G.addEventListener("load",Q,{once:!0}),G.addEventListener("error",Q,{once:!0})}))}if(z.length){let $=this._b.offsetHeight;Promise.all(z).then(()=>{if(this._b.offsetHeight!==$)this.reflow()})}}_u(q){return this._o.get(q)??q}_t(){let q=this._f;if(!q.length)return;let z=q[0].offsetTop,F=z+q[0].offsetHeight,$=[],G=[];for(let V of q){let X=V.offsetTop,H=X+V.offsetHeight;if(X<F&&H>z)z=Math.min(z,X),F=Math.max(F,H);else G.push($),$=[],z=X,F=H;$.push(V)}if($.length)G.push($);let Q=!!(this._c&&this._c!==L);if(Q&&q[0])q[0].style.marginLeft="";let T=!!(this._g&y),U=document.createDocumentFragment();for(let V=0;V<G.length;V++){let X=G[V],H=document.createElement(this._j);if(H.dataset.line="",H.style.display=g,V===0&&Q)H.style.paddingLeft=this._c;for(let Z=0;Z<X.length;Z++){if(Z>0)H.appendChild(document.createTextNode(" "));H.appendChild(this._u(X[Z]))}if(this.lines.push(H),this._d?.lines)Object.assign(H.style,this._d.lines);if(this._e?.lines)H.classList.add(this._e.lines);if(T){let Z=this._s(g,s);Z.appendChild(H),this.masks.lines.push(Z),U.appendChild(Z)}else U.appendChild(H)}this._b.replaceChildren(U)}_x(){let q=this._b.offsetParent?this._b.offsetParent.offsetWidth:window.innerWidth,z=()=>{if(this._l)return;let $=this._b.offsetParent,G=$?$.offsetWidth:window.innerWidth;if(G===q)return;if(q=G,this._h)cancelAnimationFrame(this._h);this._h=requestAnimationFrame(()=>this.reflow())};this._i=new ResizeObserver(z);let F=this._b.offsetParent??this._b.parentElement;if(F)this._i.observe(F);else this._i.observe(this._b)}reflow(){if(this._a){this._a.forEach((z)=>z.reflow()),this._v();return}if(!(this._k&y)||this._l)return;let q=document.createDocumentFragment();for(let z=0;z<this._f.length;z++){if(z>0)q.appendChild(document.createTextNode(" "));q.appendChild(this._u(this._f[z]))}if(this._b.replaceChildren(q),this.lines.length=0,this.masks.lines.length=0,this._c&&this._c!==L&&this._f[0])this._f[0].style.marginLeft=this._c;this._t()}revert(){if(this._a){this._a.forEach((q)=>q.revert()),this._v();return}this.destroy(),this._b.innerHTML=this._n,this.chars.length=0,this.words.length=0,this._f.length=0,this._o.clear(),this.lines.length=0,this.masks.chars.length=0,this.masks.words.length=0,this.masks.lines.length=0}destroy(){if(this._a){this._a.forEach((q)=>q.destroy());return}if(this._h)cancelAnimationFrame(this._h),this._h=0;if(this._i)this._i.disconnect(),this._i=null}_v(){if(!this._a)return;this.chars.length=0,this.words.length=0,this.lines.length=0,this.masks.chars.length=0,this.masks.words.length=0,this.masks.lines.length=0;for(let q of this._a)this.chars.push(...q.chars),this.words.push(...q.words),this.lines.push(...q.lines),this.masks.chars.push(...q.masks.chars),this.masks.words.push(...q.masks.words),this.masks.lines.push(...q.masks.lines)}}
2
- export{a};