feat(front): Add a confirmation modal (#634)

* feat(front): Add a confirmation modal

* feat(front): Simplify styles

* feat(front): Remove code duplication

* feat(front): Add shortcut + add Alert component
This commit is contained in:
Faton Ramadani
2022-09-28 16:30:54 +02:00
committed by GitHub
parent 6f2b8caa02
commit 876dc60610
9 changed files with 201 additions and 8 deletions
@@ -0,0 +1,63 @@
<script lang="ts">
import { classNames } from '$lib/utils'
import {
faCheckCircle,
faInfoCircle,
faWarning,
type IconDefinition
} from '@fortawesome/free-solid-svg-icons'
import Icon from 'svelte-awesome'
import type { AlertType } from './model'
export let type: AlertType = 'info'
export let title: string
const icons: Record<AlertType, IconDefinition> = {
info: faInfoCircle,
warning: faWarning,
error: faWarning,
success: faCheckCircle
}
const classes: Record<AlertType, Record<string, string>> = {
info: {
bgClass: 'bg-blue-50',
iconClass: 'text-blue-500',
titleClass: 'text-blue-800',
descriptionClass: 'text-blue-700'
},
warning: {
bgClass: 'bg-yellow-50',
iconClass: 'text-yellow-500',
titleClass: 'text-yellow-800',
descriptionClass: 'text-yellow-700'
},
error: {
bgClass: 'bg-red-50',
iconClass: 'text-red-500',
titleClass: 'text-red-800',
descriptionClass: 'text-red-700'
},
success: {
bgClass: 'bg-green-50',
iconClass: 'text-green-500',
titleClass: 'text-green-800',
descriptionClass: 'text-green-700'
}
}
</script>
<div class={classNames('rounded-md p-4', classes[type].bgClass)}>
<div class="flex">
<div class="flex h-8 w-8 items-center justify-center rounded-full">
<Icon data={icons[type]} class={classes[type].iconClass} />
</div>
<div class="ml-2 w-full">
<span class={classNames('text-sm font-medium', classes[type].titleClass)}>{title}</span>
<div class={classNames('mt-2 text-sm', classes[type].descriptionClass)}>
<slot />
</div>
</div>
</div>
</div>
@@ -0,0 +1 @@
export type AlertType = 'success' | 'error' | 'warning' | 'info'
@@ -0,0 +1,59 @@
<script lang="ts">
import { classNames } from '$lib/utils'
import { faWarning } from '@fortawesome/free-solid-svg-icons'
import { createEventDispatcher } from 'svelte'
import Icon from 'svelte-awesome'
import { fade } from 'svelte/transition'
import Button from '../button/Button.svelte'
export let title: string
export let confirmationText: string
export let open: boolean = false
const dispatch = createEventDispatcher()
</script>
{#if open}
<div transition:fade={{ duration: 100 }} class={'relative z-50'} role="dialog">
<div
class={classNames(
'fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity',
open ? 'ease-out duration-300 opacity-100' : 'ease-in duration-200 opacity-0'
)}
/>
<div class="fixed inset-0 z-10 overflow-y-auto">
<div class="flex min-h-full items-center justify-center p-4">
<div
class={classNames(
'relative transform overflow-hidden rounded-lg bg-white px-4 pt-5 pb-4 text-left shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-lg sm:p-6',
open
? 'ease-out duration-300 opacity-100 translate-y-0 sm:scale-100'
: 'ease-in duration-200 opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95'
)}
>
<div class="flex">
<div class="flex h-12 w-12 items-center justify-center rounded-full bg-red-100">
<Icon data={faWarning} class="text-red-500" />
</div>
<div class="ml-4 text-left flex-1">
<h3 class="text-lg font-medium text-gray-900">
{title}
</h3>
<div class="mt-2 text-sm text-gray-500">
<slot />
</div>
</div>
</div>
<div class="flex items-center space-x-2 flex-row-reverse space-x-reverse mt-4">
<Button on:click={() => dispatch('confirmed')} color="red" size="sm">
{confirmationText}
</Button>
<Button on:click={() => dispatch('canceled')} color="light" size="sm">Cancel</Button>
</div>
</div>
</div>
</div>
</div>
{/if}
@@ -7,13 +7,16 @@
import { createEventDispatcher, getContext } from 'svelte'
import Icon from 'svelte-awesome'
import type { FlowEditorContext } from '../types'
import RemoveStepConfirmationModal from './RemoveStepConfirmationModal.svelte'
export let module: FlowModule
let confirmationModalOpen = false
$: shouldPick = isEmptyFlowModule(module)
const dispatch = createEventDispatcher()
const { selectedId } = getContext<FlowEditorContext>('FlowEditorContext')
const { selectedId, select } = getContext<FlowEditorContext>('FlowEditorContext')
</script>
<div class="flex flex-row space-x-2" on:click|stopPropagation={() => undefined}>
@@ -33,11 +36,26 @@
<Button
size="xs"
color="alternative"
on:click={() => {
dispatch('delete')
on:click={(event) => {
if (event.shiftKey || shouldPick) {
dispatch('delete')
select('settings')
} else {
confirmationModalOpen = true
}
}}
>
<Icon data={faTrashAlt} class="mr-2" />
{$selectedId.includes('failure') ? 'Delete error handler' : 'Remove step'}
</Button>
</div>
<RemoveStepConfirmationModal
bind:open={confirmationModalOpen}
on:confirmed={() => {
dispatch('delete')
setTimeout(() => {
select('settings')
})
}}
/>
@@ -43,7 +43,6 @@
bind:flowModule={$flowStore.value.modules[parentIndex]}
bind:flowModuleState={$flowStateStore.modules[parentIndex]}
on:delete={() => {
select('settings')
$flowStateStore.modules.splice(parentIndex, 1)
$flowStateStore = $flowStateStore
$flowStore.value.modules.splice(parentIndex, 1)
@@ -0,0 +1,34 @@
<script lang="ts">
import Alert from '$lib/components/common/alert/Alert.svelte'
import Badge from '$lib/components/common/badge/Badge.svelte'
import ConfirmationModal from '$lib/components/common/confirmationModal/ConfirmationModal.svelte'
import { createEventDispatcher } from 'svelte'
export let open: boolean
const dispatch = createEventDispatcher()
</script>
<ConfirmationModal
{open}
title="Remove step"
confirmationText="Remove"
on:canceled={() => {
open = false
dispatch('canceled')
}}
on:confirmed={() => {
open = false
dispatch('confirmed')
}}
>
<div class="flex flex-col w-full space-y-4">
<span>Are you sure you want to remove this step?</span>
<Alert type="info" title="Bypass confirmation">
<div>
You can press
<Badge color="dark-gray">SHIFT</Badge>
while removing a step to bypass confirmation.
</div>
</Alert>
</div>
</ConfirmationModal>
@@ -49,7 +49,7 @@
</div>
{#if $flowStore.value.failure_module}
<input
<textarea
bind:value={$flowStore.value.failure_module.summary}
placeholder="Error handler summary"
/>
@@ -44,7 +44,7 @@
{#if deletable}
<button
type="button"
on:click={() => dispatch('delete')}
on:click={(event) => dispatch('delete', { event })}
class="text-gray-900 bg-white border border-gray-300 focus:outline-none hover:bg-gray-100 focus:ring-4 focus:ring-gray-200 font-medium rounded-sm text-xs px-2 py-1"
>
<Icon data={faTrashAlt} scale={0.8} />
@@ -4,12 +4,13 @@
import FlowModuleSchemaItem from './FlowModuleSchemaItem.svelte'
import Icon from 'svelte-awesome'
import { faPen, faPlus, faSliders } from '@fortawesome/free-solid-svg-icons'
import { emptyFlowModuleState } from '$lib/components/flows/flowStateUtils'
import { emptyFlowModuleState, isEmptyFlowModule } from '$lib/components/flows/flowStateUtils'
import { classNames, emptyModule } from '$lib/utils'
import type { FlowModuleState } from '../flowState'
import type { FlowModule } from '$lib/gen'
import FlowErrorHandlerItem from './FlowErrorHandlerItem.svelte'
import RemoveStepConfirmationModal from '../content/RemoveStepConfirmationModal.svelte'
export let prefix: string | undefined = undefined
export let modules: FlowModule[]
@@ -33,6 +34,8 @@
moduleStates = moduleStates
modules = modules
}
let indexToRemove: number | undefined = undefined
$: confirmationModalOpen = indexToRemove !== undefined
</script>
<div class="flex flex-col justify-between h-full">
@@ -116,7 +119,13 @@
isLast={index === modules.length - 1}
selected={$selectedId === [prefix, String(index)].filter(Boolean).join('-')}
deletable
on:delete={() => removeAtIndex(index)}
on:delete={(event) => {
if (event.detail.event.shiftKey || isEmptyFlowModule(mod)) {
removeAtIndex(index)
} else {
indexToRemove = index
}
}}
>
<div slot="icon">
<span>{index + 1}</span>
@@ -148,3 +157,13 @@
<FlowErrorHandlerItem />
{/if}
</div>
<RemoveStepConfirmationModal
bind:open={confirmationModalOpen}
on:confirmed={() => {
if (indexToRemove !== undefined) {
removeAtIndex(indexToRemove)
indexToRemove = undefined
}
}}
/>