Properties panel structure complete

This commit is contained in:
Conor_Mack 2020-05-07 14:30:04 +01:00
parent b52b9fdd2f
commit 8d8caa9ff5
13 changed files with 305 additions and 182 deletions

View File

@ -1,8 +1,11 @@
<script> <script>
export let value = "" export let value = ""
export let width = ""
let style = { width }
</script> </script>
<input type="text" on:change bind:value /> <input type="text" style={`width: ${width};`} on:change bind:value />
<style> <style>
input { input {

View File

@ -49,8 +49,6 @@
const onPropChanged = store.setComponentProp const onPropChanged = store.setComponentProp
const onStyleChanged = store.setComponentStyle const onStyleChanged = store.setComponentStyle
//May be able to remove some of the nested components in PropsView, PropsControl and StateBindingControl tree
function walkProps(component, action) { function walkProps(component, action) {
action(component) action(component)
if (component.children) { if (component.children) {
@ -98,45 +96,11 @@
</div> </div>
<style> <style>
.detail-prop {
height: 40px;
margin-bottom: 15px;
display: grid;
grid-template-rows: 1fr;
grid-template-columns: 70px 1fr;
grid-gap: 10px;
}
.detail-prop label {
word-wrap: break-word;
font-size: 13px;
font-weight: 700;
color: #163057;
opacity: 0.6;
padding-top: 13px;
margin-bottom: 0;
}
input {
height: 30px;
padding-left: 8px;
padding-right: 8px;
border: 1px solid #dbdbdb;
border-radius: 2px;
opacity: 0.5;
}
input:focus {
outline: 0;
background-color: #fff;
color: #666;
border-color: #1e87f0;
}
.root { .root {
height: 100%; height: 100%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
overflow-x: hidden;
} }
.title > div:nth-child(1) { .title > div:nth-child(1) {
@ -152,52 +116,4 @@
margin-top: 10px; margin-top: 10px;
flex: 1 1 auto; flex: 1 1 auto;
} }
ul {
list-style: none;
display: flex;
justify-content: space-between;
padding: 0;
}
li {
background: none;
border-radius: 3px;
width: 48px;
height: 48px;
}
li button {
width: 48px;
height: 48px;
background: none;
border: none;
border-radius: 3px;
padding: 7px;
outline: none;
cursor: pointer;
position: relative;
}
li:nth-last-child(1) {
margin-right: 0px;
background: none;
border-radius: 3px;
width: 48px;
height: 48px;
}
.selected {
color: var(--button-text);
background: #f9f9f9 !important;
width: 48px;
height: 48px;
}
.button-indicator {
position: absolute;
top: 8px;
right: 10px;
color: var(--button-text);
}
</style> </style>

View File

@ -24,13 +24,13 @@
<button <button
class:selected={selected === COMPONENT_SELECTION_TAB} class:selected={selected === COMPONENT_SELECTION_TAB}
on:click={() => selectTab(COMPONENT_SELECTION_TAB)}> on:click={() => selectTab(COMPONENT_SELECTION_TAB)}>
Components Add
</button> </button>
<button <button
class:selected={selected === PROPERTIES_TAB} class:selected={selected === PROPERTIES_TAB}
on:click={() => selectTab(PROPERTIES_TAB)}> on:click={() => selectTab(PROPERTIES_TAB)}>
Properties Edit
</button> </button>
</div> </div>
@ -55,6 +55,7 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
padding: 20px 0; padding: 20px 0;
border-left: solid 1px #e8e8ef;
} }
.switcher { .switcher {

View File

@ -1,23 +1,62 @@
<script> <script>
import PropertyGroup from "./PropertyGroup.svelte" import PropertyGroup from "./PropertyGroup.svelte"
import FlatButtonGroup from "./FlatButtonGroup.svelte"
export let panelDefinition = {} export let panelDefinition = {}
export let componentInstance = {} export let componentInstance = {}
export let componentDefinition = {} export let componentDefinition = {}
export let onPropChanged = () => {} export let onPropChanged = () => {}
let selectedCategory = "desktop"
const getProperties = name => panelDefinition.properties[name] const getProperties = name => panelDefinition.properties[name]
$: console.log("PDEF", panelDefinition) function onChange(category) {
selectedCategory = category
}
const buttonProps = [
{ value: "desktop", text: "Desktop" },
{ value: "mobile", text: "Mobile" },
{ value: "hover", text: "Hover" },
{ value: "active", text: "Active" },
{ value: "selected", text: "Selected" },
]
$: propertyGroupNames = $: propertyGroupNames =
!!panelDefinition.properties && Object.keys(panelDefinition.properties) !!panelDefinition.properties && Object.keys(panelDefinition.properties)
</script> </script>
{#each propertyGroupNames as groupName} <div class="design-view-container">
<div class="design-view-state-categories">
<FlatButtonGroup value={selectedCategory} {buttonProps} {onChange} />
</div>
<div class="design-view-property-groups">
{#each propertyGroupNames as groupName}
<PropertyGroup <PropertyGroup
name={groupName} name={groupName}
properties={getProperties(groupName)} properties={getProperties(groupName)}
{onPropChanged} {onPropChanged}
{componentDefinition} {componentDefinition}
{componentInstance} /> {componentInstance} />
{/each} {/each}
</div>
</div>
<style>
.design-view-container {
display: flex;
flex-direction: column;
width: 100%;
}
.design-view-state-categories {
flex: 0 0 50px;
}
.design-view-property-groups {
flex: 1;
}
</style>

View File

@ -0,0 +1,38 @@
<script>
export let value = ""
export let text = ""
export let icon = ""
export let onClick = value => {}
export let selected = false
$: useIcon = !!icon
</script>
<div class="flatbutton" class:selected on:click={() => onClick(value || text)}>
{#if useIcon}
<i class={icon} />
{:else}
<span>{text}</span>
{/if}
</div>
<style>
.flatbutton {
cursor: pointer;
padding: 5px;
text-align: center;
background: #ffffff;
color: #808192;
border-radius: 4px;
font-family: Roboto;
font-size: 11px;
font-weight: 500;
letter-spacing: 0.11px;
transition: background 0.5s, color 0.5s ease;
}
.selected {
background: #808192;
color: #ffffff;
}
</style>

View File

@ -0,0 +1,54 @@
<script>
import { onMount } from "svelte"
import FlatButton from "./FlatButton.svelte"
export let buttonProps = []
export let isMultiSelect = false
export let value = []
export let initialValue = ""
export let onChange = selected => {}
onMount(() => {
if (!value && !!initialValue) {
value = initialValue
}
})
function onButtonClicked(v) {
let val
if (isMultiSelect) {
if (value.includes(v)) {
let idx = value.findIndex(i => i === v)
val = [...value].splice(idx, 1)
} else {
val = [...value, v]
}
} else {
val = v
}
onChange(val)
}
</script>
<div class="flatbutton-group">
{#each buttonProps as props}
<div class="button-container">
<FlatButton
selected={value.includes(props.value)}
onClick={onButtonClicked}
{...props} />
</div>
{/each}
</div>
<style>
.flatbutton-group {
display: flex;
flex-flow: row nowrap;
}
.button-container {
flex: 1;
margin: 5px;
}
</style>

View File

@ -0,0 +1,22 @@
<script>
import { onMount } from "svelte"
export let value = ""
export let onChange = value => {}
export let options = []
export let initialValue = ""
onMount(() => {
if (!value && !!initialValue) {
value = initialValue
}
})
</script>
<select
class="uk-select uk-form-small"
{value}
on:change={ev => onChange(ev.target.value)}>
{#each options as { value, label }}
<option value={value || label}>{label}</option>
{/each}
</select>

View File

@ -0,0 +1,41 @@
<script>
export let label = ""
export let control = null
export let value = ""
export let props = {}
export let onChange = () => {}
</script>
<div class="property-control">
<div class="label">{label}</div>
<div class="control">
<svelte:component
this={control}
{value}
on:change={onChange}
{onChange}
{...props} />
</div>
</div>
<style>
.property-control {
display: flex;
flex-flow: row nowrap;
margin: 8px 0px;
}
.label {
flex: 0 0 50px;
padding: 0px 5px;
font-size: 12px;
font-weight: 500;
letter-spacing: 0.12px;
text-align: left;
}
.control {
flex: 1;
padding-left: 5px;
}
</style>

View File

@ -1,28 +1,27 @@
<script> <script>
import { excludeProps } from "./propertyCategories.js" import { excludeProps } from "./propertyCategories.js"
import PropertyControl from "./PropertyControl.svelte"
export let name = "" export let name = ""
export let properties = {} export let properties = {}
export let componentInstance = {} export let componentInstance = {}
export let componentDefinition = {} export let componentDefinition = {}
export let onPropChanged = () => {} export let onPropChanged = () => {}
let show = true export let show = false
const propExistsOnComponentDef = prop => prop in componentDefinition.props const propExistsOnComponentDef = prop => prop in componentDefinition.props
const capitalize = name => name[0].toUpperCase() + name.slice(1) const capitalize = name => name[0].toUpperCase() + name.slice(1)
function onChange(v) { function onChange(key, v) {
!!v.target ? onPropChanged(v.target.value) : onPropChanged(v) !!v.target ? onPropChanged(key, v.target.value) : onPropChanged(key, v)
} }
$: propertyDefinition = Object.entries(properties) $: propertyDefinition = Object.entries(properties)
$: console.log("props group", properties)
$: icon = show ? "ri-arrow-down-s-fill" : "ri-arrow-right-s-fill" $: icon = show ? "ri-arrow-down-s-fill" : "ri-arrow-right-s-fill"
</script> </script>
<!-- () => (show = !show) --> <div class="property-group-container">
<div class="property-group-container" on:click={() => {}}> <div class="property-group-name" on:click={() => (show = !show)}>
<div class="property-group-name">
<div class="icon"> <div class="icon">
<i class={icon} /> <i class={icon} />
</div> </div>
@ -31,17 +30,14 @@
<div class="property-panel" class:show> <div class="property-panel" class:show>
{#each propertyDefinition as [key, definition]} {#each propertyDefinition as [key, definition]}
<div class="property-control"> <!-- {#if propExistsOnComponentDef(key)} -->
{#if propExistsOnComponentDef(key)} <PropertyControl
<span>{definition.label || capitalize(key)}</span> label={definition.label || capitalize(key)}
<svelte:component control={definition.control}
this={definition.control}
value={componentInstance[key]} value={componentInstance[key]}
on:change={onChange} onChange={value => onChange(key, value)}
{onChange} props={{ ...excludeProps(definition, ['control']) }} />
{...excludeProps(definition, ['control'])} /> <!-- {/if} -->
{/if}
</div>
{/each} {/each}
</div> </div>
</div> </div>
@ -63,14 +59,19 @@
flex-flow: row nowrap; flex-flow: row nowrap;
} }
.icon {
flex: 0 0 20px;
text-align: center;
}
.name { .name {
flex: 1; flex: 1;
text-align: left; text-align: left;
padding-top: 2px;
font-size: 14px;
font-weight: 500;
letter-spacing: 0.14px;
color: #393c44;
}
.icon {
flex: 0 0 20px;
text-align: center;
} }
.property-panel { .property-panel {
@ -78,11 +79,6 @@
overflow: hidden; overflow: hidden;
} }
.property-control {
display: flex;
flex-flow: row nowrap;
}
.show { .show {
overflow: auto; overflow: auto;
height: auto; height: auto;

View File

@ -1,14 +0,0 @@
<script>
export let value = ""
export let onChange = value => {}
export let options = []
</script>
<select
class="uk-select uk-form-small"
{value}
on:change={ev => onChange(ev.target.value)}>
{#each options || [] as option}
<option value={option}>{option}</option>
{/each}
</select>

View File

@ -114,7 +114,7 @@
.root { .root {
display: grid; display: grid;
grid-template-columns: 275px 1fr 275px; grid-template-columns: 275px 1fr 300px;
height: 100%; height: 100%;
width: 100%; width: 100%;
background: #fafafa; background: #fafafa;
@ -151,7 +151,7 @@
.components-pane { .components-pane {
grid-column: 3; grid-column: 3;
background-color: var(--white); background-color: var(--white);
min-height: 0px; height: 100vh;
overflow-y: scroll; overflow-y: scroll;
} }

View File

@ -1,75 +1,102 @@
import ColorPicker from "../common/Colorpicker.svelte"
import Input from "../common/Input.svelte" import Input from "../common/Input.svelte"
import TempSelect from "./TempSelect.svelte" import OptionSelect from "./OptionSelect.svelte"
/* /*
TODO: all strings types are really inputs and could be typed as such
TODO: Options types need option items
TODO: Allow for default values for all properties TODO: Allow for default values for all properties
*/ */
export const general = { export const general = {
text: { control: Input } text: { control: Input },
} }
export const layout = { export const layout = {
flexDirection: { label: "Direction", control: "string" }, flexDirection: {
justifyContent: { label: "Justify", control: "string" }, label: "Direction",
alignItems: { label: "Align", control: "string" }, control: OptionSelect,
flexWrap: { label: "Wrap", control: "options" }, initialValue: "columnReverse",
options: [
{ label: "row" },
{ label: "row-reverse", value: "rowReverse" },
{ label: "column" },
{ label: "column-reverse", value: "columnReverse" },
],
},
justifyContent: { label: "Justify", control: Input },
alignItems: { label: "Align", control: Input },
flexWrap: {
label: "Wrap",
control: OptionSelect,
options: [{ label: "wrap" }, { label: "no wrap", value: "noWrap" }],
},
} }
export const spacing = { export const spacing = {
padding: { control: "string" }, padding: { control: Input },
margin: { control: "string" }, margin: { control: Input },
} }
export const size = { export const size = {
width: { control: "string" }, width: { control: Input },
height: { control: "string" }, height: { control: Input },
minWidth: { label: "Min W", control: "string" }, minWidth: { label: "Min W", control: Input },
minHeight: { label: "Min H", control: "string" }, minHeight: { label: "Min H", control: Input },
maxWidth: { label: "Max W", control: "string" }, maxWidth: { label: "Max W", control: Input },
maxHeight: { label: "Max H", control: "string" }, maxHeight: { label: "Max H", control: Input },
overflow: { control: "string" }, //custom overflow: { control: Input }, //custom
} }
export const position = { export const position = {
position: { control: "options" }, position: { control: Input },
} }
export const typography = { export const typography = {
font: { control: "options" }, font: { control: Input },
weight: { control: "options" }, weight: { control: Input },
size: { control: "string" }, size: { control: Input },
lineHeight: { label: "Line H", control: "string" }, lineHeight: { label: "Line H", control: Input },
color: { control: "colour" }, color: {
align: { control: "string" }, //custom control: OptionSelect,
transform: { control: "string" }, //custom options: [{ label: "red" }, { label: "blue" }, { label: "green" }],
style: { control: "string" }, //custom },
align: { control: Input }, //custom
transform: { control: Input }, //custom
style: { control: Input }, //custom
} }
export const background = { export const background = {
backgroundColor: { label: "Background Color", control: ColorPicker }, backgroundColor: { label: "Background Color", control: Input },
image: { control: Input }, //custom image: { control: Input }, //custom
} }
export const border = { export const border = {
radius: { control: "string" }, radius: { control: Input },
width: { control: "string" }, //custom width: { control: Input }, //custom
color: { control: "colour" }, color: { control: Input },
style: { control: "options" }, style: { control: Input },
} }
export const effects = { export const effects = {
opacity: "string", opacity: { control: Input },
rotate: "string", rotate: { control: Input },
shadow: "string", shadow: { control: Input },
} }
export const transitions = { export const transitions = {
property: { control: "options" }, property: { control: Input },
duration: { control: "string" }, duration: { control: Input },
ease: { control: "options" }, ease: { control: Input },
}
export const all = {
general,
layout,
spacing,
size,
position,
typography,
background,
border,
effects,
transitions,
} }
export function excludeProps(props, propsToExclude) { export function excludeProps(props, propsToExclude) {

View File

@ -1,4 +1,4 @@
import { general, layout, background } from "./propertyCategories.js" import { general, layout, typography, background, all } from "./propertyCategories.js"
export default { export default {
categories: [ categories: [
@ -13,7 +13,7 @@ export default {
icon: 'ri-layout-row-fill', icon: 'ri-layout-row-fill',
commonProps: {}, commonProps: {},
children: [], children: [],
properties: { background }, properties: { ...all },
}, },
{ {
name: 'Text', name: 'Text',
@ -29,7 +29,7 @@ export default {
properties: { properties: {
general, general,
layout, layout,
background, typography,
}, },
}, },
{ {