Add stacked drawer support and transitions
This commit is contained in:
parent
d3504d714c
commit
7484f087bc
|
@ -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 {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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)",
|
||||||
|
|
|
@ -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}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue