Composition / foldkit-mixins-builder
Styling
BuilderSlots names every element; attach a Style as to any SlotView:
import { Style } from 'foldkit-mixins'
import { BuilderSlots, BuilderView } from 'foldkit-mixins-builder'
const PageEditing = BuilderView.define(PageBuilder).pipe(
Style.attach(
Style.forSlots(BuilderSlots)({
root: Style.class('builder'),
row: Style.class('builder-row'),
canvas: Style.class('builder-canvas'),
}),
),
)define draws the panels in four regions, so a layout places four things,
and a new panel lands in its region without moving the rest:
root the container `builder`
├── panelTabs a narrow editor's tabs (see below)
├── regions
│ ├── start palette, layers
│ ├── bar toolbar, crumbs, viewports, preview
│ ├── stage alert, canvas
│ └── end inspector
└── liveroot is a container (container: builder / inline-size, the Builder's
default Style), so the regions may be laid out by the editor's own width,
@container builder (max-width: 64rem), rather than the window's: an editor
in a narrow column stacks in a wide window. The example lays regions out as
a grid of start, bar over stage, and end. A layout of your own
(assemble) draws none of these. An inline-size container takes no width from
what it holds, so give the editor one: as a flex item that does not grow, an
auto grid column, inline-block or fit-content, it is 0 wide.
A node's props are drawn by its Block's settings form, a foldkit-mixins-form
view the Builder makes. Give its Styles to define (or parts), and they are
attached to every Block's settings form:
import { FieldSlots, FormSlots } from 'foldkit-mixins-form'
const PageEditing = BuilderView.define(PageBuilder, {
settings: {
field: Style.attach(Style.forSlots(FieldSlots)({ label: Style.class('setting-label') })),
form: Style.attach(Style.forSlots(FormSlots)({ root: Style.class('settings') })),
},
})A control kind of the application's own (Input.kind) is drawn by the
renderers the application gives, the same ones its form views take. Each
Block's settings form has Messages of its own, so they are given as a
function of the Message, as Cms.controlRenderers is:
BuilderView.define(PageBuilder, { settings: { renderers: Cms.controlRenderers } }).
Without one, the inspector says which renderer is missing.
The page on the canvas is the site's own markup, so it is styled by the site's
CSS, and the editor marks it by drawing over it: the selectionBox Slot sits
over the selected node, with its Block's label in selectionLabel, and the
hoverBox Slot over the node under the pointer. They are placed from where
the nodes are measured to be (Measure, from foldkit-primitives/dom),
scroll with the page, and take no pointer; the Builder places them, and a
Style says how they look:
Style.forSlots(BuilderSlots)({
selectionBox: Style.self({ outline: '2px solid Highlight', outlineOffset: '3px' }),
selectionLabel: Style.self({ background: 'Highlight', color: 'HighlightText', fontSize: '0.75rem' }),
hoverBox: Style.self({ outline: '1px dashed GrayText', outlineOffset: '3px' }),
})A node's wrapper still carries data-composition-mark (selected, or
hovered; a node that is both is selected), which is what the boxes are
measured from, and data-composition-drop (before, inside or after) on
the node a drop would land at. A wrapper is display: contents and draws
nothing, so style the element inside it. The layer rows carry
data-builder-drop and data-builder-dragging the same way.
[data-composition-drop='before'] > * { box-shadow: 0 -3px 0 Highlight; }
[data-composition-drop='after'] > * { box-shadow: 0 3px 0 Highlight; }
[data-composition-drop='inside'] > * { outline: 2px dashed Highlight; }
[data-builder-dragging] { opacity: 0.5; }