feat(frontend): App drawer (#1246)

* feat(frontend): app drawer

* feat(frontend): app drawer

* feat(frontend): app drawer

* feat(frontend): app drawer wip

* feat(frontend): drawer wip

* feat(frontend): drawer wip

* feat(frontend): app missing prop

* feat(frontend): revert drawer changes

* feat(frontend): highlight subgrid
This commit is contained in:
Faton Ramadani
2023-02-28 15:49:57 +01:00
committed by GitHub
parent ea2ebfa92e
commit 8a0d1158c4
13 changed files with 196 additions and 21 deletions
@@ -36,6 +36,7 @@
{#if $app.subgrids?.[`${id}-0`]}
<SubGridEditor
{noPadding}
{id}
class={css?.container.class}
style={css?.container.style}
bind:subGrid={$app.subgrids[`${id}-0`]}
@@ -0,0 +1,89 @@
<script lang="ts">
import { getContext } from 'svelte'
import SubGridEditor from '../../editor/SubGridEditor.svelte'
import type { AppInput } from '../../inputType'
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
import InputValue from '../helpers/InputValue.svelte'
import Portal from 'svelte-portal'
import { concatCustomCss } from '../../utils'
import { Button, ButtonType, Drawer, DrawerContent } from '$lib/components/common'
import { twMerge } from 'tailwind-merge'
import { AlignWrapper } from '../helpers'
export let customCss: ComponentCustomCSS<'container'> | undefined = undefined
export let id: string
export let configuration: Record<string, AppInput>
export let horizontalAlignment: 'left' | 'center' | 'right' | undefined = undefined
export let verticalAlignment: 'top' | 'center' | 'bottom' | undefined = undefined
export let noWFull = false
const { app, focusedGrid, selectedComponent } = getContext<AppEditorContext>('AppEditorContext')
let gridContent: string[] | undefined = undefined
let noPadding: boolean | undefined = undefined
let drawerTitle: string | undefined = undefined
let appDrawer: Drawer
let labelValue: string
let color: ButtonType.Color
let size: ButtonType.Size
let disabled: boolean | undefined = undefined
let fillContainer: boolean | undefined = undefined
$: css = concatCustomCss($app.css?.containercomponent, customCss)
</script>
<div class="h-full w-full">
<AlignWrapper {noWFull} {horizontalAlignment} {verticalAlignment}>
<Button
btnClasses={twMerge(
$app.css?.['buttoncomponent']?.['button']?.class,
fillContainer ? 'w-full h-full' : ''
)}
{disabled}
on:pointerdown={(e) => {
e?.stopPropagation()
window.dispatchEvent(new Event('pointerup'))
}}
on:click={async (e) => {
$focusedGrid = {
parentComponentId: id,
subGridIndex: 0
}
appDrawer.toggleDrawer()
}}
{size}
{color}
>
<div>{labelValue}</div>
</Button>
</AlignWrapper>
</div>
<InputValue {id} input={configuration.gridContent} bind:value={gridContent} />
<InputValue {id} input={configuration.noPadding} bind:value={noPadding} />
<InputValue {id} input={configuration.drawerTitle} bind:value={drawerTitle} />
<InputValue {id} input={configuration.label} bind:value={labelValue} />
<InputValue {id} input={configuration.color} bind:value={color} />
<InputValue {id} input={configuration.size} bind:value={size} />
<InputValue {id} input={configuration.fillContainer} bind:value={fillContainer} />
<Portal target="#app-editor-top-level-drawer">
<Drawer bind:this={appDrawer} size="800px" alwaysOpen positionClass="!absolute">
<DrawerContent title={drawerTitle} on:close={appDrawer.toggleDrawer}>
{#if $app.subgrids?.[`${id}-0`]}
<SubGridEditor
{noPadding}
{id}
class={css?.container.class}
style={css?.container.style}
bind:subGrid={$app.subgrids[`${id}-0`]}
containerHeight={1200}
on:focus={() => {
$selectedComponent = id
}}
/>
{/if}
</DrawerContent>
</Drawer>
</Portal>
@@ -72,6 +72,7 @@
{#if $app.subgrids}
{#each tabs ?? [] as res, i}
<SubGridEditor
{id}
visible={i === selectedIndex}
bind:subGrid={$app.subgrids[`${id}-${i}`]}
{noPadding}
@@ -24,7 +24,7 @@
import Icon from 'svelte-awesome'
import { faCode, faPlus, faSliders } from '@fortawesome/free-solid-svg-icons'
import ContextPanel from './contextPanel/ContextPanel.svelte'
import { encodeState } from '$lib/utils'
import { classNames, encodeState } from '$lib/utils'
import AppPreview from './AppPreview.svelte'
import { userStore, workspaceStore } from '$lib/stores'
@@ -59,7 +59,6 @@
const runnableComponents = writable<Record<string, () => Promise<void>>>({})
const errorByComponent = writable<Record<string, { error: string; componentId: string }>>({})
const focusedGrid = writable<FocusedGrid | undefined>(undefined)
setContext<AppEditorContext>('AppEditorContext', {
@@ -126,7 +125,7 @@
{#if $connectingInput.opened}
<div
class="absolute w-full h-screen bg-black border-2 bg-opacity-25 z-20 flex justify-center items-center"
class="absolute w-full h-screen bg-black border-2 bg-opacity-25 z-20 flex justify-center items-center"
/>
{/if}
{#if !$userStore?.operator}
@@ -172,14 +171,17 @@
style={$appStore.css?.['app']?.['viewer']?.style}
>
<div
class="relative mx-auto w-full h-full overflow-auto {app.fullscreen
? ''
: 'max-w-6xl'}"
class={classNames(
'relative mx-auto w-full h-full overflow-auto',
app.fullscreen ? '' : 'max-w-6xl'
)}
>
{#if $appStore.grid}
<div class={width}>
<GridEditor {policy} />
</div>
<div id="app-editor-top-level-drawer" />
{/if}
</div>
</div>
@@ -76,6 +76,8 @@
$: lockedClasses = isLocked ? '!max-h-[400px] overflow-hidden pointer-events-none' : ''
</script>
<div id="app-editor-top-level-drawer" />
<div class="relative">
<div
class="{$$props.class} {lockedClasses} h-full
@@ -87,6 +89,7 @@
</div>
{/if}
</div>
{#if isLocked}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div
@@ -135,7 +135,7 @@
<div class="relative w-full z-20 overflow-visible">
<div
class="w-full sticky top-0 flex justify-between border-l border-r border-b {$connectingInput?.opened
class="w-full sticky top-0 flex justify-between border-l border-r border-b {$connectingInput?.opened
? ''
: 'bg-gray-50 '} px-4 py-2 items-center gap-4"
style="z-index: 1000;"
@@ -149,9 +149,10 @@
</div>
</div>
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div
style={$app.css?.['app']?.['grid']?.style}
class={twMerge('px-4 pt-4 pb-2 overflow-visible', $app.css?.['app']?.['grid']?.class ?? '')}
class={twMerge('px-4 pt-4 pb-2 overflow-visible', $app.css?.['app']?.['grid']?.class ?? '')}
on:pointerdown={onpointerdown}
on:pointerleave={onpointerup}
on:pointerup={onpointerup}
@@ -13,17 +13,18 @@
export { classes as class }
export let style = ''
export let noPadding = false
//export let id: string
export let subGrid: GridItem[] = []
export let visible: boolean = true
export let id: string
const dispatch = createEventDispatcher()
const { app, connectingInput, selectedComponent, focusedGrid } =
getContext<AppEditorContext>('AppEditorContext')
let pointerdown = false
$: highlight = id === $focusedGrid?.parentComponentId && $selectedComponent !== id
let pointerdown = false
let onComponent: string | undefined = undefined
const onpointerdown = (e) => {
@@ -64,11 +65,16 @@
</script>
<div
class="relative w-full subgrid {visible ? 'visible' : 'invisible h-0 overflow-hidden'}"
class="relative w-full subgrid {visible ? 'visible' : 'invisible h-0 overflow-hidden'} "
bind:this={container}
>
<div
class={twMerge('py-2 overflow-auto', classes ?? '', noPadding ? 'px-0' : 'px-2')}
class={twMerge(
'py-2 overflow-auto',
classes ?? '',
noPadding ? 'px-0' : 'px-2',
highlight ? 'border-indigo-500 border border-dashed' : ''
)}
on:pointerdown|stopPropagation={onpointerdown}
on:pointerleave={onpointerup}
on:pointerup={onpointerup}
@@ -38,6 +38,7 @@
} from '../../components'
import type { AppComponent } from './components'
import AppAggridTable from '../../components/display/table/AppAggridTable.svelte'
import AppDrawer from '../../components/layout/AppDrawer.svelte'
export let pComponent: AppComponent
export let selected: boolean
@@ -231,6 +232,8 @@
<AppFileInput {...component} bind:staticOutputs={$staticOutputs[component.id]} />
{:else if component.type === 'imagecomponent'}
<AppImage {...component} bind:staticOutputs={$staticOutputs[component.id]} />
{:else if component.type === 'drawercomponent'}
<AppDrawer {...component} bind:staticOutputs={$staticOutputs[component.id]} />
{/if}
</div>
</div>
@@ -27,7 +27,8 @@ import {
SeparatorHorizontal,
SeparatorVertical,
Paperclip,
Image
Image,
SidebarClose
} from 'lucide-svelte'
import type { BaseAppComponent } from '../../types'
@@ -78,6 +79,7 @@ export type TabsComponent = BaseComponent<'tabscomponent'> & {
tabs: string[]
}
export type ContainerComponent = BaseComponent<'containercomponent'>
export type DrawerComponent = BaseComponent<'drawercomponent'>
export type AppComponent = BaseAppComponent &
(
@@ -112,6 +114,7 @@ export type AppComponent = BaseAppComponent &
| FileInputComponent
| ImageComponent
| AggridComponent
| DrawerComponent
)
export type AppComponentDimensions = `${IntRange<
@@ -1274,5 +1277,64 @@ Hello \${ctx.username}
customCss: {},
card: false
}
},
drawercomponent: {
name: 'Drawer',
icon: SidebarClose,
dims: '1:1-2:1',
data: {
softWrap: true,
id: '',
type: 'drawercomponent',
horizontalAlignment: 'center',
verticalAlignment: 'center',
configuration: {
noPadding: {
type: 'static',
fieldType: 'boolean',
value: false,
onlyStatic: true
},
drawerTitle: {
type: 'static',
fieldType: 'text',
value: 'Drawer title',
onlyStatic: true
},
label: {
type: 'static',
fieldType: 'text',
value: 'Press me'
},
color: {
fieldType: 'select',
type: 'static',
onlyStatic: true,
optionValuesKey: 'buttonColorOptions',
value: 'blue'
},
size: {
fieldType: 'select',
type: 'static',
onlyStatic: true,
optionValuesKey: 'buttonSizeOptions',
value: 'xs'
},
fillContainer: {
fieldType: 'boolean',
type: 'static',
onlyStatic: true,
value: false
},
disabled: {
fieldType: 'boolean',
type: 'eval',
expr: 'false'
}
},
componentInput: undefined,
card: false,
numberOfSubgrids: 1
}
}
}
@@ -6,7 +6,8 @@ const layout: ComponentSet = {
'tabscomponent',
'containercomponent',
'horizontaldividercomponent',
'verticaldividercomponent'
'verticaldividercomponent',
'drawercomponent'
]
} as const
@@ -46,8 +47,8 @@ const display: ComponentSet = {
'plotlycomponent',
'scatterchartcomponent',
'timeseriescomponent',
'displaycomponent',
'displaycomponent'
]
} as const
export const COMPONENT_SETS = [layout, buttons, inputs, display] as const
export const COMPONENT_SETS = [layout, buttons, inputs, display] as const
@@ -76,9 +76,15 @@
<svelte:window on:keydown={onKeyDown} />
<aside class="drawer {$$props.class}" class:open class:close={!open && timeout} {style}>
<div class="overlay" on:click={handleClickAway} />
<div class="panel {placement}" class:size>
<aside
class="drawer {$$props.class} {$$props.positionClass}"
class:open
class:close={!open && timeout}
{style}
>
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div class="overlay {$$props.positionClass}" on:click={handleClickAway} />
<div class="panel {placement} {$$props.positionClass}" class:size>
{#if open || !timeout || alwaysOpen}
<slot />
{/if}
@@ -3,7 +3,6 @@
import { faClose } from '@fortawesome/free-solid-svg-icons'
import { createEventDispatcher } from 'svelte'
import { Icon } from 'svelte-awesome'
import Button from '../button/Button.svelte'
export let title: string | undefined = undefined
export let overflow_y = true
+2 -1
View File
@@ -5,7 +5,8 @@
"lib": ["esnext", "DOM"],
"target": "esnext",
"preserveValueImports": true,
"outDir": "build",
/**
svelte-preprocess cannot figure out whether you have a value or a type, so tell TypeScript
to enforce using \`import type\` instead of \`import\` for Types.