forked from tsai/budibase
committed by
GitHub
17 changed files with 701 additions and 228 deletions
@ -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> |
||||
|
After Width: | Height: | Size: 228 B |
|
After Width: | Height: | Size: 446 B |
@ -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> |
||||
|
|
||||
<div bind:this={ukModal} uk-modal {id}> |
|
||||
<div class="uk-modal-dialog uk-modal-body" uk-overflow-auto> |
|
||||
<slot /> |
|
||||
</div> |
|
||||
</div> |
|
||||
|
|
||||
|
|
||||
<style> |
<style> |
||||
|
.uk-modal-dialog { |
||||
|
border-radius: 0.3rem; |
||||
|
width: 60%; |
||||
|
height: 80vh; |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
} |
||||
|
</style> |
||||
|
|
||||
.uk-modal-dialog { |
<div bind:this={ukModal} uk-modal {id}> |
||||
border-radius: .3rem; |
<div class="uk-modal-dialog uk-modal-body" uk-overflow-auto> |
||||
} |
{#if onClosed} |
||||
|
<button class="uk-modal-close-default" type="button" uk-close /> |
||||
</style> |
{/if} |
||||
|
<slot /> |
||||
|
</div> |
||||
|
</div> |
||||
|
|||||
@ -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> |
||||
@ -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"; |
||||
Loading…
Reference in new issue