efficient-web-builder 15.0.0 → 15.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,34 @@
1
1
  # Changelog
2
2
 
3
+ ## 15.1.0 - 2026-10-15
4
+
5
+ **The site-footer document beside every page, behind a switch that is off** (card [DESIGN] Edit The Header And Footer On The Page, plan 2026-10-15 PR 2; design approved by Lewis 2026-10-15: https://claude.ai/artifact/53ddJmXavpdDspYJrqVf6q). A minor: nothing a merchant sees changes. The screen turns it on in the next release (PR 3).
6
+
7
+ ### Added
8
+
9
+ - **`lib/pinned-chrome-doc`** (pure): `spliceChrome(page, chrome)` puts the site-footer document's pinned nodes into a page's working data, the header group first and the old `Footer` or the footer group last. `splitChrome(working, chrome)` takes them back out into `{ page, chrome }`. An untouched splice splits back byte for byte. The chrome keeps its root, its unknown nodes and keys, an old `Footer` behind a footer group, and its legacy zones. A second copy of a pinned node is dropped (the `siteFooterDocChrome` rules). `lockedWhileHeld(isLockedBlock, held)` locks the pinned nodes while somebody else holds the chrome document.
10
+ - **Site page adapter, `makeSitePageAdapter(…, { editsPinnedInPlace: true })`**: loads the site-footer document with the page and splices it in; on save writes the page, and the chrome only when it changed, with its own version and the tab's clientId; a refused chrome save (changed elsewhere, taken) is reported on the chrome only and never fails the page's save; publish publishes the chrome too when it has changes not live yet; the edit lock claims the chrome beside the page and never takes it over. The site-footer page itself and templates are never spliced.
11
+ - **`WebsiteCapabilities.editsPinnedInPlace`** (false unless the host asks, and only for a site page with the pinned chrome) and **`WebsiteDocumentAdapter.pinnedChrome`** (`state()` → `{ docId, lockHolder, conflict, saveFailed, unpublished }`, `subscribe`). New types: `WebsiteCapabilities`, `WebsiteDocumentAdapter`, `PinnedChromeState`, `PinnedChromeReport`, `SitePageAdapterOptions`.
12
+
13
+ ### Guards
14
+
15
+ - New `test-pinned-chrome-doc` (19 tests): round trips on no groups, an old Footer, a Box footer, a Box header + a Box footer, both + the old `Header` placeholder; the order; what the chrome keeps; a page edit and a footer edit each leave the other half as it was; new and deleted groups; duplicates; `lockedWhileHeld`.
16
+ - New `test-pinned-chrome-adapter` (14 tests, fake backend): off by default and today's adapter; a page-only save writes the page only; a footer edit writes both, the chrome on its own version; a stale chrome version is a conflict on the chrome only; a held chrome is reported, locks the pinned nodes and is never written, and the page stays editable; publish takes the chrome only when it is not live; the site-footer page and a template are never spliced.
17
+
18
+ ## 15.0.1 - 2026-10-15
19
+
20
+ **The Button's three kept fields draw the no-control mark** (card [BUMP] Storefront Editor Packages To Latest, director JJ; found by the storefront's move to 14.0.0). A patch: nothing a merchant sees changes.
21
+
22
+ ### Fixed
23
+
24
+ - `href`, `iconPosition` and `arrow` on the Button block (#92) are declared so a save keeps them, and each says "no control here" on its field object (`noControlField(…, "always")`). But their `render` drew an empty fragment, not `<NoControlMark />`. So the drawn panel had no `data-sf-no-control` mark for them, and the storefront's `test-panel-no-empty-rows` counted three empty rows and three settings with no way back. They now draw the mark, and each names the visible field that writes it (`reachedBy`): `href` → `label` (the Button box), `iconPosition` → `icon` (the before / after glyphs on the Icon row), `arrow` → `icon` (the Icon row, which shows and replaces the old Arrow switch).
25
+ - No stored key changes. The fields stay declared, so a save keeps their values.
26
+ - No other field renders empty: every other `render` that draws nothing is a heading or tab marker (`marker: true`), which the Inspector never draws as a row. Device scope notes (`deviceNote`) draw easel's `data-sf-device-hidden` mark, which collapses the same way.
27
+
28
+ ### Guards
29
+
30
+ - New `test-no-control-fields-draw-the-mark`: §1 no field of any general block renders an empty row (a control, or a collapse mark); §2 every field stamped `"always"` draws a collapse mark; §3 the Button's kept fields draw the mark, stay declared, and name a partner on the same panel that draws a control. All three failed on 15.0.0 (§1: `ButtonBlock.href`, `ButtonBlock.iconPosition`, `ButtonBlock.arrow`).
31
+
3
32
  ## 15.0.0 - 2026-10-15
4
33
 
5
34
  **Publish is the website's main action, and Preview opens the saved draft** (card [DESIGN] A Clearer Save In Both Editors, build plan W1; final design by Lewis, 2026-10-15: https://claude.ai/artifact/NVN9yTSG8rYRh8w5Mv8Tpp). The website editor and the email designer are one editor (Lewis, 2026-10-15): the bar, the save icon and the save menu are efficient-easel 8.0.0's, the same in both. This layer only hands data: no save word, icon or menu of its own. A major: the efficient-easel peer range narrows to `^8.0.0` and the efficient-shell one to `^7.38.0` (harness `12-cicd.md`: a narrowed peer range is a major).
@@ -1,7 +1,29 @@
1
+ import type { LockHolder, SaveConflict } from "efficient-easel/edit-lock";
1
2
  import { type OfferedSets } from "./editor-blocks.js";
2
3
  import type { EditorCapabilities, EditorDocument, EditorDocumentAdapter } from "./editor-document-contract.js";
3
4
  import type { EditorPageRow, WebsiteBackend } from "./web-backend.js";
4
5
  export type { EditorPageRow };
5
6
  export type { EditorCapabilities, EditorDocument, EditorDocumentAdapter };
6
- export declare function makeSitePageAdapter(siteId: string | undefined, backend?: WebsiteBackend, offered?: OfferedSets): EditorDocumentAdapter;
7
- export declare function makeTemplatePageAdapter(templateId: string, backend?: WebsiteBackend, offered?: OfferedSets): EditorDocumentAdapter;
7
+ export type WebsiteCapabilities = EditorCapabilities & {
8
+ editsPinnedInPlace: boolean;
9
+ };
10
+ export type PinnedChromeState = {
11
+ docId: string | null;
12
+ lockHolder: LockHolder | null;
13
+ conflict: SaveConflict | null;
14
+ saveFailed: boolean;
15
+ unpublished: boolean;
16
+ };
17
+ export interface PinnedChromeReport {
18
+ state(): PinnedChromeState;
19
+ subscribe(listener: () => void): () => void;
20
+ }
21
+ export type WebsiteDocumentAdapter = EditorDocumentAdapter & {
22
+ capabilities: WebsiteCapabilities;
23
+ pinnedChrome?: PinnedChromeReport;
24
+ };
25
+ export type SitePageAdapterOptions = {
26
+ editsPinnedInPlace?: boolean;
27
+ };
28
+ export declare function makeSitePageAdapter(siteId: string | undefined, backend?: WebsiteBackend, offered?: OfferedSets, options?: SitePageAdapterOptions): WebsiteDocumentAdapter;
29
+ export declare function makeTemplatePageAdapter(templateId: string, backend?: WebsiteBackend, offered?: OfferedSets): WebsiteDocumentAdapter;
@@ -1 +1 @@
1
- "use client";import{offeredSetsOf as m}from"./editor-blocks.js";import{isEfficientBackend as c}from"./editor-session.js";import{noteLoaded as p,notePublished as d,noteSaved as v}from"./page-live-state.js";import{backendOf as u}from"./website-session.js";function h(i,t){const r=t?{siteId:t}:void 0;return{resolve:e=>i.load({slug:e,...r}).then(n=>(n&&p(n.id,n.live),n)),saveLayout:(e,n,o,s)=>i.save(e,{layout:n},{version:o,clientId:s??""}).then(a=>(v(e,a.live),a)),rereadVersion:i.version?e=>i.version(e):void 0,publish:i.publish?(e,n)=>i.publish(e,n?{version:n}:void 0).then(o=>(d(e,o.live),o)):void 0,listPages:()=>i.list(r),createPage:i.create?e=>i.create({...e,...r}):void 0,deletePage:i.remove?(e,n)=>i.remove(e,n):void 0}}function E(i,t=u(),r=m()){const e=c(t),n=i?{siteId:i}:void 0,o=e?!!i:!0,s=t.site.update;return{capabilities:{canPublish:!!t.pages.publish,hasSiteSettings:e,hasSiteTheme:!!t.theme,placeholderFrames:!1,mediaEditing:!0,pageCreationInAdminForm:!t.pages.create,canDeletePagesInline:!!t.pages.remove,hasEditLock:!!t.lock,hasEditGuard:!0,hasPinnedChrome:e&&r.commerce&&r.service,explainsSiteSettings:!1,hasSiteRecord:o,canEditSiteRecord:o&&!!s,hasPreviewLink:e||!!t.preview,hasCatalog:e&&r.commerce,hasForms:e&&r.service,hasBlog:!!t.blog&&r.service,offersCommerce:r.commerce,offersService:r.service},...h(t.pages,i),lock:t.lock,site:o?()=>t.site.get(n):void 0,updateSite:o&&s?a=>s(a,n):void 0,previewHref:t.preview?a=>t.preview.href(a):void 0}}function B(i,t=u(),r=m()){const e=c(t),n=t.templates,o=n?n.pages(i):{list:()=>Promise.reject(new Error(l)),load:()=>Promise.reject(new Error(l)),save:()=>Promise.reject(new Error(l))};return{capabilities:{canPublish:!1,hasSiteSettings:!1,hasSiteTheme:!1,placeholderFrames:!0,mediaEditing:!1,pageCreationInAdminForm:!o.create,canDeletePagesInline:!!o.remove,hasEditLock:!1,hasEditGuard:!1,hasPinnedChrome:e&&r.commerce&&r.service,explainsSiteSettings:e,hasSiteRecord:!1,canEditSiteRecord:!1,hasPreviewLink:!1,hasCatalog:e&&r.commerce,hasForms:e&&r.service,hasBlog:!!t.blog&&r.service,offersCommerce:r.commerce,offersService:r.service},...h(o,void 0),publish:void 0,templateName:n?()=>n.get(i).then(s=>s.name):void 0}}const l="This editor's backend has no website templates.";export{E as makeSitePageAdapter,B as makeTemplatePageAdapter};
1
+ "use client";import{SITE_FOOTER_SLUG as C}from"./content/footer.js";import{offeredSetsOf as F}from"./editor-blocks.js";import{isBackendError as y}from"./editor-backend-error.js";import{isEfficientBackend as H}from"./editor-session.js";import{noteLoaded as L,notePublished as A,noteSaved as O,pageLiveState as J}from"./page-live-state.js";import{spliceChrome as j,splitChrome as T}from"./pinned-chrome-doc.js";import{backendOf as k}from"./website-session.js";function b(t,i){const o=i?{siteId:i}:void 0;return{resolve:s=>t.load({slug:s,...o}).then(e=>(e&&L(e.id,e.live),e)),saveLayout:(s,e,c,m)=>t.save(s,{layout:e},{version:c,clientId:m??""}).then(u=>(O(s,u.live),u)),rereadVersion:t.version?s=>t.version(s):void 0,publish:t.publish?(s,e)=>t.publish(s,e?{version:e}:void 0).then(c=>(A(s,c.live),c)):void 0,listPages:()=>t.list(o),createPage:t.create?s=>t.create({...s,...o}):void 0,deletePage:t.remove?(s,e)=>t.remove(s,e):void 0}}function N(t,i,o,s){let e=null;const c=new Set;let m=!1,u={docId:null,lockHolder:null,conflict:null,saveFailed:!1,unpublished:!1};const S=new Set,P=()=>{if(!e)return!1;const n=J(e.id);return n==="changed"||n==="never"||n===void 0&&m},f=n=>{u={...u,...n,docId:e?.id??null,unpublished:P()};for(const r of S)r()},w=n=>n&&typeof n=="object"&&!Array.isArray(n)?n:{},h=n=>!!e&&c.has(n);return{resolve:async n=>{if(n===C)return o.resolve(n);const[r,l]=await Promise.all([o.resolve(n),t.load({slug:C,...i})]);if(l){L(l.id,l.live);const a=w(l.layout);e={id:l.id,layout:a,version:l.version,savedJson:""}}else e=null;if(m=!1,!r||!e)return f({conflict:null,saveFailed:!1}),r;const d=j(w(r.layout),e.layout);return e.savedJson=JSON.stringify(T(d,e.layout).chrome),c.add(r.id),f({conflict:null,saveFailed:!1}),{...r,layout:d}},saveLayout:async(n,r,l,d)=>{if(!e||!h(n))return o.saveLayout(n,r,l,d);const a=e,v=T(r,a.layout),B=await o.saveLayout(n,v.page,l,d),E=JSON.stringify(v.chrome);if(!v.chrome||E===a.savedJson||u.lockHolder)return B;try{const p=await t.save(a.id,{layout:v.chrome},{version:a.version,clientId:d??""});O(a.id,p.live),a.layout=v.chrome,a.version=p.version,a.savedJson=E,m=!0,f({conflict:null,saveFailed:!1})}catch(p){y(p,"changed-elsewhere")?f({conflict:p.conflict??{savedBy:"",savedByYou:!1,secondsAgo:0}}):y(p,"lock-taken")?f({lockHolder:p.holder??{name:"",secondsSinceSeen:0,secondsSinceHeld:0}}):f({saveFailed:!0})}return B},publish:o.publish?async(n,r)=>{const l=await o.publish(n,r),d=e;if(d&&h(n)&&t.publish&&P()&&!u.lockHolder){const a=await t.publish(d.id,d.version?{version:d.version}:void 0);A(d.id,a.live),a.version!==null&&(d.version=a.version),m=!1,f({})}return l}:void 0,lock:s?{claim:async(n,r,l)=>{const d=await s.claim(n,r,l),a=e;if(a&&h(n))try{await s.claim(a.id,r,!1),u.lockHolder&&f({lockHolder:null})}catch(v){y(v,"lock-taken")&&f({lockHolder:v.holder??{name:"",secondsSinceSeen:0,secondsSinceHeld:0}})}return d},release:async(n,r)=>{await s.release(n,r);const l=e;l&&h(n)&&!u.lockHolder&&await s.release(l.id,r).catch(()=>{})}}:void 0,pinnedChrome:{state:()=>u,subscribe:n=>(S.add(n),()=>{S.delete(n)})}}}function U(t,i=k(),o=F(),s={}){const e=H(i),c=t?{siteId:t}:void 0,m=e?!!t:!0,u=i.site.update,S=e&&o.commerce&&o.service,P=!!s.editsPinnedInPlace&&S,f=b(i.pages,t),w=P?N(i.pages,c,f,i.lock):null;return{capabilities:{canPublish:!!i.pages.publish,hasSiteSettings:e,hasSiteTheme:!!i.theme,placeholderFrames:!1,mediaEditing:!0,pageCreationInAdminForm:!i.pages.create,canDeletePagesInline:!!i.pages.remove,hasEditLock:!!i.lock,hasEditGuard:!0,hasPinnedChrome:S,editsPinnedInPlace:P,explainsSiteSettings:!1,hasSiteRecord:m,canEditSiteRecord:m&&!!u,hasPreviewLink:e||!!i.preview,hasCatalog:e&&o.commerce,hasForms:e&&o.service,hasBlog:!!i.blog&&o.service,offersCommerce:o.commerce,offersService:o.service},...f,lock:i.lock,...w,site:m?()=>i.site.get(c):void 0,updateSite:m&&u?h=>u(h,c):void 0,previewHref:i.preview?h=>i.preview.href(h):void 0}}function V(t,i=k(),o=F()){const s=H(i),e=i.templates,c=e?e.pages(t):{list:()=>Promise.reject(new Error(g)),load:()=>Promise.reject(new Error(g)),save:()=>Promise.reject(new Error(g))};return{capabilities:{canPublish:!1,hasSiteSettings:!1,hasSiteTheme:!1,placeholderFrames:!0,mediaEditing:!1,pageCreationInAdminForm:!c.create,canDeletePagesInline:!!c.remove,hasEditLock:!1,hasEditGuard:!1,hasPinnedChrome:s&&o.commerce&&o.service,editsPinnedInPlace:!1,explainsSiteSettings:s,hasSiteRecord:!1,canEditSiteRecord:!1,hasPreviewLink:!1,hasCatalog:s&&o.commerce,hasForms:s&&o.service,hasBlog:!!i.blog&&o.service,offersCommerce:o.commerce,offersService:o.service},...b(c,void 0),publish:void 0,templateName:e?()=>e.get(t).then(m=>m.name):void 0}}const g="This editor's backend has no website templates.";export{U as makeSitePageAdapter,V as makeTemplatePageAdapter};
@@ -0,0 +1,8 @@
1
+ export type ChromeLayout = Record<string, unknown>;
2
+ export declare const PINNED_CHROME_TYPES: readonly string[];
3
+ export declare function spliceChrome(page: ChromeLayout, chrome: ChromeLayout | null): ChromeLayout;
4
+ export declare function splitChrome(working: ChromeLayout, chrome: ChromeLayout | null): {
5
+ page: ChromeLayout;
6
+ chrome: ChromeLayout | null;
7
+ };
8
+ export declare function lockedWhileHeld(isLocked: (type: string | undefined) => boolean, held: () => boolean): (type: string | undefined) => boolean;
@@ -0,0 +1 @@
1
+ import{isSectionGroupType as F,SITE_FOOTER_TYPE as E,SITE_HEADER_TYPE as O,siteFooterDocChrome as z}from"./puck/section-groups.js";const A="Footer",N=[O,A,E],y=e=>{const t=e?.content;return Array.isArray(t)?t:[]},l=e=>{const t=e?.zones;return t&&typeof t=="object"&&!Array.isArray(t)?t:null},h=e=>{const t=e?.props?.id;return typeof t=="string"?t:void 0},I=e=>e.slice(0,Math.max(0,e.lastIndexOf(":")));function x(e){const t=z(y(e)),o=t.some(r=>r?.type===E);return t.filter(r=>r?.type===O||r?.type===E||r?.type===A&&!o)}function k(e,t){const o=new Set,r=s=>{if(Array.isArray(s)){for(const f of s)r(f);return}if(!s||typeof s!="object")return;const i=s;if(typeof i.type=="string"&&i.props&&typeof i.props=="object"){const f=h(i);f&&o.add(f)}for(const f of Object.values(s))r(f)};if(r(e),t){let s=!0;const i=new Set;for(;s;){s=!1;for(const[f,p]of Object.entries(t))i.has(f)||!o.has(I(f))||(i.add(f),r(p),s=!0)}}return o}function T(e,t){const o={},r={};for(const[s,i]of Object.entries(e??{}))(t.has(I(s))?o:r)[s]=i;return{owned:o,rest:r}}function M(e,t){if(!t)return e;const o=x(t);if(!o.length)return e;const r=o.filter(p=>p.type===O),s=o.filter(p=>p.type!==O),i={...e,content:[...r,...y(e),...s]},{owned:f}=T(l(t),k(o,l(t)));return Object.keys(f).length&&(i.zones={...l(e)??{},...f}),i}function R(e,t){if(!t)return{page:e,chrome:null};const o=x(t),r=new Set(o.map(h).filter(n=>!!n)),s=[],i=[];for(const n of y(e)){const c=h(n);c&&r.has(c)||F(n?.type)?i.push(n):s.push(n)}const f=new Map(i.map(n=>[h(n),n])),p=new Set;let d=z(y(t)).flatMap(n=>{const c=h(n);if(!c||!r.has(c))return[n];const u=f.get(c);return u?(p.add(u),[u]):[]});for(const n of i)p.has(n)||d.some(c=>c?.type===n.type)||(d=n.type===O?[n,...d]:[...d,n]);d=z(d);const P=d.length===y(t).length&&d.every((n,c)=>n===y(t)[c]),j=l(e),C=k(d,j),{owned:b,rest:S}=T(j,C),_=T(l(t),k(o,l(t))).owned,g={...t,content:P?t.content:[...d]},a=l(t);if(a||Object.keys(b).length){const n={};for(const[c,u]of Object.entries(a??{}))c in _?c in b&&(n[c]=b[c]):n[c]=u;for(const[c,u]of Object.entries(b))c in n||(n[c]=u);g.zones=n,a&&m(g.zones,a)&&(g.zones=a)}const w={...e,content:s};return j&&(Object.keys(S).length||!Object.keys(_).length?w.zones=S:delete w.zones),{page:w,chrome:g}}function m(e,t){const o=Object.keys(e),r=Object.keys(t);return o.length===r.length&&o.every((s,i)=>s===r[i]&&e[s]===t[s])}function Y(e,t){return o=>e(o)||o!==void 0&&N.includes(o)&&t()}export{N as PINNED_CHROME_TYPES,Y as lockedWhileHeld,M as spliceChrome,R as splitChrome};
@@ -1 +1 @@
1
- import{Fragment as u,jsx as o,jsxs as d}from"react/jsx-runtime";import{ButtonIcon as h,ButtonIconIcon as g,IconAfterTextIcon as I,IconBeforeTextIcon as y,StylesIcon as x,WidthIcon as b}from"efficient-easel/icons";import*as C from"efficient-easel/icons";import{SettingsField as S}from"efficient-easel/panel";import{useSelectedFields as c}from"efficient-easel/easel";import{IconPicker as F,Segmented as B}from"efficient-shell/ui";import{StorefrontTextLinkField as v}from"../../../../../components/StorefrontLinkField.js";import{BUTTON_ICONS as _,buttonIconChoice as k,buttonIconSide as T}from"../../../../content/button-icon.js";import{say as i}from"../../../../editor-words.js";import{group as z,inOwnBox as O,linkField as N,plainText as w,section as m,segmented as f}from"../../../controls.js";import{noControlField as j}from"../../../panel-no-control-mark.js";import{alignField as L}from"../../shared-fields.js";const s=()=>j({type:"custom",render:()=>o(u,{})},"always");function P(e){const n=(e==="none"?"no-symbol":e).replace(/(^|-)([a-z])/g,(r,a,p)=>p.toUpperCase()),t=C[`${n}Icon`];return t?o(t,{size:16}):null}const A=_.map(e=>({value:e,icon:P(e)}));function U({value:e,onChange:l}){const n=c(),t=n.values.href,r=typeof e=="string"?e:e&&typeof e=="object"&&"text"in e?String(e.text??""):"";return o(v,{label:"Button",icon:o(h,{size:18}),text:r,onTextChange:l,link:typeof t=="string"?t:"",onLinkChange:a=>n.onChange("href",a)})}function W({value:e,onChange:l}){const n=c(),t=k(e,n.values.arrow),r=T(n.values.iconPosition);return o(S,{label:i("Icon"),icon:o(g,{size:18}),children:d("div",{className:"flex items-center gap-2",children:[o(F,{label:i("Icon"),value:t,emptyValue:"none",options:A,onChange:l}),t!=="none"&&o(B,{label:i("Icon position"),value:r,onChange:a=>n.onChange("iconPosition",a),options:[{value:"before",icon:o(y,{size:16}),label:o("span",{className:"sr-only",children:i("Icon before text")}),title:i("Icon before text")},{value:"after",icon:o(I,{size:16}),label:o("span",{className:"sr-only",children:i("Icon after text")}),title:i("Icon after text")}]})]})})}const Q={fields:{label:O(w("Button"),e=>o(U,{value:e.value,onChange:e.onChange})),href:{...N("Button link"),...s()},_dSize:m("Size"),widthFit:f("Width",o(b,{}),[{label:"Fit content",value:"fit"},{label:"Fill",value:"fill"}],"fit"),_gStyle:z("Style"),variant:f("Style",o(x,{}),[{label:"Filled",value:"primary"},{label:"Outline",value:"ghost"}],"primary"),icon:{type:"custom",render:e=>o(W,{value:e.value,onChange:e.onChange})},iconPosition:s(),arrow:s(),_dLayout:m("Layout"),align:L()}};export{Q as ButtonBlockFields};
1
+ import{jsx as e,jsxs as u}from"react/jsx-runtime";import{ButtonIcon as d,ButtonIconIcon as h,IconAfterTextIcon as g,IconBeforeTextIcon as I,StylesIcon as y,WidthIcon as x}from"efficient-easel/icons";import*as b from"efficient-easel/icons";import{SettingsField as C}from"efficient-easel/panel";import{useSelectedFields as c}from"efficient-easel/easel";import{IconPicker as S,Segmented as B}from"efficient-shell/ui";import{StorefrontTextLinkField as v}from"../../../../../components/StorefrontLinkField.js";import{BUTTON_ICONS as F,buttonIconChoice as k,buttonIconSide as _}from"../../../../content/button-icon.js";import{say as i}from"../../../../editor-words.js";import{group as T,inOwnBox as z,linkField as N,plainText as O,section as m,segmented as f}from"../../../controls.js";import{NoControlMark as w,noControlField as j}from"../../../panel-no-control-mark.js";import{alignField as L}from"../../shared-fields.js";const s=o=>({...j({type:"custom",render:()=>e(w,{})},"always"),reachedBy:o});function P(o){const n=(o==="none"?"no-symbol":o).replace(/(^|-)([a-z])/g,(r,a,p)=>p.toUpperCase()),t=b[`${n}Icon`];return t?e(t,{size:16}):null}const A=F.map(o=>({value:o,icon:P(o)}));function U({value:o,onChange:l}){const n=c(),t=n.values.href,r=typeof o=="string"?o:o&&typeof o=="object"&&"text"in o?String(o.text??""):"";return e(v,{label:"Button",icon:e(d,{size:18}),text:r,onTextChange:l,link:typeof t=="string"?t:"",onLinkChange:a=>n.onChange("href",a)})}function W({value:o,onChange:l}){const n=c(),t=k(o,n.values.arrow),r=_(n.values.iconPosition);return e(C,{label:i("Icon"),icon:e(h,{size:18}),children:u("div",{className:"flex items-center gap-2",children:[e(S,{label:i("Icon"),value:t,emptyValue:"none",options:A,onChange:l}),t!=="none"&&e(B,{label:i("Icon position"),value:r,onChange:a=>n.onChange("iconPosition",a),options:[{value:"before",icon:e(I,{size:16}),label:e("span",{className:"sr-only",children:i("Icon before text")}),title:i("Icon before text")},{value:"after",icon:e(g,{size:16}),label:e("span",{className:"sr-only",children:i("Icon after text")}),title:i("Icon after text")}]})]})})}const Q={fields:{label:z(O("Button"),o=>e(U,{value:o.value,onChange:o.onChange})),href:{...N("Button link"),...s("label")},_dSize:m("Size"),widthFit:f("Width",e(x,{}),[{label:"Fit content",value:"fit"},{label:"Fill",value:"fill"}],"fit"),_gStyle:T("Style"),variant:f("Style",e(y,{}),[{label:"Filled",value:"primary"},{label:"Outline",value:"ghost"}],"primary"),icon:{type:"custom",render:o=>e(W,{value:o.value,onChange:o.onChange})},iconPosition:s("icon"),arrow:s("icon"),_dLayout:m("Layout"),align:L()}};export{Q as ButtonBlockFields};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "efficient-web-builder",
3
- "version": "15.0.0",
3
+ "version": "15.1.0",
4
4
  "description": "The EFFICIENT website builder: the website editor, the general sections and the renderer that draws them, compiled. Hostable by any EFFICIENT product; nothing here imports a host.",
5
5
  "license": "UNLICENSED",
6
6
  "author": "Efficient Pty Ltd",