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> |
<script> |
||||
import { |
import { store, selectedComponent, currentAsset } from "builderStore" |
||||
store, |
|
||||
selectedComponent, |
|
||||
currentAsset, |
|
||||
screenSearchString, |
|
||||
} from "builderStore" |
|
||||
import instantiateStore from "./dragDropStore" |
import instantiateStore from "./dragDropStore" |
||||
import ComponentTree from "./ComponentTree.svelte" |
import ComponentTree from "./ComponentTree.svelte" |
||||
import PathDropdownMenu from "./PathDropdownMenu.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> |
<script> |
||||
import ComponentListPanel from "./_components/ComponentListPanel.svelte" |
import NewComponentPanel from "./_components/NewComponentPanel.svelte" |
||||
|
import NewComponentTargetPanel from "./_components/NewComponentTargetPanel.svelte" |
||||
</script> |
</script> |
||||
|
|
||||
<ComponentListPanel /> |
<NewComponentPanel /> |
||||
|
<NewComponentTargetPanel /> |
||||
|
|||||
Loading…
Reference in new issue