efficient-web-builder 41.0.0 → 41.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 +12 -0
- package/dist/editor/lib/sections/section-dry-run.d.ts +8 -0
- package/dist/editor/lib/sections/section-dry-run.js +2 -2
- package/dist/render/lib/document/blocks/general/section-to-box-row-parts.d.ts +26 -0
- package/dist/render/lib/document/blocks/general/section-to-box-row-parts.js +1 -0
- package/dist/render/lib/document/blocks/general/section-to-box-rows.js +1 -1
- package/dist/render/lib/puck/blocks/general/section-to-box-row-parts.d.ts +1 -0
- package/dist/render/lib/puck/blocks/general/section-to-box-row-parts.js +1 -0
- package/dist/render/lib/sections/section-to-box.d.ts +40 -10
- package/dist/render/lib/sections/section-to-box.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 41.1.0 - 2026-10-27
|
|
4
|
+
|
|
5
|
+
Additive: every row written for 41.0.0 converts as before (the three section-to-box guards, unchanged cases, pass).
|
|
6
|
+
|
|
7
|
+
### Added (the engine parts the merge groups need; plan 2026-10-23 §3.1)
|
|
8
|
+
|
|
9
|
+
- **A core can become the survivor's block.** `{ kind: "core"; keep; as?; rename?; props?; slots? }`: `as` names the target block (a merged-away type converts straight to the survivor's shape), `rename` moves an old key to the target's key, `props` are the target's own fixed values (an old kept value goes over them), and `slots` is the ONE field that fills its slots, in order: a `BlockSeed` is one fixed block (a card template), a list part is one block or Box per item of an old array (a Timeline's Milestones, a Logos block's LogoBlocks).
|
|
10
|
+
- **A `block` part**: `{ kind: "block"; type; box?; props?; slot?; parts?; keepEmpty? }`, a named block that is not a Box (a Strip, a Milestone, a Number). `box` reads old keys onto it as a group's does (off the ITEM in a list); `slot` holds the `parts`; a leaf block has neither. In a `flat` list it is one block per item.
|
|
11
|
+
- **A row's `skip(props)`**: a reason leaves the node as it is, on the page and in the editor, and comes back as `skipped`; the dry run lists it apart (`DryRunReport.skipped`) and never counts it as converted.
|
|
12
|
+
- **A list's `item(item, index, node)`** returns `{ props?, reverse? }`: props on that item's Box (or, `flat`, on each block it makes), and `reverse` draws its parts the other way round. **`perRow(node)` / `rowName` / `rowProps`** cut the items into row Boxes of N.
|
|
13
|
+
- **A part's `when(node, item?)`** gets the list item it is read off, so a part can depend on one item.
|
|
14
|
+
|
|
3
15
|
## 41.0.0 - 2026-10-27
|
|
4
16
|
|
|
5
17
|
**MAJOR for hosts: the peers narrow.** `efficient-easel` is now `^21.1.0` (was `^20.2.0`) and `efficient-shell` is now `^10.0.0` (was `^9.1.0`): a host moves easel and shell with this release (easel 21 itself needs shell 10), and the builder reads easel 21.1's names. Beyond that the name "Puck" is being retired (Victor Mau, 2026-10-26; phase 2 of 4, expand): the new names ship BESIDE the old ones, and every deprecated `lib/puck/…` path and name keeps working in 41. 42.0.0 removes them (phase 4); nothing saved changes.
|
|
@@ -41,6 +41,13 @@ export type DryRunSiteWrites = {
|
|
|
41
41
|
nodes: number;
|
|
42
42
|
}[];
|
|
43
43
|
};
|
|
44
|
+
export type DryRunSkipped = {
|
|
45
|
+
page: string;
|
|
46
|
+
index: number;
|
|
47
|
+
type: string;
|
|
48
|
+
id: string;
|
|
49
|
+
why: string;
|
|
50
|
+
};
|
|
44
51
|
export type DryRunTypeTotals = {
|
|
45
52
|
nodes: number;
|
|
46
53
|
pages: number;
|
|
@@ -51,6 +58,7 @@ export type DryRunReport = {
|
|
|
51
58
|
noRow: string[];
|
|
52
59
|
pages: number;
|
|
53
60
|
nodes: DryRunNode[];
|
|
61
|
+
skipped: DryRunSkipped[];
|
|
54
62
|
byType: Record<string, DryRunTypeTotals>;
|
|
55
63
|
sites: DryRunSiteWrites[];
|
|
56
64
|
siteFacts: SiteFacts[];
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{convertSection as
|
|
1
|
+
import{convertSection as O,sectionToBoxRow as S,sectionToBoxTypes as x}from"../../../render/lib/sections/section-to-box.js";function _(e){const[t,r]=e.split("/");return t&&r?{schema:t,site:r}:void 0}function C(e){const t=n=>typeof n=="string"?n:null,r=e.theme_row===void 0?null:e.theme_row==="site";return{schema:String(e.schema),site:String(e.site),headingCase:t(e.heading_case),textStyles:typeof e.text_styles_is_null=="boolean"?!e.text_styles_is_null:null,themePresets:typeof e.theme_presets=="number"?e.theme_presets:null,cardShadow:t(e.card_shadow),ownThemeRow:r,deleted:e.site_deleted===!0}}const R=new Set([null,"authored"]);function T(e,t){return t?.deleted?{notNeeded:"the site is soft-deleted: no write"}:t&&t.ownThemeRow===!1?{notNeeded:"no own theme row: write refused (the command changes a site's own row, it never creates one)"}:e.kind==="heading-case-upper"?!t||t.textStyles===null?{undecided:"the site's heading case and text_styles were not read"}:t.textStyles?{notNeeded:"the site has saved text_styles: its Heading 1–3 already say their own case"}:R.has(t.headingCase)?{send:{kind:e.kind,styles:[...e.styles]}}:{notNeeded:`the site chose a heading case (${t.headingCase})`}:!t||typeof t.themePresets!="number"?{undecided:"the site's theme preset count was not read"}:{send:{kind:e.kind,copyOf:e.copyOf,set:{...e.set},index:t.themePresets}}}function D(e,t={}){const r=[...t.types??x()],n=r.filter(d=>!S(d)),s=new Set(r.filter(d=>S(d))),a=[],i=[],p={},y={},b=new Map((t.sites??[]).map(d=>[`${d.schema}/${d.site}`,d])),k=new Map,N=(d,g,o)=>{const m=`${d.schema}/${d.site}`;let u=k.get(m);u||k.set(m,u={...d,facts:!!g,writes:[],undecided:[],notNeeded:[]});const c=T(o,g);if("send"in c){const f=u.writes.find(w=>JSON.stringify(w.write)===JSON.stringify(c.send));f?f.nodes+=1:u.writes.push({write:c.send,reason:o.kind==="add-theme-preset"?o.reason:"a heading that followed the site's heading case",nodes:1});return}const[$,h]="undecided"in c?[u.undecided,c.undecided]:[u.notNeeded,c.notNeeded],l=$.find(f=>f.kind===o.kind&&f.why===h);l?l.nodes+=1:$.push({kind:o.kind,why:h,nodes:1})};for(const d of e){const g=d.layout?.content;Array.isArray(g)&&g.forEach((o,m)=>{if(!o||typeof o.type!="string"||!s.has(o.type))return;const u=S(o.type),c=_(d.label),$=c?b.get(`${c.schema}/${c.site}`):void 0,h=O(o,u,$?{site:$}:{});if(h.skipped!==void 0){i.push({page:d.label,index:m,type:o.type,id:String(o.props?.id??""),why:h.skipped});return}if(c)for(const w of h.siteWrites)N(c,$,w);const l={page:d.label,index:m,type:o.type,id:String(o.props?.id??""),carried:h.carried,dropped:h.dropped};t.draw&&(l.before=t.draw(o),l.after=t.draw(h.node),l.sameMarkup=l.before===l.after),a.push(l);const f=p[o.type]??={nodes:0,pages:0,dropped:{}};f.nodes+=1,(y[o.type]??=new Set).add(d.label),f.pages=y[o.type].size;for(const w of h.dropped)f.dropped[w.key]=(f.dropped[w.key]??0)+1})}return{types:r,noRow:n,pages:e.length,nodes:a,skipped:i,byType:p,sites:[...k.values()],siteFacts:[...t.sites??[]]}}function E(e){return e.sites.filter(t=>t.writes.length).map(t=>({schema:t.schema,site:t.site,writes:t.writes.map(r=>r.write)}))}const j=e=>e.kind==="heading-case-upper"?`Capitalization = Uppercase on ${e.styles.join(", ")}`:`add theme preset #${e.index} (shown as theme ${e.index+1}): a copy of preset #${e.copyOf} with ${Object.entries(e.set).map(([t,r])=>`${t} = ${r}`).join(", ")}`;function A(e){const t=["# Section conversion — dry run (READ ONLY: nothing was written)","",`Pages read: ${e.pages}. Nodes converted on paper: ${e.nodes.length}.`,"","| Type | Nodes | Pages | Dropped keys |","|---|---|---|---|"];for(const[n,s]of Object.entries(e.byType)){const a=Object.keys(s.dropped).length;t.push(`| ${n} | ${s.nodes} | ${s.pages} | ${a} |`)}if(e.noRow.length&&t.push("",`Asked for, but no row: ${e.noRow.join(", ")}.`),e.skipped.length){t.push("",`Nodes skipped (left as they are): ${e.skipped.length}.`);const n=new Map;for(const s of e.skipped){const a=n.get(s.type)??n.set(s.type,new Map).get(s.type);a.set(s.why,(a.get(s.why)??0)+1)}for(const[s,a]of n){t.push("",`## ${s} — skipped (not converted)`,"");for(const[i,p]of a)t.push(`- ×${p}: ${i}`)}}for(const[n,s]of Object.entries(e.byType)){const a=Object.entries(s.dropped);if(!a.length)continue;t.push("",`## ${n} — dropped`,"");const i=new Map;for(const p of e.nodes)if(p.type===n)for(const y of p.dropped)i.set(y.key,y.why);for(const[p,y]of a)t.push(`- \`${p}\` ×${y}: ${i.get(p)}`)}if(e.siteFacts.length){t.push("","## Site facts (the read-only read handed in)","","| Site | Own theme row | Deleted | Heading case | text_styles saved | Theme presets | Card shadow |","|---|---|---|---|---|---|---|");const n=s=>s==null?"—":String(s);for(const s of e.siteFacts)t.push(`| ${s.schema}/${s.site} | ${n(s.ownThemeRow)} | ${s.deleted?"YES":"no"} | ${n(s.headingCase)} | ${n(s.textStyles)} | ${n(s.themePresets)} | ${n(s.cardShadow)} |`)}if(e.sites.length){t.push("","## Site writes (DECLARED ONLY: nothing was written)","","One line per write per site. A theme preset write is its own line. Only the `send` lines are in `site-writes.json`.","","| Site | Facts read | Write | Status | Nodes | Why |","|---|---|---|---|---|---|");for(const n of e.sites){const s=`${n.schema}/${n.site}`,a=n.facts?"yes":"NO";for(const i of n.writes)t.push(`| ${s} | ${a} | ${j(i.write)} | send | ${i.nodes} | ${i.reason} |`);for(const i of n.undecided)t.push(`| ${s} | ${a} | ${i.kind} | UNDECIDED | ${i.nodes} | ${i.why} |`);for(const i of n.notNeeded)t.push(`| ${s} | ${a} | ${i.kind} | not needed | ${i.nodes} | ${i.why} |`)}}const r=e.nodes.filter(n=>n.sameMarkup!==void 0);if(r.length){const n=r.filter(s=>s.sameMarkup).length;t.push("",`Markup equal before and after: ${n} of ${r.length} (a hint; the pixels decide).`)}return t.join(`
|
|
2
2
|
`)+`
|
|
3
|
-
`}export{A as dryRunMarkdown,D as dryRunSections,C as siteFactsFromRead,
|
|
3
|
+
`}export{A as dryRunMarkdown,D as dryRunSections,C as siteFactsFromRead,_ as siteOfLabel,E as siteWritesFile};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type BoxEntry, type PartRow, type SiteWrite } from "../../../sections/section-to-box.js";
|
|
2
|
+
type Props = Record<string, unknown>;
|
|
3
|
+
export declare const TEXT: Props;
|
|
4
|
+
export declare const DIM: Props;
|
|
5
|
+
export declare const BUTTON: Props;
|
|
6
|
+
export declare const BOX: Props;
|
|
7
|
+
export declare const INNER_BOX: Props;
|
|
8
|
+
export declare const SECTION_BOX: Props;
|
|
9
|
+
export declare const SAME_KEYS: readonly BoxEntry[];
|
|
10
|
+
export declare const DROP_COMMON: Readonly<Record<string, string>>;
|
|
11
|
+
export declare const DROP_SIDE_PADDING: Readonly<Record<string, string>>;
|
|
12
|
+
export declare const num: (v: unknown) => number | undefined;
|
|
13
|
+
export type OwnPad = {
|
|
14
|
+
top: number;
|
|
15
|
+
bottom: number;
|
|
16
|
+
topPhone: number;
|
|
17
|
+
bottomPhone: number;
|
|
18
|
+
};
|
|
19
|
+
export declare function wrapperPad(p: Props, own: OwnPad): Props;
|
|
20
|
+
export declare const wrapperPadKeys: (own: OwnPad) => BoxEntry[];
|
|
21
|
+
export declare const HEADING_CASE_UPPER: SiteWrite;
|
|
22
|
+
export declare const headingCaseOf: (key: string) => (node: {
|
|
23
|
+
props: Props;
|
|
24
|
+
}) => readonly SiteWrite[];
|
|
25
|
+
export declare const smallText: (key: string, style?: Props) => PartRow;
|
|
26
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{hasWords as c}from"../../../sections/section-to-box.js";const s={align:"left",tone:"default",contained:!0},g={...s,tone:"dim"},h={variant:"primary",align:"left",contained:!0},d={justify:"start",align:"stretch",wrap:!1,blockSpacing:"gap",padV2:!0},P={...d,direction:"column",sectionPaddingTop:0,sectionPaddingBottom:0},m={...d,sectionAccent:0},u=["gridSpan","backgroundWidth","contentWidth","contentMax","widthMode","sectionHeightMode","sectionHeightValue","sectionHeightUnit","sectionHeightModePhone","sectionHeightValuePhone","sectionHeightUnitPhone","sectionCorners","sectionCornerSides","sectionBorder","sectionBorderPhone","sectionAccent","stickTop","stickTopDistance","stickTopPhone","stickTopDistancePhone","anchor","sectionAnimation","_hidden"],x={sectionSurface:"read by nothing in src/ (searched 2026-10-24): a retired theme key that draws nothing today, so dropping it changes nothing"},l=Object.fromEntries(["sectionPaddingLeft","sectionPaddingRight","sectionPaddingLeftPhone","sectionPaddingRightPhone"].map(t=>[t,"needs Box side padding outside its gutter: the old section added it outside its frame, a Box's side padding replaces the gutter. 0 live nodes store it (JJ's read-only export, 2026-10-24)"])),n=t=>typeof t=="number"&&Number.isFinite(t)?t:void 0;function a(t,o){const i=n(t.sectionPaddingTop)??0,e=n(t.sectionPaddingBottom)??0;return{sectionPaddingTop:i+o.top,sectionPaddingBottom:e+o.bottom,sectionPaddingTopPhone:(n(t.sectionPaddingTopPhone)??i)+o.topPhone,sectionPaddingBottomPhone:(n(t.sectionPaddingBottomPhone)??e)+o.bottomPhone}}const T=t=>["sectionPaddingTop","sectionPaddingBottom","sectionPaddingTopPhone","sectionPaddingBottomPhone"].map(o=>[o,o,(i,e)=>a(e,t)[o]]),r={kind:"heading-case-upper",styles:["heading-1","heading-2","heading-3"]},f=t=>o=>c(o.props[t])?[r]:[],B=(t,o)=>({kind:"text",key:t,role:"eyebrow",props:s,...o?{style:o}:{}});export{d as BOX,h as BUTTON,g as DIM,x as DROP_COMMON,l as DROP_SIDE_PADDING,r as HEADING_CASE_UPPER,P as INNER_BOX,u as SAME_KEYS,m as SECTION_BOX,s as TEXT,f as headingCaseOf,n as num,B as smallText,a as wrapperPad,T as wrapperPadKeys};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{resolveSectionPadding as
|
|
1
|
+
import{resolveSectionPadding as H}from"../../../../../format/lib/content/section-padding.js";import{SECTION_PAD_INNER_DEFAULTS as E}from"../../passes/section-padding.js";import{hasWords as v,headParts as N,registerSectionToBox as O}from"../../../sections/section-to-box.js";import{BUTTON as c,DIM as g,DROP_COMMON as s,DROP_SIDE_PADDING as h,INNER_BOX as o,SAME_KEYS as d,SECTION_BOX as p,TEXT as i,headingCaseOf as m,num as A,smallText as r,wrapperPad as u,wrapperPadKeys as P}from"./section-to-box-row-parts.js";function a(e,t){const n=H(e,E[t]);return{sectionPaddingTop:n.top,sectionPaddingBottom:n.bottom}}const k=e=>[["padTop","sectionPaddingTop",(t,n)=>a(n,e).sectionPaddingTop],["padBottom","sectionPaddingBottom",(t,n)=>a(n,e).sectionPaddingBottom],["sectionPaddingTop","sectionPaddingTop",(t,n)=>a(n,e).sectionPaddingTop],["sectionPaddingBottom","sectionPaddingBottom",(t,n)=>a(n,e).sectionPaddingBottom]],w={top:72,bottom:72,topPhone:72,bottomPhone:72},[R,D]=N("Prose"),I={name:"Editorial Text",on:!1,boxProps:e=>({...p,direction:"column",gap:28,sectionBorder:{top:1},...u(e,w)}),box:[["topBorder","sectionBorder",e=>e===!1?void 0:{top:1}],...d,...P(w)],parts:[{kind:"group",name:"Heading",props:{...o,gap:16},parts:[{...R,props:i},{...D,props:i,style:{textStyle:"heading-3",lineHeight:1,letterSpacing:-.02}}]},{kind:"text",key:"body",props:g,style:{size:16,lineHeight:1.7,width:680,paragraphSpacing:18}}],site:m("title"),drop:{...s,...h,headPlacement:"needs a row per head placement: an own-box Prose draws no head (its words sit in a Section Header above it), the row always draws the head. 0 of 123 live nodes store it (JJ's export, 2026-10-24)"}},W={buttonPaddingTop:20,buttonPaddingBottom:20,buttonPaddingLeft:28,buttonPaddingRight:28},f=(e,t)=>({kind:"list",key:"buttons",name:"Button",flat:!0,when:e,parts:[{kind:"button",key:"label",href:"href",variant:{key:"variant",map:{primary:"primary",outline:"ghost"},fallback:"ghost"},style:{letterSpacing:.3},props:{...c,...W,...t}}]}),b=e=>v(e.title),C={name:"Call-to-Action Heading",on:!1,boxProps:e=>({...p,direction:"row",align:"center",justify:b(e)?"between":"end",gap:60,share:"content",sectionBorder:{bottom:1},...a(e,"TitleWithButtons")}),box:[...d,...k("TitleWithButtons")],parts:[{kind:"group",name:"Title",props:o,parts:[{kind:"text",key:"title",role:"heading",props:i,style:{textStyle:"heading-1",lineHeight:.94,letterSpacing:-.035,width:1e3}}]},{kind:"group",name:"Buttons",props:{...o,gap:14},parts:[f(b,{widthFit:"fill",iconPosition:"end"}),f(e=>!b(e),{})]}],site:m("title"),drop:{...s,buttonsAlign:"moves the buttons only when there is no heading (4 of 128 live nodes), where the Box puts them at the end. 0 of 128 live nodes store it (JJ's export, 2026-10-24)"}},S={top:40,bottom:64,topPhone:24,bottomPhone:36},z={buttonFillOpacity:0,buttonBorder:{width:0},buttonPaddingTop:0,buttonPaddingRight:0,buttonPaddingBottom:0,buttonPaddingLeft:0},L={name:"Section Header",on:!1,boxProps:e=>({...p,direction:"column",gap:0,...u(e,S)}),box:[...d,"fullWidth","pageWidth",...P(S)],parts:[{kind:"group",name:"Header",props:{...o,direction:"row",align:"end",justify:"between",gap:40,share:"content",sectionBorder:{bottom:1},sectionPaddingBottom:24,sectionPaddingBottomPhone:20},parts:[{kind:"group",name:"Title",props:{...o,direction:"row",align:"end",gap:40,share:"content"},parts:[{kind:"group",name:"Number",props:{...o,align:"start",gap:12},parts:[{kind:"image",key:"logoSrc",alt:"logoAlt",props:{aspect:"auto",contained:!1}},r("num")]},{kind:"text",key:"title",role:"heading",props:i,style:{lineHeight:.95,letterSpacing:-.025}}]},{kind:"group",name:"Words",props:{...o,align:"end",gap:12},parts:[{kind:"text",key:"description",props:{...g,align:"right"},style:{role:"heading",size:14,lineHeight:1.5,width:340}},{kind:"button",key:"linkText",href:"linkHref",variant:"ghost",props:{...c,align:"right",...z}}]}]}],site:m("title"),drop:{...s,...h,headStyle:"needs a row per head look: the row draws the standard look; the editorial, simple, statement, natural and essay looks are other layouts. 0 of 69 live nodes store it (JJ's export, 2026-10-24)",eyebrowLook:"read only by the simple, statement, natural and essay looks, which this row does not draw. 0 of 69 live nodes store it",rowGap:"read only by the natural look, which this row does not draw. 0 of 69 live nodes store it",descriptionSize:"read only by the natural look, which this row does not draw. 0 of 69 live nodes store it",numStyle:"Solid (the default; 4 of 69 live nodes store it) IS the eyebrow Text the row draws; Outlined (0 live) needs an outlined-number text look"}},M={name:"Lead Text",on:!1,boxProps:e=>({...p,direction:"column",gap:40,sectionBorder:{bottom:1},...a(e,"ParagraphsA")}),box:[...d,...k("ParagraphsA")],parts:[{kind:"group",name:"Numbers",props:{...o,direction:"row",justify:"between",gap:60,share:"content",sectionBorder:{bottom:1},sectionPaddingBottom:40},parts:[r("eyebrow",{color:"labels",letterSpacing:.24}),{kind:"group",name:"Stats",props:{...o,align:"end"},parts:[{kind:"list",key:"stats",name:"Stat",props:{...o,direction:"row",share:"content",justify:"end",gap:8},parts:[r("value",{color:"text",letterSpacing:.18,lineHeight:1.8}),r("label",{color:"labels",letterSpacing:.18,lineHeight:1.8})]}]}]},{kind:"group",name:"Words",props:{...o,direction:"row",align:"end",gap:60,share:"bigLeft"},parts:[{kind:"text",key:"lead",props:i,style:{role:"heading3",size:22,sizePhone:20,lineHeight:1.45,letterSpacing:-.005,width:780,paragraphSpacing:0}},{kind:"group",name:"Founders",props:{...o,gap:8},parts:[{kind:"group",name:"Note",props:{...o,gap:6},parts:[{kind:"text",key:"foundersHeading",props:i,style:{role:"heading",size:18,sizePhone:16,weight:500,letterSpacing:-.01,lineHeight:1.7}},{kind:"text",key:"foundersBody",props:g,style:{role:"heading",size:14,sizePhone:12,lineHeight:1.7}}]},r("attribution",{letterSpacing:.24,lineHeight:1.7})]}]}],drop:{...s}},B={top:24,bottom:24,topPhone:24,bottomPhone:24},x={schema:"xtreme_mudder",site:"xtreme-mudder"},X="#F2F2EF",F={kind:"add-theme-preset",copyOf:0,set:{background:X},reason:"Banner card with no picture"},T=e=>e.site?.schema===x.schema&&e.site.site===x.site,_=e=>typeof e.imageSrc!="string"||!e.imageSrc;function J(e,t){if(!_(e)||!T(t))return{};const n=t.site?.themePresets,l=t.site?.cardShadow,y=typeof l=="string"&&l!==""&&l!=="none"?l:void 0;return{...typeof n=="number"?{sectionAccent:n}:{},...y?{shadow:y}:{}}}const G={name:"Banner",on:!1,boxProps:e=>({...p,direction:"column",gap:0,...u(e,B)}),box:[...d,"contained",...P(B)],parts:[{kind:"group",name:"Card",keepEmpty:!0,props:(e,t)=>({...o,gap:28,placement:"top-left",darken:60,darkenStyle:"even",sectionBorder:{width:1},sectionPaddingTop:64,sectionPaddingBottom:64,sectionPaddingTopPhone:40,sectionPaddingBottomPhone:40,sectionPaddingLeft:40,sectionPaddingRight:40,sectionPaddingLeftPhone:24,sectionPaddingRightPhone:24,...J(e,t)}),box:[["imageSrc","bgImage"],["overlay","darken",e=>Math.min(Math.max(A(e)??60,0),100)]],parts:[{kind:"group",name:"Words",props:{...o,gap:20},parts:[r("eyebrow"),{kind:"text",key:"title",role:"heading",props:i,style:{weight:500,lineHeight:1.05,letterSpacing:-.03,case:"none",width:680}},{kind:"text",key:"body",props:g,style:{size:16,lineHeight:1.6,width:680}}]},{kind:"button",key:"ctaText",href:"ctaHref",variant:"primary",props:c}]}],site:(e,t)=>_(e.props)&&T(t)?[F]:[],drop:{...s,...h}},K={Prose:I,TitleWithButtons:C,SectionHeader:L,ParagraphsA:M,Banner:G};function Q(){O(K)}export{K as GENERAL_SECTION_TO_BOX,x as XM_SITE,Q as registerGeneralSectionRows};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "../../../document/blocks/general/section-to-box-row-parts.d.ts";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export*from"../../../document/blocks/general/section-to-box-row-parts.js";
|
|
@@ -41,6 +41,7 @@ export type SectionConversion = {
|
|
|
41
41
|
carried: Carried[];
|
|
42
42
|
dropped: Dropped[];
|
|
43
43
|
siteWrites: SiteWrite[];
|
|
44
|
+
skipped?: string;
|
|
44
45
|
};
|
|
45
46
|
export type TextRole = "heading" | "body" | "eyebrow";
|
|
46
47
|
export type BoxEntry = string | readonly [string, string] | readonly [string, string, (value: unknown, props: Props) => unknown];
|
|
@@ -50,8 +51,31 @@ export type ButtonVariantOf = {
|
|
|
50
51
|
fallback: string;
|
|
51
52
|
};
|
|
52
53
|
export type PartRow = PartRowKind & {
|
|
53
|
-
when?: (node: Props) => boolean;
|
|
54
|
+
when?: (node: Props, item?: Props) => boolean;
|
|
54
55
|
};
|
|
56
|
+
type OwnProps = Props | ((node: Props, ctx: ConvertContext) => Props);
|
|
57
|
+
export type ListItemLook = {
|
|
58
|
+
props?: Props;
|
|
59
|
+
reverse?: boolean;
|
|
60
|
+
};
|
|
61
|
+
export type ListPart = {
|
|
62
|
+
kind: "list";
|
|
63
|
+
key: string;
|
|
64
|
+
name: string;
|
|
65
|
+
parts: readonly PartRow[];
|
|
66
|
+
flat?: boolean;
|
|
67
|
+
props?: Props;
|
|
68
|
+
item?: (item: Props, index: number, node: Props) => ListItemLook;
|
|
69
|
+
perRow?: (node: Props) => number;
|
|
70
|
+
rowName?: string;
|
|
71
|
+
rowProps?: Props;
|
|
72
|
+
};
|
|
73
|
+
export type BlockSeed = {
|
|
74
|
+
type: string;
|
|
75
|
+
props?: Props;
|
|
76
|
+
slots?: Readonly<Record<string, readonly BlockSeed[]>>;
|
|
77
|
+
};
|
|
78
|
+
export type SlotFill = BlockSeed | ListPart;
|
|
55
79
|
type PartRowKind = {
|
|
56
80
|
kind: "text";
|
|
57
81
|
key: string;
|
|
@@ -70,23 +94,28 @@ type PartRowKind = {
|
|
|
70
94
|
key: string;
|
|
71
95
|
alt?: string;
|
|
72
96
|
props?: Props;
|
|
73
|
-
} | {
|
|
74
|
-
kind: "list";
|
|
75
|
-
key: string;
|
|
76
|
-
name: string;
|
|
77
|
-
parts: readonly PartRow[];
|
|
78
|
-
flat?: boolean;
|
|
79
|
-
props?: Props;
|
|
80
|
-
} | {
|
|
97
|
+
} | ListPart | {
|
|
81
98
|
kind: "group";
|
|
82
99
|
name: string;
|
|
83
100
|
parts: readonly PartRow[];
|
|
84
|
-
props?:
|
|
101
|
+
props?: OwnProps;
|
|
102
|
+
box?: readonly BoxEntry[];
|
|
103
|
+
keepEmpty?: boolean;
|
|
104
|
+
} | {
|
|
105
|
+
kind: "block";
|
|
106
|
+
type: string;
|
|
85
107
|
box?: readonly BoxEntry[];
|
|
108
|
+
props?: OwnProps;
|
|
109
|
+
slot?: string;
|
|
110
|
+
parts?: readonly PartRow[];
|
|
86
111
|
keepEmpty?: boolean;
|
|
87
112
|
} | {
|
|
88
113
|
kind: "core";
|
|
89
114
|
keep: readonly string[];
|
|
115
|
+
as?: string;
|
|
116
|
+
rename?: Readonly<Record<string, string>>;
|
|
117
|
+
props?: Props;
|
|
118
|
+
slots?: Readonly<Record<string, readonly SlotFill[]>>;
|
|
90
119
|
};
|
|
91
120
|
export interface SectionToBoxRow {
|
|
92
121
|
name: string;
|
|
@@ -96,6 +125,7 @@ export interface SectionToBoxRow {
|
|
|
96
125
|
parts: readonly PartRow[];
|
|
97
126
|
drop?: Readonly<Record<string, string>>;
|
|
98
127
|
site?: (node: SectionNode, ctx: ConvertContext) => readonly SiteWrite[];
|
|
128
|
+
skip?: (props: Props) => string | undefined;
|
|
99
129
|
}
|
|
100
130
|
export declare function registerBoxNativeSections(types: Readonly<Record<string, string>>): void;
|
|
101
131
|
export declare function boxNativeReason(type: string): string | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{readHidable as
|
|
1
|
+
import{readHidable as K}from"efficient-easel/draw";import{BOX_TYPE as M,CONTAINER_BLOCKS_SLOT as V}from"../../../format/lib/document/container-blocks.js";import{headingTextSeed as Y,labelTextSeed as z}from"../../../format/lib/document/text-seeds.js";import{SPLITTABLE_SECTIONS as D}from"../../../format/lib/sections/section-head-placement.js";const T=M,_=V,E={Flex:"the Box itself",Hero:"its slides hold Boxes of real blocks (Hero › Slide › Box)",Divider:"one block with no text, picture or button",CustomHTML:"the HTML box is the whole section; it has no parts"};function ot(e){for(const[n,r]of Object.entries(e)){if(n in E&&E[n]!==r)throw new Error(`lib/sections/section-to-box: "${n}" is already Box-native`);E[n]=r}}function nt(e){return E[e]}const F=new Set(["id"]),g={};function st(e){for(const[n,r]of Object.entries(e)){if(n in g&&g[n]!==r)throw new Error(`lib/sections/section-to-box: "${n}" already has a row`);g[n]=r}}function it(e){return g[e]}function rt(){return Object.keys(g)}function ct(e){return g[e]?.on===!0}function at(e){const n=D[e];if(!n)throw new Error(`lib/sections/section-to-box: "${e}" has no head row in SPLITTABLE_SECTIONS`);const r=[{kind:"text",key:n.num,role:"eyebrow"},{kind:"text",key:n.title,role:"heading"}];return n.description&&r.push({kind:"text",key:n.description,role:"body"}),n.headRows.includes("linkText")&&n.headRows.includes("linkHref")&&r.push({kind:"button",key:"linkText",href:"linkHref"}),r}const I=e=>K(e),ft=e=>I(e).text.trim()!=="",G=e=>e==="heading"?Y():e==="eyebrow"?z():{};function q(e,n,r){const d={...e._partStyles?.text??{},...n??{},...r??{}};return Object.keys(d).length?{_partStyles:{text:d}}:{}}function J(e,n){if(e===void 0)return{};if(typeof e=="string")return{variant:e};const r=n[e.key];return{variant:typeof r=="string"&&Object.hasOwn(e.map,r)?e.map[r]:e.fallback}}function A(e,n,r,p){const d={...n};for(const h of e){const[x,w,O]=typeof h=="string"?[h,h,void 0]:h;if(r[x]===void 0)continue;const $=O?O(r[x],r):r[x];$===void 0?delete d[w]:d[w]=$,p(x,w,$===void 0)}return d}function L(e,n,r={}){const p=e.props,d=n.skip?.(p);if(d!==void 0)return{node:e,carried:[],dropped:[],siteWrites:[],skipped:d};const h=String(p.id??e.type),x=[],w=[],O=new Set,$=(s,o)=>{x.push({key:s,home:o}),O.add(s)},R=p._partStyles??{};let C=0;const m=(s,o)=>({type:s,props:{...o,id:`${h}~${s}-${++C}`}}),j=(s,o,y,f,u,a,k)=>{if(u){for(const i of s??[]){const c=typeof i=="string"?i:i[0];y[c]!==void 0&&k(c,`nothing — the ${f} has no parts, so it is not drawn`)}return}const t=typeof o=="function"?o(p,r):o??{};return A(s??[],t,y,(i,c,l)=>k(i,l?`nothing — the old value draws nothing (${f}: no ${c})`:`${a}${f}: ${c}`))},P=s=>s.map(o=>{const y={...o.props??{}};for(const[f,u]of Object.entries(o.slots??{}))y[f]=P(u);return m(o.type,y)}),N=(s,o,y)=>{const f=Array.isArray(o[s.key])?o[s.key]:[],u=f.map((i,c)=>{const l=i??{},b=s.item?.(l,c,p)??{},S=B(b.reverse?[...s.parts].reverse():s.parts,l,!1,`item ${c+1}: `);return s.flat?b.props?S.map(v=>({...v,props:{...v.props,...b.props,id:v.props.id}})):S:[m(T,{name:`${s.name} ${c+1}`,...s.props??{},...b.props??{},[_]:S})]}),a=s.perRow?.(p),k=s.perRow?Number.isInteger(a)&&a>=1?a:1:0;if(y(s.key,(f.length?s.flat?`each item's parts (${f.length} items), in order`:`one Box per item (${f.length}), its parts inside`:"nothing — the list is empty")+(k&&f.length?`, in rows of ${k}`:"")),!k)return u.flat();const t=[];for(let i=0;i*k<u.length;i++)t.push(m(T,{name:`${s.rowName??`${s.name} row`} ${i+1}`,...s.rowProps??{},[_]:u.slice(i*k,(i+1)*k).flat()}));return t},B=(s,o,y,f)=>{const u=[],a=(t,i)=>y?$(t,i):void 0,k=t=>y?R[t]:void 0;for(const t of s)if(!(t.when&&!t.when(p,y?void 0:o)))switch(t.kind){case"text":{const i=I(o[t.key]);if(!i.text.trim()){a(t.key,"nothing — empty words draw nothing, so no block");break}const c=G(t.role??"body"),{_partStyles:l,...b}=c;u.push(m("Text",{text:i.text,...b,...t.props??{},...q(c,t.style,k(t.key)),...i.hidden?{_hidden:!0}:{}})),a(t.key,`${f}Text block: its words${i.hidden?", hidden":""}`);break}case"button":{const i=I(o[t.key]);if(!i.text.trim()){a(t.key,"nothing — no words, no button"),a(t.href,"nothing — no button to link"),typeof t.variant=="object"&&a(t.variant.key,"nothing — no button to style");break}const c=typeof o[t.href]=="string"&&o[t.href]?o[t.href]:"#",l={...t.style??{},...k(t.key)??{}};u.push(m("ButtonBlock",{label:i.text,href:c,...t.props??{},...J(t.variant,o),...Object.keys(l).length?{_partStyles:{label:l}}:{},...i.hidden?{_hidden:!0}:{}})),a(t.key,`${f}Button block: its words`),a(t.href,`${f}Button block: its link`),typeof t.variant=="object"&&a(t.variant.key,`${f}Button block: its Style`);break}case"image":{const i=o[t.key];if(typeof i!="string"||!i){a(t.key,"nothing — no picture, no block"),t.alt&&a(t.alt,"nothing — no picture to describe");break}const c=t.alt&&typeof o[t.alt]=="string"?o[t.alt]:"";u.push(m("ImageBlock",{src:i,alt:c,...t.props??{}})),a(t.key,`${f}Image block: its picture`),t.alt&&a(t.alt,`${f}Image block: its alt text`);break}case"list":{u.push(...N(t,o,a));break}case"group":{const i=B(t.parts,o,y,f),c=j(t.box,t.props,o,`${t.name} Box`,!i.length&&!t.keepEmpty,f,a);c&&u.push(m(T,{name:t.name,...c,[_]:i}));break}case"block":{const i=t.parts?B(t.parts,o,y,f):[],c=j(t.box,t.props,o,`${t.type} block`,!!t.parts&&!i.length&&!t.keepEmpty,f,a);c&&u.push(m(t.type,{...c,...t.slot?{[t.slot]:i}:{}}));break}case"core":{const i=t.as??e.type,c={headPlacement:"own-box",...t.props??{}};for(const l of t.keep)o[l]!==void 0&&(c[l]=o[l],a(l,`the ${i} block inside the Box`));for(const[l,b]of Object.entries(t.rename??{}))o[l]!==void 0&&(c[b]=o[l],a(l,`the ${i} block inside the Box: ${b}`));for(const[l,b]of Object.entries(t.slots??{}))c[l]=b.flatMap(S=>"kind"in S?N(S,o,a):P([S]));u.push(m(i,c));break}}return u},H=B(n.parts,p,!0,""),W=typeof n.boxProps=="function"?n.boxProps(p):n.boxProps??{},X=A(n.box??[],W,p,(s,o,y)=>$(s,y?`nothing — the old value draws nothing (Box: no ${o})`:`Box: ${o}`));p._partStyles!==void 0&&$("_partStyles","each old part's style moved to its new part");for(const[s,o]of Object.entries(n.drop??{}))s in p&&(w.push({key:s,why:o}),O.add(s));for(const s of Object.keys(p))F.has(s)||O.has(s)||w.push({key:s,why:"not in the row — not carried"});return{node:{type:T,props:{name:n.name,...X,[_]:H,id:`${h}~box`}},carried:x,dropped:w,siteWrites:[...n.site?.(e,r)??[]]}}function pt(e){const n=e.content;if(!Array.isArray(n))return e;let r=!1;const p=n.map(d=>{const h=d&&typeof d.type=="string"?g[d.type]:void 0;if(!h?.on)return d;const x=L(d,h);return x.skipped!==void 0?d:(r=!0,x.node)});return r?{...e,content:p}:e}function dt(e){if(!Array.isArray(e))return[];const n=[];return e.forEach((r,p)=>{const d=r&&typeof r.type=="string"?g[r.type]:void 0;if(!d?.on)return;const h=L(r,d);h.skipped===void 0&&n.push({index:p,node:h.node})}),n}export{_ as SECTION_BOX_SLOT,T as SECTION_BOX_TYPE,nt as boxNativeReason,L as convertSection,pt as convertSectionsInPage,ct as convertsToBox,ft as hasWords,at as headParts,ot as registerBoxNativeSections,st as registerSectionToBox,dt as sectionToBoxEdits,it as sectionToBoxRow,rt as sectionToBoxTypes};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "efficient-web-builder",
|
|
3
|
-
"version": "41.
|
|
3
|
+
"version": "41.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",
|