Update grid layout to include nested flex wrappers for more layout control
This commit is contained in:
parent
0d6e7bd5d3
commit
29ddeab0d4
|
@ -132,8 +132,8 @@
|
|||
hoverStore.hover(data.id, false)
|
||||
} else if (type === "update-prop") {
|
||||
await componentStore.updateSetting(data.prop, data.value)
|
||||
} else if (type === "update-styles") {
|
||||
await componentStore.updateStyles(data.styles, data.id)
|
||||
} else if (type === "update-meta-styles") {
|
||||
await componentStore.updateMetaStyles(data.styles, data.id)
|
||||
} else if (type === "delete-component" && data.id) {
|
||||
// Legacy type, can be deleted in future
|
||||
confirmDeleteComponent(data.id)
|
||||
|
|
|
@ -64,6 +64,7 @@ export class ComponentStore extends BudiStore {
|
|||
this.moveDown = this.moveDown.bind(this)
|
||||
this.updateStyle = this.updateStyle.bind(this)
|
||||
this.updateStyles = this.updateStyles.bind(this)
|
||||
this.updateMetaStyles = this.updateMetaStyles.bind(this)
|
||||
this.updateCustomStyle = this.updateCustomStyle.bind(this)
|
||||
this.updateConditions = this.updateConditions.bind(this)
|
||||
this.requestEjectBlock = this.requestEjectBlock.bind(this)
|
||||
|
@ -978,6 +979,16 @@ export class ComponentStore extends BudiStore {
|
|||
await this.patch(patchFn, id)
|
||||
}
|
||||
|
||||
async updateMetaStyles(styles, id) {
|
||||
const patchFn = component => {
|
||||
component._styles.meta = {
|
||||
...component._styles.meta,
|
||||
...styles,
|
||||
}
|
||||
}
|
||||
await this.patch(patchFn, id)
|
||||
}
|
||||
|
||||
async updateCustomStyle(style) {
|
||||
await this.patch(component => {
|
||||
component._styles.custom = style
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
getActionContextKey,
|
||||
getActionDependentContextKeys,
|
||||
} from "../utils/buttonActions.js"
|
||||
import { buildStyleString } from "utils/styleable.js"
|
||||
|
||||
export let instance = {}
|
||||
export let isLayout = false
|
||||
|
@ -102,8 +103,8 @@
|
|||
let settingsDefinitionMap
|
||||
let missingRequiredSettings = false
|
||||
|
||||
// Temporary styles which can be added in the app preview for things like DND.
|
||||
// We clear these whenever a new instance is received.
|
||||
// Temporary meta styles which can be added in the app preview for things like
|
||||
// DND. We clear these whenever a new instance is received.
|
||||
let ephemeralStyles
|
||||
|
||||
// Single string of all HBS blocks, used to check if we use a certain binding
|
||||
|
@ -196,16 +197,20 @@
|
|||
$: currentTheme = $context?.device?.theme
|
||||
$: darkMode = !currentTheme?.includes("light")
|
||||
|
||||
// Build meta styles and stringify to apply to the wrapper node
|
||||
$: metaStyles = {
|
||||
...instance._styles?.meta,
|
||||
...ephemeralStyles,
|
||||
}
|
||||
$: metaCSS = buildStyleString(metaStyles)
|
||||
|
||||
// Update component context
|
||||
$: store.set({
|
||||
id,
|
||||
children: children.length,
|
||||
styles: {
|
||||
...instance._styles,
|
||||
normal: {
|
||||
...instance._styles?.normal,
|
||||
...ephemeralStyles,
|
||||
},
|
||||
meta: metaStyles,
|
||||
custom: customCSS,
|
||||
id,
|
||||
empty: emptyState,
|
||||
|
@ -605,6 +610,14 @@
|
|||
}
|
||||
}
|
||||
|
||||
const select = e => {
|
||||
if (isBlock) {
|
||||
return
|
||||
}
|
||||
e.stopPropagation()
|
||||
builderStore.actions.selectComponent(id)
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
// Register this component instance for external access
|
||||
if ($appStore.isDevApp) {
|
||||
|
@ -635,6 +648,8 @@
|
|||
{#if constructor && initialSettings && (visible || inSelectedPath) && !builderHidden}
|
||||
<!-- The ID is used as a class because getElementsByClassName is O(1) -->
|
||||
<!-- and the performance matters for the selection indicators -->
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<div
|
||||
class={`component ${id}`}
|
||||
class:draggable
|
||||
|
@ -650,6 +665,9 @@
|
|||
data-name={name}
|
||||
data-icon={icon}
|
||||
data-parent={$component.id}
|
||||
style={metaCSS}
|
||||
{draggable}
|
||||
on:click={select}
|
||||
>
|
||||
{#if errorState}
|
||||
<ComponentErrorState
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
export let wrap
|
||||
export let onClick
|
||||
|
||||
$: directionClass = direction ? `valid-container direction-${direction}` : ""
|
||||
$: directionClass = direction ? `flex-container direction-${direction}` : ""
|
||||
$: hAlignClass = hAlign ? `hAlign-${hAlign}` : ""
|
||||
$: vAlignClass = vAlign ? `vAlign-${vAlign}` : ""
|
||||
$: sizeClass = size ? `size-${size}` : ""
|
||||
|
@ -39,11 +39,11 @@
|
|||
</div>
|
||||
|
||||
<style>
|
||||
.valid-container {
|
||||
.flex-container {
|
||||
display: flex;
|
||||
max-width: 100%;
|
||||
}
|
||||
.valid-container :global(.component > *) {
|
||||
.flex-container :global(.component > *) {
|
||||
max-width: 100%;
|
||||
}
|
||||
.direction-row {
|
||||
|
|
|
@ -8,10 +8,16 @@
|
|||
export let cols = 12
|
||||
export let rows = 12
|
||||
|
||||
let width
|
||||
let height
|
||||
|
||||
$: cols = cols || 12
|
||||
$: rows = rows || 12
|
||||
$: coords = generateCoords(rows, cols)
|
||||
$: mobile = $context.device.mobile
|
||||
$: empty = $component.empty
|
||||
$: colSize = width / cols
|
||||
$: rowSize = height / rows
|
||||
|
||||
const generateCoords = (rows, cols) => {
|
||||
let grid = []
|
||||
|
@ -27,14 +33,18 @@
|
|||
<div
|
||||
class="grid"
|
||||
class:mobile
|
||||
bind:clientWidth={width}
|
||||
bind:clientHeight={height}
|
||||
use:styleable={{
|
||||
...$component.styles,
|
||||
normal: {
|
||||
...$component.styles?.normal,
|
||||
"--cols": cols,
|
||||
"--rows": rows,
|
||||
gap: "0 !important",
|
||||
"--col-size": `${colSize}px`,
|
||||
"--row-size": `${rowSize}px`,
|
||||
},
|
||||
empty: false,
|
||||
}}
|
||||
data-rows={rows}
|
||||
data-cols={cols}
|
||||
|
@ -46,55 +56,18 @@
|
|||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
<slot />
|
||||
|
||||
<!-- Only render the slot if not empty, as we don't want the placeholder -->
|
||||
{#if !empty}
|
||||
<slot />
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
/*
|
||||
Ensure all children of containers which are top level children of
|
||||
grids do not overflow
|
||||
*/
|
||||
:global(.grid > .component > .valid-container > .component > *) {
|
||||
max-height: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
/* Ensure all top level children have some grid styles set */
|
||||
:global(.grid > .component > *) {
|
||||
overflow: hidden;
|
||||
width: auto;
|
||||
height: auto;
|
||||
max-height: 100%;
|
||||
max-width: 100%;
|
||||
min-width: 0;
|
||||
|
||||
/* On desktop, use desktop metadata and fall back to mobile */
|
||||
--col-start: var(--grid-desktop-col-start, var(--grid-mobile-col-start, 1));
|
||||
--col-end: var(--grid-desktop-col-end, var(--grid-mobile-col-end, 2));
|
||||
--row-start: var(--grid-desktop-row-start, var(--grid-mobile-row-start, 1));
|
||||
--row-end: var(--grid-desktop-row-end, var(--grid-mobile-row-end, 2));
|
||||
|
||||
/* Ensure grid metadata falls within limits */
|
||||
grid-column-start: min(max(1, var(--col-start)), var(--cols)) !important;
|
||||
grid-column-end: min(
|
||||
max(2, var(--col-end)),
|
||||
calc(var(--cols) + 1)
|
||||
) !important;
|
||||
grid-row-start: min(max(1, var(--row-start)), var(--rows)) !important;
|
||||
grid-row-end: min(max(2, var(--row-end)), calc(var(--rows) + 1)) !important;
|
||||
}
|
||||
|
||||
/* On mobile, use mobile metadata and fall back to desktop */
|
||||
:global(.grid.mobile > .component > *) {
|
||||
--col-start: var(--grid-mobile-col-start, var(--grid-desktop-col-start, 1));
|
||||
--col-end: var(--grid-mobile-col-end, var(--grid-desktop-col-end, 2));
|
||||
--row-start: var(--grid-mobile-row-start, var(--grid-desktop-row-start, 1));
|
||||
--row-end: var(--grid-mobile-row-end, var(--grid-desktop-row-end, 2));
|
||||
}
|
||||
|
||||
.grid {
|
||||
position: relative;
|
||||
height: 400px;
|
||||
gap: 0;
|
||||
}
|
||||
.grid,
|
||||
.underlay {
|
||||
|
@ -118,4 +91,36 @@
|
|||
.placeholder {
|
||||
background-color: var(--spectrum-global-color-gray-100);
|
||||
}
|
||||
|
||||
/* Ensure all top level children have grid styles applied */
|
||||
.grid :global(> .component) {
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: stretch;
|
||||
|
||||
/* On desktop, use desktop metadata and fall back to mobile */
|
||||
--col-start: var(--grid-desktop-col-start, var(--grid-mobile-col-start, 1));
|
||||
--col-end: var(--grid-desktop-col-end, var(--grid-mobile-col-end, 2));
|
||||
--row-start: var(--grid-desktop-row-start, var(--grid-mobile-row-start, 1));
|
||||
--row-end: var(--grid-desktop-row-end, var(--grid-mobile-row-end, 2));
|
||||
|
||||
/* Ensure grid metadata falls within limits */
|
||||
grid-column-start: min(max(1, var(--col-start)), var(--cols)) !important;
|
||||
grid-column-end: min(
|
||||
max(2, var(--col-end)),
|
||||
calc(var(--cols) + 1)
|
||||
) !important;
|
||||
grid-row-start: min(max(1, var(--row-start)), var(--rows)) !important;
|
||||
grid-row-end: min(max(2, var(--row-end)), calc(var(--rows) + 1)) !important;
|
||||
}
|
||||
|
||||
/* On mobile, use mobile metadata and fall back to desktop */
|
||||
.grid.mobile :global(> .component) {
|
||||
--col-start: var(--grid-mobile-col-start, var(--grid-desktop-col-start, 1));
|
||||
--col-end: var(--grid-mobile-col-end, var(--grid-desktop-col-end, 2));
|
||||
--row-start: var(--grid-mobile-row-start, var(--grid-desktop-row-start, 1));
|
||||
--row-end: var(--grid-mobile-row-end, var(--grid-desktop-row-end, 2));
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -200,7 +200,7 @@
|
|||
const domGrid = getDOMNode(dragInfo.gridId)
|
||||
const gridCols = parseInt(domGrid.dataset.cols)
|
||||
const gridRows = parseInt(domGrid.dataset.rows)
|
||||
const domNode = getDOMNode(dragInfo.id)
|
||||
const domNode = getDOMNode(dragInfo.id)?.parentNode
|
||||
const styles = window.getComputedStyle(domNode)
|
||||
if (domGrid) {
|
||||
// Util to get the current grid CSS variable for this device. If unset,
|
||||
|
@ -237,7 +237,7 @@
|
|||
const stopDragging = async () => {
|
||||
// Save changes
|
||||
if ($gridStyles) {
|
||||
await builderStore.actions.updateStyles($gridStyles, dragInfo.id)
|
||||
await builderStore.actions.updateMetaStyles($gridStyles, dragInfo.id)
|
||||
}
|
||||
|
||||
// Reset listener
|
||||
|
|
|
@ -124,9 +124,8 @@
|
|||
|
||||
// Extract valid children
|
||||
// Sanity limit of 100 active indicators
|
||||
const children = Array.from(
|
||||
document.getElementsByClassName(`${componentId}-dom`)
|
||||
)
|
||||
const className = nextState.insideGrid ? componentId : `${componentId}-dom`
|
||||
const children = Array.from(document.getElementsByClassName(className))
|
||||
.filter(x => x != null)
|
||||
.slice(0, 100)
|
||||
|
||||
|
|
|
@ -40,8 +40,11 @@ const createBuilderStore = () => {
|
|||
updateProp: (prop, value) => {
|
||||
eventStore.actions.dispatchEvent("update-prop", { prop, value })
|
||||
},
|
||||
updateStyles: async (styles, id) => {
|
||||
await eventStore.actions.dispatchEvent("update-styles", { styles, id })
|
||||
updateMetaStyles: async (styles, id) => {
|
||||
await eventStore.actions.dispatchEvent("update-meta-styles", {
|
||||
styles,
|
||||
id,
|
||||
})
|
||||
},
|
||||
keyDown: (key, ctrlKey) => {
|
||||
eventStore.actions.dispatchEvent("key-down", { key, ctrlKey })
|
||||
|
|
|
@ -3,7 +3,7 @@ import { builderStore } from "stores"
|
|||
/**
|
||||
* Helper to build a CSS string from a style object.
|
||||
*/
|
||||
const buildStyleString = (styleObject, customStyles) => {
|
||||
export const buildStyleString = (styleObject, customStyles) => {
|
||||
let str = ""
|
||||
Object.entries(styleObject || {}).forEach(([style, value]) => {
|
||||
if (style && value != null) {
|
||||
|
|
Loading…
Reference in New Issue