1
0
Fork 0
mirror of synced 2024-09-20 19:33:10 +12:00

Move most grid specific logic into a dedicated file to avoid polluting rest of the codebase

This commit is contained in:
Andrew Kingston 2024-08-02 16:13:49 +01:00
parent dbfad2cb3a
commit 0ba00a5117
No known key found for this signature in database
9 changed files with 160 additions and 98 deletions

View file

@ -103,7 +103,7 @@
key: "props.cols", key: "props.cols",
label: "Columns", label: "Columns",
control: Stepper, control: Stepper,
defaultValue: 12, defaultValue: 24,
props: { props: {
min: 2, min: 2,
max: 50, max: 50,
@ -113,7 +113,7 @@
key: "props.rows", key: "props.rows",
label: "Rows", label: "Rows",
control: Stepper, control: Stepper,
defaultValue: 12, defaultValue: 24,
props: { props: {
min: 2, min: 2,
max: 50, max: 50,

View file

@ -1391,6 +1391,10 @@
"width": 25, "width": 25,
"height": 25 "height": 25
}, },
"grid": {
"hAlign": "center",
"vAlign": "center"
},
"settings": [ "settings": [
{ {
"type": "icon", "type": "icon",
@ -1704,6 +1708,10 @@
"width": 260, "width": 260,
"height": 143 "height": 143
}, },
"grid": {
"hAlign": "center",
"vAlign": "center"
},
"settings": [ "settings": [
{ {
"type": "text", "type": "text",
@ -1737,6 +1745,10 @@
"width": 400, "width": 400,
"height": 100 "height": 100
}, },
"grid": {
"hAlign": "stretch",
"vAlign": "stretch"
},
"settings": [ "settings": [
{ {
"type": "text", "type": "text",
@ -5240,6 +5252,10 @@
"width": 300, "width": 300,
"height": 120 "height": 120
}, },
"grid": {
"hAlign": "center",
"vAlign": "center"
},
"settings": [ "settings": [
{ {
"type": "text", "type": "text",

View file

@ -284,7 +284,7 @@
visibility: hidden; visibility: hidden;
padding: 0; padding: 0;
margin: 0; margin: 0;
overflow: hidden; overflow: clip;
width: 100%; width: 100%;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
@ -301,7 +301,7 @@
width: 100%; width: 100%;
height: 100%; height: 100%;
position: relative; position: relative;
overflow: hidden; overflow: clip;
background-color: transparent; background-color: transparent;
} }
@ -311,7 +311,7 @@
} }
#app-root { #app-root {
overflow: hidden; overflow: clip;
height: 100%; height: 100%;
width: 100%; width: 100%;
display: flex; display: flex;

View file

@ -39,8 +39,7 @@
getActionContextKey, getActionContextKey,
getActionDependentContextKeys, getActionDependentContextKeys,
} from "../utils/buttonActions.js" } from "../utils/buttonActions.js"
import { buildStyleString } from "utils/styleable.js" import { gridLayout } from "utils/grid.js"
import { getBaseGridVars } from "utils/grid.js"
export let instance = {} export let instance = {}
export let isLayout = false export let isLayout = false
@ -198,15 +197,18 @@
$: currentTheme = $context?.device?.theme $: currentTheme = $context?.device?.theme
$: darkMode = !currentTheme?.includes("light") $: darkMode = !currentTheme?.includes("light")
// Build up full styles and split them into variables and non-variables $: normalStyles = {
$: baseStyles = getBaseStyles(definition, errorState)
$: styles = {
...baseStyles,
...instance._styles?.normal, ...instance._styles?.normal,
...ephemeralStyles, ...ephemeralStyles,
} }
$: parsedStyles = parseStyles(styles) $: gridMetadata = {
$: wrapperCSS = buildStyleString(parsedStyles.variables) id,
interactive,
styles: normalStyles,
draggable,
definition,
errored: errorState,
}
// Update component context // Update component context
$: store.set({ $: store.set({
@ -214,8 +216,7 @@
children: children.length, children: children.length,
styles: { styles: {
...instance._styles, ...instance._styles,
normal: parsedStyles.nonVariables, normal: normalStyles,
variables: parsedStyles.variables,
custom: customCSS, custom: customCSS,
id, id,
empty: emptyState, empty: emptyState,
@ -615,31 +616,6 @@
} }
} }
const handleWrapperClick = e => {
e.stopPropagation()
builderStore.actions.selectComponent(id)
}
// Splits component styles into variables and non-variables
const parseStyles = styles => {
let variables = {}
let nonVariables = {}
for (let style of Object.keys(styles || {})) {
const group = style.startsWith("--") ? variables : nonVariables
group[style] = styles[style]
}
return { variables, nonVariables }
}
// Generates any required base styles based on the component definition
const getBaseStyles = (definition, errored = false) => {
return {
"--default-width": errored ? 500 : definition.size?.width || 100,
"--default-height": errored ? 60 : definition.size?.height || 100,
...getBaseGridVars(definition, errored),
}
}
onMount(() => { onMount(() => {
// Register this component instance for external access // Register this component instance for external access
if ($appStore.isDevApp) { if ($appStore.isDevApp) {
@ -683,14 +659,11 @@
class:parent={hasChildren} class:parent={hasChildren}
class:block={isBlock} class:block={isBlock}
class:error={errorState} class:error={errorState}
class:fill={definition.grid?.fill}
data-id={id} data-id={id}
data-name={name} data-name={name}
data-icon={icon} data-icon={icon}
data-parent={$component.id} data-parent={$component.id}
style={wrapperCSS} use:gridLayout={gridMetadata}
{draggable}
on:click|self={interactive ? handleWrapperClick : null}
> >
{#if errorState} {#if errorState}
<ComponentErrorState <ComponentErrorState

View file

@ -480,7 +480,7 @@
position: relative; position: relative;
padding: 32px; padding: 32px;
} }
.main:has(> .grid) { .main:has(.screenslot-dom > .component > .grid) {
padding-top: 0; padding-top: 0;
padding-bottom: 0; padding-bottom: 0;
} }

View file

@ -68,16 +68,6 @@
position: relative; position: relative;
height: 400px; height: 400px;
gap: 0; gap: 0;
/* Prevent cross-grid variable inheritance */
/* --grid-desktop-col-start: initial;
--grid-desktop-col-end: initial;
--grid-desktop-row-start: initial;
--grid-desktop-row-end: initial;
--grid-mobile-col-start: initial;
--grid-mobile-col-end: initial;
--grid-mobile-row-start: initial;
--grid-mobile-row-end: initial;*/
} }
.grid, .grid,
.underlay { .underlay {
@ -128,12 +118,8 @@
); );
/* Flex vars */ /* Flex vars */
--h-align: var(--grid-desktop-h-align, var(--grid-mobile-h-align, stretch)); --h-align: var(--grid-desktop-h-align, var(--grid-mobile-h-align));
--v-align: var(--grid-desktop-v-align, var(--grid-mobile-v-align, center)); --v-align: var(--grid-desktop-v-align, var(--grid-mobile-v-align));
--child-flex: var(
--grid-desktop-child-flex,
var(--grid-mobile-child-flex, 0 0 auto)
);
/* Ensure grid metadata falls within limits */ /* Ensure grid metadata falls within limits */
grid-column-start: min(max(1, var(--col-start)), var(--cols)) !important; grid-column-start: min(max(1, var(--col-start)), var(--cols)) !important;
@ -171,16 +157,20 @@
); );
/* Flex vars */ /* Flex vars */
--h-align: var(--grid-mobile-h-align, var(--grid-desktop-h-align, stretch)); --h-align: var(--grid-mobile-h-align, var(--grid-desktop-h-align));
--v-align: var(--grid-mobile-v-align, var(--grid-desktop-v-align, center)); --v-align: var(--grid-mobile-v-align, var(--grid-desktop-v-align));
--child-flex: var(
--grid-mobile-child-flex,
var(--grid-desktop-child-flex, 0 0 auto)
);
} }
/* Handle grid children which need to fill the outer component wrapper */ /* Handle grid children which need to fill the outer component wrapper */
.grid :global(> .component > *) { .grid :global(> .component > *) {
flex: var(--child-flex) !important; flex: 0 0 auto !important;
}
.grid:not(.mobile) :global(> .component.grid-desktop-grow > *) {
flex: 1 1 0 !important;
height: 0 !important;
}
.grid.mobile :global(> .component.grid-mobile-grow > *) {
flex: 1 1 0 !important;
height: 0 !important;
} }
</style> </style>

View file

@ -2,7 +2,12 @@
import { onMount, onDestroy } from "svelte" import { onMount, onDestroy } from "svelte"
import { builderStore, componentStore } from "stores" import { builderStore, componentStore } from "stores"
import { Utils, memo } from "@budibase/frontend-core" import { Utils, memo } from "@budibase/frontend-core"
import { isGridEvent, getGridParentID, getGridVar } from "utils/grid" import {
isGridEvent,
getGridParentID,
gridCSSVars,
GridVars,
} from "utils/grid"
// Smallest possible 1x1 transparent GIF // Smallest possible 1x1 transparent GIF
const ghost = new Image(1, 1) const ghost = new Image(1, 1)
@ -15,10 +20,10 @@
// Grid CSS variables // Grid CSS variables
$: vars = { $: vars = {
colStart: $getGridVar("col-start"), colStart: $gridCSSVars[GridVars.ColStart],
colEnd: $getGridVar("col-end"), colEnd: $gridCSSVars[GridVars.ColEnd],
rowStart: $getGridVar("row-start"), rowStart: $gridCSSVars[GridVars.RowStart],
rowEnd: $getGridVar("row-end"), rowEnd: $gridCSSVars[GridVars.RowEnd],
} }
// Some memoisation of primitive types for performance // Some memoisation of primitive types for performance

View file

@ -7,7 +7,7 @@
import { builderStore, componentStore, dndIsDragging } from "stores" import { builderStore, componentStore, dndIsDragging } from "stores"
import { Utils } from "@budibase/frontend-core" import { Utils } from "@budibase/frontend-core"
import { findComponentParent } from "utils/components" import { findComponentParent } from "utils/components"
import { getGridVar } from "utils/grid" import { gridCSSVars, GridVars } from "utils/grid"
const verticalOffset = 36 const verticalOffset = 36
const horizontalOffset = 2 const horizontalOffset = 2
@ -44,8 +44,8 @@
insideGrid && insideGrid &&
(definition?.grid?.hAlign !== "stretch" || (definition?.grid?.hAlign !== "stretch" ||
definition?.grid?.vAlign !== "stretch") definition?.grid?.vAlign !== "stretch")
$: gridHAlignVar = $getGridVar("h-align") $: gridHAlignVar = $gridCSSVars[GridVars.HAlign]
$: gridVAlignVar = $getGridVar("v-align") $: gridVAlignVar = $gridCSSVars[GridVars.VAlign]
$: gridStyles = $state?.styles $: gridStyles = $state?.styles
const getBarSettings = definition => { const getBarSettings = definition => {

View file

@ -1,5 +1,6 @@
import { builderStore, componentStore } from "stores" import { builderStore } from "stores"
import { derived, get, readable } from "svelte/store" import { derived } from "svelte/store"
import { buildStyleString } from "utils/styleable.js"
/** /**
* We use CSS variables on components to control positioning and layout of * We use CSS variables on components to control positioning and layout of
@ -12,20 +13,42 @@ import { derived, get, readable } from "svelte/store"
* `grid.hAlign` and `grid.vAlign` keys in the manifest. * `grid.hAlign` and `grid.vAlign` keys in the manifest.
*/ */
// Enum representing the different CSS variables we use for grid metadata
export const GridVars = {
HAlign: "h-align",
VAlign: "v-align",
ColStart: "col-start",
ColEnd: "col-end",
RowStart: "row-start",
RowEnd: "row-end",
}
// Classes used in selectors inside grid containers to control child styles
export const GridClasses = {
DesktopFill: "grid-desktop-grow",
MobileFill: "grid-mobile-grow",
}
// Enum for device preview type, included in grid CSS variables // Enum for device preview type, included in grid CSS variables
const Devices = { const Devices = {
Desktop: "desktop", Desktop: "desktop",
Mobile: "mobile", Mobile: "mobile",
} }
// Generates the CSS variable for a certain grid param suffix, for the current // A derived map of all CSS variables for the current device
// device
const previewDevice = derived(builderStore, $store => $store.previewDevice) const previewDevice = derived(builderStore, $store => $store.previewDevice)
export const getGridVar = derived(previewDevice, device => suffix => { export const gridCSSVars = derived(previewDevice, $device => {
const prefix = device === Devices.Mobile ? Devices.Mobile : Devices.Desktop const device = $device === Devices.Mobile ? Devices.Mobile : Devices.Desktop
return `--grid-${prefix}-${suffix}` let vars = {}
for (let type of Object.values(GridVars)) {
vars[type] = `--grid-${device}-${type}`
}
return vars
}) })
// Builds a CSS variable name for a certain piece of grid metadata
export const getGridCSSVar = (device, type) => `--grid-${device}-${type}`
// Generates the CSS variable for a certain grid param suffix, for the other // Generates the CSS variable for a certain grid param suffix, for the other
// device variant than the one included in this variable // device variant than the one included in this variable
export const getOtherDeviceGridVar = cssVar => { export const getOtherDeviceGridVar = cssVar => {
@ -69,21 +92,6 @@ export const getGridParentID = node => {
return node?.parentNode?.closest(".grid")?.parentNode.dataset.id return node?.parentNode?.closest(".grid")?.parentNode.dataset.id
} }
// Generates the base set of grid CSS vars from a component definition
export const getBaseGridVars = (definition, errored = false) => {
const hAlign = errored ? "stretch" : definition?.grid?.hAlign || "stretch"
const vAlign = errored ? "stretch" : definition?.grid?.vAlign || "center"
const flexStyles = vAlign === "stretch" ? "1 1 0" : "0 0 auto"
return {
"--grid-desktop-h-align": hAlign,
"--grid-mobile-h-align": hAlign,
"--grid-desktop-v-align": vAlign,
"--grid-mobile-v-align": vAlign,
"--grid-desktop-child-flex": flexStyles,
"--grid-mobile-child-flex": flexStyles,
}
}
// Gets the current value of a certain grid CSS variable for a component // Gets the current value of a certain grid CSS variable for a component
export const getGridVarValue = (styles, variable) => { export const getGridVarValue = (styles, variable) => {
// Try the desired variable // Try the desired variable
@ -97,3 +105,73 @@ export const getGridVarValue = (styles, variable) => {
// Otherwise use the default // Otherwise use the default
return val ? val : getDefaultGridVarValue(variable) return val ? val : getDefaultGridVarValue(variable)
} }
// Svelte action to apply required class names and styles to our component
// wrappers
export const gridLayout = (node, metadata) => {
let selectComponent
const applyMetadata = metadata => {
const { id, styles, interactive, errored, definition } = metadata
consol.log(styles)
// Callback to select the component when clicking on the wrapper
selectComponent = e => {
e.preventDefault()
builderStore.actions.selectComponent(id)
}
// Generate base set of grid CSS vars based for this component
const hAlign = errored ? "stretch" : definition?.grid?.hAlign || "stretch"
const vAlign = errored ? "stretch" : definition?.grid?.vAlign || "center"
const vars = {
"--default-width": errored ? 500 : definition.size?.width || 100,
"--default-height": errored ? 60 : definition.size?.height || 100,
"--grid-desktop-h-align": hAlign,
"--grid-mobile-h-align": hAlign,
"--grid-desktop-v-align": vAlign,
"--grid-mobile-v-align": vAlign,
}
// Extract any other CSS variables from the saved component styles
for (let style of Object.keys(styles)) {
if (style.startsWith("--")) {
vars[style] = styles[style]
delete styles[style]
}
}
// Apply all CSS variables to the wrapper
node.style = buildStyleString(vars)
// Toggle classes to specify whether our children should fill
const desktopVar = getGridCSSVar(Devices.Desktop, GridVars.VAlign)
const mobileVar = getGridCSSVar(Devices.Mobile, GridVars.VAlign)
node.classList.toggle(
GridClasses.DesktopFill,
vars[desktopVar] === "stretch"
)
node.classList.toggle(GridClasses.MobileFill, vars[mobileVar] === "stretch")
// Add a listener to select this node on click
if (interactive) {
node.addEventListener("click", selectComponent, false)
}
}
const removeListeners = () => {
node.removeEventListener("click", selectComponent)
}
applyMetadata(metadata)
return {
update(newMetadata) {
removeListeners()
applyMetadata(newMetadata)
},
destroy() {
removeListeners()
},
}
}