Files
windmill/frontend/src/lib/components/apps/editor/settingsPanel/StylePanel.svelte
T

279 lines
8.2 KiB
Svelte

<script lang="ts">
import { Tab, TabContent } from '$lib/components/common'
import { sendUserToast } from '$lib/toast'
import { getContext } from 'svelte'
import type { AppViewerContext, ComponentCssProperty } from '../../types'
import { ccomponents, components } from '../component'
import CssProperty from '../componentsPanel/CssProperty.svelte'
import { quickStyleProperties } from '../componentsPanel/quickStyleProperties'
import Tabs from '$lib/components/common/tabs/Tabs.svelte'
import ConfirmationModal from '$lib/components/common/confirmationModal/ConfirmationModal.svelte'
import Badge from '$lib/components/common/badge/Badge.svelte'
import Button from '$lib/components/common/button/Button.svelte'
import { enterpriseLicense } from '$lib/stores'
import Tooltip from '$lib/components/Tooltip.svelte'
import { customisationByComponent, hasStyleValue } from '../componentsPanel/cssUtils'
import CssMigrationModal from './CSSMigrationModal.svelte'
import CssPropertyWrapper from './CssPropertyWrapper.svelte'
import { onMount } from 'svelte'
import { findComponentSettings } from '../appUtils'
const { app, cssEditorOpen, selectedComponent } = getContext<AppViewerContext>('AppViewerContext')
$: component = findComponentSettings($app, $selectedComponent?.[0])?.item?.data
let tab: 'local' | 'global' = 'local'
let overrideGlobalCSS: (() => void) | undefined = undefined
let overrideLocalCSS: (() => void) | undefined = undefined
$: type = component?.type
let migrationModal: CssMigrationModal | undefined = undefined
$: customCssByComponentType =
component?.type && $app.css
? Object.entries($app.css[component.type] || {}).map(([id, v]) => ({
id,
forceStyle: v?.style != undefined,
forceClass: v?.['class'] != undefined
}))
: undefined
function copyLocalToGlobal(name: string, value: ComponentCssProperty | undefined) {
if (!value) {
sendUserToast('No local CSS to copy')
} else {
const type = component?.type
if (!type) return
if (hasStyleValue($app.css?.[type]?.[name])) {
overrideGlobalCSS = () => {
$app.css![type]![name] = JSON.parse(JSON.stringify(value))
app.set($app)
}
} else {
if (!$app.css![type]) {
initGlobalCss()
}
$app.css![type]![name] = JSON.parse(JSON.stringify(value))
app.set($app)
sendUserToast('Global CSS copied')
}
}
}
function copyGlobalToLocal(id: string, value: any) {
if (!value) {
sendUserToast('No global CSS to copy')
} else {
if (hasStyleValue(value)) {
overrideLocalCSS = () => {
component!.customCss![id] = JSON.parse(JSON.stringify(value))
app.set($app)
}
} else {
component!.customCss![id] = JSON.parse(JSON.stringify(value))
app.set($app)
sendUserToast('Local CSS copied')
}
}
}
function initGlobalCss() {
// If the global css is not initialised, we initialise it.
// Should only happen once per app
if (!$app.css) {
$app.css = {}
}
// If the global css for this component type is not initialised, we initialise it.
// Should only happen once per component type
if (
$app.css &&
component &&
!$app.css[component.type]?.style &&
components[component.type] &&
$app.css[component.type] === undefined
) {
$app.css[component.type] = JSON.parse(JSON.stringify(components[component.type].customCss))
app.set($app)
}
}
function getSelector(key: string) {
return customisationByComponent
.find((c) => c.components.includes(component?.type ?? ''))
?.selectors.find((s) => {
return s.customCssKey === key
})?.selector
}
onMount(() => {
initGlobalCss()
})
</script>
{#if component}
{#key component?.id}
<div class="px-2 flex items-center gap-2 flex-row justify-between">
{#if !cssEditorOpen}
<Button
color="blue"
size="xs2"
variant="border"
on:click={() => {
$cssEditorOpen = true
}}
>
<div class="flex flex-row gap-1 text-xs items-center">
Open CSS editor{$enterpriseLicense === undefined ? ' (EE only)' : ''}
<Tooltip light>
You can also use the App CSS Editor to customise the CSS of all components.
</Tooltip>
</div>
</Button>
{:else}
<div />
{/if}
<div class="flex flex-row gap-2 items-center justify-between">
{#if $enterpriseLicense !== undefined}
<Button
color="dark"
size="xs2"
on:click={() => {
migrationModal?.open()
}}
>
Convert to global CSS
</Button>
{/if}
</div>
</div>
<Tabs bind:selected={tab}>
<Tab value="local" size="xs">
<div class="flex flex-row gap-2 items-center">
ID
<Badge color="indigo" size="xs">
{component?.id}
</Badge>
<Tooltip light>
You can customise the CSS and the classes of this component instance. Theses
customisations will only be applied to this component.
</Tooltip>
</div>
</Tab>
<Tab value="global" size="xs">
<div class="flex flex-row gap-2 items-center">
Global: {type ? ccomponents[type].name : ''}
<Tooltip light>
You can customise the CSS and the classes of all components of this type. Theses
customisations will be applied to all components of this type.
</Tooltip>
</div>
</Tab>
<svelte:fragment slot="content">
<TabContent value="local">
{#if component && component.customCss !== undefined}
{#each Object.keys(ccomponents[component.type].customCss ?? {}) as name}
<div class="w-full">
<CssProperty
quickStyleProperties={quickStyleProperties?.[component.type]?.[name]}
forceStyle={ccomponents[component.type].customCss[name].style !== undefined}
forceClass={ccomponents[component.type].customCss[name].class !== undefined}
tooltip={ccomponents[component.type].customCss[name].tooltip}
{name}
wmClass={getSelector(name)}
componentType={component.type}
bind:value={component.customCss[name]}
on:change={() => app.set($app)}
shouldDisplayRight={hasStyleValue(component.customCss[name])}
on:right={() => {
copyLocalToGlobal(name, component?.customCss?.[name])
tab = 'global'
}}
overridding={hasStyleValue($app.css?.[component.type]?.[name]) &&
hasStyleValue(component.customCss[name])}
/>
</div>
{/each}
{/if}
</TabContent>
<TabContent value="global">
{#if type}
{#each customCssByComponentType ?? [] as { id, forceStyle, forceClass }}
<div class="w-full">
{#if $app.css && type && $app.css[type] && component?.customCss}
<CssPropertyWrapper
{forceStyle}
{forceClass}
{id}
bind:property={$app.css[type]}
on:left={() => {
copyGlobalToLocal(
id,
component?.type ? $app?.css?.[component?.type]?.[id] : undefined
)
tab = 'local'
}}
overriden={hasStyleValue(component.customCss[id])}
wmClass={getSelector(id)}
/>
{/if}
</div>
{/each}
{/if}
</TabContent>
</svelte:fragment>
</Tabs>
<ConfirmationModal
title="Confirm overriding global CSS"
confirmationText="Override global CSS"
open={Boolean(overrideGlobalCSS)}
on:confirmed={() => {
if (overrideGlobalCSS) {
overrideGlobalCSS()
overrideGlobalCSS = undefined
}
sendUserToast('Global CSS overridden')
}}
on:canceled={() => {
overrideGlobalCSS = undefined
}}
>
<div class="text-primary pb-2">
The global CSS for this component already exists. Do you want to override it?
</div>
</ConfirmationModal>
<ConfirmationModal
title="Confirm overriding local CSS"
confirmationText="Override local CSS"
open={Boolean(overrideLocalCSS)}
on:confirmed={() => {
if (overrideLocalCSS) {
overrideLocalCSS()
overrideLocalCSS = undefined
}
sendUserToast('Local CSS overridden')
}}
on:canceled={() => {
overrideLocalCSS = undefined
}}
>
<div class="text-primary pb-2">
The local CSS for this component already exists. Do you want to override it?
</div>
</ConfirmationModal>
<CssMigrationModal bind:this={migrationModal} bind:component />
{/key}
{:else}
<span class="text-sm text-secondary mx-2">Select a component to style it in this panel</span>
{/if}