react-loaded 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -212,7 +212,12 @@ The skeleton preserves:
212
212
 
213
213
  ## Ref Handling
214
214
 
215
- Components must forward refs for optimal rendering. If a component does not forward its ref, React Loaded automatically wraps it in a `div` and logs a development warning.
215
+ React Loaded supports both React ref models:
216
+
217
+ - **React 19+:** `ref` can be passed as a regular prop.
218
+ - **React 18:** function components should use `forwardRef`.
219
+
220
+ For best rendering, your skeleton `element` should expose a DOM ref. If it does not, React Loaded automatically wraps it in a `div` and logs a development warning.
216
221
 
217
222
  To suppress the warning:
218
223
 
@@ -223,7 +228,7 @@ To suppress the warning:
223
228
  />
224
229
  ```
225
230
 
226
- Or better, wrap third-party components:
231
+ Or better, wrap third-party components so a DOM ref is always available:
227
232
 
228
233
  ```tsx
229
234
  const WrappedComponent = forwardRef((props, ref) => (
@@ -253,7 +258,7 @@ The same seed always produces the same text widths, making skeleton output deter
253
258
 
254
259
  - **React 18 and 19** are supported.
255
260
  - Persistence uses `localStorage` under the root key `react-loaded` with a versioned schema.
256
- - In skeleton mode, the library applies CSS classes on the subtree. Components should accept `className` and ideally forward refs.
261
+ - In skeleton mode, the library applies CSS classes on the subtree. Components should accept `className` and expose a usable ref (React 19 `ref` prop or React 18 `forwardRef`).
257
262
  - Dev warnings are enabled when `NODE_ENV !== "production"`. If your environment doesn’t inject `NODE_ENV`, you can force them with `globalThis.__REACT_LOADED_DEV__ = true`.
258
263
  - SSR: the library uses an isomorphic layout effect to avoid server warnings and keep hydration stable.
259
264
  - JSR/Deno: CSS module imports aren’t supported. For Node/bundlers, import `react-loaded/style.css`. For Deno, you’ll need to copy the CSS into your app (or recreate the styles).
package/dist/index.cjs CHANGED
@@ -1,2 +1,2 @@
1
- 'use strict';var react=require('react'),jsxRuntime=require('react/jsx-runtime');var h=react.createContext(false);function Z(){return react.useContext(h)}function S(){let e=globalThis,t=e.__REACT_LOADED_DEV__;if(typeof t=="boolean")return t;let n=e.__DEV__;if(typeof n=="boolean")return n;let o=globalThis.process,s=typeof o=="object"&&o!==null?o.env?.NODE_ENV:void 0;return typeof s=="string"?s!=="production":false}var ne=typeof globalThis<"u"&&typeof globalThis.document<"u",w=ne?react.useLayoutEffect:react.useEffect;var le=6,ie=40;function V(e){if(!e||typeof e!="object")return false;let t=e;return !(t.nodeType!==1||typeof t.tagName!="string"||typeof t.querySelectorAll!="function"||typeof Element<"u"&&!(e instanceof Element))}var x=new Set;function M(e){if(!V(e))return false;try{return e.querySelectorAll("*"),!0}catch{return false}}function de(e){if(M(e))return e;if(e&&typeof e=="object"&&"nativeElement"in e){let t=e.nativeElement;if(M(t))return t}return null}function P(e){let t=e.type;if(typeof t=="string")return `<${t}>`;if(typeof t=="function"){let n=t;return `<${n.displayName||n.name||"Unknown"}>`}if(typeof t=="object"&&t!==null){let n=t;return `<${n.displayName||n.name||"Unknown"}>`}return "<Unknown>"}function ue(e){let t=e.props?.ref;if(t)return t;let n=e.ref;if(n)return n}function ce(e,t){e&&(typeof e=="function"?e(t):e.current=t);}var T=new Set(["IMG","VIDEO","CANVAS"]),R=new Set(["SVG"]),fe=new Set(["BUTTON","INPUT","TEXTAREA","SELECT","A"]),me="button,input,textarea,select,a,[role='button']",pe=new Set(["SCRIPT","STYLE","LINK","META","NOSCRIPT","TEMPLATE"]);function v(e){return e.tagName.toUpperCase()}function L(e,t=v(e)){return fe.has(t)?true:e.getAttribute("role")==="button"}function ke(e,t){return !!(e.closest(me)&&!L(e,t))}function be(e,t=v(e)){return !!(T.has(t)||R.has(t)||L(e,t)||e.childElementCount===0&&e.textContent?.trim())}function ge(e,t){let n=e.length,o=Math.max(4,.8*n),s=Ee(t)*o,a=n+2+s;return Math.max(le,Math.min(ie,a))}function Ee(e){if(!e)return 0;let t=2166136261;for(let o=0;o<e.length;o+=1)t^=e.charCodeAt(o),t=Math.imul(t,16777619);return (t>>>0)/4294967295*2-1}function Se(e){let t=globalThis.getComputedStyle(e).textAlign;return t==="center"?"center":t==="right"||t==="end"?"right":"left"}function ye(e,t={}){let{animate:n=true,seed:o}=t,s=o==null?"loaded":String(o);if(!V(e))return;let a=e;a.classList.add("loaded-skeleton-mode"),n&&a.classList.add("loaded-animate"),a.classList.contains("loaded-skeleton-wrapper")||a.classList.add("loaded-skeleton-bg");let m=e.getElementsByTagName("*"),f=0,u=l=>{let i=v(l);if(pe.has(i)||!be(l,i)||ke(l,i))return;let r=l,d=l.textContent?.trim();if(l.childElementCount===0&&d&&!T.has(i)&&!R.has(i)&&!L(l,i)){r.classList.add("loaded-text-skeleton"),r.dataset.skeletonAlign=Se(r);let b=`${s}|${f}|${d??""}`;f+=1;let O=ge(d??"",b);r.style.setProperty("--skeleton-text-width",`${O}ch`);}else T.has(i)?r.classList.add("loaded-skeleton-media"):R.has(i)?(r.classList.add("loaded-skeleton-content"),r.classList.add("loaded-skeleton-svg")):(r.classList.add("loaded-skeleton-content"),r.setAttribute("tabindex","-1"));};u(e);for(let l of m)u(l);}function N({element:e,children:t,loading:n=false,animate:o=true,className:s="",seed:a,suppressRefWarning:k=false}){let m=react.useRef(false),f=react.useRef(false),u=react.useRef(null),l=react.useRef(null),i=react.useRef(null),[r,d]=react.useState(false);(!n||u.current!==e)&&(m.current=false,f.current=false,u.current=e),react.useEffect(()=>{let p=e.type,g=e.key??null,E=l.current,q=i.current;E!==null&&(E!==p||q!==g)&&d(false),l.current=p,i.current=g;},[e.type,e.key]);let c=ue(e),b=react.useCallback(p=>{f.current=true;let g=de(p);if(p!==null&&!g&&!r){if(d(true),!k&&S()){let E=P(e);x.has(E)||(console.warn(`[SmartSkeleton] ${E} does not forward its ref to a DOM element. A wrapper <div> has been added automatically. Use forwardRef to avoid this.`),x.add(E));}return}g&&n&&!m.current&&(ye(g,{animate:o,seed:a}),m.current=true),ce(c,p);},[n,e,r,k,c,o,a]);if(w(()=>{if(!(!n||r)&&!f.current&&(d(true),!k&&S())){let p=P(e);x.has(p)||(console.warn(`[SmartSkeleton] ${p} does not accept a ref. A wrapper <div> has been added automatically. Use forwardRef to avoid this.`),x.add(p));}},[n,r,e,k]),!n)return t??null;let F=e.props.className??"",I=["loaded-skeleton-mode",o&&"loaded-animate"].filter(Boolean).join(" "),J=[I,"loaded-skeleton-wrapper",s].filter(Boolean).join(" "),Y=[F,I,"loaded-skeleton-bg",s].filter(Boolean).join(" ");return jsxRuntime.jsx(h.Provider,{value:true,children:r?jsxRuntime.jsx("div",{ref:b,className:J,"aria-hidden":"true",children:e}):react.cloneElement(e,{ref:b,className:Y,"aria-hidden":true})})}var B="react-loaded",xe="loaded",$=1;function G(e){return !!e&&typeof e=="object"&&!Array.isArray(e)}function U(e){if(!G(e))return {};let t={};for(let[n,o]of Object.entries(e))typeof o=="number"&&(t[n]=o);return t}function W(e){return G(e)&&e.v===$?U(e.counts):U(e)}function Ce(e){if(typeof localStorage>"u")return {};try{let t=localStorage.getItem(e);return t===null?{}:W(JSON.parse(t))}catch{return {}}}function H(e){if(typeof localStorage>"u")return;let t={v:$,counts:e};try{localStorage.setItem(B,JSON.stringify(t));}catch{}}function K(){if(typeof localStorage>"u")return {};try{let e=localStorage.getItem(B);if(e!==null)return W(JSON.parse(e));let t=Ce(xe);return Object.keys(t).length>0&&H(t),t}catch{return {}}}function Te(e){let n=K()[e];return typeof n=="number"?n:null}function Re(e,t){if(!(typeof localStorage>"u"))try{let n=K();n[e]=t,H(n);}catch{}}function _({storageKey:e,defaultCount:t=3,currentCount:n,loading:o,minCount:s=1,maxCount:a}){let[k,m]=react.useState(()=>A(t,s,a)),f=react.useRef(false);return w(()=>{if(!e)return;let u=Te(e);if(u===null)return;let l=A(u,s,a);m(i=>Object.is(i,l)?i:l);},[e,s,a]),react.useEffect(()=>{if(!o&&n!==void 0){let u=A(n,s,a);m(u),e&&Re(e,u);}},[o,n,e,s,a]),react.useEffect(()=>{S()&&!e&&!f.current&&(console.warn("[Loaded] SmartSkeletonList used without storageKey. The count will reset on remount. Add a storageKey to persist across sessions."),f.current=true);},[e]),k}function A(e,t,n){let o=Math.max(e,t);return n!==void 0&&(o=Math.min(o,n)),o}function Le({loading:e=false,items:t,renderItem:n,renderSkeleton:o,storageKey:s,defaultCount:a=3,minCount:k=1,maxCount:m,animate:f=true,seed:u,suppressRefWarning:l=false,keyExtractor:i=(r,d)=>d}){let r=_({storageKey:s,defaultCount:a,currentCount:t?.length,loading:e,minCount:k,maxCount:m});if(e){let d=new Array(r);for(let c=0;c<r;c+=1){let b=u===void 0?`${c}`:`${u}:${c}`;d[c]=jsxRuntime.jsx(N,{loading:true,element:o(c),animate:f,seed:b,suppressRefWarning:l},`skeleton-${c}`);}return jsxRuntime.jsx(jsxRuntime.Fragment,{children:d})}return !t||t.length===0?null:jsxRuntime.jsx(jsxRuntime.Fragment,{children:t.map((d,c)=>jsxRuntime.jsx(react.Fragment,{children:n(d,c)},i(d,c)))})}exports.SkeletonContext=h;exports.SmartSkeleton=N;exports.SmartSkeletonList=Le;exports.useIsSkeletonMode=Z;exports.usePersistedCount=_;//# sourceMappingURL=index.cjs.map
1
+ 'use strict';var react=require('react'),jsxRuntime=require('react/jsx-runtime');var C=react.createContext(false);function de(){return react.useContext(C)}function x(){let e=globalThis,t=e.__REACT_LOADED_DEV__;if(typeof t=="boolean")return t;let n=e.__DEV__;if(typeof n=="boolean")return n;let o=globalThis.process,l=typeof o=="object"&&o!==null?o.env?.NODE_ENV:void 0;return typeof l=="string"?l!=="production":false}var fe=typeof globalThis<"u"&&typeof globalThis.document<"u",y=fe?react.useLayoutEffect:react.useEffect;var be=6,ge=40;function X(e){if(!e||typeof e!="object")return false;let t=e;return !(t.nodeType!==1||typeof t.tagName!="string"||typeof t.querySelectorAll!="function"||typeof Element<"u"&&!(e instanceof Element))}var H=new Set;function F(e){if(!X(e))return false;try{return e.querySelectorAll("*"),!0}catch{return false}}function L(e){if(F(e))return e;if(e&&typeof e=="object"&&"nativeElement"in e){let t=e.nativeElement;if(F(t))return t}return null}function Ee(e){let t=e.type;if(typeof t=="string")return `<${t}>`;if(typeof t=="function"){let n=t;return `<${n.displayName||n.name||"Unknown"}>`}if(typeof t=="object"&&t!==null){let n=t;return `<${n.displayName||n.name||"Unknown"}>`}return "<Unknown>"}var J=Number.parseInt(react.version,10),Se=Number.isFinite(J)&&J>=19;function ye(e){let n=e.props?.ref;if(n!==void 0)return n;if(Se)return;let o=e.ref;if(o!==void 0)return o}function he(e,t){e&&(typeof e=="function"?e(t):e.current=t);}var _=new Set(["IMG","VIDEO","CANVAS"]),I=new Set(["SVG"]),we=new Set(["BUTTON","INPUT","TEXTAREA","SELECT","A"]),Re="button,input,textarea,select,a,[role='button']",Te=new Set(["SCRIPT","STYLE","LINK","META","NOSCRIPT","TEMPLATE"]);function O(e){return e.tagName.toUpperCase()}function P(e,t=O(e)){return we.has(t)?true:e.getAttribute("role")==="button"}function Ce(e,t){return !!(e.closest(Re)&&!P(e,t))}function xe(e,t=O(e)){return !!(_.has(t)||I.has(t)||P(e,t)||e.childElementCount===0&&e.textContent?.trim())}function ve(e,t){let n=e.length,o=Math.max(4,.8*n),l=Ne(t)*o,r=n+2+l;return Math.max(be,Math.min(ge,r))}function Ne(e){if(!e)return 0;let t=2166136261;for(let o=0;o<e.length;o+=1)t^=e.charCodeAt(o),t=Math.imul(t,16777619);return (t>>>0)/4294967295*2-1}function Le(e){let t=globalThis.getComputedStyle(e).textAlign;return t==="center"?"center":t==="right"||t==="end"?"right":"left"}function A(e,t={}){let{animate:n=true,seed:o}=t,l=o==null?"loaded":String(o);if(!X(e))return;let r=e;r.classList.add("loaded-skeleton-mode"),n&&r.classList.add("loaded-animate"),r.classList.contains("loaded-skeleton-wrapper")||r.classList.add("loaded-skeleton-bg");let c=e.getElementsByTagName("*"),p=0,d=s=>{let i=O(s);if(Te.has(i)||!xe(s,i)||Ce(s,i))return;let a=s,f=s.textContent?.trim();if(s.childElementCount===0&&f&&!_.has(i)&&!I.has(i)&&!P(s,i)){a.classList.add("loaded-text-skeleton"),a.dataset.skeletonAlign=Le(a);let b=`${l}|${p}|${f??""}`;p+=1;let h=ve(f??"",b);a.style.setProperty("--skeleton-text-width",`${h}ch`);}else _.has(i)?a.classList.add("loaded-skeleton-media"):I.has(i)?(a.classList.add("loaded-skeleton-content"),a.classList.add("loaded-skeleton-svg")):(a.classList.add("loaded-skeleton-content"),a.setAttribute("tabindex","-1"));};d(e);for(let s of c)d(s);}function M({element:e,children:t,loading:n=false,animate:o=true,className:l="",seed:r,suppressRefWarning:E=false}){let c=react.useRef(false),p=react.useRef(false),d=react.useRef(null),s=react.useRef(null),i=react.useRef(false),[a,f]=react.useState(false),u=e.type,b=e.key??null,h=react.useRef(n),V=react.useRef(u),j=react.useRef(b),S=react.useCallback(()=>{s.current!==null&&(clearTimeout(s.current),s.current=null);},[]);react.useEffect(()=>{i.current=a;},[a]),y(()=>{let k=h.current,m=V.current,R=j.current;(k&&!n||(m!==u||R!==b))&&(c.current=false,p.current=false,d.current=null,S(),i.current&&f(false)),h.current=n,V.current=u,j.current=b;},[n,u,b,S]),react.useEffect(()=>()=>{S();},[S]);let U=ye(e),w=react.useCallback(k=>{if(!i.current&&(f(true),!E&&x())){let m=Ee(e);H.has(m)||(console.warn(k==="non-dom-ref"?`[SmartSkeleton] ${m} does not forward its ref to a DOM element. A wrapper <div> has been added automatically. Use forwardRef to avoid this.`:`[SmartSkeleton] ${m} does not accept a ref. A wrapper <div> has been added automatically. Use forwardRef to avoid this.`),H.add(m));}},[e,E]),B=react.useCallback(k=>{p.current=true,d.current=k;let m=L(k);m&&n&&!c.current&&(A(m,{animate:o,seed:r}),c.current=true),he(U,k);},[n,U,o,r]);if(y(()=>{if(!n||a)return;S();let k=d.current,m=L(k);if(m&&!c.current&&(A(m,{animate:o,seed:r}),c.current=true),p.current){k!==null&&!m&&w("non-dom-ref");return}return s.current=setTimeout(()=>{if(s.current=null,!n||i.current)return;let R=d.current,T=L(R);if(T&&!c.current&&(A(T,{animate:o,seed:r}),c.current=true),p.current){R!==null&&!T&&w("non-dom-ref");return}w("no-ref-call");},0),()=>{S();}},[n,a,o,r,w,S]),!n)return t??null;let re=e.props.className??"",$=["loaded-skeleton-mode",o&&"loaded-animate"].filter(Boolean).join(" "),se=[$,"loaded-skeleton-wrapper",l].filter(Boolean).join(" "),ae=[re,$,"loaded-skeleton-bg",l].filter(Boolean).join(" ");return jsxRuntime.jsx(C.Provider,{value:true,children:a?jsxRuntime.jsx("div",{ref:B,className:se,"aria-hidden":"true",children:e}):react.cloneElement(e,{ref:B,className:ae,"aria-hidden":true})})}var z="react-loaded",Ie="loaded",Q=1;function Z(e){return !!e&&typeof e=="object"&&!Array.isArray(e)}function q(e){if(!Z(e))return {};let t={};for(let[n,o]of Object.entries(e))typeof o=="number"&&(t[n]=o);return t}function ee(e){return Z(e)&&e.v===Q?q(e.counts):q(e)}function Oe(e){if(typeof localStorage>"u")return {};try{let t=localStorage.getItem(e);return t===null?{}:ee(JSON.parse(t))}catch{return {}}}function te(e){if(typeof localStorage>"u")return;let t={v:Q,counts:e};try{localStorage.setItem(z,JSON.stringify(t));}catch{}}function ne(){if(typeof localStorage>"u")return {};try{let e=localStorage.getItem(z);if(e!==null)return ee(JSON.parse(e));let t=Oe(Ie);return Object.keys(t).length>0&&te(t),t}catch{return {}}}function Pe(e){let n=ne()[e];return typeof n=="number"?n:null}function Me(e,t){if(!(typeof localStorage>"u"))try{let n=ne();n[e]=t,te(n);}catch{}}function D({storageKey:e,defaultCount:t=3,currentCount:n,loading:o,minCount:l=1,maxCount:r}){let[E,c]=react.useState(()=>W(t,l,r)),p=react.useRef(false);return y(()=>{if(!e)return;let d=Pe(e);if(d===null)return;let s=W(d,l,r);c(i=>Object.is(i,s)?i:s);},[e,l,r]),react.useEffect(()=>{if(!o&&n!==void 0){let d=W(n,l,r);c(d),e&&Me(e,d);}},[o,n,e,l,r]),react.useEffect(()=>{x()&&!e&&!p.current&&(console.warn("[Loaded] SmartSkeletonList used without storageKey. The count will reset on remount. Add a storageKey to persist across sessions."),p.current=true);},[e]),E}function W(e,t,n){let o=Math.max(e,t);return n!==void 0&&(o=Math.min(o,n)),o}function De({loading:e=false,items:t,renderItem:n,renderSkeleton:o,storageKey:l,defaultCount:r=3,minCount:E=1,maxCount:c,animate:p=true,seed:d,suppressRefWarning:s=false,keyExtractor:i=(a,f)=>f}){let a=D({storageKey:l,defaultCount:r,currentCount:t?.length,loading:e,minCount:E,maxCount:c});if(e){let f=new Array(a);for(let u=0;u<a;u+=1){let b=d===void 0?`${u}`:`${d}:${u}`;f[u]=jsxRuntime.jsx(M,{loading:true,element:o(u),animate:p,seed:b,suppressRefWarning:s},`skeleton-${u}`);}return jsxRuntime.jsx(jsxRuntime.Fragment,{children:f})}return !t||t.length===0?null:jsxRuntime.jsx(jsxRuntime.Fragment,{children:t.map((f,u)=>jsxRuntime.jsx(react.Fragment,{children:n(f,u)},i(f,u)))})}exports.SkeletonContext=C;exports.SmartSkeleton=M;exports.SmartSkeletonList=De;exports.useIsSkeletonMode=de;exports.usePersistedCount=D;//# sourceMappingURL=index.cjs.map
2
2
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/components/SkeletonContext/SkeletonContext.tsx","../src/utils/isDevEnv.ts","../src/utils/useIsomorphicLayoutEffect.ts","../src/components/SmartSkeleton/SmartSkeleton.tsx","../src/hooks/usePersistedCount/usePersistedCount.ts","../src/components/SmartSkeletonList/SmartSkeletonList.tsx"],"names":["SkeletonContext","createContext","useIsSkeletonMode","useContext","isDevEnv","maybeGlobal","override","devFlag","maybeProcess","nodeEnv","canUseDOM","useIsomorphicLayoutEffect","useLayoutEffect","useEffect","TEXT_WIDTH_MIN_CH","TEXT_WIDTH_MAX_CH","isElement","value","maybeElement","warnedComponents","isUsableElement","resolveRefTarget","node","nativeElement","getElementDisplayName","element","type","fn","obj","getOriginalRef","propsRef","legacyRef","forwardRef","originalRef","MEDIA_ELEMENTS","SVG_ELEMENTS","INTERACTIVE_ELEMENTS","BUTTON_LIKE_SELECTOR","SKIPPED_TAGS","getTagName","el","isButtonLikeElement","tagName","isButtonLikeDescendant","isContentElement","calculateTextWidthCh","text","seedKey","textLength","jitterRange","jitter","deterministicJitter","width","hash","index","resolveTextAlign","align","applySkeletonClasses","rootElement","options","animate","seed","baseSeed","htmlRoot","descendants","textIndex","processElement","htmlEl","textContent","widthCh","SmartSkeleton","children","loading","className","suppressRefWarning","hasAppliedRef","useRef","refWasCalledRef","lastElementRef","previousElementTypeRef","previousElementKeyRef","needsWrapper","setNeedsWrapper","useState","elementType","elementKey","previousType","previousKey","refCallback","useCallback","target","displayName","existingClassName","baseClasses","wrapperClassName","mergedClassName","jsx","cloneElement","STORAGE_KEY","LEGACY_STORAGE_KEY","STORAGE_VERSION","isRecord","toNumberRecord","result","key","maybeNumber","parseStoredCounts","readStoredCountsFromKey","raw","writeStoredCounts","counts","payload","getStoredCounts","rawNew","legacyCounts","getStoredCount","setStoredCount","count","usePersistedCount","storageKey","defaultCount","currentCount","minCount","maxCount","setCount","clampCount","hasWarnedRef","stored","next","prev","newCount","min","max","SmartSkeletonList","items","renderItem","renderSkeleton","keyExtractor","_","skeletonCount","skeletons","itemSeed","Fragment","item"],"mappings":"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","file":"index.cjs","sourcesContent":["import { createContext, useContext } from \"react\";\n\nexport const SkeletonContext = createContext(false);\n\nexport function useIsSkeletonMode(): boolean {\n return useContext(SkeletonContext);\n}\n","export function isDevEnv(): boolean {\n const maybeGlobal = globalThis as unknown as Record<string, unknown>;\n\n // Manual override for environments where NODE_ENV isn't injected.\n // Example: `globalThis.__REACT_LOADED_DEV__ = true`.\n const override = maybeGlobal.__REACT_LOADED_DEV__;\n if (typeof override === \"boolean\") return override;\n\n // Common global used by some toolchains/runtimes.\n const devFlag = maybeGlobal.__DEV__;\n if (typeof devFlag === \"boolean\") return devFlag;\n\n const maybeProcess = (globalThis as unknown as { process?: unknown }).process;\n const nodeEnv =\n typeof maybeProcess === \"object\" && maybeProcess !== null\n ? (maybeProcess as { env?: { NODE_ENV?: unknown } }).env?.NODE_ENV\n : undefined;\n\n if (typeof nodeEnv === \"string\") {\n return nodeEnv !== \"production\";\n }\n\n // No environment detected — assume production (convention: opt-in to dev mode).\n return false;\n}\n","import { useEffect, useLayoutEffect } from \"react\";\n\nconst canUseDOM =\n typeof globalThis !== \"undefined\" &&\n typeof (globalThis as { document?: unknown }).document !== \"undefined\";\n\nexport const useIsomorphicLayoutEffect = canUseDOM\n ? useLayoutEffect\n : useEffect;\n","import {\n cloneElement,\n type ReactElement,\n type Ref,\n useCallback,\n useEffect,\n useRef,\n useState,\n} from \"react\";\nimport { isDevEnv } from \"../../utils/isDevEnv\";\nimport { useIsomorphicLayoutEffect } from \"../../utils/useIsomorphicLayoutEffect\";\nimport { SkeletonContext } from \"../SkeletonContext/SkeletonContext\";\nimport \"./SmartSkeleton.css\";\n\n// Text width configuration (in ch units)\nconst TEXT_WIDTH_MIN_CH = 6;\nconst TEXT_WIDTH_MAX_CH = 40;\n\nfunction isElement(value: unknown): value is Element {\n if (!value || typeof value !== \"object\") return false;\n const maybeElement = value as Element;\n // Must have nodeType 1 (Element) and a working querySelectorAll\n if (maybeElement.nodeType !== 1) return false;\n if (typeof maybeElement.tagName !== \"string\") return false;\n if (typeof maybeElement.querySelectorAll !== \"function\") return false;\n // Additional check: instanceof Element if available\n if (typeof Element !== \"undefined\" && !(value instanceof Element)) {\n return false;\n }\n return true;\n}\n\nconst warnedComponents = new Set<string>();\n\nfunction isUsableElement(value: unknown): value is Element {\n if (!isElement(value)) return false;\n // Test that querySelectorAll actually works\n try {\n (value as Element).querySelectorAll(\"*\");\n return true;\n } catch {\n return false;\n }\n}\n\nfunction resolveRefTarget(node: unknown): Element | null {\n if (isUsableElement(node)) return node;\n if (node && typeof node === \"object\" && \"nativeElement\" in node) {\n const nativeElement = (node as { nativeElement?: unknown }).nativeElement;\n if (isUsableElement(nativeElement)) return nativeElement;\n }\n return null;\n}\n\nfunction getElementDisplayName(element: ReactElement): string {\n const type = element.type;\n if (typeof type === \"string\") {\n return `<${type}>`;\n }\n if (typeof type === \"function\") {\n const fn = type as { displayName?: string; name?: string };\n return `<${fn.displayName || fn.name || \"Unknown\"}>`;\n }\n if (typeof type === \"object\" && type !== null) {\n const obj = type as { displayName?: string; name?: string };\n return `<${obj.displayName || obj.name || \"Unknown\"}>`;\n }\n return \"<Unknown>\";\n}\n\n/**\n * Get the original ref from the element, supporting both React 18 and React 19.\n * React 19: ref is a regular prop on element.props.ref\n * React 18: ref is on element.ref\n */\nfunction getOriginalRef(element: ReactElement): Ref<unknown> | undefined {\n // React 19 style (ref as prop)\n const propsRef = (element.props as { ref?: Ref<unknown> })?.ref;\n if (propsRef) return propsRef;\n\n // React 18 style\n const legacyRef = (element as ReactElement & { ref?: Ref<unknown> }).ref;\n if (legacyRef) return legacyRef;\n\n return undefined;\n}\n\n/**\n * Forward a ref value to the original ref (callback or object style).\n */\nfunction forwardRef(originalRef: Ref<unknown> | undefined, node: unknown) {\n if (!originalRef) return;\n if (typeof originalRef === \"function\") {\n originalRef(node);\n } else {\n (originalRef as React.MutableRefObject<unknown>).current = node;\n }\n}\n\nconst MEDIA_ELEMENTS = new Set([\"IMG\", \"VIDEO\", \"CANVAS\"]);\nconst SVG_ELEMENTS = new Set([\"SVG\"]);\n\nconst INTERACTIVE_ELEMENTS = new Set([\n \"BUTTON\",\n \"INPUT\",\n \"TEXTAREA\",\n \"SELECT\",\n \"A\",\n]);\n\nconst BUTTON_LIKE_SELECTOR = \"button,input,textarea,select,a,[role='button']\";\nconst SKIPPED_TAGS = new Set([\n \"SCRIPT\",\n \"STYLE\",\n \"LINK\",\n \"META\",\n \"NOSCRIPT\",\n \"TEMPLATE\",\n]);\n\nfunction getTagName(el: Element): string {\n return el.tagName.toUpperCase();\n}\n\nfunction isButtonLikeElement(el: Element, tagName = getTagName(el)): boolean {\n if (INTERACTIVE_ELEMENTS.has(tagName)) return true;\n const role = el.getAttribute(\"role\");\n return role === \"button\";\n}\n\nfunction isButtonLikeDescendant(el: Element, tagName: string): boolean {\n const closestButton = el.closest(BUTTON_LIKE_SELECTOR);\n return Boolean(closestButton && !isButtonLikeElement(el, tagName));\n}\n\nfunction isContentElement(el: Element, tagName = getTagName(el)): boolean {\n if (MEDIA_ELEMENTS.has(tagName)) return true;\n if (SVG_ELEMENTS.has(tagName)) return true;\n if (isButtonLikeElement(el, tagName)) return true;\n\n const isLeafNode = el.childElementCount === 0;\n\n // Text elements that are leaf nodes (no child elements, only text)\n if (isLeafNode && el.textContent?.trim()) return true;\n\n return false;\n}\n\n/**\n * Calculate text skeleton width in ch units based on text content.\n * Uses a deterministic jitter: widthCh = clamp(6, 40, len + 2 + jitter)\n */\nfunction calculateTextWidthCh(text: string, seedKey: string): number {\n const textLength = text.length;\n const jitterRange = Math.max(4, 0.8 * textLength);\n const jitter = deterministicJitter(seedKey) * jitterRange;\n const width = textLength + 2 + jitter;\n return Math.max(TEXT_WIDTH_MIN_CH, Math.min(TEXT_WIDTH_MAX_CH, width));\n}\n\nfunction deterministicJitter(seedKey: string): number {\n if (!seedKey) return 0;\n let hash = 2166136261;\n for (let index = 0; index < seedKey.length; index += 1) {\n hash ^= seedKey.charCodeAt(index);\n hash = Math.imul(hash, 16777619);\n }\n const normalized = (hash >>> 0) / 0xffffffff;\n return normalized * 2 - 1;\n}\n\nfunction resolveTextAlign(el: HTMLElement): \"left\" | \"center\" | \"right\" {\n const align = globalThis.getComputedStyle(el).textAlign;\n if (align === \"center\") return \"center\";\n if (align === \"right\" || align === \"end\") return \"right\";\n return \"left\";\n}\n\nexport function applySkeletonClasses(\n rootElement: Element,\n options: { animate?: boolean; seed?: string | number } = {},\n): void {\n const { animate = true, seed } = options;\n const baseSeed =\n seed === undefined || seed === null ? \"loaded\" : String(seed);\n\n if (!isElement(rootElement)) {\n return;\n }\n\n const htmlRoot = rootElement as HTMLElement;\n\n // Apply skeleton mode to the root element\n htmlRoot.classList.add(\"loaded-skeleton-mode\");\n\n if (animate) {\n htmlRoot.classList.add(\"loaded-animate\");\n }\n\n // Apply background class for standalone usage (when not used via SmartSkeleton JSX)\n // If element has loaded-skeleton-wrapper, CSS handles bg via > :first-child rule\n // If element already has loaded-skeleton-bg (from JSX), this is a no-op\n const isWrapper = htmlRoot.classList.contains(\"loaded-skeleton-wrapper\");\n if (!isWrapper) {\n htmlRoot.classList.add(\"loaded-skeleton-bg\");\n }\n\n // Only add specific classes where needed (text, media, content)\n const descendants = rootElement.getElementsByTagName(\"*\");\n\n let textIndex = 0;\n\n const processElement = (el: Element) => {\n const tagName = getTagName(el);\n if (SKIPPED_TAGS.has(tagName)) return;\n if (!isContentElement(el, tagName)) return;\n if (isButtonLikeDescendant(el, tagName)) return;\n\n const htmlEl = el as HTMLElement;\n const textContent = el.textContent?.trim();\n const isLeafWithText = el.childElementCount === 0 && textContent;\n\n if (\n isLeafWithText &&\n !MEDIA_ELEMENTS.has(tagName) &&\n !SVG_ELEMENTS.has(tagName) &&\n !isButtonLikeElement(el, tagName)\n ) {\n // Text elements: overlay bar with ch-based width\n htmlEl.classList.add(\"loaded-text-skeleton\");\n htmlEl.dataset.skeletonAlign = resolveTextAlign(htmlEl);\n const seedKey = `${baseSeed}|${textIndex}|${textContent ?? \"\"}`;\n textIndex += 1;\n const widthCh = calculateTextWidthCh(textContent ?? \"\", seedKey);\n htmlEl.style.setProperty(\"--skeleton-text-width\", `${widthCh}ch`);\n } else if (MEDIA_ELEMENTS.has(tagName)) {\n // Media elements\n htmlEl.classList.add(\"loaded-skeleton-media\");\n } else if (SVG_ELEMENTS.has(tagName)) {\n // SVG elements rendered as rounded content blocks\n htmlEl.classList.add(\"loaded-skeleton-content\");\n htmlEl.classList.add(\"loaded-skeleton-svg\");\n } else {\n // Interactive elements (buttons, inputs, etc.)\n htmlEl.classList.add(\"loaded-skeleton-content\");\n // Prevent keyboard focus / interaction while in skeleton mode.\n // aria-hidden does not remove elements from the tab order.\n htmlEl.setAttribute(\"tabindex\", \"-1\");\n }\n };\n\n processElement(rootElement);\n for (const el of descendants) {\n processElement(el);\n }\n}\n\nexport interface SmartSkeletonProps {\n /** The skeleton element with mock data, rendered when loading */\n element: ReactElement;\n /** The real content to render when not loading. If omitted, returns null when loading=false. */\n children?: ReactElement;\n /** Whether the skeleton is currently loading. Default: false */\n loading?: boolean;\n /** Enable shimmer animation. Default: true */\n animate?: boolean;\n /** Additional CSS class name */\n className?: string;\n /** Optional seed to stabilize skeleton text widths */\n seed?: string | number;\n /** Suppress warning when auto-wrapper is applied. Default: false */\n suppressRefWarning?: boolean;\n}\n\nexport function SmartSkeleton({\n element,\n children,\n loading = false,\n animate = true,\n className = \"\",\n seed,\n suppressRefWarning = false,\n}: SmartSkeletonProps): ReactElement | null {\n const hasAppliedRef = useRef(false);\n const refWasCalledRef = useRef(false);\n const lastElementRef = useRef<ReactElement | null>(null);\n const previousElementTypeRef = useRef<ReactElement[\"type\"] | null>(null);\n const previousElementKeyRef = useRef<ReactElement[\"key\"] | null>(null);\n const [needsWrapper, setNeedsWrapper] = useState(false);\n\n // Reset flags when loading changes or element changes\n if (!loading || lastElementRef.current !== element) {\n hasAppliedRef.current = false;\n refWasCalledRef.current = false;\n lastElementRef.current = element;\n }\n\n useEffect(() => {\n const elementType = element.type;\n const elementKey = element.key ?? null;\n const previousType = previousElementTypeRef.current;\n const previousKey = previousElementKeyRef.current;\n\n if (\n previousType !== null &&\n (previousType !== elementType || previousKey !== elementKey)\n ) {\n setNeedsWrapper(false);\n }\n\n previousElementTypeRef.current = elementType;\n previousElementKeyRef.current = elementKey;\n }, [element.type, element.key]);\n\n const originalRef = getOriginalRef(element);\n\n const refCallback = useCallback(\n (node: unknown) => {\n refWasCalledRef.current = true;\n\n const target = resolveRefTarget(node);\n\n // If we received a node but couldn't get a DOM element from it,\n // we need to use a wrapper\n if (node !== null && !target && !needsWrapper) {\n setNeedsWrapper(true);\n\n // Emit warning (deduplicated by component name)\n if (!suppressRefWarning && isDevEnv()) {\n const displayName = getElementDisplayName(element);\n if (!warnedComponents.has(displayName)) {\n console.warn(\n `[SmartSkeleton] ${displayName} does not forward its ref to a DOM element. ` +\n `A wrapper <div> has been added automatically. Use forwardRef to avoid this.`,\n );\n warnedComponents.add(displayName);\n }\n }\n return;\n }\n\n if (target && loading && !hasAppliedRef.current) {\n applySkeletonClasses(target, { animate, seed });\n hasAppliedRef.current = true;\n }\n\n // Forward ref to original element\n forwardRef(originalRef, node);\n },\n [\n loading,\n element,\n needsWrapper,\n suppressRefWarning,\n originalRef,\n animate,\n seed,\n ],\n );\n\n // Detect if ref was never called (component ignores ref entirely)\n // useLayoutEffect runs synchronously after DOM mutations but before paint\n useIsomorphicLayoutEffect(() => {\n if (!loading || needsWrapper) return;\n\n // At this point, React has already attempted to attach refs\n // If refWasCalledRef is still false, the component ignored the ref\n if (!refWasCalledRef.current) {\n setNeedsWrapper(true);\n\n // Emit warning\n if (!suppressRefWarning && isDevEnv()) {\n const displayName = getElementDisplayName(element);\n if (!warnedComponents.has(displayName)) {\n console.warn(\n `[SmartSkeleton] ${displayName} does not accept a ref. ` +\n `A wrapper <div> has been added automatically. Use forwardRef to avoid this.`,\n );\n warnedComponents.add(displayName);\n }\n }\n }\n }, [loading, needsWrapper, element, suppressRefWarning]);\n\n // Not loading: return children or null\n if (!loading) {\n return children ?? null;\n }\n\n // Build merged className for skeleton mode\n const elementProps = element.props as { className?: string };\n const existingClassName = elementProps.className ?? \"\";\n\n // Base classes for skeleton mode\n const baseClasses = [\"loaded-skeleton-mode\", animate && \"loaded-animate\"]\n .filter(Boolean)\n .join(\" \");\n\n // When wrapping: wrapper gets mode + wrapper marker (no bg - it goes on child via ref)\n const wrapperClassName = [baseClasses, \"loaded-skeleton-wrapper\", className]\n .filter(Boolean)\n .join(\" \");\n\n // When not wrapping: element gets mode + bg directly (for SSR)\n const mergedClassName = [\n existingClassName,\n baseClasses,\n \"loaded-skeleton-bg\",\n className,\n ]\n .filter(Boolean)\n .join(\" \");\n\n return (\n <SkeletonContext.Provider value={true}>\n {needsWrapper ? (\n <div ref={refCallback} className={wrapperClassName} aria-hidden=\"true\">\n {element}\n </div>\n ) : (\n cloneElement(element as ReactElement<Record<string, unknown>>, {\n ref: refCallback,\n className: mergedClassName,\n \"aria-hidden\": true,\n })\n )}\n </SkeletonContext.Provider>\n );\n}\n","import { useEffect, useRef, useState } from \"react\";\nimport { isDevEnv } from \"../../utils/isDevEnv\";\nimport { useIsomorphicLayoutEffect } from \"../../utils/useIsomorphicLayoutEffect\";\n\nconst STORAGE_KEY = \"react-loaded\";\nconst LEGACY_STORAGE_KEY = \"loaded\";\nconst STORAGE_VERSION = 1 as const;\n\ntype StoredCounts = Record<string, number>;\ntype StoredPayloadV1 = { v: typeof STORAGE_VERSION; counts: StoredCounts };\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return Boolean(value) && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction toNumberRecord(value: unknown): StoredCounts {\n if (!isRecord(value)) return {};\n const result: StoredCounts = {};\n for (const [key, maybeNumber] of Object.entries(value)) {\n if (typeof maybeNumber === \"number\") {\n result[key] = maybeNumber;\n }\n }\n return result;\n}\n\nfunction parseStoredCounts(value: unknown): StoredCounts {\n // Current schema: { v: 1, counts: Record<string, number> }\n if (isRecord(value) && value.v === STORAGE_VERSION) {\n return toNumberRecord(value.counts);\n }\n\n // Legacy schema: Record<string, number>\n return toNumberRecord(value);\n}\n\nfunction readStoredCountsFromKey(key: string): StoredCounts {\n if (typeof localStorage === \"undefined\") return {};\n try {\n const raw = localStorage.getItem(key);\n if (raw === null) return {};\n return parseStoredCounts(JSON.parse(raw));\n } catch {\n return {};\n }\n}\n\nfunction writeStoredCounts(counts: StoredCounts): void {\n if (typeof localStorage === \"undefined\") return;\n const payload: StoredPayloadV1 = { v: STORAGE_VERSION, counts };\n try {\n localStorage.setItem(STORAGE_KEY, JSON.stringify(payload));\n } catch {\n // Silently fail if localStorage is full or unavailable\n }\n}\n\nfunction getStoredCounts(): Record<string, number> {\n if (typeof localStorage === \"undefined\") return {};\n\n try {\n const rawNew = localStorage.getItem(STORAGE_KEY);\n if (rawNew !== null) {\n return parseStoredCounts(JSON.parse(rawNew));\n }\n\n // Backward compatibility: migrate legacy key once if present.\n const legacyCounts = readStoredCountsFromKey(LEGACY_STORAGE_KEY);\n if (Object.keys(legacyCounts).length > 0) {\n writeStoredCounts(legacyCounts);\n }\n return legacyCounts;\n } catch {\n return {};\n }\n}\n\nfunction getStoredCount(key: string): number | null {\n const counts = getStoredCounts();\n const value = counts[key];\n return typeof value === \"number\" ? value : null;\n}\n\nfunction setStoredCount(key: string, count: number): void {\n if (typeof localStorage === \"undefined\") return;\n\n try {\n const counts = getStoredCounts();\n counts[key] = count;\n writeStoredCounts(counts);\n } catch {\n // Silently fail if localStorage is full or unavailable\n }\n}\n\nexport interface UsePersistedCountOptions {\n storageKey?: string;\n defaultCount?: number;\n currentCount?: number;\n loading: boolean;\n minCount?: number;\n maxCount?: number;\n}\n\nexport function usePersistedCount({\n storageKey,\n defaultCount = 3,\n currentCount,\n loading,\n minCount = 1,\n maxCount,\n}: UsePersistedCountOptions): number {\n // Always start from the default to match SSR output, then (on the client)\n // sync to the persisted value in a layout effect before first paint.\n const [count, setCount] = useState<number>(() =>\n clampCount(defaultCount, minCount, maxCount),\n );\n\n const hasWarnedRef = useRef(false);\n\n useIsomorphicLayoutEffect(() => {\n if (!storageKey) return;\n const stored = getStoredCount(storageKey);\n if (stored === null) return;\n const next = clampCount(stored, minCount, maxCount);\n setCount((prev) => (Object.is(prev, next) ? prev : next));\n }, [storageKey, minCount, maxCount]);\n\n useEffect(() => {\n if (!loading && currentCount !== undefined) {\n const newCount = clampCount(currentCount, minCount, maxCount);\n setCount(newCount);\n\n if (storageKey) {\n setStoredCount(storageKey, newCount);\n }\n }\n }, [loading, currentCount, storageKey, minCount, maxCount]);\n\n useEffect(() => {\n if (isDevEnv() && !storageKey && !hasWarnedRef.current) {\n console.warn(\n \"[Loaded] SmartSkeletonList used without storageKey. \" +\n \"The count will reset on remount. Add a storageKey to persist across sessions.\",\n );\n hasWarnedRef.current = true;\n }\n }, [storageKey]);\n\n return count;\n}\n\nfunction clampCount(\n value: number,\n min: number,\n max: number | undefined,\n): number {\n let result = Math.max(value, min);\n if (max !== undefined) {\n result = Math.min(result, max);\n }\n return result;\n}\n","import { Fragment, type ReactElement } from \"react\";\nimport { usePersistedCount } from \"../../hooks/usePersistedCount/usePersistedCount\";\nimport { SmartSkeleton } from \"../SmartSkeleton/SmartSkeleton\";\n\nexport interface SmartSkeletonListProps<T> {\n /** Whether the list is currently loading. Default: false */\n loading?: boolean;\n /** The items to render. Pass undefined while loading. */\n items: T[] | undefined;\n /** Render function for each item when loaded */\n renderItem: (item: T, index: number) => ReactElement;\n /** Render function for skeleton placeholders */\n renderSkeleton: (index: number) => ReactElement;\n /** Key for localStorage persistence. Without it, count resets on remount. */\n storageKey?: string;\n /** Initial skeleton count before any data is known. Default: 3 */\n defaultCount?: number;\n /** Minimum skeletons to show. Default: 1 */\n minCount?: number;\n /** Maximum skeletons to show */\n maxCount?: number;\n /** Enable shimmer animation. Default: true */\n animate?: boolean;\n /** Optional seed to stabilize skeleton text widths */\n seed?: string | number;\n /** Suppress warning when auto-wrapper is applied. Default: false */\n suppressRefWarning?: boolean;\n /** Extract unique key for each item. Default: index */\n keyExtractor?: (item: T, index: number) => string | number;\n}\n\nexport function SmartSkeletonList<T>({\n loading = false,\n items,\n renderItem,\n renderSkeleton,\n storageKey,\n defaultCount = 3,\n minCount = 1,\n maxCount,\n animate = true,\n seed,\n suppressRefWarning = false,\n keyExtractor = (_, index) => index,\n}: SmartSkeletonListProps<T>): ReactElement | null {\n const skeletonCount = usePersistedCount({\n storageKey,\n defaultCount,\n currentCount: items?.length,\n loading,\n minCount,\n maxCount,\n });\n\n if (loading) {\n const skeletons = new Array(skeletonCount);\n for (let index = 0; index < skeletonCount; index += 1) {\n const itemSeed = seed === undefined ? `${index}` : `${seed}:${index}`;\n skeletons[index] = (\n <SmartSkeleton\n key={`skeleton-${index}`}\n loading={true}\n element={renderSkeleton(index)}\n animate={animate}\n seed={itemSeed}\n suppressRefWarning={suppressRefWarning}\n />\n );\n }\n return <>{skeletons}</>;\n }\n\n if (!items || items.length === 0) {\n return null;\n }\n\n return (\n <>\n {items.map((item, index) => (\n <Fragment key={keyExtractor(item, index)}>\n {renderItem(item, index)}\n </Fragment>\n ))}\n </>\n );\n}\n"]}
1
+ {"version":3,"sources":["../src/components/SkeletonContext/SkeletonContext.tsx","../src/utils/isDevEnv.ts","../src/utils/useIsomorphicLayoutEffect.ts","../src/components/SmartSkeleton/SmartSkeleton.tsx","../src/hooks/usePersistedCount/usePersistedCount.ts","../src/components/SmartSkeletonList/SmartSkeletonList.tsx"],"names":["SkeletonContext","createContext","useIsSkeletonMode","useContext","isDevEnv","maybeGlobal","override","devFlag","maybeProcess","nodeEnv","canUseDOM","useIsomorphicLayoutEffect","useLayoutEffect","useEffect","TEXT_WIDTH_MIN_CH","TEXT_WIDTH_MAX_CH","isElement","value","maybeElement","warnedComponents","isUsableElement","resolveRefTarget","node","nativeElement","getElementDisplayName","element","type","fn","obj","REACT_MAJOR_VERSION","reactVersion","IS_REACT_19_OR_NEWER","getOriginalRef","propsRef","legacyRef","forwardRef","originalRef","MEDIA_ELEMENTS","SVG_ELEMENTS","INTERACTIVE_ELEMENTS","BUTTON_LIKE_SELECTOR","SKIPPED_TAGS","getTagName","el","isButtonLikeElement","tagName","isButtonLikeDescendant","isContentElement","calculateTextWidthCh","text","seedKey","textLength","jitterRange","jitter","deterministicJitter","width","hash","index","resolveTextAlign","align","applySkeletonClasses","rootElement","options","animate","seed","baseSeed","htmlRoot","descendants","textIndex","processElement","htmlEl","textContent","widthCh","SmartSkeleton","children","loading","className","suppressRefWarning","hasAppliedRef","useRef","refWasCalledRef","lastRefNodeRef","deferredWrapperCheckTimeoutRef","needsWrapperRef","needsWrapper","setNeedsWrapper","useState","currentElementType","currentElementKey","previousLoadingRef","previousElementTypeRef","previousElementKeyRef","cancelDeferredWrapperCheck","useCallback","previousLoading","previousElementType","previousElementKey","enableWrapperWithWarning","reason","displayName","refCallback","target","delayedNode","delayedTarget","existingClassName","baseClasses","wrapperClassName","mergedClassName","jsx","cloneElement","STORAGE_KEY","LEGACY_STORAGE_KEY","STORAGE_VERSION","isRecord","toNumberRecord","result","key","maybeNumber","parseStoredCounts","readStoredCountsFromKey","raw","writeStoredCounts","counts","payload","getStoredCounts","rawNew","legacyCounts","getStoredCount","setStoredCount","count","usePersistedCount","storageKey","defaultCount","currentCount","minCount","maxCount","setCount","clampCount","hasWarnedRef","stored","next","prev","newCount","min","max","SmartSkeletonList","items","renderItem","renderSkeleton","keyExtractor","_","skeletonCount","skeletons","itemSeed","Fragment","item"],"mappings":"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","file":"index.cjs","sourcesContent":["import { createContext, useContext } from \"react\";\n\nexport const SkeletonContext = createContext(false);\n\nexport function useIsSkeletonMode(): boolean {\n return useContext(SkeletonContext);\n}\n","export function isDevEnv(): boolean {\n const maybeGlobal = globalThis as unknown as Record<string, unknown>;\n\n // Manual override for environments where NODE_ENV isn't injected.\n // Example: `globalThis.__REACT_LOADED_DEV__ = true`.\n const override = maybeGlobal.__REACT_LOADED_DEV__;\n if (typeof override === \"boolean\") return override;\n\n // Common global used by some toolchains/runtimes.\n const devFlag = maybeGlobal.__DEV__;\n if (typeof devFlag === \"boolean\") return devFlag;\n\n const maybeProcess = (globalThis as unknown as { process?: unknown }).process;\n const nodeEnv =\n typeof maybeProcess === \"object\" && maybeProcess !== null\n ? (maybeProcess as { env?: { NODE_ENV?: unknown } }).env?.NODE_ENV\n : undefined;\n\n if (typeof nodeEnv === \"string\") {\n return nodeEnv !== \"production\";\n }\n\n // No environment detected — assume production (convention: opt-in to dev mode).\n return false;\n}\n","import { useEffect, useLayoutEffect } from \"react\";\n\nconst canUseDOM =\n typeof globalThis !== \"undefined\" &&\n typeof (globalThis as { document?: unknown }).document !== \"undefined\";\n\nexport const useIsomorphicLayoutEffect = canUseDOM\n ? useLayoutEffect\n : useEffect;\n","import {\n cloneElement,\n type ReactElement,\n type Ref,\n version as reactVersion,\n useCallback,\n useEffect,\n useRef,\n useState,\n} from \"react\";\nimport { isDevEnv } from \"../../utils/isDevEnv\";\nimport { useIsomorphicLayoutEffect } from \"../../utils/useIsomorphicLayoutEffect\";\nimport { SkeletonContext } from \"../SkeletonContext/SkeletonContext\";\nimport \"./SmartSkeleton.css\";\n\n// Text width configuration (in ch units)\nconst TEXT_WIDTH_MIN_CH = 6;\nconst TEXT_WIDTH_MAX_CH = 40;\n\nfunction isElement(value: unknown): value is Element {\n if (!value || typeof value !== \"object\") return false;\n const maybeElement = value as Element;\n // Must have nodeType 1 (Element) and a working querySelectorAll\n if (maybeElement.nodeType !== 1) return false;\n if (typeof maybeElement.tagName !== \"string\") return false;\n if (typeof maybeElement.querySelectorAll !== \"function\") return false;\n // Additional check: instanceof Element if available\n if (typeof Element !== \"undefined\" && !(value instanceof Element)) {\n return false;\n }\n return true;\n}\n\nconst warnedComponents = new Set<string>();\n\nfunction isUsableElement(value: unknown): value is Element {\n if (!isElement(value)) return false;\n // Test that querySelectorAll actually works\n try {\n (value as Element).querySelectorAll(\"*\");\n return true;\n } catch {\n return false;\n }\n}\n\nfunction resolveRefTarget(node: unknown): Element | null {\n if (isUsableElement(node)) return node;\n if (node && typeof node === \"object\" && \"nativeElement\" in node) {\n const nativeElement = (node as { nativeElement?: unknown }).nativeElement;\n if (isUsableElement(nativeElement)) return nativeElement;\n }\n return null;\n}\n\nfunction getElementDisplayName(element: ReactElement): string {\n const type = element.type;\n if (typeof type === \"string\") {\n return `<${type}>`;\n }\n if (typeof type === \"function\") {\n const fn = type as { displayName?: string; name?: string };\n return `<${fn.displayName || fn.name || \"Unknown\"}>`;\n }\n if (typeof type === \"object\" && type !== null) {\n const obj = type as { displayName?: string; name?: string };\n return `<${obj.displayName || obj.name || \"Unknown\"}>`;\n }\n return \"<Unknown>\";\n}\n\nconst REACT_MAJOR_VERSION = Number.parseInt(reactVersion, 10);\nconst IS_REACT_19_OR_NEWER =\n Number.isFinite(REACT_MAJOR_VERSION) && REACT_MAJOR_VERSION >= 19;\n\n/**\n * Get the original ref from the element, supporting both React 18 and React 19.\n * React 19: ref is a regular prop on element.props.ref\n * React 18: ref is on element.ref\n */\nfunction getOriginalRef(element: ReactElement): Ref<unknown> | undefined {\n // React 19 style (ref as prop)\n const elementProps = element.props as { ref?: Ref<unknown> } | undefined;\n const propsRef = elementProps?.ref;\n if (propsRef !== undefined) return propsRef;\n\n // React 19+ warns on element.ref access; skip legacy fallback entirely.\n if (IS_REACT_19_OR_NEWER) return undefined;\n\n // React 18 style\n const legacyRef = (element as ReactElement & { ref?: Ref<unknown> }).ref;\n if (legacyRef !== undefined) return legacyRef;\n\n return undefined;\n}\n\n/**\n * Forward a ref value to the original ref (callback or object style).\n */\nfunction forwardRef(originalRef: Ref<unknown> | undefined, node: unknown) {\n if (!originalRef) return;\n if (typeof originalRef === \"function\") {\n originalRef(node);\n } else {\n (originalRef as React.MutableRefObject<unknown>).current = node;\n }\n}\n\nconst MEDIA_ELEMENTS = new Set([\"IMG\", \"VIDEO\", \"CANVAS\"]);\nconst SVG_ELEMENTS = new Set([\"SVG\"]);\n\nconst INTERACTIVE_ELEMENTS = new Set([\n \"BUTTON\",\n \"INPUT\",\n \"TEXTAREA\",\n \"SELECT\",\n \"A\",\n]);\n\nconst BUTTON_LIKE_SELECTOR = \"button,input,textarea,select,a,[role='button']\";\nconst SKIPPED_TAGS = new Set([\n \"SCRIPT\",\n \"STYLE\",\n \"LINK\",\n \"META\",\n \"NOSCRIPT\",\n \"TEMPLATE\",\n]);\n\nfunction getTagName(el: Element): string {\n return el.tagName.toUpperCase();\n}\n\nfunction isButtonLikeElement(el: Element, tagName = getTagName(el)): boolean {\n if (INTERACTIVE_ELEMENTS.has(tagName)) return true;\n const role = el.getAttribute(\"role\");\n return role === \"button\";\n}\n\nfunction isButtonLikeDescendant(el: Element, tagName: string): boolean {\n const closestButton = el.closest(BUTTON_LIKE_SELECTOR);\n return Boolean(closestButton && !isButtonLikeElement(el, tagName));\n}\n\nfunction isContentElement(el: Element, tagName = getTagName(el)): boolean {\n if (MEDIA_ELEMENTS.has(tagName)) return true;\n if (SVG_ELEMENTS.has(tagName)) return true;\n if (isButtonLikeElement(el, tagName)) return true;\n\n const isLeafNode = el.childElementCount === 0;\n\n // Text elements that are leaf nodes (no child elements, only text)\n if (isLeafNode && el.textContent?.trim()) return true;\n\n return false;\n}\n\n/**\n * Calculate text skeleton width in ch units based on text content.\n * Uses a deterministic jitter: widthCh = clamp(6, 40, len + 2 + jitter)\n */\nfunction calculateTextWidthCh(text: string, seedKey: string): number {\n const textLength = text.length;\n const jitterRange = Math.max(4, 0.8 * textLength);\n const jitter = deterministicJitter(seedKey) * jitterRange;\n const width = textLength + 2 + jitter;\n return Math.max(TEXT_WIDTH_MIN_CH, Math.min(TEXT_WIDTH_MAX_CH, width));\n}\n\nfunction deterministicJitter(seedKey: string): number {\n if (!seedKey) return 0;\n let hash = 2166136261;\n for (let index = 0; index < seedKey.length; index += 1) {\n hash ^= seedKey.charCodeAt(index);\n hash = Math.imul(hash, 16777619);\n }\n const normalized = (hash >>> 0) / 0xffffffff;\n return normalized * 2 - 1;\n}\n\nfunction resolveTextAlign(el: HTMLElement): \"left\" | \"center\" | \"right\" {\n const align = globalThis.getComputedStyle(el).textAlign;\n if (align === \"center\") return \"center\";\n if (align === \"right\" || align === \"end\") return \"right\";\n return \"left\";\n}\n\nexport function applySkeletonClasses(\n rootElement: Element,\n options: { animate?: boolean; seed?: string | number } = {},\n): void {\n const { animate = true, seed } = options;\n const baseSeed =\n seed === undefined || seed === null ? \"loaded\" : String(seed);\n\n if (!isElement(rootElement)) {\n return;\n }\n\n const htmlRoot = rootElement as HTMLElement;\n\n // Apply skeleton mode to the root element\n htmlRoot.classList.add(\"loaded-skeleton-mode\");\n\n if (animate) {\n htmlRoot.classList.add(\"loaded-animate\");\n }\n\n // Apply background class for standalone usage (when not used via SmartSkeleton JSX)\n // If element has loaded-skeleton-wrapper, CSS handles bg via > :first-child rule\n // If element already has loaded-skeleton-bg (from JSX), this is a no-op\n const isWrapper = htmlRoot.classList.contains(\"loaded-skeleton-wrapper\");\n if (!isWrapper) {\n htmlRoot.classList.add(\"loaded-skeleton-bg\");\n }\n\n // Only add specific classes where needed (text, media, content)\n const descendants = rootElement.getElementsByTagName(\"*\");\n\n let textIndex = 0;\n\n const processElement = (el: Element) => {\n const tagName = getTagName(el);\n if (SKIPPED_TAGS.has(tagName)) return;\n if (!isContentElement(el, tagName)) return;\n if (isButtonLikeDescendant(el, tagName)) return;\n\n const htmlEl = el as HTMLElement;\n const textContent = el.textContent?.trim();\n const isLeafWithText = el.childElementCount === 0 && textContent;\n\n if (\n isLeafWithText &&\n !MEDIA_ELEMENTS.has(tagName) &&\n !SVG_ELEMENTS.has(tagName) &&\n !isButtonLikeElement(el, tagName)\n ) {\n // Text elements: overlay bar with ch-based width\n htmlEl.classList.add(\"loaded-text-skeleton\");\n htmlEl.dataset.skeletonAlign = resolveTextAlign(htmlEl);\n const seedKey = `${baseSeed}|${textIndex}|${textContent ?? \"\"}`;\n textIndex += 1;\n const widthCh = calculateTextWidthCh(textContent ?? \"\", seedKey);\n htmlEl.style.setProperty(\"--skeleton-text-width\", `${widthCh}ch`);\n } else if (MEDIA_ELEMENTS.has(tagName)) {\n // Media elements\n htmlEl.classList.add(\"loaded-skeleton-media\");\n } else if (SVG_ELEMENTS.has(tagName)) {\n // SVG elements rendered as rounded content blocks\n htmlEl.classList.add(\"loaded-skeleton-content\");\n htmlEl.classList.add(\"loaded-skeleton-svg\");\n } else {\n // Interactive elements (buttons, inputs, etc.)\n htmlEl.classList.add(\"loaded-skeleton-content\");\n // Prevent keyboard focus / interaction while in skeleton mode.\n // aria-hidden does not remove elements from the tab order.\n htmlEl.setAttribute(\"tabindex\", \"-1\");\n }\n };\n\n processElement(rootElement);\n for (const el of descendants) {\n processElement(el);\n }\n}\n\nexport interface SmartSkeletonProps {\n /** The skeleton element with mock data, rendered when loading */\n element: ReactElement;\n /** The real content to render when not loading. If omitted, returns null when loading=false. */\n children?: ReactElement;\n /** Whether the skeleton is currently loading. Default: false */\n loading?: boolean;\n /** Enable shimmer animation. Default: true */\n animate?: boolean;\n /** Additional CSS class name */\n className?: string;\n /** Optional seed to stabilize skeleton text widths */\n seed?: string | number;\n /** Suppress warning when auto-wrapper is applied. Default: false */\n suppressRefWarning?: boolean;\n}\n\nexport function SmartSkeleton({\n element,\n children,\n loading = false,\n animate = true,\n className = \"\",\n seed,\n suppressRefWarning = false,\n}: SmartSkeletonProps): ReactElement | null {\n const hasAppliedRef = useRef(false);\n const refWasCalledRef = useRef(false);\n const lastRefNodeRef = useRef<unknown>(null);\n const deferredWrapperCheckTimeoutRef = useRef<ReturnType<\n typeof setTimeout\n > | null>(null);\n const needsWrapperRef = useRef(false);\n const [needsWrapper, setNeedsWrapper] = useState(false);\n const currentElementType = element.type;\n const currentElementKey = element.key ?? null;\n const previousLoadingRef = useRef(loading);\n const previousElementTypeRef =\n useRef<ReactElement[\"type\"]>(currentElementType);\n const previousElementKeyRef = useRef<ReactElement[\"key\"] | null>(\n currentElementKey,\n );\n\n const cancelDeferredWrapperCheck = useCallback(() => {\n if (deferredWrapperCheckTimeoutRef.current !== null) {\n clearTimeout(deferredWrapperCheckTimeoutRef.current);\n deferredWrapperCheckTimeoutRef.current = null;\n }\n }, []);\n\n useEffect(() => {\n needsWrapperRef.current = needsWrapper;\n }, [needsWrapper]);\n\n // Keep render pure: reset mutable tracking only after commit.\n useIsomorphicLayoutEffect(() => {\n const previousLoading = previousLoadingRef.current;\n const previousElementType = previousElementTypeRef.current;\n const previousElementKey = previousElementKeyRef.current;\n\n const didExitLoading = previousLoading && !loading;\n const hasElementIdentityChanged =\n previousElementType !== currentElementType ||\n previousElementKey !== currentElementKey;\n\n if (didExitLoading || hasElementIdentityChanged) {\n hasAppliedRef.current = false;\n refWasCalledRef.current = false;\n lastRefNodeRef.current = null;\n cancelDeferredWrapperCheck();\n\n if (needsWrapperRef.current) {\n setNeedsWrapper(false);\n }\n }\n\n previousLoadingRef.current = loading;\n previousElementTypeRef.current = currentElementType;\n previousElementKeyRef.current = currentElementKey;\n }, [\n loading,\n currentElementType,\n currentElementKey,\n cancelDeferredWrapperCheck,\n ]);\n\n useEffect(() => {\n return () => {\n cancelDeferredWrapperCheck();\n };\n }, [cancelDeferredWrapperCheck]);\n\n const originalRef = getOriginalRef(element);\n\n const enableWrapperWithWarning = useCallback(\n (reason: \"non-dom-ref\" | \"no-ref-call\") => {\n if (needsWrapperRef.current) return;\n setNeedsWrapper(true);\n\n if (!suppressRefWarning && isDevEnv()) {\n const displayName = getElementDisplayName(element);\n if (!warnedComponents.has(displayName)) {\n if (reason === \"non-dom-ref\") {\n console.warn(\n `[SmartSkeleton] ${displayName} does not forward its ref to a DOM element. ` +\n `A wrapper <div> has been added automatically. Use forwardRef to avoid this.`,\n );\n } else {\n console.warn(\n `[SmartSkeleton] ${displayName} does not accept a ref. ` +\n `A wrapper <div> has been added automatically. Use forwardRef to avoid this.`,\n );\n }\n warnedComponents.add(displayName);\n }\n }\n },\n [element, suppressRefWarning],\n );\n\n const refCallback = useCallback(\n (node: unknown) => {\n refWasCalledRef.current = true;\n lastRefNodeRef.current = node;\n\n const target = resolveRefTarget(node);\n\n if (target && loading && !hasAppliedRef.current) {\n applySkeletonClasses(target, { animate, seed });\n hasAppliedRef.current = true;\n }\n\n // Forward ref to original element\n forwardRef(originalRef, node);\n },\n [loading, originalRef, animate, seed],\n );\n\n // Decide wrapper fallback after commit to avoid eager false positives:\n // some environments attach refs slightly later in the same tick.\n useIsomorphicLayoutEffect(() => {\n if (!loading || needsWrapper) return;\n\n cancelDeferredWrapperCheck();\n\n const node = lastRefNodeRef.current;\n const target = resolveRefTarget(node);\n\n if (target && !hasAppliedRef.current) {\n applySkeletonClasses(target, { animate, seed });\n hasAppliedRef.current = true;\n }\n\n if (refWasCalledRef.current) {\n if (node !== null && !target) {\n enableWrapperWithWarning(\"non-dom-ref\");\n }\n return;\n }\n\n deferredWrapperCheckTimeoutRef.current = setTimeout(() => {\n deferredWrapperCheckTimeoutRef.current = null;\n\n if (!loading || needsWrapperRef.current) return;\n\n const delayedNode = lastRefNodeRef.current;\n const delayedTarget = resolveRefTarget(delayedNode);\n\n if (delayedTarget && !hasAppliedRef.current) {\n applySkeletonClasses(delayedTarget, { animate, seed });\n hasAppliedRef.current = true;\n }\n\n if (refWasCalledRef.current) {\n if (delayedNode !== null && !delayedTarget) {\n enableWrapperWithWarning(\"non-dom-ref\");\n }\n return;\n }\n\n enableWrapperWithWarning(\"no-ref-call\");\n }, 0);\n\n return () => {\n cancelDeferredWrapperCheck();\n };\n }, [\n loading,\n needsWrapper,\n animate,\n seed,\n enableWrapperWithWarning,\n cancelDeferredWrapperCheck,\n ]);\n\n // Not loading: return children or null\n if (!loading) {\n return children ?? null;\n }\n\n // Build merged className for skeleton mode\n const elementProps = element.props as { className?: string };\n const existingClassName = elementProps.className ?? \"\";\n\n // Base classes for skeleton mode\n const baseClasses = [\"loaded-skeleton-mode\", animate && \"loaded-animate\"]\n .filter(Boolean)\n .join(\" \");\n\n // When wrapping: wrapper gets mode + wrapper marker (no bg - it goes on child via ref)\n const wrapperClassName = [baseClasses, \"loaded-skeleton-wrapper\", className]\n .filter(Boolean)\n .join(\" \");\n\n // When not wrapping: element gets mode + bg directly (for SSR)\n const mergedClassName = [\n existingClassName,\n baseClasses,\n \"loaded-skeleton-bg\",\n className,\n ]\n .filter(Boolean)\n .join(\" \");\n\n return (\n <SkeletonContext.Provider value={true}>\n {needsWrapper ? (\n <div ref={refCallback} className={wrapperClassName} aria-hidden=\"true\">\n {element}\n </div>\n ) : (\n cloneElement(element as ReactElement<Record<string, unknown>>, {\n ref: refCallback,\n className: mergedClassName,\n \"aria-hidden\": true,\n })\n )}\n </SkeletonContext.Provider>\n );\n}\n","import { useEffect, useRef, useState } from \"react\";\nimport { isDevEnv } from \"../../utils/isDevEnv\";\nimport { useIsomorphicLayoutEffect } from \"../../utils/useIsomorphicLayoutEffect\";\n\nconst STORAGE_KEY = \"react-loaded\";\nconst LEGACY_STORAGE_KEY = \"loaded\";\nconst STORAGE_VERSION = 1 as const;\n\ntype StoredCounts = Record<string, number>;\ntype StoredPayloadV1 = { v: typeof STORAGE_VERSION; counts: StoredCounts };\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return Boolean(value) && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction toNumberRecord(value: unknown): StoredCounts {\n if (!isRecord(value)) return {};\n const result: StoredCounts = {};\n for (const [key, maybeNumber] of Object.entries(value)) {\n if (typeof maybeNumber === \"number\") {\n result[key] = maybeNumber;\n }\n }\n return result;\n}\n\nfunction parseStoredCounts(value: unknown): StoredCounts {\n // Current schema: { v: 1, counts: Record<string, number> }\n if (isRecord(value) && value.v === STORAGE_VERSION) {\n return toNumberRecord(value.counts);\n }\n\n // Legacy schema: Record<string, number>\n return toNumberRecord(value);\n}\n\nfunction readStoredCountsFromKey(key: string): StoredCounts {\n if (typeof localStorage === \"undefined\") return {};\n try {\n const raw = localStorage.getItem(key);\n if (raw === null) return {};\n return parseStoredCounts(JSON.parse(raw));\n } catch {\n return {};\n }\n}\n\nfunction writeStoredCounts(counts: StoredCounts): void {\n if (typeof localStorage === \"undefined\") return;\n const payload: StoredPayloadV1 = { v: STORAGE_VERSION, counts };\n try {\n localStorage.setItem(STORAGE_KEY, JSON.stringify(payload));\n } catch {\n // Silently fail if localStorage is full or unavailable\n }\n}\n\nfunction getStoredCounts(): Record<string, number> {\n if (typeof localStorage === \"undefined\") return {};\n\n try {\n const rawNew = localStorage.getItem(STORAGE_KEY);\n if (rawNew !== null) {\n return parseStoredCounts(JSON.parse(rawNew));\n }\n\n // Backward compatibility: migrate legacy key once if present.\n const legacyCounts = readStoredCountsFromKey(LEGACY_STORAGE_KEY);\n if (Object.keys(legacyCounts).length > 0) {\n writeStoredCounts(legacyCounts);\n }\n return legacyCounts;\n } catch {\n return {};\n }\n}\n\nfunction getStoredCount(key: string): number | null {\n const counts = getStoredCounts();\n const value = counts[key];\n return typeof value === \"number\" ? value : null;\n}\n\nfunction setStoredCount(key: string, count: number): void {\n if (typeof localStorage === \"undefined\") return;\n\n try {\n const counts = getStoredCounts();\n counts[key] = count;\n writeStoredCounts(counts);\n } catch {\n // Silently fail if localStorage is full or unavailable\n }\n}\n\nexport interface UsePersistedCountOptions {\n storageKey?: string;\n defaultCount?: number;\n currentCount?: number;\n loading: boolean;\n minCount?: number;\n maxCount?: number;\n}\n\nexport function usePersistedCount({\n storageKey,\n defaultCount = 3,\n currentCount,\n loading,\n minCount = 1,\n maxCount,\n}: UsePersistedCountOptions): number {\n // Always start from the default to match SSR output, then (on the client)\n // sync to the persisted value in a layout effect before first paint.\n const [count, setCount] = useState<number>(() =>\n clampCount(defaultCount, minCount, maxCount),\n );\n\n const hasWarnedRef = useRef(false);\n\n useIsomorphicLayoutEffect(() => {\n if (!storageKey) return;\n const stored = getStoredCount(storageKey);\n if (stored === null) return;\n const next = clampCount(stored, minCount, maxCount);\n setCount((prev) => (Object.is(prev, next) ? prev : next));\n }, [storageKey, minCount, maxCount]);\n\n useEffect(() => {\n if (!loading && currentCount !== undefined) {\n const newCount = clampCount(currentCount, minCount, maxCount);\n setCount(newCount);\n\n if (storageKey) {\n setStoredCount(storageKey, newCount);\n }\n }\n }, [loading, currentCount, storageKey, minCount, maxCount]);\n\n useEffect(() => {\n if (isDevEnv() && !storageKey && !hasWarnedRef.current) {\n console.warn(\n \"[Loaded] SmartSkeletonList used without storageKey. \" +\n \"The count will reset on remount. Add a storageKey to persist across sessions.\",\n );\n hasWarnedRef.current = true;\n }\n }, [storageKey]);\n\n return count;\n}\n\nfunction clampCount(\n value: number,\n min: number,\n max: number | undefined,\n): number {\n let result = Math.max(value, min);\n if (max !== undefined) {\n result = Math.min(result, max);\n }\n return result;\n}\n","import { Fragment, type ReactElement } from \"react\";\nimport { usePersistedCount } from \"../../hooks/usePersistedCount/usePersistedCount\";\nimport { SmartSkeleton } from \"../SmartSkeleton/SmartSkeleton\";\n\nexport interface SmartSkeletonListProps<T> {\n /** Whether the list is currently loading. Default: false */\n loading?: boolean;\n /** The items to render. Pass undefined while loading. */\n items: T[] | undefined;\n /** Render function for each item when loaded */\n renderItem: (item: T, index: number) => ReactElement;\n /** Render function for skeleton placeholders */\n renderSkeleton: (index: number) => ReactElement;\n /** Key for localStorage persistence. Without it, count resets on remount. */\n storageKey?: string;\n /** Initial skeleton count before any data is known. Default: 3 */\n defaultCount?: number;\n /** Minimum skeletons to show. Default: 1 */\n minCount?: number;\n /** Maximum skeletons to show */\n maxCount?: number;\n /** Enable shimmer animation. Default: true */\n animate?: boolean;\n /** Optional seed to stabilize skeleton text widths */\n seed?: string | number;\n /** Suppress warning when auto-wrapper is applied. Default: false */\n suppressRefWarning?: boolean;\n /** Extract unique key for each item. Default: index */\n keyExtractor?: (item: T, index: number) => string | number;\n}\n\nexport function SmartSkeletonList<T>({\n loading = false,\n items,\n renderItem,\n renderSkeleton,\n storageKey,\n defaultCount = 3,\n minCount = 1,\n maxCount,\n animate = true,\n seed,\n suppressRefWarning = false,\n keyExtractor = (_, index) => index,\n}: SmartSkeletonListProps<T>): ReactElement | null {\n const skeletonCount = usePersistedCount({\n storageKey,\n defaultCount,\n currentCount: items?.length,\n loading,\n minCount,\n maxCount,\n });\n\n if (loading) {\n const skeletons = new Array(skeletonCount);\n for (let index = 0; index < skeletonCount; index += 1) {\n const itemSeed = seed === undefined ? `${index}` : `${seed}:${index}`;\n skeletons[index] = (\n <SmartSkeleton\n key={`skeleton-${index}`}\n loading={true}\n element={renderSkeleton(index)}\n animate={animate}\n seed={itemSeed}\n suppressRefWarning={suppressRefWarning}\n />\n );\n }\n return <>{skeletons}</>;\n }\n\n if (!items || items.length === 0) {\n return null;\n }\n\n return (\n <>\n {items.map((item, index) => (\n <Fragment key={keyExtractor(item, index)}>\n {renderItem(item, index)}\n </Fragment>\n ))}\n </>\n );\n}\n"]}
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import {createContext,useContext,useRef,useState,useEffect,useCallback,cloneElement,Fragment as Fragment$1,useLayoutEffect}from'react';import {jsx,Fragment}from'react/jsx-runtime';var h=createContext(false);function Z(){return useContext(h)}function S(){let e=globalThis,t=e.__REACT_LOADED_DEV__;if(typeof t=="boolean")return t;let n=e.__DEV__;if(typeof n=="boolean")return n;let o=globalThis.process,s=typeof o=="object"&&o!==null?o.env?.NODE_ENV:void 0;return typeof s=="string"?s!=="production":false}var ne=typeof globalThis<"u"&&typeof globalThis.document<"u",w=ne?useLayoutEffect:useEffect;var le=6,ie=40;function V(e){if(!e||typeof e!="object")return false;let t=e;return !(t.nodeType!==1||typeof t.tagName!="string"||typeof t.querySelectorAll!="function"||typeof Element<"u"&&!(e instanceof Element))}var x=new Set;function M(e){if(!V(e))return false;try{return e.querySelectorAll("*"),!0}catch{return false}}function de(e){if(M(e))return e;if(e&&typeof e=="object"&&"nativeElement"in e){let t=e.nativeElement;if(M(t))return t}return null}function P(e){let t=e.type;if(typeof t=="string")return `<${t}>`;if(typeof t=="function"){let n=t;return `<${n.displayName||n.name||"Unknown"}>`}if(typeof t=="object"&&t!==null){let n=t;return `<${n.displayName||n.name||"Unknown"}>`}return "<Unknown>"}function ue(e){let t=e.props?.ref;if(t)return t;let n=e.ref;if(n)return n}function ce(e,t){e&&(typeof e=="function"?e(t):e.current=t);}var T=new Set(["IMG","VIDEO","CANVAS"]),R=new Set(["SVG"]),fe=new Set(["BUTTON","INPUT","TEXTAREA","SELECT","A"]),me="button,input,textarea,select,a,[role='button']",pe=new Set(["SCRIPT","STYLE","LINK","META","NOSCRIPT","TEMPLATE"]);function v(e){return e.tagName.toUpperCase()}function L(e,t=v(e)){return fe.has(t)?true:e.getAttribute("role")==="button"}function ke(e,t){return !!(e.closest(me)&&!L(e,t))}function be(e,t=v(e)){return !!(T.has(t)||R.has(t)||L(e,t)||e.childElementCount===0&&e.textContent?.trim())}function ge(e,t){let n=e.length,o=Math.max(4,.8*n),s=Ee(t)*o,a=n+2+s;return Math.max(le,Math.min(ie,a))}function Ee(e){if(!e)return 0;let t=2166136261;for(let o=0;o<e.length;o+=1)t^=e.charCodeAt(o),t=Math.imul(t,16777619);return (t>>>0)/4294967295*2-1}function Se(e){let t=globalThis.getComputedStyle(e).textAlign;return t==="center"?"center":t==="right"||t==="end"?"right":"left"}function ye(e,t={}){let{animate:n=true,seed:o}=t,s=o==null?"loaded":String(o);if(!V(e))return;let a=e;a.classList.add("loaded-skeleton-mode"),n&&a.classList.add("loaded-animate"),a.classList.contains("loaded-skeleton-wrapper")||a.classList.add("loaded-skeleton-bg");let m=e.getElementsByTagName("*"),f=0,u=l=>{let i=v(l);if(pe.has(i)||!be(l,i)||ke(l,i))return;let r=l,d=l.textContent?.trim();if(l.childElementCount===0&&d&&!T.has(i)&&!R.has(i)&&!L(l,i)){r.classList.add("loaded-text-skeleton"),r.dataset.skeletonAlign=Se(r);let b=`${s}|${f}|${d??""}`;f+=1;let O=ge(d??"",b);r.style.setProperty("--skeleton-text-width",`${O}ch`);}else T.has(i)?r.classList.add("loaded-skeleton-media"):R.has(i)?(r.classList.add("loaded-skeleton-content"),r.classList.add("loaded-skeleton-svg")):(r.classList.add("loaded-skeleton-content"),r.setAttribute("tabindex","-1"));};u(e);for(let l of m)u(l);}function N({element:e,children:t,loading:n=false,animate:o=true,className:s="",seed:a,suppressRefWarning:k=false}){let m=useRef(false),f=useRef(false),u=useRef(null),l=useRef(null),i=useRef(null),[r,d]=useState(false);(!n||u.current!==e)&&(m.current=false,f.current=false,u.current=e),useEffect(()=>{let p=e.type,g=e.key??null,E=l.current,q=i.current;E!==null&&(E!==p||q!==g)&&d(false),l.current=p,i.current=g;},[e.type,e.key]);let c=ue(e),b=useCallback(p=>{f.current=true;let g=de(p);if(p!==null&&!g&&!r){if(d(true),!k&&S()){let E=P(e);x.has(E)||(console.warn(`[SmartSkeleton] ${E} does not forward its ref to a DOM element. A wrapper <div> has been added automatically. Use forwardRef to avoid this.`),x.add(E));}return}g&&n&&!m.current&&(ye(g,{animate:o,seed:a}),m.current=true),ce(c,p);},[n,e,r,k,c,o,a]);if(w(()=>{if(!(!n||r)&&!f.current&&(d(true),!k&&S())){let p=P(e);x.has(p)||(console.warn(`[SmartSkeleton] ${p} does not accept a ref. A wrapper <div> has been added automatically. Use forwardRef to avoid this.`),x.add(p));}},[n,r,e,k]),!n)return t??null;let F=e.props.className??"",I=["loaded-skeleton-mode",o&&"loaded-animate"].filter(Boolean).join(" "),J=[I,"loaded-skeleton-wrapper",s].filter(Boolean).join(" "),Y=[F,I,"loaded-skeleton-bg",s].filter(Boolean).join(" ");return jsx(h.Provider,{value:true,children:r?jsx("div",{ref:b,className:J,"aria-hidden":"true",children:e}):cloneElement(e,{ref:b,className:Y,"aria-hidden":true})})}var B="react-loaded",xe="loaded",$=1;function G(e){return !!e&&typeof e=="object"&&!Array.isArray(e)}function U(e){if(!G(e))return {};let t={};for(let[n,o]of Object.entries(e))typeof o=="number"&&(t[n]=o);return t}function W(e){return G(e)&&e.v===$?U(e.counts):U(e)}function Ce(e){if(typeof localStorage>"u")return {};try{let t=localStorage.getItem(e);return t===null?{}:W(JSON.parse(t))}catch{return {}}}function H(e){if(typeof localStorage>"u")return;let t={v:$,counts:e};try{localStorage.setItem(B,JSON.stringify(t));}catch{}}function K(){if(typeof localStorage>"u")return {};try{let e=localStorage.getItem(B);if(e!==null)return W(JSON.parse(e));let t=Ce(xe);return Object.keys(t).length>0&&H(t),t}catch{return {}}}function Te(e){let n=K()[e];return typeof n=="number"?n:null}function Re(e,t){if(!(typeof localStorage>"u"))try{let n=K();n[e]=t,H(n);}catch{}}function _({storageKey:e,defaultCount:t=3,currentCount:n,loading:o,minCount:s=1,maxCount:a}){let[k,m]=useState(()=>A(t,s,a)),f=useRef(false);return w(()=>{if(!e)return;let u=Te(e);if(u===null)return;let l=A(u,s,a);m(i=>Object.is(i,l)?i:l);},[e,s,a]),useEffect(()=>{if(!o&&n!==void 0){let u=A(n,s,a);m(u),e&&Re(e,u);}},[o,n,e,s,a]),useEffect(()=>{S()&&!e&&!f.current&&(console.warn("[Loaded] SmartSkeletonList used without storageKey. The count will reset on remount. Add a storageKey to persist across sessions."),f.current=true);},[e]),k}function A(e,t,n){let o=Math.max(e,t);return n!==void 0&&(o=Math.min(o,n)),o}function Le({loading:e=false,items:t,renderItem:n,renderSkeleton:o,storageKey:s,defaultCount:a=3,minCount:k=1,maxCount:m,animate:f=true,seed:u,suppressRefWarning:l=false,keyExtractor:i=(r,d)=>d}){let r=_({storageKey:s,defaultCount:a,currentCount:t?.length,loading:e,minCount:k,maxCount:m});if(e){let d=new Array(r);for(let c=0;c<r;c+=1){let b=u===void 0?`${c}`:`${u}:${c}`;d[c]=jsx(N,{loading:true,element:o(c),animate:f,seed:b,suppressRefWarning:l},`skeleton-${c}`);}return jsx(Fragment,{children:d})}return !t||t.length===0?null:jsx(Fragment,{children:t.map((d,c)=>jsx(Fragment$1,{children:n(d,c)},i(d,c)))})}export{h as SkeletonContext,N as SmartSkeleton,Le as SmartSkeletonList,Z as useIsSkeletonMode,_ as usePersistedCount};//# sourceMappingURL=index.js.map
1
+ import {createContext,useContext,useRef,useState,useCallback,useEffect,cloneElement,Fragment as Fragment$1,useLayoutEffect,version}from'react';import {jsx,Fragment}from'react/jsx-runtime';var C=createContext(false);function de(){return useContext(C)}function x(){let e=globalThis,t=e.__REACT_LOADED_DEV__;if(typeof t=="boolean")return t;let n=e.__DEV__;if(typeof n=="boolean")return n;let o=globalThis.process,l=typeof o=="object"&&o!==null?o.env?.NODE_ENV:void 0;return typeof l=="string"?l!=="production":false}var fe=typeof globalThis<"u"&&typeof globalThis.document<"u",y=fe?useLayoutEffect:useEffect;var be=6,ge=40;function X(e){if(!e||typeof e!="object")return false;let t=e;return !(t.nodeType!==1||typeof t.tagName!="string"||typeof t.querySelectorAll!="function"||typeof Element<"u"&&!(e instanceof Element))}var H=new Set;function F(e){if(!X(e))return false;try{return e.querySelectorAll("*"),!0}catch{return false}}function L(e){if(F(e))return e;if(e&&typeof e=="object"&&"nativeElement"in e){let t=e.nativeElement;if(F(t))return t}return null}function Ee(e){let t=e.type;if(typeof t=="string")return `<${t}>`;if(typeof t=="function"){let n=t;return `<${n.displayName||n.name||"Unknown"}>`}if(typeof t=="object"&&t!==null){let n=t;return `<${n.displayName||n.name||"Unknown"}>`}return "<Unknown>"}var J=Number.parseInt(version,10),Se=Number.isFinite(J)&&J>=19;function ye(e){let n=e.props?.ref;if(n!==void 0)return n;if(Se)return;let o=e.ref;if(o!==void 0)return o}function he(e,t){e&&(typeof e=="function"?e(t):e.current=t);}var _=new Set(["IMG","VIDEO","CANVAS"]),I=new Set(["SVG"]),we=new Set(["BUTTON","INPUT","TEXTAREA","SELECT","A"]),Re="button,input,textarea,select,a,[role='button']",Te=new Set(["SCRIPT","STYLE","LINK","META","NOSCRIPT","TEMPLATE"]);function O(e){return e.tagName.toUpperCase()}function P(e,t=O(e)){return we.has(t)?true:e.getAttribute("role")==="button"}function Ce(e,t){return !!(e.closest(Re)&&!P(e,t))}function xe(e,t=O(e)){return !!(_.has(t)||I.has(t)||P(e,t)||e.childElementCount===0&&e.textContent?.trim())}function ve(e,t){let n=e.length,o=Math.max(4,.8*n),l=Ne(t)*o,r=n+2+l;return Math.max(be,Math.min(ge,r))}function Ne(e){if(!e)return 0;let t=2166136261;for(let o=0;o<e.length;o+=1)t^=e.charCodeAt(o),t=Math.imul(t,16777619);return (t>>>0)/4294967295*2-1}function Le(e){let t=globalThis.getComputedStyle(e).textAlign;return t==="center"?"center":t==="right"||t==="end"?"right":"left"}function A(e,t={}){let{animate:n=true,seed:o}=t,l=o==null?"loaded":String(o);if(!X(e))return;let r=e;r.classList.add("loaded-skeleton-mode"),n&&r.classList.add("loaded-animate"),r.classList.contains("loaded-skeleton-wrapper")||r.classList.add("loaded-skeleton-bg");let c=e.getElementsByTagName("*"),p=0,d=s=>{let i=O(s);if(Te.has(i)||!xe(s,i)||Ce(s,i))return;let a=s,f=s.textContent?.trim();if(s.childElementCount===0&&f&&!_.has(i)&&!I.has(i)&&!P(s,i)){a.classList.add("loaded-text-skeleton"),a.dataset.skeletonAlign=Le(a);let b=`${l}|${p}|${f??""}`;p+=1;let h=ve(f??"",b);a.style.setProperty("--skeleton-text-width",`${h}ch`);}else _.has(i)?a.classList.add("loaded-skeleton-media"):I.has(i)?(a.classList.add("loaded-skeleton-content"),a.classList.add("loaded-skeleton-svg")):(a.classList.add("loaded-skeleton-content"),a.setAttribute("tabindex","-1"));};d(e);for(let s of c)d(s);}function M({element:e,children:t,loading:n=false,animate:o=true,className:l="",seed:r,suppressRefWarning:E=false}){let c=useRef(false),p=useRef(false),d=useRef(null),s=useRef(null),i=useRef(false),[a,f]=useState(false),u=e.type,b=e.key??null,h=useRef(n),V=useRef(u),j=useRef(b),S=useCallback(()=>{s.current!==null&&(clearTimeout(s.current),s.current=null);},[]);useEffect(()=>{i.current=a;},[a]),y(()=>{let k=h.current,m=V.current,R=j.current;(k&&!n||(m!==u||R!==b))&&(c.current=false,p.current=false,d.current=null,S(),i.current&&f(false)),h.current=n,V.current=u,j.current=b;},[n,u,b,S]),useEffect(()=>()=>{S();},[S]);let U=ye(e),w=useCallback(k=>{if(!i.current&&(f(true),!E&&x())){let m=Ee(e);H.has(m)||(console.warn(k==="non-dom-ref"?`[SmartSkeleton] ${m} does not forward its ref to a DOM element. A wrapper <div> has been added automatically. Use forwardRef to avoid this.`:`[SmartSkeleton] ${m} does not accept a ref. A wrapper <div> has been added automatically. Use forwardRef to avoid this.`),H.add(m));}},[e,E]),B=useCallback(k=>{p.current=true,d.current=k;let m=L(k);m&&n&&!c.current&&(A(m,{animate:o,seed:r}),c.current=true),he(U,k);},[n,U,o,r]);if(y(()=>{if(!n||a)return;S();let k=d.current,m=L(k);if(m&&!c.current&&(A(m,{animate:o,seed:r}),c.current=true),p.current){k!==null&&!m&&w("non-dom-ref");return}return s.current=setTimeout(()=>{if(s.current=null,!n||i.current)return;let R=d.current,T=L(R);if(T&&!c.current&&(A(T,{animate:o,seed:r}),c.current=true),p.current){R!==null&&!T&&w("non-dom-ref");return}w("no-ref-call");},0),()=>{S();}},[n,a,o,r,w,S]),!n)return t??null;let re=e.props.className??"",$=["loaded-skeleton-mode",o&&"loaded-animate"].filter(Boolean).join(" "),se=[$,"loaded-skeleton-wrapper",l].filter(Boolean).join(" "),ae=[re,$,"loaded-skeleton-bg",l].filter(Boolean).join(" ");return jsx(C.Provider,{value:true,children:a?jsx("div",{ref:B,className:se,"aria-hidden":"true",children:e}):cloneElement(e,{ref:B,className:ae,"aria-hidden":true})})}var z="react-loaded",Ie="loaded",Q=1;function Z(e){return !!e&&typeof e=="object"&&!Array.isArray(e)}function q(e){if(!Z(e))return {};let t={};for(let[n,o]of Object.entries(e))typeof o=="number"&&(t[n]=o);return t}function ee(e){return Z(e)&&e.v===Q?q(e.counts):q(e)}function Oe(e){if(typeof localStorage>"u")return {};try{let t=localStorage.getItem(e);return t===null?{}:ee(JSON.parse(t))}catch{return {}}}function te(e){if(typeof localStorage>"u")return;let t={v:Q,counts:e};try{localStorage.setItem(z,JSON.stringify(t));}catch{}}function ne(){if(typeof localStorage>"u")return {};try{let e=localStorage.getItem(z);if(e!==null)return ee(JSON.parse(e));let t=Oe(Ie);return Object.keys(t).length>0&&te(t),t}catch{return {}}}function Pe(e){let n=ne()[e];return typeof n=="number"?n:null}function Me(e,t){if(!(typeof localStorage>"u"))try{let n=ne();n[e]=t,te(n);}catch{}}function D({storageKey:e,defaultCount:t=3,currentCount:n,loading:o,minCount:l=1,maxCount:r}){let[E,c]=useState(()=>W(t,l,r)),p=useRef(false);return y(()=>{if(!e)return;let d=Pe(e);if(d===null)return;let s=W(d,l,r);c(i=>Object.is(i,s)?i:s);},[e,l,r]),useEffect(()=>{if(!o&&n!==void 0){let d=W(n,l,r);c(d),e&&Me(e,d);}},[o,n,e,l,r]),useEffect(()=>{x()&&!e&&!p.current&&(console.warn("[Loaded] SmartSkeletonList used without storageKey. The count will reset on remount. Add a storageKey to persist across sessions."),p.current=true);},[e]),E}function W(e,t,n){let o=Math.max(e,t);return n!==void 0&&(o=Math.min(o,n)),o}function De({loading:e=false,items:t,renderItem:n,renderSkeleton:o,storageKey:l,defaultCount:r=3,minCount:E=1,maxCount:c,animate:p=true,seed:d,suppressRefWarning:s=false,keyExtractor:i=(a,f)=>f}){let a=D({storageKey:l,defaultCount:r,currentCount:t?.length,loading:e,minCount:E,maxCount:c});if(e){let f=new Array(a);for(let u=0;u<a;u+=1){let b=d===void 0?`${u}`:`${d}:${u}`;f[u]=jsx(M,{loading:true,element:o(u),animate:p,seed:b,suppressRefWarning:s},`skeleton-${u}`);}return jsx(Fragment,{children:f})}return !t||t.length===0?null:jsx(Fragment,{children:t.map((f,u)=>jsx(Fragment$1,{children:n(f,u)},i(f,u)))})}export{C as SkeletonContext,M as SmartSkeleton,De as SmartSkeletonList,de as useIsSkeletonMode,D as usePersistedCount};//# sourceMappingURL=index.js.map
2
2
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/components/SkeletonContext/SkeletonContext.tsx","../src/utils/isDevEnv.ts","../src/utils/useIsomorphicLayoutEffect.ts","../src/components/SmartSkeleton/SmartSkeleton.tsx","../src/hooks/usePersistedCount/usePersistedCount.ts","../src/components/SmartSkeletonList/SmartSkeletonList.tsx"],"names":["SkeletonContext","createContext","useIsSkeletonMode","useContext","isDevEnv","maybeGlobal","override","devFlag","maybeProcess","nodeEnv","canUseDOM","useIsomorphicLayoutEffect","useLayoutEffect","useEffect","TEXT_WIDTH_MIN_CH","TEXT_WIDTH_MAX_CH","isElement","value","maybeElement","warnedComponents","isUsableElement","resolveRefTarget","node","nativeElement","getElementDisplayName","element","type","fn","obj","getOriginalRef","propsRef","legacyRef","forwardRef","originalRef","MEDIA_ELEMENTS","SVG_ELEMENTS","INTERACTIVE_ELEMENTS","BUTTON_LIKE_SELECTOR","SKIPPED_TAGS","getTagName","el","isButtonLikeElement","tagName","isButtonLikeDescendant","isContentElement","calculateTextWidthCh","text","seedKey","textLength","jitterRange","jitter","deterministicJitter","width","hash","index","resolveTextAlign","align","applySkeletonClasses","rootElement","options","animate","seed","baseSeed","htmlRoot","descendants","textIndex","processElement","htmlEl","textContent","widthCh","SmartSkeleton","children","loading","className","suppressRefWarning","hasAppliedRef","useRef","refWasCalledRef","lastElementRef","previousElementTypeRef","previousElementKeyRef","needsWrapper","setNeedsWrapper","useState","elementType","elementKey","previousType","previousKey","refCallback","useCallback","target","displayName","existingClassName","baseClasses","wrapperClassName","mergedClassName","jsx","cloneElement","STORAGE_KEY","LEGACY_STORAGE_KEY","STORAGE_VERSION","isRecord","toNumberRecord","result","key","maybeNumber","parseStoredCounts","readStoredCountsFromKey","raw","writeStoredCounts","counts","payload","getStoredCounts","rawNew","legacyCounts","getStoredCount","setStoredCount","count","usePersistedCount","storageKey","defaultCount","currentCount","minCount","maxCount","setCount","clampCount","hasWarnedRef","stored","next","prev","newCount","min","max","SmartSkeletonList","items","renderItem","renderSkeleton","keyExtractor","_","skeletonCount","skeletons","itemSeed","Fragment","item"],"mappings":"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","file":"index.js","sourcesContent":["import { createContext, useContext } from \"react\";\n\nexport const SkeletonContext = createContext(false);\n\nexport function useIsSkeletonMode(): boolean {\n return useContext(SkeletonContext);\n}\n","export function isDevEnv(): boolean {\n const maybeGlobal = globalThis as unknown as Record<string, unknown>;\n\n // Manual override for environments where NODE_ENV isn't injected.\n // Example: `globalThis.__REACT_LOADED_DEV__ = true`.\n const override = maybeGlobal.__REACT_LOADED_DEV__;\n if (typeof override === \"boolean\") return override;\n\n // Common global used by some toolchains/runtimes.\n const devFlag = maybeGlobal.__DEV__;\n if (typeof devFlag === \"boolean\") return devFlag;\n\n const maybeProcess = (globalThis as unknown as { process?: unknown }).process;\n const nodeEnv =\n typeof maybeProcess === \"object\" && maybeProcess !== null\n ? (maybeProcess as { env?: { NODE_ENV?: unknown } }).env?.NODE_ENV\n : undefined;\n\n if (typeof nodeEnv === \"string\") {\n return nodeEnv !== \"production\";\n }\n\n // No environment detected — assume production (convention: opt-in to dev mode).\n return false;\n}\n","import { useEffect, useLayoutEffect } from \"react\";\n\nconst canUseDOM =\n typeof globalThis !== \"undefined\" &&\n typeof (globalThis as { document?: unknown }).document !== \"undefined\";\n\nexport const useIsomorphicLayoutEffect = canUseDOM\n ? useLayoutEffect\n : useEffect;\n","import {\n cloneElement,\n type ReactElement,\n type Ref,\n useCallback,\n useEffect,\n useRef,\n useState,\n} from \"react\";\nimport { isDevEnv } from \"../../utils/isDevEnv\";\nimport { useIsomorphicLayoutEffect } from \"../../utils/useIsomorphicLayoutEffect\";\nimport { SkeletonContext } from \"../SkeletonContext/SkeletonContext\";\nimport \"./SmartSkeleton.css\";\n\n// Text width configuration (in ch units)\nconst TEXT_WIDTH_MIN_CH = 6;\nconst TEXT_WIDTH_MAX_CH = 40;\n\nfunction isElement(value: unknown): value is Element {\n if (!value || typeof value !== \"object\") return false;\n const maybeElement = value as Element;\n // Must have nodeType 1 (Element) and a working querySelectorAll\n if (maybeElement.nodeType !== 1) return false;\n if (typeof maybeElement.tagName !== \"string\") return false;\n if (typeof maybeElement.querySelectorAll !== \"function\") return false;\n // Additional check: instanceof Element if available\n if (typeof Element !== \"undefined\" && !(value instanceof Element)) {\n return false;\n }\n return true;\n}\n\nconst warnedComponents = new Set<string>();\n\nfunction isUsableElement(value: unknown): value is Element {\n if (!isElement(value)) return false;\n // Test that querySelectorAll actually works\n try {\n (value as Element).querySelectorAll(\"*\");\n return true;\n } catch {\n return false;\n }\n}\n\nfunction resolveRefTarget(node: unknown): Element | null {\n if (isUsableElement(node)) return node;\n if (node && typeof node === \"object\" && \"nativeElement\" in node) {\n const nativeElement = (node as { nativeElement?: unknown }).nativeElement;\n if (isUsableElement(nativeElement)) return nativeElement;\n }\n return null;\n}\n\nfunction getElementDisplayName(element: ReactElement): string {\n const type = element.type;\n if (typeof type === \"string\") {\n return `<${type}>`;\n }\n if (typeof type === \"function\") {\n const fn = type as { displayName?: string; name?: string };\n return `<${fn.displayName || fn.name || \"Unknown\"}>`;\n }\n if (typeof type === \"object\" && type !== null) {\n const obj = type as { displayName?: string; name?: string };\n return `<${obj.displayName || obj.name || \"Unknown\"}>`;\n }\n return \"<Unknown>\";\n}\n\n/**\n * Get the original ref from the element, supporting both React 18 and React 19.\n * React 19: ref is a regular prop on element.props.ref\n * React 18: ref is on element.ref\n */\nfunction getOriginalRef(element: ReactElement): Ref<unknown> | undefined {\n // React 19 style (ref as prop)\n const propsRef = (element.props as { ref?: Ref<unknown> })?.ref;\n if (propsRef) return propsRef;\n\n // React 18 style\n const legacyRef = (element as ReactElement & { ref?: Ref<unknown> }).ref;\n if (legacyRef) return legacyRef;\n\n return undefined;\n}\n\n/**\n * Forward a ref value to the original ref (callback or object style).\n */\nfunction forwardRef(originalRef: Ref<unknown> | undefined, node: unknown) {\n if (!originalRef) return;\n if (typeof originalRef === \"function\") {\n originalRef(node);\n } else {\n (originalRef as React.MutableRefObject<unknown>).current = node;\n }\n}\n\nconst MEDIA_ELEMENTS = new Set([\"IMG\", \"VIDEO\", \"CANVAS\"]);\nconst SVG_ELEMENTS = new Set([\"SVG\"]);\n\nconst INTERACTIVE_ELEMENTS = new Set([\n \"BUTTON\",\n \"INPUT\",\n \"TEXTAREA\",\n \"SELECT\",\n \"A\",\n]);\n\nconst BUTTON_LIKE_SELECTOR = \"button,input,textarea,select,a,[role='button']\";\nconst SKIPPED_TAGS = new Set([\n \"SCRIPT\",\n \"STYLE\",\n \"LINK\",\n \"META\",\n \"NOSCRIPT\",\n \"TEMPLATE\",\n]);\n\nfunction getTagName(el: Element): string {\n return el.tagName.toUpperCase();\n}\n\nfunction isButtonLikeElement(el: Element, tagName = getTagName(el)): boolean {\n if (INTERACTIVE_ELEMENTS.has(tagName)) return true;\n const role = el.getAttribute(\"role\");\n return role === \"button\";\n}\n\nfunction isButtonLikeDescendant(el: Element, tagName: string): boolean {\n const closestButton = el.closest(BUTTON_LIKE_SELECTOR);\n return Boolean(closestButton && !isButtonLikeElement(el, tagName));\n}\n\nfunction isContentElement(el: Element, tagName = getTagName(el)): boolean {\n if (MEDIA_ELEMENTS.has(tagName)) return true;\n if (SVG_ELEMENTS.has(tagName)) return true;\n if (isButtonLikeElement(el, tagName)) return true;\n\n const isLeafNode = el.childElementCount === 0;\n\n // Text elements that are leaf nodes (no child elements, only text)\n if (isLeafNode && el.textContent?.trim()) return true;\n\n return false;\n}\n\n/**\n * Calculate text skeleton width in ch units based on text content.\n * Uses a deterministic jitter: widthCh = clamp(6, 40, len + 2 + jitter)\n */\nfunction calculateTextWidthCh(text: string, seedKey: string): number {\n const textLength = text.length;\n const jitterRange = Math.max(4, 0.8 * textLength);\n const jitter = deterministicJitter(seedKey) * jitterRange;\n const width = textLength + 2 + jitter;\n return Math.max(TEXT_WIDTH_MIN_CH, Math.min(TEXT_WIDTH_MAX_CH, width));\n}\n\nfunction deterministicJitter(seedKey: string): number {\n if (!seedKey) return 0;\n let hash = 2166136261;\n for (let index = 0; index < seedKey.length; index += 1) {\n hash ^= seedKey.charCodeAt(index);\n hash = Math.imul(hash, 16777619);\n }\n const normalized = (hash >>> 0) / 0xffffffff;\n return normalized * 2 - 1;\n}\n\nfunction resolveTextAlign(el: HTMLElement): \"left\" | \"center\" | \"right\" {\n const align = globalThis.getComputedStyle(el).textAlign;\n if (align === \"center\") return \"center\";\n if (align === \"right\" || align === \"end\") return \"right\";\n return \"left\";\n}\n\nexport function applySkeletonClasses(\n rootElement: Element,\n options: { animate?: boolean; seed?: string | number } = {},\n): void {\n const { animate = true, seed } = options;\n const baseSeed =\n seed === undefined || seed === null ? \"loaded\" : String(seed);\n\n if (!isElement(rootElement)) {\n return;\n }\n\n const htmlRoot = rootElement as HTMLElement;\n\n // Apply skeleton mode to the root element\n htmlRoot.classList.add(\"loaded-skeleton-mode\");\n\n if (animate) {\n htmlRoot.classList.add(\"loaded-animate\");\n }\n\n // Apply background class for standalone usage (when not used via SmartSkeleton JSX)\n // If element has loaded-skeleton-wrapper, CSS handles bg via > :first-child rule\n // If element already has loaded-skeleton-bg (from JSX), this is a no-op\n const isWrapper = htmlRoot.classList.contains(\"loaded-skeleton-wrapper\");\n if (!isWrapper) {\n htmlRoot.classList.add(\"loaded-skeleton-bg\");\n }\n\n // Only add specific classes where needed (text, media, content)\n const descendants = rootElement.getElementsByTagName(\"*\");\n\n let textIndex = 0;\n\n const processElement = (el: Element) => {\n const tagName = getTagName(el);\n if (SKIPPED_TAGS.has(tagName)) return;\n if (!isContentElement(el, tagName)) return;\n if (isButtonLikeDescendant(el, tagName)) return;\n\n const htmlEl = el as HTMLElement;\n const textContent = el.textContent?.trim();\n const isLeafWithText = el.childElementCount === 0 && textContent;\n\n if (\n isLeafWithText &&\n !MEDIA_ELEMENTS.has(tagName) &&\n !SVG_ELEMENTS.has(tagName) &&\n !isButtonLikeElement(el, tagName)\n ) {\n // Text elements: overlay bar with ch-based width\n htmlEl.classList.add(\"loaded-text-skeleton\");\n htmlEl.dataset.skeletonAlign = resolveTextAlign(htmlEl);\n const seedKey = `${baseSeed}|${textIndex}|${textContent ?? \"\"}`;\n textIndex += 1;\n const widthCh = calculateTextWidthCh(textContent ?? \"\", seedKey);\n htmlEl.style.setProperty(\"--skeleton-text-width\", `${widthCh}ch`);\n } else if (MEDIA_ELEMENTS.has(tagName)) {\n // Media elements\n htmlEl.classList.add(\"loaded-skeleton-media\");\n } else if (SVG_ELEMENTS.has(tagName)) {\n // SVG elements rendered as rounded content blocks\n htmlEl.classList.add(\"loaded-skeleton-content\");\n htmlEl.classList.add(\"loaded-skeleton-svg\");\n } else {\n // Interactive elements (buttons, inputs, etc.)\n htmlEl.classList.add(\"loaded-skeleton-content\");\n // Prevent keyboard focus / interaction while in skeleton mode.\n // aria-hidden does not remove elements from the tab order.\n htmlEl.setAttribute(\"tabindex\", \"-1\");\n }\n };\n\n processElement(rootElement);\n for (const el of descendants) {\n processElement(el);\n }\n}\n\nexport interface SmartSkeletonProps {\n /** The skeleton element with mock data, rendered when loading */\n element: ReactElement;\n /** The real content to render when not loading. If omitted, returns null when loading=false. */\n children?: ReactElement;\n /** Whether the skeleton is currently loading. Default: false */\n loading?: boolean;\n /** Enable shimmer animation. Default: true */\n animate?: boolean;\n /** Additional CSS class name */\n className?: string;\n /** Optional seed to stabilize skeleton text widths */\n seed?: string | number;\n /** Suppress warning when auto-wrapper is applied. Default: false */\n suppressRefWarning?: boolean;\n}\n\nexport function SmartSkeleton({\n element,\n children,\n loading = false,\n animate = true,\n className = \"\",\n seed,\n suppressRefWarning = false,\n}: SmartSkeletonProps): ReactElement | null {\n const hasAppliedRef = useRef(false);\n const refWasCalledRef = useRef(false);\n const lastElementRef = useRef<ReactElement | null>(null);\n const previousElementTypeRef = useRef<ReactElement[\"type\"] | null>(null);\n const previousElementKeyRef = useRef<ReactElement[\"key\"] | null>(null);\n const [needsWrapper, setNeedsWrapper] = useState(false);\n\n // Reset flags when loading changes or element changes\n if (!loading || lastElementRef.current !== element) {\n hasAppliedRef.current = false;\n refWasCalledRef.current = false;\n lastElementRef.current = element;\n }\n\n useEffect(() => {\n const elementType = element.type;\n const elementKey = element.key ?? null;\n const previousType = previousElementTypeRef.current;\n const previousKey = previousElementKeyRef.current;\n\n if (\n previousType !== null &&\n (previousType !== elementType || previousKey !== elementKey)\n ) {\n setNeedsWrapper(false);\n }\n\n previousElementTypeRef.current = elementType;\n previousElementKeyRef.current = elementKey;\n }, [element.type, element.key]);\n\n const originalRef = getOriginalRef(element);\n\n const refCallback = useCallback(\n (node: unknown) => {\n refWasCalledRef.current = true;\n\n const target = resolveRefTarget(node);\n\n // If we received a node but couldn't get a DOM element from it,\n // we need to use a wrapper\n if (node !== null && !target && !needsWrapper) {\n setNeedsWrapper(true);\n\n // Emit warning (deduplicated by component name)\n if (!suppressRefWarning && isDevEnv()) {\n const displayName = getElementDisplayName(element);\n if (!warnedComponents.has(displayName)) {\n console.warn(\n `[SmartSkeleton] ${displayName} does not forward its ref to a DOM element. ` +\n `A wrapper <div> has been added automatically. Use forwardRef to avoid this.`,\n );\n warnedComponents.add(displayName);\n }\n }\n return;\n }\n\n if (target && loading && !hasAppliedRef.current) {\n applySkeletonClasses(target, { animate, seed });\n hasAppliedRef.current = true;\n }\n\n // Forward ref to original element\n forwardRef(originalRef, node);\n },\n [\n loading,\n element,\n needsWrapper,\n suppressRefWarning,\n originalRef,\n animate,\n seed,\n ],\n );\n\n // Detect if ref was never called (component ignores ref entirely)\n // useLayoutEffect runs synchronously after DOM mutations but before paint\n useIsomorphicLayoutEffect(() => {\n if (!loading || needsWrapper) return;\n\n // At this point, React has already attempted to attach refs\n // If refWasCalledRef is still false, the component ignored the ref\n if (!refWasCalledRef.current) {\n setNeedsWrapper(true);\n\n // Emit warning\n if (!suppressRefWarning && isDevEnv()) {\n const displayName = getElementDisplayName(element);\n if (!warnedComponents.has(displayName)) {\n console.warn(\n `[SmartSkeleton] ${displayName} does not accept a ref. ` +\n `A wrapper <div> has been added automatically. Use forwardRef to avoid this.`,\n );\n warnedComponents.add(displayName);\n }\n }\n }\n }, [loading, needsWrapper, element, suppressRefWarning]);\n\n // Not loading: return children or null\n if (!loading) {\n return children ?? null;\n }\n\n // Build merged className for skeleton mode\n const elementProps = element.props as { className?: string };\n const existingClassName = elementProps.className ?? \"\";\n\n // Base classes for skeleton mode\n const baseClasses = [\"loaded-skeleton-mode\", animate && \"loaded-animate\"]\n .filter(Boolean)\n .join(\" \");\n\n // When wrapping: wrapper gets mode + wrapper marker (no bg - it goes on child via ref)\n const wrapperClassName = [baseClasses, \"loaded-skeleton-wrapper\", className]\n .filter(Boolean)\n .join(\" \");\n\n // When not wrapping: element gets mode + bg directly (for SSR)\n const mergedClassName = [\n existingClassName,\n baseClasses,\n \"loaded-skeleton-bg\",\n className,\n ]\n .filter(Boolean)\n .join(\" \");\n\n return (\n <SkeletonContext.Provider value={true}>\n {needsWrapper ? (\n <div ref={refCallback} className={wrapperClassName} aria-hidden=\"true\">\n {element}\n </div>\n ) : (\n cloneElement(element as ReactElement<Record<string, unknown>>, {\n ref: refCallback,\n className: mergedClassName,\n \"aria-hidden\": true,\n })\n )}\n </SkeletonContext.Provider>\n );\n}\n","import { useEffect, useRef, useState } from \"react\";\nimport { isDevEnv } from \"../../utils/isDevEnv\";\nimport { useIsomorphicLayoutEffect } from \"../../utils/useIsomorphicLayoutEffect\";\n\nconst STORAGE_KEY = \"react-loaded\";\nconst LEGACY_STORAGE_KEY = \"loaded\";\nconst STORAGE_VERSION = 1 as const;\n\ntype StoredCounts = Record<string, number>;\ntype StoredPayloadV1 = { v: typeof STORAGE_VERSION; counts: StoredCounts };\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return Boolean(value) && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction toNumberRecord(value: unknown): StoredCounts {\n if (!isRecord(value)) return {};\n const result: StoredCounts = {};\n for (const [key, maybeNumber] of Object.entries(value)) {\n if (typeof maybeNumber === \"number\") {\n result[key] = maybeNumber;\n }\n }\n return result;\n}\n\nfunction parseStoredCounts(value: unknown): StoredCounts {\n // Current schema: { v: 1, counts: Record<string, number> }\n if (isRecord(value) && value.v === STORAGE_VERSION) {\n return toNumberRecord(value.counts);\n }\n\n // Legacy schema: Record<string, number>\n return toNumberRecord(value);\n}\n\nfunction readStoredCountsFromKey(key: string): StoredCounts {\n if (typeof localStorage === \"undefined\") return {};\n try {\n const raw = localStorage.getItem(key);\n if (raw === null) return {};\n return parseStoredCounts(JSON.parse(raw));\n } catch {\n return {};\n }\n}\n\nfunction writeStoredCounts(counts: StoredCounts): void {\n if (typeof localStorage === \"undefined\") return;\n const payload: StoredPayloadV1 = { v: STORAGE_VERSION, counts };\n try {\n localStorage.setItem(STORAGE_KEY, JSON.stringify(payload));\n } catch {\n // Silently fail if localStorage is full or unavailable\n }\n}\n\nfunction getStoredCounts(): Record<string, number> {\n if (typeof localStorage === \"undefined\") return {};\n\n try {\n const rawNew = localStorage.getItem(STORAGE_KEY);\n if (rawNew !== null) {\n return parseStoredCounts(JSON.parse(rawNew));\n }\n\n // Backward compatibility: migrate legacy key once if present.\n const legacyCounts = readStoredCountsFromKey(LEGACY_STORAGE_KEY);\n if (Object.keys(legacyCounts).length > 0) {\n writeStoredCounts(legacyCounts);\n }\n return legacyCounts;\n } catch {\n return {};\n }\n}\n\nfunction getStoredCount(key: string): number | null {\n const counts = getStoredCounts();\n const value = counts[key];\n return typeof value === \"number\" ? value : null;\n}\n\nfunction setStoredCount(key: string, count: number): void {\n if (typeof localStorage === \"undefined\") return;\n\n try {\n const counts = getStoredCounts();\n counts[key] = count;\n writeStoredCounts(counts);\n } catch {\n // Silently fail if localStorage is full or unavailable\n }\n}\n\nexport interface UsePersistedCountOptions {\n storageKey?: string;\n defaultCount?: number;\n currentCount?: number;\n loading: boolean;\n minCount?: number;\n maxCount?: number;\n}\n\nexport function usePersistedCount({\n storageKey,\n defaultCount = 3,\n currentCount,\n loading,\n minCount = 1,\n maxCount,\n}: UsePersistedCountOptions): number {\n // Always start from the default to match SSR output, then (on the client)\n // sync to the persisted value in a layout effect before first paint.\n const [count, setCount] = useState<number>(() =>\n clampCount(defaultCount, minCount, maxCount),\n );\n\n const hasWarnedRef = useRef(false);\n\n useIsomorphicLayoutEffect(() => {\n if (!storageKey) return;\n const stored = getStoredCount(storageKey);\n if (stored === null) return;\n const next = clampCount(stored, minCount, maxCount);\n setCount((prev) => (Object.is(prev, next) ? prev : next));\n }, [storageKey, minCount, maxCount]);\n\n useEffect(() => {\n if (!loading && currentCount !== undefined) {\n const newCount = clampCount(currentCount, minCount, maxCount);\n setCount(newCount);\n\n if (storageKey) {\n setStoredCount(storageKey, newCount);\n }\n }\n }, [loading, currentCount, storageKey, minCount, maxCount]);\n\n useEffect(() => {\n if (isDevEnv() && !storageKey && !hasWarnedRef.current) {\n console.warn(\n \"[Loaded] SmartSkeletonList used without storageKey. \" +\n \"The count will reset on remount. Add a storageKey to persist across sessions.\",\n );\n hasWarnedRef.current = true;\n }\n }, [storageKey]);\n\n return count;\n}\n\nfunction clampCount(\n value: number,\n min: number,\n max: number | undefined,\n): number {\n let result = Math.max(value, min);\n if (max !== undefined) {\n result = Math.min(result, max);\n }\n return result;\n}\n","import { Fragment, type ReactElement } from \"react\";\nimport { usePersistedCount } from \"../../hooks/usePersistedCount/usePersistedCount\";\nimport { SmartSkeleton } from \"../SmartSkeleton/SmartSkeleton\";\n\nexport interface SmartSkeletonListProps<T> {\n /** Whether the list is currently loading. Default: false */\n loading?: boolean;\n /** The items to render. Pass undefined while loading. */\n items: T[] | undefined;\n /** Render function for each item when loaded */\n renderItem: (item: T, index: number) => ReactElement;\n /** Render function for skeleton placeholders */\n renderSkeleton: (index: number) => ReactElement;\n /** Key for localStorage persistence. Without it, count resets on remount. */\n storageKey?: string;\n /** Initial skeleton count before any data is known. Default: 3 */\n defaultCount?: number;\n /** Minimum skeletons to show. Default: 1 */\n minCount?: number;\n /** Maximum skeletons to show */\n maxCount?: number;\n /** Enable shimmer animation. Default: true */\n animate?: boolean;\n /** Optional seed to stabilize skeleton text widths */\n seed?: string | number;\n /** Suppress warning when auto-wrapper is applied. Default: false */\n suppressRefWarning?: boolean;\n /** Extract unique key for each item. Default: index */\n keyExtractor?: (item: T, index: number) => string | number;\n}\n\nexport function SmartSkeletonList<T>({\n loading = false,\n items,\n renderItem,\n renderSkeleton,\n storageKey,\n defaultCount = 3,\n minCount = 1,\n maxCount,\n animate = true,\n seed,\n suppressRefWarning = false,\n keyExtractor = (_, index) => index,\n}: SmartSkeletonListProps<T>): ReactElement | null {\n const skeletonCount = usePersistedCount({\n storageKey,\n defaultCount,\n currentCount: items?.length,\n loading,\n minCount,\n maxCount,\n });\n\n if (loading) {\n const skeletons = new Array(skeletonCount);\n for (let index = 0; index < skeletonCount; index += 1) {\n const itemSeed = seed === undefined ? `${index}` : `${seed}:${index}`;\n skeletons[index] = (\n <SmartSkeleton\n key={`skeleton-${index}`}\n loading={true}\n element={renderSkeleton(index)}\n animate={animate}\n seed={itemSeed}\n suppressRefWarning={suppressRefWarning}\n />\n );\n }\n return <>{skeletons}</>;\n }\n\n if (!items || items.length === 0) {\n return null;\n }\n\n return (\n <>\n {items.map((item, index) => (\n <Fragment key={keyExtractor(item, index)}>\n {renderItem(item, index)}\n </Fragment>\n ))}\n </>\n );\n}\n"]}
1
+ {"version":3,"sources":["../src/components/SkeletonContext/SkeletonContext.tsx","../src/utils/isDevEnv.ts","../src/utils/useIsomorphicLayoutEffect.ts","../src/components/SmartSkeleton/SmartSkeleton.tsx","../src/hooks/usePersistedCount/usePersistedCount.ts","../src/components/SmartSkeletonList/SmartSkeletonList.tsx"],"names":["SkeletonContext","createContext","useIsSkeletonMode","useContext","isDevEnv","maybeGlobal","override","devFlag","maybeProcess","nodeEnv","canUseDOM","useIsomorphicLayoutEffect","useLayoutEffect","useEffect","TEXT_WIDTH_MIN_CH","TEXT_WIDTH_MAX_CH","isElement","value","maybeElement","warnedComponents","isUsableElement","resolveRefTarget","node","nativeElement","getElementDisplayName","element","type","fn","obj","REACT_MAJOR_VERSION","reactVersion","IS_REACT_19_OR_NEWER","getOriginalRef","propsRef","legacyRef","forwardRef","originalRef","MEDIA_ELEMENTS","SVG_ELEMENTS","INTERACTIVE_ELEMENTS","BUTTON_LIKE_SELECTOR","SKIPPED_TAGS","getTagName","el","isButtonLikeElement","tagName","isButtonLikeDescendant","isContentElement","calculateTextWidthCh","text","seedKey","textLength","jitterRange","jitter","deterministicJitter","width","hash","index","resolveTextAlign","align","applySkeletonClasses","rootElement","options","animate","seed","baseSeed","htmlRoot","descendants","textIndex","processElement","htmlEl","textContent","widthCh","SmartSkeleton","children","loading","className","suppressRefWarning","hasAppliedRef","useRef","refWasCalledRef","lastRefNodeRef","deferredWrapperCheckTimeoutRef","needsWrapperRef","needsWrapper","setNeedsWrapper","useState","currentElementType","currentElementKey","previousLoadingRef","previousElementTypeRef","previousElementKeyRef","cancelDeferredWrapperCheck","useCallback","previousLoading","previousElementType","previousElementKey","enableWrapperWithWarning","reason","displayName","refCallback","target","delayedNode","delayedTarget","existingClassName","baseClasses","wrapperClassName","mergedClassName","jsx","cloneElement","STORAGE_KEY","LEGACY_STORAGE_KEY","STORAGE_VERSION","isRecord","toNumberRecord","result","key","maybeNumber","parseStoredCounts","readStoredCountsFromKey","raw","writeStoredCounts","counts","payload","getStoredCounts","rawNew","legacyCounts","getStoredCount","setStoredCount","count","usePersistedCount","storageKey","defaultCount","currentCount","minCount","maxCount","setCount","clampCount","hasWarnedRef","stored","next","prev","newCount","min","max","SmartSkeletonList","items","renderItem","renderSkeleton","keyExtractor","_","skeletonCount","skeletons","itemSeed","Fragment","item"],"mappings":"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","file":"index.js","sourcesContent":["import { createContext, useContext } from \"react\";\n\nexport const SkeletonContext = createContext(false);\n\nexport function useIsSkeletonMode(): boolean {\n return useContext(SkeletonContext);\n}\n","export function isDevEnv(): boolean {\n const maybeGlobal = globalThis as unknown as Record<string, unknown>;\n\n // Manual override for environments where NODE_ENV isn't injected.\n // Example: `globalThis.__REACT_LOADED_DEV__ = true`.\n const override = maybeGlobal.__REACT_LOADED_DEV__;\n if (typeof override === \"boolean\") return override;\n\n // Common global used by some toolchains/runtimes.\n const devFlag = maybeGlobal.__DEV__;\n if (typeof devFlag === \"boolean\") return devFlag;\n\n const maybeProcess = (globalThis as unknown as { process?: unknown }).process;\n const nodeEnv =\n typeof maybeProcess === \"object\" && maybeProcess !== null\n ? (maybeProcess as { env?: { NODE_ENV?: unknown } }).env?.NODE_ENV\n : undefined;\n\n if (typeof nodeEnv === \"string\") {\n return nodeEnv !== \"production\";\n }\n\n // No environment detected — assume production (convention: opt-in to dev mode).\n return false;\n}\n","import { useEffect, useLayoutEffect } from \"react\";\n\nconst canUseDOM =\n typeof globalThis !== \"undefined\" &&\n typeof (globalThis as { document?: unknown }).document !== \"undefined\";\n\nexport const useIsomorphicLayoutEffect = canUseDOM\n ? useLayoutEffect\n : useEffect;\n","import {\n cloneElement,\n type ReactElement,\n type Ref,\n version as reactVersion,\n useCallback,\n useEffect,\n useRef,\n useState,\n} from \"react\";\nimport { isDevEnv } from \"../../utils/isDevEnv\";\nimport { useIsomorphicLayoutEffect } from \"../../utils/useIsomorphicLayoutEffect\";\nimport { SkeletonContext } from \"../SkeletonContext/SkeletonContext\";\nimport \"./SmartSkeleton.css\";\n\n// Text width configuration (in ch units)\nconst TEXT_WIDTH_MIN_CH = 6;\nconst TEXT_WIDTH_MAX_CH = 40;\n\nfunction isElement(value: unknown): value is Element {\n if (!value || typeof value !== \"object\") return false;\n const maybeElement = value as Element;\n // Must have nodeType 1 (Element) and a working querySelectorAll\n if (maybeElement.nodeType !== 1) return false;\n if (typeof maybeElement.tagName !== \"string\") return false;\n if (typeof maybeElement.querySelectorAll !== \"function\") return false;\n // Additional check: instanceof Element if available\n if (typeof Element !== \"undefined\" && !(value instanceof Element)) {\n return false;\n }\n return true;\n}\n\nconst warnedComponents = new Set<string>();\n\nfunction isUsableElement(value: unknown): value is Element {\n if (!isElement(value)) return false;\n // Test that querySelectorAll actually works\n try {\n (value as Element).querySelectorAll(\"*\");\n return true;\n } catch {\n return false;\n }\n}\n\nfunction resolveRefTarget(node: unknown): Element | null {\n if (isUsableElement(node)) return node;\n if (node && typeof node === \"object\" && \"nativeElement\" in node) {\n const nativeElement = (node as { nativeElement?: unknown }).nativeElement;\n if (isUsableElement(nativeElement)) return nativeElement;\n }\n return null;\n}\n\nfunction getElementDisplayName(element: ReactElement): string {\n const type = element.type;\n if (typeof type === \"string\") {\n return `<${type}>`;\n }\n if (typeof type === \"function\") {\n const fn = type as { displayName?: string; name?: string };\n return `<${fn.displayName || fn.name || \"Unknown\"}>`;\n }\n if (typeof type === \"object\" && type !== null) {\n const obj = type as { displayName?: string; name?: string };\n return `<${obj.displayName || obj.name || \"Unknown\"}>`;\n }\n return \"<Unknown>\";\n}\n\nconst REACT_MAJOR_VERSION = Number.parseInt(reactVersion, 10);\nconst IS_REACT_19_OR_NEWER =\n Number.isFinite(REACT_MAJOR_VERSION) && REACT_MAJOR_VERSION >= 19;\n\n/**\n * Get the original ref from the element, supporting both React 18 and React 19.\n * React 19: ref is a regular prop on element.props.ref\n * React 18: ref is on element.ref\n */\nfunction getOriginalRef(element: ReactElement): Ref<unknown> | undefined {\n // React 19 style (ref as prop)\n const elementProps = element.props as { ref?: Ref<unknown> } | undefined;\n const propsRef = elementProps?.ref;\n if (propsRef !== undefined) return propsRef;\n\n // React 19+ warns on element.ref access; skip legacy fallback entirely.\n if (IS_REACT_19_OR_NEWER) return undefined;\n\n // React 18 style\n const legacyRef = (element as ReactElement & { ref?: Ref<unknown> }).ref;\n if (legacyRef !== undefined) return legacyRef;\n\n return undefined;\n}\n\n/**\n * Forward a ref value to the original ref (callback or object style).\n */\nfunction forwardRef(originalRef: Ref<unknown> | undefined, node: unknown) {\n if (!originalRef) return;\n if (typeof originalRef === \"function\") {\n originalRef(node);\n } else {\n (originalRef as React.MutableRefObject<unknown>).current = node;\n }\n}\n\nconst MEDIA_ELEMENTS = new Set([\"IMG\", \"VIDEO\", \"CANVAS\"]);\nconst SVG_ELEMENTS = new Set([\"SVG\"]);\n\nconst INTERACTIVE_ELEMENTS = new Set([\n \"BUTTON\",\n \"INPUT\",\n \"TEXTAREA\",\n \"SELECT\",\n \"A\",\n]);\n\nconst BUTTON_LIKE_SELECTOR = \"button,input,textarea,select,a,[role='button']\";\nconst SKIPPED_TAGS = new Set([\n \"SCRIPT\",\n \"STYLE\",\n \"LINK\",\n \"META\",\n \"NOSCRIPT\",\n \"TEMPLATE\",\n]);\n\nfunction getTagName(el: Element): string {\n return el.tagName.toUpperCase();\n}\n\nfunction isButtonLikeElement(el: Element, tagName = getTagName(el)): boolean {\n if (INTERACTIVE_ELEMENTS.has(tagName)) return true;\n const role = el.getAttribute(\"role\");\n return role === \"button\";\n}\n\nfunction isButtonLikeDescendant(el: Element, tagName: string): boolean {\n const closestButton = el.closest(BUTTON_LIKE_SELECTOR);\n return Boolean(closestButton && !isButtonLikeElement(el, tagName));\n}\n\nfunction isContentElement(el: Element, tagName = getTagName(el)): boolean {\n if (MEDIA_ELEMENTS.has(tagName)) return true;\n if (SVG_ELEMENTS.has(tagName)) return true;\n if (isButtonLikeElement(el, tagName)) return true;\n\n const isLeafNode = el.childElementCount === 0;\n\n // Text elements that are leaf nodes (no child elements, only text)\n if (isLeafNode && el.textContent?.trim()) return true;\n\n return false;\n}\n\n/**\n * Calculate text skeleton width in ch units based on text content.\n * Uses a deterministic jitter: widthCh = clamp(6, 40, len + 2 + jitter)\n */\nfunction calculateTextWidthCh(text: string, seedKey: string): number {\n const textLength = text.length;\n const jitterRange = Math.max(4, 0.8 * textLength);\n const jitter = deterministicJitter(seedKey) * jitterRange;\n const width = textLength + 2 + jitter;\n return Math.max(TEXT_WIDTH_MIN_CH, Math.min(TEXT_WIDTH_MAX_CH, width));\n}\n\nfunction deterministicJitter(seedKey: string): number {\n if (!seedKey) return 0;\n let hash = 2166136261;\n for (let index = 0; index < seedKey.length; index += 1) {\n hash ^= seedKey.charCodeAt(index);\n hash = Math.imul(hash, 16777619);\n }\n const normalized = (hash >>> 0) / 0xffffffff;\n return normalized * 2 - 1;\n}\n\nfunction resolveTextAlign(el: HTMLElement): \"left\" | \"center\" | \"right\" {\n const align = globalThis.getComputedStyle(el).textAlign;\n if (align === \"center\") return \"center\";\n if (align === \"right\" || align === \"end\") return \"right\";\n return \"left\";\n}\n\nexport function applySkeletonClasses(\n rootElement: Element,\n options: { animate?: boolean; seed?: string | number } = {},\n): void {\n const { animate = true, seed } = options;\n const baseSeed =\n seed === undefined || seed === null ? \"loaded\" : String(seed);\n\n if (!isElement(rootElement)) {\n return;\n }\n\n const htmlRoot = rootElement as HTMLElement;\n\n // Apply skeleton mode to the root element\n htmlRoot.classList.add(\"loaded-skeleton-mode\");\n\n if (animate) {\n htmlRoot.classList.add(\"loaded-animate\");\n }\n\n // Apply background class for standalone usage (when not used via SmartSkeleton JSX)\n // If element has loaded-skeleton-wrapper, CSS handles bg via > :first-child rule\n // If element already has loaded-skeleton-bg (from JSX), this is a no-op\n const isWrapper = htmlRoot.classList.contains(\"loaded-skeleton-wrapper\");\n if (!isWrapper) {\n htmlRoot.classList.add(\"loaded-skeleton-bg\");\n }\n\n // Only add specific classes where needed (text, media, content)\n const descendants = rootElement.getElementsByTagName(\"*\");\n\n let textIndex = 0;\n\n const processElement = (el: Element) => {\n const tagName = getTagName(el);\n if (SKIPPED_TAGS.has(tagName)) return;\n if (!isContentElement(el, tagName)) return;\n if (isButtonLikeDescendant(el, tagName)) return;\n\n const htmlEl = el as HTMLElement;\n const textContent = el.textContent?.trim();\n const isLeafWithText = el.childElementCount === 0 && textContent;\n\n if (\n isLeafWithText &&\n !MEDIA_ELEMENTS.has(tagName) &&\n !SVG_ELEMENTS.has(tagName) &&\n !isButtonLikeElement(el, tagName)\n ) {\n // Text elements: overlay bar with ch-based width\n htmlEl.classList.add(\"loaded-text-skeleton\");\n htmlEl.dataset.skeletonAlign = resolveTextAlign(htmlEl);\n const seedKey = `${baseSeed}|${textIndex}|${textContent ?? \"\"}`;\n textIndex += 1;\n const widthCh = calculateTextWidthCh(textContent ?? \"\", seedKey);\n htmlEl.style.setProperty(\"--skeleton-text-width\", `${widthCh}ch`);\n } else if (MEDIA_ELEMENTS.has(tagName)) {\n // Media elements\n htmlEl.classList.add(\"loaded-skeleton-media\");\n } else if (SVG_ELEMENTS.has(tagName)) {\n // SVG elements rendered as rounded content blocks\n htmlEl.classList.add(\"loaded-skeleton-content\");\n htmlEl.classList.add(\"loaded-skeleton-svg\");\n } else {\n // Interactive elements (buttons, inputs, etc.)\n htmlEl.classList.add(\"loaded-skeleton-content\");\n // Prevent keyboard focus / interaction while in skeleton mode.\n // aria-hidden does not remove elements from the tab order.\n htmlEl.setAttribute(\"tabindex\", \"-1\");\n }\n };\n\n processElement(rootElement);\n for (const el of descendants) {\n processElement(el);\n }\n}\n\nexport interface SmartSkeletonProps {\n /** The skeleton element with mock data, rendered when loading */\n element: ReactElement;\n /** The real content to render when not loading. If omitted, returns null when loading=false. */\n children?: ReactElement;\n /** Whether the skeleton is currently loading. Default: false */\n loading?: boolean;\n /** Enable shimmer animation. Default: true */\n animate?: boolean;\n /** Additional CSS class name */\n className?: string;\n /** Optional seed to stabilize skeleton text widths */\n seed?: string | number;\n /** Suppress warning when auto-wrapper is applied. Default: false */\n suppressRefWarning?: boolean;\n}\n\nexport function SmartSkeleton({\n element,\n children,\n loading = false,\n animate = true,\n className = \"\",\n seed,\n suppressRefWarning = false,\n}: SmartSkeletonProps): ReactElement | null {\n const hasAppliedRef = useRef(false);\n const refWasCalledRef = useRef(false);\n const lastRefNodeRef = useRef<unknown>(null);\n const deferredWrapperCheckTimeoutRef = useRef<ReturnType<\n typeof setTimeout\n > | null>(null);\n const needsWrapperRef = useRef(false);\n const [needsWrapper, setNeedsWrapper] = useState(false);\n const currentElementType = element.type;\n const currentElementKey = element.key ?? null;\n const previousLoadingRef = useRef(loading);\n const previousElementTypeRef =\n useRef<ReactElement[\"type\"]>(currentElementType);\n const previousElementKeyRef = useRef<ReactElement[\"key\"] | null>(\n currentElementKey,\n );\n\n const cancelDeferredWrapperCheck = useCallback(() => {\n if (deferredWrapperCheckTimeoutRef.current !== null) {\n clearTimeout(deferredWrapperCheckTimeoutRef.current);\n deferredWrapperCheckTimeoutRef.current = null;\n }\n }, []);\n\n useEffect(() => {\n needsWrapperRef.current = needsWrapper;\n }, [needsWrapper]);\n\n // Keep render pure: reset mutable tracking only after commit.\n useIsomorphicLayoutEffect(() => {\n const previousLoading = previousLoadingRef.current;\n const previousElementType = previousElementTypeRef.current;\n const previousElementKey = previousElementKeyRef.current;\n\n const didExitLoading = previousLoading && !loading;\n const hasElementIdentityChanged =\n previousElementType !== currentElementType ||\n previousElementKey !== currentElementKey;\n\n if (didExitLoading || hasElementIdentityChanged) {\n hasAppliedRef.current = false;\n refWasCalledRef.current = false;\n lastRefNodeRef.current = null;\n cancelDeferredWrapperCheck();\n\n if (needsWrapperRef.current) {\n setNeedsWrapper(false);\n }\n }\n\n previousLoadingRef.current = loading;\n previousElementTypeRef.current = currentElementType;\n previousElementKeyRef.current = currentElementKey;\n }, [\n loading,\n currentElementType,\n currentElementKey,\n cancelDeferredWrapperCheck,\n ]);\n\n useEffect(() => {\n return () => {\n cancelDeferredWrapperCheck();\n };\n }, [cancelDeferredWrapperCheck]);\n\n const originalRef = getOriginalRef(element);\n\n const enableWrapperWithWarning = useCallback(\n (reason: \"non-dom-ref\" | \"no-ref-call\") => {\n if (needsWrapperRef.current) return;\n setNeedsWrapper(true);\n\n if (!suppressRefWarning && isDevEnv()) {\n const displayName = getElementDisplayName(element);\n if (!warnedComponents.has(displayName)) {\n if (reason === \"non-dom-ref\") {\n console.warn(\n `[SmartSkeleton] ${displayName} does not forward its ref to a DOM element. ` +\n `A wrapper <div> has been added automatically. Use forwardRef to avoid this.`,\n );\n } else {\n console.warn(\n `[SmartSkeleton] ${displayName} does not accept a ref. ` +\n `A wrapper <div> has been added automatically. Use forwardRef to avoid this.`,\n );\n }\n warnedComponents.add(displayName);\n }\n }\n },\n [element, suppressRefWarning],\n );\n\n const refCallback = useCallback(\n (node: unknown) => {\n refWasCalledRef.current = true;\n lastRefNodeRef.current = node;\n\n const target = resolveRefTarget(node);\n\n if (target && loading && !hasAppliedRef.current) {\n applySkeletonClasses(target, { animate, seed });\n hasAppliedRef.current = true;\n }\n\n // Forward ref to original element\n forwardRef(originalRef, node);\n },\n [loading, originalRef, animate, seed],\n );\n\n // Decide wrapper fallback after commit to avoid eager false positives:\n // some environments attach refs slightly later in the same tick.\n useIsomorphicLayoutEffect(() => {\n if (!loading || needsWrapper) return;\n\n cancelDeferredWrapperCheck();\n\n const node = lastRefNodeRef.current;\n const target = resolveRefTarget(node);\n\n if (target && !hasAppliedRef.current) {\n applySkeletonClasses(target, { animate, seed });\n hasAppliedRef.current = true;\n }\n\n if (refWasCalledRef.current) {\n if (node !== null && !target) {\n enableWrapperWithWarning(\"non-dom-ref\");\n }\n return;\n }\n\n deferredWrapperCheckTimeoutRef.current = setTimeout(() => {\n deferredWrapperCheckTimeoutRef.current = null;\n\n if (!loading || needsWrapperRef.current) return;\n\n const delayedNode = lastRefNodeRef.current;\n const delayedTarget = resolveRefTarget(delayedNode);\n\n if (delayedTarget && !hasAppliedRef.current) {\n applySkeletonClasses(delayedTarget, { animate, seed });\n hasAppliedRef.current = true;\n }\n\n if (refWasCalledRef.current) {\n if (delayedNode !== null && !delayedTarget) {\n enableWrapperWithWarning(\"non-dom-ref\");\n }\n return;\n }\n\n enableWrapperWithWarning(\"no-ref-call\");\n }, 0);\n\n return () => {\n cancelDeferredWrapperCheck();\n };\n }, [\n loading,\n needsWrapper,\n animate,\n seed,\n enableWrapperWithWarning,\n cancelDeferredWrapperCheck,\n ]);\n\n // Not loading: return children or null\n if (!loading) {\n return children ?? null;\n }\n\n // Build merged className for skeleton mode\n const elementProps = element.props as { className?: string };\n const existingClassName = elementProps.className ?? \"\";\n\n // Base classes for skeleton mode\n const baseClasses = [\"loaded-skeleton-mode\", animate && \"loaded-animate\"]\n .filter(Boolean)\n .join(\" \");\n\n // When wrapping: wrapper gets mode + wrapper marker (no bg - it goes on child via ref)\n const wrapperClassName = [baseClasses, \"loaded-skeleton-wrapper\", className]\n .filter(Boolean)\n .join(\" \");\n\n // When not wrapping: element gets mode + bg directly (for SSR)\n const mergedClassName = [\n existingClassName,\n baseClasses,\n \"loaded-skeleton-bg\",\n className,\n ]\n .filter(Boolean)\n .join(\" \");\n\n return (\n <SkeletonContext.Provider value={true}>\n {needsWrapper ? (\n <div ref={refCallback} className={wrapperClassName} aria-hidden=\"true\">\n {element}\n </div>\n ) : (\n cloneElement(element as ReactElement<Record<string, unknown>>, {\n ref: refCallback,\n className: mergedClassName,\n \"aria-hidden\": true,\n })\n )}\n </SkeletonContext.Provider>\n );\n}\n","import { useEffect, useRef, useState } from \"react\";\nimport { isDevEnv } from \"../../utils/isDevEnv\";\nimport { useIsomorphicLayoutEffect } from \"../../utils/useIsomorphicLayoutEffect\";\n\nconst STORAGE_KEY = \"react-loaded\";\nconst LEGACY_STORAGE_KEY = \"loaded\";\nconst STORAGE_VERSION = 1 as const;\n\ntype StoredCounts = Record<string, number>;\ntype StoredPayloadV1 = { v: typeof STORAGE_VERSION; counts: StoredCounts };\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return Boolean(value) && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction toNumberRecord(value: unknown): StoredCounts {\n if (!isRecord(value)) return {};\n const result: StoredCounts = {};\n for (const [key, maybeNumber] of Object.entries(value)) {\n if (typeof maybeNumber === \"number\") {\n result[key] = maybeNumber;\n }\n }\n return result;\n}\n\nfunction parseStoredCounts(value: unknown): StoredCounts {\n // Current schema: { v: 1, counts: Record<string, number> }\n if (isRecord(value) && value.v === STORAGE_VERSION) {\n return toNumberRecord(value.counts);\n }\n\n // Legacy schema: Record<string, number>\n return toNumberRecord(value);\n}\n\nfunction readStoredCountsFromKey(key: string): StoredCounts {\n if (typeof localStorage === \"undefined\") return {};\n try {\n const raw = localStorage.getItem(key);\n if (raw === null) return {};\n return parseStoredCounts(JSON.parse(raw));\n } catch {\n return {};\n }\n}\n\nfunction writeStoredCounts(counts: StoredCounts): void {\n if (typeof localStorage === \"undefined\") return;\n const payload: StoredPayloadV1 = { v: STORAGE_VERSION, counts };\n try {\n localStorage.setItem(STORAGE_KEY, JSON.stringify(payload));\n } catch {\n // Silently fail if localStorage is full or unavailable\n }\n}\n\nfunction getStoredCounts(): Record<string, number> {\n if (typeof localStorage === \"undefined\") return {};\n\n try {\n const rawNew = localStorage.getItem(STORAGE_KEY);\n if (rawNew !== null) {\n return parseStoredCounts(JSON.parse(rawNew));\n }\n\n // Backward compatibility: migrate legacy key once if present.\n const legacyCounts = readStoredCountsFromKey(LEGACY_STORAGE_KEY);\n if (Object.keys(legacyCounts).length > 0) {\n writeStoredCounts(legacyCounts);\n }\n return legacyCounts;\n } catch {\n return {};\n }\n}\n\nfunction getStoredCount(key: string): number | null {\n const counts = getStoredCounts();\n const value = counts[key];\n return typeof value === \"number\" ? value : null;\n}\n\nfunction setStoredCount(key: string, count: number): void {\n if (typeof localStorage === \"undefined\") return;\n\n try {\n const counts = getStoredCounts();\n counts[key] = count;\n writeStoredCounts(counts);\n } catch {\n // Silently fail if localStorage is full or unavailable\n }\n}\n\nexport interface UsePersistedCountOptions {\n storageKey?: string;\n defaultCount?: number;\n currentCount?: number;\n loading: boolean;\n minCount?: number;\n maxCount?: number;\n}\n\nexport function usePersistedCount({\n storageKey,\n defaultCount = 3,\n currentCount,\n loading,\n minCount = 1,\n maxCount,\n}: UsePersistedCountOptions): number {\n // Always start from the default to match SSR output, then (on the client)\n // sync to the persisted value in a layout effect before first paint.\n const [count, setCount] = useState<number>(() =>\n clampCount(defaultCount, minCount, maxCount),\n );\n\n const hasWarnedRef = useRef(false);\n\n useIsomorphicLayoutEffect(() => {\n if (!storageKey) return;\n const stored = getStoredCount(storageKey);\n if (stored === null) return;\n const next = clampCount(stored, minCount, maxCount);\n setCount((prev) => (Object.is(prev, next) ? prev : next));\n }, [storageKey, minCount, maxCount]);\n\n useEffect(() => {\n if (!loading && currentCount !== undefined) {\n const newCount = clampCount(currentCount, minCount, maxCount);\n setCount(newCount);\n\n if (storageKey) {\n setStoredCount(storageKey, newCount);\n }\n }\n }, [loading, currentCount, storageKey, minCount, maxCount]);\n\n useEffect(() => {\n if (isDevEnv() && !storageKey && !hasWarnedRef.current) {\n console.warn(\n \"[Loaded] SmartSkeletonList used without storageKey. \" +\n \"The count will reset on remount. Add a storageKey to persist across sessions.\",\n );\n hasWarnedRef.current = true;\n }\n }, [storageKey]);\n\n return count;\n}\n\nfunction clampCount(\n value: number,\n min: number,\n max: number | undefined,\n): number {\n let result = Math.max(value, min);\n if (max !== undefined) {\n result = Math.min(result, max);\n }\n return result;\n}\n","import { Fragment, type ReactElement } from \"react\";\nimport { usePersistedCount } from \"../../hooks/usePersistedCount/usePersistedCount\";\nimport { SmartSkeleton } from \"../SmartSkeleton/SmartSkeleton\";\n\nexport interface SmartSkeletonListProps<T> {\n /** Whether the list is currently loading. Default: false */\n loading?: boolean;\n /** The items to render. Pass undefined while loading. */\n items: T[] | undefined;\n /** Render function for each item when loaded */\n renderItem: (item: T, index: number) => ReactElement;\n /** Render function for skeleton placeholders */\n renderSkeleton: (index: number) => ReactElement;\n /** Key for localStorage persistence. Without it, count resets on remount. */\n storageKey?: string;\n /** Initial skeleton count before any data is known. Default: 3 */\n defaultCount?: number;\n /** Minimum skeletons to show. Default: 1 */\n minCount?: number;\n /** Maximum skeletons to show */\n maxCount?: number;\n /** Enable shimmer animation. Default: true */\n animate?: boolean;\n /** Optional seed to stabilize skeleton text widths */\n seed?: string | number;\n /** Suppress warning when auto-wrapper is applied. Default: false */\n suppressRefWarning?: boolean;\n /** Extract unique key for each item. Default: index */\n keyExtractor?: (item: T, index: number) => string | number;\n}\n\nexport function SmartSkeletonList<T>({\n loading = false,\n items,\n renderItem,\n renderSkeleton,\n storageKey,\n defaultCount = 3,\n minCount = 1,\n maxCount,\n animate = true,\n seed,\n suppressRefWarning = false,\n keyExtractor = (_, index) => index,\n}: SmartSkeletonListProps<T>): ReactElement | null {\n const skeletonCount = usePersistedCount({\n storageKey,\n defaultCount,\n currentCount: items?.length,\n loading,\n minCount,\n maxCount,\n });\n\n if (loading) {\n const skeletons = new Array(skeletonCount);\n for (let index = 0; index < skeletonCount; index += 1) {\n const itemSeed = seed === undefined ? `${index}` : `${seed}:${index}`;\n skeletons[index] = (\n <SmartSkeleton\n key={`skeleton-${index}`}\n loading={true}\n element={renderSkeleton(index)}\n animate={animate}\n seed={itemSeed}\n suppressRefWarning={suppressRefWarning}\n />\n );\n }\n return <>{skeletons}</>;\n }\n\n if (!items || items.length === 0) {\n return null;\n }\n\n return (\n <>\n {items.map((item, index) => (\n <Fragment key={keyExtractor(item, index)}>\n {renderItem(item, index)}\n </Fragment>\n ))}\n </>\n );\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-loaded",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Zero-layout-shift skeleton screens for React",
5
5
  "homepage": "https://github.com/Pierre-LHOSTE/react-loaded#readme",
6
6
  "repository": {
@@ -51,13 +51,6 @@
51
51
  "devDependencies": {
52
52
  "@ant-design/icons": "^6.1.0",
53
53
  "@biomejs/biome": "^2.3.13",
54
- "@chromatic-com/storybook": "^5.0.0",
55
- "@storybook/addon-a11y": "^10.2.1",
56
- "@storybook/addon-docs": "^10.2.1",
57
- "@storybook/addon-onboarding": "^10.2.1",
58
- "@storybook/addon-vitest": "^10.2.1",
59
- "@storybook/react": "^10.2.1",
60
- "@storybook/react-vite": "^10.2.1",
61
54
  "@testing-library/jest-dom": "^6.9.0",
62
55
  "@testing-library/react": "^16.3.0",
63
56
  "@testing-library/user-event": "^14.6.1",
@@ -71,7 +64,6 @@
71
64
  "playwright": "^1.58.0",
72
65
  "react": "^19.0.0",
73
66
  "react-dom": "^19.0.0",
74
- "storybook": "^10.2.1",
75
67
  "tsup": "^8.0.0",
76
68
  "typescript": "^5.7.0",
77
69
  "vitest": "^4.0.18"
@@ -91,7 +83,12 @@
91
83
  "bench": "vitest bench --config vitest.bench.config.ts",
92
84
  "bench:baseline": "vitest bench --config vitest.bench.config.ts --outputJson .benchmarks/baseline.json",
93
85
  "bench:compare": "vitest bench --config vitest.bench.config.ts --compare .benchmarks/baseline.json",
94
- "storybook": "storybook dev -p 6006",
95
- "build-storybook": "storybook build"
86
+ "release:verify": "pnpm run check && pnpm run typecheck && pnpm run test && pnpm run build",
87
+ "publish:npm": "pnpm run release:verify && pnpm publish --access public --ignore-scripts",
88
+ "publish:npm:dry-run": "pnpm run release:verify && pnpm publish --access public --dry-run --ignore-scripts",
89
+ "publish:jsr": "pnpm run release:verify && pnpm dlx jsr publish",
90
+ "publish:jsr:dry-run": "pnpm run release:verify && pnpm dlx jsr publish --dry-run",
91
+ "publish:all": "pnpm run release:verify && pnpm publish --access public --ignore-scripts && pnpm dlx jsr publish",
92
+ "publish:all:dry-run": "pnpm run release:verify && pnpm publish --access public --dry-run --ignore-scripts && pnpm dlx jsr publish --dry-run"
96
93
  }
97
94
  }