Add stacked drawer support and transitions

This commit is contained in:
Andrew Kingston 2024-02-23 18:15:01 +00:00
parent d3504d714c
commit 7484f087bc
5 changed files with 68 additions and 27 deletions

View File

@ -1,15 +1,16 @@
<script context="module">
import { writable } from "svelte/store"
const openDrawers = writable([])
</script>
<script> <script>
import Portal from "svelte-portal" import Portal from "svelte-portal"
import Button from "../Button/Button.svelte" import Button from "../Button/Button.svelte"
import Body from "../Typography/Body.svelte" import { setContext, createEventDispatcher, onDestroy } from "svelte"
import Heading from "../Typography/Heading.svelte"
import { setContext, createEventDispatcher } from "svelte"
import { generate } from "shortid" import { generate } from "shortid"
export let title export let title
export let fillWidth
export let left = "314px"
export let width = "calc(100% - 648px)"
export let headless = false export let headless = false
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
@ -17,12 +18,15 @@
let visible = false let visible = false
let drawerId = generate() let drawerId = generate()
$: depth = $openDrawers.length - $openDrawers.indexOf(drawerId) - 1
export function show() { export function show() {
if (visible) { if (visible) {
return return
} }
visible = true visible = true
dispatch("drawerShow", drawerId) dispatch("drawerShow", drawerId)
openDrawers.update(state => [...state, drawerId])
} }
export function hide() { export function hide() {
@ -31,6 +35,7 @@
} }
visible = false visible = false
dispatch("drawerHide", drawerId) dispatch("drawerHide", drawerId)
openDrawers.update(state => state.filter(id => id !== drawerId))
} }
setContext("drawer-actions", { setContext("drawer-actions", {
@ -54,22 +59,33 @@
}, },
} }
} }
const getScaleFactor = depth => {
// Quadratic function approaching a limit of 1 as depth tends to infinity
const lim = 1 - 1 / (depth * depth + 1)
// Scale drawers between 1 and 0.9 as depth approaches infinity
return 1 - lim * 0.1
}
onDestroy(() => {
if (visible) {
hide()
}
})
</script> </script>
{#if visible} {#if visible}
<Portal> <Portal target=".drawer-container">
<section <div
class:fillWidth
class="drawer" class="drawer"
class:headless class:headless
transition:slide|local transition:slide|local
style={`width: ${width}; left: ${left};`} style="--scale-factor:{getScaleFactor(depth)}"
class:stacked={depth > 0}
> >
{#if !headless} {#if !headless}
<header> <header>
<div class="text"> <div class="text">{title}</div>
{title}
</div>
<div class="buttons"> <div class="buttons">
<Button secondary quiet on:click={hide}>Cancel</Button> <Button secondary quiet on:click={hide}>Cancel</Button>
<slot name="buttons" /> <slot name="buttons" />
@ -77,30 +93,41 @@
</header> </header>
{/if} {/if}
<slot name="body" /> <slot name="body" />
</section> </div>
</Portal> </Portal>
{/if} {/if}
<style> <style>
.buttons {
display: flex;
gap: var(--spacing-m);
}
.drawer { .drawer {
--drawer-spacing: 10px;
position: absolute; position: absolute;
bottom: 0; left: var(--drawer-spacing);
bottom: var(--drawer-spacing);
transform: translateY(calc(-210% * (1 - var(--scale-factor))))
scale(var(--scale-factor));
width: calc(100% - 2 * var(--drawer-spacing));
background: var(--background); background: var(--background);
border: var(--border-light); border: var(--border-light);
z-index: 3; z-index: 3;
border-radius: 8px; border-radius: 8px;
overflow: hidden; overflow: hidden;
margin: 8px; box-sizing: border-box;
transition: transform 360ms ease-out;
} }
.drawer::after {
.fillWidth { content: "";
left: 260px !important; position: absolute;
width: calc(100% - 260px) !important; top: 0;
left: 0;
width: 100%;
height: 100%;
background: var(--background);
pointer-events: none;
transition: opacity 360ms ease-out;
opacity: calc(10 * (1 - var(--scale-factor)));
}
.drawer.stacked::after {
pointer-events: all;
} }
header { header {

View File

@ -297,6 +297,10 @@
</div> </div>
<style> <style>
.code-editor :global(.cm-editor) {
background: var(--spectrum-global-color-gray-50) !important;
}
/* Unify spacing between HBS and JS */ /* Unify spacing between HBS and JS */
.code-editor :global(.cm-content) { .code-editor :global(.cm-content) {
padding: 10px 0; padding: 10px 0;

View File

@ -44,8 +44,6 @@ export const getDefaultTheme = opts => {
border: border:
"var(--spectrum-alias-border-size-thin) solid var(--spectrum-alias-border-color)", "var(--spectrum-alias-border-size-thin) solid var(--spectrum-alias-border-color)",
borderRadius: "var(--border-radius-s)", borderRadius: "var(--border-radius-s)",
backgroundColor:
"var( --spectrum-textfield-m-background-color, var(--spectrum-global-color-gray-50) )",
resize: resize ? `${resize}` : "", resize: resize ? `${resize}` : "",
overflow: "hidden", overflow: "hidden",
color: "var(--spectrum-alias-text-color)", color: "var(--spectrum-alias-text-color)",

View File

@ -25,6 +25,7 @@
import { BindingHelpers } from "./utils" import { BindingHelpers } from "./utils"
import formatHighlight from "json-format-highlight" import formatHighlight from "json-format-highlight"
import { capitalise } from "helpers" import { capitalise } from "helpers"
import DrawerBindableInput from "components/common/bindings/DrawerBindableInput.svelte"
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
@ -173,6 +174,7 @@
{capitalise(tab)} {capitalise(tab)}
</ActionButton> </ActionButton>
{/each} {/each}
<DrawerBindableInput />
</div> </div>
<div class="side-tabs"> <div class="side-tabs">
{#each sideTabs as tab} {#each sideTabs as tab}

View File

@ -6,6 +6,7 @@
</script> </script>
<div class="app-panel"> <div class="app-panel">
<div class="drawer-container" />
<div class="header"> <div class="header">
<div class="header-left"> <div class="header-left">
<UndoRedoControl store={screenStore.history} /> <UndoRedoControl store={screenStore.history} />
@ -33,6 +34,15 @@
justify-content: flex-start; justify-content: flex-start;
align-items: stretch; align-items: stretch;
padding: 9px var(--spacing-m); padding: 9px var(--spacing-m);
position: relative;
}
.drawer-container {
position: absolute;
height: 100%;
width: 100%;
overflow: hidden;
top: 0;
left: 0;
} }
.header { .header {
display: flex; display: flex;