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.
- package/dist/chunk-4O4FSRJL.js +1 -0
- package/dist/chunk-SS2NY7PP.js +4 -0
- package/dist/chunk-UJU42JM4.js +14 -0
- package/dist/page-copy/index.js +5 -5
- package/dist/panel/index.d.ts +5 -527
- package/dist/screen/lib/document/block-set-host.js +1 -1
- package/dist/screen/lib/document/block-size-fields.d.ts +4 -0
- package/dist/screen/lib/document/block-size-fields.js +1 -1
- package/dist/screen/lib/document/box-fields.d.ts +138 -10
- package/dist/screen/lib/document/box-fields.js +1 -1
- package/dist/screen/lib/document/button-fields.d.ts +4 -0
- package/dist/screen/lib/document/button-fields.js +1 -1
- package/dist/screen/lib/document/image-fields.d.ts +4 -0
- package/dist/screen/lib/document/style-fields.d.ts +4 -0
- package/dist/screen/lib/document/style-fields.js +1 -1
- package/dist/strings/index.js +1 -1
- package/dist/types-BusXrvDU.d.ts +529 -0
- package/package.json +1 -1
- package/dist/chunk-6F6OAL3C.js +0 -13
- package/dist/chunk-C4HXU4HJ.js +0 -1
- package/dist/chunk-RKLDSLU6.js +0 -4
|
@@ -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};
|
package/dist/page-copy/index.js
CHANGED
|
@@ -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
|
|
2
|
-
return
|
|
3
|
-
/* @__PURE__ */c(l,{label:a.titleLabel,htmlFor:
|
|
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:
|
|
5
|
-
return c(a,{open:!0,dirty:r.touched,onClose:()=>{r.busy||o()},title:l.title({name:
|
|
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};
|
package/dist/panel/index.d.ts
CHANGED
|
@@ -1,534 +1,12 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
|
-
import {
|
|
4
|
-
|
|
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,
|
|
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 };
|