@object-ui/plugin-calendar 3.0.3 → 3.1.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,6 +1,6 @@
1
- (function(A,p){typeof exports=="object"&&typeof module<"u"?p(exports,require("react"),require("@object-ui/core"),require("@object-ui/react"),require("lucide-react"),require("@object-ui/components")):typeof define=="function"&&define.amd?define(["exports","react","@object-ui/core","@object-ui/react","lucide-react","@object-ui/components"],p):(A=typeof globalThis<"u"?globalThis:A||self,p(A.ObjectUIPluginCalendar={},A.React,A.ObjectUICore,A.ObjectUIReact,A.LucideReact,A.ObjectUIComponents))})(this,(function(A,p,K,oe,Q,v){"use strict";function me(t){const n=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(t){for(const l in t)if(l!=="default"){const s=Object.getOwnPropertyDescriptor(t,l);Object.defineProperty(n,l,s.get?s:{enumerable:!0,get:()=>t[l]})}}return n.default=t,Object.freeze(n)}const L=me(p);var Z={exports:{}},J={};var ie;function ge(){if(ie)return J;ie=1;var t=Symbol.for("react.transitional.element"),n=Symbol.for("react.fragment");function l(s,m,c){var g=null;if(c!==void 0&&(g=""+c),m.key!==void 0&&(g=""+m.key),"key"in m){c={};for(var u in m)u!=="key"&&(c[u]=m[u])}else c=m;return m=c.ref,{$$typeof:t,type:s,key:g,ref:m!==void 0?m:null,props:c}}return J.Fragment=n,J.jsx=l,J.jsxs=l,J}var B={};var se;function he(){return se||(se=1,process.env.NODE_ENV!=="production"&&(function(){function t(e){if(e==null)return null;if(typeof e=="function")return e.$$typeof===j?null:e.displayName||e.name||null;if(typeof e=="string")return e;switch(e){case V:return"Fragment";case F:return"Profiler";case x:return"StrictMode";case S:return"Suspense";case I:return"SuspenseList";case T:return"Activity"}if(typeof e=="object")switch(typeof e.tag=="number"&&console.error("Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue."),e.$$typeof){case $:return"Portal";case _:return e.displayName||"Context";case P:return(e._context.displayName||"Context")+".Consumer";case M:var f=e.render;return e=e.displayName,e||(e=f.displayName||f.name||"",e=e!==""?"ForwardRef("+e+")":"ForwardRef"),e;case Y:return f=e.displayName||null,f!==null?f:t(e.type)||"Memo";case i:f=e._payload,e=e._init;try{return t(e(f))}catch{}}return null}function n(e){return""+e}function l(e){try{n(e);var f=!1}catch{f=!0}if(f){f=console;var w=f.error,E=typeof Symbol=="function"&&Symbol.toStringTag&&e[Symbol.toStringTag]||e.constructor.name||"Object";return w.call(f,"The provided key is an unsupported type %s. This value must be coerced to a string before using it here.",E),n(e)}}function s(e){if(e===V)return"<>";if(typeof e=="object"&&e!==null&&e.$$typeof===i)return"<...>";try{var f=t(e);return f?"<"+f+">":"<...>"}catch{return"<...>"}}function m(){var e=W.A;return e===null?null:e.getOwner()}function c(){return Error("react-stack-top-frame")}function g(e){if(q.call(e,"key")){var f=Object.getOwnPropertyDescriptor(e,"key").get;if(f&&f.isReactWarning)return!1}return e.key!==void 0}function u(e,f){function w(){U||(U=!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)",f))}w.isReactWarning=!0,Object.defineProperty(e,"key",{get:w,configurable:!0})}function h(){var e=t(this.type);return D[e]||(D[e]=!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.")),e=this.props.ref,e!==void 0?e:null}function d(e,f,w,E,H,X){var C=w.ref;return e={$$typeof:N,type:e,key:f,props:w,_owner:E},(C!==void 0?C:null)!==null?Object.defineProperty(e,"ref",{enumerable:!1,get:h}):Object.defineProperty(e,"ref",{enumerable:!1,value:null}),e._store={},Object.defineProperty(e._store,"validated",{configurable:!1,enumerable:!1,writable:!0,value:0}),Object.defineProperty(e,"_debugInfo",{configurable:!1,enumerable:!1,writable:!0,value:null}),Object.defineProperty(e,"_debugStack",{configurable:!1,enumerable:!1,writable:!0,value:H}),Object.defineProperty(e,"_debugTask",{configurable:!1,enumerable:!1,writable:!0,value:X}),Object.freeze&&(Object.freeze(e.props),Object.freeze(e)),e}function y(e,f,w,E,H,X){var C=f.children;if(C!==void 0)if(E)if(ne(C)){for(E=0;E<C.length;E++)a(C[E]);Object.freeze&&Object.freeze(C)}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 a(C);if(q.call(f,"key")){C=t(e);var z=Object.keys(f).filter(function(Te){return Te!=="key"});E=0<z.length?"{key: someKey, "+z.join(": ..., ")+": ...}":"{key: someKey}",R[C+E]||(z=0<z.length?"{"+z.join(": ..., ")+": ...}":"{}",console.error(`A props object containing a "key" prop is being spread into JSX:
1
+ (function(U,g){typeof exports=="object"&&typeof module<"u"?g(exports,require("react"),require("@object-ui/core"),require("@object-ui/react"),require("lucide-react"),require("@object-ui/components")):typeof define=="function"&&define.amd?define(["exports","react","@object-ui/core","@object-ui/react","lucide-react","@object-ui/components"],g):(U=typeof globalThis<"u"?globalThis:U||self,g(U.ObjectUIPluginCalendar={},U.React,U.ObjectUICore,U.ObjectUIReact,U.LucideReact,U.ObjectUIComponents))})(this,(function(U,g,J,ue,Z,C){"use strict";function Te(e){const r=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(e){for(const n in e)if(n!=="default"){const c=Object.getOwnPropertyDescriptor(e,n);Object.defineProperty(r,n,c.get?c:{enumerable:!0,get:()=>e[n]})}}return r.default=e,Object.freeze(r)}const V=Te(g);var ee={exports:{}},X={};var de;function Ne(){if(de)return X;de=1;var e=Symbol.for("react.transitional.element"),r=Symbol.for("react.fragment");function n(c,i,o){var d=null;if(o!==void 0&&(d=""+o),i.key!==void 0&&(d=""+i.key),"key"in i){o={};for(var v in i)v!=="key"&&(o[v]=i[v])}else o=i;return i=o.ref,{$$typeof:e,type:c,key:d,ref:i!==void 0?i:null,props:o}}return X.Fragment=r,X.jsx=n,X.jsxs=n,X}var K={};var fe;function Oe(){return fe||(fe=1,process.env.NODE_ENV!=="production"&&(function(){function e(t){if(t==null)return null;if(typeof t=="function")return t.$$typeof===_?null:t.displayName||t.name||null;if(typeof t=="string")return t;switch(t){case A:return"Fragment";case M:return"Profiler";case F:return"StrictMode";case I:return"Suspense";case T:return"SuspenseList";case f:return"Activity"}if(typeof t=="object")switch(typeof t.tag=="number"&&console.error("Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue."),t.$$typeof){case w:return"Portal";case b:return t.displayName||"Context";case Y:return(t._context.displayName||"Context")+".Consumer";case O:var x=t.render;return t=t.displayName,t||(t=x.displayName||x.name||"",t=t!==""?"ForwardRef("+t+")":"ForwardRef"),t;case P:return x=t.displayName||null,x!==null?x:e(t.type)||"Memo";case D:x=t._payload,t=t._init;try{return e(t(x))}catch{}}return null}function r(t){return""+t}function n(t){try{r(t);var x=!1}catch{x=!0}if(x){x=console;var j=x.error,k=typeof Symbol=="function"&&Symbol.toStringTag&&t[Symbol.toStringTag]||t.constructor.name||"Object";return j.call(x,"The provided key is an unsupported type %s. This value must be coerced to a string before using it here.",k),r(t)}}function c(t){if(t===A)return"<>";if(typeof t=="object"&&t!==null&&t.$$typeof===D)return"<...>";try{var x=e(t);return x?"<"+x+">":"<...>"}catch{return"<...>"}}function i(){var t=u.A;return t===null?null:t.getOwner()}function o(){return Error("react-stack-top-frame")}function d(t){if(S.call(t,"key")){var x=Object.getOwnPropertyDescriptor(t,"key").get;if(x&&x.isReactWarning)return!1}return t.key!==void 0}function v(t,x){function j(){Q||(Q=!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)",x))}j.isReactWarning=!0,Object.defineProperty(t,"key",{get:j,configurable:!0})}function y(){var t=e(this.type);return re[t]||(re[t]=!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.")),t=this.props.ref,t!==void 0?t:null}function p(t,x,j,k,z,G){var R=j.ref;return t={$$typeof:h,type:t,key:x,props:j,_owner:k},(R!==void 0?R:null)!==null?Object.defineProperty(t,"ref",{enumerable:!1,get:y}):Object.defineProperty(t,"ref",{enumerable:!1,value:null}),t._store={},Object.defineProperty(t._store,"validated",{configurable:!1,enumerable:!1,writable:!0,value:0}),Object.defineProperty(t,"_debugInfo",{configurable:!1,enumerable:!1,writable:!0,value:null}),Object.defineProperty(t,"_debugStack",{configurable:!1,enumerable:!1,writable:!0,value:z}),Object.defineProperty(t,"_debugTask",{configurable:!1,enumerable:!1,writable:!0,value:G}),Object.freeze&&(Object.freeze(t.props),Object.freeze(t)),t}function E(t,x,j,k,z,G){var R=x.children;if(R!==void 0)if(k)if(L(R)){for(k=0;k<R.length;k++)a(R[k]);Object.freeze&&Object.freeze(R)}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 a(R);if(S.call(x,"key")){R=e(t);var W=Object.keys(x).filter(function(it){return it!=="key"});k=0<W.length?"{key: someKey, "+W.join(": ..., ")+": ...}":"{key: someKey}",$[R+k]||(W=0<W.length?"{"+W.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} />`,E,C,z,C),R[C+E]=!0)}if(C=null,w!==void 0&&(l(w),C=""+w),g(f)&&(l(f.key),C=""+f.key),"key"in f){w={};for(var le in f)le!=="key"&&(w[le]=f[le])}else w=f;return C&&u(w,typeof e=="function"?e.displayName||e.name||"Unknown":e),d(e,C,w,m(),H,X)}function a(e){b(e)?e._store&&(e._store.validated=1):typeof e=="object"&&e!==null&&e.$$typeof===i&&(e._payload.status==="fulfilled"?b(e._payload.value)&&e._payload.value._store&&(e._payload.value._store.validated=1):e._store&&(e._store.validated=1))}function b(e){return typeof e=="object"&&e!==null&&e.$$typeof===N}var o=p,N=Symbol.for("react.transitional.element"),$=Symbol.for("react.portal"),V=Symbol.for("react.fragment"),x=Symbol.for("react.strict_mode"),F=Symbol.for("react.profiler"),P=Symbol.for("react.consumer"),_=Symbol.for("react.context"),M=Symbol.for("react.forward_ref"),S=Symbol.for("react.suspense"),I=Symbol.for("react.suspense_list"),Y=Symbol.for("react.memo"),i=Symbol.for("react.lazy"),T=Symbol.for("react.activity"),j=Symbol.for("react.client.reference"),W=o.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,q=Object.prototype.hasOwnProperty,ne=Array.isArray,G=console.createTask?console.createTask:function(){return null};o={react_stack_bottom_frame:function(e){return e()}};var U,D={},O=o.react_stack_bottom_frame.bind(o,c)(),k=G(s(c)),R={};B.Fragment=V,B.jsx=function(e,f,w){var E=1e4>W.recentlyCreatedOwnerStacks++;return y(e,f,w,!1,E?Error("react-stack-top-frame"):O,E?G(s(e)):k)},B.jsxs=function(e,f,w){var E=1e4>W.recentlyCreatedOwnerStacks++;return y(e,f,w,!0,E?Error("react-stack-top-frame"):O,E?G(s(e)):k)}})()),B}var ce;function pe(){return ce||(ce=1,process.env.NODE_ENV==="production"?Z.exports=ge():Z.exports=he()),Z.exports}var r=pe();const ee="bg-blue-500 text-white";function te({events:t=[],view:n="month",currentDate:l=new Date,locale:s="default",onEventClick:m,onDateClick:c,onViewChange:g,onNavigate:u,onAddClick:h,onEventDrop:d,className:y}){const[a,b]=L.useState(n),[o,N]=L.useState(l);L.useEffect(()=>{N(l)},[l]),L.useEffect(()=>{b(n)},[n]);const $=L.useRef(g);$.current=g,L.useEffect(()=>{const i=window.matchMedia("(max-width: 639px)"),T=j=>{j.matches&&(b("day"),$.current?.("day"))};return T(i),i.addEventListener("change",T),()=>i.removeEventListener("change",T)},[]);const V=()=>{const i=new Date(o);a==="month"?i.setMonth(i.getMonth()-1):a==="week"?i.setDate(i.getDate()-7):i.setDate(i.getDate()-1),N(i),u?.(i)},x=()=>{const i=new Date(o);a==="month"?i.setMonth(i.getMonth()+1):a==="week"?i.setDate(i.getDate()+7):i.setDate(i.getDate()+1),N(i),u?.(i)},F=()=>{const i=new Date;N(i),u?.(i)},P=i=>{b(i),g?.(i)},_=()=>{if(a==="month")return o.toLocaleDateString(s,{month:"long",year:"numeric"});if(a==="week"){const i=de(o),T=new Date(i);return T.setDate(T.getDate()+6),`${i.toLocaleDateString(s,{month:"short",day:"numeric"})} - ${T.toLocaleDateString(s,{month:"short",day:"numeric",year:"numeric"})}`}else return o.toLocaleDateString(s,{weekday:"long",month:"long",day:"numeric",year:"numeric"})},M=L.useRef(0),S=i=>{M.current=i.touches[0].clientX},I=i=>{const T=M.current-i.changedTouches[0].clientX;if(Math.abs(T)>50){const j=new Date(o);a==="day"?j.setDate(j.getDate()+(T>0?1:-1)):a==="week"?j.setDate(j.getDate()+(T>0?7:-7)):j.setMonth(j.getMonth()+(T>0?1:-1)),N(j),u?.(j)}},Y=i=>{i&&(N(i),u?.(i))};return r.jsxs("div",{role:"region","aria-label":"Calendar",className:v.cn("flex flex-col h-full bg-background",y),children:[r.jsxs("div",{className:"flex items-center justify-between p-4 border-b",children:[r.jsxs("div",{className:"flex items-center gap-4",children:[r.jsxs("div",{className:"flex items-center bg-muted/50 rounded-lg p-1 gap-1",children:[r.jsx(v.Button,{variant:"ghost",size:"sm",onClick:F,className:"h-8","aria-label":"Go to today",children:"Today"}),r.jsx("div",{className:"h-4 w-px bg-border mx-1"}),r.jsx(v.Button,{variant:"ghost",size:"icon","aria-label":"Previous period",onClick:V,className:"h-8 w-8",children:r.jsx(Q.ChevronLeftIcon,{className:"h-4 w-4"})}),r.jsx(v.Button,{variant:"ghost",size:"icon","aria-label":"Next period",onClick:x,className:"h-8 w-8",children:r.jsx(Q.ChevronRightIcon,{className:"h-4 w-4"})})]}),r.jsxs(v.Popover,{children:[r.jsx(v.PopoverTrigger,{asChild:!0,children:r.jsxs(v.Button,{variant:"ghost","aria-label":`Current date: ${_()}`,className:v.cn("text-xl font-semibold h-auto px-3 py-1 hover:bg-muted/50 transition-colors","flex items-center gap-2"),children:[r.jsx(Q.CalendarIcon,{className:"h-5 w-5 text-muted-foreground"}),r.jsx("span",{children:_()})]})}),r.jsx(v.PopoverContent,{className:"w-auto p-0",align:"start",children:r.jsx(v.Calendar,{mode:"single",selected:o,onSelect:Y,initialFocus:!0,fromYear:2e3,toYear:2050})})]})]}),r.jsxs("div",{className:"flex items-center gap-2",children:[r.jsxs(v.Select,{value:a,onValueChange:P,children:[r.jsx(v.SelectTrigger,{className:"w-32 bg-background",children:r.jsx(v.SelectValue,{})}),r.jsxs(v.SelectContent,{children:[r.jsx(v.SelectItem,{value:"day",children:"Day"}),r.jsx(v.SelectItem,{value:"week",children:"Week"}),r.jsx(v.SelectItem,{value:"month",children:"Month"})]})]}),h&&r.jsxs(v.Button,{onClick:h,size:"sm",className:"gap-1",children:[r.jsx(Q.PlusIcon,{className:"h-4 w-4"}),"New"]})]})]}),r.jsxs("div",{className:"flex-1 overflow-auto",onTouchStart:S,onTouchEnd:I,children:[a==="month"&&r.jsx(xe,{date:o,events:t,onEventClick:m,onDateClick:c,onEventDrop:d}),a==="week"&&r.jsx(De,{date:o,events:t,locale:s,onEventClick:m,onDateClick:c}),a==="day"&&r.jsx(ye,{date:o,events:t,onEventClick:m,onDateClick:c})]})]})}function de(t){const n=new Date(t),l=n.getDay(),s=n.getDate()-l;return n.setDate(s),n}function be(t){const n=t.getFullYear(),l=t.getMonth(),s=new Date(n,l,1),m=new Date(n,l+1,0),c=s.getDay(),g=[];for(let h=c-1;h>=0;h--){const d=new Date(s.getTime());d.setDate(d.getDate()-(h+1)),g.push(d)}for(let h=1;h<=m.getDate();h++)g.push(new Date(n,l,h));const u=42-g.length;for(let h=1;h<=u;h++){const d=new Date(m.getTime());d.setDate(d.getDate()+h),g.push(d)}return g}function ue(t,n){return t.getFullYear()===n.getFullYear()&&t.getMonth()===n.getMonth()&&t.getDate()===n.getDate()}function re(t,n){return n.filter(l=>{const s=new Date(l.start),m=l.end?new Date(l.end):new Date(s),c=new Date(t);c.setHours(0,0,0,0);const g=new Date(t);g.setHours(23,59,59,999);const u=new Date(s);u.setHours(0,0,0,0);const h=new Date(m);return h.setHours(23,59,59,999),c<=h&&g>=u})}function xe({date:t,events:n,onEventClick:l,onDateClick:s,onEventDrop:m}){const c=be(t),g=new Date,u=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],[h,d]=L.useState(null),[y,a]=L.useState(null),b=(x,F)=>{d(F.id),x.dataTransfer.effectAllowed="move",x.dataTransfer.setData("text/plain",String(F.id))},o=()=>{d(null),a(null)},N=(x,F)=>{x.preventDefault(),x.dataTransfer.dropEffect="move",a(F)},$=x=>{x.currentTarget.contains(x.relatedTarget)||a(null)},V=(x,F)=>{if(x.preventDefault(),a(null),d(null),!m)return;const P=x.dataTransfer.getData("text/plain"),_=n.find(j=>String(j.id)===P);if(!_)return;const M=new Date(_.start),S=new Date(M);S.setHours(0,0,0,0);const I=new Date(F);I.setHours(0,0,0,0);const Y=I.getTime()-S.getTime();if(Y===0)return;const i=new Date(M.getTime()+Y);let T;_.end&&(T=new Date(new Date(_.end).getTime()+Y)),m(_,i,T)};return r.jsxs("div",{className:"flex flex-col h-full",children:[r.jsx("div",{role:"row",className:"grid grid-cols-7 border-b",children:u.map(x=>r.jsx("div",{role:"columnheader",className:"p-2 text-center text-sm font-medium text-muted-foreground border-r last:border-r-0",children:x},x))}),r.jsx("div",{role:"grid","aria-label":"Calendar grid",className:"grid grid-cols-7 flex-1 auto-rows-fr",children:c.map((x,F)=>{const P=re(x,n),_=x.getMonth()===t.getMonth(),M=ue(x,g);return r.jsxs("div",{role:"gridcell","aria-label":`${x.toLocaleDateString("default",{weekday:"long",month:"long",day:"numeric",year:"numeric"})}${P.length>0?`, ${P.length} event${P.length>1?"s":""}`:""}`,className:v.cn("border-b border-r last:border-r-0 p-2 min-h-[100px] cursor-pointer hover:bg-accent/50",!_&&"bg-muted/30 text-muted-foreground",y===F&&"ring-2 ring-primary"),onClick:()=>s?.(x),onDragOver:S=>N(S,F),onDragLeave:$,onDrop:S=>V(S,x),children:[r.jsx("div",{className:v.cn("text-sm font-medium mb-1",M&&"inline-flex items-center justify-center rounded-full bg-primary text-primary-foreground h-6 w-6"),...M?{"aria-current":"date"}:{},children:x.getDate()}),r.jsxs("div",{className:"space-y-1",children:[P.slice(0,3).map(S=>r.jsx("div",{role:"button","aria-label":S.title,draggable:!!m,onDragStart:I=>b(I,S),onDragEnd:o,className:v.cn("text-xs px-2 py-1 rounded truncate cursor-pointer hover:opacity-80",S.color||ee,h===S.id&&"opacity-50"),style:S.color&&S.color.startsWith("#")?{backgroundColor:S.color}:void 0,onClick:I=>{I.stopPropagation(),l?.(S)},children:S.title},S.id)),P.length>3&&r.jsxs("div",{className:"text-xs text-muted-foreground px-2",children:["+",P.length-3," more"]})]})]},F)})})]})}function De({date:t,events:n,locale:l="default",onEventClick:s,onDateClick:m}){const c=L.useRef(null),g=a=>{m&&(c.current=setTimeout(()=>{m(a)},500))},u=()=>{c.current&&(clearTimeout(c.current),c.current=null)},h=de(t),d=Array.from({length:7},(a,b)=>{const o=new Date(h);return o.setDate(o.getDate()+b),o}),y=new Date;return r.jsxs("div",{className:"flex flex-col h-full",children:[r.jsx("div",{className:"grid grid-cols-7 border-b",children:d.map(a=>{const b=ue(a,y);return r.jsxs("div",{className:"p-3 text-center border-r last:border-r-0",children:[r.jsx("div",{className:"text-sm font-medium text-muted-foreground",children:a.toLocaleDateString(l,{weekday:"short"})}),r.jsx("div",{className:v.cn("text-lg font-semibold mt-1",b&&"inline-flex items-center justify-center rounded-full bg-primary text-primary-foreground h-8 w-8"),children:a.getDate()})]},a.toISOString())})}),r.jsx("div",{role:"grid",className:"grid grid-cols-7 flex-1",children:d.map(a=>{const b=re(a,n);return r.jsx("div",{role:"gridcell","aria-label":`${a.toLocaleDateString("default",{weekday:"long",month:"long",day:"numeric",year:"numeric"})}${b.length>0?`, ${b.length} event${b.length>1?"s":""}`:""}`,className:"border-r last:border-r-0 p-2 min-h-[400px] cursor-pointer hover:bg-accent/50",onClick:()=>m?.(a),onTouchStart:()=>g(a),onTouchEnd:u,children:r.jsx("div",{className:"space-y-2",children:b.map(o=>r.jsxs("div",{role:"button","aria-label":o.title,className:v.cn("text-xs sm:text-sm px-2 sm:px-3 py-1.5 sm:py-2 rounded cursor-pointer hover:opacity-80",o.color||ee),style:o.color&&o.color.startsWith("#")?{backgroundColor:o.color}:void 0,onClick:N=>{N.stopPropagation(),s?.(o)},children:[r.jsx("div",{className:"font-medium truncate",children:o.title}),!o.allDay&&r.jsx("div",{className:"text-xs opacity-90 mt-1",children:o.start.toLocaleTimeString("default",{hour:"numeric",minute:"2-digit"})})]},o.id))})},a.toISOString())})})]})}function ye({date:t,events:n,onEventClick:l,onDateClick:s}){const m=re(t,n),c=Array.from({length:24},(d,y)=>y),g=L.useRef(null),u=d=>{s&&(g.current=setTimeout(()=>{const y=new Date(t);y.setHours(d,0,0,0),s(y)},500))},h=()=>{g.current&&(clearTimeout(g.current),g.current=null)};return r.jsx("div",{className:"flex flex-col h-full",children:r.jsx("div",{role:"list",className:"flex-1 overflow-auto",children:c.map(d=>{const y=m.filter(a=>a.allDay?d===0:a.start.getHours()===d);return r.jsxs("div",{role:"listitem",className:"flex border-b min-h-[60px]",children:[r.jsx("div",{className:"w-20 p-2 text-sm text-muted-foreground border-r",children:d===0?"12 AM":d<12?`${d} AM`:d===12?"12 PM":`${d-12} PM`}),r.jsx("div",{className:"flex-1 p-2 space-y-2",onTouchStart:()=>u(d),onTouchEnd:h,children:y.map(a=>r.jsxs("div",{"aria-label":a.title,className:v.cn("px-2 sm:px-3 py-1.5 sm:py-2 rounded cursor-pointer hover:opacity-80",a.color||ee),style:a.color&&a.color.startsWith("#")?{backgroundColor:a.color}:void 0,onClick:()=>l?.(a),children:[r.jsx("div",{className:"font-medium truncate",children:a.title}),!a.allDay&&r.jsxs("div",{className:"text-xs opacity-90 mt-1",children:[a.start.toLocaleTimeString("default",{hour:"numeric",minute:"2-digit"}),a.end&&` - ${a.end.toLocaleTimeString("default",{hour:"numeric",minute:"2-digit"})}`]})]},a.id))})]},d)})})})}function ve(t){const{onRefresh:n,threshold:l=80,enabled:s=!0}=t,m=p.useRef(null),[c,g]=p.useState(!1),[u,h]=p.useState(0),d=p.useRef(0),y=p.useCallback(o=>{if(!s||c)return;const N=m.current;N&&N.scrollTop===0&&(d.current=o.touches[0].clientY)},[s,c]),a=p.useCallback(o=>{if(!s||c||!d.current)return;const $=o.touches[0].clientY-d.current;$>0&&h(Math.min($,l*1.5))},[s,c,l]),b=p.useCallback(async()=>{if(!(!s||c)){if(u>=l){g(!0);try{await n()}finally{g(!1)}}h(0),d.current=0}},[s,c,u,l,n]);return p.useEffect(()=>{const o=m.current;if(!(!o||!s))return o.addEventListener("touchstart",y,{passive:!0}),o.addEventListener("touchmove",a,{passive:!0}),o.addEventListener("touchend",b,{passive:!0}),()=>{o.removeEventListener("touchstart",y),o.removeEventListener("touchmove",a),o.removeEventListener("touchend",b)}},[y,a,b,s]),{ref:m,isRefreshing:c,pullDistance:u}}const je=p.createContext(null);je.displayName="MobileContext";function we(t){return"data"in t&&t.data?t.data:"staticData"in t&&t.staticData?{provider:"value",items:t.staticData}:t.objectName?{provider:"object",object:t.objectName}:null}function Se(t){if(t){if(typeof t=="string"){const n=t.split(" "),l=n[0],s=n[1]?.toLowerCase()==="desc"?"desc":"asc";return{[l]:s}}if(Array.isArray(t))return t.reduce((n,l)=>(l.field&&l.order&&(n[l.field]=l.order),n),{})}}function Ee(t){return"filter"in t&&t.filter&&typeof t.filter=="object"&&"calendar"in t.filter?t.filter.calendar:t.calendar?t.calendar:t.startDateField||t.dateField?{startDateField:t.startDateField||t.dateField,endDateField:t.endDateField||t.endField,titleField:t.titleField||"name",colorField:t.colorField,allDayField:t.allDayField}:null}const fe=({schema:t,dataSource:n,className:l,onEventClick:s,onRowClick:m,onDateClick:c,onNavigate:g,onViewChange:u,...h})=>{const[d,y]=p.useState([]),[a,b]=p.useState(!0),[o,N]=p.useState(null),[$,V]=p.useState(null),[x,F]=p.useState(new Date),[P,_]=p.useState("month"),[M,S]=p.useState(0),I=p.useCallback(async()=>{S(D=>D+1)},[]),{ref:Y,isRefreshing:i,pullDistance:T}=ve({onRefresh:I,enabled:!!n&&!!t.objectName}),j=p.useMemo(()=>we(t),[t.data,t.staticData,t.objectName]),W=p.useMemo(()=>Ee(t),[t.filter,t.calendar,t.dateField,t.endField,t.titleField,t.colorField]),q=j?.provider==="value";p.useEffect(()=>{let D=!0;return(async()=>{try{if(!D)return;if(b(!0),q&&j?.provider==="value"){D&&(y(j.items),b(!1));return}if(t.data||h.data){const k=t.data||h.data;if(Array.isArray(k)){y(k),b(!1);return}}if(!n)throw new Error("DataSource required for object/api providers");if(j?.provider==="object"){const k=j.object,R=await n.find(k,{$filter:t.filter,$orderby:Se(t.sort)});let e=[];Array.isArray(R)?e=R:R&&typeof R=="object"&&(Array.isArray(R.data)?e=R.data:Array.isArray(R.value)&&(e=R.value)),D&&y(e)}else j?.provider==="api"&&(console.warn("API provider not yet implemented for ObjectCalendar"),D&&y([]));D&&b(!1)}catch(k){console.error("[ObjectCalendar] Error fetching data:",k),D&&(N(k),b(!1))}})(),()=>{D=!1}},[j,n,q,t.filter,t.sort,M]),p.useEffect(()=>{!q&&n&&(async()=>{try{if(!n)return;const O=j?.provider==="object"?j.object:t.objectName;if(!O)return;const k=await n.getObjectSchema(O);V(k)}catch(O){console.error("Failed to fetch object schema:",O)}})()},[t.objectName,n,q,j]);const ne=p.useMemo(()=>{if(!W||!d.length)return[];const{startDateField:D,endDateField:O,titleField:k,colorField:R}=W;return d.map((e,f)=>{const w=e[D],E=O?e[O]:null,H=e[k]||"Untitled",X=R?e[R]:void 0;return{id:e.id||e._id||`event-${f}`,title:H,start:w?new Date(w):new Date,end:E?new Date(E):void 0,color:X,allDay:!E,data:e}}).filter(e=>!isNaN(e.start.getTime()))},[d,W]),G=p.useCallback(()=>{c?.(new Date)},[c]),U=oe.useNavigationOverlay({navigation:t.navigation,objectName:t.objectName,onRowClick:m});return a?r.jsx("div",{className:l,children:r.jsx("div",{className:"flex items-center justify-center h-96",children:r.jsx("div",{className:"text-muted-foreground",children:"Loading calendar..."})})}):o?r.jsx("div",{className:l,children:r.jsx("div",{className:"flex items-center justify-center h-96",children:r.jsxs("div",{className:"text-destructive",children:["Error: ",o.message]})})}):W?r.jsxs("div",{ref:Y,className:l,children:[T>0&&r.jsx("div",{className:"flex items-center justify-center text-xs text-muted-foreground",style:{height:T},children:i?"Refreshing…":"Pull to refresh"}),r.jsx("div",{className:"border rounded-lg bg-background h-[calc(100vh-120px)] sm:h-[calc(100vh-160px)] md:h-[calc(100vh-200px)] min-h-[400px] sm:min-h-[600px]",children:r.jsx(te,{events:ne,currentDate:x,view:t.defaultView||"month",onEventClick:D=>{U.handleClick(D.data),s?.(D.data)},onDateClick:c,onNavigate:D=>{F(D),g?.(D)},onViewChange:D=>{_(D),u?.(D)},onAddClick:G})}),U.isOverlay&&r.jsx(v.NavigationOverlay,{...U,title:"Event Details",children:D=>r.jsx("div",{className:"space-y-3",children:Object.entries(D).map(([O,k])=>r.jsxs("div",{className:"flex flex-col",children:[r.jsx("span",{className:"text-xs font-medium text-muted-foreground uppercase tracking-wide",children:O.replace(/_/g," ")}),r.jsx("span",{className:"text-sm",children:String(k??"—")})]},O))})})]}):r.jsx("div",{className:l,children:r.jsx("div",{className:"flex items-center justify-center h-96",children:r.jsx("div",{className:"text-muted-foreground",children:"Calendar configuration required. Please specify startDateField and titleField."})})})};K.ComponentRegistry.register("calendar-view",({schema:t,className:n,onAction:l,...s})=>{const m=p.useMemo(()=>!t.data||!Array.isArray(t.data)?[]:t.data.map((u,h)=>{const d=t.titleField||"title",y=t.startDateField||"start",a=t.endDateField||"end",b=t.colorField||"color",o=t.allDayField||"allDay";return{id:u._id||u.id||h,title:u[d]||"Untitled Event",start:new Date(u[y]),end:u[a]?new Date(u[a]):void 0,allDay:u[o],color:u[b],data:u}}),[t.data,t.titleField,t.startDateField,t.endDateField,t.colorField,t.allDayField]),c=u=>{t.onEventClick&&l?.({type:"event-click",payload:u})},g=()=>{l?.({type:"create",payload:{}})};return r.jsx(te,{className:n,events:m,onEventClick:c,onAddClick:g,...s})},{namespace:"plugin-calendar",label:"Calendar View",inputs:[{name:"data",type:"array",label:"Data",description:"Array of record objects to display as events"},{name:"titleField",type:"string",label:"Title Field",defaultValue:"title",description:"Field name to use for event title"},{name:"startDateField",type:"string",label:"Start Date Field",defaultValue:"start",description:"Field name for event start date"},{name:"endDateField",type:"string",label:"End Date Field",defaultValue:"end",description:"Field name for event end date (optional)"},{name:"allDayField",type:"string",label:"All Day Field",defaultValue:"allDay",description:"Field name for all-day flag"},{name:"colorField",type:"string",label:"Color Field",defaultValue:"color",description:"Field name for event color"},{name:"colorMapping",type:"object",label:"Color Mapping",description:'Map field values to colors (e.g., {meeting: "blue", deadline: "red"})'},{name:"view",type:"enum",enum:["month","week","day"],defaultValue:"month",label:"View Mode",description:"Calendar view mode (month, week, or day)"},{name:"currentDate",type:"string",label:"Current Date",description:"ISO date string for initial calendar date"},{name:"allowCreate",type:"boolean",label:"Allow Create",defaultValue:!1,description:"Allow creating events by clicking on dates"},{name:"className",type:"string",label:"CSS Class"}],defaultProps:{view:"month",titleField:"title",startDateField:"start",endDateField:"end",allDayField:"allDay",colorField:"color",allowCreate:!1,data:[{id:1,title:"Team Meeting",start:new Date(new Date().setHours(10,0,0,0)).toISOString(),end:new Date(new Date().setHours(11,0,0,0)).toISOString(),color:"#3b82f6",allDay:!1},{id:2,title:"Project Deadline",start:new Date(new Date().setDate(new Date().getDate()+3)).toISOString(),color:"#ef4444",allDay:!0},{id:3,title:"Conference",start:new Date(new Date().setDate(new Date().getDate()+7)).toISOString(),end:new Date(new Date().setDate(new Date().getDate()+9)).toISOString(),color:"#10b981",allDay:!0}],className:"h-[600px] border rounded-lg"}});const ae=({schema:t})=>{const{dataSource:n}=oe.useSchemaContext();return r.jsx(fe,{schema:t,dataSource:n})};K.ComponentRegistry.register("object-calendar",ae,{namespace:"plugin-calendar",label:"Object Calendar",category:"view",inputs:[{name:"objectName",type:"string",label:"Object Name",required:!0},{name:"calendar",type:"object",label:"Calendar Config",description:"startDateField, endDateField, titleField, colorField"}]}),K.ComponentRegistry.register("calendar",ae,{namespace:"view",label:"Calendar View",category:"view",inputs:[{name:"objectName",type:"string",label:"Object Name",required:!0},{name:"calendar",type:"object",label:"Calendar Config",description:"startDateField, endDateField, titleField, colorField"}]}),A.CalendarView=te,A.ObjectCalendar=fe,A.ObjectCalendarRenderer=ae,Object.defineProperty(A,Symbol.toStringTag,{value:"Module"})}));
6
+ <%s key={someKey} {...props} />`,k,R,W,R),$[R+k]=!0)}if(R=null,j!==void 0&&(n(j),R=""+j),d(x)&&(n(x.key),R=""+x.key),"key"in x){j={};for(var ce in x)ce!=="key"&&(j[ce]=x[ce])}else j=x;return R&&v(j,typeof t=="function"?t.displayName||t.name||"Unknown":t),p(t,R,j,i(),z,G)}function a(t){m(t)?t._store&&(t._store.validated=1):typeof t=="object"&&t!==null&&t.$$typeof===D&&(t._payload.status==="fulfilled"?m(t._payload.value)&&t._payload.value._store&&(t._payload.value._store.validated=1):t._store&&(t._store.validated=1))}function m(t){return typeof t=="object"&&t!==null&&t.$$typeof===h}var l=g,h=Symbol.for("react.transitional.element"),w=Symbol.for("react.portal"),A=Symbol.for("react.fragment"),F=Symbol.for("react.strict_mode"),M=Symbol.for("react.profiler"),Y=Symbol.for("react.consumer"),b=Symbol.for("react.context"),O=Symbol.for("react.forward_ref"),I=Symbol.for("react.suspense"),T=Symbol.for("react.suspense_list"),P=Symbol.for("react.memo"),D=Symbol.for("react.lazy"),f=Symbol.for("react.activity"),_=Symbol.for("react.client.reference"),u=l.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,S=Object.prototype.hasOwnProperty,L=Array.isArray,H=console.createTask?console.createTask:function(){return null};l={react_stack_bottom_frame:function(t){return t()}};var Q,re={},B=l.react_stack_bottom_frame.bind(l,o)(),N=H(c(o)),$={};K.Fragment=A,K.jsx=function(t,x,j){var k=1e4>u.recentlyCreatedOwnerStacks++;return E(t,x,j,!1,k?Error("react-stack-top-frame"):B,k?H(c(t)):N)},K.jsxs=function(t,x,j){var k=1e4>u.recentlyCreatedOwnerStacks++;return E(t,x,j,!0,k?Error("react-stack-top-frame"):B,k?H(c(t)):N)}})()),K}var ge;function Ce(){return ge||(ge=1,process.env.NODE_ENV==="production"?ee.exports=Ne():ee.exports=Oe()),ee.exports}var s=Ce();const Fe=(e,r,n,c)=>{const i=[n,{code:r,...c||{}}];if(e?.services?.logger?.forward)return e.services.logger.forward(i,"warn","react-i18next::",!0);q(i[0])&&(i[0]=`react-i18next:: ${i[0]}`),e?.services?.logger?.warn?e.services.logger.warn(...i):console?.warn&&console.warn(...i)},me={},pe=(e,r,n,c)=>{q(n)&&me[n]||(q(n)&&(me[n]=new Date),Fe(e,r,n,c))},he=(e,r)=>()=>{if(e.isInitialized)r();else{const n=()=>{setTimeout(()=>{e.off("initialized",n)},0),r()};e.on("initialized",n)}},ne=(e,r,n)=>{e.loadNamespaces(r,he(e,n))},ye=(e,r,n,c)=>{if(q(n)&&(n=[n]),e.options.preload&&e.options.preload.indexOf(r)>-1)return ne(e,n,c);n.forEach(i=>{e.options.ns.indexOf(i)<0&&e.options.ns.push(i)}),e.loadLanguages(r,he(e,c))},ke=(e,r,n={})=>!r.languages||!r.languages.length?(pe(r,"NO_LANGUAGES","i18n.languages were undefined or empty",{languages:r.languages}),!0):r.hasLoadedNamespace(e,{lng:n.lng,precheck:(c,i)=>{if(n.bindI18n&&n.bindI18n.indexOf("languageChanging")>-1&&c.services.backendConnector.backend&&c.isLanguageChangingTo&&!i(c.isLanguageChangingTo,e))return!1}}),q=e=>typeof e=="string",Le=e=>typeof e=="object"&&e!==null,Ae=/&(?:amp|#38|lt|#60|gt|#62|apos|#39|quot|#34|nbsp|#160|copy|#169|reg|#174|hellip|#8230|#x2F|#47);/g,Re={"&amp;":"&","&#38;":"&","&lt;":"<","&#60;":"<","&gt;":">","&#62;":">","&apos;":"'","&#39;":"'","&quot;":'"',"&#34;":'"',"&nbsp;":" ","&#160;":" ","&copy;":"©","&#169;":"©","&reg;":"®","&#174;":"®","&hellip;":"…","&#8230;":"…","&#x2F;":"/","&#47;":"/"},Pe=e=>Re[e];let Ie={bindI18n:"languageChanged",bindI18nStore:"",transEmptyNodeValue:"",transSupportBasicHtmlNodes:!0,transWrapTextNodes:"",transKeepBasicHtmlNodesFor:["br","strong","i","p"],useSuspense:!0,unescape:e=>e.replace(Ae,Pe),transDefaultProps:void 0};const Me=()=>Ie;let $e;const Ve=()=>$e,Ye=g.createContext();class Ue{constructor(){this.usedNamespaces={}}addUsedNamespaces(r){r.forEach(n=>{this.usedNamespaces[n]||(this.usedNamespaces[n]=!0)})}getUsedNamespaces(){return Object.keys(this.usedNamespaces)}}var te={exports:{}},ae={};var xe;function He(){if(xe)return ae;xe=1;var e=g;function r(a,m){return a===m&&(a!==0||1/a===1/m)||a!==a&&m!==m}var n=typeof Object.is=="function"?Object.is:r,c=e.useState,i=e.useEffect,o=e.useLayoutEffect,d=e.useDebugValue;function v(a,m){var l=m(),h=c({inst:{value:l,getSnapshot:m}}),w=h[0].inst,A=h[1];return o(function(){w.value=l,w.getSnapshot=m,y(w)&&A({inst:w})},[a,l,m]),i(function(){return y(w)&&A({inst:w}),a(function(){y(w)&&A({inst:w})})},[a]),d(l),l}function y(a){var m=a.getSnapshot;a=a.value;try{var l=m();return!n(a,l)}catch{return!0}}function p(a,m){return m()}var E=typeof window>"u"||typeof window.document>"u"||typeof window.document.createElement>"u"?p:v;return ae.useSyncExternalStore=e.useSyncExternalStore!==void 0?e.useSyncExternalStore:E,ae}var oe={};var be;function We(){return be||(be=1,process.env.NODE_ENV!=="production"&&(function(){function e(l,h){return l===h&&(l!==0||1/l===1/h)||l!==l&&h!==h}function r(l,h){E||i.startTransition===void 0||(E=!0,console.error("You are using an outdated, pre-release alpha of React 18 that does not support useSyncExternalStore. The use-sync-external-store shim will not work correctly. Upgrade to a newer pre-release."));var w=h();if(!a){var A=h();o(w,A)||(console.error("The result of getSnapshot should be cached to avoid an infinite loop"),a=!0)}A=d({inst:{value:w,getSnapshot:h}});var F=A[0].inst,M=A[1];return y(function(){F.value=w,F.getSnapshot=h,n(F)&&M({inst:F})},[l,w,h]),v(function(){return n(F)&&M({inst:F}),l(function(){n(F)&&M({inst:F})})},[l]),p(w),w}function n(l){var h=l.getSnapshot;l=l.value;try{var w=h();return!o(l,w)}catch{return!0}}function c(l,h){return h()}typeof __REACT_DEVTOOLS_GLOBAL_HOOK__<"u"&&typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart=="function"&&__REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(Error());var i=g,o=typeof Object.is=="function"?Object.is:e,d=i.useState,v=i.useEffect,y=i.useLayoutEffect,p=i.useDebugValue,E=!1,a=!1,m=typeof window>"u"||typeof window.document>"u"||typeof window.document.createElement>"u"?c:r;oe.useSyncExternalStore=i.useSyncExternalStore!==void 0?i.useSyncExternalStore:m,typeof __REACT_DEVTOOLS_GLOBAL_HOOK__<"u"&&typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop=="function"&&__REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop(Error())})()),oe}var ve;function qe(){return ve||(ve=1,process.env.NODE_ENV==="production"?te.exports=He():te.exports=We()),te.exports}var ze=qe();const Ge={t:(e,r)=>q(r)?r:Le(r)&&q(r.defaultValue)?r.defaultValue:Array.isArray(e)?e[e.length-1]:e,ready:!1},Be=()=>()=>{},Je=(e,r={})=>{const{i18n:n}=r,{i18n:c,defaultNS:i}=g.useContext(Ye)||{},o=n||c||Ve();o&&!o.reportNamespaces&&(o.reportNamespaces=new Ue),o||pe(o,"NO_I18NEXT_INSTANCE","useTranslation: You will need to pass in an i18next instance by using initReactI18next");const d=g.useMemo(()=>({...Me(),...o?.options?.react,...r}),[o,r]),{useSuspense:v,keyPrefix:y}=d,p=i||o?.options?.defaultNS,E=q(p)?[p]:p||["translation"],a=g.useMemo(()=>E,E);o?.reportNamespaces?.addUsedNamespaces?.(a);const m=g.useRef(0),l=g.useCallback(D=>{if(!o)return Be;const{bindI18n:f,bindI18nStore:_}=d,u=()=>{m.current+=1,D()};return f&&o.on(f,u),_&&o.store.on(_,u),()=>{f&&f.split(" ").forEach(S=>o.off(S,u)),_&&_.split(" ").forEach(S=>o.store.off(S,u))}},[o,d]),h=g.useRef(),w=g.useCallback(()=>{if(!o)return Ge;const D=!!(o.isInitialized||o.initializedStoreOnce)&&a.every(H=>ke(H,o,d)),f=r.lng||o.language,_=m.current,u=h.current;if(u&&u.ready===D&&u.lng===f&&u.keyPrefix===y&&u.revision===_)return u;const L={t:o.getFixedT(f,d.nsMode==="fallback"?a:a[0],y),ready:D,lng:f,keyPrefix:y,revision:_};return h.current=L,L},[o,a,y,d,r.lng]),[A,F]=g.useState(0),{t:M,ready:Y}=ze.useSyncExternalStore(l,w,w);g.useEffect(()=>{if(o&&!Y&&!v){const D=()=>F(f=>f+1);r.lng?ye(o,r.lng,a,D):ne(o,a,D)}},[o,r.lng,a,Y,v,A]);const b=o||{},O=g.useRef(null),I=g.useRef(),T=D=>{const f=Object.getOwnPropertyDescriptors(D);f.__original&&delete f.__original;const _=Object.create(Object.getPrototypeOf(D),f);if(!Object.prototype.hasOwnProperty.call(_,"__original"))try{Object.defineProperty(_,"__original",{value:D,writable:!1,enumerable:!1,configurable:!1})}catch{}return _},P=g.useMemo(()=>{const D=b,f=D?.language;let _=D;D&&(O.current&&O.current.__original===D?I.current!==f?(_=T(D),O.current=_,I.current=f):_=O.current:(_=T(D),O.current=_,I.current=f));const u=[M,_,Y];return u.t=M,u.i18n=_,u.ready=Y,u},[M,b,Y,b.resolvedLanguage,b.language,b.languages]);if(o&&v&&!Y)throw new Promise(D=>{const f=()=>D();r.lng?ye(o,r.lng,a,f):ne(o,a,f)});return P},Xe=g.createContext(null);function Ke(e){const r=g.useContext(Xe),{t:n,i18n:c}=Je();return{t:n,language:r?.language||c.language||"en",changeLanguage:r?.changeLanguage||(async i=>{await c.changeLanguage(i)}),direction:r?.direction||"ltr",i18n:c}}const se="bg-blue-500 text-white",Qe=new Date,De={"calendar.today":"Today","calendar.month":"Month","calendar.week":"Week","calendar.day":"Day","calendar.newEvent":"New event","calendar.moreEvents":"+{{count}} more"};function we(){try{const e=Ke();return e.t("calendar.today")==="calendar.today"?{t:(n,c)=>{let i=De[n]||n;if(c)for(const[o,d]of Object.entries(c))i=i.replace(`{{${o}}}`,String(d));return i},language:"en"}:{t:e.t,language:e.language}}catch{return{t:(e,r)=>{let n=De[e]||e;if(r)for(const[c,i]of Object.entries(r))n=n.replace(`{{${c}}}`,String(i));return n},language:"en"}}}function le({events:e=[],view:r="month",currentDate:n=Qe,locale:c="default",onEventClick:i,onDateClick:o,onViewChange:d,onNavigate:v,onAddClick:y,onEventDrop:p,className:E}){const[a,m]=V.useState(r),[l,h]=V.useState(n),{t:w,language:A}=we(),F=c!=="default"?c:A;V.useEffect(()=>{h(n)},[n]),V.useEffect(()=>{m(r)},[r]);const M=V.useRef(d);M.current=d,V.useEffect(()=>{const u=window.matchMedia("(max-width: 639px)"),S=L=>{L.matches&&(m("day"),M.current?.("day"))};return S(u),u.addEventListener("change",S),()=>u.removeEventListener("change",S)},[]);const Y=()=>{const u=new Date(l);a==="month"?u.setMonth(u.getMonth()-1):a==="week"?u.setDate(u.getDate()-7):u.setDate(u.getDate()-1),h(u),v?.(u)},b=()=>{const u=new Date(l);a==="month"?u.setMonth(u.getMonth()+1):a==="week"?u.setDate(u.getDate()+7):u.setDate(u.getDate()+1),h(u),v?.(u)},O=()=>{const u=new Date;h(u),v?.(u)},I=u=>{m(u),d?.(u)},T=()=>{if(a==="month")return l.toLocaleDateString(F,{month:"long",year:"numeric"});if(a==="week"){const u=Se(l),S=new Date(u);return S.setDate(S.getDate()+6),`${u.toLocaleDateString(F,{month:"short",day:"numeric"})} - ${S.toLocaleDateString(F,{month:"short",day:"numeric",year:"numeric"})}`}else return l.toLocaleDateString(F,{weekday:"long",month:"long",day:"numeric",year:"numeric"})},P=V.useRef(0),D=u=>{P.current=u.touches[0].clientX},f=u=>{const S=P.current-u.changedTouches[0].clientX;if(Math.abs(S)>50){const L=new Date(l);a==="day"?L.setDate(L.getDate()+(S>0?1:-1)):a==="week"?L.setDate(L.getDate()+(S>0?7:-7)):L.setMonth(L.getMonth()+(S>0?1:-1)),h(L),v?.(L)}},_=u=>{u&&(h(u),v?.(u))};return s.jsxs("div",{role:"region","aria-label":"Calendar",className:C.cn("flex flex-col h-full bg-background min-w-0 overflow-hidden",E),children:[s.jsxs("div",{className:"flex flex-wrap items-center justify-between gap-2 p-2 sm:p-4 border-b min-w-0",children:[s.jsxs("div",{className:"flex items-center gap-4",children:[s.jsxs("div",{className:"flex items-center bg-muted/50 rounded-lg p-1 gap-1",children:[s.jsx(C.Button,{variant:"ghost",size:"sm",onClick:O,className:"h-8","aria-label":"Go to today",children:w("calendar.today")}),s.jsx("div",{className:"h-4 w-px bg-border mx-1"}),s.jsx(C.Button,{variant:"ghost",size:"icon","aria-label":"Previous period",onClick:Y,className:"h-8 w-8",children:s.jsx(Z.ChevronLeftIcon,{className:"h-4 w-4"})}),s.jsx(C.Button,{variant:"ghost",size:"icon","aria-label":"Next period",onClick:b,className:"h-8 w-8",children:s.jsx(Z.ChevronRightIcon,{className:"h-4 w-4"})})]}),s.jsxs(C.Popover,{children:[s.jsx(C.PopoverTrigger,{asChild:!0,children:s.jsxs(C.Button,{variant:"ghost","aria-label":`Current date: ${T()}`,className:C.cn("text-base sm:text-xl font-semibold h-auto px-2 sm:px-3 py-1 hover:bg-muted/50 transition-colors","flex items-center gap-2"),children:[s.jsx(Z.CalendarIcon,{className:"h-5 w-5 text-muted-foreground"}),s.jsx("span",{children:T()})]})}),s.jsx(C.PopoverContent,{className:"w-auto p-0",align:"start",children:s.jsx(C.Calendar,{mode:"single",selected:l,onSelect:_,initialFocus:!0,fromYear:2e3,toYear:2050})})]})]}),s.jsxs("div",{className:"flex items-center gap-2",children:[s.jsxs(C.Select,{value:a,onValueChange:I,children:[s.jsx(C.SelectTrigger,{className:"w-32 bg-background",children:s.jsx(C.SelectValue,{})}),s.jsxs(C.SelectContent,{children:[s.jsx(C.SelectItem,{value:"day",children:w("calendar.day")}),s.jsx(C.SelectItem,{value:"week",children:w("calendar.week")}),s.jsx(C.SelectItem,{value:"month",children:w("calendar.month")})]})]}),y&&s.jsxs(C.Button,{onClick:y,size:"sm",className:"gap-1",children:[s.jsx(Z.PlusIcon,{className:"h-4 w-4"}),w("calendar.newEvent")]})]})]}),s.jsxs("div",{className:"flex-1 overflow-auto",onTouchStart:D,onTouchEnd:f,children:[a==="month"&&s.jsx(et,{date:l,events:e,locale:F,onEventClick:i,onDateClick:o,onEventDrop:p}),a==="week"&&s.jsx(tt,{date:l,events:e,locale:F,onEventClick:i,onDateClick:o}),a==="day"&&s.jsx(rt,{date:l,events:e,onEventClick:i,onDateClick:o})]})]})}function Se(e){const r=new Date(e),n=r.getDay(),c=r.getDate()-n;return r.setDate(c),r}function Ze(e){const r=e.getFullYear(),n=e.getMonth(),c=new Date(r,n,1),i=new Date(r,n+1,0),o=c.getDay(),d=[];for(let y=o-1;y>=0;y--){const p=new Date(c.getTime());p.setDate(p.getDate()-(y+1)),d.push(p)}for(let y=1;y<=i.getDate();y++)d.push(new Date(r,n,y));const v=42-d.length;for(let y=1;y<=v;y++){const p=new Date(i.getTime());p.setDate(p.getDate()+y),d.push(p)}return d}function je(e,r){return e.getFullYear()===r.getFullYear()&&e.getMonth()===r.getMonth()&&e.getDate()===r.getDate()}function Ee(e,r){return r.filter(n=>{const c=new Date(n.start),i=n.end?new Date(n.end):new Date(c),o=new Date(e);o.setHours(0,0,0,0);const d=new Date(e);d.setHours(23,59,59,999);const v=new Date(c);v.setHours(0,0,0,0);const y=new Date(i);return y.setHours(23,59,59,999),o<=y&&d>=v})}function et({date:e,events:r,locale:n="default",onEventClick:c,onDateClick:i,onEventDrop:o}){const d=V.useMemo(()=>Ze(e),[e.getFullYear(),e.getMonth()]),v=V.useMemo(()=>new Date,[]),{t:y}=we(),p=V.useMemo(()=>{const b=new Date(2024,0,7);return Array.from({length:7},(O,I)=>{const T=new Date(b);return T.setDate(T.getDate()+I),T.toLocaleDateString(n,{weekday:"short"})})},[n]),[E,a]=V.useState(null),[m,l]=V.useState(null),h=V.useMemo(()=>{const b=new Map;for(const O of r){const I=new Date(O.start),T=O.end?new Date(O.end):new Date(I);I.setHours(0,0,0,0),T.setHours(0,0,0,0);const P=new Date(I);for(;P<=T;){const D=`${P.getFullYear()}-${P.getMonth()}-${P.getDate()}`,f=b.get(D);f?f.push(O):b.set(D,[O]),P.setDate(P.getDate()+1)}}return b},[r]),w=(b,O)=>{a(O.id),b.dataTransfer.effectAllowed="move",b.dataTransfer.setData("text/plain",String(O.id))},A=()=>{a(null),l(null)},F=(b,O)=>{b.preventDefault(),b.dataTransfer.dropEffect="move",l(O)},M=b=>{b.currentTarget.contains(b.relatedTarget)||l(null)},Y=(b,O)=>{if(b.preventDefault(),l(null),a(null),!o)return;const I=b.dataTransfer.getData("text/plain"),T=r.find(L=>String(L.id)===I);if(!T)return;const P=new Date(T.start),D=new Date(P);D.setHours(0,0,0,0);const f=new Date(O);f.setHours(0,0,0,0);const _=f.getTime()-D.getTime();if(_===0)return;const u=new Date(P.getTime()+_);let S;T.end&&(S=new Date(new Date(T.end).getTime()+_)),o(T,u,S)};return s.jsxs("div",{className:"flex flex-col h-full",children:[s.jsx("div",{role:"row",className:"grid grid-cols-7 border-b",children:p.map(b=>s.jsx("div",{role:"columnheader",className:"p-2 text-center text-sm font-medium text-muted-foreground border-r last:border-r-0",children:b},b))}),s.jsx("div",{role:"grid","aria-label":"Calendar grid",className:"grid grid-cols-7 flex-1 auto-rows-fr",children:d.map((b,O)=>{const I=`${b.getFullYear()}-${b.getMonth()}-${b.getDate()}`,T=h.get(I)||[],P=b.getMonth()===e.getMonth(),D=je(b,v);return s.jsxs("div",{role:"gridcell","aria-label":`${b.toLocaleDateString("default",{weekday:"long",month:"long",day:"numeric",year:"numeric"})}${T.length>0?`, ${T.length} event${T.length>1?"s":""}`:""}`,className:C.cn("border-b border-r last:border-r-0 p-2 min-h-[100px] cursor-pointer hover:bg-accent/50",!P&&"bg-muted/50 text-muted-foreground opacity-50",m===O&&"ring-2 ring-primary"),onClick:()=>i?.(b),onDragOver:f=>F(f,O),onDragLeave:M,onDrop:f=>Y(f,b),children:[s.jsx("div",{className:C.cn("text-sm font-medium mb-2",D&&"inline-flex items-center justify-center rounded-full bg-primary text-primary-foreground h-6 w-6"),...D?{"aria-current":"date"}:{},children:b.getDate()}),s.jsxs("div",{className:"space-y-1",children:[T.slice(0,3).map(f=>s.jsx("div",{role:"button",title:f.title,"aria-label":f.title,draggable:!!o,onDragStart:_=>w(_,f),onDragEnd:A,className:C.cn("text-xs px-2 py-1 rounded truncate cursor-pointer hover:opacity-80",f.color?.startsWith("#")?"text-white":f.color||se,E===f.id&&"opacity-50"),style:f.color&&f.color.startsWith("#")?{backgroundColor:f.color}:void 0,onClick:_=>{_.stopPropagation(),c?.(f)},children:f.title},f.id)),T.length>3&&s.jsx("div",{className:"text-xs text-muted-foreground px-2",children:y("calendar.moreEvents",{count:T.length-3})})]})]},O)})})]})}function tt({date:e,events:r,locale:n="default",onEventClick:c,onDateClick:i}){const o=V.useRef(null),d=a=>{i&&(o.current=setTimeout(()=>{i(a)},500))},v=()=>{o.current&&(clearTimeout(o.current),o.current=null)},y=Se(e),p=Array.from({length:7},(a,m)=>{const l=new Date(y);return l.setDate(l.getDate()+m),l}),E=new Date;return s.jsxs("div",{className:"flex flex-col h-full",children:[s.jsx("div",{className:"grid grid-cols-7 border-b",children:p.map(a=>{const m=je(a,E);return s.jsxs("div",{className:"p-3 text-center border-r last:border-r-0",children:[s.jsx("div",{className:"text-sm font-medium text-muted-foreground",children:a.toLocaleDateString(n,{weekday:"short"})}),s.jsx("div",{className:C.cn("text-lg font-semibold mt-1",m&&"inline-flex items-center justify-center rounded-full bg-primary text-primary-foreground h-8 w-8"),children:a.getDate()})]},a.toISOString())})}),s.jsx("div",{role:"grid",className:"grid grid-cols-7 flex-1",children:p.map(a=>{const m=Ee(a,r);return s.jsx("div",{role:"gridcell","aria-label":`${a.toLocaleDateString("default",{weekday:"long",month:"long",day:"numeric",year:"numeric"})}${m.length>0?`, ${m.length} event${m.length>1?"s":""}`:""}`,className:"border-r last:border-r-0 p-2 min-h-[400px] cursor-pointer hover:bg-accent/50",onClick:()=>i?.(a),onTouchStart:()=>d(a),onTouchEnd:v,children:s.jsx("div",{className:"space-y-2",children:m.map(l=>s.jsxs("div",{role:"button",title:l.title,"aria-label":l.title,className:C.cn("text-xs sm:text-sm px-2 sm:px-3 py-1.5 sm:py-2 rounded cursor-pointer hover:opacity-80",l.color?.startsWith("#")?"text-white":l.color||se),style:l.color&&l.color.startsWith("#")?{backgroundColor:l.color}:void 0,onClick:h=>{h.stopPropagation(),c?.(l)},children:[s.jsx("div",{className:"font-medium truncate",children:l.title}),!l.allDay&&s.jsx("div",{className:"text-xs opacity-90 mt-1",children:l.start.toLocaleTimeString("default",{hour:"numeric",minute:"2-digit"})})]},l.id))})},a.toISOString())})})]})}function rt({date:e,events:r,onEventClick:n,onDateClick:c}){const i=Ee(e,r),o=Array.from({length:24},(p,E)=>E),d=V.useRef(null),v=p=>{c&&(d.current=setTimeout(()=>{const E=new Date(e);E.setHours(p,0,0,0),c(E)},500))},y=()=>{d.current&&(clearTimeout(d.current),d.current=null)};return s.jsx("div",{className:"flex flex-col h-full",children:s.jsx("div",{role:"list",className:"flex-1 overflow-auto",children:o.map(p=>{const E=i.filter(a=>a.allDay?p===0:a.start.getHours()===p);return s.jsxs("div",{role:"listitem",className:"flex border-b min-h-[60px]",children:[s.jsx("div",{className:"w-20 p-2 text-sm text-muted-foreground border-r",children:p===0?"12 AM":p<12?`${p} AM`:p===12?"12 PM":`${p-12} PM`}),s.jsx("div",{className:"flex-1 p-2 space-y-2",onTouchStart:()=>v(p),onTouchEnd:y,children:E.map(a=>s.jsxs("div",{title:a.title,"aria-label":a.title,className:C.cn("px-2 sm:px-3 py-1.5 sm:py-2 rounded cursor-pointer hover:opacity-80",a.color?.startsWith("#")?"text-white":a.color||se),style:a.color&&a.color.startsWith("#")?{backgroundColor:a.color}:void 0,onClick:()=>n?.(a),children:[s.jsx("div",{className:"font-medium truncate",children:a.title}),!a.allDay&&s.jsxs("div",{className:"text-xs opacity-90 mt-1",children:[a.start.toLocaleTimeString("default",{hour:"numeric",minute:"2-digit"}),a.end&&` - ${a.end.toLocaleTimeString("default",{hour:"numeric",minute:"2-digit"})}`]})]},a.id))})]},p)})})})}function nt(e){const{onRefresh:r,threshold:n=80,enabled:c=!0}=e,i=g.useRef(null),[o,d]=g.useState(!1),[v,y]=g.useState(0),p=g.useRef(0),E=g.useCallback(l=>{if(!c||o)return;const h=i.current;h&&h.scrollTop===0&&(p.current=l.touches[0].clientY)},[c,o]),a=g.useCallback(l=>{if(!c||o||!p.current)return;const w=l.touches[0].clientY-p.current;w>0&&y(Math.min(w,n*1.5))},[c,o,n]),m=g.useCallback(async()=>{if(!c||o)return;const l=v;if(y(0),p.current=0,l>=n){d(!0);try{await r()}finally{d(!1)}}},[c,o,v,n,r]);return g.useEffect(()=>{const l=i.current;if(!(!l||!c))return l.addEventListener("touchstart",E,{passive:!0}),l.addEventListener("touchmove",a,{passive:!0}),l.addEventListener("touchend",m,{passive:!0}),()=>{l.removeEventListener("touchstart",E),l.removeEventListener("touchmove",a),l.removeEventListener("touchend",m)}},[E,a,m,c]),{ref:i,isRefreshing:o,pullDistance:v}}const at=g.createContext(null);at.displayName="MobileContext";function ot(e){return"data"in e&&e.data?e.data:"staticData"in e&&e.staticData?{provider:"value",items:e.staticData}:e.objectName?{provider:"object",object:e.objectName}:null}function st(e){if(e){if(typeof e=="string"){const r=e.split(" "),n=r[0],c=r[1]?.toLowerCase()==="desc"?"desc":"asc";return{[n]:c}}if(Array.isArray(e))return e.reduce((r,n)=>(n.field&&n.order&&(r[n.field]=n.order),r),{})}}function lt(e){return"filter"in e&&e.filter&&typeof e.filter=="object"&&"calendar"in e.filter?e.filter.calendar:e.calendar?e.calendar:e.startDateField||e.dateField?{startDateField:e.startDateField||e.dateField,endDateField:e.endDateField||e.endField,titleField:e.titleField||"name",colorField:e.colorField,allDayField:e.allDayField}:null}const _e=({schema:e,dataSource:r,className:n,onEventClick:c,onRowClick:i,onDateClick:o,onNavigate:d,onViewChange:v,onEventDrop:y,locale:p,...E})=>{const[a,m]=g.useState([]),[l,h]=g.useState(!0),[w,A]=g.useState(null),[F,M]=g.useState(null),[Y,b]=g.useState(new Date),[O,I]=g.useState("month"),[T,P]=g.useState(0),D=g.useCallback(async()=>{P(N=>N+1)},[]),{ref:f,isRefreshing:_,pullDistance:u}=nt({onRefresh:D,enabled:!!r&&!!e.objectName}),S=g.useMemo(()=>ot(e),[e.data,e.staticData,e.objectName]),L=g.useMemo(()=>lt(e),[e.filter,e.calendar,e.dateField,e.endField,e.titleField,e.colorField]),H=S?.provider==="value",Q=g.useRef(null);Q.current=F,g.useEffect(()=>{let N=!0;return(async()=>{try{if(!N)return;if(h(!0),H&&S?.provider==="value"){N&&(m(S.items),h(!1));return}if(e.data||E.data){const t=e.data||E.data;if(Array.isArray(t)){m(t),h(!1);return}}if(!r||typeof r.find!="function")throw new Error("DataSource required for object/api providers");if(S?.provider==="object"){const t=S.object,x=J.buildExpandFields(Q.current?.fields),j=await r.find(t,{$filter:e.filter,$orderby:st(e.sort),...x.length>0?{$expand:x}:{}});let k=J.extractRecords(j);N&&m(k)}else S?.provider==="api"&&(console.warn("API provider not yet implemented for ObjectCalendar"),N&&m([]));N&&h(!1)}catch(t){console.error("[ObjectCalendar] Error fetching data:",t),N&&(A(t),h(!1))}})(),()=>{N=!1}},[S,r,H,e.filter,e.sort,T]),g.useEffect(()=>{!H&&r&&(async()=>{try{if(!r)return;const $=S?.provider==="object"?S.object:e.objectName;if(!$)return;const t=await r.getObjectSchema($);M(t)}catch($){console.error("Failed to fetch object schema:",$)}})()},[e.objectName,r,H,S]);const re=g.useMemo(()=>{if(!L||!a.length)return[];const{startDateField:N,endDateField:$,titleField:t,colorField:x}=L;return a.map((j,k)=>{const z=j[N],G=$?j[$]:null,R=j[t]||"Untitled",W=x?j[x]:void 0;return{id:j.id||j._id||`event-${k}`,title:R,start:z?new Date(z):new Date,end:G?new Date(G):void 0,color:W,allDay:!G,data:j}}).filter(j=>!isNaN(j.start.getTime()))},[a,L]);g.useCallback(()=>{o?.(new Date)},[o]);const B=ue.useNavigationOverlay({navigation:e.navigation,objectName:e.objectName,onRowClick:i});return l?s.jsx("div",{className:n,children:s.jsx("div",{className:"flex items-center justify-center h-96",children:s.jsx("div",{className:"text-muted-foreground",children:"Loading calendar..."})})}):w?s.jsx("div",{className:n,children:s.jsx("div",{className:"flex items-center justify-center h-96",children:s.jsxs("div",{className:"text-destructive",children:["Error: ",w.message]})})}):L?s.jsxs("div",{ref:f,className:n,children:[u>0&&s.jsx("div",{className:"flex items-center justify-center text-xs text-muted-foreground",style:{height:u},children:_?"Refreshing…":"Pull to refresh"}),s.jsx("div",{className:"border rounded-lg bg-background h-[calc(100vh-120px)] sm:h-[calc(100vh-160px)] md:h-[calc(100vh-200px)] min-h-[400px] sm:min-h-[600px]",children:s.jsx(le,{events:re,currentDate:Y,view:e.defaultView||"month",locale:p,onEventClick:N=>{B.handleClick(N.data),c?.(N.data)},onDateClick:o,onNavigate:N=>{b(N),d?.(N)},onViewChange:N=>{I(N),v?.(N)},onAddClick:void 0,onEventDrop:y?(N,$,t)=>{y(N.data,$,t)}:void 0})}),B.isOverlay&&s.jsx(C.NavigationOverlay,{...B,title:"Event Details",children:N=>s.jsx("div",{className:"space-y-3",children:Object.entries(N).map(([$,t])=>s.jsxs("div",{className:"flex flex-col",children:[s.jsx("span",{className:"text-xs font-medium text-muted-foreground uppercase tracking-wide",children:$.replace(/_/g," ")}),s.jsx("span",{className:"text-sm",children:String(t??"—")})]},$))})})]}):s.jsx("div",{className:n,children:s.jsx("div",{className:"flex items-center justify-center h-96",children:s.jsx("div",{className:"text-muted-foreground",children:"Calendar configuration required. Please specify startDateField and titleField."})})})};J.ComponentRegistry.register("calendar-view",({schema:e,className:r,onAction:n,...c})=>{const i=g.useMemo(()=>!e.data||!Array.isArray(e.data)?[]:e.data.map((d,v)=>{const y=e.titleField||"title",p=e.startDateField||"start",E=e.endDateField||"end",a=e.colorField||"color",m=e.allDayField||"allDay";return{id:d._id||d.id||v,title:d[y]||"Untitled Event",start:new Date(d[p]),end:d[E]?new Date(d[E]):void 0,allDay:d[m],color:d[a],data:d}}),[e.data,e.titleField,e.startDateField,e.endDateField,e.colorField,e.allDayField]),o=d=>{n?.({type:"event-click",payload:d})};return s.jsx(le,{className:r,events:i,onEventClick:o,...c})},{namespace:"plugin-calendar",label:"Calendar View",inputs:[{name:"data",type:"array",label:"Data",description:"Array of record objects to display as events"},{name:"titleField",type:"string",label:"Title Field",defaultValue:"title",description:"Field name to use for event title"},{name:"startDateField",type:"string",label:"Start Date Field",defaultValue:"start",description:"Field name for event start date"},{name:"endDateField",type:"string",label:"End Date Field",defaultValue:"end",description:"Field name for event end date (optional)"},{name:"allDayField",type:"string",label:"All Day Field",defaultValue:"allDay",description:"Field name for all-day flag"},{name:"colorField",type:"string",label:"Color Field",defaultValue:"color",description:"Field name for event color"},{name:"colorMapping",type:"object",label:"Color Mapping",description:'Map field values to colors (e.g., {meeting: "blue", deadline: "red"})'},{name:"view",type:"enum",enum:["month","week","day"],defaultValue:"month",label:"View Mode",description:"Calendar view mode (month, week, or day)"},{name:"currentDate",type:"string",label:"Current Date",description:"ISO date string for initial calendar date"},{name:"allowCreate",type:"boolean",label:"Allow Create",defaultValue:!1,description:"Allow creating events by clicking on dates"},{name:"className",type:"string",label:"CSS Class"}],defaultProps:{view:"month",titleField:"title",startDateField:"start",endDateField:"end",allDayField:"allDay",colorField:"color",allowCreate:!1,data:[{id:1,title:"Team Meeting",start:new Date(new Date().setHours(10,0,0,0)).toISOString(),end:new Date(new Date().setHours(11,0,0,0)).toISOString(),color:"#3b82f6",allDay:!1},{id:2,title:"Project Deadline",start:new Date(new Date().setDate(new Date().getDate()+3)).toISOString(),color:"#ef4444",allDay:!0},{id:3,title:"Conference",start:new Date(new Date().setDate(new Date().getDate()+7)).toISOString(),end:new Date(new Date().setDate(new Date().getDate()+9)).toISOString(),color:"#10b981",allDay:!0}],className:"h-[600px] border rounded-lg"}});const ie=({schema:e,data:r,loading:n,...c})=>{const{dataSource:i}=ue.useSchemaContext()||{};return s.jsx(_e,{schema:e,dataSource:i,...c})};J.ComponentRegistry.register("object-calendar",ie,{namespace:"plugin-calendar",label:"Object Calendar",category:"view",inputs:[{name:"objectName",type:"string",label:"Object Name",required:!0},{name:"calendar",type:"object",label:"Calendar Config",description:"startDateField, endDateField, titleField, colorField"}]}),J.ComponentRegistry.register("calendar",ie,{namespace:"view",label:"Calendar View",category:"view",inputs:[{name:"objectName",type:"string",label:"Object Name",required:!0},{name:"calendar",type:"object",label:"Calendar Config",description:"startDateField, endDateField, titleField, colorField"}]}),U.CalendarView=le,U.ObjectCalendar=_e,U.ObjectCalendarRenderer=ie,Object.defineProperty(U,Symbol.toStringTag,{value:"Module"})}));
@@ -1 +1 @@
1
- {"version":3,"file":"CalendarView.d.ts","sourceRoot":"","sources":["../../src/CalendarView.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAsBH,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,GAAG,MAAM,CAAA;IACnB,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,IAAI,CAAA;IACX,GAAG,CAAC,EAAE,IAAI,CAAA;IACV,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,GAAG,CAAA;CACX;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,CAAC,EAAE,aAAa,EAAE,CAAA;IACxB,IAAI,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,KAAK,CAAA;IAC/B,WAAW,CAAC,EAAE,IAAI,CAAA;IAClB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAA;IAC7C,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAA;IAClC,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,KAAK,KAAK,IAAI,CAAA;IACvD,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAA;IACjC,UAAU,CAAC,EAAE,MAAM,IAAI,CAAA;IACvB,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,CAAC,EAAE,IAAI,KAAK,IAAI,CAAA;IAC3E,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,iBAAS,YAAY,CAAC,EACpB,MAAW,EACX,IAAc,EACd,WAAwB,EACxB,MAAkB,EAClB,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,UAAU,EACV,UAAU,EACV,WAAW,EACX,SAAS,GACV,EAAE,iBAAiB,2CAoOnB;AAobD,OAAO,EAAE,YAAY,EAAE,CAAA"}
1
+ {"version":3,"file":"CalendarView.d.ts","sourceRoot":"","sources":["../../src/CalendarView.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AA2EH,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,GAAG,MAAM,CAAA;IACnB,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,IAAI,CAAA;IACX,GAAG,CAAC,EAAE,IAAI,CAAA;IACV,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,GAAG,CAAA;CACX;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,CAAC,EAAE,aAAa,EAAE,CAAA;IACxB,IAAI,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,KAAK,CAAA;IAC/B,WAAW,CAAC,EAAE,IAAI,CAAA;IAClB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAA;IAC7C,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAA;IAClC,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,KAAK,KAAK,IAAI,CAAA;IACvD,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAA;IACjC,UAAU,CAAC,EAAE,MAAM,IAAI,CAAA;IACvB,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,CAAC,EAAE,IAAI,KAAK,IAAI,CAAA;IAC3E,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,iBAAS,YAAY,CAAC,EACpB,MAAW,EACX,IAAc,EACd,WAAiC,EACjC,MAAkB,EAClB,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,UAAU,EACV,UAAU,EACV,WAAW,EACX,SAAS,GACV,EAAE,iBAAiB,2CAuOnB;AAwdD,OAAO,EAAE,YAAY,EAAE,CAAA"}
@@ -23,6 +23,8 @@ export interface ObjectCalendarProps {
23
23
  onDelete?: (record: any) => void;
24
24
  onNavigate?: (date: Date) => void;
25
25
  onViewChange?: (view: 'month' | 'week' | 'day') => void;
26
+ onEventDrop?: (record: any, newStart: Date, newEnd?: Date) => void;
27
+ locale?: string;
26
28
  }
27
29
  export declare const ObjectCalendar: React.FC<ObjectCalendarProps>;
28
30
  //# sourceMappingURL=ObjectCalendar.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ObjectCalendar.d.ts","sourceRoot":"","sources":["../../src/ObjectCalendar.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAoD,MAAM,OAAO,CAAC;AACzE,OAAO,KAAK,EAAE,gBAAgB,EAAE,UAAU,EAA4B,MAAM,kBAAkB,CAAC;AAM/F,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,UAAU,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,GAAG,CAAC;IACb,IAAI,CAAC,EAAE,GAAG,CAAC;IACX,wBAAwB;IACxB,WAAW,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,KAAK,CAAC;CACxC;AAED,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,gBAAgB,GAAG,cAAc,CAAC;IAC1C,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,IAAI,CAAC;IACrC,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,IAAI,CAAC;IACnC,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;IACnC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,IAAI,CAAC;IAC/B,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,IAAI,CAAC;IACjC,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;IAClC,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,KAAK,KAAK,IAAI,CAAC;CACzD;AAkFD,eAAO,MAAM,cAAc,EAAE,KAAK,CAAC,EAAE,CAAC,mBAAmB,CAqQxD,CAAC"}
1
+ {"version":3,"file":"ObjectCalendar.d.ts","sourceRoot":"","sources":["../../src/ObjectCalendar.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAA4D,MAAM,OAAO,CAAC;AACjF,OAAO,KAAK,EAAE,gBAAgB,EAAE,UAAU,EAA4B,MAAM,kBAAkB,CAAC;AAO/F,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,UAAU,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,GAAG,CAAC;IACb,IAAI,CAAC,EAAE,GAAG,CAAC;IACX,wBAAwB;IACxB,WAAW,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,KAAK,CAAC;CACxC;AAED,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,gBAAgB,GAAG,cAAc,CAAC;IAC1C,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,IAAI,CAAC;IACrC,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,IAAI,CAAC;IACnC,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;IACnC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,IAAI,CAAC;IAC/B,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,IAAI,CAAC;IACjC,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;IAClC,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,KAAK,KAAK,IAAI,CAAC;IACxD,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,CAAC,EAAE,IAAI,KAAK,IAAI,CAAC;IACnE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAkFD,eAAO,MAAM,cAAc,EAAE,KAAK,CAAC,EAAE,CAAC,mBAAmB,CAwQxD,CAAC"}
@@ -6,5 +6,6 @@ export { CalendarView } from './CalendarView';
6
6
  export type { CalendarViewProps, CalendarEvent } from './CalendarView';
7
7
  export declare const ObjectCalendarRenderer: React.FC<{
8
8
  schema: any;
9
+ [key: string]: any;
9
10
  }>;
10
11
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAG5D,OAAO,EAAE,cAAc,EAAE,CAAC;AAC1B,YAAY,EAAE,mBAAmB,EAAE,CAAC;AAGpC,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,YAAY,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAGvE,OAAO,0BAA0B,CAAC;AAGlC,eAAO,MAAM,sBAAsB,EAAE,KAAK,CAAC,EAAE,CAAC;IAAE,MAAM,EAAE,GAAG,CAAA;CAAE,CAG5D,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAG5D,OAAO,EAAE,cAAc,EAAE,CAAC;AAC1B,YAAY,EAAE,mBAAmB,EAAE,CAAC;AAGpC,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,YAAY,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAGvE,OAAO,0BAA0B,CAAC;AAGlC,eAAO,MAAM,sBAAsB,EAAE,KAAK,CAAC,EAAE,CAAC;IAAE,MAAM,EAAE,GAAG,CAAC;IAAC,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;CAAE,CAGhF,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@object-ui/plugin-calendar",
3
- "version": "3.0.3",
3
+ "version": "3.1.0",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "description": "Calendar view plugins for Object UI - includes both ObjectQL-integrated and standalone calendar components",
@@ -24,26 +24,27 @@
24
24
  }
25
25
  },
26
26
  "dependencies": {
27
- "lucide-react": "^0.563.0",
28
- "@object-ui/components": "3.0.3",
29
- "@object-ui/core": "3.0.3",
30
- "@object-ui/fields": "3.0.3",
31
- "@object-ui/mobile": "3.0.3",
32
- "@object-ui/react": "3.0.3",
33
- "@object-ui/types": "3.0.3"
27
+ "lucide-react": "^0.576.0",
28
+ "@object-ui/components": "3.1.0",
29
+ "@object-ui/core": "3.1.0",
30
+ "@object-ui/fields": "3.1.0",
31
+ "@object-ui/i18n": "3.1.0",
32
+ "@object-ui/mobile": "3.1.0",
33
+ "@object-ui/react": "3.1.0",
34
+ "@object-ui/types": "3.1.0"
34
35
  },
35
36
  "peerDependencies": {
36
37
  "react": "^18.0.0 || ^19.0.0",
37
38
  "react-dom": "^18.0.0 || ^19.0.0"
38
39
  },
39
40
  "devDependencies": {
40
- "@types/react": "19.2.13",
41
+ "@types/react": "19.2.14",
41
42
  "@types/react-dom": "19.2.3",
42
43
  "@vitejs/plugin-react": "^5.1.4",
43
44
  "typescript": "^5.9.3",
44
45
  "vite": "^7.3.1",
45
46
  "vite-plugin-dts": "^4.5.4",
46
- "@object-ui/data-objectstack": "3.0.3"
47
+ "@object-ui/data-objectstack": "3.1.0"
47
48
  },
48
49
  "scripts": {
49
50
  "build": "vite build",
@@ -95,7 +95,7 @@ describe('CalendarView', () => {
95
95
 
96
96
  const triggerButton = dateLabel.closest('button');
97
97
  expect(triggerButton).toBeInTheDocument();
98
- expect(triggerButton).toHaveClass('text-xl font-semibold');
98
+ expect(triggerButton).toHaveClass('text-base sm:text-xl font-semibold');
99
99
  });
100
100
 
101
101
  it('opens date picker on click', () => {
@@ -23,8 +23,61 @@ import {
23
23
  PopoverContent,
24
24
  PopoverTrigger
25
25
  } from "@object-ui/components"
26
+ import { useObjectTranslation } from "@object-ui/i18n"
26
27
 
27
28
  const DEFAULT_EVENT_COLOR = "bg-blue-500 text-white"
29
+ const STABLE_DEFAULT_DATE = new Date()
30
+
31
+ // Default English translations for fallback when I18nProvider is not available
32
+ const DEFAULT_TRANSLATIONS: Record<string, string> = {
33
+ 'calendar.today': 'Today',
34
+ 'calendar.month': 'Month',
35
+ 'calendar.week': 'Week',
36
+ 'calendar.day': 'Day',
37
+ 'calendar.newEvent': 'New event',
38
+ 'calendar.moreEvents': '+{{count}} more',
39
+ }
40
+
41
+ /**
42
+ * Safe wrapper for useObjectTranslation that falls back to English defaults
43
+ * when I18nProvider is not available (e.g., standalone usage outside console).
44
+ */
45
+ function useCalendarTranslation() {
46
+ try {
47
+ const result = useObjectTranslation()
48
+ // Check if i18n is properly initialized by testing a known key
49
+ const testValue = result.t('calendar.today')
50
+ if (testValue === 'calendar.today') {
51
+ // i18n returned the key itself — not initialized
52
+ return {
53
+ t: (key: string, options?: Record<string, unknown>) => {
54
+ let value = DEFAULT_TRANSLATIONS[key] || key
55
+ if (options) {
56
+ for (const [k, v] of Object.entries(options)) {
57
+ value = value.replace(`{{${k}}}`, String(v))
58
+ }
59
+ }
60
+ return value
61
+ },
62
+ language: 'en',
63
+ }
64
+ }
65
+ return { t: result.t, language: result.language }
66
+ } catch {
67
+ return {
68
+ t: (key: string, options?: Record<string, unknown>) => {
69
+ let value = DEFAULT_TRANSLATIONS[key] || key
70
+ if (options) {
71
+ for (const [k, v] of Object.entries(options)) {
72
+ value = value.replace(`{{${k}}}`, String(v))
73
+ }
74
+ }
75
+ return value
76
+ },
77
+ language: 'en',
78
+ }
79
+ }
80
+ }
28
81
 
29
82
  export interface CalendarEvent {
30
83
  id: string | number
@@ -53,7 +106,7 @@ export interface CalendarViewProps {
53
106
  function CalendarView({
54
107
  events = [],
55
108
  view = "month",
56
- currentDate = new Date(),
109
+ currentDate = STABLE_DEFAULT_DATE,
57
110
  locale = "default",
58
111
  onEventClick,
59
112
  onDateClick,
@@ -65,6 +118,8 @@ function CalendarView({
65
118
  }: CalendarViewProps) {
66
119
  const [selectedView, setSelectedView] = React.useState(view)
67
120
  const [selectedDate, setSelectedDate] = React.useState(currentDate)
121
+ const { t, language } = useCalendarTranslation()
122
+ const effectiveLocale = locale !== "default" ? locale : language
68
123
 
69
124
  // Sync state if props change
70
125
  React.useEffect(() => {
@@ -131,7 +186,7 @@ function CalendarView({
131
186
 
132
187
  const getDateLabel = () => {
133
188
  if (selectedView === "month") {
134
- return selectedDate.toLocaleDateString(locale, {
189
+ return selectedDate.toLocaleDateString(effectiveLocale, {
135
190
  month: "long",
136
191
  year: "numeric",
137
192
  })
@@ -139,16 +194,16 @@ function CalendarView({
139
194
  const weekStart = getWeekStart(selectedDate)
140
195
  const weekEnd = new Date(weekStart)
141
196
  weekEnd.setDate(weekEnd.getDate() + 6)
142
- return `${weekStart.toLocaleDateString(locale, {
197
+ return `${weekStart.toLocaleDateString(effectiveLocale, {
143
198
  month: "short",
144
199
  day: "numeric",
145
- })} - ${weekEnd.toLocaleDateString(locale, {
200
+ })} - ${weekEnd.toLocaleDateString(effectiveLocale, {
146
201
  month: "short",
147
202
  day: "numeric",
148
203
  year: "numeric",
149
204
  })}`
150
205
  } else {
151
- return selectedDate.toLocaleDateString(locale, {
206
+ return selectedDate.toLocaleDateString(effectiveLocale, {
152
207
  weekday: "long",
153
208
  month: "long",
154
209
  day: "numeric",
@@ -182,13 +237,13 @@ function CalendarView({
182
237
  }
183
238
 
184
239
  return (
185
- <div role="region" aria-label="Calendar" className={cn("flex flex-col h-full bg-background", className)}>
240
+ <div role="region" aria-label="Calendar" className={cn("flex flex-col h-full bg-background min-w-0 overflow-hidden", className)}>
186
241
  {/* Header */}
187
- <div className="flex items-center justify-between p-4 border-b">
242
+ <div className="flex flex-wrap items-center justify-between gap-2 p-2 sm:p-4 border-b min-w-0">
188
243
  <div className="flex items-center gap-4">
189
244
  <div className="flex items-center bg-muted/50 rounded-lg p-1 gap-1">
190
245
  <Button variant="ghost" size="sm" onClick={handleToday} className="h-8" aria-label="Go to today">
191
- Today
246
+ {t('calendar.today')}
192
247
  </Button>
193
248
  <div className="h-4 w-px bg-border mx-1" />
194
249
  <Button
@@ -217,7 +272,7 @@ function CalendarView({
217
272
  variant="ghost"
218
273
  aria-label={`Current date: ${getDateLabel()}`}
219
274
  className={cn(
220
- "text-xl font-semibold h-auto px-3 py-1 hover:bg-muted/50 transition-colors",
275
+ "text-base sm:text-xl font-semibold h-auto px-2 sm:px-3 py-1 hover:bg-muted/50 transition-colors",
221
276
  "flex items-center gap-2"
222
277
  )}
223
278
  >
@@ -244,16 +299,16 @@ function CalendarView({
244
299
  <SelectValue />
245
300
  </SelectTrigger>
246
301
  <SelectContent>
247
- <SelectItem value="day">Day</SelectItem>
248
- <SelectItem value="week">Week</SelectItem>
249
- <SelectItem value="month">Month</SelectItem>
302
+ <SelectItem value="day">{t('calendar.day')}</SelectItem>
303
+ <SelectItem value="week">{t('calendar.week')}</SelectItem>
304
+ <SelectItem value="month">{t('calendar.month')}</SelectItem>
250
305
  </SelectContent>
251
306
  </Select>
252
307
 
253
308
  {onAddClick && (
254
309
  <Button onClick={onAddClick} size="sm" className="gap-1">
255
310
  <PlusIcon className="h-4 w-4" />
256
- New
311
+ {t('calendar.newEvent')}
257
312
  </Button>
258
313
  )}
259
314
  </div>
@@ -265,6 +320,7 @@ function CalendarView({
265
320
  <MonthView
266
321
  date={selectedDate}
267
322
  events={events}
323
+ locale={effectiveLocale}
268
324
  onEventClick={onEventClick}
269
325
  onDateClick={onDateClick}
270
326
  onEventDrop={onEventDrop}
@@ -274,7 +330,7 @@ function CalendarView({
274
330
  <WeekView
275
331
  date={selectedDate}
276
332
  events={events}
277
- locale={locale}
333
+ locale={effectiveLocale}
278
334
  onEventClick={onEventClick}
279
335
  onDateClick={onDateClick}
280
336
  />
@@ -362,18 +418,50 @@ function getEventsForDate(date: Date, events: CalendarEvent[]): CalendarEvent[]
362
418
  interface MonthViewProps {
363
419
  date: Date
364
420
  events: CalendarEvent[]
421
+ locale?: string
365
422
  onEventClick?: (event: CalendarEvent) => void
366
423
  onDateClick?: (date: Date) => void
367
424
  onEventDrop?: (event: CalendarEvent, newStart: Date, newEnd?: Date) => void
368
425
  }
369
426
 
370
- function MonthView({ date, events, onEventClick, onDateClick, onEventDrop }: MonthViewProps) {
371
- const days = getMonthDays(date)
372
- const today = new Date()
373
- const weekDays = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
427
+ function MonthView({ date, events, locale = "default", onEventClick, onDateClick, onEventDrop }: MonthViewProps) {
428
+ const days = React.useMemo(() => getMonthDays(date), [date.getFullYear(), date.getMonth()])
429
+ const today = React.useMemo(() => new Date(), [])
430
+ const { t } = useCalendarTranslation()
431
+ const weekDays = React.useMemo(() => {
432
+ const refSunday = new Date(2024, 0, 7)
433
+ return Array.from({ length: 7 }, (_, i) => {
434
+ const d = new Date(refSunday)
435
+ d.setDate(d.getDate() + i)
436
+ return d.toLocaleDateString(locale, { weekday: "short" })
437
+ })
438
+ }, [locale])
374
439
  const [draggedEventId, setDraggedEventId] = React.useState<string | number | null>(null)
375
440
  const [dropTargetIndex, setDropTargetIndex] = React.useState<number | null>(null)
376
441
 
442
+ // Pre-build event index by date key for O(1) lookup per cell instead of O(N)
443
+ const eventsByDate = React.useMemo(() => {
444
+ const map = new Map<string, CalendarEvent[]>()
445
+ for (const event of events) {
446
+ const eventStart = new Date(event.start)
447
+ const eventEnd = event.end ? new Date(event.end) : new Date(eventStart)
448
+ eventStart.setHours(0, 0, 0, 0)
449
+ eventEnd.setHours(0, 0, 0, 0)
450
+ const cursor = new Date(eventStart)
451
+ while (cursor <= eventEnd) {
452
+ const key = `${cursor.getFullYear()}-${cursor.getMonth()}-${cursor.getDate()}`
453
+ const arr = map.get(key)
454
+ if (arr) {
455
+ arr.push(event)
456
+ } else {
457
+ map.set(key, [event])
458
+ }
459
+ cursor.setDate(cursor.getDate() + 1)
460
+ }
461
+ }
462
+ return map
463
+ }, [events])
464
+
377
465
  const handleDragStart = (e: React.DragEvent, event: CalendarEvent) => {
378
466
  setDraggedEventId(event.id)
379
467
  e.dataTransfer.effectAllowed = "move"
@@ -447,7 +535,8 @@ function MonthView({ date, events, onEventClick, onDateClick, onEventDrop }: Mon
447
535
  {/* Calendar days */}
448
536
  <div role="grid" aria-label="Calendar grid" className="grid grid-cols-7 flex-1 auto-rows-fr">
449
537
  {days.map((day, index) => {
450
- const dayEvents = getEventsForDate(day, events)
538
+ const key = `${day.getFullYear()}-${day.getMonth()}-${day.getDate()}`
539
+ const dayEvents = eventsByDate.get(key) || []
451
540
  const isCurrentMonth = day.getMonth() === date.getMonth()
452
541
  const isToday = isSameDay(day, today)
453
542
 
@@ -458,7 +547,7 @@ function MonthView({ date, events, onEventClick, onDateClick, onEventDrop }: Mon
458
547
  aria-label={`${day.toLocaleDateString("default", { weekday: "long", month: "long", day: "numeric", year: "numeric" })}${dayEvents.length > 0 ? `, ${dayEvents.length} event${dayEvents.length > 1 ? "s" : ""}` : ""}`}
459
548
  className={cn(
460
549
  "border-b border-r last:border-r-0 p-2 min-h-[100px] cursor-pointer hover:bg-accent/50",
461
- !isCurrentMonth && "bg-muted/30 text-muted-foreground",
550
+ !isCurrentMonth && "bg-muted/50 text-muted-foreground opacity-50",
462
551
  dropTargetIndex === index && "ring-2 ring-primary"
463
552
  )}
464
553
  onClick={() => onDateClick?.(day)}
@@ -468,7 +557,7 @@ function MonthView({ date, events, onEventClick, onDateClick, onEventDrop }: Mon
468
557
  >
469
558
  <div
470
559
  className={cn(
471
- "text-sm font-medium mb-1",
560
+ "text-sm font-medium mb-2",
472
561
  isToday &&
473
562
  "inline-flex items-center justify-center rounded-full bg-primary text-primary-foreground h-6 w-6"
474
563
  )}
@@ -481,13 +570,14 @@ function MonthView({ date, events, onEventClick, onDateClick, onEventDrop }: Mon
481
570
  <div
482
571
  key={event.id}
483
572
  role="button"
573
+ title={event.title}
484
574
  aria-label={event.title}
485
575
  draggable={!!onEventDrop}
486
576
  onDragStart={(e) => handleDragStart(e, event)}
487
577
  onDragEnd={handleDragEnd}
488
578
  className={cn(
489
579
  "text-xs px-2 py-1 rounded truncate cursor-pointer hover:opacity-80",
490
- event.color || DEFAULT_EVENT_COLOR,
580
+ event.color?.startsWith("#") ? "text-white" : (event.color || DEFAULT_EVENT_COLOR),
491
581
  draggedEventId === event.id && "opacity-50"
492
582
  )}
493
583
  style={
@@ -505,7 +595,7 @@ function MonthView({ date, events, onEventClick, onDateClick, onEventDrop }: Mon
505
595
  ))}
506
596
  {dayEvents.length > 3 && (
507
597
  <div className="text-xs text-muted-foreground px-2">
508
- +{dayEvents.length - 3} more
598
+ {t('calendar.moreEvents', { count: dayEvents.length - 3 })}
509
599
  </div>
510
600
  )}
511
601
  </div>
@@ -597,10 +687,11 @@ function WeekView({ date, events, locale = "default", onEventClick, onDateClick
597
687
  <div
598
688
  key={event.id}
599
689
  role="button"
690
+ title={event.title}
600
691
  aria-label={event.title}
601
692
  className={cn(
602
693
  "text-xs sm:text-sm px-2 sm:px-3 py-1.5 sm:py-2 rounded cursor-pointer hover:opacity-80",
603
- event.color || DEFAULT_EVENT_COLOR
694
+ event.color?.startsWith("#") ? "text-white" : (event.color || DEFAULT_EVENT_COLOR)
604
695
  )}
605
696
  style={
606
697
  event.color && event.color.startsWith("#")
@@ -689,10 +780,11 @@ function DayView({ date, events, onEventClick, onDateClick }: DayViewProps) {
689
780
  {hourEvents.map((event) => (
690
781
  <div
691
782
  key={event.id}
783
+ title={event.title}
692
784
  aria-label={event.title}
693
785
  className={cn(
694
786
  "px-2 sm:px-3 py-1.5 sm:py-2 rounded cursor-pointer hover:opacity-80",
695
- event.color || DEFAULT_EVENT_COLOR
787
+ event.color?.startsWith("#") ? "text-white" : (event.color || DEFAULT_EVENT_COLOR)
696
788
  )}
697
789
  style={
698
790
  event.color && event.color.startsWith("#")