forked from tsai/budibase
12 changed files with 238 additions and 528 deletions
@ -0,0 +1,30 @@ |
|||
<script> |
|||
export let disabled = false; |
|||
</script> |
|||
|
|||
<style> |
|||
.action-button { |
|||
color: #0055ff; |
|||
background: rgb(54, 133, 249, 0.1); |
|||
font-size: 18px; |
|||
font-weight: bold; |
|||
border-radius: 5px; |
|||
border: none; |
|||
width: 167px; |
|||
height: 64px; |
|||
} |
|||
|
|||
.action-button:hover { |
|||
cursor: pointer; |
|||
} |
|||
|
|||
.action-button:disabled { |
|||
color:#163057; |
|||
cursor: default; |
|||
background: transparent; |
|||
} |
|||
</style> |
|||
|
|||
<button on:click class="action-button" {disabled}> |
|||
<slot /> |
|||
</button> |
|||
@ -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,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> |
|||
@ -1,177 +0,0 @@ |
|||
<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> |
|||
Loading…
Reference in new issue