budibase/packages/client/src/components/app/SidePanel.svelte

110 lines
2.5 KiB
Svelte

<script>
import { getContext } from "svelte"
const component = getContext("component")
const { styleable, sidePanelStore, builderStore, dndIsDragging } =
getContext("sdk")
export let onClose
export let ignoreClicksOutside
// Automatically show and hide the side panel when inside the builder.
// For some unknown reason, svelte reactivity breaks if we reference the
// reactive variable "open" inside the following expression, or if we define
// "open" above this expression.
$: {
if ($builderStore.inBuilder) {
if (
$component.inSelectedPath &&
$sidePanelStore.contentId !== $component.id
) {
sidePanelStore.actions.open($component.id)
} else if (
!$component.inSelectedPath &&
$sidePanelStore.contentId === $component.id &&
!$dndIsDragging
) {
sidePanelStore.actions.close()
}
}
}
// $: {
// }
// Derive visibility
$: open = $sidePanelStore.contentId === $component.id
// Derive a render key which is only changed whenever this panel is made
// visible after being hidden. We need to key the content to avoid showing
// stale data when re-revealing a side panel that was closed, but we cannot
// hide the content altogether when hidden as this breaks ejection.
let renderKey = null
$: {
if (open) {
sidePanelStore.actions.setIgnoreClicksOutside(ignoreClicksOutside)
renderKey = Math.random()
}
}
const handleSidePanelClose = async () => {
if (onClose) {
await onClose()
}
}
const showInSidePanel = (el, visible) => {
const update = visible => {
const target = document.getElementById("side-panel-container")
const node = el
if (visible) {
if (!target.contains(node)) {
target.appendChild(node)
}
} else {
if (target.contains(node)) {
target.removeChild(node)
handleSidePanelClose()
}
}
}
// Apply initial visibility
update(visible)
return {
update,
destroy: () => update(false),
}
}
</script>
<div
use:styleable={$component.styles}
use:showInSidePanel={open}
class="side-panel"
class:open
>
{#key renderKey}
<slot />
{/key}
</div>
<style>
.side-panel {
flex: 1 1 auto;
display: none;
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
gap: var(--spacing-xl);
}
.side-panel.open {
display: flex;
}
.side-panel :global(.component > *) {
max-width: 100%;
}
</style>