efficient-easel 0.100.0 → 0.101.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/dist/chrome/index.js +24 -16
- package/dist/{chunk-QKVR3K73.js → chunk-2FZJWH24.js} +1 -1
- package/dist/chunk-546BBOPE.js +1 -0
- package/dist/{chunk-LGKQXSKO.js → chunk-EOJNJCMJ.js} +8 -8
- package/dist/{chunk-A5UK76FC.js → chunk-EWVJSRRY.js} +6 -6
- package/dist/chunk-FPOKPNO4.js +1 -0
- package/dist/chunk-H24N5TMZ.js +11 -0
- package/dist/chunk-IG3U5NWJ.js +1 -0
- package/dist/chunk-KTVH2ZRL.js +15 -0
- package/dist/{chunk-6EIVTKZZ.js → chunk-L5KW4HG5.js} +21 -20
- package/dist/{chunk-TRVZ7C23.js → chunk-QTLAVDJE.js} +1 -1
- package/dist/chunk-UH7IGZKF.js +1 -0
- package/dist/{chunk-V7GIIXFB.js → chunk-ZFDCDJNZ.js} +1 -1
- package/dist/{chunk-DICUMYMG.js → chunk-ZNS7727Y.js} +1 -1
- package/dist/copy/copy.css +83 -29
- package/dist/copy/index.d.ts +41 -7
- package/dist/copy/index.js +32 -31
- package/dist/drag/index.js +4 -4
- package/dist/fields/fields.css +185 -30
- package/dist/fields/index.d.ts +165 -20
- package/dist/fields/index.js +22 -37
- package/dist/gold/index.js +1 -1
- package/dist/icons/index.d.ts +4 -1
- package/dist/icons/index.js +1 -1
- package/dist/index.js +1 -1
- package/dist/markup/index.d.ts +18 -1
- package/dist/markup/index.js +1 -1
- package/dist/menu/index.js +1 -15
- package/dist/node-ops/index.js +1 -1
- package/dist/panel/index.d.ts +49 -38
- package/dist/panel/index.js +18 -18
- package/dist/panel/panel.css +99 -81
- package/dist/pick/index.d.ts +40 -2
- package/dist/pick/index.js +1 -1
- package/dist/pick/pick.css +183 -10
- package/dist/rail/index.js +1 -1
- package/dist/strings/catalogue.d.ts +3 -3
- package/dist/strings/catalogue.js +1 -1
- package/dist/strings/index.d.ts +3 -3
- package/dist/strings/index.js +1 -1
- package/dist/style-clipboard/index.js +1 -1
- package/dist/text-styles/index.js +13 -13
- package/dist/tree/index.js +1 -1
- package/dist/{words-CJjUKcV5.d.ts → words-BGrHZAJJ.d.ts} +13 -4
- package/dist/{words-CzoyDNHm.d.ts → words-D8kf071T.d.ts} +40 -2
- package/dist/{words-DBq7vfHy.d.ts → words-XH0TvZPg.d.ts} +8 -5
- package/package.json +1 -1
- package/dist/chunk-AZT7V4UM.js +0 -10
- package/dist/chunk-HDVD27VB.js +0 -1
- package/dist/chunk-JQM3V7XQ.js +0 -1
- package/dist/chunk-LA54QLP5.js +0 -1
- package/dist/chunk-MH5GUZFQ.js +0 -9
- package/dist/chunk-SAJUCHOX.js +0 -1
package/dist/drag/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import{DEFAULT_DRAG_WORDS as e}from"../chunk-O2NLQBFX.js";import{dropEffectFor as t,scrollBoxOf as n,frameScrollBox as l,startAutoScroll as r,isNoOpDrop as a}from"../chunk-67GNT6PF.js";export{AUTO_SCROLL_EDGE,AUTO_SCROLL_MAX_SPEED,autoScrollDelta,autoScrollSpeed,createDragSession,dropEffectFor,dropOnRow,frameScrollBox,gapToMoveIndex,isNoOpDrop,scrollBoxOf,scrollParent,startAutoScroll,stepGap,useTreeDrag}from"../chunk-67GNT6PF.js";import{useCanvasLayout as o,useCanvasEvent as s}from"../chunk-PAELDYYS.js";import{framePointToHost as i,elementScale as c,frameRectToLayer as d,clientPointToLayer as u}from"../chunk-ZNCEOSXO.js";import{CloseIcon as h,SearchIcon as p}from"../chunk-
|
|
2
|
-
/* @__PURE__ */N("div",{className:["es-canvas-handle",
|
|
3
|
-
/* @__PURE__ */D("span",{className:"es-canvas-grip","aria-hidden":"true",children:"⋮⋮"}),
|
|
1
|
+
import{DEFAULT_DRAG_WORDS as e}from"../chunk-O2NLQBFX.js";import{dropEffectFor as t,scrollBoxOf as n,frameScrollBox as l,startAutoScroll as r,isNoOpDrop as a}from"../chunk-67GNT6PF.js";export{AUTO_SCROLL_EDGE,AUTO_SCROLL_MAX_SPEED,autoScrollDelta,autoScrollSpeed,createDragSession,dropEffectFor,dropOnRow,frameScrollBox,gapToMoveIndex,isNoOpDrop,scrollBoxOf,scrollParent,startAutoScroll,stepGap,useTreeDrag}from"../chunk-67GNT6PF.js";import{useCanvasLayout as o,useCanvasEvent as s}from"../chunk-PAELDYYS.js";import{framePointToHost as i,elementScale as c,frameRectToLayer as d,clientPointToLayer as u}from"../chunk-ZNCEOSXO.js";import{CloseIcon as h,SearchIcon as p}from"../chunk-L5KW4HG5.js";import{parseZoneKey as f}from"../chunk-KRXQM65X.js";import"../chunk-NKB3KVAB.js";import{useRef as m,useEffect as g,useState as b,useCallback as y,useMemo as v,useSyncExternalStore as x,useId as k,useLayoutEffect as w}from"react";import{jsxs as N,jsx as D}from"react/jsx-runtime";var A=(e,t,n)=>t>=e.left&&t<=e.left+e.width&&n>=e.top&&n<=e.top+e.height,S=e=>e.width*e.height;function T(e,t,n){let l=null;for(const r of e)A(r.box,t,n)&&(!l||S(r.box)<=S(l.box))&&(l=r);return l}function z(e,t,n){const l="x"===e.axis?t:n;let r=0;for(const t of e.children){l>("x"===e.axis?t.left+t.width/2:t.top+t.height/2)&&(r+=1)}return r}function E(e,t){const n=e.children;if("x"===e.axis){const l=n[t-1],r=n[t],a=l&&r?(l.left+l.width+r.left)/2:l?l.left+l.width:r?r.left:e.box.left,o=r??l??e.box;return{left:a-1,top:o.top,width:2,height:o.height}}const l=n[t-1],r=n[t],a=l&&r?(l.top+l.height+r.top)/2:l?l.top+l.height:r?r.top:e.box.top;return{left:e.box.left,top:a-1,width:e.box.width,height:2}}function C(e,t,n,l,r=()=>!1,a=()=>!0){const o=e.filter(e=>A(e.box,t,n)).sort((e,t)=>S(e.box)-S(t.box));for(const e of o){const o=z(e,t,n),s={...e.list,index:o};if(r(s))return null;if(l(s))return{zone:e,to:s,line:E(e,o)};if(!a(s))return null}return null}function L(e,t=e.key){const n=[];for(let l=1;l<e.children.length;l++){const r=e.children[l-1],a=e.children[l],o=r.top+r.height;n.push({key:`${t}:${l}`,place:{...e.list,index:l},box:{left:e.box.left,top:o,width:e.box.width,height:Math.max(a.top-o,0)}})}return n}var O={nodes:[],zones:[]},j="data-puck-dropzone",B="data-puck-component";function K(e,t){if(e.length>=2){const[t,n]=e;return Math.abs(n.top-t.top)<Math.min(t.height,n.height)/2&&n.left>t.left?"x":"y"}const n=t.ownerDocument.defaultView?.getComputedStyle(t);return n&&n.display.includes("flex")&&n.flexDirection.startsWith("row")?"x":"y"}function M(e,t){const{frame:n,document:l}=e,r=e=>d(n,function(e){const t=e.getBoundingClientRect();return{left:t.left,top:t.top,width:t.width,height:t.height}}(e),t),a=[],o=[];for(const e of l.querySelectorAll(`[${j}]`)){const t=e.getAttribute(j)??"";let n;try{n=f(t)}catch{continue}const l=[...e.querySelectorAll(`[${B}]`)].filter(t=>t.parentElement?.closest(`[${j}]`)===e),s=l.map(r);o.push({key:t,list:n,box:r(e),axis:K(s,e),children:s}),l.forEach((e,t)=>{const l=e.getAttribute(B);l&&a.push({id:l,place:{...n,index:t},box:s[t]})})}return{nodes:a,zones:o}}var $="data-es-empty-cell",P="data-puck-dropzone",X=140;var R=e=>({left:e.left,top:e.top,width:e.width,height:e.height});function q(d){const{canvas:h,session:p,readOnly:f,words:k=e.canvas}=d,A=m(d);g(()=>{A.current=d});const S=m(null),z=o(h),[E,L]=b(0),j=y(()=>L(e=>e+1),[]),[B,K]=b(O);g(()=>{const e=S.current;h&&e&&K((A.current.measure??M)(h,e))},[h,z,d.revision,E]);const X=d.model,q=v(()=>X(B),[X,B]),F=x(p.subscribe,p.shown,p.shown),[G,_]=b(null),[Y,Z]=b(null),I=m(null),Q=()=>{I.current&&clearTimeout(I.current),I.current=null},U=()=>{Q(),I.current=setTimeout(()=>{p.get()||_(null)},140)};g(()=>Q,[]);const V=m(null);s(h,"mousemove",e=>{const t=S.current;if(!h||!t)return;const n=i(h.frame,e.clientX,e.clientY,t),l=c(t),r={x:n.x/l,y:n.y/l};if(V.current=r,A.current.readOnly||p.get())return;Q();const a=T(q.sources,r.x,r.y);_(e=>e?.key===a?.key&&e?.box===a?.box?e:a)}),s(h,"mouseout",e=>{e.relatedTarget||(V.current=null,U())}),s(h,"mouseover",e=>{e.relatedTarget||j()});const W=m(null),H=y(()=>{W.current?.(),W.current=null,Z(null)},[]);g(()=>p.subscribe(()=>{p.get()?j():H()}),[p,H,j]),g(()=>H,[H]),("undefined"==typeof document?g:w)(()=>{if(p.get())return;const e=V.current;_(t=>f?null:e?T(q.sources,e.x,e.y):t?q.sources.find(e=>e.key===t.key)??null:null)},[q,F,p,f]);const J=e=>{const t=p.get(),n=S.current;if(!t||!n)return null;const l=u(n,e.clientX,e.clientY);V.current=l;const r=C(q.zones,l.x,l.y,e=>A.current.canDrop(t.item,e),e=>!!t.from&&a(t.from,e),e=>A.current.fallThrough?.(t.item,e,t.from)??!0);return r?{h:t,t:r}:null},ee={onDragEnter:e=>{if(W.current)return;const t=n(e.currentTarget.parentElement?.parentElement??null),a=h?l(h.document):null,o=[];t&&o.push(r(t)),a&&h&&o.push(r(a,h.frame)),W.current=o.length?()=>o.forEach(e=>e()):null},onDragOver:e=>{const n=J(e);if(!n)return void Z(null);e.preventDefault(),e.dataTransfer.dropEffect=t(e.dataTransfer.effectAllowed);const l={zoneKey:n.t.zone.key,line:n.t.line,axis:n.t.zone.axis};Z(e=>e&&e.zoneKey===l.zoneKey&&e.line.left===l.line.left&&e.line.top===l.line.top?e:l)},onDragLeave:e=>{const t=e.relatedTarget;t&&e.currentTarget.contains(t)||(V.current=null,Z(null))},onDrop:e=>{const t=J(e);t&&(e.preventDefault(),p.end(),H(),A.current.onDrop(t.h.item,t.t.to))}},te={draggable:!f&&!!G,onMouseEnter:Q,onMouseLeave:()=>!p.get()&&U(),onDragStart:e=>{if(G&&!f){p.start({key:G.key,item:G.item,from:G.from}),e.dataTransfer.effectAllowed="move";try{e.dataTransfer.setData("text/plain",G.key)}catch{}}else e.preventDefault()},onDragEnd:()=>{p.end(),H()},onClick:()=>{G&&!f&&A.current.onPick?.(G.item)}},{seams:ne=[]}=q,le=q.zones.filter(e=>0===e.children.length),re=d.onZoneAdd&&!f?le:[],ae=re.map(e=>e.key).join("\n");return g(()=>{const e=h?.document;if(!e)return;const t=new Set(ae?ae.split("\n"):[]);for(const n of e.querySelectorAll(`[${P}]`))n.toggleAttribute($,t.has(n.getAttribute(P)??""));return()=>{for(const t of e.querySelectorAll(`[${$}]`))t.removeAttribute($)}},[h,ae,B,z,E]),/* @__PURE__ */N("div",{ref:S,className:"es-canvas-layer",children:[F||!d.onSeamAdd||f?null:ne.map(e=>/* @__PURE__ */D("div",{className:"es-canvas-seam",style:{left:e.box.left,top:e.box.top+e.box.height/2-8,width:e.box.width,height:16},children:/* @__PURE__ */D("button",{type:"button",className:"es-canvas-seam-add",onClick:()=>d.onSeamAdd?.(e.place),children:k.addButton({label:d.seamLabel??k.addSection})})},e.key)),re.length?re.map(e=>/* @__PURE__ */D("button",{type:"button",className:Y?.zoneKey===e.key?"es-canvas-empty is-over":"es-canvas-empty",style:R(e.box),onClick:()=>d.onZoneAdd?.({...e.list,index:0}),children:/* @__PURE__ */D("span",{className:"es-canvas-empty-add",children:k.addButton({label:d.zoneAddLabel??k.addBlock})})},e.key)):null,F?/* @__PURE__ */D("div",{className:"es-canvas-catch",...ee}):null,Y&&!le.some(e=>e.key===Y.zoneKey)?/* @__PURE__ */D("div",{className:`es-canvas-line is-${Y.axis}`,style:R(Y.line)}):null,
|
|
2
|
+
/* @__PURE__ */N("div",{className:["es-canvas-handle",G&&!f?"is-shown":"",F?"is-dragging":""].filter(Boolean).join(" "),style:G?{left:G.box.left+2,top:Math.max(G.box.top-11,0)}:void 0,title:G?G.title??k.handle({label:G.label}):void 0,...te,children:[
|
|
3
|
+
/* @__PURE__ */D("span",{className:"es-canvas-grip","aria-hidden":"true",children:"⋮⋮"}),G?.label]})]})}var F=e=>e.tiles.length>0&&e.tiles.every(e=>null!=e.cover);function G({groups:t,session:n,itemFor:l,onInsert:r,at:a,hint:o,searchLabel:s,readOnly:i,words:c=e.add}){const d=o??c.hint,u=s??c.search,[f,m]=b(""),g=k(),y=f.trim().toLowerCase(),v=t.map(e=>({...e,tiles:y?e.tiles.filter(e=>((e,t)=>e.label.toLowerCase().includes(t)||(e.keywords??[]).some(e=>e.toLowerCase().includes(t)))(e,y)):e.tiles})).filter(e=>e.tiles.length>0),x=(e,t)=>r=>{if(t)r.preventDefault();else{n.start({key:`new:${e}`,item:l(e)}),r.dataTransfer.effectAllowed="copy";try{r.dataTransfer.setData("text/plain",e)}catch{}}};/* @__PURE__ */
|
|
4
4
|
return N("div",{className:"es-add",children:[a?/* @__PURE__ */N("div",{className:"es-add-at",role:"status",children:[
|
|
5
5
|
/* @__PURE__ */D("span",{children:a.label}),
|
|
6
6
|
/* @__PURE__ */D("button",{type:"button",className:"es-add-at-clear","aria-label":c.stop,title:c.stop,onClick:a.onClear,children:/* @__PURE__ */D(h,{size:12,"aria-hidden":"true"})})]}):null,
|
|
@@ -15,4 +15,4 @@ return N("div",{className:"es-add",children:[a?/* @__PURE__ */N("div",{className
|
|
|
15
15
|
/* @__PURE__ */D("span",{className:"es-add-group-count",children:e.tiles.length})]}),
|
|
16
16
|
/* @__PURE__ */D("div",{className:F(e)?"es-add-grid is-cards":"es-add-grid",children:e.tiles.map(e=>{const t=Boolean(i||e.disabled),l=i?void 0:e.disabled?e.disabledReason:e.title??c.tile({label:e.label});/* @__PURE__ */
|
|
17
17
|
return N("button",{type:"button",className:null!=e.cover?"es-add-tile has-cover":"es-add-tile",draggable:!t,"aria-disabled":t||void 0,"aria-label":c.tile({label:e.label}),title:l,onDragStart:x(e.type,t),onDragEnd:()=>n.end(),onClick:()=>!t&&r(e.type),children:[null!=e.cover?/* @__PURE__ */D("span",{className:"es-add-tile-cover","aria-hidden":"true",children:e.cover}):e.icon?/* @__PURE__ */D("span",{className:"es-add-tile-glyph","aria-hidden":"true",children:e.icon}):null,
|
|
18
|
-
/* @__PURE__ */D("span",{className:"es-add-tile-label",children:e.label})]},e.type)})})]},e.title))]})}export{
|
|
18
|
+
/* @__PURE__ */D("span",{className:"es-add-tile-label",children:e.label})]},e.type)})})]},e.title))]})}export{G as AddPanel,q as CanvasDragLayer,O as EMPTY_MEASURE,X as HANDLE_GRACE_MS,C as canvasTarget,A as contains,z as gapInZone,E as gapLine,M as measureCanvas,T as pickSource,L as seamsBetween};
|
package/dist/fields/fields.css
CHANGED
|
@@ -349,9 +349,13 @@
|
|
|
349
349
|
.es-insp-row:empty,
|
|
350
350
|
.es-insp-row[hidden] { display: none; }
|
|
351
351
|
|
|
352
|
-
/* ── a list (ListField
|
|
352
|
+
/* ── a list (ListField): ONE look in both editors (owner, 2026-10-06) ────────
|
|
353
|
+
The website ticker's rows: one row per item, a × at its end, "+ Add <noun>"
|
|
354
|
+
under the last. The whole row drags (no grip); a many-field item opens under
|
|
355
|
+
its own row. Sizes from the ticker (.sf-textlist): a 32px row, a 28 × 32 ×.
|
|
356
|
+
Gaps are the spacing scale: 8 between rows (the seam IS the gap), 4 inside. */
|
|
353
357
|
|
|
354
|
-
.es-list { display: flex; flex-direction: column; gap: 8px; min-width: 0; margin-top: max(var(--es-row-gap), var(--es-rhythm-between)); }
|
|
358
|
+
.es-list { position: relative; display: flex; flex-direction: column; gap: 8px; min-width: 0; margin-top: max(var(--es-row-gap), var(--es-rhythm-between)); }
|
|
355
359
|
.es-list-head {
|
|
356
360
|
display: flex;
|
|
357
361
|
align-items: baseline;
|
|
@@ -359,40 +363,191 @@
|
|
|
359
363
|
gap: 8px;
|
|
360
364
|
}
|
|
361
365
|
.es-list-label { display: inline-flex; align-items: center; gap: 8px; font-size: 12px; font-weight: 600; color: var(--es-text); }
|
|
362
|
-
.es-list-count { font-size: 11px; color: var(--es-muted); }
|
|
363
|
-
/* The rows are Layers' rows (tree.css); the list lets them reach the panel's
|
|
364
|
-
edges the way Layers' do, so a row's hover reads as the same row. */
|
|
365
|
-
.es-list-tree { margin: 0 -8px; }
|
|
366
|
-
/* …but the list is NOT a scroll box, as Layers is (`.es-tree { overflow-y:
|
|
367
|
-
auto }`): the panel around it scrolls. A row's drag hint hangs 4px under the
|
|
368
|
-
row, 22px tall, and under the LAST row the tree has less room than that: it
|
|
369
|
-
clipped the hint to 8px (second host, 2026-10-07). What overflowed also
|
|
370
|
-
made the list a scroller of its own inside the panel: a wheel over it moved
|
|
371
|
-
the rows 14px, and it was the nearest box that scrolled, so a drag in the
|
|
372
|
-
list scrolled IT at its edges, not the panel. Measured in Chrome
|
|
373
|
-
(scripts/second-host-clickthrough.mjs). Two classes, so it wins whichever
|
|
374
|
-
sheet loads last. */
|
|
375
|
-
.es-tree.es-list-tree { overflow: visible; }
|
|
366
|
+
.es-list-count { font-size: 11px; color: var(--es-muted); font-variant-numeric: tabular-nums; }
|
|
376
367
|
|
|
377
|
-
.
|
|
368
|
+
/* A host's bar for the row in use, between the name and the rows. */
|
|
369
|
+
.es-list-above { min-width: 0; }
|
|
370
|
+
|
|
371
|
+
/* The rows: a column with no gap of its own, because the seams between rows
|
|
372
|
+
are the gap (tree.css `.es-tree-seam`, one rule for both). NOT a scroll box,
|
|
373
|
+
and not Layers' `.es-tree` (which is one): the panel around the list scrolls.
|
|
374
|
+
Inside a scroll box of its own the drag hint under the last row was clipped
|
|
375
|
+
and the list scrolled by itself under a wheel (second host, 2026-10-07). */
|
|
376
|
+
.es-list-rows { display: flex; flex-direction: column; min-width: 0; --es-rhythm-between: 8px; }
|
|
377
|
+
.es-list-rows > .es-tree-seam:last-child { margin-top: -8px; }
|
|
378
|
+
.es-list-entry { display: flex; flex-direction: column; min-width: 0; }
|
|
379
|
+
|
|
380
|
+
.es-list-row {
|
|
381
|
+
position: relative;
|
|
382
|
+
display: flex;
|
|
383
|
+
align-items: center;
|
|
384
|
+
gap: 4px;
|
|
385
|
+
min-width: 0;
|
|
386
|
+
min-height: 32px;
|
|
387
|
+
border-radius: var(--es-row-radius);
|
|
388
|
+
--es-row-scrim: var(--es-panel);
|
|
389
|
+
}
|
|
390
|
+
.es-list-row.is-draggable { cursor: grab; }
|
|
391
|
+
.es-list-row.is-item { padding-left: 8px; }
|
|
392
|
+
/* A row whose own control is not a text box (a name, a picture) reads as a row. */
|
|
393
|
+
.es-list-row.is-inline:not(:has(input, textarea, [contenteditable])) { padding-left: 8px; }
|
|
394
|
+
.es-list-row.is-inline:not(:has(input, textarea, [contenteditable])):hover { --es-row-scrim: var(--es-hover); background: var(--es-row-scrim); }
|
|
395
|
+
.es-list-row.is-item:hover,
|
|
396
|
+
.es-list-row.is-open { --es-row-scrim: var(--es-hover); background: var(--es-row-scrim); }
|
|
397
|
+
.es-list-row.is-dragging { opacity: 0.4; }
|
|
398
|
+
.es-list-row.is-lifting { background: var(--es-raised); box-shadow: 0 8px 20px rgba(16, 24, 40, 0.16), 0 2px 6px rgba(16, 24, 40, 0.1); }
|
|
399
|
+
.es-list-row:focus-visible { outline: 2px solid var(--es-accent); outline-offset: 1px; }
|
|
400
|
+
/* The one drop line, in the middle of the 8px gap — where a seam draws its own. */
|
|
401
|
+
.es-list-row.is-drop-before::before,
|
|
402
|
+
.es-list-row.is-drop-after::after {
|
|
403
|
+
content: "";
|
|
404
|
+
position: absolute;
|
|
405
|
+
left: 8px;
|
|
406
|
+
right: 8px;
|
|
407
|
+
height: 2px;
|
|
408
|
+
border-radius: 2px;
|
|
409
|
+
background: var(--es-accent);
|
|
410
|
+
pointer-events: none;
|
|
411
|
+
}
|
|
412
|
+
.es-list-row.is-drop-before::before { top: -5px; }
|
|
413
|
+
.es-list-row.is-drop-after::after { bottom: -5px; }
|
|
414
|
+
|
|
415
|
+
/* The row's own control (renderRow): it fills the row up to the ×. */
|
|
416
|
+
.es-list-row-main { flex: 1 1 auto; min-width: 0; min-height: 32px; display: flex; align-items: center; font-size: 12px; color: var(--es-text); }
|
|
417
|
+
.es-list-row-main > * { flex: 1 1 auto; min-width: 0; }
|
|
418
|
+
|
|
419
|
+
/* A many-field item: its name, its summary, a chevron that turns when open. */
|
|
420
|
+
.es-list-open {
|
|
421
|
+
flex: 1 1 auto;
|
|
422
|
+
min-width: 0;
|
|
423
|
+
display: flex;
|
|
424
|
+
align-items: center;
|
|
425
|
+
gap: 4px;
|
|
378
426
|
height: 32px;
|
|
379
|
-
|
|
380
|
-
border
|
|
427
|
+
padding: 0;
|
|
428
|
+
border: 0;
|
|
381
429
|
background: none;
|
|
382
|
-
color: var(--es-text
|
|
430
|
+
color: var(--es-text);
|
|
383
431
|
font: inherit;
|
|
384
432
|
font-size: 12px;
|
|
433
|
+
text-align: left;
|
|
434
|
+
cursor: inherit;
|
|
435
|
+
}
|
|
436
|
+
.es-list-open:focus-visible { outline: 2px solid var(--es-accent); outline-offset: 1px; border-radius: 4px; }
|
|
437
|
+
.es-list-thumb { flex: none; display: inline-flex; align-items: center; }
|
|
438
|
+
.es-list-row-name { flex: none; font-weight: 600; white-space: nowrap; }
|
|
439
|
+
.es-list-row-summary { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--es-muted); }
|
|
440
|
+
.es-list-chev { flex: none; color: var(--es-icon); transition: transform 0.12s; }
|
|
441
|
+
.es-list-row.is-open .es-list-chev { transform: rotate(90deg); }
|
|
442
|
+
|
|
443
|
+
.es-list-x {
|
|
444
|
+
flex: none;
|
|
445
|
+
width: 28px;
|
|
446
|
+
height: 32px;
|
|
447
|
+
border: 0;
|
|
448
|
+
border-radius: var(--es-row-radius);
|
|
449
|
+
background: transparent;
|
|
450
|
+
color: var(--es-muted);
|
|
451
|
+
font: inherit;
|
|
452
|
+
font-size: 16px;
|
|
453
|
+
line-height: 1;
|
|
385
454
|
cursor: pointer;
|
|
386
455
|
}
|
|
387
|
-
.es-list-
|
|
456
|
+
.es-list-x:hover:not(:disabled) { background: var(--es-danger-tint); color: var(--es-danger); }
|
|
457
|
+
.es-list-x:disabled { color: var(--es-disabled); cursor: default; }
|
|
458
|
+
.es-list-x:focus-visible { outline: 2px solid var(--es-accent); outline-offset: 2px; }
|
|
459
|
+
.es-list-lock {
|
|
460
|
+
flex: none;
|
|
461
|
+
width: 28px;
|
|
462
|
+
height: 32px;
|
|
463
|
+
display: inline-flex;
|
|
464
|
+
align-items: center;
|
|
465
|
+
justify-content: center;
|
|
466
|
+
color: var(--es-disabled);
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
/* What a draggable row says after 0.7 s on it — Layers' hint, never over a text box. */
|
|
470
|
+
.es-list-hint {
|
|
471
|
+
position: absolute;
|
|
472
|
+
left: 8px;
|
|
473
|
+
top: calc(100% + 4px);
|
|
474
|
+
z-index: 5;
|
|
475
|
+
padding: 4px 8px;
|
|
476
|
+
border-radius: 6px;
|
|
477
|
+
background: var(--es-toast-bg);
|
|
478
|
+
color: var(--es-on-accent);
|
|
479
|
+
font-size: 11px;
|
|
480
|
+
line-height: 1.3;
|
|
481
|
+
white-space: nowrap;
|
|
482
|
+
pointer-events: none;
|
|
483
|
+
opacity: 0;
|
|
484
|
+
transition: opacity 0.1s;
|
|
485
|
+
}
|
|
486
|
+
.es-list-row.is-draggable:hover:not(:has(input:hover, textarea:hover, [contenteditable]:hover)) .es-list-hint {
|
|
487
|
+
opacity: 1;
|
|
488
|
+
transition-delay: 0.7s;
|
|
489
|
+
}
|
|
490
|
+
.es-list-row.is-dragging .es-list-hint,
|
|
491
|
+
.es-list-row.is-lifting .es-list-hint { display: none; }
|
|
492
|
+
|
|
493
|
+
/* The open item, under its row: its own settings, indented on a hairline. */
|
|
494
|
+
.es-list-item {
|
|
495
|
+
display: flex;
|
|
496
|
+
flex-direction: column;
|
|
497
|
+
min-width: 0;
|
|
498
|
+
margin: 4px 0 8px 8px;
|
|
499
|
+
padding: 0 8px 4px 12px;
|
|
500
|
+
border-left: 2px solid var(--es-line);
|
|
501
|
+
}
|
|
502
|
+
.es-list-item > :first-child { margin-top: 0; }
|
|
503
|
+
|
|
504
|
+
.es-list-add {
|
|
505
|
+
align-self: flex-start;
|
|
506
|
+
padding: 4px 2px;
|
|
507
|
+
border: 0;
|
|
508
|
+
background: none;
|
|
509
|
+
color: var(--es-accent);
|
|
510
|
+
font: inherit;
|
|
511
|
+
font-size: 12px;
|
|
512
|
+
font-weight: 500;
|
|
513
|
+
cursor: pointer;
|
|
514
|
+
}
|
|
515
|
+
.es-list-add:hover:not(:disabled) { text-decoration: underline; }
|
|
388
516
|
.es-list-add:disabled { color: var(--es-disabled); cursor: default; }
|
|
389
|
-
.es-list-add:focus-visible { outline: 2px solid var(--es-accent); outline-offset:
|
|
390
|
-
|
|
391
|
-
/*
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
.es-list-title { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
|
|
395
|
-
.es-list-name { font-size: 14px; font-weight: 600; color: var(--es-text); }
|
|
396
|
-
.es-list-item { min-width: 0; }
|
|
397
|
-
.es-list-item-actions { display: flex; gap: 8px; margin-top: 8px; }
|
|
517
|
+
.es-list-add:focus-visible { outline: 2px solid var(--es-accent); outline-offset: 2px; }
|
|
518
|
+
|
|
519
|
+
/* Read out after a move; never seen. */
|
|
520
|
+
.es-list-said { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
|
|
521
|
+
|
|
398
522
|
.es-fbtn--danger { color: var(--es-danger); }
|
|
523
|
+
|
|
524
|
+
/* ── the link picker (LinkPicker, [MERGE] One Link Picker For Both Editors) ──
|
|
525
|
+
The mockup's stack (owner, 2026-10-06): Link type, the target, and on the
|
|
526
|
+
listing Filter and Value — one box under the next, the whole width of the
|
|
527
|
+
row, 8px apart (the spacing scale). The same stack at 320px and at 220px:
|
|
528
|
+
nothing beside anything, so nothing is cut off mid-word in the narrow
|
|
529
|
+
panel. Every box is `FieldSelect` or `.es-fin`; this adds only the stack and
|
|
530
|
+
the two quiet lines under it. */
|
|
531
|
+
.es-flink {
|
|
532
|
+
display: flex;
|
|
533
|
+
flex-direction: column;
|
|
534
|
+
gap: 8px;
|
|
535
|
+
min-width: 0;
|
|
536
|
+
}
|
|
537
|
+
/* Inside a SettingsField the slot is a flex ROW, and a column box written for
|
|
538
|
+
a block row sizes to its own content there (291px became 117px in the shop
|
|
539
|
+
when its link stack sat in the slot). Fill the slot instead. */
|
|
540
|
+
.es-field-ctl > .es-flink {
|
|
541
|
+
flex: 1 1 auto;
|
|
542
|
+
}
|
|
543
|
+
.es-flink > .es-fin[aria-invalid='true'] {
|
|
544
|
+
border-color: var(--es-danger);
|
|
545
|
+
}
|
|
546
|
+
.es-flink-error,
|
|
547
|
+
.es-flink-hint {
|
|
548
|
+
margin: 0;
|
|
549
|
+
font-size: 11px;
|
|
550
|
+
line-height: 1.45;
|
|
551
|
+
}
|
|
552
|
+
.es-flink-error { color: var(--es-danger); }
|
|
553
|
+
.es-flink-hint { color: var(--es-muted); }
|
package/dist/fields/index.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { S as SettingsBucket } from '../buckets-CuLM47h9.js';
|
|
|
4
4
|
import { I as InspectorFields, a as InspectorFieldDef, F as FieldRenderer, b as FieldTextProps, c as FieldNumberProps, d as FieldSliderProps, e as FieldSelectManyProps, f as FieldSelectOneProps, g as FieldOption } from '../types-VqL6t5HH.js';
|
|
5
5
|
export { A as ArrayFieldDef, C as CustomFieldDef, h as CustomFieldRenderProps, i as FieldSelectProps, N as NumberFieldDef, O as ObjectFieldDef, R as RadioFieldDef, S as SelectFieldDef, j as SlotFieldDef, T as TextFieldDef, k as TextareaFieldDef } from '../types-VqL6t5HH.js';
|
|
6
6
|
import { R as ResolvableItem } from '../resolve-D68pb3Bx.js';
|
|
7
|
-
export { F as FieldsWords } from '../words-
|
|
7
|
+
export { F as FieldsWords } from '../words-D8kf071T.js';
|
|
8
8
|
import '../types-41704JE4.js';
|
|
9
9
|
|
|
10
10
|
interface InspectorProps {
|
|
@@ -178,26 +178,37 @@ declare function useResolvedFields({ id, node, block, parent, appState }: Resolv
|
|
|
178
178
|
};
|
|
179
179
|
|
|
180
180
|
/**
|
|
181
|
-
* A LIST VALUE
|
|
182
|
-
*
|
|
183
|
-
*
|
|
184
|
-
* two rows that adds one there. Pressing a row opens that item in the panel,
|
|
185
|
-
* with a way back at the top.
|
|
181
|
+
* A LIST VALUE — slides, buttons, links, lines of copy, any `array` field —
|
|
182
|
+
* drawn ONE way in both editors (owner, 2026-10-06: "the ticker look"). One row
|
|
183
|
+
* per item with a × at its end, and "+ Add <noun>" under the last row.
|
|
186
184
|
*
|
|
187
|
-
*
|
|
188
|
-
*
|
|
189
|
-
*
|
|
190
|
-
*
|
|
191
|
-
*
|
|
185
|
+
* one field `renderRow` puts the host's own control IN the row (the
|
|
186
|
+
* ticker's text box): nothing to open, the item is edited where
|
|
187
|
+
* it stands;
|
|
188
|
+
* many fields the row is the item's name and summary; pressing it opens the
|
|
189
|
+
* item IN PLACE, under its row, one at a time (the owner's pick
|
|
190
|
+
* A of the 2026-10-06 mockup — the drill-in page is gone).
|
|
192
191
|
*
|
|
193
|
-
*
|
|
194
|
-
*
|
|
192
|
+
* REORDER IS NEVER CUT TO FIT THE LOOK. The WHOLE ROW drags — no grip (owner,
|
|
193
|
+
* 2026-09-29) — a text box in it too, until the caret is in that box: from
|
|
194
|
+
* then on a press there selects text. Alt+↑ /
|
|
195
|
+
* Alt+↓ move it from the keyboard, the caret in the text box included, and the
|
|
196
|
+
* caret goes with the item. The right-click menu (or Shift+F10) offers Move up,
|
|
197
|
+
* Move down, Duplicate and Delete, and only what the row can do. The count, the
|
|
198
|
+
* "+" between two rows and Duplicate stay, as Layers keeps them.
|
|
195
199
|
*
|
|
196
|
-
*
|
|
197
|
-
*
|
|
200
|
+
* THE DATA DOES NOT CHANGE: a list is the array the document stored, and every
|
|
201
|
+
* write is that array with an item added, moved, copied, changed or taken out.
|
|
202
|
+
* No id is added to an item; a row's key is its position.
|
|
203
|
+
*
|
|
204
|
+
* CONTROLLED, the list and which item is open: the Inspector holds them, so an
|
|
205
|
+
* editor can keep the open item across a re-render of the panel.
|
|
206
|
+
*
|
|
207
|
+
* Its words are the strings provider's — `EaselStrings.fields.list` and
|
|
208
|
+
* `.tree` — English with none.
|
|
198
209
|
*/
|
|
199
210
|
interface ListFieldProps<T> {
|
|
200
|
-
/** The list's name, e.g. "Slides".
|
|
211
|
+
/** The list's name, e.g. "Slides". */
|
|
201
212
|
label: string;
|
|
202
213
|
/** A glyph before the name, as a settings row wears one. */
|
|
203
214
|
icon?: ReactNode;
|
|
@@ -216,11 +227,37 @@ interface ListFieldProps<T> {
|
|
|
216
227
|
min?: number;
|
|
217
228
|
max?: number;
|
|
218
229
|
readOnly?: boolean;
|
|
219
|
-
/** The item open
|
|
230
|
+
/** The item open under its row, by position, or null. */
|
|
220
231
|
open: number | null;
|
|
221
232
|
onOpen: (index: number | null) => void;
|
|
222
|
-
/** The open item's own settings. `change` writes the item. */
|
|
233
|
+
/** The open item's own settings, drawn under its row. `change` writes the item. */
|
|
223
234
|
renderItem: (item: T, index: number, change: (next: T) => void) => ReactNode;
|
|
235
|
+
/**
|
|
236
|
+
* The row's own control, for an item that is ONE value (a line of copy): the
|
|
237
|
+
* row holds it, and nothing opens. `change` writes the item.
|
|
238
|
+
*/
|
|
239
|
+
renderRow?: (item: T, index: number, change: (next: T) => void) => ReactNode;
|
|
240
|
+
/** May this item be taken out? False: no ×, a lock, no Delete — it still moves (the email's Unsubscribe). */
|
|
241
|
+
canRemove?: (item: T, index: number) => boolean;
|
|
242
|
+
/** Why a locked item cannot be taken out — the lock's tooltip. Default `fields.list.locked`. */
|
|
243
|
+
lockedReason?: (item: T, index: number) => string;
|
|
244
|
+
/** A set that is never added to or taken from (the phone column order): it only moves. */
|
|
245
|
+
fixed?: boolean;
|
|
246
|
+
/**
|
|
247
|
+
* False: an item is deleted INSIDE (its own page, as a Locations group is),
|
|
248
|
+
* never from its row — no ×, no lock, no Delete in the menu. Default true.
|
|
249
|
+
*/
|
|
250
|
+
removeOnRow?: boolean;
|
|
251
|
+
/** May this item be copied? False: no Duplicate in the menu (a copy that would merge with its original). Default: yes. */
|
|
252
|
+
canDuplicate?: (item: T, index: number) => boolean;
|
|
253
|
+
/** Drawn once, under the list's name and above its rows — a bar that acts on the row in use (the ticker's formatting bar). */
|
|
254
|
+
above?: ReactNode;
|
|
255
|
+
/**
|
|
256
|
+
* False: no head of the list's own — the host draws the list inside a
|
|
257
|
+
* settings row that already names it (a `SettingsField` with its glyph and
|
|
258
|
+
* hint), and shows the count in that row's `value`. Default true.
|
|
259
|
+
*/
|
|
260
|
+
head?: boolean;
|
|
224
261
|
}
|
|
225
262
|
/** The list with `item` put at `index`. */
|
|
226
263
|
declare function insertAt<T>(items: readonly T[], index: number, item: T): T[];
|
|
@@ -233,7 +270,7 @@ declare function removeAt<T>(items: readonly T[], index: number): T[];
|
|
|
233
270
|
* writes through to the original (a list item is JSON — Puck stored it so).
|
|
234
271
|
*/
|
|
235
272
|
declare function copyItem<T>(item: T): T;
|
|
236
|
-
declare function ListField<T>({ label, icon, noun: ownNoun, items, onChange, newItem, summary, itemName, thumb, min, max, readOnly, open, onOpen, renderItem, }: ListFieldProps<T>): react.JSX.Element;
|
|
273
|
+
declare function ListField<T>({ label, icon, noun: ownNoun, items, onChange, newItem, summary, itemName, thumb, min, max, readOnly, open, onOpen, renderItem, renderRow, canRemove: removable, lockedReason, fixed, removeOnRow, canDuplicate, above, head, }: ListFieldProps<T>): react.JSX.Element;
|
|
237
274
|
|
|
238
275
|
/**
|
|
239
276
|
* A text value: the shell's own box, one line or several (blueprint step 4.1,
|
|
@@ -356,4 +393,112 @@ declare function optionFor<V>(options: readonly FieldOption<V>[], value: V | und
|
|
|
356
393
|
declare function FieldSelect<V>(props: FieldSelectManyProps<V>): ReactNode;
|
|
357
394
|
declare function FieldSelect<V>(props: FieldSelectOneProps<V>): ReactNode;
|
|
358
395
|
|
|
359
|
-
|
|
396
|
+
/** One thing a link can point at: the URL it writes and the name it shows. */
|
|
397
|
+
type LinkOption = {
|
|
398
|
+
value: string;
|
|
399
|
+
label: string;
|
|
400
|
+
};
|
|
401
|
+
/** Options under one heading — the anchors of one page. */
|
|
402
|
+
type LinkOptionGroup = {
|
|
403
|
+
label: string;
|
|
404
|
+
options: readonly LinkOption[];
|
|
405
|
+
};
|
|
406
|
+
/** One filter on a link onto the product listing, or none. */
|
|
407
|
+
type LinkFilter = {
|
|
408
|
+
param: string;
|
|
409
|
+
value: string;
|
|
410
|
+
} | null;
|
|
411
|
+
/** A filter a listing link may offer, with the values it may take. */
|
|
412
|
+
type LinkFilterField = {
|
|
413
|
+
param: string;
|
|
414
|
+
label: string;
|
|
415
|
+
values: readonly LinkOption[];
|
|
416
|
+
};
|
|
417
|
+
/** What a kind's target box is: a list, a list in groups, or a typed address. */
|
|
418
|
+
type LinkTarget = {
|
|
419
|
+
type: 'select';
|
|
420
|
+
options: readonly LinkOption[];
|
|
421
|
+
placeholder: string;
|
|
422
|
+
} | {
|
|
423
|
+
type: 'groups';
|
|
424
|
+
groups: readonly LinkOptionGroup[];
|
|
425
|
+
placeholder: string;
|
|
426
|
+
} | {
|
|
427
|
+
type: 'text';
|
|
428
|
+
};
|
|
429
|
+
/** One entry of the Link type list, with everything its target box offers. */
|
|
430
|
+
type LinkKindChoice = {
|
|
431
|
+
/** The kind's key — what `rules.kindOf` answers. */
|
|
432
|
+
value: string;
|
|
433
|
+
/** Its name in the list ("Page", "Blog post"), and its target box's name. */
|
|
434
|
+
label: string;
|
|
435
|
+
target: LinkTarget;
|
|
436
|
+
/** A quiet line under the boxes while this kind is chosen. */
|
|
437
|
+
hint?: string;
|
|
438
|
+
/** Left out of the list while its target has nothing to pick — unless the
|
|
439
|
+
* saved link is this kind, so a link is never shown as something it is not. */
|
|
440
|
+
hideWhenEmpty?: boolean;
|
|
441
|
+
};
|
|
442
|
+
/**
|
|
443
|
+
* Everything the picker needs to know about a URL — and it knows nothing else.
|
|
444
|
+
* A host hands in its own rules (the storefront's come from
|
|
445
|
+
* `efficient-web-builder/links`), so a new kind or a new filter never changes
|
|
446
|
+
* this file.
|
|
447
|
+
*/
|
|
448
|
+
type LinkRules = {
|
|
449
|
+
/** Which kind a saved URL is. */
|
|
450
|
+
kindOf: (url: string) => string;
|
|
451
|
+
/** The target a URL points at, with any listing filter taken off. */
|
|
452
|
+
targetOf: (url: string) => string;
|
|
453
|
+
/** The filter a URL carries onto the listing, or null. */
|
|
454
|
+
readFilter: (url: string) => LinkFilter;
|
|
455
|
+
/** The URL of a target with this filter written on (or cleared). */
|
|
456
|
+
withFilter: (url: string, filter: LinkFilter) => string;
|
|
457
|
+
/** May this typed address be saved? */
|
|
458
|
+
isSafe: (url: string) => boolean;
|
|
459
|
+
};
|
|
460
|
+
type LinkPickerProps = {
|
|
461
|
+
/** The saved link, or `''`. */
|
|
462
|
+
value: string;
|
|
463
|
+
/**
|
|
464
|
+
* The new link, and — when a thing was PICKED — its name, so a host can fill
|
|
465
|
+
* its own label with it (the admin's menu item does). No name when the link
|
|
466
|
+
* was typed or cleared.
|
|
467
|
+
*/
|
|
468
|
+
onChange: (url: string, name?: string) => void;
|
|
469
|
+
/** The Link type list, in order, each with what its target box offers. */
|
|
470
|
+
kinds: readonly LinkKindChoice[];
|
|
471
|
+
rules: LinkRules;
|
|
472
|
+
/**
|
|
473
|
+
* The filters a link onto this target may carry, or null when the target is
|
|
474
|
+
* not the product listing. Asked only for a kind whose target is a list.
|
|
475
|
+
*/
|
|
476
|
+
filtersFor?: (target: string) => readonly LinkFilterField[] | null;
|
|
477
|
+
/** The whole control's accessible name — "Button link", "Where “Wheels” goes". */
|
|
478
|
+
label?: string;
|
|
479
|
+
readOnly?: boolean;
|
|
480
|
+
};
|
|
481
|
+
/**
|
|
482
|
+
* ONE LINK PICKER ([MERGE] One Link Picker For Both Editors, owner
|
|
483
|
+
* 2026-10-06; mockup https://claude.ai/artifact/6mVmY5787gnXdSWP3syqhu v4): a
|
|
484
|
+
* type — Page / Anchor / Blog post / Product / Custom URL — then which one; on
|
|
485
|
+
* the product listing a Filter and then a Value. The website editor's Link
|
|
486
|
+
* field and the admin's menu item draw this same control.
|
|
487
|
+
*
|
|
488
|
+
* IT HOLDS NO URL RULE. Which kind a link is, which page it points at, what
|
|
489
|
+
* filter it carries and how one is written, which typed address is unsafe —
|
|
490
|
+
* all of it is `rules`, and every option and every name is the host's. Only
|
|
491
|
+
* the order of the boxes and how they answer each other is decided here.
|
|
492
|
+
*
|
|
493
|
+
* It never writes on its own. Opening a saved link writes nothing; a target,
|
|
494
|
+
* filter or value the host no longer offers is shown as "(unavailable)" and
|
|
495
|
+
* kept, never silently replaced.
|
|
496
|
+
*
|
|
497
|
+
* Every list is `FieldSelect`, the editor's one pop-up list — the anchors
|
|
498
|
+
* under one heading per page (`FieldOption.group`), a long list with a search
|
|
499
|
+
* box — never the browser's `<select>`. The boxes stack, one under the next,
|
|
500
|
+
* at every panel width.
|
|
501
|
+
*/
|
|
502
|
+
declare function LinkPicker({ value, onChange, kinds, rules, filtersFor, label, readOnly }: LinkPickerProps): react.JSX.Element | null;
|
|
503
|
+
|
|
504
|
+
export { FieldNumber, FieldNumberProps, FieldOption, type FieldPlan, FieldRenderer, FieldSelect, FieldSelectManyProps, FieldSelectOneProps, FieldSlider, FieldSliderProps, FieldText, FieldTextProps, type FieldsBlock, Inspector, InspectorField, InspectorFieldDef, type InspectorFieldProps, InspectorFields, type InspectorProps, type LinkFilter, type LinkFilterField, type LinkKindChoice, type LinkOption, type LinkOptionGroup, LinkPicker, type LinkPickerProps, type LinkRules, type LinkTarget, ListField, type ListFieldProps, type NumberEntryOptions, type PlannedRow, type ResolvedFieldsInput, SettingsBucket, bucketsOf, clampNumber, copyItem, insertAt, labelOf, moveTo, optionFor, parseTyped, planFields, removeAt, useNumberEntry, useResolvedFields };
|