basic architecture complete

This commit is contained in:
Martin McKeaveney 2020-01-29 21:04:48 +00:00
parent e54fd6a0d9
commit f0e07cdfca
6 changed files with 294 additions and 156 deletions

View File

@ -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>

View File

@ -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}

View File

@ -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>

View File

@ -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>

View File

@ -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";

View File

@ -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);
}; };