feat(frontend): alert component (#3140)

* feat(frontend): add alert component

* feat(frontend): fix build

* feat(frontend): alert styling

* feat(frontend): add initial isCollapsed

---------

Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
This commit is contained in:
Faton Ramadani
2024-02-05 17:23:18 +01:00
committed by GitHub
co-authored by Ruben Fiszel
parent 4c37479b67
commit 2637fa23bb
6 changed files with 204 additions and 13 deletions
@@ -0,0 +1,80 @@
<script lang="ts">
import { getContext } from 'svelte'
import { initConfig, initOutput } from '../../editor/appUtils'
import type { AppViewerContext, ComponentCustomCSS, RichConfigurations } from '../../types'
import { initCss } from '../../utils'
import { components } from '../../editor/component'
import ResolveConfig from '../helpers/ResolveConfig.svelte'
import ResolveStyle from '../helpers/ResolveStyle.svelte'
import { twMerge } from 'tailwind-merge'
import InitializeComponent from '../helpers/InitializeComponent.svelte'
import { Alert } from '$lib/components/common'
import { AlignWrapper } from '../helpers'
export let id: string
export let configuration: RichConfigurations
export let customCss: ComponentCustomCSS<'alertcomponent'> | undefined = undefined
export let render: boolean
export let verticalAlignment: 'top' | 'center' | 'bottom' | undefined = undefined
const { app, worldStore } = getContext<AppViewerContext>('AppViewerContext')
let resolvedConfig = initConfig(
components['alertcomponent'].initialData.configuration,
configuration
)
initOutput($worldStore, id, {})
let css = initCss($app.css?.alertcomponent, customCss)
</script>
{#each Object.keys(components['alertcomponent'].initialData.configuration) as key (key)}
<ResolveConfig
{id}
{key}
bind:resolvedConfig={resolvedConfig[key]}
configuration={configuration[key]}
/>
{/each}
{#each Object.keys(css ?? {}) as key (key)}
<ResolveStyle
{id}
{customCss}
{key}
bind:css={css[key]}
componentStyle={$app.css?.alertcomponent}
/>
{/each}
<InitializeComponent {id} />
{#if render}
<AlignWrapper {verticalAlignment}>
<div
class={twMerge('w-full', css?.container?.class, 'wm-alert-card-container')}
style={css?.container?.style}
>
<Alert
title={resolvedConfig.title ?? ''}
type={resolvedConfig.type}
notRounded={resolvedConfig.notRounded}
tooltip={resolvedConfig.tooltip}
size={resolvedConfig.size}
collapsible={resolvedConfig.collapsible}
bgClass={css?.background?.class}
bgStyle={css?.background?.style}
iconClass={css?.icon?.class}
iconStyle={css?.icon?.style}
titleClass={css?.title?.class}
titleStyle={css?.title?.style}
descriptionClass={css?.description?.class}
descriptionStyle={css?.description?.style}
isCollapsed={resolvedConfig.initiallyCollapsed}
>
{resolvedConfig.description}
</Alert>
</div>
</AlignWrapper>
{/if}
@@ -69,6 +69,7 @@
import AppAgCharts from '../../components/display/charts/AppAgCharts.svelte'
import AppDbExplorer from '../../components/display/dbtable/AppDbExplorer.svelte'
import AppS3FileInput from '../../components/inputs/AppS3FileInput.svelte'
import AppAlert from '../../components/display/AppAlert.svelte'
import AppDateSliderInput from '../../components/inputs/AppDateSliderInput.svelte'
export let component: AppComponent
@@ -756,6 +757,14 @@
{componentContainerHeight}
{render}
/>
{:else if component.type === 'alertcomponent'}
<AppAlert
id={component.id}
configuration={component.configuration}
customCss={component.customCss}
verticalAlignment={component.verticalAlignment}
{render}
/>
{/if}
</div>
</div>
@@ -45,7 +45,8 @@ import {
Menu,
Network,
Database,
UploadCloud
UploadCloud,
AlertTriangle
} from 'lucide-svelte'
import type {
Aligned,
@@ -204,6 +205,8 @@ export type DecisionTreeComponent = BaseComponent<'decisiontreecomponent'> & {
nodes: DecisionTreeNode[]
}
export type AlertComponent = BaseComponent<'alertcomponent'>
export type TypedComponent =
| DBExplorerComponent
| DisplayComponent
@@ -271,6 +274,7 @@ export type TypedComponent =
| S3FileInputComponent
| AgChartsComponent
| AgChartsComponentEe
| AlertComponent
| DateSliderComponent
export type AppComponent = BaseAppComponent & TypedComponent
@@ -3244,6 +3248,75 @@ This is a paragraph.
},
componentInput: undefined
}
},
alertcomponent: {
name: 'Alert',
icon: AlertTriangle,
documentationLink: `${documentationBaseUrl}/alert`,
dims: '2:1-4:5' as AppComponentDimensions,
customCss: {
container: { class: '', style: '' },
background: { class: '', style: '' },
icon: { class: '', style: '' },
title: { class: '', style: '' },
description: { class: '', style: '' }
},
initialData: {
verticalAlignment: 'center',
componentInput: undefined,
configuration: {
type: {
fieldType: 'select',
type: 'static',
selectOptions: [
{ value: 'info', label: 'Info' },
{ value: 'success', label: 'Success' },
{ value: 'warning', label: 'Warning' },
{ value: 'error', label: 'Error' }
],
value: 'info'
},
title: {
type: 'static',
value: 'Title',
fieldType: 'text'
},
description: {
type: 'static',
value: 'Description',
fieldType: 'text'
},
notRounded: {
type: 'static',
value: false,
fieldType: 'boolean'
},
tooltip: {
type: 'static',
value: '',
fieldType: 'text'
},
size: {
type: 'static',
value: 'sm',
fieldType: 'select',
selectOptions: [
{ value: 'xs', label: 'Extra Small' },
{ value: 'sm', label: 'Small' }
]
},
collapsible: {
type: 'static',
value: false,
fieldType: 'boolean'
},
initiallyCollapsed: {
type: 'static',
value: false,
fieldType: 'boolean'
}
}
}
}
} as const
@@ -68,7 +68,8 @@ const display: ComponentSet = {
'jobidlogcomponent',
'jobidflowstatuscomponent',
'statcomponent',
'menucomponent'
'menucomponent',
'alertcomponent'
]
} as const
@@ -767,5 +767,12 @@ export const quickStyleProperties: Record<
decisiontreecomponent: {
container: containerDefaultProps
},
dbexplorercomponent: {}
dbexplorercomponent: {},
alertcomponent: {
container: containerDefaultProps,
background: containerDefaultProps,
icon: containerDefaultProps,
title: containerDefaultProps,
description: containerDefaultProps
}
}
@@ -1,5 +1,4 @@
<script lang="ts">
import { classNames } from '$lib/utils'
import type { AlertType } from './model'
import Tooltip from '$lib/components/Tooltip.svelte'
import {
@@ -21,7 +20,16 @@
export let size: 'xs' | 'sm' = 'sm'
export let collapsible: boolean = false
let isCollapsed = true
export let bgClass: string | undefined = undefined
export let bgStyle: string | undefined = undefined
export let iconClass: string | undefined = undefined
export let iconStyle: string | undefined = undefined
export let titleClass: string | undefined = undefined
export let titleStyle: string | undefined = undefined
export let descriptionClass: string | undefined = undefined
export let descriptionStyle: string | undefined = undefined
export let isCollapsed = true
const icons: Record<AlertType, any> = {
info: Info,
@@ -65,25 +73,34 @@
</script>
<div
class={classNames(
class={twMerge(
notRounded ? '' : 'rounded-md',
size === 'sm' ? 'p-4' : 'p-2',
classes[type].bgClass,
bgClass,
$$props.class
)}
style={bgStyle}
>
<div class="flex">
<div class="flex h-8 w-8 items-center justify-center rounded-full">
<svelte:component this={icons[type]} class={classes[type].iconClass} size={16} />
<svelte:component
this={icons[type]}
class={twMerge(classes[type].iconClass, iconClass)}
style={iconStyle}
size={16}
/>
</div>
<div class={twMerge('ml-2 w-full')}>
<div class={twMerge('w-full flex flex-row items-center justify-between h-8')}>
<span
class={classNames(
class={twMerge(
size === 'sm' ? 'text-sm' : 'text-xs',
'font-medium',
classes[type].titleClass
classes[type].titleClass,
titleClass
)}
style={titleStyle}
>
{title}
{#if tooltip != '' || documentationLink}
@@ -104,10 +121,12 @@
{#if $$slots.default && !isCollapsed}
<div transition:slide|local={{ duration: 200 }} class="mt-2">
<div
class={classNames(
class={twMerge(
size === 'sm' ? 'text-sm' : 'text-xs',
classes[type].descriptionClass
classes[type].descriptionClass,
descriptionClass
)}
style={descriptionStyle}
>
<slot />
</div>
@@ -116,10 +135,12 @@
{#if $$slots.default && !collapsible}
<div class="mt-2">
<div
class={classNames(
class={twMerge(
size === 'sm' ? 'text-sm' : 'text-xs',
classes[type].descriptionClass
classes[type].descriptionClass,
descriptionClass
)}
style={descriptionStyle}
>
<slot />
</div>