efficient-easel 26.3.0 → 26.4.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.
@@ -0,0 +1 @@
1
+ import{styleFieldBuilders as e,keptByRow as s}from"./chunk-SS2NY7PP.js";import{boxFieldBuilders as o,PHONE_KEY_ACCESS as l,phoneTwinFields as r,section as i}from"./chunk-UJU42JM4.js";import{deviceField as t}from"./chunk-7GGNFLHT.js";import{BUTTON_STYLE_KEYS as c,BUTTON_PAD_KEYS as n}from"./chunk-BTQ3FJKT.js";import{TextColorIcon as u,GroundIcon as d}from"efficient-shell/icons/efficient";import{jsx as f}from"react/jsx-runtime";function a(a){const p=o(a.say),y=e(a.say),F=c,m=a.style,k=a.phone??l;return{_dSize:i("Size"),...p.sidesPaddingFields({keys:n,group:!1,...a.padding}),_dDesign:i("Design"),[F.fill]:y.colourField({key:F.fill,access:m,label:"Fill color",icon:/* @__PURE__ */f(d,{}),useFollows:a.follows.useFill,usePresets:a.usePresets,resetTo:a.resetTo,opacity:{key:F.fillOpacity,useUnset:a.follows.useFillOpacity}}),[F.fillOpacity]:s(F.fill),[F.border]:y.borderField({key:F.border,access:m,useFollows:a.follows.useBorder}),[F.text]:y.colourField({key:F.text,access:m,label:"Text color",icon:/* @__PURE__ */f(u,{}),useFollows:a.follows.useText,usePresets:a.usePresets,resetTo:a.resetTo}),[F.corners]:y.cornersField({key:F.corners,access:m,sidesKey:F.cornerSides,useFollows:a.follows.useCorners}),[F.cornerSides]:s(F.corners),_dLayout:i("Layout"),...r("align",p.blockAlignmentField({phone:!0},""),k),align:t("desktop",p.blockAlignmentField())}}export{a as buttonFieldSet};
@@ -0,0 +1,4 @@
1
+ import{useAccessPatch as e,FLAT_ACCESS as o,onDesktopWords as r,useAccessValue as t}from"./chunk-UJU42JM4.js";import{CornersInput as s,BorderInput as l,SettingsColour as n}from"./chunk-CLAADE4J.js";import{SettingsDefaultReset as i}from"./chunk-2T22IY3J.js";import{noControlField as a,NoControlMark as c}from"./chunk-22I55W37.js";import{CORNER_SIDES as d}from"./chunk-BTQ3FJKT.js";import{DEFAULT_PANEL_WORDS as u,DEFAULT_COLOUR_ROW_WORDS as p}from"./chunk-56ID6FKZ.js";import{SettingsField as y}from"./chunk-MIX7EGRG.js";import{useWords as f}from"./chunk-MFIRYGHW.js";import{CornerIcon as h,BorderAllIcon as m}from"efficient-shell/icons/efficient";import{jsx as b}from"react/jsx-runtime";var w=e=>e;function g(e,o){const r=o.twinAccess;return r?{key:e.key,access:r,ownField:r.ownField}:e}function v(e,r){const s=!e.access||e.ownField?{value:r.value,onChange:r.onChange}:void 0;return t(e.access??o,e.key,s)}function k(e,r){return t(e.access??o,r??"")}function F(e){return{...a({type:"custom",render:()=>/* @__PURE__ */b(c,{})},"always"),reachedBy:e}}function j({o:t,say:s,props:l}){const i=f("colourRow",p),a=f("panel",u),c=t.useFollows(),d=t.usePresets?.(),y=t.opacity?.useUnset?.()??100,h=g(t,l),[m,w]=v(h,l),[F,j]=k(h,t.opacity?.key),B=e(h.access??o),C="string"==typeof m?m:"",A="number"==typeof F,O="follows"in l,T=(O&&"string"==typeof l.follows&&""!==l.follows?l.follows:void 0)??c,x=t.access||O?void 0:"";/* @__PURE__ */
2
+ return b(n,{label:s(t.label),icon:t.icon,value:C,onChange:e=>w(""===e?x:e),inherited:T,presets:d,changed:""!==C||A,onReset:()=>{A&&t.opacity?B([[t.key,x],[t.opacity.key,void 0]]):w(x)},resetTo:O?r(s,T.toUpperCase()):t.resetTo?t.resetTo(c.toUpperCase()):c.toUpperCase(),...t.opacity?{opacity:A?F:y,onOpacityChange:e=>j(e)}:{},readOnly:l.readOnly,words:i,resetWords:a})}var B=20;function C({o:e,say:o,props:t}){const s=e.useFollows(),[n,a]=v(g(e,t),t),c=n&&"object"==typeof n?n:{},d="follows"in t,u=d&&t.follows&&"object"==typeof t.follows?t.follows:{},p="number"==typeof u.width?u.width:s.width,f="string"==typeof u.color&&""!==u.color?u.color:s.color,h=Object.keys(c).length>0,w={width:o("Border width"),sides:{top:o("Border top"),right:o("Border right"),bottom:o("Border bottom"),left:o("Border left")},style:o("Border style"),styles:{solid:o("Solid"),dashed:o("Dashed"),dotted:o("Dotted")},color:o("Border color"),unit:"px"};/* @__PURE__ */
3
+ return b(y,{label:o("Border"),icon:/* @__PURE__ */b(m,{}),action:d?/* @__PURE__ */b(i,{label:o("Border"),changed:h,detail:r(o,`${p}px`),disabled:t.readOnly,onReset:()=>a(void 0)}):void 0,children:/* @__PURE__ */b(l,{label:o("Border"),value:c,each:!1,onChange:o=>a(function(e){const o=Object.fromEntries(Object.entries(e).filter(([,e])=>null!=e&&""!==e));return Object.keys(o).length?o:void 0}(e.styles?{width:o.width,color:o.color,style:o.style}:{width:o.width,color:o.color})),placeholder:{sides:{top:p,right:p,bottom:p,left:p},style:u.style??"solid",color:f},presets:[f],words:w,styles:e.styles??!1,max:20,readOnly:t.readOnly})})}var A=40;function O({o:r,say:t,props:l}){const n=f("panel",u),a=r.useFollows(),[c,p]=v(r,l),[m,w]=k(r,r.sidesKey),g=e(r.access??o),F=r.access?void 0:null,j="number"==typeof c&&Number.isFinite(c)?c:null,B=Array.isArray(m)?d.filter(e=>m.includes(e)):[...d],C=[{value:"tl",label:t("Top left")},{value:"tr",label:t("Top right")},{value:"bl",label:t("Bottom left")},{value:"br",label:t("Bottom right")}];/* @__PURE__ */
4
+ return b(y,{label:t("Corners"),icon:/* @__PURE__ */b(h,{}),changed:null!==j||Array.isArray(m),action:/* @__PURE__ */b(i,{label:t("Corners"),changed:null!==j||Array.isArray(m),detail:`${a}px`,onReset:()=>Array.isArray(m)?g([[r.key,F],[r.sidesKey,F]]):p(F),words:n}),children:/* @__PURE__ */b(s,{label:t("Corners"),value:B,corners:C,onChange:e=>{const o=d.filter(o=>e.includes(o));w(o.length===d.length?F:o)},radius:j,onRadiusChange:e=>p(e??F),radiusMin:0,radiusMax:40,radiusStep:r.step??1,radiusPlaceholder:String(a),disabled:l.readOnly})})}function T(e=w){return{colourField:o=>({type:"custom",render:r=>/* @__PURE__ */b(j,{o:o,say:e,props:r})}),borderField:o=>({type:"custom",render:r=>/* @__PURE__ */b(C,{o:o,say:e,props:r})}),cornersField:o=>({type:"custom",render:r=>/* @__PURE__ */b(O,{o:o,say:e,props:r})})}}var{colourField:x,borderField:R,cornersField:U}=T();export{B as BORDER_WIDTH_MAX,A as CORNER_RADIUS_MAX,R as borderField,x as colourField,U as cornersField,F as keptByRow,T as styleFieldBuilders};
@@ -0,0 +1,14 @@
1
+ import{sidesStartEach as e,SidesInput as n,SidesEachToggle as t}from"./chunk-CLAADE4J.js";import{SettingsDefaultReset as l}from"./chunk-2T22IY3J.js";import{deviceField as o}from"./chunk-7GGNFLHT.js";import{noControlField as a,NoControlMark as i}from"./chunk-22I55W37.js";import{describedAs as r}from"./chunk-LX23TCTP.js";import{withStyleKind as s}from"./chunk-CWELHADJ.js";import{phoneKey as u}from"./chunk-E52W3M5I.js";import{foldMetaOf as c,FOLD_FIELD_MARKER as d}from"./chunk-ANPOR6EY.js";import{useSelectedHost as p,useEaselApi as f,useEasel as b}from"./chunk-YAFMYW4J.js";import{DEFAULT_PANEL_WORDS as m}from"./chunk-56ID6FKZ.js";import{SettingsSwitch as h}from"./chunk-OECWLNKJ.js";import{FieldSelect as v}from"./chunk-KFWNGPW3.js";import{SettingsField as g}from"./chunk-MIX7EGRG.js";import{SettingsSegmented as y}from"./chunk-DX3VILZ3.js";import{useWords as k}from"./chunk-MFIRYGHW.js";import{useState as w}from"react";import{AlignmentIcon as x,BoxJustifyIcon as C,WrapIcon as F,GapIcon as j,WidthIcon as L,PlacementIcon as O,NudgeIcon as S,DesktopIcon as E,BoxXEndIcon as P,BoxXCenterIcon as A,BoxXStartIcon as I,PaddingAllIcon as R,BoxYBetweenIcon as M,BoxYStretchIcon as W,BoxYEndIcon as K,BoxYCenterIcon as B,BoxYStartIcon as G,BoxXBetweenIcon as N,BoxXStretchIcon as T,BoxItemAlignmentIcon as V}from"efficient-shell/icons/efficient";import{textAlignOptions as $,AlignmentGrid as J,PositionInput as D,Slider as H}from"efficient-shell/ui";import{jsx as Y,Fragment as _,jsxs as X}from"react/jsx-runtime";var z=e=>e;function Z(e,n){return e("{value} on desktop").replace("{value}",n)}var U=()=>{};function q(){return null}function Q(e){if(!e.trim())throw new Error("noGlyph() needs a reason naming the ruling that decided it.");/* @__PURE__ */
2
+ return Y(q,{},e)}function ee(e){return e.type===q}function ne(e){return ee(e)?void 0:e}function te(e){for(const[n,t]of Object.entries(e))t&&"object"==typeof t&&!c(t)&&s(t,`section:${n}`);return e}function le(e,n){return{type:"custom",[d]:{label:e,level:n},marker:!0,fold:e,render:()=>/* @__PURE__ */Y(_,{})}}function oe(e){return le(e,2)}function ae(e){return le(e,1)}var ie=[{label:"Start",value:"start"},{label:"Center",value:"center"},{label:"Space between",value:"between"},{label:"End",value:"end"}],re=[{label:"Start",value:"start"},{label:"Center",value:"center"},{label:"End",value:"end"},{label:"Stretch",value:"stretch"}],se="Wrap",ue=[{label:"Wrap",value:"wrap"},{label:"No wrap",value:"nowrap"}],ce="Direction",de=[{label:"Stacked",value:"column"},{label:"One row",value:"row"}],pe={min:-32,max:64,step:2,builtIn:0},fe={min:30,max:100,step:5,fallback:60},be=["top-left","top-center","top-right","left","center","right","bottom-left","bottom-center","bottom-right"],me="bottom-left",he={"top-left":"Top left","top-center":"Top center","top-right":"Top right",left:"Left",center:"Center",right:"Right","bottom-left":"Bottom left","bottom-center":"Bottom center","bottom-right":"Bottom right"},ve=be.map(e=>({value:e,label:he[e]}));function ge(e,n=me){return be.includes(e)?e:n}var ye=40,ke=e=>"number"==typeof e?e:null;function we(e,n,t){const l={};for(const o of["x","y"])t[o]!==n[o]&&(l[e[o]]=t[o]??void 0);return l}var xe=["top","right","bottom","left"],Ce={top:"sectionPaddingTop",right:"sectionPaddingRight",bottom:"sectionPaddingBottom",left:"sectionPaddingLeft"},Fe=e=>"number"==typeof e?e:null;function je(e,n,t){const l={};for(const o of xe)t[o]!==n[o]&&(l[e[o]]=t[o]??void 0);return l}function Le(e){if("number"==typeof e)return{top:e,right:e,bottom:e,left:e};const n=e&&"object"==typeof e?e:{};return Object.fromEntries(xe.map(e=>[e,Fe(n[e])]))}function Oe(e){const n={};for(const t of xe)"number"==typeof e[t]&&(n[t]=e[t]);return Object.keys(n).length?n:void 0}function Se(e,n={}){const t=n=>{const t=n[e];return t&&"object"==typeof t?t:{}},l=e=>Object.hasOwn(n,e)?n[e]:e;return{read:(e,n)=>t(e)[l(n)],write:(n,o,a)=>{const i={...t(n)};return void 0===a?delete i[l(o)]:i[l(o)]=a,{[e]:Object.keys(i).length?i:void 0}}}}function Ee(e,n,t){const l=Pe(e),o=p();if(t)return[t.value,t.onChange];const a=o?.values??{};return[e.read(a,n),e=>l([[n,e]])]}function Pe(e){const n=p(),t=function(){try{return f()}catch{return null}}();return l=>{if(!n)return;const o=t?.getState(),a=(o?"root"===n.id?o.appState.data.root.props:o.getItemById(n.id)?.props:void 0)??n.values??{};let i={};for(const[n,t]of l)i={...i,...e.write({...a,...i},n,t)};n.onChangeMany(i)}}var Ae={read:(e,n)=>e[n],write:(e,n,t)=>({[n]:t})},Ie={fieldKey:u,ownField:!0,read:(e,n)=>e[u(n)],write:(e,n,t)=>({[u(n)]:t})};function Re(e){return{...Se(e),fieldKey:n=>`_${e}_${n}`}}function Me({row:e,desktopKey:n,access:t,props:l}){const o=p(),a=o?.values?.[n],[i,r]=Ee(t,n);return t.ownField?e.render({...l,follows:a,twinAccess:t}):e.render({...l,value:i,storedValue:i,follows:a,twinAccess:t,onChange:r})}function We(e,n,t=Ie,l={}){const a=l.phoneRow??n,i={...a,render:n=>/* @__PURE__ */Y(Me,{row:a,desktopKey:e,access:t,props:n})};return{[e]:o("desktop",n),[t.fieldKey(e)]:o("mobile",i)}}var Ke=["left","center","right"];function Be(e,{options:n,value:t,onChange:l,ariaLabel:o,disabled:a=!1}){/* @__PURE__ */
3
+ return Y(y,{options:n.map(n=>n.picture?{id:n.value,label:n.picture,title:e(n.title??n.label)}:{id:n.value,label:e(n.label),title:void 0===n.title?void 0:e(n.title)}),value:t,onChange:l,ariaLabel:e(o),disabled:a})}function Ge(e,{label:n,value:t,onChange:l,points:o}){const a=o.find(e=>e.disabled),i=o.map(n=>({...n,label:e(n.label),...n.title?{title:e(n.title)}:null}));/* @__PURE__ */
4
+ return Y(g,{label:e(n),icon:/* @__PURE__ */Y(O,{}),hint:a?.title?e(a.title):void 0,children:/* @__PURE__ */Y(J,{label:e(n),value:t,points:i,onChange:l})})}function Ne({say:e,label:n,value:t,onChange:l,pointsFor:o}){const{appState:a,selectedItem:i}=b(e=>e),r=i?.props?.id?String(i.props.id):void 0;return Ge(e,{label:n,value:t,onChange:l,points:o(a?.data,r)})}function Te({say:e,o:n}){const t=p(),o=k("panel",m),a=t?.values??{},i={x:ke(a[n.keys.x]),y:ke(a[n.keys.y])},r=e(n.label??"Position"),s=Boolean(i.x||i.y),u=e=>{const l=we(n.keys,i,e);Object.keys(l).length&&t?.onChangeMany(l)},c=/* @__PURE__ */Y(D,{label:r,value:i,onChange:u,words:{x:e("Left and right"),y:e("Up and down"),unit:"px"},placeholder:{x:"0",y:"0"},min:n.min,max:n.max,step:n.step??1}),d=/* @__PURE__ */Y(l,{words:o,label:r,changed:s,detail:"0 px, 0 px",onReset:()=>u({x:null,y:null})});return null===n.label?/* @__PURE__ */X("div",{className:"sf-position-line",children:[c,d]}):/* @__PURE__ */Y(g,{label:r,icon:/* @__PURE__ */Y(S,{}),changed:s,action:d,children:c})}var Ve={x:{start:/* @__PURE__ */Y(I,{}),center:/* @__PURE__ */Y(A,{}),end:/* @__PURE__ */Y(P,{}),stretch:/* @__PURE__ */Y(T,{}),between:/* @__PURE__ */Y(N,{})},y:{start:/* @__PURE__ */Y(G,{}),center:/* @__PURE__ */Y(B,{}),end:/* @__PURE__ */Y(K,{}),stretch:/* @__PURE__ */Y(W,{}),between:/* @__PURE__ */Y(M,{})}},$e=e=>"row"===e?"row":"column";function Je({say:e,kind:n,label:t,options:l,value:o,onChange:a,o:i}){const r=p(),s="justify"===n==("row"===(i.runs??$e)(r?.values?.[i.directionKey??"direction"]))?"x":"y";return Be(e,{options:l.map(e=>({label:e.label,value:e.value,picture:Ve[s][e.value]})),value:o,onChange:a,ariaLabel:t})}function De(e,n){const t=(e.useFallback??U)(n);return Object.fromEntries(xe.map(n=>[n,t?.[n]??e.fallback[n]]))}function He({say:e,tier:n,o:t,props:l}){const o=De(t,l),a=p(),i=a?.values??{},r="mobile"===n?t.phoneKeys:t.keys,s=Object.fromEntries(xe.map(e=>[e,Fe(i[r[e]])])),u="mobile"===n?Object.fromEntries(xe.map(e=>[e,Fe(i[t.keys[e]])])):null;/* @__PURE__ */
5
+ return Y(_e,{say:e,o:t,fallback:o,value:s,follows:u,readOnly:l.readOnly,onChange:e=>{const n=je(r,s,e);Object.keys(n).length&&a?.onChangeMany(n)}})}function Ye({say:e,o:n,props:t}){const l=De(n,t);/* @__PURE__ */
6
+ return Y(_e,{say:e,o:n,fallback:l,value:Le(t.value),follows:"follows"in t?Le(t.follows):null,readOnly:t.readOnly,onChange:e=>t.onChange(Oe(e))})}function _e({say:l,o:o,fallback:a,value:i,follows:r,readOnly:s,onChange:u}){const c=Object.fromEntries(xe.map(e=>[e,String(r?.[e]??a[e])])),[d,p]=w(()=>e(i,c));/* @__PURE__ */
7
+ return Y(g,{label:l(o.label),icon:o.icon??/* @__PURE__ */Y(R,{}),action:/* @__PURE__ */Y(t,{each:d,label:l("Each side"),onChange:p,disabled:s}),children:/* @__PURE__ */Y(n,{label:l(o.label),each:d,value:i,placeholder:c,min:o.min,max:o.max,step:o.step,readOnly:s,onChange:u,words:{sides:{top:l("Padding top"),right:l("Padding right"),bottom:l("Padding bottom"),left:l("Padding left")},vertical:l("Top and bottom"),horizontal:l("Left and right"),unit:"px"}})})}var Xe=e=>""===e||"px"===e?e:` ${e}`;function ze({label:e,value:n,onChange:t,min:o,max:a,step:i=1,unit:r="",hint:s,emptyLabel:u,inputMax:c,readOnly:d=!1,clears:p,detail:f,resetWords:b}){const m="number"==typeof n,h=p?/* @__PURE__ */Y(l,{label:e,changed:m,detail:f??u??(void 0===s?void 0:`${s}${Xe(r)}`),disabled:d,onReset:()=>t(void 0),...b?{words:b}:{}}):void 0;return{track:/* @__PURE__ */Y("div",{className:"sf-slider",children:/* @__PURE__ */Y(H,{min:o,max:a,step:i,value:n,suffix:r||void 0,showInput:!0,label:e,disabled:d,...p&&void 0!==s?{placeholderValue:s}:{},...u?{emptyLabel:u}:{},...void 0!==c?{inputMax:c}:{},size:"sm",boxSize:"md",className:"sf-slider-track",onChange:e=>{null===e?p&&m&&t(void 0):e!==n&&t(e)}})}),reset:h}}function Ze({caption:e,label:n,value:t,hint:l,emptyLabel:o,...a}){const i=void 0!==l||void 0!==o,r="number"==typeof t,{track:s,reset:u}=ze({...a,label:n,value:r?t:i?null:a.min,hint:l,emptyLabel:o,clears:i});/* @__PURE__ */
8
+ return Y(g,{label:e??n,action:u,children:s})}function Ue({draw:e,props:n,useMax:t,useHint:l}){return e(n,{max:(t??U)(n),hint:(l??U)(n)})}function qe(e=z){function n(n,t,l,o,a,i,s="",u){const c="function"==typeof u?{action:u}:u??{},{action:d,clear:p}=c,f=(r,u)=>{const f="storedValue"in r?r.storedValue:r.value,b="follows"in r,m=b&&!1!==p?.phoneFollows,h=b||void 0!==p,v="number"==typeof r.value,y=void 0===p?i:u.hint??("function"==typeof p.hint?p.hint(r):p.hint)??i,k=m&&"number"==typeof r.follows?r.follows:y,w=Xe(s),x=!p?.emptyLabel||m&&"number"==typeof r.follows?void 0:p.emptyLabel,C=x??`${k}${w}`,F="function"==typeof c.hint?c.hint(r):c.hint,{track:j,reset:L}=ze({label:e(n),value:v?r.value:h?null:i,onChange:e=>r.onChange(e),min:l,max:u.max??o,step:a,unit:s,hint:h?k:void 0,emptyLabel:x,inputMax:c.inputMax,readOnly:!0===r.readOnly,clears:h,detail:m?Z(e,C):C});/* @__PURE__ */
9
+ return Y(g,{label:e(n),icon:ne(t),hint:void 0===F?void 0:e(F),action:h?L:d?d(f,r.onChange):void 0,children:j})},b=c.useMax,m=p?.useHint;return{type:"custom",...r({},{type:"number",label:n}),render:b||m?e=>/* @__PURE__ */Y(Ue,{draw:f,props:e,useMax:b,useHint:m}):e=>f(e,{})}}function t(n){return Be(e,n)}function s(n,o,a,i,s={}){const u=a.map(e=>"string"==typeof e?{label:e,value:e}:e);return{type:"custom",...r({},{type:"radio",label:n,options:u.map(e=>({value:e.value,label:e.label}))}),render:a=>{const{value:r,onChange:c,field:d}=a,p=u.some(e=>e.value===r),f=!0===a.readOnly;if("follows"in a&&!u.some(e=>""===e.value)){const d=s.phoneRest?s.phoneRest(a.follows):a.follows,b=u.find(e=>e.value===d)??u.find(e=>e.value===i);/* @__PURE__ */
10
+ return Y(g,{label:e(n),icon:ne(o),action:/* @__PURE__ */Y(l,{label:e(n),changed:p,detail:Z(e,e(b?.label??"")),disabled:f,onReset:()=>c(void 0)}),children:t({options:u,value:p?r:b?.value??i,onChange:c,ariaLabel:n,disabled:f})})}const b="follows"in a&&s.followsLabel?s.followsLabel(a.follows):void 0;/* @__PURE__ */
11
+ return Y(g,{label:e(n),icon:ne(o),action:"follows"in a&&s.followsLabel?/* @__PURE__ */Y(l,{label:e(n),changed:null!=r&&""!==r,detail:void 0===b?void 0:Z(e,e(b)),disabled:f,onReset:()=>c(void 0)}):void 0,children:t({options:u,value:p?r:r||d?.currentValue||i,onChange:c,ariaLabel:n,disabled:f})})}}}function c(e,n){return n.phone?e.unshift({label:"Match desktop",value:"",picture:/* @__PURE__ */Y(E,{})}):n.inherit&&e.unshift({label:n.inherit,value:""}),e}function d(e,n){return n.inherit||n.phone?{...e,render:n=>e.render({...n,onChange:e=>n.onChange(""===e?void 0:e)})}:e}function p(e,n,t,l){if(l.access){const o=l.inherit?"":l.fallback??"left",a=t({...l,phone:!1}),i=e=>{const n="string"==typeof e&&""!==e&&a.some(n=>n.value===e)?e:o;return a.find(e=>e.value===n)?.label};return We(e,n(l,o),l.access,{phoneRow:n({...l,phone:!0},"",i)})}return{[e]:o("desktop",n(l)),[u(e)]:o("mobile",n({...l,phone:!0},""))}}function f(e){return c($(e.allow??Ke).map(e=>({label:e.label,value:e.value,picture:e.icon})),e)}function b(e,n=e.fallback??"left",t){return d(s(e.label??"Text alignment",/* @__PURE__ */Y(x,{}),f(e),n,t?{followsLabel:t}:{}),e)}function m(e){const n={left:Ve.x.start,center:Ve.x.center,right:Ve.x.end};return c($((e.allow??Ke).filter(e=>e in n)).map(e=>({label:e.label,value:e.value,picture:n[e.value]})),e)}function y(e,n=e.fallback??"left",t){return d(s(e.label??"Block alignment",/* @__PURE__ */Y(C,{}),m(e),n,t?{followsLabel:t}:{}),e)}function k(n,t,l,o,a,i){return{type:"custom",options:o,fallback:a,...r({},{type:"radio",label:t,options:o.map(e=>({value:e.value,label:e.label}))}),render:({value:r,onChange:s,field:u})=>{const c=u.options??o,d=u.fallback??a,p=c.some(e=>e.value===r)?r:d;/* @__PURE__ */
12
+ return Y(g,{label:e(t),icon:ne(l),children:/* @__PURE__ */Y(Je,{say:e,kind:n,label:t,options:c,value:p,onChange:s,o:i})})}}}function w(e={}){return k("justify","Justify",/* @__PURE__ */Y(C,{}),ie,ie[0].value,e)}function O(e={}){return k("align","Item alignment",/* @__PURE__ */Y(V,{}),re,"stretch",e)}function S(){const n=se;return{type:"custom",options:ue,fallback:ue[1].value,...r({},{type:"radio",label:n,options:ue.map(e=>({value:e.value,label:e.label}))}),render:({value:t,onChange:l,id:o,name:a})=>{const i=`${o??a??n}-label`;/* @__PURE__ */
13
+ return Y(g,{label:e(n),labelId:i,icon:/* @__PURE__ */Y(F,{}),action:/* @__PURE__ */Y(h,{checked:(r=t,!0===r||"wrap"===r),onChange:e=>l(e?ue[0].value:ue[1].value),ariaLabelledBy:i})});var r}}}function P({offered:e,fallback:n,resolve:t}){const l=t??(t=>e.some(e=>e.value===t)?t:n),o=s(ce,Q("`Direction` names how a container’s children run — a MODIFIER, not a thing. doc/naming.md N10.1 gives the idea a dash in its Glyph column; the shell’s own `direction` meaning says the bare row is CORRECT and not an omission (SS seat, 2026-09-11)."),e.map(e=>({label:e.label,value:e.value})),l(void 0));return{type:"custom",render:e=>o.render({...e,value:l(e.value),onChange:n=>e.onChange(n)})}}function A({min:e,max:t,step:l,builtIn:o,resolve:a,action:i,mark:r}){const s=n("Gap",/* @__PURE__ */Y(j,{}),e,t,l,o,"px",i),u=a??(e=>"number"==typeof e?e:o);return{...r,type:"custom",render:e=>s.render({...e,value:u(e.value,e.field),storedValue:e.value,onChange:n=>e.onChange(n)})}}function I(n,{fallback:t=me,pointsFor:l}={}){return{type:"custom",render:({value:o,onChange:a})=>{const i=ge(o,t);return l?/* @__PURE__ */Y(Ne,{say:e,label:n,value:i,onChange:a,pointsFor:l}):Ge(e,{label:n,value:i,onChange:a,points:ve})}}}function R(n){return{[n.keys.x]:{type:"custom",render:()=>/* @__PURE__ */Y(Te,{say:e,o:n})},[n.keys.y]:{...a({type:"custom",render:()=>/* @__PURE__ */Y(i,{})},"always"),reachedBy:n.keys.x}}}function M(n={}){const t={...Ce,...n.keys},l={keys:t,phoneKeys:Object.fromEntries(xe.map(e=>[e,u(t[e])])),fallback:{top:0,right:0,bottom:0,left:0,...n.fallback},useFallback:n.useFallback,label:n.label??"Padding",icon:n.icon,min:n.min??0,max:n.max??200,step:n.step??4},r=!1!==n.phone,s=!1===n.group?{}:{_gSpacing:oe("Padding")};if(n.object){const t={type:"custom",render:n=>/* @__PURE__ */Y(Ye,{say:e,o:l,props:n})};return te({...s,...r?We(n.object.key,t,n.object.phone):{[n.object.key]:t}})}const c=n=>{const t={type:"custom",render:t=>/* @__PURE__ */Y(He,{say:e,tier:n,o:l,props:t})};return r?o(n,t):t},d=()=>a({type:"custom",render:()=>/* @__PURE__ */Y(i,{})},"always"),p=xe.filter(e=>"top"!==e);return te({...s,[l.keys.top]:c("desktop"),...r?{[l.phoneKeys.top]:c("mobile")}:{},...Object.fromEntries(p.map(e=>[l.keys[e],{...d(),reachedBy:l.keys.top}])),...r?Object.fromEntries(p.map(e=>[l.phoneKeys[e],o("mobile",d())])):{}})}return{slider:n,select:function(n,t,o,a,i){return{type:"custom",options:o,fallback:a,...i?{note:i}:{},...r({},{type:"select",label:n}),render:r=>{const{value:s,onChange:u,field:c}=r,d=c.options??o,p=c.fallback??a,f=d.some(e=>e.value===s),b="follows"in r,m=b?d.find(e=>e.value===r.follows)??d.find(e=>e.value===p):void 0,h=f?s:m?.value??p,y=!0===r.readOnly;/* @__PURE__ */
14
+ return Y(g,{label:e(n),icon:ne(t),action:b?/* @__PURE__ */Y(l,{label:e(n),changed:null!=s&&""!==s,detail:m?Z(e,e(m.label)):void 0,disabled:y,onReset:()=>u(void 0)}):void 0,children:/* @__PURE__ */Y(v,{value:h,ariaLabel:e(n),readOnly:y,onChange:u,options:[...i?[{label:e(i),value:"\0note",disabled:!0}]:[],...d.map(n=>({label:e(n.label),value:n.value}))]})})}}},Segmented:t,segmented:s,boxJustifyField:w,boxAlignField:O,boxWrapField:S,directionField:P,gapField:A,placementField:I,PlacementGridView:function(n){return Ge(e,n)},positionFields:R,sidesPaddingFields:M,alignmentField:function(e={},n=e.fallback??"left"){return b(e,n)},alignmentFields:function(e="align",n={}){return p(e,b,f,n)},AlignmentStrip:function({opts:e={},value:n,onChange:l,ariaLabel:o,disabled:a=!1}){return t({options:f(e),value:n,onChange:l,ariaLabel:o,disabled:a})},blockAlignmentField:function(e={},n=e.fallback??"left"){return y(e,n)},blockAlignmentFields:function(e="align",n={}){return p(e,y,m,n)},BlockAlignmentStrip:function({opts:e={},value:n,onChange:l,ariaLabel:o,disabled:a=!1}){return t({options:m(e),value:n,onChange:l,ariaLabel:o,disabled:a})},boxFieldSet:function(e={}){const t=e.width??fe,l=e.nudge?.limit??40,o=e.direction??{offered:de,fallback:"column"},a=o.resolve??(e=>o.offered.some(n=>n.value===e)?e:o.fallback),i={runs:e=>"row"===a(e)?"row":"column"};return{_gLayout:oe("Layout"),placement:I("Placement",e.placement),width:n("Width",/* @__PURE__ */Y(L,{}),t.min,t.max,t.step,t.fallback,"%"),direction:P(o),gap:A(e.gap??pe),justify:w(i),align:O(i),wrap:S(),_gNudge:oe("Nudge"),...R({keys:e.nudge?.keys??{x:"nudgeX",y:"nudgeY"},min:-l,max:l,label:null}),...M(e.padding)}}}}var{slider:Qe,select:en,Segmented:nn,segmented:tn,boxJustifyField:ln,boxAlignField:on,boxWrapField:an,directionField:rn,gapField:sn,placementField:un,PlacementGridView:cn,positionFields:dn,sidesPaddingFields:pn,alignmentField:fn,alignmentFields:bn,AlignmentStrip:mn,blockAlignmentField:hn,blockAlignmentFields:vn,BlockAlignmentStrip:gn,boxFieldSet:yn}=qe();export{he as ANCHOR_LABELS,mn as AlignmentStrip,re as BOX_ALIGN_POINTS,de as BOX_DIRECTIONS,ce as BOX_DIRECTION_LABEL,pe as BOX_GAP,ie as BOX_JUSTIFY_POINTS,ye as BOX_NUDGE_LIMIT,be as BOX_PLACEMENTS,me as BOX_PLACEMENT_DEFAULT,fe as BOX_WIDTH,se as BOX_WRAP_LABEL,ue as BOX_WRAP_POINTS,gn as BlockAlignmentStrip,Ae as FLAT_ACCESS,ve as OPEN_PLACEMENT_POINTS,Ie as PHONE_KEY_ACCESS,cn as PlacementGridView,Ce as SIDE_PAD_KEYS,nn as Segmented,Ze as SliderControl,fn as alignmentField,bn as alignmentFields,hn as blockAlignmentField,vn as blockAlignmentFields,on as boxAlignField,qe as boxFieldBuilders,yn as boxFieldSet,ln as boxJustifyField,an as boxWrapField,rn as directionField,sn as gapField,oe as group,ne as iconOf,ee as isNoGlyph,Q as noGlyph,ge as normaliseBoxPlacement,Se as objectAccess,Z as onDesktopWords,Re as phoneObjectAccess,We as phoneTwinFields,un as placementField,dn as positionFields,we as positionPatch,ae as section,tn as segmented,en as select,te as sharedStyleKinds,Le as sidesFromObject,pn as sidesPaddingFields,je as sidesPaddingPatch,Oe as sidesToObject,Qe as slider,Pe as useAccessPatch,Ee as useAccessValue};
@@ -1,5 +1,5 @@
1
- import{DEFAULT_PAGE_COPY_WORDS as t}from"../chunk-CEC5XLN4.js";export{DEFAULT_PAGE_COPY_WORDS}from"../chunk-CEC5XLN4.js";import{useWords as e}from"../chunk-MFIRYGHW.js";import{slugify as n}from"efficient-shell/format";import{useState as o,useId as i}from"react";import{FormField as l,Input as r,Dialog as a,Button as u}from"efficient-shell/ui";import{jsxs as s,jsx as c}from"react/jsx-runtime";var h=255,f=/* @__PURE__ */new Set(["normal","dynamic"]);function m(t,e){return(!t.kind||f.has(t.kind))&&!e?.(t.slug)}function g(t){const e=n(t);return e&&/[^a-z0-9]$/.test(t.toLowerCase())?`${e}-`:e}function d(t,e){const n=new Set(e);if(!n.has(t))return t;let o=2;for(;n.has(`${t}-${o}`);)o+=1;return`${t}-${o}`}function p(t,e){return d(n(t)||"page",e)}function C(t,e){return new Set(e).has(t)}function y(t,e){return{title:`${t.title||t.slug} copy`,slug:p(`${t.slug}-copy`,e)}}function b({source:t,taken:e,duplicate:i,onDuplicated:l}){const[r]=o(()=>y(t,e)),[a,u]=o(r.title),[s,c]=o(r.slug),[h,f]=o(!1),[m,d]=o(!1),[p,b]=o(!1),[v,k]=o(!1),x=C(s,e),S=Boolean(a.trim()&&s&&!x&&!p);return{title:a,slug:s,clash:x,canSubmit:S,busy:p,failed:v,touched:m,titleMax:255,onTitleChange:t=>{d(!0),u(t),h||c(n(t))},onSlugChange:t=>{d(!0),f(!0),c(g(t))},submit:async()=>{if(S){b(!0),k(!1);try{l(await i({title:a.trim(),slug:s}))}catch{k(!0)}finally{b(!1)}}}}}function v({form:n,host:o}){const a=e("pageCopy",t),u=i(),h=i(),f=t=>{"Enter"===t.key&&n.submit()};/* @__PURE__ */
2
- return s("div",{className:"flex flex-col gap-4",children:[
3
- /* @__PURE__ */c(l,{label:a.titleLabel,htmlFor:u,error:n.failed?a.failed:void 0,hint:a.hint,children:/* @__PURE__ */c(r,{id:u,autoFocus:!0,value:n.title,maxLength:n.titleMax,onChange:t=>n.onTitleChange(t.target.value),onKeyDown:f})}),
4
- /* @__PURE__ */c(l,{label:a.slugLabel,htmlFor:h,error:n.clash?a.clash:void 0,hint:a.address({host:o,slug:n.slug}),children:/* @__PURE__ */c(r,{id:h,value:n.slug,invalid:n.clash,onChange:t=>n.onSlugChange(t.target.value),onKeyDown:f})})]})}function k({host:n,onClose:o,...i}){const l=e("pageCopy",t),r=b(i),s=i.source.title||i.source.slug;/* @__PURE__ */
5
- return c(a,{open:!0,dirty:r.touched,onClose:()=>{r.busy||o()},title:l.title({name:s}),size:"sm",footer:/* @__PURE__ */c(u,{disabled:!r.canSubmit,loading:r.busy,onClick:()=>{r.submit()},children:l.submit}),children:/* @__PURE__ */c(v,{form:r,host:n})})}export{h as DUPLICATE_TITLE_MAX,k as DuplicatePageDialog,v as DuplicatePageFields,m as canDuplicatePage,y as copySuggestion,d as firstFreeSlug,g as slugWhileTyping,p as uniqueSlug,C as urlHandleTaken,b as usePageCopyForm};
1
+ import{DEFAULT_PAGE_COPY_WORDS as t}from"../chunk-CEC5XLN4.js";export{DEFAULT_PAGE_COPY_WORDS}from"../chunk-CEC5XLN4.js";import{useWords as e}from"../chunk-MFIRYGHW.js";import{slugify as n}from"efficient-shell/format";import{useState as o,useId as i}from"react";import{FormField as l,Input as r,Dialog as a,Button as s}from"efficient-shell/ui";import{jsxs as u,jsx as c}from"react/jsx-runtime";var h=255,m=/* @__PURE__ */new Set(["normal","dynamic"]);function f(t,e){return(!t.kind||m.has(t.kind))&&!e?.(t.slug)}function g(t){const e=n(t);return e&&/[^a-z0-9]$/.test(t.toLowerCase())?`${e}-`:e}function d(t,e){const n=new Set(e);if(!n.has(t))return t;let o=2;for(;n.has(`${t}-${o}`);)o+=1;return`${t}-${o}`}function p(t,e){return d(n(t)||"page",e)}function C(t,e){return new Set(e).has(t)}function y(t,e){return{title:`${t.title||t.slug} copy`,slug:p(`${t.slug}-copy`,e)}}function b({source:t,taken:e,duplicate:i,onDuplicated:l}){const[r]=o(()=>y(t,e)),[a,s]=o(r.title),[u,c]=o(r.slug),[h,m]=o(!1),[f,d]=o(!1),[p,b]=o(!1),[v,k]=o(!1),x=C(u,e),S=Boolean(a.trim()&&u&&!x&&!p);return{title:a,slug:u,clash:x,canSubmit:S,busy:p,failed:v,touched:f,titleMax:255,onTitleChange:t=>{d(!0),s(t),h||c(n(t))},onSlugChange:t=>{d(!0),m(!0),c(g(t))},submit:async()=>{if(S){b(!0),k(!1);try{l(await i({title:a.trim(),slug:u}))}catch{k(!0)}finally{b(!1)}}}}}function v({form:n,host:o}){const a=e("pageCopy",t),s=i(),h=i(),m=t=>{t.nativeEvent.isComposing||229===t.keyCode||"Enter"===t.key&&n.submit()};/* @__PURE__ */
2
+ return u("div",{className:"flex flex-col gap-4",children:[
3
+ /* @__PURE__ */c(l,{label:a.titleLabel,htmlFor:s,error:n.failed?a.failed:void 0,hint:a.hint,children:/* @__PURE__ */c(r,{id:s,autoFocus:!0,value:n.title,maxLength:n.titleMax,onChange:t=>n.onTitleChange(t.target.value),onKeyDown:m})}),
4
+ /* @__PURE__ */c(l,{label:a.slugLabel,htmlFor:h,error:n.clash?a.clash:void 0,hint:a.address({host:o,slug:n.slug}),children:/* @__PURE__ */c(r,{id:h,value:n.slug,invalid:n.clash,onChange:t=>n.onSlugChange(t.target.value),onKeyDown:m})})]})}function k({host:n,onClose:o,...i}){const l=e("pageCopy",t),r=b(i),u=i.source.title||i.source.slug;/* @__PURE__ */
5
+ return c(a,{open:!0,dirty:r.touched,onClose:()=>{r.busy||o()},title:l.title({name:u}),size:"sm",footer:/* @__PURE__ */c(s,{disabled:!r.canSubmit,loading:r.busy,onClick:()=>{r.submit()},children:l.submit}),children:/* @__PURE__ */c(v,{form:r,host:n})})}export{h as DUPLICATE_TITLE_MAX,k as DuplicatePageDialog,v as DuplicatePageFields,f as canDuplicatePage,y as copySuggestion,d as firstFreeSlug,g as slugWhileTyping,p as uniqueSlug,C as urlHandleTaken,b as usePageCopyForm};
@@ -1,534 +1,12 @@
1
1
  import * as react from 'react';
2
2
  import { ReactNode } from 'react';
3
- import { W as WordFit } from '../wordFit-7XpmhcaW.js';
4
- import { P as PanelWords, C as ColourRowWords } from '../words-BrN08Fup.js';
3
+ import { a as SettingsSegmentedProps, b as SettingsGroupProps, c as SettingsGroupHeadProps, d as SettingsGroupSubProps, e as SettingsFieldProps, f as SettingsSwitchProps, S as SettingsDefaultResetProps, g as SettingsDrillProps, h as SettingsActionProps } from '../types-BusXrvDU.js';
4
+ export { i as SettingsActionBaseProps, j as SettingsActionNote, k as SettingsSegmentedFit, l as SettingsSegmentedOption, m as SettingsSwitchBaseProps, n as SettingsSwitchLabel } from '../types-BusXrvDU.js';
5
5
  import { ColorPreset } from 'efficient-shell/ui';
6
6
  export { BorderInput as SettingsBorder, BorderLineStyle as SettingsBorderLineStyle, BorderInputProps as SettingsBorderProps, BorderValue as SettingsBorderValue, CornerOption as SettingsCornerPoint, CornersInput as SettingsCorners, CornersInputProps as SettingsCornersProps, AlignmentGrid as SettingsPlacement, AlignmentPoint as SettingsPlacementPoint, AlignmentGridProps as SettingsPlacementProps, SidesInput as SettingsSides, SidesEachToggle as SettingsSidesEach, SidesInputProps as SettingsSidesProps, SidesValue as SettingsSidesValue, borderSides, borderStartEach, sidesStartEach } from 'efficient-shell/ui';
7
+ import { C as ColourRowWords } from '../words-BrN08Fup.js';
7
8
  export { a as DESIGN_GROUPS, D as DesignGroup, b as SETTINGS_BUCKETS, S as SettingsBucket } from '../buckets-_MGDYJlb.js';
8
-
9
- /**
10
- * The settings-panel primitives — the shared grammar BOTH editors' inspector
11
- * panels are built from (owner-approved design, 2026-08-20).
12
- *
13
- * Every one is CONTROLLED and hook-free: the host owns which groups are open, exactly as the rail's host owns selection. That is what
14
- * keeps this a directive-free leaf a Next-16 server component can import.
15
- */
16
- /**
17
- * WHERE THIS STRIP'S WORDS STOP FITTING — the one thing the shell cannot know
18
- * about a segmented control, measured by the consumer that owns the words.
19
- *
20
- * The same shape the top-bar rail hands the same rule (`WordFit`, in
21
- * `src/fit/wordFit.ts`, where both numbers are described).
22
- *
23
- * MEASURE THEM, do not count letters. The chip's font resolves to SF on a Mac
24
- * and Segoe UI on Windows, so one table of letter widths would be wrong on most
25
- * machines; measured against real labels a letter-count guess is out by up to
26
- * 7%, which is enough to drop a word that fits. Render the strip and read the
27
- * label spans — `tests/fixtures/segmented-fit.html` does exactly that and
28
- * prints both numbers. If a label is about to change and the number cannot be
29
- * re-measured today, round it up.
30
- */
31
- type SettingsSegmentedFit = WordFit;
32
- /**
33
- * WHAT AN OPTION IS CALLED, and the one rule that governs it: EVERY option has a
34
- * name, and the name is a string (card 66303).
35
- *
36
- * `label` is spent three ways — the visible mark, `title`, and `aria-label` — so
37
- * it cannot simply be widened to a `ReactNode` and left there: a node reaches
38
- * the two attributes as `[object Object]`, which is a tooltip that says nothing
39
- * and a screen reader that says worse. Nor can it be split into `icon` + `label`
40
- * and the word left to fall away at a threshold: both spans render, so a strip
41
- * whose mark IS its label would draw the mark AND the word beside it.
42
- *
43
- * So the two cases are told apart in the type, and each carries its own name:
44
- *
45
- * a WORDED option `{ label: 'Left' }` — the word is the name. `title` is
46
- * optional and almost never given; this is every option that
47
- * exists today and none of them changes.
48
- * a DRAWN option `{ label: <AlignGlyph/>, title: 'Left' }` — the mark cannot
49
- * be read aloud, so `title` is REQUIRED and is the name.
50
- *
51
- * The part panel's Alignment row is the case that forced it: three alignments
52
- * have no honest three-letter word between them, so the label is the rule
53
- * itself. Case is the same shape one step milder — Aa / AA / aa are letterforms,
54
- * not the words Normal / Uppercase / Lowercase that name them. Passing `title`
55
- * beside a WORDED label is allowed and overrides the word, which is what a
56
- * strip whose words are abbreviations wants.
57
- *
58
- * ── NO CONSUMER TODAY, AND CARRIED ON PURPOSE ────────────────────────────────
59
- *
60
- * Written down so the next reader does not find this unused and tidy it away.
61
- * The rows above stopped needing it while this was being built: card 66246
62
- * (`efficient-storefront` 151afa212, merged 2026-08-31) moved the part panel's three
63
- * tables onto `icon` + a real word — `{ id: "left", label: "Left", icon:
64
- * <AlignLeftIcon/> }` — on the owner's ruling that an option says its name AND
65
- * draws its glyph. Every `label` in the shop is now a string, and this arm has
66
- * no caller anywhere. That is measured on merged main, not on a pending PR.
67
- *
68
- * It stays because it is the shape THE PACKAGE NEXT DOOR ALREADY USES.
69
- * `efficient-shell` ships its own `Segmented`, and its `SegmentedOption` has
70
- * carried `label: ReactNode` all along. Identical at the two copies installed on
71
- * this desk — 4.63.0 under `editor-shell`, 4.67.0 under `efficient-storefront`:
72
- *
73
- * interface SegmentedOption<T extends string = string> {
74
- * value: T; label: ReactNode; icon?: ReactNode; disabled?: boolean;
75
- * }
76
- *
77
- * A shared control that refuses a shape its sibling accepts is a fork one level
78
- * down, which is the fault this whole family of cards exists to stop. Matching
79
- * it costs nothing while nothing calls it.
80
- *
81
- * ── WHERE THE TWO SHELLS CONVERGE, AND WHERE THEY DO NOT ─────────────────────
82
- *
83
- * Read this before repeating "the two shells are converged", because it is only
84
- * true of three fields out of four, and the fourth is the one a caller touches
85
- * most:
86
- *
87
- * efficient-shell editor-shell
88
- * the label label: ReactNode label: ReactNode converged
89
- * the glyph icon?: ReactNode icon?: ReactNode converged
90
- * out of force disabled?: boolean disabled?: boolean converged
91
- * ─────────────────────────────────────────────────────────────────────────
92
- * the VALUE value: T id: V NOT converged
93
- * T extends string V unconstrained
94
- *
95
- * BOTH halves of that last row are deliberate.
96
- *
97
- * `id` and not `value`, because this field has been called `id` since the type
98
- * existed and renaming it would move something for every caller already using
99
- * it — which is the one thing card 66303 was not allowed to do.
100
- *
101
- * `V` UNCONSTRAINED and not `T extends string`, because the shop stores a
102
- * choice as a boolean: `reverse` at the storefront's old block config is
103
- * `false` = image left and `true` = image right on every published page across
104
- * every tenant. `T extends string` cannot hold it, and giving that row the house
105
- * strip by storing "left"/"right" would be a migration of live customer data
106
- * rather than a control change. So on the value type this package is the WIDER
107
- * of the two, not the poorer — the opposite of the direction the rest of this
108
- * note runs.
109
- *
110
- * ── AND WHERE WE DELIBERATELY GO FURTHER ─────────────────────────────────────
111
- *
112
- * That sibling declaration has NO name field beside its node label. A drawing
113
- * that cannot name itself is exactly the defect the 2026-08-06 audit closed on
114
- * the shop's strip, so the `title` here is REQUIRED — a guarantee, not a
115
- * feature. The union makes a nameless drawing unconstructable rather than a
116
- * documented risk; the sibling's shape permits one.
117
- *
118
- * One more thing that declaration settles, recorded because it was argued: its
119
- * `disabled` is PER OPTION and it has no strip-level one at all. That is the
120
- * independent support for the per-option field on
121
- * `SettingsSegmentedOptionBase` — the sibling has shipped exactly that.
122
- */
123
- type SettingsSegmentedLabel = {
124
- label: string;
125
- title?: string;
126
- } | {
127
- label: ReactNode;
128
- title: string;
129
- };
130
- interface SettingsSegmentedOptionBase<V = string> {
131
- /**
132
- * The VALUE this option writes, as `value` and `onChange` name it.
133
- *
134
- * `V` and not `string` since card 66303. A weight is a NUMBER — the storefront
135
- * part panel's row is `[400, 500, 600, 700]` — and "Image side" is a BOOLEAN
136
- * on every published page across every tenant, so giving those rows the house
137
- * strip by storing "left"/"right" would be a migration of live customer data
138
- * rather than a control change. `V` defaults to `string`, so every existing
139
- * declaration means exactly what it did.
140
- */
141
- id: V;
142
- /**
143
- * A 16px glyph from `efficient-easel/icons`, so the option can spend less width
144
- * than its word needs when the panel is dragged narrow.
145
- *
146
- * Optional, and the strip is unchanged without it: an option with no glyph
147
- * has nothing to fall back to, so it keeps its word at every width. Pass
148
- * glyphs for ALL the options or none of them — half words and half glyphs
149
- * reads as a rendering fault, not as a decision.
150
- *
151
- * NOT the same thing as a DRAWN label. An `icon` is a glyph BESIDE the word,
152
- * and the word is what it falls back from; a drawn label replaces the word
153
- * altogether and is named by its `title`. An option wanting the second one
154
- * puts the mark in `label`, not here.
155
- */
156
- icon?: ReactNode;
157
- /**
158
- * This choice exists but cannot be taken right now.
159
- *
160
- * Renders the button's own `disabled`, not a fade over it, and that is the
161
- * whole point of it living here: this component owns the `<button>`, so a host
162
- * dimming a parent instead leaves every segment focusable and announcing as
163
- * ENABLED — the exact defect the 2026-08-06 audit and card 66034 closed.
164
- *
165
- * Dimmed, NEVER hidden, and the pressed segment still reads as pressed: the
166
- * merchant's saved answer is still their answer, it simply is not in force.
167
- * Defaults absent, so nothing an existing caller renders moves.
168
- *
169
- * NO CONSUMER TODAY, and it is carried on purpose rather than by oversight —
170
- * so the next reader does not find it unused and tidy it away. The case it is
171
- * for is one row away: card 66246 draws Weight as four letterform glyphs, and
172
- * a merchant's typeface may only carry two real weights (measured there —
173
- * Helvetica, Georgia, Verdana and Impact render Regular and Medium as the same
174
- * letter, and Semibold and Bold as the same letter). That is a choice the
175
- * strip CAN offer and the font cannot honour, which is this field and not the
176
- * strip-level one beside it.
177
- */
178
- disabled?: boolean;
179
- }
180
- /** One option in a segmented control. */
181
- type SettingsSegmentedOption<V = string> = SettingsSegmentedOptionBase<V> & SettingsSegmentedLabel;
182
- /**
183
- * A SEGMENTED CONTROL — the shell's chip, with the other promise.
184
- *
185
- * A tab strip (the shell's `Tabs`) is a TABLIST: its chips switch a panel,
186
- * which is what `role="tab"` promises. These chips switch nothing. They write
187
- * a value and the panel stays where it is, so they are pressed buttons in a
188
- * group, and the
189
- * marker is `aria-pressed` — the same one `efficient-storefront`'s own `Segmented`
190
- * has carried since the 2026-08-06 audit.
191
- *
192
- * NOT `radiogroup` / `aria-checked`, and the reason is the keyboard rather than
193
- * the taxonomy. `role="radio"` promises roving focus: one Tab stop for the set,
194
- * arrows moving between the options and selecting as they go. This is a
195
- * hook-free controlled leaf — it owns no focus and runs no key handler, and a
196
- * host cannot add one to a `<button>` it did not render. Radio roles over
197
- * plain tab-stopped buttons would promise a keyboard that is not there, which
198
- * is the same defect as `role="tab"` over no panel.
199
- */
200
- interface SettingsSegmentedProps<V = string> {
201
- /**
202
- * `readonly` so a panel may declare its option table `as const` and keep the
203
- * literal value types — which is how every one of them is already written.
204
- * A widening only: a mutable array still binds.
205
- */
206
- options: readonly SettingsSegmentedOption<V>[];
207
- /**
208
- * The `id` of the option currently set.
209
- *
210
- * A value matching NO option presses nothing, and that is a state a caller
211
- * relies on rather than an edge: the part panel hands `""` when the selection
212
- * is mixed across several parts. The match is an EQUALITY and never a
213
- * truthiness test — `false` is a real answer ("image left"), and `value ||
214
- * fallback` collapses it into the other one.
215
- */
216
- value: V;
217
- onChange: (next: V) => void;
218
- /**
219
- * What the group is called — "Weight", "Alignment". Required: a group of
220
- * pressed buttons with no name is a set a screen reader cannot introduce.
221
- */
222
- ariaLabel: string;
223
- /**
224
- * Where this strip's words stop fitting. Optional, and WITHOUT IT NOTHING
225
- * DROPS: an unmeasured strip keeps every word at every width, because the
226
- * only alternative is guessing where to hide one. Give the options glyphs and
227
- * this too, or the glyphs have nothing to fall back at.
228
- */
229
- fit?: SettingsSegmentedFit;
230
- /**
231
- * The whole strip exists but cannot apply right now, because another setting
232
- * has taken the decision away — the Header's "Text colour at the top" under a
233
- * pinned Background (card 66216).
234
- *
235
- * Every segment goes out of force AND the track says so, so the control goes
236
- * quiet as one thing rather than as three identically faded chips. Per-option
237
- * `disabled` is the other half of this: use that for a choice the strip cannot
238
- * offer, and this for a strip the panel cannot offer.
239
- *
240
- * Dimmed, never hidden — a control that disappears reads as a feature that was
241
- * removed, and leaves the merchant no way to see what to change to get it
242
- * back. Defaults false, so every existing caller renders byte for byte as it
243
- * did.
244
- */
245
- disabled?: boolean;
246
- className?: string;
247
- }
248
- interface SettingsGroupProps {
249
- /** The group's name — "Items", "Spacing", "Typeface". */
250
- title: string;
251
- /** Open state lives with the host, so it can persist it. */
252
- open: boolean;
253
- onToggle: (open: boolean) => void;
254
- /**
255
- * How many settings in this group differ from their default. Shown as a chip
256
- * on the pill so a FOLDED group still admits it holds changes.
257
- */
258
- count?: number;
259
- /**
260
- * The heading level the title draws at, in the HOST's outline (7.2.0). The
261
- * fold button sits inside an `h{level}`, the WAI-ARIA accordion pattern.
262
- * Defaults to 3, a group under a panel's `h2` name.
263
- */
264
- level?: 2 | 3 | 4 | 5 | 6;
265
- /** Ties the pill to its body for assistive tech (`aria-controls`). One DOM
266
- * id: no whitespace (the body's id is this plus `-body`). */
267
- id?: string;
268
- children: ReactNode;
269
- className?: string;
270
- }
271
- /**
272
- * A group heading with nothing folded under it — see `SettingsGroupHead`.
273
- *
274
- * Deliberately NOT `SettingsGroupProps` minus a few fields: a heading has no
275
- * open state to own, nothing to toggle, and no count, because a heading hides
276
- * nothing that a count would have to admit to.
277
- */
278
- interface SettingsGroupHeadProps {
279
- /** The heading's name — "Items", "Spacing", "Typeface". */
280
- title: string;
281
- /**
282
- * Which heading level this is in the HOST's outline. Defaults to 3, the level
283
- * a panel section sits at under a panel title; only the host knows what sits
284
- * above it.
285
- */
286
- level?: 2 | 3 | 4 | 5 | 6;
287
- /** A DOM id, so the rows it heads can point `aria-labelledby` at it. */
288
- id?: string;
289
- className?: string;
290
- }
291
- interface SettingsGroupSubProps {
292
- /** The heading's name — "Content", "Background", "Image". */
293
- title: string;
294
- /**
295
- * Which heading level this is in the HOST's outline. Defaults to 4 — one under
296
- * `SettingsGroupHead`'s 3, because this tier sits under that pill. Only the
297
- * host knows what is actually above it.
298
- */
299
- level?: 2 | 3 | 4 | 5 | 6;
300
- /** A DOM id, so the rows it heads can point `aria-labelledby` at it. */
301
- id?: string;
302
- className?: string;
303
- }
304
- interface SettingsFieldProps {
305
- label: string;
306
- /**
307
- * An `id` for the label text, so a control that cannot be reached by
308
- * `htmlFor` can still be named by it.
309
- *
310
- * That is the boolean row: `htmlFor` associates a <label> with a form
311
- * control, and a switch is a <button>. Give the label an id here and hand the
312
- * same id to the switch's `ariaLabelledBy`, and the accessible name and the
313
- * visible name are the same string.
314
- */
315
- labelId?: string;
316
- /** An 18px glyph from `efficient-easel/icons` — one meaning, one SVG. */
317
- icon?: ReactNode;
318
- /** The current value, right-aligned on the label row ("34 px", "style"). */
319
- value?: ReactNode;
320
- /** One quiet line under the control. Use it instead of a clause-long label. */
321
- hint?: ReactNode;
322
- /** Marks the row as changed from its default: the label takes the accent. */
323
- changed?: boolean;
324
- /** Set when the control is a single form element, so the label points at it. */
325
- htmlFor?: string;
326
- /**
327
- * A trailing control on the label row — `SettingsDefaultReset` (Default /
328
- * Reset), or the SWITCH of a boolean row (card 8801, Option A: a yes-or-no
329
- * setting is one line, not two, so the switch takes the spot a value like
330
- * "34 px" normally sits in).
331
- */
332
- action?: ReactNode;
333
- /**
334
- * The control itself, on its own line under the label.
335
- *
336
- * OPTIONAL since card 8801, and only so a boolean row can put its switch in
337
- * `action` and render no control line at all. Omitting it is not a way to
338
- * draw a label on its own: a field with NO children and NO action has no
339
- * control on either slot, so it renders nothing rather than leaving an empty
340
- * row behind. Every field that passes a control is unaffected.
341
- */
342
- children?: ReactNode;
343
- className?: string;
344
- }
345
- /**
346
- * `SettingsDefaultReset` — the end of a label line: a ↺ button while the value
347
- * is changed, an empty 22px slot while it follows its default (owner
348
- * 2026-10-06; option E, final 2026-10-07).
349
- */
350
- interface SettingsDefaultResetProps {
351
- /** The field's visible label. The ↺ button is named after it ("Reset Gap"). */
352
- label: string;
353
- /** Is the value changed from its default? The host's to say. */
354
- changed: boolean;
355
- /** Clear the value back to its default. The part writes nothing itself. */
356
- onReset: () => void;
357
- /**
358
- * What the default IS, in the host's words — "24px from Site styles". The ↺
359
- * says it: in its tooltip ("Reset Gap (back to 24px from Site styles)") and
360
- * as its accessible description, while its NAME stays "Reset Gap" (GG
361
- * 2026-10-07, E2). Read it live; a baked value goes stale. Nothing shows it
362
- * while the setting is unchanged: the slot is empty then.
363
- */
364
- detail?: string;
365
- /** Read-only (a sent campaign): ↺ is drawn, and cannot be pressed. */
366
- disabled?: boolean;
367
- /**
368
- * Rare: something to press while the setting is UNCHANGED, for a row a
369
- * merchant cannot leave by hand. The website's type sizes are the case (GG
370
- * 2026-10-06, Q3): their default can be a responsive `clamp()` no slider
371
- * position stores, so one press keeps the section's own built-in size.
372
- * `icon` is drawn in the 22px slot; `title` is its name and its tooltip.
373
- * Left out, an unchanged row draws nothing — the usual case.
374
- */
375
- defaultAction?: {
376
- onPress: () => void;
377
- title: string;
378
- icon: ReactNode;
379
- };
380
- /** The host's words, from `useWords('panel', …)`. English when left out. */
381
- words?: Partial<Pick<PanelWords, 'followsDefault' | 'reset' | 'resetName' | 'backToDefault' | 'resetTitle'>>;
382
- className?: string;
383
- }
384
- /**
385
- * A switch has no text of its own, so without one of these it announces
386
- * "switch, on" and nothing else. The union makes an unnamed switch a COMPILE
387
- * error rather than a documented risk — both editors are about to grow six of
388
- * these per panel, and every one is a chance to forget.
389
- *
390
- * Prefer `ariaLabelledBy` inside a `SettingsField`: point it at the field's
391
- * `labelId` and the accessible name IS the visible label, one string doing two
392
- * jobs, so the two cannot drift apart. `ariaLabel` is for a switch standing on
393
- * its own with no visible label to point at.
394
- *
395
- * `?: never` on the other arm is what makes the two mutually exclusive while
396
- * still letting the component destructure both names.
397
- */
398
- type SettingsSwitchLabel = {
399
- ariaLabel: string;
400
- ariaLabelledBy?: never;
401
- } | {
402
- ariaLabelledBy: string;
403
- ariaLabel?: never;
404
- };
405
- interface SettingsSwitchBaseProps {
406
- /** Whether the setting is on. The HOST owns it; the switch never flips itself. */
407
- checked: boolean;
408
- /** Asked to flip. `next` is the value the merchant wants, not the current one. */
409
- onChange: (next: boolean) => void;
410
- disabled?: boolean;
411
- id?: string;
412
- className?: string;
413
- }
414
- type SettingsSwitchProps = SettingsSwitchBaseProps & SettingsSwitchLabel;
415
- /**
416
- * A DRILL-IN ROW — a name, a one-line preview, and a chevron that says pressing
417
- * it takes the panel down a level (card 8819).
418
- *
419
- * The storefront editor's text editing becomes a drill-down: the section screen
420
- * lists a section's text parts as these rows, and pressing one scopes the
421
- * inspector to that one part's editor. It lives in the shell for the reason the
422
- * rest of this grammar does — the campaign designer draws the same list, and a
423
- * second hand-rolled copy is exactly how the two editors drift.
424
- *
425
- * CONTROLLED and hook-free like the other four: it holds no open/closed state of
426
- * its own. Pressing it calls `onOpen`, and the HOST decides what drilling down
427
- * means, because only the host owns the panel's navigation.
428
- */
429
- interface SettingsDrillProps {
430
- /** The part's name — the loud primary line ("Heading", "Slide name"). */
431
- name: string;
432
- /**
433
- * One line of what the part reads as, shown quiet under the name and clipped
434
- * with an ellipsis. Optional: a row can be a name alone, and an absent preview
435
- * draws no empty second line rather than a blank one.
436
- */
437
- preview?: ReactNode;
438
- /**
439
- * Mark the PREVIEW as belonging to a part with no place on the page — an
440
- * identifier the eye never draws (a `Slide name`), reachable only from this
441
- * list. It reads quiet and italic. The mark is on the preview, not the row:
442
- * the name still reads as a name.
443
- */
444
- offPage?: boolean;
445
- /**
446
- * A trailing glyph before the chevron — the "hidden on the page" eye is the
447
- * standing case. A ReactNode, not a boolean, so the host owns the icon and its
448
- * accessible name (a bare glyph says nothing to a screen reader on its own).
449
- */
450
- status?: ReactNode;
451
- /** Asked to open. The host owns what drilling down does. */
452
- onOpen: () => void;
453
- /** A DOM id for the row button, so a host can point a label at it. */
454
- id?: string;
455
- className?: string;
456
- }
457
- /**
458
- * A FULL-WIDTH ACTION BUTTON that sits under a group's rows and COMMITS.
459
- *
460
- * "Apply to all cards" is the first caller: the merchant has styled one card and
461
- * wants the other eleven to match. That is a WRITE, and a write is why this is
462
- * not `SettingsDrill` with the chevron taken off — see the docblock on
463
- * `SettingsAction` for the whole of that argument.
464
- *
465
- * Controlled and hook-free like every primitive here. The shell owns the look
466
- * and the notice (its `toast`); the host owns the words and the write.
467
- */
468
- interface SettingsActionBaseProps {
469
- /**
470
- * What the button says — "Apply to all cards". The package supplies NO
471
- * default, the way the shell's `CornersInput` invents no corner names: the
472
- * words belong to the section that knows what it is about to rewrite.
473
- */
474
- label: ReactNode;
475
- /**
476
- * Pressed. The write is the host's; this component performs none of it. An
477
- * async write returns its promise, and the toast waits for it to resolve.
478
- * A union of two function types, not `() => void | Promise<void>`: that
479
- * form would refuse a sync callback that returns a value (a `boolean` from a
480
- * dispatch), which `() => void` has always accepted.
481
- */
482
- onPress: (() => void) | (() => Promise<void>);
483
- /**
484
- * The press is not available. A REAL `<button disabled>`, dimmed and still
485
- * there — never hidden. A control that vanishes leaves a merchant looking for
486
- * something they remember being there, and hiding it also takes away the one
487
- * place the reason can be read. Say the reason in {@link
488
- * SettingsActionNote.note}.
489
- */
490
- disabled?: boolean;
491
- /**
492
- * What the shell's toast says once {@link onPress} has landed — "Applied to
493
- * 12 cards". The host's words, and the one notice of the press: the button
494
- * keeps {@link label} and its look (Lewis 2026-10-30, one notice style in the
495
- * whole editor). A plain string, because a toast draws text. Required: this
496
- * button rewrites things that are not on screen, so a press that reports
497
- * nothing would leave the merchant guessing.
498
- */
499
- doneLabel: string;
500
- /** A glyph before the words. `aria-hidden`: the label already names the press. */
501
- icon?: ReactNode;
502
- className?: string;
503
- }
504
- /**
505
- * The note, and the id it needs to reach anybody.
506
- *
507
- * `note` is where "every card already matches" or "this rewrites all 12 cards"
508
- * goes, and it is wired to the button by `aria-describedby` — which needs a DOM
509
- * id, and this package is hook-free so it cannot invent one (no `useId`). The
510
- * union makes the id a COMPILE error rather than a silent hole: pass a note and
511
- * you must pass an `id`.
512
- *
513
- * ⚠️ THIS IS THE REASON THE CONTROL IS HERE RATHER THAN IN THE SHOP.
514
- * `efficient-storefront`'s own `components/AddChildRow.tsx` explains its disabled
515
- * button with a `title`, and a `title` on a DISABLED button reaches no screen
516
- * reader and no keyboard — the tooltip needs a hover the control no longer
517
- * answers. That hole cannot be patched from the shop's side while the shop draws
518
- * its own button, so the button moves here and brings the wiring with it.
519
- *
520
- * `?: never` on the other arm is what makes the two mutually exclusive while
521
- * still letting the component destructure both names — the same shape
522
- * {@link SettingsSwitchLabel} uses.
523
- */
524
- type SettingsActionNote = {
525
- note: ReactNode;
526
- id: string;
527
- } | {
528
- note?: never;
529
- id?: string;
530
- };
531
- type SettingsActionProps = SettingsActionBaseProps & SettingsActionNote;
9
+ import '../wordFit-7XpmhcaW.js';
532
10
 
533
11
  /**
534
12
  * A segmented control: one row of chips, one of them pressed.
@@ -919,4 +397,4 @@ interface SettingsContentWidthProps {
919
397
  }
920
398
  declare function SettingsContentWidth({ mode, onMode, number, changed, detail, onReset, fullClosed, disabled, hint, labelId, words, resetWords, }: SettingsContentWidthProps): react.JSX.Element;
921
399
 
922
- export { BACKGROUND_WIDTHS, type BackgroundWidth, type BackgroundWidthWords, ColourRowWords, type ContentWidthWords, DEFAULT_BACKGROUND_WIDTH_WORDS, DEFAULT_CONTENT_WIDTH_WORDS, SettingsAction, type SettingsActionBaseProps, type SettingsActionNote, type SettingsActionProps, SettingsBackgroundWidth, type SettingsBackgroundWidthProps, SettingsColour, type SettingsColourProps, SettingsContentWidth, type SettingsContentWidthProps, SettingsDefaultReset, type SettingsDefaultResetProps, SettingsDrill, type SettingsDrillProps, SettingsField, type SettingsFieldProps, SettingsGroup, SettingsGroupHead, type SettingsGroupHeadProps, type SettingsGroupProps, SettingsGroupSub, type SettingsGroupSubProps, SettingsSegmented, type SettingsSegmentedFit, type SettingsSegmentedOption, type SettingsSegmentedProps, SettingsSwitch, type SettingsSwitchBaseProps, type SettingsSwitchLabel, type SettingsSwitchProps };
400
+ export { BACKGROUND_WIDTHS, type BackgroundWidth, type BackgroundWidthWords, ColourRowWords, type ContentWidthWords, DEFAULT_BACKGROUND_WIDTH_WORDS, DEFAULT_CONTENT_WIDTH_WORDS, SettingsAction, SettingsActionProps, SettingsBackgroundWidth, type SettingsBackgroundWidthProps, SettingsColour, type SettingsColourProps, SettingsContentWidth, type SettingsContentWidthProps, SettingsDefaultReset, SettingsDefaultResetProps, SettingsDrill, SettingsDrillProps, SettingsField, SettingsFieldProps, SettingsGroup, SettingsGroupHead, SettingsGroupHeadProps, SettingsGroupProps, SettingsGroupSub, SettingsGroupSubProps, SettingsSegmented, SettingsSegmentedProps, SettingsSwitch, SettingsSwitchProps };