1
0
Fork 0
mirror of synced 2024-07-04 05:50:57 +12:00

component templates

This commit is contained in:
Michael Shanks 2020-02-19 21:38:21 +00:00
parent b2d71d070c
commit 99c8814533
8 changed files with 188 additions and 46 deletions

View file

@ -111,6 +111,7 @@ export const getStore = () => {
store.moveUpComponent = moveUpComponent(store)
store.moveDownComponent = moveDownComponent(store)
store.copyComponent = copyComponent(store)
store.addTemplatedComponent = addTemplatedComponent(store)
return store
}
@ -161,6 +162,7 @@ const initialise = (store, initial) => async () => {
initial.components = values(pkg.components.components).map(
expandComponentDefinition
)
initial.templates = pkg.components.templates
initial.builtins = [getBuiltin("##builtin/screenslot")]
initial.actions = values(pkg.appDefinition.actions)
initial.triggers = pkg.appDefinition.triggers
@ -735,6 +737,24 @@ const addChildComponent = store => (componentToAdd, presetName) => {
})
}
/**
* @param {string} props - props to add, as child of current component
*/
const addTemplatedComponent = store => props => {
store.update(state => {
walkProps(props, p => {
p._id = uuid()
})
state.currentComponentInfo._children = state.currentComponentInfo._children.concat(
props
)
_savePage(state)
return state
})
}
const selectComponent = store => component => {
store.update(s => {
const componentDef = component._component.startsWith("##")

View file

@ -39,7 +39,9 @@ const ok = () => {
<div class="uk-modal-header">
<h2 class="uk-modal-title">{title}</h2>
</div>
<div class="uk-modal-body">{body}</div>
<div class="uk-modal-body">
<slot>{body}</slot>
</div>
<div class="uk-modal-footer">
<ButtonGroup>
<Button grouped color="primary" on:click={ok}>

View file

@ -1,14 +1,24 @@
<script>
import { splitName } from "./pagesParsing/splitRootComponentName.js"
import { store } from "../builderStore"
import { find, sortBy } from "lodash/fp"
import { find, sortBy, groupBy } from "lodash/fp"
import { ImageIcon, InputIcon, LayoutIcon } from "../common/Icons/"
import Select from "../common/Select.svelte"
import Button from "../common/PlusButton.svelte"
import ConfirmDialog from "../common/ConfirmDialog.svelte"
import { getRecordNodes, getIndexNodes, getIndexSchema } from "../common/core"
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
const addRootComponent = (component, allComponents) => {
const { libName } = splitName(component.name)
@ -28,6 +38,28 @@
const onComponentChosen = store.addChildComponent
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)
}
$: {
const newComponentLibraries = []
@ -36,58 +68,74 @@
}
componentLibraries = newComponentLibraries
if (!selectedLib) selectedLib = newComponentLibraries[0].libName
}
$: componentLibrary = componentLibraries.find(l => l.libName === selectedLib)
</script>
<div class="root">
<Select>
{#each componentLibraries as componentLibrary}
<option value={componentLibrary.libName}>
{componentLibrary.libName}
<Select on:change={e => selectedLib = e.target.value}>
{#each componentLibraries as lib}
<option value={lib.libName}>
{lib.libName}
</option>
{/each}
</Select>
{#each componentLibraries as componentLibrary}
<div class="library-container">
<ul>
<li>
<button
class:selected={current_view === 'text'}
on:click={() => (current_view = 'text')}>
<InputIcon />
</button>
</li>
<li>
<button
class:selected={current_view === 'layout'}
on:click={() => (current_view = 'layout')}>
<LayoutIcon />
</button>
</li>
<li>
<button
class:selected={current_view === 'media'}
on:click={() => (current_view = 'media')}>
<ImageIcon />
</button>
</li>
</ul>
<div class="library-container">
<ul>
<li>
<button
class:selected={current_view === 'text'}
on:click={() => (current_view = 'text')}>
<InputIcon />
</button>
</li>
<li>
<button
class:selected={current_view === 'layout'}
on:click={() => (current_view = 'layout')}>
<LayoutIcon />
</button>
</li>
<li>
<button
class:selected={current_view === 'media'}
on:click={() => (current_view = 'media')}>
<ImageIcon />
</button>
</li>
</ul>
{#if componentLibrary}
{#each $store.builtins.concat(componentLibrary.components) as component}
<div class="component-container">
<div
class="component"
on:click={() => onComponentChosen(component.name)}>
<div class="name">{splitName(component.name).componentName}</div>
{#if component.presets && component.name === selectedComponent}
{#if (component.presets || templatesByComponent[component.name]) && component.name === selectedComponent}
<ul class="preset-menu">
<span>{splitName(component.name).componentName} Presets</span>
{#each Object.keys(component.presets) as preset}
<li
on:click|stopPropagation={() => onComponentChosen(component.name, preset)}>
{preset}
</li>
{/each}
{#if component.presets}
<span>{splitName(component.name).componentName} Presets</span>
{#each Object.keys(component.presets) as preset}
<li
on:click|stopPropagation={() => onComponentChosen(component.name, preset)}>
{preset}
</li>
{/each}
{/if}
{#if templatesByComponent[component.name]}
<span>{splitName(component.name).componentName} Templates</span>
{#each templatesByComponent[component.name] as template}
<li
on:click|stopPropagation={() => onTemplateChosen(template)}>
{template.description}
</li>
{/each}
{/if}
</ul>
{/if}
</div>
@ -105,12 +153,27 @@
{/if}
</div>
{/each}
{/if}
</div>
</div>
{/each}
</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 {
display: flex;
@ -179,6 +242,7 @@
font-size: 12px;
font-weight: bold;
text-transform: uppercase;
margin-top: 5px;
}
.preset-menu li {
@ -215,4 +279,9 @@
.open {
color: rgba(0, 85, 255, 1);
}
.template-instance-label {
margin-left: 20px;
}
</style>

View file

@ -31,7 +31,7 @@ module.exports.componentLibraryInfo = async (appPath, libname) => {
const libDir = getLibDir(appPath, libname)
const componentsPath = getComponentsFilepath(libDir)
const componentDefinitionExists = await exists(componentsPath);
const componentDefinitionExists = await exists(componentsPath)
if (!componentDefinitionExists) {
const e = new Error(
@ -41,18 +41,32 @@ module.exports.componentLibraryInfo = async (appPath, libname) => {
throw e
}
const addNamespace = name => `${libname}/${name}`
try {
const componentDefinitions = await readJSON(componentsPath)
const namespacedComponents = { _lib: componentDefinitions._lib }
for (let componentKey in componentDefinitions) {
if (componentKey === "_lib") continue
const namespacedName = `${libname}/${componentKey}`
if (componentKey === "_lib" || componentKey === "_templates") continue
const namespacedName = addNamespace(componentKey)
componentDefinitions[componentKey].name = namespacedName
namespacedComponents[namespacedName] = componentDefinitions[componentKey]
}
const namespacedTemplates = { _lib: componentDefinitions._lib }
for (let templateKey in componentDefinitions._templates || {}) {
const template = componentDefinitions._templates[templateKey]
if (template.component)
template.component = addNamespace(template.component)
const namespacedName = addNamespace(templateKey)
template.name = namespacedName
namespacedTemplates[namespacedName] =
componentDefinitions._templates[templateKey]
}
return {
components: namespacedComponents,
templates: namespacedTemplates,
libDir,
componentsPath,
}

View file

@ -130,21 +130,28 @@ const getComponentDefinitions = async (appPath, pages, componentLibrary) => {
if (!pages) return []
componentLibraries = $(pages, [values, map(p => p.componentLibraries), flatten])
componentLibraries = $(pages, [
values,
map(p => p.componentLibraries),
flatten,
])
} else {
componentLibraries = [componentLibrary]
}
const components = {}
const templates = {}
for (let library of componentLibraries) {
const info = await componentLibraryInfo(appPath, library)
merge(components, info.components)
merge(templates, info.templates)
}
if (components._lib) delete components._lib
if (templates._lib) delete templates._lib
return { components }
return { components, templates }
}
module.exports.getComponentDefinitions = getComponentDefinitions

View file

@ -1,5 +1,11 @@
{
"_lib": "./dist/index.js",
"_templates" : {
"saveRecordButton" : {
"description": "Save record button",
"component": "button"
}
},
"button" : {
"name": "Button",
"description": "an html <button />",

View file

@ -0,0 +1,23 @@
export default ({ records }) =>
records.map(r => ({
name: `Save ${r.name} Button`,
props: buttonProps(r),
}))
const buttonProps = record => ({
_component: "@budibase/standard-components/button",
_children: [
{
_component: "@budibase/standard-components/text",
text: `Save ${record.name}`,
},
],
onClick: [
{
"##eventHandlerType": "Save Record",
parameters: {
statePath: `${record.name}`,
},
},
],
})

View file

@ -6,3 +6,4 @@ export { default as select } from "./Select.svelte"
export { default as option } from "./Option.svelte"
export { default as button } from "./Button.svelte"
export { default as login } from "./Login.svelte"
export { default as saveRecordButton } from "./Templates/saveRecordButton"