efficient-web-builder 4.0.0 → 5.0.1
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 +26 -0
- package/README.md +35 -19
- package/dist/components/PartSettingsPanel.js +1 -1
- package/package.json +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,31 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 5.0.1 - 2026-10-11
|
|
4
|
+
|
|
5
|
+
- **A custom face's Style box is read as it is seen.** For a text part whose
|
|
6
|
+
face is outside the site styles, the closed Style box shows "Custom" while
|
|
7
|
+
its list's first row is "No style". A screen reader read the row's label, so
|
|
8
|
+
the box was seen as "Custom" and heard as "No style". It is "Custom" for
|
|
9
|
+
both now (`PartStyleSelect` hands `FieldSelect` a `valueText`). Nothing
|
|
10
|
+
changes on screen.
|
|
11
|
+
- Takes effect with efficient-easel 2.1.0 and efficient-shell 4.180.0. The
|
|
12
|
+
peer ranges are unchanged (`^2.0.0`, `^4.171.0`): with an older easel or
|
|
13
|
+
shell the option's label is still what is read.
|
|
14
|
+
|
|
15
|
+
## 5.0.0 - 2026-10-11
|
|
16
|
+
|
|
17
|
+
- **Breaking: efficient-easel ^2.0.0 and efficient-shell ^4.171.0** (peers;
|
|
18
|
+
were ^1.0.0 and ^4.165.0; Efficient-Pty-Ltd/efficient-easel#211, harness
|
|
19
|
+
UI-24 and UI-7). Easel 2.0.0 takes its glyphs from the shell's house set
|
|
20
|
+
(`efficient-shell/icons/efficient`) and its row and field corners from the
|
|
21
|
+
shell's role radius, and needs shell 4.171.0 for both. A host moves to this
|
|
22
|
+
release, easel 2.0.0 and a shell at or above 4.171.0 together.
|
|
23
|
+
- **Nothing in this package's own API changes.** The editor draws the same
|
|
24
|
+
glyphs (the set's data moved to the shell byte for byte) and the same
|
|
25
|
+
corners at the default theme; the theme's Corners setting now reaches the
|
|
26
|
+
editor's rows and fields through the role tokens. No guard had to change:
|
|
27
|
+
the 140 guards pass against easel 2.0.0 as they stood.
|
|
28
|
+
|
|
3
29
|
## 4.0.0 - 2026-10-11
|
|
4
30
|
|
|
5
31
|
- **Breaking: the site's corner tokens are `--site-radius-*`.**
|
package/README.md
CHANGED
|
@@ -45,27 +45,43 @@ Comments that name a file say `lib/puck/controls`; read them from `src/`.
|
|
|
45
45
|
|
|
46
46
|
## How the storefront uses it
|
|
47
47
|
|
|
48
|
-
The storefront
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
own tree first
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
-
|
|
58
|
-
|
|
48
|
+
The storefront installs this repository's npm package, like any host, at one
|
|
49
|
+
exact version in its `package.json` (efficient-storefront `doc/adr/0011`,
|
|
50
|
+
2026-10-11). Its commerce and service blocks are written with the builder's
|
|
51
|
+
control builders and passes, which they import by path: there `@/…` resolves in
|
|
52
|
+
the storefront's own tree first, then in its blocks, then in
|
|
53
|
+
`node_modules/efficient-web-builder/dist`.
|
|
54
|
+
|
|
55
|
+
- **A change to the builder is made HERE and reaches the storefront as a
|
|
56
|
+
release.** Merge it, cut a GitHub Release `vX.Y.Z` (which publishes to npm),
|
|
57
|
+
move efficient-storefront-blocks to that release, then open one storefront
|
|
58
|
+
pull request that moves the version in its `package.json` and its blocks pin
|
|
59
|
+
together. The storefront's check fails while it and the blocks' repository
|
|
60
|
+
install two different builders.
|
|
61
|
+
- **To try a branch against the storefront's suite before a release**, install
|
|
62
|
+
its build there as a tarball:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
pnpm run build && pnpm pack --pack-destination /tmp/builder-trial
|
|
66
|
+
# in an efficient-storefront worktree
|
|
67
|
+
pnpm add "efficient-web-builder@file:/tmp/builder-trial/efficient-web-builder-<version>.tgz"
|
|
68
|
+
just check
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Exactly one storefront guard fails on a trial, the one that will not let an
|
|
72
|
+
unreleased builder merge.
|
|
73
|
+
- **What a host receives is compiled.** A function's own name is a letter in
|
|
74
|
+
`dist/`, so a host's test cannot find a component by that name: it imports
|
|
75
|
+
the component and compares, or asks what it draws. Export what a host has to
|
|
76
|
+
be able to find.
|
|
59
77
|
|
|
60
78
|
So this repository's CI proves the builder stands alone, and the storefront's
|
|
61
|
-
proves it still does what it did
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
here, the storefront installs the package like any host and deletes
|
|
68
|
-
`packages/web-builder`.
|
|
79
|
+
proves it still does what it did, on the package. The guards that run the
|
|
80
|
+
editor assembled with a host stay in the host's repository.
|
|
81
|
+
|
|
82
|
+
From 2026-10-07 to 2026-10-11 the storefront kept a vendored copy of `src/`
|
|
83
|
+
instead, pinned to a commit here ([`doc/adr/0001`](doc/adr/0001-extracted-from-the-storefront.md),
|
|
84
|
+
[`doc/adr/0002`](doc/adr/0002-published-from-here-compiled.md)).
|
|
69
85
|
|
|
70
86
|
## Published as `efficient-web-builder`
|
|
71
87
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use client";import{jsx as t,jsxs as r,Fragment as O}from"react/jsx-runtime";import{useEffect as M,useId as _e,useRef as J,useState as Q}from"react";import{PartResetDot as fe}from"./PartResetDot.js";import{FieldSelect as pe,InspectorField as $e}from"efficient-easel/fields";import{SettingsField as me,SettingsGroup as Ae}from"efficient-easel/panel";import{InspectorUpLevel as G}from"efficient-easel/chrome";import{StylesIcon as Le,TextColorIcon as ze,TypeIcon as he}from"efficient-easel/icons";import{useEasel as Be}from"efficient-easel/easel";import{Tooltip as De}from"efficient-shell/ui";import{PartInkControl as Ve}from"./PartInkControl.js";import{PartTextBox as Me}from"./PartTextBox.js";import ye from"./UiIcon.js";import{usePreviewHeader as Ge}from"./PreviewHeaderContext.js";import{usePartStyle as We}from"./PreviewPartStyleContext.js";import{readHidable as W,textFieldOffPage as je}from"../lib/puck/controls.js";import{TEXT_FIELD_MARKER as He}from"../lib/puck/field-markers.js";import{useSectionOps as Ue}from"../lib/puck/use-section-ops.js";import{FONT_OPTIONS as Ke,FONT_ROLES as Z,FONT_ROLE_LABELS as ee,fontOption as _}from"../lib/fonts.js";import{isMultilineKind as Xe,partPlaceholder as Ye,typeInPlaceKind as qe}from"../lib/puck/part-text.js";import{partRowField as Je}from"../lib/puck/part-row-field.js";import{decodePartKey as Qe,partPropsWithRowValue as Ze,partRowValue as et}from"../lib/puck/part-rows.js";import{PART_WIDTH_MAX as tt,propsWithPartStyleValue as te,propsWithoutPartStyle as nt,readPartStyle as ge}from"../lib/puck/part-styles.js";import{partInks as ot}from"../lib/puck/part-inks.js";import{styleKeyForPart as st,TEXT_KIND_LABELS as lt}from"../lib/sections/section-text-kinds.js";import{commitPartText as rt}from"../lib/puck/part-typing.js";import{useEditorHost as it}from"../lib/editor-host.js";import{SOURCE_LINES as at,SOURCE_ACTIONS as ct,SOURCE_ACTION_LABEL as dt,partSourceOf as ut,sourceSlugOf as be}from"../lib/puck/source-bound-parts.js";import{encodePartId as ft,holdsAuthoredText as pt}from"../lib/puck/text-parts.js";import{regionsForSection as mt}from"../lib/sections/section-text-regions.js";import{textRegionOfPartKey as ht}from"../lib/sections/text-regions.js";const yt={400:"Regular",500:"Medium",600:"Semibold",700:"Bold"},Se=[{key:"font",label:"Font",group:"Style"},{key:"color",label:"Color",group:"Style"}],gt=[...new Set(Se.map(e=>e.group))],bt={font:t(he,{}),color:t(ze,{})};function $(e){return e.kind==="role"?ee[e.role]:e.kind==="custom"?"Custom":null}const St=e=>e.kind==="role"?`role:${e.role}`:e.kind;function xt(e){const n=e.role;return n&&Z.includes(n)?{kind:"role",role:n}:null}function Nt(e,n){if(!e)return{kind:"unread"};const{faceRole:s,base:l}=e;if(n[s]===l.fontKey)return{kind:"role",role:s};const a=Object.keys(n).filter(c=>n[c]===l.fontKey);return a.length===1?{kind:"role",role:a[0]}:{kind:"custom"}}function ne(e,n,s,l){return xt(ge(e,n))??Nt(s,l)}function un(e,n,s,l){return $(ne(e,n,s,l))}function oe(e,n){if(!e.length)return null;const s=n(e[0]);return e.every(l=>n(l)===s)?s:null}function vt(e){return Object.values(e).filter(n=>n!==void 0).length}function Pt(e,n){const s=e.style[n];return n==="color"?s??"":s!==void 0?String(s):e.base.fontKey}function kt({face:e,base:n,onPick:s}){const[l,a]=Q(!1),c=J(null),h=J(null),y=_e();M(()=>{if(!l)return;const i=T=>{c.current&&!c.current.contains(T.target)&&a(!1)};return document.addEventListener("mousedown",i),()=>document.removeEventListener("mousedown",i)},[l]);const u=e.kind==="role"?e.role:null,v=i=>{s(i),a(!1),h.current?.focus()};return r("div",{className:"sf-part-style-pick",ref:c,children:[r("button",{ref:h,type:"button",className:"sf-part-style-trigger","aria-expanded":l,"aria-controls":y,"aria-label":`Follows ${$(e)??"no style"} — change`,onClick:()=>a(i=>!i),onKeyDown:i=>{i.key==="Escape"&&l&&(i.preventDefault(),a(!1))},children:[t("span",{className:"sf-part-style-name",style:{fontFamily:n?.fontKey?`var(${_(n.fontKey)?.cssVar})`:void 0,fontWeight:n?.weight},children:$(e)}),t("span",{className:"sf-part-style-sign","aria-hidden":"true"})]}),l&&r("div",{className:"sf-multi-menu sf-part-style-menu",id:y,role:"listbox",onKeyDown:i=>{i.key==="Escape"&&(i.preventDefault(),a(!1),h.current?.focus())},children:[r("button",{type:"button",role:"option","aria-selected":u===null,className:`sf-part-style-option${u===null?" is-on":""}`,onClick:()=>v(null),children:[t("span",{className:"sf-part-style-option-name",children:"No style"}),t("span",{className:"sf-note",children:"Section’s own"})]}),Z.map(i=>t("button",{type:"button",role:"option","aria-selected":u===i,className:`sf-part-style-option${u===i?" is-on":""}`,onClick:()=>v(i),children:t("span",{className:"sf-part-style-option-name",style:{fontFamily:`var(--font-role-${i})`},children:ee[i]})},i))]})]})}const j="";function wt({face:e,base:n,onPick:s}){const l=e.kind==="role"?e.role:j,a=[{label:"No style",value:j},...Z.map(c=>({label:ee[c],value:c}))];return t(pe,{ariaLabel:"Style",value:l,options:a,inline:!0,onChange:c=>s(c===j?null:c),renderOption:c=>c.value===j?r(O,{children:[t("span",{className:"es-fsel-label sf-part-style-keep",children:c.label}),t("span",{className:"sf-part-style-aside","aria-hidden":"true",children:"Section’s own"})]}):t("span",{className:"es-fsel-label",style:{fontFamily:`var(--font-role-${c.value})`},children:c.label}),renderValue:()=>t("span",{style:{fontFamily:n?.fontKey?`var(${_(n.fontKey)?.cssVar})`:void 0,fontWeight:n?.weight},children:$(e)})})}function Tt({parts:e,onEditStyle:n,onClearAll:s,onFollowStyle:l,kindLabel:a}){const h=oe(e,T=>St(T.face))?e[0].face:null,y=h?$(h):null,u=h&&h.kind!=="unread"?e[0].base:null,v=e.reduce((T,A)=>T+vt(A.style),0),i=u?_(u.fontKey)?.label:void 0;return r("div",{className:`sf-part-style${v?" has-overrides":""}`,children:[t("div",{className:"sf-part-style-caption",children:"Style"}),e.length>1&&r("div",{className:"sf-part-style-count",children:[e.length," selected"]}),e.length===1&&a&&r("div",{className:"sf-part-style-count",children:[a," · every row"]}),y&&u?r(O,{children:[l&&h?t(kt,{face:h,base:u,onPick:l}):t("div",{className:"sf-part-style-name",style:{fontFamily:i?`var(${_(u.fontKey)?.cssVar})`:void 0,fontWeight:u.weight},children:y}),t("div",{className:"sf-part-style-meta",children:[i,`${Math.round(u.sizePx)} px`,yt[u.weight]].filter(Boolean).join(" · ")})]}):t("div",{className:"sf-part-style-name",children:"Mixed styles"}),r("div",{className:"sf-part-style-top",children:[y?v?r(O,{children:[r("span",{className:"sf-part-chip",children:[v," changed"]}),t("button",{type:"button",className:"sf-part-link",onClick:s,children:"Clear"})]}):t("span",{className:"sf-part-style-state",children:"This text follows the site style."}):t("span",{className:"sf-part-style-state",children:"Changes apply to all selected."}),t("span",{className:"sf-part-style-gap"}),y&&n&&t("button",{type:"button",className:"sf-part-link",onClick:n,children:"Edit"})]})]})}function Et({sectionName:e,onBack:n,textEditor:s,styleRow:l,children:a}){return r("div",{className:"sf-part-panel",children:[t(G,{inline:!0,name:e,onSelect:n}),s,l,a]})}function xe({source:e,slug:n}){const s=it(),l=ct[e],a=!l.needsSlug||!!n;return t("div",{className:"sf-note sf-note--source",children:r("p",{className:"es-field-hint sf-hint-keep",children:[at[e],a?r(O,{children:[" ",t("button",{type:"button",className:"sf-source-open",onClick:()=>{l.type==="storefront-editor:open-menus"?s.send(l.type):n&&s.send(l.type,{slug:n})},children:dt})]}):null]})})}const Ne=4,Rt=120;function Ct({region:e,sizePx:n,face:s,base:l,onFollowStyle:a,source:c,slug:h,onBack:y}){const u=e.follows;return r("div",{className:"sf-note",children:[t("p",{children:t("strong",{children:e.label})}),r("p",{children:["Its size follows Site styles ▸ ",u,n?` — ${Math.round(n)}px right now`:"","."]}),a?t(O,{children:t(me,{label:"Style",icon:t(Le,{}),hint:r(O,{children:["Every ",e.label.toLowerCase()," in this section follows it. The face and the size change; the letter case is the section’s own and stays as it is."]}),children:t(wt,{face:s,base:l,onPick:a})})}):t("p",{className:"es-field-hint sf-hint-keep",children:"Only the size follows a site style. This section sets its own font, weight and letter case."}),c?t(xe,{source:c,slug:h}):null,t(G,{inline:!0,name:"Page",onSelect:y})]})}function Ft({onBack:e}){return r("div",{className:"sf-note",children:[t("p",{children:t("strong",{children:"This part is no longer here."})}),t("p",{children:"The section it belonged to has been changed or removed."}),t(G,{inline:!0,name:"Page",onSelect:e})]})}function Ot({onBack:e,partId:n,why:s}){return r("div",{className:"sf-note",children:[t("p",{children:t("strong",{children:"This part's settings did not open."})}),t("p",{children:"The words are still on your page and nothing has been lost. The editor could not work out which control belongs to them, which is a fault on our side. Go back and click the section to open its settings instead."}),t("p",{className:"sf-note-detail",children:r("code",{children:[n," — ",s]})}),t(G,{inline:!0,name:"Page",onSelect:e})]})}function It({nodeId:e,propKey:n,onBack:s,onEditStyle:l}){const{getItemById:a,getSelectorForId:c,config:h}=Be(o=>o),y=Ue(),{partStyle:u}=We(),{fonts:v}=Ge(),[i,T]=Q({}),A=J(null),g=a(e),S=c(e),se=g?h.components[g.type]:void 0,{key:E,row:P}=Qe(n),le=se?.fields?.[E],k=P?Je(le,P):le,I=ft(e,n),p=g?.props??{},w=qe(k),ve=je(k),Pe=pt(k),H=g?ut(g.type):null,U=P?et(p[E],P):p[E],re=!!P&&!(Array.isArray(p[E])&&P.index<p[E].length);M(()=>{A.current?.querySelector(".es-copy [contenteditable='true'], .es-copy textarea, .sf-copybox textarea, .sf-copybox input")?.focus()},[I]);const[K,ie]=Q(0);M(()=>ie(0),[I]);const X=!!g&&!re&&(!S||!k);M(()=>{if(!X||K>=Ne)return;const o=setTimeout(()=>ie(d=>d+1),Rt);return()=>clearTimeout(o)},[X,K]);const ke=X&&K<Ne;if(!g||re)return t(Ft,{onBack:s});const L=mt(g.type),ae=L?ht(L,n):null;if(L&&ae){const o=u?.partId===I?u:null,d=L[ae],m=d.writable===!0&&!!S,b=f=>{S&&y.replaceComponent(S.zone,S.index,{...g,props:te(p,n,"role",f)})};return t(Ct,{region:d,sizePx:o?.base.sizePx??null,face:ne(p,n,o,v),base:o?.base??null,onFollowStyle:m?b:void 0,source:H,slug:be(p),onBack:s})}if(!S||!k)return ke?t("div",{className:"sf-note","aria-busy":"true"}):t(Ot,{onBack:s,partId:I,why:S?"no field":"no selector"});const z=o=>{y.replaceComponent(S.zone,S.index,{...g,props:o})},Y=o=>{if(W(o).hidden!==W(U).hidden||!w){const m=P?Ze(p,E,P,o):{...p,[E]:o};m&&z(m);return}rt({node:g,propKey:n,kind:w,zone:S.zone,index:S.index},W(o).text,y.replaceComponent)},we=U,B=st(g?.type,n),Te=ge(p,B),D=u?.partId===I?u:null,x=[{partKey:B,face:ne(p,B,D,v),sizeGroup:D?.sizeGroup??null,base:D?.base??_t,style:Te}],Ee=oe(x,o=>o.sizeGroup),ce=ot(D?.inks),V=(o,d)=>{let m=p;for(const b of x)m=te(m,b.partKey,o,d);z(m)},Re=o=>V("role",o),Ce=()=>{let o=p;for(const d of x)o=nt(o,d.partKey);z(o)},Fe=o=>{let d=p;for(const m of x)for(const[b,f]of Object.entries(o))d=te(d,m.partKey,b,f??null);z(d)},Oe=(o,d)=>{const m=x.some(N=>N.style[o]!==void 0),b=oe(x,N=>Pt(N,o)),f=b===null,F=`sf-part-${e}-${n}-${o}`;return o==="color"?t(Ve,{value:f?null:String(b),label:d,colors:ce,onChange:N=>V("color",N)},o):t(me,{label:d,icon:bt[o],className:"sf-part-field",changed:m,htmlFor:F,action:m?t(fe,{label:d,onClear:()=>V(o,null)}):void 0,children:o==="font"?t(pe,{id:F,ariaLabel:d,value:!f&&_(String(b))?String(b):void 0,placeholder:f?"Mixed":"Site style",options:Ke.map(N=>({label:N.label,value:N.key})),onChange:N=>V("font",N||null)}):null},o)},de=w==="text"||w==="textarea",{text:ue,hidden:R}=W(U),C=k?.[He]?.label??n,Ie=r("div",{className:"sf-field-head",children:[t(he,{}),t("span",{className:"sf-field-label sf-field-label--inline",children:C}),de&&t(De,{content:R?`Show ${C} on the slide`:`Hide ${C} on the slide`,children:t("button",{type:"button",className:"sf-field-eye","aria-pressed":R,"aria-label":R?`Show ${C} on the slide`:`Hide ${C} on the slide`,onClick:()=>Y({text:ue,hidden:!R}),children:R?t(ye,{name:"eye-off",size:14}):t(ye,{name:"eye",size:14})})})]}),q=!ve&&Pe;return r(Et,{sectionName:se?.label??g.type,onBack:s,textEditor:r("div",{ref:A,className:"sf-part-text",children:[w?t(Me,{text:ue,onTextChange:o=>Y(de?{text:o,hidden:R}:o),multiline:Xe(w),markup:w!=="plainText",styled:q,style:x[0].style,base:x[0].base,setValue:Fe,colors:ce.map(o=>({name:o.key,label:o.name,value:o.value})),placeholder:Ye(k),ariaLabel:C,head:Ie}):t($e,{field:k,name:n,value:we,onChange:Y,id:`sf-part-${e}-${n}`}),q&&w&&Ee===null&&t("p",{className:"es-field-hint sf-hint-keep",children:"Fixed size — the site’s Text setting doesn’t resize this."})]}),children:[H&&t(xe,{source:H,slug:be(p)}),q&>.map(o=>{const d=Se.filter(f=>f.group===o),m=d.filter(f=>x.some(F=>F.style[f.key]!==void 0)).length,b=`sf-part-g-${e}-${n}-${o.replace(/\W+/g,"-")}`;return r(Ae,{id:b,title:o,open:i[o]!==!0,onToggle:f=>T(F=>({...F,[o]:!f})),count:m,children:[t(Tt,{parts:x,onEditStyle:l,onClearAll:Ce,onFollowStyle:Re,kindLabel:lt[B]}),d.map(f=>Oe(f.key,f.label))]},o)})]})}const _t={fontKey:"",sizePx:16,weight:400,lineRatio:1.4,letterEm:0,align:"left",case:"none",paddingPx:0,widthPx:tt};export{Se as PART_FIELDS,Et as PartPanelBody,fe as PartResetDot,xe as PartSourceNote,Tt as PartStyleCard,wt as PartStyleSelect,It as default,ne as partFace,un as partStyleName};
|
|
1
|
+
"use client";import{jsx as t,jsxs as r,Fragment as O}from"react/jsx-runtime";import{useEffect as M,useId as _e,useRef as J,useState as Q}from"react";import{PartResetDot as fe}from"./PartResetDot.js";import{FieldSelect as pe,InspectorField as $e}from"efficient-easel/fields";import{SettingsField as me,SettingsGroup as Ae}from"efficient-easel/panel";import{InspectorUpLevel as G}from"efficient-easel/chrome";import{StylesIcon as Le,TextColorIcon as ze,TypeIcon as he}from"efficient-easel/icons";import{useEasel as Be}from"efficient-easel/easel";import{Tooltip as De}from"efficient-shell/ui";import{PartInkControl as Ve}from"./PartInkControl.js";import{PartTextBox as Me}from"./PartTextBox.js";import ye from"./UiIcon.js";import{usePreviewHeader as Ge}from"./PreviewHeaderContext.js";import{usePartStyle as We}from"./PreviewPartStyleContext.js";import{readHidable as W,textFieldOffPage as je}from"../lib/puck/controls.js";import{TEXT_FIELD_MARKER as He}from"../lib/puck/field-markers.js";import{useSectionOps as Ue}from"../lib/puck/use-section-ops.js";import{FONT_OPTIONS as Ke,FONT_ROLES as Z,FONT_ROLE_LABELS as ee,fontOption as $}from"../lib/fonts.js";import{isMultilineKind as Xe,partPlaceholder as Ye,typeInPlaceKind as qe}from"../lib/puck/part-text.js";import{partRowField as Je}from"../lib/puck/part-row-field.js";import{decodePartKey as Qe,partPropsWithRowValue as Ze,partRowValue as et}from"../lib/puck/part-rows.js";import{PART_WIDTH_MAX as tt,propsWithPartStyleValue as te,propsWithoutPartStyle as nt,readPartStyle as ge}from"../lib/puck/part-styles.js";import{partInks as ot}from"../lib/puck/part-inks.js";import{styleKeyForPart as st,TEXT_KIND_LABELS as lt}from"../lib/sections/section-text-kinds.js";import{commitPartText as rt}from"../lib/puck/part-typing.js";import{useEditorHost as it}from"../lib/editor-host.js";import{SOURCE_LINES as at,SOURCE_ACTIONS as ct,SOURCE_ACTION_LABEL as dt,partSourceOf as ut,sourceSlugOf as be}from"../lib/puck/source-bound-parts.js";import{encodePartId as ft,holdsAuthoredText as pt}from"../lib/puck/text-parts.js";import{regionsForSection as mt}from"../lib/sections/section-text-regions.js";import{textRegionOfPartKey as ht}from"../lib/sections/text-regions.js";const yt={400:"Regular",500:"Medium",600:"Semibold",700:"Bold"},Se=[{key:"font",label:"Font",group:"Style"},{key:"color",label:"Color",group:"Style"}],gt=[...new Set(Se.map(e=>e.group))],bt={font:t(he,{}),color:t(ze,{})};function I(e){return e.kind==="role"?ee[e.role]:e.kind==="custom"?"Custom":null}const St=e=>e.kind==="role"?`role:${e.role}`:e.kind;function xt(e){const n=e.role;return n&&Z.includes(n)?{kind:"role",role:n}:null}function vt(e,n){if(!e)return{kind:"unread"};const{faceRole:s,base:l}=e;if(n[s]===l.fontKey)return{kind:"role",role:s};const c=Object.keys(n).filter(i=>n[i]===l.fontKey);return c.length===1?{kind:"role",role:c[0]}:{kind:"custom"}}function ne(e,n,s,l){return xt(ge(e,n))??vt(s,l)}function un(e,n,s,l){return I(ne(e,n,s,l))}function oe(e,n){if(!e.length)return null;const s=n(e[0]);return e.every(l=>n(l)===s)?s:null}function Nt(e){return Object.values(e).filter(n=>n!==void 0).length}function Pt(e,n){const s=e.style[n];return n==="color"?s??"":s!==void 0?String(s):e.base.fontKey}function kt({face:e,base:n,onPick:s}){const[l,c]=Q(!1),i=J(null),h=J(null),y=_e();M(()=>{if(!l)return;const a=T=>{i.current&&!i.current.contains(T.target)&&c(!1)};return document.addEventListener("mousedown",a),()=>document.removeEventListener("mousedown",a)},[l]);const u=e.kind==="role"?e.role:null,N=a=>{s(a),c(!1),h.current?.focus()};return r("div",{className:"sf-part-style-pick",ref:i,children:[r("button",{ref:h,type:"button",className:"sf-part-style-trigger","aria-expanded":l,"aria-controls":y,"aria-label":`Follows ${I(e)??"no style"} — change`,onClick:()=>c(a=>!a),onKeyDown:a=>{a.key==="Escape"&&l&&(a.preventDefault(),c(!1))},children:[t("span",{className:"sf-part-style-name",style:{fontFamily:n?.fontKey?`var(${$(n.fontKey)?.cssVar})`:void 0,fontWeight:n?.weight},children:I(e)}),t("span",{className:"sf-part-style-sign","aria-hidden":"true"})]}),l&&r("div",{className:"sf-multi-menu sf-part-style-menu",id:y,role:"listbox",onKeyDown:a=>{a.key==="Escape"&&(a.preventDefault(),c(!1),h.current?.focus())},children:[r("button",{type:"button",role:"option","aria-selected":u===null,className:`sf-part-style-option${u===null?" is-on":""}`,onClick:()=>N(null),children:[t("span",{className:"sf-part-style-option-name",children:"No style"}),t("span",{className:"sf-note",children:"Section’s own"})]}),Z.map(a=>t("button",{type:"button",role:"option","aria-selected":u===a,className:`sf-part-style-option${u===a?" is-on":""}`,onClick:()=>N(a),children:t("span",{className:"sf-part-style-option-name",style:{fontFamily:`var(--font-role-${a})`},children:ee[a]})},a))]})]})}const j="";function wt({face:e,base:n,onPick:s}){const l=e.kind==="role"?e.role:j,c=[{label:"No style",value:j},...Z.map(i=>({label:ee[i],value:i}))];return t(pe,{ariaLabel:"Style",value:l,options:c,inline:!0,onChange:i=>s(i===j?null:i),renderOption:i=>i.value===j?r(O,{children:[t("span",{className:"es-fsel-label sf-part-style-keep",children:i.label}),t("span",{className:"sf-part-style-aside","aria-hidden":"true",children:"Section’s own"})]}):t("span",{className:"es-fsel-label",style:{fontFamily:`var(--font-role-${i.value})`},children:i.label}),renderValue:()=>t("span",{style:{fontFamily:n?.fontKey?`var(${$(n.fontKey)?.cssVar})`:void 0,fontWeight:n?.weight},children:I(e)}),valueText:i=>I(e)??i?.label??""})}function Tt({parts:e,onEditStyle:n,onClearAll:s,onFollowStyle:l,kindLabel:c}){const h=oe(e,T=>St(T.face))?e[0].face:null,y=h?I(h):null,u=h&&h.kind!=="unread"?e[0].base:null,N=e.reduce((T,A)=>T+Nt(A.style),0),a=u?$(u.fontKey)?.label:void 0;return r("div",{className:`sf-part-style${N?" has-overrides":""}`,children:[t("div",{className:"sf-part-style-caption",children:"Style"}),e.length>1&&r("div",{className:"sf-part-style-count",children:[e.length," selected"]}),e.length===1&&c&&r("div",{className:"sf-part-style-count",children:[c," · every row"]}),y&&u?r(O,{children:[l&&h?t(kt,{face:h,base:u,onPick:l}):t("div",{className:"sf-part-style-name",style:{fontFamily:a?`var(${$(u.fontKey)?.cssVar})`:void 0,fontWeight:u.weight},children:y}),t("div",{className:"sf-part-style-meta",children:[a,`${Math.round(u.sizePx)} px`,yt[u.weight]].filter(Boolean).join(" · ")})]}):t("div",{className:"sf-part-style-name",children:"Mixed styles"}),r("div",{className:"sf-part-style-top",children:[y?N?r(O,{children:[r("span",{className:"sf-part-chip",children:[N," changed"]}),t("button",{type:"button",className:"sf-part-link",onClick:s,children:"Clear"})]}):t("span",{className:"sf-part-style-state",children:"This text follows the site style."}):t("span",{className:"sf-part-style-state",children:"Changes apply to all selected."}),t("span",{className:"sf-part-style-gap"}),y&&n&&t("button",{type:"button",className:"sf-part-link",onClick:n,children:"Edit"})]})]})}function Et({sectionName:e,onBack:n,textEditor:s,styleRow:l,children:c}){return r("div",{className:"sf-part-panel",children:[t(G,{inline:!0,name:e,onSelect:n}),s,l,c]})}function xe({source:e,slug:n}){const s=it(),l=ct[e],c=!l.needsSlug||!!n;return t("div",{className:"sf-note sf-note--source",children:r("p",{className:"es-field-hint sf-hint-keep",children:[at[e],c?r(O,{children:[" ",t("button",{type:"button",className:"sf-source-open",onClick:()=>{l.type==="storefront-editor:open-menus"?s.send(l.type):n&&s.send(l.type,{slug:n})},children:dt})]}):null]})})}const ve=4,Rt=120;function Ct({region:e,sizePx:n,face:s,base:l,onFollowStyle:c,source:i,slug:h,onBack:y}){const u=e.follows;return r("div",{className:"sf-note",children:[t("p",{children:t("strong",{children:e.label})}),r("p",{children:["Its size follows Site styles ▸ ",u,n?` — ${Math.round(n)}px right now`:"","."]}),c?t(O,{children:t(me,{label:"Style",icon:t(Le,{}),hint:r(O,{children:["Every ",e.label.toLowerCase()," in this section follows it. The face and the size change; the letter case is the section’s own and stays as it is."]}),children:t(wt,{face:s,base:l,onPick:c})})}):t("p",{className:"es-field-hint sf-hint-keep",children:"Only the size follows a site style. This section sets its own font, weight and letter case."}),i?t(xe,{source:i,slug:h}):null,t(G,{inline:!0,name:"Page",onSelect:y})]})}function Ft({onBack:e}){return r("div",{className:"sf-note",children:[t("p",{children:t("strong",{children:"This part is no longer here."})}),t("p",{children:"The section it belonged to has been changed or removed."}),t(G,{inline:!0,name:"Page",onSelect:e})]})}function Ot({onBack:e,partId:n,why:s}){return r("div",{className:"sf-note",children:[t("p",{children:t("strong",{children:"This part's settings did not open."})}),t("p",{children:"The words are still on your page and nothing has been lost. The editor could not work out which control belongs to them, which is a fault on our side. Go back and click the section to open its settings instead."}),t("p",{className:"sf-note-detail",children:r("code",{children:[n," — ",s]})}),t(G,{inline:!0,name:"Page",onSelect:e})]})}function It({nodeId:e,propKey:n,onBack:s,onEditStyle:l}){const{getItemById:c,getSelectorForId:i,config:h}=Be(o=>o),y=Ue(),{partStyle:u}=We(),{fonts:N}=Ge(),[a,T]=Q({}),A=J(null),g=c(e),S=i(e),se=g?h.components[g.type]:void 0,{key:E,row:P}=Qe(n),le=se?.fields?.[E],k=P?Je(le,P):le,_=ft(e,n),p=g?.props??{},w=qe(k),Ne=je(k),Pe=pt(k),H=g?ut(g.type):null,U=P?et(p[E],P):p[E],re=!!P&&!(Array.isArray(p[E])&&P.index<p[E].length);M(()=>{A.current?.querySelector(".es-copy [contenteditable='true'], .es-copy textarea, .sf-copybox textarea, .sf-copybox input")?.focus()},[_]);const[K,ie]=Q(0);M(()=>ie(0),[_]);const X=!!g&&!re&&(!S||!k);M(()=>{if(!X||K>=ve)return;const o=setTimeout(()=>ie(d=>d+1),Rt);return()=>clearTimeout(o)},[X,K]);const ke=X&&K<ve;if(!g||re)return t(Ft,{onBack:s});const L=mt(g.type),ae=L?ht(L,n):null;if(L&&ae){const o=u?.partId===_?u:null,d=L[ae],m=d.writable===!0&&!!S,b=f=>{S&&y.replaceComponent(S.zone,S.index,{...g,props:te(p,n,"role",f)})};return t(Ct,{region:d,sizePx:o?.base.sizePx??null,face:ne(p,n,o,N),base:o?.base??null,onFollowStyle:m?b:void 0,source:H,slug:be(p),onBack:s})}if(!S||!k)return ke?t("div",{className:"sf-note","aria-busy":"true"}):t(Ot,{onBack:s,partId:_,why:S?"no field":"no selector"});const z=o=>{y.replaceComponent(S.zone,S.index,{...g,props:o})},Y=o=>{if(W(o).hidden!==W(U).hidden||!w){const m=P?Ze(p,E,P,o):{...p,[E]:o};m&&z(m);return}rt({node:g,propKey:n,kind:w,zone:S.zone,index:S.index},W(o).text,y.replaceComponent)},we=U,B=st(g?.type,n),Te=ge(p,B),D=u?.partId===_?u:null,x=[{partKey:B,face:ne(p,B,D,N),sizeGroup:D?.sizeGroup??null,base:D?.base??_t,style:Te}],Ee=oe(x,o=>o.sizeGroup),ce=ot(D?.inks),V=(o,d)=>{let m=p;for(const b of x)m=te(m,b.partKey,o,d);z(m)},Re=o=>V("role",o),Ce=()=>{let o=p;for(const d of x)o=nt(o,d.partKey);z(o)},Fe=o=>{let d=p;for(const m of x)for(const[b,f]of Object.entries(o))d=te(d,m.partKey,b,f??null);z(d)},Oe=(o,d)=>{const m=x.some(v=>v.style[o]!==void 0),b=oe(x,v=>Pt(v,o)),f=b===null,F=`sf-part-${e}-${n}-${o}`;return o==="color"?t(Ve,{value:f?null:String(b),label:d,colors:ce,onChange:v=>V("color",v)},o):t(me,{label:d,icon:bt[o],className:"sf-part-field",changed:m,htmlFor:F,action:m?t(fe,{label:d,onClear:()=>V(o,null)}):void 0,children:o==="font"?t(pe,{id:F,ariaLabel:d,value:!f&&$(String(b))?String(b):void 0,placeholder:f?"Mixed":"Site style",options:Ke.map(v=>({label:v.label,value:v.key})),onChange:v=>V("font",v||null)}):null},o)},de=w==="text"||w==="textarea",{text:ue,hidden:R}=W(U),C=k?.[He]?.label??n,Ie=r("div",{className:"sf-field-head",children:[t(he,{}),t("span",{className:"sf-field-label sf-field-label--inline",children:C}),de&&t(De,{content:R?`Show ${C} on the slide`:`Hide ${C} on the slide`,children:t("button",{type:"button",className:"sf-field-eye","aria-pressed":R,"aria-label":R?`Show ${C} on the slide`:`Hide ${C} on the slide`,onClick:()=>Y({text:ue,hidden:!R}),children:R?t(ye,{name:"eye-off",size:14}):t(ye,{name:"eye",size:14})})})]}),q=!Ne&&Pe;return r(Et,{sectionName:se?.label??g.type,onBack:s,textEditor:r("div",{ref:A,className:"sf-part-text",children:[w?t(Me,{text:ue,onTextChange:o=>Y(de?{text:o,hidden:R}:o),multiline:Xe(w),markup:w!=="plainText",styled:q,style:x[0].style,base:x[0].base,setValue:Fe,colors:ce.map(o=>({name:o.key,label:o.name,value:o.value})),placeholder:Ye(k),ariaLabel:C,head:Ie}):t($e,{field:k,name:n,value:we,onChange:Y,id:`sf-part-${e}-${n}`}),q&&w&&Ee===null&&t("p",{className:"es-field-hint sf-hint-keep",children:"Fixed size — the site’s Text setting doesn’t resize this."})]}),children:[H&&t(xe,{source:H,slug:be(p)}),q&>.map(o=>{const d=Se.filter(f=>f.group===o),m=d.filter(f=>x.some(F=>F.style[f.key]!==void 0)).length,b=`sf-part-g-${e}-${n}-${o.replace(/\W+/g,"-")}`;return r(Ae,{id:b,title:o,open:a[o]!==!0,onToggle:f=>T(F=>({...F,[o]:!f})),count:m,children:[t(Tt,{parts:x,onEditStyle:l,onClearAll:Ce,onFollowStyle:Re,kindLabel:lt[B]}),d.map(f=>Oe(f.key,f.label))]},o)})]})}const _t={fontKey:"",sizePx:16,weight:400,lineRatio:1.4,letterEm:0,align:"left",case:"none",paddingPx:0,widthPx:tt};export{Se as PART_FIELDS,Et as PartPanelBody,fe as PartResetDot,xe as PartSourceNote,Tt as PartStyleCard,wt as PartStyleSelect,It as default,ne as partFace,un as partStyleName};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "efficient-web-builder",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.1",
|
|
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",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"typecheck": "tsc --noEmit",
|
|
32
32
|
"test:boundary": "node scripts/test-boundary.mjs",
|
|
33
33
|
"test:screen": "node scripts/test-screen-in-easel.mjs",
|
|
34
|
-
"test:own-ui": "
|
|
34
|
+
"test:own-ui": "efficient-shell-ui-check",
|
|
35
35
|
"build": "node scripts/build.mjs",
|
|
36
36
|
"verify:dist": "node scripts/verify-dist.mjs",
|
|
37
37
|
"verify": "pnpm run typecheck && pnpm run test:boundary && pnpm run test:screen && pnpm run test:own-ui && pnpm run test:guards && pnpm run build && pnpm run verify:dist",
|
|
@@ -39,8 +39,8 @@
|
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
41
41
|
"@tabler/icons-react": "^3.46.0",
|
|
42
|
-
"efficient-easel": "^
|
|
43
|
-
"efficient-shell": "^4.
|
|
42
|
+
"efficient-easel": "^2.0.0",
|
|
43
|
+
"efficient-shell": "^4.171.0",
|
|
44
44
|
"isomorphic-dompurify": "^3.18.0",
|
|
45
45
|
"next": ">=16",
|
|
46
46
|
"react": ">=19",
|
|
@@ -58,8 +58,8 @@
|
|
|
58
58
|
"@types/node": "^20",
|
|
59
59
|
"@types/react": "^19",
|
|
60
60
|
"@types/react-dom": "^19",
|
|
61
|
-
"efficient-easel": "^1.0
|
|
62
|
-
"efficient-shell": "^4.
|
|
61
|
+
"efficient-easel": "^2.1.0",
|
|
62
|
+
"efficient-shell": "^4.180.0",
|
|
63
63
|
"esbuild": "^0.28.0",
|
|
64
64
|
"happy-dom": "^20.14.5",
|
|
65
65
|
"isomorphic-dompurify": "^3.18.0",
|