@object-ui/layout 0.5.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,6 +1,6 @@
1
- (function(u,b){typeof exports=="object"&&typeof module<"u"?b(exports,require("@object-ui/core"),require("react"),require("@object-ui/components"),require("@object-ui/react"),require("react-router-dom")):typeof define=="function"&&define.amd?define(["exports","@object-ui/core","react","@object-ui/components","@object-ui/react","react-router-dom"],b):(u=typeof globalThis<"u"?globalThis:u||self,b(u.ObjectUILayout={},u.core,u.require$$0,u.components,u.react,u.reactRouterDom))})(this,(function(u,b,H,f,X,N){"use strict";var E={exports:{}},g={};var O;function B(){if(O)return g;O=1;var a=Symbol.for("react.transitional.element"),d=Symbol.for("react.fragment");function c(m,s,o){var p=null;if(o!==void 0&&(p=""+o),s.key!==void 0&&(p=""+s.key),"key"in s){o={};for(var x in s)x!=="key"&&(o[x]=s[x])}else o=s;return s=o.ref,{$$typeof:a,type:m,key:p,ref:s!==void 0?s:null,props:o}}return g.Fragment=d,g.jsx=c,g.jsxs=c,g}var _={};var w;function Z(){return w||(w=1,process.env.NODE_ENV!=="production"&&(function(){function a(e){if(e==null)return null;if(typeof e=="function")return e.$$typeof===me?null:e.displayName||e.name||null;if(typeof e=="string")return e;switch(e){case y:return"Fragment";case oe:return"Profiler";case ne:return"StrictMode";case ue:return"Suspense";case ce:return"SuspenseList";case de: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 ae:return"Portal";case ie:return e.displayName||"Context";case se:return(e._context.displayName||"Context")+".Consumer";case le:var r=e.render;return e=e.displayName,e||(e=r.displayName||r.name||"",e=e!==""?"ForwardRef("+e+")":"ForwardRef"),e;case fe:return r=e.displayName||null,r!==null?r:a(e.type)||"Memo";case T:r=e._payload,e=e._init;try{return a(e(r))}catch{}}return null}function d(e){return""+e}function c(e){try{d(e);var r=!1}catch{r=!0}if(r){r=console;var n=r.error,i=typeof Symbol=="function"&&Symbol.toStringTag&&e[Symbol.toStringTag]||e.constructor.name||"Object";return n.call(r,"The provided key is an unsupported type %s. This value must be coerced to a string before using it here.",i),d(e)}}function m(e){if(e===y)return"<>";if(typeof e=="object"&&e!==null&&e.$$typeof===T)return"<...>";try{var r=a(e);return r?"<"+r+">":"<...>"}catch{return"<...>"}}function s(){var e=S.A;return e===null?null:e.getOwner()}function o(){return Error("react-stack-top-frame")}function p(e){if(U.call(e,"key")){var r=Object.getOwnPropertyDescriptor(e,"key").get;if(r&&r.isReactWarning)return!1}return e.key!==void 0}function x(e,r){function n(){W||(W=!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)",r))}n.isReactWarning=!0,Object.defineProperty(e,"key",{get:n,configurable:!0})}function re(){var e=a(this.type);return G[e]||(G[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 te(e,r,n,i,R,P){var l=n.ref;return e={$$typeof:D,type:e,key:r,props:n,_owner:i},(l!==void 0?l:null)!==null?Object.defineProperty(e,"ref",{enumerable:!1,get:re}):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:R}),Object.defineProperty(e,"_debugTask",{configurable:!1,enumerable:!1,writable:!0,value:P}),Object.freeze&&(Object.freeze(e.props),Object.freeze(e)),e}function M(e,r,n,i,R,P){var l=r.children;if(l!==void 0)if(i)if(be(l)){for(i=0;i<l.length;i++)F(l[i]);Object.freeze&&Object.freeze(l)}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 F(l);if(U.call(r,"key")){l=a(e);var v=Object.keys(r).filter(function(pe){return pe!=="key"});i=0<v.length?"{key: someKey, "+v.join(": ..., ")+": ...}":"{key: someKey}",z[l+i]||(v=0<v.length?"{"+v.join(": ..., ")+": ...}":"{}",console.error(`A props object containing a "key" prop is being spread into JSX:
1
+ (function(f,b){typeof exports=="object"&&typeof module<"u"?b(exports,require("@object-ui/core"),require("react"),require("@object-ui/components"),require("@object-ui/react"),require("react-router-dom")):typeof define=="function"&&define.amd?define(["exports","@object-ui/core","react","@object-ui/components","@object-ui/react","react-router-dom"],b):(f=typeof globalThis<"u"?globalThis:f||self,b(f.ObjectUILayout={},f.core,f.require$$0,f.components,f.react,f.reactRouterDom))})(this,(function(f,b,w,m,K,L){"use strict";var j={exports:{}},v={};var M;function ee(){if(M)return v;M=1;var r=Symbol.for("react.transitional.element"),l=Symbol.for("react.fragment");function t(s,n,o){var p=null;if(o!==void 0&&(p=""+o),n.key!==void 0&&(p=""+n.key),"key"in n){o={};for(var g in n)g!=="key"&&(o[g]=n[g])}else o=n;return n=o.ref,{$$typeof:r,type:s,key:p,ref:n!==void 0?n:null,props:o}}return v.Fragment=l,v.jsx=t,v.jsxs=t,v}var E={};var I;function re(){return I||(I=1,process.env.NODE_ENV!=="production"&&(function(){function r(e){if(e==null)return null;if(typeof e=="function")return e.$$typeof===he?null:e.displayName||e.name||null;if(typeof e=="string")return e;switch(e){case P:return"Fragment";case de:return"Profiler";case ue:return"StrictMode";case ge:return"Suspense";case be:return"SuspenseList";case xe: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 ie:return"Portal";case me:return e.displayName||"Context";case fe:return(e._context.displayName||"Context")+".Consumer";case pe:var a=e.render;return e=e.displayName,e||(e=a.displayName||a.name||"",e=e!==""?"ForwardRef("+e+")":"ForwardRef"),e;case ye:return a=e.displayName||null,a!==null?a:r(e.type)||"Memo";case k:a=e._payload,e=e._init;try{return r(e(a))}catch{}}return null}function l(e){return""+e}function t(e){try{l(e);var a=!1}catch{a=!0}if(a){a=console;var i=a.error,u=typeof Symbol=="function"&&Symbol.toStringTag&&e[Symbol.toStringTag]||e.constructor.name||"Object";return i.call(a,"The provided key is an unsupported type %s. This value must be coerced to a string before using it here.",u),l(e)}}function s(e){if(e===P)return"<>";if(typeof e=="object"&&e!==null&&e.$$typeof===k)return"<...>";try{var a=r(e);return a?"<"+a+">":"<...>"}catch{return"<...>"}}function n(){var e=C.A;return e===null?null:e.getOwner()}function o(){return Error("react-stack-top-frame")}function p(e){if(z.call(e,"key")){var a=Object.getOwnPropertyDescriptor(e,"key").get;if(a&&a.isReactWarning)return!1}return e.key!==void 0}function g(e,a){function i(){H||(H=!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)",a))}i.isReactWarning=!0,Object.defineProperty(e,"key",{get:i,configurable:!0})}function x(){var e=r(this.type);return B[e]||(B[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 R(e,a,i,u,T,N){var d=i.ref;return e={$$typeof:V,type:e,key:a,props:i,_owner:u},(d!==void 0?d:null)!==null?Object.defineProperty(e,"ref",{enumerable:!1,get:x}):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:T}),Object.defineProperty(e,"_debugTask",{configurable:!1,enumerable:!1,writable:!0,value:N}),Object.freeze&&(Object.freeze(e.props),Object.freeze(e)),e}function y(e,a,i,u,T,N){var d=a.children;if(d!==void 0)if(u)if(ve(d)){for(u=0;u<d.length;u++)W(d[u]);Object.freeze&&Object.freeze(d)}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 W(d);if(z.call(a,"key")){d=r(e);var h=Object.keys(a).filter(function(Ee){return Ee!=="key"});u=0<h.length?"{key: someKey, "+h.join(": ..., ")+": ...}":"{key: someKey}",Q[d+u]||(h=0<h.length?"{"+h.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} />`,i,l,v,l),z[l+i]=!0)}if(l=null,n!==void 0&&(c(n),l=""+n),p(r)&&(c(r.key),l=""+r.key),"key"in r){n={};for(var A in r)A!=="key"&&(n[A]=r[A])}else n=r;return l&&x(n,typeof e=="function"?e.displayName||e.name||"Unknown":e),te(e,l,n,s(),R,P)}function F(e){q(e)?e._store&&(e._store.validated=1):typeof e=="object"&&e!==null&&e.$$typeof===T&&(e._payload.status==="fulfilled"?q(e._payload.value)&&e._payload.value._store&&(e._payload.value._store.validated=1):e._store&&(e._store.validated=1))}function q(e){return typeof e=="object"&&e!==null&&e.$$typeof===D}var j=H,D=Symbol.for("react.transitional.element"),ae=Symbol.for("react.portal"),y=Symbol.for("react.fragment"),ne=Symbol.for("react.strict_mode"),oe=Symbol.for("react.profiler"),se=Symbol.for("react.consumer"),ie=Symbol.for("react.context"),le=Symbol.for("react.forward_ref"),ue=Symbol.for("react.suspense"),ce=Symbol.for("react.suspense_list"),fe=Symbol.for("react.memo"),T=Symbol.for("react.lazy"),de=Symbol.for("react.activity"),me=Symbol.for("react.client.reference"),S=j.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,U=Object.prototype.hasOwnProperty,be=Array.isArray,k=console.createTask?console.createTask:function(){return null};j={react_stack_bottom_frame:function(e){return e()}};var W,G={},J=j.react_stack_bottom_frame.bind(j,o)(),V=k(m(o)),z={};_.Fragment=y,_.jsx=function(e,r,n){var i=1e4>S.recentlyCreatedOwnerStacks++;return M(e,r,n,!1,i?Error("react-stack-top-frame"):J,i?k(m(e)):V)},_.jsxs=function(e,r,n){var i=1e4>S.recentlyCreatedOwnerStacks++;return M(e,r,n,!0,i?Error("react-stack-top-frame"):J,i?k(m(e)):V)}})()),_}var C;function Q(){return C||(C=1,process.env.NODE_ENV==="production"?E.exports=B():E.exports=Z()),E.exports}var t=Q();function h({title:a,description:d,action:c,className:m,children:s,...o}){return t.jsx("div",{className:f.cn("flex flex-col gap-4 pb-4 md:pb-8",m),...o,children:t.jsxs("div",{className:"flex items-center justify-between gap-4",children:[t.jsxs("div",{className:"flex flex-col gap-1",children:[t.jsx("h1",{className:"text-2xl font-bold tracking-tight md:text-3xl",children:a}),d&&t.jsx("p",{className:"text-sm text-muted-foreground",children:d})]}),(c||s)&&t.jsxs("div",{className:"flex items-center gap-2",children:[c,s]})]})})}function L({sidebar:a,navbar:d,children:c,className:m,defaultOpen:s=!0}){return t.jsxs(f.SidebarProvider,{defaultOpen:s,children:[a,t.jsxs(f.SidebarInset,{children:[t.jsxs("header",{className:"flex h-16 shrink-0 items-center gap-2 border-b bg-background px-4",children:[t.jsx(f.SidebarTrigger,{className:"-ml-1"}),t.jsx("div",{className:"w-px h-4 bg-border mx-2"}),d]}),t.jsx("main",{className:f.cn("flex-1 overflow-auto p-4 md:p-6",m),children:c})]})]})}const K=a=>a?Array.isArray(a)?a:[a]:[];function Y({schema:a,className:d,style:c,id:m,...s}){const o=K(a.children);return t.jsxs("div",{id:m||a.id,className:f.cn("flex flex-col h-full space-y-4",d),style:c,children:[t.jsx(h,{title:a.title,description:a.description}),t.jsx("div",{className:"flex-1 overflow-auto",children:o.map((p,x)=>t.jsx(X.SchemaRenderer,{schema:p,...s},p?.id||x))})]})}function I({className:a,children:d,...c}){return t.jsx("div",{className:f.cn("rounded-xl border bg-card text-card-foreground shadow",a),...c,children:t.jsx("div",{className:"p-6",children:d})})}function ee({items:a,title:d="Application",className:c,collapsible:m="icon"}){const s=N.useLocation();return t.jsx(f.Sidebar,{className:c,collapsible:m,children:t.jsx(f.SidebarContent,{children:t.jsxs(f.SidebarGroup,{children:[t.jsx(f.SidebarGroupLabel,{children:d}),t.jsx(f.SidebarGroupContent,{children:t.jsx(f.SidebarMenu,{children:a.map(o=>t.jsx(f.SidebarMenuItem,{children:t.jsx(f.SidebarMenuButton,{asChild:!0,isActive:s.pathname===o.href,children:t.jsxs(N.NavLink,{to:o.href,children:[o.icon&&t.jsx(o.icon,{}),t.jsx("span",{children:o.title})]})})},o.href))})})]})})})}function $(){b.ComponentRegistry.register("page-header",h,{label:"Page Header",category:"Layout",inputs:[{name:"title",type:"string"},{name:"description",type:"string"}]}),b.ComponentRegistry.register("page:header",h),b.ComponentRegistry.register("page:card",I,{label:"Page Card",category:"Layout",isContainer:!0}),b.ComponentRegistry.register("app-shell",L,{label:"App Shell",category:"Layout"}),b.ComponentRegistry.register("page",Y,{label:"Standard Page",category:"Layout",isContainer:!0})}try{$()}catch{}u.AppShell=L,u.Page=Y,u.PageCard=I,u.PageHeader=h,u.SidebarNav=ee,u.registerLayout=$,Object.defineProperty(u,Symbol.toStringTag,{value:"Module"})}));
6
+ <%s key={someKey} {...props} />`,u,d,h,d),Q[d+u]=!0)}if(d=null,i!==void 0&&(t(i),d=""+i),p(a)&&(t(a.key),d=""+a.key),"key"in a){i={};for(var O in a)O!=="key"&&(i[O]=a[O])}else i=a;return d&&g(i,typeof e=="function"?e.displayName||e.name||"Unknown":e),R(e,d,i,n(),T,N)}function W(e){J(e)?e._store&&(e._store.validated=1):typeof e=="object"&&e!==null&&e.$$typeof===k&&(e._payload.status==="fulfilled"?J(e._payload.value)&&e._payload.value._store&&(e._payload.value._store.validated=1):e._store&&(e._store.validated=1))}function J(e){return typeof e=="object"&&e!==null&&e.$$typeof===V}var S=w,V=Symbol.for("react.transitional.element"),ie=Symbol.for("react.portal"),P=Symbol.for("react.fragment"),ue=Symbol.for("react.strict_mode"),de=Symbol.for("react.profiler"),fe=Symbol.for("react.consumer"),me=Symbol.for("react.context"),pe=Symbol.for("react.forward_ref"),ge=Symbol.for("react.suspense"),be=Symbol.for("react.suspense_list"),ye=Symbol.for("react.memo"),k=Symbol.for("react.lazy"),xe=Symbol.for("react.activity"),he=Symbol.for("react.client.reference"),C=S.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,z=Object.prototype.hasOwnProperty,ve=Array.isArray,A=console.createTask?console.createTask:function(){return null};S={react_stack_bottom_frame:function(e){return e()}};var H,B={},X=S.react_stack_bottom_frame.bind(S,o)(),Z=A(s(o)),Q={};E.Fragment=P,E.jsx=function(e,a,i){var u=1e4>C.recentlyCreatedOwnerStacks++;return y(e,a,i,!1,u?Error("react-stack-top-frame"):X,u?A(s(e)):Z)},E.jsxs=function(e,a,i){var u=1e4>C.recentlyCreatedOwnerStacks++;return y(e,a,i,!0,u?Error("react-stack-top-frame"):X,u?A(s(e)):Z)}})()),E}var Y;function te(){return Y||(Y=1,process.env.NODE_ENV==="production"?j.exports=ee():j.exports=re()),j.exports}var c=te();function _({title:r,description:l,action:t,className:s,children:n,...o}){return c.jsx("div",{className:m.cn("flex flex-col gap-4 pb-4 md:pb-8",s),...o,children:c.jsxs("div",{className:"flex items-center justify-between gap-4",children:[c.jsxs("div",{className:"flex flex-col gap-1",children:[c.jsx("h1",{className:"text-2xl font-bold tracking-tight md:text-3xl",children:r}),l&&c.jsx("p",{className:"text-sm text-muted-foreground",children:l})]}),(t||n)&&c.jsxs("div",{className:"flex items-center gap-2",children:[t,n]})]})})}function $(r){const l=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(r);if(!l)return null;const t=parseInt(l[1],16)/255,s=parseInt(l[2],16)/255,n=parseInt(l[3],16)/255,o=Math.max(t,s,n),p=Math.min(t,s,n);let g=0,x=0;const R=(o+p)/2;if(o!==p){const y=o-p;switch(x=R>.5?y/(2-o-p):y/(o+p),o){case t:g=((s-n)/y+(s<n?6:0))/6;break;case s:g=((n-t)/y+2)/6;break;case n:g=((t-s)/y+4)/6;break}}return`${Math.round(g*360)} ${Math.round(x*100)}% ${Math.round(R*100)}%`}function q(r,l){w.useEffect(()=>{const t=document.documentElement;if(r?.primaryColor){const s=$(r.primaryColor);s&&(t.style.setProperty("--brand-primary",r.primaryColor),t.style.setProperty("--brand-primary-hsl",s))}else t.style.removeProperty("--brand-primary"),t.style.removeProperty("--brand-primary-hsl");if(r?.accentColor){const s=$(r.accentColor);s&&(t.style.setProperty("--brand-accent",r.accentColor),t.style.setProperty("--brand-accent-hsl",s))}else t.style.removeProperty("--brand-accent"),t.style.removeProperty("--brand-accent-hsl");if(r?.favicon){const s=document.querySelector("#favicon")||document.querySelector('link[rel="icon"]');s&&(s.href=r.favicon)}return l&&(document.title=l),()=>{t.style.removeProperty("--brand-primary"),t.style.removeProperty("--brand-primary-hsl"),t.style.removeProperty("--brand-accent"),t.style.removeProperty("--brand-accent-hsl")}},[r?.primaryColor,r?.accentColor,r?.favicon,l])}function F({sidebar:r,navbar:l,children:t,className:s,defaultOpen:n=!0,branding:o}){return q(o,o?.title),c.jsxs(m.SidebarProvider,{defaultOpen:n,children:[r,c.jsxs(m.SidebarInset,{children:[c.jsxs("header",{className:"flex h-14 sm:h-16 shrink-0 items-center gap-2 border-b bg-background px-2 sm:px-4",children:[c.jsx(m.SidebarTrigger,{className:"-ml-1"}),c.jsx("div",{className:"w-px h-4 bg-border mx-1 sm:mx-2"}),l]}),c.jsx("main",{className:m.cn("flex-1 overflow-auto p-3 sm:p-4 md:p-6",s),children:t})]})]})}function D({className:r,children:l,...t}){return c.jsx("div",{className:m.cn("rounded-xl border bg-card text-card-foreground shadow",r),...t,children:c.jsx("div",{className:"p-6",children:l})})}const ae={xs:{1:"grid-cols-1",2:"grid-cols-2",3:"grid-cols-3",4:"grid-cols-4",6:"grid-cols-6",12:"grid-cols-12"},sm:{1:"sm:grid-cols-1",2:"sm:grid-cols-2",3:"sm:grid-cols-3",4:"sm:grid-cols-4",6:"sm:grid-cols-6",12:"sm:grid-cols-12"},md:{1:"md:grid-cols-1",2:"md:grid-cols-2",3:"md:grid-cols-3",4:"md:grid-cols-4",6:"md:grid-cols-6",12:"md:grid-cols-12"},lg:{1:"lg:grid-cols-1",2:"lg:grid-cols-2",3:"lg:grid-cols-3",4:"lg:grid-cols-4",6:"lg:grid-cols-6",12:"lg:grid-cols-12"},xl:{1:"xl:grid-cols-1",2:"xl:grid-cols-2",3:"xl:grid-cols-3",4:"xl:grid-cols-4",6:"xl:grid-cols-6",12:"xl:grid-cols-12"},"2xl":{1:"2xl:grid-cols-1",2:"2xl:grid-cols-2",3:"2xl:grid-cols-3",4:"2xl:grid-cols-4",6:"2xl:grid-cols-6",12:"2xl:grid-cols-12"}};function se(r){if(!r)return"grid-cols-1";const l=[];for(const[t,s]of Object.entries(r)){const n=ae[t];if(n&&s){const p=Object.keys(n).map(Number).reduce((g,x)=>Math.abs(x-s)<Math.abs(g-s)?x:g);l.push(n[p])}}return l.join(" ")||"grid-cols-1"}const oe={0:"gap-0",1:"gap-1",2:"gap-2",3:"gap-3",4:"gap-4",5:"gap-5",6:"gap-6",8:"gap-8"},G=({columns:r,gap:l=4,className:t,children:s})=>{const n=se(r),o=typeof l=="number"?oe[l]||`gap-${l}`:"";return c.jsx("div",{className:m.cn("grid",n,o,t),children:s})},ne=r=>r?Array.isArray(r)?r:[r]:[];function le({schema:r,className:l,style:t,id:s,...n}){const o=ne(r.children);return c.jsxs("div",{id:s||r.id,className:m.cn("flex flex-col h-full space-y-4",l),style:t,children:[c.jsx(_,{title:r.title,description:r.description}),c.jsx("div",{className:"flex-1 overflow-auto",children:o.map((p,g)=>c.jsx(K.SchemaRenderer,{schema:p,...n},p?.id||g))})]})}function ce({items:r,title:l="Application",className:t,collapsible:s="icon"}){const n=L.useLocation();return c.jsx(m.Sidebar,{className:t,collapsible:s,children:c.jsx(m.SidebarContent,{children:c.jsxs(m.SidebarGroup,{children:[c.jsx(m.SidebarGroupLabel,{children:l}),c.jsx(m.SidebarGroupContent,{children:c.jsx(m.SidebarMenu,{children:r.map(o=>c.jsx(m.SidebarMenuItem,{children:c.jsx(m.SidebarMenuButton,{asChild:!0,isActive:n.pathname===o.href,children:c.jsxs(L.NavLink,{to:o.href,children:[o.icon&&c.jsx(o.icon,{}),c.jsx("span",{children:o.title})]})})},o.href))})})]})})})}function U(){b.ComponentRegistry.register("page-header",_,{namespace:"layout",label:"Page Header",category:"Layout",inputs:[{name:"title",type:"string"},{name:"description",type:"string"}]}),b.ComponentRegistry.register("page:header",_,{namespace:"layout"}),b.ComponentRegistry.register("page:card",D,{namespace:"layout",label:"Page Card",category:"Layout",isContainer:!0}),b.ComponentRegistry.register("app-shell",F,{namespace:"layout",label:"App Shell",category:"Layout"}),b.ComponentRegistry.register("responsive-grid",G,{namespace:"layout",label:"Responsive Grid",category:"Layout",isContainer:!0,inputs:[{name:"columns",type:"object"},{name:"gap",type:"number"}]})}try{U()}catch{}f.AppShell=F,f.Page=le,f.PageCard=D,f.PageHeader=_,f.ResponsiveGrid=G,f.SidebarNav=ce,f.registerLayout=U,f.useAppShellBranding=q,Object.defineProperty(f,Symbol.toStringTag,{value:"Module"})}));
@@ -1,9 +1,32 @@
1
1
  import { default as React } from 'react';
2
+ /**
3
+ * Branding configuration for the AppShell.
4
+ * Applies CSS custom properties to the document root for theme customization.
5
+ */
6
+ export interface AppShellBranding {
7
+ /** Primary brand color (hex, e.g. "#3B82F6") */
8
+ primaryColor?: string;
9
+ /** Accent brand color (hex, e.g. "#10B981") */
10
+ accentColor?: string;
11
+ /** Favicon URL — replaces the <link rel="icon"> href */
12
+ favicon?: string;
13
+ /** Logo URL — passed to sidebar/navbar via context */
14
+ logo?: string;
15
+ /** Page title suffix (sets document.title) */
16
+ title?: string;
17
+ }
2
18
  export interface AppShellProps {
3
19
  sidebar?: React.ReactNode;
4
20
  navbar?: React.ReactNode;
5
21
  children: React.ReactNode;
6
22
  className?: string;
7
23
  defaultOpen?: boolean;
24
+ /** App branding — applies CSS custom properties for theming */
25
+ branding?: AppShellBranding;
8
26
  }
9
- export declare function AppShell({ sidebar, navbar, children, className, defaultOpen, }: AppShellProps): import("react/jsx-runtime").JSX.Element;
27
+ /**
28
+ * Apply branding CSS custom properties to the document root.
29
+ * This is extracted as a standalone hook so it can be re-used independently.
30
+ */
31
+ export declare function useAppShellBranding(branding?: AppShellBranding, title?: string): void;
32
+ export declare function AppShell({ sidebar, navbar, children, className, defaultOpen, branding, }: AppShellProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,51 @@
1
+ import { default as React } from 'react';
2
+ /**
3
+ * Spec-aligned breakpoint column map (mirrors @objectstack/spec BreakpointColumnMapSchema).
4
+ * Maps breakpoint names to grid column counts (1-12).
5
+ */
6
+ export interface BreakpointColumnMap {
7
+ xs?: number;
8
+ sm?: number;
9
+ md?: number;
10
+ lg?: number;
11
+ xl?: number;
12
+ '2xl'?: number;
13
+ }
14
+ /**
15
+ * Spec-aligned breakpoint order map (mirrors @objectstack/spec BreakpointOrderMapSchema).
16
+ * Maps breakpoint names to display order numbers.
17
+ */
18
+ export interface BreakpointOrderMap {
19
+ xs?: number;
20
+ sm?: number;
21
+ md?: number;
22
+ lg?: number;
23
+ xl?: number;
24
+ '2xl'?: number;
25
+ }
26
+ export interface ResponsiveGridProps {
27
+ /** Grid column map per breakpoint */
28
+ columns?: BreakpointColumnMap;
29
+ /** Gap between grid items */
30
+ gap?: number | string;
31
+ /** Additional class names */
32
+ className?: string;
33
+ /** Children */
34
+ children: React.ReactNode;
35
+ }
36
+ /**
37
+ * ResponsiveGrid — A layout component that consumes @objectstack/spec
38
+ * BreakpointColumnMapSchema for responsive grid layouts.
39
+ *
40
+ * Uses pure Tailwind CSS classes for responsive behavior (no JS resize listeners).
41
+ *
42
+ * @example
43
+ * ```tsx
44
+ * <ResponsiveGrid columns={{ xs: 1, sm: 2, lg: 3 }} gap={4}>
45
+ * <Card>Item 1</Card>
46
+ * <Card>Item 2</Card>
47
+ * <Card>Item 3</Card>
48
+ * </ResponsiveGrid>
49
+ * ```
50
+ */
51
+ export declare const ResponsiveGrid: React.FC<ResponsiveGridProps>;
@@ -7,4 +7,5 @@ export * from './AppShell';
7
7
  export * from './Page';
8
8
  export * from './PageCard';
9
9
  export * from './SidebarNav';
10
+ export * from './ResponsiveGrid';
10
11
  export declare function registerLayout(): void;
@@ -0,0 +1,24 @@
1
+ import { StoryObj } from '@storybook/react';
2
+ /**
3
+ * AppShell responsive layout stories.
4
+ * Demonstrates the shell + responsive grid working together.
5
+ *
6
+ * Part of Q1 2026 roadmap §1.3 — Responsive layout stories in Storybook.
7
+ */
8
+ declare const meta: {
9
+ title: string;
10
+ component: any;
11
+ parameters: {
12
+ layout: string;
13
+ docs: {
14
+ description: {
15
+ component: string;
16
+ };
17
+ };
18
+ };
19
+ tags: string[];
20
+ };
21
+ export default meta;
22
+ type Story = StoryObj<typeof meta>;
23
+ export declare const ResponsiveDashboard: Story;
24
+ export declare const MinimalShell: Story;
@@ -0,0 +1,29 @@
1
+ import { StoryObj } from '@storybook/react';
2
+ /**
3
+ * ResponsiveGrid stories demonstrating spec-aligned responsive layouts.
4
+ * Uses BreakpointColumnMapSchema to configure columns per breakpoint.
5
+ *
6
+ * Part of Q1 2026 roadmap §1.3 — Responsive layout stories in Storybook.
7
+ */
8
+ declare const meta: {
9
+ title: string;
10
+ component: any;
11
+ parameters: {
12
+ layout: string;
13
+ docs: {
14
+ description: {
15
+ component: string;
16
+ };
17
+ };
18
+ };
19
+ tags: string[];
20
+ };
21
+ export default meta;
22
+ type Story = StoryObj<typeof meta>;
23
+ export declare const Default: Story;
24
+ export declare const SingleColumn: Story;
25
+ export declare const TwoColumns: Story;
26
+ export declare const FourColumnGrid: Story;
27
+ export declare const DashboardLayout: Story;
28
+ export declare const CompactGap: Story;
29
+ export declare const WideGap: Story;
package/package.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "@object-ui/layout",
3
- "version": "0.5.0",
3
+ "version": "3.0.0",
4
4
  "type": "module",
5
+ "sideEffects": false,
5
6
  "main": "dist/index.umd.cjs",
6
7
  "module": "dist/index.js",
7
8
  "types": "dist/index.d.ts",
@@ -13,15 +14,15 @@
13
14
  }
14
15
  },
15
16
  "dependencies": {
16
- "clsx": "^2.1.0",
17
+ "clsx": "^2.1.1",
17
18
  "lucide-react": "^0.563.0",
18
- "react": "^19.2.4",
19
- "react-dom": "^19.2.4",
20
- "tailwind-merge": "^2.2.1",
21
- "@object-ui/components": "0.5.0",
22
- "@object-ui/core": "0.5.0",
23
- "@object-ui/react": "0.5.0",
24
- "@object-ui/types": "0.5.0"
19
+ "react": "19.2.4",
20
+ "react-dom": "19.2.4",
21
+ "tailwind-merge": "^2.6.1",
22
+ "@object-ui/components": "3.0.0",
23
+ "@object-ui/core": "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",
@@ -29,8 +30,8 @@
29
30
  "react-router-dom": "^6.0.0 || ^7.0.0"
30
31
  },
31
32
  "devDependencies": {
33
+ "@vitejs/plugin-react": "^5.1.4",
32
34
  "react-router-dom": "^7.13.0",
33
- "@vitejs/plugin-react": "^5.1.3",
34
35
  "vite": "^7.3.1",
35
36
  "vite-plugin-dts": "^4.5.4"
36
37
  },
package/src/AppShell.tsx CHANGED
@@ -1,4 +1,4 @@
1
- import React from 'react';
1
+ import React, { useEffect } from 'react';
2
2
  import {
3
3
  SidebarProvider,
4
4
  SidebarTrigger,
@@ -7,12 +7,117 @@ import {
7
7
  } from '@object-ui/components';
8
8
  import { cn } from '@object-ui/components';
9
9
 
10
+ /**
11
+ * Branding configuration for the AppShell.
12
+ * Applies CSS custom properties to the document root for theme customization.
13
+ */
14
+ export interface AppShellBranding {
15
+ /** Primary brand color (hex, e.g. "#3B82F6") */
16
+ primaryColor?: string;
17
+ /** Accent brand color (hex, e.g. "#10B981") */
18
+ accentColor?: string;
19
+ /** Favicon URL — replaces the <link rel="icon"> href */
20
+ favicon?: string;
21
+ /** Logo URL — passed to sidebar/navbar via context */
22
+ logo?: string;
23
+ /** Page title suffix (sets document.title) */
24
+ title?: string;
25
+ }
26
+
10
27
  export interface AppShellProps {
11
28
  sidebar?: React.ReactNode;
12
29
  navbar?: React.ReactNode; // Top navbar content
13
30
  children: React.ReactNode;
14
31
  className?: string;
15
32
  defaultOpen?: boolean;
33
+ /** App branding — applies CSS custom properties for theming */
34
+ branding?: AppShellBranding;
35
+ }
36
+
37
+ /**
38
+ * Convert a hex color (#RRGGBB) to HSL string "H S% L%"
39
+ * for use in Tailwind CSS custom properties.
40
+ */
41
+ function hexToHSL(hex: string): string | null {
42
+ const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
43
+ if (!result) return null;
44
+
45
+ const r = parseInt(result[1], 16) / 255;
46
+ const g = parseInt(result[2], 16) / 255;
47
+ const b = parseInt(result[3], 16) / 255;
48
+
49
+ const max = Math.max(r, g, b);
50
+ const min = Math.min(r, g, b);
51
+ let h = 0;
52
+ let s = 0;
53
+ const l = (max + min) / 2;
54
+
55
+ if (max !== min) {
56
+ const d = max - min;
57
+ s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
58
+ switch (max) {
59
+ case r: h = ((g - b) / d + (g < b ? 6 : 0)) / 6; break;
60
+ case g: h = ((b - r) / d + 2) / 6; break;
61
+ case b: h = ((r - g) / d + 4) / 6; break;
62
+ }
63
+ }
64
+
65
+ return `${Math.round(h * 360)} ${Math.round(s * 100)}% ${Math.round(l * 100)}%`;
66
+ }
67
+
68
+ /**
69
+ * Apply branding CSS custom properties to the document root.
70
+ * This is extracted as a standalone hook so it can be re-used independently.
71
+ */
72
+ export function useAppShellBranding(branding?: AppShellBranding, title?: string) {
73
+ useEffect(() => {
74
+ const root = document.documentElement;
75
+
76
+ // Primary color
77
+ if (branding?.primaryColor) {
78
+ const hsl = hexToHSL(branding.primaryColor);
79
+ if (hsl) {
80
+ root.style.setProperty('--brand-primary', branding.primaryColor);
81
+ root.style.setProperty('--brand-primary-hsl', hsl);
82
+ }
83
+ } else {
84
+ root.style.removeProperty('--brand-primary');
85
+ root.style.removeProperty('--brand-primary-hsl');
86
+ }
87
+
88
+ // Accent color
89
+ if (branding?.accentColor) {
90
+ const hsl = hexToHSL(branding.accentColor);
91
+ if (hsl) {
92
+ root.style.setProperty('--brand-accent', branding.accentColor);
93
+ root.style.setProperty('--brand-accent-hsl', hsl);
94
+ }
95
+ } else {
96
+ root.style.removeProperty('--brand-accent');
97
+ root.style.removeProperty('--brand-accent-hsl');
98
+ }
99
+
100
+ // Favicon
101
+ if (branding?.favicon) {
102
+ const link = document.querySelector<HTMLLinkElement>('#favicon')
103
+ || document.querySelector<HTMLLinkElement>('link[rel="icon"]');
104
+ if (link) {
105
+ link.href = branding.favicon;
106
+ }
107
+ }
108
+
109
+ // Page title
110
+ if (title) {
111
+ document.title = title;
112
+ }
113
+
114
+ return () => {
115
+ root.style.removeProperty('--brand-primary');
116
+ root.style.removeProperty('--brand-primary-hsl');
117
+ root.style.removeProperty('--brand-accent');
118
+ root.style.removeProperty('--brand-accent-hsl');
119
+ };
120
+ }, [branding?.primaryColor, branding?.accentColor, branding?.favicon, title]);
16
121
  }
17
122
 
18
123
  export function AppShell({
@@ -21,17 +126,21 @@ export function AppShell({
21
126
  children,
22
127
  className,
23
128
  defaultOpen = true,
129
+ branding,
24
130
  }: AppShellProps) {
131
+ // Apply branding CSS custom properties
132
+ useAppShellBranding(branding, branding?.title);
133
+
25
134
  return (
26
135
  <SidebarProvider defaultOpen={defaultOpen}>
27
136
  {sidebar}
28
137
  <SidebarInset>
29
- <header className="flex h-16 shrink-0 items-center gap-2 border-b bg-background px-4">
138
+ <header className="flex h-14 sm:h-16 shrink-0 items-center gap-2 border-b bg-background px-2 sm:px-4">
30
139
  <SidebarTrigger className="-ml-1" />
31
- <div className="w-px h-4 bg-border mx-2" />
140
+ <div className="w-px h-4 bg-border mx-1 sm:mx-2" />
32
141
  {navbar}
33
142
  </header>
34
- <main className={cn("flex-1 overflow-auto p-4 md:p-6", className)}>
143
+ <main className={cn("flex-1 overflow-auto p-3 sm:p-4 md:p-6", className)}>
35
144
  {children}
36
145
  </main>
37
146
  </SidebarInset>
@@ -0,0 +1,118 @@
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
+ import React from 'react';
10
+ import { cn } from '@object-ui/components';
11
+
12
+ /**
13
+ * Spec-aligned breakpoint column map (mirrors @objectstack/spec BreakpointColumnMapSchema).
14
+ * Maps breakpoint names to grid column counts (1-12).
15
+ */
16
+ export interface BreakpointColumnMap {
17
+ xs?: number;
18
+ sm?: number;
19
+ md?: number;
20
+ lg?: number;
21
+ xl?: number;
22
+ '2xl'?: number;
23
+ }
24
+
25
+ /**
26
+ * Spec-aligned breakpoint order map (mirrors @objectstack/spec BreakpointOrderMapSchema).
27
+ * Maps breakpoint names to display order numbers.
28
+ */
29
+ export interface BreakpointOrderMap {
30
+ xs?: number;
31
+ sm?: number;
32
+ md?: number;
33
+ lg?: number;
34
+ xl?: number;
35
+ '2xl'?: number;
36
+ }
37
+
38
+ export interface ResponsiveGridProps {
39
+ /** Grid column map per breakpoint */
40
+ columns?: BreakpointColumnMap;
41
+ /** Gap between grid items */
42
+ gap?: number | string;
43
+ /** Additional class names */
44
+ className?: string;
45
+ /** Children */
46
+ children: React.ReactNode;
47
+ }
48
+
49
+ /**
50
+ * Tailwind class mapping for grid columns at each breakpoint.
51
+ * Uses standard Tailwind grid-cols utilities for CSS-only responsiveness.
52
+ */
53
+ const COLS_CLASSES: Record<string, Record<number, string>> = {
54
+ xs: { 1: 'grid-cols-1', 2: 'grid-cols-2', 3: 'grid-cols-3', 4: 'grid-cols-4', 6: 'grid-cols-6', 12: 'grid-cols-12' },
55
+ sm: { 1: 'sm:grid-cols-1', 2: 'sm:grid-cols-2', 3: 'sm:grid-cols-3', 4: 'sm:grid-cols-4', 6: 'sm:grid-cols-6', 12: 'sm:grid-cols-12' },
56
+ md: { 1: 'md:grid-cols-1', 2: 'md:grid-cols-2', 3: 'md:grid-cols-3', 4: 'md:grid-cols-4', 6: 'md:grid-cols-6', 12: 'md:grid-cols-12' },
57
+ lg: { 1: 'lg:grid-cols-1', 2: 'lg:grid-cols-2', 3: 'lg:grid-cols-3', 4: 'lg:grid-cols-4', 6: 'lg:grid-cols-6', 12: 'lg:grid-cols-12' },
58
+ xl: { 1: 'xl:grid-cols-1', 2: 'xl:grid-cols-2', 3: 'xl:grid-cols-3', 4: 'xl:grid-cols-4', 6: 'xl:grid-cols-6', 12: 'xl:grid-cols-12' },
59
+ '2xl': { 1: '2xl:grid-cols-1', 2: '2xl:grid-cols-2', 3: '2xl:grid-cols-3', 4: '2xl:grid-cols-4', 6: '2xl:grid-cols-6', 12: '2xl:grid-cols-12' },
60
+ };
61
+
62
+ /**
63
+ * Resolve a BreakpointColumnMap into Tailwind CSS grid classes.
64
+ */
65
+ function resolveColumnClasses(columns?: BreakpointColumnMap): string {
66
+ if (!columns) return 'grid-cols-1';
67
+
68
+ const classes: string[] = [];
69
+ for (const [bp, cols] of Object.entries(columns)) {
70
+ const bpClasses = COLS_CLASSES[bp];
71
+ if (bpClasses && cols) {
72
+ // Use closest supported column count
73
+ const supported = Object.keys(bpClasses).map(Number);
74
+ const closest = supported.reduce((prev, curr) =>
75
+ Math.abs(curr - cols) < Math.abs(prev - cols) ? curr : prev
76
+ );
77
+ classes.push(bpClasses[closest]);
78
+ }
79
+ }
80
+
81
+ return classes.join(' ') || 'grid-cols-1';
82
+ }
83
+
84
+ const GAP_CLASSES: Record<number, string> = {
85
+ 0: 'gap-0', 1: 'gap-1', 2: 'gap-2', 3: 'gap-3', 4: 'gap-4',
86
+ 5: 'gap-5', 6: 'gap-6', 8: 'gap-8',
87
+ };
88
+
89
+ /**
90
+ * ResponsiveGrid — A layout component that consumes @objectstack/spec
91
+ * BreakpointColumnMapSchema for responsive grid layouts.
92
+ *
93
+ * Uses pure Tailwind CSS classes for responsive behavior (no JS resize listeners).
94
+ *
95
+ * @example
96
+ * ```tsx
97
+ * <ResponsiveGrid columns={{ xs: 1, sm: 2, lg: 3 }} gap={4}>
98
+ * <Card>Item 1</Card>
99
+ * <Card>Item 2</Card>
100
+ * <Card>Item 3</Card>
101
+ * </ResponsiveGrid>
102
+ * ```
103
+ */
104
+ export const ResponsiveGrid: React.FC<ResponsiveGridProps> = ({
105
+ columns,
106
+ gap = 4,
107
+ className,
108
+ children,
109
+ }) => {
110
+ const colClasses = resolveColumnClasses(columns);
111
+ const gapClass = typeof gap === 'number' ? (GAP_CLASSES[gap] || `gap-${gap}`) : '';
112
+
113
+ return (
114
+ <div className={cn('grid', colClasses, gapClass, className)}>
115
+ {children}
116
+ </div>
117
+ );
118
+ };
package/src/index.ts CHANGED
@@ -9,15 +9,18 @@ import { AppShell } from './AppShell';
9
9
  import { Page } from './Page';
10
10
  import { PageCard } from './PageCard';
11
11
  import { SidebarNav } from './SidebarNav';
12
+ import { ResponsiveGrid } from './ResponsiveGrid';
12
13
 
13
14
  export * from './PageHeader';
14
15
  export * from './AppShell';
15
16
  export * from './Page';
16
17
  export * from './PageCard';
17
18
  export * from './SidebarNav';
19
+ export * from './ResponsiveGrid';
18
20
 
19
21
  export function registerLayout() {
20
22
  ComponentRegistry.register('page-header', PageHeader, {
23
+ namespace: 'layout',
21
24
  label: 'Page Header',
22
25
  category: 'Layout',
23
26
  inputs: [
@@ -27,26 +30,36 @@ export function registerLayout() {
27
30
  });
28
31
 
29
32
  // Alias for protocol compliance
30
- ComponentRegistry.register('page:header', PageHeader);
33
+ ComponentRegistry.register('page:header', PageHeader, { namespace: 'layout' });
31
34
 
32
35
  // Page Card
33
36
  ComponentRegistry.register('page:card', PageCard, {
37
+ namespace: 'layout',
34
38
  label: 'Page Card',
35
39
  category: 'Layout',
36
40
  isContainer: true
37
41
  });
38
42
 
39
43
  ComponentRegistry.register('app-shell', AppShell, {
40
- label: 'App Shell',
41
- category: 'Layout',
44
+ namespace: 'layout',
45
+ label: 'App Shell',
46
+ category: 'Layout',
42
47
  });
43
48
 
44
- // Core Page Renderer
45
- ComponentRegistry.register('page', Page, {
46
- label: 'Standard Page',
49
+ ComponentRegistry.register('responsive-grid', ResponsiveGrid, {
50
+ namespace: 'layout',
51
+ label: 'Responsive Grid',
47
52
  category: 'Layout',
48
- isContainer: true
53
+ isContainer: true,
54
+ inputs: [
55
+ { name: 'columns', type: 'object' },
56
+ { name: 'gap', type: 'number' },
57
+ ],
49
58
  });
59
+
60
+ // NOTE: 'page' registration is handled by @object-ui/components PageRenderer.
61
+ // That renderer supports page types (record/home/app/utility), named regions,
62
+ // and PageVariablesProvider. Do NOT re-register 'page' here to avoid conflicts.
50
63
  }
51
64
 
52
65
  // Keep backward compatibility for now if called directly