Properties panel structure complete

This commit is contained in:
Conor_Mack 2020-05-07 14:30:04 +01:00
parent 391237403b
commit 6d70cfd1c1
13 changed files with 305 additions and 182 deletions

View File

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

View File

@ -49,8 +49,6 @@
const onPropChanged = store.setComponentProp
const onStyleChanged = store.setComponentStyle
//May be able to remove some of the nested components in PropsView, PropsControl and StateBindingControl tree
function walkProps(component, action) {
action(component)
if (component.children) {
@ -98,45 +96,11 @@
</div>
<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 {
height: 100%;
display: flex;
flex-direction: column;
overflow-x: hidden;
}
.title > div:nth-child(1) {
@ -152,52 +116,4 @@
margin-top: 10px;
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>

View File

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

View File

@ -1,23 +1,62 @@
<script>
import PropertyGroup from "./PropertyGroup.svelte"
import FlatButtonGroup from "./FlatButtonGroup.svelte"
export let panelDefinition = {}
export let componentInstance = {}
export let componentDefinition = {}
export let onPropChanged = () => {}
let selectedCategory = "desktop"
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 =
!!panelDefinition.properties && Object.keys(panelDefinition.properties)
</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
name={groupName}
properties={getProperties(groupName)}
{onPropChanged}
{componentDefinition}
{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>
import { excludeProps } from "./propertyCategories.js"
import PropertyControl from "./PropertyControl.svelte"
export let name = ""
export let properties = {}
export let componentInstance = {}
export let componentDefinition = {}
export let onPropChanged = () => {}
let show = true
export let show = false
const propExistsOnComponentDef = prop => prop in componentDefinition.props
const capitalize = name => name[0].toUpperCase() + name.slice(1)
function onChange(v) {
!!v.target ? onPropChanged(v.target.value) : onPropChanged(v)
function onChange(key, v) {
!!v.target ? onPropChanged(key, v.target.value) : onPropChanged(key, v)
}
$: propertyDefinition = Object.entries(properties)
$: console.log("props group", properties)
$: icon = show ? "ri-arrow-down-s-fill" : "ri-arrow-right-s-fill"
</script>
<!-- () => (show = !show) -->
<div class="property-group-container" on:click={() => {}}>
<div class="property-group-name">
<div class="property-group-container">
<div class="property-group-name" on:click={() => (show = !show)}>
<div class="icon">
<i class={icon} />
</div>
@ -31,17 +30,14 @@
<div class="property-panel" class:show>
{#each propertyDefinition as [key, definition]}
<div class="property-control">
{#if propExistsOnComponentDef(key)}
<span>{definition.label || capitalize(key)}</span>
<svelte:component
this={definition.control}
<!-- {#if propExistsOnComponentDef(key)} -->
<PropertyControl
label={definition.label || capitalize(key)}
control={definition.control}
value={componentInstance[key]}
on:change={onChange}
{onChange}
{...excludeProps(definition, ['control'])} />
{/if}
</div>
onChange={value => onChange(key, value)}
props={{ ...excludeProps(definition, ['control']) }} />
<!-- {/if} -->
{/each}
</div>
</div>
@ -63,14 +59,19 @@
flex-flow: row nowrap;
}
.icon {
flex: 0 0 20px;
text-align: center;
}
.name {
flex: 1;
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 {
@ -78,11 +79,6 @@
overflow: hidden;
}
.property-control {
display: flex;
flex-flow: row nowrap;
}
.show {
overflow: 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 {
display: grid;
grid-template-columns: 275px 1fr 275px;
grid-template-columns: 275px 1fr 300px;
height: 100%;
width: 100%;
background: #fafafa;
@ -151,7 +151,7 @@
.components-pane {
grid-column: 3;
background-color: var(--white);
min-height: 0px;
height: 100vh;
overflow-y: scroll;
}

View File

@ -1,75 +1,102 @@
import ColorPicker from "../common/Colorpicker.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
*/
export const general = {
text: { control: Input }
text: { control: Input },
}
export const layout = {
flexDirection: { label: "Direction", control: "string" },
justifyContent: { label: "Justify", control: "string" },
alignItems: { label: "Align", control: "string" },
flexWrap: { label: "Wrap", control: "options" },
flexDirection: {
label: "Direction",
control: OptionSelect,
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 = {
padding: { control: "string" },
margin: { control: "string" },
padding: { control: Input },
margin: { control: Input },
}
export const size = {
width: { control: "string" },
height: { control: "string" },
minWidth: { label: "Min W", control: "string" },
minHeight: { label: "Min H", control: "string" },
maxWidth: { label: "Max W", control: "string" },
maxHeight: { label: "Max H", control: "string" },
overflow: { control: "string" }, //custom
width: { control: Input },
height: { control: Input },
minWidth: { label: "Min W", control: Input },
minHeight: { label: "Min H", control: Input },
maxWidth: { label: "Max W", control: Input },
maxHeight: { label: "Max H", control: Input },
overflow: { control: Input }, //custom
}
export const position = {
position: { control: "options" },
position: { control: Input },
}
export const typography = {
font: { control: "options" },
weight: { control: "options" },
size: { control: "string" },
lineHeight: { label: "Line H", control: "string" },
color: { control: "colour" },
align: { control: "string" }, //custom
transform: { control: "string" }, //custom
style: { control: "string" }, //custom
font: { control: Input },
weight: { control: Input },
size: { control: Input },
lineHeight: { label: "Line H", control: Input },
color: {
control: OptionSelect,
options: [{ label: "red" }, { label: "blue" }, { label: "green" }],
},
align: { control: Input }, //custom
transform: { control: Input }, //custom
style: { control: Input }, //custom
}
export const background = {
backgroundColor: { label: "Background Color", control: ColorPicker },
backgroundColor: { label: "Background Color", control: Input },
image: { control: Input }, //custom
}
export const border = {
radius: { control: "string" },
width: { control: "string" }, //custom
color: { control: "colour" },
style: { control: "options" },
radius: { control: Input },
width: { control: Input }, //custom
color: { control: Input },
style: { control: Input },
}
export const effects = {
opacity: "string",
rotate: "string",
shadow: "string",
opacity: { control: Input },
rotate: { control: Input },
shadow: { control: Input },
}
export const transitions = {
property: { control: "options" },
duration: { control: "string" },
ease: { control: "options" },
property: { control: Input },
duration: { control: Input },
ease: { control: Input },
}
export const all = {
general,
layout,
spacing,
size,
position,
typography,
background,
border,
effects,
transitions,
}
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 {
categories: [
@ -13,7 +13,7 @@ export default {
icon: 'ri-layout-row-fill',
commonProps: {},
children: [],
properties: { background },
properties: { ...all },
},
{
name: 'Text',
@ -29,7 +29,7 @@ export default {
properties: {
general,
layout,
background,
typography,
},
},
{