feat(builder): containers package -- vertical alignment + box-model/anim/vis rollout

- ColumnLayout: exposes style.alignItems on its flex ROW (aligns uneven
  columns) -- render/toHtml already spread `style` onto the row div, so this
  is a craft.props default + panel control addition, no structural change.
- Container/Section: root element is now unconditionally display:flex;
  flex-direction:column (both editor render and toHtml), so the new
  Vertical Alignment control maps to style.justifyContent, paired with a
  Min Height (NumericUnitInput) control on style.minHeight. Default
  justify-content/align-items reproduce ordinary block-flow stacking, so
  this is a visual no-op for existing published content. Works in both
  normal and "boxed" (contentWidth) modes -- the boxed inner wrapper's own
  margin:0-auto horizontal centering is preserved via flex auto-margin
  override semantics.
- ContainerStylePanel (serves Container/Section/Columns) distinguishes the
  Columns case from Container/Section via nodeProps.columns/split presence
  (no typeName plumbing needed) to pick align-items vs justify-content for
  the shared Vertical Alignment control.
- All 3 owned components: added margin/padding (per-side)/border/box-shadow/
  opacity style defaults + AnimationControl/VisibilityControl-backed
  animation/animationDelay/hideOnDesktop/hideOnTablet/hideOnMobile props.
  New containerBoxModel.tsx (package-local, not shared.tsx) DRYs the
  box-model + border/effects + animation/visibility panel sections across
  the single shared ContainerStylePanel, mirroring the sibling media
  package's mediaBoxModel.tsx.
- Tests: extended all 3 *.toHtml.test.ts files (align-items/justify-content/
  min-height emission, box-model style emission, craft.props presence).
  673 tests green, tsc + vite build clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-14 06:44:37 -07:00
parent 1d9460c173
commit 9750a6c2bf
8 changed files with 445 additions and 4 deletions
+31 -1
View File
@@ -43,6 +43,18 @@ const flexAlignFromTextAlign = (textAlign: CSSProperties['textAlign']): CSSPrope
return {};
};
// Container is ALWAYS display:flex/flex-direction:column at its root (both
// in the editor render below and in toHtml) so that `style.justifyContent`
// (the new Vertical Alignment control, paired with `style.minHeight`) has an
// axis to act on. This is a no-op visually for existing content: a column
// flex container with default `justify-content:flex-start` and
// `align-items:stretch` reproduces ordinary block-flow stacking (children
// stack top-to-bottom, full width) byte-for-byte, so nothing published
// before this feature shipped changes appearance. `flexAlignFromTextAlign`
// above still supplies `alignItems` (cross-axis, i.e. horizontal position)
// from `textAlign` independently -- the two controls act on orthogonal axes
// and don't conflict.
export const Container: UserComponent<ContainerProps> = ({
style = {},
tag = 'div',
@@ -60,6 +72,8 @@ export const Container: UserComponent<ContainerProps> = ({
const flexStyles = flexAlignFromTextAlign(style.textAlign);
const outerStyle: CSSProperties = {
display: 'flex',
flexDirection: 'column',
minHeight: '40px',
...style,
...(fullWidth ? { width: '100vw', marginLeft: 'calc(-50vw + 50%)' } : {}),
@@ -93,13 +107,27 @@ export const Container: UserComponent<ContainerProps> = ({
Container.craft = {
displayName: 'Container',
props: {
style: { padding: '20px', minHeight: '100px' },
style: {
padding: '20px',
minHeight: '100px',
justifyContent: '',
marginTop: '', marginRight: '', marginBottom: '', marginLeft: '',
paddingTop: '', paddingRight: '', paddingBottom: '', paddingLeft: '',
border: 'none',
boxShadow: 'none',
opacity: '1',
},
tag: 'div',
fullWidth: false,
contentWidth: 'full',
anchorId: '',
cssId: '',
cssClass: '',
animation: '',
animationDelay: '0',
hideOnDesktop: false,
hideOnTablet: false,
hideOnMobile: false,
},
rules: {
canDrag: () => true,
@@ -116,6 +144,8 @@ Container.craft = {
const flexStyles = flexAlignFromTextAlign(props.style?.textAlign);
const outerCss: CSSProperties = {
display: 'flex',
flexDirection: 'column',
...props.style,
...(isBoxed ? {} : flexStyles),
};