@ -1,56 +1,47 @@ |
|||
<script> |
|||
import BackendNav from "./nav/BackendNav.svelte" |
|||
import Database from "./database/DatabaseRoot.svelte" |
|||
import UserInterface from "./userInterface/UserInterfaceRoot.svelte" |
|||
import ActionsAndTriggers from "./actionsAndTriggers/ActionsAndTriggersRoot.svelte" |
|||
import AccessLevels from "./accessLevels/AccessLevelsRoot.svelte" |
|||
import ComingSoon from "./common/ComingSoon.svelte" |
|||
|
|||
import BackendNav from "./nav/BackendNav.svelte"; |
|||
import Database from "./database/DatabaseRoot.svelte" ; |
|||
import UserInterface from "./userInterface/UserInterfaceRoot.svelte" ; |
|||
import ActionsAndTriggers from "./actionsAndTriggers/ActionsAndTriggersRoot.svelte" ; |
|||
import AccessLevels from "./accessLevels/AccessLevelsRoot.svelte" ; |
|||
import ComingSoon from "./common/ComingSoon.svelte"; |
|||
|
|||
import {store} from "./builderStore"; |
|||
|
|||
export let navWidth = "50px"; |
|||
|
|||
import { store } from "./builderStore" |
|||
|
|||
export let navWidth = "50px" |
|||
</script> |
|||
|
|||
<div class="root"> |
|||
<div class="nav"> |
|||
<BackendNav /> |
|||
</div> |
|||
<div class="content" |
|||
style="width: calc(100% - {navWidth}); left: {navWidth}"> |
|||
{#if $store.activeNav === "database"} |
|||
<Database /> |
|||
{:else if $store.activeNav === "actions"} |
|||
<ActionsAndTriggers /> |
|||
{:else if $store.activeNav === "access levels"} |
|||
<AccessLevels /> |
|||
<div class="content" style="width: calc(100% - {navWidth}); left: {navWidth}"> |
|||
{#if $store.activeNav === 'database'} |
|||
<Database /> |
|||
{:else if $store.activeNav === 'actions'} |
|||
<ActionsAndTriggers /> |
|||
{:else if $store.activeNav === 'access levels'} |
|||
<AccessLevels /> |
|||
{/if} |
|||
</div> |
|||
</div> |
|||
|
|||
|
|||
|
|||
<style> |
|||
|
|||
.root { |
|||
height: 100%; |
|||
display: flex; |
|||
} |
|||
|
|||
|
|||
.content { |
|||
flex: 1 1 auto; |
|||
height: 100%; |
|||
background-color: var(--white); |
|||
margin:0; |
|||
} |
|||
|
|||
.nav { |
|||
flex: 0 1 auto; |
|||
width: 300px; |
|||
height: 100%; |
|||
} |
|||
|
|||
</style> |
|||
.root { |
|||
height: 100%; |
|||
display: flex; |
|||
} |
|||
|
|||
.content { |
|||
flex: 1 1 auto; |
|||
height: 100%; |
|||
background-color: var(--white); |
|||
margin: 0; |
|||
} |
|||
|
|||
.nav { |
|||
flex: 0 1 auto; |
|||
width: 300px; |
|||
height: 100%; |
|||
} |
|||
</style> |
|||
|
|||
@ -1,56 +1,55 @@ |
|||
<script> |
|||
import Button from "./common/Button.svelte" |
|||
import { store } from "./builderStore" |
|||
|
|||
import Button from "./common/Button.svelte" |
|||
import { store } from "./builderStore"; |
|||
|
|||
let errors = []; |
|||
|
|||
let errors = [] |
|||
</script> |
|||
|
|||
<div class="root"> |
|||
<div class="inner"> |
|||
<img src="/_builder/assets/budibase-logo.png" class="logo" alt="budibase logo"/> |
|||
<div> |
|||
|
|||
<div> |
|||
<h4 style="margin-bottom: 20px">Choose an Application</h4> |
|||
{#each $store.apps as app} |
|||
<a href={`#/${app}`} class="app-link">{app}</a> |
|||
{/each} |
|||
</div> |
|||
</div> |
|||
<div class="inner"> |
|||
<img |
|||
src="/_builder/assets/budibase-logo.png" |
|||
class="logo" |
|||
alt="budibase logo" /> |
|||
<div> |
|||
|
|||
<div> |
|||
<h4 style="margin-bottom: 20px">Choose an Application</h4> |
|||
{#each $store.apps as app} |
|||
<a href={`#/${app}`} class="app-link">{app}</a> |
|||
{/each} |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<style> |
|||
|
|||
.root { |
|||
.root { |
|||
position: fixed; |
|||
margin: 0 auto; |
|||
text-align: center; |
|||
top: 20%; |
|||
/*color: #333333; |
|||
background-color: #fdfdfd;*/ |
|||
width:100%; |
|||
} |
|||
width: 100%; |
|||
} |
|||
|
|||
.inner { |
|||
display:inline-block; |
|||
.inner { |
|||
display: inline-block; |
|||
margin: auto; |
|||
} |
|||
} |
|||
|
|||
.logo { |
|||
.logo { |
|||
width: 300px; |
|||
margin-bottom: 40px; |
|||
} |
|||
} |
|||
|
|||
.root :global(.option) { |
|||
width:250px; |
|||
} |
|||
.root :global(.option) { |
|||
width: 250px; |
|||
} |
|||
|
|||
.app-link { |
|||
.app-link { |
|||
margin-top: 10px; |
|||
display: block; |
|||
} |
|||
|
|||
</style> |
|||
} |
|||
</style> |
|||
|
|||
@ -1,27 +1,26 @@ |
|||
<script> |
|||
|
|||
import IconButton from "./common/IconButton.svelte"; |
|||
import { store } from "./builderStore"; |
|||
import UserInterfaceRoot from "./userInterface/UserInterfaceRoot.svelte"; |
|||
import { fade } from "svelte/transition"; |
|||
|
|||
import IconButton from "./common/IconButton.svelte" |
|||
import { store } from "./builderStore" |
|||
import UserInterfaceRoot from "./userInterface/UserInterfaceRoot.svelte" |
|||
import { fade } from "svelte/transition" |
|||
</script> |
|||
|
|||
<div class="root"> |
|||
<div class="content uk-container"> |
|||
<div class="content uk-container"> |
|||
|
|||
<h1>Settings</h1> |
|||
<h1>Settings</h1> |
|||
|
|||
<label> |
|||
<input type="checkbox" |
|||
class="uk-checkbox" |
|||
bind:checked={$store.useAnalytics}> |
|||
Send analytics |
|||
</label> |
|||
<label> |
|||
<input |
|||
type="checkbox" |
|||
class="uk-checkbox" |
|||
bind:checked={$store.useAnalytics} /> |
|||
Send analytics |
|||
</label> |
|||
|
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<style> |
|||
|
|||
</style> |
|||
</style> |
|||
|
|||
@ -1,99 +1,91 @@ |
|||
<script> |
|||
import { cloneDeep, map, some, filter } from "lodash/fp" |
|||
import Textbox from "../common/Textbox.svelte" |
|||
import Checkbox from "../common/Checkbox.svelte" |
|||
import ButtonGroup from "../common/ButtonGroup.svelte" |
|||
import Button from "../common/Button.svelte" |
|||
import { validateAccessLevels } from "../common/core" |
|||
import ErrorsBox from "../common/ErrorsBox.svelte" |
|||
|
|||
import {cloneDeep, map, some, filter} from "lodash/fp"; |
|||
import Textbox from "../common/Textbox.svelte"; |
|||
import Checkbox from "../common/Checkbox.svelte"; |
|||
import ButtonGroup from "../common/ButtonGroup.svelte"; |
|||
import Button from "../common/Button.svelte"; |
|||
import {validateAccessLevels} from "../common/core"; |
|||
import ErrorsBox from "../common/ErrorsBox.svelte"; |
|||
|
|||
export let level; |
|||
export let allPermissions; |
|||
export let onFinished; |
|||
export let isNew; |
|||
export let allLevels; |
|||
export let hierarchy; |
|||
export let actions; |
|||
|
|||
let errors = []; |
|||
let clonedLevel = cloneDeep(level); |
|||
|
|||
const matchPermissions = (p1, p2) => |
|||
p1.type === p2.type |
|||
&& |
|||
((!p2.nodeKey && !p1.nodeKey) |
|||
|| p2.nodeKey === p1.nodeKey); |
|||
|
|||
const hasPermission = hasPerm => |
|||
some(p => matchPermissions(p, hasPerm)) |
|||
(clonedLevel.permissions); |
|||
|
|||
$: permissionMatrix = |
|||
map(p => ({permission:p, hasPermission: hasPermission(p)})) |
|||
(allPermissions) |
|||
|
|||
const getPermissionName = perm => |
|||
perm.nodeKey |
|||
? `${perm.type} - ${perm.nodeKey}` |
|||
: perm.type; |
|||
|
|||
const save = () => { |
|||
|
|||
const newLevels = |
|||
isNew |
|||
? [...allLevels, clonedLevel] |
|||
: [...filter(l => l.name !== level.name)(allLevels), clonedLevel]; |
|||
|
|||
errors = validateAccessLevels( |
|||
hierarchy, |
|||
actions, |
|||
newLevels |
|||
); |
|||
|
|||
if(errors.length > 0) return; |
|||
|
|||
onFinished(clonedLevel); |
|||
} |
|||
|
|||
const permissionChanged = perm => ev => { |
|||
const hasPermission = ev.target.checked; |
|||
|
|||
if(hasPermission) { |
|||
clonedLevel.permissions.push(perm); |
|||
export let level |
|||
export let allPermissions |
|||
export let onFinished |
|||
export let isNew |
|||
export let allLevels |
|||
export let hierarchy |
|||
export let actions |
|||
|
|||
let errors = [] |
|||
let clonedLevel = cloneDeep(level) |
|||
|
|||
const matchPermissions = (p1, p2) => |
|||
p1.type === p2.type && |
|||
((!p2.nodeKey && !p1.nodeKey) || p2.nodeKey === p1.nodeKey) |
|||
|
|||
const hasPermission = hasPerm => |
|||
some(p => matchPermissions(p, hasPerm))(clonedLevel.permissions) |
|||
|
|||
$: permissionMatrix = map(p => ({ |
|||
permission: p, |
|||
hasPermission: hasPermission(p), |
|||
}))(allPermissions) |
|||
|
|||
const getPermissionName = perm => |
|||
perm.nodeKey ? `${perm.type} - ${perm.nodeKey}` : perm.type |
|||
|
|||
const save = () => { |
|||
const newLevels = isNew |
|||
? [...allLevels, clonedLevel] |
|||
: [...filter(l => l.name !== level.name)(allLevels), clonedLevel] |
|||
|
|||
errors = validateAccessLevels(hierarchy, actions, newLevels) |
|||
|
|||
if (errors.length > 0) return |
|||
|
|||
onFinished(clonedLevel) |
|||
} |
|||
|
|||
const permissionChanged = perm => ev => { |
|||
const hasPermission = ev.target.checked |
|||
|
|||
if (hasPermission) { |
|||
clonedLevel.permissions.push(perm) |
|||
} else { |
|||
clonedLevel.permissions = filter(p => !matchPermissions(p, perm))(clonedLevel.permissions); |
|||
clonedLevel.permissions = filter(p => !matchPermissions(p, perm))( |
|||
clonedLevel.permissions |
|||
) |
|||
} |
|||
} |
|||
|
|||
} |
|||
</script> |
|||
|
|||
<div> |
|||
|
|||
<ErrorsBox {errors} /> |
|||
|
|||
<form class="uk-form-horizontal"> |
|||
<ErrorsBox {errors} /> |
|||
|
|||
<Textbox label="Name" bind:text={clonedLevel.name} /> |
|||
<form class="uk-form-horizontal"> |
|||
|
|||
{#each permissionMatrix as permission} |
|||
<div> |
|||
<Checkbox label={getPermissionName(permission.permission)} |
|||
checked={permission.hasPermission} |
|||
on:change={permissionChanged(permission.permission)} /> |
|||
</div> |
|||
{/each} |
|||
<Textbox label="Name" bind:text={clonedLevel.name} /> |
|||
|
|||
</form> |
|||
{#each permissionMatrix as permission} |
|||
<div> |
|||
<Checkbox |
|||
label={getPermissionName(permission.permission)} |
|||
checked={permission.hasPermission} |
|||
on:change={permissionChanged(permission.permission)} /> |
|||
</div> |
|||
{/each} |
|||
|
|||
<ButtonGroup style="margin-top: 10px"> |
|||
<Button color="primary" grouped on:click={save}>Save</Button> |
|||
<Button color="secondary" grouped on:click={() => onFinished()}>Cancel</Button> |
|||
</ButtonGroup> |
|||
</form> |
|||
|
|||
<ButtonGroup style="margin-top: 10px"> |
|||
<Button color="primary" grouped on:click={save}>Save</Button> |
|||
<Button color="secondary" grouped on:click={() => onFinished()}> |
|||
Cancel |
|||
</Button> |
|||
</ButtonGroup> |
|||
|
|||
</div> |
|||
|
|||
<style> |
|||
|
|||
</style> |
|||
</style> |
|||
|
|||
@ -1,119 +1,115 @@ |
|||
<script> |
|||
|
|||
import ButtonGroup from "../common/ButtonGroup.svelte"; |
|||
import Button from "../common/Button.svelte"; |
|||
import {store} from "../builderStore"; |
|||
import {generateFullPermissions, getNewAccessLevel} from "../common/core"; |
|||
import getIcon from "../common/icon"; |
|||
import AccessLevelView from "./AccessLevelView.svelte"; |
|||
import Modal from "../common/Modal.svelte"; |
|||
|
|||
let editingLevel = null; |
|||
let editingLevelIsNew = false; |
|||
$: isEditing = (editingLevel !== null); |
|||
|
|||
let allPermissions = []; |
|||
store.subscribe(db => { |
|||
allPermissions = generateFullPermissions(db.hierarchy, db.actions); |
|||
}) |
|||
|
|||
let onLevelEdit = (level) => { |
|||
editingLevel = level; |
|||
editingLevelIsNew = false; |
|||
}; |
|||
|
|||
let onLevelCancel = () => { |
|||
editingAction = null; |
|||
}; |
|||
|
|||
let onLevelDelete = (level) => { |
|||
store.deleteLevel(level); |
|||
}; |
|||
|
|||
|
|||
let createNewLevel = () => { |
|||
editingLevelIsNew = true; |
|||
editingLevel = getNewAccessLevel(); |
|||
} |
|||
|
|||
let onEditingFinished = (level) => { |
|||
if(level) { |
|||
store.saveLevel(level, editingLevelIsNew, editingLevel); |
|||
import ButtonGroup from "../common/ButtonGroup.svelte" |
|||
import Button from "../common/Button.svelte" |
|||
import { store } from "../builderStore" |
|||
import { generateFullPermissions, getNewAccessLevel } from "../common/core" |
|||
import getIcon from "../common/icon" |
|||
import AccessLevelView from "./AccessLevelView.svelte" |
|||
import Modal from "../common/Modal.svelte" |
|||
|
|||
let editingLevel = null |
|||
let editingLevelIsNew = false |
|||
$: isEditing = editingLevel !== null |
|||
|
|||
let allPermissions = [] |
|||
store.subscribe(db => { |
|||
allPermissions = generateFullPermissions(db.hierarchy, db.actions) |
|||
}) |
|||
|
|||
let onLevelEdit = level => { |
|||
editingLevel = level |
|||
editingLevelIsNew = false |
|||
} |
|||
|
|||
let onLevelCancel = () => { |
|||
editingAction = null |
|||
} |
|||
|
|||
let onLevelDelete = level => { |
|||
store.deleteLevel(level) |
|||
} |
|||
|
|||
let createNewLevel = () => { |
|||
editingLevelIsNew = true |
|||
editingLevel = getNewAccessLevel() |
|||
} |
|||
|
|||
let onEditingFinished = level => { |
|||
if (level) { |
|||
store.saveLevel(level, editingLevelIsNew, editingLevel) |
|||
} |
|||
editingLevel = null; |
|||
} |
|||
|
|||
const getPermissionsString = perms => { |
|||
return `${perms.length} / ${allPermissions.length}`; |
|||
} |
|||
editingLevel = null |
|||
} |
|||
|
|||
const getPermissionsString = perms => { |
|||
return `${perms.length} / ${allPermissions.length}` |
|||
} |
|||
</script> |
|||
|
|||
<div class="root"> |
|||
|
|||
<ButtonGroup> |
|||
<Button grouped color="secondary" on:click={createNewLevel}>Create New Access Level</Button> |
|||
</ButtonGroup> |
|||
<ButtonGroup> |
|||
<Button grouped color="secondary" on:click={createNewLevel}> |
|||
Create New Access Level |
|||
</Button> |
|||
</ButtonGroup> |
|||
|
|||
{#if $store.accessLevels} |
|||
<table class="fields-table uk-table uk-table-small"> |
|||
<thead> |
|||
{#if $store.accessLevels} |
|||
<table class="fields-table uk-table uk-table-small"> |
|||
<thead> |
|||
<tr> |
|||
<th>Name</th> |
|||
<th>Permissions</th> |
|||
<th></th> |
|||
<th>Name</th> |
|||
<th>Permissions</th> |
|||
<th /> |
|||
</tr> |
|||
</thead> |
|||
<tbody> |
|||
</thead> |
|||
<tbody> |
|||
{#each $store.accessLevels.levels as level} |
|||
<tr> |
|||
<td >{level.name}</td> |
|||
<td >{getPermissionsString(level.permissions)}</td> |
|||
<tr> |
|||
<td>{level.name}</td> |
|||
<td>{getPermissionsString(level.permissions)}</td> |
|||
<td class="edit-button"> |
|||
<span on:click={() => onLevelEdit(level)}>{@html getIcon("edit")}</span> |
|||
<span on:click={() => onLevelDelete(level)}>{@html getIcon("trash")}</span> |
|||
<span on:click={() => onLevelEdit(level)}> |
|||
{@html getIcon('edit')} |
|||
</span> |
|||
<span on:click={() => onLevelDelete(level)}> |
|||
{@html getIcon('trash')} |
|||
</span> |
|||
</td> |
|||
</tr> |
|||
</tr> |
|||
{/each} |
|||
</tbody> |
|||
</table> |
|||
{:else} |
|||
(no actions added) |
|||
{/if} |
|||
|
|||
</tbody> |
|||
</table> |
|||
{:else}(no actions added){/if} |
|||
|
|||
<Modal bind:isOpen={isEditing}> |
|||
<Modal bind:isOpen={isEditing}> |
|||
{#if isEditing} |
|||
<AccessLevelView level={editingLevel} |
|||
allPermissions={allPermissions} |
|||
onFinished={onEditingFinished} |
|||
isNew={editingLevelIsNew} |
|||
allLevels={$store.accessLevels.levels} |
|||
hierarchy={$store.hierarchy} |
|||
actions={$store.actions} /> |
|||
{/if} |
|||
</Modal> |
|||
|
|||
<AccessLevelView |
|||
level={editingLevel} |
|||
{allPermissions} |
|||
onFinished={onEditingFinished} |
|||
isNew={editingLevelIsNew} |
|||
allLevels={$store.accessLevels.levels} |
|||
hierarchy={$store.hierarchy} |
|||
actions={$store.actions} /> |
|||
{/if} |
|||
</Modal> |
|||
|
|||
</div> |
|||
|
|||
<style> |
|||
|
|||
.root { |
|||
.root { |
|||
height: 100%; |
|||
position: relative; |
|||
padding: 1.5rem; |
|||
} |
|||
|
|||
} |
|||
|
|||
.actions-header { |
|||
.actions-header { |
|||
flex: 0 1 auto; |
|||
} |
|||
} |
|||
|
|||
.node-view { |
|||
.node-view { |
|||
overflow-y: auto; |
|||
flex: 1 1 auto; |
|||
} |
|||
|
|||
|
|||
</style> |
|||
} |
|||
</style> |
|||
|
|||
@ -1,123 +1,134 @@ |
|||
<script> |
|||
|
|||
import Textbox from "../common/Textbox.svelte"; |
|||
import Button from "../common/Button.svelte"; |
|||
import ButtonGroup from "../common/ButtonGroup.svelte"; |
|||
import {cloneDeep, filter, keys, |
|||
map, isUndefined} from "lodash/fp"; |
|||
import ErrorsBox from "../common/ErrorsBox.svelte"; |
|||
import {validateActions, pipe} from "../common/core"; |
|||
import getIcon from "../common/icon"; |
|||
|
|||
export let action; |
|||
export let onFinished = (action) => {}; |
|||
export let allActions; |
|||
export let isNew = true; |
|||
|
|||
let optKey = ""; |
|||
let optValue = ""; |
|||
|
|||
let clonedAction = cloneDeep(action); |
|||
let initialOptions = pipe(action.initialOptions, [ |
|||
import Textbox from "../common/Textbox.svelte" |
|||
import Button from "../common/Button.svelte" |
|||
import ButtonGroup from "../common/ButtonGroup.svelte" |
|||
import { cloneDeep, filter, keys, map, isUndefined } from "lodash/fp" |
|||
import ErrorsBox from "../common/ErrorsBox.svelte" |
|||
import { validateActions, pipe } from "../common/core" |
|||
import getIcon from "../common/icon" |
|||
|
|||
export let action |
|||
export let onFinished = action => {} |
|||
export let allActions |
|||
export let isNew = true |
|||
|
|||
let optKey = "" |
|||
let optValue = "" |
|||
|
|||
let clonedAction = cloneDeep(action) |
|||
let initialOptions = pipe(action.initialOptions, [ |
|||
keys, |
|||
map(k => ({key:k, value:action.initialOptions[k]})) |
|||
]); |
|||
let errors = []; |
|||
|
|||
const addNewOption = () => { |
|||
|
|||
if(optKey && optValue && isUndefined(clonedAction.initialOptions[optKey])) { |
|||
clonedAction.initialOptions[optKey] = optValue; |
|||
initialOptions = [...initialOptions, { |
|||
key:optKey, value: optValue |
|||
}]; |
|||
optKey = ""; |
|||
optValue = ""; |
|||
} |
|||
} |
|||
|
|||
const removeOption = (opt) => { |
|||
if(opt) { |
|||
delete clonedAction.initialOptions[opt.key] |
|||
initialOptions = pipe(initialOptions, [ |
|||
filter(o => o.key !== opt.key) |
|||
]); |
|||
map(k => ({ key: k, value: action.initialOptions[k] })), |
|||
]) |
|||
let errors = [] |
|||
|
|||
const addNewOption = () => { |
|||
if ( |
|||
optKey && |
|||
optValue && |
|||
isUndefined(clonedAction.initialOptions[optKey]) |
|||
) { |
|||
clonedAction.initialOptions[optKey] = optValue |
|||
initialOptions = [ |
|||
...initialOptions, |
|||
{ |
|||
key: optKey, |
|||
value: optValue, |
|||
}, |
|||
] |
|||
optKey = "" |
|||
optValue = "" |
|||
} |
|||
} |
|||
} |
|||
|
|||
const save = () => { |
|||
const removeOption = opt => { |
|||
if (opt) { |
|||
delete clonedAction.initialOptions[opt.key] |
|||
initialOptions = pipe(initialOptions, [filter(o => o.key !== opt.key)]) |
|||
} |
|||
} |
|||
|
|||
const save = () => { |
|||
const newActionsList = [ |
|||
...pipe(allActions ,[filter(a => a !== action)]), |
|||
clonedAction] |
|||
...pipe(allActions, [filter(a => a !== action)]), |
|||
clonedAction, |
|||
] |
|||
|
|||
errors = pipe(newActionsList ,[ |
|||
validateActions, |
|||
map(e => e.error) |
|||
]); |
|||
errors = pipe(newActionsList, [validateActions, map(e => e.error)]) |
|||
|
|||
if(errors.length === 0) |
|||
onFinished(clonedAction); |
|||
} |
|||
|
|||
const cancel = () => { |
|||
onFinished(); |
|||
} |
|||
if (errors.length === 0) onFinished(clonedAction) |
|||
} |
|||
|
|||
const cancel = () => { |
|||
onFinished() |
|||
} |
|||
</script> |
|||
|
|||
<div class="root"> |
|||
|
|||
<ErrorsBox {errors} /> |
|||
|
|||
<form class="uk-form-horizontal"> |
|||
|
|||
<Textbox label="Name" bind:text={clonedAction.name} /> |
|||
<Textbox label="Behaviour Source" bind:text={clonedAction.behaviourSource} /> |
|||
<Textbox label="Behaviour" bind:text={clonedAction.behaviourName} /> |
|||
|
|||
</form> |
|||
|
|||
<div class=" uk-form-stacked" style="margin-bottom: 20px"> |
|||
<label class="uk-form-label">Default Options</label> |
|||
<div class="uk-grid-small" uk-grid> |
|||
<input class="uk-input uk-width-1-4 uk-margin-right" placeholder="key" bind:value={optKey} > |
|||
<input class="uk-input uk-width-1-4 uk-margin-right" placeholder="value" bind:value={optValue} > |
|||
<Button color="primary-outline uk-width-1-4" on:click={addNewOption}>Add</Button> |
|||
</div> |
|||
<div style="margin-top: 10px"> |
|||
{#each initialOptions as option} |
|||
<span class="option-container">{option.key} : {option.value} <span style="font-size:10pt; cursor: pointer" on:click={() => removeOption(option)}>{@html getIcon("trash-2")}</span></span> |
|||
{/each} |
|||
</div> |
|||
<ErrorsBox {errors} /> |
|||
|
|||
<form class="uk-form-horizontal"> |
|||
|
|||
<Textbox label="Name" bind:text={clonedAction.name} /> |
|||
<Textbox |
|||
label="Behaviour Source" |
|||
bind:text={clonedAction.behaviourSource} /> |
|||
<Textbox label="Behaviour" bind:text={clonedAction.behaviourName} /> |
|||
|
|||
</form> |
|||
|
|||
<div class=" uk-form-stacked" style="margin-bottom: 20px"> |
|||
<label class="uk-form-label">Default Options</label> |
|||
<div class="uk-grid-small" uk-grid> |
|||
<input |
|||
class="uk-input uk-width-1-4 uk-margin-right" |
|||
placeholder="key" |
|||
bind:value={optKey} /> |
|||
<input |
|||
class="uk-input uk-width-1-4 uk-margin-right" |
|||
placeholder="value" |
|||
bind:value={optValue} /> |
|||
<Button color="primary-outline uk-width-1-4" on:click={addNewOption}> |
|||
Add |
|||
</Button> |
|||
</div> |
|||
<div style="margin-top: 10px"> |
|||
{#each initialOptions as option} |
|||
<span class="option-container"> |
|||
{option.key} : {option.value} |
|||
<span |
|||
style="font-size:10pt; cursor: pointer" |
|||
on:click={() => removeOption(option)}> |
|||
{@html getIcon('trash-2')} |
|||
</span> |
|||
</span> |
|||
{/each} |
|||
</div> |
|||
</div> |
|||
|
|||
<ButtonGroup> |
|||
<Button color="secondary" grouped on:click={save}>Save</Button> |
|||
<Button color="tertiary" grouped on:click={cancel}>Cancel</Button> |
|||
</ButtonGroup> |
|||
<ButtonGroup> |
|||
<Button color="secondary" grouped on:click={save}>Save</Button> |
|||
<Button color="tertiary" grouped on:click={cancel}>Cancel</Button> |
|||
</ButtonGroup> |
|||
|
|||
|
|||
</div> |
|||
|
|||
|
|||
<style> |
|||
|
|||
.root { |
|||
.root { |
|||
padding: 2rem; |
|||
border-radius: 2rem; |
|||
} |
|||
} |
|||
|
|||
.uk-grid-small { |
|||
.uk-grid-small { |
|||
padding: 1rem; |
|||
} |
|||
} |
|||
|
|||
.option-container { |
|||
.option-container { |
|||
border-style: dotted; |
|||
border-width: 1px; |
|||
border-color: var(--primary75); |
|||
padding: 3px; |
|||
margin-right: 5px; |
|||
} |
|||
|
|||
</style> |
|||
} |
|||
</style> |
|||
|
|||
@ -1,112 +1,111 @@ |
|||
<script> |
|||
|
|||
import getIcon from "../common/icon"; |
|||
import {store} from "../builderStore"; |
|||
import Button from "../common/Button.svelte"; |
|||
import ButtonGroup from "../common/ButtonGroup.svelte"; |
|||
import ActionView from "./ActionView.svelte"; |
|||
import Modal from "../common/Modal.svelte"; |
|||
import {pipe} from "../common/core"; |
|||
import {keys, map, join} from "lodash/fp"; |
|||
|
|||
export let editingActionIsNew = false; |
|||
export let editingAction = null; |
|||
export let onActionEdit = (action) => {}; |
|||
export let onActionDelete = (action) => {}; |
|||
export let onActionSave = (action) => {}; |
|||
export let onActionCancel = () => {}; |
|||
|
|||
$: isEditing = (editingAction !== null); |
|||
|
|||
let actionsArray = []; |
|||
store.subscribe(s => { |
|||
actionsArray = pipe(s.actions, [ |
|||
keys, |
|||
map(k => s.actions[k]) |
|||
]); |
|||
}); |
|||
|
|||
let getDefaultOptionsHtml = defaultOptions => |
|||
import getIcon from "../common/icon" |
|||
import { store } from "../builderStore" |
|||
import Button from "../common/Button.svelte" |
|||
import ButtonGroup from "../common/ButtonGroup.svelte" |
|||
import ActionView from "./ActionView.svelte" |
|||
import Modal from "../common/Modal.svelte" |
|||
import { pipe } from "../common/core" |
|||
import { keys, map, join } from "lodash/fp" |
|||
|
|||
export let editingActionIsNew = false |
|||
export let editingAction = null |
|||
export let onActionEdit = action => {} |
|||
export let onActionDelete = action => {} |
|||
export let onActionSave = action => {} |
|||
export let onActionCancel = () => {} |
|||
|
|||
$: isEditing = editingAction !== null |
|||
|
|||
let actionsArray = [] |
|||
store.subscribe(s => { |
|||
actionsArray = pipe(s.actions, [keys, map(k => s.actions[k])]) |
|||
}) |
|||
|
|||
let getDefaultOptionsHtml = defaultOptions => |
|||
pipe(defaultOptions, [ |
|||
keys, |
|||
map(k => `<span style="color:var(--slate)">${k}: </span>${JSON.stringify(defaultOptions[k])}`), |
|||
join("<br>") |
|||
]); |
|||
|
|||
|
|||
let actionEditingFinished = (action) => { |
|||
|
|||
if(action) { |
|||
onActionSave(action) |
|||
keys, |
|||
map( |
|||
k => |
|||
`<span style="color:var(--slate)">${k}: </span>${JSON.stringify( |
|||
defaultOptions[k] |
|||
)}` |
|||
), |
|||
join("<br>"), |
|||
]) |
|||
|
|||
let actionEditingFinished = action => { |
|||
if (action) { |
|||
onActionSave(action) |
|||
} else { |
|||
onActionCancel(); |
|||
onActionCancel() |
|||
} |
|||
} |
|||
|
|||
} |
|||
</script> |
|||
|
|||
<h3 class="title">Actions</h3> |
|||
|
|||
{#if actionsArray} |
|||
<table class="fields-table uk-table uk-table-small uk-table-striped"> |
|||
<table class="fields-table uk-table uk-table-small uk-table-striped"> |
|||
<thead> |
|||
<tr> |
|||
<th >Description</th> |
|||
<th>Behaviour Source</th> |
|||
<th>Behaviour Name</th> |
|||
<th>Default Options</th> |
|||
<th></th> |
|||
</tr> |
|||
<tr> |
|||
<th>Description</th> |
|||
<th>Behaviour Source</th> |
|||
<th>Behaviour Name</th> |
|||
<th>Default Options</th> |
|||
<th /> |
|||
</tr> |
|||
</thead> |
|||
<tbody> |
|||
{#each actionsArray as action} |
|||
{#each actionsArray as action} |
|||
<tr> |
|||
<td class="table-content">{action.name}</td> |
|||
<td class="table-content">{action.behaviourSource}</td> |
|||
<td class="table-content">{action.behaviourName}</td> |
|||
<td class="table-content">{@html getDefaultOptionsHtml(action.initialOptions)}</td> |
|||
<td class="edit-button"> |
|||
<span on:click={() => onActionEdit(action)}>{@html getIcon("edit")}</span> |
|||
<span on:click={() => onActionDelete(action)}>{@html getIcon("trash")}</span> |
|||
</td> |
|||
<td class="table-content">{action.name}</td> |
|||
<td class="table-content">{action.behaviourSource}</td> |
|||
<td class="table-content">{action.behaviourName}</td> |
|||
<td class="table-content"> |
|||
{@html getDefaultOptionsHtml(action.initialOptions)} |
|||
</td> |
|||
<td class="edit-button"> |
|||
<span on:click={() => onActionEdit(action)}> |
|||
{@html getIcon('edit')} |
|||
</span> |
|||
<span on:click={() => onActionDelete(action)}> |
|||
{@html getIcon('trash')} |
|||
</span> |
|||
</td> |
|||
</tr> |
|||
{/each} |
|||
{/each} |
|||
</tbody> |
|||
</table> |
|||
{:else} |
|||
(no actions added) |
|||
{/if} |
|||
|
|||
</table> |
|||
{:else}(no actions added){/if} |
|||
|
|||
<Modal bind:isOpen={isEditing}> |
|||
{#if isEditing} |
|||
<ActionView action={editingAction} |
|||
allActions={$store.actions} |
|||
onFinished={actionEditingFinished} |
|||
isNew={editingActionIsNew}/> |
|||
{/if} |
|||
{#if isEditing} |
|||
<ActionView |
|||
action={editingAction} |
|||
allActions={$store.actions} |
|||
onFinished={actionEditingFinished} |
|||
isNew={editingActionIsNew} /> |
|||
{/if} |
|||
</Modal> |
|||
|
|||
|
|||
<style> |
|||
|
|||
.edit-button { |
|||
cursor:pointer; |
|||
.edit-button { |
|||
cursor: pointer; |
|||
color: var(--secondary25); |
|||
} |
|||
} |
|||
|
|||
tr:hover .edit-button { |
|||
tr:hover .edit-button { |
|||
color: var(--secondary75); |
|||
} |
|||
} |
|||
|
|||
.title { |
|||
.title { |
|||
margin: 3rem 0rem 0rem 0rem; |
|||
font-weight: 700; |
|||
} |
|||
} |
|||
|
|||
.table-content { |
|||
.table-content { |
|||
font-weight: 500; |
|||
font-size: .9rem; |
|||
} |
|||
|
|||
</style> |
|||
font-size: 0.9rem; |
|||
} |
|||
</style> |
|||
|
|||
@ -1,129 +1,130 @@ |
|||
<script> |
|||
|
|||
import getIcon from "../common/icon"; |
|||
import {store} from "../builderStore"; |
|||
import Button from "../common/Button.svelte"; |
|||
import ButtonGroup from "../common/ButtonGroup.svelte"; |
|||
import Actions from "./Actions.svelte"; |
|||
import Triggers from "./Triggers.svelte"; |
|||
import {getNewAction, getNewTrigger} from "../common/core"; |
|||
|
|||
let editingAction = null; |
|||
let editingActionIsNew = true; |
|||
let editingTrigger = null; |
|||
let editingTriggerIsNew = true; |
|||
|
|||
let getDefaultOptionsHtml = defaultOptions => |
|||
import getIcon from "../common/icon" |
|||
import { store } from "../builderStore" |
|||
import Button from "../common/Button.svelte" |
|||
import ButtonGroup from "../common/ButtonGroup.svelte" |
|||
import Actions from "./Actions.svelte" |
|||
import Triggers from "./Triggers.svelte" |
|||
import { getNewAction, getNewTrigger } from "../common/core" |
|||
|
|||
let editingAction = null |
|||
let editingActionIsNew = true |
|||
let editingTrigger = null |
|||
let editingTriggerIsNew = true |
|||
|
|||
let getDefaultOptionsHtml = defaultOptions => |
|||
pipe(defaultOptions, [ |
|||
keys, |
|||
map(k => `<span style="color:var(--slate)">${k}: </span>${JSON.parse(typeOptions[k])}`), |
|||
join("<br>") |
|||
]); |
|||
|
|||
let onActionEdit = (action) => { |
|||
editingAction = action; |
|||
editingActionIsNew = false; |
|||
} |
|||
|
|||
let newAction = () => { |
|||
editingAction = getNewAction(); |
|||
editingActionIsNew = true; |
|||
} |
|||
|
|||
let onActionDelete = (action) => { |
|||
store.deleteAction(action); |
|||
} |
|||
|
|||
let deleteTrigger = () => {} |
|||
|
|||
let editTrigger = (trigger) => { |
|||
editingTrigger = trigger; |
|||
editingTriggerIsNew = false; |
|||
} |
|||
|
|||
let newTrigger = () => { |
|||
editingTrigger = getNewTrigger(); |
|||
editingTriggerIsNew = true; |
|||
} |
|||
|
|||
let onActionSave = action => { |
|||
store.saveAction( |
|||
action, |
|||
editingActionIsNew, |
|||
editingAction); |
|||
|
|||
editingAction = null; |
|||
} |
|||
|
|||
let onActionCancel = () => { |
|||
editingAction = null; |
|||
} |
|||
|
|||
let onTriggerSave = trigger => { |
|||
store.saveTrigger( |
|||
trigger, |
|||
editingTriggerIsNew, |
|||
editingTrigger); |
|||
|
|||
editingTrigger = null; |
|||
} |
|||
|
|||
let onTriggerCancel = () => { |
|||
editingTrigger = null; |
|||
} |
|||
|
|||
let onTriggerEdit = (trigger) => { |
|||
editingTrigger = trigger; |
|||
editingTriggerIsNew = false; |
|||
} |
|||
|
|||
|
|||
let onTriggerDelete = (trigger) => { |
|||
store.deleteTrigger(trigger); |
|||
} |
|||
|
|||
keys, |
|||
map( |
|||
k => |
|||
`<span style="color:var(--slate)">${k}: </span>${JSON.parse( |
|||
typeOptions[k] |
|||
)}` |
|||
), |
|||
join("<br>"), |
|||
]) |
|||
|
|||
let onActionEdit = action => { |
|||
editingAction = action |
|||
editingActionIsNew = false |
|||
} |
|||
|
|||
let newAction = () => { |
|||
editingAction = getNewAction() |
|||
editingActionIsNew = true |
|||
} |
|||
|
|||
let onActionDelete = action => { |
|||
store.deleteAction(action) |
|||
} |
|||
|
|||
let deleteTrigger = () => {} |
|||
|
|||
let editTrigger = trigger => { |
|||
editingTrigger = trigger |
|||
editingTriggerIsNew = false |
|||
} |
|||
|
|||
let newTrigger = () => { |
|||
editingTrigger = getNewTrigger() |
|||
editingTriggerIsNew = true |
|||
} |
|||
|
|||
let onActionSave = action => { |
|||
store.saveAction(action, editingActionIsNew, editingAction) |
|||
|
|||
editingAction = null |
|||
} |
|||
|
|||
let onActionCancel = () => { |
|||
editingAction = null |
|||
} |
|||
|
|||
let onTriggerSave = trigger => { |
|||
store.saveTrigger(trigger, editingTriggerIsNew, editingTrigger) |
|||
|
|||
editingTrigger = null |
|||
} |
|||
|
|||
let onTriggerCancel = () => { |
|||
editingTrigger = null |
|||
} |
|||
|
|||
let onTriggerEdit = trigger => { |
|||
editingTrigger = trigger |
|||
editingTriggerIsNew = false |
|||
} |
|||
|
|||
let onTriggerDelete = trigger => { |
|||
store.deleteTrigger(trigger) |
|||
} |
|||
</script> |
|||
|
|||
<div class="root"> |
|||
<div class="actions-header"> |
|||
<ButtonGroup> |
|||
<Button color="secondary" |
|||
grouped |
|||
on:click={newAction}>Create New Action</Button> |
|||
<Button color="tertiary" |
|||
grouped |
|||
on:click={newTrigger}>Create New Trigger</Button> |
|||
</ButtonGroup> |
|||
</div> |
|||
|
|||
<div class="node-view"> |
|||
<Actions {editingActionIsNew} {editingAction} |
|||
{onActionEdit} {onActionDelete} {onActionSave} |
|||
{onActionCancel} /> |
|||
|
|||
<Triggers {editingTriggerIsNew} {editingTrigger} |
|||
{onTriggerEdit} {onTriggerDelete} {onTriggerSave} |
|||
{onTriggerCancel} /> |
|||
</div> |
|||
<div class="actions-header"> |
|||
<ButtonGroup> |
|||
<Button color="secondary" grouped on:click={newAction}> |
|||
Create New Action |
|||
</Button> |
|||
<Button color="tertiary" grouped on:click={newTrigger}> |
|||
Create New Trigger |
|||
</Button> |
|||
</ButtonGroup> |
|||
</div> |
|||
|
|||
<div class="node-view"> |
|||
<Actions |
|||
{editingActionIsNew} |
|||
{editingAction} |
|||
{onActionEdit} |
|||
{onActionDelete} |
|||
{onActionSave} |
|||
{onActionCancel} /> |
|||
|
|||
<Triggers |
|||
{editingTriggerIsNew} |
|||
{editingTrigger} |
|||
{onTriggerEdit} |
|||
{onTriggerDelete} |
|||
{onTriggerSave} |
|||
{onTriggerCancel} /> |
|||
</div> |
|||
|
|||
</div> |
|||
|
|||
<style> |
|||
|
|||
.root { |
|||
.root { |
|||
height: 100%; |
|||
position: relative; |
|||
padding: 1.5rem; |
|||
} |
|||
|
|||
} |
|||
|
|||
.actions-header { |
|||
.actions-header { |
|||
flex: 0 1 auto; |
|||
} |
|||
} |
|||
|
|||
.node-view { |
|||
.node-view { |
|||
overflow-y: auto; |
|||
flex: 1 1 auto; |
|||
} |
|||
|
|||
</style> |
|||
} |
|||
</style> |
|||
|
|||
@ -1,68 +1,71 @@ |
|||
<script> |
|||
|
|||
import Textbox from "../common/Textbox.svelte"; |
|||
import Button from "../common/Button.svelte"; |
|||
import Dropdown from "../common/Dropdown.svelte"; |
|||
import ButtonGroup from "../common/ButtonGroup.svelte"; |
|||
import CodeArea from "../common/CodeArea.svelte"; |
|||
import {cloneDeep, filter, keys, some, |
|||
map, isUndefined} from "lodash/fp"; |
|||
import ErrorsBox from "../common/ErrorsBox.svelte"; |
|||
import {validateTriggers, pipe, events} from "../common/core"; |
|||
import getIcon from "../common/icon"; |
|||
|
|||
export let trigger; |
|||
export let onFinished = (action) => {}; |
|||
export let allTriggers; |
|||
export let allActions; |
|||
export let isNew = true; |
|||
|
|||
let clonedTrigger = cloneDeep(trigger); |
|||
let errors = []; |
|||
$: actionNames = map(a => a.name)(allActions); |
|||
|
|||
let cancel = () => onFinished(); |
|||
let save = () => { |
|||
import Textbox from "../common/Textbox.svelte" |
|||
import Button from "../common/Button.svelte" |
|||
import Dropdown from "../common/Dropdown.svelte" |
|||
import ButtonGroup from "../common/ButtonGroup.svelte" |
|||
import CodeArea from "../common/CodeArea.svelte" |
|||
import { cloneDeep, filter, keys, some, map, isUndefined } from "lodash/fp" |
|||
import ErrorsBox from "../common/ErrorsBox.svelte" |
|||
import { validateTriggers, pipe, events } from "../common/core" |
|||
import getIcon from "../common/icon" |
|||
|
|||
export let trigger |
|||
export let onFinished = action => {} |
|||
export let allTriggers |
|||
export let allActions |
|||
export let isNew = true |
|||
|
|||
let clonedTrigger = cloneDeep(trigger) |
|||
let errors = [] |
|||
$: actionNames = map(a => a.name)(allActions) |
|||
|
|||
let cancel = () => onFinished() |
|||
let save = () => { |
|||
const newTriggersList = [ |
|||
...pipe(allTriggers ,[filter(t => t !== trigger)]), |
|||
clonedTrigger] |
|||
|
|||
errors = validateTriggers(newTriggersList, allActions); |
|||
...pipe(allTriggers, [filter(t => t !== trigger)]), |
|||
clonedTrigger, |
|||
] |
|||
|
|||
const test = map(t =>(!t.actionName || some(a => a.name === t.actionName)(allActions)))(newTriggersList) |
|||
errors = validateTriggers(newTriggersList, allActions) |
|||
|
|||
if(errors.length === 0) |
|||
onFinished(clonedTrigger); |
|||
} |
|||
const test = map( |
|||
t => !t.actionName || some(a => a.name === t.actionName)(allActions) |
|||
)(newTriggersList) |
|||
|
|||
if (errors.length === 0) onFinished(clonedTrigger) |
|||
} |
|||
</script> |
|||
|
|||
<div> |
|||
|
|||
<ErrorsBox {errors} style="margin-bottom:20px"/> |
|||
<ErrorsBox {errors} style="margin-bottom:20px" /> |
|||
|
|||
<form class="uk-form-horizontal"> |
|||
|
|||
<form class="uk-form-horizontal"> |
|||
|
|||
<Dropdown label="Event" |
|||
options={["",...events]} |
|||
bind:selected={clonedTrigger.eventName} /> |
|||
<Dropdown label="Action" |
|||
options={["",...actionNames]} |
|||
bind:selected={clonedTrigger.actionName} /> |
|||
<CodeArea label="Condition (javascript)" |
|||
bind:text={clonedTrigger.condition} /> |
|||
<CodeArea label="Action Options Creator (javascript)" |
|||
bind:text={clonedTrigger.optionsCreator} /> |
|||
<Dropdown |
|||
label="Event" |
|||
options={['', ...events]} |
|||
bind:selected={clonedTrigger.eventName} /> |
|||
<Dropdown |
|||
label="Action" |
|||
options={['', ...actionNames]} |
|||
bind:selected={clonedTrigger.actionName} /> |
|||
<CodeArea |
|||
label="Condition (javascript)" |
|||
bind:text={clonedTrigger.condition} /> |
|||
<CodeArea |
|||
label="Action Options Creator (javascript)" |
|||
bind:text={clonedTrigger.optionsCreator} /> |
|||
|
|||
</form> |
|||
</form> |
|||
|
|||
<ButtonGroup> |
|||
<Button color="primary" grouped on:click={save}>Save</Button> |
|||
<Button color="tertiary" grouped on:click={cancel}>Cancel</Button> |
|||
</ButtonGroup> |
|||
<ButtonGroup> |
|||
<Button color="primary" grouped on:click={save}>Save</Button> |
|||
<Button color="tertiary" grouped on:click={cancel}>Cancel</Button> |
|||
</ButtonGroup> |
|||
|
|||
</div> |
|||
|
|||
<style> |
|||
|
|||
</style> |
|||
</style> |
|||
|
|||
@ -1,94 +1,90 @@ |
|||
<script> |
|||
|
|||
import {store} from "../builderStore"; |
|||
import getIcon from "../common/icon"; |
|||
import Button from "../common/Button.svelte"; |
|||
import Modal from "../common/Modal.svelte"; |
|||
import TriggerView from "./TriggerView.svelte"; |
|||
|
|||
|
|||
export let editingTrigger = null; |
|||
export let editingTriggerIsNew = true; |
|||
export let onTriggerEdit = (trigger) => {}; |
|||
export let onTriggerDelete = (trigger) => {}; |
|||
export let onTriggerSave = (trigger) => {}; |
|||
export let onTriggerCancel = () => {}; |
|||
|
|||
$: isEditing = (editingTrigger !== null); |
|||
|
|||
let triggerEditingFinished = (trigger) => { |
|||
|
|||
if(trigger) { |
|||
onTriggerSave(trigger) |
|||
import { store } from "../builderStore" |
|||
import getIcon from "../common/icon" |
|||
import Button from "../common/Button.svelte" |
|||
import Modal from "../common/Modal.svelte" |
|||
import TriggerView from "./TriggerView.svelte" |
|||
|
|||
export let editingTrigger = null |
|||
export let editingTriggerIsNew = true |
|||
export let onTriggerEdit = trigger => {} |
|||
export let onTriggerDelete = trigger => {} |
|||
export let onTriggerSave = trigger => {} |
|||
export let onTriggerCancel = () => {} |
|||
|
|||
$: isEditing = editingTrigger !== null |
|||
|
|||
let triggerEditingFinished = trigger => { |
|||
if (trigger) { |
|||
onTriggerSave(trigger) |
|||
} else { |
|||
onTriggerCancel(); |
|||
onTriggerCancel() |
|||
} |
|||
} |
|||
|
|||
} |
|||
</script> |
|||
|
|||
<h3 class="title">Triggers</h3> |
|||
|
|||
{#if $store.triggers} |
|||
<table class="fields-table uk-table uk-table-small uk-table-striped"> |
|||
<table class="fields-table uk-table uk-table-small uk-table-striped"> |
|||
<thead> |
|||
<tr> |
|||
<th>Event</th> |
|||
<th>Action</th> |
|||
<th>Condition</th> |
|||
<th>Create Options</th> |
|||
<th></th> |
|||
</tr> |
|||
<tr> |
|||
<th>Event</th> |
|||
<th>Action</th> |
|||
<th>Condition</th> |
|||
<th>Create Options</th> |
|||
<th /> |
|||
</tr> |
|||
</thead> |
|||
<tbody> |
|||
{#each $store.triggers as trigger} |
|||
{#each $store.triggers as trigger} |
|||
<tr> |
|||
<td class="table-content">{trigger.eventName}</td> |
|||
<td class="table-content">{trigger.actionName}</td> |
|||
<td class="table-content">{trigger.condition}</td> |
|||
<td class="table-content">{trigger.optionsCreator}</td> |
|||
<td class="edit-button"> |
|||
<span on:click={() => onTriggerEdit(trigger)}>{@html getIcon("edit")}</span> |
|||
<span on:click={() => onTriggerDelete(trigger)}>{@html getIcon("trash")}</span> |
|||
</td> |
|||
<td class="table-content">{trigger.eventName}</td> |
|||
<td class="table-content">{trigger.actionName}</td> |
|||
<td class="table-content">{trigger.condition}</td> |
|||
<td class="table-content">{trigger.optionsCreator}</td> |
|||
<td class="edit-button"> |
|||
<span on:click={() => onTriggerEdit(trigger)}> |
|||
{@html getIcon('edit')} |
|||
</span> |
|||
<span on:click={() => onTriggerDelete(trigger)}> |
|||
{@html getIcon('trash')} |
|||
</span> |
|||
</td> |
|||
</tr> |
|||
{/each} |
|||
{/each} |
|||
</tbody> |
|||
</table> |
|||
{:else} |
|||
(no triggers added) |
|||
{/if} |
|||
|
|||
</table> |
|||
{:else}(no triggers added){/if} |
|||
|
|||
<Modal bind:isOpen={isEditing}> |
|||
{#if isEditing} |
|||
<TriggerView trigger={editingTrigger} |
|||
allActions={$store.actions} |
|||
allTriggers={$store.triggers} |
|||
onFinished={triggerEditingFinished} |
|||
isNew={editingTriggerIsNew}/> |
|||
{/if} |
|||
{#if isEditing} |
|||
<TriggerView |
|||
trigger={editingTrigger} |
|||
allActions={$store.actions} |
|||
allTriggers={$store.triggers} |
|||
onFinished={triggerEditingFinished} |
|||
isNew={editingTriggerIsNew} /> |
|||
{/if} |
|||
</Modal> |
|||
|
|||
<style> |
|||
|
|||
.edit-button { |
|||
cursor:pointer; |
|||
.edit-button { |
|||
cursor: pointer; |
|||
color: var(--secondary25); |
|||
} |
|||
} |
|||
|
|||
.title { |
|||
.title { |
|||
margin: 3rem 0rem 0rem 0rem; |
|||
font-weight: 700; |
|||
} |
|||
} |
|||
|
|||
.table-content { |
|||
.table-content { |
|||
font-weight: 500; |
|||
font-size: .9rem; |
|||
} |
|||
font-size: 0.9rem; |
|||
} |
|||
|
|||
tr:hover .edit-button { |
|||
tr:hover .edit-button { |
|||
color: var(--secondary75); |
|||
} |
|||
|
|||
</style> |
|||
} |
|||
</style> |
|||
|
|||
@ -1,163 +1,167 @@ |
|||
<script> |
|||
export let color = "primary" |
|||
export let className = "" |
|||
export let style = "" |
|||
export let groupPosition = "" |
|||
export let grouped = false |
|||
|
|||
export let color = "primary"; |
|||
export let className = ""; |
|||
export let style = ""; |
|||
export let groupPosition = ""; |
|||
export let grouped = false; |
|||
|
|||
$: borderClass = grouped |
|||
? "" |
|||
: "border-normal"; |
|||
|
|||
$: borderClass = grouped ? "" : "border-normal" |
|||
</script> |
|||
|
|||
<button class="{color} {className} {borderClass} {grouped ? "grouped" : ""}" |
|||
style="{style}" |
|||
on:click > |
|||
<slot/> |
|||
<button |
|||
class="{color} |
|||
{className} |
|||
{borderClass} |
|||
{grouped ? 'grouped' : ''}" |
|||
{style} |
|||
on:click> |
|||
<slot /> |
|||
</button> |
|||
|
|||
<style> |
|||
|
|||
.border-normal { border-radius: var(--borderradiusall); } |
|||
.border-left { border-radius: var(--borderradius) 0 0 var(--borderradius); } |
|||
.border-right { border-radius: 0 var(--borderradius) var(--borderradius) 0; } |
|||
.border-middle { border-radius: 0; } |
|||
|
|||
button { |
|||
border-style: solid; |
|||
.border-normal { |
|||
border-radius: var(--borderradiusall); |
|||
} |
|||
.border-left { |
|||
border-radius: var(--borderradius) 0 0 var(--borderradius); |
|||
} |
|||
.border-right { |
|||
border-radius: 0 var(--borderradius) var(--borderradius) 0; |
|||
} |
|||
.border-middle { |
|||
border-radius: 0; |
|||
} |
|||
|
|||
button { |
|||
border-style: solid; |
|||
padding: 7.5px 15px; |
|||
cursor:pointer; |
|||
margin:5px; |
|||
cursor: pointer; |
|||
margin: 5px; |
|||
border-radius: 5px; |
|||
} |
|||
} |
|||
|
|||
/* ---- PRIMARY ----*/ |
|||
.primary { |
|||
/* ---- PRIMARY ----*/ |
|||
.primary { |
|||
background-color: var(--primary100); |
|||
border-color: var(--primary100); |
|||
color: var(--white); |
|||
} |
|||
} |
|||
|
|||
.primary:hover { |
|||
.primary:hover { |
|||
background-color: var(--primary75); |
|||
border-color: var(--primary75); |
|||
} |
|||
} |
|||
|
|||
.primary:active { |
|||
.primary:active { |
|||
background-color: var(--primarydark); |
|||
border-color: var(--primarydark); |
|||
} |
|||
} |
|||
|
|||
.primary-outline { |
|||
.primary-outline { |
|||
background-color: var(--white); |
|||
border-color: var(--primary100); |
|||
color: var(--primary100); |
|||
} |
|||
} |
|||
|
|||
.primary-outline:hover { |
|||
.primary-outline:hover { |
|||
background-color: var(--primary10); |
|||
} |
|||
} |
|||
|
|||
.primary-outline:pressed { |
|||
.primary-outline:pressed { |
|||
background-color: var(--primary25); |
|||
} |
|||
} |
|||
|
|||
/* ---- secondary ----*/ |
|||
/* ---- secondary ----*/ |
|||
|
|||
.secondary { |
|||
.secondary { |
|||
background-color: var(--secondary100); |
|||
border-color: var(--secondary100); |
|||
color: var(--white); |
|||
} |
|||
} |
|||
|
|||
.secondary:hover { |
|||
.secondary:hover { |
|||
background-color: var(--secondary75); |
|||
border-color: var(--secondary75); |
|||
} |
|||
} |
|||
|
|||
.secondary:pressed { |
|||
.secondary:pressed { |
|||
background-color: var(--secondarydark); |
|||
border-color: var(--secondarydark); |
|||
} |
|||
} |
|||
|
|||
.secondary-outline { |
|||
.secondary-outline { |
|||
background-color: var(--white); |
|||
border-color: var(--secondary100); |
|||
color: var(--secondary100); |
|||
} |
|||
} |
|||
|
|||
.secondary-outline:hover { |
|||
.secondary-outline:hover { |
|||
background-color: var(--secondary10); |
|||
} |
|||
} |
|||
|
|||
.secondary-outline:pressed { |
|||
.secondary-outline:pressed { |
|||
background-color: var(--secondary25); |
|||
} |
|||
} |
|||
|
|||
|
|||
/* ---- success ----*/ |
|||
.success { |
|||
/* ---- success ----*/ |
|||
.success { |
|||
background-color: var(--success100); |
|||
border-color: var(--success100); |
|||
color: var(--white); |
|||
} |
|||
} |
|||
|
|||
.success:hover { |
|||
.success:hover { |
|||
background-color: var(--success75); |
|||
border-color: var(--success75); |
|||
} |
|||
} |
|||
|
|||
.success:pressed { |
|||
.success:pressed { |
|||
background-color: var(--successdark); |
|||
border-color: var(--successdark); |
|||
} |
|||
} |
|||
|
|||
.success-outline { |
|||
.success-outline { |
|||
background-color: var(--white); |
|||
border-color: var(--success100); |
|||
color: var(--success100); |
|||
} |
|||
} |
|||
|
|||
.success-outline:hover { |
|||
.success-outline:hover { |
|||
background-color: var(--success10); |
|||
} |
|||
} |
|||
|
|||
.success-outline:pressed { |
|||
.success-outline:pressed { |
|||
background-color: var(--success25); |
|||
} |
|||
} |
|||
|
|||
/* ---- deletion ----*/ |
|||
.deletion { |
|||
/* ---- deletion ----*/ |
|||
.deletion { |
|||
background-color: var(--deletion100); |
|||
border-color: var(--deletion100); |
|||
color: var(--white); |
|||
} |
|||
} |
|||
|
|||
.deletion:hover { |
|||
.deletion:hover { |
|||
background-color: var(--deletion75); |
|||
border-color: var(--deletion75); |
|||
} |
|||
} |
|||
|
|||
.deletion:pressed { |
|||
.deletion:pressed { |
|||
background-color: var(--deletiondark); |
|||
border-color: var(--deletiondark); |
|||
} |
|||
} |
|||
|
|||
.deletion-outline { |
|||
.deletion-outline { |
|||
background-color: var(--white); |
|||
border-color: var(--deletion100); |
|||
color: var(--deletion100); |
|||
} |
|||
} |
|||
|
|||
.deletion-outline:hover { |
|||
.deletion-outline:hover { |
|||
background-color: var(--deletion10); |
|||
} |
|||
} |
|||
|
|||
.deletion-outline:pressed { |
|||
.deletion-outline:pressed { |
|||
background-color: var(--deletion25); |
|||
} |
|||
|
|||
|
|||
</style> |
|||
} |
|||
</style> |
|||
|
|||
@ -1,27 +1,25 @@ |
|||
<script> |
|||
export let style=""; |
|||
export let style = "" |
|||
</script> |
|||
|
|||
<div class="root" style={style}> |
|||
<slot /> |
|||
<div class="root" {style}> |
|||
<slot /> |
|||
</div> |
|||
|
|||
<style> |
|||
|
|||
.root { |
|||
.root { |
|||
display: flex; |
|||
} |
|||
} |
|||
|
|||
.root:last-child { |
|||
.root:last-child { |
|||
border-radius: 0 var(--borderradius) var(--borderradius) 0; |
|||
} |
|||
} |
|||
|
|||
.root:first-child { |
|||
border-radius: var(--borderradius) 0 0 var(--borderradius); |
|||
} |
|||
.root:first-child { |
|||
border-radius: var(--borderradius) 0 0 var(--borderradius); |
|||
} |
|||
|
|||
.root:not(:first-child):not(:last-child) { |
|||
.root:not(:first-child):not(:last-child) { |
|||
border-radius: 0; |
|||
} |
|||
|
|||
</style> |
|||
} |
|||
</style> |
|||
|
|||
@ -1,16 +1,13 @@ |
|||
<script> |
|||
|
|||
export let checked=false; |
|||
export let label=""; |
|||
|
|||
export let checked = false |
|||
export let label = "" |
|||
</script> |
|||
|
|||
<input class="uk-checkbox" type="checkbox" bind:checked on:change />{label} |
|||
<input class="uk-checkbox" type="checkbox" bind:checked on:change /> |
|||
{label} |
|||
|
|||
<style> |
|||
|
|||
input { |
|||
margin-right:7px; |
|||
} |
|||
|
|||
</style> |
|||
input { |
|||
margin-right: 7px; |
|||
} |
|||
</style> |
|||
|
|||
@ -1,24 +1,22 @@ |
|||
<script> |
|||
// todo: use https://ace.c9.io |
|||
export let text = ""; |
|||
export let label = ""; |
|||
// todo: use https://ace.c9.io |
|||
export let text = "" |
|||
export let label = "" |
|||
</script> |
|||
|
|||
<div>{label}</div> |
|||
<textarea class="uk-textarea" bind:value={text}></textarea> |
|||
<textarea class="uk-textarea" bind:value={text} /> |
|||
|
|||
<style> |
|||
|
|||
textarea { |
|||
padding:3px; |
|||
margin-top:5px; |
|||
margin-bottom:10px; |
|||
textarea { |
|||
padding: 3px; |
|||
margin-top: 5px; |
|||
margin-bottom: 10px; |
|||
background: var(--lightslate); |
|||
color: var(--white); |
|||
font-family: 'Courier New', Courier, monospace; |
|||
width:95%; |
|||
height:100px; |
|||
font-family: "Courier New", Courier, monospace; |
|||
width: 95%; |
|||
height: 100px; |
|||
border-radius: 5px; |
|||
} |
|||
|
|||
</style> |
|||
} |
|||
</style> |
|||
|
|||
@ -1,17 +1,13 @@ |
|||
<script> |
|||
|
|||
export let name = ""; |
|||
|
|||
export let name = "" |
|||
</script> |
|||
|
|||
<div> |
|||
<h4>Coming Sometime: {name}</h4> |
|||
<h4>Coming Sometime: {name}</h4> |
|||
</div> |
|||
|
|||
<style> |
|||
|
|||
h4 { |
|||
h4 { |
|||
margin-top: 20px; |
|||
} |
|||
|
|||
</style> |
|||
} |
|||
</style> |
|||
|
|||
@ -1,39 +1,44 @@ |
|||
<script> |
|||
import {createEventDispatcher} from "svelte"; |
|||
|
|||
export let selected; |
|||
export let label; |
|||
export let options; |
|||
export let valueMember; |
|||
export let textMember; |
|||
export let multiple=false; |
|||
export let width = "medium"; |
|||
export let size = "small"; |
|||
|
|||
const dispatch =createEventDispatcher(); |
|||
|
|||
|
|||
import { createEventDispatcher } from "svelte" |
|||
|
|||
export let selected |
|||
export let label |
|||
export let options |
|||
export let valueMember |
|||
export let textMember |
|||
export let multiple = false |
|||
export let width = "medium" |
|||
export let size = "small" |
|||
|
|||
const dispatch = createEventDispatcher() |
|||
</script> |
|||
|
|||
|
|||
<div class="uk-margin"> |
|||
<label class="uk-form-label">{label}</label> |
|||
<div class="uk-form-controls"> |
|||
{#if multiple} |
|||
|
|||
<select class="uk-select uk-form-width-{width} uk-form-{size}" multiple bind:value={selected} on:change> |
|||
{#each options as option} |
|||
<option value={!valueMember ? option : valueMember(option)}>{!textMember ? option : textMember(option)}</option> |
|||
{/each} |
|||
</select> |
|||
|
|||
{:else} |
|||
|
|||
<select class="uk-select uk-form-width-{width} uk-form-{size}" bind:value={selected} on:change> |
|||
{#each options as option} |
|||
<option value={!valueMember ? option : valueMember(option)}>{!textMember ? option : textMember(option)}</option> |
|||
{/each} |
|||
</select> |
|||
{/if} |
|||
</div> |
|||
<label class="uk-form-label">{label}</label> |
|||
<div class="uk-form-controls"> |
|||
{#if multiple} |
|||
<select |
|||
class="uk-select uk-form-width-{width} uk-form-{size}" |
|||
multiple |
|||
bind:value={selected} |
|||
on:change> |
|||
{#each options as option} |
|||
<option value={!valueMember ? option : valueMember(option)}> |
|||
{!textMember ? option : textMember(option)} |
|||
</option> |
|||
{/each} |
|||
</select> |
|||
{:else} |
|||
<select |
|||
class="uk-select uk-form-width-{width} uk-form-{size}" |
|||
bind:value={selected} |
|||
on:change> |
|||
{#each options as option} |
|||
<option value={!valueMember ? option : valueMember(option)}> |
|||
{!textMember ? option : textMember(option)} |
|||
</option> |
|||
{/each} |
|||
</select> |
|||
{/if} |
|||
</div> |
|||
</div> |
|||
|
|||
@ -1,68 +1,65 @@ |
|||
<script> |
|||
|
|||
import getIcon from "./icon"; |
|||
export let iconName; |
|||
export let actions = []; // [ {label: "Action Name", onclick: () => {...} } ] |
|||
let isDroppedDown = false; |
|||
|
|||
import getIcon from "./icon" |
|||
export let iconName |
|||
export let actions = [] // [ {label: "Action Name", onclick: () => {...} } ] |
|||
let isDroppedDown = false |
|||
</script> |
|||
|
|||
<div class="root" on:click={() => (isDroppedDown = !isDroppedDown)}> |
|||
{@html getIcon(iconName)} |
|||
|
|||
<div class="root" on:click={() => isDroppedDown = !isDroppedDown}> |
|||
{@html getIcon(iconName)} |
|||
|
|||
<div class="dropdown-background" on:click|stopPropagation={() => isDroppedDown = false} style="display: {isDroppedDown ? 'block' : 'none'}"></div> |
|||
<div |
|||
class="dropdown-background" |
|||
on:click|stopPropagation={() => (isDroppedDown = false)} |
|||
style="display: {isDroppedDown ? 'block' : 'none'}" /> |
|||
|
|||
<div class="dropdown-content" style="display: {isDroppedDown ? 'inline-block' : 'none'}"> |
|||
{#each actions as action} |
|||
<div class="action-row" on:click={action.onclick}> |
|||
{action.label} |
|||
</div> |
|||
{/each} |
|||
</div> |
|||
|
|||
</div> |
|||
<div |
|||
class="dropdown-content" |
|||
style="display: {isDroppedDown ? 'inline-block' : 'none'}"> |
|||
{#each actions as action} |
|||
<div class="action-row" on:click={action.onclick}>{action.label}</div> |
|||
{/each} |
|||
</div> |
|||
|
|||
</div> |
|||
|
|||
<style> |
|||
|
|||
.dropdown-background { |
|||
.dropdown-background { |
|||
position: fixed; |
|||
top:0; |
|||
left:0; |
|||
width:100vw; |
|||
height:100vh; |
|||
} |
|||
top: 0; |
|||
left: 0; |
|||
width: 100vw; |
|||
height: 100vh; |
|||
} |
|||
|
|||
.root { |
|||
.root { |
|||
cursor: pointer; |
|||
z-index: 1; |
|||
} |
|||
} |
|||
|
|||
.dropdown-content { |
|||
.dropdown-content { |
|||
position: absolute; |
|||
background-color: var(--white); |
|||
min-width: 160px; |
|||
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2); |
|||
box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2); |
|||
z-index: 1; |
|||
font-weight: normal; |
|||
border-style: solid; |
|||
border-width: 1px; |
|||
border-color: var(--secondary10); |
|||
} |
|||
} |
|||
|
|||
.dropdown-content:not(:focus) { |
|||
.dropdown-content:not(:focus) { |
|||
display: none; |
|||
} |
|||
} |
|||
|
|||
.action-row { |
|||
.action-row { |
|||
padding: 7px 10px; |
|||
cursor:pointer; |
|||
} |
|||
cursor: pointer; |
|||
} |
|||
|
|||
.action-row:hover { |
|||
.action-row:hover { |
|||
background-color: var(--primary100); |
|||
color:var(--white); |
|||
} |
|||
|
|||
</style> |
|||
color: var(--white); |
|||
} |
|||
</style> |
|||
|
|||
@ -1,29 +1,29 @@ |
|||
<script> |
|||
export let errors = []; |
|||
export let errors = [] |
|||
|
|||
$:hasErrors = errors.length > 0; |
|||
$: hasErrors = errors.length > 0 |
|||
</script> |
|||
|
|||
{#if hasErrors} |
|||
<div class="error-container"> |
|||
<div class="error-container"> |
|||
{#each errors as error} |
|||
<div class="error-row">{error.field ? `${error.field}: ` : ""}{error.error}</div> |
|||
<div class="error-row"> |
|||
{error.field ? `${error.field}: ` : ''}{error.error} |
|||
</div> |
|||
{/each} |
|||
</div> |
|||
</div> |
|||
{/if} |
|||
|
|||
<style> |
|||
|
|||
.error-container { |
|||
padding:10px; |
|||
.error-container { |
|||
padding: 10px; |
|||
border-style: solid; |
|||
border-color: var(--deletion100); |
|||
border-radius: var(--borderradiusall); |
|||
background: var(--deletion75); |
|||
} |
|||
} |
|||
|
|||
.error-row { |
|||
.error-row { |
|||
padding: 5px 0px; |
|||
} |
|||
|
|||
</style> |
|||
} |
|||
</style> |
|||
|
|||
@ -1,63 +1,59 @@ |
|||
<script> |
|||
import getIcon from "./icon"; |
|||
|
|||
export let size = 18; |
|||
export let icon = ""; |
|||
export let style = ""; |
|||
export let color = "var(--secondary100)"; |
|||
export let hoverColor = "var(--secondary75)"; |
|||
export let attributes = {}; |
|||
|
|||
let currentAttributes = []; |
|||
const addAttributes = (node, attributes) => { |
|||
|
|||
const add = (_attributes) => { |
|||
const attrs = []; |
|||
for(let attr in _attributes) { |
|||
node.setAttribute(attr, _attributes[attr]); |
|||
attrs.push("uk-toggle") |
|||
} |
|||
currentAttributes = attrs; |
|||
import getIcon from "./icon" |
|||
|
|||
export let size = 18 |
|||
export let icon = "" |
|||
export let style = "" |
|||
export let color = "var(--secondary100)" |
|||
export let hoverColor = "var(--secondary75)" |
|||
export let attributes = {} |
|||
|
|||
let currentAttributes = [] |
|||
const addAttributes = (node, attributes) => { |
|||
const add = _attributes => { |
|||
const attrs = [] |
|||
for (let attr in _attributes) { |
|||
node.setAttribute(attr, _attributes[attr]) |
|||
attrs.push("uk-toggle") |
|||
} |
|||
currentAttributes = attrs |
|||
} |
|||
|
|||
add(attributes); |
|||
add(attributes) |
|||
|
|||
return { |
|||
// should implement update method |
|||
update(attributes) { |
|||
for(let attr of currentAttributes) { |
|||
node.removeAttribute(attr) |
|||
} |
|||
add(attributes); |
|||
}, |
|||
destroy() {} |
|||
// should implement update method |
|||
update(attributes) { |
|||
for (let attr of currentAttributes) { |
|||
node.removeAttribute(attr) |
|||
} |
|||
add(attributes) |
|||
}, |
|||
destroy() {}, |
|||
} |
|||
} |
|||
|
|||
} |
|||
</script> |
|||
|
|||
<button style="{style}{style ? ";" : ""} color:{color}; --hovercolor:{hoverColor}" |
|||
on:click |
|||
use:addAttributes={attributes}> |
|||
{@html getIcon(icon, size)} |
|||
<button |
|||
style="{style}{style ? ';' : ''} color:{color}; --hovercolor:{hoverColor}" |
|||
on:click |
|||
use:addAttributes={attributes}> |
|||
{@html getIcon(icon, size)} |
|||
</button> |
|||
|
|||
|
|||
<style> |
|||
|
|||
button { |
|||
button { |
|||
border-style: none; |
|||
background-color: rgba(0,0,0,0); |
|||
background-color: rgba(0, 0, 0, 0); |
|||
cursor: pointer; |
|||
outline:none; |
|||
} |
|||
outline: none; |
|||
} |
|||
|
|||
button:hover { |
|||
button:hover { |
|||
color: var(--hovercolor); |
|||
} |
|||
|
|||
button:active { |
|||
outline:none; |
|||
} |
|||
} |
|||
|
|||
</style> |
|||
button:active { |
|||
outline: none; |
|||
} |
|||
</style> |
|||
|
|||
|
Before Width: | Height: | Size: 228 B After Width: | Height: | Size: 246 B |
|
Before Width: | Height: | Size: 158 B After Width: | Height: | Size: 157 B |
|
Before Width: | Height: | Size: 400 B After Width: | Height: | Size: 430 B |
|
Before Width: | Height: | Size: 339 B After Width: | Height: | Size: 365 B |
|
Before Width: | Height: | Size: 280 B After Width: | Height: | Size: 305 B |
|
Before Width: | Height: | Size: 415 B After Width: | Height: | Size: 448 B |
|
Before Width: | Height: | Size: 446 B After Width: | Height: | Size: 447 B |
|
Before Width: | Height: | Size: 346 B After Width: | Height: | Size: 372 B |
@ -1,35 +1,30 @@ |
|||
<script> |
|||
import {join} from "lodash/fp"; |
|||
import { join } from "lodash/fp" |
|||
|
|||
export let values; |
|||
export let label; |
|||
export let values |
|||
export let label |
|||
|
|||
const inputChanged = ev => { |
|||
const inputChanged = ev => { |
|||
try { |
|||
values = ev.target.value.split("\n"); |
|||
} catch(_) { |
|||
values = []; |
|||
values = ev.target.value.split("\n") |
|||
} catch (_) { |
|||
values = [] |
|||
} |
|||
} |
|||
|
|||
$: valuesText = join("\n")(values); |
|||
|
|||
} |
|||
|
|||
$: valuesText = join("\n")(values) |
|||
</script> |
|||
|
|||
|
|||
<div class="uk-margin"> |
|||
<label class="uk-form-label">{label}</label> |
|||
<div class="uk-form-controls"> |
|||
<textarea value={valuesText} on:change={inputChanged} ></textarea> |
|||
</div> |
|||
<label class="uk-form-label">{label}</label> |
|||
<div class="uk-form-controls"> |
|||
<textarea value={valuesText} on:change={inputChanged} /> |
|||
</div> |
|||
</div> |
|||
|
|||
<style> |
|||
|
|||
textarea { |
|||
width:300px; |
|||
height:200px; |
|||
} |
|||
|
|||
</style> |
|||
textarea { |
|||
width: 300px; |
|||
height: 200px; |
|||
} |
|||
</style> |
|||
|
|||
@ -1,62 +1,59 @@ |
|||
<script> |
|||
import Button from "../common/Button.svelte"; |
|||
import ButtonGroup from "../common/ButtonGroup.svelte"; |
|||
import {store} from "../builderStore"; |
|||
import Modal from "../common/Modal.svelte"; |
|||
import ErrorsBox from "../common/ErrorsBox.svelte"; |
|||
|
|||
export let left; |
|||
let confirmDelete = false; |
|||
const openConfirmDelete = () => { |
|||
confirmDelete = true; |
|||
} |
|||
|
|||
const deleteCurrentNode = () => { |
|||
confirmDelete = false; |
|||
store.deleteCurrentNode(); |
|||
} |
|||
|
|||
import Button from "../common/Button.svelte" |
|||
import ButtonGroup from "../common/ButtonGroup.svelte" |
|||
import { store } from "../builderStore" |
|||
import Modal from "../common/Modal.svelte" |
|||
import ErrorsBox from "../common/ErrorsBox.svelte" |
|||
|
|||
export let left |
|||
let confirmDelete = false |
|||
const openConfirmDelete = () => { |
|||
confirmDelete = true |
|||
} |
|||
|
|||
const deleteCurrentNode = () => { |
|||
confirmDelete = false |
|||
store.deleteCurrentNode() |
|||
} |
|||
</script> |
|||
|
|||
<div class="root" style="left: {left}"> |
|||
|
|||
<ButtonGroup> |
|||
<Button color="secondary" grouped on:click={store.saveCurrentNode}> |
|||
{#if $store.currentNodeIsNew} |
|||
Create |
|||
{:else} |
|||
Update |
|||
{/if} |
|||
</Button> |
|||
|
|||
{#if !$store.currentNodeIsNew} |
|||
<Button color="tertiary" grouped on:click={openConfirmDelete}> |
|||
Delete |
|||
</Button> |
|||
{/if} |
|||
</ButtonGroup> |
|||
|
|||
{#if !!$store.errors && $store.errors.length > 0} |
|||
<ButtonGroup> |
|||
<Button color="secondary" grouped on:click={store.saveCurrentNode}> |
|||
{#if $store.currentNodeIsNew}Create{:else}Update{/if} |
|||
</Button> |
|||
|
|||
{#if !$store.currentNodeIsNew} |
|||
<Button color="tertiary" grouped on:click={openConfirmDelete}> |
|||
Delete |
|||
</Button> |
|||
{/if} |
|||
</ButtonGroup> |
|||
|
|||
{#if !!$store.errors && $store.errors.length > 0} |
|||
<div style="width: 500px"> |
|||
<ErrorsBox errors={$store.errors}/> |
|||
<ErrorsBox errors={$store.errors} /> |
|||
</div> |
|||
{/if} |
|||
|
|||
<Modal bind:isOpen={confirmDelete}> |
|||
<div style="margin: 10px 0px 20px 0px">Are you sure you want to delete {$store.currentNode.name} ?</div> |
|||
<div style="float:right"> |
|||
<Button color="primary" on:click={deleteCurrentNode}>Yes</Button> |
|||
<Button color="secondary" on:click={() => confirmDelete = false}>No</Button> |
|||
</div> |
|||
</Modal> |
|||
{/if} |
|||
|
|||
<Modal bind:isOpen={confirmDelete}> |
|||
<div style="margin: 10px 0px 20px 0px"> |
|||
Are you sure you want to delete {$store.currentNode.name} ? |
|||
</div> |
|||
<div style="float:right"> |
|||
<Button color="primary" on:click={deleteCurrentNode}>Yes</Button> |
|||
<Button color="secondary" on:click={() => (confirmDelete = false)}> |
|||
No |
|||
</Button> |
|||
</div> |
|||
</Modal> |
|||
</div> |
|||
|
|||
<style> |
|||
|
|||
.root { |
|||
.root { |
|||
padding: 1.5rem; |
|||
width: 100%; |
|||
align-items: right; |
|||
} |
|||
|
|||
</style> |
|||
} |
|||
</style> |
|||
|
|||
@ -1,20 +1,16 @@ |
|||
<script> |
|||
import RecordCard from "./RecordCard.svelte" |
|||
|
|||
import RecordCard from "./RecordCard.svelte"; |
|||
|
|||
export let record = {}; |
|||
|
|||
export let record = {} |
|||
</script> |
|||
|
|||
<div class="root"> |
|||
<div class="title">{record.name}</div> |
|||
<div class="inner"> |
|||
<div class="node-path">{record.nodeKey()}</div> |
|||
</div> |
|||
<div class="title">{record.name}</div> |
|||
<div class="inner"> |
|||
<div class="node-path">{record.nodeKey()}</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<style> |
|||
|
|||
|
|||
|
|||
</style> |
|||
</style> |
|||
|
|||
@ -1,82 +1,85 @@ |
|||
<script> |
|||
import HierarchyRow from "./HierarchyRow.svelte" |
|||
import RecordView from "./RecordView.svelte" |
|||
import IndexView from "./IndexView.svelte" |
|||
import ActionsHeader from "./ActionsHeader.svelte" |
|||
import { store } from "../builderStore" |
|||
import getIcon from "../common/icon" |
|||
import DropdownButton from "../common/DropdownButton.svelte" |
|||
import { hierarchy as hierarchyFunctions } from "../../../core/src" |
|||
|
|||
import HierarchyRow from "./HierarchyRow.svelte"; |
|||
import RecordView from "./RecordView.svelte"; |
|||
import IndexView from "./IndexView.svelte"; |
|||
import ActionsHeader from "./ActionsHeader.svelte"; |
|||
import {store} from "../builderStore"; |
|||
import getIcon from "../common/icon"; |
|||
import DropdownButton from "../common/DropdownButton.svelte"; |
|||
import {hierarchy as hierarchyFunctions} from "../../../core/src"; |
|||
const hierarchyWidth = "200px" |
|||
|
|||
const hierarchyWidth = "200px"; |
|||
const defaultNewIndexActions = [ |
|||
{ |
|||
label: "New Root Index", |
|||
onclick: store.newRootIndex, |
|||
}, |
|||
] |
|||
|
|||
const defaultNewIndexActions = [{ |
|||
label:"New Root Index", |
|||
onclick: store.newRootIndex |
|||
}]; |
|||
const defaultNewRecordActions = [ |
|||
{ |
|||
label: "New Root Record", |
|||
onclick: store.newRootRecord, |
|||
}, |
|||
] |
|||
|
|||
const defaultNewRecordActions = [{ |
|||
label:"New Root Record", |
|||
onclick: store.newRootRecord |
|||
}]; |
|||
let newIndexActions = defaultNewIndexActions |
|||
let newRecordActions = defaultNewRecordActions |
|||
|
|||
let newIndexActions = defaultNewIndexActions; |
|||
let newRecordActions = defaultNewRecordActions; |
|||
|
|||
store.subscribe(db => { |
|||
if(!db.currentNode || hierarchyFunctions.isIndex(db.currentNode)) { |
|||
newRecordActions = defaultNewRecordActions; |
|||
newIndexActions = defaultNewIndexActions; |
|||
store.subscribe(db => { |
|||
if (!db.currentNode || hierarchyFunctions.isIndex(db.currentNode)) { |
|||
newRecordActions = defaultNewRecordActions |
|||
newIndexActions = defaultNewIndexActions |
|||
} else { |
|||
newRecordActions = [ |
|||
...defaultNewRecordActions, |
|||
{label: `New Child Record of ${db.currentNode.name}`, |
|||
onclick: store.newChildRecord} |
|||
]; |
|||
newRecordActions = [ |
|||
...defaultNewRecordActions, |
|||
{ |
|||
label: `New Child Record of ${db.currentNode.name}`, |
|||
onclick: store.newChildRecord, |
|||
}, |
|||
] |
|||
|
|||
newIndexActions = [ |
|||
...defaultNewIndexActions, |
|||
{label: `New Index on ${db.currentNode.name}`, |
|||
onclick: store.newChildIndex} |
|||
]; |
|||
newIndexActions = [ |
|||
...defaultNewIndexActions, |
|||
{ |
|||
label: `New Index on ${db.currentNode.name}`, |
|||
onclick: store.newChildIndex, |
|||
}, |
|||
] |
|||
} |
|||
}); |
|||
|
|||
|
|||
}) |
|||
</script> |
|||
|
|||
<div class="root"> |
|||
<div class="actions-header"> |
|||
{#if $store.currentNode} |
|||
<ActionsHeader left={hierarchyWidth}/> |
|||
{/if} |
|||
</div> |
|||
<div class="node-view"> |
|||
{#if !$store.currentNode} |
|||
<h1 style="margin-left: 100px">:)</h1> |
|||
{:else if $store.currentNode.type === "record"} |
|||
<RecordView /> |
|||
{:else} |
|||
<IndexView /> |
|||
{/if} |
|||
</div> |
|||
<div class="actions-header"> |
|||
{#if $store.currentNode} |
|||
<ActionsHeader left={hierarchyWidth} /> |
|||
{/if} |
|||
</div> |
|||
<div class="node-view"> |
|||
{#if !$store.currentNode} |
|||
<h1 style="margin-left: 100px">:)</h1> |
|||
{:else if $store.currentNode.type === 'record'} |
|||
<RecordView /> |
|||
{:else} |
|||
<IndexView /> |
|||
{/if} |
|||
</div> |
|||
</div> |
|||
|
|||
|
|||
<style> |
|||
.root { |
|||
.root { |
|||
height: 100%; |
|||
position: relative; |
|||
} |
|||
} |
|||
|
|||
.actions-header { |
|||
.actions-header { |
|||
flex: 0 1 auto; |
|||
} |
|||
} |
|||
|
|||
.node-view { |
|||
.node-view { |
|||
overflow-y: auto; |
|||
flex: 1 1 auto; |
|||
} |
|||
|
|||
</style> |
|||
} |
|||
</style> |
|||
|
|||
@ -1,121 +1,158 @@ |
|||
<script> |
|||
import Dropdown from "../common/Dropdown.svelte"; |
|||
import Textbox from "../common/Textbox.svelte"; |
|||
import Button from "../common/Button.svelte"; |
|||
import ButtonGroup from "../common/ButtonGroup.svelte"; |
|||
import NumberBox from "../common/NumberBox.svelte"; |
|||
import ValuesList from "../common/ValuesList.svelte"; |
|||
import ErrorsBox from "../common/ErrorsBox.svelte"; |
|||
import Checkbox from "../common/Checkbox.svelte"; |
|||
import DatePicker from "../common/DatePicker.svelte"; |
|||
import {cloneDeep, assign, keys, |
|||
isNumber, includes, map, isBoolean} from "lodash/fp"; |
|||
import {allTypes, validate, getPotentialReferenceIndexes, |
|||
getDefaultTypeOptions, getNode, |
|||
getPotentialReverseReferenceIndexes} from "../common/core"; |
|||
|
|||
export let field; |
|||
export let allFields; |
|||
export let onFinished = () => {}; |
|||
export let store; |
|||
|
|||
let errors = []; |
|||
let clonedField = cloneDeep(field); |
|||
|
|||
$: isNew = !!field && field.name.length === 0; |
|||
|
|||
$: possibleReferenceIndexes = getPotentialReferenceIndexes( |
|||
store.hierarchy, store.currentNode |
|||
); |
|||
|
|||
$: selectedReverseRefIndex = |
|||
!clonedField.typeOptions.indexNodeKey |
|||
import Dropdown from "../common/Dropdown.svelte" |
|||
import Textbox from "../common/Textbox.svelte" |
|||
import Button from "../common/Button.svelte" |
|||
import ButtonGroup from "../common/ButtonGroup.svelte" |
|||
import NumberBox from "../common/NumberBox.svelte" |
|||
import ValuesList from "../common/ValuesList.svelte" |
|||
import ErrorsBox from "../common/ErrorsBox.svelte" |
|||
import Checkbox from "../common/Checkbox.svelte" |
|||
import DatePicker from "../common/DatePicker.svelte" |
|||
import { |
|||
cloneDeep, |
|||
assign, |
|||
keys, |
|||
isNumber, |
|||
includes, |
|||
map, |
|||
isBoolean, |
|||
} from "lodash/fp" |
|||
import { |
|||
allTypes, |
|||
validate, |
|||
getPotentialReferenceIndexes, |
|||
getDefaultTypeOptions, |
|||
getNode, |
|||
getPotentialReverseReferenceIndexes, |
|||
} from "../common/core" |
|||
|
|||
export let field |
|||
export let allFields |
|||
export let onFinished = () => {} |
|||
export let store |
|||
|
|||
let errors = [] |
|||
let clonedField = cloneDeep(field) |
|||
|
|||
$: isNew = !!field && field.name.length === 0 |
|||
|
|||
$: possibleReferenceIndexes = getPotentialReferenceIndexes( |
|||
store.hierarchy, |
|||
store.currentNode |
|||
) |
|||
|
|||
$: selectedReverseRefIndex = !clonedField.typeOptions.indexNodeKey |
|||
? "" |
|||
: getNode(store.hierarchy, clonedField.typeOptions.indexNodeKey); |
|||
: getNode(store.hierarchy, clonedField.typeOptions.indexNodeKey) |
|||
|
|||
$: possibleReverseReferenceIndexes = |
|||
!selectedReverseRefIndex |
|||
$: possibleReverseReferenceIndexes = !selectedReverseRefIndex |
|||
? [] |
|||
: getPotentialReverseReferenceIndexes( |
|||
store.hierarchy, selectedReverseRefIndex); |
|||
|
|||
const typeChanged = (ev) => |
|||
clonedField.typeOptions = getDefaultTypeOptions(ev.detail); |
|||
|
|||
|
|||
const save = () => { |
|||
|
|||
errors = validate.field(allFields)(clonedField); |
|||
if(errors.length > 0) return; |
|||
field.typeOptions = cloneDeep(clonedField.typeOptions); |
|||
onFinished( |
|||
assign(field)(clonedField) |
|||
); |
|||
} |
|||
|
|||
store.hierarchy, |
|||
selectedReverseRefIndex |
|||
) |
|||
|
|||
const typeChanged = ev => |
|||
(clonedField.typeOptions = getDefaultTypeOptions(ev.detail)) |
|||
|
|||
const save = () => { |
|||
errors = validate.field(allFields)(clonedField) |
|||
if (errors.length > 0) return |
|||
field.typeOptions = cloneDeep(clonedField.typeOptions) |
|||
onFinished(assign(field)(clonedField)) |
|||
} |
|||
</script> |
|||
|
|||
<div class="root"> |
|||
|
|||
<ErrorsBox errors={errors} /> |
|||
|
|||
<form class="uk-form-horizontal"> |
|||
|
|||
<Dropdown label="Type" bind:selected={clonedField.type} options={keys(allTypes)} on:change={typeChanged} /> |
|||
|
|||
{#if isNew} |
|||
<Textbox label="Field Name" bind:text={clonedField.name} /> |
|||
{:else} |
|||
<div style="font-weight: bold">{clonedField.name}</div> |
|||
{/if} |
|||
|
|||
<Textbox label="Label" bind:text={clonedField.label} /> |
|||
|
|||
{#if clonedField.type === "string"} |
|||
<NumberBox label="Max Length" bind:value={clonedField.typeOptions.maxLength} /> |
|||
<ValuesList label="Values (options)" bind:values={clonedField.typeOptions.values} /> |
|||
<Checkbox label="Declared Values Only" bind:checked={clonedField.typeOptions.allowDeclaredValuesOnly} /> |
|||
{:else if clonedField.type === "bool"} |
|||
<Checkbox label="Allow Null" bind:checked={clonedField.typeOptions.allowNulls} /> |
|||
{:else if clonedField.type === "datetime"} |
|||
<DatePicker label="Min Value" bind:value={clonedField.typeOptions.minValue} /> |
|||
<DatePicker label="Max Value" bind:value={clonedField.typeOptions.maxValue} /> |
|||
{:else if clonedField.type === "number"} |
|||
<NumberBox label="Min Value" bind:value={clonedField.typeOptions.minValue} /> |
|||
<NumberBox label="Max Value" bind:value={clonedField.typeOptions.maxValue} /> |
|||
<NumberBox label="Decimal Places" bind:value={clonedField.typeOptions.decimalPlaces} /> |
|||
{:else if clonedField.type === "reference"} |
|||
<Dropdown label="Lookup Index" |
|||
options={possibleReferenceIndexes} |
|||
valueMember={n => n.nodeKey()} |
|||
textMember={n => n.name} |
|||
bind:selected={clonedField.typeOptions.indexNodeKey} /> |
|||
|
|||
<Dropdown label="Reverse Reference Index" |
|||
options={possibleReverseReferenceIndexes} |
|||
multiple=true |
|||
valueMember={n => n.nodeKey()} |
|||
textMember={n => n.name} |
|||
bind:selected={clonedField.typeOptions.reverseIndexNodeKeys} /> |
|||
|
|||
<Textbox label="Display Value" bind:text={clonedField.typeOptions.displayValue} /> |
|||
|
|||
{:else if clonedField.type.startsWith("array")} |
|||
<NumberBox label="Min Length" bind:value={clonedField.typeOptions.minLength} /> |
|||
<NumberBox label="Max Length" bind:value={clonedField.typeOptions.maxLength} /> |
|||
{/if} |
|||
|
|||
</form> |
|||
|
|||
<ButtonGroup style="float: right;"> |
|||
<Button color="primary" grouped on:click={save}>Save</Button> |
|||
<Button color="tertiary" grouped on:click={() => onFinished(false)}>Cancel</Button> |
|||
</ButtonGroup> |
|||
<ErrorsBox {errors} /> |
|||
|
|||
<form class="uk-form-horizontal"> |
|||
|
|||
<Dropdown |
|||
label="Type" |
|||
bind:selected={clonedField.type} |
|||
options={keys(allTypes)} |
|||
on:change={typeChanged} /> |
|||
|
|||
{#if isNew} |
|||
<Textbox label="Field Name" bind:text={clonedField.name} /> |
|||
{:else} |
|||
<div style="font-weight: bold">{clonedField.name}</div> |
|||
{/if} |
|||
|
|||
<Textbox label="Label" bind:text={clonedField.label} /> |
|||
|
|||
{#if clonedField.type === 'string'} |
|||
<NumberBox |
|||
label="Max Length" |
|||
bind:value={clonedField.typeOptions.maxLength} /> |
|||
<ValuesList |
|||
label="Values (options)" |
|||
bind:values={clonedField.typeOptions.values} /> |
|||
<Checkbox |
|||
label="Declared Values Only" |
|||
bind:checked={clonedField.typeOptions.allowDeclaredValuesOnly} /> |
|||
{:else if clonedField.type === 'bool'} |
|||
<Checkbox |
|||
label="Allow Null" |
|||
bind:checked={clonedField.typeOptions.allowNulls} /> |
|||
{:else if clonedField.type === 'datetime'} |
|||
<DatePicker |
|||
label="Min Value" |
|||
bind:value={clonedField.typeOptions.minValue} /> |
|||
<DatePicker |
|||
label="Max Value" |
|||
bind:value={clonedField.typeOptions.maxValue} /> |
|||
{:else if clonedField.type === 'number'} |
|||
<NumberBox |
|||
label="Min Value" |
|||
bind:value={clonedField.typeOptions.minValue} /> |
|||
<NumberBox |
|||
label="Max Value" |
|||
bind:value={clonedField.typeOptions.maxValue} /> |
|||
<NumberBox |
|||
label="Decimal Places" |
|||
bind:value={clonedField.typeOptions.decimalPlaces} /> |
|||
{:else if clonedField.type === 'reference'} |
|||
<Dropdown |
|||
label="Lookup Index" |
|||
options={possibleReferenceIndexes} |
|||
valueMember={n => n.nodeKey()} |
|||
textMember={n => n.name} |
|||
bind:selected={clonedField.typeOptions.indexNodeKey} /> |
|||
|
|||
<Dropdown |
|||
label="Reverse Reference Index" |
|||
options={possibleReverseReferenceIndexes} |
|||
multiple="true" |
|||
valueMember={n => n.nodeKey()} |
|||
textMember={n => n.name} |
|||
bind:selected={clonedField.typeOptions.reverseIndexNodeKeys} /> |
|||
|
|||
<Textbox |
|||
label="Display Value" |
|||
bind:text={clonedField.typeOptions.displayValue} /> |
|||
{:else if clonedField.type.startsWith('array')} |
|||
<NumberBox |
|||
label="Min Length" |
|||
bind:value={clonedField.typeOptions.minLength} /> |
|||
<NumberBox |
|||
label="Max Length" |
|||
bind:value={clonedField.typeOptions.maxLength} /> |
|||
{/if} |
|||
|
|||
</form> |
|||
|
|||
<ButtonGroup style="float: right;"> |
|||
<Button color="primary" grouped on:click={save}>Save</Button> |
|||
<Button color="tertiary" grouped on:click={() => onFinished(false)}> |
|||
Cancel |
|||
</Button> |
|||
</ButtonGroup> |
|||
|
|||
</div> |
|||
|
|||
<style> |
|||
|
|||
|
|||
|
|||
</style> |
|||
</style> |
|||
|
|||
@ -1,45 +1,42 @@ |
|||
<script> |
|||
|
|||
import {store} from "../builderStore"; |
|||
import {cloneDeep} from "lodash/fp"; |
|||
export let level = 0; |
|||
export let node; |
|||
|
|||
import { store } from "../builderStore" |
|||
import { cloneDeep } from "lodash/fp" |
|||
export let level = 0 |
|||
export let node |
|||
</script> |
|||
|
|||
<div class="root"> |
|||
<div class="title" on:click={() => store.selectExistingNode(node.nodeId)} style="padding-left: {20 + (level * 20)}px"> |
|||
{node.name} |
|||
</div> |
|||
{#if node.children} |
|||
<div |
|||
class="title" |
|||
on:click={() => store.selectExistingNode(node.nodeId)} |
|||
style="padding-left: {20 + level * 20}px"> |
|||
{node.name} |
|||
</div> |
|||
{#if node.children} |
|||
{#each node.children as child} |
|||
<svelte:self node={child} |
|||
level={level+1}/> |
|||
<svelte:self node={child} level={level + 1} /> |
|||
{/each} |
|||
{/if} |
|||
{/if} |
|||
</div> |
|||
|
|||
|
|||
<style> |
|||
|
|||
.root { |
|||
.root { |
|||
display: block; |
|||
font-size: .9rem; |
|||
font-size: 0.9rem; |
|||
width: 100%; |
|||
cursor: pointer; |
|||
font-weight: bold; |
|||
} |
|||
} |
|||
|
|||
.title { |
|||
.title { |
|||
font: var(--fontblack); |
|||
padding-top: 10px; |
|||
padding-right: 5px; |
|||
padding-bottom: 10px; |
|||
color: var(--secondary100); |
|||
} |
|||
} |
|||
|
|||
.title:hover { |
|||
.title:hover { |
|||
background-color: var(--secondary10); |
|||
} |
|||
|
|||
</style> |
|||
} |
|||
</style> |
|||
|
|||
@ -1,76 +1,79 @@ |
|||
<script> |
|||
|
|||
import Textbox from "../common/Textbox.svelte"; |
|||
import CodeArea from "../common/CodeArea.svelte"; |
|||
import Button from "../common/Button.svelte"; |
|||
import Dropdown from "../common/Dropdown.svelte"; |
|||
import {store} from "../builderStore"; |
|||
import {filter, some, map} from "lodash/fp"; |
|||
import {hierarchy as hierarchyFunctions, common} from "../../../core/src"; |
|||
|
|||
const pipe = common.$; |
|||
|
|||
let index; |
|||
let indexableRecords = []; |
|||
|
|||
store.subscribe($store => { |
|||
index = $store.currentNode; |
|||
indexableRecords = pipe($store.hierarchy,[ |
|||
hierarchyFunctions.getFlattenedHierarchy, |
|||
filter(hierarchyFunctions.isDecendant(index.parent())), |
|||
filter(hierarchyFunctions.isRecord), |
|||
map(n => ({ |
|||
node:n, |
|||
isallowed: some(id => n.nodeId === id)(index.allowedRecordNodeIds) |
|||
})) |
|||
]); |
|||
}); |
|||
|
|||
const toggleAllowedRecord = record => { |
|||
if(record.isallowed) { |
|||
index.allowedRecordNodeIds = filter(id => id !== record.node.nodeId) |
|||
(index.allowedRecordNodeIds); |
|||
import Textbox from "../common/Textbox.svelte" |
|||
import CodeArea from "../common/CodeArea.svelte" |
|||
import Button from "../common/Button.svelte" |
|||
import Dropdown from "../common/Dropdown.svelte" |
|||
import { store } from "../builderStore" |
|||
import { filter, some, map } from "lodash/fp" |
|||
import { hierarchy as hierarchyFunctions, common } from "../../../core/src" |
|||
|
|||
const pipe = common.$ |
|||
|
|||
let index |
|||
let indexableRecords = [] |
|||
|
|||
store.subscribe($store => { |
|||
index = $store.currentNode |
|||
indexableRecords = pipe($store.hierarchy, [ |
|||
hierarchyFunctions.getFlattenedHierarchy, |
|||
filter(hierarchyFunctions.isDecendant(index.parent())), |
|||
filter(hierarchyFunctions.isRecord), |
|||
map(n => ({ |
|||
node: n, |
|||
isallowed: some(id => n.nodeId === id)(index.allowedRecordNodeIds), |
|||
})), |
|||
]) |
|||
}) |
|||
|
|||
const toggleAllowedRecord = record => { |
|||
if (record.isallowed) { |
|||
index.allowedRecordNodeIds = filter(id => id !== record.node.nodeId)( |
|||
index.allowedRecordNodeIds |
|||
) |
|||
} else { |
|||
index.allowedRecordNodeIds.push(record.node.nodeId); |
|||
index.allowedRecordNodeIds.push(record.node.nodeId) |
|||
} |
|||
}; |
|||
|
|||
} |
|||
</script> |
|||
|
|||
<form class="uk-form-horizontal root"> |
|||
<Textbox bind:text={index.name} label="Name"/> |
|||
|
|||
<div class="allowed-records"> |
|||
<div>Records to Index</div> |
|||
{#each indexableRecords as rec} |
|||
<input type="checkbox" checked={rec.isallowed} on:change={() => toggleAllowedRecord(rec)}/> |
|||
<span>{rec.node.name}</span> |
|||
{/each} |
|||
</div> |
|||
<Textbox bind:text={index.name} label="Name" /> |
|||
|
|||
<div class="allowed-records"> |
|||
<div>Records to Index</div> |
|||
{#each indexableRecords as rec} |
|||
<input |
|||
type="checkbox" |
|||
checked={rec.isallowed} |
|||
on:change={() => toggleAllowedRecord(rec)} /> |
|||
<span>{rec.node.name}</span> |
|||
{/each} |
|||
</div> |
|||
|
|||
<Dropdown |
|||
label="Index Type" |
|||
bind:selected={index.indexType} |
|||
options={['ancestor', 'reference']} /> |
|||
|
|||
<CodeArea bind:text={index.map} label="Map (javascript)" /> |
|||
<CodeArea bind:text={index.filter} label="Filter (javascript expression)" /> |
|||
<CodeArea |
|||
bind:text={index.getShardName} |
|||
label="Shard Name (javascript expression)" /> |
|||
|
|||
|
|||
<Dropdown label="Index Type" bind:selected={index.indexType} options={["ancestor", "reference"]} /> |
|||
|
|||
<CodeArea bind:text={index.map} label="Map (javascript)"/> |
|||
<CodeArea bind:text={index.filter} label="Filter (javascript expression)"/> |
|||
<CodeArea bind:text={index.getShardName} label="Shard Name (javascript expression)"/> |
|||
|
|||
|
|||
</form> |
|||
|
|||
<style> |
|||
|
|||
.root { |
|||
.root { |
|||
height: 100%; |
|||
padding: 15px; |
|||
} |
|||
} |
|||
|
|||
.allowed-records { |
|||
.allowed-records { |
|||
margin: 20px 0px; |
|||
} |
|||
|
|||
.allowed-records > span { |
|||
margin-right:30px; |
|||
} |
|||
} |
|||
|
|||
</style> |
|||
.allowed-records > span { |
|||
margin-right: 30px; |
|||
} |
|||
</style> |
|||
|
|||
@ -1,300 +1,307 @@ |
|||
<script> |
|||
|
|||
import Textbox from "../common/Textbox.svelte"; |
|||
import Button from "../common/Button.svelte"; |
|||
import getIcon from "../common/icon"; |
|||
import FieldView from "./FieldView.svelte"; |
|||
import Modal from "../common/Modal.svelte"; |
|||
import {map, join, filter, some, |
|||
find, keys, isDate} from "lodash/fp"; |
|||
import { store } from "../builderStore"; |
|||
import {common, hierarchy as h} from "../../../core/src"; |
|||
import {templateApi, pipe, validate} from "../common/core"; |
|||
|
|||
let record; |
|||
let getIndexAllowedRecords; |
|||
let editingField = false; |
|||
let fieldToEdit; |
|||
let isNewField = false; |
|||
let newField; |
|||
let editField; |
|||
let deleteField; |
|||
let onFinishedFieldEdit; |
|||
let editIndex; |
|||
|
|||
store.subscribe($store => { |
|||
record = $store.currentNode; |
|||
const flattened = h.getFlattenedHierarchy($store.hierarchy); |
|||
getIndexAllowedRecords = index => |
|||
pipe(index.allowedRecordNodeIds, [ |
|||
filter(id => some(n => n.nodeId === id)(flattened)), |
|||
map(id => find(n => n.nodeId === id) |
|||
(flattened).name), |
|||
join(", ") |
|||
]); |
|||
import Textbox from "../common/Textbox.svelte" |
|||
import Button from "../common/Button.svelte" |
|||
import getIcon from "../common/icon" |
|||
import FieldView from "./FieldView.svelte" |
|||
import Modal from "../common/Modal.svelte" |
|||
import { map, join, filter, some, find, keys, isDate } from "lodash/fp" |
|||
import { store } from "../builderStore" |
|||
import { common, hierarchy as h } from "../../../core/src" |
|||
import { templateApi, pipe, validate } from "../common/core" |
|||
|
|||
let record |
|||
let getIndexAllowedRecords |
|||
let editingField = false |
|||
let fieldToEdit |
|||
let isNewField = false |
|||
let newField |
|||
let editField |
|||
let deleteField |
|||
let onFinishedFieldEdit |
|||
let editIndex |
|||
|
|||
store.subscribe($store => { |
|||
record = $store.currentNode |
|||
const flattened = h.getFlattenedHierarchy($store.hierarchy) |
|||
getIndexAllowedRecords = index => |
|||
pipe(index.allowedRecordNodeIds, [ |
|||
filter(id => some(n => n.nodeId === id)(flattened)), |
|||
map(id => find(n => n.nodeId === id)(flattened).name), |
|||
join(", "), |
|||
]) |
|||
|
|||
newField = () => { |
|||
isNewField = true; |
|||
fieldToEdit = templateApi($store.hierarchy).getNewField("string"); |
|||
editingField = true; |
|||
isNewField = true |
|||
fieldToEdit = templateApi($store.hierarchy).getNewField("string") |
|||
editingField = true |
|||
} |
|||
|
|||
|
|||
onFinishedFieldEdit = (field) => { |
|||
if(field) { |
|||
store.saveField(field); |
|||
} |
|||
editingField = false; |
|||
onFinishedFieldEdit = field => { |
|||
if (field) { |
|||
store.saveField(field) |
|||
} |
|||
editingField = false |
|||
} |
|||
|
|||
editField = (field) => { |
|||
isNewField = false; |
|||
fieldToEdit = field; |
|||
editingField = true; |
|||
editField = field => { |
|||
isNewField = false |
|||
fieldToEdit = field |
|||
editingField = true |
|||
} |
|||
|
|||
deleteField = (field) => { |
|||
store.deleteField(field); |
|||
deleteField = field => { |
|||
store.deleteField(field) |
|||
} |
|||
|
|||
editIndex = index => { |
|||
store.selectExistingNode(index.nodeId); |
|||
store.selectExistingNode(index.nodeId) |
|||
} |
|||
|
|||
}) |
|||
|
|||
let getTypeOptionsValueText = value => { |
|||
if(value === Number.MAX_SAFE_INTEGER |
|||
|| value === Number.MIN_SAFE_INTEGER |
|||
|| new Date(value).getTime() === new Date(8640000000000000).getTime() |
|||
|| new Date(value).getTime() === new Date(-8640000000000000).getTime()) return "(any)"; |
|||
|
|||
if(value === null) return "(not set)"; |
|||
return value; |
|||
} |
|||
|
|||
let getTypeOptions = typeOptions => |
|||
}) |
|||
|
|||
let getTypeOptionsValueText = value => { |
|||
if ( |
|||
value === Number.MAX_SAFE_INTEGER || |
|||
value === Number.MIN_SAFE_INTEGER || |
|||
new Date(value).getTime() === new Date(8640000000000000).getTime() || |
|||
new Date(value).getTime() === new Date(-8640000000000000).getTime() |
|||
) |
|||
return "(any)" |
|||
|
|||
if (value === null) return "(not set)" |
|||
return value |
|||
} |
|||
|
|||
let getTypeOptions = typeOptions => |
|||
pipe(typeOptions, [ |
|||
keys, |
|||
map(k => `<span style="color:var(--slate)">${k}: </span>${getTypeOptionsValueText(typeOptions[k])}`), |
|||
join("<br>") |
|||
]); |
|||
|
|||
const nameChanged = ev => { |
|||
const pluralName = n => `${n}s`; |
|||
if(record.collectionName === "") { |
|||
record.collectionName = pluralName(ev.target.value); |
|||
keys, |
|||
map( |
|||
k => |
|||
`<span style="color:var(--slate)">${k}: </span>${getTypeOptionsValueText( |
|||
typeOptions[k] |
|||
)}` |
|||
), |
|||
join("<br>"), |
|||
]) |
|||
|
|||
const nameChanged = ev => { |
|||
const pluralName = n => `${n}s` |
|||
if (record.collectionName === "") { |
|||
record.collectionName = pluralName(ev.target.value) |
|||
} |
|||
} |
|||
|
|||
} |
|||
</script> |
|||
|
|||
<div class="root"> |
|||
|
|||
<form class="uk-form-horizontal"> |
|||
<h3 class="settings-title"> |
|||
Settings |
|||
</h3> |
|||
|
|||
<Textbox label="Name:" bind:text={record.name} on:change={nameChanged}/> |
|||
{#if !record.isSingle} |
|||
<Textbox label="Collection Name:" bind:text={record.collectionName} /> |
|||
<Textbox label="Shard Factor:" bind:text={record.allidsShardFactor} /> |
|||
{/if} |
|||
<div class="recordkey">{record.nodeKey()}</div> |
|||
|
|||
</form> |
|||
<h3 class="title"> |
|||
Fields <span class="add-field-button" on:click={newField}>{@html getIcon("plus")}</span> |
|||
</h3> |
|||
|
|||
{#if record.fields.length > 0} |
|||
<table class="fields-table uk-table"> |
|||
<thead> |
|||
<tr> |
|||
<th>Name</th> |
|||
<th>Type</th> |
|||
<th>Options</th> |
|||
<th></th> |
|||
</tr> |
|||
</thead> |
|||
<tbody> |
|||
{#each record.fields as field} |
|||
<tr> |
|||
<td > |
|||
<div class="field-label">{field.label}</div> |
|||
<div style="font-size: 0.8em; color: var(--slate)">{field.name}</div> |
|||
</td> |
|||
<td >{field.type}</td> |
|||
<td >{@html getTypeOptions(field.typeOptions)}</td> |
|||
<td> |
|||
<span class="edit-button" on:click={() => editField(field)}>{@html getIcon("edit")}</span> |
|||
<span class="edit-button" on:click={() => deleteField(field)}>{@html getIcon("trash")}</span> |
|||
</td> |
|||
</tr> |
|||
{/each} |
|||
</tbody> |
|||
</table> |
|||
{:else} |
|||
(no fields added) |
|||
<form class="uk-form-horizontal"> |
|||
<h3 class="settings-title">Settings</h3> |
|||
|
|||
<Textbox label="Name:" bind:text={record.name} on:change={nameChanged} /> |
|||
{#if !record.isSingle} |
|||
<Textbox label="Collection Name:" bind:text={record.collectionName} /> |
|||
<Textbox label="Shard Factor:" bind:text={record.allidsShardFactor} /> |
|||
{/if} |
|||
<div class="recordkey">{record.nodeKey()}</div> |
|||
|
|||
</form> |
|||
<h3 class="title"> |
|||
Fields |
|||
<span class="add-field-button" on:click={newField}> |
|||
{@html getIcon('plus')} |
|||
</span> |
|||
</h3> |
|||
|
|||
{#if editingField} |
|||
<Modal bind:isOpen={editingField} onClosed={() => onFinishedFieldEdit(false) }> |
|||
<FieldView field={fieldToEdit} |
|||
onFinished={onFinishedFieldEdit} |
|||
allFields={record.fields} |
|||
store={$store}/> |
|||
{#if record.fields.length > 0} |
|||
<table class="fields-table uk-table"> |
|||
<thead> |
|||
<tr> |
|||
<th>Name</th> |
|||
<th>Type</th> |
|||
<th>Options</th> |
|||
<th /> |
|||
</tr> |
|||
</thead> |
|||
<tbody> |
|||
{#each record.fields as field} |
|||
<tr> |
|||
<td> |
|||
<div class="field-label">{field.label}</div> |
|||
<div style="font-size: 0.8em; color: var(--slate)"> |
|||
{field.name} |
|||
</div> |
|||
</td> |
|||
<td>{field.type}</td> |
|||
<td> |
|||
{@html getTypeOptions(field.typeOptions)} |
|||
</td> |
|||
<td> |
|||
<span class="edit-button" on:click={() => editField(field)}> |
|||
{@html getIcon('edit')} |
|||
</span> |
|||
<span class="edit-button" on:click={() => deleteField(field)}> |
|||
{@html getIcon('trash')} |
|||
</span> |
|||
</td> |
|||
</tr> |
|||
{/each} |
|||
</tbody> |
|||
</table> |
|||
{:else}(no fields added){/if} |
|||
|
|||
{#if editingField} |
|||
<Modal |
|||
bind:isOpen={editingField} |
|||
onClosed={() => onFinishedFieldEdit(false)}> |
|||
<FieldView |
|||
field={fieldToEdit} |
|||
onFinished={onFinishedFieldEdit} |
|||
allFields={record.fields} |
|||
store={$store} /> |
|||
</Modal> |
|||
{/if} |
|||
{/if} |
|||
|
|||
<h3 class="title"> |
|||
Indexes |
|||
</h3> |
|||
<h3 class="title">Indexes</h3> |
|||
|
|||
{#each record.indexes as index} |
|||
{#each record.indexes as index} |
|||
<div class="index-container"> |
|||
<div class="index-name"> |
|||
{index.name} |
|||
<span style="margin-left: 7px" on:click={() => editIndex(index)}>{@html getIcon("edit")}</span> |
|||
</div> |
|||
<div class="index-field-row"> |
|||
<span class="index-label">records indexed: </span> |
|||
<span>{getIndexAllowedRecords(index)}</span> |
|||
<span class="index-label" style="margin-left: 15px">type:</span> |
|||
<span>{index.indexType}</span> |
|||
</div> |
|||
<div class="index-name"> |
|||
{index.name} |
|||
<span style="margin-left: 7px" on:click={() => editIndex(index)}> |
|||
{@html getIcon('edit')} |
|||
</span> |
|||
</div> |
|||
<div class="index-field-row"> |
|||
<span class="index-label">records indexed:</span> |
|||
<span>{getIndexAllowedRecords(index)}</span> |
|||
<span class="index-label" style="margin-left: 15px">type:</span> |
|||
<span>{index.indexType}</span> |
|||
</div> |
|||
<div class="index-field-row"> |
|||
<span class="index-label">map:</span> |
|||
<code class="index-mapfilter">{index.map}</code> |
|||
</div> |
|||
{#if index.filter} |
|||
<div class="index-field-row"> |
|||
<span class="index-label">map:</span> |
|||
<code class="index-mapfilter">{index.map}</code> |
|||
<span class="index-label">filter:</span> |
|||
<code class="index-mapfilter">{index.filter}</code> |
|||
</div> |
|||
{#if index.filter} |
|||
<div class="index-field-row"> |
|||
<span class="index-label">filter:</span> |
|||
<code class="index-mapfilter">{index.filter}</code> |
|||
</div> |
|||
{/if} |
|||
</div> |
|||
{:else} |
|||
<div class="no-indexes"> |
|||
No indexes added. |
|||
{/if} |
|||
</div> |
|||
{/each} |
|||
{:else} |
|||
<div class="no-indexes">No indexes added.</div> |
|||
{/each} |
|||
|
|||
</div> |
|||
|
|||
|
|||
<style> |
|||
|
|||
.root { |
|||
.root { |
|||
height: 100%; |
|||
padding: 2rem; |
|||
} |
|||
} |
|||
|
|||
.settings-title { |
|||
.settings-title { |
|||
font-weight: 700; |
|||
} |
|||
} |
|||
|
|||
.title { |
|||
.title { |
|||
margin: 3rem 0rem 0rem 0rem; |
|||
font-weight: 700; |
|||
} |
|||
} |
|||
|
|||
.recordkey { |
|||
.recordkey { |
|||
font-size: 14px; |
|||
font-weight: 600; |
|||
color: var(--primary100); |
|||
} |
|||
} |
|||
|
|||
.fields-table { |
|||
.fields-table { |
|||
margin: 1rem 1rem 0rem 0rem; |
|||
border-collapse:collapse; |
|||
} |
|||
border-collapse: collapse; |
|||
} |
|||
|
|||
.add-field-button { |
|||
cursor:pointer; |
|||
} |
|||
.add-field-button { |
|||
cursor: pointer; |
|||
} |
|||
|
|||
.edit-button { |
|||
cursor:pointer; |
|||
.edit-button { |
|||
cursor: pointer; |
|||
color: var(--secondary25); |
|||
} |
|||
} |
|||
|
|||
.edit-button:hover { |
|||
cursor:pointer; |
|||
.edit-button:hover { |
|||
cursor: pointer; |
|||
color: var(--secondary75); |
|||
} |
|||
} |
|||
|
|||
th { |
|||
text-align: left; |
|||
} |
|||
th { |
|||
text-align: left; |
|||
} |
|||
|
|||
td { |
|||
td { |
|||
padding: 1rem 5rem 1rem 0rem; |
|||
margin:0; |
|||
margin: 0; |
|||
font-size: 14px; |
|||
font-weight: 500; |
|||
} |
|||
|
|||
} |
|||
|
|||
.field-label { |
|||
font-size: 14px; |
|||
font-weight: 500; |
|||
} |
|||
.field-label { |
|||
font-size: 14px; |
|||
font-weight: 500; |
|||
} |
|||
|
|||
thead > tr { |
|||
thead > tr { |
|||
border-width: 0px 0px 1px 0px; |
|||
border-style: solid; |
|||
border-color: var(--secondary75); |
|||
margin-bottom: 20px; |
|||
} |
|||
} |
|||
|
|||
tbody > tr { |
|||
tbody > tr { |
|||
border-width: 0px 0px 1px 0px; |
|||
border-style: solid; |
|||
border-color: var(--primary10); |
|||
} |
|||
} |
|||
|
|||
tbody > tr:hover { |
|||
tbody > tr:hover { |
|||
background-color: var(--primary10); |
|||
} |
|||
} |
|||
|
|||
tbody > tr:hover .edit-button { |
|||
tbody > tr:hover .edit-button { |
|||
color: var(--secondary75); |
|||
} |
|||
} |
|||
|
|||
.index-container { |
|||
.index-container { |
|||
border-style: solid; |
|||
border-width: 0 0 1px 0; |
|||
border-color: var(--secondary25); |
|||
padding: 10px; |
|||
margin-bottom: 5px; |
|||
} |
|||
} |
|||
|
|||
.index-label { |
|||
.index-label { |
|||
color: var(--slate); |
|||
} |
|||
} |
|||
|
|||
.index-name { |
|||
.index-name { |
|||
font-weight: bold; |
|||
color: var(--primary100); |
|||
} |
|||
} |
|||
|
|||
.index-container code { |
|||
.index-container code { |
|||
margin: 0; |
|||
display: inline; |
|||
background-color: var(--primary10); |
|||
color: var(--secondary100); |
|||
padding:3px; |
|||
} |
|||
padding: 3px; |
|||
} |
|||
|
|||
.index-field-row { |
|||
.index-field-row { |
|||
margin: 1rem 0rem 0rem 0rem; |
|||
} |
|||
} |
|||
|
|||
.no-indexes { |
|||
.no-indexes { |
|||
margin: 1rem 0rem 0rem 0rem; |
|||
font-family: var(--fontnormal); |
|||
font-size: 14px; |
|||
} |
|||
</style> |
|||
} |
|||
</style> |
|||
|
|||
@ -1,163 +1,153 @@ |
|||
<script> |
|||
|
|||
import { store } from "../builderStore"; |
|||
import HierarchyRow from "./HierarchyRow.svelte"; |
|||
import DropdownButton from "../common/DropdownButton.svelte"; |
|||
import {hierarchy as hierarchyFunctions} from "../../../core/src"; |
|||
import NavItem from "./NavItem.svelte"; |
|||
import getIcon from "../common/icon"; |
|||
|
|||
|
|||
const newRootRecord = () => { |
|||
store.newRootRecord(); |
|||
} |
|||
|
|||
const newRootIndex = () => { |
|||
store.newRootIndex(); |
|||
} |
|||
|
|||
const newChildRecord = () => { |
|||
store.newChildRecord(); |
|||
} |
|||
|
|||
const newChildIndex = () => { |
|||
store.newChildIndex(); |
|||
} |
|||
|
|||
const defaultNewChildActions = [ |
|||
import { store } from "../builderStore" |
|||
import HierarchyRow from "./HierarchyRow.svelte" |
|||
import DropdownButton from "../common/DropdownButton.svelte" |
|||
import { hierarchy as hierarchyFunctions } from "../../../core/src" |
|||
import NavItem from "./NavItem.svelte" |
|||
import getIcon from "../common/icon" |
|||
|
|||
const newRootRecord = () => { |
|||
store.newRootRecord() |
|||
} |
|||
|
|||
const newRootIndex = () => { |
|||
store.newRootIndex() |
|||
} |
|||
|
|||
const newChildRecord = () => { |
|||
store.newChildRecord() |
|||
} |
|||
|
|||
const newChildIndex = () => { |
|||
store.newChildIndex() |
|||
} |
|||
|
|||
const defaultNewChildActions = [ |
|||
{ |
|||
label:"New Root Record", |
|||
onclick: newRootRecord |
|||
label: "New Root Record", |
|||
onclick: newRootRecord, |
|||
}, |
|||
{ |
|||
label:"New Root Index", |
|||
onclick: newRootIndex |
|||
} |
|||
]; |
|||
label: "New Root Index", |
|||
onclick: newRootIndex, |
|||
}, |
|||
] |
|||
|
|||
let newChildActions = defaultNewChildActions; |
|||
let newChildActions = defaultNewChildActions |
|||
|
|||
const setActiveNav = (name) => () => { |
|||
store.setActiveNav(name); |
|||
} |
|||
const setActiveNav = name => () => { |
|||
store.setActiveNav(name) |
|||
} |
|||
|
|||
store.subscribe(db => { |
|||
if(!db.currentNode || hierarchyFunctions.isIndex(db.currentNode)) { |
|||
newChildActions = defaultNewChildActions; |
|||
store.subscribe(db => { |
|||
if (!db.currentNode || hierarchyFunctions.isIndex(db.currentNode)) { |
|||
newChildActions = defaultNewChildActions |
|||
} else { |
|||
newChildActions = [ |
|||
{ |
|||
label:"New Root Record", |
|||
onclick: newRootRecord |
|||
}, |
|||
{ |
|||
label:"New Root Index", |
|||
onclick: newRootIndex |
|||
}, |
|||
{ |
|||
label: `New Child Record of ${db.currentNode.name}`, |
|||
onclick: newChildRecord |
|||
}, |
|||
{ |
|||
label: `New Index on ${db.currentNode.name}`, |
|||
onclick: newChildIndex |
|||
} |
|||
]; |
|||
newChildActions = [ |
|||
{ |
|||
label: "New Root Record", |
|||
onclick: newRootRecord, |
|||
}, |
|||
{ |
|||
label: "New Root Index", |
|||
onclick: newRootIndex, |
|||
}, |
|||
{ |
|||
label: `New Child Record of ${db.currentNode.name}`, |
|||
onclick: newChildRecord, |
|||
}, |
|||
{ |
|||
label: `New Index on ${db.currentNode.name}`, |
|||
onclick: newChildIndex, |
|||
}, |
|||
] |
|||
} |
|||
}); |
|||
|
|||
}) |
|||
</script> |
|||
|
|||
|
|||
<div class="items-root"> |
|||
<div class="hierarchy"> |
|||
<div class="components-list-container"> |
|||
<div class="nav-group-header"> |
|||
<div>{@html getIcon("database","18")}</div> |
|||
<div class="hierarchy-title">Database</div> |
|||
<DropdownButton iconName="plus" actions={newChildActions} /> |
|||
</div> |
|||
<div class="hierarchy"> |
|||
<div class="components-list-container"> |
|||
<div class="nav-group-header"> |
|||
<div> |
|||
{@html getIcon('database', '18')} |
|||
</div> |
|||
<div class="hierarchy-title">Database</div> |
|||
<DropdownButton iconName="plus" actions={newChildActions} /> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="hierarchy-items-container"> |
|||
{#each $store.hierarchy.children as record} |
|||
<HierarchyRow node={record} |
|||
type="record" /> |
|||
{/each} |
|||
<div class="hierarchy-items-container"> |
|||
{#each $store.hierarchy.children as record} |
|||
<HierarchyRow node={record} type="record" /> |
|||
{/each} |
|||
|
|||
{#each $store.hierarchy.indexes as index} |
|||
<HierarchyRow node={index} |
|||
type="index" /> |
|||
{/each} |
|||
</div> |
|||
{#each $store.hierarchy.indexes as index} |
|||
<HierarchyRow node={index} type="index" /> |
|||
{/each} |
|||
</div> |
|||
</div> |
|||
|
|||
<NavItem name="actions" label="Actions & Triggers"/> |
|||
<NavItem name="access levels" label="User Levels"/> |
|||
<NavItem name="actions" label="Actions & Triggers" /> |
|||
<NavItem name="access levels" label="User Levels" /> |
|||
|
|||
</div> |
|||
|
|||
|
|||
<style> |
|||
|
|||
|
|||
.items-root { |
|||
.items-root { |
|||
display: flex; |
|||
flex-direction: column; |
|||
max-height: 100%; |
|||
height: 100%; |
|||
background-color: var(--secondary5); |
|||
} |
|||
} |
|||
|
|||
.nav-group-header { |
|||
display:grid; |
|||
.nav-group-header { |
|||
display: grid; |
|||
grid-template-columns: [icon] auto [title] 1fr [button] auto; |
|||
padding: 2rem 1rem 0rem 1rem; |
|||
font-size: .9rem; |
|||
font-size: 0.9rem; |
|||
font-weight: bold; |
|||
} |
|||
} |
|||
|
|||
.nav-group-header>div:nth-child(1) { |
|||
padding: 0rem .7rem 0rem 0rem; |
|||
.nav-group-header > div:nth-child(1) { |
|||
padding: 0rem 0.7rem 0rem 0rem; |
|||
vertical-align: bottom; |
|||
grid-column-start: icon; |
|||
margin-right: 5px; |
|||
} |
|||
} |
|||
|
|||
.nav-group-header>div:nth-child(2) { |
|||
margin-left:5px; |
|||
.nav-group-header > div:nth-child(2) { |
|||
margin-left: 5px; |
|||
vertical-align: bottom; |
|||
grid-column-start: title; |
|||
margin-top:auto; |
|||
} |
|||
margin-top: auto; |
|||
} |
|||
|
|||
.nav-group-header>div:nth-child(3) { |
|||
.nav-group-header > div:nth-child(3) { |
|||
vertical-align: bottom; |
|||
grid-column-start: button; |
|||
cursor: pointer; |
|||
color: var(--primary75); |
|||
} |
|||
} |
|||
|
|||
.nav-group-header>div:nth-child(3):hover { |
|||
color: var(--primary75); |
|||
} |
|||
.nav-group-header > div:nth-child(3):hover { |
|||
color: var(--primary75); |
|||
} |
|||
|
|||
.hierarchy-title { |
|||
.hierarchy-title { |
|||
flex: auto 1 1; |
|||
} |
|||
|
|||
} |
|||
|
|||
.hierarchy { |
|||
display:flex; |
|||
.hierarchy { |
|||
display: flex; |
|||
flex-direction: column; |
|||
flex: 1 0 auto; |
|||
height: 100px; |
|||
} |
|||
} |
|||
|
|||
.hierarchy-items-container { |
|||
.hierarchy-items-container { |
|||
flex: 1 1 auto; |
|||
overflow-y:auto; |
|||
} |
|||
|
|||
|
|||
</style> |
|||
overflow-y: auto; |
|||
} |
|||
</style> |
|||
|
|||
@ -1,66 +1,63 @@ |
|||
<script> |
|||
|
|||
import {store} from "../builderStore"; |
|||
import {cloneDeep} from "lodash/fp"; |
|||
import getIcon from "../common/icon"; |
|||
export let level = 0; |
|||
export let node; |
|||
export let type; |
|||
|
|||
let navActive = ""; |
|||
$:icon = type==="index" ? "list" : "file"; |
|||
|
|||
store.subscribe(s => { |
|||
if(s.currentNode) |
|||
navActive = (s.activeNav === "database" && node.nodeId === s.currentNode.nodeId |
|||
? "active" : "") |
|||
}); |
|||
|
|||
import { store } from "../builderStore" |
|||
import { cloneDeep } from "lodash/fp" |
|||
import getIcon from "../common/icon" |
|||
export let level = 0 |
|||
export let node |
|||
export let type |
|||
|
|||
let navActive = "" |
|||
$: icon = type === "index" ? "list" : "file" |
|||
|
|||
store.subscribe(s => { |
|||
if (s.currentNode) |
|||
navActive = |
|||
s.activeNav === "database" && node.nodeId === s.currentNode.nodeId |
|||
? "active" |
|||
: "" |
|||
}) |
|||
</script> |
|||
|
|||
<div class="root"> |
|||
<div class="title {navActive}" on:click={() => store.selectExistingNode(node.nodeId)} style="padding-left: {20 + (level * 20)}px"> |
|||
{@html getIcon(icon, 12)} <span style="margin-left: 1rem">{node.name}</span> |
|||
</div> |
|||
{#if node.children} |
|||
{#each node.children as child} |
|||
<svelte:self node={child} |
|||
level={level+1} |
|||
type="record"/> |
|||
{/each} |
|||
{/if} |
|||
{#if node.indexes} |
|||
{#each node.indexes as index} |
|||
<svelte:self node={index} |
|||
level={level+1} |
|||
type="index"/> |
|||
{/each} |
|||
{/if} |
|||
<div |
|||
class="title {navActive}" |
|||
on:click={() => store.selectExistingNode(node.nodeId)} |
|||
style="padding-left: {20 + level * 20}px"> |
|||
{@html getIcon(icon, 12)} |
|||
<span style="margin-left: 1rem">{node.name}</span> |
|||
</div> |
|||
{#if node.children} |
|||
{#each node.children as child} |
|||
<svelte:self node={child} level={level + 1} type="record" /> |
|||
{/each} |
|||
{/if} |
|||
{#if node.indexes} |
|||
{#each node.indexes as index} |
|||
<svelte:self node={index} level={level + 1} type="index" /> |
|||
{/each} |
|||
{/if} |
|||
</div> |
|||
|
|||
|
|||
<style> |
|||
|
|||
.root { |
|||
.root { |
|||
display: block; |
|||
font-size: .9rem; |
|||
font-size: 0.9rem; |
|||
width: 100%; |
|||
cursor: pointer; |
|||
color: var(--secondary50); |
|||
font-weight: 500; |
|||
} |
|||
} |
|||
|
|||
.title { |
|||
padding-top: .5rem; |
|||
padding-right: .5rem; |
|||
} |
|||
.title { |
|||
padding-top: 0.5rem; |
|||
padding-right: 0.5rem; |
|||
} |
|||
|
|||
.title:hover { |
|||
.title:hover { |
|||
background-color: var(--secondary10); |
|||
} |
|||
} |
|||
|
|||
.active { |
|||
.active { |
|||
background-color: var(--primary10); |
|||
} |
|||
|
|||
</style> |
|||
} |
|||
</style> |
|||
|
|||
@ -1,43 +1,35 @@ |
|||
<script> |
|||
import { store } from "../builderStore" |
|||
import getIcon from "../common/icon" |
|||
|
|||
import {store} from "../builderStore"; |
|||
import getIcon from "../common/icon"; |
|||
export let name = "" |
|||
export let label = "" |
|||
|
|||
export let name = ""; |
|||
export let label = ""; |
|||
let navActive = "" |
|||
|
|||
let navActive = ""; |
|||
|
|||
store.subscribe(db => { |
|||
navActive = (db.activeNav === name ? "active" : "") |
|||
}); |
|||
|
|||
const setActive = () => |
|||
store.setActiveNav(name); |
|||
store.subscribe(db => { |
|||
navActive = db.activeNav === name ? "active" : "" |
|||
}) |
|||
|
|||
const setActive = () => store.setActiveNav(name) |
|||
</script> |
|||
|
|||
<div class="nav-item {navActive}" on:click={setActive}> |
|||
{label} |
|||
</div> |
|||
|
|||
<div class="nav-item {navActive}" on:click={setActive}>{label}</div> |
|||
|
|||
<style> |
|||
|
|||
.nav-item { |
|||
.nav-item { |
|||
padding: 1.5rem 1rem 0rem 1rem; |
|||
font-size: .9rem; |
|||
font-size: 0.9rem; |
|||
font-weight: bold; |
|||
cursor: pointer; |
|||
flex: 0 0 auto; |
|||
} |
|||
} |
|||
|
|||
.nav-item:hover { |
|||
.nav-item:hover { |
|||
background-color: var(--primary10); |
|||
} |
|||
} |
|||
|
|||
.active { |
|||
.active { |
|||
background-color: var(--primary10); |
|||
} |
|||
|
|||
</style> |
|||
} |
|||
</style> |
|||
|
|||
@ -1,64 +1,59 @@ |
|||
<script> |
|||
import { searchAllComponents } from "./pagesParsing/searchComponents" |
|||
import { store } from "../builderStore" |
|||
|
|||
import { searchAllComponents } from "./pagesParsing/searchComponents"; |
|||
import { store } from "../builderStore"; |
|||
export let onComponentChosen = () => {} |
|||
|
|||
export let onComponentChosen = () => {}; |
|||
let phrase = "" |
|||
|
|||
let phrase = ""; |
|||
components = $store.components |
|||
|
|||
components = $store.components; |
|||
|
|||
$: filteredComponents = |
|||
!phrase |
|||
? [] |
|||
: searchAllComponents(components, phrase); |
|||
$: filteredComponents = !phrase ? [] : searchAllComponents(components, phrase) |
|||
</script> |
|||
|
|||
<div class="root"> |
|||
|
|||
<form class="uk-search uk-search-large"> |
|||
<span uk-search-icon></span> |
|||
<input class="uk-search-input" |
|||
type="search" |
|||
placeholder="Based on component..." |
|||
bind:value={phrase}> |
|||
</form> |
|||
|
|||
<div> |
|||
{#each filteredComponents as component} |
|||
<div class="component" on:click={() => onComponentChosen(component)}> |
|||
<div class="title">{component.name}</div> |
|||
<div class="description">{component.description}</div> |
|||
</div> |
|||
{/each} |
|||
</div> |
|||
<form class="uk-search uk-search-large"> |
|||
<span uk-search-icon /> |
|||
<input |
|||
class="uk-search-input" |
|||
type="search" |
|||
placeholder="Based on component..." |
|||
bind:value={phrase} /> |
|||
</form> |
|||
|
|||
<div> |
|||
{#each filteredComponents as component} |
|||
<div class="component" on:click={() => onComponentChosen(component)}> |
|||
<div class="title">{component.name}</div> |
|||
<div class="description">{component.description}</div> |
|||
</div> |
|||
{/each} |
|||
</div> |
|||
|
|||
</div> |
|||
|
|||
<style> |
|||
|
|||
.component { |
|||
padding:5px; |
|||
.component { |
|||
padding: 5px; |
|||
border-style: solid; |
|||
border-width: 0 0 1px 0; |
|||
border-color: var(--lightslate); |
|||
cursor: pointer; |
|||
} |
|||
} |
|||
|
|||
.component:hover { |
|||
.component:hover { |
|||
background-color: var(--primary10); |
|||
} |
|||
} |
|||
|
|||
.component > .title { |
|||
.component > .title { |
|||
font-size: 13pt; |
|||
color: var(--secondary100); |
|||
} |
|||
} |
|||
|
|||
.component > .description { |
|||
.component > .description { |
|||
font-size: 10pt; |
|||
color: var(--primary75); |
|||
font-style: italic; |
|||
} |
|||
|
|||
} |
|||
</style> |
|||
|
|||
@ -1,184 +1,138 @@ |
|||
<script> |
|||
import { |
|||
isRootComponent |
|||
} from "./pagesParsing/searchComponents" |
|||
import { splitName } from "./pagesParsing/splitRootComponentName.js" |
|||
import { store } from "../builderStore"; |
|||
import { find, sortBy } from "lodash/fp"; |
|||
|
|||
export let onComponentChosen; |
|||
export let onGeneratorChosen; |
|||
export let allowGenerators; |
|||
|
|||
let screens = []; |
|||
let componentLibraries=[]; |
|||
|
|||
const addRootComponent = (c, all, isGenerator) => { |
|||
const { libName } = splitName(c.name); |
|||
let group = find(r => r.libName === libName)(all); |
|||
|
|||
if(!group) { |
|||
group = { |
|||
libName, |
|||
components: [], |
|||
generators: [] |
|||
}; |
|||
|
|||
all.push(group); |
|||
} |
|||
|
|||
if(isGenerator) { |
|||
group.generators.push(c) |
|||
} else { |
|||
group.components.push(c) |
|||
} |
|||
|
|||
}; |
|||
|
|||
$: { |
|||
|
|||
const newComponentLibraries = []; |
|||
const newscreens = []; |
|||
|
|||
for(let comp of sortBy(["name"])($store.components)) { |
|||
if(isRootComponent(comp)) { |
|||
addRootComponent( |
|||
comp, |
|||
newComponentLibraries, |
|||
false); |
|||
} else { |
|||
newscreens.push(comp); |
|||
} |
|||
} |
|||
|
|||
for(let generator of $store.generators) { |
|||
addRootComponent( |
|||
generator, |
|||
newComponentLibraries, |
|||
true); |
|||
} |
|||
|
|||
screens = sortBy(["name"])(newscreens); |
|||
componentLibraries = newComponentLibraries; |
|||
}; |
|||
import { isRootComponent } from "./pagesParsing/searchComponents" |
|||
import { splitName } from "./pagesParsing/splitRootComponentName.js" |
|||
import { store } from "../builderStore" |
|||
import { find, sortBy } from "lodash/fp" |
|||
|
|||
export let onComponentChosen |
|||
export let onGeneratorChosen |
|||
export let allowGenerators |
|||
|
|||
let screens = [] |
|||
let componentLibraries = [] |
|||
|
|||
const addRootComponent = (c, all, isGenerator) => { |
|||
const { libName } = splitName(c.name) |
|||
let group = find(r => r.libName === libName)(all) |
|||
|
|||
if (!group) { |
|||
group = { |
|||
libName, |
|||
components: [], |
|||
generators: [], |
|||
} |
|||
|
|||
all.push(group) |
|||
} |
|||
|
|||
if (isGenerator) { |
|||
group.generators.push(c) |
|||
} else { |
|||
group.components.push(c) |
|||
} |
|||
} |
|||
|
|||
$: { |
|||
const newComponentLibraries = [] |
|||
const newscreens = [] |
|||
|
|||
for (let comp of sortBy(["name"])($store.components)) { |
|||
if (isRootComponent(comp)) { |
|||
addRootComponent(comp, newComponentLibraries, false) |
|||
} else { |
|||
newscreens.push(comp) |
|||
} |
|||
} |
|||
|
|||
for (let generator of $store.generators) { |
|||
addRootComponent(generator, newComponentLibraries, true) |
|||
} |
|||
|
|||
screens = sortBy(["name"])(newscreens) |
|||
componentLibraries = newComponentLibraries |
|||
} |
|||
</script> |
|||
|
|||
{#each componentLibraries as lib} |
|||
<div class="library-header"> |
|||
{lib.libName} |
|||
</div> |
|||
<div class="library-header">{lib.libName}</div> |
|||
|
|||
<div class="library-container"> |
|||
<div class="library-container"> |
|||
|
|||
{#if allowGenerators} |
|||
<div class="inner-header"> |
|||
Generators |
|||
</div> |
|||
|
|||
{#each lib.generators as generator} |
|||
<div class="inner-header">Generators</div> |
|||
|
|||
<div class="component" |
|||
on:click={() => onGeneratorChosen(generator)}> |
|||
<div class="name"> |
|||
{splitName(generator.name).componentName} |
|||
{#each lib.generators as generator} |
|||
<div class="component" on:click={() => onGeneratorChosen(generator)}> |
|||
<div class="name">{splitName(generator.name).componentName}</div> |
|||
<div class="description">{generator.description}</div> |
|||
</div> |
|||
<div class="description"> |
|||
{generator.description} |
|||
</div> |
|||
</div> |
|||
|
|||
{/each} |
|||
{/each} |
|||
{/if} |
|||
|
|||
<div class="inner-header"> |
|||
Components |
|||
</div> |
|||
<div class="inner-header">Components</div> |
|||
|
|||
{#each lib.components as component} |
|||
|
|||
<div class="component" |
|||
on:click={() => onComponentChosen(component)}> |
|||
<div class="name"> |
|||
{splitName(component.name).componentName} |
|||
</div> |
|||
<div class="description"> |
|||
{component.description} |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="component" on:click={() => onComponentChosen(component)}> |
|||
<div class="name">{splitName(component.name).componentName}</div> |
|||
<div class="description">{component.description}</div> |
|||
</div> |
|||
{/each} |
|||
|
|||
</div> |
|||
|
|||
</div> |
|||
{/each} |
|||
|
|||
|
|||
<div class="library-header"> |
|||
My Components |
|||
</div> |
|||
<div class="library-header">My Components</div> |
|||
|
|||
<div class="library-container"> |
|||
|
|||
{#each screens as component} |
|||
|
|||
<div class="component" |
|||
on:click={() => onComponentChosen(component)}> |
|||
<div class="name"> |
|||
{component.name} |
|||
</div> |
|||
<div class="description"> |
|||
{component.description} |
|||
</div> |
|||
{#each screens as component} |
|||
<div class="component" on:click={() => onComponentChosen(component)}> |
|||
<div class="name">{component.name}</div> |
|||
<div class="description">{component.description}</div> |
|||
</div> |
|||
|
|||
{/each} |
|||
{/each} |
|||
|
|||
</div> |
|||
|
|||
|
|||
<style> |
|||
|
|||
.library-header { |
|||
.library-header { |
|||
font-size: 1.1em; |
|||
border-color: var(--primary25); |
|||
border-width: 1px 0px; |
|||
border-style: solid; |
|||
background-color: var(--primary10); |
|||
padding: 5px 0; |
|||
} |
|||
} |
|||
|
|||
.library-container { |
|||
.library-container { |
|||
padding: 0 0 10px 10px; |
|||
} |
|||
} |
|||
|
|||
.inner-header { |
|||
.inner-header { |
|||
font-size: 0.9em; |
|||
font-weight: bold; |
|||
margin-top: 7px; |
|||
margin-bottom: 3px; |
|||
} |
|||
} |
|||
|
|||
.component { |
|||
.component { |
|||
padding: 2px 0px; |
|||
cursor: pointer; |
|||
} |
|||
} |
|||
|
|||
.component:hover { |
|||
.component:hover { |
|||
background-color: var(--lightslate); |
|||
} |
|||
} |
|||
|
|||
.component > .name { |
|||
.component > .name { |
|||
color: var(--secondary100); |
|||
display: inline-block; |
|||
} |
|||
} |
|||
|
|||
.component > .description { |
|||
.component > .description { |
|||
font-size: 0.8em; |
|||
color: var(--secondary75); |
|||
display: inline-block; |
|||
margin-left: 10px; |
|||
} |
|||
|
|||
|
|||
|
|||
} |
|||
</style> |
|||
|
|||
@ -1,120 +1,114 @@ |
|||
<script> |
|||
import ComponentsHierarchyChildren from './ComponentsHierarchyChildren.svelte'; |
|||
|
|||
import { |
|||
last, |
|||
sortBy, |
|||
map, |
|||
trimCharsStart, |
|||
trimChars, |
|||
join, |
|||
} from "lodash/fp"; |
|||
|
|||
import { pipe } from "../common/core"; |
|||
import { store } from "../builderStore"; |
|||
import { ArrowDownIcon } from '../common/Icons/' |
|||
|
|||
export let components = [] |
|||
|
|||
const joinPath = join("/"); |
|||
|
|||
const normalizedName = name => pipe(name, [ |
|||
trimCharsStart("./"), |
|||
trimCharsStart("~/"), |
|||
trimCharsStart("../"), |
|||
trimChars(" ") |
|||
]); |
|||
|
|||
const lastPartOfName = (c) => |
|||
last(c.name ? c.name.split("/") : c._component.split("/")) |
|||
|
|||
const isComponentSelected = (current, comp) => |
|||
current && |
|||
current.component && |
|||
comp.component && |
|||
current.component.name === comp.component.name |
|||
|
|||
const isFolderSelected = (current, folder) => |
|||
isInSubfolder(current, folder) |
|||
|
|||
$: _components = |
|||
pipe(components, [ |
|||
map(c => ({component: c, title:lastPartOfName(c)})), |
|||
sortBy("title") |
|||
]); |
|||
|
|||
function select_component(screen, component) { |
|||
store.setCurrentScreen(screen); |
|||
store.selectComponent(component); |
|||
} |
|||
|
|||
const isScreenSelected = component => |
|||
component.component && |
|||
$store.currentFrontEndItem && |
|||
component.component.name === $store.currentFrontEndItem.name; |
|||
import ComponentsHierarchyChildren from "./ComponentsHierarchyChildren.svelte" |
|||
|
|||
</script> |
|||
import { last, sortBy, map, trimCharsStart, trimChars, join } from "lodash/fp" |
|||
|
|||
<div class="root"> |
|||
import { pipe } from "../common/core" |
|||
import { store } from "../builderStore" |
|||
import { ArrowDownIcon } from "../common/Icons/" |
|||
|
|||
export let components = [] |
|||
|
|||
const joinPath = join("/") |
|||
|
|||
const normalizedName = name => |
|||
pipe(name, [ |
|||
trimCharsStart("./"), |
|||
trimCharsStart("~/"), |
|||
trimCharsStart("../"), |
|||
trimChars(" "), |
|||
]) |
|||
|
|||
const lastPartOfName = c => |
|||
last(c.name ? c.name.split("/") : c._component.split("/")) |
|||
|
|||
const isComponentSelected = (current, comp) => |
|||
current && |
|||
current.component && |
|||
comp.component && |
|||
current.component.name === comp.component.name |
|||
|
|||
{#each _components as component} |
|||
<div class="hierarchy-item component" |
|||
class:selected={isComponentSelected($store.currentComponentInfo, component)} |
|||
on:click|stopPropagation={() => store.setCurrentScreen(component.component.name)}> |
|||
const isFolderSelected = (current, folder) => isInSubfolder(current, folder) |
|||
|
|||
<span class="icon" style="transform: rotate({isScreenSelected(component) ? 0 : -90}deg);"> |
|||
{#if component.component.props && component.component.props._children} |
|||
<ArrowDownIcon /> |
|||
{/if} |
|||
</span> |
|||
$: _components = pipe(components, [ |
|||
map(c => ({ component: c, title: lastPartOfName(c) })), |
|||
sortBy("title"), |
|||
]) |
|||
|
|||
<span class="title">{component.title}</span> |
|||
</div> |
|||
function select_component(screen, component) { |
|||
store.setCurrentScreen(screen) |
|||
store.selectComponent(component) |
|||
} |
|||
|
|||
{#if isScreenSelected(component) && component.component.props && component.component.props._children} |
|||
<ComponentsHierarchyChildren components={component.component.props._children} |
|||
currentComponent={$store.currentComponentInfo} |
|||
onSelect={child => select_component(component.component.name, child)} /> |
|||
const isScreenSelected = component => |
|||
component.component && |
|||
$store.currentFrontEndItem && |
|||
component.component.name === $store.currentFrontEndItem.name |
|||
</script> |
|||
|
|||
<div class="root"> |
|||
|
|||
{#each _components as component} |
|||
<div |
|||
class="hierarchy-item component" |
|||
class:selected={isComponentSelected($store.currentComponentInfo, component)} |
|||
on:click|stopPropagation={() => store.setCurrentScreen(component.component.name)}> |
|||
|
|||
<span |
|||
class="icon" |
|||
style="transform: rotate({isScreenSelected(component) ? 0 : -90}deg);"> |
|||
{#if component.component.props && component.component.props._children} |
|||
<ArrowDownIcon /> |
|||
{/if} |
|||
{/each} |
|||
</span> |
|||
|
|||
<span class="title">{component.title}</span> |
|||
</div> |
|||
|
|||
{#if isScreenSelected(component) && component.component.props && component.component.props._children} |
|||
<ComponentsHierarchyChildren |
|||
components={component.component.props._children} |
|||
currentComponent={$store.currentComponentInfo} |
|||
onSelect={child => select_component(component.component.name, child)} /> |
|||
{/if} |
|||
{/each} |
|||
|
|||
</div> |
|||
|
|||
<style> |
|||
|
|||
.root { |
|||
.root { |
|||
font-weight: 500; |
|||
font-size: 0.9rem; |
|||
color: #828fa5; |
|||
} |
|||
} |
|||
|
|||
.hierarchy-item { |
|||
.hierarchy-item { |
|||
cursor: pointer; |
|||
padding: 11px 7px; |
|||
margin: 5px 0; |
|||
border-radius: 5px; |
|||
display: flex; |
|||
align-items: center; |
|||
} |
|||
} |
|||
|
|||
.hierarchy-item:hover { |
|||
.hierarchy-item:hover { |
|||
/* color: var(--secondary); */ |
|||
background: #fafafa; |
|||
} |
|||
} |
|||
|
|||
.selected { |
|||
.selected { |
|||
color: var(--button-text); |
|||
background: var(--background-button)!important; |
|||
} |
|||
background: var(--background-button) !important; |
|||
} |
|||
|
|||
.title { |
|||
.title { |
|||
margin-left: 10px; |
|||
} |
|||
} |
|||
|
|||
.icon { |
|||
.icon { |
|||
display: inline-block; |
|||
transition: 0.2s; |
|||
width: 24px; |
|||
height: 24px; |
|||
} |
|||
} |
|||
</style> |
|||
|
|||
@ -1,85 +1,71 @@ |
|||
<script> |
|||
import { store } from "../builderStore"; |
|||
import { map, join } from "lodash/fp"; |
|||
import { pipe } from "../common/core"; |
|||
import { buildPropsHierarchy } from "./pagesParsing/buildPropsHierarchy"; |
|||
import { store } from "../builderStore" |
|||
import { map, join } from "lodash/fp" |
|||
import { pipe } from "../common/core" |
|||
import { buildPropsHierarchy } from "./pagesParsing/buildPropsHierarchy" |
|||
|
|||
let iframe; |
|||
let iframe |
|||
|
|||
$: iframe && console.log(iframe.contentDocument.head.insertAdjacentHTML('beforeend', '<style></style>')) |
|||
$: hasComponent = !!$store.currentFrontEndItem; |
|||
$: styles = hasComponent ? $store.currentFrontEndItem._css : ''; |
|||
$: iframe && |
|||
console.log( |
|||
iframe.contentDocument.head.insertAdjacentHTML( |
|||
"beforeend", |
|||
'<style ✂prettier:content✂=""></style>' |
|||
) |
|||
) |
|||
$: hasComponent = !!$store.currentFrontEndItem |
|||
$: styles = hasComponent ? $store.currentFrontEndItem._css : "" |
|||
|
|||
$: stylesheetLinks = pipe($store.pages.stylesheets, [ |
|||
map(s => `<link rel="stylesheet" href="${s}"/>`), |
|||
join("\n") |
|||
]); |
|||
$: stylesheetLinks = pipe($store.pages.stylesheets, [ |
|||
map(s => `<link rel="stylesheet" href="${s}"/>`), |
|||
join("\n"), |
|||
]) |
|||
|
|||
$: appDefinition = { |
|||
componentLibraries: $store.loadLibraryUrls(), |
|||
props: buildPropsHierarchy( |
|||
$store.components, |
|||
$store.screens, |
|||
$store.currentFrontEndItem), |
|||
hierarchy: $store.hierarchy, |
|||
appRootPath: "" |
|||
}; |
|||
|
|||
$: appDefinition = { |
|||
componentLibraries: $store.loadLibraryUrls(), |
|||
props: buildPropsHierarchy( |
|||
$store.components, |
|||
$store.screens, |
|||
$store.currentFrontEndItem |
|||
), |
|||
hierarchy: $store.hierarchy, |
|||
appRootPath: "", |
|||
} |
|||
</script> |
|||
|
|||
|
|||
<div class="component-container"> |
|||
{#if hasComponent} |
|||
<iframe style="height: 100%; width: 100%" |
|||
title="componentPreview" |
|||
bind:this={iframe} |
|||
srcdoc={ |
|||
`<html> |
|||
{#if hasComponent} |
|||
<iframe |
|||
style="height: 100%; width: 100%" |
|||
title="componentPreview" |
|||
bind:this={iframe} |
|||
srcdoc={`<html> |
|||
|
|||
<head> |
|||
${stylesheetLinks} |
|||
<script> |
|||
window["##BUDIBASE_APPDEFINITION##"] = ${JSON.stringify(appDefinition)}; |
|||
window["##BUDIBASE_UIFUNCTIONS"] = ${$store.currentScreenFunctions}; |
|||
|
|||
import('/_builder/budibase-client.esm.mjs') |
|||
.then(module => { |
|||
module.loadBudibase({ window, localStorage }); |
|||
}) |
|||
</script> |
|||
<style> |
|||
|
|||
body { |
|||
box-sizing: border-box; |
|||
padding: 20px; |
|||
} |
|||
${styles} |
|||
</style> |
|||
</head> |
|||
<script ✂prettier:content✂="CiAgICAgICAgd2luZG93WyIjI0JVRElCQVNFX0FQUERFRklOSVRJT04jIyJdID0gJHtKU09OLnN0cmluZ2lmeShhcHBEZWZpbml0aW9uKX07CiAgICAgICAgd2luZG93WyIjI0JVRElCQVNFX1VJRlVOQ1RJT05TIl0gPSAkeyRzdG9yZS5jdXJyZW50U2NyZWVuRnVuY3Rpb25zfTsKICAgICAgICAKICAgICAgICBpbXBvcnQoJy9fYnVpbGRlci9idWRpYmFzZS1jbGllbnQuZXNtLm1qcycpCiAgICAgICAgLnRoZW4obW9kdWxlID0+IHsKICAgICAgICAgICAgbW9kdWxlLmxvYWRCdWRpYmFzZSh7IHdpbmRvdywgbG9jYWxTdG9yYWdlIH0pOwogICAgICAgIH0pCiAgICA=">{}</script> <style ✂prettier:content✂="CgogICAgICAgIGJvZHkgewogICAgICAgICAgICBib3gtc2l6aW5nOiBib3JkZXItYm94OwogICAgICAgICAgICBwYWRkaW5nOiAyMHB4OwogICAgICAgIH0KICAgICR7c3R5bGVzfQogICAg"></style></head> |
|||
<body> |
|||
</body> |
|||
</html>`}> |
|||
</iframe> |
|||
{/if} |
|||
</html>`} /> |
|||
{/if} |
|||
</div> |
|||
|
|||
|
|||
<style> |
|||
.component-container { |
|||
grid-row-start: middle; |
|||
grid-column-start: middle; |
|||
position: relative; |
|||
overflow: hidden; |
|||
padding-top: 56.25%; |
|||
margin: auto; |
|||
} |
|||
.component-container { |
|||
grid-row-start: middle; |
|||
grid-column-start: middle; |
|||
position: relative; |
|||
overflow: hidden; |
|||
padding-top: 56.25%; |
|||
margin: auto; |
|||
} |
|||
|
|||
.component-container iframe { |
|||
border: 0; |
|||
height: 100%; |
|||
left: 0; |
|||
position: absolute; |
|||
top: 0; |
|||
width: 100%; |
|||
} |
|||
.component-container iframe { |
|||
border: 0; |
|||
height: 100%; |
|||
left: 0; |
|||
position: absolute; |
|||
top: 0; |
|||
width: 100%; |
|||
} |
|||
</style> |
|||
|
|||
@ -1,90 +1,91 @@ |
|||
<script> |
|||
import PropsView from "./PropsView.svelte"; |
|||
import { store } from "../builderStore"; |
|||
import IconButton from "../common/IconButton.svelte"; |
|||
import Textbox from "../common/Textbox.svelte"; |
|||
import Button from "../common/Button.svelte"; |
|||
import { LayoutIcon, PaintIcon, TerminalIcon } from '../common/Icons/'; |
|||
import PropsView from "./PropsView.svelte" |
|||
import { store } from "../builderStore" |
|||
import IconButton from "../common/IconButton.svelte" |
|||
import Textbox from "../common/Textbox.svelte" |
|||
import Button from "../common/Button.svelte" |
|||
import { LayoutIcon, PaintIcon, TerminalIcon } from "../common/Icons/" |
|||
|
|||
import { |
|||
cloneDeep, |
|||
join, |
|||
split, |
|||
last |
|||
} from "lodash/fp"; |
|||
import { assign } from "lodash"; |
|||
import { cloneDeep, join, split, last } from "lodash/fp" |
|||
import { assign } from "lodash" |
|||
|
|||
$: component = $store.currentFrontEndItem; |
|||
$: componentInfo = $store.currentComponentInfo; |
|||
$: components = $store.components; |
|||
$: component = $store.currentFrontEndItem |
|||
$: componentInfo = $store.currentComponentInfo |
|||
$: components = $store.components |
|||
|
|||
const updateComponent = doChange => doChange(cloneDeep(component)) |
|||
|
|||
const updateComponent = doChange => |
|||
doChange(cloneDeep(component)); |
|||
|
|||
const onPropsChanged = newProps => { |
|||
updateComponent(newComponent => |
|||
assign(newComponent.props, newProps)); |
|||
} |
|||
const onPropsChanged = newProps => { |
|||
updateComponent(newComponent => assign(newComponent.props, newProps)) |
|||
} |
|||
</script> |
|||
|
|||
<div class="root"> |
|||
|
|||
<ul> |
|||
<li><button><PaintIcon /></button></li> |
|||
<li><button><LayoutIcon /></button></li> |
|||
<li><button><TerminalIcon /></button></li> |
|||
</ul> |
|||
|
|||
<div class="component-props-container"> |
|||
<PropsView |
|||
{componentInfo} |
|||
{onPropsChanged} /> |
|||
</div> |
|||
<ul> |
|||
<li> |
|||
<button> |
|||
<PaintIcon /> |
|||
</button> |
|||
</li> |
|||
<li> |
|||
<button> |
|||
<LayoutIcon /> |
|||
</button> |
|||
</li> |
|||
<li> |
|||
<button> |
|||
<TerminalIcon /> |
|||
</button> |
|||
</li> |
|||
</ul> |
|||
|
|||
<div class="component-props-container"> |
|||
<PropsView {componentInfo} {onPropsChanged} /> |
|||
</div> |
|||
</div> |
|||
|
|||
<style> |
|||
.root { |
|||
height: 100%; |
|||
display: flex; |
|||
flex-direction: column; |
|||
|
|||
} |
|||
|
|||
.title > div:nth-child(1) { |
|||
grid-column-start: name; |
|||
color: var(--secondary100); |
|||
} |
|||
|
|||
.title > div:nth-child(2) { |
|||
grid-column-start: actions; |
|||
} |
|||
|
|||
.component-props-container { |
|||
flex: 1 1 auto; |
|||
overflow-y: auto; |
|||
} |
|||
|
|||
ul { |
|||
list-style: none; |
|||
display: flex; |
|||
padding: 0; |
|||
} |
|||
|
|||
li { |
|||
margin-right: 20px; |
|||
background: none; |
|||
border-radius: 5px; |
|||
width: 45px; |
|||
height: 45px; |
|||
} |
|||
|
|||
li button { |
|||
width: 100%; |
|||
height: 100%; |
|||
background: none; |
|||
border: none; |
|||
border-radius: 5px; |
|||
padding: 12px; |
|||
} |
|||
.root { |
|||
height: 100%; |
|||
display: flex; |
|||
flex-direction: column; |
|||
} |
|||
|
|||
.title > div:nth-child(1) { |
|||
grid-column-start: name; |
|||
color: var(--secondary100); |
|||
} |
|||
|
|||
.title > div:nth-child(2) { |
|||
grid-column-start: actions; |
|||
} |
|||
|
|||
.component-props-container { |
|||
flex: 1 1 auto; |
|||
overflow-y: auto; |
|||
} |
|||
|
|||
ul { |
|||
list-style: none; |
|||
display: flex; |
|||
padding: 0; |
|||
} |
|||
|
|||
li { |
|||
margin-right: 20px; |
|||
background: none; |
|||
border-radius: 5px; |
|||
width: 45px; |
|||
height: 45px; |
|||
} |
|||
|
|||
li button { |
|||
width: 100%; |
|||
height: 100%; |
|||
background: none; |
|||
border: none; |
|||
border-radius: 5px; |
|||
padding: 12px; |
|||
} |
|||
</style> |
|||
|
|||
@ -1,111 +1,106 @@ |
|||
<script> |
|||
import ComponentSelector from "./ComponentSelector.svelte"; |
|||
import { store } from "../builderStore"; |
|||
import PropsView from "./PropsView.svelte"; |
|||
import Textbox from "../common/Textbox.svelte"; |
|||
import Button from "../common/Button.svelte"; |
|||
import ButtonGroup from "../common/ButtonGroup.svelte"; |
|||
import { pipe } from "../common/core"; |
|||
import UIkit from "uikit"; |
|||
import { isRootComponent } from "./pagesParsing/searchComponents"; |
|||
import { splitName } from "./pagesParsing/splitRootComponentName.js" |
|||
|
|||
import { |
|||
find, filter, some, map, includes |
|||
} from "lodash/fp"; |
|||
import { assign } from "lodash"; |
|||
|
|||
|
|||
export const show = () => { |
|||
UIkit.modal(componentSelectorModal).show(); |
|||
} |
|||
|
|||
let componentSelectorModal; |
|||
let layoutComponents; |
|||
let layoutComponent; |
|||
let screens; |
|||
let name=""; |
|||
let saveAttempted=false; |
|||
|
|||
store.subscribe(s => { |
|||
layoutComponents = pipe(s.components, [ |
|||
filter(c => c.container), |
|||
map(c => ({name:c.name, ...splitName(c.name)})) |
|||
]); |
|||
|
|||
layoutComponent = layoutComponent |
|||
? find(c => c.name === layoutComponent.name)(layoutComponents) |
|||
: layoutComponents[0]; |
|||
|
|||
screens = s.screens; |
|||
}); |
|||
|
|||
const save = () => { |
|||
saveAttempted = true; |
|||
|
|||
const isValid = name.length > 0 |
|||
&& !screenNameExists(name) |
|||
&& layoutComponent; |
|||
|
|||
if(!isValid) return; |
|||
|
|||
store.createScreen(name, layoutComponent.name); |
|||
UIkit.modal(componentSelectorModal).hide(); |
|||
} |
|||
|
|||
const cancel = () => { |
|||
UIkit.modal(componentSelectorModal).hide(); |
|||
} |
|||
|
|||
const screenNameExists = (name) => |
|||
some(s => s.name.toLowerCase() === name.toLowerCase())(screens) |
|||
|
|||
import ComponentSelector from "./ComponentSelector.svelte" |
|||
import { store } from "../builderStore" |
|||
import PropsView from "./PropsView.svelte" |
|||
import Textbox from "../common/Textbox.svelte" |
|||
import Button from "../common/Button.svelte" |
|||
import ButtonGroup from "../common/ButtonGroup.svelte" |
|||
import { pipe } from "../common/core" |
|||
import UIkit from "uikit" |
|||
import { isRootComponent } from "./pagesParsing/searchComponents" |
|||
import { splitName } from "./pagesParsing/splitRootComponentName.js" |
|||
|
|||
import { find, filter, some, map, includes } from "lodash/fp" |
|||
import { assign } from "lodash" |
|||
|
|||
export const show = () => { |
|||
UIkit.modal(componentSelectorModal).show() |
|||
} |
|||
|
|||
let componentSelectorModal |
|||
let layoutComponents |
|||
let layoutComponent |
|||
let screens |
|||
let name = "" |
|||
let saveAttempted = false |
|||
|
|||
store.subscribe(s => { |
|||
layoutComponents = pipe(s.components, [ |
|||
filter(c => c.container), |
|||
map(c => ({ name: c.name, ...splitName(c.name) })), |
|||
]) |
|||
|
|||
layoutComponent = layoutComponent |
|||
? find(c => c.name === layoutComponent.name)(layoutComponents) |
|||
: layoutComponents[0] |
|||
|
|||
screens = s.screens |
|||
}) |
|||
|
|||
const save = () => { |
|||
saveAttempted = true |
|||
|
|||
const isValid = |
|||
name.length > 0 && !screenNameExists(name) && layoutComponent |
|||
|
|||
if (!isValid) return |
|||
|
|||
store.createScreen(name, layoutComponent.name) |
|||
UIkit.modal(componentSelectorModal).hide() |
|||
} |
|||
|
|||
const cancel = () => { |
|||
UIkit.modal(componentSelectorModal).hide() |
|||
} |
|||
|
|||
const screenNameExists = name => |
|||
some(s => s.name.toLowerCase() === name.toLowerCase())(screens) |
|||
</script> |
|||
|
|||
<div bind:this={componentSelectorModal} id="new-component-modal" uk-modal> |
|||
<div class="uk-modal-dialog" uk-overflow-auto> |
|||
<div class="uk-modal-dialog" uk-overflow-auto> |
|||
|
|||
<div class="uk-modal-header"> |
|||
<h1>New Screen</h1> |
|||
</div> |
|||
<div class="uk-modal-header"> |
|||
<h1>New Screen</h1> |
|||
</div> |
|||
|
|||
<div class="uk-modal-body uk-form-horizontal"> |
|||
<div class="uk-margin"> |
|||
<label class="uk-form-label">Name</label> |
|||
<div class="uk-form-controls"> |
|||
<input class="uk-input uk-form-small" |
|||
class:uk-form-danger={saveAttempted && (name.length === 0 || screenNameExists(name))} |
|||
bind:value={name} > |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="uk-margin"> |
|||
<label class="uk-form-label">Layout Component</label> |
|||
<div class="uk-form-controls"> |
|||
<select class="uk-select uk-form-small" |
|||
bind:value={layoutComponent} |
|||
class:uk-form-danger={saveAttempted && !layoutComponent}> |
|||
{#each layoutComponents as comp} |
|||
<option value={comp}> |
|||
{comp.componentName} - {comp.libName} |
|||
</option> |
|||
{/each} |
|||
</select> |
|||
</div> |
|||
</div> |
|||
|
|||
<ButtonGroup style="float: right;"> |
|||
<Button color="primary" grouped on:click={save}>Create Screen</Button> |
|||
<Button color="tertiary" grouped on:click={cancel}>Cancel</Button> |
|||
</ButtonGroup> |
|||
<div class="uk-modal-body uk-form-horizontal"> |
|||
<div class="uk-margin"> |
|||
<label class="uk-form-label">Name</label> |
|||
<div class="uk-form-controls"> |
|||
<input |
|||
class="uk-input uk-form-small" |
|||
class:uk-form-danger={saveAttempted && (name.length === 0 || screenNameExists(name))} |
|||
bind:value={name} /> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="uk-margin"> |
|||
<label class="uk-form-label">Layout Component</label> |
|||
<div class="uk-form-controls"> |
|||
<select |
|||
class="uk-select uk-form-small" |
|||
bind:value={layoutComponent} |
|||
class:uk-form-danger={saveAttempted && !layoutComponent}> |
|||
{#each layoutComponents as comp} |
|||
<option value={comp}> |
|||
{comp.componentName} - {comp.libName} |
|||
</option> |
|||
{/each} |
|||
</select> |
|||
</div> |
|||
</div> |
|||
|
|||
<ButtonGroup style="float: right;"> |
|||
<Button color="primary" grouped on:click={save}>Create Screen</Button> |
|||
<Button color="tertiary" grouped on:click={cancel}>Cancel</Button> |
|||
</ButtonGroup> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
|
|||
|
|||
<style> |
|||
h1 { |
|||
font-size:1.2em; |
|||
} |
|||
h1 { |
|||
font-size: 1.2em; |
|||
} |
|||
</style> |
|||
|
|||
@ -1,63 +1,66 @@ |
|||
<script> |
|||
import Textbox from "../common/Textbox.svelte" |
|||
import Dropdown from "../common/Dropdown.svelte" |
|||
import Button from "../common/Button.svelte" |
|||
import { store } from "../builderStore" |
|||
import { isRootComponent } from "./pagesParsing/searchComponents" |
|||
import { pipe } from "../common/core" |
|||
import { filter, find, concat } from "lodash/fp" |
|||
|
|||
import Textbox from "../common/Textbox.svelte"; |
|||
import Dropdown from "../common/Dropdown.svelte"; |
|||
import Button from "../common/Button.svelte"; |
|||
import { store } from "../builderStore"; |
|||
import { isRootComponent } from "./pagesParsing/searchComponents"; |
|||
import { pipe } from "../common/core"; |
|||
import { |
|||
filter, find, concat |
|||
} from "lodash/fp"; |
|||
|
|||
const notSeletedComponent = {name:"(none selected)"}; |
|||
|
|||
$: page = $store.pages[$store.currentPageName]; |
|||
$: title = page.index.title; |
|||
$: components = pipe($store.components, [ |
|||
filter(store => !isRootComponent($store)), |
|||
concat([notSeletedComponent]) |
|||
]); |
|||
$: entryComponent = find(c => c.name === page.appBody)(components) || notSeletedComponent; |
|||
|
|||
|
|||
const save = () => { |
|||
if(!title || !entryComponent || entryComponent === notSeletedComponent) return; |
|||
const page = { |
|||
index: { |
|||
title |
|||
}, |
|||
appBody: entryComponent.name, |
|||
} |
|||
store.savePage(page); |
|||
const notSeletedComponent = { name: "(none selected)" } |
|||
|
|||
$: page = $store.pages[$store.currentPageName] |
|||
$: title = page.index.title |
|||
$: components = pipe($store.components, [ |
|||
filter(store => !isRootComponent($store)), |
|||
concat([notSeletedComponent]), |
|||
]) |
|||
$: entryComponent = |
|||
find(c => c.name === page.appBody)(components) || notSeletedComponent |
|||
|
|||
const save = () => { |
|||
if (!title || !entryComponent || entryComponent === notSeletedComponent) |
|||
return |
|||
const page = { |
|||
index: { |
|||
title, |
|||
}, |
|||
appBody: entryComponent.name, |
|||
} |
|||
store.savePage(page) |
|||
} |
|||
</script> |
|||
|
|||
<div class="root"> |
|||
|
|||
<h3>{$store.currentPageName}</h3> |
|||
<h3>{$store.currentPageName}</h3> |
|||
|
|||
<form class="uk-form-horizontal"> |
|||
<Textbox bind:text={title} label="Title" hasError={!title}/> |
|||
<div class="help-text">The title of your page, displayed in the bowser tab</div> |
|||
<Dropdown label="App Entry Component" |
|||
options={components} |
|||
bind:selected={entryComponent} |
|||
textMember={(v) => v.name} /> |
|||
<form class="uk-form-horizontal"> |
|||
<Textbox bind:text={title} label="Title" hasError={!title} /> |
|||
<div class="help-text"> |
|||
The title of your page, displayed in the bowser tab |
|||
</div> |
|||
<Dropdown |
|||
label="App Entry Component" |
|||
options={components} |
|||
bind:selected={entryComponent} |
|||
textMember={v => v.name} /> |
|||
|
|||
<div class="help-text">The component that will be loaded into the body of the page</div> |
|||
<div style="margin-top: 20px"></div> |
|||
<Button on:click={save}>Save</Button> |
|||
</form> |
|||
<div class="help-text"> |
|||
The component that will be loaded into the body of the page |
|||
</div> |
|||
<div style="margin-top: 20px" /> |
|||
<Button on:click={save}>Save</Button> |
|||
</form> |
|||
|
|||
</div> |
|||
|
|||
<style> |
|||
.root { |
|||
padding: 15px; |
|||
} |
|||
.help-text { |
|||
color: var(--slate); |
|||
font-size: 10pt; |
|||
} |
|||
.root { |
|||
padding: 15px; |
|||
} |
|||
.help-text { |
|||
color: var(--slate); |
|||
font-size: 10pt; |
|||
} |
|||
</style> |
|||
|
|||
@ -1,73 +1,80 @@ |
|||
<script> |
|||
import { store } from "../builderStore"; |
|||
import getIcon from "../common/icon"; |
|||
import { store } from "../builderStore" |
|||
import getIcon from "../common/icon" |
|||
|
|||
const getPage = (s, name) => { |
|||
const props = s.pages[name]; |
|||
return ({name, props}); |
|||
} |
|||
const getPage = (s, name) => { |
|||
const props = s.pages[name] |
|||
return { name, props } |
|||
} |
|||
|
|||
const pages = [{ |
|||
title: 'Main', |
|||
id: 'main' |
|||
}, { |
|||
title: 'Login', |
|||
id: 'unauthenticated' |
|||
}] |
|||
const pages = [ |
|||
{ |
|||
title: "Main", |
|||
id: "main", |
|||
}, |
|||
{ |
|||
title: "Login", |
|||
id: "unauthenticated", |
|||
}, |
|||
] |
|||
|
|||
store.setCurrentPage('main') |
|||
store.setCurrentPage("main") |
|||
</script> |
|||
|
|||
<div class="root"> |
|||
<select id="page" name="select" on:change={({target}) => store.setCurrentPage(target.value)}> |
|||
<select |
|||
id="page" |
|||
name="select" |
|||
on:change={({ target }) => store.setCurrentPage(target.value)}> |
|||
|
|||
{#each pages as {title, id}} |
|||
<option value="{id}">Page: {title}</option> |
|||
{#each pages as { title, id }} |
|||
<option value={id}>Page: {title}</option> |
|||
{/each} |
|||
|
|||
</select> |
|||
<span class="arrow">{@html getIcon("chevron-down","24")}</span> |
|||
</select> |
|||
<span class="arrow"> |
|||
{@html getIcon('chevron-down', '24')} |
|||
</span> |
|||
</div> |
|||
|
|||
<style> |
|||
.root { |
|||
padding-bottom: 10px; |
|||
font-size: .9rem; |
|||
color: var(--secondary50); |
|||
font-weight: bold; |
|||
position: relative; |
|||
} |
|||
.root { |
|||
padding-bottom: 10px; |
|||
font-size: 0.9rem; |
|||
color: var(--secondary50); |
|||
font-weight: bold; |
|||
position: relative; |
|||
} |
|||
|
|||
select { |
|||
display: block; |
|||
font-size: 16px; |
|||
font-family: sans-serif; |
|||
font-weight: 700; |
|||
color: #444; |
|||
line-height: 1.3; |
|||
padding: 1em 2.6em 0.9em 1.4em; |
|||
width: 100%; |
|||
max-width: 100%; |
|||
box-sizing: border-box; |
|||
margin: 0; |
|||
border: none; |
|||
border-radius: 0.5em; |
|||
-moz-appearance: none; |
|||
-webkit-appearance: none; |
|||
appearance: none; |
|||
background-color: #fafafa; |
|||
} |
|||
|
|||
select { |
|||
display: block; |
|||
font-size: 16px; |
|||
font-family: sans-serif; |
|||
font-weight: 700; |
|||
color: #444; |
|||
line-height: 1.3; |
|||
padding: 1em 2.6em 0.9em 1.4em; |
|||
width: 100%; |
|||
max-width: 100%; |
|||
box-sizing: border-box; |
|||
margin: 0; |
|||
border: none; |
|||
border-radius: .5em; |
|||
-moz-appearance: none; |
|||
-webkit-appearance: none; |
|||
appearance: none; |
|||
background-color: #fafafa; |
|||
} |
|||
|
|||
.arrow { |
|||
position: absolute; |
|||
right: 10px; |
|||
top: 0; |
|||
bottom: 0; |
|||
margin: auto; |
|||
width: 30px; |
|||
height: 30px; |
|||
pointer-events: none; |
|||
color: var(--primary100); |
|||
} |
|||
.arrow { |
|||
position: absolute; |
|||
right: 10px; |
|||
top: 0; |
|||
bottom: 0; |
|||
margin: auto; |
|||
width: 30px; |
|||
height: 30px; |
|||
pointer-events: none; |
|||
color: var(--primary100); |
|||
} |
|||
</style> |
|||
|
|||
@ -1,52 +1,50 @@ |
|||
<script> |
|||
import Checkbox from "../common/Checkbox.svelte"; |
|||
import Textbox from "../common/Textbox.svelte"; |
|||
import Dropdown from "../common/Dropdown.svelte"; |
|||
import StateBindingControl from "./StateBindingControl.svelte"; |
|||
|
|||
export let setProp = () => {}; |
|||
export let index; |
|||
export let prop_name; |
|||
export let prop_value; |
|||
export let prop_type = {}; |
|||
|
|||
$: isOdd = (index % 2 !== 0); |
|||
|
|||
const setComponentProp = (props) => { |
|||
setProp(propDef.____name, props); |
|||
} |
|||
import Checkbox from "../common/Checkbox.svelte" |
|||
import Textbox from "../common/Textbox.svelte" |
|||
import Dropdown from "../common/Dropdown.svelte" |
|||
import StateBindingControl from "./StateBindingControl.svelte" |
|||
|
|||
export let setProp = () => {} |
|||
export let index |
|||
export let prop_name |
|||
export let prop_value |
|||
export let prop_type = {} |
|||
|
|||
$: isOdd = index % 2 !== 0 |
|||
|
|||
const setComponentProp = props => { |
|||
setProp(propDef.____name, props) |
|||
} |
|||
</script> |
|||
|
|||
|
|||
<div class="root" > |
|||
{#if prop_type !== "event" } |
|||
|
|||
<div class="root"> |
|||
{#if prop_type !== 'event'} |
|||
<h5>{prop_name}</h5> |
|||
<StateBindingControl value={prop_value} |
|||
type={prop_type} |
|||
options={prop_type.options} |
|||
onChanged={v => setProp(prop_name, v)}/> |
|||
|
|||
{/if} |
|||
<StateBindingControl |
|||
value={prop_value} |
|||
type={prop_type} |
|||
options={prop_type.options} |
|||
onChanged={v => setProp(prop_name, v)} /> |
|||
{/if} |
|||
</div> |
|||
|
|||
<style> |
|||
.root { |
|||
height: 40px; |
|||
margin-bottom: 15px; |
|||
display: grid; |
|||
grid-template-rows: 1fr; |
|||
grid-template-columns: 70px 1fr; |
|||
grid-gap: 10px; |
|||
} |
|||
|
|||
h5 { |
|||
word-wrap: break-word; |
|||
font-size: 12px; |
|||
font-weight: 700; |
|||
color: #163057; |
|||
opacity: 0.6; |
|||
padding-top: 12px; |
|||
margin-bottom: 0; |
|||
} |
|||
.root { |
|||
height: 40px; |
|||
margin-bottom: 15px; |
|||
display: grid; |
|||
grid-template-rows: 1fr; |
|||
grid-template-columns: 70px 1fr; |
|||
grid-gap: 10px; |
|||
} |
|||
|
|||
h5 { |
|||
word-wrap: break-word; |
|||
font-size: 12px; |
|||
font-weight: 700; |
|||
color: #163057; |
|||
opacity: 0.6; |
|||
padding-top: 12px; |
|||
margin-bottom: 0; |
|||
} |
|||
</style> |
|||
|
|||
@ -1,70 +1,72 @@ |
|||
<script> |
|||
import { some, includes, filter } from "lodash/fp"; |
|||
import Textbox from "../common/Textbox.svelte"; |
|||
import Dropdown from "../common/Dropdown.svelte"; |
|||
import PropControl from "./PropControl.svelte"; |
|||
import IconButton from "../common/IconButton.svelte"; |
|||
|
|||
export let componentInfo; |
|||
export let onPropChanged = () => {}; |
|||
export let components; |
|||
|
|||
let errors = []; |
|||
let props = {}; |
|||
|
|||
const props_to_ignore = ['_component','_children', '_styles', '_code', '_id']; |
|||
|
|||
$: propDefs = componentInfo && Object.entries(componentInfo).filter(([name])=> !props_to_ignore.includes(name)); |
|||
|
|||
function find_type(prop_name) { |
|||
if(!componentInfo._component) return; |
|||
return components.find(({name}) => name === componentInfo._component).props[prop_name]; |
|||
} |
|||
|
|||
let setProp = (name, value) => { |
|||
onPropChanged(name, value); |
|||
} |
|||
|
|||
const fieldHasError = (propName) => |
|||
some(e => e.propName === propName)(errors); |
|||
import { some, includes, filter } from "lodash/fp" |
|||
import Textbox from "../common/Textbox.svelte" |
|||
import Dropdown from "../common/Dropdown.svelte" |
|||
import PropControl from "./PropControl.svelte" |
|||
import IconButton from "../common/IconButton.svelte" |
|||
|
|||
export let componentInfo |
|||
export let onPropChanged = () => {} |
|||
export let components |
|||
|
|||
let errors = [] |
|||
let props = {} |
|||
|
|||
const props_to_ignore = ["_component", "_children", "_styles", "_code", "_id"] |
|||
|
|||
$: propDefs = |
|||
componentInfo && |
|||
Object.entries(componentInfo).filter( |
|||
([name]) => !props_to_ignore.includes(name) |
|||
) |
|||
|
|||
function find_type(prop_name) { |
|||
if (!componentInfo._component) return |
|||
return components.find(({ name }) => name === componentInfo._component) |
|||
.props[prop_name] |
|||
} |
|||
|
|||
let setProp = (name, value) => { |
|||
onPropChanged(name, value) |
|||
} |
|||
|
|||
const fieldHasError = propName => some(e => e.propName === propName)(errors) |
|||
</script> |
|||
|
|||
<div class="root"> |
|||
|
|||
<form class="uk-form-stacked form-root"> |
|||
{#each propDefs as [prop_name, prop_value], index} |
|||
|
|||
<div class="prop-container"> |
|||
<form class="uk-form-stacked form-root"> |
|||
{#each propDefs as [prop_name, prop_value], index} |
|||
<div class="prop-container"> |
|||
|
|||
<PropControl {setProp} |
|||
{prop_name} |
|||
{prop_value} |
|||
prop_type={find_type(prop_name)} |
|||
{index} |
|||
disabled={false} /> |
|||
<PropControl |
|||
{setProp} |
|||
{prop_name} |
|||
{prop_value} |
|||
prop_type={find_type(prop_name)} |
|||
{index} |
|||
disabled={false} /> |
|||
|
|||
</div> |
|||
</div> |
|||
{/each} |
|||
|
|||
{/each} |
|||
|
|||
</form> |
|||
</form> |
|||
|
|||
</div> |
|||
|
|||
|
|||
<style> |
|||
.root { |
|||
font-size:10pt; |
|||
width: 100%; |
|||
} |
|||
|
|||
.form-root { |
|||
display: flex; |
|||
flex-wrap: wrap; |
|||
} |
|||
|
|||
.prop-container { |
|||
flex: 1 1 auto; |
|||
min-width: 250px; |
|||
} |
|||
.root { |
|||
font-size: 10pt; |
|||
width: 100%; |
|||
} |
|||
|
|||
.form-root { |
|||
display: flex; |
|||
flex-wrap: wrap; |
|||
} |
|||
|
|||
.prop-container { |
|||
flex: 1 1 auto; |
|||
min-width: 250px; |
|||
} |
|||
</style> |
|||
|
|||
@ -1,137 +1,134 @@ |
|||
<script> |
|||
import { store } from "../builderStore"; |
|||
import Textbox from "../common/Textbox.svelte"; |
|||
import Button from "../common/Button.svelte"; |
|||
import IconButton from "../common/IconButton.svelte"; |
|||
import { libraryDependencies } from "./pagesParsing/findDependencies"; |
|||
import UIkit from "uikit"; |
|||
|
|||
let addNewLib = ""; |
|||
let addNewStylesheet = ""; |
|||
let modalElement; |
|||
|
|||
$: components = $store.components; |
|||
|
|||
const removeLibrary = lib => { |
|||
const dependencies = libraryDependencies(components, lib); |
|||
if(dependencies.length > 0) return; |
|||
store.removeComponentLibrary(lib); |
|||
} |
|||
|
|||
const addLib = () => { |
|||
store.addComponentLibrary(addNewLib) |
|||
.then(() => { |
|||
addNewLib = ""; |
|||
}); |
|||
} |
|||
|
|||
const removeStylesheet = stylesheet => { |
|||
store.removeStylesheet(stylesheet); |
|||
} |
|||
|
|||
const addStylesheet = () => { |
|||
if(addNewStylesheet) |
|||
store.addStylesheet(addNewStylesheet); |
|||
} |
|||
|
|||
export const close = () => { |
|||
UIkit.modal(modalElement).hide(); |
|||
} |
|||
|
|||
export const show = () => { |
|||
UIkit.modal(modalElement).show(); |
|||
} |
|||
import { store } from "../builderStore" |
|||
import Textbox from "../common/Textbox.svelte" |
|||
import Button from "../common/Button.svelte" |
|||
import IconButton from "../common/IconButton.svelte" |
|||
import { libraryDependencies } from "./pagesParsing/findDependencies" |
|||
import UIkit from "uikit" |
|||
|
|||
let addNewLib = "" |
|||
let addNewStylesheet = "" |
|||
let modalElement |
|||
|
|||
$: components = $store.components |
|||
|
|||
const removeLibrary = lib => { |
|||
const dependencies = libraryDependencies(components, lib) |
|||
if (dependencies.length > 0) return |
|||
store.removeComponentLibrary(lib) |
|||
} |
|||
|
|||
const addLib = () => { |
|||
store.addComponentLibrary(addNewLib).then(() => { |
|||
addNewLib = "" |
|||
}) |
|||
} |
|||
|
|||
const removeStylesheet = stylesheet => { |
|||
store.removeStylesheet(stylesheet) |
|||
} |
|||
|
|||
const addStylesheet = () => { |
|||
if (addNewStylesheet) store.addStylesheet(addNewStylesheet) |
|||
} |
|||
|
|||
export const close = () => { |
|||
UIkit.modal(modalElement).hide() |
|||
} |
|||
|
|||
export const show = () => { |
|||
UIkit.modal(modalElement).show() |
|||
} |
|||
</script> |
|||
|
|||
<div bind:this={modalElement} id="new-component-modal" uk-modal> |
|||
<div class="uk-modal-dialog"> |
|||
|
|||
<div class="uk-modal-header header"> |
|||
<div>Settings</div> |
|||
<div> |
|||
<IconButton icon="x" |
|||
on:click={close}/> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="uk-modal-body uk-form-horizontal"> |
|||
|
|||
<div class="section-container"> |
|||
<p>Component Libraries |
|||
<span> |
|||
<input bind:value={addNewLib} /> |
|||
<Button color="primary-outline" |
|||
on:click={addLib}>Add</Button> |
|||
</span> |
|||
</p> |
|||
{#each $store.pages.componentLibraries as lib} |
|||
<div> |
|||
<span class="row-text">{lib}</span> |
|||
<IconButton icon="x" |
|||
on:click={() => removeLibrary(lib)}/> |
|||
</div> |
|||
{/each} |
|||
</div> |
|||
|
|||
|
|||
<div class="section-container"> |
|||
<p>Stylesheets |
|||
<span> |
|||
<input bind:value={addNewStylesheet} /> |
|||
<Button color="primary-outline" |
|||
on:click={addStylesheet} >Add</Button> |
|||
</span> |
|||
</p> |
|||
{#each $store.pages.stylesheets as stylesheet} |
|||
<div> |
|||
<span class="row-text">{stylesheet}</span> |
|||
<IconButton icon="x" |
|||
on:click={() => removeStylesheet(stylesheet)}/> |
|||
</div> |
|||
{/each} |
|||
</div> |
|||
|
|||
|
|||
</div> |
|||
<div class="uk-modal-dialog"> |
|||
|
|||
<div class="uk-modal-header header"> |
|||
<div>Settings</div> |
|||
<div> |
|||
<IconButton icon="x" on:click={close} /> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="uk-modal-body uk-form-horizontal"> |
|||
|
|||
<div class="section-container"> |
|||
<p> |
|||
Component Libraries |
|||
<span> |
|||
<input bind:value={addNewLib} /> |
|||
<Button color="primary-outline" on:click={addLib}>Add</Button> |
|||
</span> |
|||
</p> |
|||
{#each $store.pages.componentLibraries as lib} |
|||
<div> |
|||
<span class="row-text">{lib}</span> |
|||
<IconButton icon="x" on:click={() => removeLibrary(lib)} /> |
|||
</div> |
|||
{/each} |
|||
</div> |
|||
|
|||
<div class="section-container"> |
|||
<p> |
|||
Stylesheets |
|||
<span> |
|||
<input bind:value={addNewStylesheet} /> |
|||
<Button color="primary-outline" on:click={addStylesheet}> |
|||
Add |
|||
</Button> |
|||
</span> |
|||
</p> |
|||
{#each $store.pages.stylesheets as stylesheet} |
|||
<div> |
|||
<span class="row-text">{stylesheet}</span> |
|||
<IconButton |
|||
icon="x" |
|||
on:click={() => removeStylesheet(stylesheet)} /> |
|||
</div> |
|||
{/each} |
|||
</div> |
|||
|
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<style> |
|||
.section-container { |
|||
padding: 15px; |
|||
border-style: dotted; |
|||
border-width: 1px; |
|||
border-color: var(--lightslate); |
|||
border-radius: 2px; |
|||
} |
|||
|
|||
.section-container:nth-child(1) { |
|||
margin-bottom: 15px; |
|||
} |
|||
|
|||
.row-text { |
|||
margin-right: 15px; |
|||
color: var(--primary100); |
|||
} |
|||
|
|||
input { |
|||
margin-right: 15px; |
|||
} |
|||
|
|||
p > span { |
|||
margin-left: 30px; |
|||
} |
|||
|
|||
.header { |
|||
display: grid; |
|||
grid-template-columns: [title] 1fr [icon] auto; |
|||
} |
|||
|
|||
.header > div:nth-child(1) { |
|||
grid-column-start: title; |
|||
} |
|||
|
|||
.header > div:nth-child(2) { |
|||
grid-column-start: icon; |
|||
} |
|||
.section-container { |
|||
padding: 15px; |
|||
border-style: dotted; |
|||
border-width: 1px; |
|||
border-color: var(--lightslate); |
|||
border-radius: 2px; |
|||
} |
|||
|
|||
.section-container:nth-child(1) { |
|||
margin-bottom: 15px; |
|||
} |
|||
|
|||
.row-text { |
|||
margin-right: 15px; |
|||
color: var(--primary100); |
|||
} |
|||
|
|||
input { |
|||
margin-right: 15px; |
|||
} |
|||
|
|||
p > span { |
|||
margin-left: 30px; |
|||
} |
|||
|
|||
.header { |
|||
display: grid; |
|||
grid-template-columns: [title] 1fr [icon] auto; |
|||
} |
|||
|
|||
.header > div:nth-child(1) { |
|||
grid-column-start: title; |
|||
} |
|||
|
|||
.header > div:nth-child(2) { |
|||
grid-column-start: icon; |
|||
} |
|||
</style> |
|||
|
|||
@ -1,163 +1,160 @@ |
|||
<script> |
|||
import IconButton from "../common/IconButton.svelte"; |
|||
import Input from "../common/Input.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"; |
|||
} |
|||
import IconButton from "../common/IconButton.svelte" |
|||
import Input from "../common/Input.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 clearBinding = () => { |
|||
forceIsBound = false |
|||
onChanged("") |
|||
} |
|||
|
|||
const setBindingPath = ev => { |
|||
forceIsBound = canOnlyBind; |
|||
bind(ev.target.value, bindingFallbackValue, bindingSource) |
|||
const bind = (path, fallback, source) => { |
|||
if (!path) { |
|||
clearBinding("") |
|||
return |
|||
} |
|||
|
|||
const setBindingFallback = ev => { |
|||
bind(bindingPath, ev.target.value, bindingSource); |
|||
} |
|||
|
|||
const setBindingSource = ev => { |
|||
bind(bindingPath, bindingFallbackValue, ev.target.value); |
|||
} |
|||
|
|||
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) |
|||
} |
|||
</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> |
|||
<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} /> |
|||
{/if} |
|||
|
|||
</div> |
|||
<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} |
|||
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 /> |
|||
{/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; |
|||
} |
|||
.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> |
|||
|
|||
@ -1,180 +1,178 @@ |
|||
<script> |
|||
import ComponentsHierarchy from "./ComponentsHierarchy.svelte"; |
|||
import PagesList from "./PagesList.svelte" |
|||
import { store } from "../builderStore"; |
|||
import IconButton from "../common/IconButton.svelte"; |
|||
import Modal from "../common/Modal.svelte"; |
|||
import NewComponent from "./NewComponent.svelte"; |
|||
import CurrentItemPreview from "./CurrentItemPreview.svelte"; |
|||
import SettingsView from "./SettingsView.svelte"; |
|||
import PageView from "./PageView.svelte"; |
|||
import ComponentsPaneSwitcher from "./ComponentsPaneSwitcher.svelte"; |
|||
|
|||
let newComponentPicker; |
|||
|
|||
const newComponent = () => { |
|||
newComponentPicker.show(); |
|||
} |
|||
|
|||
let settingsView; |
|||
const settings = () => { |
|||
settingsView.show(); |
|||
} |
|||
import ComponentsHierarchy from "./ComponentsHierarchy.svelte" |
|||
import PagesList from "./PagesList.svelte" |
|||
import { store } from "../builderStore" |
|||
import IconButton from "../common/IconButton.svelte" |
|||
import Modal from "../common/Modal.svelte" |
|||
import NewComponent from "./NewComponent.svelte" |
|||
import CurrentItemPreview from "./CurrentItemPreview.svelte" |
|||
import SettingsView from "./SettingsView.svelte" |
|||
import PageView from "./PageView.svelte" |
|||
import ComponentsPaneSwitcher from "./ComponentsPaneSwitcher.svelte" |
|||
|
|||
let newComponentPicker |
|||
|
|||
const newComponent = () => { |
|||
newComponentPicker.show() |
|||
} |
|||
|
|||
let settingsView |
|||
const settings = () => { |
|||
settingsView.show() |
|||
} |
|||
</script> |
|||
|
|||
<div class="root"> |
|||
|
|||
<div class="ui-nav"> |
|||
<div class="ui-nav"> |
|||
|
|||
<div class="pages-list-container"> |
|||
<div class="nav-group-header"> |
|||
<div class="pages-list-container"> |
|||
<div class="nav-group-header"> |
|||
|
|||
<span class="navigator-title">Navigator</span> |
|||
</div> |
|||
<div class="nav-items-container"> |
|||
<PagesList /> |
|||
</div> |
|||
</div> |
|||
<span class="navigator-title">Navigator</span> |
|||
</div> |
|||
<div class="nav-items-container"> |
|||
<PagesList /> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="components-list-container"> |
|||
<div class="nav-group-header"> |
|||
|
|||
<span class="components-nav-header">Screens</span> |
|||
<div> |
|||
<button on:click={newComponent}>+</button> |
|||
</div> |
|||
</div> |
|||
<div class="nav-items-container"> |
|||
<ComponentsHierarchy components={$store.screens}/> |
|||
</div> |
|||
</div> |
|||
<div class="components-list-container"> |
|||
<div class="nav-group-header"> |
|||
|
|||
<span class="components-nav-header">Screens</span> |
|||
<div> |
|||
<button on:click={newComponent}>+</button> |
|||
</div> |
|||
</div> |
|||
<div class="nav-items-container"> |
|||
<ComponentsHierarchy components={$store.screens} /> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="preview-pane"> |
|||
{#if $store.currentFrontEndType === "screen"} |
|||
<CurrentItemPreview /> |
|||
{:else if $store.currentFrontEndType === "page"} |
|||
<PageView /> |
|||
{/if} |
|||
</div> |
|||
</div> |
|||
|
|||
{#if $store.currentFrontEndType === "screen"} |
|||
<div class="components-pane"> |
|||
<ComponentsPaneSwitcher /> |
|||
</div> |
|||
<div class="preview-pane"> |
|||
{#if $store.currentFrontEndType === 'screen'} |
|||
<CurrentItemPreview /> |
|||
{:else if $store.currentFrontEndType === 'page'} |
|||
<PageView /> |
|||
{/if} |
|||
</div> |
|||
|
|||
</div> |
|||
{#if $store.currentFrontEndType === 'screen'} |
|||
<div class="components-pane"> |
|||
<ComponentsPaneSwitcher /> |
|||
</div> |
|||
{/if} |
|||
|
|||
</div> |
|||
|
|||
<NewComponent bind:this={newComponentPicker}/> |
|||
<NewComponent bind:this={newComponentPicker} /> |
|||
<SettingsView bind:this={settingsView} /> |
|||
|
|||
|
|||
<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); |
|||
} |
|||
|
|||
.root { |
|||
display: grid; |
|||
grid-template-columns: 290px 1fr 300px; |
|||
height: 100%; |
|||
width: 100%; |
|||
background: #fafafa; |
|||
} |
|||
|
|||
.ui-nav { |
|||
grid-column: 1; |
|||
background-color: var(--secondary5); |
|||
height: 100%; |
|||
padding: 0 1.5rem 0rem 1.5rem |
|||
} |
|||
|
|||
.preview-pane { |
|||
grid-column: 2; |
|||
margin: 80px 60px; |
|||
background: #fff; |
|||
border-radius: 5px; |
|||
box-shadow: 0 0px 6px rgba(0,0,0,0.05) |
|||
} |
|||
|
|||
.components-pane { |
|||
grid-column: 3; |
|||
background-color: var(--secondary5); |
|||
min-height: 0px; |
|||
overflow-y: hidden; |
|||
} |
|||
|
|||
.components-nav-header { |
|||
font-size: 0.75rem; |
|||
color: #999; |
|||
text-transform: uppercase; |
|||
} |
|||
|
|||
.nav-group-header { |
|||
font-size: .9rem; |
|||
padding-left: 1rem; |
|||
} |
|||
|
|||
.nav-items-container { |
|||
padding: 1rem 0rem 0rem 0rem; |
|||
} |
|||
|
|||
.nav-group-header { |
|||
display: flex; |
|||
padding: 2rem 0 0 0; |
|||
font-size: .9rem; |
|||
font-weight: bold; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
} |
|||
|
|||
.nav-group-header>div:nth-child(1) { |
|||
padding: 0rem .5rem 0rem 0rem; |
|||
vertical-align: bottom; |
|||
grid-column-start: icon; |
|||
margin-right: 5px; |
|||
} |
|||
|
|||
.nav-group-header>span:nth-child(2) { |
|||
margin-left:5px; |
|||
vertical-align: bottom; |
|||
grid-column-start: title; |
|||
margin-top:auto; |
|||
} |
|||
|
|||
.nav-group-header>div:nth-child(3) { |
|||
vertical-align: bottom; |
|||
grid-column-start: button; |
|||
cursor: pointer; |
|||
color: var(--primary75); |
|||
} |
|||
|
|||
.nav-group-header>div:nth-child(3):hover { |
|||
color: var(--primary75); |
|||
} |
|||
|
|||
.navigator-title { |
|||
text-transform: uppercase; |
|||
font-weight: 400; |
|||
color: #999; |
|||
} |
|||
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); |
|||
} |
|||
|
|||
.root { |
|||
display: grid; |
|||
grid-template-columns: 290px 1fr 300px; |
|||
height: 100%; |
|||
width: 100%; |
|||
background: #fafafa; |
|||
} |
|||
|
|||
.ui-nav { |
|||
grid-column: 1; |
|||
background-color: var(--secondary5); |
|||
height: 100%; |
|||
padding: 0 1.5rem 0rem 1.5rem; |
|||
} |
|||
|
|||
.preview-pane { |
|||
grid-column: 2; |
|||
margin: 80px 60px; |
|||
background: #fff; |
|||
border-radius: 5px; |
|||
box-shadow: 0 0px 6px rgba(0, 0, 0, 0.05); |
|||
} |
|||
|
|||
.components-pane { |
|||
grid-column: 3; |
|||
background-color: var(--secondary5); |
|||
min-height: 0px; |
|||
overflow-y: hidden; |
|||
} |
|||
|
|||
.components-nav-header { |
|||
font-size: 0.75rem; |
|||
color: #999; |
|||
text-transform: uppercase; |
|||
} |
|||
|
|||
.nav-group-header { |
|||
font-size: 0.9rem; |
|||
padding-left: 1rem; |
|||
} |
|||
|
|||
.nav-items-container { |
|||
padding: 1rem 0rem 0rem 0rem; |
|||
} |
|||
|
|||
.nav-group-header { |
|||
display: flex; |
|||
padding: 2rem 0 0 0; |
|||
font-size: 0.9rem; |
|||
font-weight: bold; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
} |
|||
|
|||
.nav-group-header > div:nth-child(1) { |
|||
padding: 0rem 0.5rem 0rem 0rem; |
|||
vertical-align: bottom; |
|||
grid-column-start: icon; |
|||
margin-right: 5px; |
|||
} |
|||
|
|||
.nav-group-header > span:nth-child(2) { |
|||
margin-left: 5px; |
|||
vertical-align: bottom; |
|||
grid-column-start: title; |
|||
margin-top: auto; |
|||
} |
|||
|
|||
.nav-group-header > div:nth-child(3) { |
|||
vertical-align: bottom; |
|||
grid-column-start: button; |
|||
cursor: pointer; |
|||
color: var(--primary75); |
|||
} |
|||
|
|||
.nav-group-header > div:nth-child(3):hover { |
|||
color: var(--primary75); |
|||
} |
|||
|
|||
.navigator-title { |
|||
text-transform: uppercase; |
|||
font-weight: 400; |
|||
color: #999; |
|||
} |
|||
</style> |
|||
|
|||
@ -1,12 +1,8 @@ |
|||
<script> |
|||
export let text=""; |
|||
export let className="" |
|||
|
|||
export let _bb; |
|||
|
|||
export let text = "" |
|||
export let className = "" |
|||
|
|||
export let _bb |
|||
</script> |
|||
|
|||
<h1 class={className}> |
|||
{text} |
|||
</h1> |
|||
<h1 class={className}>{text}</h1> |
|||
|
|||
@ -1,18 +1,14 @@ |
|||
<script> |
|||
|
|||
export let _children = []; |
|||
export let className=""; |
|||
export let onLoad; |
|||
export let _bb; |
|||
|
|||
let rootDiv; |
|||
$:{ |
|||
if(_bb && rootDiv && _children && _children.length) |
|||
_bb.hydrateChildren(_children, rootDiv); |
|||
} |
|||
|
|||
|
|||
export let _children = [] |
|||
export let className = "" |
|||
export let onLoad |
|||
export let _bb |
|||
|
|||
let rootDiv |
|||
$: { |
|||
if (_bb && rootDiv && _children && _children.length) |
|||
_bb.hydrateChildren(_children, rootDiv) |
|||
} |
|||
</script> |
|||
|
|||
<div class="{className}" bind:this={rootDiv}> |
|||
</div> |
|||
<div class={className} bind:this={rootDiv} /> |
|||
|
|||
@ -1,12 +1,8 @@ |
|||
<script> |
|||
export let text=""; |
|||
export let className="" |
|||
|
|||
export let _bb; |
|||
|
|||
export let text = "" |
|||
export let className = "" |
|||
|
|||
export let _bb |
|||
</script> |
|||
|
|||
<h1 class={className}> |
|||
{text} |
|||
</h1> |
|||
<h1 class={className}>{text}</h1> |
|||
|
|||
@ -1,12 +1,8 @@ |
|||
<script> |
|||
export let text=""; |
|||
export let className="" |
|||
|
|||
export let _bb; |
|||
|
|||
export let text = "" |
|||
export let className = "" |
|||
|
|||
export let _bb |
|||
</script> |
|||
|
|||
<h1 class={className}> |
|||
{text} |
|||
</h1> |
|||
<h1 class={className}>{text}</h1> |
|||
|
|||
@ -1,12 +1,8 @@ |
|||
<script> |
|||
export let text=""; |
|||
export let className="" |
|||
|
|||
export let _bb; |
|||
|
|||
export let text = "" |
|||
export let className = "" |
|||
|
|||
export let _bb |
|||
</script> |
|||
|
|||
<h3 class={className}> |
|||
{text} |
|||
</h3> |
|||
<h3 class={className}>{text}</h3> |
|||
|
|||
@ -1,12 +1,8 @@ |
|||
<script> |
|||
export let text=""; |
|||
export let className="" |
|||
|
|||
export let _bb; |
|||
|
|||
export let text = "" |
|||
export let className = "" |
|||
|
|||
export let _bb |
|||
</script> |
|||
|
|||
<h4 class={className}> |
|||
{text} |
|||
</h4> |
|||
<h4 class={className}>{text}</h4> |
|||
|
|||
@ -1,12 +1,8 @@ |
|||
<script> |
|||
export let text=""; |
|||
export let className="" |
|||
|
|||
export let _bb; |
|||
|
|||
export let text = "" |
|||
export let className = "" |
|||
|
|||
export let _bb |
|||
</script> |
|||
|
|||
<h6 class={className}> |
|||
{text} |
|||
</h6> |
|||
<h6 class={className}>{text}</h6> |
|||
|
|||
@ -1,14 +1,10 @@ |
|||
<script> |
|||
import {buildStyle} from "./buildStyle"; |
|||
|
|||
export let text=""; |
|||
export let className="" |
|||
|
|||
export let _bb; |
|||
import { buildStyle } from "./buildStyle" |
|||
|
|||
export let text = "" |
|||
export let className = "" |
|||
|
|||
export let _bb |
|||
</script> |
|||
|
|||
<h1 class={className}> |
|||
{text} |
|||
</h1> |
|||
<h1 class={className}>{text}</h1> |
|||
|
|||
@ -1,22 +1,17 @@ |
|||
<script> |
|||
export let value = "" |
|||
export let className = "" |
|||
export let type = "text" |
|||
|
|||
export let value=""; |
|||
export let className = ""; |
|||
export let type = "text"; |
|||
export let _bb |
|||
|
|||
export let _bb; |
|||
|
|||
let actualValue = ""; |
|||
|
|||
const onchange = (ev) => { |
|||
if(_bb) { |
|||
_bb.setStateFromBinding(_bb.props.value, ev.target.value); |
|||
} |
|||
} |
|||
let actualValue = "" |
|||
|
|||
const onchange = ev => { |
|||
if (_bb) { |
|||
_bb.setStateFromBinding(_bb.props.value, ev.target.value) |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<input class={className} |
|||
type={type} |
|||
value={value} |
|||
on:change={onchange}/> |
|||
<input class={className} {type} {value} on:change={onchange} /> |
|||
|
|||
@ -1,27 +1,23 @@ |
|||
<script> |
|||
export let value = "" |
|||
export let className = "" |
|||
export let type = "text" |
|||
export let options = [] |
|||
|
|||
export let value=""; |
|||
export let className = ""; |
|||
export let type = "text"; |
|||
export let options = []; |
|||
export let _bb |
|||
|
|||
export let _bb; |
|||
|
|||
let actualValue = ""; |
|||
|
|||
const onchange = (ev) => { |
|||
if(_bb) { |
|||
_bb.setStateFromBinding(_bb.props.value, ev.target.value); |
|||
} |
|||
} |
|||
let actualValue = "" |
|||
|
|||
const onchange = ev => { |
|||
if (_bb) { |
|||
_bb.setStateFromBinding(_bb.props.value, ev.target.value) |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<select class={className} |
|||
value={value} |
|||
on:change={onchange}> |
|||
<option></option> |
|||
{#each options as opt} |
|||
<option id={opt.id ? opt.id : opt.value}>{opt.value}</option> |
|||
{/each} |
|||
<select class={className} {value} on:change={onchange}> |
|||
<option /> |
|||
{#each options as opt} |
|||
<option id={opt.id ? opt.id : opt.value}>{opt.value}</option> |
|||
{/each} |
|||
</select> |
|||
|
|||
@ -1,82 +1,74 @@ |
|||
<script> |
|||
export let columns = [] |
|||
export let data = "" |
|||
export let tableClass = "" |
|||
export let theadClass = "" |
|||
export let tbodyClass = "" |
|||
export let trClass = "" |
|||
export let thClass = "" |
|||
export let onRowClick |
|||
|
|||
export let columns=[]; |
|||
export let data=""; |
|||
export let tableClass=""; |
|||
export let theadClass=""; |
|||
export let tbodyClass=""; |
|||
export let trClass=""; |
|||
export let thClass=""; |
|||
export let onRowClick; |
|||
|
|||
export let _bb; |
|||
|
|||
const rowClickHandler = (row) => () => { |
|||
_bb.call(onRowClick, row); |
|||
} |
|||
|
|||
const cellValue = (colIndex, row) => { |
|||
const val = _bb.getStateOrValue( |
|||
_bb.props.columns[colIndex].value |
|||
, row) |
|||
return val; |
|||
} |
|||
export let _bb |
|||
|
|||
const rowClickHandler = row => () => { |
|||
_bb.call(onRowClick, row) |
|||
} |
|||
|
|||
const cellValue = (colIndex, row) => { |
|||
const val = _bb.getStateOrValue(_bb.props.columns[colIndex].value, row) |
|||
return val |
|||
} |
|||
</script> |
|||
|
|||
<table class={tableClass}> |
|||
<thead class={theadClass}> |
|||
<tr class={trClass}> |
|||
{#each columns as col} |
|||
<th class={thClass}>{col.title}</th> |
|||
{/each} |
|||
</tr> |
|||
</thead> |
|||
<tbody class={tbodyClass}> |
|||
{#if data} |
|||
{#each data as row} |
|||
<tr class={trClass} |
|||
on:click={rowClickHandler(row)} > |
|||
{#each columns as col, index} |
|||
<table class={tableClass}> |
|||
<thead class={theadClass}> |
|||
<tr class={trClass}> |
|||
{#each columns as col} |
|||
<th class={thClass}>{col.title}</th> |
|||
{/each} |
|||
</tr> |
|||
</thead> |
|||
<tbody class={tbodyClass}> |
|||
{#if data} |
|||
{#each data as row} |
|||
<tr class={trClass} on:click={rowClickHandler(row)}> |
|||
{#each columns as col, index} |
|||
<th class={thClass}>{cellValue(index, row)}</th> |
|||
{/each} |
|||
{/each} |
|||
</tr> |
|||
{/each} |
|||
{/if} |
|||
</tbody> |
|||
</table> |
|||
{/each} |
|||
{/if} |
|||
</tbody> |
|||
</table> |
|||
|
|||
<style> |
|||
|
|||
.table-default { |
|||
.table-default { |
|||
width: 100%; |
|||
margin-bottom: 1rem; |
|||
color: #212529; |
|||
border-collapse: collapse; |
|||
} |
|||
} |
|||
|
|||
.table-default .thead-default .th-default { |
|||
.table-default .thead-default .th-default { |
|||
vertical-align: bottom; |
|||
border-bottom: 2px solid #dee2e6; |
|||
font-weight: bold; |
|||
} |
|||
} |
|||
|
|||
.table-default .th-default { |
|||
padding: .75rem; |
|||
.table-default .th-default { |
|||
padding: 0.75rem; |
|||
vertical-align: top; |
|||
border-top: 1px solid #dee2e6; |
|||
font-weight: normal; |
|||
} |
|||
} |
|||
|
|||
.th-default { |
|||
.th-default { |
|||
text-align: inherit; |
|||
} |
|||
} |
|||
|
|||
.table-default .tbody-default .tr-default:hover { |
|||
.table-default .tbody-default .tr-default:hover { |
|||
color: #212529; |
|||
background-color: rgba(0,0,0,.075); |
|||
background-color: rgba(0, 0, 0, 0.075); |
|||
cursor: pointer; |
|||
} |
|||
|
|||
</style> |
|||
} |
|||
</style> |
|||
|
|||