feat(frontend): add fork/save buttons + consistent styling for slider/range

This commit is contained in:
Ruben Fiszel
2023-03-06 22:35:25 +01:00
parent adc3a4054e
commit f0ba4da849
14 changed files with 117 additions and 42 deletions
+26 -10
View File
@@ -7,7 +7,7 @@
import { setContext } from 'svelte'
import { writable } from 'svelte/store'
import CenteredPage from './CenteredPage.svelte'
import { Button } from './common'
import { Button, ButtonPopup, ButtonPopupItem } from './common'
import { dirtyStore } from './common/confirmationModal/dirtyStore'
import UnsavedConfirmationModal from './common/confirmationModal/UnsavedConfirmationModal.svelte'
import { OFFSET } from './CronInput.svelte'
@@ -26,8 +26,6 @@
export let initialArgs: Record<string, any> = {}
export let loading = false
let pathError = ''
async function createSchedule(path: string) {
const { cron, args, enabled } = $scheduleStore
@@ -49,7 +47,10 @@
}
}
async function saveFlow(): Promise<void> {
let loadingSave = false
async function saveFlow(leave: boolean): Promise<void> {
loadingSave = true
const flow = cleanInputs($flowStore)
const { cron, args, enabled } = $scheduleStore
$dirtyStore = false
@@ -111,7 +112,12 @@
await createSchedule(flow.path)
}
}
goto(`/flows/get/${$flowStore.path}?workspace_id=${$workspaceStore}`)
loadingSave = false
if (leave) {
goto(`/flows/get/${$flowStore.path}?workspace_id=${$workspaceStore}`)
} else if (initialPath !== $flowStore.path) {
goto(`/flows/edit/${$flowStore.path}?workspace_id=${$workspaceStore}`)
}
}
let timeout: NodeJS.Timeout | undefined = undefined
@@ -139,7 +145,7 @@
}, 500)
}
const selectedIdStore = writable<string>(selectedId)
const selectedIdStore = writable<string>(selectedId ?? 'settings-metadata')
const scheduleStore = writable<Schedule>({ args: {}, cron: '', enabled: false })
const previewArgsStore = writable<Record<string, any>>(initialArgs)
@@ -237,12 +243,22 @@
<div class="flex flex-row space-x-2">
<FlowPreviewButtons />
<div class="center-center">
<Button
disabled={pathError != ''}
startIcon={{ icon: faSave }}
<ButtonPopup
loading={loadingSave}
size="sm"
on:click={saveFlow}>Save</Button
startIcon={{ icon: faSave }}
on:click={() => saveFlow(false)}
>
<svelte:fragment slot="main">Save</svelte:fragment>
<ButtonPopupItem on:click={() => saveFlow(true)}>Save and exit</ButtonPopupItem>
{#if initialPath != ''}
<ButtonPopupItem
on:click={() => {
window.open(`/flows/add?template=${initialPath}`)
}}>Fork</ButtonPopupItem
>
{/if}
</ButtonPopup>
</div>
</div>
</div>
@@ -167,7 +167,7 @@
{#if showOptions}
<ul
class="options"
transition:fly={{ duration: 200, y: 5 }}
transition:fly|local={{ duration: 200, y: 5 }}
on:mousedown|preventDefault={handleOptionMousedown}
>
{#each filtered as option}
@@ -14,7 +14,7 @@
import CenteredPage from './CenteredPage.svelte'
import UnsavedConfirmationModal from './common/confirmationModal/UnsavedConfirmationModal.svelte'
import { dirtyStore } from './common/confirmationModal/dirtyStore'
import { Button, Kbd } from './common'
import { Button, ButtonPopup, ButtonPopupItem, Kbd } from './common'
import { faChevronDown, faChevronUp, faPen, faSave } from '@fortawesome/free-solid-svg-icons'
import Breadcrumb from './common/breadcrumb/Breadcrumb.svelte'
import LanguageIcon from './common/languageIcons/LanguageIcon.svelte'
@@ -54,7 +54,9 @@
script.content = initialCode(language, kind, template)
}
async function editScript(): Promise<void> {
let loadingSave = false
async function editScript(leave: boolean): Promise<void> {
loadingSave = true
try {
$dirtyStore = false
localStorage.removeItem(script.path)
@@ -83,11 +85,17 @@
kind: script.kind
}
})
history.replaceState(history.state, '', `/scripts/edit/${newHash}?step=2`)
goto(`/scripts/get/${newHash}?workspace_id=${$workspaceStore}`)
if (leave) {
history.replaceState(history.state, '', `/scripts/edit/${newHash}?step=2`)
goto(`/scripts/get/${newHash}?workspace_id=${$workspaceStore}`)
} else {
await goto(`/scripts/edit/${newHash}?step=2`)
script.hash = newHash
}
} catch (error) {
sendUserToast(`Impossible to save the script: ${error.body}`, true)
sendUserToast(`Impossible to save the script: ${error.body || error.message}`, true)
}
loadingSave = false
}
async function changeStep(step: number) {
@@ -182,14 +190,24 @@
>
Next {#if step == 1}<Kbd>Enter</Kbd>{/if}
</Button>
<Button
<ButtonPopup
loading={loadingSave}
size="sm"
variant={step == 1 ? 'border' : 'contained'}
disabled={step === 1 && pathError !== ''}
btnClasses={step == 1 && initialPath == '' ? 'invisible' : ''}
startIcon={{ icon: faSave }}
on:click={editScript}>Save</Button
on:click={() => editScript(false)}
>
<svelte:fragment slot="main">Save</svelte:fragment>
<ButtonPopupItem on:click={() => editScript(true)}>Save and exit</ButtonPopupItem>
{#if initialPath != ''}
<ButtonPopupItem
on:click={() => {
window.open(`/scripts/add?template=${initialPath}`)
}}>Fork</ButtonPopupItem
>
{/if}
</ButtonPopup>
</div>
</div>
</div>
+1 -1
View File
@@ -12,5 +12,5 @@
{#if !view}<ChevronDown />{:else}<ChevronUp />{/if}</Button
>
{#if view}
<div class="my-4 px-2" transition:slide><slot /></div>
<div class="my-4 px-2" transition:slide|local><slot /></div>
{/if}
@@ -54,7 +54,12 @@
<span class={css?.limits?.class ?? ''} style={css?.limits?.style ?? ''}>
{+min}
</span>
<div class="grow" on:pointerdown|stopPropagation>
<div
class="grow"
style="--range-handle-focus: {'#7e9abd'}; --range-handle: {'#7e9abd'}; {css?.bar?.style ??
''}"
on:pointerdown|stopPropagation
>
<RangeSlider
bind:slider
bind:values
@@ -13,7 +13,8 @@
export let configuration: Record<string, AppInput>
export let verticalAlignment: 'top' | 'center' | 'bottom' | undefined = undefined
export const staticOutputs: string[] = ['result']
export let customCss: ComponentCustomCSS<'handle' | 'limits' | 'value'> | undefined = undefined
export let customCss: ComponentCustomCSS<'handle' | 'limits' | 'value' | 'bar'> | undefined =
undefined
const { app, worldStore, selectedComponent } = getContext<AppEditorContext>('AppEditorContext')
let min = 0
@@ -60,7 +61,12 @@
<span class={css?.limits?.class ?? ''} style={css?.limits?.style ?? ''}>
{+min}
</span>
<div class="grow" on:pointerdown|stopPropagation={() => ($selectedComponent = id)}>
<div
class="grow"
style="--range-handle-focus: {'#7e9abd'}; --range-handle: {'#7e9abd'}; {css?.bar?.style ??
''}"
on:pointerdown|stopPropagation={() => ($selectedComponent = id)}
>
<RangeSlider bind:slider bind:values {step} min={+min} max={+max} />
</div>
<span class={css?.limits?.class ?? ''} style={css?.limits?.style ?? ''}>
@@ -1,7 +1,14 @@
<script lang="ts">
import { goto } from '$app/navigation'
import { page } from '$app/stores'
import { Alert, Badge, Drawer, DrawerContent } from '$lib/components/common'
import {
Alert,
Badge,
ButtonPopup,
ButtonPopupItem,
Drawer,
DrawerContent
} from '$lib/components/common'
import Button from '$lib/components/common/button/Button.svelte'
import { dirtyStore } from '$lib/components/common/confirmationModal/dirtyStore'
import Skeleton from '$lib/components/common/skeleton/Skeleton.svelte'
@@ -537,14 +544,31 @@
>
<span class="hidden md:inline">Publish</span>
</Button>
<Button
loading={loading.save}
startIcon={{ icon: faSave }}
on:click={save}
color="dark"
size="xs"
>
<span class="hidden md:inline">Save</span>
</Button>
{#if appPath == ''}
<Button
loading={loading.save}
startIcon={{ icon: faSave }}
on:click={save}
color="dark"
size="xs"
>
<span class="hidden md:inline">Save</span>
</Button>
{:else}
<ButtonPopup
loading={loading.save}
startIcon={{ icon: faSave }}
on:click={save}
color="dark"
size="xs"
>
<svelte:fragment slot="main">Save</svelte:fragment>
<ButtonPopupItem
on:click={() => {
window.open(`/apps/add?template=${appPath}`)
}}>Fork</ButtonPopupItem
>
</ButtonPopup>
{/if}
</div>
</div>
@@ -1052,6 +1052,7 @@ Hello \${ctx.username}
}
},
customCss: {
bar: { style: '' },
handle: { style: '' },
limits: { class: '', style: '' },
value: { class: '', style: '' }
@@ -1098,6 +1099,7 @@ Hello \${ctx.username}
},
customCss: {
handles: { style: '' },
bar: { style: '' },
limits: { class: '', style: '' },
values: { class: '', style: '' }
} as const,
@@ -26,7 +26,7 @@
<input type="text" class="!pr-7" bind:value={value.style} on:focus />
{#if value?.style}
<button
transition:fade={{ duration: 100 }}
transition:fade|local={{ duration: 100 }}
class="absolute z-10 top-1.5 right-1 rounded-full p-1 duration-200 hover:bg-gray-200"
aria-label="Remove styles"
on:click|preventDefault|stopPropagation={() => reset('style')}
@@ -44,7 +44,7 @@
<input type="text" class="!pr-7" bind:value={value.class} on:focus />
{#if value?.class}
<button
transition:fade={{ duration: 100 }}
transition:fade|local={{ duration: 100 }}
class="absolute z-10 top-1.5 right-1 rounded-full p-1 duration-200 hover:bg-gray-200"
aria-label="Remove classes"
on:click|preventDefault|stopPropagation={() => reset('class')}
@@ -15,6 +15,7 @@
export let startIcon: ButtonType.Icon | undefined = undefined
export let endIcon: ButtonType.Icon | undefined = undefined
export let spacingSize: ButtonType.Size = size
export let loading = false
let ref: ButtonType.Element
@@ -33,6 +34,7 @@
<div class="flex justy-start items-center">
{#if $$slots.main}
<Button
{loading}
{...commonProps}
{href}
{target}
@@ -36,7 +36,7 @@
{#if retry}
<Popover notClickable>
<div
transition:fade={{ duration: 200 }}
transition:fade|local={{ duration: 200 }}
class="center-center rounded border bg-white border-gray-400 text-gray-700 px-1 py-0.5"
>
<Repeat size={14} />
@@ -47,7 +47,7 @@
{#if earlyStop}
<Popover notClickable>
<div
transition:fade={{ duration: 200 }}
transition:fade|local={{ duration: 200 }}
class="center-center bg-white rounded border border-gray-400 text-gray-700 px-1 py-0.5"
>
<Square size={14} />
@@ -58,7 +58,7 @@
{#if suspend}
<Popover notClickable>
<div
transition:fade={{ duration: 200 }}
transition:fade|local={{ duration: 200 }}
class="center-center bg-white rounded border border-gray-400 text-gray-700 px-1 py-0.5"
>
<PhoneIncoming size={14} />
@@ -69,7 +69,7 @@
{#if sleep}
<Popover notClickable>
<div
transition:fade={{ duration: 200 }}
transition:fade|local={{ duration: 200 }}
class="center-center bg-white rounded border border-gray-400 text-gray-700 px-1 py-0.5"
>
<Bed size={14} />
@@ -92,7 +92,9 @@
return (
pathname.startsWith('/apps') ||
pathname.startsWith('/flows/add') ||
pathname.startsWith('/flows/edit')
pathname.startsWith('/flows/edit') ||
pathname.startsWith('/scripts/add') ||
pathname.startsWith('/scripts/edit')
)
}
afterNavigate((n) => {
@@ -52,7 +52,7 @@
})
Object.assign(flow, template)
const oldPath = flow.path.split('/')
flow.path = `u/${$userStore?.username}/${oldPath[oldPath.length - 1]}`
flow.path = `u/${$userStore?.username.split('@')[0]}/${oldPath[oldPath.length - 1]}_fork`
flow = flow
goto('?', { replaceState: true })
selectedId = 'settings-metadata'
@@ -4,7 +4,7 @@
import { page } from '$app/stores'
import FlowBuilder from '$lib/components/FlowBuilder.svelte'
import { workspaceStore } from '$lib/stores'
import { decodeArgs, decodeState, emptySchema, sendUserToast } from '$lib/utils'
import { decodeArgs, decodeState, sendUserToast } from '$lib/utils'
import { initFlow } from '$lib/components/flows/flowStore'
import { dirtyStore } from '$lib/components/common/confirmationModal/dirtyStore'
import { goto } from '$app/navigation'