@7span/react-list 0.0.5 → 1.0.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/dist/react-list.js +400 -310
- package/dist/react-list.umd.cjs +1 -1
- package/package.json +11 -3
- package/src/components/attributes.jsx +53 -0
- package/src/components/empty.jsx +22 -0
- package/src/components/error.jsx +27 -0
- package/src/components/go-to.jsx +58 -0
- package/src/components/initial-loader.jsx +25 -0
- package/src/components/items.jsx +50 -0
- package/src/components/list.jsx +361 -0
- package/src/components/load-more.jsx +39 -0
- package/src/components/loader.jsx +29 -0
- package/src/components/pagination.jsx +158 -0
- package/src/components/per-page.jsx +65 -0
- package/src/components/refresh.jsx +34 -0
- package/src/components/search.jsx +59 -0
- package/src/components/summary.jsx +51 -0
- package/src/components/utils.js +42 -0
- package/src/context/list-provider.jsx +55 -0
- package/src/index.js +15 -0
package/dist/react-list.umd.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
(function(v,s){typeof exports=="object"&&typeof module<"u"?s(exports,require("react/jsx-runtime"),require("react")):typeof define=="function"&&define.amd?define(["exports","react/jsx-runtime","react"],s):(v=typeof globalThis<"u"?globalThis:v||self,s(v.ReactList={},v.jsxRuntime,v.react))})(this,function(v,s,n){"use strict";const B=n.createContext(null),j=({children:a,config:l})=>{const{requestHandler:o,stateManager:t={}}=l,[r,i]=n.useState({data:[],response:null,error:null,count:0,selection:[],pagination:{page:1,perPage:25},loader:{isLoading:!1,initialLoading:!0},sort:{sortBy:null,sortOrder:"desc"},search:"",filters:{},attrs:[],isEmpty:!0,isInitializing:!0});if(!o)throw new Error("ListProvider: requestHandler is required.");const f=n.useMemo(()=>({requestHandler:o,stateManager:t,listState:r,setListState:i}),[o,t,r]);return s.jsx(B.Provider,{value:f,children:a})},w=()=>{const a=n.useContext(B);if(!a)throw new Error("useListContext must be used within a ListProvider");return a},I=n.memo(({children:a,renderAttribute:l})=>{const{listState:o}=w(),{attrs:t,attrSettings:r,updateAttr:i}=o,f=n.useCallback(d=>m=>{i(d,"visible",m.target.checked)},[i]),p=n.useMemo(()=>({attrs:t,attrSettings:r,updateAttr:i}),[t,r,i]);return a?a(p):s.jsx("div",{className:"react-list-attributes",children:t.map((d,m)=>{var u;return l?l({key:`attr-${m}`,attr:d,updateAttr:i,attrSettings:r}):s.jsxs("label",{children:[s.jsx("span",{children:d.label}),s.jsx("input",{type:"checkbox",checked:((u=r==null?void 0:r[d.name])==null?void 0:u.visible)??!1,onChange:f(d.name)})]},`attr-${m}`)})})}),q=n.memo(({children:a})=>{const{listState:l}=w(),{data:o,loader:t,error:r}=l,{isLoading:i,initialLoading:f}=t;return(o==null?void 0:o.length)>0||f||i||r?null:s.jsx("div",{className:"react-list-empty",children:a||s.jsx("div",{children:s.jsx("p",{children:"No data found!"})})})}),A=n.memo(({children:a})=>{const{listState:l}=w(),{error:o,loader:t}=l,{isLoading:r}=t;return!o||r?null:s.jsx("div",{className:"react-list-error",children:typeof a=="function"?a({error:o}):a||s.jsxs("div",{children:[s.jsx("h3",{children:"Error occurred"}),s.jsxs("pre",{children:[o.name,": ",o.message]})]})})}),_=n.memo(({children:a})=>{const{listState:l}=w(),{data:o,count:t,pagination:r,setPage:i,loader:f,error:p}=l,{page:d,perPage:m}=r,{initialLoading:u,isLoading:L}=f,{pages:M,pagesCount:y}=n.useMemo(()=>{const C=Math.ceil(t/m);return{pages:Array.from({length:C},(k,P)=>P+1),pagesCount:C}},[t,m]),N=n.useCallback(C=>{i(Number(C.target.value))},[i]),b=n.useMemo(()=>({setPage:i,page:d,pages:M,pagesCount:y}),[i,d,M,y]);return u||!o||o.length===0||p?null:s.jsx("div",{className:"react-list-go-to",children:a?a(b):s.jsx("select",{value:d,onChange:N,children:M.map(C=>s.jsxs("option",{value:C,children:["Page ",C]},`page-${C}`))})})}),$=n.memo(({children:a})=>{const{listState:l}=w(),{loader:o}=l,{initialLoading:t}=o;return n.useMemo(()=>({loading:t}),[t]),t?s.jsx("div",{className:"react-list-initial-loader",children:a||s.jsx("p",{children:"Initial Loading..."})}):null}),D=n.memo(({children:a,renderItem:l})=>{const{listState:o}=w(),{data:t=[],loader:r,error:i,setSort:f,sort:p}=o,{initialLoading:d}=r,m=n.useMemo(()=>({items:t,setSort:f,sort:p}),[t,p,f]);return d||!t||t.length===0||i?null:l?s.jsx("div",{className:"react-list-items",children:t.map((u,L)=>s.jsx("div",{children:l({item:u,index:L})},u.id||L))}):typeof a=="function"?s.jsx("div",{className:"react-list-items",children:a(m)}):s.jsx("div",{className:"react-list-items",children:t.map((u,L)=>s.jsx("pre",{children:JSON.stringify(u,null,2)},u.id||L))})}),H=({initialItems:a=[],children:l,endpoint:o,page:t=1,perPage:r=25,sortBy:i="",sortOrder:f="desc",count:p=0,search:d="",filters:m={},attrs:u,version:L=1,paginationMode:M="pagination",meta:y={}})=>{const{requestHandler:N,setListState:b}=w(),C=M==="loadMore",[e,k]=n.useState({page:t,perPage:r,sortBy:i,sortOrder:f,search:d,filters:m,attrSettings:{},items:a,selection:[],error:null,response:null,count:p,isLoading:!1,initializingState:!a.length}),P=n.useCallback(async(g={},c=null)=>{e.initializingState||k(h=>({...h,error:null,isLoading:!0}));try{const h=c||e,S=await N({endpoint:o,version:L,meta:y,page:h.page,perPage:h.perPage,search:h.search,sortBy:h.sortBy,sortOrder:h.sortOrder,filters:h.filters,...g});k(z=>({...z,response:S,selection:[],items:C&&h.page>1?[...z.items,...S.items]:S.items,count:S.count,initializingState:!1,isLoading:!1}))}catch(h){throw k(S=>({...S,error:h,items:[],count:0,initializingState:!1,isLoading:!1})),h}},[o,L,C,y,N,e]),O=n.useMemo(()=>({setPage:(g,c)=>{let h=g;g===0&&(h="");const S={...e,page:h};k(S),h&&P(c,S)},setPerPage:g=>{const c={...e,perPage:g,page:1};k(c),P({},c)},setSearch:g=>{if(g!==e.search){const c={...e,search:g,page:1};k(c),P({},c)}},setSort:({by:g,order:c})=>{const h={...e,sortBy:g,sortOrder:c,page:1};k(h),P({},h)},loadMore:()=>{const g={...e,page:e.page+1};k(g),P({},g)},refresh:(g={isRefresh:!0})=>{if(C){const c={...e,page:1,items:[]};k(c),P(g,c)}else P(g)},setFilters:g=>{const c={...e,filters:g,page:1};k(c),P({},c)},updateItemById:(g,c)=>{const h=e.items.map(S=>S.id===c?{...S,...g}:S);k(S=>({...S,items:h}))},setSelection:g=>k(c=>({...c,selection:g}))}),[P,C,e]),E=n.useMemo(()=>({data:e.items,response:e.response,error:e.error,count:e.count,selection:e.selection,pagination:{page:e.page,perPage:e.perPage,hasMore:e.items.length<e.count},loader:{isLoading:e.isLoading,initialLoading:e.initializingState},sort:{sortBy:e.sortBy,sortOrder:e.sortOrder},search:e.search,filters:e.filters,attrs:u||Object.keys(e.items[0]||{}),isEmpty:e.items.length===0,...O}),[e.items,e.response,e.error,e.count,e.selection,e.page,e.perPage,e.isLoading,e.initializingState,e.sortBy,e.sortOrder,e.search,e.filters,O,u]);return n.useEffect(()=>{e.initializingState&&(a.length||O.setPage(e.page))},[]),n.useEffect(()=>{b(E)},[b,e.items,e.count,e.error,e.isLoading,e.selection,e.page,e.perPage,e.sortBy,e.sortOrder]),typeof l=="function"?l(E):l},x=n.memo(({children:a})=>{const{listState:l}=w(),{data:o,count:t,pagination:r,setPage:i,loader:f,error:p}=l,{page:d,perPage:m}=r,{isLoading:u}=f,L=n.useMemo(()=>d*m<t,[d,m,t]),M=n.useCallback(()=>{L&&!u&&i(d+1)},[L,u,i,d]),y=n.useMemo(()=>({isLoading:u,loadMore:M,hasMoreItems:L}),[u,M,L]);return!o||o.length===0||p?null:a(y)}),G=n.memo(({children:a,position:l="overlay"})=>{const{listState:o}=w(),{loader:t}=o,{isLoading:r,initializingState:i}=t;return n.useMemo(()=>({isLoading:r}),[r]),!i&&!r?null:s.jsx("div",{children:a||s.jsx("div",{children:s.jsx("p",{children:"Loading..."})})})}),W=n.memo(({children:a,pageLinks:l=5,renderFirst:o,renderPrev:t,renderPages:r,renderPage:i,renderNext:f,renderLast:p})=>{const{listState:d}=w(),{data:m,count:u,pagination:L,setPage:M,loader:y,error:N}=d,{page:b,perPage:C}=L,{initialLoading:e,isLoading:k}=y,P=n.useMemo(()=>{const c=Math.ceil(u/C),h=Math.floor(l/2),S=b*C<u;return{pagesCount:c,halfWay:h,hasNext:S,hasPrev:b!==1}},[u,C,b,l]),O=n.useMemo(()=>{const{pagesCount:c,halfWay:h}=P,S=Array.from({length:Math.min(l,c)});return b<=h?S.map((z,T)=>T+1):c-b<h?S.map((z,T)=>c-T).reverse():S.map((z,T)=>b-h+T)},[b,l,P]),E=n.useMemo(()=>({prev:()=>M(b-1),next:()=>M(b+1),first:()=>M(1),last:()=>M(P.pagesCount),setPage:c=>M(c)}),[M,b,P.pagesCount]),g=n.useMemo(()=>({page:b,perPage:C,count:u,...P,pagesToDisplay:O,...E}),[b,C,u,P,O,E]);return e||!m||m.length===0||N?null:a?a(g):s.jsxs("div",{className:"react-list-pagination",children:[o?o(g):s.jsx("button",{type:"button",disabled:!P.hasPrev,onClick:E.first,children:"First"}),t?t(g):s.jsx("button",{type:"button",disabled:!P.hasPrev,onClick:E.prev,children:"Prev"}),r?r(g):s.jsx("div",{children:O.map(c=>{const h=c===b,S={...g,page:c,isActive:h};return i?i(S):s.jsx("div",{children:h?s.jsx("span",{children:c}):s.jsx("button",{type:"button",onClick:()=>E.setPage(c),children:c})},`page-${c}`)})}),f?f(g):s.jsx("button",{type:"button",disabled:!P.hasNext,onClick:E.next,children:"Next"}),p?p(g):s.jsx("button",{type:"button",disabled:!P.hasNext,onClick:E.last,children:"Last"})]})}),F=n.memo(({children:a,options:l=[10,25,50,100]})=>{const{listState:o}=w(),{data:t,pagination:r,setPerPage:i,loader:f,error:p}=o,{perPage:d}=r,{initialLoading:m}=f,u=n.useMemo(()=>l.map(y=>typeof y!="object"?{value:y,label:y}:y),[l]),L=n.useCallback(y=>{i(Number(y.target.value))},[i]),M=n.useMemo(()=>({perPage:d,setPerPage:i,options:u}),[d,i,u]);return m||!t||t.length===0||p?null:s.jsx("div",{className:"react-list-per-page",children:a?a(M):s.jsx("select",{value:d,onChange:L,children:u.map(y=>s.jsxs("option",{value:y.value,children:[y.label," items per page"]},`option-${y.value}`))})})}),J=n.memo(({children:a})=>{const{listState:l}=w(),{loader:o,refresh:t}=l,{isLoading:r,initialLoading:i}=o,f=n.useCallback(()=>{t({isRefresh:!0})},[t]),p=n.useMemo(()=>({isLoading:r,refresh:f}),[r,f]);return i?null:a?a(p):s.jsx("div",{className:"react-list-refresh",children:s.jsx("button",{onClick:f,disabled:r,children:r?"Loading...":"Refresh"})})}),K=n.memo(({children:a,debounceTime:l=500})=>{const{listState:o}=w(),{search:t,setSearch:r}=o,[i,f]=n.useState(t??""),p=n.useRef(null);n.useEffect(()=>{t!==i&&f(t??"")},[t]);const d=u=>{f(u),p.current&&clearTimeout(p.current),p.current=setTimeout(()=>{r(u)},l)};n.useEffect(()=>()=>{p.current&&clearTimeout(p.current)},[]);const m={search:i,setSearch:d};return s.jsx("div",{className:"react-list-search",children:a?a(m):s.jsx("input",{type:"text",value:i,onChange:u=>d(u.target.value),placeholder:"Search..."})})}),Q=n.memo(({children:a})=>{const{listState:l}=w(),{data:o,count:t,pagination:r,loader:i,error:f}=l,{page:p,perPage:d}=r,{initialLoading:m,isLoading:u}=i,L=n.useMemo(()=>{const y=p*d-d+1,N=Math.min(p*d,t),b=(o==null?void 0:o.length)||0;return{from:y,to:N,visibleCount:b}},[p,d,t,o]),M=n.useMemo(()=>({...L,count:t}),[L,t]);return m||!o||o.length===0||f?null:s.jsx("div",{className:"react-list-summary",children:a?a(M):s.jsxs("span",{children:["Showing ",s.jsx("span",{children:L.visibleCount})," items (",s.jsxs("span",{children:[L.from," - ",L.to]}),") out of ",s.jsx("span",{children:t})]})})});v.ReactListAttributes=I,v.ReactListEmpty=q,v.ReactListError=A,v.ReactListGoTo=_,v.ReactListInitialLoader=$,v.ReactListItems=D,v.ReactListLoadMore=x,v.ReactListLoader=G,v.ReactListPagination=W,v.ReactListPerPage=F,v.ReactListProvider=j,v.ReactListRefresh=J,v.ReactListSearch=K,v.ReactListSummary=Q,v.default=H,Object.defineProperties(v,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});
|
|
1
|
+
(function(C,a){typeof exports=="object"&&typeof module<"u"?a(exports,require("react/jsx-runtime"),require("react")):typeof define=="function"&&define.amd?define(["exports","react/jsx-runtime","react"],a):(C=typeof globalThis<"u"?globalThis:C||self,a(C.ReactList={},C.jsxRuntime,C.react))})(this,function(C,a,r){"use strict";const H=r.createContext(null),K=({children:t,config:o})=>{const{requestHandler:l,stateManager:n={}}=o,[i,u]=r.useState({data:[],response:null,error:null,count:0,selection:[],pagination:{page:1,perPage:25},loader:{isLoading:!1,initialLoading:!0},sort:{sortBy:null,sortOrder:"desc"},search:"",filters:{},attrs:[],isEmpty:!0,isInitializing:!0});if(!l)throw new Error("ListProvider: requestHandler is required.");const f=r.useMemo(()=>({requestHandler:l,stateManager:n,listState:i,setListState:u}),[l,n,i]);return a.jsx(H.Provider,{value:f,children:t})},S=()=>{const t=r.useContext(H);if(!t)throw new Error("useListContext must be used within a ListProvider");return t},Q=r.memo(({children:t,renderAttribute:o})=>{const{listState:l}=S(),{attrs:n,attrSettings:i,updateAttr:u}=l,f=r.useCallback(d=>m=>{u(d,"visible",m.target.checked)},[u]),L=r.useMemo(()=>({attrs:n,attrSettings:i,updateAttr:u}),[n,i,u]);return t?t(L):a.jsx("div",{className:"react-list-attributes",children:n.map((d,m)=>{var h;return o?o({key:`attr-${m}`,attr:d,updateAttr:u,attrSettings:i}):a.jsxs("label",{children:[a.jsx("span",{children:d.label}),a.jsx("input",{type:"checkbox",checked:((h=i==null?void 0:i[d.name])==null?void 0:h.visible)??!1,onChange:f(d.name)})]},`attr-${m}`)})})}),U=r.memo(({children:t})=>{const{listState:o}=S(),{data:l,loader:n,error:i}=o,{isLoading:u,initialLoading:f}=n;return(l==null?void 0:l.length)>0||f||u||i?null:a.jsx("div",{className:"react-list-empty",children:t||a.jsx("div",{children:a.jsx("p",{children:"No data found!"})})})}),V=r.memo(({children:t})=>{const{listState:o}=S(),{error:l,loader:n}=o,{isLoading:i}=n;return!l||i?null:a.jsx("div",{className:"react-list-error",children:typeof t=="function"?t({error:l}):t||a.jsxs("div",{children:[a.jsx("h3",{children:"Error occurred"}),a.jsxs("pre",{children:[l.name,": ",l.message]})]})})}),X=r.memo(({children:t})=>{const{listState:o}=S(),{data:l,count:n,pagination:i,setPage:u,loader:f,error:L}=o,{page:d,perPage:m}=i,{initialLoading:h,isLoading:y}=f,{pages:v,pagesCount:P}=r.useMemo(()=>{const M=Math.ceil(n/m);return{pages:Array.from({length:M},(_,p)=>p+1),pagesCount:M}},[n,m]),T=r.useCallback(M=>{u(Number(M.target.value))},[u]),b=r.useMemo(()=>({setPage:u,page:d,pages:v,pagesCount:P}),[u,d,v,P]);return h||!l||l.length===0||L?null:a.jsx("div",{className:"react-list-go-to",children:t?t(b):a.jsx("select",{value:d,onChange:T,children:v.map(M=>a.jsxs("option",{value:M,children:["Page ",M]},`page-${M}`))})})}),Y=r.memo(({children:t})=>{const{listState:o}=S(),{loader:l}=o,{initialLoading:n}=l;return r.useMemo(()=>({loading:n}),[n]),n?a.jsx("div",{className:"react-list-initial-loader",children:t||a.jsx("p",{children:"Initial Loading..."})}):null}),Z=r.memo(({children:t,renderItem:o})=>{const{listState:l}=S(),{data:n=[],loader:i,error:u,setSort:f,sort:L}=l,{initialLoading:d,isLoading:m}=i,h=r.useMemo(()=>({items:n,isLoading:m,setSort:f,sort:L}),[n,L,f,m]);return d||!n||n.length===0||u?null:o?a.jsx("div",{className:"react-list-items",children:n.map((y,v)=>a.jsx("div",{children:o({item:y,index:v})},y.id||v))}):typeof t=="function"?a.jsx("div",{className:"react-list-items",children:t(h)}):a.jsx("div",{className:"react-list-items",children:n.map((y,v)=>a.jsx("pre",{children:JSON.stringify(y,null,2)},y.id||v))})}),$=(t,o)=>{if(t===o)return!0;if(t==null||o==null||typeof t!="object"||typeof o!="object")return t===o;if(Array.isArray(t)&&Array.isArray(o)){if(t.length!==o.length)return!1;for(let i=0;i<t.length;i++)if(!$(t[i],o[i]))return!1;return!0}if(Array.isArray(t)||Array.isArray(o))return!1;const l=Object.keys(t).filter(i=>t[i]!==void 0),n=Object.keys(o).filter(i=>o[i]!==void 0);if(l.length!==n.length)return!1;for(let i of l)if(!n.includes(i)||!$(t[i],o[i]))return!1;return!0},F=(t,o)=>!o||Object.keys(o).length===0?t&&Object.keys(t).length>0:!t||Object.keys(t).length===0?!1:!$(t,o),R=({initialItems:t=[],children:o,endpoint:l,page:n=1,perPage:i=25,sortBy:u="",sortOrder:f="desc",count:L=0,search:d="",filters:m={},attrs:h,version:y=1,paginationMode:v="pagination",meta:P={},onResponse:T,afterPageChange:b,afterLoadMore:M})=>{const{requestHandler:x,setListState:_,stateManager:p}=S(),q=r.useRef(!1),A=v==="loadMore",w=r.useCallback(e=>({endpoint:l,version:y,meta:P,search:(e==null?void 0:e.search)||d,page:(e==null?void 0:e.page)||n,perPage:(e==null?void 0:e.perPage)||i,sortBy:(e==null?void 0:e.sortBy)||u,sortOrder:(e==null?void 0:e.sortOrder)||f,filters:(e==null?void 0:e.filters)||m,attrSettings:(e==null?void 0:e.attrSettings)||{},isRefresh:!1}),[l,y,P,d,n,i,u,f,m]),k=r.useCallback(()=>{var e;try{const g=w(),c=(e=p==null?void 0:p.get)==null?void 0:e.call(p,g);return{page:c==null?void 0:c.page,perPage:c==null?void 0:c.perPage,sortBy:c==null?void 0:c.sortBy,sortOrder:c==null?void 0:c.sortOrder,search:c==null?void 0:c.search,attrSettings:c==null?void 0:c.attrSettings,filters:c==null?void 0:c.filters}}catch(g){return console.error(g),{}}},[w,p]),B=r.useCallback(()=>{const e=k();let g=n;return A?g=1:e.page!=null&&(g=e.page),{page:g,perPage:e.perPage!=null?e.perPage:i,sortBy:e.sortBy!=null?e.sortBy:u,sortOrder:e.sortOrder!=null?e.sortOrder:f,search:e.search!=null?e.search:d,filters:e.filters!=null?e.filters:m,attrSettings:e.attrSettings||{},items:t,selection:[],error:null,response:null,count:0,isLoading:!1,initializingState:!t.length,confirmedPage:null}},[k,d,n,i,u,f,d,m,A]),[s,O]=r.useState(B),I=r.useCallback(e=>{var g;if(p){const c=w(e);(g=p==null?void 0:p.set)==null||g.call(p,c)}},[p,w]),N=r.useCallback(async(e={},g=null)=>{s.initializingState||O(c=>({...c,error:null,isLoading:!0}));try{const c=g||s,E=(g==null?void 0:g.items)??s.items,z=await x({endpoint:l,version:y,meta:P,page:c.page,perPage:c.perPage,search:c.search,sortBy:c.sortBy,sortOrder:c.sortOrder,filters:c.filters,...e});T&&T(z);let W;A?(W=c.page===1?z.items:[...E,...z.items],M&&M(z)):(W=z.items,b&&b(z));const J={...c,response:z,selection:[],items:A&&c.page>1?[...E,...z.items]:z.items,count:z.count,initializingState:!1,isLoading:!1};I(J),O(J)}catch(c){throw O(E=>({...E,error:c,items:[],count:0,initializingState:!1,isLoading:!1})),c}},[l,y,A,P,x,s]),D=r.useMemo(()=>({setPage:(e,g)=>{let c=e;e===0&&(c="");const E={...s,page:c};O(E),c&&N(g,E)},setPerPage:e=>{const g={...s,perPage:e,page:1};O(g),N({},g)},setSearch:e=>{if(e!==s.search){const g={...s,search:e,page:1};O(g),N({},g)}},setSort:({by:e,order:g})=>{const c={...s,sortBy:e,sortOrder:g,page:1};O(c),N({},c)},loadMore:()=>{const e={...s,page:s.page+1};O(e),N({},e)},clearFilters:()=>{const e={...s,filters:m,page:1};O(e),N({},e)},refresh:(e={isRefresh:!0})=>{if(A){const g={...s,page:1,items:[]};O(g),N(e,g)}else N(e)},setFilters:e=>{const g={...s,filters:e,page:1};O(g),N({},g)},updateItemById:(e,g)=>{const c=s.items.map(E=>E.id===g?{...E,...e}:E);O(E=>({...E,items:c}))},setSelection:e=>O(g=>({...g,selection:e}))}),[N,A,s]),G=r.useMemo(()=>({data:s.items,response:s.response,error:s.error,count:s.count,selection:s.selection,pagination:{page:s.page,perPage:s.perPage,hasMore:s.items.length<s.count},loader:{isLoading:s.isLoading,initialLoading:s.initializingState},sort:{sortBy:s.sortBy,sortOrder:s.sortOrder},hasActiveFilters:F(s.filters,m),search:s.search,filters:s.filters,attrs:h||Object.keys(s.items[0]||{}),isEmpty:s.items.length===0,...D}),[s.items,s.response,s.error,s.count,s.selection,s.page,s.perPage,s.isLoading,s.initializingState,s.sortBy,s.sortOrder,s.search,s.filters,D,h]);return r.useEffect(()=>{if(s.initializingState&&!q.current){if(q.current=!0,p!=null&&p.init){const e=w(s);p.init(e)}t.length||D.setPage(s.page)}},[]),r.useEffect(()=>{_(G)},[_,s.items,s.count,s.error,s.isLoading,s.selection,s.page,s.perPage,s.sortBy,s.sortOrder]),typeof o=="function"?o(G):o},j=r.memo(({children:t})=>{const{listState:o}=S(),{data:l,count:n,pagination:i,setPage:u,loader:f,error:L}=o,{page:d,perPage:m}=i,{isLoading:h}=f,y=r.useMemo(()=>d*m<n,[d,m,n]),v=r.useCallback(()=>{y&&!h&&u(d+1)},[y,h,u,d]),P=r.useMemo(()=>({isLoading:h,loadMore:v,hasMoreItems:y}),[h,v,y]);return!l||l.length===0||L?null:t(P)}),ee=r.memo(({children:t,position:o="overlay"})=>{const{listState:l}=S(),{loader:n}=l,{isLoading:i,initializingState:u}=n;return r.useMemo(()=>({isLoading:i}),[i]),!u&&!i?null:a.jsx("div",{children:t||a.jsx("div",{children:a.jsx("p",{children:"Loading..."})})})}),se=r.memo(({children:t,pageLinks:o=5,renderFirst:l,renderPrev:n,renderPages:i,renderPage:u,renderNext:f,renderLast:L})=>{const{listState:d}=S(),{data:m,count:h,pagination:y,setPage:v,loader:P,error:T}=d,{page:b,perPage:M}=y,{initialLoading:x,isLoading:_}=P,p=r.useMemo(()=>{const k=Math.ceil(h/M),B=Math.floor(o/2),s=b*M<h;return{pagesCount:k,halfWay:B,hasNext:s,hasPrev:b!==1}},[h,M,b,o]),q=r.useMemo(()=>{const{pagesCount:k,halfWay:B}=p,s=Array.from({length:Math.min(o,k)});return b<=B?s.map((O,I)=>I+1):k-b<B?s.map((O,I)=>k-I).reverse():s.map((O,I)=>b-B+I)},[b,o,p]),A=r.useMemo(()=>({prev:()=>v(b-1),next:()=>v(b+1),first:()=>v(1),last:()=>v(p.pagesCount),setPage:k=>v(k)}),[v,b,p.pagesCount]),w=r.useMemo(()=>({page:b,perPage:M,count:h,...p,pagesToDisplay:q,...A}),[b,M,h,p,q,A]);return x||!m||m.length===0||T?null:t?t(w):a.jsxs("div",{className:"react-list-pagination",children:[l?l(w):a.jsx("button",{type:"button",disabled:!p.hasPrev,onClick:A.first,children:"First"}),n?n(w):a.jsx("button",{type:"button",disabled:!p.hasPrev,onClick:A.prev,children:"Prev"}),i?i(w):a.jsx("div",{children:q.map(k=>{const B=k===b,s={...w,page:k,isActive:B};return u?u(s):a.jsx("div",{children:B?a.jsx("span",{children:k}):a.jsx("button",{type:"button",onClick:()=>A.setPage(k),children:k})},`page-${k}`)})}),f?f(w):a.jsx("button",{type:"button",disabled:!p.hasNext,onClick:A.next,children:"Next"}),L?L(w):a.jsx("button",{type:"button",disabled:!p.hasNext,onClick:A.last,children:"Last"})]})}),te=r.memo(({children:t,options:o=[10,25,50,100]})=>{const{listState:l}=S(),{data:n,pagination:i,setPerPage:u,loader:f,error:L}=l,{perPage:d}=i,{initialLoading:m}=f,h=r.useMemo(()=>o.map(P=>typeof P!="object"?{value:P,label:P}:P),[o]),y=r.useCallback(P=>{u(Number(P.target.value))},[u]),v=r.useMemo(()=>({perPage:d,setPerPage:u,options:h}),[d,u,h]);return m||!n||n.length===0||L?null:a.jsx("div",{className:"react-list-per-page",children:t?t(v):a.jsx("select",{value:d,onChange:y,children:h.map(P=>a.jsxs("option",{value:P.value,children:[P.label," items per page"]},`option-${P.value}`))})})}),ne=r.memo(({children:t})=>{const{listState:o}=S(),{loader:l,refresh:n}=o,{isLoading:i,initialLoading:u}=l,f=r.useCallback(()=>{n({isRefresh:!0})},[n]),L=r.useMemo(()=>({isLoading:i,refresh:f}),[i,f]);return u?null:t?t(L):a.jsx("div",{className:"react-list-refresh",children:a.jsx("button",{onClick:f,disabled:i,children:i?"Loading...":"Refresh"})})}),ie=r.memo(({children:t,debounceTime:o=500})=>{const{listState:l}=S(),{search:n,setSearch:i}=l,[u,f]=r.useState(n??""),L=r.useRef(null);r.useEffect(()=>{n!==u&&f(n??"")},[n]);const d=h=>{f(h),L.current&&clearTimeout(L.current),L.current=setTimeout(()=>{i(h)},o)};r.useEffect(()=>()=>{L.current&&clearTimeout(L.current)},[]);const m={search:u,setSearch:d};return a.jsx("div",{className:"react-list-search",children:t?t(m):a.jsx("input",{type:"text",value:u,onChange:h=>d(h.target.value),placeholder:"Search..."})})}),re=r.memo(({children:t})=>{const{listState:o}=S(),{data:l,count:n,pagination:i,loader:u,error:f}=o,{page:L,perPage:d}=i,{initialLoading:m,isLoading:h}=u,y=r.useMemo(()=>{const P=L*d-d+1,T=Math.min(L*d,n),b=(l==null?void 0:l.length)||0;return{from:P,to:T,visibleCount:b}},[L,d,n,l]),v=r.useMemo(()=>({...y,count:n}),[y,n]);return m||!l||l.length===0||f?null:a.jsx("div",{className:"react-list-summary",children:t?t(v):a.jsxs("span",{children:["Showing ",a.jsx("span",{children:y.visibleCount})," items (",a.jsxs("span",{children:[y.from," - ",y.to]}),") out of ",a.jsx("span",{children:n})]})})});C.ReactListAttributes=Q,C.ReactListEmpty=U,C.ReactListError=V,C.ReactListGoTo=X,C.ReactListInitialLoader=Y,C.ReactListItems=Z,C.ReactListLoadMore=j,C.ReactListLoader=ee,C.ReactListPagination=se,C.ReactListPerPage=te,C.ReactListProvider=K,C.ReactListRefresh=ne,C.ReactListSearch=ie,C.ReactListSummary=re,C.default=R,Object.defineProperties(C,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@7span/react-list",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "A simple and reusable list component for React",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -10,10 +10,18 @@
|
|
|
10
10
|
"preview": "vite preview",
|
|
11
11
|
"prepublishOnly": "npm run build"
|
|
12
12
|
},
|
|
13
|
-
"main": "
|
|
13
|
+
"main": "src/index.js",
|
|
14
|
+
"exports": {
|
|
15
|
+
".": "./src/index.js"
|
|
16
|
+
},
|
|
14
17
|
"files": [
|
|
15
|
-
"dist"
|
|
18
|
+
"dist",
|
|
19
|
+
"src"
|
|
16
20
|
],
|
|
21
|
+
"peerDependencies": {
|
|
22
|
+
"react": "^18.2.0 || ^19.0.0",
|
|
23
|
+
"react-dom": "^18.2.0 || ^19.0.0"
|
|
24
|
+
},
|
|
17
25
|
"publishConfig": {
|
|
18
26
|
"access": "public"
|
|
19
27
|
},
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { memo, useCallback, useMemo } from "react";
|
|
2
|
+
import { useListContext } from "../context/list-provider";
|
|
3
|
+
|
|
4
|
+
export const ReactListAttributes = memo(({ children, renderAttribute }) => {
|
|
5
|
+
const { listState } = useListContext();
|
|
6
|
+
const { attrs, attrSettings, updateAttr } = listState;
|
|
7
|
+
|
|
8
|
+
const handleAttrChange = useCallback(
|
|
9
|
+
(attrName) => (e) => {
|
|
10
|
+
updateAttr(attrName, "visible", e.target.checked);
|
|
11
|
+
},
|
|
12
|
+
[updateAttr]
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
const scope = useMemo(
|
|
16
|
+
() => ({
|
|
17
|
+
attrs,
|
|
18
|
+
attrSettings,
|
|
19
|
+
updateAttr,
|
|
20
|
+
}),
|
|
21
|
+
[attrs, attrSettings, updateAttr]
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
if (children) {
|
|
25
|
+
return children(scope);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<div className="react-list-attributes">
|
|
30
|
+
{attrs.map((attr, index) => {
|
|
31
|
+
if (renderAttribute) {
|
|
32
|
+
return renderAttribute({
|
|
33
|
+
key: `attr-${index}`,
|
|
34
|
+
attr,
|
|
35
|
+
updateAttr,
|
|
36
|
+
attrSettings,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<label key={`attr-${index}`}>
|
|
42
|
+
<span>{attr.label}</span>
|
|
43
|
+
<input
|
|
44
|
+
type="checkbox"
|
|
45
|
+
checked={attrSettings?.[attr.name]?.visible ?? false}
|
|
46
|
+
onChange={handleAttrChange(attr.name)}
|
|
47
|
+
/>
|
|
48
|
+
</label>
|
|
49
|
+
);
|
|
50
|
+
})}
|
|
51
|
+
</div>
|
|
52
|
+
);
|
|
53
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { memo } from "react";
|
|
2
|
+
import { useListContext } from "../context/list-provider";
|
|
3
|
+
|
|
4
|
+
export const ReactListEmpty = memo(({ children }) => {
|
|
5
|
+
const { listState } = useListContext();
|
|
6
|
+
const { data: items, loader, error } = listState;
|
|
7
|
+
const { isLoading, initialLoading } = loader;
|
|
8
|
+
|
|
9
|
+
if (items?.length > 0 || initialLoading || isLoading || error) {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
<div className="react-list-empty">
|
|
15
|
+
{children || (
|
|
16
|
+
<div>
|
|
17
|
+
<p>No data found!</p>
|
|
18
|
+
</div>
|
|
19
|
+
)}
|
|
20
|
+
</div>
|
|
21
|
+
);
|
|
22
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { memo } from "react";
|
|
2
|
+
import { useListContext } from "../context/list-provider";
|
|
3
|
+
|
|
4
|
+
export const ReactListError = memo(({ children }) => {
|
|
5
|
+
const { listState } = useListContext();
|
|
6
|
+
const { error, loader } = listState;
|
|
7
|
+
const { isLoading } = loader;
|
|
8
|
+
|
|
9
|
+
if (!error || isLoading) {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
<div className="react-list-error">
|
|
15
|
+
{typeof children === "function"
|
|
16
|
+
? children({ error })
|
|
17
|
+
: children || (
|
|
18
|
+
<div>
|
|
19
|
+
<h3>Error occurred</h3>
|
|
20
|
+
<pre>
|
|
21
|
+
{error.name}: {error.message}
|
|
22
|
+
</pre>
|
|
23
|
+
</div>
|
|
24
|
+
)}
|
|
25
|
+
</div>
|
|
26
|
+
);
|
|
27
|
+
});
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { memo, useCallback, useMemo } from "react";
|
|
2
|
+
import { useListContext } from "../context/list-provider";
|
|
3
|
+
|
|
4
|
+
export const ReactListGoTo = memo(({ children }) => {
|
|
5
|
+
const { listState } = useListContext();
|
|
6
|
+
const { data, count, pagination, setPage, loader, error } = listState;
|
|
7
|
+
const { page, perPage } = pagination;
|
|
8
|
+
const { initialLoading, isLoading } = loader;
|
|
9
|
+
|
|
10
|
+
const { pages, pagesCount } = useMemo(() => {
|
|
11
|
+
const pagesCount = Math.ceil(count / perPage);
|
|
12
|
+
const pages = Array.from({ length: pagesCount }, (_, i) => i + 1);
|
|
13
|
+
return { pages, pagesCount };
|
|
14
|
+
}, [count, perPage]);
|
|
15
|
+
|
|
16
|
+
const handlePageChange = useCallback(
|
|
17
|
+
(e) => {
|
|
18
|
+
setPage(Number(e.target.value));
|
|
19
|
+
},
|
|
20
|
+
[setPage]
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
const scope = useMemo(
|
|
24
|
+
() => ({
|
|
25
|
+
setPage,
|
|
26
|
+
page,
|
|
27
|
+
pages,
|
|
28
|
+
pagesCount,
|
|
29
|
+
}),
|
|
30
|
+
[setPage, page, pages, pagesCount]
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
if (initialLoading) return null;
|
|
34
|
+
|
|
35
|
+
if (!data || data.length === 0) {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (error) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<div className="react-list-go-to">
|
|
45
|
+
{children ? (
|
|
46
|
+
children(scope)
|
|
47
|
+
) : (
|
|
48
|
+
<select value={page} onChange={handlePageChange}>
|
|
49
|
+
{pages.map((pageNum) => (
|
|
50
|
+
<option key={`page-${pageNum}`} value={pageNum}>
|
|
51
|
+
Page {pageNum}
|
|
52
|
+
</option>
|
|
53
|
+
))}
|
|
54
|
+
</select>
|
|
55
|
+
)}
|
|
56
|
+
</div>
|
|
57
|
+
);
|
|
58
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { memo, useMemo } from "react";
|
|
2
|
+
import { useListContext } from "../context/list-provider";
|
|
3
|
+
|
|
4
|
+
export const ReactListInitialLoader = memo(({ children }) => {
|
|
5
|
+
const { listState } = useListContext();
|
|
6
|
+
const { loader } = listState;
|
|
7
|
+
const { initialLoading } = loader;
|
|
8
|
+
|
|
9
|
+
const scope = useMemo(
|
|
10
|
+
() => ({
|
|
11
|
+
loading: initialLoading,
|
|
12
|
+
}),
|
|
13
|
+
[initialLoading]
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
if (!initialLoading) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<div className="react-list-initial-loader">
|
|
22
|
+
{children || <p>Initial Loading...</p>}
|
|
23
|
+
</div>
|
|
24
|
+
);
|
|
25
|
+
});
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { memo, useMemo } from "react";
|
|
2
|
+
import { useListContext } from "../context/list-provider";
|
|
3
|
+
|
|
4
|
+
export const ReactListItems = memo(({ children, renderItem }) => {
|
|
5
|
+
const { listState } = useListContext();
|
|
6
|
+
const { data: items = [], loader, error, setSort, sort } = listState;
|
|
7
|
+
const { initialLoading, isLoading } = loader;
|
|
8
|
+
|
|
9
|
+
const scope = useMemo(
|
|
10
|
+
() => ({
|
|
11
|
+
items,
|
|
12
|
+
isLoading,
|
|
13
|
+
setSort,
|
|
14
|
+
sort,
|
|
15
|
+
}),
|
|
16
|
+
[items, sort, setSort, isLoading]
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
if (initialLoading) return null;
|
|
20
|
+
|
|
21
|
+
if (!items || items.length === 0) {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (error) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (renderItem) {
|
|
30
|
+
return (
|
|
31
|
+
<div className="react-list-items">
|
|
32
|
+
{items.map((item, index) => (
|
|
33
|
+
<div key={item.id || index}>{renderItem({ item, index })}</div>
|
|
34
|
+
))}
|
|
35
|
+
</div>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (typeof children === "function") {
|
|
40
|
+
return <div className="react-list-items">{children(scope)}</div>;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<div className="react-list-items">
|
|
45
|
+
{items.map((item, index) => (
|
|
46
|
+
<pre key={item.id || index}>{JSON.stringify(item, null, 2)}</pre>
|
|
47
|
+
))}
|
|
48
|
+
</div>
|
|
49
|
+
);
|
|
50
|
+
});
|
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
2
|
+
import { useListContext } from "../context/list-provider";
|
|
3
|
+
import { hasActiveFilters } from "./utils";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* ReactList component for handling data fetching, pagination, and state management
|
|
7
|
+
*/
|
|
8
|
+
const ReactList = ({
|
|
9
|
+
initialItems = [],
|
|
10
|
+
children,
|
|
11
|
+
endpoint,
|
|
12
|
+
page = 1,
|
|
13
|
+
perPage = 25,
|
|
14
|
+
sortBy = "",
|
|
15
|
+
sortOrder = "desc",
|
|
16
|
+
count = 0,
|
|
17
|
+
search = "",
|
|
18
|
+
filters = {},
|
|
19
|
+
attrs,
|
|
20
|
+
version = 1,
|
|
21
|
+
paginationMode = "pagination",
|
|
22
|
+
meta = {},
|
|
23
|
+
onResponse,
|
|
24
|
+
afterPageChange,
|
|
25
|
+
afterLoadMore,
|
|
26
|
+
}) => {
|
|
27
|
+
const { requestHandler, setListState, stateManager } = useListContext();
|
|
28
|
+
|
|
29
|
+
const initRef = useRef(false);
|
|
30
|
+
|
|
31
|
+
const isLoadMore = paginationMode === "loadMore";
|
|
32
|
+
|
|
33
|
+
const getContext = useCallback(
|
|
34
|
+
(currentState) => {
|
|
35
|
+
return {
|
|
36
|
+
endpoint,
|
|
37
|
+
version,
|
|
38
|
+
meta,
|
|
39
|
+
search: currentState?.search || search,
|
|
40
|
+
page: currentState?.page || page,
|
|
41
|
+
perPage: currentState?.perPage || perPage,
|
|
42
|
+
sortBy: currentState?.sortBy || sortBy,
|
|
43
|
+
sortOrder: currentState?.sortOrder || sortOrder,
|
|
44
|
+
filters: currentState?.filters || filters,
|
|
45
|
+
attrSettings: currentState?.attrSettings || {},
|
|
46
|
+
isRefresh: false,
|
|
47
|
+
};
|
|
48
|
+
},
|
|
49
|
+
[endpoint, version, meta, search, page, perPage, sortBy, sortOrder, filters]
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
const getSavedState = useCallback(() => {
|
|
53
|
+
try {
|
|
54
|
+
const context = getContext();
|
|
55
|
+
const oldState = stateManager?.get?.(context);
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
page: oldState?.page,
|
|
59
|
+
perPage: oldState?.perPage,
|
|
60
|
+
sortBy: oldState?.sortBy,
|
|
61
|
+
sortOrder: oldState?.sortOrder,
|
|
62
|
+
search: oldState?.search,
|
|
63
|
+
attrSettings: oldState?.attrSettings,
|
|
64
|
+
filters: oldState?.filters,
|
|
65
|
+
};
|
|
66
|
+
} catch (err) {
|
|
67
|
+
console.error(err);
|
|
68
|
+
return {};
|
|
69
|
+
}
|
|
70
|
+
}, [getContext, stateManager]);
|
|
71
|
+
|
|
72
|
+
const initializeState = useCallback(() => {
|
|
73
|
+
const savedState = getSavedState();
|
|
74
|
+
|
|
75
|
+
let initialPage = page;
|
|
76
|
+
if (isLoadMore) {
|
|
77
|
+
initialPage = 1;
|
|
78
|
+
} else if (savedState.page != null) {
|
|
79
|
+
initialPage = savedState.page;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
page: initialPage,
|
|
84
|
+
perPage: savedState.perPage != null ? savedState.perPage : perPage,
|
|
85
|
+
sortBy: savedState.sortBy != null ? savedState.sortBy : sortBy,
|
|
86
|
+
sortOrder:
|
|
87
|
+
savedState.sortOrder != null ? savedState.sortOrder : sortOrder,
|
|
88
|
+
search: savedState.search != null ? savedState.search : search,
|
|
89
|
+
filters: savedState.filters != null ? savedState.filters : filters,
|
|
90
|
+
attrSettings: savedState.attrSettings || {},
|
|
91
|
+
items: initialItems,
|
|
92
|
+
selection: [],
|
|
93
|
+
error: null,
|
|
94
|
+
response: null,
|
|
95
|
+
count: 0,
|
|
96
|
+
isLoading: false,
|
|
97
|
+
initializingState: !initialItems.length,
|
|
98
|
+
confirmedPage: null,
|
|
99
|
+
};
|
|
100
|
+
}, [
|
|
101
|
+
getSavedState,
|
|
102
|
+
search,
|
|
103
|
+
page,
|
|
104
|
+
perPage,
|
|
105
|
+
sortBy,
|
|
106
|
+
sortOrder,
|
|
107
|
+
search,
|
|
108
|
+
filters,
|
|
109
|
+
isLoadMore,
|
|
110
|
+
]);
|
|
111
|
+
|
|
112
|
+
// Initialize state with default values
|
|
113
|
+
const [state, setState] = useState(initializeState);
|
|
114
|
+
|
|
115
|
+
const updateStateManager = useCallback(
|
|
116
|
+
(stateToSave) => {
|
|
117
|
+
if (stateManager) {
|
|
118
|
+
const context = getContext(stateToSave);
|
|
119
|
+
stateManager?.set?.(context);
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
[stateManager, getContext]
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Fetch data from the API
|
|
127
|
+
* @param {Object} addContext - Additional context to pass to the request handler
|
|
128
|
+
* @param {Object} newState - New state to use for the request
|
|
129
|
+
*/
|
|
130
|
+
const fetchData = useCallback(
|
|
131
|
+
async (addContext = {}, newState = null) => {
|
|
132
|
+
// Only set loading state if not initializing
|
|
133
|
+
if (!state.initializingState) {
|
|
134
|
+
setState((prev) => ({ ...prev, error: null, isLoading: true }));
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
try {
|
|
138
|
+
const currentState = newState || state;
|
|
139
|
+
const previousItems = newState?.items ?? state.items;
|
|
140
|
+
const res = await requestHandler({
|
|
141
|
+
endpoint,
|
|
142
|
+
version,
|
|
143
|
+
meta,
|
|
144
|
+
page: currentState.page,
|
|
145
|
+
perPage: currentState.perPage,
|
|
146
|
+
search: currentState.search,
|
|
147
|
+
sortBy: currentState.sortBy,
|
|
148
|
+
sortOrder: currentState.sortOrder,
|
|
149
|
+
filters: currentState.filters,
|
|
150
|
+
...addContext,
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
if (onResponse) onResponse(res);
|
|
154
|
+
|
|
155
|
+
let newItems;
|
|
156
|
+
|
|
157
|
+
if (isLoadMore) {
|
|
158
|
+
newItems =
|
|
159
|
+
currentState.page === 1
|
|
160
|
+
? res.items
|
|
161
|
+
: [...previousItems, ...res.items];
|
|
162
|
+
if (afterLoadMore) afterLoadMore(res);
|
|
163
|
+
} else {
|
|
164
|
+
newItems = res.items;
|
|
165
|
+
if (afterPageChange) afterPageChange(res);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const updatedState = {
|
|
169
|
+
...currentState,
|
|
170
|
+
response: res,
|
|
171
|
+
selection: [],
|
|
172
|
+
// Append items for loadMore, replace for pagination
|
|
173
|
+
items:
|
|
174
|
+
isLoadMore && currentState.page > 1
|
|
175
|
+
? [...previousItems, ...res.items]
|
|
176
|
+
: res.items,
|
|
177
|
+
count: res.count,
|
|
178
|
+
initializingState: false,
|
|
179
|
+
isLoading: false,
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
updateStateManager(updatedState);
|
|
183
|
+
|
|
184
|
+
setState(updatedState);
|
|
185
|
+
} catch (err) {
|
|
186
|
+
setState((prev) => ({
|
|
187
|
+
...prev,
|
|
188
|
+
error: err,
|
|
189
|
+
items: [],
|
|
190
|
+
count: 0,
|
|
191
|
+
initializingState: false,
|
|
192
|
+
isLoading: false,
|
|
193
|
+
}));
|
|
194
|
+
throw err;
|
|
195
|
+
}
|
|
196
|
+
},
|
|
197
|
+
[endpoint, version, isLoadMore, meta, requestHandler, state]
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Handlers for various actions (pagination, sorting, filtering, etc.)
|
|
202
|
+
*/
|
|
203
|
+
const handlers = useMemo(
|
|
204
|
+
() => ({
|
|
205
|
+
setPage: (value, addContext) => {
|
|
206
|
+
let newPage = value;
|
|
207
|
+
if (value === 0) {
|
|
208
|
+
newPage = "";
|
|
209
|
+
}
|
|
210
|
+
const newState = { ...state, page: newPage };
|
|
211
|
+
setState(newState);
|
|
212
|
+
if (newPage) fetchData(addContext, newState);
|
|
213
|
+
},
|
|
214
|
+
|
|
215
|
+
setPerPage: (value) => {
|
|
216
|
+
const newState = { ...state, perPage: value, page: 1 };
|
|
217
|
+
setState(newState);
|
|
218
|
+
fetchData({}, newState);
|
|
219
|
+
},
|
|
220
|
+
|
|
221
|
+
setSearch: (value) => {
|
|
222
|
+
// Only update if value changed to prevent unnecessary requests
|
|
223
|
+
if (value !== state.search) {
|
|
224
|
+
const newState = { ...state, search: value, page: 1 };
|
|
225
|
+
setState(newState);
|
|
226
|
+
fetchData({}, newState);
|
|
227
|
+
}
|
|
228
|
+
},
|
|
229
|
+
|
|
230
|
+
setSort: ({ by, order }) => {
|
|
231
|
+
const newState = { ...state, sortBy: by, sortOrder: order, page: 1 };
|
|
232
|
+
setState(newState);
|
|
233
|
+
fetchData({}, newState);
|
|
234
|
+
},
|
|
235
|
+
|
|
236
|
+
loadMore: () => {
|
|
237
|
+
const newState = { ...state, page: state.page + 1 };
|
|
238
|
+
setState(newState);
|
|
239
|
+
fetchData({}, newState);
|
|
240
|
+
},
|
|
241
|
+
|
|
242
|
+
clearFilters: () => {
|
|
243
|
+
const newState = { ...state, filters: filters, page: 1 };
|
|
244
|
+
setState(newState);
|
|
245
|
+
fetchData({}, newState);
|
|
246
|
+
},
|
|
247
|
+
|
|
248
|
+
refresh: (addContext = { isRefresh: true }) => {
|
|
249
|
+
if (isLoadMore) {
|
|
250
|
+
// For loadMore, reset to page 1
|
|
251
|
+
const newState = { ...state, page: 1, items: [] };
|
|
252
|
+
setState(newState);
|
|
253
|
+
fetchData(addContext, newState);
|
|
254
|
+
} else {
|
|
255
|
+
// For pagination, keep current page
|
|
256
|
+
fetchData(addContext);
|
|
257
|
+
}
|
|
258
|
+
},
|
|
259
|
+
|
|
260
|
+
setFilters: (filters) => {
|
|
261
|
+
const newState = { ...state, filters, page: 1 };
|
|
262
|
+
setState(newState);
|
|
263
|
+
fetchData({}, newState);
|
|
264
|
+
},
|
|
265
|
+
updateItemById: (item, id) => {
|
|
266
|
+
const newItems = state.items.map((i) => {
|
|
267
|
+
if (i.id === id) {
|
|
268
|
+
return { ...i, ...item };
|
|
269
|
+
}
|
|
270
|
+
return i;
|
|
271
|
+
});
|
|
272
|
+
setState((prev) => ({ ...prev, items: newItems }));
|
|
273
|
+
},
|
|
274
|
+
setSelection: (selection) => setState((prev) => ({ ...prev, selection })),
|
|
275
|
+
}),
|
|
276
|
+
[fetchData, isLoadMore, state]
|
|
277
|
+
);
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Memoized state for context to prevent unnecessary re-renders
|
|
281
|
+
*/
|
|
282
|
+
const memoizedState = useMemo(
|
|
283
|
+
() => ({
|
|
284
|
+
data: state.items,
|
|
285
|
+
response: state.response,
|
|
286
|
+
error: state.error,
|
|
287
|
+
count: state.count,
|
|
288
|
+
selection: state.selection,
|
|
289
|
+
pagination: {
|
|
290
|
+
page: state.page,
|
|
291
|
+
perPage: state.perPage,
|
|
292
|
+
hasMore: state.items.length < state.count,
|
|
293
|
+
},
|
|
294
|
+
loader: {
|
|
295
|
+
isLoading: state.isLoading,
|
|
296
|
+
initialLoading: state.initializingState,
|
|
297
|
+
},
|
|
298
|
+
sort: { sortBy: state.sortBy, sortOrder: state.sortOrder },
|
|
299
|
+
hasActiveFilters: hasActiveFilters(state.filters, filters),
|
|
300
|
+
search: state.search,
|
|
301
|
+
filters: state.filters,
|
|
302
|
+
attrs: attrs || Object.keys(state.items[0] || {}),
|
|
303
|
+
isEmpty: state.items.length === 0,
|
|
304
|
+
...handlers,
|
|
305
|
+
}),
|
|
306
|
+
[
|
|
307
|
+
state.items,
|
|
308
|
+
state.response,
|
|
309
|
+
state.error,
|
|
310
|
+
state.count,
|
|
311
|
+
state.selection,
|
|
312
|
+
state.page,
|
|
313
|
+
state.perPage,
|
|
314
|
+
state.isLoading,
|
|
315
|
+
state.initializingState,
|
|
316
|
+
state.sortBy,
|
|
317
|
+
state.sortOrder,
|
|
318
|
+
state.search,
|
|
319
|
+
state.filters,
|
|
320
|
+
handlers,
|
|
321
|
+
attrs,
|
|
322
|
+
]
|
|
323
|
+
);
|
|
324
|
+
|
|
325
|
+
useEffect(() => {
|
|
326
|
+
if (!state.initializingState) {
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
if (!initRef.current) {
|
|
330
|
+
initRef.current = true;
|
|
331
|
+
|
|
332
|
+
// Initialize state manager
|
|
333
|
+
if (stateManager?.init) {
|
|
334
|
+
const context = getContext(state);
|
|
335
|
+
stateManager.init(context);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
if (!initialItems.length) handlers.setPage(state.page);
|
|
339
|
+
}
|
|
340
|
+
}, []);
|
|
341
|
+
|
|
342
|
+
// Update list state in context
|
|
343
|
+
useEffect(() => {
|
|
344
|
+
setListState(memoizedState);
|
|
345
|
+
}, [
|
|
346
|
+
setListState,
|
|
347
|
+
state.items,
|
|
348
|
+
state.count,
|
|
349
|
+
state.error,
|
|
350
|
+
state.isLoading,
|
|
351
|
+
state.selection,
|
|
352
|
+
state.page,
|
|
353
|
+
state.perPage,
|
|
354
|
+
state.sortBy,
|
|
355
|
+
state.sortOrder,
|
|
356
|
+
]);
|
|
357
|
+
|
|
358
|
+
return typeof children === "function" ? children(memoizedState) : children;
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
export default ReactList;
|