react-loaded 0.1.2 → 0.2.0
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 +2 -0
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +1 -1
- package/dist/index.css.map +1 -1
- package/dist/index.d.cts +8 -3
- package/dist/index.d.ts +8 -3
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -110,6 +110,7 @@ Wraps a single component to display it in skeleton mode while loading.
|
|
|
110
110
|
| `children` | `ReactElement` | - | The real content when loaded. Returns `null` if omitted |
|
|
111
111
|
| `loading` | `boolean` | `false` | Whether to show the skeleton |
|
|
112
112
|
| `animate` | `boolean` | `true` | Enable shimmer animation |
|
|
113
|
+
| `variant` | `"filled" \| "ghost"` | `"filled"` | Skeleton background style (`ghost` disables wrapper/card background) |
|
|
113
114
|
| `className` | `string` | - | Additional CSS classes |
|
|
114
115
|
| `seed` | `string \| number` | - | Stable seed for text width randomness |
|
|
115
116
|
| `suppressRefWarning` | `boolean` | `false` | Suppress console warning when auto-wrapper is needed |
|
|
@@ -129,6 +130,7 @@ Renders a list with skeleton placeholders and optional count persistence.
|
|
|
129
130
|
| `minCount` | `number` | `1` | Minimum skeletons to display |
|
|
130
131
|
| `maxCount` | `number` | - | Maximum skeletons to display |
|
|
131
132
|
| `animate` | `boolean` | `true` | Enable shimmer animation |
|
|
133
|
+
| `variant` | `"filled" \| "ghost"` | `"filled"` | Skeleton background style for each list placeholder |
|
|
132
134
|
| `seed` | `string \| number` | - | Stable seed for text width randomness |
|
|
133
135
|
| `suppressRefWarning` | `boolean` | `false` | Suppress console warning when auto-wrapper is needed |
|
|
134
136
|
| `keyExtractor` | `(item: T, index: number) => string | number` | `index` | Extract unique key for each item |
|
package/dist/index.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
'use strict';var react=require('react'),jsxRuntime=require('react/jsx-runtime');var
|
|
1
|
+
'use strict';var react=require('react'),jsxRuntime=require('react/jsx-runtime');var C=react.createContext(false);function ke(){return react.useContext(C)}function T(){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 Ee=typeof globalThis<"u"&&typeof globalThis.document<"u",w=Ee?react.useLayoutEffect:react.useEffect;function F(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))}function I(e){if(!F(e))return false;try{return e.querySelectorAll("*"),!0}catch{return false}}var _=new Set(["IMG","VIDEO","CANVAS"]),A=new Set(["SVG"]),Se=new Set(["BUTTON","INPUT","TEXTAREA","SELECT","A"]),ye="button,input,textarea,select,a,[role='button']",he=new Set(["SCRIPT","STYLE","LINK","META","NOSCRIPT","TEMPLATE"]);function O(e){return e.tagName.toUpperCase()}function M(e,t=O(e)){return Se.has(t)?true:e.getAttribute("role")==="button"}function Re(e,t){return !!(e.closest(ye)&&!M(e,t))}function Ce(e,t=O(e)){return !!(_.has(t)||A.has(t)||M(e,t)||e.childElementCount===0&&e.textContent?.trim())}function Te(e,t){let n=e.length,o=Math.max(4,.8*n),s=we(t)*o,i=n+2+s;return Math.max(6,Math.min(40,i))}function we(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 xe(e){let t=globalThis.getComputedStyle(e).textAlign;return t==="center"?"center":t==="right"||t==="end"?"right":"left"}function P(e,t={}){let{animate:n=true,seed:o,variant:s="filled"}=t,i=o==null?"loaded":String(o);if(!F(e))return;let a=e;if(a.classList.add("loaded-skeleton-mode"),n?a.classList.add("loaded-animate"):a.classList.remove("loaded-animate"),!a.classList.contains("loaded-skeleton-wrapper"))s==="filled"?a.classList.add("loaded-skeleton-bg"):a.classList.remove("loaded-skeleton-bg");else {let r=a.firstElementChild;r&&s==="filled"?r.classList.add("loaded-skeleton-bg"):r&&r.classList.remove("loaded-skeleton-bg"),a.classList.remove("loaded-skeleton-bg");}let p=e.getElementsByTagName("*"),d=0,f=r=>{let c=O(r);if(he.has(c)||!Ce(r,c))return;let l=r;if(Re(r,c)){l.classList.add("loaded-skeleton-force-hide");return}let u=r.textContent?.trim();if(r.childElementCount===0&&u&&!_.has(c)&&!A.has(c)&&!M(r,c)){l.classList.add("loaded-text-skeleton"),l.dataset.skeletonAlign=xe(l);let y=`${i}|${d}|${u??""}`;d+=1;let h=Te(u??"",y);l.style.setProperty("--skeleton-text-width",`${h}ch`);}else _.has(c)?l.classList.add("loaded-skeleton-media"):A.has(c)?(l.classList.add("loaded-skeleton-content"),l.classList.add("loaded-skeleton-svg")):(l.classList.add("loaded-skeleton-content"),l.setAttribute("tabindex","-1"));};f(e);for(let r of p)f(r);}var J=Number.parseInt(react.version,10),Le=Number.isFinite(J)&&J>=19;function V(e){if(I(e))return e;if(e&&typeof e=="object"&&"nativeElement"in e){let t=e.nativeElement;if(I(t))return t}return null}function K(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 Y(e){let n=e.props?.ref;if(n!==void 0)return n;if(Le)return;let o=e.ref;if(o!==void 0)return o}function q(e,t){e&&(typeof e=="function"?e(t):e.current=t);}var z=new Set;function W({element:e,children:t,loading:n=false,animate:o=true,className:s="",seed:i,variant:a="filled",suppressRefWarning:g=false}){let p=e.type,d=e.key??null,f=react.useRef(false),r=react.useRef(false),c=react.useRef(null),l=react.useRef(false),[m,u]=react.useState(false),S=react.useRef(n),y=react.useRef(p),h=react.useRef(d),R=react.useCallback(k=>{l.current=k,u(k);},[]),j=Y(e),v=react.useCallback(k=>{if(l.current||R(true),!g&&T()){let b=K(e);z.has(b)||(console.warn(k==="non-dom-ref"?`[SmartSkeleton] ${b} does not forward its ref to a DOM element. A wrapper <div> has been added automatically. Use forwardRef to avoid this.`:`[SmartSkeleton] ${b} does not accept a ref. A wrapper <div> has been added automatically. Use forwardRef to avoid this.`),z.add(b));}},[e,g,R]),H=react.useCallback(k=>{r.current=true,c.current=k;let b=V(k);b&&n&&!f.current&&(P(b,{animate:o,seed:i,variant:a}),f.current=true),q(j,k);},[n,j,o,i,a]);if(w(()=>{let k=S.current,b=y.current,fe=h.current,G=k&&!n,X=b!==p||fe!==d;if(S.current=n,y.current=p,h.current=d,(G||X)&&(f.current=false,G?(r.current=false,c.current=null):X&&c.current===null&&(r.current=false),l.current)){R(false);return}if(!n||m)return;let L=c.current,N=V(L);if(N&&!f.current&&(P(N,{animate:o,seed:i,variant:a}),f.current=true),r.current){if(L!==null&&!N){v("non-dom-ref");return}if(L!==null)return;r.current=false;}v("no-ref-call");},[n,m,p,d,o,i,a,v,R]),!n)return t??null;let ie=e.props.className??"",$=["loaded-skeleton-mode",o&&"loaded-animate"].filter(Boolean).join(" "),de=a==="filled"?"loaded-skeleton-bg":"",ue=[$,"loaded-skeleton-wrapper",s].filter(Boolean).join(" "),ce=[ie,$,de,s].filter(Boolean).join(" ");return jsxRuntime.jsx(C.Provider,{value:true,children:m?jsxRuntime.jsx("div",{ref:H,className:ue,"aria-hidden":"true",children:e}):react.cloneElement(e,{ref:H,className:ce,"aria-hidden":true})})}var te="react-loaded",Oe="loaded",ne=1;function oe(e){return !!e&&typeof e=="object"&&!Array.isArray(e)}function ee(e){if(!oe(e))return {};let t={};for(let[n,o]of Object.entries(e))typeof o=="number"&&(t[n]=o);return t}function re(e){return oe(e)&&e.v===ne?ee(e.counts):ee(e)}function Me(e){if(typeof localStorage>"u")return {};try{let t=localStorage.getItem(e);return t===null?{}:re(JSON.parse(t))}catch{return {}}}function se(e){if(typeof localStorage>"u")return;let t={v:ne,counts:e};try{localStorage.setItem(te,JSON.stringify(t));}catch{}}function ae(){if(typeof localStorage>"u")return {};try{let e=localStorage.getItem(te);if(e!==null)return re(JSON.parse(e));let t=Me(Oe);return Object.keys(t).length>0&&se(t),t}catch{return {}}}function Pe(e){let n=ae()[e];return typeof n=="number"?n:null}function Ve(e,t){if(!(typeof localStorage>"u"))try{let n=ae();n[e]=t,se(n);}catch{}}function U({storageKey:e,defaultCount:t=3,currentCount:n,loading:o,minCount:s=1,maxCount:i}){let[a,g]=react.useState(()=>B(t,s,i)),p=react.useRef(false);return w(()=>{if(!e)return;let d=Pe(e);if(d===null)return;let f=B(d,s,i);g(r=>Object.is(r,f)?r:f);},[e,s,i]),react.useEffect(()=>{if(!o&&n!==void 0){let d=B(n,s,i);g(d),e&&Ve(e,d);}},[o,n,e,s,i]),react.useEffect(()=>{T()&&!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]),a}function B(e,t,n){let o=Math.max(e,t);return n!==void 0&&(o=Math.min(o,n)),o}function We({loading:e=false,items:t,renderItem:n,renderSkeleton:o,storageKey:s,defaultCount:i=3,minCount:a=1,maxCount:g,animate:p=true,seed:d,variant:f="filled",suppressRefWarning:r=false,keyExtractor:c=(l,m)=>m}){let l=U({storageKey:s,defaultCount:i,currentCount:t?.length,loading:e,minCount:a,maxCount:g});if(e){let m=new Array(l);for(let u=0;u<l;u+=1){let S=d===void 0?`${u}`:`${d}:${u}`;m[u]=jsxRuntime.jsx(W,{loading:true,element:o(u),animate:p,seed:S,variant:f,suppressRefWarning:r},`skeleton-${u}`);}return jsxRuntime.jsx(jsxRuntime.Fragment,{children:m})}return !t||t.length===0?null:jsxRuntime.jsx(jsxRuntime.Fragment,{children:t.map((m,u)=>jsxRuntime.jsx(react.Fragment,{children:n(m,u)},c(m,u)))})}exports.SkeletonContext=C;exports.SmartSkeleton=W;exports.SmartSkeletonList=We;exports.useIsSkeletonMode=ke;exports.usePersistedCount=U;//# sourceMappingURL=index.cjs.map
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/SkeletonContext/SkeletonContext.tsx","../src/utils/isDevEnv.ts","../src/utils/useIsomorphicLayoutEffect.ts","../src/components/SmartSkeleton/applySkeletonClasses.ts","../src/components/SmartSkeleton/refUtils.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","isElement","value","maybeElement","isUsableElement","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","REACT_MAJOR_VERSION","reactVersion","IS_REACT_19_OR_NEWER","resolveRefTarget","node","nativeElement","getElementDisplayName","element","type","fn","obj","getOriginalRef","propsRef","legacyRef","forwardRef","originalRef","warnedComponents","SmartSkeleton","children","loading","className","suppressRefWarning","currentElementType","currentElementKey","hasAppliedRef","useRef","refWasCalledRef","lastRefNodeRef","needsWrapperRef","needsWrapper","setNeedsWrapper","useState","previousLoadingRef","previousElementTypeRef","previousElementKeyRef","setWrapperState","useCallback","next","enableWrapperWithWarning","reason","displayName","refCallback","target","previousLoading","previousElementType","previousElementKey","didExitLoading","hasElementIdentityChanged","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","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\treturn useContext(SkeletonContext);\n}\n","export function isDevEnv(): boolean {\n\tconst maybeGlobal = globalThis as unknown as Record<string, unknown>;\n\n\t// Manual override for environments where NODE_ENV isn't injected.\n\t// Example: `globalThis.__REACT_LOADED_DEV__ = true`.\n\tconst override = maybeGlobal.__REACT_LOADED_DEV__;\n\tif (typeof override === \"boolean\") return override;\n\n\t// Common global used by some toolchains/runtimes.\n\tconst devFlag = maybeGlobal.__DEV__;\n\tif (typeof devFlag === \"boolean\") return devFlag;\n\n\tconst maybeProcess = (globalThis as unknown as { process?: unknown }).process;\n\tconst nodeEnv =\n\t\ttypeof maybeProcess === \"object\" && maybeProcess !== null\n\t\t\t? (maybeProcess as { env?: { NODE_ENV?: unknown } }).env?.NODE_ENV\n\t\t\t: undefined;\n\n\tif (typeof nodeEnv === \"string\") {\n\t\treturn nodeEnv !== \"production\";\n\t}\n\n\t// No environment detected — assume production (convention: opt-in to dev mode).\n\treturn false;\n}\n","import { useEffect, useLayoutEffect } from \"react\";\n\nconst canUseDOM =\n\ttypeof globalThis !== \"undefined\" &&\n\ttypeof (globalThis as { document?: unknown }).document !== \"undefined\";\n\nexport const useIsomorphicLayoutEffect = canUseDOM\n\t? useLayoutEffect\n\t: useEffect;\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\tif (!value || typeof value !== \"object\") return false;\n\tconst maybeElement = value as Element;\n\t// Must have nodeType 1 (Element) and a working querySelectorAll\n\tif (maybeElement.nodeType !== 1) return false;\n\tif (typeof maybeElement.tagName !== \"string\") return false;\n\tif (typeof maybeElement.querySelectorAll !== \"function\") return false;\n\t// Additional check: instanceof Element if available\n\tif (typeof Element !== \"undefined\" && !(value instanceof Element)) {\n\t\treturn false;\n\t}\n\treturn true;\n}\n\nexport function isUsableElement(value: unknown): value is Element {\n\tif (!isElement(value)) return false;\n\t// Test that querySelectorAll actually works\n\ttry {\n\t\t(value as Element).querySelectorAll(\"*\");\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nconst MEDIA_ELEMENTS = new Set([\"IMG\", \"VIDEO\", \"CANVAS\"]);\nconst SVG_ELEMENTS = new Set([\"SVG\"]);\n\nconst INTERACTIVE_ELEMENTS = new Set([\n\t\"BUTTON\",\n\t\"INPUT\",\n\t\"TEXTAREA\",\n\t\"SELECT\",\n\t\"A\",\n]);\n\nconst BUTTON_LIKE_SELECTOR = \"button,input,textarea,select,a,[role='button']\";\nconst SKIPPED_TAGS = new Set([\n\t\"SCRIPT\",\n\t\"STYLE\",\n\t\"LINK\",\n\t\"META\",\n\t\"NOSCRIPT\",\n\t\"TEMPLATE\",\n]);\n\nfunction getTagName(el: Element): string {\n\treturn el.tagName.toUpperCase();\n}\n\nfunction isButtonLikeElement(el: Element, tagName = getTagName(el)): boolean {\n\tif (INTERACTIVE_ELEMENTS.has(tagName)) return true;\n\tconst role = el.getAttribute(\"role\");\n\treturn role === \"button\";\n}\n\nfunction isButtonLikeDescendant(el: Element, tagName: string): boolean {\n\tconst closestButton = el.closest(BUTTON_LIKE_SELECTOR);\n\treturn Boolean(closestButton && !isButtonLikeElement(el, tagName));\n}\n\nfunction isContentElement(el: Element, tagName = getTagName(el)): boolean {\n\tif (MEDIA_ELEMENTS.has(tagName)) return true;\n\tif (SVG_ELEMENTS.has(tagName)) return true;\n\tif (isButtonLikeElement(el, tagName)) return true;\n\n\tconst isLeafNode = el.childElementCount === 0;\n\n\t// Text elements that are leaf nodes (no child elements, only text)\n\tif (isLeafNode && el.textContent?.trim()) return true;\n\n\treturn 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\tconst textLength = text.length;\n\tconst jitterRange = Math.max(4, 0.8 * textLength);\n\tconst jitter = deterministicJitter(seedKey) * jitterRange;\n\tconst width = textLength + 2 + jitter;\n\treturn Math.max(TEXT_WIDTH_MIN_CH, Math.min(TEXT_WIDTH_MAX_CH, width));\n}\n\nfunction deterministicJitter(seedKey: string): number {\n\tif (!seedKey) return 0;\n\tlet hash = 2166136261;\n\tfor (let index = 0; index < seedKey.length; index += 1) {\n\t\thash ^= seedKey.charCodeAt(index);\n\t\thash = Math.imul(hash, 16777619);\n\t}\n\tconst normalized = (hash >>> 0) / 0xffffffff;\n\treturn normalized * 2 - 1;\n}\n\nfunction resolveTextAlign(el: HTMLElement): \"left\" | \"center\" | \"right\" {\n\tconst align = globalThis.getComputedStyle(el).textAlign;\n\tif (align === \"center\") return \"center\";\n\tif (align === \"right\" || align === \"end\") return \"right\";\n\treturn \"left\";\n}\n\nexport function applySkeletonClasses(\n\trootElement: Element,\n\toptions: { animate?: boolean; seed?: string | number } = {},\n): void {\n\tconst { animate = true, seed } = options;\n\tconst baseSeed =\n\t\tseed === undefined || seed === null ? \"loaded\" : String(seed);\n\n\tif (!isElement(rootElement)) {\n\t\treturn;\n\t}\n\n\tconst htmlRoot = rootElement as HTMLElement;\n\n\t// Apply skeleton mode to the root element\n\thtmlRoot.classList.add(\"loaded-skeleton-mode\");\n\n\tif (animate) {\n\t\thtmlRoot.classList.add(\"loaded-animate\");\n\t}\n\n\t// Apply background class for standalone usage (when not used via SmartSkeleton JSX)\n\t// If element has loaded-skeleton-wrapper, CSS handles bg via > :first-child rule\n\t// If element already has loaded-skeleton-bg (from JSX), this is a no-op\n\tconst isWrapper = htmlRoot.classList.contains(\"loaded-skeleton-wrapper\");\n\tif (!isWrapper) {\n\t\thtmlRoot.classList.add(\"loaded-skeleton-bg\");\n\t}\n\n\t// Only add specific classes where needed (text, media, content)\n\tconst descendants = rootElement.getElementsByTagName(\"*\");\n\n\tlet textIndex = 0;\n\n\tconst processElement = (el: Element) => {\n\t\tconst tagName = getTagName(el);\n\t\tif (SKIPPED_TAGS.has(tagName)) return;\n\t\tif (!isContentElement(el, tagName)) return;\n\n\t\tconst htmlEl = el as HTMLElement;\n\t\tconst isInsideButtonLike = isButtonLikeDescendant(el, tagName);\n\t\tif (isInsideButtonLike) {\n\t\t\thtmlEl.classList.add(\"loaded-skeleton-force-hide\");\n\t\t\treturn;\n\t\t}\n\n\t\tconst textContent = el.textContent?.trim();\n\t\tconst isLeafWithText = el.childElementCount === 0 && textContent;\n\n\t\tif (\n\t\t\tisLeafWithText &&\n\t\t\t!MEDIA_ELEMENTS.has(tagName) &&\n\t\t\t!SVG_ELEMENTS.has(tagName) &&\n\t\t\t!isButtonLikeElement(el, tagName)\n\t\t) {\n\t\t\t// Text elements: overlay bar with ch-based width\n\t\t\thtmlEl.classList.add(\"loaded-text-skeleton\");\n\t\t\thtmlEl.dataset.skeletonAlign = resolveTextAlign(htmlEl);\n\t\t\tconst seedKey = `${baseSeed}|${textIndex}|${textContent ?? \"\"}`;\n\t\t\ttextIndex += 1;\n\t\t\tconst widthCh = calculateTextWidthCh(textContent ?? \"\", seedKey);\n\t\t\thtmlEl.style.setProperty(\"--skeleton-text-width\", `${widthCh}ch`);\n\t\t} else if (MEDIA_ELEMENTS.has(tagName)) {\n\t\t\t// Media elements\n\t\t\thtmlEl.classList.add(\"loaded-skeleton-media\");\n\t\t} else if (SVG_ELEMENTS.has(tagName)) {\n\t\t\t// SVG elements rendered as rounded content blocks\n\t\t\thtmlEl.classList.add(\"loaded-skeleton-content\");\n\t\t\thtmlEl.classList.add(\"loaded-skeleton-svg\");\n\t\t} else {\n\t\t\t// Interactive elements (buttons, inputs, etc.)\n\t\t\thtmlEl.classList.add(\"loaded-skeleton-content\");\n\t\t\t// Prevent keyboard focus / interaction while in skeleton mode.\n\t\t\t// aria-hidden does not remove elements from the tab order.\n\t\t\thtmlEl.setAttribute(\"tabindex\", \"-1\");\n\t\t}\n\t};\n\n\tprocessElement(rootElement);\n\tfor (const el of descendants) {\n\t\tprocessElement(el);\n\t}\n}\n","import { type ReactElement, type Ref, version as reactVersion } from \"react\";\nimport { isUsableElement } from \"./applySkeletonClasses\";\n\nexport const REACT_MAJOR_VERSION = Number.parseInt(reactVersion, 10);\nconst IS_REACT_19_OR_NEWER =\n\tNumber.isFinite(REACT_MAJOR_VERSION) && REACT_MAJOR_VERSION >= 19;\n\nexport function resolveRefTarget(node: unknown): Element | null {\n\tif (isUsableElement(node)) return node;\n\tif (node && typeof node === \"object\" && \"nativeElement\" in node) {\n\t\tconst nativeElement = (node as { nativeElement?: unknown }).nativeElement;\n\t\tif (isUsableElement(nativeElement)) return nativeElement;\n\t}\n\treturn null;\n}\n\nexport function getElementDisplayName(element: ReactElement): string {\n\tconst type = element.type;\n\tif (typeof type === \"string\") {\n\t\treturn `<${type}>`;\n\t}\n\tif (typeof type === \"function\") {\n\t\tconst fn = type as { displayName?: string; name?: string };\n\t\treturn `<${fn.displayName || fn.name || \"Unknown\"}>`;\n\t}\n\tif (typeof type === \"object\" && type !== null) {\n\t\tconst obj = type as { displayName?: string; name?: string };\n\t\treturn `<${obj.displayName || obj.name || \"Unknown\"}>`;\n\t}\n\treturn \"<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 */\nexport function getOriginalRef(\n\telement: ReactElement,\n): Ref<unknown> | undefined {\n\t// React 19 style (ref as prop)\n\tconst elementProps = element.props as { ref?: Ref<unknown> } | undefined;\n\tconst propsRef = elementProps?.ref;\n\tif (propsRef !== undefined) return propsRef;\n\n\t// React 19+ warns on element.ref access; skip legacy fallback entirely.\n\tif (IS_REACT_19_OR_NEWER) return undefined;\n\n\t// React 18 style\n\tconst legacyRef = (element as ReactElement & { ref?: Ref<unknown> }).ref;\n\tif (legacyRef !== undefined) return legacyRef;\n\n\treturn undefined;\n}\n\n/**\n * Forward a ref value to the original ref (callback or object style).\n */\nexport function forwardRef(\n\toriginalRef: Ref<unknown> | undefined,\n\tnode: unknown,\n) {\n\tif (!originalRef) return;\n\tif (typeof originalRef === \"function\") {\n\t\toriginalRef(node);\n\t} else {\n\t\t(originalRef as React.MutableRefObject<unknown>).current = node;\n\t}\n}\n","import {\n\tcloneElement,\n\ttype ReactElement,\n\tuseCallback,\n\tuseRef,\n\tuseState,\n} from \"react\";\nimport { isDevEnv } from \"../../utils/isDevEnv\";\nimport { useIsomorphicLayoutEffect } from \"../../utils/useIsomorphicLayoutEffect\";\nimport { SkeletonContext } from \"../SkeletonContext/SkeletonContext\";\nimport { applySkeletonClasses } from \"./applySkeletonClasses\";\nimport {\n\tforwardRef,\n\tgetElementDisplayName,\n\tgetOriginalRef,\n\tresolveRefTarget,\n} from \"./refUtils\";\nimport \"./SmartSkeleton.css\";\n\nexport { applySkeletonClasses } from \"./applySkeletonClasses\";\n\nconst warnedComponents = new Set<string>();\n\nexport interface SmartSkeletonProps {\n\t/** The skeleton element with mock data, rendered when loading */\n\telement: ReactElement;\n\t/** The real content to render when not loading. If omitted, returns null when loading=false. */\n\tchildren?: ReactElement;\n\t/** Whether the skeleton is currently loading. Default: false */\n\tloading?: boolean;\n\t/** Enable shimmer animation. Default: true */\n\tanimate?: boolean;\n\t/** Additional CSS class name */\n\tclassName?: string;\n\t/** Optional seed to stabilize skeleton text widths */\n\tseed?: string | number;\n\t/** Suppress warning when auto-wrapper is applied. Default: false */\n\tsuppressRefWarning?: boolean;\n}\n\nexport function SmartSkeleton({\n\telement,\n\tchildren,\n\tloading = false,\n\tanimate = true,\n\tclassName = \"\",\n\tseed,\n\tsuppressRefWarning = false,\n}: SmartSkeletonProps): ReactElement | null {\n\tconst currentElementType = element.type;\n\tconst currentElementKey = element.key ?? null;\n\tconst hasAppliedRef = useRef(false);\n\tconst refWasCalledRef = useRef(false);\n\tconst lastRefNodeRef = useRef<unknown>(null);\n\tconst needsWrapperRef = useRef(false);\n\tconst [needsWrapper, setNeedsWrapper] = useState(false);\n\tconst previousLoadingRef = useRef(loading);\n\tconst previousElementTypeRef =\n\t\tuseRef<ReactElement[\"type\"]>(currentElementType);\n\tconst previousElementKeyRef = useRef<ReactElement[\"key\"] | null>(\n\t\tcurrentElementKey,\n\t);\n\n\tconst setWrapperState = useCallback((next: boolean) => {\n\t\tneedsWrapperRef.current = next;\n\t\tsetNeedsWrapper(next);\n\t}, []);\n\n\tconst originalRef = getOriginalRef(element);\n\n\tconst enableWrapperWithWarning = useCallback(\n\t\t(reason: \"non-dom-ref\" | \"no-ref-call\") => {\n\t\t\tif (!needsWrapperRef.current) {\n\t\t\t\tsetWrapperState(true);\n\t\t\t}\n\n\t\t\tif (!suppressRefWarning && isDevEnv()) {\n\t\t\t\tconst displayName = getElementDisplayName(element);\n\t\t\t\tif (!warnedComponents.has(displayName)) {\n\t\t\t\t\tif (reason === \"non-dom-ref\") {\n\t\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t\t`[SmartSkeleton] ${displayName} does not forward its ref to a DOM element. ` +\n\t\t\t\t\t\t\t\t`A wrapper <div> has been added automatically. Use forwardRef to avoid this.`,\n\t\t\t\t\t\t);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t\t`[SmartSkeleton] ${displayName} does not accept a ref. ` +\n\t\t\t\t\t\t\t\t`A wrapper <div> has been added automatically. Use forwardRef to avoid this.`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\twarnedComponents.add(displayName);\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t[element, suppressRefWarning, setWrapperState],\n\t);\n\n\tconst refCallback = useCallback(\n\t\t(node: unknown) => {\n\t\t\trefWasCalledRef.current = true;\n\t\t\tlastRefNodeRef.current = node;\n\n\t\t\tconst target = resolveRefTarget(node);\n\n\t\t\tif (target && loading && !hasAppliedRef.current) {\n\t\t\t\tapplySkeletonClasses(target, { animate, seed });\n\t\t\t\thasAppliedRef.current = true;\n\t\t\t}\n\n\t\t\t// Forward ref to original element\n\t\t\tforwardRef(originalRef, node);\n\t\t},\n\t\t[loading, originalRef, animate, seed],\n\t);\n\n\t// Single layout effect: handles identity reset AND wrapper fallback decision.\n\t// Merged into one effect because React fires ref callbacks (during commit)\n\t// BEFORE layout effects. Having a separate reset effect would clear the ref\n\t// data that was just written by the current commit's ref callbacks.\n\tuseIsomorphicLayoutEffect(() => {\n\t\tconst previousLoading = previousLoadingRef.current;\n\t\tconst previousElementType = previousElementTypeRef.current;\n\t\tconst previousElementKey = previousElementKeyRef.current;\n\n\t\tconst didExitLoading = previousLoading && !loading;\n\t\tconst hasElementIdentityChanged =\n\t\t\tpreviousElementType !== currentElementType ||\n\t\t\tpreviousElementKey !== currentElementKey;\n\n\t\tpreviousLoadingRef.current = loading;\n\t\tpreviousElementTypeRef.current = currentElementType;\n\t\tpreviousElementKeyRef.current = currentElementKey;\n\n\t\tif (didExitLoading || hasElementIdentityChanged) {\n\t\t\thasAppliedRef.current = false;\n\t\t\tif (didExitLoading) {\n\t\t\t\trefWasCalledRef.current = false;\n\t\t\t\tlastRefNodeRef.current = null;\n\t\t\t} else if (hasElementIdentityChanged && lastRefNodeRef.current === null) {\n\t\t\t\t// Ignore cleanup-only ref callbacks from previous element identity.\n\t\t\t\trefWasCalledRef.current = false;\n\t\t\t}\n\n\t\t\tif (needsWrapperRef.current) {\n\t\t\t\tsetWrapperState(false);\n\t\t\t\t// Re-render will re-run this effect with the direct path.\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tif (!loading) return;\n\t\tif (needsWrapper) return;\n\n\t\tconst node = lastRefNodeRef.current;\n\t\tconst target = resolveRefTarget(node);\n\n\t\tif (target && !hasAppliedRef.current) {\n\t\t\tapplySkeletonClasses(target, { animate, seed });\n\t\t\thasAppliedRef.current = true;\n\t\t}\n\n\t\tif (refWasCalledRef.current) {\n\t\t\tif (node !== null && !target) {\n\t\t\t\tenableWrapperWithWarning(\"non-dom-ref\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (node !== null) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// Ref callback was only invoked with null during cleanup.\n\t\t\t// Treat as a missing ref call for the current element.\n\t\t\trefWasCalledRef.current = false;\n\t\t}\n\n\t\t// Ref was not called: the component doesn't accept refs.\n\t\t// Switch to wrapper mode synchronously (before browser paint).\n\t\tenableWrapperWithWarning(\"no-ref-call\");\n\t}, [\n\t\tloading,\n\t\tneedsWrapper,\n\t\tcurrentElementType,\n\t\tcurrentElementKey,\n\t\tanimate,\n\t\tseed,\n\t\tenableWrapperWithWarning,\n\t\tsetWrapperState,\n\t]);\n\n\t// Not loading: return children or null\n\tif (!loading) {\n\t\treturn children ?? null;\n\t}\n\n\t// Build merged className for skeleton mode\n\tconst elementProps = element.props as { className?: string };\n\tconst existingClassName = elementProps.className ?? \"\";\n\n\t// Base classes for skeleton mode\n\tconst baseClasses = [\"loaded-skeleton-mode\", animate && \"loaded-animate\"]\n\t\t.filter(Boolean)\n\t\t.join(\" \");\n\n\t// When wrapping: wrapper gets mode + wrapper marker (no bg - it goes on child via ref)\n\tconst wrapperClassName = [baseClasses, \"loaded-skeleton-wrapper\", className]\n\t\t.filter(Boolean)\n\t\t.join(\" \");\n\n\t// When not wrapping: element gets mode + bg directly (for SSR)\n\tconst mergedClassName = [\n\t\texistingClassName,\n\t\tbaseClasses,\n\t\t\"loaded-skeleton-bg\",\n\t\tclassName,\n\t]\n\t\t.filter(Boolean)\n\t\t.join(\" \");\n\n\treturn (\n\t\t<SkeletonContext.Provider value={true}>\n\t\t\t{needsWrapper ? (\n\t\t\t\t<div ref={refCallback} className={wrapperClassName} aria-hidden=\"true\">\n\t\t\t\t\t{element}\n\t\t\t\t</div>\n\t\t\t) : (\n\t\t\t\tcloneElement(element as ReactElement<Record<string, unknown>>, {\n\t\t\t\t\tref: refCallback,\n\t\t\t\t\tclassName: mergedClassName,\n\t\t\t\t\t\"aria-hidden\": true,\n\t\t\t\t})\n\t\t\t)}\n\t\t</SkeletonContext.Provider>\n\t);\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\treturn Boolean(value) && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction toNumberRecord(value: unknown): StoredCounts {\n\tif (!isRecord(value)) return {};\n\tconst result: StoredCounts = {};\n\tfor (const [key, maybeNumber] of Object.entries(value)) {\n\t\tif (typeof maybeNumber === \"number\") {\n\t\t\tresult[key] = maybeNumber;\n\t\t}\n\t}\n\treturn result;\n}\n\nfunction parseStoredCounts(value: unknown): StoredCounts {\n\t// Current schema: { v: 1, counts: Record<string, number> }\n\tif (isRecord(value) && value.v === STORAGE_VERSION) {\n\t\treturn toNumberRecord(value.counts);\n\t}\n\n\t// Legacy schema: Record<string, number>\n\treturn toNumberRecord(value);\n}\n\nfunction readStoredCountsFromKey(key: string): StoredCounts {\n\tif (typeof localStorage === \"undefined\") return {};\n\ttry {\n\t\tconst raw = localStorage.getItem(key);\n\t\tif (raw === null) return {};\n\t\treturn parseStoredCounts(JSON.parse(raw));\n\t} catch {\n\t\treturn {};\n\t}\n}\n\nfunction writeStoredCounts(counts: StoredCounts): void {\n\tif (typeof localStorage === \"undefined\") return;\n\tconst payload: StoredPayloadV1 = { v: STORAGE_VERSION, counts };\n\ttry {\n\t\tlocalStorage.setItem(STORAGE_KEY, JSON.stringify(payload));\n\t} catch {\n\t\t// Silently fail if localStorage is full or unavailable\n\t}\n}\n\nfunction getStoredCounts(): Record<string, number> {\n\tif (typeof localStorage === \"undefined\") return {};\n\n\ttry {\n\t\tconst rawNew = localStorage.getItem(STORAGE_KEY);\n\t\tif (rawNew !== null) {\n\t\t\treturn parseStoredCounts(JSON.parse(rawNew));\n\t\t}\n\n\t\t// Backward compatibility: migrate legacy key once if present.\n\t\tconst legacyCounts = readStoredCountsFromKey(LEGACY_STORAGE_KEY);\n\t\tif (Object.keys(legacyCounts).length > 0) {\n\t\t\twriteStoredCounts(legacyCounts);\n\t\t}\n\t\treturn legacyCounts;\n\t} catch {\n\t\treturn {};\n\t}\n}\n\nfunction getStoredCount(key: string): number | null {\n\tconst counts = getStoredCounts();\n\tconst value = counts[key];\n\treturn typeof value === \"number\" ? value : null;\n}\n\nfunction setStoredCount(key: string, count: number): void {\n\tif (typeof localStorage === \"undefined\") return;\n\n\ttry {\n\t\tconst counts = getStoredCounts();\n\t\tcounts[key] = count;\n\t\twriteStoredCounts(counts);\n\t} catch {\n\t\t// Silently fail if localStorage is full or unavailable\n\t}\n}\n\nexport interface UsePersistedCountOptions {\n\tstorageKey?: string;\n\tdefaultCount?: number;\n\tcurrentCount?: number;\n\tloading: boolean;\n\tminCount?: number;\n\tmaxCount?: number;\n}\n\nexport function usePersistedCount({\n\tstorageKey,\n\tdefaultCount = 3,\n\tcurrentCount,\n\tloading,\n\tminCount = 1,\n\tmaxCount,\n}: UsePersistedCountOptions): number {\n\t// Always start from the default to match SSR output, then (on the client)\n\t// sync to the persisted value in a layout effect before first paint.\n\tconst [count, setCount] = useState<number>(() =>\n\t\tclampCount(defaultCount, minCount, maxCount),\n\t);\n\n\tconst hasWarnedRef = useRef(false);\n\n\tuseIsomorphicLayoutEffect(() => {\n\t\tif (!storageKey) return;\n\t\tconst stored = getStoredCount(storageKey);\n\t\tif (stored === null) return;\n\t\tconst next = clampCount(stored, minCount, maxCount);\n\t\tsetCount((prev) => (Object.is(prev, next) ? prev : next));\n\t}, [storageKey, minCount, maxCount]);\n\n\tuseEffect(() => {\n\t\tif (!loading && currentCount !== undefined) {\n\t\t\tconst newCount = clampCount(currentCount, minCount, maxCount);\n\t\t\tsetCount(newCount);\n\n\t\t\tif (storageKey) {\n\t\t\t\tsetStoredCount(storageKey, newCount);\n\t\t\t}\n\t\t}\n\t}, [loading, currentCount, storageKey, minCount, maxCount]);\n\n\tuseEffect(() => {\n\t\tif (isDevEnv() && !storageKey && !hasWarnedRef.current) {\n\t\t\tconsole.warn(\n\t\t\t\t\"[Loaded] SmartSkeletonList used without storageKey. \" +\n\t\t\t\t\t\"The count will reset on remount. Add a storageKey to persist across sessions.\",\n\t\t\t);\n\t\t\thasWarnedRef.current = true;\n\t\t}\n\t}, [storageKey]);\n\n\treturn count;\n}\n\nfunction clampCount(\n\tvalue: number,\n\tmin: number,\n\tmax: number | undefined,\n): number {\n\tlet result = Math.max(value, min);\n\tif (max !== undefined) {\n\t\tresult = Math.min(result, max);\n\t}\n\treturn 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\t/** Whether the list is currently loading. Default: false */\n\tloading?: boolean;\n\t/** The items to render. Pass undefined while loading. */\n\titems: T[] | undefined;\n\t/** Render function for each item when loaded */\n\trenderItem: (item: T, index: number) => ReactElement;\n\t/** Render function for skeleton placeholders */\n\trenderSkeleton: (index: number) => ReactElement;\n\t/** Key for localStorage persistence. Without it, count resets on remount. */\n\tstorageKey?: string;\n\t/** Initial skeleton count before any data is known. Default: 3 */\n\tdefaultCount?: number;\n\t/** Minimum skeletons to show. Default: 1 */\n\tminCount?: number;\n\t/** Maximum skeletons to show */\n\tmaxCount?: number;\n\t/** Enable shimmer animation. Default: true */\n\tanimate?: boolean;\n\t/** Optional seed to stabilize skeleton text widths */\n\tseed?: string | number;\n\t/** Suppress warning when auto-wrapper is applied. Default: false */\n\tsuppressRefWarning?: boolean;\n\t/** Extract unique key for each item. Default: index */\n\tkeyExtractor?: (item: T, index: number) => string | number;\n}\n\nexport function SmartSkeletonList<T>({\n\tloading = false,\n\titems,\n\trenderItem,\n\trenderSkeleton,\n\tstorageKey,\n\tdefaultCount = 3,\n\tminCount = 1,\n\tmaxCount,\n\tanimate = true,\n\tseed,\n\tsuppressRefWarning = false,\n\tkeyExtractor = (_, index) => index,\n}: SmartSkeletonListProps<T>): ReactElement | null {\n\tconst skeletonCount = usePersistedCount({\n\t\tstorageKey,\n\t\tdefaultCount,\n\t\tcurrentCount: items?.length,\n\t\tloading,\n\t\tminCount,\n\t\tmaxCount,\n\t});\n\n\tif (loading) {\n\t\tconst skeletons = new Array(skeletonCount);\n\t\tfor (let index = 0; index < skeletonCount; index += 1) {\n\t\t\tconst itemSeed = seed === undefined ? `${index}` : `${seed}:${index}`;\n\t\t\tskeletons[index] = (\n\t\t\t\t<SmartSkeleton\n\t\t\t\t\tkey={`skeleton-${index}`}\n\t\t\t\t\tloading={true}\n\t\t\t\t\telement={renderSkeleton(index)}\n\t\t\t\t\tanimate={animate}\n\t\t\t\t\tseed={itemSeed}\n\t\t\t\t\tsuppressRefWarning={suppressRefWarning}\n\t\t\t\t/>\n\t\t\t);\n\t\t}\n\t\treturn <>{skeletons}</>;\n\t}\n\n\tif (!items || items.length === 0) {\n\t\treturn null;\n\t}\n\n\treturn (\n\t\t<>\n\t\t\t{items.map((item, index) => (\n\t\t\t\t<Fragment key={keyExtractor(item, index)}>\n\t\t\t\t\t{renderItem(item, index)}\n\t\t\t\t</Fragment>\n\t\t\t))}\n\t\t</>\n\t);\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/components/SkeletonContext/SkeletonContext.tsx","../src/utils/isDevEnv.ts","../src/utils/useIsomorphicLayoutEffect.ts","../src/components/SmartSkeleton/applySkeletonClasses.ts","../src/components/SmartSkeleton/refUtils.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","isElement","value","maybeElement","isUsableElement","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","variant","baseSeed","htmlRoot","firstChild","descendants","textIndex","processElement","htmlEl","textContent","widthCh","REACT_MAJOR_VERSION","reactVersion","IS_REACT_19_OR_NEWER","resolveRefTarget","node","nativeElement","getElementDisplayName","element","type","fn","obj","getOriginalRef","propsRef","legacyRef","forwardRef","originalRef","warnedComponents","SmartSkeleton","children","loading","className","suppressRefWarning","currentElementType","currentElementKey","hasAppliedRef","useRef","refWasCalledRef","lastRefNodeRef","needsWrapperRef","needsWrapper","setNeedsWrapper","useState","previousLoadingRef","previousElementTypeRef","previousElementKeyRef","setWrapperState","useCallback","next","enableWrapperWithWarning","reason","displayName","refCallback","target","previousLoading","previousElementType","previousElementKey","didExitLoading","hasElementIdentityChanged","existingClassName","baseClasses","filledOnlyClass","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","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\treturn useContext(SkeletonContext);\n}\n","export function isDevEnv(): boolean {\n\tconst maybeGlobal = globalThis as unknown as Record<string, unknown>;\n\n\t// Manual override for environments where NODE_ENV isn't injected.\n\t// Example: `globalThis.__REACT_LOADED_DEV__ = true`.\n\tconst override = maybeGlobal.__REACT_LOADED_DEV__;\n\tif (typeof override === \"boolean\") return override;\n\n\t// Common global used by some toolchains/runtimes.\n\tconst devFlag = maybeGlobal.__DEV__;\n\tif (typeof devFlag === \"boolean\") return devFlag;\n\n\tconst maybeProcess = (globalThis as unknown as { process?: unknown }).process;\n\tconst nodeEnv =\n\t\ttypeof maybeProcess === \"object\" && maybeProcess !== null\n\t\t\t? (maybeProcess as { env?: { NODE_ENV?: unknown } }).env?.NODE_ENV\n\t\t\t: undefined;\n\n\tif (typeof nodeEnv === \"string\") {\n\t\treturn nodeEnv !== \"production\";\n\t}\n\n\t// No environment detected — assume production (convention: opt-in to dev mode).\n\treturn false;\n}\n","import { useEffect, useLayoutEffect } from \"react\";\n\nconst canUseDOM =\n\ttypeof globalThis !== \"undefined\" &&\n\ttypeof (globalThis as { document?: unknown }).document !== \"undefined\";\n\nexport const useIsomorphicLayoutEffect = canUseDOM\n\t? useLayoutEffect\n\t: useEffect;\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\tif (!value || typeof value !== \"object\") return false;\n\tconst maybeElement = value as Element;\n\t// Must have nodeType 1 (Element) and a working querySelectorAll\n\tif (maybeElement.nodeType !== 1) return false;\n\tif (typeof maybeElement.tagName !== \"string\") return false;\n\tif (typeof maybeElement.querySelectorAll !== \"function\") return false;\n\t// Additional check: instanceof Element if available\n\tif (typeof Element !== \"undefined\" && !(value instanceof Element)) {\n\t\treturn false;\n\t}\n\treturn true;\n}\n\nexport function isUsableElement(value: unknown): value is Element {\n\tif (!isElement(value)) return false;\n\t// Test that querySelectorAll actually works\n\ttry {\n\t\t(value as Element).querySelectorAll(\"*\");\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nconst MEDIA_ELEMENTS = new Set([\"IMG\", \"VIDEO\", \"CANVAS\"]);\nconst SVG_ELEMENTS = new Set([\"SVG\"]);\n\nconst INTERACTIVE_ELEMENTS = new Set([\n\t\"BUTTON\",\n\t\"INPUT\",\n\t\"TEXTAREA\",\n\t\"SELECT\",\n\t\"A\",\n]);\n\nconst BUTTON_LIKE_SELECTOR = \"button,input,textarea,select,a,[role='button']\";\nconst SKIPPED_TAGS = new Set([\n\t\"SCRIPT\",\n\t\"STYLE\",\n\t\"LINK\",\n\t\"META\",\n\t\"NOSCRIPT\",\n\t\"TEMPLATE\",\n]);\n\nfunction getTagName(el: Element): string {\n\treturn el.tagName.toUpperCase();\n}\n\nfunction isButtonLikeElement(el: Element, tagName = getTagName(el)): boolean {\n\tif (INTERACTIVE_ELEMENTS.has(tagName)) return true;\n\tconst role = el.getAttribute(\"role\");\n\treturn role === \"button\";\n}\n\nfunction isButtonLikeDescendant(el: Element, tagName: string): boolean {\n\tconst closestButton = el.closest(BUTTON_LIKE_SELECTOR);\n\treturn Boolean(closestButton && !isButtonLikeElement(el, tagName));\n}\n\nfunction isContentElement(el: Element, tagName = getTagName(el)): boolean {\n\tif (MEDIA_ELEMENTS.has(tagName)) return true;\n\tif (SVG_ELEMENTS.has(tagName)) return true;\n\tif (isButtonLikeElement(el, tagName)) return true;\n\n\tconst isLeafNode = el.childElementCount === 0;\n\n\t// Text elements that are leaf nodes (no child elements, only text)\n\tif (isLeafNode && el.textContent?.trim()) return true;\n\n\treturn 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\tconst textLength = text.length;\n\tconst jitterRange = Math.max(4, 0.8 * textLength);\n\tconst jitter = deterministicJitter(seedKey) * jitterRange;\n\tconst width = textLength + 2 + jitter;\n\treturn Math.max(TEXT_WIDTH_MIN_CH, Math.min(TEXT_WIDTH_MAX_CH, width));\n}\n\nfunction deterministicJitter(seedKey: string): number {\n\tif (!seedKey) return 0;\n\tlet hash = 2166136261;\n\tfor (let index = 0; index < seedKey.length; index += 1) {\n\t\thash ^= seedKey.charCodeAt(index);\n\t\thash = Math.imul(hash, 16777619);\n\t}\n\tconst normalized = (hash >>> 0) / 0xffffffff;\n\treturn normalized * 2 - 1;\n}\n\nfunction resolveTextAlign(el: HTMLElement): \"left\" | \"center\" | \"right\" {\n\tconst align = globalThis.getComputedStyle(el).textAlign;\n\tif (align === \"center\") return \"center\";\n\tif (align === \"right\" || align === \"end\") return \"right\";\n\treturn \"left\";\n}\n\nexport function applySkeletonClasses(\n\trootElement: Element,\n\toptions: {\n\t\tanimate?: boolean;\n\t\tseed?: string | number;\n\t\tvariant?: \"filled\" | \"ghost\";\n\t} = {},\n): void {\n\tconst { animate = true, seed, variant = \"filled\" } = options;\n\tconst baseSeed =\n\t\tseed === undefined || seed === null ? \"loaded\" : String(seed);\n\n\tif (!isElement(rootElement)) {\n\t\treturn;\n\t}\n\n\tconst htmlRoot = rootElement as HTMLElement;\n\n\t// Apply skeleton mode to the root element\n\thtmlRoot.classList.add(\"loaded-skeleton-mode\");\n\n\tif (animate) {\n\t\thtmlRoot.classList.add(\"loaded-animate\");\n\t} else {\n\t\thtmlRoot.classList.remove(\"loaded-animate\");\n\t}\n\n\t// Apply background class for standalone usage.\n\t// Wrapper mode targets first child to preserve border-radius from user content.\n\tconst isWrapper = htmlRoot.classList.contains(\"loaded-skeleton-wrapper\");\n\tif (!isWrapper) {\n\t\tif (variant === \"filled\") {\n\t\t\thtmlRoot.classList.add(\"loaded-skeleton-bg\");\n\t\t} else {\n\t\t\thtmlRoot.classList.remove(\"loaded-skeleton-bg\");\n\t\t}\n\t} else {\n\t\tconst firstChild = htmlRoot.firstElementChild as HTMLElement | null;\n\t\tif (firstChild && variant === \"filled\") {\n\t\t\tfirstChild.classList.add(\"loaded-skeleton-bg\");\n\t\t} else if (firstChild) {\n\t\t\tfirstChild.classList.remove(\"loaded-skeleton-bg\");\n\t\t}\n\t\thtmlRoot.classList.remove(\"loaded-skeleton-bg\");\n\t}\n\n\t// Only add specific classes where needed (text, media, content)\n\tconst descendants = rootElement.getElementsByTagName(\"*\");\n\n\tlet textIndex = 0;\n\n\tconst processElement = (el: Element) => {\n\t\tconst tagName = getTagName(el);\n\t\tif (SKIPPED_TAGS.has(tagName)) return;\n\t\tif (!isContentElement(el, tagName)) return;\n\n\t\tconst htmlEl = el as HTMLElement;\n\t\tconst isInsideButtonLike = isButtonLikeDescendant(el, tagName);\n\t\tif (isInsideButtonLike) {\n\t\t\thtmlEl.classList.add(\"loaded-skeleton-force-hide\");\n\t\t\treturn;\n\t\t}\n\n\t\tconst textContent = el.textContent?.trim();\n\t\tconst isLeafWithText = el.childElementCount === 0 && textContent;\n\n\t\tif (\n\t\t\tisLeafWithText &&\n\t\t\t!MEDIA_ELEMENTS.has(tagName) &&\n\t\t\t!SVG_ELEMENTS.has(tagName) &&\n\t\t\t!isButtonLikeElement(el, tagName)\n\t\t) {\n\t\t\t// Text elements: overlay bar with ch-based width\n\t\t\thtmlEl.classList.add(\"loaded-text-skeleton\");\n\t\t\thtmlEl.dataset.skeletonAlign = resolveTextAlign(htmlEl);\n\t\t\tconst seedKey = `${baseSeed}|${textIndex}|${textContent ?? \"\"}`;\n\t\t\ttextIndex += 1;\n\t\t\tconst widthCh = calculateTextWidthCh(textContent ?? \"\", seedKey);\n\t\t\thtmlEl.style.setProperty(\"--skeleton-text-width\", `${widthCh}ch`);\n\t\t} else if (MEDIA_ELEMENTS.has(tagName)) {\n\t\t\t// Media elements\n\t\t\thtmlEl.classList.add(\"loaded-skeleton-media\");\n\t\t} else if (SVG_ELEMENTS.has(tagName)) {\n\t\t\t// SVG elements rendered as rounded content blocks\n\t\t\thtmlEl.classList.add(\"loaded-skeleton-content\");\n\t\t\thtmlEl.classList.add(\"loaded-skeleton-svg\");\n\t\t} else {\n\t\t\t// Interactive elements (buttons, inputs, etc.)\n\t\t\thtmlEl.classList.add(\"loaded-skeleton-content\");\n\t\t\t// Prevent keyboard focus / interaction while in skeleton mode.\n\t\t\t// aria-hidden does not remove elements from the tab order.\n\t\t\thtmlEl.setAttribute(\"tabindex\", \"-1\");\n\t\t}\n\t};\n\n\tprocessElement(rootElement);\n\tfor (const el of descendants) {\n\t\tprocessElement(el);\n\t}\n}\n","import { type ReactElement, type Ref, version as reactVersion } from \"react\";\nimport { isUsableElement } from \"./applySkeletonClasses\";\n\nexport const REACT_MAJOR_VERSION = Number.parseInt(reactVersion, 10);\nconst IS_REACT_19_OR_NEWER =\n\tNumber.isFinite(REACT_MAJOR_VERSION) && REACT_MAJOR_VERSION >= 19;\n\nexport function resolveRefTarget(node: unknown): Element | null {\n\tif (isUsableElement(node)) return node;\n\tif (node && typeof node === \"object\" && \"nativeElement\" in node) {\n\t\tconst nativeElement = (node as { nativeElement?: unknown }).nativeElement;\n\t\tif (isUsableElement(nativeElement)) return nativeElement;\n\t}\n\treturn null;\n}\n\nexport function getElementDisplayName(element: ReactElement): string {\n\tconst type = element.type;\n\tif (typeof type === \"string\") {\n\t\treturn `<${type}>`;\n\t}\n\tif (typeof type === \"function\") {\n\t\tconst fn = type as { displayName?: string; name?: string };\n\t\treturn `<${fn.displayName || fn.name || \"Unknown\"}>`;\n\t}\n\tif (typeof type === \"object\" && type !== null) {\n\t\tconst obj = type as { displayName?: string; name?: string };\n\t\treturn `<${obj.displayName || obj.name || \"Unknown\"}>`;\n\t}\n\treturn \"<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 */\nexport function getOriginalRef(\n\telement: ReactElement,\n): Ref<unknown> | undefined {\n\t// React 19 style (ref as prop)\n\tconst elementProps = element.props as { ref?: Ref<unknown> } | undefined;\n\tconst propsRef = elementProps?.ref;\n\tif (propsRef !== undefined) return propsRef;\n\n\t// React 19+ warns on element.ref access; skip legacy fallback entirely.\n\tif (IS_REACT_19_OR_NEWER) return undefined;\n\n\t// React 18 style\n\tconst legacyRef = (element as ReactElement & { ref?: Ref<unknown> }).ref;\n\tif (legacyRef !== undefined) return legacyRef;\n\n\treturn undefined;\n}\n\n/**\n * Forward a ref value to the original ref (callback or object style).\n */\nexport function forwardRef(\n\toriginalRef: Ref<unknown> | undefined,\n\tnode: unknown,\n) {\n\tif (!originalRef) return;\n\tif (typeof originalRef === \"function\") {\n\t\toriginalRef(node);\n\t} else {\n\t\t(originalRef as React.MutableRefObject<unknown>).current = node;\n\t}\n}\n","import {\n\tcloneElement,\n\ttype ReactElement,\n\tuseCallback,\n\tuseRef,\n\tuseState,\n} from \"react\";\nimport { isDevEnv } from \"../../utils/isDevEnv\";\nimport { useIsomorphicLayoutEffect } from \"../../utils/useIsomorphicLayoutEffect\";\nimport { SkeletonContext } from \"../SkeletonContext/SkeletonContext\";\nimport { applySkeletonClasses } from \"./applySkeletonClasses\";\nimport {\n\tforwardRef,\n\tgetElementDisplayName,\n\tgetOriginalRef,\n\tresolveRefTarget,\n} from \"./refUtils\";\nimport \"./SmartSkeleton.css\";\n\nexport { applySkeletonClasses } from \"./applySkeletonClasses\";\n\nconst warnedComponents = new Set<string>();\n\nexport type SmartSkeletonVariant = \"filled\" | \"ghost\";\n\nexport interface SmartSkeletonProps {\n\t/** The skeleton element with mock data, rendered when loading */\n\telement: ReactElement;\n\t/** The real content to render when not loading. If omitted, returns null when loading=false. */\n\tchildren?: ReactElement;\n\t/** Whether the skeleton is currently loading. Default: false */\n\tloading?: boolean;\n\t/** Enable shimmer animation. Default: true */\n\tanimate?: boolean;\n\t/** Additional CSS class name */\n\tclassName?: string;\n\t/** Optional seed to stabilize skeleton text widths */\n\tseed?: string | number;\n\t/** Visual variant for skeleton surface. `filled` adds a background, `ghost` does not. Default: \"filled\" */\n\tvariant?: SmartSkeletonVariant;\n\t/** Suppress warning when auto-wrapper is applied. Default: false */\n\tsuppressRefWarning?: boolean;\n}\n\nexport function SmartSkeleton({\n\telement,\n\tchildren,\n\tloading = false,\n\tanimate = true,\n\tclassName = \"\",\n\tseed,\n\tvariant = \"filled\",\n\tsuppressRefWarning = false,\n}: SmartSkeletonProps): ReactElement | null {\n\tconst currentElementType = element.type;\n\tconst currentElementKey = element.key ?? null;\n\tconst hasAppliedRef = useRef(false);\n\tconst refWasCalledRef = useRef(false);\n\tconst lastRefNodeRef = useRef<unknown>(null);\n\tconst needsWrapperRef = useRef(false);\n\tconst [needsWrapper, setNeedsWrapper] = useState(false);\n\tconst previousLoadingRef = useRef(loading);\n\tconst previousElementTypeRef =\n\t\tuseRef<ReactElement[\"type\"]>(currentElementType);\n\tconst previousElementKeyRef = useRef<ReactElement[\"key\"] | null>(\n\t\tcurrentElementKey,\n\t);\n\n\tconst setWrapperState = useCallback((next: boolean) => {\n\t\tneedsWrapperRef.current = next;\n\t\tsetNeedsWrapper(next);\n\t}, []);\n\n\tconst originalRef = getOriginalRef(element);\n\n\tconst enableWrapperWithWarning = useCallback(\n\t\t(reason: \"non-dom-ref\" | \"no-ref-call\") => {\n\t\t\tif (!needsWrapperRef.current) {\n\t\t\t\tsetWrapperState(true);\n\t\t\t}\n\n\t\t\tif (!suppressRefWarning && isDevEnv()) {\n\t\t\t\tconst displayName = getElementDisplayName(element);\n\t\t\t\tif (!warnedComponents.has(displayName)) {\n\t\t\t\t\tif (reason === \"non-dom-ref\") {\n\t\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t\t`[SmartSkeleton] ${displayName} does not forward its ref to a DOM element. ` +\n\t\t\t\t\t\t\t\t`A wrapper <div> has been added automatically. Use forwardRef to avoid this.`,\n\t\t\t\t\t\t);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t\t`[SmartSkeleton] ${displayName} does not accept a ref. ` +\n\t\t\t\t\t\t\t\t`A wrapper <div> has been added automatically. Use forwardRef to avoid this.`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\twarnedComponents.add(displayName);\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t[element, suppressRefWarning, setWrapperState],\n\t);\n\n\tconst refCallback = useCallback(\n\t\t(node: unknown) => {\n\t\t\trefWasCalledRef.current = true;\n\t\t\tlastRefNodeRef.current = node;\n\n\t\t\tconst target = resolveRefTarget(node);\n\n\t\t\tif (target && loading && !hasAppliedRef.current) {\n\t\t\t\tapplySkeletonClasses(target, { animate, seed, variant });\n\t\t\t\thasAppliedRef.current = true;\n\t\t\t}\n\n\t\t\t// Forward ref to original element\n\t\t\tforwardRef(originalRef, node);\n\t\t},\n\t\t[loading, originalRef, animate, seed, variant],\n\t);\n\n\t// Single layout effect: handles identity reset AND wrapper fallback decision.\n\t// Merged into one effect because React fires ref callbacks (during commit)\n\t// BEFORE layout effects. Having a separate reset effect would clear the ref\n\t// data that was just written by the current commit's ref callbacks.\n\tuseIsomorphicLayoutEffect(() => {\n\t\tconst previousLoading = previousLoadingRef.current;\n\t\tconst previousElementType = previousElementTypeRef.current;\n\t\tconst previousElementKey = previousElementKeyRef.current;\n\n\t\tconst didExitLoading = previousLoading && !loading;\n\t\tconst hasElementIdentityChanged =\n\t\t\tpreviousElementType !== currentElementType ||\n\t\t\tpreviousElementKey !== currentElementKey;\n\n\t\tpreviousLoadingRef.current = loading;\n\t\tpreviousElementTypeRef.current = currentElementType;\n\t\tpreviousElementKeyRef.current = currentElementKey;\n\n\t\tif (didExitLoading || hasElementIdentityChanged) {\n\t\t\thasAppliedRef.current = false;\n\t\t\tif (didExitLoading) {\n\t\t\t\trefWasCalledRef.current = false;\n\t\t\t\tlastRefNodeRef.current = null;\n\t\t\t} else if (hasElementIdentityChanged && lastRefNodeRef.current === null) {\n\t\t\t\t// Ignore cleanup-only ref callbacks from previous element identity.\n\t\t\t\trefWasCalledRef.current = false;\n\t\t\t}\n\n\t\t\tif (needsWrapperRef.current) {\n\t\t\t\tsetWrapperState(false);\n\t\t\t\t// Re-render will re-run this effect with the direct path.\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tif (!loading) return;\n\t\tif (needsWrapper) return;\n\n\t\tconst node = lastRefNodeRef.current;\n\t\tconst target = resolveRefTarget(node);\n\n\t\tif (target && !hasAppliedRef.current) {\n\t\t\tapplySkeletonClasses(target, { animate, seed, variant });\n\t\t\thasAppliedRef.current = true;\n\t\t}\n\n\t\tif (refWasCalledRef.current) {\n\t\t\tif (node !== null && !target) {\n\t\t\t\tenableWrapperWithWarning(\"non-dom-ref\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (node !== null) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// Ref callback was only invoked with null during cleanup.\n\t\t\t// Treat as a missing ref call for the current element.\n\t\t\trefWasCalledRef.current = false;\n\t\t}\n\n\t\t// Ref was not called: the component doesn't accept refs.\n\t\t// Switch to wrapper mode synchronously (before browser paint).\n\t\tenableWrapperWithWarning(\"no-ref-call\");\n\t}, [\n\t\tloading,\n\t\tneedsWrapper,\n\t\tcurrentElementType,\n\t\tcurrentElementKey,\n\t\tanimate,\n\t\tseed,\n\t\tvariant,\n\t\tenableWrapperWithWarning,\n\t\tsetWrapperState,\n\t]);\n\n\t// Not loading: return children or null\n\tif (!loading) {\n\t\treturn children ?? null;\n\t}\n\n\t// Build merged className for skeleton mode\n\tconst elementProps = element.props as { className?: string };\n\tconst existingClassName = elementProps.className ?? \"\";\n\n\t// Base classes for skeleton mode\n\tconst baseClasses = [\"loaded-skeleton-mode\", animate && \"loaded-animate\"]\n\t\t.filter(Boolean)\n\t\t.join(\" \");\n\tconst filledOnlyClass = variant === \"filled\" ? \"loaded-skeleton-bg\" : \"\";\n\n\t// When wrapping: wrapper gets mode + wrapper marker (no bg - it goes on child via ref)\n\tconst wrapperClassName = [baseClasses, \"loaded-skeleton-wrapper\", className]\n\t\t.filter(Boolean)\n\t\t.join(\" \");\n\n\t// When not wrapping: element gets mode + bg directly (for SSR)\n\tconst mergedClassName = [\n\t\texistingClassName,\n\t\tbaseClasses,\n\t\tfilledOnlyClass,\n\t\tclassName,\n\t]\n\t\t.filter(Boolean)\n\t\t.join(\" \");\n\n\treturn (\n\t\t<SkeletonContext.Provider value={true}>\n\t\t\t{needsWrapper ? (\n\t\t\t\t<div ref={refCallback} className={wrapperClassName} aria-hidden=\"true\">\n\t\t\t\t\t{element}\n\t\t\t\t</div>\n\t\t\t) : (\n\t\t\t\tcloneElement(element as ReactElement<Record<string, unknown>>, {\n\t\t\t\t\tref: refCallback,\n\t\t\t\t\tclassName: mergedClassName,\n\t\t\t\t\t\"aria-hidden\": true,\n\t\t\t\t})\n\t\t\t)}\n\t\t</SkeletonContext.Provider>\n\t);\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\treturn Boolean(value) && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction toNumberRecord(value: unknown): StoredCounts {\n\tif (!isRecord(value)) return {};\n\tconst result: StoredCounts = {};\n\tfor (const [key, maybeNumber] of Object.entries(value)) {\n\t\tif (typeof maybeNumber === \"number\") {\n\t\t\tresult[key] = maybeNumber;\n\t\t}\n\t}\n\treturn result;\n}\n\nfunction parseStoredCounts(value: unknown): StoredCounts {\n\t// Current schema: { v: 1, counts: Record<string, number> }\n\tif (isRecord(value) && value.v === STORAGE_VERSION) {\n\t\treturn toNumberRecord(value.counts);\n\t}\n\n\t// Legacy schema: Record<string, number>\n\treturn toNumberRecord(value);\n}\n\nfunction readStoredCountsFromKey(key: string): StoredCounts {\n\tif (typeof localStorage === \"undefined\") return {};\n\ttry {\n\t\tconst raw = localStorage.getItem(key);\n\t\tif (raw === null) return {};\n\t\treturn parseStoredCounts(JSON.parse(raw));\n\t} catch {\n\t\treturn {};\n\t}\n}\n\nfunction writeStoredCounts(counts: StoredCounts): void {\n\tif (typeof localStorage === \"undefined\") return;\n\tconst payload: StoredPayloadV1 = { v: STORAGE_VERSION, counts };\n\ttry {\n\t\tlocalStorage.setItem(STORAGE_KEY, JSON.stringify(payload));\n\t} catch {\n\t\t// Silently fail if localStorage is full or unavailable\n\t}\n}\n\nfunction getStoredCounts(): Record<string, number> {\n\tif (typeof localStorage === \"undefined\") return {};\n\n\ttry {\n\t\tconst rawNew = localStorage.getItem(STORAGE_KEY);\n\t\tif (rawNew !== null) {\n\t\t\treturn parseStoredCounts(JSON.parse(rawNew));\n\t\t}\n\n\t\t// Backward compatibility: migrate legacy key once if present.\n\t\tconst legacyCounts = readStoredCountsFromKey(LEGACY_STORAGE_KEY);\n\t\tif (Object.keys(legacyCounts).length > 0) {\n\t\t\twriteStoredCounts(legacyCounts);\n\t\t}\n\t\treturn legacyCounts;\n\t} catch {\n\t\treturn {};\n\t}\n}\n\nfunction getStoredCount(key: string): number | null {\n\tconst counts = getStoredCounts();\n\tconst value = counts[key];\n\treturn typeof value === \"number\" ? value : null;\n}\n\nfunction setStoredCount(key: string, count: number): void {\n\tif (typeof localStorage === \"undefined\") return;\n\n\ttry {\n\t\tconst counts = getStoredCounts();\n\t\tcounts[key] = count;\n\t\twriteStoredCounts(counts);\n\t} catch {\n\t\t// Silently fail if localStorage is full or unavailable\n\t}\n}\n\nexport interface UsePersistedCountOptions {\n\tstorageKey?: string;\n\tdefaultCount?: number;\n\tcurrentCount?: number;\n\tloading: boolean;\n\tminCount?: number;\n\tmaxCount?: number;\n}\n\nexport function usePersistedCount({\n\tstorageKey,\n\tdefaultCount = 3,\n\tcurrentCount,\n\tloading,\n\tminCount = 1,\n\tmaxCount,\n}: UsePersistedCountOptions): number {\n\t// Always start from the default to match SSR output, then (on the client)\n\t// sync to the persisted value in a layout effect before first paint.\n\tconst [count, setCount] = useState<number>(() =>\n\t\tclampCount(defaultCount, minCount, maxCount),\n\t);\n\n\tconst hasWarnedRef = useRef(false);\n\n\tuseIsomorphicLayoutEffect(() => {\n\t\tif (!storageKey) return;\n\t\tconst stored = getStoredCount(storageKey);\n\t\tif (stored === null) return;\n\t\tconst next = clampCount(stored, minCount, maxCount);\n\t\tsetCount((prev) => (Object.is(prev, next) ? prev : next));\n\t}, [storageKey, minCount, maxCount]);\n\n\tuseEffect(() => {\n\t\tif (!loading && currentCount !== undefined) {\n\t\t\tconst newCount = clampCount(currentCount, minCount, maxCount);\n\t\t\tsetCount(newCount);\n\n\t\t\tif (storageKey) {\n\t\t\t\tsetStoredCount(storageKey, newCount);\n\t\t\t}\n\t\t}\n\t}, [loading, currentCount, storageKey, minCount, maxCount]);\n\n\tuseEffect(() => {\n\t\tif (isDevEnv() && !storageKey && !hasWarnedRef.current) {\n\t\t\tconsole.warn(\n\t\t\t\t\"[Loaded] SmartSkeletonList used without storageKey. \" +\n\t\t\t\t\t\"The count will reset on remount. Add a storageKey to persist across sessions.\",\n\t\t\t);\n\t\t\thasWarnedRef.current = true;\n\t\t}\n\t}, [storageKey]);\n\n\treturn count;\n}\n\nfunction clampCount(\n\tvalue: number,\n\tmin: number,\n\tmax: number | undefined,\n): number {\n\tlet result = Math.max(value, min);\n\tif (max !== undefined) {\n\t\tresult = Math.min(result, max);\n\t}\n\treturn result;\n}\n","import { Fragment, type ReactElement } from \"react\";\nimport { usePersistedCount } from \"../../hooks/usePersistedCount/usePersistedCount\";\nimport {\n\tSmartSkeleton,\n\ttype SmartSkeletonVariant,\n} from \"../SmartSkeleton/SmartSkeleton\";\n\nexport interface SmartSkeletonListProps<T> {\n\t/** Whether the list is currently loading. Default: false */\n\tloading?: boolean;\n\t/** The items to render. Pass undefined while loading. */\n\titems: T[] | undefined;\n\t/** Render function for each item when loaded */\n\trenderItem: (item: T, index: number) => ReactElement;\n\t/** Render function for skeleton placeholders */\n\trenderSkeleton: (index: number) => ReactElement;\n\t/** Key for localStorage persistence. Without it, count resets on remount. */\n\tstorageKey?: string;\n\t/** Initial skeleton count before any data is known. Default: 3 */\n\tdefaultCount?: number;\n\t/** Minimum skeletons to show. Default: 1 */\n\tminCount?: number;\n\t/** Maximum skeletons to show */\n\tmaxCount?: number;\n\t/** Enable shimmer animation. Default: true */\n\tanimate?: boolean;\n\t/** Optional seed to stabilize skeleton text widths */\n\tseed?: string | number;\n\t/** Visual variant for skeleton surface. `filled` adds a background, `ghost` does not. Default: \"filled\" */\n\tvariant?: SmartSkeletonVariant;\n\t/** Suppress warning when auto-wrapper is applied. Default: false */\n\tsuppressRefWarning?: boolean;\n\t/** Extract unique key for each item. Default: index */\n\tkeyExtractor?: (item: T, index: number) => string | number;\n}\n\nexport function SmartSkeletonList<T>({\n\tloading = false,\n\titems,\n\trenderItem,\n\trenderSkeleton,\n\tstorageKey,\n\tdefaultCount = 3,\n\tminCount = 1,\n\tmaxCount,\n\tanimate = true,\n\tseed,\n\tvariant = \"filled\",\n\tsuppressRefWarning = false,\n\tkeyExtractor = (_, index) => index,\n}: SmartSkeletonListProps<T>): ReactElement | null {\n\tconst skeletonCount = usePersistedCount({\n\t\tstorageKey,\n\t\tdefaultCount,\n\t\tcurrentCount: items?.length,\n\t\tloading,\n\t\tminCount,\n\t\tmaxCount,\n\t});\n\n\tif (loading) {\n\t\tconst skeletons = new Array(skeletonCount);\n\t\tfor (let index = 0; index < skeletonCount; index += 1) {\n\t\t\tconst itemSeed = seed === undefined ? `${index}` : `${seed}:${index}`;\n\t\t\tskeletons[index] = (\n\t\t\t\t<SmartSkeleton\n\t\t\t\t\tkey={`skeleton-${index}`}\n\t\t\t\t\tloading={true}\n\t\t\t\t\telement={renderSkeleton(index)}\n\t\t\t\t\tanimate={animate}\n\t\t\t\t\tseed={itemSeed}\n\t\t\t\t\tvariant={variant}\n\t\t\t\t\tsuppressRefWarning={suppressRefWarning}\n\t\t\t\t/>\n\t\t\t);\n\t\t}\n\t\treturn <>{skeletons}</>;\n\t}\n\n\tif (!items || items.length === 0) {\n\t\treturn null;\n\t}\n\n\treturn (\n\t\t<>\n\t\t\t{items.map((item, index) => (\n\t\t\t\t<Fragment key={keyExtractor(item, index)}>\n\t\t\t\t\t{renderItem(item, index)}\n\t\t\t\t</Fragment>\n\t\t\t))}\n\t\t</>\n\t);\n}\n"]}
|
package/dist/index.css
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
:root{--loaded-bg-wrapper: rgba(229, 231, 235, 1);--loaded-bg-content: rgba(156, 163, 175, .6);--loaded-border-radius: 4px;--loaded-text-inset: .3em}.loaded-skeleton-mode{user-select:none;pointer-events:none}.loaded-skeleton-mode,.loaded-skeleton-mode *{color:transparent!important;background-color:transparent!important;border-color:transparent!important;background-image:none!important;box-shadow:none!important;text-shadow:none!important}.loaded-skeleton-mode img{object-position:-9999px -9999px!important}.loaded-skeleton-mode *:before,.loaded-skeleton-mode *:after{background:transparent!important;background-image:none!important;color:transparent!important;border-color:transparent!important;box-shadow:none!important;text-shadow:none!important}.loaded-skeleton-mode ::placeholder{color:transparent!important;opacity:0!important}.loaded-skeleton-mode.loaded-skeleton-bg,.loaded-skeleton-mode .loaded-skeleton-bg{background-color:var(--loaded-bg-wrapper)!important}.loaded-skeleton-mode
|
|
1
|
+
:root{--loaded-bg-wrapper: rgba(229, 231, 235, 1);--loaded-bg-content: rgba(156, 163, 175, .6);--loaded-border-radius: 4px;--loaded-text-inset: .3em}.loaded-skeleton-mode{user-select:none;pointer-events:none}.loaded-skeleton-mode,.loaded-skeleton-mode *{color:transparent!important;background-color:transparent!important;border-color:transparent!important;background-image:none!important;box-shadow:none!important;text-shadow:none!important}.loaded-skeleton-mode img{object-position:-9999px -9999px!important}.loaded-skeleton-mode *:before,.loaded-skeleton-mode *:after{background:transparent!important;background-image:none!important;color:transparent!important;border-color:transparent!important;box-shadow:none!important;text-shadow:none!important}.loaded-skeleton-mode ::placeholder{color:transparent!important;opacity:0!important}.loaded-skeleton-mode.loaded-skeleton-bg,.loaded-skeleton-mode .loaded-skeleton-bg{background-color:var(--loaded-bg-wrapper)!important}.loaded-skeleton-mode .loaded-skeleton-content{background-color:var(--loaded-bg-content)!important}.loaded-skeleton-mode .loaded-text-skeleton{position:relative;background-color:transparent!important}.loaded-skeleton-mode .loaded-text-skeleton:before{content:""!important;position:absolute!important;left:0!important;width:var(--skeleton-text-width, 100%)!important;max-width:90%!important;top:var(--loaded-text-inset)!important;bottom:var(--loaded-text-inset)!important;background:var(--loaded-bg-content)!important;background-image:none!important;border-radius:var(--loaded-border-radius)!important;pointer-events:none!important;transform:none!important}.loaded-skeleton-mode .loaded-text-skeleton[data-skeleton-align=center]:before{left:50%!important;transform:translate(-50%)!important}.loaded-skeleton-mode .loaded-text-skeleton[data-skeleton-align=right]:before{left:auto!important;right:0!important}.loaded-skeleton-mode .loaded-skeleton-media{background-color:var(--loaded-bg-content)!important;opacity:1!important}.loaded-skeleton-mode .loaded-skeleton-svg{background-color:var(--loaded-bg-content)!important;border-radius:50%!important}.loaded-skeleton-mode .loaded-skeleton-svg *{visibility:hidden!important}.loaded-skeleton-mode .loaded-skeleton-force-hide{opacity:0!important;visibility:hidden!important}@keyframes loaded-shimmer{0%{opacity:1}50%{opacity:.6}to{opacity:1}}.loaded-skeleton-mode.loaded-animate{animation:loaded-shimmer 1.5s ease-in-out infinite}
|
|
2
2
|
/*# sourceMappingURL=index.css.map */
|
package/dist/index.css.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/SmartSkeleton/SmartSkeleton.css"],"sourcesContent":[":root {\n\t--loaded-bg-wrapper: rgba(229, 231, 235, 1);\n\t--loaded-bg-content: rgba(156, 163, 175, 0.6);\n\t--loaded-border-radius: 4px;\n\t--loaded-text-inset: 0.3em;\n}\n\n.loaded-skeleton-mode {\n\tuser-select: none;\n\tpointer-events: none;\n}\n\n/* Base styles applied to ALL elements inside skeleton via CSS selector */\n.loaded-skeleton-mode,\n.loaded-skeleton-mode * {\n\tcolor: transparent !important;\n\tbackground-color: transparent !important;\n\tborder-color: transparent !important;\n\tbackground-image: none !important;\n\tbox-shadow: none !important;\n\ttext-shadow: none !important;\n}\n\n.loaded-skeleton-mode img {\n\tobject-position: -9999px -9999px !important;\n}\n\n/* Reset pseudo-elements */\n.loaded-skeleton-mode *::before,\n.loaded-skeleton-mode *::after {\n\tbackground: transparent !important;\n\tbackground-image: none !important;\n\tcolor: transparent !important;\n\tborder-color: transparent !important;\n\tbox-shadow: none !important;\n\ttext-shadow: none !important;\n}\n\n/* Hide input placeholders during skeleton */\n.loaded-skeleton-mode ::placeholder {\n\tcolor: transparent !important;\n\topacity: 0 !important;\n}\n\n/* Main background for non-wrapper case (element has loaded-skeleton-bg directly) */\n.loaded-skeleton-mode.loaded-skeleton-bg,\n.loaded-skeleton-mode .loaded-skeleton-bg {\n\tbackground-color: var(--loaded-bg-wrapper) !important;\n}\n\n/*
|
|
1
|
+
{"version":3,"sources":["../src/components/SmartSkeleton/SmartSkeleton.css"],"sourcesContent":[":root {\n\t--loaded-bg-wrapper: rgba(229, 231, 235, 1);\n\t--loaded-bg-content: rgba(156, 163, 175, 0.6);\n\t--loaded-border-radius: 4px;\n\t--loaded-text-inset: 0.3em;\n}\n\n.loaded-skeleton-mode {\n\tuser-select: none;\n\tpointer-events: none;\n}\n\n/* Base styles applied to ALL elements inside skeleton via CSS selector */\n.loaded-skeleton-mode,\n.loaded-skeleton-mode * {\n\tcolor: transparent !important;\n\tbackground-color: transparent !important;\n\tborder-color: transparent !important;\n\tbackground-image: none !important;\n\tbox-shadow: none !important;\n\ttext-shadow: none !important;\n}\n\n.loaded-skeleton-mode img {\n\tobject-position: -9999px -9999px !important;\n}\n\n/* Reset pseudo-elements */\n.loaded-skeleton-mode *::before,\n.loaded-skeleton-mode *::after {\n\tbackground: transparent !important;\n\tbackground-image: none !important;\n\tcolor: transparent !important;\n\tborder-color: transparent !important;\n\tbox-shadow: none !important;\n\ttext-shadow: none !important;\n}\n\n/* Hide input placeholders during skeleton */\n.loaded-skeleton-mode ::placeholder {\n\tcolor: transparent !important;\n\topacity: 0 !important;\n}\n\n/* Main background for non-wrapper case (element has loaded-skeleton-bg directly) */\n.loaded-skeleton-mode.loaded-skeleton-bg,\n.loaded-skeleton-mode .loaded-skeleton-bg {\n\tbackground-color: var(--loaded-bg-wrapper) !important;\n}\n\n/* Content elements (buttons, inputs, etc.) */\n.loaded-skeleton-mode .loaded-skeleton-content {\n\tbackground-color: var(--loaded-bg-content) !important;\n}\n\n/* Text skeleton with pseudo-element */\n.loaded-skeleton-mode .loaded-text-skeleton {\n\tposition: relative;\n\tbackground-color: transparent !important;\n}\n\n/* Text skeleton bar drawn via pseudo-element */\n.loaded-skeleton-mode .loaded-text-skeleton::before {\n\tcontent: \"\" !important;\n\tposition: absolute !important;\n\tleft: 0 !important;\n\twidth: var(--skeleton-text-width, 100%) !important;\n\tmax-width: 90% !important;\n\ttop: var(--loaded-text-inset) !important;\n\tbottom: var(--loaded-text-inset) !important;\n\tbackground: var(--loaded-bg-content) !important;\n\tbackground-image: none !important;\n\tborder-radius: var(--loaded-border-radius) !important;\n\tpointer-events: none !important;\n\ttransform: none !important;\n}\n\n.loaded-skeleton-mode\n\t.loaded-text-skeleton[data-skeleton-align=\"center\"]::before {\n\tleft: 50% !important;\n\ttransform: translateX(-50%) !important;\n}\n\n.loaded-skeleton-mode\n\t.loaded-text-skeleton[data-skeleton-align=\"right\"]::before {\n\tleft: auto !important;\n\tright: 0 !important;\n}\n\n/* Media elements (images, videos, etc.) */\n.loaded-skeleton-mode .loaded-skeleton-media {\n\tbackground-color: var(--loaded-bg-content) !important;\n\topacity: 1 !important;\n}\n\n/* SVG elements rendered as rounded blocks */\n.loaded-skeleton-mode .loaded-skeleton-svg {\n\tbackground-color: var(--loaded-bg-content) !important;\n\tborder-radius: 50% !important;\n}\n\n/* Hide SVG internal content (paths, circles, etc.) */\n.loaded-skeleton-mode .loaded-skeleton-svg * {\n\tvisibility: hidden !important;\n}\n\n.loaded-skeleton-mode .loaded-skeleton-force-hide {\n\topacity: 0 !important;\n\tvisibility: hidden !important;\n}\n\n/* Animation */\n@keyframes loaded-shimmer {\n\t0% {\n\t\topacity: 1;\n\t}\n\t50% {\n\t\topacity: 0.6;\n\t}\n\t100% {\n\t\topacity: 1;\n\t}\n}\n\n.loaded-skeleton-mode.loaded-animate {\n\tanimation: loaded-shimmer 1.5s ease-in-out infinite;\n}\n"],"mappings":"AAAA,MACC,qBAAqB,KAAK,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GACzC,qBAAqB,KAAK,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,IACzC,wBAAwB,IACxB,qBAAqB,IACtB,CAEA,CAAC,qBACA,YAAa,KACb,eAAgB,IACjB,CAGA,CANC,qBAOD,CAPC,qBAOqB,EACrB,MAAO,sBACP,iBAAkB,sBAClB,aAAc,sBACd,iBAAkB,eAClB,WAAY,eACZ,YAAa,cACd,CAEA,CAhBC,qBAgBqB,IACrB,gBAAiB,QAAQ,iBAC1B,CAGA,CArBC,qBAqBqB,CAAC,QACvB,CAtBC,qBAsBqB,CAAC,OACtB,WAAY,sBACZ,iBAAkB,eAClB,MAAO,sBACP,aAAc,sBACd,WAAY,eACZ,YAAa,cACd,CAGA,CAhCC,qBAgCqB,cACrB,MAAO,sBACP,QAAS,WACV,CAGA,CAtCC,oBAsCoB,CAAC,mBACtB,CAvCC,qBAuCqB,CADA,mBAErB,iBAAkB,IAAI,8BACvB,CAGA,CA5CC,qBA4CqB,CAAC,wBACtB,iBAAkB,IAAI,8BACvB,CAGA,CAjDC,qBAiDqB,CAAC,qBACtB,SAAU,SACV,iBAAkB,qBACnB,CAGA,CAvDC,qBAuDqB,CANC,oBAMoB,QAC1C,QAAS,aACT,SAAU,mBACV,KAAM,YACN,MAAO,IAAI,qBAAqB,EAAE,gBAClC,UAAW,cACX,IAAK,IAAI,+BACT,OAAQ,IAAI,+BACZ,WAAY,IAAI,+BAChB,iBAAkB,eAClB,cAAe,IAAI,kCACnB,eAAgB,eAChB,UAAW,cACZ,CAEA,CAtEC,qBAuEA,CAtBsB,oBAsBD,CAAC,2BAA6B,QACnD,KAAM,cACN,UAAW,UAAW,eACvB,CAEA,CA5EC,qBA6EA,CA5BsB,oBA4BD,CAAC,0BAA4B,QAClD,KAAM,eACN,MAAO,WACR,CAGA,CAnFC,qBAmFqB,CAAC,sBACtB,iBAAkB,IAAI,+BACtB,QAAS,WACV,CAGA,CAzFC,qBAyFqB,CAAC,oBACtB,iBAAkB,IAAI,+BAjGvB,cAkGgB,aAChB,CAGA,CA/FC,qBA+FqB,CANC,oBAMoB,EAC1C,WAAY,gBACb,CAEA,CAnGC,qBAmGqB,CAAC,2BACtB,QAAS,YACT,WAAY,gBACb,CAGA,WAAW,eACV,GACC,QAAS,CACV,CACA,IACC,QAAS,EACV,CACA,GACC,QAAS,CACV,CACD,CAEA,CArHC,oBAqHoB,CAAC,eACrB,UAAW,eAAe,KAAK,YAAY,QAC5C","names":[]}
|
package/dist/index.d.cts
CHANGED
|
@@ -4,6 +4,7 @@ import { ReactElement } from 'react';
|
|
|
4
4
|
declare const SkeletonContext: react.Context<boolean>;
|
|
5
5
|
declare function useIsSkeletonMode(): boolean;
|
|
6
6
|
|
|
7
|
+
type SmartSkeletonVariant = "filled" | "ghost";
|
|
7
8
|
interface SmartSkeletonProps {
|
|
8
9
|
/** The skeleton element with mock data, rendered when loading */
|
|
9
10
|
element: ReactElement;
|
|
@@ -17,10 +18,12 @@ interface SmartSkeletonProps {
|
|
|
17
18
|
className?: string;
|
|
18
19
|
/** Optional seed to stabilize skeleton text widths */
|
|
19
20
|
seed?: string | number;
|
|
21
|
+
/** Visual variant for skeleton surface. `filled` adds a background, `ghost` does not. Default: "filled" */
|
|
22
|
+
variant?: SmartSkeletonVariant;
|
|
20
23
|
/** Suppress warning when auto-wrapper is applied. Default: false */
|
|
21
24
|
suppressRefWarning?: boolean;
|
|
22
25
|
}
|
|
23
|
-
declare function SmartSkeleton({ element, children, loading, animate, className, seed, suppressRefWarning, }: SmartSkeletonProps): ReactElement | null;
|
|
26
|
+
declare function SmartSkeleton({ element, children, loading, animate, className, seed, variant, suppressRefWarning, }: SmartSkeletonProps): ReactElement | null;
|
|
24
27
|
|
|
25
28
|
interface SmartSkeletonListProps<T> {
|
|
26
29
|
/** Whether the list is currently loading. Default: false */
|
|
@@ -43,12 +46,14 @@ interface SmartSkeletonListProps<T> {
|
|
|
43
46
|
animate?: boolean;
|
|
44
47
|
/** Optional seed to stabilize skeleton text widths */
|
|
45
48
|
seed?: string | number;
|
|
49
|
+
/** Visual variant for skeleton surface. `filled` adds a background, `ghost` does not. Default: "filled" */
|
|
50
|
+
variant?: SmartSkeletonVariant;
|
|
46
51
|
/** Suppress warning when auto-wrapper is applied. Default: false */
|
|
47
52
|
suppressRefWarning?: boolean;
|
|
48
53
|
/** Extract unique key for each item. Default: index */
|
|
49
54
|
keyExtractor?: (item: T, index: number) => string | number;
|
|
50
55
|
}
|
|
51
|
-
declare function SmartSkeletonList<T>({ loading, items, renderItem, renderSkeleton, storageKey, defaultCount, minCount, maxCount, animate, seed, suppressRefWarning, keyExtractor, }: SmartSkeletonListProps<T>): ReactElement | null;
|
|
56
|
+
declare function SmartSkeletonList<T>({ loading, items, renderItem, renderSkeleton, storageKey, defaultCount, minCount, maxCount, animate, seed, variant, suppressRefWarning, keyExtractor, }: SmartSkeletonListProps<T>): ReactElement | null;
|
|
52
57
|
|
|
53
58
|
interface UsePersistedCountOptions {
|
|
54
59
|
storageKey?: string;
|
|
@@ -60,4 +65,4 @@ interface UsePersistedCountOptions {
|
|
|
60
65
|
}
|
|
61
66
|
declare function usePersistedCount({ storageKey, defaultCount, currentCount, loading, minCount, maxCount, }: UsePersistedCountOptions): number;
|
|
62
67
|
|
|
63
|
-
export { SkeletonContext, SmartSkeleton, SmartSkeletonList, type SmartSkeletonListProps, type SmartSkeletonProps, type UsePersistedCountOptions, useIsSkeletonMode, usePersistedCount };
|
|
68
|
+
export { SkeletonContext, SmartSkeleton, SmartSkeletonList, type SmartSkeletonListProps, type SmartSkeletonProps, type SmartSkeletonVariant, type UsePersistedCountOptions, useIsSkeletonMode, usePersistedCount };
|
package/dist/index.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { ReactElement } from 'react';
|
|
|
4
4
|
declare const SkeletonContext: react.Context<boolean>;
|
|
5
5
|
declare function useIsSkeletonMode(): boolean;
|
|
6
6
|
|
|
7
|
+
type SmartSkeletonVariant = "filled" | "ghost";
|
|
7
8
|
interface SmartSkeletonProps {
|
|
8
9
|
/** The skeleton element with mock data, rendered when loading */
|
|
9
10
|
element: ReactElement;
|
|
@@ -17,10 +18,12 @@ interface SmartSkeletonProps {
|
|
|
17
18
|
className?: string;
|
|
18
19
|
/** Optional seed to stabilize skeleton text widths */
|
|
19
20
|
seed?: string | number;
|
|
21
|
+
/** Visual variant for skeleton surface. `filled` adds a background, `ghost` does not. Default: "filled" */
|
|
22
|
+
variant?: SmartSkeletonVariant;
|
|
20
23
|
/** Suppress warning when auto-wrapper is applied. Default: false */
|
|
21
24
|
suppressRefWarning?: boolean;
|
|
22
25
|
}
|
|
23
|
-
declare function SmartSkeleton({ element, children, loading, animate, className, seed, suppressRefWarning, }: SmartSkeletonProps): ReactElement | null;
|
|
26
|
+
declare function SmartSkeleton({ element, children, loading, animate, className, seed, variant, suppressRefWarning, }: SmartSkeletonProps): ReactElement | null;
|
|
24
27
|
|
|
25
28
|
interface SmartSkeletonListProps<T> {
|
|
26
29
|
/** Whether the list is currently loading. Default: false */
|
|
@@ -43,12 +46,14 @@ interface SmartSkeletonListProps<T> {
|
|
|
43
46
|
animate?: boolean;
|
|
44
47
|
/** Optional seed to stabilize skeleton text widths */
|
|
45
48
|
seed?: string | number;
|
|
49
|
+
/** Visual variant for skeleton surface. `filled` adds a background, `ghost` does not. Default: "filled" */
|
|
50
|
+
variant?: SmartSkeletonVariant;
|
|
46
51
|
/** Suppress warning when auto-wrapper is applied. Default: false */
|
|
47
52
|
suppressRefWarning?: boolean;
|
|
48
53
|
/** Extract unique key for each item. Default: index */
|
|
49
54
|
keyExtractor?: (item: T, index: number) => string | number;
|
|
50
55
|
}
|
|
51
|
-
declare function SmartSkeletonList<T>({ loading, items, renderItem, renderSkeleton, storageKey, defaultCount, minCount, maxCount, animate, seed, suppressRefWarning, keyExtractor, }: SmartSkeletonListProps<T>): ReactElement | null;
|
|
56
|
+
declare function SmartSkeletonList<T>({ loading, items, renderItem, renderSkeleton, storageKey, defaultCount, minCount, maxCount, animate, seed, variant, suppressRefWarning, keyExtractor, }: SmartSkeletonListProps<T>): ReactElement | null;
|
|
52
57
|
|
|
53
58
|
interface UsePersistedCountOptions {
|
|
54
59
|
storageKey?: string;
|
|
@@ -60,4 +65,4 @@ interface UsePersistedCountOptions {
|
|
|
60
65
|
}
|
|
61
66
|
declare function usePersistedCount({ storageKey, defaultCount, currentCount, loading, minCount, maxCount, }: UsePersistedCountOptions): number;
|
|
62
67
|
|
|
63
|
-
export { SkeletonContext, SmartSkeleton, SmartSkeletonList, type SmartSkeletonListProps, type SmartSkeletonProps, type UsePersistedCountOptions, useIsSkeletonMode, usePersistedCount };
|
|
68
|
+
export { SkeletonContext, SmartSkeleton, SmartSkeletonList, type SmartSkeletonListProps, type SmartSkeletonProps, type SmartSkeletonVariant, type UsePersistedCountOptions, useIsSkeletonMode, usePersistedCount };
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {createContext,useContext,useRef,useState,useCallback,cloneElement,useEffect,Fragment as Fragment$1,useLayoutEffect,version}from'react';import {jsx,Fragment}from'react/jsx-runtime';var
|
|
1
|
+
import {createContext,useContext,useRef,useState,useCallback,cloneElement,useEffect,Fragment as Fragment$1,useLayoutEffect,version}from'react';import {jsx,Fragment}from'react/jsx-runtime';var C=createContext(false);function ke(){return useContext(C)}function T(){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 Ee=typeof globalThis<"u"&&typeof globalThis.document<"u",w=Ee?useLayoutEffect:useEffect;function F(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))}function I(e){if(!F(e))return false;try{return e.querySelectorAll("*"),!0}catch{return false}}var _=new Set(["IMG","VIDEO","CANVAS"]),A=new Set(["SVG"]),Se=new Set(["BUTTON","INPUT","TEXTAREA","SELECT","A"]),ye="button,input,textarea,select,a,[role='button']",he=new Set(["SCRIPT","STYLE","LINK","META","NOSCRIPT","TEMPLATE"]);function O(e){return e.tagName.toUpperCase()}function M(e,t=O(e)){return Se.has(t)?true:e.getAttribute("role")==="button"}function Re(e,t){return !!(e.closest(ye)&&!M(e,t))}function Ce(e,t=O(e)){return !!(_.has(t)||A.has(t)||M(e,t)||e.childElementCount===0&&e.textContent?.trim())}function Te(e,t){let n=e.length,o=Math.max(4,.8*n),s=we(t)*o,i=n+2+s;return Math.max(6,Math.min(40,i))}function we(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 xe(e){let t=globalThis.getComputedStyle(e).textAlign;return t==="center"?"center":t==="right"||t==="end"?"right":"left"}function P(e,t={}){let{animate:n=true,seed:o,variant:s="filled"}=t,i=o==null?"loaded":String(o);if(!F(e))return;let a=e;if(a.classList.add("loaded-skeleton-mode"),n?a.classList.add("loaded-animate"):a.classList.remove("loaded-animate"),!a.classList.contains("loaded-skeleton-wrapper"))s==="filled"?a.classList.add("loaded-skeleton-bg"):a.classList.remove("loaded-skeleton-bg");else {let r=a.firstElementChild;r&&s==="filled"?r.classList.add("loaded-skeleton-bg"):r&&r.classList.remove("loaded-skeleton-bg"),a.classList.remove("loaded-skeleton-bg");}let p=e.getElementsByTagName("*"),d=0,f=r=>{let c=O(r);if(he.has(c)||!Ce(r,c))return;let l=r;if(Re(r,c)){l.classList.add("loaded-skeleton-force-hide");return}let u=r.textContent?.trim();if(r.childElementCount===0&&u&&!_.has(c)&&!A.has(c)&&!M(r,c)){l.classList.add("loaded-text-skeleton"),l.dataset.skeletonAlign=xe(l);let y=`${i}|${d}|${u??""}`;d+=1;let h=Te(u??"",y);l.style.setProperty("--skeleton-text-width",`${h}ch`);}else _.has(c)?l.classList.add("loaded-skeleton-media"):A.has(c)?(l.classList.add("loaded-skeleton-content"),l.classList.add("loaded-skeleton-svg")):(l.classList.add("loaded-skeleton-content"),l.setAttribute("tabindex","-1"));};f(e);for(let r of p)f(r);}var J=Number.parseInt(version,10),Le=Number.isFinite(J)&&J>=19;function V(e){if(I(e))return e;if(e&&typeof e=="object"&&"nativeElement"in e){let t=e.nativeElement;if(I(t))return t}return null}function K(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 Y(e){let n=e.props?.ref;if(n!==void 0)return n;if(Le)return;let o=e.ref;if(o!==void 0)return o}function q(e,t){e&&(typeof e=="function"?e(t):e.current=t);}var z=new Set;function W({element:e,children:t,loading:n=false,animate:o=true,className:s="",seed:i,variant:a="filled",suppressRefWarning:g=false}){let p=e.type,d=e.key??null,f=useRef(false),r=useRef(false),c=useRef(null),l=useRef(false),[m,u]=useState(false),S=useRef(n),y=useRef(p),h=useRef(d),R=useCallback(k=>{l.current=k,u(k);},[]),j=Y(e),v=useCallback(k=>{if(l.current||R(true),!g&&T()){let b=K(e);z.has(b)||(console.warn(k==="non-dom-ref"?`[SmartSkeleton] ${b} does not forward its ref to a DOM element. A wrapper <div> has been added automatically. Use forwardRef to avoid this.`:`[SmartSkeleton] ${b} does not accept a ref. A wrapper <div> has been added automatically. Use forwardRef to avoid this.`),z.add(b));}},[e,g,R]),H=useCallback(k=>{r.current=true,c.current=k;let b=V(k);b&&n&&!f.current&&(P(b,{animate:o,seed:i,variant:a}),f.current=true),q(j,k);},[n,j,o,i,a]);if(w(()=>{let k=S.current,b=y.current,fe=h.current,G=k&&!n,X=b!==p||fe!==d;if(S.current=n,y.current=p,h.current=d,(G||X)&&(f.current=false,G?(r.current=false,c.current=null):X&&c.current===null&&(r.current=false),l.current)){R(false);return}if(!n||m)return;let L=c.current,N=V(L);if(N&&!f.current&&(P(N,{animate:o,seed:i,variant:a}),f.current=true),r.current){if(L!==null&&!N){v("non-dom-ref");return}if(L!==null)return;r.current=false;}v("no-ref-call");},[n,m,p,d,o,i,a,v,R]),!n)return t??null;let ie=e.props.className??"",$=["loaded-skeleton-mode",o&&"loaded-animate"].filter(Boolean).join(" "),de=a==="filled"?"loaded-skeleton-bg":"",ue=[$,"loaded-skeleton-wrapper",s].filter(Boolean).join(" "),ce=[ie,$,de,s].filter(Boolean).join(" ");return jsx(C.Provider,{value:true,children:m?jsx("div",{ref:H,className:ue,"aria-hidden":"true",children:e}):cloneElement(e,{ref:H,className:ce,"aria-hidden":true})})}var te="react-loaded",Oe="loaded",ne=1;function oe(e){return !!e&&typeof e=="object"&&!Array.isArray(e)}function ee(e){if(!oe(e))return {};let t={};for(let[n,o]of Object.entries(e))typeof o=="number"&&(t[n]=o);return t}function re(e){return oe(e)&&e.v===ne?ee(e.counts):ee(e)}function Me(e){if(typeof localStorage>"u")return {};try{let t=localStorage.getItem(e);return t===null?{}:re(JSON.parse(t))}catch{return {}}}function se(e){if(typeof localStorage>"u")return;let t={v:ne,counts:e};try{localStorage.setItem(te,JSON.stringify(t));}catch{}}function ae(){if(typeof localStorage>"u")return {};try{let e=localStorage.getItem(te);if(e!==null)return re(JSON.parse(e));let t=Me(Oe);return Object.keys(t).length>0&&se(t),t}catch{return {}}}function Pe(e){let n=ae()[e];return typeof n=="number"?n:null}function Ve(e,t){if(!(typeof localStorage>"u"))try{let n=ae();n[e]=t,se(n);}catch{}}function U({storageKey:e,defaultCount:t=3,currentCount:n,loading:o,minCount:s=1,maxCount:i}){let[a,g]=useState(()=>B(t,s,i)),p=useRef(false);return w(()=>{if(!e)return;let d=Pe(e);if(d===null)return;let f=B(d,s,i);g(r=>Object.is(r,f)?r:f);},[e,s,i]),useEffect(()=>{if(!o&&n!==void 0){let d=B(n,s,i);g(d),e&&Ve(e,d);}},[o,n,e,s,i]),useEffect(()=>{T()&&!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]),a}function B(e,t,n){let o=Math.max(e,t);return n!==void 0&&(o=Math.min(o,n)),o}function We({loading:e=false,items:t,renderItem:n,renderSkeleton:o,storageKey:s,defaultCount:i=3,minCount:a=1,maxCount:g,animate:p=true,seed:d,variant:f="filled",suppressRefWarning:r=false,keyExtractor:c=(l,m)=>m}){let l=U({storageKey:s,defaultCount:i,currentCount:t?.length,loading:e,minCount:a,maxCount:g});if(e){let m=new Array(l);for(let u=0;u<l;u+=1){let S=d===void 0?`${u}`:`${d}:${u}`;m[u]=jsx(W,{loading:true,element:o(u),animate:p,seed:S,variant:f,suppressRefWarning:r},`skeleton-${u}`);}return jsx(Fragment,{children:m})}return !t||t.length===0?null:jsx(Fragment,{children:t.map((m,u)=>jsx(Fragment$1,{children:n(m,u)},c(m,u)))})}export{C as SkeletonContext,W as SmartSkeleton,We as SmartSkeletonList,ke as useIsSkeletonMode,U 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/applySkeletonClasses.ts","../src/components/SmartSkeleton/refUtils.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","isElement","value","maybeElement","isUsableElement","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","REACT_MAJOR_VERSION","reactVersion","IS_REACT_19_OR_NEWER","resolveRefTarget","node","nativeElement","getElementDisplayName","element","type","fn","obj","getOriginalRef","propsRef","legacyRef","forwardRef","originalRef","warnedComponents","SmartSkeleton","children","loading","className","suppressRefWarning","currentElementType","currentElementKey","hasAppliedRef","useRef","refWasCalledRef","lastRefNodeRef","needsWrapperRef","needsWrapper","setNeedsWrapper","useState","previousLoadingRef","previousElementTypeRef","previousElementKeyRef","setWrapperState","useCallback","next","enableWrapperWithWarning","reason","displayName","refCallback","target","previousLoading","previousElementType","previousElementKey","didExitLoading","hasElementIdentityChanged","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","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\treturn useContext(SkeletonContext);\n}\n","export function isDevEnv(): boolean {\n\tconst maybeGlobal = globalThis as unknown as Record<string, unknown>;\n\n\t// Manual override for environments where NODE_ENV isn't injected.\n\t// Example: `globalThis.__REACT_LOADED_DEV__ = true`.\n\tconst override = maybeGlobal.__REACT_LOADED_DEV__;\n\tif (typeof override === \"boolean\") return override;\n\n\t// Common global used by some toolchains/runtimes.\n\tconst devFlag = maybeGlobal.__DEV__;\n\tif (typeof devFlag === \"boolean\") return devFlag;\n\n\tconst maybeProcess = (globalThis as unknown as { process?: unknown }).process;\n\tconst nodeEnv =\n\t\ttypeof maybeProcess === \"object\" && maybeProcess !== null\n\t\t\t? (maybeProcess as { env?: { NODE_ENV?: unknown } }).env?.NODE_ENV\n\t\t\t: undefined;\n\n\tif (typeof nodeEnv === \"string\") {\n\t\treturn nodeEnv !== \"production\";\n\t}\n\n\t// No environment detected — assume production (convention: opt-in to dev mode).\n\treturn false;\n}\n","import { useEffect, useLayoutEffect } from \"react\";\n\nconst canUseDOM =\n\ttypeof globalThis !== \"undefined\" &&\n\ttypeof (globalThis as { document?: unknown }).document !== \"undefined\";\n\nexport const useIsomorphicLayoutEffect = canUseDOM\n\t? useLayoutEffect\n\t: useEffect;\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\tif (!value || typeof value !== \"object\") return false;\n\tconst maybeElement = value as Element;\n\t// Must have nodeType 1 (Element) and a working querySelectorAll\n\tif (maybeElement.nodeType !== 1) return false;\n\tif (typeof maybeElement.tagName !== \"string\") return false;\n\tif (typeof maybeElement.querySelectorAll !== \"function\") return false;\n\t// Additional check: instanceof Element if available\n\tif (typeof Element !== \"undefined\" && !(value instanceof Element)) {\n\t\treturn false;\n\t}\n\treturn true;\n}\n\nexport function isUsableElement(value: unknown): value is Element {\n\tif (!isElement(value)) return false;\n\t// Test that querySelectorAll actually works\n\ttry {\n\t\t(value as Element).querySelectorAll(\"*\");\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nconst MEDIA_ELEMENTS = new Set([\"IMG\", \"VIDEO\", \"CANVAS\"]);\nconst SVG_ELEMENTS = new Set([\"SVG\"]);\n\nconst INTERACTIVE_ELEMENTS = new Set([\n\t\"BUTTON\",\n\t\"INPUT\",\n\t\"TEXTAREA\",\n\t\"SELECT\",\n\t\"A\",\n]);\n\nconst BUTTON_LIKE_SELECTOR = \"button,input,textarea,select,a,[role='button']\";\nconst SKIPPED_TAGS = new Set([\n\t\"SCRIPT\",\n\t\"STYLE\",\n\t\"LINK\",\n\t\"META\",\n\t\"NOSCRIPT\",\n\t\"TEMPLATE\",\n]);\n\nfunction getTagName(el: Element): string {\n\treturn el.tagName.toUpperCase();\n}\n\nfunction isButtonLikeElement(el: Element, tagName = getTagName(el)): boolean {\n\tif (INTERACTIVE_ELEMENTS.has(tagName)) return true;\n\tconst role = el.getAttribute(\"role\");\n\treturn role === \"button\";\n}\n\nfunction isButtonLikeDescendant(el: Element, tagName: string): boolean {\n\tconst closestButton = el.closest(BUTTON_LIKE_SELECTOR);\n\treturn Boolean(closestButton && !isButtonLikeElement(el, tagName));\n}\n\nfunction isContentElement(el: Element, tagName = getTagName(el)): boolean {\n\tif (MEDIA_ELEMENTS.has(tagName)) return true;\n\tif (SVG_ELEMENTS.has(tagName)) return true;\n\tif (isButtonLikeElement(el, tagName)) return true;\n\n\tconst isLeafNode = el.childElementCount === 0;\n\n\t// Text elements that are leaf nodes (no child elements, only text)\n\tif (isLeafNode && el.textContent?.trim()) return true;\n\n\treturn 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\tconst textLength = text.length;\n\tconst jitterRange = Math.max(4, 0.8 * textLength);\n\tconst jitter = deterministicJitter(seedKey) * jitterRange;\n\tconst width = textLength + 2 + jitter;\n\treturn Math.max(TEXT_WIDTH_MIN_CH, Math.min(TEXT_WIDTH_MAX_CH, width));\n}\n\nfunction deterministicJitter(seedKey: string): number {\n\tif (!seedKey) return 0;\n\tlet hash = 2166136261;\n\tfor (let index = 0; index < seedKey.length; index += 1) {\n\t\thash ^= seedKey.charCodeAt(index);\n\t\thash = Math.imul(hash, 16777619);\n\t}\n\tconst normalized = (hash >>> 0) / 0xffffffff;\n\treturn normalized * 2 - 1;\n}\n\nfunction resolveTextAlign(el: HTMLElement): \"left\" | \"center\" | \"right\" {\n\tconst align = globalThis.getComputedStyle(el).textAlign;\n\tif (align === \"center\") return \"center\";\n\tif (align === \"right\" || align === \"end\") return \"right\";\n\treturn \"left\";\n}\n\nexport function applySkeletonClasses(\n\trootElement: Element,\n\toptions: { animate?: boolean; seed?: string | number } = {},\n): void {\n\tconst { animate = true, seed } = options;\n\tconst baseSeed =\n\t\tseed === undefined || seed === null ? \"loaded\" : String(seed);\n\n\tif (!isElement(rootElement)) {\n\t\treturn;\n\t}\n\n\tconst htmlRoot = rootElement as HTMLElement;\n\n\t// Apply skeleton mode to the root element\n\thtmlRoot.classList.add(\"loaded-skeleton-mode\");\n\n\tif (animate) {\n\t\thtmlRoot.classList.add(\"loaded-animate\");\n\t}\n\n\t// Apply background class for standalone usage (when not used via SmartSkeleton JSX)\n\t// If element has loaded-skeleton-wrapper, CSS handles bg via > :first-child rule\n\t// If element already has loaded-skeleton-bg (from JSX), this is a no-op\n\tconst isWrapper = htmlRoot.classList.contains(\"loaded-skeleton-wrapper\");\n\tif (!isWrapper) {\n\t\thtmlRoot.classList.add(\"loaded-skeleton-bg\");\n\t}\n\n\t// Only add specific classes where needed (text, media, content)\n\tconst descendants = rootElement.getElementsByTagName(\"*\");\n\n\tlet textIndex = 0;\n\n\tconst processElement = (el: Element) => {\n\t\tconst tagName = getTagName(el);\n\t\tif (SKIPPED_TAGS.has(tagName)) return;\n\t\tif (!isContentElement(el, tagName)) return;\n\n\t\tconst htmlEl = el as HTMLElement;\n\t\tconst isInsideButtonLike = isButtonLikeDescendant(el, tagName);\n\t\tif (isInsideButtonLike) {\n\t\t\thtmlEl.classList.add(\"loaded-skeleton-force-hide\");\n\t\t\treturn;\n\t\t}\n\n\t\tconst textContent = el.textContent?.trim();\n\t\tconst isLeafWithText = el.childElementCount === 0 && textContent;\n\n\t\tif (\n\t\t\tisLeafWithText &&\n\t\t\t!MEDIA_ELEMENTS.has(tagName) &&\n\t\t\t!SVG_ELEMENTS.has(tagName) &&\n\t\t\t!isButtonLikeElement(el, tagName)\n\t\t) {\n\t\t\t// Text elements: overlay bar with ch-based width\n\t\t\thtmlEl.classList.add(\"loaded-text-skeleton\");\n\t\t\thtmlEl.dataset.skeletonAlign = resolveTextAlign(htmlEl);\n\t\t\tconst seedKey = `${baseSeed}|${textIndex}|${textContent ?? \"\"}`;\n\t\t\ttextIndex += 1;\n\t\t\tconst widthCh = calculateTextWidthCh(textContent ?? \"\", seedKey);\n\t\t\thtmlEl.style.setProperty(\"--skeleton-text-width\", `${widthCh}ch`);\n\t\t} else if (MEDIA_ELEMENTS.has(tagName)) {\n\t\t\t// Media elements\n\t\t\thtmlEl.classList.add(\"loaded-skeleton-media\");\n\t\t} else if (SVG_ELEMENTS.has(tagName)) {\n\t\t\t// SVG elements rendered as rounded content blocks\n\t\t\thtmlEl.classList.add(\"loaded-skeleton-content\");\n\t\t\thtmlEl.classList.add(\"loaded-skeleton-svg\");\n\t\t} else {\n\t\t\t// Interactive elements (buttons, inputs, etc.)\n\t\t\thtmlEl.classList.add(\"loaded-skeleton-content\");\n\t\t\t// Prevent keyboard focus / interaction while in skeleton mode.\n\t\t\t// aria-hidden does not remove elements from the tab order.\n\t\t\thtmlEl.setAttribute(\"tabindex\", \"-1\");\n\t\t}\n\t};\n\n\tprocessElement(rootElement);\n\tfor (const el of descendants) {\n\t\tprocessElement(el);\n\t}\n}\n","import { type ReactElement, type Ref, version as reactVersion } from \"react\";\nimport { isUsableElement } from \"./applySkeletonClasses\";\n\nexport const REACT_MAJOR_VERSION = Number.parseInt(reactVersion, 10);\nconst IS_REACT_19_OR_NEWER =\n\tNumber.isFinite(REACT_MAJOR_VERSION) && REACT_MAJOR_VERSION >= 19;\n\nexport function resolveRefTarget(node: unknown): Element | null {\n\tif (isUsableElement(node)) return node;\n\tif (node && typeof node === \"object\" && \"nativeElement\" in node) {\n\t\tconst nativeElement = (node as { nativeElement?: unknown }).nativeElement;\n\t\tif (isUsableElement(nativeElement)) return nativeElement;\n\t}\n\treturn null;\n}\n\nexport function getElementDisplayName(element: ReactElement): string {\n\tconst type = element.type;\n\tif (typeof type === \"string\") {\n\t\treturn `<${type}>`;\n\t}\n\tif (typeof type === \"function\") {\n\t\tconst fn = type as { displayName?: string; name?: string };\n\t\treturn `<${fn.displayName || fn.name || \"Unknown\"}>`;\n\t}\n\tif (typeof type === \"object\" && type !== null) {\n\t\tconst obj = type as { displayName?: string; name?: string };\n\t\treturn `<${obj.displayName || obj.name || \"Unknown\"}>`;\n\t}\n\treturn \"<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 */\nexport function getOriginalRef(\n\telement: ReactElement,\n): Ref<unknown> | undefined {\n\t// React 19 style (ref as prop)\n\tconst elementProps = element.props as { ref?: Ref<unknown> } | undefined;\n\tconst propsRef = elementProps?.ref;\n\tif (propsRef !== undefined) return propsRef;\n\n\t// React 19+ warns on element.ref access; skip legacy fallback entirely.\n\tif (IS_REACT_19_OR_NEWER) return undefined;\n\n\t// React 18 style\n\tconst legacyRef = (element as ReactElement & { ref?: Ref<unknown> }).ref;\n\tif (legacyRef !== undefined) return legacyRef;\n\n\treturn undefined;\n}\n\n/**\n * Forward a ref value to the original ref (callback or object style).\n */\nexport function forwardRef(\n\toriginalRef: Ref<unknown> | undefined,\n\tnode: unknown,\n) {\n\tif (!originalRef) return;\n\tif (typeof originalRef === \"function\") {\n\t\toriginalRef(node);\n\t} else {\n\t\t(originalRef as React.MutableRefObject<unknown>).current = node;\n\t}\n}\n","import {\n\tcloneElement,\n\ttype ReactElement,\n\tuseCallback,\n\tuseRef,\n\tuseState,\n} from \"react\";\nimport { isDevEnv } from \"../../utils/isDevEnv\";\nimport { useIsomorphicLayoutEffect } from \"../../utils/useIsomorphicLayoutEffect\";\nimport { SkeletonContext } from \"../SkeletonContext/SkeletonContext\";\nimport { applySkeletonClasses } from \"./applySkeletonClasses\";\nimport {\n\tforwardRef,\n\tgetElementDisplayName,\n\tgetOriginalRef,\n\tresolveRefTarget,\n} from \"./refUtils\";\nimport \"./SmartSkeleton.css\";\n\nexport { applySkeletonClasses } from \"./applySkeletonClasses\";\n\nconst warnedComponents = new Set<string>();\n\nexport interface SmartSkeletonProps {\n\t/** The skeleton element with mock data, rendered when loading */\n\telement: ReactElement;\n\t/** The real content to render when not loading. If omitted, returns null when loading=false. */\n\tchildren?: ReactElement;\n\t/** Whether the skeleton is currently loading. Default: false */\n\tloading?: boolean;\n\t/** Enable shimmer animation. Default: true */\n\tanimate?: boolean;\n\t/** Additional CSS class name */\n\tclassName?: string;\n\t/** Optional seed to stabilize skeleton text widths */\n\tseed?: string | number;\n\t/** Suppress warning when auto-wrapper is applied. Default: false */\n\tsuppressRefWarning?: boolean;\n}\n\nexport function SmartSkeleton({\n\telement,\n\tchildren,\n\tloading = false,\n\tanimate = true,\n\tclassName = \"\",\n\tseed,\n\tsuppressRefWarning = false,\n}: SmartSkeletonProps): ReactElement | null {\n\tconst currentElementType = element.type;\n\tconst currentElementKey = element.key ?? null;\n\tconst hasAppliedRef = useRef(false);\n\tconst refWasCalledRef = useRef(false);\n\tconst lastRefNodeRef = useRef<unknown>(null);\n\tconst needsWrapperRef = useRef(false);\n\tconst [needsWrapper, setNeedsWrapper] = useState(false);\n\tconst previousLoadingRef = useRef(loading);\n\tconst previousElementTypeRef =\n\t\tuseRef<ReactElement[\"type\"]>(currentElementType);\n\tconst previousElementKeyRef = useRef<ReactElement[\"key\"] | null>(\n\t\tcurrentElementKey,\n\t);\n\n\tconst setWrapperState = useCallback((next: boolean) => {\n\t\tneedsWrapperRef.current = next;\n\t\tsetNeedsWrapper(next);\n\t}, []);\n\n\tconst originalRef = getOriginalRef(element);\n\n\tconst enableWrapperWithWarning = useCallback(\n\t\t(reason: \"non-dom-ref\" | \"no-ref-call\") => {\n\t\t\tif (!needsWrapperRef.current) {\n\t\t\t\tsetWrapperState(true);\n\t\t\t}\n\n\t\t\tif (!suppressRefWarning && isDevEnv()) {\n\t\t\t\tconst displayName = getElementDisplayName(element);\n\t\t\t\tif (!warnedComponents.has(displayName)) {\n\t\t\t\t\tif (reason === \"non-dom-ref\") {\n\t\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t\t`[SmartSkeleton] ${displayName} does not forward its ref to a DOM element. ` +\n\t\t\t\t\t\t\t\t`A wrapper <div> has been added automatically. Use forwardRef to avoid this.`,\n\t\t\t\t\t\t);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t\t`[SmartSkeleton] ${displayName} does not accept a ref. ` +\n\t\t\t\t\t\t\t\t`A wrapper <div> has been added automatically. Use forwardRef to avoid this.`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\twarnedComponents.add(displayName);\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t[element, suppressRefWarning, setWrapperState],\n\t);\n\n\tconst refCallback = useCallback(\n\t\t(node: unknown) => {\n\t\t\trefWasCalledRef.current = true;\n\t\t\tlastRefNodeRef.current = node;\n\n\t\t\tconst target = resolveRefTarget(node);\n\n\t\t\tif (target && loading && !hasAppliedRef.current) {\n\t\t\t\tapplySkeletonClasses(target, { animate, seed });\n\t\t\t\thasAppliedRef.current = true;\n\t\t\t}\n\n\t\t\t// Forward ref to original element\n\t\t\tforwardRef(originalRef, node);\n\t\t},\n\t\t[loading, originalRef, animate, seed],\n\t);\n\n\t// Single layout effect: handles identity reset AND wrapper fallback decision.\n\t// Merged into one effect because React fires ref callbacks (during commit)\n\t// BEFORE layout effects. Having a separate reset effect would clear the ref\n\t// data that was just written by the current commit's ref callbacks.\n\tuseIsomorphicLayoutEffect(() => {\n\t\tconst previousLoading = previousLoadingRef.current;\n\t\tconst previousElementType = previousElementTypeRef.current;\n\t\tconst previousElementKey = previousElementKeyRef.current;\n\n\t\tconst didExitLoading = previousLoading && !loading;\n\t\tconst hasElementIdentityChanged =\n\t\t\tpreviousElementType !== currentElementType ||\n\t\t\tpreviousElementKey !== currentElementKey;\n\n\t\tpreviousLoadingRef.current = loading;\n\t\tpreviousElementTypeRef.current = currentElementType;\n\t\tpreviousElementKeyRef.current = currentElementKey;\n\n\t\tif (didExitLoading || hasElementIdentityChanged) {\n\t\t\thasAppliedRef.current = false;\n\t\t\tif (didExitLoading) {\n\t\t\t\trefWasCalledRef.current = false;\n\t\t\t\tlastRefNodeRef.current = null;\n\t\t\t} else if (hasElementIdentityChanged && lastRefNodeRef.current === null) {\n\t\t\t\t// Ignore cleanup-only ref callbacks from previous element identity.\n\t\t\t\trefWasCalledRef.current = false;\n\t\t\t}\n\n\t\t\tif (needsWrapperRef.current) {\n\t\t\t\tsetWrapperState(false);\n\t\t\t\t// Re-render will re-run this effect with the direct path.\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tif (!loading) return;\n\t\tif (needsWrapper) return;\n\n\t\tconst node = lastRefNodeRef.current;\n\t\tconst target = resolveRefTarget(node);\n\n\t\tif (target && !hasAppliedRef.current) {\n\t\t\tapplySkeletonClasses(target, { animate, seed });\n\t\t\thasAppliedRef.current = true;\n\t\t}\n\n\t\tif (refWasCalledRef.current) {\n\t\t\tif (node !== null && !target) {\n\t\t\t\tenableWrapperWithWarning(\"non-dom-ref\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (node !== null) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// Ref callback was only invoked with null during cleanup.\n\t\t\t// Treat as a missing ref call for the current element.\n\t\t\trefWasCalledRef.current = false;\n\t\t}\n\n\t\t// Ref was not called: the component doesn't accept refs.\n\t\t// Switch to wrapper mode synchronously (before browser paint).\n\t\tenableWrapperWithWarning(\"no-ref-call\");\n\t}, [\n\t\tloading,\n\t\tneedsWrapper,\n\t\tcurrentElementType,\n\t\tcurrentElementKey,\n\t\tanimate,\n\t\tseed,\n\t\tenableWrapperWithWarning,\n\t\tsetWrapperState,\n\t]);\n\n\t// Not loading: return children or null\n\tif (!loading) {\n\t\treturn children ?? null;\n\t}\n\n\t// Build merged className for skeleton mode\n\tconst elementProps = element.props as { className?: string };\n\tconst existingClassName = elementProps.className ?? \"\";\n\n\t// Base classes for skeleton mode\n\tconst baseClasses = [\"loaded-skeleton-mode\", animate && \"loaded-animate\"]\n\t\t.filter(Boolean)\n\t\t.join(\" \");\n\n\t// When wrapping: wrapper gets mode + wrapper marker (no bg - it goes on child via ref)\n\tconst wrapperClassName = [baseClasses, \"loaded-skeleton-wrapper\", className]\n\t\t.filter(Boolean)\n\t\t.join(\" \");\n\n\t// When not wrapping: element gets mode + bg directly (for SSR)\n\tconst mergedClassName = [\n\t\texistingClassName,\n\t\tbaseClasses,\n\t\t\"loaded-skeleton-bg\",\n\t\tclassName,\n\t]\n\t\t.filter(Boolean)\n\t\t.join(\" \");\n\n\treturn (\n\t\t<SkeletonContext.Provider value={true}>\n\t\t\t{needsWrapper ? (\n\t\t\t\t<div ref={refCallback} className={wrapperClassName} aria-hidden=\"true\">\n\t\t\t\t\t{element}\n\t\t\t\t</div>\n\t\t\t) : (\n\t\t\t\tcloneElement(element as ReactElement<Record<string, unknown>>, {\n\t\t\t\t\tref: refCallback,\n\t\t\t\t\tclassName: mergedClassName,\n\t\t\t\t\t\"aria-hidden\": true,\n\t\t\t\t})\n\t\t\t)}\n\t\t</SkeletonContext.Provider>\n\t);\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\treturn Boolean(value) && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction toNumberRecord(value: unknown): StoredCounts {\n\tif (!isRecord(value)) return {};\n\tconst result: StoredCounts = {};\n\tfor (const [key, maybeNumber] of Object.entries(value)) {\n\t\tif (typeof maybeNumber === \"number\") {\n\t\t\tresult[key] = maybeNumber;\n\t\t}\n\t}\n\treturn result;\n}\n\nfunction parseStoredCounts(value: unknown): StoredCounts {\n\t// Current schema: { v: 1, counts: Record<string, number> }\n\tif (isRecord(value) && value.v === STORAGE_VERSION) {\n\t\treturn toNumberRecord(value.counts);\n\t}\n\n\t// Legacy schema: Record<string, number>\n\treturn toNumberRecord(value);\n}\n\nfunction readStoredCountsFromKey(key: string): StoredCounts {\n\tif (typeof localStorage === \"undefined\") return {};\n\ttry {\n\t\tconst raw = localStorage.getItem(key);\n\t\tif (raw === null) return {};\n\t\treturn parseStoredCounts(JSON.parse(raw));\n\t} catch {\n\t\treturn {};\n\t}\n}\n\nfunction writeStoredCounts(counts: StoredCounts): void {\n\tif (typeof localStorage === \"undefined\") return;\n\tconst payload: StoredPayloadV1 = { v: STORAGE_VERSION, counts };\n\ttry {\n\t\tlocalStorage.setItem(STORAGE_KEY, JSON.stringify(payload));\n\t} catch {\n\t\t// Silently fail if localStorage is full or unavailable\n\t}\n}\n\nfunction getStoredCounts(): Record<string, number> {\n\tif (typeof localStorage === \"undefined\") return {};\n\n\ttry {\n\t\tconst rawNew = localStorage.getItem(STORAGE_KEY);\n\t\tif (rawNew !== null) {\n\t\t\treturn parseStoredCounts(JSON.parse(rawNew));\n\t\t}\n\n\t\t// Backward compatibility: migrate legacy key once if present.\n\t\tconst legacyCounts = readStoredCountsFromKey(LEGACY_STORAGE_KEY);\n\t\tif (Object.keys(legacyCounts).length > 0) {\n\t\t\twriteStoredCounts(legacyCounts);\n\t\t}\n\t\treturn legacyCounts;\n\t} catch {\n\t\treturn {};\n\t}\n}\n\nfunction getStoredCount(key: string): number | null {\n\tconst counts = getStoredCounts();\n\tconst value = counts[key];\n\treturn typeof value === \"number\" ? value : null;\n}\n\nfunction setStoredCount(key: string, count: number): void {\n\tif (typeof localStorage === \"undefined\") return;\n\n\ttry {\n\t\tconst counts = getStoredCounts();\n\t\tcounts[key] = count;\n\t\twriteStoredCounts(counts);\n\t} catch {\n\t\t// Silently fail if localStorage is full or unavailable\n\t}\n}\n\nexport interface UsePersistedCountOptions {\n\tstorageKey?: string;\n\tdefaultCount?: number;\n\tcurrentCount?: number;\n\tloading: boolean;\n\tminCount?: number;\n\tmaxCount?: number;\n}\n\nexport function usePersistedCount({\n\tstorageKey,\n\tdefaultCount = 3,\n\tcurrentCount,\n\tloading,\n\tminCount = 1,\n\tmaxCount,\n}: UsePersistedCountOptions): number {\n\t// Always start from the default to match SSR output, then (on the client)\n\t// sync to the persisted value in a layout effect before first paint.\n\tconst [count, setCount] = useState<number>(() =>\n\t\tclampCount(defaultCount, minCount, maxCount),\n\t);\n\n\tconst hasWarnedRef = useRef(false);\n\n\tuseIsomorphicLayoutEffect(() => {\n\t\tif (!storageKey) return;\n\t\tconst stored = getStoredCount(storageKey);\n\t\tif (stored === null) return;\n\t\tconst next = clampCount(stored, minCount, maxCount);\n\t\tsetCount((prev) => (Object.is(prev, next) ? prev : next));\n\t}, [storageKey, minCount, maxCount]);\n\n\tuseEffect(() => {\n\t\tif (!loading && currentCount !== undefined) {\n\t\t\tconst newCount = clampCount(currentCount, minCount, maxCount);\n\t\t\tsetCount(newCount);\n\n\t\t\tif (storageKey) {\n\t\t\t\tsetStoredCount(storageKey, newCount);\n\t\t\t}\n\t\t}\n\t}, [loading, currentCount, storageKey, minCount, maxCount]);\n\n\tuseEffect(() => {\n\t\tif (isDevEnv() && !storageKey && !hasWarnedRef.current) {\n\t\t\tconsole.warn(\n\t\t\t\t\"[Loaded] SmartSkeletonList used without storageKey. \" +\n\t\t\t\t\t\"The count will reset on remount. Add a storageKey to persist across sessions.\",\n\t\t\t);\n\t\t\thasWarnedRef.current = true;\n\t\t}\n\t}, [storageKey]);\n\n\treturn count;\n}\n\nfunction clampCount(\n\tvalue: number,\n\tmin: number,\n\tmax: number | undefined,\n): number {\n\tlet result = Math.max(value, min);\n\tif (max !== undefined) {\n\t\tresult = Math.min(result, max);\n\t}\n\treturn 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\t/** Whether the list is currently loading. Default: false */\n\tloading?: boolean;\n\t/** The items to render. Pass undefined while loading. */\n\titems: T[] | undefined;\n\t/** Render function for each item when loaded */\n\trenderItem: (item: T, index: number) => ReactElement;\n\t/** Render function for skeleton placeholders */\n\trenderSkeleton: (index: number) => ReactElement;\n\t/** Key for localStorage persistence. Without it, count resets on remount. */\n\tstorageKey?: string;\n\t/** Initial skeleton count before any data is known. Default: 3 */\n\tdefaultCount?: number;\n\t/** Minimum skeletons to show. Default: 1 */\n\tminCount?: number;\n\t/** Maximum skeletons to show */\n\tmaxCount?: number;\n\t/** Enable shimmer animation. Default: true */\n\tanimate?: boolean;\n\t/** Optional seed to stabilize skeleton text widths */\n\tseed?: string | number;\n\t/** Suppress warning when auto-wrapper is applied. Default: false */\n\tsuppressRefWarning?: boolean;\n\t/** Extract unique key for each item. Default: index */\n\tkeyExtractor?: (item: T, index: number) => string | number;\n}\n\nexport function SmartSkeletonList<T>({\n\tloading = false,\n\titems,\n\trenderItem,\n\trenderSkeleton,\n\tstorageKey,\n\tdefaultCount = 3,\n\tminCount = 1,\n\tmaxCount,\n\tanimate = true,\n\tseed,\n\tsuppressRefWarning = false,\n\tkeyExtractor = (_, index) => index,\n}: SmartSkeletonListProps<T>): ReactElement | null {\n\tconst skeletonCount = usePersistedCount({\n\t\tstorageKey,\n\t\tdefaultCount,\n\t\tcurrentCount: items?.length,\n\t\tloading,\n\t\tminCount,\n\t\tmaxCount,\n\t});\n\n\tif (loading) {\n\t\tconst skeletons = new Array(skeletonCount);\n\t\tfor (let index = 0; index < skeletonCount; index += 1) {\n\t\t\tconst itemSeed = seed === undefined ? `${index}` : `${seed}:${index}`;\n\t\t\tskeletons[index] = (\n\t\t\t\t<SmartSkeleton\n\t\t\t\t\tkey={`skeleton-${index}`}\n\t\t\t\t\tloading={true}\n\t\t\t\t\telement={renderSkeleton(index)}\n\t\t\t\t\tanimate={animate}\n\t\t\t\t\tseed={itemSeed}\n\t\t\t\t\tsuppressRefWarning={suppressRefWarning}\n\t\t\t\t/>\n\t\t\t);\n\t\t}\n\t\treturn <>{skeletons}</>;\n\t}\n\n\tif (!items || items.length === 0) {\n\t\treturn null;\n\t}\n\n\treturn (\n\t\t<>\n\t\t\t{items.map((item, index) => (\n\t\t\t\t<Fragment key={keyExtractor(item, index)}>\n\t\t\t\t\t{renderItem(item, index)}\n\t\t\t\t</Fragment>\n\t\t\t))}\n\t\t</>\n\t);\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/components/SkeletonContext/SkeletonContext.tsx","../src/utils/isDevEnv.ts","../src/utils/useIsomorphicLayoutEffect.ts","../src/components/SmartSkeleton/applySkeletonClasses.ts","../src/components/SmartSkeleton/refUtils.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","isElement","value","maybeElement","isUsableElement","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","variant","baseSeed","htmlRoot","firstChild","descendants","textIndex","processElement","htmlEl","textContent","widthCh","REACT_MAJOR_VERSION","reactVersion","IS_REACT_19_OR_NEWER","resolveRefTarget","node","nativeElement","getElementDisplayName","element","type","fn","obj","getOriginalRef","propsRef","legacyRef","forwardRef","originalRef","warnedComponents","SmartSkeleton","children","loading","className","suppressRefWarning","currentElementType","currentElementKey","hasAppliedRef","useRef","refWasCalledRef","lastRefNodeRef","needsWrapperRef","needsWrapper","setNeedsWrapper","useState","previousLoadingRef","previousElementTypeRef","previousElementKeyRef","setWrapperState","useCallback","next","enableWrapperWithWarning","reason","displayName","refCallback","target","previousLoading","previousElementType","previousElementKey","didExitLoading","hasElementIdentityChanged","existingClassName","baseClasses","filledOnlyClass","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","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\treturn useContext(SkeletonContext);\n}\n","export function isDevEnv(): boolean {\n\tconst maybeGlobal = globalThis as unknown as Record<string, unknown>;\n\n\t// Manual override for environments where NODE_ENV isn't injected.\n\t// Example: `globalThis.__REACT_LOADED_DEV__ = true`.\n\tconst override = maybeGlobal.__REACT_LOADED_DEV__;\n\tif (typeof override === \"boolean\") return override;\n\n\t// Common global used by some toolchains/runtimes.\n\tconst devFlag = maybeGlobal.__DEV__;\n\tif (typeof devFlag === \"boolean\") return devFlag;\n\n\tconst maybeProcess = (globalThis as unknown as { process?: unknown }).process;\n\tconst nodeEnv =\n\t\ttypeof maybeProcess === \"object\" && maybeProcess !== null\n\t\t\t? (maybeProcess as { env?: { NODE_ENV?: unknown } }).env?.NODE_ENV\n\t\t\t: undefined;\n\n\tif (typeof nodeEnv === \"string\") {\n\t\treturn nodeEnv !== \"production\";\n\t}\n\n\t// No environment detected — assume production (convention: opt-in to dev mode).\n\treturn false;\n}\n","import { useEffect, useLayoutEffect } from \"react\";\n\nconst canUseDOM =\n\ttypeof globalThis !== \"undefined\" &&\n\ttypeof (globalThis as { document?: unknown }).document !== \"undefined\";\n\nexport const useIsomorphicLayoutEffect = canUseDOM\n\t? useLayoutEffect\n\t: useEffect;\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\tif (!value || typeof value !== \"object\") return false;\n\tconst maybeElement = value as Element;\n\t// Must have nodeType 1 (Element) and a working querySelectorAll\n\tif (maybeElement.nodeType !== 1) return false;\n\tif (typeof maybeElement.tagName !== \"string\") return false;\n\tif (typeof maybeElement.querySelectorAll !== \"function\") return false;\n\t// Additional check: instanceof Element if available\n\tif (typeof Element !== \"undefined\" && !(value instanceof Element)) {\n\t\treturn false;\n\t}\n\treturn true;\n}\n\nexport function isUsableElement(value: unknown): value is Element {\n\tif (!isElement(value)) return false;\n\t// Test that querySelectorAll actually works\n\ttry {\n\t\t(value as Element).querySelectorAll(\"*\");\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nconst MEDIA_ELEMENTS = new Set([\"IMG\", \"VIDEO\", \"CANVAS\"]);\nconst SVG_ELEMENTS = new Set([\"SVG\"]);\n\nconst INTERACTIVE_ELEMENTS = new Set([\n\t\"BUTTON\",\n\t\"INPUT\",\n\t\"TEXTAREA\",\n\t\"SELECT\",\n\t\"A\",\n]);\n\nconst BUTTON_LIKE_SELECTOR = \"button,input,textarea,select,a,[role='button']\";\nconst SKIPPED_TAGS = new Set([\n\t\"SCRIPT\",\n\t\"STYLE\",\n\t\"LINK\",\n\t\"META\",\n\t\"NOSCRIPT\",\n\t\"TEMPLATE\",\n]);\n\nfunction getTagName(el: Element): string {\n\treturn el.tagName.toUpperCase();\n}\n\nfunction isButtonLikeElement(el: Element, tagName = getTagName(el)): boolean {\n\tif (INTERACTIVE_ELEMENTS.has(tagName)) return true;\n\tconst role = el.getAttribute(\"role\");\n\treturn role === \"button\";\n}\n\nfunction isButtonLikeDescendant(el: Element, tagName: string): boolean {\n\tconst closestButton = el.closest(BUTTON_LIKE_SELECTOR);\n\treturn Boolean(closestButton && !isButtonLikeElement(el, tagName));\n}\n\nfunction isContentElement(el: Element, tagName = getTagName(el)): boolean {\n\tif (MEDIA_ELEMENTS.has(tagName)) return true;\n\tif (SVG_ELEMENTS.has(tagName)) return true;\n\tif (isButtonLikeElement(el, tagName)) return true;\n\n\tconst isLeafNode = el.childElementCount === 0;\n\n\t// Text elements that are leaf nodes (no child elements, only text)\n\tif (isLeafNode && el.textContent?.trim()) return true;\n\n\treturn 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\tconst textLength = text.length;\n\tconst jitterRange = Math.max(4, 0.8 * textLength);\n\tconst jitter = deterministicJitter(seedKey) * jitterRange;\n\tconst width = textLength + 2 + jitter;\n\treturn Math.max(TEXT_WIDTH_MIN_CH, Math.min(TEXT_WIDTH_MAX_CH, width));\n}\n\nfunction deterministicJitter(seedKey: string): number {\n\tif (!seedKey) return 0;\n\tlet hash = 2166136261;\n\tfor (let index = 0; index < seedKey.length; index += 1) {\n\t\thash ^= seedKey.charCodeAt(index);\n\t\thash = Math.imul(hash, 16777619);\n\t}\n\tconst normalized = (hash >>> 0) / 0xffffffff;\n\treturn normalized * 2 - 1;\n}\n\nfunction resolveTextAlign(el: HTMLElement): \"left\" | \"center\" | \"right\" {\n\tconst align = globalThis.getComputedStyle(el).textAlign;\n\tif (align === \"center\") return \"center\";\n\tif (align === \"right\" || align === \"end\") return \"right\";\n\treturn \"left\";\n}\n\nexport function applySkeletonClasses(\n\trootElement: Element,\n\toptions: {\n\t\tanimate?: boolean;\n\t\tseed?: string | number;\n\t\tvariant?: \"filled\" | \"ghost\";\n\t} = {},\n): void {\n\tconst { animate = true, seed, variant = \"filled\" } = options;\n\tconst baseSeed =\n\t\tseed === undefined || seed === null ? \"loaded\" : String(seed);\n\n\tif (!isElement(rootElement)) {\n\t\treturn;\n\t}\n\n\tconst htmlRoot = rootElement as HTMLElement;\n\n\t// Apply skeleton mode to the root element\n\thtmlRoot.classList.add(\"loaded-skeleton-mode\");\n\n\tif (animate) {\n\t\thtmlRoot.classList.add(\"loaded-animate\");\n\t} else {\n\t\thtmlRoot.classList.remove(\"loaded-animate\");\n\t}\n\n\t// Apply background class for standalone usage.\n\t// Wrapper mode targets first child to preserve border-radius from user content.\n\tconst isWrapper = htmlRoot.classList.contains(\"loaded-skeleton-wrapper\");\n\tif (!isWrapper) {\n\t\tif (variant === \"filled\") {\n\t\t\thtmlRoot.classList.add(\"loaded-skeleton-bg\");\n\t\t} else {\n\t\t\thtmlRoot.classList.remove(\"loaded-skeleton-bg\");\n\t\t}\n\t} else {\n\t\tconst firstChild = htmlRoot.firstElementChild as HTMLElement | null;\n\t\tif (firstChild && variant === \"filled\") {\n\t\t\tfirstChild.classList.add(\"loaded-skeleton-bg\");\n\t\t} else if (firstChild) {\n\t\t\tfirstChild.classList.remove(\"loaded-skeleton-bg\");\n\t\t}\n\t\thtmlRoot.classList.remove(\"loaded-skeleton-bg\");\n\t}\n\n\t// Only add specific classes where needed (text, media, content)\n\tconst descendants = rootElement.getElementsByTagName(\"*\");\n\n\tlet textIndex = 0;\n\n\tconst processElement = (el: Element) => {\n\t\tconst tagName = getTagName(el);\n\t\tif (SKIPPED_TAGS.has(tagName)) return;\n\t\tif (!isContentElement(el, tagName)) return;\n\n\t\tconst htmlEl = el as HTMLElement;\n\t\tconst isInsideButtonLike = isButtonLikeDescendant(el, tagName);\n\t\tif (isInsideButtonLike) {\n\t\t\thtmlEl.classList.add(\"loaded-skeleton-force-hide\");\n\t\t\treturn;\n\t\t}\n\n\t\tconst textContent = el.textContent?.trim();\n\t\tconst isLeafWithText = el.childElementCount === 0 && textContent;\n\n\t\tif (\n\t\t\tisLeafWithText &&\n\t\t\t!MEDIA_ELEMENTS.has(tagName) &&\n\t\t\t!SVG_ELEMENTS.has(tagName) &&\n\t\t\t!isButtonLikeElement(el, tagName)\n\t\t) {\n\t\t\t// Text elements: overlay bar with ch-based width\n\t\t\thtmlEl.classList.add(\"loaded-text-skeleton\");\n\t\t\thtmlEl.dataset.skeletonAlign = resolveTextAlign(htmlEl);\n\t\t\tconst seedKey = `${baseSeed}|${textIndex}|${textContent ?? \"\"}`;\n\t\t\ttextIndex += 1;\n\t\t\tconst widthCh = calculateTextWidthCh(textContent ?? \"\", seedKey);\n\t\t\thtmlEl.style.setProperty(\"--skeleton-text-width\", `${widthCh}ch`);\n\t\t} else if (MEDIA_ELEMENTS.has(tagName)) {\n\t\t\t// Media elements\n\t\t\thtmlEl.classList.add(\"loaded-skeleton-media\");\n\t\t} else if (SVG_ELEMENTS.has(tagName)) {\n\t\t\t// SVG elements rendered as rounded content blocks\n\t\t\thtmlEl.classList.add(\"loaded-skeleton-content\");\n\t\t\thtmlEl.classList.add(\"loaded-skeleton-svg\");\n\t\t} else {\n\t\t\t// Interactive elements (buttons, inputs, etc.)\n\t\t\thtmlEl.classList.add(\"loaded-skeleton-content\");\n\t\t\t// Prevent keyboard focus / interaction while in skeleton mode.\n\t\t\t// aria-hidden does not remove elements from the tab order.\n\t\t\thtmlEl.setAttribute(\"tabindex\", \"-1\");\n\t\t}\n\t};\n\n\tprocessElement(rootElement);\n\tfor (const el of descendants) {\n\t\tprocessElement(el);\n\t}\n}\n","import { type ReactElement, type Ref, version as reactVersion } from \"react\";\nimport { isUsableElement } from \"./applySkeletonClasses\";\n\nexport const REACT_MAJOR_VERSION = Number.parseInt(reactVersion, 10);\nconst IS_REACT_19_OR_NEWER =\n\tNumber.isFinite(REACT_MAJOR_VERSION) && REACT_MAJOR_VERSION >= 19;\n\nexport function resolveRefTarget(node: unknown): Element | null {\n\tif (isUsableElement(node)) return node;\n\tif (node && typeof node === \"object\" && \"nativeElement\" in node) {\n\t\tconst nativeElement = (node as { nativeElement?: unknown }).nativeElement;\n\t\tif (isUsableElement(nativeElement)) return nativeElement;\n\t}\n\treturn null;\n}\n\nexport function getElementDisplayName(element: ReactElement): string {\n\tconst type = element.type;\n\tif (typeof type === \"string\") {\n\t\treturn `<${type}>`;\n\t}\n\tif (typeof type === \"function\") {\n\t\tconst fn = type as { displayName?: string; name?: string };\n\t\treturn `<${fn.displayName || fn.name || \"Unknown\"}>`;\n\t}\n\tif (typeof type === \"object\" && type !== null) {\n\t\tconst obj = type as { displayName?: string; name?: string };\n\t\treturn `<${obj.displayName || obj.name || \"Unknown\"}>`;\n\t}\n\treturn \"<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 */\nexport function getOriginalRef(\n\telement: ReactElement,\n): Ref<unknown> | undefined {\n\t// React 19 style (ref as prop)\n\tconst elementProps = element.props as { ref?: Ref<unknown> } | undefined;\n\tconst propsRef = elementProps?.ref;\n\tif (propsRef !== undefined) return propsRef;\n\n\t// React 19+ warns on element.ref access; skip legacy fallback entirely.\n\tif (IS_REACT_19_OR_NEWER) return undefined;\n\n\t// React 18 style\n\tconst legacyRef = (element as ReactElement & { ref?: Ref<unknown> }).ref;\n\tif (legacyRef !== undefined) return legacyRef;\n\n\treturn undefined;\n}\n\n/**\n * Forward a ref value to the original ref (callback or object style).\n */\nexport function forwardRef(\n\toriginalRef: Ref<unknown> | undefined,\n\tnode: unknown,\n) {\n\tif (!originalRef) return;\n\tif (typeof originalRef === \"function\") {\n\t\toriginalRef(node);\n\t} else {\n\t\t(originalRef as React.MutableRefObject<unknown>).current = node;\n\t}\n}\n","import {\n\tcloneElement,\n\ttype ReactElement,\n\tuseCallback,\n\tuseRef,\n\tuseState,\n} from \"react\";\nimport { isDevEnv } from \"../../utils/isDevEnv\";\nimport { useIsomorphicLayoutEffect } from \"../../utils/useIsomorphicLayoutEffect\";\nimport { SkeletonContext } from \"../SkeletonContext/SkeletonContext\";\nimport { applySkeletonClasses } from \"./applySkeletonClasses\";\nimport {\n\tforwardRef,\n\tgetElementDisplayName,\n\tgetOriginalRef,\n\tresolveRefTarget,\n} from \"./refUtils\";\nimport \"./SmartSkeleton.css\";\n\nexport { applySkeletonClasses } from \"./applySkeletonClasses\";\n\nconst warnedComponents = new Set<string>();\n\nexport type SmartSkeletonVariant = \"filled\" | \"ghost\";\n\nexport interface SmartSkeletonProps {\n\t/** The skeleton element with mock data, rendered when loading */\n\telement: ReactElement;\n\t/** The real content to render when not loading. If omitted, returns null when loading=false. */\n\tchildren?: ReactElement;\n\t/** Whether the skeleton is currently loading. Default: false */\n\tloading?: boolean;\n\t/** Enable shimmer animation. Default: true */\n\tanimate?: boolean;\n\t/** Additional CSS class name */\n\tclassName?: string;\n\t/** Optional seed to stabilize skeleton text widths */\n\tseed?: string | number;\n\t/** Visual variant for skeleton surface. `filled` adds a background, `ghost` does not. Default: \"filled\" */\n\tvariant?: SmartSkeletonVariant;\n\t/** Suppress warning when auto-wrapper is applied. Default: false */\n\tsuppressRefWarning?: boolean;\n}\n\nexport function SmartSkeleton({\n\telement,\n\tchildren,\n\tloading = false,\n\tanimate = true,\n\tclassName = \"\",\n\tseed,\n\tvariant = \"filled\",\n\tsuppressRefWarning = false,\n}: SmartSkeletonProps): ReactElement | null {\n\tconst currentElementType = element.type;\n\tconst currentElementKey = element.key ?? null;\n\tconst hasAppliedRef = useRef(false);\n\tconst refWasCalledRef = useRef(false);\n\tconst lastRefNodeRef = useRef<unknown>(null);\n\tconst needsWrapperRef = useRef(false);\n\tconst [needsWrapper, setNeedsWrapper] = useState(false);\n\tconst previousLoadingRef = useRef(loading);\n\tconst previousElementTypeRef =\n\t\tuseRef<ReactElement[\"type\"]>(currentElementType);\n\tconst previousElementKeyRef = useRef<ReactElement[\"key\"] | null>(\n\t\tcurrentElementKey,\n\t);\n\n\tconst setWrapperState = useCallback((next: boolean) => {\n\t\tneedsWrapperRef.current = next;\n\t\tsetNeedsWrapper(next);\n\t}, []);\n\n\tconst originalRef = getOriginalRef(element);\n\n\tconst enableWrapperWithWarning = useCallback(\n\t\t(reason: \"non-dom-ref\" | \"no-ref-call\") => {\n\t\t\tif (!needsWrapperRef.current) {\n\t\t\t\tsetWrapperState(true);\n\t\t\t}\n\n\t\t\tif (!suppressRefWarning && isDevEnv()) {\n\t\t\t\tconst displayName = getElementDisplayName(element);\n\t\t\t\tif (!warnedComponents.has(displayName)) {\n\t\t\t\t\tif (reason === \"non-dom-ref\") {\n\t\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t\t`[SmartSkeleton] ${displayName} does not forward its ref to a DOM element. ` +\n\t\t\t\t\t\t\t\t`A wrapper <div> has been added automatically. Use forwardRef to avoid this.`,\n\t\t\t\t\t\t);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t\t`[SmartSkeleton] ${displayName} does not accept a ref. ` +\n\t\t\t\t\t\t\t\t`A wrapper <div> has been added automatically. Use forwardRef to avoid this.`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\twarnedComponents.add(displayName);\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t[element, suppressRefWarning, setWrapperState],\n\t);\n\n\tconst refCallback = useCallback(\n\t\t(node: unknown) => {\n\t\t\trefWasCalledRef.current = true;\n\t\t\tlastRefNodeRef.current = node;\n\n\t\t\tconst target = resolveRefTarget(node);\n\n\t\t\tif (target && loading && !hasAppliedRef.current) {\n\t\t\t\tapplySkeletonClasses(target, { animate, seed, variant });\n\t\t\t\thasAppliedRef.current = true;\n\t\t\t}\n\n\t\t\t// Forward ref to original element\n\t\t\tforwardRef(originalRef, node);\n\t\t},\n\t\t[loading, originalRef, animate, seed, variant],\n\t);\n\n\t// Single layout effect: handles identity reset AND wrapper fallback decision.\n\t// Merged into one effect because React fires ref callbacks (during commit)\n\t// BEFORE layout effects. Having a separate reset effect would clear the ref\n\t// data that was just written by the current commit's ref callbacks.\n\tuseIsomorphicLayoutEffect(() => {\n\t\tconst previousLoading = previousLoadingRef.current;\n\t\tconst previousElementType = previousElementTypeRef.current;\n\t\tconst previousElementKey = previousElementKeyRef.current;\n\n\t\tconst didExitLoading = previousLoading && !loading;\n\t\tconst hasElementIdentityChanged =\n\t\t\tpreviousElementType !== currentElementType ||\n\t\t\tpreviousElementKey !== currentElementKey;\n\n\t\tpreviousLoadingRef.current = loading;\n\t\tpreviousElementTypeRef.current = currentElementType;\n\t\tpreviousElementKeyRef.current = currentElementKey;\n\n\t\tif (didExitLoading || hasElementIdentityChanged) {\n\t\t\thasAppliedRef.current = false;\n\t\t\tif (didExitLoading) {\n\t\t\t\trefWasCalledRef.current = false;\n\t\t\t\tlastRefNodeRef.current = null;\n\t\t\t} else if (hasElementIdentityChanged && lastRefNodeRef.current === null) {\n\t\t\t\t// Ignore cleanup-only ref callbacks from previous element identity.\n\t\t\t\trefWasCalledRef.current = false;\n\t\t\t}\n\n\t\t\tif (needsWrapperRef.current) {\n\t\t\t\tsetWrapperState(false);\n\t\t\t\t// Re-render will re-run this effect with the direct path.\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tif (!loading) return;\n\t\tif (needsWrapper) return;\n\n\t\tconst node = lastRefNodeRef.current;\n\t\tconst target = resolveRefTarget(node);\n\n\t\tif (target && !hasAppliedRef.current) {\n\t\t\tapplySkeletonClasses(target, { animate, seed, variant });\n\t\t\thasAppliedRef.current = true;\n\t\t}\n\n\t\tif (refWasCalledRef.current) {\n\t\t\tif (node !== null && !target) {\n\t\t\t\tenableWrapperWithWarning(\"non-dom-ref\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (node !== null) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// Ref callback was only invoked with null during cleanup.\n\t\t\t// Treat as a missing ref call for the current element.\n\t\t\trefWasCalledRef.current = false;\n\t\t}\n\n\t\t// Ref was not called: the component doesn't accept refs.\n\t\t// Switch to wrapper mode synchronously (before browser paint).\n\t\tenableWrapperWithWarning(\"no-ref-call\");\n\t}, [\n\t\tloading,\n\t\tneedsWrapper,\n\t\tcurrentElementType,\n\t\tcurrentElementKey,\n\t\tanimate,\n\t\tseed,\n\t\tvariant,\n\t\tenableWrapperWithWarning,\n\t\tsetWrapperState,\n\t]);\n\n\t// Not loading: return children or null\n\tif (!loading) {\n\t\treturn children ?? null;\n\t}\n\n\t// Build merged className for skeleton mode\n\tconst elementProps = element.props as { className?: string };\n\tconst existingClassName = elementProps.className ?? \"\";\n\n\t// Base classes for skeleton mode\n\tconst baseClasses = [\"loaded-skeleton-mode\", animate && \"loaded-animate\"]\n\t\t.filter(Boolean)\n\t\t.join(\" \");\n\tconst filledOnlyClass = variant === \"filled\" ? \"loaded-skeleton-bg\" : \"\";\n\n\t// When wrapping: wrapper gets mode + wrapper marker (no bg - it goes on child via ref)\n\tconst wrapperClassName = [baseClasses, \"loaded-skeleton-wrapper\", className]\n\t\t.filter(Boolean)\n\t\t.join(\" \");\n\n\t// When not wrapping: element gets mode + bg directly (for SSR)\n\tconst mergedClassName = [\n\t\texistingClassName,\n\t\tbaseClasses,\n\t\tfilledOnlyClass,\n\t\tclassName,\n\t]\n\t\t.filter(Boolean)\n\t\t.join(\" \");\n\n\treturn (\n\t\t<SkeletonContext.Provider value={true}>\n\t\t\t{needsWrapper ? (\n\t\t\t\t<div ref={refCallback} className={wrapperClassName} aria-hidden=\"true\">\n\t\t\t\t\t{element}\n\t\t\t\t</div>\n\t\t\t) : (\n\t\t\t\tcloneElement(element as ReactElement<Record<string, unknown>>, {\n\t\t\t\t\tref: refCallback,\n\t\t\t\t\tclassName: mergedClassName,\n\t\t\t\t\t\"aria-hidden\": true,\n\t\t\t\t})\n\t\t\t)}\n\t\t</SkeletonContext.Provider>\n\t);\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\treturn Boolean(value) && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction toNumberRecord(value: unknown): StoredCounts {\n\tif (!isRecord(value)) return {};\n\tconst result: StoredCounts = {};\n\tfor (const [key, maybeNumber] of Object.entries(value)) {\n\t\tif (typeof maybeNumber === \"number\") {\n\t\t\tresult[key] = maybeNumber;\n\t\t}\n\t}\n\treturn result;\n}\n\nfunction parseStoredCounts(value: unknown): StoredCounts {\n\t// Current schema: { v: 1, counts: Record<string, number> }\n\tif (isRecord(value) && value.v === STORAGE_VERSION) {\n\t\treturn toNumberRecord(value.counts);\n\t}\n\n\t// Legacy schema: Record<string, number>\n\treturn toNumberRecord(value);\n}\n\nfunction readStoredCountsFromKey(key: string): StoredCounts {\n\tif (typeof localStorage === \"undefined\") return {};\n\ttry {\n\t\tconst raw = localStorage.getItem(key);\n\t\tif (raw === null) return {};\n\t\treturn parseStoredCounts(JSON.parse(raw));\n\t} catch {\n\t\treturn {};\n\t}\n}\n\nfunction writeStoredCounts(counts: StoredCounts): void {\n\tif (typeof localStorage === \"undefined\") return;\n\tconst payload: StoredPayloadV1 = { v: STORAGE_VERSION, counts };\n\ttry {\n\t\tlocalStorage.setItem(STORAGE_KEY, JSON.stringify(payload));\n\t} catch {\n\t\t// Silently fail if localStorage is full or unavailable\n\t}\n}\n\nfunction getStoredCounts(): Record<string, number> {\n\tif (typeof localStorage === \"undefined\") return {};\n\n\ttry {\n\t\tconst rawNew = localStorage.getItem(STORAGE_KEY);\n\t\tif (rawNew !== null) {\n\t\t\treturn parseStoredCounts(JSON.parse(rawNew));\n\t\t}\n\n\t\t// Backward compatibility: migrate legacy key once if present.\n\t\tconst legacyCounts = readStoredCountsFromKey(LEGACY_STORAGE_KEY);\n\t\tif (Object.keys(legacyCounts).length > 0) {\n\t\t\twriteStoredCounts(legacyCounts);\n\t\t}\n\t\treturn legacyCounts;\n\t} catch {\n\t\treturn {};\n\t}\n}\n\nfunction getStoredCount(key: string): number | null {\n\tconst counts = getStoredCounts();\n\tconst value = counts[key];\n\treturn typeof value === \"number\" ? value : null;\n}\n\nfunction setStoredCount(key: string, count: number): void {\n\tif (typeof localStorage === \"undefined\") return;\n\n\ttry {\n\t\tconst counts = getStoredCounts();\n\t\tcounts[key] = count;\n\t\twriteStoredCounts(counts);\n\t} catch {\n\t\t// Silently fail if localStorage is full or unavailable\n\t}\n}\n\nexport interface UsePersistedCountOptions {\n\tstorageKey?: string;\n\tdefaultCount?: number;\n\tcurrentCount?: number;\n\tloading: boolean;\n\tminCount?: number;\n\tmaxCount?: number;\n}\n\nexport function usePersistedCount({\n\tstorageKey,\n\tdefaultCount = 3,\n\tcurrentCount,\n\tloading,\n\tminCount = 1,\n\tmaxCount,\n}: UsePersistedCountOptions): number {\n\t// Always start from the default to match SSR output, then (on the client)\n\t// sync to the persisted value in a layout effect before first paint.\n\tconst [count, setCount] = useState<number>(() =>\n\t\tclampCount(defaultCount, minCount, maxCount),\n\t);\n\n\tconst hasWarnedRef = useRef(false);\n\n\tuseIsomorphicLayoutEffect(() => {\n\t\tif (!storageKey) return;\n\t\tconst stored = getStoredCount(storageKey);\n\t\tif (stored === null) return;\n\t\tconst next = clampCount(stored, minCount, maxCount);\n\t\tsetCount((prev) => (Object.is(prev, next) ? prev : next));\n\t}, [storageKey, minCount, maxCount]);\n\n\tuseEffect(() => {\n\t\tif (!loading && currentCount !== undefined) {\n\t\t\tconst newCount = clampCount(currentCount, minCount, maxCount);\n\t\t\tsetCount(newCount);\n\n\t\t\tif (storageKey) {\n\t\t\t\tsetStoredCount(storageKey, newCount);\n\t\t\t}\n\t\t}\n\t}, [loading, currentCount, storageKey, minCount, maxCount]);\n\n\tuseEffect(() => {\n\t\tif (isDevEnv() && !storageKey && !hasWarnedRef.current) {\n\t\t\tconsole.warn(\n\t\t\t\t\"[Loaded] SmartSkeletonList used without storageKey. \" +\n\t\t\t\t\t\"The count will reset on remount. Add a storageKey to persist across sessions.\",\n\t\t\t);\n\t\t\thasWarnedRef.current = true;\n\t\t}\n\t}, [storageKey]);\n\n\treturn count;\n}\n\nfunction clampCount(\n\tvalue: number,\n\tmin: number,\n\tmax: number | undefined,\n): number {\n\tlet result = Math.max(value, min);\n\tif (max !== undefined) {\n\t\tresult = Math.min(result, max);\n\t}\n\treturn result;\n}\n","import { Fragment, type ReactElement } from \"react\";\nimport { usePersistedCount } from \"../../hooks/usePersistedCount/usePersistedCount\";\nimport {\n\tSmartSkeleton,\n\ttype SmartSkeletonVariant,\n} from \"../SmartSkeleton/SmartSkeleton\";\n\nexport interface SmartSkeletonListProps<T> {\n\t/** Whether the list is currently loading. Default: false */\n\tloading?: boolean;\n\t/** The items to render. Pass undefined while loading. */\n\titems: T[] | undefined;\n\t/** Render function for each item when loaded */\n\trenderItem: (item: T, index: number) => ReactElement;\n\t/** Render function for skeleton placeholders */\n\trenderSkeleton: (index: number) => ReactElement;\n\t/** Key for localStorage persistence. Without it, count resets on remount. */\n\tstorageKey?: string;\n\t/** Initial skeleton count before any data is known. Default: 3 */\n\tdefaultCount?: number;\n\t/** Minimum skeletons to show. Default: 1 */\n\tminCount?: number;\n\t/** Maximum skeletons to show */\n\tmaxCount?: number;\n\t/** Enable shimmer animation. Default: true */\n\tanimate?: boolean;\n\t/** Optional seed to stabilize skeleton text widths */\n\tseed?: string | number;\n\t/** Visual variant for skeleton surface. `filled` adds a background, `ghost` does not. Default: \"filled\" */\n\tvariant?: SmartSkeletonVariant;\n\t/** Suppress warning when auto-wrapper is applied. Default: false */\n\tsuppressRefWarning?: boolean;\n\t/** Extract unique key for each item. Default: index */\n\tkeyExtractor?: (item: T, index: number) => string | number;\n}\n\nexport function SmartSkeletonList<T>({\n\tloading = false,\n\titems,\n\trenderItem,\n\trenderSkeleton,\n\tstorageKey,\n\tdefaultCount = 3,\n\tminCount = 1,\n\tmaxCount,\n\tanimate = true,\n\tseed,\n\tvariant = \"filled\",\n\tsuppressRefWarning = false,\n\tkeyExtractor = (_, index) => index,\n}: SmartSkeletonListProps<T>): ReactElement | null {\n\tconst skeletonCount = usePersistedCount({\n\t\tstorageKey,\n\t\tdefaultCount,\n\t\tcurrentCount: items?.length,\n\t\tloading,\n\t\tminCount,\n\t\tmaxCount,\n\t});\n\n\tif (loading) {\n\t\tconst skeletons = new Array(skeletonCount);\n\t\tfor (let index = 0; index < skeletonCount; index += 1) {\n\t\t\tconst itemSeed = seed === undefined ? `${index}` : `${seed}:${index}`;\n\t\t\tskeletons[index] = (\n\t\t\t\t<SmartSkeleton\n\t\t\t\t\tkey={`skeleton-${index}`}\n\t\t\t\t\tloading={true}\n\t\t\t\t\telement={renderSkeleton(index)}\n\t\t\t\t\tanimate={animate}\n\t\t\t\t\tseed={itemSeed}\n\t\t\t\t\tvariant={variant}\n\t\t\t\t\tsuppressRefWarning={suppressRefWarning}\n\t\t\t\t/>\n\t\t\t);\n\t\t}\n\t\treturn <>{skeletons}</>;\n\t}\n\n\tif (!items || items.length === 0) {\n\t\treturn null;\n\t}\n\n\treturn (\n\t\t<>\n\t\t\t{items.map((item, index) => (\n\t\t\t\t<Fragment key={keyExtractor(item, index)}>\n\t\t\t\t\t{renderItem(item, index)}\n\t\t\t\t</Fragment>\n\t\t\t))}\n\t\t</>\n\t);\n}\n"]}
|