Merge pull request #211 from Budibase/simplified-component-panel-tabs

Simplified component panel tabs
This commit is contained in:
Kevin Åberg Kultalahti 2020-04-17 11:47:02 +02:00 committed by GitHub
commit 594efca680
6 changed files with 290 additions and 254 deletions

View File

@ -146,6 +146,7 @@
.root {
height: 100%;
padding: 20px;
display: flex;
flex-direction: column;
}

View File

@ -1,264 +1,54 @@
<script>
import { splitName } from "./pagesParsing/splitRootComponentName.js"
import { store } from "builderStore"
import {
find,
sortBy,
groupBy,
values,
filter,
map,
uniqBy,
flatten,
} from "lodash/fp"
import { ImageIcon, InputIcon, LayoutIcon } from "components/common/Icons/"
import Select from "components/common/Select.svelte"
import Button from "components/common/PlusButton.svelte"
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
import {
getRecordNodes,
getIndexNodes,
getIndexSchema,
pipe,
} from "components/common/core"
import ComponentItem from "./ComponentItem.svelte"
import panelStructure from "./temporaryPanelStructure.json"
export let toggleTab
// This should be fetched from somewhere in the future, rather than be hardcoded.
import components from "./temporaryPanelStructure.js"
import Tab from "./ComponentTab/Tab.svelte"
let componentLibraries = []
let current_view = "text"
let selectedComponent = null
let selectedLib
let selectTemplateDialog
let templateInstances = []
let selectedTemplateInstance
$: templatesByComponent = groupBy(t => t.component)($store.templates)
$: hierarchy = $store.hierarchy
$: libraryModules = $store.libraries
$: standaloneTemplates = pipe(
templatesByComponent,
[
values,
flatten,
filter(t => !$store.components.some(c => c.name === t.component)),
map(t => ({ name: splitName(t.component).componentName, template: t })),
uniqBy(t => t.name),
]
)
const addRootComponent = (component, allComponents) => {
const { libName } = splitName(component.name)
let group = find(r => r.libName === libName)(allComponents)
if (!group) {
group = {
libName,
components: [],
}
allComponents.push(group)
}
group.components.push(component)
}
const onComponentChosen = component => {
if (component.template) {
onTemplateChosen(component.template)
} else {
store.addChildComponent(component.name)
toggleTab()
}
}
const onTemplateChosen = template => {
selectedComponent = null
const { componentName, libName } = splitName(template.name)
const templateOptions = {
records: getRecordNodes(hierarchy),
indexes: getIndexNodes(hierarchy),
helpers: {
indexSchema: getIndexSchema(hierarchy),
},
}
templateInstances = libraryModules[libName][componentName](templateOptions)
if (!templateInstances || templateInstances.length === 0) return
selectedTemplateInstance = templateInstances[0].name
selectTemplateDialog.show()
}
const onTemplateInstanceChosen = () => {
selectedComponent = null
const instance = templateInstances.find(
i => i.name === selectedTemplateInstance
)
store.addTemplatedComponent(instance.props)
toggleTab()
}
function generate_components_list(components) {
return ($store.currentFrontEndType === "page"
? $store.builtins.concat(components)
: components
).concat(standaloneTemplates)
}
$: {
const newComponentLibraries = []
for (let comp of sortBy(["name"])($store.components)) {
addRootComponent(comp, newComponentLibraries)
}
componentLibraries = newComponentLibraries
if (!selectedLib) selectedLib = newComponentLibraries[0].libName
}
$: componentLibrary = componentLibraries.find(l => l.libName === selectedLib)
$: componentItems =
panelStructure.categories[
panelStructure.categories.findIndex(i => i.name === "Basic")
].components
const categories = components.categories
let selectedCategory = categories[0]
</script>
<div class="root">
{#each componentItems as item}
<ComponentItem
name={item.component}
description={item.description}
icon={item.icon} />
{/each}
<ul class="tabs">
{#each categories as category}
<li
on:click={() => (selectedCategory = category)}
class:active={selectedCategory === category}>
{category.name}
</li>
{/each}
</ul>
<div class="panel">
<Tab components={selectedCategory.components} />
</div>
</div>
<ConfirmDialog
bind:this={selectTemplateDialog}
title="Choose Template"
onCancel={() => (selectedComponent = null)}
onOk={onTemplateInstanceChosen}>
{#each templateInstances.map(i => i.name) as instance}
<div class="uk-margin uk-grid-small uk-child-width-auto uk-grid">
<label>
<input
class="uk-radio"
type="radio"
bind:group={selectedTemplateInstance}
value={instance} />
<span class="template-instance-label">{instance}</span>
</label>
</div>
{/each}
</ConfirmDialog>
<style>
.root {
.tabs {
display: flex;
flex-direction: column;
}
.library-container {
padding: 0 0 10px 0;
flex: 1 1 auto;
min-height: 0px;
margin-top: 20px;
}
.component-container {
display: flex;
align-items: center;
}
.component {
position: relative;
padding: 0 15px;
cursor: pointer;
border: 1px solid #d8d8d8;
border-radius: 2px;
margin: 5px 0;
height: 40px;
box-sizing: border-box;
color: #000333;
display: flex;
align-items: center;
flex: 1;
margin-right: 5px;
}
.component:hover {
background-color: var(--lightslate);
}
.component > .name {
color: #000333;
display: inline-block;
font-size: 13px;
opacity: 0.8;
}
ul {
justify-content: center;
list-style: none;
display: flex;
padding: 0;
}
margin: 0 auto;
padding: 0 30px;
border-bottom: 1px solid #d8d8d8;
.preset-menu {
flex-direction: column;
position: absolute;
top: 25px;
left: 0;
right: 0;
z-index: 1;
background: #fafafa;
padding: 10px;
border-radius: 2px;
color: var(--secondary80);
}
.preset-menu > span {
font-size: 13px;
text-transform: uppercase;
margin-top: 5px;
}
.preset-menu li {
font-size: 14px;
margin-top: 13px;
}
.preset-menu li:hover {
font-weight: bold;
font-weight: 500;
letter-spacing: 0.14px;
}
li {
margin-right: 20px;
background: none;
border-radius: 5px;
}
/* li button {
width: 100%;
height: 100%;
background: none;
border: none;
border-radius: 5px;
padding: 13px;
outline: none;
color: #808192;
margin: 0 5px;
padding: 0 8px;
cursor: pointer;
} */
/* .selected {
color: var(--button-text);
background: var(--background-button) !important;
} */
.open {
color: rgba(0, 85, 255, 1);
}
.template-instance-label {
margin-left: 20px;
.panel {
padding: 20px;
}
.active {
border-bottom: solid 3px #0055ff;
color: #393c44;
}
</style>

View File

@ -0,0 +1,10 @@
<script>
import Item from "./Item.svelte"
export let components
console.log("Components: ", components)
</script>
{#each components as { icon, name, description }}
<Item {icon} {name} {description} />
{/each}

View File

@ -60,9 +60,7 @@
.switcher {
display: flex;
justify-content: space-between;
margin-bottom: 20px;
padding: 0 20px 20px;
border-bottom: 1px solid #d8d8d8;
margin: 20px;
}
.switcher > button {
@ -85,11 +83,4 @@
color: var(--secondary100);
font-weight: 600;
}
.panel {
flex: 1 1 auto;
height: 0px;
overflow-y: auto;
padding: 0 5px 40px 10px;
}
</style>

View File

@ -0,0 +1,244 @@
export default {
categories: [
{
name: 'Basic',
components: [
{
name: 'Container',
description: 'This component contains things within itself',
icon: 'ri-layout-row-fill',
commonProps: {},
type: []
},
{
name: 'Text',
description: 'This is a simple text component',
icon: 'ri-t-box-fill',
commonProps: {},
type: [
{
_component: '@budibase/standard-components/header',
name: 'Headline',
icon: 'headline',
props: {
type: {
type: 'options',
options: [
'h1',
'h2'
],
'default': 'h1'
}
}
},
{
_component: '@budibase/standard-components/text',
name: 'Paragraph',
icon: 'paragraph',
props: {}
}
]
},
{
name: 'Button',
description: 'A basic html button that is ready for styling',
icon: 'ri-radio-button-fill',
commonProps: {},
type: []
},
{
name: 'Icon',
description: 'A basic component for displaying icons',
icon: 'ri-sun-fill',
commonProps: {},
type: []
},
{
name: 'Avatar',
description: 'A basic component for rendering an avatar',
icon: 'ri-user-smile-fill',
commonProps: {},
type: []
},
{
name: 'Link',
description: 'A basic link component for internal and external links',
icon: 'ri-link',
commonProps: {},
type: []
}
]
},
{
name: 'Form',
components: [
{
name: 'Button',
description: 'A basic html button that is ready for styling',
icon: 'ri-radio-button-fill',
commonProps: {},
type: []
},
{
name: 'Icon',
description: 'A basic component for displaying icons',
icon: 'ri-sun-fill',
commonProps: {},
type: []
},
{
name: 'Avatar',
description: 'A basic component for rendering an avatar',
icon: 'ri-user-smile-fill',
commonProps: {},
type: []
},
{
name: 'Link',
description: 'A basic link component for internal and external links',
icon: 'ri-link',
commonProps: {},
type: []
}
]
},
{
name: 'Blocks',
components: [
{
name: 'Container',
description: 'This component contains things within itself',
icon: 'ri-layout-row-fill',
commonProps: {},
type: []
},
{
name: 'Text',
description: 'This is a simple text component',
icon: 'ri-t-box-fill',
commonProps: {},
type: [
{
_component: '@budibase/standard-components/header',
name: 'Headline',
icon: 'headline',
props: {
type: {
type: 'options',
options: [
'h1',
'h2'
],
'default': 'h1'
}
}
},
{
_component: '@budibase/standard-components/text',
name: 'Paragraph',
icon: 'paragraph',
props: {}
}
]
},
{
name: 'Button',
description: 'A basic html button that is ready for styling',
icon: 'ri-radio-button-fill',
commonProps: {},
type: []
},
{
name: 'Icon',
description: 'A basic component for displaying icons',
icon: 'ri-sun-fill',
commonProps: {},
type: []
},
{
name: 'Avatar',
description: 'A basic component for rendering an avatar',
icon: 'ri-user-smile-fill',
commonProps: {},
type: []
},
{
name: 'Link',
description: 'A basic link component for internal and external links',
icon: 'ri-link',
commonProps: {},
type: []
}
]
},
{
name: 'Data',
components: [
{
name: 'Container',
description: 'This component contains things within itself',
icon: 'ri-layout-row-fill',
commonProps: {},
type: []
},
{
name: 'Text',
description: 'This is a simple text component',
icon: 'ri-t-box-fill',
commonProps: {},
type: [
{
_component: '@budibase/standard-components/header',
name: 'Headline',
icon: 'headline',
props: {
type: {
type: 'options',
options: [
'h1',
'h2'
],
'default': 'h1'
}
}
},
{
_component: '@budibase/standard-components/text',
name: 'Paragraph',
icon: 'paragraph',
props: {}
}
]
},
{
name: 'Button',
description: 'A basic html button that is ready for styling',
icon: 'ri-radio-button-fill',
commonProps: {},
type: []
},
{
name: 'Icon',
description: 'A basic component for displaying icons',
icon: 'ri-sun-fill',
commonProps: {},
type: []
},
{
name: 'Avatar',
description: 'A basic component for rendering an avatar',
icon: 'ri-user-smile-fill',
commonProps: {},
type: []
},
{
name: 'Link',
description: 'A basic link component for internal and external links',
icon: 'ri-link',
commonProps: {},
type: []
}
]
},
]
}