commit
d5e3fd583c
|
@ -0,0 +1,53 @@
|
||||||
|
<script>
|
||||||
|
export let disabled = false;
|
||||||
|
export let hidden = false;
|
||||||
|
export let primary = true;
|
||||||
|
export let alert = false;
|
||||||
|
export let warning = false;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.primary {
|
||||||
|
color: #0055ff;
|
||||||
|
background: rgb(54, 133, 249, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert {
|
||||||
|
color: rgba(255, 0, 31, 1);
|
||||||
|
background: rgba(255, 0, 31, 0.1);;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: bold;
|
||||||
|
border-radius: 5px;
|
||||||
|
border: none;
|
||||||
|
width: 167px;
|
||||||
|
height: 64px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button:disabled {
|
||||||
|
color: rgba(22, 48, 87, 0.2);
|
||||||
|
cursor: default;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hidden {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<button
|
||||||
|
on:click
|
||||||
|
class="button"
|
||||||
|
class:hidden
|
||||||
|
class:primary
|
||||||
|
class:alert
|
||||||
|
class:warning
|
||||||
|
{disabled}>
|
||||||
|
<slot />
|
||||||
|
</button>
|
|
@ -0,0 +1,9 @@
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
width="24"
|
||||||
|
height="24">
|
||||||
|
<path fill="none" d="M0 0h24v24H0z" />
|
||||||
|
<path
|
||||||
|
d="M13 9h8L11 24v-9H4l9-15v9zm-2 2V7.22L7.532 13H13v4.394L17.263 11H11z" />
|
||||||
|
</svg>
|
After Width: | Height: | Size: 228 B |
|
@ -0,0 +1,19 @@
|
||||||
|
<svg
|
||||||
|
on:click
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
width="24"
|
||||||
|
height="24">
|
||||||
|
<path fill="none" d="M0 0h24v24H0z" />
|
||||||
|
<path
|
||||||
|
d="M15.728 9.686l-1.414-1.414L5
|
||||||
|
17.586V19h1.414l9.314-9.314zm1.414-1.414l1.414-1.414-1.414-1.414-1.414 1.414
|
||||||
|
1.414 1.414zM7.242 21H3v-4.243L16.435 3.322a1 1 0 0 1 1.414 0l2.829 2.829a1
|
||||||
|
1 0 0 1 0 1.414L7.243 21z" />
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
svg:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
</style>
|
After Width: | Height: | Size: 446 B |
|
@ -4,3 +4,5 @@ export { default as TerminalIcon } from './Terminal.svelte';
|
||||||
export { default as InputIcon } from './Input.svelte';
|
export { default as InputIcon } from './Input.svelte';
|
||||||
export { default as ImageIcon } from './Image.svelte';
|
export { default as ImageIcon } from './Image.svelte';
|
||||||
export { default as ArrowDownIcon } from './ArrowDown.svelte';
|
export { default as ArrowDownIcon } from './ArrowDown.svelte';
|
||||||
|
export { default as EventsIcon } from './Events.svelte';
|
||||||
|
export { default as PencilIcon } from './Pencil.svelte';
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
<script>
|
||||||
|
export let value = "";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
input {
|
||||||
|
display: block;
|
||||||
|
font-size: 14px;
|
||||||
|
font-family: sans-serif;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #163057;
|
||||||
|
line-height: 1.3;
|
||||||
|
padding: 1em 2.6em 0.9em 1.4em;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
-moz-appearance: none;
|
||||||
|
-webkit-appearance: none;
|
||||||
|
appearance: none;
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
height: 50px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<input type="text" on:change bind:value />
|
|
@ -1,41 +1,44 @@
|
||||||
<script>
|
<script>
|
||||||
import UIkit from "uikit";
|
import UIkit from "uikit";
|
||||||
|
|
||||||
export let isOpen = false;
|
export let isOpen = false;
|
||||||
export let onClosed = () => {};
|
export let onClosed = () => {};
|
||||||
export let id = "";
|
export let id = "";
|
||||||
|
|
||||||
let ukModal;
|
let ukModal;
|
||||||
let listenerAdded = false;
|
let listenerAdded = false;
|
||||||
|
|
||||||
$: {
|
$: {
|
||||||
if(ukModal && !listenerAdded) {
|
if (ukModal && !listenerAdded) {
|
||||||
listenerAdded = true;
|
listenerAdded = true;
|
||||||
ukModal.addEventListener("hidden", onClosed);
|
ukModal.addEventListener("hidden", onClosed);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ukModal) {
|
if (ukModal) {
|
||||||
if(isOpen) {
|
if (isOpen) {
|
||||||
UIkit.modal(ukModal).show();
|
UIkit.modal(ukModal).show();
|
||||||
} else {
|
} else {
|
||||||
UIkit.modal(ukModal).hide();
|
UIkit.modal(ukModal).hide();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.uk-modal-dialog {
|
||||||
|
border-radius: 0.3rem;
|
||||||
|
width: 60%;
|
||||||
|
height: 80vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
<div bind:this={ukModal} uk-modal {id}>
|
<div bind:this={ukModal} uk-modal {id}>
|
||||||
<div class="uk-modal-dialog uk-modal-body" uk-overflow-auto>
|
<div class="uk-modal-dialog uk-modal-body" uk-overflow-auto>
|
||||||
|
{#if onClosed}
|
||||||
|
<button class="uk-modal-close-default" type="button" uk-close />
|
||||||
|
{/if}
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<style>
|
|
||||||
|
|
||||||
.uk-modal-dialog {
|
|
||||||
border-radius: .3rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
<style>
|
||||||
|
button {
|
||||||
|
cursor: pointer;
|
||||||
|
outline: none;
|
||||||
|
border: none;
|
||||||
|
border-radius: 5px;
|
||||||
|
background: rgba(249, 249, 249, 1);
|
||||||
|
|
||||||
|
width: 1.8rem;
|
||||||
|
height: 1.8rem;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
font-size: 1.2rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: rgba(22, 48, 87, 1);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<button on:click>+</button>
|
|
@ -0,0 +1,55 @@
|
||||||
|
<script>
|
||||||
|
import getIcon from "./icon";
|
||||||
|
export let value;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.select-container {
|
||||||
|
padding-bottom: 10px;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: var(--secondary50);
|
||||||
|
font-weight: bold;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
display: block;
|
||||||
|
font-size: 14px;
|
||||||
|
font-family: sans-serif;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #163057;
|
||||||
|
line-height: 1.3;
|
||||||
|
padding: 1em 2.6em 0.9em 1.4em;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
-moz-appearance: none;
|
||||||
|
-webkit-appearance: none;
|
||||||
|
appearance: none;
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
height: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.arrow {
|
||||||
|
position: absolute;
|
||||||
|
right: 10px;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
margin: auto;
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
pointer-events: none;
|
||||||
|
color: var(--primary100);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div class="select-container">
|
||||||
|
<select on:change {value}>
|
||||||
|
<slot />
|
||||||
|
</select>
|
||||||
|
<span class="arrow">
|
||||||
|
{@html getIcon('chevron-down', '24')}
|
||||||
|
</span>
|
||||||
|
</div>
|
|
@ -2,9 +2,10 @@
|
||||||
import PropsView from "./PropsView.svelte";
|
import PropsView from "./PropsView.svelte";
|
||||||
import { store } from "../builderStore";
|
import { store } from "../builderStore";
|
||||||
import IconButton from "../common/IconButton.svelte";
|
import IconButton from "../common/IconButton.svelte";
|
||||||
import { LayoutIcon, PaintIcon, TerminalIcon } from '../common/Icons/';
|
import { LayoutIcon, PaintIcon, TerminalIcon, EventsIcon } from '../common/Icons/';
|
||||||
import CodeEditor from './CodeEditor.svelte';
|
import CodeEditor from './CodeEditor.svelte';
|
||||||
import LayoutEditor from './LayoutEditor.svelte';
|
import LayoutEditor from './LayoutEditor.svelte';
|
||||||
|
import EventsEditor from "./EventsEditor";
|
||||||
|
|
||||||
let current_view = 'props';
|
let current_view = 'props';
|
||||||
|
|
||||||
|
@ -17,6 +18,7 @@
|
||||||
|
|
||||||
const onPropChanged = store.setComponentProp;
|
const onPropChanged = store.setComponentProp;
|
||||||
const onStyleChanged = store.setComponentStyle;
|
const onStyleChanged = store.setComponentStyle;
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="root">
|
<div class="root">
|
||||||
|
@ -36,6 +38,11 @@
|
||||||
<TerminalIcon />
|
<TerminalIcon />
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<button class:selected={current_view === 'events'} on:click={() => current_view = 'events'}>
|
||||||
|
<EventsIcon />
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
{#if !componentInfo.component}
|
{#if !componentInfo.component}
|
||||||
|
@ -45,6 +52,8 @@
|
||||||
<PropsView {componentInfo} {components} {onPropChanged} />
|
<PropsView {componentInfo} {components} {onPropChanged} />
|
||||||
{:else if current_view === 'layout'}
|
{:else if current_view === 'layout'}
|
||||||
<LayoutEditor {onStyleChanged} {componentInfo}/>
|
<LayoutEditor {onStyleChanged} {componentInfo}/>
|
||||||
|
{:else if current_view === 'events'}
|
||||||
|
<EventsEditor {componentInfo} {components} {onPropChanged} />
|
||||||
{:else}
|
{:else}
|
||||||
<CodeEditor />
|
<CodeEditor />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
@ -1,88 +0,0 @@
|
||||||
<script>
|
|
||||||
import IconButton from "../common/IconButton.svelte";
|
|
||||||
import EventSelector from "./EventSelector.svelte";
|
|
||||||
import {
|
|
||||||
filter
|
|
||||||
} from "lodash/fp";
|
|
||||||
import {EVENT_TYPE_MEMBER_NAME} from "../common/eventHandlers";
|
|
||||||
|
|
||||||
export let parentProps;
|
|
||||||
export let propDef;
|
|
||||||
export let onValueChanged;
|
|
||||||
|
|
||||||
$: events = parentProps[propDef.____name];
|
|
||||||
|
|
||||||
const addHandler = () => {
|
|
||||||
const newHandler = {parameters:{}};
|
|
||||||
newHandler[EVENT_TYPE_MEMBER_NAME] = "";
|
|
||||||
events = [...events, newHandler];
|
|
||||||
onValueChanged(events);
|
|
||||||
}
|
|
||||||
|
|
||||||
const onEventHandlerChanged = (oldEvent) => (newEvent) => {
|
|
||||||
const indexOfOldEvent = events.indexOf(oldEvent);
|
|
||||||
const newEvents = [...events];
|
|
||||||
newEvents.splice(
|
|
||||||
events.indexOf(oldEvent),
|
|
||||||
1,
|
|
||||||
newEvent);
|
|
||||||
events = newEvents;
|
|
||||||
onValueChanged(events);
|
|
||||||
}
|
|
||||||
|
|
||||||
const removeHandler = (index) => () => {
|
|
||||||
events = filter(e => e !== events[index])(events);
|
|
||||||
onValueChanged(events);
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div class="root">
|
|
||||||
<div class="control-container">
|
|
||||||
{#each events as ev, index}
|
|
||||||
|
|
||||||
<div class="handler-container">
|
|
||||||
<EventSelector onChanged={onEventHandlerChanged(ev)}
|
|
||||||
onRemoved={removeHandler(index)}
|
|
||||||
event={ev} />
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="separator"></div>
|
|
||||||
{/each}
|
|
||||||
|
|
||||||
<div class="addelement-container"
|
|
||||||
on:click={addHandler}>
|
|
||||||
<IconButton icon="plus"
|
|
||||||
size="12"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.addelement-container {
|
|
||||||
cursor: pointer;
|
|
||||||
padding: 3px 0px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.addelement-container:hover {
|
|
||||||
background-color: var(--primary25);
|
|
||||||
margin-top: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.control-container {
|
|
||||||
padding-left: 3px;
|
|
||||||
background: var(--secondary10);
|
|
||||||
}
|
|
||||||
|
|
||||||
.separator {
|
|
||||||
width: 60%;
|
|
||||||
margin: 10px auto;
|
|
||||||
border-style:solid;
|
|
||||||
border-width: 1px 0 0 0;
|
|
||||||
border-color: var(--primary25);
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,105 +0,0 @@
|
||||||
<script>
|
|
||||||
import IconButton from "../common/IconButton.svelte";
|
|
||||||
import StateBindingControl from "./StateBindingControl.svelte";
|
|
||||||
import {
|
|
||||||
find, map, keys, reduce, keyBy
|
|
||||||
} from "lodash/fp";
|
|
||||||
import { pipe, userWithFullAccess } from "../common/core";
|
|
||||||
import { EVENT_TYPE_MEMBER_NAME, allHandlers } from "../common/eventHandlers";
|
|
||||||
import { store } from "../builderStore";
|
|
||||||
|
|
||||||
export let event;
|
|
||||||
export let onChanged;
|
|
||||||
export let onRemoved;
|
|
||||||
|
|
||||||
let eventType;
|
|
||||||
let parameters = [];
|
|
||||||
|
|
||||||
|
|
||||||
$: events = allHandlers(
|
|
||||||
{hierarchy: $store.hierarchy},
|
|
||||||
userWithFullAccess({
|
|
||||||
hierarchy: s.hierarchy,
|
|
||||||
actions: keyBy("name")($store.actions)
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
$: if(event) {
|
|
||||||
eventType = event[EVENT_TYPE_MEMBER_NAME];
|
|
||||||
parameters = pipe(event.parameters, [
|
|
||||||
keys,
|
|
||||||
map(k => ({name:k, value:event.parameters[k]}))
|
|
||||||
]);
|
|
||||||
} else {
|
|
||||||
eventType = "";
|
|
||||||
parameters = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
const eventChanged = (type, parameters) => {
|
|
||||||
const paramsAsObject = reduce(
|
|
||||||
(obj, p) => {
|
|
||||||
obj[p.name] = p.value;
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
, {}
|
|
||||||
)(parameters)
|
|
||||||
|
|
||||||
const ev = {};
|
|
||||||
ev[EVENT_TYPE_MEMBER_NAME]=type;
|
|
||||||
ev.parameters = paramsAsObject;
|
|
||||||
|
|
||||||
onChanged(ev);
|
|
||||||
}
|
|
||||||
|
|
||||||
const eventTypeChanged = (ev) => {
|
|
||||||
const eType = find(e => e.name === ev.target.value)(events);
|
|
||||||
const emptyParameters = map(p => ({name:p, value:""}))(eType.parameters);
|
|
||||||
eventChanged(eType.name, emptyParameters);
|
|
||||||
}
|
|
||||||
|
|
||||||
const onParameterChanged = index => val => {
|
|
||||||
const newparameters = [...parameters];
|
|
||||||
newparameters[index].value = val;
|
|
||||||
eventChanged(eventType, newparameters);
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div class="type-selector-container">
|
|
||||||
<select class="type-selector uk-select uk-form-small " value={eventType} on:change={eventTypeChanged}>
|
|
||||||
<option></option>
|
|
||||||
{#each events as ev}
|
|
||||||
<option value={ev.name}>{ev.name}</option>
|
|
||||||
{/each}
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<IconButton icon="trash"
|
|
||||||
size="12"
|
|
||||||
on:click={onRemoved}/>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{#if parameters}
|
|
||||||
{#each parameters as p, index}
|
|
||||||
|
|
||||||
<div>
|
|
||||||
{p.name}
|
|
||||||
</div>
|
|
||||||
<StateBindingControl onChanged={onParameterChanged(index)}
|
|
||||||
value={p.value} />
|
|
||||||
|
|
||||||
{/each}
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.type-selector-container {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.type-selector {
|
|
||||||
border-color: var(--primary50);
|
|
||||||
border-radius: 2px;
|
|
||||||
width: 50px;
|
|
||||||
flex: 1 0 auto;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -0,0 +1,164 @@
|
||||||
|
<script>
|
||||||
|
import Modal from "../../common/Modal.svelte";
|
||||||
|
import HandlerSelector from "./HandlerSelector.svelte";
|
||||||
|
import IconButton from "../../common/IconButton.svelte";
|
||||||
|
import ActionButton from "../../common/ActionButton.svelte";
|
||||||
|
import PlusButton from "../../common/PlusButton.svelte";
|
||||||
|
import Select from "../../common/Select.svelte";
|
||||||
|
import Input from "../../common/Input.svelte";
|
||||||
|
import getIcon from "../../common/icon";
|
||||||
|
|
||||||
|
import { EVENT_TYPE_MEMBER_NAME } from "../../common/eventHandlers";
|
||||||
|
|
||||||
|
export let event;
|
||||||
|
export let eventOptions;
|
||||||
|
export let open;
|
||||||
|
export let onClose;
|
||||||
|
export let onPropChanged;
|
||||||
|
|
||||||
|
let eventType = "onClick";
|
||||||
|
let draftEventHandler = { parameters: [] };
|
||||||
|
|
||||||
|
$: eventData = event || { handlers: [] };
|
||||||
|
|
||||||
|
const closeModal = () => {
|
||||||
|
onClose();
|
||||||
|
draftEventHandler = { parameters: [] };
|
||||||
|
eventData = { handlers: [] };
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateEventHandler = (updatedHandler, index) => {
|
||||||
|
eventData.handlers[index] = updatedHandler;
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateDraftEventHandler = updatedHandler => {
|
||||||
|
draftEventHandler = updatedHandler;
|
||||||
|
};
|
||||||
|
|
||||||
|
const deleteEventHandler = index => {
|
||||||
|
eventData.handlers.splice(index, 1);
|
||||||
|
eventData = eventData;
|
||||||
|
};
|
||||||
|
|
||||||
|
const createNewEventHandler = handler => {
|
||||||
|
const newHandler = handler || {
|
||||||
|
parameters: {},
|
||||||
|
[EVENT_TYPE_MEMBER_NAME]: ""
|
||||||
|
};
|
||||||
|
eventData.handlers.push(newHandler);
|
||||||
|
eventData = eventData;
|
||||||
|
};
|
||||||
|
|
||||||
|
const deleteEvent = () => {
|
||||||
|
onPropChanged(eventType, []);
|
||||||
|
closeModal();
|
||||||
|
};
|
||||||
|
|
||||||
|
const saveEventData = () => {
|
||||||
|
onPropChanged(eventType, eventData.handlers);
|
||||||
|
closeModal();
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
h2 {
|
||||||
|
color: var(--primary100);
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
h5 {
|
||||||
|
color: rgba(22, 48, 87, 0.6);
|
||||||
|
font-size: 15px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.event-options {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
grid-gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions,
|
||||||
|
header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions {
|
||||||
|
margin-top: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
margin-top: 30px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: rgba(22, 48, 87, 0.6);
|
||||||
|
font-size: 12px;
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<Modal bind:isOpen={open} onClosed={closeModal}>
|
||||||
|
<h2>
|
||||||
|
{eventData.name ? `${eventData.name} Event` : 'Create a New Component Event'}
|
||||||
|
</h2>
|
||||||
|
<a href="https://docs.budibase.com/" target="_blank">
|
||||||
|
Click here to learn more about component events
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<div class="event-options">
|
||||||
|
<div>
|
||||||
|
<header>
|
||||||
|
<h5>Event Type</h5>
|
||||||
|
{@html getIcon('info', 20)}
|
||||||
|
</header>
|
||||||
|
<Select :value={eventType}>
|
||||||
|
{#each eventOptions as option}
|
||||||
|
<option value={option.name}>{option.name}</option>
|
||||||
|
{/each}
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<header>
|
||||||
|
<h5>Event Action(s)</h5>
|
||||||
|
{@html getIcon('info', 20)}
|
||||||
|
</header>
|
||||||
|
<HandlerSelector
|
||||||
|
newHandler
|
||||||
|
onChanged={updateDraftEventHandler}
|
||||||
|
onCreate={() => {
|
||||||
|
createNewEventHandler(draftEventHandler);
|
||||||
|
draftEventHandler = { parameters: [] };
|
||||||
|
}}
|
||||||
|
handler={draftEventHandler} />
|
||||||
|
{#if eventData}
|
||||||
|
{#each eventData.handlers as handler, index}
|
||||||
|
<HandlerSelector
|
||||||
|
{index}
|
||||||
|
onChanged={updateEventHandler}
|
||||||
|
onRemoved={() => deleteEventHandler(index)}
|
||||||
|
{handler} />
|
||||||
|
{/each}
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<div class="actions">
|
||||||
|
<ActionButton
|
||||||
|
alert
|
||||||
|
disabled={eventData.handlers.length === 0}
|
||||||
|
hidden={!eventData.name}
|
||||||
|
on:click={deleteEvent}>
|
||||||
|
Delete
|
||||||
|
</ActionButton>
|
||||||
|
<ActionButton
|
||||||
|
disabled={eventData.handlers.length === 0}
|
||||||
|
on:click={saveEventData}>
|
||||||
|
Save
|
||||||
|
</ActionButton>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
|
@ -0,0 +1,152 @@
|
||||||
|
<script>
|
||||||
|
import {
|
||||||
|
keys,
|
||||||
|
map,
|
||||||
|
some,
|
||||||
|
includes,
|
||||||
|
cloneDeep,
|
||||||
|
isEqual,
|
||||||
|
sortBy,
|
||||||
|
filter,
|
||||||
|
difference
|
||||||
|
} from "lodash/fp";
|
||||||
|
import { pipe } from "../../common/core";
|
||||||
|
import Checkbox from "../../common/Checkbox.svelte";
|
||||||
|
import Textbox from "../../common/Textbox.svelte";
|
||||||
|
import Dropdown from "../../common/Dropdown.svelte";
|
||||||
|
import PlusButton from "../../common/PlusButton.svelte";
|
||||||
|
import IconButton from "../../common/IconButton.svelte";
|
||||||
|
import Modal from "../../common/Modal.svelte";
|
||||||
|
import EventEditorModal from "./EventEditorModal.svelte";
|
||||||
|
import HandlerSelector from "./HandlerSelector.svelte";
|
||||||
|
|
||||||
|
import { PencilIcon } from "../../common/Icons";
|
||||||
|
import { EVENT_TYPE_MEMBER_NAME } from "../../common/eventHandlers";
|
||||||
|
|
||||||
|
export const EVENT_TYPE = "event";
|
||||||
|
|
||||||
|
export let componentInfo;
|
||||||
|
export let onPropChanged = () => {};
|
||||||
|
export let components;
|
||||||
|
|
||||||
|
let modalOpen = false;
|
||||||
|
let events = [];
|
||||||
|
let selectedEvent = null;
|
||||||
|
|
||||||
|
$: {
|
||||||
|
events = Object.keys(componentInfo)
|
||||||
|
.filter(key => findType(key) === EVENT_TYPE)
|
||||||
|
.map(key => ({ name: key, handlers: componentInfo[key] }));
|
||||||
|
}
|
||||||
|
|
||||||
|
function findType(propName) {
|
||||||
|
if (!componentInfo._component) return;
|
||||||
|
return components.find(({ name }) => name === componentInfo._component)
|
||||||
|
.props[propName];
|
||||||
|
}
|
||||||
|
|
||||||
|
const openModal = event => {
|
||||||
|
selectedEvent = event;
|
||||||
|
modalOpen = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const closeModal = () => {
|
||||||
|
selectedEvent = null;
|
||||||
|
modalOpen = false;
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
h3 {
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #8997ab;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.root {
|
||||||
|
font-size: 10pt;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-root {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.handler-container {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
border: 2px solid #f9f9f9;
|
||||||
|
height: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hierarchy-item {
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 11px 7px;
|
||||||
|
margin: 5px 0;
|
||||||
|
border-radius: 5px;
|
||||||
|
font-size: 1.5em;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hierarchy-item:hover {
|
||||||
|
background: #f9f9f9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.event-name {
|
||||||
|
margin-top: 5px;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 16px;
|
||||||
|
color: rgba(22, 48, 87, 0.6);
|
||||||
|
align-self: end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit-text {
|
||||||
|
font-family: Arial, Helvetica, sans-serif;
|
||||||
|
font-weight: bold;
|
||||||
|
align-self: end;
|
||||||
|
justify-self: end;
|
||||||
|
font-size: 10px;
|
||||||
|
color: rgba(35, 65, 105, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.selected {
|
||||||
|
color: var(--button-text);
|
||||||
|
background: var(--background-button) !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<header>
|
||||||
|
<h3>Events</h3>
|
||||||
|
<PlusButton on:click={() => openModal()} />
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="root">
|
||||||
|
<form class="uk-form-stacked form-root">
|
||||||
|
{#each events as event, index}
|
||||||
|
{#if event.handlers.length > 0}
|
||||||
|
<div
|
||||||
|
class="handler-container hierarchy-item {selectedEvent && selectedEvent.index === index ? 'selected' : ''}"
|
||||||
|
on:click={() => openModal({ ...event, index })}>
|
||||||
|
<span class="event-name">{event.name}</span>
|
||||||
|
<span class="edit-text">EDIT</span>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{/each}
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<EventEditorModal
|
||||||
|
{onPropChanged}
|
||||||
|
open={modalOpen}
|
||||||
|
onClose={closeModal}
|
||||||
|
eventOptions={events}
|
||||||
|
event={selectedEvent}
|
||||||
|
/>
|
|
@ -0,0 +1,148 @@
|
||||||
|
<script>
|
||||||
|
import IconButton from "../../common/IconButton.svelte";
|
||||||
|
import PlusButton from "../../common/PlusButton.svelte";
|
||||||
|
import Select from "../../common/Select.svelte";
|
||||||
|
import StateBindingControl from "../StateBindingControl.svelte";
|
||||||
|
import { find, map, keys, reduce, keyBy } from "lodash/fp";
|
||||||
|
import { pipe, userWithFullAccess } from "../../common/core";
|
||||||
|
import {
|
||||||
|
EVENT_TYPE_MEMBER_NAME,
|
||||||
|
allHandlers
|
||||||
|
} from "../../common/eventHandlers";
|
||||||
|
import { store } from "../../builderStore";
|
||||||
|
|
||||||
|
export let handler;
|
||||||
|
export let onCreate;
|
||||||
|
export let onChanged;
|
||||||
|
export let onRemoved;
|
||||||
|
|
||||||
|
export let index;
|
||||||
|
export let newHandler;
|
||||||
|
|
||||||
|
let eventOptions;
|
||||||
|
let handlerType;
|
||||||
|
let parameters = [];
|
||||||
|
|
||||||
|
$: eventOptions = allHandlers(
|
||||||
|
{ hierarchy: $store.hierarchy },
|
||||||
|
userWithFullAccess({
|
||||||
|
hierarchy: $store.hierarchy,
|
||||||
|
actions: keyBy("name")($store.actions)
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
$: {
|
||||||
|
if (handler) {
|
||||||
|
handlerType = handler[EVENT_TYPE_MEMBER_NAME];
|
||||||
|
parameters = Object.entries(handler.parameters).map(([name, value]) => ({
|
||||||
|
name,
|
||||||
|
value
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
// Empty Handler
|
||||||
|
handlerType = "";
|
||||||
|
parameters = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handlerChanged = (type, params) => {
|
||||||
|
const handlerParams = {};
|
||||||
|
for (let param of params) {
|
||||||
|
handlerParams[param.name] = param.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatedHandler = {
|
||||||
|
[EVENT_TYPE_MEMBER_NAME]: type,
|
||||||
|
parameters: handlerParams
|
||||||
|
};
|
||||||
|
|
||||||
|
onChanged(updatedHandler, index);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlerTypeChanged = e => {
|
||||||
|
const handlerType = eventOptions.find(
|
||||||
|
handler => handler.name === e.target.value
|
||||||
|
);
|
||||||
|
const defaultParams = handlerType.parameters.map(param => ({
|
||||||
|
name: param,
|
||||||
|
value: ""
|
||||||
|
}));
|
||||||
|
|
||||||
|
handlerChanged(handlerType.name, defaultParams);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onParameterChanged = index => value => {
|
||||||
|
const newParams = [...parameters];
|
||||||
|
newParams[index].value = value;
|
||||||
|
handlerChanged(handlerType, newParams);
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.type-selector-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
background: rgba(223, 223, 223, 0.5);
|
||||||
|
border: 1px solid #dfdfdf;
|
||||||
|
margin-bottom: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.handler-option {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.new-handler {
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.handler-controls {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
grid-gap: 10px;
|
||||||
|
padding: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.event-action-button {
|
||||||
|
margin-right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
font-size: 12px;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div class="type-selector-container {newHandler && 'new-handler'}">
|
||||||
|
<div class="handler-controls">
|
||||||
|
<div class="handler-option">
|
||||||
|
<span>Action</span>
|
||||||
|
<Select value={handlerType} on:change={handlerTypeChanged}>
|
||||||
|
<option />
|
||||||
|
{#each eventOptions as option}
|
||||||
|
<option value={option.name}>{option.name}</option>
|
||||||
|
{/each}
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
{#if parameters}
|
||||||
|
{#each parameters as param, idx}
|
||||||
|
<div class="handler-option">
|
||||||
|
<span>{param.name}</span>
|
||||||
|
<StateBindingControl
|
||||||
|
onChanged={onParameterChanged(idx)}
|
||||||
|
value={param.value} />
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
<div class="event-action-button">
|
||||||
|
{#if parameters.length > 0}
|
||||||
|
{#if newHandler}
|
||||||
|
<PlusButton on:click={onCreate} />
|
||||||
|
{:else}
|
||||||
|
<IconButton icon="x" on:click={onRemoved} />
|
||||||
|
{/if}
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -0,0 +1 @@
|
||||||
|
export { default } from "./EventsEditor.svelte";
|
|
@ -1,5 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
import IconButton from "../common/IconButton.svelte";
|
import IconButton from "../common/IconButton.svelte";
|
||||||
|
import Input from "../common/Input.svelte";
|
||||||
import {
|
import {
|
||||||
isBinding, getBinding, setBinding
|
isBinding, getBinding, setBinding
|
||||||
} from "../common/binding";
|
} from "../common/binding";
|
||||||
|
@ -119,9 +120,9 @@
|
||||||
{/each}
|
{/each}
|
||||||
</select>
|
</select>
|
||||||
{:else}
|
{:else}
|
||||||
<input on:change={ev => onChanged(ev.target.value)}
|
<Input
|
||||||
bind:value={value}
|
on:change={ev => onChanged(ev.target.value)}
|
||||||
style="flex: 1 0 auto;" />
|
bind:value={value} />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -14,7 +14,7 @@ module.exports = async (budibaseContext) => {
|
||||||
app.context.master,
|
app.context.master,
|
||||||
config.latestPackagesFolder
|
config.latestPackagesFolder
|
||||||
);
|
);
|
||||||
app.use(koaBody({ multipart : true }));
|
app.use(koaBody({ multipart: true }));
|
||||||
app.use(router(config, app).routes());
|
app.use(router(config, app).routes());
|
||||||
return app.listen(config.port);
|
return app.listen(config.port);
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue