@ahrowe/ui 0.36.0 → 0.38.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 +6 -2
- package/dist/cjs-types/common/commandPalette/commandPalette.d.ts +4 -0
- package/dist/cjs-types/common/commandPalette/commandPalette.types.d.ts +42 -0
- package/dist/cjs-types/common/commandPalette/filterCommands.d.ts +15 -0
- package/dist/cjs-types/common/commandPalette/index.d.ts +2 -0
- package/dist/cjs-types/common/configProvider/configProvider.types.d.ts +6 -0
- package/dist/cjs-types/common/copyButton/copyButton.d.ts +4 -0
- package/dist/cjs-types/common/copyButton/copyButton.types.d.ts +36 -0
- package/dist/cjs-types/common/copyButton/index.d.ts +2 -0
- package/dist/cjs-types/common/errorBoundary/errorBoundary.d.ts +0 -3
- package/dist/cjs-types/common/errorBoundary/errorBoundary.types.d.ts +2 -3
- package/dist/cjs-types/common/hooks/index.d.ts +15 -0
- package/dist/cjs-types/common/hooks/useClipboard.d.ts +16 -0
- package/dist/cjs-types/common/hooks/useHotkeys.d.ts +45 -0
- package/dist/cjs-types/common/hooks/useSwipeDismiss.d.ts +6 -2
- package/dist/cjs-types/common/inputDropdown/inputDropdown.types.d.ts +7 -1
- package/dist/cjs-types/common/loading/index.d.ts +0 -1
- package/dist/cjs-types/common/menu/index.d.ts +1 -0
- package/dist/cjs-types/common/menu/menu.types.d.ts +11 -3
- package/dist/cjs-types/common/menu/menuHotkeys.d.ts +20 -0
- package/dist/cjs-types/common/popover/usePopoverPosition.d.ts +1 -0
- package/dist/cjs-types/common/sticky/sticky.d.ts +1 -1
- package/dist/cjs-types/common/sticky/sticky.types.d.ts +15 -0
- package/dist/cjs-types/common/sticky/stickyStack.d.ts +5 -0
- package/dist/cjs-types/common/themeProvider/themeProvider.d.ts +8 -7
- package/dist/cjs-types/common/utils/scrollAncestors.d.ts +10 -0
- package/dist/cjs-types/common/virtualList/scroller.d.ts +33 -0
- package/dist/cjs-types/common/virtualList/useRowDrag.d.ts +6 -1
- package/dist/cjs-types/common/virtualList/useRowNavigation.d.ts +11 -1
- package/dist/cjs-types/common/virtualList/useVirtualWindow.d.ts +22 -3
- package/dist/cjs-types/common/virtualList/virtualList.d.ts +1 -1
- package/dist/cjs-types/common/virtualList/virtualList.types.d.ts +43 -3
- package/dist/cjs-types/common/weatherIcon/index.d.ts +3 -0
- package/dist/cjs-types/common/weatherIcon/parts.d.ts +26 -0
- package/dist/cjs-types/common/weatherIcon/weatherIcon.d.ts +4 -0
- package/dist/cjs-types/common/weatherIcon/weatherIcon.types.d.ts +44 -0
- package/dist/cjs-types/index.d.ts +7 -0
- package/dist/esm/common/buttonGroup/buttonGroup.module.mjs.map +1 -1
- package/dist/esm/common/commandPalette/commandPalette.mjs +2 -0
- package/dist/esm/common/commandPalette/commandPalette.mjs.map +1 -0
- package/dist/esm/common/commandPalette/commandPalette.module.mjs +2 -0
- package/dist/esm/common/commandPalette/commandPalette.module.mjs.map +1 -0
- package/dist/esm/common/commandPalette/filterCommands.mjs +2 -0
- package/dist/esm/common/commandPalette/filterCommands.mjs.map +1 -0
- package/dist/esm/common/copyButton/copyButton.mjs +2 -0
- package/dist/esm/common/copyButton/copyButton.mjs.map +1 -0
- package/dist/esm/common/copyButton/copyButton.module.mjs +2 -0
- package/dist/esm/common/copyButton/copyButton.module.mjs.map +1 -0
- package/dist/esm/common/dropdown/dropdown.mjs +1 -1
- package/dist/esm/common/dropdown/dropdown.module.mjs.map +1 -1
- package/dist/esm/common/errorBoundary/errorBoundary.mjs +2 -2
- package/dist/esm/common/errorBoundary/errorBoundary.mjs.map +1 -1
- package/dist/esm/common/errorBoundary/errorBoundary.module.mjs +1 -1
- package/dist/esm/common/errorBoundary/errorBoundary.module.mjs.map +1 -1
- package/dist/esm/common/hooks/useClipboard.mjs +2 -0
- package/dist/esm/common/hooks/useClipboard.mjs.map +1 -0
- package/dist/esm/common/hooks/useHotkeys.mjs +2 -0
- package/dist/esm/common/hooks/useHotkeys.mjs.map +1 -0
- package/dist/esm/common/hooks/useSwipeDismiss.mjs +1 -1
- package/dist/esm/common/hooks/useSwipeDismiss.mjs.map +1 -1
- package/dist/esm/common/input/input.mjs +1 -1
- package/dist/esm/common/input/input.mjs.map +1 -1
- package/dist/esm/common/input/input.module.mjs +1 -1
- package/dist/esm/common/input/input.module.mjs.map +1 -1
- package/dist/esm/common/inputDropdown/inputDropdown.mjs +1 -1
- package/dist/esm/common/inputDropdown/inputDropdown.mjs.map +1 -1
- package/dist/esm/common/inputDropdown/inputDropdown.module.mjs +1 -1
- package/dist/esm/common/inputDropdown/inputDropdown.module.mjs.map +1 -1
- package/dist/esm/common/menu/menu.mjs +1 -1
- package/dist/esm/common/menu/menu.mjs.map +1 -1
- package/dist/esm/common/menu/menu.types.mjs.map +1 -1
- package/dist/esm/common/menu/menuHotkeys.mjs +2 -0
- package/dist/esm/common/menu/menuHotkeys.mjs.map +1 -0
- package/dist/esm/common/multiSelect/multiSelect.mjs +1 -1
- package/dist/esm/common/multiSelect/multiSelect.mjs.map +1 -1
- package/dist/esm/common/multiSelect/multiSelect.module.mjs +1 -1
- package/dist/esm/common/multiSelect/multiSelect.module.mjs.map +1 -1
- package/dist/esm/common/otpInput/otpInput.module.mjs.map +1 -1
- package/dist/esm/common/popover/usePopoverPosition.mjs +1 -1
- package/dist/esm/common/popover/usePopoverPosition.mjs.map +1 -1
- package/dist/esm/common/sticky/sticky.mjs +1 -1
- package/dist/esm/common/sticky/sticky.mjs.map +1 -1
- package/dist/esm/common/sticky/stickyStack.mjs +1 -1
- package/dist/esm/common/sticky/stickyStack.mjs.map +1 -1
- package/dist/esm/common/themeProvider/themeProvider.mjs +1 -1
- package/dist/esm/common/themeProvider/themeProvider.mjs.map +1 -1
- package/dist/esm/common/timer/timer.mjs +1 -1
- package/dist/esm/common/timer/timer.mjs.map +1 -1
- package/dist/esm/common/utils/scrollAncestors.mjs +1 -1
- package/dist/esm/common/utils/scrollAncestors.mjs.map +1 -1
- package/dist/esm/common/virtualList/scroller.mjs +2 -0
- package/dist/esm/common/virtualList/scroller.mjs.map +1 -0
- package/dist/esm/common/virtualList/useRowDrag.mjs +1 -1
- package/dist/esm/common/virtualList/useRowDrag.mjs.map +1 -1
- package/dist/esm/common/virtualList/useRowNavigation.mjs +1 -1
- package/dist/esm/common/virtualList/useRowNavigation.mjs.map +1 -1
- package/dist/esm/common/virtualList/useVirtualWindow.mjs +1 -1
- package/dist/esm/common/virtualList/useVirtualWindow.mjs.map +1 -1
- package/dist/esm/common/virtualList/virtualList.mjs +1 -1
- package/dist/esm/common/virtualList/virtualList.mjs.map +1 -1
- package/dist/esm/common/virtualList/virtualList.module.mjs +1 -1
- package/dist/esm/common/virtualList/virtualList.module.mjs.map +1 -1
- package/dist/esm/common/weatherIcon/parts.mjs +2 -0
- package/dist/esm/common/weatherIcon/parts.mjs.map +1 -0
- package/dist/esm/common/weatherIcon/weatherIcon.mjs +2 -0
- package/dist/esm/common/weatherIcon/weatherIcon.mjs.map +1 -0
- package/dist/esm/common/weatherIcon/weatherIcon.module.mjs +2 -0
- package/dist/esm/common/weatherIcon/weatherIcon.module.mjs.map +1 -0
- package/dist/esm/common/weatherIcon/weatherIcon.types.mjs +2 -0
- package/dist/esm/common/weatherIcon/weatherIcon.types.mjs.map +1 -0
- package/dist/esm/index.mjs +1 -1
- package/dist/index.cjs +3 -3
- package/dist/index.cjs.map +1 -1
- package/dist/style.css +1 -1
- package/dist/types/common/commandPalette/commandPalette.d.ts +4 -0
- package/dist/types/common/commandPalette/commandPalette.types.d.ts +42 -0
- package/dist/types/common/commandPalette/filterCommands.d.ts +15 -0
- package/dist/types/common/commandPalette/index.d.ts +2 -0
- package/dist/types/common/configProvider/configProvider.types.d.ts +6 -0
- package/dist/types/common/copyButton/copyButton.d.ts +4 -0
- package/dist/types/common/copyButton/copyButton.types.d.ts +36 -0
- package/dist/types/common/copyButton/index.d.ts +2 -0
- package/dist/types/common/errorBoundary/errorBoundary.d.ts +0 -3
- package/dist/types/common/errorBoundary/errorBoundary.types.d.ts +2 -3
- package/dist/types/common/hooks/index.d.ts +15 -0
- package/dist/types/common/hooks/useClipboard.d.ts +16 -0
- package/dist/types/common/hooks/useHotkeys.d.ts +45 -0
- package/dist/types/common/hooks/useSwipeDismiss.d.ts +6 -2
- package/dist/types/common/inputDropdown/inputDropdown.types.d.ts +7 -1
- package/dist/types/common/loading/index.d.ts +0 -1
- package/dist/types/common/menu/index.d.ts +1 -0
- package/dist/types/common/menu/menu.types.d.ts +11 -3
- package/dist/types/common/menu/menuHotkeys.d.ts +20 -0
- package/dist/types/common/popover/usePopoverPosition.d.ts +1 -0
- package/dist/types/common/sticky/sticky.d.ts +1 -1
- package/dist/types/common/sticky/sticky.types.d.ts +15 -0
- package/dist/types/common/sticky/stickyStack.d.ts +5 -0
- package/dist/types/common/themeProvider/themeProvider.d.ts +8 -7
- package/dist/types/common/utils/scrollAncestors.d.ts +10 -0
- package/dist/types/common/virtualList/scroller.d.ts +33 -0
- package/dist/types/common/virtualList/useRowDrag.d.ts +6 -1
- package/dist/types/common/virtualList/useRowNavigation.d.ts +11 -1
- package/dist/types/common/virtualList/useVirtualWindow.d.ts +22 -3
- package/dist/types/common/virtualList/virtualList.d.ts +1 -1
- package/dist/types/common/virtualList/virtualList.types.d.ts +43 -3
- package/dist/types/common/weatherIcon/index.d.ts +3 -0
- package/dist/types/common/weatherIcon/parts.d.ts +26 -0
- package/dist/types/common/weatherIcon/weatherIcon.d.ts +4 -0
- package/dist/types/common/weatherIcon/weatherIcon.types.d.ts +44 -0
- package/dist/types/index.d.ts +7 -0
- package/docs/ButtonGroup.md +2 -0
- package/docs/CLAUDE.md +6 -2
- package/docs/CommandPalette.md +84 -0
- package/docs/ConfigProvider.md +2 -2
- package/docs/CopyButton.md +45 -0
- package/docs/ErrorBoundary.md +4 -4
- package/docs/Fab.md +1 -2
- package/docs/Hooks.md +268 -0
- package/docs/InputDropdown.md +9 -4
- package/docs/Loading.md +56 -15
- package/docs/Menu.md +30 -6
- package/docs/Popover.md +2 -0
- package/docs/Sticky.md +22 -1
- package/docs/VirtualList.md +44 -4
- package/docs/WeatherIcon.md +49 -0
- package/package.json +1 -2
- package/dist/cjs-types/common/loading/iconLoading/iconLoading.d.ts +0 -5
- package/dist/cjs-types/common/loading/iconLoading/index.d.ts +0 -2
- package/dist/esm/common/loading/iconLoading/iconLoading.mjs +0 -2
- package/dist/esm/common/loading/iconLoading/iconLoading.mjs.map +0 -1
- package/dist/esm/common/loading/iconLoading/iconLoading.module.mjs +0 -2
- package/dist/esm/common/loading/iconLoading/iconLoading.module.mjs.map +0 -1
- package/dist/esm/common/loading/iconLoading/index.mjs +0 -2
- package/dist/esm/common/loading/iconLoading/index.mjs.map +0 -1
- package/dist/types/common/loading/iconLoading/iconLoading.d.ts +0 -5
- package/dist/types/common/loading/iconLoading/index.d.ts +0 -2
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use client";import{buildOffsets as
|
|
1
|
+
"use client";import{getScrollParent as e}from"../utils/scrollAncestors.mjs";import{buildOffsets as t,findStartIndex as n,getVisibleRange as r}from"./virtualList.utils.mjs";import{isWindowScroller as i,measureViewport as a,resolveScroller as o,scrollportBounds as s}from"./scroller.mjs";import{useCallback as c,useEffect as l,useLayoutEffect as u,useMemo as d,useRef as f,useState as p}from"react";function ee(e,t){if(typeof e==`number`)return e;let n=e.trim();if(n===``)return 0;let r=Number(n);if(!Number.isNaN(r))return r;let i=/^(-?\d*\.?\d+)px$/.exec(n);if(i)return parseFloat(i[1]);if(typeof document>`u`||!t)return 0;let a=document.createElement(`div`);a.style.cssText=`position:absolute;visibility:hidden;height:${n};`,t.appendChild(a);let o=a.getBoundingClientRect().height;return t.removeChild(a),Number.isFinite(o)?o:0}var te=100,ne=20;function m({items:m,keys:h,getItemKey:g,estimatedRowHeight:re,rowGap:_,overscan:ie,onLoadMore:v,loadMoreThreshold:y,isLoading:b,scrollParent:x}){let[S,ae]=p(0),[C,oe]=p(0),[se,ce]=p(0),[w,le]=p(0),[T,ue]=p(()=>ee(_,null)),[E,de]=p(new Map),[D,O]=p(!1),k=f(!1),A=f(null),j=f(null),M=f([]),N=f(null),P=f([]),F=x!=null,I=f(null),L=f(null),[R,fe]=p(null);u(()=>{let t;if(x===`nearest`){let n=L.current;(!n||!i(n)&&!n.isConnected)&&(L.current=A.current?e(A.current)??window:null),t=L.current}else t=o(x);I.current=t,fe(e=>e===t?e:t)});let[z]=p(()=>typeof ResizeObserver>`u`?null:new ResizeObserver(e=>{de(t=>{let n=null;for(let r of e){let e=r.target,i=Number(e.dataset.index);if(Number.isNaN(i))continue;let a=P.current[i];if(a===void 0)continue;let o=r.borderBoxSize?.[0],s=o?Math.round(o.blockSize):e.offsetHeight;t.get(a)!==s&&(n||=new Map(t),n.set(a,s))}return n??t})}));l(()=>()=>z?.disconnect(),[z]);let B=c(e=>{let t=Math.max(0,e.rel);ae(e=>e===t?e:t),oe(t=>t===e.visibleHeight?t:e.visibleHeight),ce(t=>t===e.viewportHeight?t:e.viewportHeight),le(t=>t===e.width?t:e.width)},[]),V=c(()=>{let e=A.current;e&&B(a(e,I.current))},[B]),H=c(()=>{let e=A.current;if(!e)return{top:0,height:0};let t=a(e,I.current);return{top:Math.max(0,t.rel),height:t.visibleHeight}},[]),pe=c(()=>{let e=A.current;if(!e)return null;let t=e.getBoundingClientRect(),n=I.current;if(!n)return{top:t.top,bottom:t.bottom,left:t.left,right:t.right};let{top:r,height:i}=s(n);return{top:Math.max(t.top,r),bottom:Math.min(t.bottom,r+i),left:t.left,right:t.right}},[]),me=c(e=>{let t=A.current;if(!t)return;let n=I.current;n?i(n)?n.scrollBy(0,e):n.scrollTop+=e:t.scrollTop+=e},[]);u(()=>{let e=A.current;if(!e)return;V();let t=typeof ResizeObserver>`u`?null:new ResizeObserver(()=>V());return t?.observe(e),R?(t?.observe(i(R)?document.documentElement:R),window.addEventListener(`resize`,V),()=>{t?.disconnect(),window.removeEventListener(`resize`,V)}):()=>t?.disconnect()},[R,V]);let he=f(v);u(()=>{he.current=v});let U=c(()=>{let e=he.current;e&&!k.current&&(k.current=!0,O(!0),e().finally(()=>{k.current=!1,O(!1)}))},[]),ge=f(-1),W=c(()=>{let e=A.current;if(!e)return;let t=a(e,I.current);N.current??=requestAnimationFrame(()=>{N.current=null,V()}),t.distanceToEnd<=y&&ge.current!==P.current.length&&(ge.current=P.current.length,U())},[V,y,U]);l(()=>{let e=R??A.current;if(e)return e.addEventListener(`scroll`,W,{passive:!0}),()=>e.removeEventListener(`scroll`,W)},[R,W]),l(()=>()=>{N.current!=null&&cancelAnimationFrame(N.current)},[]),u(()=>{P.current=h}),u(()=>{let e=ee(_,A.current);ue(t=>t===e?t:e)},[_,w,C]);let _e=d(()=>{if(E.size===0)return re;let e=0;for(let t of E.values())e+=t;return e/E.size},[E,re]),{offsets:G,totalHeight:K}=d(()=>t(h,E,_e,T),[h,E,_e,T]),ve=f(``),q=F?se:C;l(()=>{if(!v||b||D||q===0)return;let e=j.current?.offsetHeight??0;if(K>=q-e)return;let t=`${m.length}:${q}`;ve.current!==t&&(ve.current=t,U())},[v,b,D,m.length,K,q,U]);let J=c((e,t)=>{let n=A.current;if(!n)return;let r=I.current;if(!r){let r=Math.max(0,e);t&&typeof n.scrollTo==`function`?n.scrollTo({top:r,behavior:t}):n.scrollTop=r;return}let{rel:o}=a(n,r);if(i(r)){r.scrollTo({top:Math.max(0,r.scrollY+e-o),behavior:t??`auto`});return}let s=Math.max(0,r.scrollTop+e-o);t&&typeof r.scrollTo==`function`?r.scrollTo({top:s,behavior:t}):r.scrollTop=s},[]);u(()=>{let e=A.current,t=M.current;if(M.current=G,!e||t.length===0||X.current)return;let r=H().top;if(r<=0)return;let i=t.length-1,a=n(t,r,i);if(a>=m.length)return;let o=r-t[a],s=G[a]+o;Math.abs(s-r)>.5&&(J(s),ae(s))},[G,m.length,H,J]);let Y=c((e,t)=>{let n=A.current;if(!n||m.length===0)return null;let r=Math.max(0,Math.min(e,m.length-1)),i=G[r]??0,o=(G[r+1]??i)-i,s=j.current?.offsetHeight??0,c=Math.max(0,a(n,I.current).viewportHeight-s),l=t?.align??`start`,u=i;return l===`center`?u-=(c-o)/2:l===`end`&&(u-=c-o),u},[m.length,G]),X=f(null);u(()=>{let e=X.current,t=A.current;if(!e)return;let n=e.items===m&&t?Y(e.index,e.options):null;if(n==null){X.current=null;return}let r=Math.abs(n-e.lastTarget)<=1;if(r&&Math.abs(H().top-n)<=1||e.attempts>=ne){X.current=null;return}r||(e.lastTarget=n,e.attempts+=1,J(n,e.options?.behavior))}),l(()=>{let e=A.current;if(!e)return;let t=()=>{X.current=null},n=R??e;return n.addEventListener(`wheel`,t,{passive:!0}),n.addEventListener(`touchstart`,t,{passive:!0}),e.addEventListener(`keydown`,t),()=>{n.removeEventListener(`wheel`,t),n.removeEventListener(`touchstart`,t),e.removeEventListener(`keydown`,t)}},[R]);let ye=d(()=>{function e(e,t){let n=Y(e,t);n!=null&&(X.current={index:e,options:t,lastTarget:n,attempts:0,items:m},J(n,t?.behavior))}return{scrollToIndex:e,scrollToKey(t,n){let r=m.findIndex((e,n)=>(g?g(e,n):n)===t);r>=0&&e(r,n)},scrollToTop(e){X.current=null,J(0,e?.behavior)},getScrollElement:()=>{let e=I.current;return e?i(e)?null:e:A.current},remeasure:V}},[m,g,Y,J,V]),{startIndex:Z,endIndex:Q}=d(()=>r(G,S,C,ie,m.length),[G,S,C,ie,m.length]),$=f(!1);return l(()=>{$.current||C===0||m.length<te||Q-Z+1<m.length||($.current=!0,console.warn(`VirtualList: rendering all ${m.length} rows, so nothing is being virtualized. Its viewport is as tall as its content. Give the list a definite height, or pass scrollParent to have it ride a scrollport it does not own.`))},[C,m.length,Z,Q]),{rootRef:A,headerRef:j,rowObserver:z,keysRef:P,offsets:G,totalHeight:K,containerHeight:C,viewportHeight:se,containerWidth:w,startIndex:Z,endIndex:Q,showLoadMoreSpinner:D,isWindowed:F,getVisibleSlice:H,getVisibleRect:pe,scrollBy:me,scrollApi:ye}}export{m as useVirtualWindow};
|
|
2
2
|
//# sourceMappingURL=useVirtualWindow.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useVirtualWindow.mjs","names":[],"sources":["../../../../package/common/virtualList/useVirtualWindow.ts"],"sourcesContent":["import React, { useState, useRef, useEffect, useLayoutEffect, useMemo, useCallback } from 'react';\n\nimport { buildOffsets, findStartIndex, getVisibleRange } from './virtualList.utils';\nimport type { VirtualListHandle, VirtualListScrollOptions } from './virtualList.types';\n\n/**\n * Resolve a `rowGap` value to pixels for the offset math. Numbers and plain\n * `px`/numeric strings parse directly (no DOM, SSR/test-safe); any other CSS\n * length (`em`, `rem`, `%`, `var(...)`) is resolved via a hidden probe appended\n * to `contextEl` so it inherits the list's font-size and custom properties.\n */\nfunction resolveCssLength(value: number | string, contextEl: HTMLElement | null): number {\n if (typeof value === 'number') return value;\n const trimmed = value.trim();\n if (trimmed === '') return 0;\n const asNumber = Number(trimmed);\n if (!Number.isNaN(asNumber)) return asNumber; // \"8\" → 8\n const pxMatch = /^(-?\\d*\\.?\\d+)px$/.exec(trimmed);\n if (pxMatch) return parseFloat(pxMatch[1]); // \"8px\" → 8\n if (typeof document === 'undefined' || !contextEl) return 0;\n const probe = document.createElement('div');\n probe.style.cssText = `position:absolute;visibility:hidden;height:${trimmed};`;\n contextEl.appendChild(probe);\n const px = probe.getBoundingClientRect().height;\n contextEl.removeChild(probe);\n return Number.isFinite(px) ? px : 0;\n}\n\ninterface UseVirtualWindowArgs<T> {\n items: T[];\n /** Stable key per row (already derived by the caller). */\n keys: (string | number)[];\n getItemKey?: (item: T, index: number) => string | number;\n estimatedRowHeight: number;\n rowGap: number | string;\n overscan: number;\n onLoadMore?: () => Promise<void>;\n loadMoreThreshold: number;\n /** Initial/filter load in progress — the body is replaced by a spinner. */\n isLoading: boolean;\n}\n\ninterface UseVirtualWindowResult {\n rootRef: React.RefObject<HTMLDivElement | null>;\n headerRef: React.RefObject<HTMLDivElement | null>;\n /** Shared by every row to measure its height (read back via `data-index`). */\n rowObserver: ResizeObserver | null;\n /** Latest index → key mapping, for async consumers (e.g. the drag hook). */\n keysRef: React.RefObject<(string | number)[]>;\n offsets: number[];\n totalHeight: number;\n containerHeight: number;\n containerWidth: number;\n startIndex: number;\n endIndex: number;\n handleScroll: (e: React.UIEvent<HTMLDivElement>) => void;\n showLoadMoreSpinner: boolean;\n /** The scroll-related imperative methods, for the component to compose into\n * the forwarded handle alongside column controls. */\n scrollApi: Omit<VirtualListHandle, 'recalculateColumns' | 'resetColumnWidths'>;\n}\n\n/**\n * The generic virtualization engine: viewport measurement, per-row height\n * caching, offset/visible-range computation, scroll coalescing, scroll\n * anchoring on height changes, infinite-load triggering, and the imperative\n * scroll handle. Mode-agnostic — knows nothing about columns, selection or\n * dragging.\n */\n/**\n * Loop guard for a jump re-aiming at its target. It normally ends because the\n * target stops moving, long before this; the cap is only there so a target that\n * can never be reached, the last row asked for with `align: 'start'`, can't\n * re-aim forever.\n */\nconst MAX_JUMP_CORRECTIONS = 20;\n\nexport function useVirtualWindow<T>({\n items,\n keys,\n getItemKey,\n estimatedRowHeight,\n rowGap,\n overscan,\n onLoadMore,\n loadMoreThreshold,\n isLoading,\n}: UseVirtualWindowArgs<T>): UseVirtualWindowResult {\n const [scrollTop, setScrollTop] = useState(0);\n const [containerHeight, setContainerHeight] = useState(0);\n const [containerWidth, setContainerWidth] = useState(0);\n // rowGap may be any CSS length; resolve it to px for the offset math. Seed with\n // the synchronous fast path (number / px), then refine before paint for units\n // that need the DOM (em/rem/%/var).\n const [resolvedRowGap, setResolvedRowGap] = useState<number>(() => resolveCssLength(rowGap, null));\n const [heightsMap, setHeightsMap] = useState<Map<string | number, number>>(new Map());\n const [showLoadMoreSpinner, setShowLoadMoreSpinner] = useState(false);\n const isLoadingMoreRef = useRef(false);\n\n const rootRef = useRef<HTMLDivElement>(null);\n const headerRef = useRef<HTMLDivElement>(null);\n const prevOffsetsRef = useRef<number[]>([]);\n const scrollRafRef = useRef<number | null>(null);\n const pendingScrollTopRef = useRef(0);\n const keysRef = useRef<(string | number)[]>([]);\n\n // One ResizeObserver shared by every row, created once via a lazy state\n // initializer so rows mounting in the same commit can register in their\n // layout effect. Batching all entries into a single setState avoids a\n // re-render per row when many rows enter the viewport at once (the main\n // source of scroll jank). The row index is read back from `data-index`.\n const [rowObserver] = useState<ResizeObserver | null>(() => {\n if (typeof ResizeObserver === 'undefined') return null;\n return new ResizeObserver((entries) => {\n setHeightsMap((prev) => {\n let next: Map<string | number, number> | null = null;\n for (const entry of entries) {\n const el = entry.target as HTMLElement;\n const idx = Number(el.dataset.index);\n if (Number.isNaN(idx)) continue;\n // Resolve the row's stable key so the cache survives reorders/filters.\n const key = keysRef.current[idx];\n if (key === undefined) continue;\n // Prefer the size the observer already computed — reading offsetHeight\n // here would force a synchronous reflow on every measurement batch.\n const box = entry.borderBoxSize?.[0];\n const h = box ? Math.round(box.blockSize) : el.offsetHeight;\n if (prev.get(key) === h) continue;\n if (!next) next = new Map(prev);\n next.set(key, h);\n }\n return next ?? prev;\n });\n });\n });\n useEffect(() => () => rowObserver?.disconnect(), [rowObserver]);\n\n useLayoutEffect(() => {\n const el = rootRef.current;\n if (!el) return;\n // Measure synchronously before the first paint so the initial render fills\n // the viewport, instead of painting a few overscan rows and re-rendering.\n setContainerHeight(el.clientHeight);\n setContainerWidth(el.clientWidth);\n // Track width too: `fit` columns are measured against this box and capped at\n // its width, so when the container settles to its real width after a route\n // transition / async layout, that change has to re-trigger the measure —\n // otherwise an early measure against a 0/tiny width gets pinned and sticks.\n const observer = new ResizeObserver(() => {\n setContainerHeight(el.clientHeight);\n setContainerWidth(el.clientWidth);\n });\n observer.observe(el);\n return () => observer.disconnect();\n }, []);\n\n // Read through a ref so the trigger below stays stable even when the consumer\n // passes a fresh `onLoadMore` closure on every render.\n const onLoadMoreRef = useRef(onLoadMore);\n useLayoutEffect(() => {\n onLoadMoreRef.current = onLoadMore;\n });\n\n /** Start a load unless one is already in flight (and drive the bottom spinner). */\n const triggerLoadMore = useCallback(() => {\n const load = onLoadMoreRef.current;\n if (!load || isLoadingMoreRef.current) return;\n isLoadingMoreRef.current = true;\n setShowLoadMoreSpinner(true);\n load().finally(() => {\n isLoadingMoreRef.current = false;\n setShowLoadMoreSpinner(false);\n });\n }, []);\n\n const handleScroll = useCallback(\n (e: React.UIEvent<HTMLDivElement>) => {\n const el = e.currentTarget;\n // Coalesce scroll-driven re-renders to at most one per animation frame.\n // Scroll events can fire several times per frame (especially while dragging\n // the scrollbar thumb); rendering the visible window once per frame keeps\n // the content in step with the cursor instead of lagging behind.\n pendingScrollTopRef.current = el.scrollTop;\n if (scrollRafRef.current == null) {\n scrollRafRef.current = requestAnimationFrame(() => {\n scrollRafRef.current = null;\n setScrollTop(pendingScrollTopRef.current);\n });\n }\n\n if (el.scrollTop + el.clientHeight >= el.scrollHeight - loadMoreThreshold) triggerLoadMore();\n },\n [loadMoreThreshold, triggerLoadMore],\n );\n\n useEffect(\n () => () => {\n if (scrollRafRef.current != null) cancelAnimationFrame(scrollRafRef.current);\n },\n [],\n );\n\n useLayoutEffect(() => {\n keysRef.current = keys;\n });\n\n // Resolve the gap to px before paint, re-running when the value or the\n // container's layout changes (a theme/font change that alters em/var is caught\n // by the container ResizeObserver feeding containerWidth/Height).\n useLayoutEffect(() => {\n const px = resolveCssLength(rowGap, rootRef.current);\n setResolvedRowGap((prev) => (prev === px ? prev : px));\n }, [rowGap, containerWidth, containerHeight]);\n\n // What an unmeasured row is assumed to be. `estimatedRowHeight` is only the\n // seed: once rows have been measured, their average is a far better guess, and\n // it is the guess every row between here and a distant jump target is made of.\n // With a fixed estimate the error compounds over that distance (40 assumed\n // against 35 real puts row 75 at row 86), and no amount of re-aiming fixes it,\n // because the rows that are wrong are precisely the ones never rendered.\n const assumedRowHeight = useMemo(() => {\n if (heightsMap.size === 0) return estimatedRowHeight;\n let total = 0;\n for (const height of heightsMap.values()) total += height;\n return total / heightsMap.size;\n }, [heightsMap, estimatedRowHeight]);\n\n const { offsets, totalHeight } = useMemo(\n () => buildOffsets(keys, heightsMap, assumedRowHeight, resolvedRowGap),\n [keys, heightsMap, assumedRowHeight, resolvedRowGap],\n );\n\n // The scroll trigger above can only fire on a list that actually scrolls. When\n // a batch is smaller than the viewport there is no overflow, so no scroll event\n // ever happens and lazy loading dead-ends after the first batch. Keep asking\n // for more until the content overflows; the consumer ends the loop by appending\n // nothing (or by dropping `onLoadMore` once its source is exhausted).\n //\n // Each (row count, viewport height) pair is probed at most once, so rows\n // settling to their measured heights can't re-ask a source that already ran\n // dry, and progress depends entirely on the consumer appending rows.\n const fillProbeRef = useRef('');\n useEffect(() => {\n if (!onLoadMore || isLoading || showLoadMoreSpinner) return;\n // 0 while the list is unmeasured or `display: none` — nothing to fill yet.\n if (containerHeight === 0) return;\n // The sticky header eats into the scroll area, so it isn't usable space.\n const headerH = headerRef.current?.offsetHeight ?? 0;\n if (totalHeight >= containerHeight - headerH) return; // already scrollable\n const probe = `${items.length}:${containerHeight}`;\n if (fillProbeRef.current === probe) return;\n fillProbeRef.current = probe;\n triggerLoadMore();\n }, [onLoadMore, isLoading, showLoadMoreSpinner, items.length, totalHeight, containerHeight, triggerLoadMore]);\n\n useLayoutEffect(() => {\n const el = rootRef.current;\n const prevOffsets = prevOffsetsRef.current;\n prevOffsetsRef.current = offsets;\n\n // Nothing to anchor against on the first pass or when pinned at the top.\n if (!el || prevOffsets.length === 0) return;\n // A jump in flight is aiming at a row; anchoring would hold the row it\n // happens to be passing instead, and the two would fight to a standstill.\n if (pendingJumpRef.current) return;\n const curScrollTop = el.scrollTop;\n if (curScrollTop <= 0) return;\n\n // Scroll anchoring: pin the row at the top of the viewport. Find it under\n // the *previous* offsets, then re-derive scrollTop from its *new* offset so\n // it stays visually fixed when rows above it are measured or resized. This\n // is more robust than summing per-row height deltas — it can't drift, and\n // one correction covers any number of simultaneous changes above the fold.\n const prevCount = prevOffsets.length - 1;\n const anchor = findStartIndex(prevOffsets, curScrollTop, prevCount);\n if (anchor >= items.length) return;\n const within = curScrollTop - prevOffsets[anchor];\n const target = offsets[anchor] + within;\n if (Math.abs(target - curScrollTop) > 0.5) {\n el.scrollTop = target;\n pendingScrollTopRef.current = target;\n setScrollTop(target);\n }\n }, [offsets, items.length]);\n\n const scrollToOffset = useCallback((top: number, behavior?: ScrollBehavior) => {\n const el = rootRef.current;\n if (!el) return;\n const clamped = Math.max(0, top);\n if (typeof el.scrollTo === 'function') el.scrollTo({ top: clamped, behavior: behavior ?? 'auto' });\n else el.scrollTop = clamped;\n }, []);\n\n /** Scroll position that puts row `index` where `align` asks for, or null if there is none. */\n const computeScrollTop = useCallback(\n (index: number, options?: VirtualListScrollOptions): number | null => {\n const el = rootRef.current;\n if (!el || items.length === 0) return null;\n const i = Math.max(0, Math.min(index, items.length - 1));\n const rowTop = offsets[i] ?? 0;\n const rowHeight = (offsets[i + 1] ?? rowTop) - rowTop;\n // Row offsets are measured from the top of the body, which itself starts\n // below the sticky header: the header is in flow, so scrolling by exactly\n // `rowTop` already puts the row flush under it. Subtracting the header\n // height on top of that, as this once did, left the row one header lower,\n // which in a table whose rows are about as tall as its header reads as\n // landing a row short.\n const headerHeight = headerRef.current?.offsetHeight ?? 0;\n // What is left of the viewport once the header has taken its share.\n const usable = Math.max(0, el.clientHeight - headerHeight);\n const align = options?.align ?? 'start';\n let top = rowTop;\n if (align === 'center') top -= (usable - rowHeight) / 2;\n else if (align === 'end') top -= usable - rowHeight;\n return top;\n },\n [items.length, offsets],\n );\n\n // A jump lands on the wrong row while the rows between here and there have\n // never been rendered: their offsets are `estimatedRowHeight` guesses, and the\n // error compounds over the distance (40px guessed against 35px real puts row\n // 75 at row 86). Scrolling there renders those rows, measuring corrects the\n // offsets, and the effect below re-aims at the target until it stops moving.\n // This is why the same jump is accurate once the list has been scrolled\n // through: by then every row in between carries a measured height.\n const pendingJumpRef = useRef<{\n index: number;\n options?: VirtualListScrollOptions;\n lastTarget: number;\n attempts: number;\n items: T[];\n } | null>(null);\n\n // Deliberately without a dependency list: every commit is a chance that new\n // measurements have moved the target, including the ones that come from the\n // scroll position itself changing mid-animation.\n useLayoutEffect(() => {\n const pending = pendingJumpRef.current;\n const el = rootRef.current;\n if (!pending) return;\n const target = pending.items === items && el ? computeScrollTop(pending.index, pending.options) : null;\n if (target == null) {\n pendingJumpRef.current = null; // new data supersedes an in-flight jump\n return;\n }\n const isStable = Math.abs(target - pending.lastTarget) <= 1;\n // Only once it has both stopped moving and been reached: with a smooth\n // behaviour the target can sit still for several frames while the animation\n // is still on its way there, and giving up then would leave it short.\n if ((isStable && Math.abs(el!.scrollTop - target) <= 1) || pending.attempts >= MAX_JUMP_CORRECTIONS) {\n pendingJumpRef.current = null;\n return;\n }\n if (isStable) return;\n pending.lastTarget = target;\n pending.attempts += 1;\n scrollToOffset(target, pending.options?.behavior);\n });\n\n // Any input of the user's own outranks a jump still in flight: being pulled\n // back to a row you just scrolled away from is worse than not arriving.\n useEffect(() => {\n const el = rootRef.current;\n if (!el) return;\n const cancel = () => {\n pendingJumpRef.current = null;\n };\n el.addEventListener('wheel', cancel, { passive: true });\n el.addEventListener('touchstart', cancel, { passive: true });\n el.addEventListener('keydown', cancel);\n return () => {\n el.removeEventListener('wheel', cancel);\n el.removeEventListener('touchstart', cancel);\n el.removeEventListener('keydown', cancel);\n };\n }, []);\n\n const scrollApi = useMemo<Omit<VirtualListHandle, 'recalculateColumns' | 'resetColumnWidths'>>(() => {\n function scrollToIndex(index: number, options?: VirtualListScrollOptions) {\n const target = computeScrollTop(index, options);\n if (target == null) return;\n pendingJumpRef.current = { index, options, lastTarget: target, attempts: 0, items };\n scrollToOffset(target, options?.behavior);\n }\n return {\n scrollToIndex,\n scrollToKey(key, options) {\n const i = items.findIndex((item, idx) => (getItemKey ? getItemKey(item, idx) : idx) === key);\n if (i >= 0) scrollToIndex(i, options);\n },\n scrollToTop(options) {\n pendingJumpRef.current = null;\n scrollToOffset(0, options?.behavior);\n },\n getScrollElement: () => rootRef.current,\n };\n }, [items, getItemKey, computeScrollTop, scrollToOffset]);\n\n const { startIndex, endIndex } = useMemo(\n () => getVisibleRange(offsets, scrollTop, containerHeight, overscan, items.length),\n [offsets, scrollTop, containerHeight, overscan, items.length],\n );\n\n return {\n rootRef,\n headerRef,\n rowObserver,\n keysRef,\n offsets,\n totalHeight,\n containerHeight,\n containerWidth,\n startIndex,\n endIndex,\n handleScroll,\n showLoadMoreSpinner,\n scrollApi,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useVirtualWindow.mjs","names":[],"sources":["../../../../package/common/virtualList/useVirtualWindow.ts"],"sourcesContent":["import React, { useState, useRef, useEffect, useLayoutEffect, useMemo, useCallback } from 'react';\n\nimport { buildOffsets, findStartIndex, getVisibleRange } from './virtualList.utils';\nimport { getScrollParent } from '../utils/scrollAncestors';\nimport { isWindowScroller, measureViewport, resolveScroller, scrollportBounds } from './scroller';\nimport type { Scroller, Viewport } from './scroller';\nimport type { VirtualListHandle, VirtualListScrollOptions, VirtualListScrollParent } from './virtualList.types';\n\n/**\n * Resolve a `rowGap` value to pixels for the offset math. Numbers and plain\n * `px`/numeric strings parse directly (no DOM, SSR/test-safe); any other CSS\n * length (`em`, `rem`, `%`, `var(...)`) is resolved via a hidden probe appended\n * to `contextEl` so it inherits the list's font-size and custom properties.\n */\nfunction resolveCssLength(value: number | string, contextEl: HTMLElement | null): number {\n if (typeof value === 'number') return value;\n const trimmed = value.trim();\n if (trimmed === '') return 0;\n const asNumber = Number(trimmed);\n if (!Number.isNaN(asNumber)) return asNumber; // \"8\" → 8\n const pxMatch = /^(-?\\d*\\.?\\d+)px$/.exec(trimmed);\n if (pxMatch) return parseFloat(pxMatch[1]); // \"8px\" → 8\n if (typeof document === 'undefined' || !contextEl) return 0;\n const probe = document.createElement('div');\n probe.style.cssText = `position:absolute;visibility:hidden;height:${trimmed};`;\n contextEl.appendChild(probe);\n const px = probe.getBoundingClientRect().height;\n contextEl.removeChild(probe);\n return Number.isFinite(px) ? px : 0;\n}\n\n/** No viewport holds this many rows at once, so rendering them all is a bug. */\nconst NOT_VIRTUALIZING_ROWS = 100;\n\ninterface UseVirtualWindowArgs<T> {\n items: T[];\n /** Stable key per row (already derived by the caller). */\n keys: (string | number)[];\n getItemKey?: (item: T, index: number) => string | number;\n estimatedRowHeight: number;\n rowGap: number | string;\n overscan: number;\n onLoadMore?: () => Promise<void>;\n loadMoreThreshold: number;\n /** Initial/filter load in progress — the body is replaced by a spinner. */\n isLoading: boolean;\n scrollParent?: VirtualListScrollParent;\n}\n\n/** Where the list is on screen, which is not its own box once it rides a scrollport. */\nexport interface ListViewportRect {\n top: number;\n bottom: number;\n left: number;\n right: number;\n}\n\n/** The list-space slice currently on screen, read live for keyboard navigation. */\nexport interface VisibleSlice {\n top: number;\n height: number;\n}\n\ninterface UseVirtualWindowResult {\n rootRef: React.RefObject<HTMLDivElement | null>;\n headerRef: React.RefObject<HTMLDivElement | null>;\n /** Shared by every row to measure its height (read back via `data-index`). */\n rowObserver: ResizeObserver | null;\n /** Latest index → key mapping, for async consumers (e.g. the drag hook). */\n keysRef: React.RefObject<(string | number)[]>;\n offsets: number[];\n totalHeight: number;\n /** The list's visible slice, which is what decides which rows are rendered. */\n containerHeight: number;\n /** The whole scrollport, which is what a scroll target is aimed within. */\n viewportHeight: number;\n containerWidth: number;\n startIndex: number;\n endIndex: number;\n showLoadMoreSpinner: boolean;\n isWindowed: boolean;\n getVisibleSlice: () => VisibleSlice;\n getVisibleRect: () => ListViewportRect | null;\n scrollBy: (dy: number) => void;\n /** The scroll-related imperative methods, for the component to compose into\n * the forwarded handle alongside column controls. */\n scrollApi: Omit<VirtualListHandle, 'recalculateColumns' | 'resetColumnWidths'>;\n}\n\n/**\n * The generic virtualization engine: viewport measurement, per-row height\n * caching, offset/visible-range computation, scroll coalescing, scroll\n * anchoring on height changes, infinite-load triggering, and the imperative\n * scroll handle. Mode-agnostic — knows nothing about columns, selection or\n * dragging.\n */\n/**\n * Loop guard for a jump re-aiming at its target. It normally ends because the\n * target stops moving, long before this; the cap is only there so a target that\n * can never be reached, the last row asked for with `align: 'start'`, can't\n * re-aim forever.\n */\nconst MAX_JUMP_CORRECTIONS = 20;\n\nexport function useVirtualWindow<T>({\n items,\n keys,\n getItemKey,\n estimatedRowHeight,\n rowGap,\n overscan,\n onLoadMore,\n loadMoreThreshold,\n isLoading,\n scrollParent,\n}: UseVirtualWindowArgs<T>): UseVirtualWindowResult {\n const [scrollTop, setScrollTop] = useState(0);\n const [containerHeight, setContainerHeight] = useState(0);\n const [viewportHeight, setViewportHeight] = useState(0);\n const [containerWidth, setContainerWidth] = useState(0);\n // rowGap may be any CSS length; resolve it to px for the offset math. Seed with\n // the synchronous fast path (number / px), then refine before paint for units\n // that need the DOM (em/rem/%/var).\n const [resolvedRowGap, setResolvedRowGap] = useState<number>(() => resolveCssLength(rowGap, null));\n const [heightsMap, setHeightsMap] = useState<Map<string | number, number>>(new Map());\n const [showLoadMoreSpinner, setShowLoadMoreSpinner] = useState(false);\n const isLoadingMoreRef = useRef(false);\n\n const rootRef = useRef<HTMLDivElement>(null);\n const headerRef = useRef<HTMLDivElement>(null);\n const prevOffsetsRef = useRef<number[]>([]);\n const scrollRafRef = useRef<number | null>(null);\n const keysRef = useRef<(string | number)[]>([]);\n\n const isWindowed = scrollParent != null;\n // The ref is what every callback reads, so none of them has to be rebuilt when\n // the scroller resolves; the state exists only to re-run the listener effects.\n const scrollerRef = useRef<Scroller | null>(null);\n const nearestRef = useRef<Scroller | null>(null);\n const [scroller, setScroller] = useState<Scroller | null>(null);\n // No dependency list, and declared before every effect that measures: a\n // getter's identity changes each render and the element it returns can appear\n // at any commit. The setState cannot chain, because what it stores is stable\n // across commits and it bails out on an equal one.\n // oxlint-disable-next-line react-hooks/exhaustive-deps\n useLayoutEffect(() => {\n let next: Scroller | null;\n if (scrollParent === 'nearest') {\n // Walking the ancestors reads computed style and so forces a style recalc:\n // resolve once, and again only if what it found has left the DOM.\n const cached = nearestRef.current;\n if (!cached || (!isWindowScroller(cached) && !cached.isConnected))\n nearestRef.current = rootRef.current ? (getScrollParent(rootRef.current) ?? window) : null;\n next = nearestRef.current;\n } else {\n next = resolveScroller(scrollParent);\n }\n scrollerRef.current = next;\n setScroller((prev) => (prev === next ? prev : next));\n });\n\n // One ResizeObserver shared by every row, created once via a lazy state\n // initializer so rows mounting in the same commit can register in their\n // layout effect. Batching all entries into a single setState avoids a\n // re-render per row when many rows enter the viewport at once (the main\n // source of scroll jank). The row index is read back from `data-index`.\n const [rowObserver] = useState<ResizeObserver | null>(() => {\n if (typeof ResizeObserver === 'undefined') return null;\n return new ResizeObserver((entries) => {\n setHeightsMap((prev) => {\n let next: Map<string | number, number> | null = null;\n for (const entry of entries) {\n const el = entry.target as HTMLElement;\n const idx = Number(el.dataset.index);\n if (Number.isNaN(idx)) continue;\n // Resolve the row's stable key so the cache survives reorders/filters.\n const key = keysRef.current[idx];\n if (key === undefined) continue;\n // Prefer the size the observer already computed — reading offsetHeight\n // here would force a synchronous reflow on every measurement batch.\n const box = entry.borderBoxSize?.[0];\n const h = box ? Math.round(box.blockSize) : el.offsetHeight;\n if (prev.get(key) === h) continue;\n if (!next) next = new Map(prev);\n next.set(key, h);\n }\n return next ?? prev;\n });\n });\n });\n useEffect(() => () => rowObserver?.disconnect(), [rowObserver]);\n\n const applyViewport = useCallback((v: Viewport) => {\n const top = Math.max(0, v.rel);\n setScrollTop((prev) => (prev === top ? prev : top));\n setContainerHeight((prev) => (prev === v.visibleHeight ? prev : v.visibleHeight));\n setViewportHeight((prev) => (prev === v.viewportHeight ? prev : v.viewportHeight));\n setContainerWidth((prev) => (prev === v.width ? prev : v.width));\n }, []);\n\n const syncViewport = useCallback(() => {\n const el = rootRef.current;\n if (!el) return;\n applyViewport(measureViewport(el, scrollerRef.current));\n }, [applyViewport]);\n\n const getVisibleSlice = useCallback((): VisibleSlice => {\n const el = rootRef.current;\n if (!el) return { top: 0, height: 0 };\n const v = measureViewport(el, scrollerRef.current);\n return { top: Math.max(0, v.rel), height: v.visibleHeight };\n }, []);\n\n const getVisibleRect = useCallback((): ListViewportRect | null => {\n const el = rootRef.current;\n if (!el) return null;\n const rect = el.getBoundingClientRect();\n const scrollerNow = scrollerRef.current;\n if (!scrollerNow) return { top: rect.top, bottom: rect.bottom, left: rect.left, right: rect.right };\n const { top, height } = scrollportBounds(scrollerNow);\n return {\n top: Math.max(rect.top, top),\n bottom: Math.min(rect.bottom, top + height),\n left: rect.left,\n right: rect.right,\n };\n }, []);\n\n const scrollBy = useCallback((dy: number) => {\n const el = rootRef.current;\n if (!el) return;\n const scrollerNow = scrollerRef.current;\n if (!scrollerNow) el.scrollTop += dy;\n else if (isWindowScroller(scrollerNow)) scrollerNow.scrollBy(0, dy);\n else scrollerNow.scrollTop += dy;\n }, []);\n\n useLayoutEffect(() => {\n const el = rootRef.current;\n if (!el) return;\n // Measure synchronously before the first paint so the initial render fills\n // the viewport, instead of painting a few overscan rows and re-rendering.\n syncViewport();\n const observer = typeof ResizeObserver === 'undefined' ? null : new ResizeObserver(() => syncViewport());\n // Track width too: `fit` columns are measured against this box and capped at\n // its width, so when the container settles to its real width after a route\n // transition / async layout, that change has to re-trigger the measure —\n // otherwise an early measure against a 0/tiny width gets pinned and sticks.\n observer?.observe(el);\n if (!scroller) return () => observer?.disconnect();\n // Where the list sits inside its scrollport is half of every measurement,\n // and an element that merely *moves* fires nothing. The scrollport's own\n // content box catches the common cause, something above the list growing;\n // `remeasure()` is there for the rest.\n observer?.observe(isWindowScroller(scroller) ? document.documentElement : scroller);\n window.addEventListener('resize', syncViewport);\n return () => {\n observer?.disconnect();\n window.removeEventListener('resize', syncViewport);\n };\n }, [scroller, syncViewport]);\n\n // Read through a ref so the trigger below stays stable even when the consumer\n // passes a fresh `onLoadMore` closure on every render.\n const onLoadMoreRef = useRef(onLoadMore);\n useLayoutEffect(() => {\n onLoadMoreRef.current = onLoadMore;\n });\n\n /** Start a load unless one is already in flight (and drive the bottom spinner). */\n const triggerLoadMore = useCallback(() => {\n const load = onLoadMoreRef.current;\n if (!load || isLoadingMoreRef.current) return;\n isLoadingMoreRef.current = true;\n setShowLoadMoreSpinner(true);\n load().finally(() => {\n isLoadingMoreRef.current = false;\n setShowLoadMoreSpinner(false);\n });\n }, []);\n\n // One ask per batch, the same probe the fill check below uses. The end of the\n // list staying in view keeps qualifying, and in `scrollParent` mode it does so\n // for the whole rest of the page.\n const loadMoreProbeRef = useRef(-1);\n const handleScroll = useCallback(() => {\n const el = rootRef.current;\n if (!el) return;\n // Only the load-more check needs its answer now; the render measures again in\n // the frame, so a scroll-anchoring correction landing in between is not undone\n // by a reading taken before it.\n const v = measureViewport(el, scrollerRef.current);\n // Coalesce scroll-driven re-renders to at most one per animation frame.\n // Scroll events can fire several times per frame (especially while dragging\n // the scrollbar thumb); rendering the visible window once per frame keeps\n // the content in step with the cursor instead of lagging behind.\n if (scrollRafRef.current == null) {\n scrollRafRef.current = requestAnimationFrame(() => {\n scrollRafRef.current = null;\n syncViewport();\n });\n }\n\n if (v.distanceToEnd <= loadMoreThreshold && loadMoreProbeRef.current !== keysRef.current.length) {\n loadMoreProbeRef.current = keysRef.current.length;\n triggerLoadMore();\n }\n }, [syncViewport, loadMoreThreshold, triggerLoadMore]);\n\n // Imperative rather than an `onScroll` prop, because with `scrollParent` the\n // events happen on an element the list does not render. Doing it the same way\n // in both modes also leaves the root's `onScroll` free for the consumer.\n useEffect(() => {\n const target: EventTarget | null = scroller ?? rootRef.current;\n if (!target) return;\n target.addEventListener('scroll', handleScroll, { passive: true });\n return () => target.removeEventListener('scroll', handleScroll);\n }, [scroller, handleScroll]);\n\n useEffect(\n () => () => {\n if (scrollRafRef.current != null) cancelAnimationFrame(scrollRafRef.current);\n },\n [],\n );\n\n useLayoutEffect(() => {\n keysRef.current = keys;\n });\n\n // Resolve the gap to px before paint, re-running when the value or the\n // container's layout changes (a theme/font change that alters em/var is caught\n // by the container ResizeObserver feeding containerWidth/Height).\n useLayoutEffect(() => {\n const px = resolveCssLength(rowGap, rootRef.current);\n setResolvedRowGap((prev) => (prev === px ? prev : px));\n }, [rowGap, containerWidth, containerHeight]);\n\n // What an unmeasured row is assumed to be. `estimatedRowHeight` is only the\n // seed: once rows have been measured, their average is a far better guess, and\n // it is the guess every row between here and a distant jump target is made of.\n // With a fixed estimate the error compounds over that distance (40 assumed\n // against 35 real puts row 75 at row 86), and no amount of re-aiming fixes it,\n // because the rows that are wrong are precisely the ones never rendered.\n const assumedRowHeight = useMemo(() => {\n if (heightsMap.size === 0) return estimatedRowHeight;\n let total = 0;\n for (const height of heightsMap.values()) total += height;\n return total / heightsMap.size;\n }, [heightsMap, estimatedRowHeight]);\n\n const { offsets, totalHeight } = useMemo(\n () => buildOffsets(keys, heightsMap, assumedRowHeight, resolvedRowGap),\n [keys, heightsMap, assumedRowHeight, resolvedRowGap],\n );\n\n // The scroll trigger above can only fire on a list that actually scrolls. When\n // a batch is smaller than the viewport there is no overflow, so no scroll event\n // ever happens and lazy loading dead-ends after the first batch. Keep asking\n // for more until the content overflows; the consumer ends the loop by appending\n // nothing (or by dropping `onLoadMore` once its source is exhausted).\n //\n // Each (row count, viewport height) pair is probed at most once, so rows\n // settling to their measured heights can't re-ask a source that already ran\n // dry, and progress depends entirely on the consumer appending rows.\n //\n // The bar to clear is the whole scrollport, not the slice the list occupies\n // now: a windowed list scrolling in from the bottom of a page would otherwise\n // stop filling at whatever happened to show.\n const fillProbeRef = useRef('');\n const fillHeight = isWindowed ? viewportHeight : containerHeight;\n useEffect(() => {\n if (!onLoadMore || isLoading || showLoadMoreSpinner) return;\n // 0 while the list is unmeasured or `display: none` — nothing to fill yet.\n if (fillHeight === 0) return;\n // The sticky header eats into the scroll area, so it isn't usable space.\n const headerH = headerRef.current?.offsetHeight ?? 0;\n if (totalHeight >= fillHeight - headerH) return; // already scrollable\n const probe = `${items.length}:${fillHeight}`;\n if (fillProbeRef.current === probe) return;\n fillProbeRef.current = probe;\n triggerLoadMore();\n }, [onLoadMore, isLoading, showLoadMoreSpinner, items.length, totalHeight, fillHeight, triggerLoadMore]);\n\n /** Put list-space offset `top` at the top of the scrollport, whoever owns it. */\n const scrollToOffset = useCallback((top: number, behavior?: ScrollBehavior) => {\n const el = rootRef.current;\n if (!el) return;\n const scrollerNow = scrollerRef.current;\n if (!scrollerNow) {\n const clamped = Math.max(0, top);\n // Assignment rather than `scrollTo` when no behaviour was asked for: it is\n // what the anchoring correction needs, and the two are the same for `auto`.\n if (behavior && typeof el.scrollTo === 'function') el.scrollTo({ top: clamped, behavior });\n else el.scrollTop = clamped;\n return;\n }\n // List space is relative, so the move is a delta on where the scrollport is\n // now, and `rel` is unclamped here: a list still below it has to be reachable\n // from above as well as from within.\n const { rel } = measureViewport(el, scrollerNow);\n if (isWindowScroller(scrollerNow)) {\n scrollerNow.scrollTo({ top: Math.max(0, scrollerNow.scrollY + top - rel), behavior: behavior ?? 'auto' });\n return;\n }\n const target = Math.max(0, scrollerNow.scrollTop + top - rel);\n if (behavior && typeof scrollerNow.scrollTo === 'function') scrollerNow.scrollTo({ top: target, behavior });\n else scrollerNow.scrollTop = target;\n }, []);\n\n useLayoutEffect(() => {\n const el = rootRef.current;\n const prevOffsets = prevOffsetsRef.current;\n prevOffsetsRef.current = offsets;\n\n // Nothing to anchor against on the first pass or when pinned at the top.\n if (!el || prevOffsets.length === 0) return;\n // A jump in flight is aiming at a row; anchoring would hold the row it\n // happens to be passing instead, and the two would fight to a standstill.\n if (pendingJumpRef.current) return;\n const curScrollTop = getVisibleSlice().top;\n if (curScrollTop <= 0) return;\n\n // Scroll anchoring: pin the row at the top of the viewport. Find it under\n // the *previous* offsets, then re-derive scrollTop from its *new* offset so\n // it stays visually fixed when rows above it are measured or resized. This\n // is more robust than summing per-row height deltas — it can't drift, and\n // one correction covers any number of simultaneous changes above the fold.\n const prevCount = prevOffsets.length - 1;\n const anchor = findStartIndex(prevOffsets, curScrollTop, prevCount);\n if (anchor >= items.length) return;\n const within = curScrollTop - prevOffsets[anchor];\n const target = offsets[anchor] + within;\n if (Math.abs(target - curScrollTop) > 0.5) {\n scrollToOffset(target);\n setScrollTop(target);\n }\n }, [offsets, items.length, getVisibleSlice, scrollToOffset]);\n\n /** Scroll position that puts row `index` where `align` asks for, or null if there is none. */\n const computeScrollTop = useCallback(\n (index: number, options?: VirtualListScrollOptions): number | null => {\n const el = rootRef.current;\n if (!el || items.length === 0) return null;\n const i = Math.max(0, Math.min(index, items.length - 1));\n const rowTop = offsets[i] ?? 0;\n const rowHeight = (offsets[i + 1] ?? rowTop) - rowTop;\n // Row offsets are measured from the top of the body, which itself starts\n // below the sticky header: the header is in flow, so scrolling by exactly\n // `rowTop` already puts the row flush under it. Subtracting the header\n // height on top of that, as this once did, left the row one header lower,\n // which in a table whose rows are about as tall as its header reads as\n // landing a row short.\n const headerHeight = headerRef.current?.offsetHeight ?? 0;\n // The whole scrollport, not the slice shown now: the jump is aimed at where\n // the row lands after the scroll, by which point the list covers it.\n const usable = Math.max(0, measureViewport(el, scrollerRef.current).viewportHeight - headerHeight);\n const align = options?.align ?? 'start';\n let top = rowTop;\n if (align === 'center') top -= (usable - rowHeight) / 2;\n else if (align === 'end') top -= usable - rowHeight;\n return top;\n },\n [items.length, offsets],\n );\n\n // A jump lands on the wrong row while the rows between here and there have\n // never been rendered: their offsets are `estimatedRowHeight` guesses, and the\n // error compounds over the distance (40px guessed against 35px real puts row\n // 75 at row 86). Scrolling there renders those rows, measuring corrects the\n // offsets, and the effect below re-aims at the target until it stops moving.\n // This is why the same jump is accurate once the list has been scrolled\n // through: by then every row in between carries a measured height.\n const pendingJumpRef = useRef<{\n index: number;\n options?: VirtualListScrollOptions;\n lastTarget: number;\n attempts: number;\n items: T[];\n } | null>(null);\n\n // Deliberately without a dependency list: every commit is a chance that new\n // measurements have moved the target, including the ones that come from the\n // scroll position itself changing mid-animation.\n useLayoutEffect(() => {\n const pending = pendingJumpRef.current;\n const el = rootRef.current;\n if (!pending) return;\n const target = pending.items === items && el ? computeScrollTop(pending.index, pending.options) : null;\n if (target == null) {\n pendingJumpRef.current = null; // new data supersedes an in-flight jump\n return;\n }\n const isStable = Math.abs(target - pending.lastTarget) <= 1;\n // Only once it has both stopped moving and been reached: with a smooth\n // behaviour the target can sit still for several frames while the animation\n // is still on its way there, and giving up then would leave it short.\n if ((isStable && Math.abs(getVisibleSlice().top - target) <= 1) || pending.attempts >= MAX_JUMP_CORRECTIONS) {\n pendingJumpRef.current = null;\n return;\n }\n if (isStable) return;\n pending.lastTarget = target;\n pending.attempts += 1;\n scrollToOffset(target, pending.options?.behavior);\n });\n\n // Any input of the user's own outranks a jump still in flight: being pulled\n // back to a row you just scrolled away from is worse than not arriving.\n useEffect(() => {\n const el = rootRef.current;\n if (!el) return;\n const cancel = () => {\n pendingJumpRef.current = null;\n };\n // The wheel and the finger belong to whatever scrolls; the keys belong to\n // the list, and hijacking them page-wide would cancel on any typing.\n const pointerTarget: EventTarget = scroller ?? el;\n pointerTarget.addEventListener('wheel', cancel, { passive: true });\n pointerTarget.addEventListener('touchstart', cancel, { passive: true });\n el.addEventListener('keydown', cancel);\n return () => {\n pointerTarget.removeEventListener('wheel', cancel);\n pointerTarget.removeEventListener('touchstart', cancel);\n el.removeEventListener('keydown', cancel);\n };\n }, [scroller]);\n\n const scrollApi = useMemo<Omit<VirtualListHandle, 'recalculateColumns' | 'resetColumnWidths'>>(() => {\n function scrollToIndex(index: number, options?: VirtualListScrollOptions) {\n const target = computeScrollTop(index, options);\n if (target == null) return;\n pendingJumpRef.current = { index, options, lastTarget: target, attempts: 0, items };\n scrollToOffset(target, options?.behavior);\n }\n return {\n scrollToIndex,\n scrollToKey(key, options) {\n const i = items.findIndex((item, idx) => (getItemKey ? getItemKey(item, idx) : idx) === key);\n if (i >= 0) scrollToIndex(i, options);\n },\n scrollToTop(options) {\n pendingJumpRef.current = null;\n scrollToOffset(0, options?.behavior);\n },\n getScrollElement: () => {\n const scrollerNow = scrollerRef.current;\n if (!scrollerNow) return rootRef.current;\n return isWindowScroller(scrollerNow) ? null : scrollerNow;\n },\n remeasure: syncViewport,\n };\n }, [items, getItemKey, computeScrollTop, scrollToOffset, syncViewport]);\n\n const { startIndex, endIndex } = useMemo(\n () => getVisibleRange(offsets, scrollTop, containerHeight, overscan, items.length),\n [offsets, scrollTop, containerHeight, overscan, items.length],\n );\n\n // Virtualizing nothing fails silently: the page is simply slow, which points\n // nowhere near the cause. That cause is almost always a viewport as tall as its\n // content, which is what a percentage height amounts to inside a parent with no\n // definite height of its own.\n const warnedRef = useRef(false);\n useEffect(() => {\n if (warnedRef.current || containerHeight === 0) return;\n if (items.length < NOT_VIRTUALIZING_ROWS || endIndex - startIndex + 1 < items.length) return;\n warnedRef.current = true;\n console.warn(\n `VirtualList: rendering all ${items.length} rows, so nothing is being virtualized. Its viewport is as tall as its content. Give the list a definite height, or pass scrollParent to have it ride a scrollport it does not own.`,\n );\n }, [containerHeight, items.length, startIndex, endIndex]);\n\n return {\n rootRef,\n headerRef,\n rowObserver,\n keysRef,\n offsets,\n totalHeight,\n containerHeight,\n viewportHeight,\n containerWidth,\n startIndex,\n endIndex,\n showLoadMoreSpinner,\n isWindowed,\n getVisibleSlice,\n getVisibleRect,\n scrollBy,\n scrollApi,\n };\n}\n"],"mappings":"6YAcA,SAAS,GAAiB,EAAwB,EAAuC,CACvF,GAAI,OAAO,GAAU,SAAU,OAAO,EACtC,IAAM,EAAU,EAAM,KAAK,EAC3B,GAAI,IAAY,GAAI,MAAO,GAC3B,IAAM,EAAW,OAAO,CAAO,EAC/B,GAAI,CAAC,OAAO,MAAM,CAAQ,EAAG,OAAO,EACpC,IAAM,EAAU,oBAAoB,KAAK,CAAO,EAChD,GAAI,EAAS,OAAO,WAAW,EAAQ,EAAE,EACzC,GAAI,OAAO,SAAa,KAAe,CAAC,EAAW,MAAO,GAC1D,IAAM,EAAQ,SAAS,cAAc,KAAK,EAC1C,EAAM,MAAM,QAAU,8CAA8C,EAAQ,GAC5E,EAAU,YAAY,CAAK,EAC3B,IAAM,EAAK,EAAM,sBAAsB,CAAC,CAAC,OAEzC,OADA,EAAU,YAAY,CAAK,EACpB,OAAO,SAAS,CAAE,EAAI,EAAK,CACpC,CAGA,IAAM,GAAwB,IAsExB,GAAuB,GAE7B,SAAgB,EAAoB,CAClC,QACA,OACA,aACA,sBACA,SACA,YACA,aACA,oBACA,YACA,gBACkD,CAClD,GAAM,CAAC,EAAW,IAAgB,EAAS,CAAC,EACtC,CAAC,EAAiB,IAAsB,EAAS,CAAC,EAClD,CAAC,GAAgB,IAAqB,EAAS,CAAC,EAChD,CAAC,EAAgB,IAAqB,EAAS,CAAC,EAIhD,CAAC,EAAgB,IAAqB,MAAuB,GAAiB,EAAQ,IAAI,CAAC,EAC3F,CAAC,EAAY,IAAiB,EAAuC,IAAI,GAAK,EAC9E,CAAC,EAAqB,GAA0B,EAAS,EAAK,EAC9D,EAAmB,EAAO,EAAK,EAE/B,EAAU,EAAuB,IAAI,EACrC,EAAY,EAAuB,IAAI,EACvC,EAAiB,EAAiB,CAAC,CAAC,EACpC,EAAe,EAAsB,IAAI,EACzC,EAAU,EAA4B,CAAC,CAAC,EAExC,EAAa,GAAgB,KAG7B,EAAc,EAAwB,IAAI,EAC1C,EAAa,EAAwB,IAAI,EACzC,CAAC,EAAU,IAAe,EAA0B,IAAI,EAM9D,MAAsB,CACpB,IAAI,EACJ,GAAI,IAAiB,UAAW,CAG9B,IAAM,EAAS,EAAW,SACtB,CAAC,GAAW,CAAC,EAAiB,CAAM,GAAK,CAAC,EAAO,eACnD,EAAW,QAAU,EAAQ,QAAW,EAAgB,EAAQ,OAAO,GAAK,OAAU,MACxF,EAAO,EAAW,OACpB,KACE,GAAO,EAAgB,CAAY,EAErC,EAAY,QAAU,EACtB,GAAa,GAAU,IAAS,EAAO,EAAO,CAAK,CACrD,CAAC,EAOD,GAAM,CAAC,GAAe,MAChB,OAAO,eAAmB,IAAoB,KAC3C,IAAI,eAAgB,GAAY,CACrC,GAAe,GAAS,CACtB,IAAI,EAA4C,KAChD,IAAK,IAAM,KAAS,EAAS,CAC3B,IAAM,EAAK,EAAM,OACX,EAAM,OAAO,EAAG,QAAQ,KAAK,EACnC,GAAI,OAAO,MAAM,CAAG,EAAG,SAEvB,IAAM,EAAM,EAAQ,QAAQ,GAC5B,GAAI,IAAQ,IAAA,GAAW,SAGvB,IAAM,EAAM,EAAM,gBAAgB,GAC5B,EAAI,EAAM,KAAK,MAAM,EAAI,SAAS,EAAI,EAAG,aAC3C,EAAK,IAAI,CAAG,IAAM,IACtB,AAAW,IAAO,IAAI,IAAI,CAAI,EAC9B,EAAK,IAAI,EAAK,CAAC,EACjB,CACA,OAAO,GAAQ,CACjB,CAAC,CACH,CAAC,CACF,EACD,UAAsB,GAAa,WAAW,EAAG,CAAC,CAAW,CAAC,EAE9D,IAAM,EAAgB,EAAa,GAAgB,CACjD,IAAM,EAAM,KAAK,IAAI,EAAG,EAAE,GAAG,EAC7B,GAAc,GAAU,IAAS,EAAM,EAAO,CAAI,EAClD,GAAoB,GAAU,IAAS,EAAE,cAAgB,EAAO,EAAE,aAAc,EAChF,GAAmB,GAAU,IAAS,EAAE,eAAiB,EAAO,EAAE,cAAe,EACjF,GAAmB,GAAU,IAAS,EAAE,MAAQ,EAAO,EAAE,KAAM,CACjE,EAAG,CAAC,CAAC,EAEC,EAAe,MAAkB,CACrC,IAAM,EAAK,EAAQ,QACd,GACL,EAAc,EAAgB,EAAI,EAAY,OAAO,CAAC,CACxD,EAAG,CAAC,CAAa,CAAC,EAEZ,EAAkB,MAAgC,CACtD,IAAM,EAAK,EAAQ,QACnB,GAAI,CAAC,EAAI,MAAO,CAAE,IAAK,EAAG,OAAQ,CAAE,EACpC,IAAM,EAAI,EAAgB,EAAI,EAAY,OAAO,EACjD,MAAO,CAAE,IAAK,KAAK,IAAI,EAAG,EAAE,GAAG,EAAG,OAAQ,EAAE,aAAc,CAC5D,EAAG,CAAC,CAAC,EAEC,GAAiB,MAA2C,CAChE,IAAM,EAAK,EAAQ,QACnB,GAAI,CAAC,EAAI,OAAO,KAChB,IAAM,EAAO,EAAG,sBAAsB,EAChC,EAAc,EAAY,QAChC,GAAI,CAAC,EAAa,MAAO,CAAE,IAAK,EAAK,IAAK,OAAQ,EAAK,OAAQ,KAAM,EAAK,KAAM,MAAO,EAAK,KAAM,EAClG,GAAM,CAAE,MAAK,UAAW,EAAiB,CAAW,EACpD,MAAO,CACL,IAAK,KAAK,IAAI,EAAK,IAAK,CAAG,EAC3B,OAAQ,KAAK,IAAI,EAAK,OAAQ,EAAM,CAAM,EAC1C,KAAM,EAAK,KACX,MAAO,EAAK,KACd,CACF,EAAG,CAAC,CAAC,EAEC,GAAW,EAAa,GAAe,CAC3C,IAAM,EAAK,EAAQ,QACnB,GAAI,CAAC,EAAI,OACT,IAAM,EAAc,EAAY,QAC3B,EACI,EAAiB,CAAW,EAAG,EAAY,SAAS,EAAG,CAAE,EAC7D,EAAY,WAAa,EAFZ,EAAG,WAAa,CAGpC,EAAG,CAAC,CAAC,EAEL,MAAsB,CACpB,IAAM,EAAK,EAAQ,QACnB,GAAI,CAAC,EAAI,OAGT,EAAa,EACb,IAAM,EAAW,OAAO,eAAmB,IAAc,KAAO,IAAI,mBAAqB,EAAa,CAAC,EAavG,OARA,GAAU,QAAQ,CAAE,EACf,GAKL,GAAU,QAAQ,EAAiB,CAAQ,EAAI,SAAS,gBAAkB,CAAQ,EAClF,OAAO,iBAAiB,SAAU,CAAY,MACjC,CACX,GAAU,WAAW,EACrB,OAAO,oBAAoB,SAAU,CAAY,CACnD,OAV4B,GAAU,WAAW,CAWnD,EAAG,CAAC,EAAU,CAAY,CAAC,EAI3B,IAAM,GAAgB,EAAO,CAAU,EACvC,MAAsB,CACpB,GAAc,QAAU,CAC1B,CAAC,EAGD,IAAM,EAAkB,MAAkB,CACxC,IAAM,EAAO,GAAc,QACtB,GAAQ,GAAiB,UAC9B,EAAiB,QAAU,GAC3B,EAAuB,EAAI,EAC3B,EAAK,CAAC,CAAC,YAAc,CACnB,EAAiB,QAAU,GAC3B,EAAuB,EAAK,CAC9B,CAAC,EACH,EAAG,CAAC,CAAC,EAKC,GAAmB,EAAO,EAAE,EAC5B,EAAe,MAAkB,CACrC,IAAM,EAAK,EAAQ,QACnB,GAAI,CAAC,EAAI,OAIT,IAAM,EAAI,EAAgB,EAAI,EAAY,OAAO,EAKjD,AACE,EAAa,UAAU,0BAA4B,CACjD,EAAa,QAAU,KACvB,EAAa,CACf,CAAC,EAGC,EAAE,eAAiB,GAAqB,GAAiB,UAAY,EAAQ,QAAQ,SACvF,GAAiB,QAAU,EAAQ,QAAQ,OAC3C,EAAgB,EAEpB,EAAG,CAAC,EAAc,EAAmB,CAAe,CAAC,EAKrD,MAAgB,CACd,IAAM,EAA6B,GAAY,EAAQ,QAClD,KAEL,OADA,EAAO,iBAAiB,SAAU,EAAc,CAAE,QAAS,EAAK,CAAC,MACpD,EAAO,oBAAoB,SAAU,CAAY,CAChE,EAAG,CAAC,EAAU,CAAY,CAAC,EAE3B,UACc,CACN,EAAa,SAAW,MAAM,qBAAqB,EAAa,OAAO,CAC7E,EACA,CAAC,CACH,EAEA,MAAsB,CACpB,EAAQ,QAAU,CACpB,CAAC,EAKD,MAAsB,CACpB,IAAM,EAAK,GAAiB,EAAQ,EAAQ,OAAO,EACnD,GAAmB,GAAU,IAAS,EAAK,EAAO,CAAG,CACvD,EAAG,CAAC,EAAQ,EAAgB,CAAe,CAAC,EAQ5C,IAAM,GAAmB,MAAc,CACrC,GAAI,EAAW,OAAS,EAAG,OAAO,GAClC,IAAI,EAAQ,EACZ,IAAK,IAAM,KAAU,EAAW,OAAO,EAAG,GAAS,EACnD,OAAO,EAAQ,EAAW,IAC5B,EAAG,CAAC,EAAY,EAAkB,CAAC,EAE7B,CAAE,UAAS,eAAgB,MACzB,EAAa,EAAM,EAAY,GAAkB,CAAc,EACrE,CAAC,EAAM,EAAY,GAAkB,CAAc,CACrD,EAeM,GAAe,EAAO,EAAE,EACxB,EAAa,EAAa,GAAiB,EACjD,MAAgB,CAGd,GAFI,CAAC,GAAc,GAAa,GAE5B,IAAe,EAAG,OAEtB,IAAM,EAAU,EAAU,SAAS,cAAgB,EACnD,GAAI,GAAe,EAAa,EAAS,OACzC,IAAM,EAAQ,GAAG,EAAM,OAAO,GAAG,IAC7B,GAAa,UAAY,IAC7B,GAAa,QAAU,EACvB,EAAgB,EAClB,EAAG,CAAC,EAAY,EAAW,EAAqB,EAAM,OAAQ,EAAa,EAAY,CAAe,CAAC,EAGvG,IAAM,EAAiB,GAAa,EAAa,IAA8B,CAC7E,IAAM,EAAK,EAAQ,QACnB,GAAI,CAAC,EAAI,OACT,IAAM,EAAc,EAAY,QAChC,GAAI,CAAC,EAAa,CAChB,IAAM,EAAU,KAAK,IAAI,EAAG,CAAG,EAG3B,GAAY,OAAO,EAAG,UAAa,WAAY,EAAG,SAAS,CAAE,IAAK,EAAS,UAAS,CAAC,EACpF,EAAG,UAAY,EACpB,MACF,CAIA,GAAM,CAAE,OAAQ,EAAgB,EAAI,CAAW,EAC/C,GAAI,EAAiB,CAAW,EAAG,CACjC,EAAY,SAAS,CAAE,IAAK,KAAK,IAAI,EAAG,EAAY,QAAU,EAAM,CAAG,EAAG,SAAU,GAAY,MAAO,CAAC,EACxG,MACF,CACA,IAAM,EAAS,KAAK,IAAI,EAAG,EAAY,UAAY,EAAM,CAAG,EACxD,GAAY,OAAO,EAAY,UAAa,WAAY,EAAY,SAAS,CAAE,IAAK,EAAQ,UAAS,CAAC,EACrG,EAAY,UAAY,CAC/B,EAAG,CAAC,CAAC,EAEL,MAAsB,CACpB,IAAM,EAAK,EAAQ,QACb,EAAc,EAAe,QAOnC,GANA,EAAe,QAAU,EAGrB,CAAC,GAAM,EAAY,SAAW,GAG9B,EAAe,QAAS,OAC5B,IAAM,EAAe,EAAgB,CAAC,CAAC,IACvC,GAAI,GAAgB,EAAG,OAOvB,IAAM,EAAY,EAAY,OAAS,EACjC,EAAS,EAAe,EAAa,EAAc,CAAS,EAClE,GAAI,GAAU,EAAM,OAAQ,OAC5B,IAAM,EAAS,EAAe,EAAY,GACpC,EAAS,EAAQ,GAAU,EAC7B,KAAK,IAAI,EAAS,CAAY,EAAI,KACpC,EAAe,CAAM,EACrB,GAAa,CAAM,EAEvB,EAAG,CAAC,EAAS,EAAM,OAAQ,EAAiB,CAAc,CAAC,EAG3D,IAAM,EAAmB,GACtB,EAAe,IAAsD,CACpE,IAAM,EAAK,EAAQ,QACnB,GAAI,CAAC,GAAM,EAAM,SAAW,EAAG,OAAO,KACtC,IAAM,EAAI,KAAK,IAAI,EAAG,KAAK,IAAI,EAAO,EAAM,OAAS,CAAC,CAAC,EACjD,EAAS,EAAQ,IAAM,EACvB,GAAa,EAAQ,EAAI,IAAM,GAAU,EAOzC,EAAe,EAAU,SAAS,cAAgB,EAGlD,EAAS,KAAK,IAAI,EAAG,EAAgB,EAAI,EAAY,OAAO,CAAC,CAAC,eAAiB,CAAY,EAC3F,EAAQ,GAAS,OAAS,QAC5B,EAAM,EAGV,OAFI,IAAU,SAAU,IAAQ,EAAS,GAAa,EAC7C,IAAU,QAAO,GAAO,EAAS,GACnC,CACT,EACA,CAAC,EAAM,OAAQ,CAAO,CACxB,EASM,EAAiB,EAMb,IAAI,EAKd,MAAsB,CACpB,IAAM,EAAU,EAAe,QACzB,EAAK,EAAQ,QACnB,GAAI,CAAC,EAAS,OACd,IAAM,EAAS,EAAQ,QAAU,GAAS,EAAK,EAAiB,EAAQ,MAAO,EAAQ,OAAO,EAAI,KAClG,GAAI,GAAU,KAAM,CAClB,EAAe,QAAU,KACzB,MACF,CACA,IAAM,EAAW,KAAK,IAAI,EAAS,EAAQ,UAAU,GAAK,EAI1D,GAAK,GAAY,KAAK,IAAI,EAAgB,CAAC,CAAC,IAAM,CAAM,GAAK,GAAM,EAAQ,UAAY,GAAsB,CAC3G,EAAe,QAAU,KACzB,MACF,CACI,IACJ,EAAQ,WAAa,EACrB,EAAQ,UAAY,EACpB,EAAe,EAAQ,EAAQ,SAAS,QAAQ,EAClD,CAAC,EAID,MAAgB,CACd,IAAM,EAAK,EAAQ,QACnB,GAAI,CAAC,EAAI,OACT,IAAM,MAAe,CACnB,EAAe,QAAU,IAC3B,EAGM,EAA6B,GAAY,EAI/C,OAHA,EAAc,iBAAiB,QAAS,EAAQ,CAAE,QAAS,EAAK,CAAC,EACjE,EAAc,iBAAiB,aAAc,EAAQ,CAAE,QAAS,EAAK,CAAC,EACtE,EAAG,iBAAiB,UAAW,CAAM,MACxB,CACX,EAAc,oBAAoB,QAAS,CAAM,EACjD,EAAc,oBAAoB,aAAc,CAAM,EACtD,EAAG,oBAAoB,UAAW,CAAM,CAC1C,CACF,EAAG,CAAC,CAAQ,CAAC,EAEb,IAAM,GAAY,MAAmF,CACnG,SAAS,EAAc,EAAe,EAAoC,CACxE,IAAM,EAAS,EAAiB,EAAO,CAAO,EAC1C,GAAU,OACd,EAAe,QAAU,CAAE,QAAO,UAAS,WAAY,EAAQ,SAAU,EAAG,OAAM,EAClF,EAAe,EAAQ,GAAS,QAAQ,EAC1C,CACA,MAAO,CACL,gBACA,YAAY,EAAK,EAAS,CACxB,IAAM,EAAI,EAAM,WAAW,EAAM,KAAS,EAAa,EAAW,EAAM,CAAG,EAAI,KAAS,CAAG,EACvF,GAAK,GAAG,EAAc,EAAG,CAAO,CACtC,EACA,YAAY,EAAS,CACnB,EAAe,QAAU,KACzB,EAAe,EAAG,GAAS,QAAQ,CACrC,EACA,qBAAwB,CACtB,IAAM,EAAc,EAAY,QAEhC,OADK,EACE,EAAiB,CAAW,EAAI,KAAO,EADrB,EAAQ,OAEnC,EACA,UAAW,CACb,CACF,EAAG,CAAC,EAAO,EAAY,EAAkB,EAAgB,CAAY,CAAC,EAEhE,CAAE,aAAY,YAAa,MACzB,EAAgB,EAAS,EAAW,EAAiB,GAAU,EAAM,MAAM,EACjF,CAAC,EAAS,EAAW,EAAiB,GAAU,EAAM,MAAM,CAC9D,EAMM,EAAY,EAAO,EAAK,EAU9B,OATA,MAAgB,CACV,EAAU,SAAW,IAAoB,GACzC,EAAM,OAAS,IAAyB,EAAW,EAAa,EAAI,EAAM,SAC9E,EAAU,QAAU,GACpB,QAAQ,KACN,8BAA8B,EAAM,OAAO,oLAC7C,EACF,EAAG,CAAC,EAAiB,EAAM,OAAQ,EAAY,CAAQ,CAAC,EAEjD,CACL,UACA,YACA,cACA,UACA,UACA,cACA,kBACA,kBACA,iBACA,aACA,WACA,sBACA,aACA,kBACA,kBACA,YACA,YACF,CACF"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use client";import{useLabels as e}from"../configProvider/useLabels.mjs";import t from"../loading/spinnerLoading/index.mjs";import n from"../checkbox/checkbox.mjs";import r from"./virtualList.module.mjs";import"./virtualList.utils.mjs";import ee from"./virtualRow.mjs";import i from"./virtualListHeader.mjs";import{useVirtualWindow as a}from"./useVirtualWindow.mjs";import{useSelection as o}from"./useSelection.mjs";import{useColumns as te}from"./useColumns.mjs";import{useRowDrag as ne}from"./useRowDrag.mjs";import{useSorting as re}from"./useSorting.mjs";import{useColumnResize as ie}from"./useColumnResize.mjs";import{useRowNavigation as ae}from"./useRowNavigation.mjs";import{useCallback as
|
|
1
|
+
"use client";import{useLabels as e}from"../configProvider/useLabels.mjs";import t from"../loading/spinnerLoading/index.mjs";import n from"../checkbox/checkbox.mjs";import r from"./virtualList.module.mjs";import"./virtualList.utils.mjs";import ee from"./virtualRow.mjs";import i from"./virtualListHeader.mjs";import{useVirtualWindow as a}from"./useVirtualWindow.mjs";import{useSelection as o}from"./useSelection.mjs";import{useColumns as te}from"./useColumns.mjs";import{useRowDrag as ne}from"./useRowDrag.mjs";import{useSorting as re}from"./useSorting.mjs";import{useColumnResize as ie}from"./useColumnResize.mjs";import{useRowNavigation as ae}from"./useRowNavigation.mjs";import{useCallback as oe,useEffect as s,useId as se,useImperativeHandle as ce,useMemo as c,useRef as le}from"react";import l from"classnames";import{Fragment as ue,jsx as u,jsxs as d}from"react/jsx-runtime";var de={selectRow:`Select row`,selectAllRows:`Select all rows`,toggleColumns:`Toggle column visibility`,columnsHidden:e=>`${e} columns hidden`,resetColumnWidths:`Reset column widths`};function f(e,t){return!e||!t?e??t:n=>{e(n),t(n)}}function p(p){let{ref:fe,items:pe,renderRow:me,ariaRoles:m,columns:h,height:he=`100%`,estimatedRowHeight:ge=40,overscan:_e=3,rowGap:ve=0,rowPadding:g,selectedKey:_,getItemKey:v,onRowClick:y,keyboardNavigation:b=!0,tabAcrossRows:ye=!1,scrollParent:be,visibleColumnKeys:xe,onVisibleColumnsChange:Se,persistColumnsKey:x,resizableColumns:Ce=!1,columnWidths:we,onColumnWidthsChange:Te,labels:Ee,sort:De,defaultSort:Oe,onSortChange:ke,showColumnToggle:Ae,onLoadMore:je,loadMoreThreshold:Me=100,isLoading:S=!1,multiSelect:C=!1,selectedKeys:Ne,onSelectionChange:Pe,reorderable:w=!1,onReorder:Fe,longPressDelay:Ie=400,treeReorder:T=!1,getItemDepth:E,isGroup:D,onTreeDrop:Le,treeIndentPx:Re=24,dragHandle:ze=!1,showDivider:Be=!0,rowHover:Ve=!0,className:He,style:Ue,classNames:O,styles:k,...A}=p,j=p.scrollParent!=null&&p.height!==void 0;s(()=>{j&&console.warn("VirtualList: `height` is ignored alongside `scrollParent`, which leaves the list no viewport to size.")},[j]);let M=e(`VirtualList`,Ee,de),{rows:N,effectiveSort:We,isSorted:P,handleHeaderSort:Ge}=re({items:pe,columns:h,sort:De,defaultSort:Oe,onSortChange:ke}),F=oe((e,t)=>v?v(e,t):t,[v]),I=c(()=>N.map((e,t)=>F(e,t)),[N,F]),Ke=c(()=>E?N.map((e,t)=>E(e,t)):[],[N,E]),qe=oe(e=>D?D(N[e],e):!1,[D,N]),L=a({items:N,keys:I,getItemKey:v,estimatedRowHeight:ge,rowGap:ve,overscan:_e,onLoadMore:je,loadMoreThreshold:Me,isLoading:S,scrollParent:be}),{rootRef:R,headerRef:z,rowObserver:Je,keysRef:Ye,offsets:B,totalHeight:Xe,startIndex:V,endIndex:H}=L,U=le([]),W=ie({columns:h,visibleColumnsRef:U,resizableColumns:Ce,columnWidths:we,onColumnWidthsChange:Te,persistColumnsKey:x,rootRef:R}),G=o({items:N,getKey:F,multiSelect:C,selectedKeys:Ne,onSelectionChange:Pe}),{effectiveSelectedKeys:K}=G,q=te({columns:h,items:N,multiSelect:C,visibleColumnKeys:xe,onVisibleColumnsChange:Se,persistColumnsKey:x,showColumnToggle:Ae,userWidths:W.widths,isFrozen:W.isFrozen,rootRef:R,startIndex:V,endIndex:H,containerHeight:L.containerHeight,containerWidth:L.containerWidth}),{hasColumns:J,visibleColumns:Ze,columnTemplate:Qe,widthVars:$e,shouldShowToggle:et,recalculateColumns:tt}=q;U.current=Ze,ce(fe,()=>({...L.scrollApi,recalculateColumns:tt,resetColumnWidths:W.reset}),[L.scrollApi,tt,W.reset]);let nt=P&&(w||T);s(()=>{nt&&console.warn(`VirtualList: row reordering is disabled while the list is sorted. Clear the sort to drag rows.`)},[nt]);let Y=ne({reorderable:w&&!P,treeReorder:T&&!P,onReorder:Fe,onTreeDrop:Le,longPressDelay:Ie,dragHandle:ze,treeIndentPx:Re,keys:I,depths:Ke,isGroupAt:qe,rootRef:R,keysRef:Ye,getVisibleRect:L.getVisibleRect,scrollBy:L.scrollBy}),rt=se(),it=e=>`${rt}-row-${e}`,X=ae({enabled:b,tabAcrossRows:ye,rowCount:N.length,offsets:B,rootRef:R,headerRef:z,getVisibleSlice:L.getVisibleSlice,viewportHeight:L.viewportHeight,scrollToIndex:L.scrollApi.scrollToIndex,initialIndex:_==null?0:Math.max(0,I.indexOf(_)),multiSelect:C,onActivate:y&&(e=>y(N[e],e)),onToggleSelect:e=>G.handleRowSelect(I[e])}),at=typeof g==`number`?`${g}px`:g,Z=e=>{let t=N[e],i=F(t,e),a=_!=null&&i===_||K.has(i),o=Y.getRowDragProps(e,i);return u(ee,{id:it(e),index:e,top:B[e],padding:at,columnTemplate:Qe,isSelected:a,isActive:e===X.activeIndex,manageTabStops:b,isEntered:e===X.enteredIndex,activeClassName:O?.activeRow,activeStyle:k?.activeRow,showDivider:Be,hover:Ve,onClick:()=>{Y.consumeSuppressedClick()||(X.setActiveIndex(e),y?.(t,e))},observer:Je,ariaRowIndex:J?e+2:void 0,ariaPosInSet:J?void 0:e+1,ariaSetSize:J?void 0:N.length,rowRole:m?.row??(J?`row`:`listitem`),reserveToggleGutter:et,...o,dropIndicatorClassName:O?.dropIndicator,dropIndicatorStyle:k?.dropIndicator,rowClassName:O?.row,rowStyle:k?.row,children:J?d(ue,{children:[C&&u(`div`,{className:l(r.virtualListSelectCell,O?.selectCell),style:k?.selectCell,role:`gridcell`,onClick:e=>e.stopPropagation(),children:u(n,{selected:K.has(i),onToggle:()=>G.handleRowSelect(i),"aria-label":M.selectRow,tabIndex:-1})}),Ze.map(n=>u(`div`,{className:l(r.virtualListCell,O?.cell),style:k?.cell,role:`gridcell`,"data-col-key":n.key,"data-fit-key":n.width.type===`fit`?n.key:void 0,children:n.renderCell(t,e)},n.key))]}):me?.(t,e)},i)},Q=[];for(let e=V;e<=H;e++)Q.push(Z(e));let $=Y.draggingKey==null?-1:I.indexOf(Y.draggingKey);$>=0&&($<V||$>H)&&Q.push(Z($)),X.enteredIndex>=0&&(X.enteredIndex<V||X.enteredIndex>H)&&Q.push(Z(X.enteredIndex));let ot={...X.containerProps,onKeyDown:f(A.onKeyDown,X.containerProps.onKeyDown),onFocus:f(A.onFocus,X.containerProps.onFocus),onBlur:f(A.onBlur,X.containerProps.onBlur)};return d(`div`,{...A,...ot,ref:R,className:l(r.virtualList,L.isWindowed&&r.virtualListWindowed,Y.draggingKey!=null&&r.virtualListReordering,W.isResizing&&r.virtualListResizing,He,O?.root),style:{...L.isWindowed?null:{height:he},...$e,...Ue,...k?.root},role:m?.container??(J?`grid`:`list`),"aria-rowcount":J?N.length+1:void 0,"aria-activedescendant":X.activeIndex>=0&&(m?.container??(J?`grid`:`list`))!==`list`?it(X.activeIndex):void 0,children:[J&&u(i,{headerRef:z,columns:h,cols:q,resize:W,sort:We,onSort:Ge,multiSelect:C,allSelected:G.allSelected,someSelected:G.someSelected,onSelectAll:G.handleSelectAll,labels:M,classNames:O,styles:k}),S?u(`div`,{className:r.virtualListLoadingFull,children:u(t,{className:r.virtualListSpinner})}):u(`div`,{className:l(r.virtualListBody,O?.body),style:{height:Xe,...k?.body},children:Q}),L.showLoadMoreSpinner&&u(`div`,{className:l(r.virtualListLoadingIndicator,O?.loadingIndicator),style:k?.loadingIndicator,children:u(t,{className:r.virtualListSpinner})})]})}export{p as default};
|
|
2
2
|
//# sourceMappingURL=virtualList.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"virtualList.mjs","names":[],"sources":["../../../../package/common/virtualList/virtualList.tsx"],"sourcesContent":["import React, { useMemo, useCallback, useEffect, useId, useImperativeHandle, useRef } from 'react';\nimport cx from 'classnames';\nimport styles from './virtualList.module.pcss';\nimport SpinnerLoading from '../loading/spinnerLoading';\nimport Checkbox from '../checkbox';\nimport VirtualRow from './virtualRow';\nimport VirtualListHeader from './virtualListHeader';\nimport { useVirtualWindow } from './useVirtualWindow';\nimport { useSelection } from './useSelection';\nimport { useColumns } from './useColumns';\nimport { useRowDrag } from './useRowDrag';\nimport { useSorting } from './useSorting';\nimport { useColumnResize } from './useColumnResize';\nimport { useRowNavigation } from './useRowNavigation';\nimport { useLabels } from '../configProvider';\nimport type { VirtualListColumn, VirtualListLabels, VirtualListProps } from './virtualList.types';\n\nconst DEFAULT_LABELS: Required<VirtualListLabels> = {\n selectRow: 'Select row',\n selectAllRows: 'Select all rows',\n toggleColumns: 'Toggle column visibility',\n columnsHidden: (count) => `${count} columns hidden`,\n resetColumnWidths: 'Reset column widths',\n};\n\n// Pure helpers live in ./virtualList.utils; re-exported here so existing\n// imports (and unit tests) keep working against the component entry point.\nexport {\n buildOffsets,\n findStartIndex,\n getVisibleRange,\n resolveReorderTarget,\n resolveTreeDropTarget,\n lastDescendantIndex,\n buildColumnTemplate,\n compareSortValues,\n isColumnSortable,\n isEmptySortValue,\n nextSort,\n sortItems,\n} from './virtualList.utils';\n\n// ─── Orchestrator ─────────────────────────────────────────────────────────────\n// VirtualListInner wires the hooks together and renders the two pieces of\n// markup. Each concern lives in its own module:\n// useSorting — table mode: header sort state, and the sorted row order\n// useVirtualWindow — generic scroll / measurement / windowing (all modes)\n// useColumnResize — table mode: user column widths, and the drag that sets them\n// useSelection — multi-row selection\n// useColumns — table mode: columns, visibility, persistence, fit widths\n// useRowDrag — reorder + tree drag-and-drop (mouse + touch)\n// useRowNavigation — arrow-key navigation over the rows\n// virtualListHeader — the header row: sorting, resizing, the column menu\n// virtualRow — one positioned row, with its drag wiring\n//\n// The hook order is not free: useColumnResize decides whether the layout is\n// frozen, which useColumns needs to build the grid template.\n\nfunction VirtualList<T>({\n ref,\n items,\n renderRow,\n ariaRoles,\n columns,\n height = '100%',\n estimatedRowHeight = 40,\n overscan = 3,\n rowGap = 0,\n rowPadding,\n selectedKey,\n getItemKey,\n onRowClick,\n keyboardNavigation = true,\n visibleColumnKeys,\n onVisibleColumnsChange,\n persistColumnsKey,\n resizableColumns = false,\n columnWidths,\n onColumnWidthsChange,\n labels,\n sort,\n defaultSort,\n onSortChange,\n showColumnToggle,\n onLoadMore,\n loadMoreThreshold = 100,\n isLoading = false,\n multiSelect = false,\n selectedKeys,\n onSelectionChange,\n reorderable = false,\n onReorder,\n longPressDelay = 400,\n treeReorder = false,\n getItemDepth,\n isGroup,\n onTreeDrop,\n treeIndentPx = 24,\n dragHandle = false,\n showDivider = true,\n rowHover = true,\n className,\n style,\n classNames,\n styles: slotStyles,\n ...rest\n}: VirtualListProps<T>) {\n const text = useLabels('VirtualList', labels, DEFAULT_LABELS);\n const { rows, effectiveSort, isSorted, handleHeaderSort } = useSorting<T>({\n items,\n columns,\n sort,\n defaultSort,\n onSortChange,\n });\n\n const getKey = useCallback(\n (item: T, index: number): string | number => (getItemKey ? getItemKey(item, index) : index),\n [getItemKey],\n );\n const keys = useMemo(() => rows.map((item, i) => getKey(item, i)), [rows, getKey]);\n const depths = useMemo(\n () => (getItemDepth ? rows.map((item, i) => getItemDepth(item, i)) : []),\n [rows, getItemDepth],\n );\n const isGroupAt = useCallback((i: number) => (isGroup ? isGroup(rows[i], i) : false), [isGroup, rows]);\n\n const win = useVirtualWindow<T>({\n items: rows,\n keys,\n getItemKey,\n estimatedRowHeight,\n rowGap,\n overscan,\n onLoadMore,\n loadMoreThreshold,\n isLoading,\n });\n const { rootRef, headerRef, rowObserver, keysRef, offsets, totalHeight, startIndex, endIndex } = win;\n\n // Filled from `cols` below: the resize hook only reads the visible set when a\n // handle is actually used, which is what lets it run before the hook that\n // derives it.\n const visibleColumnsRef = useRef<VirtualListColumn<T>[]>([]);\n const columnResize = useColumnResize<T>({\n columns,\n visibleColumnsRef,\n resizableColumns,\n columnWidths,\n onColumnWidthsChange,\n persistColumnsKey,\n rootRef,\n });\n\n const selection = useSelection<T>({ items: rows, getKey, multiSelect, selectedKeys, onSelectionChange });\n const { effectiveSelectedKeys } = selection;\n\n const cols = useColumns<T>({\n columns,\n items: rows,\n multiSelect,\n visibleColumnKeys,\n onVisibleColumnsChange,\n persistColumnsKey,\n showColumnToggle,\n userWidths: columnResize.widths,\n isFrozen: columnResize.isFrozen,\n rootRef,\n startIndex,\n endIndex,\n containerHeight: win.containerHeight,\n containerWidth: win.containerWidth,\n });\n // The rest of `cols` is the header's business and goes there whole.\n const { hasColumns, visibleColumns, columnTemplate, widthVars, shouldShowToggle, recalculateColumns } = cols;\n visibleColumnsRef.current = visibleColumns;\n\n // Compose the forwarded handle from the hooks that own each piece: scroll\n // methods from the windowing engine, column controls from the column hooks.\n useImperativeHandle(ref, () => ({ ...win.scrollApi, recalculateColumns, resetColumnWidths: columnResize.reset }), [\n win.scrollApi,\n recalculateColumns,\n columnResize.reset,\n ]);\n\n // Dragging a row to position N is meaningless while the list decides the\n // order: the drop would be undone by the next sort pass.\n const dragBlockedBySort = isSorted && (reorderable || treeReorder);\n useEffect(() => {\n if (dragBlockedBySort)\n console.warn('VirtualList: row reordering is disabled while the list is sorted. Clear the sort to drag rows.');\n }, [dragBlockedBySort]);\n\n const drag = useRowDrag({\n reorderable: reorderable && !isSorted,\n treeReorder: treeReorder && !isSorted,\n onReorder,\n onTreeDrop,\n longPressDelay,\n dragHandle,\n treeIndentPx,\n keys,\n depths,\n isGroupAt,\n rootRef,\n keysRef,\n });\n\n const listId = useId();\n const rowDomId = (index: number) => `${listId}-row-${index}`;\n const nav = useRowNavigation({\n enabled: keyboardNavigation,\n rowCount: rows.length,\n offsets,\n rootRef,\n headerRef,\n scrollToIndex: win.scrollApi.scrollToIndex,\n initialIndex: selectedKey != null ? Math.max(0, keys.indexOf(selectedKey)) : 0,\n multiSelect,\n onActivate: onRowClick && ((index: number) => onRowClick(rows[index], index)),\n onToggleSelect: (index) => selection.handleRowSelect(keys[index]),\n });\n\n const rowPaddingValue = typeof rowPadding === 'number' ? `${rowPadding}px` : rowPadding;\n\n const renderRowAt = (i: number) => {\n const item = rows[i];\n const key = getKey(item, i);\n const isSelected = (selectedKey != null && key === selectedKey) || effectiveSelectedKeys.has(key);\n const dragProps = drag.getRowDragProps(i, key);\n\n return (\n <VirtualRow\n key={key}\n id={rowDomId(i)}\n index={i}\n top={offsets[i]}\n padding={rowPaddingValue}\n columnTemplate={columnTemplate}\n isSelected={isSelected}\n isActive={i === nav.activeIndex}\n manageTabStops={keyboardNavigation}\n isEntered={i === nav.enteredIndex}\n activeClassName={classNames?.activeRow}\n activeStyle={slotStyles?.activeRow}\n showDivider={showDivider}\n hover={rowHover}\n onClick={() => {\n if (drag.consumeSuppressedClick()) return;\n // Keep the arrows where the pointer last was.\n nav.setActiveIndex(i);\n onRowClick?.(item, i);\n }}\n observer={rowObserver}\n ariaRowIndex={hasColumns ? i + 2 : undefined}\n ariaPosInSet={hasColumns ? undefined : i + 1}\n ariaSetSize={hasColumns ? undefined : rows.length}\n rowRole={ariaRoles?.row ?? (hasColumns ? 'row' : 'listitem')}\n reserveToggleGutter={shouldShowToggle}\n {...dragProps}\n dropIndicatorClassName={classNames?.dropIndicator}\n dropIndicatorStyle={slotStyles?.dropIndicator}\n rowClassName={classNames?.row}\n rowStyle={slotStyles?.row}\n >\n {hasColumns ? (\n <>\n {multiSelect && (\n <div\n className={cx(styles.virtualListSelectCell, classNames?.selectCell)}\n style={slotStyles?.selectCell}\n role='gridcell'\n onClick={(e) => e.stopPropagation()}\n >\n <Checkbox\n selected={effectiveSelectedKeys.has(key)}\n onToggle={() => selection.handleRowSelect(key)}\n aria-label={text.selectRow}\n // One tab stop per rendered row, changing as the list scrolls,\n // would strand the focus the moment a row unmounts. Space on\n // the active row is the keyboard path instead.\n tabIndex={-1}\n />\n </div>\n )}\n {visibleColumns.map((col) => (\n <div\n key={col.key}\n className={cx(styles.virtualListCell, classNames?.cell)}\n style={slotStyles?.cell}\n role='gridcell'\n data-col-key={col.key}\n data-fit-key={col.width.type === 'fit' ? col.key : undefined}\n >\n {col.renderCell(item, i)}\n </div>\n ))}\n </>\n ) : (\n renderRow?.(item, i)\n )}\n </VirtualRow>\n );\n };\n\n const visibleRows: React.ReactNode[] = [];\n for (let i = startIndex; i <= endIndex; i++) visibleRows.push(renderRowAt(i));\n\n // Keep the row being touch-dragged mounted even if it scrolls out of the\n // window. Touch events are dispatched to the element under the finger at\n // touchstart; if that row unmounts, the browser stops delivering touchmove\n // and the drag freezes. Pinning its DOM node keeps the gesture alive.\n const draggedIndex = drag.draggingKey != null ? keys.indexOf(drag.draggingKey) : -1;\n if (draggedIndex >= 0 && (draggedIndex < startIndex || draggedIndex > endIndex)) {\n visibleRows.push(renderRowAt(draggedIndex));\n }\n\n // Same reason, one step further: the row being used holds the focus, and\n // unmounting it would drop that focus to the document body mid-scroll.\n if (nav.enteredIndex >= 0 && (nav.enteredIndex < startIndex || nav.enteredIndex > endIndex)) {\n visibleRows.push(renderRowAt(nav.enteredIndex));\n }\n\n return (\n <div\n {...rest}\n {...nav.containerProps}\n ref={rootRef}\n className={cx(\n styles.virtualList,\n drag.draggingKey != null && styles.virtualListReordering,\n columnResize.isResizing && styles.virtualListResizing,\n className,\n classNames?.root,\n )}\n style={{ height, ...widthVars, ...style, ...slotStyles?.root }}\n onScroll={win.handleScroll}\n role={ariaRoles?.container ?? (hasColumns ? 'grid' : 'list')}\n aria-rowcount={hasColumns ? rows.length + 1 : undefined}\n // Only a composite role may point at an active descendant, and the default\n // `list` is not one. In list mode, pass `ariaRoles` (e.g. listbox/option)\n // to have the active row announced.\n aria-activedescendant={\n nav.activeIndex >= 0 && (ariaRoles?.container ?? (hasColumns ? 'grid' : 'list')) !== 'list'\n ? rowDomId(nav.activeIndex)\n : undefined\n }\n >\n {hasColumns && (\n <VirtualListHeader<T>\n headerRef={headerRef}\n columns={columns!}\n cols={cols}\n resize={columnResize}\n sort={effectiveSort}\n onSort={handleHeaderSort}\n multiSelect={multiSelect}\n allSelected={selection.allSelected}\n someSelected={selection.someSelected}\n onSelectAll={selection.handleSelectAll}\n labels={text}\n classNames={classNames}\n styles={slotStyles}\n />\n )}\n {isLoading ? (\n <div className={styles.virtualListLoadingFull}>\n <SpinnerLoading className={styles.virtualListSpinner} />\n </div>\n ) : (\n <div\n className={cx(styles.virtualListBody, classNames?.body)}\n style={{ height: totalHeight, ...slotStyles?.body }}\n >\n {visibleRows}\n </div>\n )}\n {win.showLoadMoreSpinner && (\n <div\n className={cx(styles.virtualListLoadingIndicator, classNames?.loadingIndicator)}\n style={slotStyles?.loadingIndicator}\n >\n <SpinnerLoading className={styles.virtualListSpinner} />\n </div>\n )}\n </div>\n );\n}\n\nexport default VirtualList;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"virtualList.mjs","names":[],"sources":["../../../../package/common/virtualList/virtualList.tsx"],"sourcesContent":["import React, { useMemo, useCallback, useEffect, useId, useImperativeHandle, useRef } from 'react';\nimport cx from 'classnames';\nimport styles from './virtualList.module.pcss';\nimport SpinnerLoading from '../loading/spinnerLoading';\nimport Checkbox from '../checkbox';\nimport VirtualRow from './virtualRow';\nimport VirtualListHeader from './virtualListHeader';\nimport { useVirtualWindow } from './useVirtualWindow';\nimport { useSelection } from './useSelection';\nimport { useColumns } from './useColumns';\nimport { useRowDrag } from './useRowDrag';\nimport { useSorting } from './useSorting';\nimport { useColumnResize } from './useColumnResize';\nimport { useRowNavigation } from './useRowNavigation';\nimport { useLabels } from '../configProvider';\nimport type { VirtualListColumn, VirtualListLabels, VirtualListProps } from './virtualList.types';\n\nconst DEFAULT_LABELS: Required<VirtualListLabels> = {\n selectRow: 'Select row',\n selectAllRows: 'Select all rows',\n toggleColumns: 'Toggle column visibility',\n columnsHidden: (count) => `${count} columns hidden`,\n resetColumnWidths: 'Reset column widths',\n};\n\n// Pure helpers live in ./virtualList.utils; re-exported here so existing\n// imports (and unit tests) keep working against the component entry point.\nexport {\n buildOffsets,\n findStartIndex,\n getVisibleRange,\n resolveReorderTarget,\n resolveTreeDropTarget,\n lastDescendantIndex,\n buildColumnTemplate,\n compareSortValues,\n isColumnSortable,\n isEmptySortValue,\n nextSort,\n sortItems,\n} from './virtualList.utils';\n\n// ─── Orchestrator ─────────────────────────────────────────────────────────────\n// VirtualListInner wires the hooks together and renders the two pieces of\n// markup. Each concern lives in its own module:\n// useSorting — table mode: header sort state, and the sorted row order\n// useVirtualWindow — generic scroll / measurement / windowing (all modes)\n// useColumnResize — table mode: user column widths, and the drag that sets them\n// useSelection — multi-row selection\n// useColumns — table mode: columns, visibility, persistence, fit widths\n// useRowDrag — reorder + tree drag-and-drop (mouse + touch)\n// useRowNavigation — arrow-key navigation over the rows\n// virtualListHeader — the header row: sorting, resizing, the column menu\n// virtualRow — one positioned row, with its drag wiring\n//\n// The hook order is not free: useColumnResize decides whether the layout is\n// frozen, which useColumns needs to build the grid template.\n\n/** One handler after the other, or whichever of the two exists. */\nfunction chain<E>(theirs: ((event: E) => void) | undefined, ours: ((event: E) => void) | undefined) {\n if (!theirs || !ours) return theirs ?? ours;\n return (event: E) => {\n theirs(event);\n ours(event);\n };\n}\n\nfunction VirtualList<T>(props: VirtualListProps<T>) {\n const {\n ref,\n items,\n renderRow,\n ariaRoles,\n columns,\n height = '100%',\n estimatedRowHeight = 40,\n overscan = 3,\n rowGap = 0,\n rowPadding,\n selectedKey,\n getItemKey,\n onRowClick,\n keyboardNavigation = true,\n tabAcrossRows = false,\n scrollParent,\n visibleColumnKeys,\n onVisibleColumnsChange,\n persistColumnsKey,\n resizableColumns = false,\n columnWidths,\n onColumnWidthsChange,\n labels,\n sort,\n defaultSort,\n onSortChange,\n showColumnToggle,\n onLoadMore,\n loadMoreThreshold = 100,\n isLoading = false,\n multiSelect = false,\n selectedKeys,\n onSelectionChange,\n reorderable = false,\n onReorder,\n longPressDelay = 400,\n treeReorder = false,\n getItemDepth,\n isGroup,\n onTreeDrop,\n treeIndentPx = 24,\n dragHandle = false,\n showDivider = true,\n rowHover = true,\n className,\n style,\n classNames,\n styles: slotStyles,\n ...rest\n } = props;\n // Read off the raw props: the destructured value carries a default and so\n // cannot tell a passed '100%' from an omitted one.\n const heightIgnored = props.scrollParent != null && props.height !== undefined;\n useEffect(() => {\n if (heightIgnored)\n console.warn(\n 'VirtualList: `height` is ignored alongside `scrollParent`, which leaves the list no viewport to size.',\n );\n }, [heightIgnored]);\n\n const text = useLabels('VirtualList', labels, DEFAULT_LABELS);\n const { rows, effectiveSort, isSorted, handleHeaderSort } = useSorting<T>({\n items,\n columns,\n sort,\n defaultSort,\n onSortChange,\n });\n\n const getKey = useCallback(\n (item: T, index: number): string | number => (getItemKey ? getItemKey(item, index) : index),\n [getItemKey],\n );\n const keys = useMemo(() => rows.map((item, i) => getKey(item, i)), [rows, getKey]);\n const depths = useMemo(\n () => (getItemDepth ? rows.map((item, i) => getItemDepth(item, i)) : []),\n [rows, getItemDepth],\n );\n const isGroupAt = useCallback((i: number) => (isGroup ? isGroup(rows[i], i) : false), [isGroup, rows]);\n\n const win = useVirtualWindow<T>({\n items: rows,\n keys,\n getItemKey,\n estimatedRowHeight,\n rowGap,\n overscan,\n onLoadMore,\n loadMoreThreshold,\n isLoading,\n scrollParent,\n });\n const { rootRef, headerRef, rowObserver, keysRef, offsets, totalHeight, startIndex, endIndex } = win;\n\n // Filled from `cols` below: the resize hook only reads the visible set when a\n // handle is actually used, which is what lets it run before the hook that\n // derives it.\n const visibleColumnsRef = useRef<VirtualListColumn<T>[]>([]);\n const columnResize = useColumnResize<T>({\n columns,\n visibleColumnsRef,\n resizableColumns,\n columnWidths,\n onColumnWidthsChange,\n persistColumnsKey,\n rootRef,\n });\n\n const selection = useSelection<T>({ items: rows, getKey, multiSelect, selectedKeys, onSelectionChange });\n const { effectiveSelectedKeys } = selection;\n\n const cols = useColumns<T>({\n columns,\n items: rows,\n multiSelect,\n visibleColumnKeys,\n onVisibleColumnsChange,\n persistColumnsKey,\n showColumnToggle,\n userWidths: columnResize.widths,\n isFrozen: columnResize.isFrozen,\n rootRef,\n startIndex,\n endIndex,\n containerHeight: win.containerHeight,\n containerWidth: win.containerWidth,\n });\n // The rest of `cols` is the header's business and goes there whole.\n const { hasColumns, visibleColumns, columnTemplate, widthVars, shouldShowToggle, recalculateColumns } = cols;\n visibleColumnsRef.current = visibleColumns;\n\n // Compose the forwarded handle from the hooks that own each piece: scroll\n // methods from the windowing engine, column controls from the column hooks.\n useImperativeHandle(ref, () => ({ ...win.scrollApi, recalculateColumns, resetColumnWidths: columnResize.reset }), [\n win.scrollApi,\n recalculateColumns,\n columnResize.reset,\n ]);\n\n // Dragging a row to position N is meaningless while the list decides the\n // order: the drop would be undone by the next sort pass.\n const dragBlockedBySort = isSorted && (reorderable || treeReorder);\n useEffect(() => {\n if (dragBlockedBySort)\n console.warn('VirtualList: row reordering is disabled while the list is sorted. Clear the sort to drag rows.');\n }, [dragBlockedBySort]);\n\n const drag = useRowDrag({\n reorderable: reorderable && !isSorted,\n treeReorder: treeReorder && !isSorted,\n onReorder,\n onTreeDrop,\n longPressDelay,\n dragHandle,\n treeIndentPx,\n keys,\n depths,\n isGroupAt,\n rootRef,\n keysRef,\n getVisibleRect: win.getVisibleRect,\n scrollBy: win.scrollBy,\n });\n\n const listId = useId();\n const rowDomId = (index: number) => `${listId}-row-${index}`;\n const nav = useRowNavigation({\n enabled: keyboardNavigation,\n tabAcrossRows,\n rowCount: rows.length,\n offsets,\n rootRef,\n headerRef,\n getVisibleSlice: win.getVisibleSlice,\n viewportHeight: win.viewportHeight,\n scrollToIndex: win.scrollApi.scrollToIndex,\n initialIndex: selectedKey != null ? Math.max(0, keys.indexOf(selectedKey)) : 0,\n multiSelect,\n onActivate: onRowClick && ((index: number) => onRowClick(rows[index], index)),\n onToggleSelect: (index) => selection.handleRowSelect(keys[index]),\n });\n\n const rowPaddingValue = typeof rowPadding === 'number' ? `${rowPadding}px` : rowPadding;\n\n const renderRowAt = (i: number) => {\n const item = rows[i];\n const key = getKey(item, i);\n const isSelected = (selectedKey != null && key === selectedKey) || effectiveSelectedKeys.has(key);\n const dragProps = drag.getRowDragProps(i, key);\n\n return (\n <VirtualRow\n key={key}\n id={rowDomId(i)}\n index={i}\n top={offsets[i]}\n padding={rowPaddingValue}\n columnTemplate={columnTemplate}\n isSelected={isSelected}\n isActive={i === nav.activeIndex}\n manageTabStops={keyboardNavigation}\n isEntered={i === nav.enteredIndex}\n activeClassName={classNames?.activeRow}\n activeStyle={slotStyles?.activeRow}\n showDivider={showDivider}\n hover={rowHover}\n onClick={() => {\n if (drag.consumeSuppressedClick()) return;\n // Keep the arrows where the pointer last was.\n nav.setActiveIndex(i);\n onRowClick?.(item, i);\n }}\n observer={rowObserver}\n ariaRowIndex={hasColumns ? i + 2 : undefined}\n ariaPosInSet={hasColumns ? undefined : i + 1}\n ariaSetSize={hasColumns ? undefined : rows.length}\n rowRole={ariaRoles?.row ?? (hasColumns ? 'row' : 'listitem')}\n reserveToggleGutter={shouldShowToggle}\n {...dragProps}\n dropIndicatorClassName={classNames?.dropIndicator}\n dropIndicatorStyle={slotStyles?.dropIndicator}\n rowClassName={classNames?.row}\n rowStyle={slotStyles?.row}\n >\n {hasColumns ? (\n <>\n {multiSelect && (\n <div\n className={cx(styles.virtualListSelectCell, classNames?.selectCell)}\n style={slotStyles?.selectCell}\n role='gridcell'\n onClick={(e) => e.stopPropagation()}\n >\n <Checkbox\n selected={effectiveSelectedKeys.has(key)}\n onToggle={() => selection.handleRowSelect(key)}\n aria-label={text.selectRow}\n // One tab stop per rendered row, changing as the list scrolls,\n // would strand the focus the moment a row unmounts. Space on\n // the active row is the keyboard path instead.\n tabIndex={-1}\n />\n </div>\n )}\n {visibleColumns.map((col) => (\n <div\n key={col.key}\n className={cx(styles.virtualListCell, classNames?.cell)}\n style={slotStyles?.cell}\n role='gridcell'\n data-col-key={col.key}\n data-fit-key={col.width.type === 'fit' ? col.key : undefined}\n >\n {col.renderCell(item, i)}\n </div>\n ))}\n </>\n ) : (\n renderRow?.(item, i)\n )}\n </VirtualRow>\n );\n };\n\n const visibleRows: React.ReactNode[] = [];\n for (let i = startIndex; i <= endIndex; i++) visibleRows.push(renderRowAt(i));\n\n // Keep the row being touch-dragged mounted even if it scrolls out of the\n // window. Touch events are dispatched to the element under the finger at\n // touchstart; if that row unmounts, the browser stops delivering touchmove\n // and the drag freezes. Pinning its DOM node keeps the gesture alive.\n const draggedIndex = drag.draggingKey != null ? keys.indexOf(drag.draggingKey) : -1;\n if (draggedIndex >= 0 && (draggedIndex < startIndex || draggedIndex > endIndex)) {\n visibleRows.push(renderRowAt(draggedIndex));\n }\n\n // Same reason, one step further: the row being used holds the focus, and\n // unmounting it would drop that focus to the document body mid-scroll.\n if (nav.enteredIndex >= 0 && (nav.enteredIndex < startIndex || nav.enteredIndex > endIndex)) {\n visibleRows.push(renderRowAt(nav.enteredIndex));\n }\n\n // Navigation must not replace handlers the consumer passed. Theirs runs first,\n // and a `preventDefault` in it keeps the event: the navigation handlers all\n // check `defaultPrevented`.\n const containerProps = {\n ...nav.containerProps,\n onKeyDown: chain<React.KeyboardEvent<HTMLDivElement>>(rest.onKeyDown, nav.containerProps.onKeyDown),\n onFocus: chain<React.FocusEvent<HTMLDivElement>>(rest.onFocus, nav.containerProps.onFocus),\n onBlur: chain<React.FocusEvent<HTMLDivElement>>(rest.onBlur, nav.containerProps.onBlur),\n };\n\n return (\n <div\n {...rest}\n {...containerProps}\n ref={rootRef}\n className={cx(\n styles.virtualList,\n win.isWindowed && styles.virtualListWindowed,\n drag.draggingKey != null && styles.virtualListReordering,\n columnResize.isResizing && styles.virtualListResizing,\n className,\n classNames?.root,\n )}\n style={{ ...(win.isWindowed ? null : { height }), ...widthVars, ...style, ...slotStyles?.root }}\n role={ariaRoles?.container ?? (hasColumns ? 'grid' : 'list')}\n aria-rowcount={hasColumns ? rows.length + 1 : undefined}\n // Only a composite role may point at an active descendant, and the default\n // `list` is not one. In list mode, pass `ariaRoles` (e.g. listbox/option)\n // to have the active row announced.\n aria-activedescendant={\n nav.activeIndex >= 0 && (ariaRoles?.container ?? (hasColumns ? 'grid' : 'list')) !== 'list'\n ? rowDomId(nav.activeIndex)\n : undefined\n }\n >\n {hasColumns && (\n <VirtualListHeader<T>\n headerRef={headerRef}\n columns={columns!}\n cols={cols}\n resize={columnResize}\n sort={effectiveSort}\n onSort={handleHeaderSort}\n multiSelect={multiSelect}\n allSelected={selection.allSelected}\n someSelected={selection.someSelected}\n onSelectAll={selection.handleSelectAll}\n labels={text}\n classNames={classNames}\n styles={slotStyles}\n />\n )}\n {isLoading ? (\n <div className={styles.virtualListLoadingFull}>\n <SpinnerLoading className={styles.virtualListSpinner} />\n </div>\n ) : (\n <div\n className={cx(styles.virtualListBody, classNames?.body)}\n style={{ height: totalHeight, ...slotStyles?.body }}\n >\n {visibleRows}\n </div>\n )}\n {win.showLoadMoreSpinner && (\n <div\n className={cx(styles.virtualListLoadingIndicator, classNames?.loadingIndicator)}\n style={slotStyles?.loadingIndicator}\n >\n <SpinnerLoading className={styles.virtualListSpinner} />\n </div>\n )}\n </div>\n );\n}\n\nexport default VirtualList;\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use client";var e=`virtualList_qBPNc`,t=`virtualList-drag-clone_artnC`,n=`virtualList-header_0Ellb`,r=`virtualList-header-cell_c8NGV`,i=`virtualList-header-resize_cbYFc`,a=`virtualList-header-sort_Cdpe3`,o=`virtualList-header-sort-label_6MaZ3`,s=`virtualList-header-sort-icon_Z-Nrd`,c=`virtualList-header-sort-icon-active_g7qrb`,l=`virtualList-header-toggle_pYFxD`,u=`virtualList-toggle-panel_Uksmk`,d=`virtualList-toggle-wrapper_JP32M`,f=`virtualList-toggle-popover_gbpd7`,p=`virtualList-toggle-item_5gu5d`,m=`virtualList-toggle-reset_3UANJ`,h=`virtualList-column-chip_nOqr6`,g=`virtualList-select-cell_s9kAy`,_=`virtualList-body_k-fRr`,v=`virtualList-row-active_H5r5-`,y=`virtualList-resizing_28Mn4`,b=`virtualList-row_TTY50`,x=`virtualList-row-no-divider_Ct0gV`,S=`virtualList-row-no-hover_2d3mH`,C=`virtualList-row-selected_hwEN-`,w=`virtualList-row-draggable_I86ha`,T=`virtualList-row-dragging_6T5tQ`,E=`virtualList-row-drop-inside_Uhi9n`,D=`virtualList-drop-line_EM-g4`,O=`virtualList-drop-line-before_n-0iZ`,k=`virtualList-drop-line-after_2dld-`,A=`virtualList-reordering_Rt9E3`,j=`virtualList-cell_H0zBv`,M=`virtualList-loading-full_wzZNM`,N=`virtualList-loading-indicator_EJced`,P=`virtualList-spinner_apClW`,F={virtualList:e,"virtualList-drag-clone":`virtualList-drag-clone_artnC`,virtualListDragClone:t,"virtualList-header":`virtualList-header_0Ellb`,virtualListHeader:n,"virtualList-header-cell":`virtualList-header-cell_c8NGV`,virtualListHeaderCell:r,"virtualList-header-resize":`virtualList-header-resize_cbYFc`,virtualListHeaderResize:i,"virtualList-header-sort":`virtualList-header-sort_Cdpe3`,virtualListHeaderSort:a,"virtualList-header-sort-label":`virtualList-header-sort-label_6MaZ3`,virtualListHeaderSortLabel:o,"virtualList-header-sort-icon":`virtualList-header-sort-icon_Z-Nrd`,virtualListHeaderSortIcon:s,"virtualList-header-sort-icon-active":`virtualList-header-sort-icon-active_g7qrb`,virtualListHeaderSortIconActive:c,"virtualList-header-toggle":`virtualList-header-toggle_pYFxD`,virtualListHeaderToggle:l,"virtualList-toggle-panel":`virtualList-toggle-panel_Uksmk`,virtualListTogglePanel:u,"virtualList-toggle-wrapper":`virtualList-toggle-wrapper_JP32M`,virtualListToggleWrapper:d,"virtualList-toggle-popover":`virtualList-toggle-popover_gbpd7`,virtualListTogglePopover:f,"virtualList-toggle-item":`virtualList-toggle-item_5gu5d`,virtualListToggleItem:p,"virtualList-toggle-reset":`virtualList-toggle-reset_3UANJ`,virtualListToggleReset:m,"virtualList-column-chip":`virtualList-column-chip_nOqr6`,virtualListColumnChip:h,"virtualList-select-cell":`virtualList-select-cell_s9kAy`,virtualListSelectCell:g,"virtualList-body":`virtualList-body_k-fRr`,virtualListBody:_,"virtualList-row-active":`virtualList-row-active_H5r5-`,virtualListRowActive:v,"virtualList-resizing":`virtualList-resizing_28Mn4`,virtualListResizing:y,"virtualList-row":`virtualList-row_TTY50`,virtualListRow:b,"virtualList-row-no-divider":`virtualList-row-no-divider_Ct0gV`,virtualListRowNoDivider:x,"virtualList-row-no-hover":`virtualList-row-no-hover_2d3mH`,virtualListRowNoHover:S,"virtualList-row-selected":`virtualList-row-selected_hwEN-`,virtualListRowSelected:C,"virtualList-row-draggable":`virtualList-row-draggable_I86ha`,virtualListRowDraggable:w,"virtualList-row-dragging":`virtualList-row-dragging_6T5tQ`,virtualListRowDragging:T,"virtualList-row-drop-inside":`virtualList-row-drop-inside_Uhi9n`,virtualListRowDropInside:E,"virtualList-drop-line":`virtualList-drop-line_EM-g4`,virtualListDropLine:D,"virtualList-drop-line-before":`virtualList-drop-line-before_n-0iZ`,virtualListDropLineBefore:O,"virtualList-drop-line-after":`virtualList-drop-line-after_2dld-`,virtualListDropLineAfter:k,"virtualList-reordering":`virtualList-reordering_Rt9E3`,virtualListReordering:A,"virtualList-cell":`virtualList-cell_H0zBv`,virtualListCell:j,"virtualList-loading-full":`virtualList-loading-full_wzZNM`,virtualListLoadingFull:M,"virtualList-loading-indicator":`virtualList-loading-indicator_EJced`,virtualListLoadingIndicator:N,"virtualList-spinner":`virtualList-spinner_apClW`,virtualListSpinner:P};export{
|
|
1
|
+
"use client";var e=`virtualList_qBPNc`,t=`virtualList-drag-clone_artnC`,n=`virtualList-header_0Ellb`,r=`virtualList-header-cell_c8NGV`,i=`virtualList-header-resize_cbYFc`,a=`virtualList-header-sort_Cdpe3`,o=`virtualList-header-sort-label_6MaZ3`,s=`virtualList-header-sort-icon_Z-Nrd`,c=`virtualList-header-sort-icon-active_g7qrb`,l=`virtualList-header-toggle_pYFxD`,u=`virtualList-toggle-panel_Uksmk`,d=`virtualList-toggle-wrapper_JP32M`,f=`virtualList-toggle-popover_gbpd7`,p=`virtualList-toggle-item_5gu5d`,m=`virtualList-toggle-reset_3UANJ`,h=`virtualList-column-chip_nOqr6`,g=`virtualList-select-cell_s9kAy`,_=`virtualList-body_k-fRr`,v=`virtualList-row-active_H5r5-`,y=`virtualList-resizing_28Mn4`,b=`virtualList-row_TTY50`,x=`virtualList-row-no-divider_Ct0gV`,S=`virtualList-row-no-hover_2d3mH`,C=`virtualList-row-selected_hwEN-`,w=`virtualList-row-draggable_I86ha`,T=`virtualList-row-dragging_6T5tQ`,E=`virtualList-row-drop-inside_Uhi9n`,D=`virtualList-drop-line_EM-g4`,O=`virtualList-drop-line-before_n-0iZ`,k=`virtualList-drop-line-after_2dld-`,A=`virtualList-reordering_Rt9E3`,j=`virtualList-cell_H0zBv`,M=`virtualList-loading-full_wzZNM`,N=`virtualList-loading-indicator_EJced`,P=`virtualList-spinner_apClW`,F=`virtualList-windowed_riUyT`,I={virtualList:e,"virtualList-drag-clone":`virtualList-drag-clone_artnC`,virtualListDragClone:t,"virtualList-header":`virtualList-header_0Ellb`,virtualListHeader:n,"virtualList-header-cell":`virtualList-header-cell_c8NGV`,virtualListHeaderCell:r,"virtualList-header-resize":`virtualList-header-resize_cbYFc`,virtualListHeaderResize:i,"virtualList-header-sort":`virtualList-header-sort_Cdpe3`,virtualListHeaderSort:a,"virtualList-header-sort-label":`virtualList-header-sort-label_6MaZ3`,virtualListHeaderSortLabel:o,"virtualList-header-sort-icon":`virtualList-header-sort-icon_Z-Nrd`,virtualListHeaderSortIcon:s,"virtualList-header-sort-icon-active":`virtualList-header-sort-icon-active_g7qrb`,virtualListHeaderSortIconActive:c,"virtualList-header-toggle":`virtualList-header-toggle_pYFxD`,virtualListHeaderToggle:l,"virtualList-toggle-panel":`virtualList-toggle-panel_Uksmk`,virtualListTogglePanel:u,"virtualList-toggle-wrapper":`virtualList-toggle-wrapper_JP32M`,virtualListToggleWrapper:d,"virtualList-toggle-popover":`virtualList-toggle-popover_gbpd7`,virtualListTogglePopover:f,"virtualList-toggle-item":`virtualList-toggle-item_5gu5d`,virtualListToggleItem:p,"virtualList-toggle-reset":`virtualList-toggle-reset_3UANJ`,virtualListToggleReset:m,"virtualList-column-chip":`virtualList-column-chip_nOqr6`,virtualListColumnChip:h,"virtualList-select-cell":`virtualList-select-cell_s9kAy`,virtualListSelectCell:g,"virtualList-body":`virtualList-body_k-fRr`,virtualListBody:_,"virtualList-row-active":`virtualList-row-active_H5r5-`,virtualListRowActive:v,"virtualList-resizing":`virtualList-resizing_28Mn4`,virtualListResizing:y,"virtualList-row":`virtualList-row_TTY50`,virtualListRow:b,"virtualList-row-no-divider":`virtualList-row-no-divider_Ct0gV`,virtualListRowNoDivider:x,"virtualList-row-no-hover":`virtualList-row-no-hover_2d3mH`,virtualListRowNoHover:S,"virtualList-row-selected":`virtualList-row-selected_hwEN-`,virtualListRowSelected:C,"virtualList-row-draggable":`virtualList-row-draggable_I86ha`,virtualListRowDraggable:w,"virtualList-row-dragging":`virtualList-row-dragging_6T5tQ`,virtualListRowDragging:T,"virtualList-row-drop-inside":`virtualList-row-drop-inside_Uhi9n`,virtualListRowDropInside:E,"virtualList-drop-line":`virtualList-drop-line_EM-g4`,virtualListDropLine:D,"virtualList-drop-line-before":`virtualList-drop-line-before_n-0iZ`,virtualListDropLineBefore:O,"virtualList-drop-line-after":`virtualList-drop-line-after_2dld-`,virtualListDropLineAfter:k,"virtualList-reordering":`virtualList-reordering_Rt9E3`,virtualListReordering:A,"virtualList-cell":`virtualList-cell_H0zBv`,virtualListCell:j,"virtualList-loading-full":`virtualList-loading-full_wzZNM`,virtualListLoadingFull:M,"virtualList-loading-indicator":`virtualList-loading-indicator_EJced`,virtualListLoadingIndicator:N,"virtualList-spinner":`virtualList-spinner_apClW`,virtualListSpinner:P,"virtualList-windowed":`virtualList-windowed_riUyT`,virtualListWindowed:F};export{I as default,e as virtualList,_ as virtualListBody,j as virtualListCell,h as virtualListColumnChip,t as virtualListDragClone,D as virtualListDropLine,k as virtualListDropLineAfter,O as virtualListDropLineBefore,n as virtualListHeader,r as virtualListHeaderCell,i as virtualListHeaderResize,a as virtualListHeaderSort,s as virtualListHeaderSortIcon,c as virtualListHeaderSortIconActive,o as virtualListHeaderSortLabel,l as virtualListHeaderToggle,M as virtualListLoadingFull,N as virtualListLoadingIndicator,A as virtualListReordering,y as virtualListResizing,b as virtualListRow,v as virtualListRowActive,w as virtualListRowDraggable,T as virtualListRowDragging,E as virtualListRowDropInside,x as virtualListRowNoDivider,S as virtualListRowNoHover,C as virtualListRowSelected,g as virtualListSelectCell,P as virtualListSpinner,p as virtualListToggleItem,u as virtualListTogglePanel,f as virtualListTogglePopover,m as virtualListToggleReset,d as virtualListToggleWrapper,F as virtualListWindowed};
|
|
2
2
|
//# sourceMappingURL=virtualList.module.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"virtualList.module.mjs","names":[],"sources":["../../../../package/common/virtualList/virtualList.module.pcss"],"sourcesContent":[".virtualList {\n overflow-y: auto;\n box-sizing: border-box;\n color: var(--text-color);\n font-family: var(--default-font);\n\n /* Disable the browser's own scroll anchoring — the component does its own\n scroll-position correction when row heights change, and the two fighting\n each other causes the thumb to desync while dragging. */\n overflow-anchor: none;\n\n /* Reserve the scrollbar gutter so rows don't re-wrap (and re-measure) when the\n vertical scrollbar appears/disappears as content grows. */\n scrollbar-gutter: stable;\n\n /* Establish a stacking context so every z-index inside the list (sticky\n header, toggle popover, drag clone) stays scoped to this component and\n can't paint over outside content like a modal. */\n isolation: isolate;\n\n &-drag-clone {\n box-sizing: border-box;\n contain: layout paint style;\n cursor: grabbing;\n\n /* It tracks the finger via transform — never animate that. */\n transition: none;\n }\n\n &-header {\n display: grid;\n position: sticky;\n top: 0;\n\n /* Frozen layouts are exact pixels and may total more than the viewport, so\n the header and the row canvas below both take that total and the list\n scrolls sideways. `--vl-total` is only set once a column has been\n resized; until then this resolves to 100% and nothing changes. */\n width: max(100%, var(--vl-total, 0px));\n\n /* Above focused rows (z-index 1) so a focused row scrolled under the header\n never paints over it; the drag clone (z-index 3) still floats above both. */\n z-index: 2;\n background: var(--background-accent);\n border-bottom: 1px solid var(--border-color);\n box-sizing: border-box;\n\n &-cell {\n padding: 8px 12px;\n font-weight: 600;\n font-size: 13px;\n color: var(--text-dark);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n box-sizing: border-box;\n\n /* Anchors the resize handle to the cell's trailing edge. */\n position: relative;\n\n /* The cell, not the button inside it, carries the header's tab stop, so\n this is the ring a keyboard user follows. Inset because the cell clips\n its overflow and an outward outline would be cut off. */\n &:focus-visible {\n outline: 1px solid var(--focus-border-color, var(--primary-color));\n outline-offset: -1px;\n }\n }\n\n /* Resize handle: a grab strip on the column's trailing edge, wider than the\n line it sits on because a 1px target is unhittable. It stays inside the\n cell rather than straddling the edge, since the cell clips its overflow\n to keep a long header out of its neighbour. */\n &-resize {\n position: absolute;\n top: 0;\n bottom: 0;\n right: 0;\n width: 10px;\n cursor: col-resize;\n -webkit-tap-highlight-color: transparent;\n touch-action: none;\n\n /* The line itself, drawn on the trailing edge and revealed on hover. */\n &::after {\n content: '';\n position: absolute;\n top: 4px;\n bottom: 4px;\n right: 0;\n width: 2px;\n border-radius: 1px;\n background: var(--primary-color);\n opacity: 0;\n transition: opacity 0.12s;\n }\n\n &:hover::after,\n &:active::after {\n opacity: 1;\n }\n }\n\n /* The clickable part of a sortable header cell. Strips the button chrome and\n inherits the cell's typography, so a sortable header looks identical to a\n plain one until it's hovered. */\n &-sort {\n font: inherit;\n color: inherit;\n background: none;\n border: none;\n margin: 0;\n padding: 0;\n display: flex;\n align-items: center;\n gap: 6px;\n min-width: 0;\n max-width: 100%;\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n\n &:focus-visible {\n outline: 1px solid var(--focus-border-color, var(--primary-color));\n outline-offset: 2px;\n }\n\n /* The cell's own ellipsis can't apply once its content is a flex button,\n so the truncation moves onto the label. */\n &-label {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n &-icon {\n flex: none;\n font-size: 11px;\n opacity: 0.35;\n transition:\n opacity 0.15s,\n color 0.15s;\n\n &-active {\n opacity: 1;\n color: var(--primary-color);\n }\n }\n\n &:hover &-icon {\n opacity: 0.75;\n }\n\n &:hover &-icon-active {\n opacity: 1;\n }\n }\n\n &-toggle {\n background: none;\n border: none;\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n padding: 4px 8px;\n color: var(--text-dark);\n display: flex;\n align-items: center;\n border-radius: var(--default-border-radius);\n transition: background 0.15s;\n\n &:hover {\n background: var(--background-accent-light);\n color: var(--text-color);\n }\n }\n }\n\n /* The entries pad themselves so a hover highlight reaches the panel edge. */\n &-toggle-panel {\n padding: 4px 0;\n }\n\n &-toggle {\n &-wrapper {\n position: absolute;\n right: 0;\n top: 0;\n bottom: 0;\n display: flex;\n align-items: center;\n padding: 0 4px;\n }\n\n /* Chrome (background, shadow, radius, positioning) is provided by the\n Popover panel this renders inside — keep only sizing here. */\n &-popover {\n display: flex;\n flex-direction: column;\n min-width: 160px;\n }\n\n &-item {\n padding: 4px 12px;\n white-space: nowrap;\n transition: background 0.1s;\n font-size: 14px;\n\n /* `:focus` as well, because the panel takes the focus when the keyboard\n opens it, and the checkbox itself is the focusable element. Not\n `--background-accent-light`: it is the same colour as that panel in the\n default theme. */\n &:hover,\n &:focus {\n background: var(--background-accent);\n }\n }\n\n /* Only rendered once a column has actually been resized, so it reads as an\n undo for something the user just did rather than a permanent option. */\n &-reset {\n margin-top: 4px;\n padding: 6px 12px;\n border: none;\n border-top: 1px solid var(--border-color);\n background: none;\n font: inherit;\n font-size: 13px;\n color: var(--text-color);\n text-align: left;\n white-space: nowrap;\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n transition: background 0.1s;\n\n &:hover,\n &:focus {\n background: var(--background-accent);\n }\n }\n }\n\n /* Count badge on the column-toggle cog: how many columns the user has hidden.\n Positioned at the cog's upper-right corner; non-interactive. */\n &-column-chip {\n position: absolute;\n top: 0;\n right: 0;\n min-width: 16px;\n height: 16px;\n padding: 0 4px;\n box-sizing: border-box;\n display: flex;\n align-items: center;\n justify-content: center;\n background: var(--primary-color);\n color: var(--text-on-primary);\n font-size: 10px;\n font-weight: 600;\n line-height: 1;\n border-radius: 8px;\n pointer-events: none;\n }\n\n &-select-cell {\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 0 4px;\n box-sizing: border-box;\n\n /* Only the header's select-all cell is focusable; the ones in rows are not. */\n &:focus-visible {\n outline: 1px solid var(--focus-border-color, var(--primary-color));\n outline-offset: -1px;\n }\n }\n\n &-body {\n position: relative;\n box-sizing: border-box;\n\n /* Rows are absolute with left/right 0, so they inherit this width and stay\n aligned with the header when the table is wider than the viewport. */\n width: max(100%, var(--vl-total, 0px));\n }\n\n /* The list itself holds the tab stop, so the active row cannot use a\n `:focus-visible` of its own. Ringing the list instead would mark the whole\n table rather than the row the arrow keys are on, so the list drops its own\n outline and lends it to that row. Scoped to `:focus-visible`, a mouse user\n never sees it. */\n &:focus-visible {\n outline: none;\n\n & .virtualList-row-active {\n outline: 2px solid var(--focus-border-color, var(--primary-color));\n outline-offset: -2px;\n }\n }\n\n /* A resize drag travels over the rows, and their hover highlight following the\n pointer is noise. Inert rather than a `:hover` override, which would have to\n out-specify the selected-row rule to work. The drag itself is unaffected:\n pointer capture bypasses hit testing. */\n &-resizing &-body {\n pointer-events: none;\n }\n\n &-row {\n border-bottom: 1px solid var(--border-color);\n box-sizing: border-box;\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n transition: background 0.1s;\n\n /* Isolate each row's layout/style so measuring or restyling one row can't force\n the whole list to re-layout — keeps fast scrolling smooth. */\n contain: layout style;\n\n &-no-divider {\n border-bottom: none;\n }\n\n &:hover {\n background: var(--background-accent-light);\n }\n\n /* Opt out of the hover highlight (e.g. when each row is a Card that handles\n its own hover). Declared before the `-selected` rule below, so a selected\n row's highlight still wins on hover. */\n &-no-hover:hover {\n background: transparent;\n }\n\n &-selected {\n background: var(--primary-lighter);\n\n &:hover {\n background: var(--primary-lighter);\n }\n }\n\n &-draggable {\n cursor: grab;\n\n /* Stop the mobile long-press from selecting text or showing the callout. */\n user-select: none;\n -webkit-touch-callout: none;\n\n &:active {\n cursor: grabbing;\n }\n }\n\n &-dragging {\n opacity: 0.4;\n }\n\n /* Tree-reorder `inside` drop: the dragged node becomes a child of this row,\n so highlight the whole row instead of drawing a between-rows line. */\n &-drop-inside {\n background: var(--primary-lighter);\n box-shadow: inset 0 0 0 2px var(--primary-color);\n }\n }\n\n /* Tree-reorder `before`/`after` drop line. Positioned inside the (relatively\n positioned absolute) row; `left` is set inline to the target's indent so it\n starts where the dropped content will. */\n &-drop-line {\n position: absolute;\n right: 0;\n height: 2px;\n background: var(--primary-color);\n pointer-events: none;\n z-index: 1;\n\n /* A small knob at the line's start, the usual tree drop-indicator look. */\n &::before {\n content: '';\n position: absolute;\n left: 0;\n top: 50%;\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: var(--primary-color);\n transform: translate(-50%, -50%);\n }\n\n &-before {\n top: -1px;\n }\n\n &-after {\n bottom: -1px;\n }\n }\n\n /* While a row is being dragged, suppress hover backgrounds so the pointer\n moving across rows doesn't light up the wrong one, and stop text selection.\n `touch-action: none` stops the touch drag from also scrolling the list\n (only active once the long-press has armed the drag). */\n &-reordering {\n touch-action: none;\n\n .virtualList-row {\n user-select: none;\n\n &:hover {\n background: transparent;\n }\n\n &.virtualList-row-selected:hover {\n background: var(--primary-lighter);\n }\n\n &.virtualList-row-dragging {\n background: transparent;\n }\n }\n }\n\n &-cell {\n padding: 8px 12px;\n overflow: hidden;\n text-overflow: ellipsis;\n font-size: 14px;\n box-sizing: border-box;\n display: flex;\n align-items: center;\n min-width: 0;\n\n /* `fit` columns are content-sized — keep their cells on a single line so the\n content can't wrap, and so the width measurement reads the true intrinsic\n width. (Other cells are free to wrap for variable-height rows.) */\n &[data-fit-key] {\n white-space: nowrap;\n }\n }\n\n &-loading {\n &-full {\n display: flex;\n justify-content: center;\n align-items: center;\n height: 100%;\n }\n\n &-indicator {\n display: flex;\n justify-content: center;\n align-items: center;\n padding: 8px 0;\n }\n }\n\n &-spinner {\n width: 32px;\n height: 32px;\n }\n}\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"virtualList.module.mjs","names":[],"sources":["../../../../package/common/virtualList/virtualList.module.pcss"],"sourcesContent":[".virtualList {\n overflow-y: auto;\n box-sizing: border-box;\n color: var(--text-color);\n font-family: var(--default-font);\n\n /* Disable the browser's own scroll anchoring — the component does its own\n scroll-position correction when row heights change, and the two fighting\n each other causes the thumb to desync while dragging. */\n overflow-anchor: none;\n\n /* Reserve the scrollbar gutter so rows don't re-wrap (and re-measure) when the\n vertical scrollbar appears/disappears as content grows. */\n scrollbar-gutter: stable;\n\n /* Establish a stacking context so every z-index inside the list (sticky\n header, toggle popover, drag clone) stays scoped to this component and\n can't paint over outside content like a modal. */\n isolation: isolate;\n\n &-drag-clone {\n box-sizing: border-box;\n contain: layout paint style;\n cursor: grabbing;\n\n /* It tracks the finger via transform — never animate that. */\n transition: none;\n }\n\n &-header {\n display: grid;\n position: sticky;\n\n /* With `scrollParent` the header sticks to the page's scrollport, where a\n fixed app bar may already sit; the list is given the offset from outside. */\n top: var(--vl-header-top, 0);\n\n /* Frozen layouts are exact pixels and may total more than the viewport, so\n the header and the row canvas below both take that total and the list\n scrolls sideways. `--vl-total` is only set once a column has been\n resized; until then this resolves to 100% and nothing changes. */\n width: max(100%, var(--vl-total, 0px));\n\n /* Above focused rows (z-index 1) so a focused row scrolled under the header\n never paints over it; the drag clone (z-index 3) still floats above both. */\n z-index: 2;\n background: var(--background-accent);\n border-bottom: 1px solid var(--border-color);\n box-sizing: border-box;\n\n &-cell {\n padding: 8px 12px;\n font-weight: 600;\n font-size: 13px;\n color: var(--text-dark);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n box-sizing: border-box;\n\n /* Anchors the resize handle to the cell's trailing edge. */\n position: relative;\n\n /* The cell, not the button inside it, carries the header's tab stop, so\n this is the ring a keyboard user follows. Inset because the cell clips\n its overflow and an outward outline would be cut off. */\n &:focus-visible {\n outline: 1px solid var(--focus-border-color, var(--primary-color));\n outline-offset: -1px;\n }\n }\n\n /* Resize handle: a grab strip on the column's trailing edge, wider than the\n line it sits on because a 1px target is unhittable. It stays inside the\n cell rather than straddling the edge, since the cell clips its overflow\n to keep a long header out of its neighbour. */\n &-resize {\n position: absolute;\n top: 0;\n bottom: 0;\n right: 0;\n width: 10px;\n cursor: col-resize;\n -webkit-tap-highlight-color: transparent;\n touch-action: none;\n\n /* The line itself, drawn on the trailing edge and revealed on hover. */\n &::after {\n content: '';\n position: absolute;\n top: 4px;\n bottom: 4px;\n right: 0;\n width: 2px;\n border-radius: 1px;\n background: var(--primary-color);\n opacity: 0;\n transition: opacity 0.12s;\n }\n\n &:hover::after,\n &:active::after {\n opacity: 1;\n }\n }\n\n /* The clickable part of a sortable header cell. Strips the button chrome and\n inherits the cell's typography, so a sortable header looks identical to a\n plain one until it's hovered. */\n &-sort {\n font: inherit;\n color: inherit;\n background: none;\n border: none;\n margin: 0;\n padding: 0;\n display: flex;\n align-items: center;\n gap: 6px;\n min-width: 0;\n max-width: 100%;\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n\n &:focus-visible {\n outline: 1px solid var(--focus-border-color, var(--primary-color));\n outline-offset: 2px;\n }\n\n /* The cell's own ellipsis can't apply once its content is a flex button,\n so the truncation moves onto the label. */\n &-label {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n &-icon {\n flex: none;\n font-size: 11px;\n opacity: 0.35;\n transition:\n opacity 0.15s,\n color 0.15s;\n\n &-active {\n opacity: 1;\n color: var(--primary-color);\n }\n }\n\n &:hover &-icon {\n opacity: 0.75;\n }\n\n &:hover &-icon-active {\n opacity: 1;\n }\n }\n\n &-toggle {\n background: none;\n border: none;\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n padding: 4px 8px;\n color: var(--text-dark);\n display: flex;\n align-items: center;\n border-radius: var(--default-border-radius);\n transition: background 0.15s;\n\n &:hover {\n background: var(--background-accent-light);\n color: var(--text-color);\n }\n }\n }\n\n /* The entries pad themselves so a hover highlight reaches the panel edge. */\n &-toggle-panel {\n padding: 4px 0;\n }\n\n &-toggle {\n &-wrapper {\n position: absolute;\n right: 0;\n top: 0;\n bottom: 0;\n display: flex;\n align-items: center;\n padding: 0 4px;\n }\n\n /* Chrome (background, shadow, radius, positioning) is provided by the\n Popover panel this renders inside — keep only sizing here. */\n &-popover {\n display: flex;\n flex-direction: column;\n min-width: 160px;\n }\n\n &-item {\n padding: 4px 12px;\n white-space: nowrap;\n transition: background 0.1s;\n font-size: 14px;\n\n /* `:focus` as well, because the panel takes the focus when the keyboard\n opens it, and the checkbox itself is the focusable element. Not\n `--background-accent-light`: it is the same colour as that panel in the\n default theme. */\n &:hover,\n &:focus {\n background: var(--background-accent);\n }\n }\n\n /* Only rendered once a column has actually been resized, so it reads as an\n undo for something the user just did rather than a permanent option. */\n &-reset {\n margin-top: 4px;\n padding: 6px 12px;\n border: none;\n border-top: 1px solid var(--border-color);\n background: none;\n font: inherit;\n font-size: 13px;\n color: var(--text-color);\n text-align: left;\n white-space: nowrap;\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n transition: background 0.1s;\n\n &:hover,\n &:focus {\n background: var(--background-accent);\n }\n }\n }\n\n /* Count badge on the column-toggle cog: how many columns the user has hidden.\n Positioned at the cog's upper-right corner; non-interactive. */\n &-column-chip {\n position: absolute;\n top: 0;\n right: 0;\n min-width: 16px;\n height: 16px;\n padding: 0 4px;\n box-sizing: border-box;\n display: flex;\n align-items: center;\n justify-content: center;\n background: var(--primary-color);\n color: var(--text-on-primary);\n font-size: 10px;\n font-weight: 600;\n line-height: 1;\n border-radius: 8px;\n pointer-events: none;\n }\n\n &-select-cell {\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 0 4px;\n box-sizing: border-box;\n\n /* Only the header's select-all cell is focusable; the ones in rows are not. */\n &:focus-visible {\n outline: 1px solid var(--focus-border-color, var(--primary-color));\n outline-offset: -1px;\n }\n }\n\n &-body {\n position: relative;\n box-sizing: border-box;\n\n /* Rows are absolute with left/right 0, so they inherit this width and stay\n aligned with the header when the table is wider than the viewport. */\n width: max(100%, var(--vl-total, 0px));\n }\n\n /* The list itself holds the tab stop, so the active row cannot use a\n `:focus-visible` of its own. Ringing the list instead would mark the whole\n table rather than the row the arrow keys are on, so the list drops its own\n outline and lends it to that row. Scoped to `:focus-visible`, a mouse user\n never sees it. */\n &:focus-visible {\n outline: none;\n\n & .virtualList-row-active {\n outline: 2px solid var(--focus-border-color, var(--primary-color));\n outline-offset: -2px;\n }\n }\n\n /* A resize drag travels over the rows, and their hover highlight following the\n pointer is noise. Inert rather than a `:hover` override, which would have to\n out-specify the selected-row rule to work. The drag itself is unaffected:\n pointer capture bypasses hit testing. */\n &-resizing &-body {\n pointer-events: none;\n }\n\n &-row {\n border-bottom: 1px solid var(--border-color);\n box-sizing: border-box;\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n transition: background 0.1s;\n\n /* Isolate each row's layout/style so measuring or restyling one row can't force\n the whole list to re-layout — keeps fast scrolling smooth. */\n contain: layout style;\n\n &-no-divider {\n border-bottom: none;\n }\n\n &:hover {\n background: var(--background-accent-light);\n }\n\n /* Opt out of the hover highlight (e.g. when each row is a Card that handles\n its own hover). Declared before the `-selected` rule below, so a selected\n row's highlight still wins on hover. */\n &-no-hover:hover {\n background: transparent;\n }\n\n &-selected {\n background: var(--primary-lighter);\n\n &:hover {\n background: var(--primary-lighter);\n }\n }\n\n &-draggable {\n cursor: grab;\n\n /* Stop the mobile long-press from selecting text or showing the callout. */\n user-select: none;\n -webkit-touch-callout: none;\n\n &:active {\n cursor: grabbing;\n }\n }\n\n &-dragging {\n opacity: 0.4;\n }\n\n /* Tree-reorder `inside` drop: the dragged node becomes a child of this row,\n so highlight the whole row instead of drawing a between-rows line. */\n &-drop-inside {\n background: var(--primary-lighter);\n box-shadow: inset 0 0 0 2px var(--primary-color);\n }\n }\n\n /* Tree-reorder `before`/`after` drop line. Positioned inside the (relatively\n positioned absolute) row; `left` is set inline to the target's indent so it\n starts where the dropped content will. */\n &-drop-line {\n position: absolute;\n right: 0;\n height: 2px;\n background: var(--primary-color);\n pointer-events: none;\n z-index: 1;\n\n /* A small knob at the line's start, the usual tree drop-indicator look. */\n &::before {\n content: '';\n position: absolute;\n left: 0;\n top: 50%;\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: var(--primary-color);\n transform: translate(-50%, -50%);\n }\n\n &-before {\n top: -1px;\n }\n\n &-after {\n bottom: -1px;\n }\n }\n\n /* While a row is being dragged, suppress hover backgrounds so the pointer\n moving across rows doesn't light up the wrong one, and stop text selection.\n `touch-action: none` stops the touch drag from also scrolling the list\n (only active once the long-press has armed the drag). */\n &-reordering {\n touch-action: none;\n\n .virtualList-row {\n user-select: none;\n\n &:hover {\n background: transparent;\n }\n\n &.virtualList-row-selected:hover {\n background: var(--primary-lighter);\n }\n\n &.virtualList-row-dragging {\n background: transparent;\n }\n }\n }\n\n &-cell {\n padding: 8px 12px;\n overflow: hidden;\n text-overflow: ellipsis;\n font-size: 14px;\n box-sizing: border-box;\n display: flex;\n align-items: center;\n min-width: 0;\n\n /* `fit` columns are content-sized — keep their cells on a single line so the\n content can't wrap, and so the width measurement reads the true intrinsic\n width. (Other cells are free to wrap for variable-height rows.) */\n &[data-fit-key] {\n white-space: nowrap;\n }\n }\n\n &-loading {\n &-full {\n display: flex;\n justify-content: center;\n align-items: center;\n height: 100%;\n }\n\n &-indicator {\n display: flex;\n justify-content: center;\n align-items: center;\n padding: 8px 0;\n }\n }\n\n &-spinner {\n width: 32px;\n height: 32px;\n }\n\n /* Must stay after the declarations it undoes: both selectors are one class, so\n only source order separates them. `overflow` cannot be split per axis here\n either, because a container with `overflow-x` and `overflow-y: visible`\n computes the visible one to `auto` and hands the list back its own vertical\n scrollbar. So a frozen layout wider than the list overflows the page\n sideways instead of scrolling inside it. */\n &-windowed {\n height: auto;\n overflow: visible;\n scrollbar-gutter: auto;\n\n .virtualList-loading-full {\n height: auto;\n padding: 32px 0;\n }\n }\n}\n"],"mappings":""}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use client";import e from"./weatherIcon.module.mjs";import t from"classnames";import{jsx as n,jsxs as r}from"react/jsx-runtime";var i=`M 26.3 62.9 C 15.4 65.0 15.0 48.5 27.8 49.8 C 23.9 43.9 34.2 34.0 43.0 39.6 C 46.0 26.2 73.9 32.0 68.9 46.3 C 85.0 44.3 84.6 64.2 72.4 62.5 Z`;function a(e,t,n,r){return{animationDuration:`${t}s`,animationDelay:`${e?n:-r*t}s`}}function o({className:r,delayed:a=!1}){return n(`path`,{className:t(e.weatherIconCloud,a&&e.weatherIconCloudDelayed,r),d:i})}var s=12;function c({className:t}){return r(`g`,{className:t,children:[n(`g`,{className:e.weatherIconSunRays,children:Array.from({length:s},(t,r)=>n(`g`,{transform:`rotate(${r/s*360} 50 50)`,children:n(`line`,{className:e.weatherIconSunRay,x1:50,y1:23,x2:50,y2:r%2==0?16:11})},r))}),n(`circle`,{className:e.weatherIconSunCore,cx:50,cy:50,r:18})]})}var l=[{x:74,y:32,size:2.2,duration:2.4,delay:0},{x:28,y:30,size:2.6,duration:3.1,delay:.4},{x:80,y:60,size:1.8,duration:2.7,delay:.9},{x:22,y:66,size:2.4,duration:3.6,delay:.2},{x:58,y:82,size:2,duration:2.9,delay:1.3},{x:34,y:80,size:2.5,duration:3.3,delay:.6}];function u({className:t}){return r(`g`,{className:t,children:[l.map(t=>n(`g`,{transform:`translate(${t.x}, ${t.y})`,children:n(`g`,{className:e.weatherIconStar,style:{animationDuration:`${t.duration}s`,animationDelay:`${t.delay}s`},children:n(`path`,{d:`M 0 ${-t.size} L 0 ${t.size} M ${-t.size} 0 L ${t.size} 0`})})},`${t.x}-${t.y}`)),n(`circle`,{className:e.weatherIconMoonCore,cx:50,cy:50,r:18})]})}var d=function(e){return e.Light=`light`,e.Medium=`medium`,e.Heavy=`heavy`,e}({}),f={light:{lanes:4,duration:1.5},medium:{lanes:6,duration:.95},heavy:{lanes:10,duration:.55}},p=[.85,1.1,.95,1.2,.9,1.05,1.15,.8,1,1.08],m=[.05,.62,.31,.85,.18,.5,.74,.1,.43,.95],h=2,g=.65;function _(e,t=m,n=.8){return .1+n*t[e%t.length]}function v({animated:t,intensity:i=`medium`}){let{lanes:o,duration:s}=f[i];return n(`g`,{className:e.weatherIconRain,children:Array.from({length:o},(i,c)=>{let l=26+50*c/(o-1),u=s*p[c],d=u*m[c];return Array.from({length:h},(i,o)=>{let s=a(t,u,d+o*u/h,_(c*h+o,m,g));return r(`g`,{children:[n(`line`,{className:e.weatherIconDrop,style:s,x1:l,y1:68,x2:l-2.4,y2:92}),n(`ellipse`,{className:e.weatherIconSplash,style:s,cx:l-2.4,cy:92,rx:`1.4`,ry:`0.7`})]},`${c}-${o}`)})})})}var y=[.65,1.5,.85,1.25,1.65,.75,1.1],b=[.15,.7,.35,.9,.05,.55,.8],x=7;function S({animated:t}){return n(`g`,{className:e.weatherIconSnow,children:Array.from({length:x},(r,i)=>{let o=24+54*i/6,s=7*y[i],c=s*b[i];return Array.from({length:h},(r,l)=>n(`g`,{transform:`translate(${o}, 68)`,children:n(`g`,{className:e.weatherIconFlake,style:a(t,s,c+l*s*.7,_(i*h+l,b)),children:n(`path`,{d:`M 0 -2 L 0 2 M -1.7 -1 L 1.7 1 M -1.7 1 L 1.7 -1`})})},`${i}-${l}`))})})}var C=[{x:34,y:66,dx:-1,dy:18,duration:.6,delay:.05},{x:47,y:68,dx:-2,dy:17,duration:.7,delay:.3},{x:58,y:65,dx:1,dy:18,duration:.55,delay:.15},{x:40,y:70,dx:-1,dy:16,duration:.75,delay:.45},{x:65,y:67,dx:-2,dy:17,duration:.65,delay:0},{x:52,y:71,dx:1,dy:16,duration:.7,delay:.35}];function w({animated:t}){let i=C.length*h;return n(`g`,{className:e.weatherIconHail,children:C.flatMap((o,s)=>Array.from({length:h},(c,l)=>{let u=s*h+l,d=a(t,o.duration,o.delay+l*o.duration/h,.1+.8*(u+.5)/i);return r(`g`,{children:[n(`g`,{transform:`translate(${o.x}, ${o.y})`,children:n(`g`,{className:e.weatherIconStone,style:{...d,"--weather-icon-dx":o.dx,"--weather-icon-dy":o.dy},children:n(`circle`,{r:`2`})})}),n(`ellipse`,{className:e.weatherIconImpact,style:d,cx:o.x+o.dx,cy:o.y+o.dy,rx:`1.8`,ry:`0.9`})]},u)}))})}function T(){return r(`g`,{className:e.weatherIconFog,children:[n(`line`,{x1:`32`,y1:`40`,x2:`68`,y2:`40`}),n(`line`,{x1:`20`,y1:`52`,x2:`80`,y2:`52`}),n(`line`,{x1:`26`,y1:`64`,x2:`74`,y2:`64`}),n(`line`,{x1:`34`,y1:`76`,x2:`66`,y2:`76`})]})}function E(){return n(`path`,{className:e.weatherIconBolt,d:`M 54 52 L 44 72 H 52 L 46 90 L 62 66 H 54 L 60 52 Z`})}export{i as CLOUD_PATH,o as Cloud,T as Fog,w as Hail,E as Lightning,u as Moon,v as Rain,d as RainIntensity,S as Snow,c as Sun};
|
|
2
|
+
//# sourceMappingURL=parts.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parts.mjs","names":[],"sources":["../../../../package/common/weatherIcon/parts.tsx"],"sourcesContent":["import type { CSSProperties, ReactElement } from 'react';\nimport cx from 'classnames';\n\nimport type { WeatherPartProps } from './weatherIcon.types';\nimport styles from './weatherIcon.module.pcss';\n\n/** Exported so a scene can clip what sits behind the cloud to this same silhouette. */\nexport const CLOUD_PATH =\n 'M 26.3 62.9 C 15.4 65.0 15.0 48.5 27.8 49.8 C 23.9 43.9 34.2 34.0 43.0 39.6 C 46.0 26.2 73.9 32.0 68.9 46.3 C 85.0 44.3 84.6 64.2 72.4 62.5 Z';\n\n/**\n * A still frame of a falling part must not be its first one, which bunches every drop right under\n * the cloud. A negative delay on a paused animation freezes it that far into its cycle. The\n * fractions come from a jitter table rather than a ramp: lanes run left to right, so a ramp would\n * freeze them along one diagonal line.\n */\nfunction fallStyle(animated: boolean, duration: number, delay: number, freezeFraction: number): CSSProperties {\n return {\n animationDuration: `${duration}s`,\n animationDelay: `${animated ? delay : -freezeFraction * duration}s`,\n };\n}\n\nexport function Cloud({ className, delayed = false }: { className?: string; delayed?: boolean }): ReactElement {\n return (\n <path\n className={cx(styles.weatherIconCloud, delayed && styles.weatherIconCloudDelayed, className)}\n d={CLOUD_PATH}\n />\n );\n}\n\nconst RAY_COUNT = 12;\n\nexport function Sun({ className }: { className?: string }): ReactElement {\n return (\n <g className={className}>\n <g className={styles.weatherIconSunRays}>\n {Array.from({ length: RAY_COUNT }, (_, index) => (\n <g key={index} transform={`rotate(${(index / RAY_COUNT) * 360} 50 50)`}>\n {/* Every second ray reaches further. */}\n <line className={styles.weatherIconSunRay} x1={50} y1={23} x2={50} y2={index % 2 === 0 ? 16 : 11} />\n </g>\n ))}\n </g>\n <circle className={styles.weatherIconSunCore} cx={50} cy={50} r={18} />\n </g>\n );\n}\n\n// Scattered at uneven distances as well as angles: at one distance they read as a ring.\nconst STARS = [\n { x: 74, y: 32, size: 2.2, duration: 2.4, delay: 0 },\n { x: 28, y: 30, size: 2.6, duration: 3.1, delay: 0.4 },\n { x: 80, y: 60, size: 1.8, duration: 2.7, delay: 0.9 },\n { x: 22, y: 66, size: 2.4, duration: 3.6, delay: 0.2 },\n { x: 58, y: 82, size: 2, duration: 2.9, delay: 1.3 },\n { x: 34, y: 80, size: 2.5, duration: 3.3, delay: 0.6 },\n];\n\nexport function Moon({ className }: { className?: string }): ReactElement {\n return (\n <g className={className}>\n {STARS.map((star) => (\n // The position sits on its own group: an animated `transform` would replace a static one.\n <g key={`${star.x}-${star.y}`} transform={`translate(${star.x}, ${star.y})`}>\n <g\n className={styles.weatherIconStar}\n style={{ animationDuration: `${star.duration}s`, animationDelay: `${star.delay}s` }}\n >\n <path d={`M 0 ${-star.size} L 0 ${star.size} M ${-star.size} 0 L ${star.size} 0`} />\n </g>\n </g>\n ))}\n <circle className={styles.weatherIconMoonCore} cx={50} cy={50} r={18} />\n </g>\n );\n}\n\nexport enum RainIntensity {\n Light = 'light',\n Medium = 'medium',\n Heavy = 'heavy',\n}\n\n// Density and speed rise together: speed alone reads as light rain on fast-forward.\nconst RAIN = {\n [RainIntensity.Light]: { lanes: 4, duration: 1.5 },\n [RainIntensity.Medium]: { lanes: 6, duration: 0.95 },\n [RainIntensity.Heavy]: { lanes: 10, duration: 0.55 },\n};\n\nconst DURATION_JITTER = [0.85, 1.1, 0.95, 1.2, 0.9, 1.05, 1.15, 0.8, 1, 1.08];\nconst DELAY_JITTER = [0.05, 0.62, 0.31, 0.85, 0.18, 0.5, 0.74, 0.1, 0.43, 0.95];\nconst PER_LANE = 2;\n// A drop fades out from 80% of its fall, so a still frame keeps every drop short of that.\nconst RAIN_FREEZE_SPAN = 0.65;\n\nfunction freezeAt(index: number, jitter: number[] = DELAY_JITTER, span = 0.8): number {\n return 0.1 + span * jitter[index % jitter.length];\n}\n\nexport function Rain({\n animated,\n intensity = RainIntensity.Medium,\n}: WeatherPartProps & { intensity?: RainIntensity }): ReactElement {\n const { lanes, duration: base } = RAIN[intensity];\n return (\n <g className={styles.weatherIconRain}>\n {Array.from({ length: lanes }, (_, lane) => {\n const x1 = 26 + (50 * lane) / (lanes - 1);\n const duration = base * DURATION_JITTER[lane];\n const delay = duration * DELAY_JITTER[lane];\n return Array.from({ length: PER_LANE }, (_, drop) => {\n const style = fallStyle(\n animated,\n duration,\n delay + (drop * duration) / PER_LANE,\n freezeAt(lane * PER_LANE + drop, DELAY_JITTER, RAIN_FREEZE_SPAN),\n );\n return (\n <g key={`${lane}-${drop}`}>\n <line className={styles.weatherIconDrop} style={style} x1={x1} y1={68} x2={x1 - 2.4} y2={92} />\n <ellipse className={styles.weatherIconSplash} style={style} cx={x1 - 2.4} cy={92} rx='1.4' ry='0.7' />\n </g>\n );\n });\n })}\n </g>\n );\n}\n\n// A wide spread of speeds: with similar ones, flakes drift out of step so slowly that they spend\n// long stretches falling in a line.\nconst FLAKE_DURATION_JITTER = [0.65, 1.5, 0.85, 1.25, 1.65, 0.75, 1.1];\nconst FLAKE_DELAY_JITTER = [0.15, 0.7, 0.35, 0.9, 0.05, 0.55, 0.8];\nconst FLAKE_LANES = 7;\n\nexport function Snow({ animated }: WeatherPartProps): ReactElement {\n return (\n <g className={styles.weatherIconSnow}>\n {Array.from({ length: FLAKE_LANES }, (_, lane) => {\n const x = 24 + (54 * lane) / (FLAKE_LANES - 1);\n const duration = 7 * FLAKE_DURATION_JITTER[lane];\n const delay = duration * FLAKE_DELAY_JITTER[lane];\n return Array.from({ length: PER_LANE }, (_, flake) => (\n <g key={`${lane}-${flake}`} transform={`translate(${x}, 68)`}>\n <g\n className={styles.weatherIconFlake}\n style={fallStyle(\n animated,\n duration,\n delay + flake * duration * 0.7,\n freezeAt(lane * PER_LANE + flake, FLAKE_DELAY_JITTER),\n )}\n >\n <path d='M 0 -2 L 0 2 M -1.7 -1 L 1.7 1 M -1.7 1 L 1.7 -1' />\n </g>\n </g>\n ));\n })}\n </g>\n );\n}\n\n// `dx`/`dy` is where the stone first lands. The keyframe bounces it on from there, so every lane\n// shares one keyframe instead of having its own.\nconst HAIL_LANES = [\n { x: 34, y: 66, dx: -1, dy: 18, duration: 0.6, delay: 0.05 },\n { x: 47, y: 68, dx: -2, dy: 17, duration: 0.7, delay: 0.3 },\n { x: 58, y: 65, dx: 1, dy: 18, duration: 0.55, delay: 0.15 },\n { x: 40, y: 70, dx: -1, dy: 16, duration: 0.75, delay: 0.45 },\n { x: 65, y: 67, dx: -2, dy: 17, duration: 0.65, delay: 0 },\n { x: 52, y: 71, dx: 1, dy: 16, duration: 0.7, delay: 0.35 },\n];\n\nexport function Hail({ animated }: WeatherPartProps): ReactElement {\n const total = HAIL_LANES.length * PER_LANE;\n return (\n <g className={styles.weatherIconHail}>\n {HAIL_LANES.flatMap((lane, laneIndex) =>\n Array.from({ length: PER_LANE }, (_, stone) => {\n const index = laneIndex * PER_LANE + stone;\n const style = fallStyle(\n animated,\n lane.duration,\n lane.delay + (stone * lane.duration) / PER_LANE,\n 0.1 + (0.8 * (index + 0.5)) / total,\n );\n return (\n <g key={index}>\n <g transform={`translate(${lane.x}, ${lane.y})`}>\n <g\n className={styles.weatherIconStone}\n style={{ ...style, '--weather-icon-dx': lane.dx, '--weather-icon-dy': lane.dy } as CSSProperties}\n >\n <circle r='2' />\n </g>\n </g>\n <ellipse\n className={styles.weatherIconImpact}\n style={style}\n cx={lane.x + lane.dx}\n cy={lane.y + lane.dy}\n rx='1.8'\n ry='0.9'\n />\n </g>\n );\n }),\n )}\n </g>\n );\n}\n\nexport function Fog(): ReactElement {\n return (\n <g className={styles.weatherIconFog}>\n <line x1='32' y1='40' x2='68' y2='40' />\n <line x1='20' y1='52' x2='80' y2='52' />\n <line x1='26' y1='64' x2='74' y2='64' />\n <line x1='34' y1='76' x2='66' y2='76' />\n </g>\n );\n}\n\nexport function Lightning(): ReactElement {\n return <path className={styles.weatherIconBolt} d='M 54 52 L 44 72 H 52 L 46 90 L 62 66 H 54 L 60 52 Z' />;\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use client";import{useComponentDefaults as e}from"../configProvider/useComponentDefaults.mjs";import{useLabels as t}from"../configProvider/useLabels.mjs";import n from"../hooks/useMediaQuery.mjs";import{WeatherType as r}from"./weatherIcon.types.mjs";import i from"./weatherIcon.module.mjs";import{CLOUD_PATH as a,Cloud as o,Fog as s,Hail as c,Lightning as l,Moon as u,Rain as d,RainIntensity as f,Snow as p,Sun as m}from"./parts.mjs";import{useId as h}from"react";import g from"classnames";import{Fragment as _,jsx as v,jsxs as y}from"react/jsx-runtime";var b={[r.Sunny]:`Sunny`,[r.ClearNight]:`Clear night`,[r.Cloudy]:`Cloudy`,[r.PartlyCloudy]:`Partly cloudy`,[r.PartlyCloudyNight]:`Partly cloudy night`,[r.RainyLight]:`Light rain`,[r.Rainy]:`Rain`,[r.RainyHeavy]:`Heavy rain`,[r.Snowy]:`Snow`,[r.Hail]:`Hail`,[r.Foggy]:`Fog`,[r.Thunderstorm]:`Thunderstorm`};function x(x){let{type:S,size:C=64,animated:w=!0,labels:T,className:E,style:D,...O}=e(`WeatherIcon`,x),k=t(`WeatherIcon`,T,b),A=w&&!n(`(prefers-reduced-motion: reduce)`),j=`weather-icon-clip-${h().replace(/[^\w-]/g,``)}`,M=e=>v(`g`,{clipPath:`url(#${j})`,children:e});function N(){switch(S){case r.ClearNight:return v(u,{});case r.Cloudy:return y(_,{children:[M(v(`g`,{className:i.weatherIconCloudBehind,children:v(o,{delayed:!0})})),v(o,{})]});case r.PartlyCloudy:return y(_,{children:[M(v(m,{className:i.weatherIconBehind})),v(o,{})]});case r.PartlyCloudyNight:return y(_,{children:[M(v(u,{className:i.weatherIconBehind})),v(o,{})]});case r.RainyLight:case r.Rainy:case r.RainyHeavy:return y(_,{children:[v(o,{}),v(d,{animated:A,intensity:S===r.RainyLight?f.Light:S===r.RainyHeavy?f.Heavy:f.Medium})]});case r.Snowy:return y(_,{children:[v(o,{}),v(p,{animated:A})]});case r.Hail:return y(_,{children:[v(o,{}),v(c,{animated:A})]});case r.Foggy:return v(s,{});case r.Thunderstorm:return y(_,{children:[v(o,{}),M(v(l,{}))]});default:return v(m,{})}}return y(`svg`,{...O,role:`img`,"aria-label":k[S]??k[r.Sunny],className:g(i.weatherIcon,!A&&i.weatherIconStill,E),style:{width:C,height:C,...D},viewBox:`0 0 100 100`,children:[v(`defs`,{children:v(`clipPath`,{id:j,clipRule:`evenodd`,children:v(`path`,{d:`M -20 -20 H 120 V 120 H -20 Z ${a}`})})}),N()]})}export{x as default};
|
|
2
|
+
//# sourceMappingURL=weatherIcon.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"weatherIcon.mjs","names":[],"sources":["../../../../package/common/weatherIcon/weatherIcon.tsx"],"sourcesContent":["import { useId, type ReactElement } from 'react';\nimport cx from 'classnames';\n\nimport { useComponentDefaults, useLabels } from '../configProvider';\nimport { useMediaQuery } from '../hooks/useMediaQuery';\nimport { WeatherType, type WeatherIconLabels, type WeatherIconProps } from './weatherIcon.types';\nimport { CLOUD_PATH, Cloud, Fog, Hail, Lightning, Moon, Rain, RainIntensity, Snow, Sun } from './parts';\nimport styles from './weatherIcon.module.pcss';\n\nconst DEFAULT_LABELS: Required<WeatherIconLabels> = {\n [WeatherType.Sunny]: 'Sunny',\n [WeatherType.ClearNight]: 'Clear night',\n [WeatherType.Cloudy]: 'Cloudy',\n [WeatherType.PartlyCloudy]: 'Partly cloudy',\n [WeatherType.PartlyCloudyNight]: 'Partly cloudy night',\n [WeatherType.RainyLight]: 'Light rain',\n [WeatherType.Rainy]: 'Rain',\n [WeatherType.RainyHeavy]: 'Heavy rain',\n [WeatherType.Snowy]: 'Snow',\n [WeatherType.Hail]: 'Hail',\n [WeatherType.Foggy]: 'Fog',\n [WeatherType.Thunderstorm]: 'Thunderstorm',\n};\n\nfunction WeatherIcon(props: WeatherIconProps): ReactElement {\n const {\n type,\n size = 64,\n animated = true,\n labels,\n className,\n style,\n ...rest\n } = useComponentDefaults('WeatherIcon', props);\n const text = useLabels('WeatherIcon', labels, DEFAULT_LABELS);\n const isAnimated = animated && !useMediaQuery('(prefers-reduced-motion: reduce)');\n // useId's characters are not all safe inside `url(#…)`, and they changed between React 19 minors.\n const clipId = `weather-icon-clip-${useId().replace(/[^\\w-]/g, '')}`;\n\n // The cloud is only an outline, so what sits behind it would show through without this.\n const behindCloud = (content: ReactElement) => <g clipPath={`url(#${clipId})`}>{content}</g>;\n\n function scene(): ReactElement {\n switch (type) {\n case WeatherType.ClearNight:\n return <Moon />;\n case WeatherType.Cloudy:\n return (\n <>\n {/* The offset sits on a wrapper: the cloud's own `transform` is animated and would replace it. */}\n {behindCloud(\n <g className={styles.weatherIconCloudBehind}>\n <Cloud delayed />\n </g>,\n )}\n <Cloud />\n </>\n );\n case WeatherType.PartlyCloudy:\n return (\n <>\n {behindCloud(<Sun className={styles.weatherIconBehind} />)}\n <Cloud />\n </>\n );\n case WeatherType.PartlyCloudyNight:\n return (\n <>\n {behindCloud(<Moon className={styles.weatherIconBehind} />)}\n <Cloud />\n </>\n );\n case WeatherType.RainyLight:\n case WeatherType.Rainy:\n case WeatherType.RainyHeavy:\n return (\n <>\n <Cloud />\n <Rain\n animated={isAnimated}\n intensity={\n type === WeatherType.RainyLight\n ? RainIntensity.Light\n : type === WeatherType.RainyHeavy\n ? RainIntensity.Heavy\n : RainIntensity.Medium\n }\n />\n </>\n );\n case WeatherType.Snowy:\n return (\n <>\n <Cloud />\n <Snow animated={isAnimated} />\n </>\n );\n case WeatherType.Hail:\n return (\n <>\n <Cloud />\n <Hail animated={isAnimated} />\n </>\n );\n case WeatherType.Foggy:\n return <Fog />;\n case WeatherType.Thunderstorm:\n return (\n <>\n <Cloud />\n {behindCloud(<Lightning />)}\n </>\n );\n default:\n return <Sun />;\n }\n }\n\n return (\n <svg\n {...rest}\n role='img'\n aria-label={text[type] ?? text[WeatherType.Sunny]}\n className={cx(styles.weatherIcon, !isAnimated && styles.weatherIconStill, className)}\n style={{ width: size, height: size, ...style }}\n viewBox='0 0 100 100'\n >\n <defs>\n {/* One path, both contours: two separate paths in a clipPath union instead of cutting a hole. */}\n <clipPath id={clipId} clipRule='evenodd'>\n <path d={`M -20 -20 H 120 V 120 H -20 Z ${CLOUD_PATH}`} />\n </clipPath>\n </defs>\n {scene()}\n </svg>\n );\n}\n\nexport default WeatherIcon;\n"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use client";var e=`weatherIcon_FrcJF`,t=`weatherIcon-behind_e2VOz`,n=`weatherIcon-cloudBehind_MZlmZ`,r=`weatherIcon-cloud_KW-i7`,i=`weatherIconCloudDrift_0thIg`,a=`weatherIcon-cloudDelayed_bI1iH`,o=`weatherIcon-sunRays_PBW93`,s=`weatherIconRotate_Hp9YK`,c=`weatherIcon-sunRay_ODCJR`,l=`weatherIconRayPulse_57eG4`,u=`weatherIcon-sunCore_Ucxlh`,d=`weatherIcon-moonCore_USwOg`,f=`weatherIconGlow_W2Qxf`,p=`weatherIcon-star_3CMD1`,m=`weatherIconTwinkle_8DkMV`,h=`weatherIcon-rain_A9qFe`,g=`weatherIcon-drop_SdkW9`,_=`weatherIconFall_NlqDE`,v=`weatherIcon-splash_DF4Wc`,y=`weatherIconRing_kr1SP`,b=`weatherIcon-snow_95z-c`,x=`weatherIcon-flake_LGzkw`,S=`weatherIconSnowFall_I2EGa`,C=`weatherIcon-stone_U0daF`,w=`weatherIconHailFall_U-AE4`,T=`weatherIcon-impact_kCHtC`,E=`weatherIcon-fog_GC8wj`,D=`weatherIconSway_-CD1q`,O=`weatherIcon-bolt_S9sie`,k=`weatherIconFlash_GIecz`,A=`weatherIcon-still_lr-Nl`,j={weatherIcon:e,"weatherIcon-behind":`weatherIcon-behind_e2VOz`,weatherIconBehind:t,"weatherIcon-cloudBehind":`weatherIcon-cloudBehind_MZlmZ`,weatherIconCloudBehind:n,"weatherIcon-cloud":`weatherIcon-cloud_KW-i7`,weatherIconCloud:r,weatherIconCloudDrift:i,"weatherIcon-cloudDelayed":`weatherIcon-cloudDelayed_bI1iH`,weatherIconCloudDelayed:a,"weatherIcon-sunRays":`weatherIcon-sunRays_PBW93`,weatherIconSunRays:o,weatherIconRotate:s,"weatherIcon-sunRay":`weatherIcon-sunRay_ODCJR`,weatherIconSunRay:c,weatherIconRayPulse:l,"weatherIcon-sunCore":`weatherIcon-sunCore_Ucxlh`,weatherIconSunCore:u,"weatherIcon-moonCore":`weatherIcon-moonCore_USwOg`,weatherIconMoonCore:d,weatherIconGlow:f,"weatherIcon-star":`weatherIcon-star_3CMD1`,weatherIconStar:p,weatherIconTwinkle:m,"weatherIcon-rain":`weatherIcon-rain_A9qFe`,weatherIconRain:h,"weatherIcon-drop":`weatherIcon-drop_SdkW9`,weatherIconDrop:g,weatherIconFall:_,"weatherIcon-splash":`weatherIcon-splash_DF4Wc`,weatherIconSplash:v,weatherIconRing:y,"weatherIcon-snow":`weatherIcon-snow_95z-c`,weatherIconSnow:b,"weatherIcon-flake":`weatherIcon-flake_LGzkw`,weatherIconFlake:x,weatherIconSnowFall:S,"weatherIcon-stone":`weatherIcon-stone_U0daF`,weatherIconStone:C,weatherIconHailFall:w,"weatherIcon-impact":`weatherIcon-impact_kCHtC`,weatherIconImpact:T,"weatherIcon-fog":`weatherIcon-fog_GC8wj`,weatherIconFog:E,weatherIconSway:D,"weatherIcon-bolt":`weatherIcon-bolt_S9sie`,weatherIconBolt:O,weatherIconFlash:k,"weatherIcon-still":`weatherIcon-still_lr-Nl`,weatherIconStill:A};export{j as default,e as weatherIcon,t as weatherIconBehind,O as weatherIconBolt,r as weatherIconCloud,n as weatherIconCloudBehind,a as weatherIconCloudDelayed,i as weatherIconCloudDrift,g as weatherIconDrop,_ as weatherIconFall,x as weatherIconFlake,k as weatherIconFlash,E as weatherIconFog,f as weatherIconGlow,w as weatherIconHailFall,T as weatherIconImpact,d as weatherIconMoonCore,h as weatherIconRain,l as weatherIconRayPulse,y as weatherIconRing,s as weatherIconRotate,b as weatherIconSnow,S as weatherIconSnowFall,v as weatherIconSplash,p as weatherIconStar,A as weatherIconStill,C as weatherIconStone,u as weatherIconSunCore,c as weatherIconSunRay,o as weatherIconSunRays,D as weatherIconSway,m as weatherIconTwinkle};
|
|
2
|
+
//# sourceMappingURL=weatherIcon.module.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"weatherIcon.module.mjs","names":[],"sources":["../../../../package/common/weatherIcon/weatherIcon.module.pcss"],"sourcesContent":["/* Line art in `currentColor`, like every other icon: it takes the text colour it sits in. */\n.weatherIcon {\n display: inline-block;\n flex-shrink: 0;\n fill: none;\n stroke: currentcolor;\n stroke-linecap: round;\n stroke-linejoin: round;\n\n &-behind {\n transform-origin: 50px 50px;\n transform: translate(14px, -16px) scale(0.62);\n }\n\n &-cloudBehind {\n transform-origin: 50px 50px;\n transform: translate(16px, -14px) scale(0.8);\n opacity: 0.55;\n }\n\n /* Wandering and breathing share one keyframe: a running animation replaces the whole\n `transform`, so two of them on one element would clobber each other. */\n &-cloud {\n stroke-width: 3;\n transform-box: fill-box;\n transform-origin: center;\n animation: weatherIconCloudDrift 20s ease-in-out infinite;\n }\n\n /* Negative, so the second cloud starts partway through its cycle instead of easing in. */\n &-cloudDelayed {\n animation-delay: -9s;\n }\n\n &-sunRays {\n transform-box: fill-box;\n transform-origin: center;\n animation: weatherIconRotate 150s linear infinite;\n }\n\n /* Anchored at the ray's inner end (y1 in parts.tsx), so it grows outwards whichever way it points. */\n &-sunRay {\n stroke-width: 3;\n transform-origin: 50px 23px;\n animation: weatherIconRayPulse 5s ease-in-out infinite;\n }\n\n &-sunRays > g:nth-child(3n + 2) &-sunRay {\n animation-delay: 0.8s;\n }\n\n &-sunRays > g:nth-child(3n) &-sunRay {\n animation-delay: 1.6s;\n }\n\n &-sunCore,\n &-moonCore {\n stroke-width: 3;\n transform-box: fill-box;\n transform-origin: center;\n animation: weatherIconGlow 4s ease-in-out infinite;\n }\n\n &-star {\n stroke-width: 1.4;\n transform-box: fill-box;\n transform-origin: center;\n animation-name: weatherIconTwinkle;\n animation-timing-function: ease-in-out;\n animation-iteration-count: infinite;\n }\n\n &-rain {\n stroke-width: 2;\n }\n\n /* A dash of 6 slides along a path of 24.12 with a gap of exactly that length: it grows out of\n the cloud, falls, and shrinks into the ground. With any other gap the loop does not close.\n `backwards` keeps it hidden through its delay instead of showing the unanimated frame. */\n &-drop {\n stroke-dasharray: 6 24.12;\n animation-name: weatherIconFall;\n animation-timing-function: linear;\n animation-iteration-count: infinite;\n animation-fill-mode: backwards;\n }\n\n /* The drop's leading edge reaches the ground 24.12 into a cycle of 30.12, at 80%. The drop\n fades out as it hits and the splash is at its fullest by 82%: on a slow lane, a splash that\n peaked later came a visible moment after its drop. Left to sink into the ground, the drop's\n tail also arrived after the splash and ended as a round-capped dot. */\n &-splash {\n stroke-width: 1;\n opacity: 0;\n transform-box: fill-box;\n transform-origin: center;\n animation-name: weatherIconRing;\n animation-timing-function: ease-out;\n animation-iteration-count: infinite;\n }\n\n &-snow {\n stroke-width: 1.3;\n }\n\n /* Linear: easing would stop the flake in mid-air at every sway waypoint. */\n &-flake {\n animation-name: weatherIconSnowFall;\n animation-timing-function: linear;\n animation-iteration-count: infinite;\n animation-fill-mode: backwards;\n }\n\n &-stone {\n fill: currentcolor;\n stroke: none;\n animation-name: weatherIconHailFall;\n animation-timing-function: linear;\n animation-iteration-count: infinite;\n animation-fill-mode: backwards;\n }\n\n &-impact {\n stroke-width: 1;\n opacity: 0;\n transform-box: fill-box;\n transform-origin: center;\n animation-name: weatherIconRing;\n animation-timing-function: ease-out;\n animation-iteration-count: infinite;\n }\n\n /* Straight bars of different lengths: a wavy fog reads as an odd shape, not as fog. */\n &-fog {\n stroke-width: 4;\n\n & line:nth-child(1) {\n opacity: 0.55;\n animation: weatherIconSway 6.5s ease-in-out infinite;\n }\n\n & line:nth-child(2) {\n opacity: 0.85;\n animation: weatherIconSway 8s ease-in-out -1.2s infinite reverse;\n }\n\n & line:nth-child(3) {\n opacity: 0.7;\n animation: weatherIconSway 7s ease-in-out -0.6s infinite;\n }\n\n & line:nth-child(4) {\n opacity: 0.5;\n animation: weatherIconSway 9s ease-in-out -2s infinite reverse;\n }\n }\n\n /* Two quick flashes and a long dark stretch, well under the three flashes a second that\n WCAG 2.3.1 sets as the limit. */\n &-bolt {\n stroke-width: 2.5;\n animation: weatherIconFlash 4s linear infinite;\n }\n}\n\n@keyframes weatherIconCloudDrift {\n 0%,\n 100% {\n transform: translate(0, 0) scale(1);\n }\n\n 22% {\n transform: translate(1.2px, -1.6px) scale(1.015);\n }\n\n 48% {\n transform: translate(-0.9px, 1.4px) scale(1.03);\n }\n\n 74% {\n transform: translate(0.8px, -1.2px) scale(1.015);\n }\n\n 90% {\n transform: translate(-1.1px, 1px) scale(1.005);\n }\n}\n\n@keyframes weatherIconRotate {\n to {\n transform: rotate(360deg);\n }\n}\n\n@keyframes weatherIconRayPulse {\n 50% {\n transform: scaleY(1.3);\n }\n}\n\n@keyframes weatherIconGlow {\n 0%,\n 100% {\n opacity: 0.92;\n transform: scale(1);\n }\n\n 50% {\n opacity: 1;\n transform: scale(1.04);\n }\n}\n\n@keyframes weatherIconTwinkle {\n 0%,\n 100% {\n opacity: 0.3;\n transform: scale(0.7);\n }\n\n 50% {\n opacity: 1;\n transform: scale(1.1);\n }\n}\n\n@keyframes weatherIconFall {\n 0% {\n stroke-dashoffset: 6;\n opacity: 1;\n }\n\n 80% {\n opacity: 1;\n }\n\n 84% {\n opacity: 0;\n }\n\n 100% {\n stroke-dashoffset: -24.12;\n opacity: 0;\n }\n}\n\n@keyframes weatherIconRing {\n 0%,\n 80% {\n opacity: 0;\n transform: scale(0.6);\n }\n\n 82% {\n opacity: 0.85;\n transform: scale(1);\n }\n\n 100% {\n opacity: 0;\n transform: scale(1.4);\n }\n}\n\n@keyframes weatherIconSnowFall {\n 0% {\n transform: translate(0, 0);\n opacity: 0;\n }\n\n 6% {\n opacity: 1;\n }\n\n 25% {\n transform: translate(3px, 8px);\n }\n\n 50% {\n transform: translate(-3px, 16px);\n }\n\n 75% {\n transform: translate(2px, 24px);\n }\n\n 92% {\n transform: translate(-1px, 29px);\n opacity: 1;\n }\n\n 100% {\n transform: translate(0, 30px);\n opacity: 0;\n }\n}\n\n/* Lands at 80%, then bounces on along the way it was already drifting (sideways as well as up),\n which reads as a bounce rather than a pop. Each stone sets its own landing spot. */\n@keyframes weatherIconHailFall {\n 0% {\n transform: translate(0, 0);\n opacity: 0;\n }\n\n 10% {\n opacity: 1;\n }\n\n 80% {\n transform: translate(calc(var(--weather-icon-dx) * 1px), calc(var(--weather-icon-dy) * 1px));\n opacity: 1;\n animation-timing-function: ease-out;\n }\n\n 88% {\n transform: translate(calc(var(--weather-icon-dx) * 2px), calc((var(--weather-icon-dy) - 2.5) * 1px));\n opacity: 1;\n animation-timing-function: ease-in;\n }\n\n 100% {\n transform: translate(calc(var(--weather-icon-dx) * 2.6px), calc(var(--weather-icon-dy) * 1px));\n opacity: 0;\n }\n}\n\n@keyframes weatherIconSway {\n 50% {\n transform: translateX(3px);\n }\n}\n\n@keyframes weatherIconFlash {\n 0%,\n 8%,\n 14%,\n 100% {\n opacity: 0.35;\n }\n\n 4%,\n 11% {\n opacity: 1;\n }\n}\n\n/* Falling parts pause rather than stop: parts.tsx gives each a negative delay that freezes it\n partway down. The rest stop, and an impact frozen mid-pulse would be a stray mark, so it hides. */\n.weatherIcon-still {\n & .weatherIcon-drop,\n & .weatherIcon-flake,\n & .weatherIcon-stone {\n animation-play-state: paused;\n }\n\n & .weatherIcon-cloud,\n & .weatherIcon-sunRays,\n & .weatherIcon-sunRay,\n & .weatherIcon-sunCore,\n & .weatherIcon-moonCore,\n & .weatherIcon-star,\n & .weatherIcon-fog line,\n & .weatherIcon-bolt {\n animation: none;\n }\n\n & .weatherIcon-splash,\n & .weatherIcon-impact {\n animation: none;\n opacity: 0;\n }\n\n & .weatherIcon-bolt {\n opacity: 1;\n }\n}\n\n/* The component switches to its still frame by itself; this covers where it cannot read the\n setting, such as a server render. */\n@media (prefers-reduced-motion: reduce) {\n .weatherIcon * {\n animation-play-state: paused;\n }\n}\n"],"mappings":""}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use client";var e=function(e){return e.Sunny=`sunny`,e.ClearNight=`clearNight`,e.Cloudy=`cloudy`,e.PartlyCloudy=`partlyCloudy`,e.PartlyCloudyNight=`partlyCloudyNight`,e.RainyLight=`rainyLight`,e.Rainy=`rainy`,e.RainyHeavy=`rainyHeavy`,e.Snowy=`snowy`,e.Hail=`hail`,e.Foggy=`foggy`,e.Thunderstorm=`thunderstorm`,e}({});export{e as WeatherType};
|
|
2
|
+
//# sourceMappingURL=weatherIcon.types.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"weatherIcon.types.mjs","names":[],"sources":["../../../../package/common/weatherIcon/weatherIcon.types.ts"],"sourcesContent":["import type { CSSProperties, SVGAttributes } from 'react';\nimport type { DataProps } from '../types/slots.types';\n\nexport enum WeatherType {\n Sunny = 'sunny',\n ClearNight = 'clearNight',\n Cloudy = 'cloudy',\n PartlyCloudy = 'partlyCloudy',\n PartlyCloudyNight = 'partlyCloudyNight',\n RainyLight = 'rainyLight',\n Rainy = 'rainy',\n RainyHeavy = 'rainyHeavy',\n Snowy = 'snowy',\n Hail = 'hail',\n Foggy = 'foggy',\n Thunderstorm = 'thunderstorm',\n}\n\n/** The accessible name of each weather, read out by screen readers. */\nexport type WeatherIconLabels = Partial<Record<`${WeatherType}`, string>>;\n\n/**\n * The root is an `<svg>`, so the native attributes come from `SVGAttributes` rather than\n * `HtmlProps`. Its `type` is the attribute of `<script>` and `<style>`, not of an `<svg>`.\n */\nexport interface WeatherIconProps extends DataProps, Omit<SVGAttributes<SVGSVGElement>, 'type'> {\n /**\n * The weather to draw. Takes the enum or its string value, so an API's own enum with the same\n * values passes as it is.\n */\n type: WeatherType | `${WeatherType}`;\n /** Width and height, any CSS size. Default `64`. */\n size?: number | string;\n /**\n * Default `true`. `false` draws a still frame, for small or repeated icons where the motion is\n * too small to see but costs the same. Always still under `prefers-reduced-motion`.\n */\n animated?: boolean;\n /** Text this component renders of its own. */\n labels?: WeatherIconLabels;\n className?: string;\n style?: CSSProperties;\n}\n\n/** What a falling part needs to know to pick its still frame. */\nexport interface WeatherPartProps {\n animated: boolean;\n}\n"],"mappings":"aAGA,IAAY,EAAL,SAAA,EAAA,OACL,GAAA,MAAA,QACA,EAAA,WAAA,aACA,EAAA,OAAA,SACA,EAAA,aAAA,eACA,EAAA,kBAAA,oBACA,EAAA,WAAA,aACA,EAAA,MAAA,QACA,EAAA,WAAA,aACA,EAAA,MAAA,QACA,EAAA,KAAA,OACA,EAAA,MAAA,QACA,EAAA,aAAA,gBACF,EAAA,CAAA,CAAA"}
|