feat(frontend): add expand (#1268)

* feat(frontend): add expand

* feat(frontend): fix container height

* feat(frontend): remove code duplication

* feat(frontend): add historic
This commit is contained in:
Faton Ramadani
2023-03-08 22:56:30 +01:00
committed by GitHub
parent 0a5e181a3a
commit b854ee3439
6 changed files with 186 additions and 70 deletions
@@ -1,7 +1,7 @@
<script lang="ts">
import { classNames } from '$lib/utils'
import type { AppEditorContext } from '../types'
import { Anchor, Bug, Move } from 'lucide-svelte'
import { Anchor, Bug, Expand, Move } from 'lucide-svelte'
import { createEventDispatcher, getContext } from 'svelte'
import Popover from '$lib/components/Popover.svelte'
import { Alert, Button } from '$lib/components/common'
@@ -55,6 +55,17 @@
<Anchor aria-label="Lock position" size={14} />
{/if}
</button>
<button
title="Position locking"
class={classNames(
'text-gray-800 px-1 text-2xs py-0.5 font-bold w-fit shadow border border-gray-300 absolute -top-1 right-[0.5rem] z-50 cursor-pointer',
' hover:bg-gray-300',
selected ? 'bg-gray-200/80' : 'bg-gray-200/80'
)}
on:click={() => dispatch('expand')}
>
<Expand aria-label="Lock position" size={14} />
</button>
{/if}
{#if selected || hover}
@@ -12,6 +12,7 @@
import Component from './component/Component.svelte'
import { deepEqual } from 'fast-equals'
import { push } from '$lib/history'
import { expandGriditem, findAvailableSpace, findGridItem } from './appUtils'
export let policy: Policy
@@ -25,7 +26,8 @@
summary,
focusedGrid,
parentWidth,
history
history,
breakpoint
} = getContext<AppEditorContext>('AppEditorContext')
// The drag is disabled when the user is connecting an input
@@ -203,11 +205,16 @@
locked={isFixed(gridComponent)}
on:delete={() => removeGridElement(gridComponent.data)}
on:lock={() => {
let fComponent = $app.grid.find((c) => c.id === gridComponent.id)
if (fComponent) {
fComponent = toggleFixed(fComponent)
const gridItem = findGridItem($app, gridComponent.data.id)
if (gridItem) {
toggleFixed(gridItem)
}
}}
on:expand={() => {
push(history, $app)
expandGriditem($app.grid, gridComponent, $breakpoint)
$app = { ...$app }
}}
/>
</div>
{/if}
@@ -6,7 +6,8 @@
import { columnConfiguration, isFixed, toggleFixed } from '../gridUtils'
import type { AppEditorContext, GridItem } from '../types'
import Component from './component/Component.svelte'
import { findGridItem } from './appUtils'
import { expandGriditem, findAvailableSpace, findGridItem } from './appUtils'
import { push } from '$lib/history'
export let containerHeight: number
let classes = ''
@@ -21,8 +22,16 @@
const dispatch = createEventDispatcher()
const { app, connectingInput, selectedComponent, focusedGrid, mode, parentWidth } =
getContext<AppEditorContext>('AppEditorContext')
const {
app,
connectingInput,
selectedComponent,
focusedGrid,
mode,
parentWidth,
breakpoint,
history
} = getContext<AppEditorContext>('AppEditorContext')
$: highlight = id === $focusedGrid?.parentComponentId && shouldHighlight
@@ -129,6 +138,18 @@
selected={$selectedComponent === dataItem.data.id}
locked={isFixed(gridComponent)}
on:lock={() => lock(gridComponent)}
on:expand={() => {
const parentGridItem = findGridItem($app, id)
if (!parentGridItem) {
return
}
push(history, $app)
expandGriditem(subGrid, gridComponent, $breakpoint, parentGridItem)
$app = { ...$app }
}}
/>
</div>
{/if}
@@ -1,5 +1,5 @@
import { getNextId } from '$lib/components/flows/flowStateUtils'
import type { App, FocusedGrid, GridItem } from '../types'
import type { App, EditorBreakpoint, FocusedGrid, GridItem } from '../types'
import { getRecommendedDimensionsByComponent, type AppComponent } from './component'
import gridHelp from '@windmill-labs/svelte-grid/src/utils/helper'
import { gridColumns } from '../gridUtils'
@@ -31,7 +31,6 @@ export function getNextGridItemId(app: App): string {
}
export function createNewGridItem(grid: GridItem[], id: string, data: AppComponent): GridItem {
const appComponent = data
appComponent.id = id
@@ -176,3 +175,131 @@ export function duplicateGridItem(
}
return undefined
}
type AvailableSpace = {
left: number
right: number
top: number
bottom: number
}
export function findAvailableSpace(
grid: GridItem[],
gridItem: GridItem,
editorBreakpoint: EditorBreakpoint,
parentGridItem: GridItem | undefined = undefined
): AvailableSpace | undefined {
if (gridItem) {
const breakpoint = editorBreakpoint === 'sm' ? 3 : 12
const maxHeight = parentGridItem ? parentGridItem[breakpoint].h - 1 : 12
const maxWidth = 12
const availableSpace = {
left: 0,
right: 0,
top: 0,
bottom: 0
}
const items = grid.map((item) => {
return {
id: item.id,
x: item[breakpoint].x,
y: item[breakpoint].y,
w: item[breakpoint].w,
h: item[breakpoint].h
}
})
const item = items.find((item) => item.id === gridItem.id)
if (!item) {
return availableSpace
}
if (item.x > 0) {
for (let x = item.x - 1; x >= 0; x--) {
const itemToCheck = { ...item, x, w: 1 }
const isItemInWay = items.some((item) => isOverlapping(item, itemToCheck))
if (isItemInWay) {
break
} else {
availableSpace.left++
}
}
}
if (item.x + item.w < maxWidth) {
for (let x = item.x + item.w; x < maxWidth; x++) {
const itemToCheck = { ...item, x, w: 1 }
const isItemInWay = items.some((item) => isOverlapping(item, itemToCheck))
if (isItemInWay) {
break
} else {
availableSpace.right++
}
}
}
if (item.y > 0) {
for (let y = item.y - 1; y >= 0; y--) {
const itemToCheck = { ...item, h: 1, y }
const isItemInWay = items.some((item) => isOverlapping(item, itemToCheck))
if (isItemInWay) {
break
} else {
availableSpace.top++
}
}
}
if (item.y + item.h < maxHeight) {
for (let y = item.y + item.h; y < maxHeight; y++) {
const itemToCheck = { ...item, h: 1, y }
const isItemInWay = items.some((item) => isOverlapping(item, itemToCheck))
if (isItemInWay) {
break
} else {
availableSpace.bottom++
}
}
}
return availableSpace
}
}
function isOverlapping(item1: any, item2: any) {
return (
item1.x < item2.x + item2.w &&
item1.x + item1.w > item2.x &&
item1.y < item2.y + item2.h &&
item1.y + item1.h > item2.y
)
}
export function expandGriditem(
grid: GridItem[],
gridComponent: GridItem,
$breakpoint: EditorBreakpoint,
parentGridItem: GridItem | undefined = undefined
) {
const availableSpace = findAvailableSpace(grid, gridComponent, $breakpoint, parentGridItem)
if (!availableSpace) {
return
}
const { left, right, top, bottom } = availableSpace
const width = $breakpoint === 'sm' ? 3 : 12
const previousGridItem = JSON.parse(JSON.stringify(gridComponent[width]))
gridComponent[width].x = previousGridItem.x - left
gridComponent[width].y = previousGridItem.y - top
gridComponent[width].w = previousGridItem.w + left + right
gridComponent[width].h = previousGridItem.h + top + bottom
}
@@ -69,7 +69,16 @@
class="h-full flex flex-col w-full component"
>
{#if $mode !== 'preview'}
<ComponentHeader {hover} {pointerdown} {component} {selected} on:delete on:lock {locked} />
<ComponentHeader
{hover}
{pointerdown}
{component}
{selected}
on:delete
on:lock
on:expand
{locked}
/>
{/if}
<div
-59
View File
@@ -237,65 +237,6 @@ export function toKebabCase(text: string) {
return text.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase()
}
export function findParent(root: GridItem[], id: string): GridItem | undefined {
for (const a of root) {
if (a.id === id) {
return a
}
if (a.data.subGrids) {
// Recursively search the sub-grids
for (const subGrid of a.data.subGrids) {
const result = findParent(subGrid, id)
if (result) {
return result
}
}
}
}
return undefined
}
export function insertNewGridItem(
root: GridItem[],
id: string,
subGridIndex: number,
newId: string,
data: AppComponent
): GridItem[] {
const parentA = findParent(root, id)
if (!parentA) {
throw new Error(`Parent A object with ID ${id} not found.`)
}
const subGrid = parentA.data.subGrids[subGridIndex]
if (!subGrid) {
throw new Error(`Sub-grid with index ${subGridIndex} not found.`)
}
const newItem = createNewGridItem(subGrid ?? [], newId, data)
subGrid.push(newItem)
return root
}
function recursiveGetIds(gridItem: GridItem): string[] {
const subGrids = gridItem.data.subGrids ?? []
const subGridIds = subGrids
.map((subGrid: GridItem[]) => subGrid.map(recursiveGetIds))
.flat(Infinity)
return [gridItem.data.id, ...subGridIds]
}
export function getNextGridItemId(grid: GridItem[] = []): string {
const gridItemIds = grid.map(recursiveGetIds).flat()
const id = getNextId(gridItemIds)
return id
}
export function concatCustomCss<T extends string = string>(
appCss?: ComponentCustomCSS<T>,
componentCss?: ComponentCustomCSS<T>