@object-ui/plugin-grid 2.0.0 → 3.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.
@@ -1,9 +1,9 @@
1
- (function(A,R){typeof exports=="object"&&typeof module<"u"?R(exports,require("react"),require("@object-ui/core"),require("@object-ui/react"),require("@object-ui/fields"),require("@object-ui/components"),require("lucide-react"),require("react-dom")):typeof define=="function"&&define.amd?define(["exports","react","@object-ui/core","@object-ui/react","@object-ui/fields","@object-ui/components","lucide-react","react-dom"],R):(A=typeof globalThis<"u"?globalThis:A||self,R(A.ObjectUIPluginGrid={},A.React,A.core,A.react,A.fields,A.components,A.lucideReact,A.ReactDOM))})(this,(function(A,R,he,B,me,q,de,Ce){"use strict";function _e(n){const a=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(n){for(const e in n)if(e!=="default"){const s=Object.getOwnPropertyDescriptor(n,e);Object.defineProperty(a,e,s.get?s:{enumerable:!0,get:()=>n[e]})}}return a.default=n,Object.freeze(a)}const le=_e(R);var ae={exports:{}},Q={};var pe;function Ae(){if(pe)return Q;pe=1;var n=Symbol.for("react.transitional.element"),a=Symbol.for("react.fragment");function e(s,t,i){var o=null;if(i!==void 0&&(o=""+i),t.key!==void 0&&(o=""+t.key),"key"in t){i={};for(var l in t)l!=="key"&&(i[l]=t[l])}else i=t;return t=i.ref,{$$typeof:n,type:s,key:o,ref:t!==void 0?t:null,props:i}}return Q.Fragment=a,Q.jsx=e,Q.jsxs=e,Q}var ee={};var ge;function Re(){return ge||(ge=1,process.env.NODE_ENV!=="production"&&(function(){function n(r){if(r==null)return null;if(typeof r=="function")return r.$$typeof===U?null:r.displayName||r.name||null;if(typeof r=="string")return r;switch(r){case w:return"Fragment";case O:return"Profiler";case z:return"StrictMode";case Y:return"Suspense";case $:return"SuspenseList";case J:return"Activity"}if(typeof r=="object")switch(typeof r.tag=="number"&&console.error("Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue."),r.$$typeof){case v:return"Portal";case M:return r.displayName||"Context";case F:return(r._context.displayName||"Context")+".Consumer";case C:var h=r.render;return r=r.displayName,r||(r=h.displayName||h.name||"",r=r!==""?"ForwardRef("+r+")":"ForwardRef"),r;case L:return h=r.displayName||null,h!==null?h:n(r.type)||"Memo";case I:h=r._payload,r=r._init;try{return n(r(h))}catch{}}return null}function a(r){return""+r}function e(r){try{a(r);var h=!1}catch{h=!0}if(h){h=console;var y=h.error,S=typeof Symbol=="function"&&Symbol.toStringTag&&r[Symbol.toStringTag]||r.constructor.name||"Object";return y.call(h,"The provided key is an unsupported type %s. This value must be coerced to a string before using it here.",S),a(r)}}function s(r){if(r===w)return"<>";if(typeof r=="object"&&r!==null&&r.$$typeof===I)return"<...>";try{var h=n(r);return h?"<"+h+">":"<...>"}catch{return"<...>"}}function t(){var r=K.A;return r===null?null:r.getOwner()}function i(){return Error("react-stack-top-frame")}function o(r){if(te.call(r,"key")){var h=Object.getOwnPropertyDescriptor(r,"key").get;if(h&&h.isReactWarning)return!1}return r.key!==void 0}function l(r,h){function y(){ce||(ce=!0,console.error("%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://react.dev/link/special-props)",h))}y.isReactWarning=!0,Object.defineProperty(r,"key",{get:y,configurable:!0})}function p(){var r=n(this.type);return ne[r]||(ne[r]=!0,console.error("Accessing element.ref was removed in React 19. ref is now a regular prop. It will be removed from the JSX Element type in a future release.")),r=this.props.ref,r!==void 0?r:null}function g(r,h,y,S,G,Z){var f=y.ref;return r={$$typeof:_,type:r,key:h,props:y,_owner:S},(f!==void 0?f:null)!==null?Object.defineProperty(r,"ref",{enumerable:!1,get:p}):Object.defineProperty(r,"ref",{enumerable:!1,value:null}),r._store={},Object.defineProperty(r._store,"validated",{configurable:!1,enumerable:!1,writable:!0,value:0}),Object.defineProperty(r,"_debugInfo",{configurable:!1,enumerable:!1,writable:!0,value:null}),Object.defineProperty(r,"_debugStack",{configurable:!1,enumerable:!1,writable:!0,value:G}),Object.defineProperty(r,"_debugTask",{configurable:!1,enumerable:!1,writable:!0,value:Z}),Object.freeze&&(Object.freeze(r.props),Object.freeze(r)),r}function m(r,h,y,S,G,Z){var f=h.children;if(f!==void 0)if(S)if(D(f)){for(S=0;S<f.length;S++)E(f[S]);Object.freeze&&Object.freeze(f)}else console.error("React.jsx: Static children should always be an array. You are likely explicitly calling React.jsxs or React.jsxDEV. Use the Babel transform instead.");else E(f);if(te.call(h,"key")){f=n(r);var x=Object.keys(h).filter(function(j){return j!=="key"});S=0<x.length?"{key: someKey, "+x.join(": ..., ")+": ...}":"{key: someKey}",ue[f+S]||(x=0<x.length?"{"+x.join(": ..., ")+": ...}":"{}",console.error(`A props object containing a "key" prop is being spread into JSX:
1
+ (function(k,g){typeof exports=="object"&&typeof module<"u"?g(exports,require("react"),require("@object-ui/core"),require("@object-ui/react"),require("@object-ui/fields"),require("@object-ui/components"),require("lucide-react"),require("react-dom")):typeof define=="function"&&define.amd?define(["exports","react","@object-ui/core","@object-ui/react","@object-ui/fields","@object-ui/components","lucide-react","react-dom"],g):(k=typeof globalThis<"u"?globalThis:k||self,g(k.ObjectUIPluginGrid={},k.React,k.core,k.react,k.fields,k.components,k.lucideReact,k.ReactDOM))})(this,(function(k,g,ye,Z,Ee,F,J,Le){"use strict";function $e(t){const c=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(t){for(const e in t)if(e!=="default"){const n=Object.getOwnPropertyDescriptor(t,e);Object.defineProperty(c,e,n.get?n:{enumerable:!0,get:()=>t[e]})}}return c.default=t,Object.freeze(c)}const me=$e(g);var pe={exports:{}},le={};var je;function We(){if(je)return le;je=1;var t=Symbol.for("react.transitional.element"),c=Symbol.for("react.fragment");function e(n,s,i){var o=null;if(i!==void 0&&(o=""+i),s.key!==void 0&&(o=""+s.key),"key"in s){i={};for(var l in s)l!=="key"&&(i[l]=s[l])}else i=s;return s=i.ref,{$$typeof:t,type:n,key:o,ref:s!==void 0?s:null,props:i}}return le.Fragment=c,le.jsx=e,le.jsxs=e,le}var ae={};var Se;function Ve(){return Se||(Se=1,process.env.NODE_ENV!=="production"&&(function(){function t(r){if(r==null)return null;if(typeof r=="function")return r.$$typeof===ne?null:r.displayName||r.name||null;if(typeof r=="string")return r;switch(r){case C:return"Fragment";case T:return"Profiler";case A:return"StrictMode";case M:return"Suspense";case W:return"SuspenseList";case te:return"Activity"}if(typeof r=="object")switch(typeof r.tag=="number"&&console.error("Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue."),r.$$typeof){case x:return"Portal";case R:return r.displayName||"Context";case $:return(r._context.displayName||"Context")+".Consumer";case K:var h=r.render;return r=r.displayName,r||(r=h.displayName||h.name||"",r=r!==""?"ForwardRef("+r+")":"ForwardRef"),r;case be:return h=r.displayName||null,h!==null?h:t(r.type)||"Memo";case ee:h=r._payload,r=r._init;try{return t(r(h))}catch{}}return null}function c(r){return""+r}function e(r){try{c(r);var h=!1}catch{h=!0}if(h){h=console;var w=h.error,O=typeof Symbol=="function"&&Symbol.toStringTag&&r[Symbol.toStringTag]||r.constructor.name||"Object";return w.call(h,"The provided key is an unsupported type %s. This value must be coerced to a string before using it here.",O),c(r)}}function n(r){if(r===C)return"<>";if(typeof r=="object"&&r!==null&&r.$$typeof===ee)return"<...>";try{var h=t(r);return h?"<"+h+">":"<...>"}catch{return"<...>"}}function s(){var r=se.A;return r===null?null:r.getOwner()}function i(){return Error("react-stack-top-frame")}function o(r){if(D.call(r,"key")){var h=Object.getOwnPropertyDescriptor(r,"key").get;if(h&&h.isReactWarning)return!1}return r.key!==void 0}function l(r,h){function w(){V||(V=!0,console.error("%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://react.dev/link/special-props)",h))}w.isReactWarning=!0,Object.defineProperty(r,"key",{get:w,configurable:!0})}function b(){var r=t(this.type);return Y[r]||(Y[r]=!0,console.error("Accessing element.ref was removed in React 19. ref is now a regular prop. It will be removed from the JSX Element type in a future release.")),r=this.props.ref,r!==void 0?r:null}function m(r,h,w,O,re,ie){var N=w.ref;return r={$$typeof:E,type:r,key:h,props:w,_owner:O},(N!==void 0?N:null)!==null?Object.defineProperty(r,"ref",{enumerable:!1,get:b}):Object.defineProperty(r,"ref",{enumerable:!1,value:null}),r._store={},Object.defineProperty(r._store,"validated",{configurable:!1,enumerable:!1,writable:!0,value:0}),Object.defineProperty(r,"_debugInfo",{configurable:!1,enumerable:!1,writable:!0,value:null}),Object.defineProperty(r,"_debugStack",{configurable:!1,enumerable:!1,writable:!0,value:re}),Object.defineProperty(r,"_debugTask",{configurable:!1,enumerable:!1,writable:!0,value:ie}),Object.freeze&&(Object.freeze(r.props),Object.freeze(r)),r}function f(r,h,w,O,re,ie){var N=h.children;if(N!==void 0)if(O)if(U(N)){for(O=0;O<N.length;O++)y(N[O]);Object.freeze&&Object.freeze(N)}else console.error("React.jsx: Static children should always be an array. You are likely explicitly calling React.jsxs or React.jsxDEV. Use the Babel transform instead.");else y(N);if(D.call(h,"key")){N=t(r);var q=Object.keys(h).filter(function(B){return B!=="key"});O=0<q.length?"{key: someKey, "+q.join(": ..., ")+": ...}":"{key: someKey}",ce[N+O]||(q=0<q.length?"{"+q.join(": ..., ")+": ...}":"{}",console.error(`A props object containing a "key" prop is being spread into JSX:
2
2
  let props = %s;
3
3
  <%s {...props} />
4
4
  React keys must be passed directly to JSX without using spread:
5
5
  let props = %s;
6
- <%s key={someKey} {...props} />`,S,f,x,f),ue[f+S]=!0)}if(f=null,y!==void 0&&(e(y),f=""+y),o(h)&&(e(h.key),f=""+h.key),"key"in h){y={};for(var N in h)N!=="key"&&(y[N]=h[N])}else y=h;return f&&l(y,typeof r=="function"?r.displayName||r.name||"Unknown":r),g(r,f,y,t(),G,Z)}function E(r){u(r)?r._store&&(r._store.validated=1):typeof r=="object"&&r!==null&&r.$$typeof===I&&(r._payload.status==="fulfilled"?u(r._payload.value)&&r._payload.value._store&&(r._payload.value._store.validated=1):r._store&&(r._store.validated=1))}function u(r){return typeof r=="object"&&r!==null&&r.$$typeof===_}var b=R,_=Symbol.for("react.transitional.element"),v=Symbol.for("react.portal"),w=Symbol.for("react.fragment"),z=Symbol.for("react.strict_mode"),O=Symbol.for("react.profiler"),F=Symbol.for("react.consumer"),M=Symbol.for("react.context"),C=Symbol.for("react.forward_ref"),Y=Symbol.for("react.suspense"),$=Symbol.for("react.suspense_list"),L=Symbol.for("react.memo"),I=Symbol.for("react.lazy"),J=Symbol.for("react.activity"),U=Symbol.for("react.client.reference"),K=b.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,te=Object.prototype.hasOwnProperty,D=Array.isArray,X=console.createTask?console.createTask:function(){return null};b={react_stack_bottom_frame:function(r){return r()}};var ce,ne={},V=b.react_stack_bottom_frame.bind(b,i)(),se=X(s(i)),ue={};ee.Fragment=w,ee.jsx=function(r,h,y){var S=1e4>K.recentlyCreatedOwnerStacks++;return m(r,h,y,!1,S?Error("react-stack-top-frame"):V,S?X(s(r)):se)},ee.jsxs=function(r,h,y){var S=1e4>K.recentlyCreatedOwnerStacks++;return m(r,h,y,!0,S?Error("react-stack-top-frame"):V,S?X(s(r)):se)}})()),ee}var be;function Te(){return be||(be=1,process.env.NODE_ENV==="production"?ae.exports=Ae():ae.exports=Re()),ae.exports}var c=Te();function Ne(n){return n.data?Array.isArray(n.data)?{provider:"value",items:n.data}:n.data:n.staticData?{provider:"value",items:n.staticData}:n.objectName?{provider:"object",object:n.objectName}:null}function ve(n){if(!(!n||n.length===0))return typeof n[0]=="object"&&n[0]!==null,n}const ye=({schema:n,dataSource:a,onEdit:e,onDelete:s,onRowSelect:t,onRowClick:i,onCellChange:o,onRowSave:l,onBatchSave:p,...g})=>{const[m,E]=R.useState([]),[u,b]=R.useState(!0),[_,v]=R.useState(null),[w,z]=R.useState(null),O=g.data,F=B.useDataScope(n.bind),M=Ne(n),C=R.useMemo(()=>O&&Array.isArray(O)?{provider:"value",items:O}:F&&Array.isArray(F)?{provider:"value",items:F}:M,[JSON.stringify(M),F,O]),Y=C?.provider==="value",$=C?.provider==="object"&&C&&"object"in C?C.object:n.objectName,L=n.fields,I=n.columns,J=n.filter,U=n.sort,K=n.pagination,te=n.pageSize;R.useEffect(()=>{Y&&C?.provider==="value"&&(E(f=>{const x=C.items;return JSON.stringify(f)!==JSON.stringify(x)?x:f}),b(!1))},[Y,C]),R.useEffect(()=>{if(Y)return;let f=!1;return(async()=>{b(!0),v(null);try{let N=null;if((ve(I)||L)&&$)N={name:$,fields:{}};else if($&&a){const d=await a.getObjectSchema($);if(f)return;N=d}else throw $?new Error("DataSource required"):new Error("Object name required for data fetching");if(f||z(N),a&&$){const k={$select:(()=>{if(L)return L;if(I&&Array.isArray(I))return I.map(T=>typeof T=="string"?T:T.field)})(),$top:K?.pageSize||te||50};J&&Array.isArray(J)?k.$filter=J:n.defaultFilters&&(k.$filter=n.defaultFilters),U?typeof U=="string"?k.$orderby=U:Array.isArray(U)&&(k.$orderby=U.map(T=>`${T.field} ${T.order}`).join(", ")):n.defaultSort&&(k.$orderby=`${n.defaultSort.field} ${n.defaultSort.order}`);const W=await a.find($,k);if(f)return;E(W.data||[])}}catch(N){f||v(N)}finally{f||b(!1)}})(),()=>{f=!0}},[$,L,I,J,U,K,te,a,Y,C]);const D=B.useNavigationOverlay({navigation:n.navigation,objectName:n.objectName,onNavigate:n.onNavigate,onRowClick:i}),{execute:X}=B.useAction(),ce=R.useCallback(()=>{const f=ve(I);if(f){if(f.length>0&&typeof f[0]=="object"&&f[0]!==null){const j=f[0];if("accessorKey"in j)return f;if("field"in j)return f.filter(d=>d?.field&&typeof d.field=="string"&&!d.hidden).map(d=>{const k=d.label||d.field.charAt(0).toUpperCase()+d.field.slice(1).replace(/_/g," ");let W;const T=d.type?me.getCellRenderer(d.type):null;return d.link&&d.action?W=(P,re)=>{const ie=T?c.jsx(T,{value:P,field:{name:d.field,type:d.type||"text"}}):String(P??"");return c.jsx("button",{type:"button",className:"text-primary underline-offset-4 hover:underline cursor-pointer bg-transparent border-none p-0 text-left font-inherit",onClick:oe=>{oe.stopPropagation(),D.handleClick(re)},children:ie})}:d.link?W=(P,re)=>{const ie=T?c.jsx(T,{value:P,field:{name:d.field,type:d.type||"text"}}):String(P??"");return c.jsx("button",{type:"button",className:"text-primary underline-offset-4 hover:underline cursor-pointer bg-transparent border-none p-0 text-left font-inherit",onClick:oe=>{oe.stopPropagation(),D.handleClick(re)},children:ie})}:d.action?W=(P,re)=>{const ie=T?c.jsx(T,{value:P,field:{name:d.field,type:d.type||"text"}}):String(P??"");return c.jsx("button",{type:"button",className:"text-primary underline-offset-4 hover:underline cursor-pointer bg-transparent border-none p-0 text-left font-inherit",onClick:oe=>{oe.stopPropagation(),X({type:d.action,params:{record:re,field:d.field,value:P}})},children:ie})}:T&&(W=P=>c.jsx(T,{value:P,field:{name:d.field,type:d.type||"text"}})),{header:k,accessorKey:d.field,...d.width&&{width:d.width},...d.align&&{align:d.align},sortable:d.sortable!==!1,...d.resizable!==void 0&&{resizable:d.resizable},...d.wrap!==void 0&&{wrap:d.wrap},...W&&{cell:W}}})}return f.filter(j=>typeof j=="string"&&j.trim().length>0).map(j=>({header:w?.fields?.[j]?.label||j.charAt(0).toUpperCase()+j.slice(1).replace(/_/g," "),accessorKey:j}))}if(Y){const j=C?.provider==="value"?C.items:[];if(j.length>0)return(L||Object.keys(j[0])).map(k=>({header:k.charAt(0).toUpperCase()+k.slice(1).replace(/_/g," "),accessorKey:k}))}if(!w)return[];const x=[];return(L||Object.keys(w.fields||{})).forEach(j=>{const d=w.fields?.[j];if(!d||d.permissions&&d.permissions.read===!1)return;const k=me.getCellRenderer(d.type);x.push({header:d.label||j,accessorKey:j,cell:W=>c.jsx(k,{value:W,field:d}),sortable:d.sortable!==!1})}),x},[w,L,I,C,Y,D.handleClick,X]);if(_)return c.jsxs("div",{className:"p-4 border border-red-300 bg-red-50 rounded-md",children:[c.jsx("h3",{className:"text-red-800 font-semibold",children:"Error loading grid"}),c.jsx("p",{className:"text-red-600 text-sm mt-1",children:_.message})]});if(u&&m.length===0)return c.jsxs("div",{className:"p-8 text-center",children:[c.jsx("div",{className:"inline-block animate-spin rounded-full h-8 w-8 border-b-2 border-gray-900"}),c.jsx("p",{className:"mt-2 text-sm text-gray-600",children:"Loading grid..."})]});const ne=ce(),V="operations"in n?n.operations:void 0,se=V&&(V.update||V.delete),ue=se?[...ne,{header:"Actions",accessorKey:"_actions",cell:(f,x)=>c.jsxs(q.DropdownMenu,{children:[c.jsx(q.DropdownMenuTrigger,{asChild:!0,children:c.jsxs(q.Button,{variant:"ghost",size:"icon",className:"h-8 w-8",children:[c.jsx(de.MoreVertical,{className:"h-4 w-4"}),c.jsx("span",{className:"sr-only",children:"Open menu"})]})}),c.jsxs(q.DropdownMenuContent,{align:"end",children:[V?.update&&e&&c.jsxs(q.DropdownMenuItem,{onClick:()=>e(x),children:[c.jsx(de.Edit,{className:"mr-2 h-4 w-4"}),"Edit"]}),V?.delete&&s&&c.jsxs(q.DropdownMenuItem,{onClick:()=>s(x),children:[c.jsx(de.Trash2,{className:"mr-2 h-4 w-4"}),"Delete"]})]})]}),sortable:!1}]:ne;let r=!1;n.selection?.type?r=n.selection.type==="none"?!1:n.selection.type:n.selectable!==void 0&&(r=n.selectable);const h=n.pagination!==void 0?!0:n.showPagination!==void 0?n.showPagination:!0,y=n.pagination?.pageSize||n.pageSize||10,S=n.searchableFields!==void 0?n.searchableFields.length>0:n.showSearch!==void 0?n.showSearch:!0,G={type:"data-table",caption:n.label||n.title,columns:ue,data:m,pagination:h,pageSize:y,searchable:S,selectable:r,sortable:!0,exportable:V?.export,rowActions:se,resizableColumns:n.resizable??n.resizableColumns??!0,reorderableColumns:n.reorderableColumns??!1,editable:n.editable??!1,className:n.className,onSelectionChange:t,onRowClick:D.handleClick,onCellChange:o,onRowSave:l,onBatchSave:p},Z=n.label?`${n.label} Detail`:n.objectName?`${n.objectName.charAt(0).toUpperCase()+n.objectName.slice(1)} Detail`:"Record Detail";return D.isOverlay&&D.mode==="split"?c.jsx(q.NavigationOverlay,{...D,title:Z,mainContent:c.jsx(B.SchemaRenderer,{schema:G}),children:f=>c.jsx("div",{className:"space-y-3",children:Object.entries(f).map(([x,N])=>c.jsxs("div",{className:"flex flex-col",children:[c.jsx("span",{className:"text-xs font-medium text-muted-foreground uppercase tracking-wide",children:x.replace(/_/g," ")}),c.jsx("span",{className:"text-sm",children:String(N??"—")})]},x))})}):c.jsxs(c.Fragment,{children:[c.jsx(B.SchemaRenderer,{schema:G}),D.isOverlay&&c.jsx(q.NavigationOverlay,{...D,title:Z,children:f=>c.jsx("div",{className:"space-y-3",children:Object.entries(f).map(([x,N])=>c.jsxs("div",{className:"flex flex-col",children:[c.jsx("span",{className:"text-xs font-medium text-muted-foreground uppercase tracking-wide",children:x.replace(/_/g," ")}),c.jsx("span",{className:"text-sm",children:String(N??"—")})]},x))})})]})};function H(n,a,e){let s=e.initialDeps??[],t,i=!0;function o(){var l,p,g;let m;e.key&&((l=e.debug)!=null&&l.call(e))&&(m=Date.now());const E=n();if(!(E.length!==s.length||E.some((_,v)=>s[v]!==_)))return t;s=E;let b;if(e.key&&((p=e.debug)!=null&&p.call(e))&&(b=Date.now()),t=a(...E),e.key&&((g=e.debug)!=null&&g.call(e))){const _=Math.round((Date.now()-m)*100)/100,v=Math.round((Date.now()-b)*100)/100,w=v/16,z=(O,F)=>{for(O=String(O);O.length<F;)O=" "+O;return O};console.info(`%c⏱ ${z(v,5)} /${z(_,5)} ms`,`
6
+ <%s key={someKey} {...props} />`,O,N,q,N),ce[N+O]=!0)}if(N=null,w!==void 0&&(e(w),N=""+w),o(h)&&(e(h.key),N=""+h.key),"key"in h){w={};for(var oe in h)oe!=="key"&&(w[oe]=h[oe])}else w=h;return N&&l(w,typeof r=="function"?r.displayName||r.name||"Unknown":r),m(r,N,w,s(),re,ie)}function y(r){u(r)?r._store&&(r._store.validated=1):typeof r=="object"&&r!==null&&r.$$typeof===ee&&(r._payload.status==="fulfilled"?u(r._payload.value)&&r._payload.value._store&&(r._payload.value._store.validated=1):r._store&&(r._store.validated=1))}function u(r){return typeof r=="object"&&r!==null&&r.$$typeof===E}var d=g,E=Symbol.for("react.transitional.element"),x=Symbol.for("react.portal"),C=Symbol.for("react.fragment"),A=Symbol.for("react.strict_mode"),T=Symbol.for("react.profiler"),$=Symbol.for("react.consumer"),R=Symbol.for("react.context"),K=Symbol.for("react.forward_ref"),M=Symbol.for("react.suspense"),W=Symbol.for("react.suspense_list"),be=Symbol.for("react.memo"),ee=Symbol.for("react.lazy"),te=Symbol.for("react.activity"),ne=Symbol.for("react.client.reference"),se=d.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,D=Object.prototype.hasOwnProperty,U=Array.isArray,L=console.createTask?console.createTask:function(){return null};d={react_stack_bottom_frame:function(r){return r()}};var V,Y={},X=d.react_stack_bottom_frame.bind(d,i)(),G=L(n(i)),ce={};ae.Fragment=C,ae.jsx=function(r,h,w){var O=1e4>se.recentlyCreatedOwnerStacks++;return f(r,h,w,!1,O?Error("react-stack-top-frame"):X,O?L(n(r)):G)},ae.jsxs=function(r,h,w){var O=1e4>se.recentlyCreatedOwnerStacks++;return f(r,h,w,!0,O?Error("react-stack-top-frame"):X,O?L(n(r)):G)}})()),ae}var we;function Ye(){return we||(we=1,process.env.NODE_ENV==="production"?pe.exports=We():pe.exports=Ve()),pe.exports}var a=Ye();function Ge(t){const{onRefresh:c,threshold:e=80,enabled:n=!0}=t,s=g.useRef(null),[i,o]=g.useState(!1),[l,b]=g.useState(0),m=g.useRef(0),f=g.useCallback(d=>{if(!n||i)return;const E=s.current;E&&E.scrollTop===0&&(m.current=d.touches[0].clientY)},[n,i]),y=g.useCallback(d=>{if(!n||i||!m.current)return;const x=d.touches[0].clientY-m.current;x>0&&b(Math.min(x,e*1.5))},[n,i,e]),u=g.useCallback(async()=>{if(!(!n||i)){if(l>=e){o(!0);try{await c()}finally{o(!1)}}b(0),m.current=0}},[n,i,l,e,c]);return g.useEffect(()=>{const d=s.current;if(!(!d||!n))return d.addEventListener("touchstart",f,{passive:!0}),d.addEventListener("touchmove",y,{passive:!0}),d.addEventListener("touchend",u,{passive:!0}),()=>{d.removeEventListener("touchstart",f),d.removeEventListener("touchmove",y),d.removeEventListener("touchend",u)}},[f,y,u,n]),{ref:s,isRefreshing:i,pullDistance:l}}const qe=g.createContext(null);qe.displayName="MobileContext";const Ke={red:"bg-red-100",green:"bg-green-100",blue:"bg-blue-100",yellow:"bg-yellow-100",orange:"bg-orange-100",purple:"bg-purple-100",pink:"bg-pink-100",gray:"bg-gray-100",grey:"bg-gray-100",indigo:"bg-indigo-100",teal:"bg-teal-100",cyan:"bg-cyan-100",amber:"bg-amber-100",lime:"bg-lime-100",emerald:"bg-emerald-100",rose:"bg-rose-100",sky:"bg-sky-100",violet:"bg-violet-100",fuchsia:"bg-fuchsia-100",slate:"bg-slate-100",zinc:"bg-zinc-100",stone:"bg-stone-100",neutral:"bg-neutral-100"};function Ue(t){if(t.startsWith("bg-"))return t;const c=t.toLowerCase().trim();return Ke[c]}function Ce(t){return g.useCallback(c=>{if(!t?.field||!t.colors)return;const e=String(c[t.field]??""),n=t.colors[e];if(n)return Ue(n)},[t?.field,t?.colors])}function Be(t,c){return c.map(e=>String(t[e.field]??"")).join(" / ")}function He(t,c){return c.map(e=>{const n=t[e.field];return n!=null&&n!==""?String(n):"(empty)"}).join(" / ")}function Je(t,c,e){const n=t.localeCompare(c,void 0,{numeric:!0,sensitivity:"base"});return e==="desc"?-n:n}function Oe(t,c){const e=t?.fields,n=!!(e&&e.length>0),[s,i]=g.useState({}),o=g.useMemo(()=>e?e.some(m=>m.collapsed):!1,[e]),l=g.useMemo(()=>{if(!n||!e)return[];const m=new Map,f=[];for(const u of c){const d=Be(u,e);m.has(d)||(m.set(d,{label:He(u,e),rows:[]}),f.push(d)),m.get(d).rows.push(u)}const y=e[0]?.order??"asc";return f.sort((u,d)=>Je(u,d,y)),f.map(u=>{const d=m.get(u),E=u in s?s[u]:o;return{key:u,label:d.label,rows:d.rows,collapsed:E}})},[c,e,n,s,o]),b=g.useCallback(m=>{i(f=>({...f,[m]:f[m]!==void 0?!f[m]:!o}))},[o]);return{groups:l,isGrouped:n,toggleGroup:b}}function Xe(t){return t.data?Array.isArray(t.data)?{provider:"value",items:t.data}:t.data:t.staticData?{provider:"value",items:t.staticData}:t.objectName?{provider:"object",object:t.objectName}:null}function Ne(t){if(!(!t||t.length===0))return typeof t[0]=="object"&&t[0]!==null,t}const _e=({schema:t,dataSource:c,onEdit:e,onDelete:n,onRowSelect:s,onRowClick:i,onCellChange:o,onRowSave:l,onBatchSave:b,...m})=>{const[f,y]=g.useState([]),[u,d]=g.useState(!0),[E,x]=g.useState(null),[C,A]=g.useState(null),[T,$]=g.useState(!1),[R,K]=g.useState(0),M=g.useCallback(async()=>{K(v=>v+1)},[]),{ref:W,isRefreshing:be,pullDistance:ee}=Ge({onRefresh:M,enabled:!!c&&!!t.objectName});g.useEffect(()=>{const v=()=>$(window.innerWidth<480);return v(),window.addEventListener("resize",v),()=>window.removeEventListener("resize",v)},[]);const te=m.data,ne=Z.useDataScope(t.bind),se=Xe(t),D=g.useMemo(()=>te&&Array.isArray(te)?{provider:"value",items:te}:ne&&Array.isArray(ne)?{provider:"value",items:ne}:se,[JSON.stringify(se),ne,te]),U=D?.provider==="value",L=D?.provider==="object"&&D&&"object"in D?D.object:t.objectName,V=t.fields,Y=t.columns,X=t.filter,G=t.sort,ce=t.pagination,r=t.pageSize;g.useEffect(()=>{U&&D?.provider==="value"&&(y(v=>{const j=D.items;return JSON.stringify(v)!==JSON.stringify(j)?j:v}),d(!1))},[U,D]),g.useEffect(()=>{if(U)return;let v=!1;return(async()=>{d(!0),x(null);try{let I=null;if((Ne(Y)||V)&&L)I={name:L,fields:{}};else if(L&&c){const p=await c.getObjectSchema(L);if(v)return;I=p}else throw L?new Error("DataSource required"):new Error("Object name required for data fetching");if(v||A(I),c&&L){const P={$select:(()=>{if(V)return V;if(Y&&Array.isArray(Y))return Y.map(z=>typeof z=="string"?z:z.field)})(),$top:ce?.pageSize||r||50};X&&Array.isArray(X)?P.$filter=X:t.defaultFilters&&(P.$filter=t.defaultFilters),G?typeof G=="string"?P.$orderby=G:Array.isArray(G)&&(P.$orderby=G.map(z=>`${z.field} ${z.order}`).join(", ")):t.defaultSort&&(P.$orderby=`${t.defaultSort.field} ${t.defaultSort.order}`);const ue=await c.find(L,P);if(v)return;y(ue.data||[])}}catch(I){v||x(I)}finally{v||d(!1)}})(),()=>{v=!0}},[L,V,Y,X,G,ce,r,c,U,D,R]);const h=Z.useNavigationOverlay({navigation:t.navigation,objectName:t.objectName,onNavigate:t.onNavigate,onRowClick:i}),{execute:w}=Z.useAction(),O=Ce(t.rowColor),{groups:re,isGrouped:ie,toggleGroup:N}=Oe(t.grouping,f),q=g.useCallback(()=>{const v=Ne(Y);if(v){if(v.length>0&&typeof v[0]=="object"&&v[0]!==null){const S=v[0];if("accessorKey"in S)return v;if("field"in S)return v.filter(p=>p?.field&&typeof p.field=="string"&&!p.hidden).map((p,P)=>{const ue=p.label||p.field.charAt(0).toUpperCase()+p.field.slice(1).replace(/_/g," ");let z;const H=p.type?Ee.getCellRenderer(p.type):null;p.link&&p.action?z=(_,de)=>{const fe=H?a.jsx(H,{value:_,field:{name:p.field,type:p.type||"text"}}):_!=null&&_!==""?String(_):a.jsx("span",{className:"text-muted-foreground",children:"-"});return a.jsx("button",{type:"button",className:"text-primary font-medium underline-offset-4 hover:underline cursor-pointer bg-transparent border-none p-0 text-left font-inherit",onClick:he=>{he.stopPropagation(),h.handleClick(de)},children:fe})}:p.link?z=(_,de)=>{const fe=H?a.jsx(H,{value:_,field:{name:p.field,type:p.type||"text"}}):_!=null&&_!==""?String(_):a.jsx("span",{className:"text-muted-foreground",children:"-"});return a.jsx("button",{type:"button",className:"text-primary font-medium underline-offset-4 hover:underline cursor-pointer bg-transparent border-none p-0 text-left font-inherit",onClick:he=>{he.stopPropagation(),h.handleClick(de)},children:fe})}:p.action?z=(_,de)=>{const fe=H?a.jsx(H,{value:_,field:{name:p.field,type:p.type||"text"}}):_!=null&&_!==""?String(_):a.jsx("span",{className:"text-muted-foreground",children:"-"});return a.jsx("button",{type:"button",className:"text-primary underline-offset-4 hover:underline cursor-pointer bg-transparent border-none p-0 text-left font-inherit",onClick:he=>{he.stopPropagation(),w({type:p.action,params:{record:de,field:p.field,value:_}})},children:fe})}:H?z=_=>a.jsx(H,{value:_,field:{name:p.field,type:p.type||"text"}}):z=_=>_!=null&&_!==""?a.jsx("span",{children:String(_)}):a.jsx("span",{className:"text-muted-foreground",children:"-"});const bt=["number","currency","percent"],Fe=p.align||(p.type&&bt.includes(p.type)?"right":void 0),vt=P===0||p.essential===!0;return{header:ue,accessorKey:p.field,...!vt&&{className:"hidden sm:table-cell"},...p.width&&{width:p.width},...Fe&&{align:Fe},sortable:p.sortable!==!1,...p.resizable!==void 0&&{resizable:p.resizable},...p.wrap!==void 0&&{wrap:p.wrap},...z&&{cell:z}}})}return v.filter(S=>typeof S=="string"&&S.trim().length>0).map(S=>({header:C?.fields?.[S]?.label||S.charAt(0).toUpperCase()+S.slice(1).replace(/_/g," "),accessorKey:S}))}if(U){const S=D?.provider==="value"?D.items:[];if(S.length>0)return(V||Object.keys(S[0])).map(P=>({header:P.charAt(0).toUpperCase()+P.slice(1).replace(/_/g," "),accessorKey:P}))}if(!C)return[];const j=[];return(V||Object.keys(C.fields||{})).forEach(S=>{const p=C.fields?.[S];if(!p||p.permissions&&p.permissions.read===!1)return;const P=Ee.getCellRenderer(p.type),ue=["number","currency","percent"];j.push({header:p.label||S,accessorKey:S,...ue.includes(p.type)&&{align:"right"},cell:z=>a.jsx(P,{value:z,field:p}),sortable:p.sortable!==!1})}),j},[C,V,Y,D,U,h.handleClick,w]);if(E)return a.jsxs("div",{className:"p-3 sm:p-4 border border-red-300 bg-red-50 rounded-md",children:[a.jsx("h3",{className:"text-red-800 font-semibold",children:"Error loading grid"}),a.jsx("p",{className:"text-red-600 text-sm mt-1",children:E.message})]});if(u&&f.length===0)return a.jsxs("div",{className:"p-4 sm:p-8 text-center",children:[a.jsx("div",{className:"inline-block animate-spin rounded-full h-8 w-8 border-b-2 border-gray-900"}),a.jsx("p",{className:"mt-2 text-sm text-gray-600",children:"Loading grid..."})]});const oe=q(),B="operations"in t?t.operations:void 0,De=B&&(B.update||B.delete),ft=De?[...oe,{header:"Actions",accessorKey:"_actions",cell:(v,j)=>a.jsxs(F.DropdownMenu,{children:[a.jsx(F.DropdownMenuTrigger,{asChild:!0,children:a.jsxs(F.Button,{variant:"ghost",size:"icon",className:"h-8 w-8 min-h-[44px] min-w-[44px] sm:min-h-0 sm:min-w-0",children:[a.jsx(J.MoreVertical,{className:"h-4 w-4"}),a.jsx("span",{className:"sr-only",children:"Open menu"})]})}),a.jsxs(F.DropdownMenuContent,{align:"end",children:[B?.update&&e&&a.jsxs(F.DropdownMenuItem,{onClick:()=>e(j),children:[a.jsx(J.Edit,{className:"mr-2 h-4 w-4"}),"Edit"]}),B?.delete&&n&&a.jsxs(F.DropdownMenuItem,{onClick:()=>n(j),children:[a.jsx(J.Trash2,{className:"mr-2 h-4 w-4"}),"Delete"]})]})]}),sortable:!1}]:oe;let ve=!1;t.selection?.type?ve=t.selection.type==="none"?!1:t.selection.type:t.selectable!==void 0&&(ve=t.selectable);const ht=t.pagination!==void 0?!0:t.showPagination!==void 0?t.showPagination:!0,mt=t.pagination?.pageSize||t.pageSize||10,pt=t.searchableFields!==void 0?t.searchableFields.length>0:t.showSearch!==void 0?t.showSearch:!0,Ie={type:"data-table",caption:t.label||t.title,columns:ft,data:f,pagination:ht,pageSize:mt,searchable:pt,selectable:ve,sortable:!0,exportable:B?.export,rowActions:De,resizableColumns:t.resizable??t.resizableColumns??!0,reorderableColumns:t.reorderableColumns??!1,editable:t.editable??!1,className:t.className,cellClassName:"px-2 py-1.5 sm:px-3 sm:py-2 md:px-4 md:py-2.5",rowClassName:t.rowColor?(v,j)=>O(v):void 0,onSelectionChange:s,onRowClick:h.handleClick,onCellChange:o,onRowSave:l,onBatchSave:b},gt=v=>({...Ie,caption:void 0,data:v,pagination:!1,searchable:!1}),xe=t.label?`${t.label} Detail`:t.objectName?`${t.objectName.charAt(0).toUpperCase()+t.objectName.slice(1)} Detail`:"Record Detail";if(T&&f.length>0&&!ie){const v=q().filter(j=>j.accessorKey!=="_actions");return a.jsxs(a.Fragment,{children:[a.jsx("div",{className:"space-y-2 p-2",children:f.map((j,I)=>a.jsx("div",{className:"border rounded-lg p-3 bg-card hover:bg-accent/50 cursor-pointer transition-colors touch-manipulation",onClick:()=>h.handleClick(j),children:v.slice(0,4).map(S=>a.jsxs("div",{className:"flex justify-between items-center py-1",children:[a.jsx("span",{className:"text-xs text-muted-foreground",children:S.header}),a.jsx("span",{className:"text-sm font-medium truncate ml-2 text-right",children:S.cell?S.cell(j[S.accessorKey],j):String(j[S.accessorKey]??"—")})]},S.accessorKey))},j.id||j._id||I))}),h.isOverlay&&a.jsx(F.NavigationOverlay,{...h,title:xe,children:j=>a.jsx("div",{className:"space-y-3",children:Object.entries(j).map(([I,S])=>a.jsxs("div",{className:"flex flex-col",children:[a.jsx("span",{className:"text-xs font-medium text-muted-foreground uppercase tracking-wide",children:I.replace(/_/g," ")}),a.jsx("span",{className:"text-sm",children:String(S??"—")})]},I))})})]})}const Pe=ie?a.jsx("div",{className:"space-y-2",children:re.map(v=>a.jsxs("div",{className:"border rounded-md",children:[a.jsxs("button",{type:"button",className:"flex w-full items-center gap-2 px-3 py-2 text-sm font-medium text-left bg-muted/50 hover:bg-muted transition-colors",onClick:()=>N(v.key),children:[v.collapsed?a.jsx(J.ChevronRight,{className:"h-4 w-4 shrink-0"}):a.jsx(J.ChevronDown,{className:"h-4 w-4 shrink-0"}),a.jsx("span",{children:v.label}),a.jsx("span",{className:"ml-auto text-xs text-muted-foreground",children:v.rows.length})]}),!v.collapsed&&a.jsx(Z.SchemaRenderer,{schema:gt(v.rows)})]},v.key))}):a.jsx(Z.SchemaRenderer,{schema:Ie});return h.isOverlay&&h.mode==="split"?a.jsx(F.NavigationOverlay,{...h,title:xe,mainContent:Pe,children:v=>a.jsx("div",{className:"space-y-3",children:Object.entries(v).map(([j,I])=>a.jsxs("div",{className:"flex flex-col",children:[a.jsx("span",{className:"text-xs font-medium text-muted-foreground uppercase tracking-wide",children:j.replace(/_/g," ")}),a.jsx("span",{className:"text-sm",children:String(I??"—")})]},j))})}):a.jsxs("div",{ref:W,className:"relative h-full",children:[ee>0&&a.jsx("div",{className:"flex items-center justify-center text-xs text-muted-foreground",style:{height:ee},children:be?"Refreshing…":"Pull to refresh"}),Pe,h.isOverlay&&a.jsx(F.NavigationOverlay,{...h,title:xe,children:v=>a.jsx("div",{className:"space-y-3",children:Object.entries(v).map(([j,I])=>a.jsxs("div",{className:"flex flex-col",children:[a.jsx("span",{className:"text-xs font-medium text-muted-foreground uppercase tracking-wide",children:j.replace(/_/g," ")}),a.jsx("span",{className:"text-sm",children:String(I??"—")})]},j))})})]})};function Q(t,c,e){let n=e.initialDeps??[],s,i=!0;function o(){var l,b,m;let f;e.key&&((l=e.debug)!=null&&l.call(e))&&(f=Date.now());const y=t();if(!(y.length!==n.length||y.some((E,x)=>n[x]!==E)))return s;n=y;let d;if(e.key&&((b=e.debug)!=null&&b.call(e))&&(d=Date.now()),s=c(...y),e.key&&((m=e.debug)!=null&&m.call(e))){const E=Math.round((Date.now()-f)*100)/100,x=Math.round((Date.now()-d)*100)/100,C=x/16,A=(T,$)=>{for(T=String(T);T.length<$;)T=" "+T;return T};console.info(`%c⏱ ${A(x,5)} /${A(E,5)} ms`,`
7
7
  font-size: .6rem;
8
8
  font-weight: bold;
9
- color: hsl(${Math.max(0,Math.min(120-120*w,120))}deg 100% 31%);`,e?.key)}return e?.onChange&&!(i&&e.skipInitialOnChange)&&e.onChange(t),i=!1,t}return o.updateDeps=l=>{s=l},o}function xe(n,a){if(n===void 0)throw new Error("Unexpected undefined");return n}const ke=(n,a)=>Math.abs(n-a)<1.01,ze=(n,a,e)=>{let s;return function(...t){n.clearTimeout(s),s=n.setTimeout(()=>a.apply(this,t),e)}},Ee=n=>{const{offsetWidth:a,offsetHeight:e}=n;return{width:a,height:e}},Me=n=>n,Ie=n=>{const a=Math.max(n.startIndex-n.overscan,0),e=Math.min(n.endIndex+n.overscan,n.count-1),s=[];for(let t=a;t<=e;t++)s.push(t);return s},De=(n,a)=>{const e=n.scrollElement;if(!e)return;const s=n.targetWindow;if(!s)return;const t=o=>{const{width:l,height:p}=o;a({width:Math.round(l),height:Math.round(p)})};if(t(Ee(e)),!s.ResizeObserver)return()=>{};const i=new s.ResizeObserver(o=>{const l=()=>{const p=o[0];if(p?.borderBoxSize){const g=p.borderBoxSize[0];if(g){t({width:g.inlineSize,height:g.blockSize});return}}t(Ee(e))};n.options.useAnimationFrameWithResizeObserver?requestAnimationFrame(l):l()});return i.observe(e,{box:"border-box"}),()=>{i.unobserve(e)}},Se={passive:!0},je=typeof window>"u"?!0:"onscrollend"in window,Pe=(n,a)=>{const e=n.scrollElement;if(!e)return;const s=n.targetWindow;if(!s)return;let t=0;const i=n.options.useScrollendEvent&&je?()=>{}:ze(s,()=>{a(t,!1)},n.options.isScrollingResetDelay),o=m=>()=>{const{horizontal:E,isRtl:u}=n.options;t=E?e.scrollLeft*(u&&-1||1):e.scrollTop,i(),a(t,m)},l=o(!0),p=o(!1);e.addEventListener("scroll",l,Se);const g=n.options.useScrollendEvent&&je;return g&&e.addEventListener("scrollend",p,Se),()=>{e.removeEventListener("scroll",l),g&&e.removeEventListener("scrollend",p)}},Fe=(n,a,e)=>{if(a?.borderBoxSize){const s=a.borderBoxSize[0];if(s)return Math.round(s[e.options.horizontal?"inlineSize":"blockSize"])}return n[e.options.horizontal?"offsetWidth":"offsetHeight"]},$e=(n,{adjustments:a=0,behavior:e},s)=>{var t,i;const o=n+a;(i=(t=s.scrollElement)==null?void 0:t.scrollTo)==null||i.call(t,{[s.options.horizontal?"left":"top"]:o,behavior:e})};class We{constructor(a){this.unsubs=[],this.scrollElement=null,this.targetWindow=null,this.isScrolling=!1,this.currentScrollToIndex=null,this.measurementsCache=[],this.itemSizeCache=new Map,this.laneAssignments=new Map,this.pendingMeasuredCacheIndexes=[],this.prevLanes=void 0,this.lanesChangedFlag=!1,this.lanesSettling=!1,this.scrollRect=null,this.scrollOffset=null,this.scrollDirection=null,this.scrollAdjustments=0,this.elementsCache=new Map,this.observer=(()=>{let e=null;const s=()=>e||(!this.targetWindow||!this.targetWindow.ResizeObserver?null:e=new this.targetWindow.ResizeObserver(t=>{t.forEach(i=>{const o=()=>{this._measureElement(i.target,i)};this.options.useAnimationFrameWithResizeObserver?requestAnimationFrame(o):o()})}));return{disconnect:()=>{var t;(t=s())==null||t.disconnect(),e=null},observe:t=>{var i;return(i=s())==null?void 0:i.observe(t,{box:"border-box"})},unobserve:t=>{var i;return(i=s())==null?void 0:i.unobserve(t)}}})(),this.range=null,this.setOptions=e=>{Object.entries(e).forEach(([s,t])=>{typeof t>"u"&&delete e[s]}),this.options={debug:!1,initialOffset:0,overscan:1,paddingStart:0,paddingEnd:0,scrollPaddingStart:0,scrollPaddingEnd:0,horizontal:!1,getItemKey:Me,rangeExtractor:Ie,onChange:()=>{},measureElement:Fe,initialRect:{width:0,height:0},scrollMargin:0,gap:0,indexAttribute:"data-index",initialMeasurementsCache:[],lanes:1,isScrollingResetDelay:150,enabled:!0,isRtl:!1,useScrollendEvent:!1,useAnimationFrameWithResizeObserver:!1,...e}},this.notify=e=>{var s,t;(t=(s=this.options).onChange)==null||t.call(s,this,e)},this.maybeNotify=H(()=>(this.calculateRange(),[this.isScrolling,this.range?this.range.startIndex:null,this.range?this.range.endIndex:null]),e=>{this.notify(e)},{key:process.env.NODE_ENV!=="production"&&"maybeNotify",debug:()=>this.options.debug,initialDeps:[this.isScrolling,this.range?this.range.startIndex:null,this.range?this.range.endIndex:null]}),this.cleanup=()=>{this.unsubs.filter(Boolean).forEach(e=>e()),this.unsubs=[],this.observer.disconnect(),this.scrollElement=null,this.targetWindow=null},this._didMount=()=>()=>{this.cleanup()},this._willUpdate=()=>{var e;const s=this.options.enabled?this.options.getScrollElement():null;if(this.scrollElement!==s){if(this.cleanup(),!s){this.maybeNotify();return}this.scrollElement=s,this.scrollElement&&"ownerDocument"in this.scrollElement?this.targetWindow=this.scrollElement.ownerDocument.defaultView:this.targetWindow=((e=this.scrollElement)==null?void 0:e.window)??null,this.elementsCache.forEach(t=>{this.observer.observe(t)}),this.unsubs.push(this.options.observeElementRect(this,t=>{this.scrollRect=t,this.maybeNotify()})),this.unsubs.push(this.options.observeElementOffset(this,(t,i)=>{this.scrollAdjustments=0,this.scrollDirection=i?this.getScrollOffset()<t?"forward":"backward":null,this.scrollOffset=t,this.isScrolling=i,this.maybeNotify()})),this._scrollToOffset(this.getScrollOffset(),{adjustments:void 0,behavior:void 0})}},this.getSize=()=>this.options.enabled?(this.scrollRect=this.scrollRect??this.options.initialRect,this.scrollRect[this.options.horizontal?"width":"height"]):(this.scrollRect=null,0),this.getScrollOffset=()=>this.options.enabled?(this.scrollOffset=this.scrollOffset??(typeof this.options.initialOffset=="function"?this.options.initialOffset():this.options.initialOffset),this.scrollOffset):(this.scrollOffset=null,0),this.getFurthestMeasurement=(e,s)=>{const t=new Map,i=new Map;for(let o=s-1;o>=0;o--){const l=e[o];if(t.has(l.lane))continue;const p=i.get(l.lane);if(p==null||l.end>p.end?i.set(l.lane,l):l.end<p.end&&t.set(l.lane,!0),t.size===this.options.lanes)break}return i.size===this.options.lanes?Array.from(i.values()).sort((o,l)=>o.end===l.end?o.index-l.index:o.end-l.end)[0]:void 0},this.getMeasurementOptions=H(()=>[this.options.count,this.options.paddingStart,this.options.scrollMargin,this.options.getItemKey,this.options.enabled,this.options.lanes],(e,s,t,i,o,l)=>(this.prevLanes!==void 0&&this.prevLanes!==l&&(this.lanesChangedFlag=!0),this.prevLanes=l,this.pendingMeasuredCacheIndexes=[],{count:e,paddingStart:s,scrollMargin:t,getItemKey:i,enabled:o,lanes:l}),{key:!1}),this.getMeasurements=H(()=>[this.getMeasurementOptions(),this.itemSizeCache],({count:e,paddingStart:s,scrollMargin:t,getItemKey:i,enabled:o,lanes:l},p)=>{if(!o)return this.measurementsCache=[],this.itemSizeCache.clear(),this.laneAssignments.clear(),[];if(this.laneAssignments.size>e)for(const u of this.laneAssignments.keys())u>=e&&this.laneAssignments.delete(u);this.lanesChangedFlag&&(this.lanesChangedFlag=!1,this.lanesSettling=!0,this.measurementsCache=[],this.itemSizeCache.clear(),this.laneAssignments.clear(),this.pendingMeasuredCacheIndexes=[]),this.measurementsCache.length===0&&!this.lanesSettling&&(this.measurementsCache=this.options.initialMeasurementsCache,this.measurementsCache.forEach(u=>{this.itemSizeCache.set(u.key,u.size)}));const g=this.lanesSettling?0:this.pendingMeasuredCacheIndexes.length>0?Math.min(...this.pendingMeasuredCacheIndexes):0;this.pendingMeasuredCacheIndexes=[],this.lanesSettling&&this.measurementsCache.length===e&&(this.lanesSettling=!1);const m=this.measurementsCache.slice(0,g),E=new Array(l).fill(void 0);for(let u=0;u<g;u++){const b=m[u];b&&(E[b.lane]=u)}for(let u=g;u<e;u++){const b=i(u),_=this.laneAssignments.get(u);let v,w;if(_!==void 0&&this.options.lanes>1){v=_;const M=E[v],C=M!==void 0?m[M]:void 0;w=C?C.end+this.options.gap:s+t}else{const M=this.options.lanes===1?m[u-1]:this.getFurthestMeasurement(m,u);w=M?M.end+this.options.gap:s+t,v=M?M.lane:u%this.options.lanes,this.options.lanes>1&&this.laneAssignments.set(u,v)}const z=p.get(b),O=typeof z=="number"?z:this.options.estimateSize(u),F=w+O;m[u]={index:u,start:w,size:O,end:F,key:b,lane:v},E[v]=u}return this.measurementsCache=m,m},{key:process.env.NODE_ENV!=="production"&&"getMeasurements",debug:()=>this.options.debug}),this.calculateRange=H(()=>[this.getMeasurements(),this.getSize(),this.getScrollOffset(),this.options.lanes],(e,s,t,i)=>this.range=e.length>0&&s>0?Le({measurements:e,outerSize:s,scrollOffset:t,lanes:i}):null,{key:process.env.NODE_ENV!=="production"&&"calculateRange",debug:()=>this.options.debug}),this.getVirtualIndexes=H(()=>{let e=null,s=null;const t=this.calculateRange();return t&&(e=t.startIndex,s=t.endIndex),this.maybeNotify.updateDeps([this.isScrolling,e,s]),[this.options.rangeExtractor,this.options.overscan,this.options.count,e,s]},(e,s,t,i,o)=>i===null||o===null?[]:e({startIndex:i,endIndex:o,overscan:s,count:t}),{key:process.env.NODE_ENV!=="production"&&"getVirtualIndexes",debug:()=>this.options.debug}),this.indexFromElement=e=>{const s=this.options.indexAttribute,t=e.getAttribute(s);return t?parseInt(t,10):(console.warn(`Missing attribute name '${s}={index}' on measured element.`),-1)},this._measureElement=(e,s)=>{const t=this.indexFromElement(e),i=this.measurementsCache[t];if(!i)return;const o=i.key,l=this.elementsCache.get(o);l!==e&&(l&&this.observer.unobserve(l),this.observer.observe(e),this.elementsCache.set(o,e)),e.isConnected&&this.resizeItem(t,this.options.measureElement(e,s,this))},this.resizeItem=(e,s)=>{const t=this.measurementsCache[e];if(!t)return;const i=this.itemSizeCache.get(t.key)??t.size,o=s-i;o!==0&&((this.shouldAdjustScrollPositionOnItemSizeChange!==void 0?this.shouldAdjustScrollPositionOnItemSizeChange(t,o,this):t.start<this.getScrollOffset()+this.scrollAdjustments)&&(process.env.NODE_ENV!=="production"&&this.options.debug&&console.info("correction",o),this._scrollToOffset(this.getScrollOffset(),{adjustments:this.scrollAdjustments+=o,behavior:void 0})),this.pendingMeasuredCacheIndexes.push(t.index),this.itemSizeCache=new Map(this.itemSizeCache.set(t.key,s)),this.notify(!1))},this.measureElement=e=>{if(!e){this.elementsCache.forEach((s,t)=>{s.isConnected||(this.observer.unobserve(s),this.elementsCache.delete(t))});return}this._measureElement(e,void 0)},this.getVirtualItems=H(()=>[this.getVirtualIndexes(),this.getMeasurements()],(e,s)=>{const t=[];for(let i=0,o=e.length;i<o;i++){const l=e[i],p=s[l];t.push(p)}return t},{key:process.env.NODE_ENV!=="production"&&"getVirtualItems",debug:()=>this.options.debug}),this.getVirtualItemForOffset=e=>{const s=this.getMeasurements();if(s.length!==0)return xe(s[we(0,s.length-1,t=>xe(s[t]).start,e)])},this.getMaxScrollOffset=()=>{if(!this.scrollElement)return 0;if("scrollHeight"in this.scrollElement)return this.options.horizontal?this.scrollElement.scrollWidth-this.scrollElement.clientWidth:this.scrollElement.scrollHeight-this.scrollElement.clientHeight;{const e=this.scrollElement.document.documentElement;return this.options.horizontal?e.scrollWidth-this.scrollElement.innerWidth:e.scrollHeight-this.scrollElement.innerHeight}},this.getOffsetForAlignment=(e,s,t=0)=>{if(!this.scrollElement)return 0;const i=this.getSize(),o=this.getScrollOffset();s==="auto"&&(s=e>=o+i?"end":"start"),s==="center"?e+=(t-i)/2:s==="end"&&(e-=i);const l=this.getMaxScrollOffset();return Math.max(Math.min(l,e),0)},this.getOffsetForIndex=(e,s="auto")=>{e=Math.max(0,Math.min(e,this.options.count-1));const t=this.measurementsCache[e];if(!t)return;const i=this.getSize(),o=this.getScrollOffset();if(s==="auto")if(t.end>=o+i-this.options.scrollPaddingEnd)s="end";else if(t.start<=o+this.options.scrollPaddingStart)s="start";else return[o,s];if(s==="end"&&e===this.options.count-1)return[this.getMaxScrollOffset(),s];const l=s==="end"?t.end+this.options.scrollPaddingEnd:t.start-this.options.scrollPaddingStart;return[this.getOffsetForAlignment(l,s,t.size),s]},this.isDynamicMode=()=>this.elementsCache.size>0,this.scrollToOffset=(e,{align:s="start",behavior:t}={})=>{t==="smooth"&&this.isDynamicMode()&&console.warn("The `smooth` scroll behavior is not fully supported with dynamic size."),this._scrollToOffset(this.getOffsetForAlignment(e,s),{adjustments:void 0,behavior:t})},this.scrollToIndex=(e,{align:s="auto",behavior:t}={})=>{t==="smooth"&&this.isDynamicMode()&&console.warn("The `smooth` scroll behavior is not fully supported with dynamic size."),e=Math.max(0,Math.min(e,this.options.count-1)),this.currentScrollToIndex=e;let i=0;const o=10,l=g=>{if(!this.targetWindow)return;const m=this.getOffsetForIndex(e,g);if(!m){console.warn("Failed to get offset for index:",e);return}const[E,u]=m;this._scrollToOffset(E,{adjustments:void 0,behavior:t}),this.targetWindow.requestAnimationFrame(()=>{const b=()=>{if(this.currentScrollToIndex!==e)return;const _=this.getScrollOffset(),v=this.getOffsetForIndex(e,u);if(!v){console.warn("Failed to get offset for index:",e);return}ke(v[0],_)||p(u)};this.isDynamicMode()?this.targetWindow.requestAnimationFrame(b):b()})},p=g=>{this.targetWindow&&this.currentScrollToIndex===e&&(i++,i<o?(process.env.NODE_ENV!=="production"&&this.options.debug&&console.info("Schedule retry",i,o),this.targetWindow.requestAnimationFrame(()=>l(g))):console.warn(`Failed to scroll to index ${e} after ${o} attempts.`))};l(s)},this.scrollBy=(e,{behavior:s}={})=>{s==="smooth"&&this.isDynamicMode()&&console.warn("The `smooth` scroll behavior is not fully supported with dynamic size."),this._scrollToOffset(this.getScrollOffset()+e,{adjustments:void 0,behavior:s})},this.getTotalSize=()=>{var e;const s=this.getMeasurements();let t;if(s.length===0)t=this.options.paddingStart;else if(this.options.lanes===1)t=((e=s[s.length-1])==null?void 0:e.end)??0;else{const i=Array(this.options.lanes).fill(null);let o=s.length-1;for(;o>=0&&i.some(l=>l===null);){const l=s[o];i[l.lane]===null&&(i[l.lane]=l.end),o--}t=Math.max(...i.filter(l=>l!==null))}return Math.max(t-this.options.scrollMargin+this.options.paddingEnd,0)},this._scrollToOffset=(e,{adjustments:s,behavior:t})=>{this.options.scrollToFn(e,{behavior:t,adjustments:s},this)},this.measure=()=>{this.itemSizeCache=new Map,this.laneAssignments=new Map,this.notify(!1)},this.setOptions(a)}}const we=(n,a,e,s)=>{for(;n<=a;){const t=(n+a)/2|0,i=e(t);if(i<s)n=t+1;else if(i>s)a=t-1;else return t}return n>0?n-1:0};function Le({measurements:n,outerSize:a,scrollOffset:e,lanes:s}){const t=n.length-1,i=p=>n[p].start;if(n.length<=s)return{startIndex:0,endIndex:t};let o=we(0,t,i,e),l=o;if(s===1)for(;l<t&&n[l].end<e+a;)l++;else if(s>1){const p=Array(s).fill(0);for(;l<t&&p.some(m=>m<e+a);){const m=n[l];p[m.lane]=m.end,l++}const g=Array(s).fill(e+a);for(;o>=0&&g.some(m=>m>=e);){const m=n[o];g[m.lane]=m.start,o--}o=Math.max(0,o-o%s),l=Math.min(t,l+(s-1-l%s))}return{startIndex:o,endIndex:l}}const Oe=typeof document<"u"?le.useLayoutEffect:le.useEffect;function Ve({useFlushSync:n=!0,...a}){const e=le.useReducer(()=>({}),{})[1],s={...a,onChange:(i,o)=>{var l;n&&o?Ce.flushSync(e):e(),(l=a.onChange)==null||l.call(a,i,o)}},[t]=le.useState(()=>new We(s));return t.setOptions(s),Oe(()=>t._didMount(),[]),Oe(()=>t._willUpdate()),t}function qe(n){return Ve({observeElementRect:De,observeElementOffset:Pe,scrollToFn:$e,...n})}const Ye=({data:n,columns:a,rowHeight:e=40,height:s=600,className:t="",headerClassName:i="",rowClassName:o,onRowClick:l,overscan:p=5})=>{const g=R.useRef(null),m=qe({count:n.length,getScrollElement:()=>g.current,estimateSize:()=>e,overscan:p}),E=m.getVirtualItems();return c.jsxs("div",{className:t,children:[c.jsx("div",{className:`grid border-b sticky top-0 bg-background z-10 ${i}`,style:{gridTemplateColumns:a.map(u=>u.width||"1fr").join(" ")},children:a.map((u,b)=>c.jsx("div",{className:`px-4 py-2 font-semibold text-sm ${u.align==="center"?"text-center":u.align==="right"?"text-right":"text-left"}`,children:u.header},b))}),c.jsx("div",{ref:g,className:"overflow-auto",style:{height:typeof s=="number"?`${s}px`:s,contain:"strict"},children:c.jsx("div",{style:{height:`${m.getTotalSize()}px`,width:"100%",position:"relative"},children:E.map(u=>{const b=n[u.index],_=typeof o=="function"?o(b,u.index):o||"";return c.jsx("div",{className:`grid border-b hover:bg-muted/50 cursor-pointer ${_}`,style:{position:"absolute",top:0,left:0,width:"100%",height:`${u.size}px`,transform:`translateY(${u.start}px)`,gridTemplateColumns:a.map(v=>v.width||"1fr").join(" ")},onClick:()=>l?.(b,u.index),children:a.map((v,w)=>{const z=b[v.accessorKey],O=v.cell?v.cell(z,b):z;return c.jsx("div",{className:`px-4 py-2 text-sm flex items-center ${v.align==="center"?"text-center justify-center":v.align==="right"?"text-right justify-end":"text-left justify-start"}`,children:O},w)})},u.key)})})}),c.jsxs("div",{className:"px-4 py-2 text-xs text-muted-foreground border-t",children:["Showing ",E.length," of ",n.length," rows (virtual scrolling enabled)"]})]})},fe=({schema:n,...a})=>{const{dataSource:e}=B.useSchemaContext()||{};return c.jsx(ye,{schema:n,dataSource:e,...a})};he.ComponentRegistry.register("object-grid",fe,{namespace:"plugin-grid",label:"Object Grid",category:"plugin",inputs:[{name:"objectName",type:"string",label:"Object Name",required:!0},{name:"columns",type:"array",label:"Columns"},{name:"filters",type:"array",label:"Filters"}]}),he.ComponentRegistry.register("grid",fe,{namespace:"view",label:"Data Grid",category:"view",inputs:[{name:"objectName",type:"string",label:"Object Name",required:!0},{name:"columns",type:"array",label:"Columns"},{name:"filters",type:"array",label:"Filters"}]}),A.ObjectGrid=ye,A.ObjectGridRenderer=fe,A.VirtualGrid=Ye,Object.defineProperty(A,Symbol.toStringTag,{value:"Module"})}));
9
+ color: hsl(${Math.max(0,Math.min(120-120*C,120))}deg 100% 31%);`,e?.key)}return e?.onChange&&!(i&&e.skipInitialOnChange)&&e.onChange(s),i=!1,s}return o.updateDeps=l=>{n=l},o}function ke(t,c){if(t===void 0)throw new Error("Unexpected undefined");return t}const Ze=(t,c)=>Math.abs(t-c)<1.01,Qe=(t,c,e)=>{let n;return function(...s){t.clearTimeout(n),n=t.setTimeout(()=>c.apply(this,s),e)}},Te=t=>{const{offsetWidth:c,offsetHeight:e}=t;return{width:c,height:e}},et=t=>t,tt=t=>{const c=Math.max(t.startIndex-t.overscan,0),e=Math.min(t.endIndex+t.overscan,t.count-1),n=[];for(let s=c;s<=e;s++)n.push(s);return n},nt=(t,c)=>{const e=t.scrollElement;if(!e)return;const n=t.targetWindow;if(!n)return;const s=o=>{const{width:l,height:b}=o;c({width:Math.round(l),height:Math.round(b)})};if(s(Te(e)),!n.ResizeObserver)return()=>{};const i=new n.ResizeObserver(o=>{const l=()=>{const b=o[0];if(b?.borderBoxSize){const m=b.borderBoxSize[0];if(m){s({width:m.inlineSize,height:m.blockSize});return}}s(Te(e))};t.options.useAnimationFrameWithResizeObserver?requestAnimationFrame(l):l()});return i.observe(e,{box:"border-box"}),()=>{i.unobserve(e)}},Ae={passive:!0},Me=typeof window>"u"?!0:"onscrollend"in window,st=(t,c)=>{const e=t.scrollElement;if(!e)return;const n=t.targetWindow;if(!n)return;let s=0;const i=t.options.useScrollendEvent&&Me?()=>{}:Qe(n,()=>{c(s,!1)},t.options.isScrollingResetDelay),o=f=>()=>{const{horizontal:y,isRtl:u}=t.options;s=y?e.scrollLeft*(u&&-1||1):e.scrollTop,i(),c(s,f)},l=o(!0),b=o(!1);e.addEventListener("scroll",l,Ae);const m=t.options.useScrollendEvent&&Me;return m&&e.addEventListener("scrollend",b,Ae),()=>{e.removeEventListener("scroll",l),m&&e.removeEventListener("scrollend",b)}},rt=(t,c,e)=>{if(c?.borderBoxSize){const n=c.borderBoxSize[0];if(n)return Math.round(n[e.options.horizontal?"inlineSize":"blockSize"])}return t[e.options.horizontal?"offsetWidth":"offsetHeight"]},it=(t,{adjustments:c=0,behavior:e},n)=>{var s,i;const o=t+c;(i=(s=n.scrollElement)==null?void 0:s.scrollTo)==null||i.call(s,{[n.options.horizontal?"left":"top"]:o,behavior:e})};class ot{constructor(c){this.unsubs=[],this.scrollElement=null,this.targetWindow=null,this.isScrolling=!1,this.currentScrollToIndex=null,this.measurementsCache=[],this.itemSizeCache=new Map,this.laneAssignments=new Map,this.pendingMeasuredCacheIndexes=[],this.prevLanes=void 0,this.lanesChangedFlag=!1,this.lanesSettling=!1,this.scrollRect=null,this.scrollOffset=null,this.scrollDirection=null,this.scrollAdjustments=0,this.elementsCache=new Map,this.observer=(()=>{let e=null;const n=()=>e||(!this.targetWindow||!this.targetWindow.ResizeObserver?null:e=new this.targetWindow.ResizeObserver(s=>{s.forEach(i=>{const o=()=>{this._measureElement(i.target,i)};this.options.useAnimationFrameWithResizeObserver?requestAnimationFrame(o):o()})}));return{disconnect:()=>{var s;(s=n())==null||s.disconnect(),e=null},observe:s=>{var i;return(i=n())==null?void 0:i.observe(s,{box:"border-box"})},unobserve:s=>{var i;return(i=n())==null?void 0:i.unobserve(s)}}})(),this.range=null,this.setOptions=e=>{Object.entries(e).forEach(([n,s])=>{typeof s>"u"&&delete e[n]}),this.options={debug:!1,initialOffset:0,overscan:1,paddingStart:0,paddingEnd:0,scrollPaddingStart:0,scrollPaddingEnd:0,horizontal:!1,getItemKey:et,rangeExtractor:tt,onChange:()=>{},measureElement:rt,initialRect:{width:0,height:0},scrollMargin:0,gap:0,indexAttribute:"data-index",initialMeasurementsCache:[],lanes:1,isScrollingResetDelay:150,enabled:!0,isRtl:!1,useScrollendEvent:!1,useAnimationFrameWithResizeObserver:!1,...e}},this.notify=e=>{var n,s;(s=(n=this.options).onChange)==null||s.call(n,this,e)},this.maybeNotify=Q(()=>(this.calculateRange(),[this.isScrolling,this.range?this.range.startIndex:null,this.range?this.range.endIndex:null]),e=>{this.notify(e)},{key:process.env.NODE_ENV!=="production"&&"maybeNotify",debug:()=>this.options.debug,initialDeps:[this.isScrolling,this.range?this.range.startIndex:null,this.range?this.range.endIndex:null]}),this.cleanup=()=>{this.unsubs.filter(Boolean).forEach(e=>e()),this.unsubs=[],this.observer.disconnect(),this.scrollElement=null,this.targetWindow=null},this._didMount=()=>()=>{this.cleanup()},this._willUpdate=()=>{var e;const n=this.options.enabled?this.options.getScrollElement():null;if(this.scrollElement!==n){if(this.cleanup(),!n){this.maybeNotify();return}this.scrollElement=n,this.scrollElement&&"ownerDocument"in this.scrollElement?this.targetWindow=this.scrollElement.ownerDocument.defaultView:this.targetWindow=((e=this.scrollElement)==null?void 0:e.window)??null,this.elementsCache.forEach(s=>{this.observer.observe(s)}),this.unsubs.push(this.options.observeElementRect(this,s=>{this.scrollRect=s,this.maybeNotify()})),this.unsubs.push(this.options.observeElementOffset(this,(s,i)=>{this.scrollAdjustments=0,this.scrollDirection=i?this.getScrollOffset()<s?"forward":"backward":null,this.scrollOffset=s,this.isScrolling=i,this.maybeNotify()})),this._scrollToOffset(this.getScrollOffset(),{adjustments:void 0,behavior:void 0})}},this.getSize=()=>this.options.enabled?(this.scrollRect=this.scrollRect??this.options.initialRect,this.scrollRect[this.options.horizontal?"width":"height"]):(this.scrollRect=null,0),this.getScrollOffset=()=>this.options.enabled?(this.scrollOffset=this.scrollOffset??(typeof this.options.initialOffset=="function"?this.options.initialOffset():this.options.initialOffset),this.scrollOffset):(this.scrollOffset=null,0),this.getFurthestMeasurement=(e,n)=>{const s=new Map,i=new Map;for(let o=n-1;o>=0;o--){const l=e[o];if(s.has(l.lane))continue;const b=i.get(l.lane);if(b==null||l.end>b.end?i.set(l.lane,l):l.end<b.end&&s.set(l.lane,!0),s.size===this.options.lanes)break}return i.size===this.options.lanes?Array.from(i.values()).sort((o,l)=>o.end===l.end?o.index-l.index:o.end-l.end)[0]:void 0},this.getMeasurementOptions=Q(()=>[this.options.count,this.options.paddingStart,this.options.scrollMargin,this.options.getItemKey,this.options.enabled,this.options.lanes],(e,n,s,i,o,l)=>(this.prevLanes!==void 0&&this.prevLanes!==l&&(this.lanesChangedFlag=!0),this.prevLanes=l,this.pendingMeasuredCacheIndexes=[],{count:e,paddingStart:n,scrollMargin:s,getItemKey:i,enabled:o,lanes:l}),{key:!1}),this.getMeasurements=Q(()=>[this.getMeasurementOptions(),this.itemSizeCache],({count:e,paddingStart:n,scrollMargin:s,getItemKey:i,enabled:o,lanes:l},b)=>{if(!o)return this.measurementsCache=[],this.itemSizeCache.clear(),this.laneAssignments.clear(),[];if(this.laneAssignments.size>e)for(const u of this.laneAssignments.keys())u>=e&&this.laneAssignments.delete(u);this.lanesChangedFlag&&(this.lanesChangedFlag=!1,this.lanesSettling=!0,this.measurementsCache=[],this.itemSizeCache.clear(),this.laneAssignments.clear(),this.pendingMeasuredCacheIndexes=[]),this.measurementsCache.length===0&&!this.lanesSettling&&(this.measurementsCache=this.options.initialMeasurementsCache,this.measurementsCache.forEach(u=>{this.itemSizeCache.set(u.key,u.size)}));const m=this.lanesSettling?0:this.pendingMeasuredCacheIndexes.length>0?Math.min(...this.pendingMeasuredCacheIndexes):0;this.pendingMeasuredCacheIndexes=[],this.lanesSettling&&this.measurementsCache.length===e&&(this.lanesSettling=!1);const f=this.measurementsCache.slice(0,m),y=new Array(l).fill(void 0);for(let u=0;u<m;u++){const d=f[u];d&&(y[d.lane]=u)}for(let u=m;u<e;u++){const d=i(u),E=this.laneAssignments.get(u);let x,C;if(E!==void 0&&this.options.lanes>1){x=E;const R=y[x],K=R!==void 0?f[R]:void 0;C=K?K.end+this.options.gap:n+s}else{const R=this.options.lanes===1?f[u-1]:this.getFurthestMeasurement(f,u);C=R?R.end+this.options.gap:n+s,x=R?R.lane:u%this.options.lanes,this.options.lanes>1&&this.laneAssignments.set(u,x)}const A=b.get(d),T=typeof A=="number"?A:this.options.estimateSize(u),$=C+T;f[u]={index:u,start:C,size:T,end:$,key:d,lane:x},y[x]=u}return this.measurementsCache=f,f},{key:process.env.NODE_ENV!=="production"&&"getMeasurements",debug:()=>this.options.debug}),this.calculateRange=Q(()=>[this.getMeasurements(),this.getSize(),this.getScrollOffset(),this.options.lanes],(e,n,s,i)=>this.range=e.length>0&&n>0?lt({measurements:e,outerSize:n,scrollOffset:s,lanes:i}):null,{key:process.env.NODE_ENV!=="production"&&"calculateRange",debug:()=>this.options.debug}),this.getVirtualIndexes=Q(()=>{let e=null,n=null;const s=this.calculateRange();return s&&(e=s.startIndex,n=s.endIndex),this.maybeNotify.updateDeps([this.isScrolling,e,n]),[this.options.rangeExtractor,this.options.overscan,this.options.count,e,n]},(e,n,s,i,o)=>i===null||o===null?[]:e({startIndex:i,endIndex:o,overscan:n,count:s}),{key:process.env.NODE_ENV!=="production"&&"getVirtualIndexes",debug:()=>this.options.debug}),this.indexFromElement=e=>{const n=this.options.indexAttribute,s=e.getAttribute(n);return s?parseInt(s,10):(console.warn(`Missing attribute name '${n}={index}' on measured element.`),-1)},this._measureElement=(e,n)=>{const s=this.indexFromElement(e),i=this.measurementsCache[s];if(!i)return;const o=i.key,l=this.elementsCache.get(o);l!==e&&(l&&this.observer.unobserve(l),this.observer.observe(e),this.elementsCache.set(o,e)),e.isConnected&&this.resizeItem(s,this.options.measureElement(e,n,this))},this.resizeItem=(e,n)=>{const s=this.measurementsCache[e];if(!s)return;const i=this.itemSizeCache.get(s.key)??s.size,o=n-i;o!==0&&((this.shouldAdjustScrollPositionOnItemSizeChange!==void 0?this.shouldAdjustScrollPositionOnItemSizeChange(s,o,this):s.start<this.getScrollOffset()+this.scrollAdjustments)&&(process.env.NODE_ENV!=="production"&&this.options.debug&&console.info("correction",o),this._scrollToOffset(this.getScrollOffset(),{adjustments:this.scrollAdjustments+=o,behavior:void 0})),this.pendingMeasuredCacheIndexes.push(s.index),this.itemSizeCache=new Map(this.itemSizeCache.set(s.key,n)),this.notify(!1))},this.measureElement=e=>{if(!e){this.elementsCache.forEach((n,s)=>{n.isConnected||(this.observer.unobserve(n),this.elementsCache.delete(s))});return}this._measureElement(e,void 0)},this.getVirtualItems=Q(()=>[this.getVirtualIndexes(),this.getMeasurements()],(e,n)=>{const s=[];for(let i=0,o=e.length;i<o;i++){const l=e[i],b=n[l];s.push(b)}return s},{key:process.env.NODE_ENV!=="production"&&"getVirtualItems",debug:()=>this.options.debug}),this.getVirtualItemForOffset=e=>{const n=this.getMeasurements();if(n.length!==0)return ke(n[ze(0,n.length-1,s=>ke(n[s]).start,e)])},this.getMaxScrollOffset=()=>{if(!this.scrollElement)return 0;if("scrollHeight"in this.scrollElement)return this.options.horizontal?this.scrollElement.scrollWidth-this.scrollElement.clientWidth:this.scrollElement.scrollHeight-this.scrollElement.clientHeight;{const e=this.scrollElement.document.documentElement;return this.options.horizontal?e.scrollWidth-this.scrollElement.innerWidth:e.scrollHeight-this.scrollElement.innerHeight}},this.getOffsetForAlignment=(e,n,s=0)=>{if(!this.scrollElement)return 0;const i=this.getSize(),o=this.getScrollOffset();n==="auto"&&(n=e>=o+i?"end":"start"),n==="center"?e+=(s-i)/2:n==="end"&&(e-=i);const l=this.getMaxScrollOffset();return Math.max(Math.min(l,e),0)},this.getOffsetForIndex=(e,n="auto")=>{e=Math.max(0,Math.min(e,this.options.count-1));const s=this.measurementsCache[e];if(!s)return;const i=this.getSize(),o=this.getScrollOffset();if(n==="auto")if(s.end>=o+i-this.options.scrollPaddingEnd)n="end";else if(s.start<=o+this.options.scrollPaddingStart)n="start";else return[o,n];if(n==="end"&&e===this.options.count-1)return[this.getMaxScrollOffset(),n];const l=n==="end"?s.end+this.options.scrollPaddingEnd:s.start-this.options.scrollPaddingStart;return[this.getOffsetForAlignment(l,n,s.size),n]},this.isDynamicMode=()=>this.elementsCache.size>0,this.scrollToOffset=(e,{align:n="start",behavior:s}={})=>{s==="smooth"&&this.isDynamicMode()&&console.warn("The `smooth` scroll behavior is not fully supported with dynamic size."),this._scrollToOffset(this.getOffsetForAlignment(e,n),{adjustments:void 0,behavior:s})},this.scrollToIndex=(e,{align:n="auto",behavior:s}={})=>{s==="smooth"&&this.isDynamicMode()&&console.warn("The `smooth` scroll behavior is not fully supported with dynamic size."),e=Math.max(0,Math.min(e,this.options.count-1)),this.currentScrollToIndex=e;let i=0;const o=10,l=m=>{if(!this.targetWindow)return;const f=this.getOffsetForIndex(e,m);if(!f){console.warn("Failed to get offset for index:",e);return}const[y,u]=f;this._scrollToOffset(y,{adjustments:void 0,behavior:s}),this.targetWindow.requestAnimationFrame(()=>{const d=()=>{if(this.currentScrollToIndex!==e)return;const E=this.getScrollOffset(),x=this.getOffsetForIndex(e,u);if(!x){console.warn("Failed to get offset for index:",e);return}Ze(x[0],E)||b(u)};this.isDynamicMode()?this.targetWindow.requestAnimationFrame(d):d()})},b=m=>{this.targetWindow&&this.currentScrollToIndex===e&&(i++,i<o?(process.env.NODE_ENV!=="production"&&this.options.debug&&console.info("Schedule retry",i,o),this.targetWindow.requestAnimationFrame(()=>l(m))):console.warn(`Failed to scroll to index ${e} after ${o} attempts.`))};l(n)},this.scrollBy=(e,{behavior:n}={})=>{n==="smooth"&&this.isDynamicMode()&&console.warn("The `smooth` scroll behavior is not fully supported with dynamic size."),this._scrollToOffset(this.getScrollOffset()+e,{adjustments:void 0,behavior:n})},this.getTotalSize=()=>{var e;const n=this.getMeasurements();let s;if(n.length===0)s=this.options.paddingStart;else if(this.options.lanes===1)s=((e=n[n.length-1])==null?void 0:e.end)??0;else{const i=Array(this.options.lanes).fill(null);let o=n.length-1;for(;o>=0&&i.some(l=>l===null);){const l=n[o];i[l.lane]===null&&(i[l.lane]=l.end),o--}s=Math.max(...i.filter(l=>l!==null))}return Math.max(s-this.options.scrollMargin+this.options.paddingEnd,0)},this._scrollToOffset=(e,{adjustments:n,behavior:s})=>{this.options.scrollToFn(e,{behavior:s,adjustments:n},this)},this.measure=()=>{this.itemSizeCache=new Map,this.laneAssignments=new Map,this.notify(!1)},this.setOptions(c)}}const ze=(t,c,e,n)=>{for(;t<=c;){const s=(t+c)/2|0,i=e(s);if(i<n)t=s+1;else if(i>n)c=s-1;else return s}return t>0?t-1:0};function lt({measurements:t,outerSize:c,scrollOffset:e,lanes:n}){const s=t.length-1,i=b=>t[b].start;if(t.length<=n)return{startIndex:0,endIndex:s};let o=ze(0,s,i,e),l=o;if(n===1)for(;l<s&&t[l].end<e+c;)l++;else if(n>1){const b=Array(n).fill(0);for(;l<s&&b.some(f=>f<e+c);){const f=t[l];b[f.lane]=f.end,l++}const m=Array(n).fill(e+c);for(;o>=0&&m.some(f=>f>=e);){const f=t[o];m[f.lane]=f.start,o--}o=Math.max(0,o-o%n),l=Math.min(s,l+(n-1-l%n))}return{startIndex:o,endIndex:l}}const Re=typeof document<"u"?me.useLayoutEffect:me.useEffect;function at({useFlushSync:t=!0,...c}){const e=me.useReducer(()=>({}),{})[1],n={...c,onChange:(i,o)=>{var l;t&&o?Le.flushSync(e):e(),(l=c.onChange)==null||l.call(c,i,o)}},[s]=me.useState(()=>new ot(n));return s.setOptions(n),Re(()=>s._didMount(),[]),Re(()=>s._willUpdate()),s}function ct(t){return at({observeElementRect:nt,observeElementOffset:st,scrollToFn:it,...t})}const ut=({data:t,columns:c,rowHeight:e=40,height:n=600,className:s="",headerClassName:i="",rowClassName:o,onRowClick:l,overscan:b=5})=>{const m=g.useRef(null),f=ct({count:t.length,getScrollElement:()=>m.current,estimateSize:()=>e,overscan:b}),y=f.getVirtualItems();return a.jsxs("div",{className:s,children:[a.jsx("div",{className:`grid border-b sticky top-0 bg-background z-10 ${i}`,style:{gridTemplateColumns:c.map(u=>u.width||"1fr").join(" ")},children:c.map((u,d)=>a.jsx("div",{className:`px-4 py-2 font-semibold text-sm ${u.align==="center"?"text-center":u.align==="right"?"text-right":"text-left"}`,children:u.header},d))}),a.jsx("div",{ref:m,className:"overflow-auto",style:{height:typeof n=="number"?`${n}px`:n,contain:"strict"},children:a.jsx("div",{style:{height:`${f.getTotalSize()}px`,width:"100%",position:"relative"},children:y.map(u=>{const d=t[u.index],E=typeof o=="function"?o(d,u.index):o||"";return a.jsx("div",{className:`grid border-b hover:bg-muted/50 cursor-pointer ${E}`,style:{position:"absolute",top:0,left:0,width:"100%",height:`${u.size}px`,transform:`translateY(${u.start}px)`,gridTemplateColumns:c.map(x=>x.width||"1fr").join(" ")},onClick:()=>l?.(d,u.index),children:c.map((x,C)=>{const A=d[x.accessorKey],T=x.cell?x.cell(A,d):A;return a.jsx("div",{className:`px-4 py-2 text-sm flex items-center ${x.align==="center"?"text-center justify-center":x.align==="right"?"text-right justify-end":"text-left justify-start"}`,children:T},C)})},u.key)})})}),a.jsxs("div",{className:"px-4 py-2 text-xs text-muted-foreground border-t",children:["Showing ",y.length," of ",t.length," rows (virtual scrolling enabled)"]})]})};function dt({value:t,onSave:c,onCancel:e,validate:n,type:s="text",placeholder:i,editing:o=!1,className:l,disabled:b=!1}){const[m,f]=g.useState(o),[y,u]=g.useState(String(t??"")),[d,E]=g.useState(),[x,C]=g.useState(!1),A=g.useRef(null);g.useEffect(()=>{m||u(String(t??""))},[t,m]),g.useEffect(()=>{m&&A.current&&(A.current.focus(),A.current.select())},[m]);const T=g.useCallback(()=>{b||(f(!0),u(String(t??"")),E(void 0))},[b,t]),$=g.useCallback(()=>{f(!1),u(String(t??"")),E(void 0),e?.()},[t,e]),R=g.useCallback(async()=>{if(n){const W=n(y);if(W){E(W);return}}const M=s==="number"?Number(y):y;C(!0);try{const W=await c(M);if(typeof W=="string"){E(W),C(!1);return}f(!1),E(void 0)}catch(W){E(W?.message||"Save failed")}finally{C(!1)}},[y,n,s,c]),K=g.useCallback(M=>{M.key==="Enter"?(M.preventDefault(),R()):M.key==="Escape"&&(M.preventDefault(),$())},[R,$]);return m?a.jsxs("div",{"data-slot":"inline-editing",className:F.cn("relative flex items-center gap-1",l),children:[a.jsxs("div",{className:"flex-1 relative",children:[a.jsx("input",{ref:A,"data-slot":"inline-editing-input",type:s,value:y,onChange:M=>{u(M.target.value),d&&E(void 0)},onKeyDown:K,placeholder:i,disabled:x,"aria-invalid":!!d,"aria-describedby":d?"inline-editing-error":void 0,className:F.cn("w-full rounded border px-2 py-1 text-sm outline-none transition-colors","focus:ring-2 focus:ring-ring focus:border-input",d?"border-destructive focus:ring-destructive/30":"border-input",x&&"opacity-50")}),d&&a.jsx("p",{id:"inline-editing-error","data-slot":"inline-editing-error",className:"absolute left-0 top-full mt-0.5 text-xs text-destructive",role:"alert",children:d})]}),a.jsx("button",{"data-slot":"inline-editing-save",type:"button",onClick:R,disabled:x,"aria-label":"Save",className:F.cn("inline-flex h-6 w-6 items-center justify-center rounded text-primary hover:bg-primary/10 transition-colors",x&&"opacity-50 cursor-not-allowed"),children:a.jsx(J.Check,{className:"h-3.5 w-3.5"})}),a.jsx("button",{"data-slot":"inline-editing-cancel",type:"button",onClick:$,disabled:x,"aria-label":"Cancel",className:"inline-flex h-6 w-6 items-center justify-center rounded text-muted-foreground hover:bg-destructive/10 hover:text-destructive transition-colors",children:a.jsx(J.X,{className:"h-3.5 w-3.5"})})]}):a.jsx("div",{"data-slot":"inline-editing",className:F.cn("group relative cursor-pointer rounded px-2 py-1 hover:bg-muted/50 transition-colors min-h-[1.75rem] flex items-center",b&&"cursor-default opacity-60",l),onClick:T,role:"button",tabIndex:b?-1:0,onKeyDown:M=>{(M.key==="Enter"||M.key===" ")&&(M.preventDefault(),T())},"aria-label":`Edit value: ${String(t??"")}`,children:a.jsx("span",{"data-slot":"inline-editing-display",className:"truncate text-sm",children:t!=null&&String(t)!==""?String(t):a.jsx("span",{className:"text-muted-foreground italic",children:i||"Click to edit"})})})}const ge=({schema:t,...c})=>{const{dataSource:e}=Z.useSchemaContext()||{};return a.jsx(_e,{schema:t,dataSource:e,...c})};ye.ComponentRegistry.register("object-grid",ge,{namespace:"plugin-grid",label:"Object Grid",category:"plugin",inputs:[{name:"objectName",type:"string",label:"Object Name",required:!0},{name:"columns",type:"array",label:"Columns"},{name:"filters",type:"array",label:"Filters"}]}),ye.ComponentRegistry.register("grid",ge,{namespace:"view",label:"Data Grid",category:"view",inputs:[{name:"objectName",type:"string",label:"Object Name",required:!0},{name:"columns",type:"array",label:"Columns"},{name:"filters",type:"array",label:"Filters"}]}),k.InlineEditing=dt,k.ObjectGrid=_e,k.ObjectGridRenderer=ge,k.VirtualGrid=ut,k.useGroupedData=Oe,k.useRowColor=Ce,Object.defineProperty(k,Symbol.toStringTag,{value:"Module"})}));
@@ -0,0 +1,28 @@
1
+ /**
2
+ * ObjectUI
3
+ * Copyright (c) 2024-present ObjectStack Inc.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+ export interface InlineEditingProps {
9
+ /** Current cell value */
10
+ value: any;
11
+ /** Called with new value on save. Return a string to signal validation error. */
12
+ onSave: (newValue: any) => void | string | Promise<void | string>;
13
+ /** Called when editing is cancelled */
14
+ onCancel?: () => void;
15
+ /** Validate before saving. Return error message or undefined. */
16
+ validate?: (value: any) => string | undefined;
17
+ /** Field type hint used to determine input type */
18
+ type?: 'text' | 'number' | 'email';
19
+ /** Placeholder text */
20
+ placeholder?: string;
21
+ /** Whether the cell is in editing mode initially */
22
+ editing?: boolean;
23
+ /** Additional class names */
24
+ className?: string;
25
+ /** Whether this field is disabled */
26
+ disabled?: boolean;
27
+ }
28
+ export declare function InlineEditing({ value, onSave, onCancel, validate, type, placeholder, editing: editingProp, className, disabled, }: InlineEditingProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,25 @@
1
+ import { StoryObj } from '@storybook/react';
2
+ declare const meta: {
3
+ title: string;
4
+ component: import('react').ForwardRefExoticComponent<Omit<{
5
+ schema: import('../../core/src').SchemaNode;
6
+ } & Record<string, any>, "ref"> & import('react').RefAttributes<any>>;
7
+ parameters: {
8
+ layout: string;
9
+ };
10
+ tags: string[];
11
+ argTypes: {
12
+ schema: {
13
+ table: {
14
+ disable: true;
15
+ };
16
+ };
17
+ };
18
+ };
19
+ export default meta;
20
+ type Story = StoryObj<typeof meta>;
21
+ export declare const EmptyData: Story;
22
+ export declare const SingleRow: Story;
23
+ export declare const ManyColumns: Story;
24
+ export declare const LongCellValues: Story;
25
+ export declare const NullAndUndefinedValues: Story;
@@ -0,0 +1,33 @@
1
+ import { StoryObj } from '@storybook/react';
2
+ declare const meta: {
3
+ title: string;
4
+ component: import('react').ForwardRefExoticComponent<Omit<{
5
+ schema: import('../../core/src').SchemaNode;
6
+ } & Record<string, any>, "ref"> & import('react').RefAttributes<any>>;
7
+ parameters: {
8
+ layout: string;
9
+ };
10
+ tags: string[];
11
+ argTypes: {
12
+ schema: {
13
+ table: {
14
+ disable: true;
15
+ };
16
+ };
17
+ };
18
+ };
19
+ export default meta;
20
+ type Story = StoryObj<typeof meta>;
21
+ export declare const Default: Story;
22
+ export declare const WithRowActions: Story;
23
+ /**
24
+ * CRM Deals Pipeline — demonstrates professional data formatting:
25
+ * - Currency with thousand separators (Amount column, right-aligned)
26
+ * - Percentage with progress bar (Probability column, right-aligned)
27
+ * - Formatted dates (Close Date column)
28
+ * - Colored badges for stage/status (Stage column)
29
+ * - Bold clickable name as primary link (Name column)
30
+ * - Empty value placeholder (Account column)
31
+ */
32
+ export declare const CRMDeals: Story;
33
+ export declare const EditableGrid: Story;
@@ -2,8 +2,13 @@ import { default as React } from 'react';
2
2
  import { ObjectGrid } from './ObjectGrid';
3
3
  import { VirtualGrid } from './VirtualGrid';
4
4
  export { ObjectGrid, VirtualGrid };
5
+ export { InlineEditing } from './InlineEditing';
6
+ export { useRowColor } from './useRowColor';
7
+ export { useGroupedData } from './useGroupedData';
5
8
  export type { ObjectGridProps } from './ObjectGrid';
6
9
  export type { VirtualGridProps, VirtualGridColumn } from './VirtualGrid';
10
+ export type { InlineEditingProps } from './InlineEditing';
11
+ export type { GroupEntry, UseGroupedDataResult } from './useGroupedData';
7
12
  export declare const ObjectGridRenderer: React.FC<{
8
13
  schema: any;
9
14
  [key: string]: any;
@@ -0,0 +1,30 @@
1
+ import { GroupingConfig } from '../../types/src';
2
+ export interface GroupEntry {
3
+ /** Composite key identifying this group (field values joined by ' / ') */
4
+ key: string;
5
+ /** Display label for the group header */
6
+ label: string;
7
+ /** Rows belonging to this group */
8
+ rows: any[];
9
+ /** Whether the group section is collapsed */
10
+ collapsed: boolean;
11
+ }
12
+ export interface UseGroupedDataResult {
13
+ /** Grouped entries (empty when grouping is not configured) */
14
+ groups: GroupEntry[];
15
+ /** Whether grouping is active */
16
+ isGrouped: boolean;
17
+ /** Toggle the collapsed state of a group by its key */
18
+ toggleGroup: (key: string) => void;
19
+ }
20
+ /**
21
+ * Hook that groups a flat data array by the fields specified in GroupingConfig.
22
+ *
23
+ * Supports multi-level grouping, per-field sort order, and per-field default
24
+ * collapsed state. Collapse state is managed internally so the consumer only
25
+ * needs to wire `toggleGroup` to the UI.
26
+ *
27
+ * @param config - GroupingConfig from the grid schema (optional)
28
+ * @param data - flat data rows
29
+ */
30
+ export declare function useGroupedData(config: GroupingConfig | undefined, data: any[]): UseGroupedDataResult;
@@ -0,0 +1,8 @@
1
+ import { RowColorConfig } from '../../types/src';
2
+ /**
3
+ * Hook that returns a row-className resolver based on RowColorConfig.
4
+ *
5
+ * @param config - RowColorConfig from the grid schema (optional)
6
+ * @returns a function `(row) => className | undefined`
7
+ */
8
+ export declare function useRowColor(config: RowColorConfig | undefined): (row: Record<string, any>) => string | undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@object-ui/plugin-grid",
3
- "version": "2.0.0",
3
+ "version": "3.0.0",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "description": "Grid plugin for Object UI",
@@ -15,25 +15,26 @@
15
15
  }
16
16
  },
17
17
  "dependencies": {
18
- "@tanstack/react-virtual": "^3.11.3",
18
+ "@tanstack/react-virtual": "^3.13.18",
19
19
  "lucide-react": "^0.563.0",
20
- "@object-ui/components": "2.0.0",
21
- "@object-ui/core": "2.0.0",
22
- "@object-ui/fields": "2.0.0",
23
- "@object-ui/react": "2.0.0",
24
- "@object-ui/types": "2.0.0"
20
+ "@object-ui/components": "3.0.0",
21
+ "@object-ui/core": "3.0.0",
22
+ "@object-ui/fields": "3.0.0",
23
+ "@object-ui/mobile": "3.0.0",
24
+ "@object-ui/react": "3.0.0",
25
+ "@object-ui/types": "3.0.0"
25
26
  },
26
27
  "peerDependencies": {
27
28
  "react": "^18.0.0 || ^19.0.0",
28
29
  "react-dom": "^18.0.0 || ^19.0.0"
29
30
  },
30
31
  "devDependencies": {
31
- "@vitejs/plugin-react": "^5.1.3",
32
- "msw": "^2.12.9",
32
+ "@vitejs/plugin-react": "^5.1.4",
33
+ "msw": "^2.12.10",
33
34
  "typescript": "^5.9.3",
34
35
  "vite": "^7.3.1",
35
36
  "vite-plugin-dts": "^4.5.4",
36
- "@object-ui/data-objectstack": "2.0.0"
37
+ "@object-ui/data-objectstack": "3.0.0"
37
38
  },
38
39
  "scripts": {
39
40
  "build": "vite build",
@@ -0,0 +1,235 @@
1
+ /**
2
+ * ObjectUI
3
+ * Copyright (c) 2024-present ObjectStack Inc.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+
9
+ /**
10
+ * InlineEditing Component
11
+ *
12
+ * A reusable inline cell editor for grid views. Provides save/cancel
13
+ * controls and validation feedback. Designed to be integrated into
14
+ * ObjectGrid or VirtualGrid rows.
15
+ *
16
+ * Features:
17
+ * - Click-to-edit with automatic focus
18
+ * - Save (Enter / ✓) and Cancel (Escape / ✗) actions
19
+ * - Validation feedback with error messages
20
+ * - Keyboard navigation (Enter to save, Escape to cancel)
21
+ */
22
+
23
+ import React, { useState, useRef, useEffect, useCallback } from 'react';
24
+ import { cn } from '@object-ui/components';
25
+ import { Check, X } from 'lucide-react';
26
+
27
+ export interface InlineEditingProps {
28
+ /** Current cell value */
29
+ value: any;
30
+ /** Called with new value on save. Return a string to signal validation error. */
31
+ onSave: (newValue: any) => void | string | Promise<void | string>;
32
+ /** Called when editing is cancelled */
33
+ onCancel?: () => void;
34
+ /** Validate before saving. Return error message or undefined. */
35
+ validate?: (value: any) => string | undefined;
36
+ /** Field type hint used to determine input type */
37
+ type?: 'text' | 'number' | 'email';
38
+ /** Placeholder text */
39
+ placeholder?: string;
40
+ /** Whether the cell is in editing mode initially */
41
+ editing?: boolean;
42
+ /** Additional class names */
43
+ className?: string;
44
+ /** Whether this field is disabled */
45
+ disabled?: boolean;
46
+ }
47
+
48
+ export function InlineEditing({
49
+ value,
50
+ onSave,
51
+ onCancel,
52
+ validate,
53
+ type = 'text',
54
+ placeholder,
55
+ editing: editingProp = false,
56
+ className,
57
+ disabled = false,
58
+ }: InlineEditingProps) {
59
+ const [isEditing, setIsEditing] = useState(editingProp);
60
+ const [editValue, setEditValue] = useState<string>(String(value ?? ''));
61
+ const [error, setError] = useState<string | undefined>();
62
+ const [saving, setSaving] = useState(false);
63
+ const inputRef = useRef<HTMLInputElement>(null);
64
+
65
+ // Sync with prop changes
66
+ useEffect(() => {
67
+ if (!isEditing) {
68
+ setEditValue(String(value ?? ''));
69
+ }
70
+ }, [value, isEditing]);
71
+
72
+ // Auto-focus when entering edit mode
73
+ useEffect(() => {
74
+ if (isEditing && inputRef.current) {
75
+ inputRef.current.focus();
76
+ inputRef.current.select();
77
+ }
78
+ }, [isEditing]);
79
+
80
+ const startEditing = useCallback(() => {
81
+ if (disabled) return;
82
+ setIsEditing(true);
83
+ setEditValue(String(value ?? ''));
84
+ setError(undefined);
85
+ }, [disabled, value]);
86
+
87
+ const cancel = useCallback(() => {
88
+ setIsEditing(false);
89
+ setEditValue(String(value ?? ''));
90
+ setError(undefined);
91
+ onCancel?.();
92
+ }, [value, onCancel]);
93
+
94
+ const save = useCallback(async () => {
95
+ // Run validation
96
+ if (validate) {
97
+ const validationError = validate(editValue);
98
+ if (validationError) {
99
+ setError(validationError);
100
+ return;
101
+ }
102
+ }
103
+
104
+ // Coerce number values
105
+ const coercedValue = type === 'number' ? Number(editValue) : editValue;
106
+
107
+ setSaving(true);
108
+ try {
109
+ const result = await onSave(coercedValue);
110
+ if (typeof result === 'string') {
111
+ setError(result);
112
+ setSaving(false);
113
+ return;
114
+ }
115
+ setIsEditing(false);
116
+ setError(undefined);
117
+ } catch (err: any) {
118
+ setError(err?.message || 'Save failed');
119
+ } finally {
120
+ setSaving(false);
121
+ }
122
+ }, [editValue, validate, type, onSave]);
123
+
124
+ const handleKeyDown = useCallback(
125
+ (e: React.KeyboardEvent) => {
126
+ if (e.key === 'Enter') {
127
+ e.preventDefault();
128
+ save();
129
+ } else if (e.key === 'Escape') {
130
+ e.preventDefault();
131
+ cancel();
132
+ }
133
+ },
134
+ [save, cancel],
135
+ );
136
+
137
+ // Display mode
138
+ if (!isEditing) {
139
+ return (
140
+ <div
141
+ data-slot="inline-editing"
142
+ className={cn(
143
+ 'group relative cursor-pointer rounded px-2 py-1 hover:bg-muted/50 transition-colors min-h-[1.75rem] flex items-center',
144
+ disabled && 'cursor-default opacity-60',
145
+ className,
146
+ )}
147
+ onClick={startEditing}
148
+ role="button"
149
+ tabIndex={disabled ? -1 : 0}
150
+ onKeyDown={(e) => {
151
+ if (e.key === 'Enter' || e.key === ' ') {
152
+ e.preventDefault();
153
+ startEditing();
154
+ }
155
+ }}
156
+ aria-label={`Edit value: ${String(value ?? '')}`}
157
+ >
158
+ <span data-slot="inline-editing-display" className="truncate text-sm">
159
+ {value != null && String(value) !== '' ? String(value) : (
160
+ <span className="text-muted-foreground italic">{placeholder || 'Click to edit'}</span>
161
+ )}
162
+ </span>
163
+ </div>
164
+ );
165
+ }
166
+
167
+ // Edit mode
168
+ return (
169
+ <div
170
+ data-slot="inline-editing"
171
+ className={cn('relative flex items-center gap-1', className)}
172
+ >
173
+ <div className="flex-1 relative">
174
+ <input
175
+ ref={inputRef}
176
+ data-slot="inline-editing-input"
177
+ type={type}
178
+ value={editValue}
179
+ onChange={(e) => {
180
+ setEditValue(e.target.value);
181
+ if (error) setError(undefined);
182
+ }}
183
+ onKeyDown={handleKeyDown}
184
+ placeholder={placeholder}
185
+ disabled={saving}
186
+ aria-invalid={!!error}
187
+ aria-describedby={error ? 'inline-editing-error' : undefined}
188
+ className={cn(
189
+ 'w-full rounded border px-2 py-1 text-sm outline-none transition-colors',
190
+ 'focus:ring-2 focus:ring-ring focus:border-input',
191
+ error
192
+ ? 'border-destructive focus:ring-destructive/30'
193
+ : 'border-input',
194
+ saving && 'opacity-50',
195
+ )}
196
+ />
197
+ {error && (
198
+ <p
199
+ id="inline-editing-error"
200
+ data-slot="inline-editing-error"
201
+ className="absolute left-0 top-full mt-0.5 text-xs text-destructive"
202
+ role="alert"
203
+ >
204
+ {error}
205
+ </p>
206
+ )}
207
+ </div>
208
+
209
+ {/* Action buttons */}
210
+ <button
211
+ data-slot="inline-editing-save"
212
+ type="button"
213
+ onClick={save}
214
+ disabled={saving}
215
+ aria-label="Save"
216
+ className={cn(
217
+ 'inline-flex h-6 w-6 items-center justify-center rounded text-primary hover:bg-primary/10 transition-colors',
218
+ saving && 'opacity-50 cursor-not-allowed',
219
+ )}
220
+ >
221
+ <Check className="h-3.5 w-3.5" />
222
+ </button>
223
+ <button
224
+ data-slot="inline-editing-cancel"
225
+ type="button"
226
+ onClick={cancel}
227
+ disabled={saving}
228
+ aria-label="Cancel"
229
+ className="inline-flex h-6 w-6 items-center justify-center rounded text-muted-foreground hover:bg-destructive/10 hover:text-destructive transition-colors"
230
+ >
231
+ <X className="h-3.5 w-3.5" />
232
+ </button>
233
+ </div>
234
+ );
235
+ }