mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-23 00:00:33 +00:00
feat(frontend): add indicator when a component is locked (#4296)
* feat(frontend): add indicator when a component is locked * feat(frontend): add indicator when a component is locked * feat(frontend): improve interactivity * feat(frontend): fix how the lock overlay renders * feat(frontend): use builin detection * feat(frontend): fix zIndex * feat(frontend): update the shadow when dragged over a locked component
This commit is contained in:
@@ -124,3 +124,23 @@ svelte-virtual-list-contents > * + * {
|
||||
.nonmain-editor .cursor.monaco-mouse-cursor-text {
|
||||
width: 1px !important;
|
||||
}
|
||||
|
||||
.bg-locked {
|
||||
background-image: repeating-linear-gradient(
|
||||
-45deg,
|
||||
rgba(128, 128, 128, 0.2),
|
||||
rgba(128, 128, 128, 0.2) 10px,
|
||||
rgba(192, 192, 192, 0.2) 10px,
|
||||
rgba(192, 192, 192, 0.2) 20px
|
||||
);
|
||||
}
|
||||
|
||||
.bg-locked-hover {
|
||||
background-image: repeating-linear-gradient(
|
||||
-45deg,
|
||||
rgba(255, 99, 71, 0.2),
|
||||
rgba(255, 99, 71, 0.2) 10px,
|
||||
rgba(255, 69, 58, 0.2),
|
||||
rgba(255, 69, 58, 0.2) 20px
|
||||
);
|
||||
}
|
||||
|
||||
@@ -136,6 +136,7 @@
|
||||
}}
|
||||
let:dataItem
|
||||
let:hidden
|
||||
let:overlapped
|
||||
cols={columnConfiguration}
|
||||
>
|
||||
<ComponentWrapper
|
||||
@@ -176,6 +177,7 @@
|
||||
on:fillHeight={() => {
|
||||
handleFillHeight(dataItem.id)
|
||||
}}
|
||||
overlapped={overlapped !== undefined}
|
||||
/>
|
||||
</GridEditorMenu>
|
||||
</ComponentWrapper>
|
||||
@@ -193,10 +195,6 @@
|
||||
{/if}
|
||||
|
||||
<style global>
|
||||
.svlt-grid-shadow {
|
||||
/* Back shadow */
|
||||
background: #93c4fdd0 !important;
|
||||
}
|
||||
.svlt-grid-active {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
import type { AppEditorContext, AppViewerContext } from '../../types'
|
||||
import ComponentHeader from '../ComponentHeader.svelte'
|
||||
import type { AppComponent } from './components'
|
||||
import { Lock } from 'lucide-svelte'
|
||||
|
||||
import AppMultiSelect from '../../components/inputs/AppMultiSelect.svelte'
|
||||
import AppMultiSelectV2 from '../../components/inputs/AppMultiSelectV2.svelte'
|
||||
@@ -87,8 +88,9 @@
|
||||
export let render: boolean
|
||||
export let hidden: boolean
|
||||
export let fullHeight: boolean
|
||||
export let overlapped: boolean = false
|
||||
|
||||
const { mode, app, hoverStore, connectingInput } =
|
||||
const { mode, app, hoverStore, connectingInput, selectedComponent } =
|
||||
getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
const editorContext = getContext<AppEditorContext>('AppEditorContext')
|
||||
@@ -96,6 +98,8 @@
|
||||
$: ismoving =
|
||||
movingcomponents != undefined && $mode == 'dnd' && $movingcomponents?.includes(component.id)
|
||||
|
||||
const componentActive = editorContext?.componentActive
|
||||
|
||||
let initializing: boolean | undefined = undefined
|
||||
let errorHandledByComponent: boolean = false
|
||||
let componentContainerHeight: number = 0
|
||||
@@ -129,10 +133,25 @@
|
||||
}
|
||||
}}
|
||||
on:mouseout|stopPropagation={mouseOut}
|
||||
class="h-full flex flex-col w-full component {initializing
|
||||
? 'overflow-hidden h-0'
|
||||
: ''} {hidden && $mode === 'preview' ? 'hidden' : ''} "
|
||||
class={twMerge(
|
||||
'h-full flex flex-col w-full component relative',
|
||||
initializing ? 'overflow-hidden h-0' : '',
|
||||
hidden && $mode === 'preview' ? 'hidden' : ''
|
||||
)}
|
||||
>
|
||||
{#if locked && componentActive && $componentActive && $selectedComponent?.[0] !== component.id}
|
||||
<div
|
||||
class={twMerge(
|
||||
'absolute inset-0 bg-locked center-center flex-col z-50',
|
||||
overlapped ? 'bg-locked-hover' : ''
|
||||
)}
|
||||
>
|
||||
<div class="bg-surface p-2 shadow-sm rounded-md flex center-center flex-col gap-2">
|
||||
<Lock size={24} class="text-primary " />
|
||||
<div class="text-xs">Locked. The component cannot be moved.</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{#if $mode !== 'preview'}
|
||||
<ComponentHeader
|
||||
on:mouseover={() => {
|
||||
|
||||
@@ -114,8 +114,10 @@
|
||||
...shadows[id]
|
||||
}
|
||||
}
|
||||
let { items, overlap } = moveItem(activeItem, sortedItems, getComputedCols)
|
||||
|
||||
sortedItems = moveItem(activeItem, sortedItems, getComputedCols)
|
||||
sortedItems = items
|
||||
overlapped = overlap ? id : undefined
|
||||
}
|
||||
}
|
||||
|
||||
@@ -173,6 +175,7 @@
|
||||
}
|
||||
})
|
||||
}
|
||||
let overlapped: string | undefined = undefined
|
||||
</script>
|
||||
|
||||
<div class="svlt-grid-container" style="height: {containerHeight}px" bind:this={container}>
|
||||
@@ -202,9 +205,10 @@
|
||||
{sensor}
|
||||
container={scroller}
|
||||
nativeContainer={container}
|
||||
{overlapped}
|
||||
>
|
||||
{#if item[getComputedCols]}
|
||||
<slot dataItem={item} hidden={false} />
|
||||
<slot dataItem={item} hidden={false} {overlapped} />
|
||||
{/if}
|
||||
</MoveResize>
|
||||
{/if}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { createEventDispatcher, getContext, onMount } from 'svelte'
|
||||
import type { AppEditorContext, AppViewerContext } from '../types'
|
||||
import { writable } from 'svelte/store'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
@@ -26,6 +27,7 @@
|
||||
export let nativeContainer
|
||||
export let onTop
|
||||
export let shadow: { x: number; y: number; w: number; h: number } | undefined = undefined
|
||||
export let overlapped: string | undefined = undefined
|
||||
|
||||
const ctx = getContext<AppEditorContext>('AppEditorContext')
|
||||
const { mode } = getContext<AppViewerContext>('AppViewerContext')
|
||||
@@ -382,7 +384,10 @@
|
||||
|
||||
{#if xPerPx > 0 && (active || trans) && shadow}
|
||||
<div
|
||||
class="svlt-grid-shadow shadow-active"
|
||||
class={twMerge(
|
||||
'svlt-grid-shadow shadow-active',
|
||||
overlapped ? 'svlte-grid-shadow-forbidden' : ''
|
||||
)}
|
||||
style="width: {shadow.w * xPerPx - gapX * 2}px; height: {shadow.h * yPerPx -
|
||||
gapY * 2}px; transform: translate({shadow.x * xPerPx + gapX}px, {shadow.y * yPerPx +
|
||||
gapY}px); "
|
||||
@@ -460,10 +465,13 @@
|
||||
|
||||
.svlt-grid-shadow {
|
||||
position: absolute;
|
||||
background: red;
|
||||
will-change: transform;
|
||||
background: pink;
|
||||
backface-visibility: hidden;
|
||||
-webkit-backface-visibility: hidden;
|
||||
background: #93c4fdd0;
|
||||
}
|
||||
|
||||
.svlte-grid-shadow-forbidden {
|
||||
background: rgba(255, 99, 71, 0.2) !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -133,7 +133,12 @@ export function moveItem(active, items, cols) {
|
||||
const fixed = closeObj.find((value) => value[cols].fixed)
|
||||
|
||||
// If found fixed, reset the active to its original position
|
||||
if (fixed) return items
|
||||
if (fixed) {
|
||||
return {
|
||||
items: items,
|
||||
overlap: true
|
||||
}
|
||||
}
|
||||
|
||||
// Update items
|
||||
items = updateItem(items, active, item, cols)
|
||||
@@ -171,7 +176,10 @@ export function moveItem(active, items, cols) {
|
||||
})
|
||||
|
||||
// Return result
|
||||
return tempItems
|
||||
return {
|
||||
items: tempItems,
|
||||
overlap: false
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function
|
||||
@@ -181,7 +189,7 @@ export function normalize(items, col) {
|
||||
result.forEach((value) => {
|
||||
const getItem = value[col]
|
||||
if (!getItem.static) {
|
||||
result = moveItem(getItem, result, col)
|
||||
result = moveItem(getItem, result, col).items
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user