Edit component pane improvements
This commit is contained in:
parent
3bcef7f668
commit
0d7d9f471e
|
@ -26,6 +26,10 @@ import {writable} from "svelte/store";
|
||||||
import { defaultPagesObject } from "../userInterface/pagesParsing/defaultPagesObject"
|
import { defaultPagesObject } from "../userInterface/pagesParsing/defaultPagesObject"
|
||||||
import api from "./api";
|
import api from "./api";
|
||||||
import { isRootComponent } from "../userInterface/pagesParsing/searchComponents";
|
import { isRootComponent } from "../userInterface/pagesParsing/searchComponents";
|
||||||
|
import {
|
||||||
|
getComponentInfo,
|
||||||
|
getNewComponentInfo
|
||||||
|
} from "../userInterface/pagesParsing/createProps";
|
||||||
|
|
||||||
export const getStore = () => {
|
export const getStore = () => {
|
||||||
|
|
||||||
|
@ -40,6 +44,8 @@ export const getStore = () => {
|
||||||
unauthenticatedUi:{},
|
unauthenticatedUi:{},
|
||||||
allComponents:[],
|
allComponents:[],
|
||||||
currentFrontEndItem:null,
|
currentFrontEndItem:null,
|
||||||
|
currentComponentInfo:null,
|
||||||
|
currentComponentIsNew:false,
|
||||||
currentNodeIsNew: false,
|
currentNodeIsNew: false,
|
||||||
errors: [],
|
errors: [],
|
||||||
activeNav: "database",
|
activeNav: "database",
|
||||||
|
@ -75,6 +81,7 @@ export const getStore = () => {
|
||||||
store.deleteDerivedComponent = deleteDerivedComponent(store);
|
store.deleteDerivedComponent = deleteDerivedComponent(store);
|
||||||
store.setCurrentComponent = setCurrentComponent(store);
|
store.setCurrentComponent = setCurrentComponent(store);
|
||||||
store.setCurrentPage = setCurrentPage(store);
|
store.setCurrentPage = setCurrentPage(store);
|
||||||
|
store.createDerivedComponent = createDerivedComponent(store);
|
||||||
return store;
|
return store;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -384,6 +391,18 @@ const saveDerivedComponent = store => (derivedComponent) => {
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const createDerivedComponent = store => (componentName) => {
|
||||||
|
store.update(s => {
|
||||||
|
const newComponentInfo = getNewComponentInfo(
|
||||||
|
s.allComponents, componentName);
|
||||||
|
|
||||||
|
s.currentFrontEndItem = newComponentInfo.component;
|
||||||
|
s.currentComponentInfo = newComponentInfo;
|
||||||
|
s.currentComponentIsNew = true;
|
||||||
|
return s;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const deleteDerivedComponent = store => name => {
|
const deleteDerivedComponent = store => name => {
|
||||||
store.update(s => {
|
store.update(s => {
|
||||||
|
|
||||||
|
@ -392,6 +411,9 @@ const deleteDerivedComponent = store => name => {
|
||||||
]);
|
]);
|
||||||
|
|
||||||
s.allComponents = allComponents;
|
s.allComponents = allComponents;
|
||||||
|
if(s.currentFrontEndItem.name === name) {
|
||||||
|
s.currentFrontEndItem = null;
|
||||||
|
}
|
||||||
|
|
||||||
api.delete(`/_builder/api/${s.appname}/derivedcomponent/${name}`);
|
api.delete(`/_builder/api/${s.appname}/derivedcomponent/${name}`);
|
||||||
|
|
||||||
|
@ -502,6 +524,8 @@ const setCurrentComponent = store => component => {
|
||||||
store.update(s => {
|
store.update(s => {
|
||||||
s.currentFrontEndItem = component;
|
s.currentFrontEndItem = component;
|
||||||
s.currentFrontEndIsComponent = true;
|
s.currentFrontEndIsComponent = true;
|
||||||
|
s.currentComponentIsNew = false;
|
||||||
|
s.currentComponentInfo = getComponentInfo(s.allComponents, component.name);
|
||||||
return s;
|
return s;
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,7 +40,7 @@ const addAttributes = (node, attributes) => {
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<button style="{style} color:{color}"
|
<button style="{style} color:{color} --hovercolor:{hoverColor}"
|
||||||
on:click
|
on:click
|
||||||
use:addAttributes={attributes}>
|
use:addAttributes={attributes}>
|
||||||
{@html getIcon(icon, size)}
|
{@html getIcon(icon, size)}
|
||||||
|
@ -53,6 +53,15 @@ button {
|
||||||
border-style: none;
|
border-style: none;
|
||||||
background-color: rgba(0,0,0,0);
|
background-color: rgba(0,0,0,0);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
outline:none;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
color: var(--hovercolor);
|
||||||
|
}
|
||||||
|
|
||||||
|
button:active {
|
||||||
|
outline:none;
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
|
@ -0,0 +1,136 @@
|
||||||
|
<script>
|
||||||
|
import {
|
||||||
|
last
|
||||||
|
} from "lodash/fp";
|
||||||
|
import IconButton from "../common/IconButton.svelte";
|
||||||
|
import ComponentSearch from "./ComponentSearch.svelte";
|
||||||
|
import Button from "../common/Button.svelte";
|
||||||
|
import ButtonGroup from "../common/ButtonGroup.svelte";
|
||||||
|
import UIkit from "uikit";
|
||||||
|
import {
|
||||||
|
getComponentInfo
|
||||||
|
} from "./pagesParsing/createProps";
|
||||||
|
import { store } from "../builderStore";
|
||||||
|
|
||||||
|
const emptyProps = () => ({_component:""})
|
||||||
|
|
||||||
|
export let props = emptyProps();
|
||||||
|
export let onValueChanged = () => {};
|
||||||
|
export let label = ""
|
||||||
|
export let disabled = false;
|
||||||
|
|
||||||
|
const CHOOSE_COMPONENT = "choose_component";
|
||||||
|
const CLEAR_COMPONENT = "clear_component";
|
||||||
|
|
||||||
|
let allComponents;
|
||||||
|
let modalElement;
|
||||||
|
let modalAction;
|
||||||
|
|
||||||
|
store.subscribe(s => {
|
||||||
|
allComponents = s.allComponents;
|
||||||
|
});
|
||||||
|
|
||||||
|
$: componentSelected = props._component.length > 0;
|
||||||
|
$: shortName = last(props._component.split("/"));
|
||||||
|
|
||||||
|
const chooseComponent = () => {
|
||||||
|
modalAction = CHOOSE_COMPONENT;
|
||||||
|
showDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
const clearComponent = () => {
|
||||||
|
modalAction = CLEAR_COMPONENT;
|
||||||
|
showDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
const onComponentChosen = (component) => {
|
||||||
|
const componentInfo = getComponentInfo(allComponents, component.name);
|
||||||
|
props = componentInfo.fullProps;
|
||||||
|
onValueChanged(props);
|
||||||
|
hideDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
const hideDialog = () => {
|
||||||
|
UIkit.modal(modalElement).hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
const showDialog = () => {
|
||||||
|
UIkit.modal(modalElement).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
const confirmClearComponent = () => {
|
||||||
|
props = emptyProps();
|
||||||
|
onValueChanged(emptyProps());
|
||||||
|
hideDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<label class="uk-form-label">{label}</label>
|
||||||
|
<div class="root uk-form-controls">
|
||||||
|
<div class:selectedname={componentSelected}>
|
||||||
|
{componentSelected ? shortName : "(none)"}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{#if !disabled && componentSelected}
|
||||||
|
<IconButton icon="edit" />
|
||||||
|
|
||||||
|
<IconButton icon="trash"
|
||||||
|
on:click={clearComponent} />
|
||||||
|
{:else if !disabled && !componentSelected}
|
||||||
|
<IconButton icon="plus"
|
||||||
|
on:click={chooseComponent} />
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div bind:this={modalElement} uk-modal>
|
||||||
|
<div class="uk-modal-dialog">
|
||||||
|
|
||||||
|
{#if modalAction === CHOOSE_COMPONENT}
|
||||||
|
<div class="uk-modal-body">
|
||||||
|
<ComponentSearch {onComponentChosen} />
|
||||||
|
</div>
|
||||||
|
{:else if modalAction === CLEAR_COMPONENT}
|
||||||
|
<div class="uk-modal-body">
|
||||||
|
Clear this component ?
|
||||||
|
</div>
|
||||||
|
<div class="uk-modal-footer">
|
||||||
|
<ButtonGroup>
|
||||||
|
<Button grouped
|
||||||
|
on:click={hideDialog}
|
||||||
|
color="secondary" >Cancel</Button>
|
||||||
|
<Button grouped
|
||||||
|
on:click={confirmClearComponent}>OK</Button>
|
||||||
|
</ButtonGroup>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
|
||||||
|
.root {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: [name] 1fr [actions] auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.root > div:nth-child(1) {
|
||||||
|
grid-column-start: name;
|
||||||
|
color: var(--secondary50);
|
||||||
|
}
|
||||||
|
|
||||||
|
.root > div:nth-child(2) {
|
||||||
|
grid-column-start: actions;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selectedname {
|
||||||
|
font-weight: bold;
|
||||||
|
color: var(--secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
|
@ -1,12 +1,17 @@
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
import { searchAllComponents } from "./pagesParsing/searchComponents";
|
import { searchAllComponents } from "./pagesParsing/searchComponents";
|
||||||
|
import { store } from "../builderStore";
|
||||||
|
|
||||||
export let allComponents = [];
|
|
||||||
export let onComponentChosen = () => {};
|
export let onComponentChosen = () => {};
|
||||||
|
|
||||||
|
let allComponents = [];
|
||||||
let phrase = "";
|
let phrase = "";
|
||||||
|
|
||||||
|
store.subscribe(s => {
|
||||||
|
allComponents = s.allComponents;
|
||||||
|
});
|
||||||
|
|
||||||
$: filteredComponents =
|
$: filteredComponents =
|
||||||
!phrase
|
!phrase
|
||||||
? []
|
? []
|
||||||
|
|
|
@ -0,0 +1,223 @@
|
||||||
|
<script>
|
||||||
|
|
||||||
|
import PropsView from "./PropsView.svelte";
|
||||||
|
import { store } from "../builderStore";
|
||||||
|
import { isRootComponent } from "./pagesParsing/searchComponents";
|
||||||
|
import IconButton from "../common/IconButton.svelte";
|
||||||
|
import Textbox from "../common/Textbox.svelte";
|
||||||
|
import UIkit from "uikit";
|
||||||
|
import { pipe } from "../common/core";
|
||||||
|
import {
|
||||||
|
getComponentInfo
|
||||||
|
} from "./pagesParsing/createProps";
|
||||||
|
import Button from "../common/Button.svelte";
|
||||||
|
import ButtonGroup from "../common/ButtonGroup.svelte";
|
||||||
|
|
||||||
|
import {
|
||||||
|
cloneDeep,
|
||||||
|
join,
|
||||||
|
split,
|
||||||
|
map,
|
||||||
|
keys,
|
||||||
|
isUndefined,
|
||||||
|
last
|
||||||
|
} from "lodash/fp";
|
||||||
|
import { assign } from "lodash";
|
||||||
|
|
||||||
|
let component;
|
||||||
|
let name = "";
|
||||||
|
let description = "";
|
||||||
|
let tagsString = "";
|
||||||
|
let nameInvalid = "";
|
||||||
|
let componentDetailsExpanded = false;
|
||||||
|
let componentInfo;
|
||||||
|
let modalElement
|
||||||
|
let propsValidationErrors = [];
|
||||||
|
$: shortName = last(name.split("/"));
|
||||||
|
|
||||||
|
store.subscribe(s => {
|
||||||
|
component = s.currentFrontEndItem;
|
||||||
|
if(!component) return;
|
||||||
|
name = component.name;
|
||||||
|
description = component.description;
|
||||||
|
tagsString = join(", ")(component.tags);
|
||||||
|
componentInfo = s.currentComponentInfo;
|
||||||
|
componentDetailsExpanded = s.currentComponentIsNew;
|
||||||
|
});
|
||||||
|
|
||||||
|
const save = () => {
|
||||||
|
|
||||||
|
if(!validate()) return;
|
||||||
|
|
||||||
|
component.name = name;
|
||||||
|
component.description = description;
|
||||||
|
component.tags = pipe(tagsString, [
|
||||||
|
split(","),
|
||||||
|
map(s => s.trim())
|
||||||
|
]);
|
||||||
|
|
||||||
|
store.saveDerivedComponent(component);
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteComponent = () => {
|
||||||
|
showDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
const confirmDeleteComponent = () => {
|
||||||
|
store.deleteDerivedComponent(component.name);
|
||||||
|
hideDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
const onPropsValidate = result => {
|
||||||
|
propsValidationErrors = result;
|
||||||
|
}
|
||||||
|
|
||||||
|
const onPropsChanged = props => {
|
||||||
|
assign(component.props, props);
|
||||||
|
}
|
||||||
|
|
||||||
|
const validate = () => {
|
||||||
|
const fieldInvalid = (field, err) =>
|
||||||
|
errors[field] = err;
|
||||||
|
const fieldValid = field =>
|
||||||
|
errors[field] && delete errors[field];
|
||||||
|
|
||||||
|
if(!name) nameInvalid = "component name i not supplied";
|
||||||
|
else nameInvalid = "";
|
||||||
|
|
||||||
|
return (!nameInvalid && propsValidationErrors.length === 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
const hideDialog = () => {
|
||||||
|
UIkit.modal(modalElement).hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
const showDialog = () => {
|
||||||
|
UIkit.modal(modalElement).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="root">
|
||||||
|
|
||||||
|
<div class="title">
|
||||||
|
<div>{shortName}</div>
|
||||||
|
<div>
|
||||||
|
<IconButton icon="save"
|
||||||
|
on:click={save}
|
||||||
|
color="var(--primary100)"
|
||||||
|
hoverColor="red"/>
|
||||||
|
<IconButton icon="trash"
|
||||||
|
on:click={deleteComponent}
|
||||||
|
color="var(--primary100)"
|
||||||
|
hoverColor="red"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="body">
|
||||||
|
|
||||||
|
<div class="section-header" on:click={() => componentDetailsExpanded = !componentDetailsExpanded}>
|
||||||
|
<span style="margin-right: 7px">Component Details</span>
|
||||||
|
<IconButton icon={componentDetailsExpanded ? "chevron-down" : "chevron-right"}/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{#if componentDetailsExpanded}
|
||||||
|
<div>
|
||||||
|
<Textbox label="Name"
|
||||||
|
infoText="use forward slash to store in subfolders"
|
||||||
|
bind:text={name}
|
||||||
|
hasError={!!nameInvalid}/>
|
||||||
|
<div class="info-text"></div>
|
||||||
|
<Textbox label="Description"
|
||||||
|
bind:text={description}/>
|
||||||
|
<Textbox label="Tags"
|
||||||
|
infoText="comma separated"
|
||||||
|
bind:text={tagsString}/>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<p class="section-header"><span>Properties</span></p>
|
||||||
|
|
||||||
|
|
||||||
|
<PropsView onValidate={onPropsValidate}
|
||||||
|
{componentInfo}
|
||||||
|
{onPropsChanged}/>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div bind:this={modalElement} uk-modal>
|
||||||
|
<div class="uk-modal-dialog">
|
||||||
|
|
||||||
|
<div class="uk-modal-header">
|
||||||
|
Delete {component.name} ?
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="uk-modal-body">
|
||||||
|
Are you sure you want to delete this component ?
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="uk-modal-footer">
|
||||||
|
<ButtonGroup>
|
||||||
|
<Button grouped
|
||||||
|
on:click={confirmDeleteComponent}>
|
||||||
|
OK
|
||||||
|
</Button>
|
||||||
|
<Button grouped
|
||||||
|
on:click={hideDialog}
|
||||||
|
color="secondary" >
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
</ButtonGroup>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
|
||||||
|
.root {
|
||||||
|
height: 100%;
|
||||||
|
border-style: solid;
|
||||||
|
border-color: var(--lightslate);
|
||||||
|
border-width: 0px 0px 0px 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.body {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
background-color: white;
|
||||||
|
padding: 3px;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: [name] 1fr [actions] auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title > div:nth-child(1) {
|
||||||
|
grid-column-start: name;
|
||||||
|
color: var(--secondary100);
|
||||||
|
}
|
||||||
|
|
||||||
|
.title > div:nth-child(2) {
|
||||||
|
grid-column-start: actions;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-header {
|
||||||
|
font-style: italic;
|
||||||
|
color: var(--slate);
|
||||||
|
border-style: solid;
|
||||||
|
border-color: var(--lightslate);
|
||||||
|
border-width: 0px 0px 1px 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-header {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
|
@ -23,66 +23,26 @@ import {
|
||||||
} from "lodash/fp";
|
} from "lodash/fp";
|
||||||
import { assign } from "lodash";
|
import { assign } from "lodash";
|
||||||
|
|
||||||
let component;
|
|
||||||
|
|
||||||
let modalElement;
|
let modalElement;
|
||||||
let errors = {};
|
let allComponents;
|
||||||
let componentInfo;
|
|
||||||
|
|
||||||
let name = "";
|
store.subscribe(s => {
|
||||||
let description = "";
|
allComponents = s.allComponents;
|
||||||
let tagsString = "";
|
})
|
||||||
let propsValidationErrors = [];
|
|
||||||
let inheritedProps;
|
|
||||||
let nameInvalid = "";
|
|
||||||
let propsDefinition;
|
|
||||||
|
|
||||||
const onBasedOnChosen = (allComponents) => (c) => {
|
export const close = () => {
|
||||||
componentInfo = getNewComponentInfo(allComponents, c.name);
|
|
||||||
tagsString = join(", ")(componentInfo.component.tags);
|
|
||||||
inheritedProps = componentInfo.inheritedProps;
|
|
||||||
propsDefinition = componentInfo.propsDefinition;
|
|
||||||
component = componentInfo.component;
|
|
||||||
}
|
|
||||||
|
|
||||||
const createComponent = () => {
|
|
||||||
|
|
||||||
if(!validate()) return;
|
|
||||||
|
|
||||||
component.name = name;
|
|
||||||
component.description = description;
|
|
||||||
component.tags = pipe(tagsString, [
|
|
||||||
split(","),
|
|
||||||
map(s => s.trim())
|
|
||||||
]);
|
|
||||||
|
|
||||||
store.saveDerivedComponent(component);
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
|
|
||||||
const close = () => {
|
|
||||||
component = null;
|
|
||||||
componentInfo = null;
|
|
||||||
UIkit.modal(modalElement).hide();
|
UIkit.modal(modalElement).hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
const onPropsValidate = result => {
|
export const show = () => {
|
||||||
propsValidationErrors = result;
|
UIkit.modal(modalElement).show();
|
||||||
}
|
}
|
||||||
|
|
||||||
const onPropsChanged = props => {
|
const onComponentChosen = (c) => {
|
||||||
assign(component.props, props);
|
store.createDerivedComponent(c.name);
|
||||||
}
|
close();
|
||||||
|
|
||||||
const validate = () => {
|
|
||||||
const fieldInvalid = (field, err) =>
|
|
||||||
errors[field] = err;
|
|
||||||
const fieldValid = field =>
|
|
||||||
errors[field] && delete errors[field];
|
|
||||||
|
|
||||||
if(!name) nameInvalid = "component name i not supplied";
|
|
||||||
else nameInvalid = "";
|
|
||||||
|
|
||||||
return (!nameInvalid && propsValidationErrors.length === 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
@ -95,49 +55,8 @@ const validate = () => {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="uk-modal-body">
|
<div class="uk-modal-body">
|
||||||
<form class="uk-form-horizontal">
|
<ComponentSearch onComponentChosen={onComponentChosen} />
|
||||||
{#if componentInfo}
|
|
||||||
|
|
||||||
<Textbox label="Name"
|
|
||||||
infoText="use forward slash to store in subfolders"
|
|
||||||
bind:text={name}
|
|
||||||
hasError={!!nameInvalid}/>
|
|
||||||
<div class="info-text"></div>
|
|
||||||
<Textbox label="Description"
|
|
||||||
bind:text={description}/>
|
|
||||||
<Textbox label="Tags"
|
|
||||||
infoText="comma separated"
|
|
||||||
bind:text={tagsString}/>
|
|
||||||
<p class="uk-heading-line props-header"><span>Properties</span></p>
|
|
||||||
<PropsView allComponents={$store.allComponents}
|
|
||||||
onValidate={onPropsValidate}
|
|
||||||
showTitle={false}
|
|
||||||
{componentInfo}
|
|
||||||
{onPropsChanged} />
|
|
||||||
|
|
||||||
|
|
||||||
{:else}
|
|
||||||
|
|
||||||
|
|
||||||
<ComponentSearch allComponents={$store.allComponents}
|
|
||||||
onComponentChosen={onBasedOnChosen($store.allComponents)} />
|
|
||||||
|
|
||||||
|
|
||||||
{/if}
|
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
{#if component}
|
|
||||||
<div class="uk-modal-footer">
|
|
||||||
<ButtonGroup>
|
|
||||||
<Button grouped
|
|
||||||
on:click={close}
|
|
||||||
color="secondary" >Cancel</Button>
|
|
||||||
<Button grouped
|
|
||||||
on:click={createComponent}>Create Component</Button>
|
|
||||||
</ButtonGroup>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -146,8 +65,4 @@ h1 {
|
||||||
font-size:1.2em;
|
font-size:1.2em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.props-header {
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
</style>
|
|
@ -0,0 +1,54 @@
|
||||||
|
<script>
|
||||||
|
|
||||||
|
import Checkbox from "../common/Checkbox.svelte";
|
||||||
|
import Textbox from "../common/Textbox.svelte";
|
||||||
|
import Dropdown from "../common/Dropdown.svelte";
|
||||||
|
import ComponentPropSelector from "./ComponentPropSelector.svelte";
|
||||||
|
|
||||||
|
export let errors = [];
|
||||||
|
export let setProp = () => {};
|
||||||
|
export let fieldHasError =() => {};
|
||||||
|
export let propDef = {};
|
||||||
|
export let props = {};
|
||||||
|
export let disabled;
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="root">
|
||||||
|
|
||||||
|
|
||||||
|
{#if propDef.type === "bool"}
|
||||||
|
<Checkbox label={propDef.____name}
|
||||||
|
checked={props[propDef.____name]}
|
||||||
|
on:change={ev => setProp(propDef.____name, ev.target.checked)}
|
||||||
|
hasError={fieldHasError(propDef.____name)} />
|
||||||
|
{:else if propDef.type === "options"}
|
||||||
|
<Dropdown label={propDef.____name}
|
||||||
|
selected={props[propDef.____name]}
|
||||||
|
options={propDef.options}
|
||||||
|
on:change={ev => setProp(propDef.____name, ev.target.value)}
|
||||||
|
hasError={fieldHasError(propDef.____name)}/>
|
||||||
|
{:else if propDef.type === "component"}
|
||||||
|
<ComponentPropSelector label={propDef.____name}
|
||||||
|
props={props[propDef.____name]}
|
||||||
|
{disabled}
|
||||||
|
onValueChanged={props => setProp(propDef.____name, props)}/>
|
||||||
|
{:else}
|
||||||
|
<Textbox label={propDef.____name}
|
||||||
|
text={props[propDef.____name]}
|
||||||
|
on:change={ev => setProp(propDef.____name, ev.target.value)}
|
||||||
|
margin={false}
|
||||||
|
hasError={fieldHasError(propDef.____name)}
|
||||||
|
{disabled}/>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
|
||||||
|
.root {
|
||||||
|
margin-top: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
|
@ -6,58 +6,66 @@ import {
|
||||||
some,
|
some,
|
||||||
includes,
|
includes,
|
||||||
cloneDeep,
|
cloneDeep,
|
||||||
isEqual
|
isEqual,
|
||||||
|
sortBy,
|
||||||
|
filter
|
||||||
} from "lodash/fp";
|
} from "lodash/fp";
|
||||||
import { pipe } from "../common/core";
|
import { pipe } from "../common/core";
|
||||||
import { getComponentInfo } from "./pagesParsing/createProps";
|
import {
|
||||||
|
getComponentInfo ,
|
||||||
|
getInstanceProps
|
||||||
|
} from "./pagesParsing/createProps";
|
||||||
import { getExactComponent } from "./pagesParsing/searchComponents";
|
import { getExactComponent } from "./pagesParsing/searchComponents";
|
||||||
import Checkbox from "../common/Checkbox.svelte";
|
import Checkbox from "../common/Checkbox.svelte";
|
||||||
import Textbox from "../common/Textbox.svelte";
|
import Textbox from "../common/Textbox.svelte";
|
||||||
import Dropdown from "../common/Dropdown.svelte";
|
import Dropdown from "../common/Dropdown.svelte";
|
||||||
import { validateProps } from "./pagesParsing/validateProps";
|
import { validateProps } from "./pagesParsing/validateProps";
|
||||||
|
import ComponentPropSelector from "./ComponentPropSelector.svelte";
|
||||||
|
import PropControl from "./PropControl.svelte";
|
||||||
|
import IconButton from "../common/IconButton.svelte";
|
||||||
|
|
||||||
export let allComponents;
|
|
||||||
export let shouldValidate = true;
|
export let shouldValidate = true;
|
||||||
export let onValidate = () => {};
|
export let onValidate = () => {};
|
||||||
export let showTitle = true;
|
|
||||||
export let componentInfo;
|
export let componentInfo;
|
||||||
export let component;
|
export let instanceProps = null;
|
||||||
export let onPropsChanged = () => {};
|
export let onPropsChanged = () => {};
|
||||||
|
|
||||||
let errors = [];
|
let errors = [];
|
||||||
let fields = [];
|
|
||||||
let props = {};
|
let props = {};
|
||||||
let propsDefinitionArray = [];
|
let propsDefinitions = [];
|
||||||
|
let inheritedPropsDefinitions = [];
|
||||||
$: {
|
let inheritedExpanded = false;
|
||||||
if(componentInfo || component)
|
|
||||||
{
|
|
||||||
if(!componentInfo || (component &&
|
|
||||||
component.name !== componentInfo.component.name)) {
|
|
||||||
componentInfo = getComponentInfo(allComponents, component.name);
|
|
||||||
}
|
|
||||||
|
|
||||||
props = cloneDeep(componentInfo.fullProps);
|
|
||||||
|
|
||||||
propsDefinitionArray = pipe(componentInfo.propsDefinition, [
|
|
||||||
keys,
|
|
||||||
map(k => ({...componentInfo.propsDefinition[k], ____name:k}))
|
|
||||||
]);
|
|
||||||
|
|
||||||
fields = pipe(componentInfo.propsDefinition,[
|
|
||||||
keys,
|
|
||||||
map(k => componentInfo.propsDefinition[k])
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const isPropInherited = name =>
|
const isPropInherited = name =>
|
||||||
includes(name)(componentInfo.inheritedProps);
|
includes(name)(componentInfo.inheritedProps);
|
||||||
|
|
||||||
let setProp = (name) => (ev, targetValue="value") => {
|
$: {
|
||||||
const newProps = cloneDeep(props);
|
if(componentInfo)
|
||||||
newProps[name] = ev.target[targetValue];
|
{
|
||||||
|
props = instanceProps
|
||||||
|
? getInstanceProps(componentInfo, instanceProps)
|
||||||
|
: cloneDeep(componentInfo.fullProps);
|
||||||
|
|
||||||
|
propsDefinitions = pipe(componentInfo.propsDefinition, [
|
||||||
|
keys,
|
||||||
|
filter(k => !isPropInherited(k)),
|
||||||
|
map(k => ({...componentInfo.propsDefinition[k], ____name:k})),
|
||||||
|
sortBy("____name")
|
||||||
|
]);
|
||||||
|
|
||||||
|
inheritedPropsDefinitions = pipe(componentInfo.propsDefinition, [
|
||||||
|
keys,
|
||||||
|
filter(k => isPropInherited(k)),
|
||||||
|
map(k => ({...componentInfo.propsDefinition[k], ____name:k})),
|
||||||
|
sortBy("____name")
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
let setProp = (name, value) => {
|
||||||
|
const newProps = cloneDeep(props);
|
||||||
|
newProps[name] = value;
|
||||||
|
|
||||||
const finalProps = {};
|
const finalProps = {};
|
||||||
|
|
||||||
|
@ -82,37 +90,48 @@ const validate = (finalProps) => {
|
||||||
const fieldHasError = (propName) =>
|
const fieldHasError = (propName) =>
|
||||||
some(e => e.propName === propName)(errors);
|
some(e => e.propName === propName)(errors);
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="root">
|
<div class="root">
|
||||||
|
|
||||||
{#if showTitle=true}
|
<form class="uk-form-stacked">
|
||||||
<div class="title">{componentInfo.component.name}</div>
|
{#each propsDefinitions as propDef}
|
||||||
<div class="component-description">{componentInfo.component.description || ""}</div>
|
|
||||||
|
<PropControl {errors}
|
||||||
|
{setProp}
|
||||||
|
{fieldHasError}
|
||||||
|
{propDef}
|
||||||
|
{props}
|
||||||
|
disabled={false} />
|
||||||
|
|
||||||
|
{/each}
|
||||||
|
|
||||||
|
{#if inheritedPropsDefinitions.length > 0}
|
||||||
|
<div class="inherited-title">
|
||||||
|
<div>Inherited</div>
|
||||||
|
<div>
|
||||||
|
<IconButton icon={inheritedExpanded ? "chevron-down" : "chevron-right"}
|
||||||
|
on:click={() => inheritedExpanded = !inheritedExpanded}/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
{#each propsDefinitionArray as propDef}
|
|
||||||
<form class="uk-form-stacked prop-row ">
|
{#if inheritedExpanded}
|
||||||
{#if propDef.type === "bool"}
|
{#each inheritedPropsDefinitions as propDef}
|
||||||
<Checkbox label={propDef.____name}
|
|
||||||
checked={props[propDef.____name]}
|
<PropControl {errors}
|
||||||
on:change={setProp(propDef.____name, "checked")}
|
{setProp}
|
||||||
hasError={fieldHasError(propDef.____name)} />
|
{fieldHasError}
|
||||||
{:else if propDef.type === "options"}
|
{propDef}
|
||||||
<Dropdown label={propDef.____name}
|
{props}
|
||||||
selected={props[propDef.____name]}
|
disabled={true} />
|
||||||
options={propDef.options}
|
|
||||||
on:change={setProp(propDef.____name)}
|
{/each}
|
||||||
hasError={fieldHasError(propDef.____name)}/>
|
|
||||||
{:else}
|
|
||||||
<Textbox label={propDef.____name}
|
|
||||||
text={props[propDef.____name]}
|
|
||||||
on:change={setProp(propDef.____name)}
|
|
||||||
margin={false}
|
|
||||||
hasError={fieldHasError(propDef.____name)}
|
|
||||||
disabled={isPropInherited(propDef.____name)} />
|
|
||||||
{/if}
|
{/if}
|
||||||
</form>
|
</form>
|
||||||
{/each}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -124,19 +143,24 @@ const fieldHasError = (propName) =>
|
||||||
font-size:10pt;
|
font-size:10pt;
|
||||||
}
|
}
|
||||||
|
|
||||||
.title {
|
.inherited-title {
|
||||||
font-size: 1.2em;
|
margin-top: 40px;
|
||||||
font-weight: bold;
|
display: grid;
|
||||||
|
grid-template-columns: [name] 1fr [actions] auto;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 0px 0px 1px 0px;
|
||||||
|
border-color: var(--lightslate);
|
||||||
|
font-style: italic;
|
||||||
}
|
}
|
||||||
|
|
||||||
.prop-row {
|
.inherited-title > div:nth-child(1) {
|
||||||
padding: 7px 3px;
|
grid-column-start: name;
|
||||||
}
|
|
||||||
|
|
||||||
.component-description {
|
|
||||||
font-size: 0.9em;
|
|
||||||
color: var(--slate);
|
color: var(--slate);
|
||||||
margin-bottom: 10px;
|
}
|
||||||
|
|
||||||
|
.inherited-title > div:nth-child(2) {
|
||||||
|
grid-column-start: actions;
|
||||||
|
color: var(--secondary100);
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
import ComponentsHierarchy from "./ComponentsHierarchy.svelte";
|
import ComponentsHierarchy from "./ComponentsHierarchy.svelte";
|
||||||
import PagesList from "./PagesList.svelte"
|
import PagesList from "./PagesList.svelte"
|
||||||
import PropsView from "./PropsView.svelte";
|
import EditComponent from "./EditComponent.svelte";
|
||||||
import { store } from "../builderStore";
|
import { store } from "../builderStore";
|
||||||
import getIcon from "../common/icon";
|
import getIcon from "../common/icon";
|
||||||
import { isRootComponent } from "./pagesParsing/searchComponents";
|
import { isRootComponent } from "./pagesParsing/searchComponents";
|
||||||
|
@ -10,9 +10,9 @@ import IconButton from "../common/IconButton.svelte";
|
||||||
import Modal from "../common/Modal.svelte";
|
import Modal from "../common/Modal.svelte";
|
||||||
import NewComponent from "./NewComponent.svelte";
|
import NewComponent from "./NewComponent.svelte";
|
||||||
|
|
||||||
let isCreatingNewComponent = false;
|
let newComponentPicker;
|
||||||
const newComponent = () => {
|
const newComponent = () => {
|
||||||
isCreatingNewComponent = true;
|
newComponentPicker.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
@ -27,8 +27,7 @@ const newComponent = () => {
|
||||||
<span>COMPONENTS</span>
|
<span>COMPONENTS</span>
|
||||||
<div>
|
<div>
|
||||||
<IconButton icon="plus"
|
<IconButton icon="plus"
|
||||||
on:click={newComponent}
|
on:click={newComponent}/>
|
||||||
attributes={{"uk-toggle" : "target: #new-component-modal" }}/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="nav-items-container">
|
<div class="nav-items-container">
|
||||||
|
@ -58,15 +57,14 @@ const newComponent = () => {
|
||||||
|
|
||||||
<div class="properties-pane">
|
<div class="properties-pane">
|
||||||
{#if $store.currentFrontEndItem && !isRootComponent($store.currentFrontEndItem)}
|
{#if $store.currentFrontEndItem && !isRootComponent($store.currentFrontEndItem)}
|
||||||
<PropsView allComponents={$store.allComponents}
|
<EditComponent />
|
||||||
component={$store.currentFrontEndItem}/>
|
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<NewComponent bind:isCreatingNewComponent={isCreatingNewComponent}/>
|
<NewComponent bind:this={newComponentPicker}/>
|
||||||
|
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
@ -100,7 +98,6 @@ const newComponent = () => {
|
||||||
grid-column-start: properties;
|
grid-column-start: properties;
|
||||||
background-color: var(--primary10);
|
background-color: var(--primary10);
|
||||||
height: 100%;
|
height: 100%;
|
||||||
padding: 10px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.pages-list-container {
|
.pages-list-container {
|
||||||
|
|
|
@ -39,7 +39,7 @@ export const createPropDefinitionForDerived = (allComponents, componentName) =>
|
||||||
|
|
||||||
export const traverseForProps = getComponentInfo;
|
export const traverseForProps = getComponentInfo;
|
||||||
|
|
||||||
export const getFinalProps = (componentInfo, props) => {
|
export const getInstanceProps = (componentInfo, props) => {
|
||||||
const finalProps = cloneDeep(componentInfo.fullProps);
|
const finalProps = cloneDeep(componentInfo.fullProps);
|
||||||
|
|
||||||
for(let p in props) {
|
for(let p in props) {
|
||||||
|
@ -89,7 +89,7 @@ export const getComponentInfo = (allComponents, cname, stack=[], subComponentPro
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return ({
|
return ({
|
||||||
propsDefinition:component.props,
|
propsDefinition:expandPropsDefinition(component.props),
|
||||||
inheritedProps,
|
inheritedProps,
|
||||||
rootDefaultProps: p.props,
|
rootDefaultProps: p.props,
|
||||||
unsetProps,
|
unsetProps,
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import {
|
import {
|
||||||
getFinalProps,
|
getInstanceProps,
|
||||||
getComponentInfo
|
getComponentInfo
|
||||||
} from "../src/userInterface/pagesParsing/createProps";
|
} from "../src/userInterface/pagesParsing/createProps";
|
||||||
import {
|
import {
|
||||||
|
@ -34,8 +34,8 @@ describe("getComponentInfo", () => {
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("getFinalProps should set supplied props on top of default props", () => {
|
it("getInstanceProps should set supplied props on top of default props", () => {
|
||||||
const result = getFinalProps(
|
const result = getInstanceProps(
|
||||||
getComponentInfo(
|
getComponentInfo(
|
||||||
allComponents(),
|
allComponents(),
|
||||||
"budibase-components/TextBox"),
|
"budibase-components/TextBox"),
|
||||||
|
|
Loading…
Reference in New Issue