1
0
Fork 0
mirror of synced 2024-10-02 10:08:09 +13:00
This commit is contained in:
Martin McKeaveney 2020-01-28 22:39:16 +00:00
parent 6aa6c4d433
commit e54fd6a0d9
10 changed files with 525 additions and 2 deletions

View file

@ -0,0 +1,8 @@
<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 10h7l-9 13v-9H4l9-13z" />
</svg>

After

Width:  |  Height:  |  Size: 181 B

View file

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

View file

@ -4,3 +4,5 @@ export { default as TerminalIcon } from './Terminal.svelte';
export { default as InputIcon } from './Input.svelte';
export { default as ImageIcon } from './Image.svelte';
export { default as ArrowDownIcon } from './ArrowDown.svelte';
export { default as EventsIcon } from './Events.svelte';
export { default as PencilIcon } from './Pencil.svelte';

View file

@ -2,9 +2,10 @@
import PropsView from "./PropsView.svelte";
import { store } from "../builderStore";
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 LayoutEditor from './LayoutEditor.svelte';
import EventsEditor from "./EventsEditor";
let current_view = 'props';
@ -36,6 +37,11 @@
<TerminalIcon />
</button>
</li>
<li>
<button class:selected={current_view === 'events'} on:click={() => current_view = 'events'}>
<EventsIcon />
</button>
</li>
</ul>
{#if !componentInfo.component}
@ -45,6 +51,8 @@
<PropsView {componentInfo} {components} {onPropChanged} />
{:else if current_view === 'layout'}
<LayoutEditor {onStyleChanged} {componentInfo}/>
{:else if current_view === 'events'}
<EventsEditor {componentInfo} {components} {onPropChanged} />
{:else}
<CodeEditor />
{/if}

View file

@ -0,0 +1,49 @@
<script>
import Modal from "../../common/Modal.svelte";
import EventSelector from "../EventSelector.svelte";
export let event;
export let open;
export let closeModal;
$: action = open && event ? "Edit" : "Create";
</script>
<Modal isOpen={open} onClosed={closeModal}>
<h2>{action} Event</h2>
<EventSelector
onChanged={console.log}
onRemoved={console.log}
{event}
/>
</Modal>
<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;
}
.prop-container {
flex: 1 1 auto;
min-width: 250px;
}
.edit-icon:hover {
cursor: pointer;
}
</style>

View file

@ -0,0 +1,110 @@
<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

@ -0,0 +1,149 @@
<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 IconButton from "../../common/IconButton.svelte";
import Modal from "../../common/Modal.svelte";
import EventSelector from "./EventSelector.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;
let newEventType = "onClick";
// TODO: only show events that have handlers
$: events =
componentInfo &&
Object.entries(componentInfo).filter(
([name]) => findType(name) == EVENT_TYPE
);
$: action = selectedEvent ? "Edit" : "Create";
function findType(propName) {
if (!componentInfo._component) return;
return components.find(({ name }) => name === componentInfo._component)
.props[propName];
}
console.log(componentInfo, events, components);
const openModal = event => {
selectedEvent = event;
modalOpen = true;
};
const closeModal = () => {
selectedEvent = null;
modalOpen = false;
};
const addEventHandler = event => {
const newEventHandler = {
parameters: {},
[EVENT_TYPE_MEMBER_NAME]: ""
};
events = [...events, newEventHandler];
onPropChanged(newEventType, events);
};
const changeEventHandler = newEvent => {
console.log({ events, newEventType, newEvent });
onPropChanged(newEventType, events);
};
const removeEventHandler = index => () => {
events = filter(e => e !== events[index])(events);
onPropChanged(newEventType, []);
};
console.log(events);
</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;
}
.prop-container {
flex: 1 1 auto;
min-width: 250px;
}
</style>
<h3>Events</h3>
<div class="root">
<form class="uk-form-stacked form-root">
{#each events as event, index}
<div class="prop-container">
{event[0]}
<PencilIcon on:click={() => openModal({ ...event, index })} />
</div>
{/each}
</form>
<button on:click={() => openModal()}>Create Event</button>
</div>
<Modal bind:isOpen={modalOpen} onClosed={closeModal}>
<h2>{action} Event</h2>
{#if selectedEvent}
{JSON.stringify(selectedEvent)}
<!-- <EventSelector
onChanged={onEventHandlerChanged(selectedEvent)}
onRemoved={removeHandler(selectedEvent && selectedEvent.index)}
event={selectedEvent} />
<div class="addelement-container" on:click={addHandler}>
<IconButton icon="plus" size="12" />
</div> -->
{:else}
<select bind:value={newEventType}>
{#each events as [name]}
<option value={name}>{name}</option>
{/each}
</select>
<EventSelector
onChanged={changeEventHandler}
onRemoved={removeEventHandler}
event={selectedEvent} />
<div class="addelement-container" on:click={addEventHandler}>
<IconButton icon="plus" size="12" />
</div>
<button>Save</button>
{/if}
</Modal>

View file

@ -0,0 +1,177 @@
<script>
import {
isString
} from "lodash/fp";
import IconButton from "../../common/IconButton.svelte";
import {
isBinding, getBinding, setBinding
} from "../../common/binding";
export let value="";
export let onChanged= () => {};
export let type="";
export let options=[];
let isBound=false;
let bindingPath="";
let bindingFallbackValue="";
let bindingSource="store";
let isExpanded = false;
let forceIsBound = false;
let canOnlyBind = false;
$: {
canOnlyBind = type === "state";
if(!forceIsBound && canOnlyBind)
forceIsBound = true;
isBound= forceIsBound || isBinding(value);
if(isBound) {
const binding = getBinding(value);
bindingPath= binding.path;
bindingFallbackValue= binding.fallback;
bindingSource = binding.source || "store";
} else {
bindingPath="";
bindingFallbackValue="";
bindingSource="store";
}
}
const clearBinding = () => {
forceIsBound = false;
onChanged("");
}
const bind = (path, fallback, source) => {
if(!path) {
clearBinding("");
return;
}
const binding = setBinding({path, fallback, source});
onChanged(binding);
}
const setBindingPath = ev => {
forceIsBound = canOnlyBind;
bind(ev.target.value, bindingFallbackValue, bindingSource)
}
const setBindingFallback = ev => {
bind(bindingPath, ev.target.value, bindingSource);
}
const setBindingSource = ev => {
bind(bindingPath, bindingFallbackValue, ev.target.value);
}
// const makeBinding = () => {
// forceIsBound=true;
// isExpanded=true;
// }
</script>
{#if isBound}
<div>
<div class="bound-header">
<div>{isExpanded ? "" : bindingPath}</div>
<IconButton icon={isExpanded ? "chevron-up" : "chevron-down"}
size="12"
on:click={() => isExpanded=!isExpanded}/>
{#if !canOnlyBind}
<IconButton icon="trash"
size="12"
on:click={clearBinding}/>
{/if}
</div>
{#if isExpanded}
<div>
<div class="binding-prop-label">Binding Path</div>
<input class="uk-input uk-form-small"
value={bindingPath}
on:change={setBindingPath} >
<div class="binding-prop-label">Fallback Value</div>
<input class="uk-input uk-form-small"
value={bindingFallbackValue}
on:change={setBindingFallback} >
<div class="binding-prop-label">Binding Source</div>
<select class="uk-select uk-form-small"
value={bindingSource}
on:change={setBindingSource}>
<option>store</option>
<option>context</option>
</select>
</div>
{/if}
</div>
{:else}
<div class="unbound-container">
{#if type === "bool"}
<div>
<IconButton icon={value == true ? "check-square" : "square"}
size="19"
on:click={() => onChanged(!value)} />
</div>
{:else if type === "options"}
<select class="uk-select uk-form-small"
value={value}
on:change={ev => onChanged(ev.target.value)}>
{#each options as option}
<option value={option}>{option}</option>
{/each}
</select>
{:else}
<input on:change={ev => onChanged(ev.target.value)}
bind:value={value}
style="flex: 1 0 auto;" />
{/if}
</div>
{/if}
<style>
.unbound-container {
display:flex;
}
.bound-header {
display: flex;
}
.bound-header > div:nth-child(1) {
flex: 1 0 auto;
width: 30px;
color: var(--secondary50);
padding-left: 5px;
}
.binding-prop-label {
color: var(--secondary50);
}
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;
}
</style>

View file

@ -0,0 +1 @@
export { default } from "./EventsEditor.svelte";

View file

@ -8,7 +8,7 @@
"build": "cd appPackages/_master && yarn && cd ../testApp && yarn && cd ../testApp2 && yarn",
"initialise": "node ./initialise/initialiseBudibase init -d ./myapps -c contributors -u admin -p admin",
"budi": "node ../cli/bin/budi",
"dev:builder": "nodemon index"
"dev:builder": "node index"
},
"keywords": [
"budibase"