basic architecture complete
This commit is contained in:
parent
e54fd6a0d9
commit
f0e07cdfca
|
@ -0,0 +1,23 @@
|
||||||
|
<style>
|
||||||
|
button {
|
||||||
|
cursor: pointer;
|
||||||
|
outline: none;
|
||||||
|
border: none;
|
||||||
|
border-radius: 5px;
|
||||||
|
background: var(--background-button);
|
||||||
|
|
||||||
|
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: var(--button-text);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<button on:click>+</button>
|
|
@ -1,110 +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 events;
|
|
||||||
let eventType;
|
|
||||||
let parameters = [];
|
|
||||||
|
|
||||||
store.subscribe(s => {
|
|
||||||
events = allHandlers(
|
|
||||||
{ hierarchy: s.hierarchy },
|
|
||||||
userWithFullAccess({
|
|
||||||
hierarchy: s.hierarchy,
|
|
||||||
actions: keyBy("name")(s.actions)
|
|
||||||
})
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
// TODO: refactor
|
|
||||||
$: {
|
|
||||||
if (event) {
|
|
||||||
eventType = event[EVENT_TYPE_MEMBER_NAME];
|
|
||||||
|
|
||||||
parameters = pipe(
|
|
||||||
event.parameters,
|
|
||||||
[keys, map(key => ({ name: key, value: event.parameters[key] }))]
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
eventType = "";
|
|
||||||
parameters = [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: refactor
|
|
||||||
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);
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: refactor
|
|
||||||
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>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.type-selector-container {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.type-selector {
|
|
||||||
border-color: var(--primary50);
|
|
||||||
border-radius: 2px;
|
|
||||||
width: 50px;
|
|
||||||
flex: 1 0 auto;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<div class="type-selector-container">
|
|
||||||
<select
|
|
||||||
class="type-selector uk-select uk-form-small "
|
|
||||||
value={eventType}
|
|
||||||
on:change={eventTypeChanged}>
|
|
||||||
<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}
|
|
|
@ -14,9 +14,10 @@
|
||||||
import Checkbox from "../../common/Checkbox.svelte";
|
import Checkbox from "../../common/Checkbox.svelte";
|
||||||
import Textbox from "../../common/Textbox.svelte";
|
import Textbox from "../../common/Textbox.svelte";
|
||||||
import Dropdown from "../../common/Dropdown.svelte";
|
import Dropdown from "../../common/Dropdown.svelte";
|
||||||
|
import PlusButton from "../../common/PlusButton.svelte";
|
||||||
import IconButton from "../../common/IconButton.svelte";
|
import IconButton from "../../common/IconButton.svelte";
|
||||||
import Modal from "../../common/Modal.svelte";
|
import Modal from "../../common/Modal.svelte";
|
||||||
import EventSelector from "./EventSelector.svelte";
|
import HandlerSelector from "./HandlerSelector.svelte";
|
||||||
|
|
||||||
import { PencilIcon } from "../../common/Icons";
|
import { PencilIcon } from "../../common/Icons";
|
||||||
import { EVENT_TYPE_MEMBER_NAME } from "../../common/eventHandlers";
|
import { EVENT_TYPE_MEMBER_NAME } from "../../common/eventHandlers";
|
||||||
|
@ -27,12 +28,28 @@
|
||||||
export let onPropChanged = () => {};
|
export let onPropChanged = () => {};
|
||||||
export let components;
|
export let components;
|
||||||
|
|
||||||
|
// Structure
|
||||||
|
// {
|
||||||
|
// [eventName]: [{eventHandler}, {eventHandler1}],
|
||||||
|
// [eventName1]: [{eventHandler}, {eventHandler1}],
|
||||||
|
// }
|
||||||
|
|
||||||
let modalOpen = false;
|
let modalOpen = false;
|
||||||
let events = [];
|
let events = [];
|
||||||
let selectedEvent = null;
|
let selectedEvent = {};
|
||||||
let newEventType = "onClick";
|
let newEventType = "onClick";
|
||||||
|
|
||||||
// TODO: only show events that have handlers
|
// TODO: only show events that have handlers
|
||||||
|
|
||||||
|
// $: {
|
||||||
|
// let componentEvents = {};
|
||||||
|
// for (let propName in componentInfo) {
|
||||||
|
// const isEventProp = findType(propName) === EVENT_TYPE;
|
||||||
|
// if (isEventProp) componentEvents[propName] = componentInfo[propName];
|
||||||
|
// }
|
||||||
|
// events = componentEvents;
|
||||||
|
// }
|
||||||
|
|
||||||
$: events =
|
$: events =
|
||||||
componentInfo &&
|
componentInfo &&
|
||||||
Object.entries(componentInfo).filter(
|
Object.entries(componentInfo).filter(
|
||||||
|
@ -47,7 +64,7 @@
|
||||||
.props[propName];
|
.props[propName];
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(componentInfo, events, components);
|
console.log({ componentInfo, events, components });
|
||||||
|
|
||||||
const openModal = event => {
|
const openModal = event => {
|
||||||
selectedEvent = event;
|
selectedEvent = event;
|
||||||
|
@ -55,30 +72,47 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
const closeModal = () => {
|
const closeModal = () => {
|
||||||
selectedEvent = null;
|
selectedEvent = {};
|
||||||
modalOpen = false;
|
modalOpen = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
const addEventHandler = event => {
|
const addEventHandler = eventType => {
|
||||||
const newEventHandler = {
|
const newEventHandler = {
|
||||||
parameters: {},
|
parameters: {},
|
||||||
[EVENT_TYPE_MEMBER_NAME]: ""
|
[EVENT_TYPE_MEMBER_NAME]: ""
|
||||||
};
|
};
|
||||||
events = [...events, newEventHandler];
|
// TODO: improve - just pass the event from props
|
||||||
onPropChanged(newEventType, events);
|
selectedEvent = {
|
||||||
|
...selectedEvent,
|
||||||
|
handlers: [...(selectedEvent.handlers || []), newEventHandler]
|
||||||
|
};
|
||||||
|
// events = [...events, newEventHandler];
|
||||||
|
onPropChanged(newEventType, [...selectedEvent.handlers, newEventHandler]);
|
||||||
};
|
};
|
||||||
|
|
||||||
const changeEventHandler = newEvent => {
|
const changeEventHandler = (updatedHandler, index) => {
|
||||||
console.log({ events, newEventType, newEvent });
|
// TODO: Improve
|
||||||
onPropChanged(newEventType, events);
|
const handlers = [...selectedEvent.handlers];
|
||||||
|
handlers[index] = updatedHandler;
|
||||||
|
|
||||||
|
console.log("CHANGED HANDLERS", handlers);
|
||||||
|
|
||||||
|
selectedEvent = {
|
||||||
|
...selectedEvent,
|
||||||
|
handlers
|
||||||
|
};
|
||||||
|
|
||||||
|
onPropChanged(newEventType, handlers);
|
||||||
};
|
};
|
||||||
|
|
||||||
const removeEventHandler = index => () => {
|
// TODO: verify
|
||||||
events = filter(e => e !== events[index])(events);
|
const removeEventHandler = index => {
|
||||||
onPropChanged(newEventType, []);
|
const handlers = [...selectedEvent.handlers];
|
||||||
|
handlers.splice(index, 1);
|
||||||
|
onPropChanged(newEventType, handlers);
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log(events);
|
console.log("DA HANDLERS", selectedEvent.handlers);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
@ -104,46 +138,119 @@
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
min-width: 250px;
|
min-width: 250px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.heading {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.handler-container {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
grid-gap: 10px;
|
||||||
|
border: 2px solid #fafafa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hierarchy-item {
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 11px 7px;
|
||||||
|
margin: 5px 0;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hierarchy-item:hover {
|
||||||
|
background: #fafafa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selected {
|
||||||
|
color: var(--button-text);
|
||||||
|
background: var(--background-button) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.event-name {
|
||||||
|
}
|
||||||
|
|
||||||
|
.handler-identifier {
|
||||||
|
font-size: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
color: var(--primary100);
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: Should be it's own component */
|
||||||
|
input {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #163057;
|
||||||
|
opacity: 0.7;
|
||||||
|
padding: 5px 10px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border: 1px solid #dbdbdb;
|
||||||
|
border-radius: 2px;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.event-action {
|
||||||
|
background: #fafafa;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<h3>Events</h3>
|
<div class="heading">
|
||||||
|
<h3>Events</h3>
|
||||||
|
<PlusButton on:click={() => openModal({})} />
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="root">
|
<div class="root">
|
||||||
|
|
||||||
<form class="uk-form-stacked form-root">
|
<form class="uk-form-stacked form-root">
|
||||||
{#each events as event, index}
|
{#each events as [name, handlers], index}
|
||||||
<div class="prop-container">
|
{#if handlers.length > 0}
|
||||||
{event[0]}
|
<div
|
||||||
<PencilIcon on:click={() => openModal({ ...event, index })} />
|
class="handler-container hierarchy-item"
|
||||||
</div>
|
on:click={() => openModal({ name, handlers, index })}>
|
||||||
|
<span class="event-name">{name}</span>
|
||||||
|
<span class="edit-text">EDIT</span>
|
||||||
|
<span class="handler-identifier">updateState</span>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
{/each}
|
{/each}
|
||||||
</form>
|
</form>
|
||||||
<button on:click={() => openModal()}>Create Event</button>
|
|
||||||
</div>
|
</div>
|
||||||
<Modal bind:isOpen={modalOpen} onClosed={closeModal}>
|
<Modal bind:isOpen={modalOpen} onClosed={closeModal}>
|
||||||
<h2>{action} Event</h2>
|
<h2>Create a New Component Event</h2>
|
||||||
{#if selectedEvent}
|
<span>Click here to learn more about component events</span>
|
||||||
{JSON.stringify(selectedEvent)}
|
|
||||||
<!-- <EventSelector
|
<h4>Event Name</h4>
|
||||||
onChanged={onEventHandlerChanged(selectedEvent)}
|
<input type="text" />
|
||||||
onRemoved={removeHandler(selectedEvent && selectedEvent.index)}
|
|
||||||
event={selectedEvent} />
|
<h4>Event Type</h4>
|
||||||
<div class="addelement-container" on:click={addHandler}>
|
<select
|
||||||
<IconButton icon="plus" size="12" />
|
class="type-selector uk-select uk-form-small"
|
||||||
</div> -->
|
bind:value={newEventType}>
|
||||||
{:else}
|
{#each events as [name]}
|
||||||
<select bind:value={newEventType}>
|
<option value={name}>{name}</option>
|
||||||
{#each events as [name]}
|
{/each}
|
||||||
<option value={name}>{name}</option>
|
</select>
|
||||||
{/each}
|
|
||||||
</select>
|
<h4>Event Action(s)</h4>
|
||||||
<EventSelector
|
{#if selectedEvent.handlers}
|
||||||
onChanged={changeEventHandler}
|
{#each selectedEvent.handlers as handler, index}
|
||||||
onRemoved={removeEventHandler}
|
<HandlerSelector
|
||||||
event={selectedEvent} />
|
{index}
|
||||||
<div class="addelement-container" on:click={addEventHandler}>
|
onChanged={changeEventHandler}
|
||||||
<IconButton icon="plus" size="12" />
|
onRemoved={removeEventHandler}
|
||||||
</div>
|
{handler} />
|
||||||
<button>Save</button>
|
<hr />
|
||||||
|
{/each}
|
||||||
{/if}
|
{/if}
|
||||||
|
<div
|
||||||
|
class="addelement-container"
|
||||||
|
on:click={() => addEventHandler(newEventType)}>
|
||||||
|
<IconButton icon="plus" size="12" />
|
||||||
|
Add Handler
|
||||||
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
|
@ -0,0 +1,112 @@
|
||||||
|
<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 handler;
|
||||||
|
export let onChanged;
|
||||||
|
export let onRemoved;
|
||||||
|
export let index;
|
||||||
|
|
||||||
|
let eventOptions;
|
||||||
|
let handlerType;
|
||||||
|
let parameters = [];
|
||||||
|
|
||||||
|
$: eventOptions = allHandlers(
|
||||||
|
{ hierarchy: $store.hierarchy },
|
||||||
|
userWithFullAccess({
|
||||||
|
hierarchy: $store.hierarchy,
|
||||||
|
actions: keyBy("name")($store.actions)
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// TODO: refactor
|
||||||
|
$: {
|
||||||
|
if (handler) {
|
||||||
|
handlerType = handler[EVENT_TYPE_MEMBER_NAME];
|
||||||
|
parameters = Object.entries(handler.parameters).map(([name, value]) => ({
|
||||||
|
name,
|
||||||
|
value
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
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
|
||||||
|
);
|
||||||
|
// Set default params for handler
|
||||||
|
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: grid;
|
||||||
|
grid-template-rows: repeat(3, 1fr);
|
||||||
|
background: #fafafa;
|
||||||
|
padding: 22px;
|
||||||
|
border: 1px solid var(--primary75);
|
||||||
|
}
|
||||||
|
|
||||||
|
.type-selector {
|
||||||
|
border-color: var(--primary50);
|
||||||
|
border-radius: 2px;
|
||||||
|
width: 50px;
|
||||||
|
flex: 1 0 auto;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div class="type-selector-container">
|
||||||
|
Action
|
||||||
|
<select
|
||||||
|
class="type-selector uk-select uk-form-small "
|
||||||
|
value={handlerType}
|
||||||
|
on:change={handlerTypeChanged}>
|
||||||
|
<option />
|
||||||
|
{#each eventOptions as option}
|
||||||
|
<option value={option.name}>{option.name}</option>
|
||||||
|
{/each}
|
||||||
|
</select>
|
||||||
|
{#if parameters}
|
||||||
|
{#each parameters as param, index}
|
||||||
|
<div>{param.name}</div>
|
||||||
|
<StateBindingControl
|
||||||
|
onChanged={onParameterChanged(index)}
|
||||||
|
value={param.value} />
|
||||||
|
{/each}
|
||||||
|
{/if}
|
||||||
|
</div>
|
|
@ -1 +1,7 @@
|
||||||
|
export const deleteElement = (array, index) => {
|
||||||
|
const arr = [...array];
|
||||||
|
array.splice(index, 1);
|
||||||
|
return arr;
|
||||||
|
};
|
||||||
|
|
||||||
export { default } from "./EventsEditor.svelte";
|
export { default } from "./EventsEditor.svelte";
|
|
@ -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