windmill-components 1.82.0 → 1.82.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components/Multiselect.svelte.d.ts +2 -2
- package/components/apps/components/buttons/AppFormButton.svelte +58 -70
- package/components/apps/editor/AppEditor.svelte +14 -12
- package/components/apps/editor/AppPreview.svelte +1 -2
- package/components/apps/editor/component/ComponentNavigation.svelte +2 -1
- package/components/apps/editor/component/README.md +4 -0
- package/components/apps/editor/componentsPanel/CssProperty.svelte +87 -47
- package/components/apps/editor/componentsPanel/CssProperty.svelte.d.ts +5 -2
- package/components/apps/editor/componentsPanel/QuickStyleMenu.svelte +167 -0
- package/components/apps/editor/componentsPanel/QuickStyleMenu.svelte.d.ts +18 -0
- package/components/apps/editor/componentsPanel/QuickStyleProperty.svelte +130 -0
- package/components/apps/editor/componentsPanel/QuickStyleProperty.svelte.d.ts +21 -0
- package/components/apps/editor/componentsPanel/quickStyleProperties.d.ts +535 -0
- package/components/apps/editor/componentsPanel/quickStyleProperties.js +595 -0
- package/components/apps/editor/settingsPanel/ComponentPanel.svelte +24 -14
- package/components/apps/editor/settingsPanel/StylePanel.svelte +23 -0
- package/components/apps/editor/settingsPanel/StylePanel.svelte.d.ts +17 -0
- package/components/apps/editor/settingsPanel/inputEditor/ColorInput.svelte +12 -12
- package/components/apps/editor/settingsPanel/inputEditor/ColorInput.svelte.d.ts +3 -2
- package/components/apps/editor/settingsPanel/inputEditor/StaticInputEditor.svelte +1 -1
- package/components/apps/editor/settingsPanel/secondaryMenu/SecondaryMenu.svelte +47 -0
- package/components/apps/editor/settingsPanel/secondaryMenu/SecondaryMenu.svelte.d.ts +14 -0
- package/components/apps/editor/settingsPanel/secondaryMenu/index.d.ts +2 -0
- package/components/apps/editor/settingsPanel/secondaryMenu/index.js +2 -0
- package/components/apps/editor/settingsPanel/secondaryMenu/menuStore.d.ts +12 -0
- package/components/apps/editor/settingsPanel/secondaryMenu/menuStore.js +10 -0
- package/components/apps/types.d.ts +2 -1
- package/components/common/button/ButtonPopup.svelte +5 -2
- package/components/common/button/ButtonPopup.svelte.d.ts +5 -1
- package/components/common/button/ButtonPopupItem.svelte +2 -1
- package/components/common/button/ButtonPopupItem.svelte.d.ts +1 -0
- package/components/common/clearableInput/ClearableInput.svelte +56 -0
- package/components/common/clearableInput/ClearableInput.svelte.d.ts +28 -0
- package/components/common/index.d.ts +1 -0
- package/components/common/index.js +1 -0
- package/components/common/kbd/Kbd.svelte +4 -1
- package/components/common/kbd/Kbd.svelte.d.ts +6 -14
- package/components/common/menu/Menu.svelte +8 -2
- package/components/common/menu/Menu.svelte.d.ts +4 -1
- package/components/common/modal/AlwaysMountedModal.svelte +109 -0
- package/components/common/modal/AlwaysMountedModal.svelte.d.ts +22 -0
- package/package.json +672 -662
- package/utils.d.ts +1 -0
- package/utils.js +3 -0
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
export default class Multiselect extends SvelteComponentTyped<{
|
|
5
5
|
value?: any[] | undefined;
|
|
6
6
|
id?: string | undefined;
|
|
7
|
-
readonly?: boolean | undefined;
|
|
8
7
|
placeholder?: string | undefined;
|
|
8
|
+
readonly?: boolean | undefined;
|
|
9
9
|
}, {
|
|
10
10
|
[evt: string]: CustomEvent<any>;
|
|
11
11
|
}, {
|
|
@@ -20,8 +20,8 @@ declare const __propDef: {
|
|
|
20
20
|
props: {
|
|
21
21
|
value?: any[] | undefined;
|
|
22
22
|
id?: string | undefined;
|
|
23
|
-
readonly?: boolean | undefined;
|
|
24
23
|
placeholder?: string | undefined;
|
|
24
|
+
readonly?: boolean | undefined;
|
|
25
25
|
};
|
|
26
26
|
events: {
|
|
27
27
|
[evt: string]: CustomEvent<any>;
|
|
@@ -3,14 +3,12 @@ import { faUser } from '@fortawesome/free-solid-svg-icons';
|
|
|
3
3
|
import { getContext } from 'svelte';
|
|
4
4
|
import { Icon } from 'svelte-awesome';
|
|
5
5
|
import AlignWrapper from '../helpers/AlignWrapper.svelte';
|
|
6
|
-
import InputValue from '../helpers/InputValue.svelte';
|
|
7
6
|
import RunnableWrapper from '../helpers/RunnableWrapper.svelte';
|
|
8
|
-
import Portal from 'svelte-portal';
|
|
9
|
-
import Modal from '../../../common/modal/Modal.svelte';
|
|
10
7
|
import { concatCustomCss } from '../../utils';
|
|
11
8
|
import { initConfig, initOutput } from '../../editor/appUtils';
|
|
12
9
|
import { components } from '../../editor/component';
|
|
13
10
|
import ResolveConfig from '../helpers/ResolveConfig.svelte';
|
|
11
|
+
import AlwaysMountedModal, { getModal } from '../../../common/modal/AlwaysMountedModal.svelte';
|
|
14
12
|
export let id;
|
|
15
13
|
export let componentInput;
|
|
16
14
|
export let configuration;
|
|
@@ -30,7 +28,6 @@ let runnableComponent;
|
|
|
30
28
|
let isLoading = false;
|
|
31
29
|
let ownClick = false;
|
|
32
30
|
let errors = {};
|
|
33
|
-
let open = false;
|
|
34
31
|
$: errorsMessage = Object.values(errors)
|
|
35
32
|
.filter((x) => x != '')
|
|
36
33
|
.join('\n');
|
|
@@ -62,73 +59,64 @@ let runnableWrapper;
|
|
|
62
59
|
/>
|
|
63
60
|
{/each}
|
|
64
61
|
|
|
65
|
-
<
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
}
|
|
62
|
+
<AlwaysMountedModal
|
|
63
|
+
title={resolvedConfig.label ?? ''}
|
|
64
|
+
class={css?.popup?.class}
|
|
65
|
+
style={css?.popup?.style}
|
|
66
|
+
>
|
|
67
|
+
<RunnableWrapper
|
|
68
|
+
bind:this={runnableWrapper}
|
|
69
|
+
{recomputeIds}
|
|
70
|
+
{render}
|
|
71
|
+
bind:runnableComponent
|
|
72
|
+
{componentInput}
|
|
73
|
+
{id}
|
|
74
|
+
{extraQueryParams}
|
|
75
|
+
autoRefresh={false}
|
|
76
|
+
forceSchemaDisplay={true}
|
|
77
|
+
runnableClass="!block"
|
|
78
|
+
{outputs}
|
|
79
|
+
doOnSuccess={resolvedConfig.onSuccess}
|
|
77
80
|
>
|
|
78
|
-
<
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
<div class="flex
|
|
93
|
-
<
|
|
94
|
-
{
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
}}
|
|
114
|
-
on:click={async () => {
|
|
115
|
-
if (!runnableComponent) {
|
|
116
|
-
runnableWrapper.onSuccess()
|
|
117
|
-
} else {
|
|
118
|
-
await runnableComponent?.runComponent()
|
|
119
|
-
}
|
|
120
|
-
open = false
|
|
121
|
-
}}
|
|
122
|
-
size="xs"
|
|
123
|
-
color="dark"
|
|
124
|
-
>
|
|
125
|
-
Submit
|
|
126
|
-
</Button>
|
|
127
|
-
</div>
|
|
81
|
+
<div class="flex flex-col gap-2 px-4 w-full">
|
|
82
|
+
<div>
|
|
83
|
+
{#if noInputs}
|
|
84
|
+
<div class="text-gray-600 italic text-sm my-4">
|
|
85
|
+
Run forms are associated with a runnable that has user inputs.
|
|
86
|
+
<br />
|
|
87
|
+
Once a script or flow is chosen, set some <strong>Runnable Inputs</strong> to
|
|
88
|
+
<strong>
|
|
89
|
+
User Input
|
|
90
|
+
<Icon data={faUser} scale={1.3} class="rounded-sm bg-gray-200 p-1 ml-0.5" />
|
|
91
|
+
</strong>
|
|
92
|
+
</div>
|
|
93
|
+
{/if}
|
|
94
|
+
</div>
|
|
95
|
+
<div class="flex justify-end">
|
|
96
|
+
<Button
|
|
97
|
+
{loading}
|
|
98
|
+
btnClasses="my-1"
|
|
99
|
+
on:pointerdown={(e) => {
|
|
100
|
+
e?.stopPropagation()
|
|
101
|
+
window.dispatchEvent(new Event('pointerup'))
|
|
102
|
+
}}
|
|
103
|
+
on:click={async () => {
|
|
104
|
+
if (!runnableComponent) {
|
|
105
|
+
runnableWrapper.onSuccess()
|
|
106
|
+
} else {
|
|
107
|
+
await runnableComponent?.runComponent()
|
|
108
|
+
}
|
|
109
|
+
getModal().close()
|
|
110
|
+
}}
|
|
111
|
+
size="xs"
|
|
112
|
+
color="dark"
|
|
113
|
+
>
|
|
114
|
+
Submit
|
|
115
|
+
</Button>
|
|
128
116
|
</div>
|
|
129
|
-
</
|
|
130
|
-
</
|
|
131
|
-
</
|
|
117
|
+
</div>
|
|
118
|
+
</RunnableWrapper>
|
|
119
|
+
</AlwaysMountedModal>
|
|
132
120
|
|
|
133
121
|
<AlignWrapper {horizontalAlignment} {verticalAlignment}>
|
|
134
122
|
{#if errorsMessage}
|
|
@@ -141,7 +129,7 @@ let runnableWrapper;
|
|
|
141
129
|
btnClasses={css?.button?.class ?? ''}
|
|
142
130
|
style={css?.button?.style ?? ''}
|
|
143
131
|
on:click={(e) => {
|
|
144
|
-
open
|
|
132
|
+
getModal().open()
|
|
145
133
|
}}
|
|
146
134
|
>
|
|
147
135
|
{resolvedConfig.label}
|
|
@@ -10,8 +10,7 @@ import Tabs from '../../common/tabs/Tabs.svelte';
|
|
|
10
10
|
import TabContent from '../../common/tabs/TabContent.svelte';
|
|
11
11
|
import { Alert, Button, Tab } from '../../common';
|
|
12
12
|
import ComponentList from './componentsPanel/ComponentList.svelte';
|
|
13
|
-
import
|
|
14
|
-
import { faCode, faPlus, faSliders } from '@fortawesome/free-solid-svg-icons';
|
|
13
|
+
import { faPlus } from '@fortawesome/free-solid-svg-icons';
|
|
15
14
|
import ContextPanel from './contextPanel/ContextPanel.svelte';
|
|
16
15
|
import { classNames, encodeState } from '../../../utils';
|
|
17
16
|
import AppPreview from './AppPreview.svelte';
|
|
@@ -26,6 +25,8 @@ import { initHistory } from '../../../history';
|
|
|
26
25
|
import ComponentNavigation from './component/ComponentNavigation.svelte';
|
|
27
26
|
import ItemPicker from '../../ItemPicker.svelte';
|
|
28
27
|
import VariableEditor from '../../VariableEditor.svelte';
|
|
28
|
+
import { SecondaryMenu } from './settingsPanel/secondaryMenu';
|
|
29
|
+
import { Component, Paintbrush, Plus } from 'lucide-svelte';
|
|
29
30
|
export let app;
|
|
30
31
|
export let path;
|
|
31
32
|
export let initialMode = 'dnd';
|
|
@@ -160,7 +161,7 @@ let variableEditor = undefined;
|
|
|
160
161
|
<Pane size={15} minSize={5} maxSize={33}>
|
|
161
162
|
<ContextPanel />
|
|
162
163
|
</Pane>
|
|
163
|
-
<Pane size={
|
|
164
|
+
<Pane size={63}>
|
|
164
165
|
<SplitPanesWrapper>
|
|
165
166
|
<Splitpanes horizontal>
|
|
166
167
|
<Pane size={70}>
|
|
@@ -201,31 +202,32 @@ let variableEditor = undefined;
|
|
|
201
202
|
</Splitpanes>
|
|
202
203
|
</SplitPanesWrapper>
|
|
203
204
|
</Pane>
|
|
204
|
-
<Pane size={
|
|
205
|
+
<Pane size={22} minSize={5} maxSize={33}>
|
|
205
206
|
<div class="relative flex flex-col h-full">
|
|
206
207
|
<Tabs bind:selected={selectedTab} wrapperClass="!h-[40px]" class="!h-full">
|
|
207
208
|
<Tab value="insert" size="xs">
|
|
208
|
-
<div class="m-1 center-center gap-
|
|
209
|
-
<
|
|
209
|
+
<div class="m-1 center-center gap-1">
|
|
210
|
+
<Plus size={18} />
|
|
210
211
|
<span>Insert</span>
|
|
211
212
|
</div>
|
|
212
213
|
</Tab>
|
|
213
214
|
<Tab value="settings" size="xs">
|
|
214
|
-
<div class="m-1 center-center gap-
|
|
215
|
-
<
|
|
216
|
-
<span>
|
|
215
|
+
<div class="m-1 center-center gap-1">
|
|
216
|
+
<Component size={18} />
|
|
217
|
+
<span>Component</span>
|
|
217
218
|
</div>
|
|
218
219
|
</Tab>
|
|
219
220
|
<Tab value="css" size="xs">
|
|
220
|
-
<div class="m-1 center-center gap-
|
|
221
|
-
<
|
|
222
|
-
<span>
|
|
221
|
+
<div class="m-1 center-center gap-1">
|
|
222
|
+
<Paintbrush size={18} />
|
|
223
|
+
<span>Styling</span>
|
|
223
224
|
</div>
|
|
224
225
|
</Tab>
|
|
225
226
|
<div slot="content" class="h-full overflow-y-auto">
|
|
226
227
|
<TabContent class="overflow-auto h-full" value="settings">
|
|
227
228
|
{#if $selectedComponent !== undefined}
|
|
228
229
|
<SettingsPanel />
|
|
230
|
+
<SecondaryMenu />
|
|
229
231
|
{:else}
|
|
230
232
|
<div class="min-w-[150px] text-sm text-gray-500 text-center py-8 px-2">
|
|
231
233
|
Select a component to see the settings for it
|
|
@@ -82,8 +82,7 @@ $: lockedClasses = isLocked ? '!max-h-[400px] overflow-hidden pointer-events-non
|
|
|
82
82
|
)}
|
|
83
83
|
>
|
|
84
84
|
<div
|
|
85
|
-
class="w-full sticky top-0 flex justify-between border-b bg-gray-50 px-4 py-1 items-center gap-4"
|
|
86
|
-
style="z-index: 1000;"
|
|
85
|
+
class="w-full sticky z-[1001] top-0 flex justify-between border-b bg-gray-50 px-4 py-1 items-center gap-4"
|
|
87
86
|
>
|
|
88
87
|
<h2 class="truncate">{summary}</h2>
|
|
89
88
|
<RecomputeAllComponents />
|
|
@@ -144,7 +144,8 @@ function handlePaste(event) {
|
|
|
144
144
|
function keydown(event) {
|
|
145
145
|
// Ignore keydown events if the user is typing in monaco
|
|
146
146
|
let classes = event.target?.['className'];
|
|
147
|
-
if (typeof classes === 'string' && classes.includes('inputarea'))
|
|
147
|
+
if ((typeof classes === 'string' && classes.includes('inputarea')) ||
|
|
148
|
+
['INPUT', 'TEXTAREA'].includes(document.activeElement?.tagName)) {
|
|
148
149
|
return;
|
|
149
150
|
}
|
|
150
151
|
switch (event.key) {
|
|
@@ -8,6 +8,10 @@
|
|
|
8
8
|
1. Add the component to one of the component sets: `layout`, `buttons`,
|
|
9
9
|
`inputs` or `display` _(this controls which group the component will be
|
|
10
10
|
placed in in the **Insert** menu)_
|
|
11
|
+
1. [**`quickStyleProperties.ts`**](../componentsPanel/quickStyleProperties.ts):
|
|
12
|
+
1. Add the component to the `quickStyleProperties` record
|
|
13
|
+
1. _(optional)_ Add the CSS properties that could be applied to the component
|
|
14
|
+
parts
|
|
11
15
|
1. [**`Component.svelte`**](./Component.svelte):
|
|
12
16
|
1. Add the new component in the Svelte `if` statement
|
|
13
17
|
1. [**`default-codes.ts`**](./default-codes.ts):
|
|
@@ -1,67 +1,107 @@
|
|
|
1
|
-
<script>import {
|
|
1
|
+
<script>import { Forward, Paintbrush2 } from 'lucide-svelte';
|
|
2
|
+
import { createEventDispatcher, getContext } from 'svelte';
|
|
2
3
|
import { fade } from 'svelte/transition';
|
|
3
|
-
import { addWhitespaceBeforeCapitals } from '../../../../utils';
|
|
4
|
+
import { addWhitespaceBeforeCapitals, sendUserToast } from '../../../../utils';
|
|
5
|
+
import { Button, ClearableInput } from '../../../common';
|
|
6
|
+
import Popover from '../../../Popover.svelte';
|
|
7
|
+
import QuickStyleMenu from './QuickStyleMenu.svelte';
|
|
4
8
|
export let name;
|
|
9
|
+
export let componentType = undefined;
|
|
5
10
|
export let value = {};
|
|
6
11
|
export let forceStyle = false;
|
|
7
12
|
export let forceClass = false;
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
13
|
+
export let quickStyleProperties = undefined;
|
|
14
|
+
const { app } = getContext('AppViewerContext');
|
|
15
|
+
const dispatch = createEventDispatcher();
|
|
16
|
+
let isQuickMenuOpen = false;
|
|
17
|
+
$: dispatch('change', value);
|
|
18
|
+
function toggleQuickMenu() {
|
|
19
|
+
isQuickMenuOpen = !isQuickMenuOpen;
|
|
20
|
+
}
|
|
21
|
+
function applyToAllInstances() {
|
|
22
|
+
if (componentType &&
|
|
23
|
+
componentType in ($app?.css || {}) &&
|
|
24
|
+
name in ($app?.css?.[componentType] || {})) {
|
|
25
|
+
$app.css[componentType][name].style = value.style;
|
|
26
|
+
sendUserToast(`Applied style to all instances of the ${componentType.replace('component', '')} component`);
|
|
11
27
|
}
|
|
12
28
|
}
|
|
13
29
|
</script>
|
|
14
30
|
|
|
15
|
-
<div
|
|
31
|
+
<div
|
|
32
|
+
class="sticky top-0 z-20 text-lg bg-white font-semibold [font-variant:small-caps] text-gray-700 pt-2 pb-1"
|
|
33
|
+
>
|
|
16
34
|
{addWhitespaceBeforeCapitals(name)}
|
|
17
35
|
</div>
|
|
18
36
|
{#if value}
|
|
19
|
-
<div class="border-l border-gray-400/80 py-1 pl-3.5
|
|
37
|
+
<div class="border-l border-gray-400/80 py-1 pl-3.5 ml-0.5">
|
|
20
38
|
{#if value.style !== undefined || forceStyle}
|
|
21
|
-
<
|
|
22
|
-
|
|
23
|
-
<
|
|
24
|
-
<
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
<div class="pb-2">
|
|
40
|
+
<!-- svelte-ignore a11y-label-has-associated-control -->
|
|
41
|
+
<label class="block">
|
|
42
|
+
<div class="text-sm font-medium text-gray-600 pb-0.5"> Style </div>
|
|
43
|
+
<div class="flex gap-1">
|
|
44
|
+
<div class="relative grow">
|
|
45
|
+
<ClearableInput
|
|
46
|
+
bind:value={value.style}
|
|
47
|
+
type="textarea"
|
|
48
|
+
wrapperClass="h-full min-h-[72px]"
|
|
49
|
+
inputClass="h-full"
|
|
50
|
+
/>
|
|
51
|
+
</div>
|
|
52
|
+
<div class="flex flex-col gap-1">
|
|
53
|
+
{#if componentType}
|
|
54
|
+
<Popover placement="bottom" notClickable disapperTimoout={0}>
|
|
55
|
+
<Button
|
|
56
|
+
variant="border"
|
|
57
|
+
color="light"
|
|
58
|
+
size="xs"
|
|
59
|
+
btnClasses="!p-1 !w-[34px] !h-[34px]"
|
|
60
|
+
aria-label="Apply to all instances of this component"
|
|
61
|
+
on:click={applyToAllInstances}
|
|
62
|
+
>
|
|
63
|
+
<Forward size={18} />
|
|
64
|
+
</Button>
|
|
65
|
+
<svelte:fragment slot="text">
|
|
66
|
+
Apply to all instances of this component
|
|
67
|
+
</svelte:fragment>
|
|
68
|
+
</Popover>
|
|
69
|
+
{/if}
|
|
70
|
+
{#if quickStyleProperties?.length}
|
|
71
|
+
<Popover placement="bottom" notClickable disapperTimoout={0}>
|
|
72
|
+
<Button
|
|
73
|
+
variant="border"
|
|
74
|
+
color="light"
|
|
75
|
+
size="xs"
|
|
76
|
+
btnClasses="!p-1 !w-[34px] !h-[34px] {isQuickMenuOpen
|
|
77
|
+
? '!bg-gray-200/60 hover:!bg-gray-200'
|
|
78
|
+
: ''}"
|
|
79
|
+
aria-label="{isQuickMenuOpen ? 'Close' : 'Open'} styling menu"
|
|
80
|
+
on:click={toggleQuickMenu}
|
|
81
|
+
>
|
|
82
|
+
<Paintbrush2 size={18} />
|
|
83
|
+
</Button>
|
|
84
|
+
<svelte:fragment slot="text">
|
|
85
|
+
{isQuickMenuOpen ? 'Close' : 'Open'} styling menu
|
|
86
|
+
</svelte:fragment>
|
|
87
|
+
</Popover>
|
|
88
|
+
{/if}
|
|
89
|
+
</div>
|
|
90
|
+
</div>
|
|
91
|
+
</label>
|
|
92
|
+
{#if quickStyleProperties?.length && isQuickMenuOpen}
|
|
93
|
+
<div transition:fade|local={{ duration: 200 }} class="w-full pt-1">
|
|
94
|
+
<QuickStyleMenu bind:value={value.style} properties={quickStyleProperties} />
|
|
95
|
+
</div>
|
|
96
|
+
{/if}
|
|
97
|
+
</div>
|
|
43
98
|
{/if}
|
|
44
99
|
{#if value.class !== undefined || forceClass}
|
|
100
|
+
<!-- svelte-ignore a11y-label-has-associated-control -->
|
|
45
101
|
<label class="block">
|
|
46
|
-
<div class="text-
|
|
102
|
+
<div class="text-sm font-medium text-gray-600 pb-0.5"> Tailwind classes </div>
|
|
47
103
|
<div class="relative">
|
|
48
|
-
<
|
|
49
|
-
on:keydown|stopPropagation
|
|
50
|
-
type="text"
|
|
51
|
-
class="!pr-7"
|
|
52
|
-
bind:value={value.class}
|
|
53
|
-
on:focus
|
|
54
|
-
/>
|
|
55
|
-
{#if value?.class}
|
|
56
|
-
<button
|
|
57
|
-
transition:fade|local={{ duration: 100 }}
|
|
58
|
-
class="absolute z-10 top-1.5 right-1 rounded-full p-1 duration-200 hover:bg-gray-200"
|
|
59
|
-
aria-label="Remove classes"
|
|
60
|
-
on:click|preventDefault|stopPropagation={() => reset('class')}
|
|
61
|
-
>
|
|
62
|
-
<X size={14} />
|
|
63
|
-
</button>
|
|
64
|
-
{/if}
|
|
104
|
+
<ClearableInput bind:value={value.class} />
|
|
65
105
|
</div>
|
|
66
106
|
</label>
|
|
67
107
|
{/if}
|
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
import { SvelteComponentTyped } from "svelte";
|
|
2
2
|
import type { ComponentCssProperty } from '../../types';
|
|
3
|
+
import type { AppComponent } from '../component/components';
|
|
4
|
+
import type { StylePropertyKey } from './quickStyleProperties';
|
|
3
5
|
declare const __propDef: {
|
|
4
6
|
props: {
|
|
5
7
|
name: string;
|
|
8
|
+
componentType?: AppComponent['type'] | undefined;
|
|
6
9
|
value?: ComponentCssProperty | undefined;
|
|
7
10
|
forceStyle?: boolean | undefined;
|
|
8
11
|
forceClass?: boolean | undefined;
|
|
12
|
+
quickStyleProperties?: StylePropertyKey[] | undefined;
|
|
9
13
|
};
|
|
10
14
|
events: {
|
|
11
|
-
|
|
12
|
-
focus: FocusEvent;
|
|
15
|
+
change: CustomEvent<any>;
|
|
13
16
|
} & {
|
|
14
17
|
[evt: string]: CustomEvent<any>;
|
|
15
18
|
};
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
<script>import { getContext, onMount, setContext } from 'svelte';
|
|
2
|
+
import parse from 'style-to-object';
|
|
3
|
+
import { isValidHexColor } from '../../../../utils';
|
|
4
|
+
import { createStyleStore, StylePropertyType, StylePropertyUnits, STYLE_STORE_KEY } from './quickStyleProperties';
|
|
5
|
+
import QuickStyleProperty from './QuickStyleProperty.svelte';
|
|
6
|
+
export let value = '';
|
|
7
|
+
export let properties;
|
|
8
|
+
const { app } = getContext('AppViewerContext');
|
|
9
|
+
const styleStore = createStyleStore(properties);
|
|
10
|
+
setContext(STYLE_STORE_KEY, styleStore);
|
|
11
|
+
let multiValues = initiateMultiValues();
|
|
12
|
+
let mounted = false;
|
|
13
|
+
$: mounted && $styleStore && writeStyle();
|
|
14
|
+
$: mounted && (!value || value) && parseStyle();
|
|
15
|
+
$: $app && setTopColors();
|
|
16
|
+
function parseStyle() {
|
|
17
|
+
styleStore.resetStyle();
|
|
18
|
+
if (!value) {
|
|
19
|
+
multiValues = initiateMultiValues();
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
const current = parse(value) || {};
|
|
23
|
+
Object.entries(current).forEach(([k, v]) => {
|
|
24
|
+
styleStore.updatePropValue(k, v);
|
|
25
|
+
const { prop, index } = styleStore.getProp(k);
|
|
26
|
+
if (Array.isArray(prop?.prop?.value) && index !== undefined) {
|
|
27
|
+
const valueArray = v.split(' ');
|
|
28
|
+
multiValues[index] = multiValues[index].map((v, i) => valueArray[i] || v);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
setTopColors();
|
|
32
|
+
}
|
|
33
|
+
function writeStyle() {
|
|
34
|
+
const current = parse(value) || {};
|
|
35
|
+
$styleStore.style.forEach((s) => {
|
|
36
|
+
current[s.prop.key] = convertValue(s.value);
|
|
37
|
+
});
|
|
38
|
+
const entries = Object.entries(current);
|
|
39
|
+
value = entries.reduce((style, [k, v]) => {
|
|
40
|
+
return v ? `${style} ${k}: ${v}; `.trim() : style;
|
|
41
|
+
}, '');
|
|
42
|
+
}
|
|
43
|
+
function convertValue(value) {
|
|
44
|
+
switch (typeof value) {
|
|
45
|
+
case 'number':
|
|
46
|
+
return value + '';
|
|
47
|
+
case 'string':
|
|
48
|
+
return value;
|
|
49
|
+
default:
|
|
50
|
+
return '';
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function setTopColors() {
|
|
54
|
+
const styles = collectStyles();
|
|
55
|
+
const parsedStyles = styles.map((style) => parse(style) || {});
|
|
56
|
+
const usedColors = {};
|
|
57
|
+
parsedStyles.forEach((style) => {
|
|
58
|
+
Object.values(style).reduce((colors, v) => {
|
|
59
|
+
// TODO: support RGB and HSL colors as well
|
|
60
|
+
// Splitting is needed so colors can be extracted
|
|
61
|
+
// from values like '1px solid #000000'
|
|
62
|
+
v.split(' ').forEach((v) => {
|
|
63
|
+
if (isValidHexColor(v)) {
|
|
64
|
+
colors[v] = (colors[v] || 0) + 1;
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
return colors;
|
|
68
|
+
}, usedColors);
|
|
69
|
+
});
|
|
70
|
+
const colors = Object.entries(usedColors)
|
|
71
|
+
.sort((a, b) => b[1] - a[1])
|
|
72
|
+
.slice(0, 3)
|
|
73
|
+
.map(([color]) => color);
|
|
74
|
+
styleStore.setTopColors(colors);
|
|
75
|
+
}
|
|
76
|
+
function collectStyles() {
|
|
77
|
+
const styles = [];
|
|
78
|
+
// Getting global app styles
|
|
79
|
+
Object.values($app.css || {}).forEach((element) => {
|
|
80
|
+
Object.values(element).filter(({ style }) => style && styles.push(style));
|
|
81
|
+
});
|
|
82
|
+
// Getting styles from individual components
|
|
83
|
+
$app.grid.map((component) => {
|
|
84
|
+
Object.values(component.data.customCss || {}).forEach(({ style }) => {
|
|
85
|
+
style && styles.push(style);
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
// Getting style from subgrids
|
|
89
|
+
Object.values($app.subgrids || {}).forEach((grid) => {
|
|
90
|
+
grid.map((component) => {
|
|
91
|
+
Object.values(component.data.customCss || {}).forEach(({ style }) => {
|
|
92
|
+
style && styles.push(style);
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
return styles;
|
|
97
|
+
}
|
|
98
|
+
function initiateMultiValues() {
|
|
99
|
+
return $styleStore.style.reduce((acc, curr, i) => {
|
|
100
|
+
if (Array.isArray(curr.prop.value)) {
|
|
101
|
+
acc[i] = Array.from({ length: curr.prop.value.length }, () => {
|
|
102
|
+
return '';
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
return acc;
|
|
106
|
+
}, {});
|
|
107
|
+
}
|
|
108
|
+
function setMultiValueProperty(index) {
|
|
109
|
+
if (multiValues[index].every((v) => !v || +v === 0 || StylePropertyUnits.includes(v))) {
|
|
110
|
+
$styleStore.style[index].value = '';
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
const values = multiValues[index].map((v, i) => {
|
|
114
|
+
v = StylePropertyUnits.includes(v) ? '' : v;
|
|
115
|
+
const type = $styleStore.style[index].prop.value[i].type;
|
|
116
|
+
if (v) {
|
|
117
|
+
multiValues[index][i] = v;
|
|
118
|
+
return v;
|
|
119
|
+
}
|
|
120
|
+
else if (type === StylePropertyType.color) {
|
|
121
|
+
return '#000000';
|
|
122
|
+
}
|
|
123
|
+
else if (type === StylePropertyType.number) {
|
|
124
|
+
return 0;
|
|
125
|
+
}
|
|
126
|
+
else if (type === StylePropertyType.unit) {
|
|
127
|
+
return 0;
|
|
128
|
+
}
|
|
129
|
+
else if (type === StylePropertyType.text) {
|
|
130
|
+
const options = $styleStore.style[index].prop.value[i].options;
|
|
131
|
+
return options ? options[0].text : '';
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
return '';
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
$styleStore.style[index].value = values.join(' ').trim();
|
|
138
|
+
}
|
|
139
|
+
onMount(() => {
|
|
140
|
+
parseStyle();
|
|
141
|
+
mounted = true;
|
|
142
|
+
});
|
|
143
|
+
</script>
|
|
144
|
+
|
|
145
|
+
<div class="bg-gray-200/60 rounded-md p-2">
|
|
146
|
+
{#each $styleStore.style as { prop }, index}
|
|
147
|
+
<div class="pb-3 last:pb-0">
|
|
148
|
+
<div class="text-sm font-medium text-gray-600 pb-0.5"> {prop.key} </div>
|
|
149
|
+
<div class="flex items-center gap-1 w-full">
|
|
150
|
+
{#if Array.isArray(prop.value)}
|
|
151
|
+
<div class="flex justify-start items-center flex-wrap gap-x-4 gap-y-1">
|
|
152
|
+
{#each prop.value as value, i}
|
|
153
|
+
<QuickStyleProperty
|
|
154
|
+
prop={{ ...prop, value }}
|
|
155
|
+
inline
|
|
156
|
+
bind:value={multiValues[index][i]}
|
|
157
|
+
on:change={() => setMultiValueProperty(index)}
|
|
158
|
+
/>
|
|
159
|
+
{/each}
|
|
160
|
+
</div>
|
|
161
|
+
{:else}
|
|
162
|
+
<QuickStyleProperty {prop} bind:value={$styleStore.style[index].value} />
|
|
163
|
+
{/if}
|
|
164
|
+
</div>
|
|
165
|
+
</div>
|
|
166
|
+
{/each}
|
|
167
|
+
</div>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { SvelteComponentTyped } from "svelte";
|
|
2
|
+
import { type StylePropertyKey } from './quickStyleProperties';
|
|
3
|
+
declare const __propDef: {
|
|
4
|
+
props: {
|
|
5
|
+
value?: string | undefined;
|
|
6
|
+
properties: StylePropertyKey[];
|
|
7
|
+
};
|
|
8
|
+
events: {
|
|
9
|
+
[evt: string]: CustomEvent<any>;
|
|
10
|
+
};
|
|
11
|
+
slots: {};
|
|
12
|
+
};
|
|
13
|
+
export type QuickStyleMenuProps = typeof __propDef.props;
|
|
14
|
+
export type QuickStyleMenuEvents = typeof __propDef.events;
|
|
15
|
+
export type QuickStyleMenuSlots = typeof __propDef.slots;
|
|
16
|
+
export default class QuickStyleMenu extends SvelteComponentTyped<QuickStyleMenuProps, QuickStyleMenuEvents, QuickStyleMenuSlots> {
|
|
17
|
+
}
|
|
18
|
+
export {};
|