mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-21 00:02:23 +00:00
feat(frontend): App component style input grouping (#1334)
* feat(frontend): App component style input grouping * fix: default value * fix * update styling * fix property toggling --------- Co-authored-by: Faton Ramadani <faton.ramadani14@gmail.com>
This commit is contained in:
@@ -1,20 +1,21 @@
|
||||
<script lang="ts">
|
||||
import { Paintbrush2 } from 'lucide-svelte'
|
||||
import { createEventDispatcher, getContext } from 'svelte'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { fade } from 'svelte/transition'
|
||||
import { addWhitespaceBeforeCapitals } from '../../../../utils'
|
||||
import { Button, ClearableInput } from '../../../common'
|
||||
import Popover from '../../../Popover.svelte'
|
||||
import type { AppViewerContext, ComponentCssProperty } from '../../types'
|
||||
import type { ComponentCssProperty } from '../../types'
|
||||
import type { TypedComponent } from '../component'
|
||||
import QuickStyleMenu from './QuickStyleMenu.svelte'
|
||||
import type { StylePropertyKey } from './quickStyleProperties'
|
||||
import type { PropertyGroup } from './quickStyleProperties'
|
||||
|
||||
export let name: string
|
||||
export let value: ComponentCssProperty = {}
|
||||
export let forceStyle: boolean = false
|
||||
export let forceClass: boolean = false
|
||||
export let quickStyleProperties: StylePropertyKey[] | undefined = undefined
|
||||
const { app } = getContext<AppViewerContext>('AppViewerContext')
|
||||
export let quickStyleProperties: PropertyGroup[] | undefined = undefined
|
||||
export let componentType: TypedComponent['type'] | undefined = undefined
|
||||
const dispatch = createEventDispatcher()
|
||||
let isQuickMenuOpen = false
|
||||
|
||||
@@ -26,12 +27,12 @@
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="sticky top-0 z-20 text-lg bg-white font-semibold [font-variant:small-caps] text-gray-700 pt-2 pb-1"
|
||||
class="sticky top-0 z-20 text-lg bg-gray-100 font-semibold lowercase leading-none [font-variant:small-caps] text-gray-700 px-3 pb-1 mt-4 mb-1"
|
||||
>
|
||||
{addWhitespaceBeforeCapitals(name)}
|
||||
</div>
|
||||
{#if value}
|
||||
<div class="border-l border-gray-400/80 py-1 pl-3.5 ml-0.5">
|
||||
<div class="px-3">
|
||||
{#if value.style !== undefined || forceStyle}
|
||||
<div class="pb-2">
|
||||
<!-- svelte-ignore a11y-label-has-associated-control -->
|
||||
@@ -54,7 +55,7 @@
|
||||
color="light"
|
||||
size="xs"
|
||||
btnClasses="!p-1 !w-[34px] !h-[34px] {isQuickMenuOpen
|
||||
? '!bg-gray-200/60 hover:!bg-gray-200'
|
||||
? '!bg-gray-200/60 hover:!bg-gray-200 focus:!bg-gray-200'
|
||||
: ''}"
|
||||
aria-label="{isQuickMenuOpen ? 'Close' : 'Open'} styling menu"
|
||||
on:click={toggleQuickMenu}
|
||||
@@ -71,7 +72,12 @@
|
||||
</label>
|
||||
{#if quickStyleProperties?.length && isQuickMenuOpen}
|
||||
<div transition:fade|local={{ duration: 200 }} class="w-full pt-1">
|
||||
<QuickStyleMenu bind:value={value.style} properties={quickStyleProperties} />
|
||||
<QuickStyleMenu
|
||||
bind:value={value.style}
|
||||
properties={quickStyleProperties}
|
||||
{componentType}
|
||||
componentProperty={name}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -2,10 +2,15 @@
|
||||
import { slide } from 'svelte/transition'
|
||||
import { ChevronDown } from 'lucide-svelte'
|
||||
import { isOpenStore } from './store'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { createEventDispatcher, onMount } from 'svelte'
|
||||
|
||||
export let title: string
|
||||
export let prefix: string | undefined = undefined
|
||||
export let openByDefault: boolean = false
|
||||
export let wrapperClasses = ''
|
||||
export let toggleClasses = ''
|
||||
export let contentWrapperClasses = ''
|
||||
export let isOpen = false
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
@@ -13,14 +18,20 @@
|
||||
$: isOpen = prefix ? $isOpenStore[storeTitle] : true
|
||||
|
||||
$: dispatch('open', isOpen)
|
||||
|
||||
onMount(() => {
|
||||
if (prefix !== undefined && !(prefix + title in $isOpenStore)) {
|
||||
$isOpenStore[prefix + title] = openByDefault
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<section class="pt-1 pb-2 px-1">
|
||||
<section class="pt-1 pb-2 px-1 {wrapperClasses}">
|
||||
{#if prefix !== undefined}
|
||||
<button
|
||||
on:click|preventDefault={() => isOpenStore.toggle(storeTitle)}
|
||||
class="w-full flex justify-between items-center text-gray-700 px-2 py-1
|
||||
rounded-sm duration-200 hover:bg-gray-100"
|
||||
rounded-sm duration-200 hover:bg-gray-100 {toggleClasses}"
|
||||
>
|
||||
<h1 class="text-sm font-semibold text-left">
|
||||
<slot name="title">
|
||||
@@ -30,7 +41,7 @@
|
||||
<ChevronDown class="rotate-0 duration-300 {isOpen ? '!rotate-180' : ''}" />
|
||||
</button>
|
||||
{#if isOpen}
|
||||
<div transition:slide|local={{ duration: 300 }} class="px-2">
|
||||
<div transition:slide|local={{ duration: 300 }} class="px-2 {contentWrapperClasses}">
|
||||
<slot />
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -8,18 +8,24 @@
|
||||
StylePropertyType,
|
||||
StylePropertyUnits,
|
||||
STYLE_STORE_KEY,
|
||||
type PropertyGroup,
|
||||
type StylePropertyKey,
|
||||
type TopColors
|
||||
} from './quickStyleProperties'
|
||||
import QuickStyleProperty from './QuickStyleProperty.svelte'
|
||||
import ListItem from './ListItem.svelte'
|
||||
import type { TypedComponent } from '../component'
|
||||
|
||||
export let value = ''
|
||||
export let properties: StylePropertyKey[]
|
||||
export let properties: PropertyGroup[]
|
||||
export let componentType: TypedComponent['type'] | undefined = undefined
|
||||
export let componentProperty: string | undefined = undefined
|
||||
const { app } = getContext<AppViewerContext>('AppViewerContext')
|
||||
const styleStore = createStyleStore(properties)
|
||||
setContext(STYLE_STORE_KEY, styleStore)
|
||||
let multiValues: Record<number, string[]> = initiateMultiValues()
|
||||
let mounted = false
|
||||
let isOpen: Record<string, boolean> = {}
|
||||
|
||||
$: mounted && $styleStore && writeStyle()
|
||||
$: mounted && (!value || value) && parseStyle()
|
||||
@@ -154,32 +160,70 @@
|
||||
$styleStore.style[index].value = values.join(' ').trim()
|
||||
}
|
||||
|
||||
function formatKebabCase(text: string) {
|
||||
text = text.toLowerCase().replace(/-/, ' ')
|
||||
if (text.length) {
|
||||
text = text[0].toUpperCase() + text.slice(1)
|
||||
}
|
||||
return text
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
parseStyle()
|
||||
mounted = true
|
||||
})
|
||||
</script>
|
||||
|
||||
<div class="bg-gray-200/60 rounded-md p-2">
|
||||
{#each $styleStore.style as { prop }, index}
|
||||
<div class="pb-3 last:pb-0">
|
||||
<div class="text-sm font-medium text-gray-600 pb-0.5"> {prop.key} </div>
|
||||
<div class="flex items-center gap-1 w-full">
|
||||
{#if Array.isArray(prop.value)}
|
||||
<div class="flex justify-start items-center flex-wrap gap-x-4 gap-y-1">
|
||||
{#each prop.value as value, i}
|
||||
<QuickStyleProperty
|
||||
prop={{ ...prop, value }}
|
||||
inline
|
||||
bind:value={multiValues[index][i]}
|
||||
on:change={() => setMultiValueProperty(index)}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
{:else}
|
||||
<QuickStyleProperty {prop} bind:value={$styleStore.style[index].value} />
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<div class="pb-2 pt-1">
|
||||
{#each properties as property, i}
|
||||
{#each Object.keys(property) as group, j}
|
||||
{@const prefix = `${componentType}_${componentProperty}_${group}`}
|
||||
<ListItem
|
||||
bind:isOpen={isOpen[prefix]}
|
||||
title={group}
|
||||
{prefix}
|
||||
openByDefault={true}
|
||||
wrapperClasses="!px-0 !pt-0"
|
||||
toggleClasses="border-b border-gray-300 !rounded-b-none !py-0
|
||||
{isOpen[prefix] ? '!bg-gray-100 hover:!bg-gray-200' : ''}"
|
||||
>
|
||||
<svelte:fragment slot="title">
|
||||
<span class="font-semibold text-gray-600 capitalize">
|
||||
{group}
|
||||
</span>
|
||||
</svelte:fragment>
|
||||
<div class="flex justify-start items-center flex-wrap gap-x-4 gap-y-1 pt-3">
|
||||
{#each property[group] as p}
|
||||
{@const {
|
||||
prop: { prop },
|
||||
index
|
||||
} = styleStore.getProp(p)}
|
||||
{#if prop !== undefined && index !== undefined}
|
||||
<div class="pb-2 pt-1">
|
||||
<div class="text-sm font-medium text-gray-600 pb-0.5">
|
||||
{formatKebabCase(prop.key)}
|
||||
</div>
|
||||
<div class="flex items-center gap-1">
|
||||
{#if Array.isArray(prop.value)}
|
||||
<div class="flex justify-start items-center flex-wrap gap-x-4 gap-y-1">
|
||||
{#each prop.value as value, i}
|
||||
<QuickStyleProperty
|
||||
prop={{ ...prop, value }}
|
||||
inline
|
||||
bind:value={multiValues[index][i]}
|
||||
on:change={() => setMultiValueProperty(index)}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
{:else}
|
||||
<QuickStyleProperty {prop} inline bind:value={$styleStore.style[index].value} />
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
</ListItem>
|
||||
{/each}
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
@@ -60,6 +60,7 @@
|
||||
{:else if type === StylePropertyType.number}
|
||||
<ClearableInput
|
||||
type="number"
|
||||
inputClass={inline ? '!w-[90px]' : ''}
|
||||
bind:value
|
||||
step={prop.value?.['step'] || 1}
|
||||
min={prop.value?.['min']}
|
||||
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
Underline
|
||||
} from 'lucide-svelte'
|
||||
import type { components } from '../component'
|
||||
import type { SvelteComponent } from 'svelte'
|
||||
|
||||
export const STYLE_STORE_KEY = 'style_store' as const
|
||||
|
||||
@@ -33,8 +34,12 @@ export type StyleStoreValue = {
|
||||
topColors: TopColors
|
||||
}
|
||||
|
||||
export function createStyleStore(properties: StylePropertyKey[]) {
|
||||
const style = StyleProperty.filter((p) => properties.includes(p.key)).map((p) => ({
|
||||
export function createStyleStore(properties: PropertyGroup[]) {
|
||||
const propertyNames = properties.reduce((acc, p) => {
|
||||
Object.values(p).forEach((names) => acc.push(...names))
|
||||
return acc
|
||||
}, [] as string[])
|
||||
const style = StyleProperty.filter((p) => propertyNames.includes(p.key)).map((p) => ({
|
||||
prop: p,
|
||||
value: '' as string | undefined
|
||||
}))
|
||||
@@ -93,10 +98,45 @@ export const StylePropertyUnits = ['px', 'em', 'rem', '%', 'vh', 'vw']
|
||||
|
||||
export type TopColors = [] | [string] | [string, string] | [string, string, string]
|
||||
|
||||
export type StylePropertyOption = {
|
||||
text: string
|
||||
icon: string | typeof SvelteComponent
|
||||
}
|
||||
|
||||
export type BaseStylePropertyValue<T extends StylePropertyType> = {
|
||||
type: T
|
||||
title?: string
|
||||
}
|
||||
|
||||
export type StylePropertyColorValue = BaseStylePropertyValue<StylePropertyType.color>
|
||||
|
||||
export type StylePropertyUnitValue = BaseStylePropertyValue<StylePropertyType.unit>
|
||||
|
||||
export type StylePropertyNumberValue = BaseStylePropertyValue<StylePropertyType.number> & {
|
||||
step?: number
|
||||
min?: number
|
||||
max?: number
|
||||
}
|
||||
|
||||
export type StylePropertyTextValue = BaseStylePropertyValue<StylePropertyType.text> & {
|
||||
options?: StylePropertyOption[]
|
||||
}
|
||||
|
||||
export type StylePropertyValue =
|
||||
| StylePropertyColorValue
|
||||
| StylePropertyUnitValue
|
||||
| StylePropertyNumberValue
|
||||
| StylePropertyTextValue
|
||||
|
||||
export type StyleProperty = {
|
||||
key: string
|
||||
value: StylePropertyValue | StylePropertyValue[]
|
||||
}
|
||||
|
||||
export type StylePropertyKey = (typeof StyleProperty)[number]['key']
|
||||
|
||||
// Using an array instead of an object to preserve the order of the properties
|
||||
export const StyleProperty = [
|
||||
export const StyleProperty: StyleProperty[] = [
|
||||
{
|
||||
key: 'display',
|
||||
value: {
|
||||
@@ -277,18 +317,18 @@ export const StyleProperty = [
|
||||
type: StylePropertyType.color
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'font-size',
|
||||
value: {
|
||||
type: StylePropertyType.unit
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'font-family',
|
||||
value: {
|
||||
type: StylePropertyType.text
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'font-size',
|
||||
value: {
|
||||
type: StylePropertyType.unit
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'font-weight',
|
||||
value: {
|
||||
@@ -421,67 +461,91 @@ export const StyleProperty = [
|
||||
]
|
||||
}
|
||||
}
|
||||
] as const
|
||||
|
||||
const allDefaultProps = StyleProperty.map(({ key }) => key)
|
||||
|
||||
const containerDefaultProps: StylePropertyKey[] = [
|
||||
'padding',
|
||||
'opacity',
|
||||
'border',
|
||||
'border-radius',
|
||||
'box-shadow',
|
||||
'background-color',
|
||||
'overflow'
|
||||
]
|
||||
|
||||
const textDefaultProps: StylePropertyKey[] = [
|
||||
'padding',
|
||||
'opacity',
|
||||
'background-color',
|
||||
'color',
|
||||
'font-size',
|
||||
'font-family',
|
||||
'font-weight',
|
||||
'font-style',
|
||||
'text-align',
|
||||
'text-decoration',
|
||||
'text-transform',
|
||||
'line-height',
|
||||
'letter-spacing',
|
||||
'word-spacing'
|
||||
export type PropertyGroup = Record<string, StylePropertyKey[]>
|
||||
|
||||
// Property groups
|
||||
const layoutGrouping: PropertyGroup = {
|
||||
layout: ['display', 'overflow']
|
||||
}
|
||||
|
||||
const sizeGrouping: PropertyGroup = {
|
||||
size: ['width', 'min-width', 'max-width', 'height', 'min-height', 'max-height']
|
||||
}
|
||||
|
||||
const spacingGrouping: PropertyGroup = {
|
||||
spacing: ['padding']
|
||||
}
|
||||
|
||||
const borderGrouping: PropertyGroup = {
|
||||
borders: ['border', 'border-radius']
|
||||
}
|
||||
|
||||
const typographyGrouping: PropertyGroup = {
|
||||
typography: [
|
||||
'color',
|
||||
'font-size',
|
||||
'font-family',
|
||||
'font-weight',
|
||||
'font-style',
|
||||
'text-align',
|
||||
'text-decoration',
|
||||
'text-transform',
|
||||
'line-height',
|
||||
'letter-spacing',
|
||||
'word-spacing'
|
||||
]
|
||||
}
|
||||
|
||||
const backgroundGrouping: PropertyGroup = {
|
||||
background: ['background-color', 'opacity', 'box-shadow']
|
||||
}
|
||||
|
||||
const miscGrouping: PropertyGroup = {
|
||||
miscellaneous: ['cursor']
|
||||
}
|
||||
|
||||
// Commonly used-together property groups
|
||||
const containerDefaultProps: PropertyGroup[] = [
|
||||
layoutGrouping,
|
||||
spacingGrouping,
|
||||
backgroundGrouping,
|
||||
borderGrouping
|
||||
]
|
||||
|
||||
const sizeDefaultProps: StylePropertyKey[] = [
|
||||
'width',
|
||||
'min-width',
|
||||
'max-width',
|
||||
'height',
|
||||
'min-height',
|
||||
'max-height'
|
||||
const buttonDefaultProps: PropertyGroup[] = [
|
||||
backgroundGrouping,
|
||||
borderGrouping,
|
||||
typographyGrouping,
|
||||
miscGrouping
|
||||
]
|
||||
|
||||
const buttonDefaultProps: StylePropertyKey[] = [
|
||||
'cursor',
|
||||
'border',
|
||||
'border-radius',
|
||||
'box-shadow',
|
||||
...textDefaultProps
|
||||
const inputDefaultProps: PropertyGroup[] = [borderGrouping, typographyGrouping, miscGrouping]
|
||||
|
||||
const sliderDefaultProps: PropertyGroup[] = [
|
||||
{
|
||||
colors: ['color', 'background-color', 'opacity']
|
||||
},
|
||||
borderGrouping,
|
||||
miscGrouping
|
||||
]
|
||||
|
||||
const inputDefaultProps: StylePropertyKey[] = [
|
||||
'cursor',
|
||||
'border',
|
||||
'border-radius',
|
||||
...textDefaultProps
|
||||
const dividerDefaultProps: PropertyGroup[] = [
|
||||
{
|
||||
colors: ['background-color', 'opacity']
|
||||
},
|
||||
sizeGrouping,
|
||||
spacingGrouping,
|
||||
borderGrouping
|
||||
]
|
||||
|
||||
export const quickStyleProperties: Record<
|
||||
keyof typeof components,
|
||||
Record<string, StylePropertyKey[]>
|
||||
Record<string, PropertyGroup[]>
|
||||
> = {
|
||||
mapcomponent: {
|
||||
map: containerDefaultProps
|
||||
map: [spacingGrouping, borderGrouping]
|
||||
},
|
||||
pdfcomponent: {
|
||||
container: containerDefaultProps
|
||||
@@ -491,40 +555,43 @@ export const quickStyleProperties: Record<
|
||||
button: buttonDefaultProps
|
||||
},
|
||||
htmlcomponent: {
|
||||
container: allDefaultProps
|
||||
container: [
|
||||
layoutGrouping,
|
||||
sizeGrouping,
|
||||
spacingGrouping,
|
||||
borderGrouping,
|
||||
typographyGrouping,
|
||||
backgroundGrouping
|
||||
]
|
||||
},
|
||||
iconcomponent: {
|
||||
container: containerDefaultProps,
|
||||
icon: [
|
||||
'padding',
|
||||
'opacity',
|
||||
'cursor',
|
||||
'width',
|
||||
'min-width',
|
||||
'max-width',
|
||||
'height',
|
||||
'min-height',
|
||||
'max-height',
|
||||
'color'
|
||||
{
|
||||
colors: ['color', 'background-color', 'opacity']
|
||||
},
|
||||
sizeGrouping,
|
||||
spacingGrouping,
|
||||
miscGrouping
|
||||
]
|
||||
},
|
||||
tabscomponent: {
|
||||
tabRow: containerDefaultProps,
|
||||
allTabs: [...textDefaultProps, ...sizeDefaultProps],
|
||||
selectedTab: [...textDefaultProps, ...sizeDefaultProps],
|
||||
allTabs: [typographyGrouping, sizeGrouping],
|
||||
selectedTab: [typographyGrouping, sizeGrouping],
|
||||
container: containerDefaultProps
|
||||
},
|
||||
textcomponent: {
|
||||
text: textDefaultProps
|
||||
text: [typographyGrouping]
|
||||
},
|
||||
imagecomponent: {
|
||||
image: containerDefaultProps
|
||||
},
|
||||
rangecomponent: {
|
||||
handles: ['opacity', 'cursor', 'border', 'border-radius', 'background-color'],
|
||||
bar: ['opacity', 'cursor', 'border', 'border-radius', 'background-color'],
|
||||
limits: textDefaultProps,
|
||||
values: textDefaultProps
|
||||
handles: sliderDefaultProps,
|
||||
bar: sliderDefaultProps,
|
||||
limits: [typographyGrouping],
|
||||
values: [typographyGrouping]
|
||||
},
|
||||
tablecomponent: {
|
||||
tableHeader: containerDefaultProps,
|
||||
@@ -544,20 +611,20 @@ export const quickStyleProperties: Record<
|
||||
input: inputDefaultProps
|
||||
},
|
||||
slidercomponent: {
|
||||
handles: ['opacity', 'cursor', 'border', 'border-radius', 'background-color'],
|
||||
bar: ['opacity', 'cursor', 'border', 'border-radius', 'background-color'],
|
||||
limits: textDefaultProps,
|
||||
values: textDefaultProps
|
||||
handles: sliderDefaultProps,
|
||||
bar: sliderDefaultProps,
|
||||
limits: [typographyGrouping],
|
||||
values: [typographyGrouping]
|
||||
},
|
||||
displaycomponent: {
|
||||
header: [...containerDefaultProps, ...textDefaultProps],
|
||||
header: [...containerDefaultProps, typographyGrouping],
|
||||
container: containerDefaultProps
|
||||
},
|
||||
barchartcomponent: {
|
||||
container: containerDefaultProps
|
||||
},
|
||||
checkboxcomponent: {
|
||||
text: textDefaultProps
|
||||
text: [typographyGrouping]
|
||||
},
|
||||
currencycomponent: {
|
||||
input: inputDefaultProps
|
||||
@@ -607,35 +674,11 @@ export const quickStyleProperties: Record<
|
||||
input: inputDefaultProps
|
||||
},
|
||||
verticaldividercomponent: {
|
||||
divider: [
|
||||
'padding',
|
||||
'opacity',
|
||||
'width',
|
||||
'min-width',
|
||||
'max-width',
|
||||
'height',
|
||||
'min-height',
|
||||
'max-height',
|
||||
'border',
|
||||
'border-radius',
|
||||
'background-color'
|
||||
],
|
||||
divider: dividerDefaultProps,
|
||||
container: containerDefaultProps
|
||||
},
|
||||
horizontaldividercomponent: {
|
||||
divider: [
|
||||
'padding',
|
||||
'opacity',
|
||||
'width',
|
||||
'min-width',
|
||||
'max-width',
|
||||
'height',
|
||||
'min-height',
|
||||
'max-height',
|
||||
'border',
|
||||
'border-radius',
|
||||
'background-color'
|
||||
],
|
||||
divider: dividerDefaultProps,
|
||||
container: containerDefaultProps
|
||||
},
|
||||
verticalsplitpanescomponent: {
|
||||
|
||||
@@ -7,6 +7,7 @@ export type IsOpenStoreItem = Record<string, boolean>
|
||||
export const isOpenStore = {
|
||||
subscribe: store.subscribe,
|
||||
update: store.update,
|
||||
set: store.set,
|
||||
/** If an item is already set, it won't get updated. */
|
||||
addItems: (items: IsOpenStoreItem[]) => {
|
||||
let newItems = {}
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
color="light"
|
||||
size="xs"
|
||||
aria-label="Apply to all instances of this component"
|
||||
btnClasses="ml-3 mt-2"
|
||||
on:click={applyToAllInstances}
|
||||
>
|
||||
Copy style to global CSS <Copy size={18} />
|
||||
@@ -60,6 +61,7 @@
|
||||
forceStyle={ccomponents[component.type].customCss[name].style !== undefined}
|
||||
forceClass={ccomponents[component.type].customCss[name].class !== undefined}
|
||||
{name}
|
||||
componentType={component.type}
|
||||
bind:value={component.customCss[name]}
|
||||
on:change={() => app.set($app)}
|
||||
/>
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
box-shadow: 0 10px 40px -5px rgba(0, 0, 0, 0.25) !important;
|
||||
border-style: none !important;
|
||||
border-radius: 0.375rem !important;
|
||||
z-index: 20;
|
||||
z-index: 30;
|
||||
}
|
||||
|
||||
.color-picker-input .slider-wrapper {
|
||||
|
||||
+2
-2
@@ -27,7 +27,7 @@
|
||||
id={SECONDARY_MENU_ID}
|
||||
class="flex flex-col w-full h-full bg-white"
|
||||
>
|
||||
<div class="flex justify-between items-center bg-white gap-1 p-3">
|
||||
<div class="flex justify-between items-center bg-white gap-1 px-3 py-2">
|
||||
<Button
|
||||
color="light"
|
||||
size="xs"
|
||||
@@ -39,7 +39,7 @@
|
||||
</Button>
|
||||
<Badge color="blue">{$selectedComponent}</Badge>
|
||||
</div>
|
||||
<div class="relative h-full overflow-y-auto px-3 pb-3">
|
||||
<div class="relative h-full overflow-y-auto">
|
||||
{#if typeof $secondaryMenu.component === 'string'}
|
||||
{@html $secondaryMenu.component}
|
||||
{:else}
|
||||
|
||||
Reference in New Issue
Block a user