Merge branch 'simplified-component-panel' of https://github.com/Budibase/budibase into simplified-component-panel
This commit is contained in:
commit
381c8158e3
|
@ -146,6 +146,7 @@
|
||||||
|
|
||||||
.root {
|
.root {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
padding: 20px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,264 +1,54 @@
|
||||||
<script>
|
<script>
|
||||||
import { splitName } from "./pagesParsing/splitRootComponentName.js"
|
// This should be fetched from somewhere in the future, rather than be hardcoded.
|
||||||
import { store } from "builderStore"
|
import components from "./temporaryPanelStructure.js"
|
||||||
import {
|
import Tab from "./ComponentTab/Tab.svelte"
|
||||||
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
|
|
||||||
|
|
||||||
let componentLibraries = []
|
const categories = components.categories
|
||||||
let current_view = "text"
|
let selectedCategory = categories[0]
|
||||||
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
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="root">
|
<div class="root">
|
||||||
|
<ul class="tabs">
|
||||||
{#each componentItems as item}
|
{#each categories as category}
|
||||||
<ComponentItem
|
<li
|
||||||
name={item.component}
|
on:click={() => (selectedCategory = category)}
|
||||||
description={item.description}
|
class:active={selectedCategory === category}>
|
||||||
icon={item.icon} />
|
{category.name}
|
||||||
|
</li>
|
||||||
{/each}
|
{/each}
|
||||||
|
</ul>
|
||||||
|
<div class="panel">
|
||||||
|
<Tab components={selectedCategory.components} />
|
||||||
|
</div>
|
||||||
</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>
|
<style>
|
||||||
.root {
|
.tabs {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
justify-content: center;
|
||||||
}
|
|
||||||
|
|
||||||
.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 {
|
|
||||||
list-style: none;
|
list-style: none;
|
||||||
display: flex;
|
margin: 0 auto;
|
||||||
padding: 0;
|
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;
|
font-size: 14px;
|
||||||
margin-top: 13px;
|
font-weight: 500;
|
||||||
}
|
letter-spacing: 0.14px;
|
||||||
|
|
||||||
.preset-menu li:hover {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
li {
|
li {
|
||||||
margin-right: 20px;
|
color: #808192;
|
||||||
background: none;
|
margin: 0 5px;
|
||||||
border-radius: 5px;
|
padding: 0 8px;
|
||||||
}
|
|
||||||
|
|
||||||
/* li button {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background: none;
|
|
||||||
border: none;
|
|
||||||
border-radius: 5px;
|
|
||||||
padding: 13px;
|
|
||||||
outline: none;
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
} */
|
|
||||||
|
|
||||||
/* .selected {
|
|
||||||
color: var(--button-text);
|
|
||||||
background: var(--background-button) !important;
|
|
||||||
} */
|
|
||||||
|
|
||||||
.open {
|
|
||||||
color: rgba(0, 85, 255, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.template-instance-label {
|
.panel {
|
||||||
margin-left: 20px;
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.active {
|
||||||
|
border-bottom: solid 3px #0055ff;
|
||||||
|
color: #393c44;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -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}
|
|
@ -60,9 +60,7 @@
|
||||||
.switcher {
|
.switcher {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
margin-bottom: 20px;
|
margin: 20px;
|
||||||
padding: 0 20px 20px;
|
|
||||||
border-bottom: 1px solid #d8d8d8;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.switcher > button {
|
.switcher > button {
|
||||||
|
@ -85,11 +83,4 @@
|
||||||
color: var(--secondary100);
|
color: var(--secondary100);
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
.panel {
|
|
||||||
flex: 1 1 auto;
|
|
||||||
height: 0px;
|
|
||||||
overflow-y: auto;
|
|
||||||
padding: 0 5px 40px 10px;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -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: []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
Loading…
Reference in New Issue