forked from tsai/budibase
24 changed files with 260 additions and 214 deletions
@ -1,114 +0,0 @@ |
|||
<script> |
|||
import { |
|||
ActionMenu, |
|||
ActionButton, |
|||
MenuItem, |
|||
Icon, |
|||
notifications, |
|||
} from "@budibase/bbui" |
|||
import { store, currentAssetName, selectedComponent } from "builderStore" |
|||
import structure from "./componentStructure.json" |
|||
|
|||
$: enrichedStructure = enrichStructure(structure, $store.components) |
|||
|
|||
const isChildAllowed = ({ name }, selectedComponent) => { |
|||
const currentComponent = store.actions.components.getDefinition( |
|||
selectedComponent?._component |
|||
) |
|||
return currentComponent?.illegalChildren?.includes(name.toLowerCase()) |
|||
} |
|||
|
|||
const enrichStructure = (structure, definitions) => { |
|||
let enrichedStructure = [] |
|||
structure.forEach(item => { |
|||
if (typeof item === "string") { |
|||
const def = definitions[`@budibase/standard-components/${item}`] |
|||
if (def) { |
|||
enrichedStructure.push({ |
|||
...def, |
|||
isCategory: false, |
|||
}) |
|||
} |
|||
} else { |
|||
enrichedStructure.push({ |
|||
...item, |
|||
isCategory: true, |
|||
children: enrichStructure(item.children || [], definitions), |
|||
}) |
|||
} |
|||
}) |
|||
return enrichedStructure |
|||
} |
|||
|
|||
const onItemChosen = async item => { |
|||
if (!item.isCategory) { |
|||
try { |
|||
await store.actions.components.create(item.component) |
|||
} catch (error) { |
|||
notifications.error("Error creating component") |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<div class="components"> |
|||
{#each enrichedStructure as item} |
|||
<ActionMenu disabled={!item.isCategory}> |
|||
<ActionButton |
|||
icon={item.icon} |
|||
disabled={!item.isCategory && isChildAllowed(item, $selectedComponent)} |
|||
quiet |
|||
size="S" |
|||
slot="control" |
|||
dataCy={`category-${item.name}`} |
|||
on:click={() => onItemChosen(item)} |
|||
> |
|||
<div class="buttonContent"> |
|||
{item.name} |
|||
{#if item.isCategory} |
|||
<Icon size="S" name="ChevronDown" /> |
|||
{/if} |
|||
</div> |
|||
</ActionButton> |
|||
{#each item.children || [] as item} |
|||
{#if !item.showOnAsset || item.showOnAsset.includes($currentAssetName)} |
|||
<MenuItem |
|||
dataCy={`component-${item.name}`} |
|||
icon={item.icon} |
|||
on:click={() => onItemChosen(item)} |
|||
disabled={isChildAllowed(item, $selectedComponent)} |
|||
> |
|||
{item.name} |
|||
</MenuItem> |
|||
{/if} |
|||
{/each} |
|||
</ActionMenu> |
|||
{/each} |
|||
</div> |
|||
|
|||
<style> |
|||
.components { |
|||
display: flex; |
|||
flex-direction: row; |
|||
justify-content: flex-start; |
|||
align-items: center; |
|||
flex-wrap: wrap; |
|||
} |
|||
.components :global(> *) { |
|||
height: 32px; |
|||
display: grid; |
|||
place-items: center; |
|||
} |
|||
|
|||
.buttonContent { |
|||
display: flex; |
|||
flex-direction: row; |
|||
justify-content: flex-start; |
|||
align-items: flex-end; |
|||
} |
|||
.buttonContent :global(svg) { |
|||
margin-left: 2px !important; |
|||
margin-right: 0 !important; |
|||
margin-bottom: -1px; |
|||
} |
|||
</style> |
|||
@ -1,10 +1,5 @@ |
|||
<script> |
|||
import { |
|||
store, |
|||
selectedComponent, |
|||
currentAsset, |
|||
screenSearchString, |
|||
} from "builderStore" |
|||
import { store, selectedComponent, currentAsset } from "builderStore" |
|||
import instantiateStore from "./dragDropStore" |
|||
import ComponentTree from "./ComponentTree.svelte" |
|||
import PathDropdownMenu from "./PathDropdownMenu.svelte" |
|||
@ -0,0 +1,52 @@ |
|||
<script> |
|||
import { store } from "builderStore" |
|||
import { ActionMenu, MenuItem, Icon, notifications } from "@budibase/bbui" |
|||
|
|||
export let component |
|||
|
|||
$: definition = store.actions.components.getDefinition(component?._component) |
|||
$: noPaste = !$store.componentToPaste |
|||
|
|||
// "editable" has been repurposed for inline text editing. |
|||
// It remains here for legacy compatibility. |
|||
// Future components should define "static": true for indicate they should |
|||
// not show a context menu. |
|||
$: showMenu = definition?.editable !== false && definition?.static !== true |
|||
|
|||
const storeComponentForCopy = (cut = false) => { |
|||
store.actions.components.copy(component, cut) |
|||
} |
|||
|
|||
const pasteComponent = mode => { |
|||
try { |
|||
store.actions.components.paste(component, mode) |
|||
} catch (error) { |
|||
notifications.error("Error saving component") |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
{#if showMenu} |
|||
<ActionMenu> |
|||
<div slot="control" class="icon"> |
|||
<Icon size="S" hoverable name="MoreSmallList" /> |
|||
</div> |
|||
<MenuItem icon="Copy" on:click={() => storeComponentForCopy(false)}> |
|||
Copy |
|||
</MenuItem> |
|||
<MenuItem |
|||
icon="ShowOneLayer" |
|||
on:click={() => pasteComponent("inside")} |
|||
disabled={noPaste} |
|||
> |
|||
Paste inside |
|||
</MenuItem> |
|||
</ActionMenu> |
|||
{/if} |
|||
|
|||
<style> |
|||
.icon { |
|||
display: grid; |
|||
place-items: center; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,30 @@ |
|||
<script> |
|||
import { syncURLToState } from "helpers/urlStateSync" |
|||
import { store, selectedScreen } from "builderStore" |
|||
import { goto, params, redirect } from "@roxi/routify" |
|||
import { onDestroy } from "svelte" |
|||
import { findComponent } from "builderStore/componentUtils" |
|||
|
|||
// Keep URL and state in sync for selected component ID |
|||
const stopSyncing = syncURLToState({ |
|||
keys: [ |
|||
{ |
|||
url: "componentId", |
|||
state: "selectedComponentId", |
|||
validate: componentId => { |
|||
return !!findComponent($selectedScreen.props, componentId) |
|||
}, |
|||
fallbackUrl: "../", |
|||
}, |
|||
], |
|||
store, |
|||
params, |
|||
goto, |
|||
redirect, |
|||
baseUrl: "..", |
|||
}) |
|||
|
|||
onDestroy(stopSyncing) |
|||
</script> |
|||
|
|||
<slot /> |
|||
@ -0,0 +1,10 @@ |
|||
<script> |
|||
import SettingsPanel from "components/design/SettingsPanel/SettingsPanel.svelte" |
|||
import { Body, Layout } from "@budibase/bbui" |
|||
</script> |
|||
|
|||
<SettingsPanel title="Screen" icon="WebPage"> |
|||
<Layout paddingX="L" paddingY="XL"> |
|||
<Body>The component you add will be placed inside Screen</Body> |
|||
</Layout> |
|||
</SettingsPanel> |
|||
@ -1,5 +1,7 @@ |
|||
<script> |
|||
import ComponentListPanel from "./_components/ComponentListPanel.svelte" |
|||
import NewComponentPanel from "./_components/NewComponentPanel.svelte" |
|||
import NewComponentTargetPanel from "./_components/NewComponentTargetPanel.svelte" |
|||
</script> |
|||
|
|||
<ComponentListPanel /> |
|||
<NewComponentPanel /> |
|||
<NewComponentTargetPanel /> |
|||
|
|||
Loading…
Reference in new issue