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.
Files changed (44) hide show
  1. package/components/Multiselect.svelte.d.ts +2 -2
  2. package/components/apps/components/buttons/AppFormButton.svelte +58 -70
  3. package/components/apps/editor/AppEditor.svelte +14 -12
  4. package/components/apps/editor/AppPreview.svelte +1 -2
  5. package/components/apps/editor/component/ComponentNavigation.svelte +2 -1
  6. package/components/apps/editor/component/README.md +4 -0
  7. package/components/apps/editor/componentsPanel/CssProperty.svelte +87 -47
  8. package/components/apps/editor/componentsPanel/CssProperty.svelte.d.ts +5 -2
  9. package/components/apps/editor/componentsPanel/QuickStyleMenu.svelte +167 -0
  10. package/components/apps/editor/componentsPanel/QuickStyleMenu.svelte.d.ts +18 -0
  11. package/components/apps/editor/componentsPanel/QuickStyleProperty.svelte +130 -0
  12. package/components/apps/editor/componentsPanel/QuickStyleProperty.svelte.d.ts +21 -0
  13. package/components/apps/editor/componentsPanel/quickStyleProperties.d.ts +535 -0
  14. package/components/apps/editor/componentsPanel/quickStyleProperties.js +595 -0
  15. package/components/apps/editor/settingsPanel/ComponentPanel.svelte +24 -14
  16. package/components/apps/editor/settingsPanel/StylePanel.svelte +23 -0
  17. package/components/apps/editor/settingsPanel/StylePanel.svelte.d.ts +17 -0
  18. package/components/apps/editor/settingsPanel/inputEditor/ColorInput.svelte +12 -12
  19. package/components/apps/editor/settingsPanel/inputEditor/ColorInput.svelte.d.ts +3 -2
  20. package/components/apps/editor/settingsPanel/inputEditor/StaticInputEditor.svelte +1 -1
  21. package/components/apps/editor/settingsPanel/secondaryMenu/SecondaryMenu.svelte +47 -0
  22. package/components/apps/editor/settingsPanel/secondaryMenu/SecondaryMenu.svelte.d.ts +14 -0
  23. package/components/apps/editor/settingsPanel/secondaryMenu/index.d.ts +2 -0
  24. package/components/apps/editor/settingsPanel/secondaryMenu/index.js +2 -0
  25. package/components/apps/editor/settingsPanel/secondaryMenu/menuStore.d.ts +12 -0
  26. package/components/apps/editor/settingsPanel/secondaryMenu/menuStore.js +10 -0
  27. package/components/apps/types.d.ts +2 -1
  28. package/components/common/button/ButtonPopup.svelte +5 -2
  29. package/components/common/button/ButtonPopup.svelte.d.ts +5 -1
  30. package/components/common/button/ButtonPopupItem.svelte +2 -1
  31. package/components/common/button/ButtonPopupItem.svelte.d.ts +1 -0
  32. package/components/common/clearableInput/ClearableInput.svelte +56 -0
  33. package/components/common/clearableInput/ClearableInput.svelte.d.ts +28 -0
  34. package/components/common/index.d.ts +1 -0
  35. package/components/common/index.js +1 -0
  36. package/components/common/kbd/Kbd.svelte +4 -1
  37. package/components/common/kbd/Kbd.svelte.d.ts +6 -14
  38. package/components/common/menu/Menu.svelte +8 -2
  39. package/components/common/menu/Menu.svelte.d.ts +4 -1
  40. package/components/common/modal/AlwaysMountedModal.svelte +109 -0
  41. package/components/common/modal/AlwaysMountedModal.svelte.d.ts +22 -0
  42. package/package.json +672 -662
  43. package/utils.d.ts +1 -0
  44. 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
- <Portal>
66
- <Modal
67
- {open}
68
- title={resolvedConfig.label ?? ''}
69
- class={css?.popup?.class}
70
- style={css?.popup?.style}
71
- on:canceled={() => {
72
- open = false
73
- }}
74
- on:confirmed={() => {
75
- open = false
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
- <RunnableWrapper
79
- bind:this={runnableWrapper}
80
- {recomputeIds}
81
- {render}
82
- bind:runnableComponent
83
- {componentInput}
84
- {id}
85
- {extraQueryParams}
86
- autoRefresh={false}
87
- forceSchemaDisplay={true}
88
- runnableClass="!block"
89
- {outputs}
90
- doOnSuccess={resolvedConfig.onSuccess}
91
- >
92
- <div class="flex flex-col gap-2 px-4 w-full">
93
- <div>
94
- {#if noInputs}
95
- <div class="text-gray-600 italic text-sm my-4">
96
- Run forms are associated with a runnable that has user inputs.
97
- <br />
98
- Once a script or flow is chosen, set some <strong>Runnable Inputs</strong> to
99
- <strong>
100
- User Input
101
- <Icon data={faUser} scale={1.3} class="rounded-sm bg-gray-200 p-1 ml-0.5" />
102
- </strong>
103
- </div>
104
- {/if}
105
- </div>
106
- <div class="flex justify-end">
107
- <Button
108
- {loading}
109
- btnClasses="my-1"
110
- on:pointerdown={(e) => {
111
- e?.stopPropagation()
112
- window.dispatchEvent(new Event('pointerup'))
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
- </RunnableWrapper>
130
- </Modal>
131
- </Portal>
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 = true
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 Icon from 'svelte-awesome';
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={64}>
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={21} minSize={5} maxSize={33}>
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-2">
209
- <Icon data={faPlus} />
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-2">
215
- <Icon data={faSliders} />
216
- <span>Settings</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-2">
221
- <Icon data={faCode} />
222
- <span>CSS</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&nbsp;for&nbsp;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 { X } from 'lucide-svelte';
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
- function reset(property) {
9
- if (value) {
10
- value[property] = '';
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 class="text-sm font-semibold text-gray-500 capitalize pt-2">
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 mt-1 ml-0.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
- <label class="block pb-2">
22
- <div class="text-xs font-medium pb-0.5"> Style </div>
23
- <div class="relative">
24
- <input
25
- on:keydown|stopPropagation
26
- type="text"
27
- class="!pr-7"
28
- bind:value={value.style}
29
- on:focus
30
- />
31
- {#if value?.style}
32
- <button
33
- transition:fade|local={{ duration: 100 }}
34
- class="absolute z-10 top-1.5 right-1 rounded-full p-1 duration-200 hover:bg-gray-200"
35
- aria-label="Remove styles"
36
- on:click|preventDefault|stopPropagation={() => reset('style')}
37
- >
38
- <X size={14} />
39
- </button>
40
- {/if}
41
- </div>
42
- </label>
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-xs font-medium pb-0.5"> Tailwind classes </div>
102
+ <div class="text-sm font-medium text-gray-600 pb-0.5"> Tailwind classes </div>
47
103
  <div class="relative">
48
- <input
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
- keydown: KeyboardEvent;
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 {};