mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-07 00:01:49 +00:00
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:
co-authored by
Ruben Fiszel
parent
4c37479b67
commit
2637fa23bb
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user