forked from tsai/budibase
143 changed files with 2969 additions and 5550 deletions
@ -1,54 +1,60 @@ |
|||||
xcontext('Create Components', () => { |
xcontext("Create Components", () => { |
||||
|
before(() => { |
||||
|
cy.server() |
||||
|
cy.visit("localhost:4001/_builder") |
||||
|
// https://on.cypress.io/type
|
||||
|
cy.createApp("Table App", "Table App Description") |
||||
|
cy.createTable("dog", "name", "age") |
||||
|
cy.addRow("bob", "15") |
||||
|
}) |
||||
|
|
||||
before(() => { |
// https://on.cypress.io/interacting-with-elements
|
||||
cy.server() |
it("should add a container", () => { |
||||
cy.visit('localhost:4001/_builder') |
cy.navigateToFrontend() |
||||
// https://on.cypress.io/type
|
cy.get(".switcher > :nth-child(2)").click() |
||||
cy.createApp('Table App', 'Table App Description') |
cy.contains("Container").click() |
||||
cy.createTable('dog', 'name', 'age') |
}) |
||||
cy.addRow('bob', '15') |
it("should add a headline", () => { |
||||
}) |
cy.addHeadlineComponent("An Amazing headline!") |
||||
|
|
||||
// https://on.cypress.io/interacting-with-elements
|
getIframeBody().contains("An Amazing headline!") |
||||
it('should add a container', () => { |
}) |
||||
cy.contains('frontend').click() |
it("change the font size of the headline", () => { |
||||
cy.get('.switcher > :nth-child(2)').click() |
cy.contains("Typography").click() |
||||
|
cy.get("[data-cy=font-size-prop-control]").click() |
||||
|
cy.contains("60px").click() |
||||
|
cy.contains("Design").click() |
||||
|
|
||||
cy.contains('Container').click() |
getIframeBody() |
||||
}) |
.contains("An Amazing headline!") |
||||
it('should add a headline', () => { |
.should("have.css", "font-size", "60px") |
||||
cy.addHeadlineComponent('An Amazing headline!') |
}) |
||||
|
|
||||
getIframeBody().contains('An Amazing headline!') |
|
||||
}) |
|
||||
it('change the font size of the headline', () => { |
|
||||
cy.contains('Typography').click() |
|
||||
cy.get('[data-cy=font-size-prop-control]').click() |
|
||||
cy.contains("60px").click() |
|
||||
cy.contains('Design').click() |
|
||||
|
|
||||
getIframeBody().contains('An Amazing headline!').should('have.css', 'font-size', '60px') |
|
||||
}) |
|
||||
}) |
}) |
||||
|
|
||||
const getIframeDocument = () => { |
const getIframeDocument = () => { |
||||
return cy |
return ( |
||||
.get('iframe') |
cy |
||||
// Cypress yields jQuery element, which has the real
|
.get("iframe") |
||||
// DOM element under property "0".
|
// Cypress yields jQuery element, which has the real
|
||||
// From the real DOM iframe element we can get
|
// DOM element under property "0".
|
||||
// the "document" element, it is stored in "contentDocument" property
|
// From the real DOM iframe element we can get
|
||||
// Cypress "its" command can access deep properties using dot notation
|
// the "document" element, it is stored in "contentDocument" property
|
||||
// https://on.cypress.io/its
|
// Cypress "its" command can access deep properties using dot notation
|
||||
.its('0.contentDocument').should('exist') |
// https://on.cypress.io/its
|
||||
|
.its("0.contentDocument") |
||||
|
.should("exist") |
||||
|
) |
||||
} |
} |
||||
|
|
||||
const getIframeBody = () => { |
const getIframeBody = () => { |
||||
// get the document
|
// get the document
|
||||
return getIframeDocument() |
return ( |
||||
// automatically retries until body is loaded
|
getIframeDocument() |
||||
.its('body').should('not.be.undefined') |
// automatically retries until body is loaded
|
||||
// wraps "body" DOM element to allow
|
.its("body") |
||||
// chaining more Cypress commands, like ".find(...)"
|
.should("not.be.undefined") |
||||
.then(cy.wrap) |
// wraps "body" DOM element to allow
|
||||
} |
// chaining more Cypress commands, like ".find(...)"
|
||||
|
.then(cy.wrap) |
||||
|
) |
||||
|
} |
||||
|
|||||
@ -0,0 +1,136 @@ |
|||||
|
<script> |
||||
|
import { sortBy } from "lodash/fp" |
||||
|
import { automationStore } from "builderStore" |
||||
|
import { DropdownMenu, Modal } from "@budibase/bbui" |
||||
|
import { DropdownContainer, DropdownItem } from "components/common/Dropdowns" |
||||
|
import analytics from "analytics" |
||||
|
import CreateWebhookModal from "../Shared/CreateWebhookModal.svelte" |
||||
|
|
||||
|
$: hasTrigger = $automationStore.selectedAutomation.hasTrigger() |
||||
|
$: tabs = [ |
||||
|
{ |
||||
|
label: "Trigger", |
||||
|
value: "TRIGGER", |
||||
|
icon: "ri-organization-chart", |
||||
|
disabled: hasTrigger, |
||||
|
}, |
||||
|
{ |
||||
|
label: "Action", |
||||
|
value: "ACTION", |
||||
|
icon: "ri-flow-chart", |
||||
|
disabled: !hasTrigger, |
||||
|
}, |
||||
|
{ |
||||
|
label: "Logic", |
||||
|
value: "LOGIC", |
||||
|
icon: "ri-filter-line", |
||||
|
disabled: !hasTrigger, |
||||
|
}, |
||||
|
] |
||||
|
|
||||
|
let selectedIndex |
||||
|
let anchors = [] |
||||
|
let popover |
||||
|
let webhookModal |
||||
|
$: selectedTab = selectedIndex == null ? null : tabs[selectedIndex].value |
||||
|
$: anchor = selectedIndex === -1 ? null : anchors[selectedIndex] |
||||
|
$: blocks = sortBy(entry => entry[1].name)( |
||||
|
Object.entries($automationStore.blockDefinitions[selectedTab] ?? {}) |
||||
|
) |
||||
|
|
||||
|
function onChangeTab(idx) { |
||||
|
selectedIndex = idx |
||||
|
popover.show() |
||||
|
} |
||||
|
|
||||
|
function closePopover() { |
||||
|
selectedIndex = null |
||||
|
popover.hide() |
||||
|
} |
||||
|
|
||||
|
function addBlockToAutomation(stepId, blockDefinition) { |
||||
|
const newBlock = { |
||||
|
...blockDefinition, |
||||
|
inputs: blockDefinition.inputs || {}, |
||||
|
stepId, |
||||
|
type: selectedTab, |
||||
|
} |
||||
|
automationStore.actions.addBlockToAutomation(newBlock) |
||||
|
analytics.captureEvent("Added Automation Block", { |
||||
|
name: blockDefinition.name, |
||||
|
}) |
||||
|
closePopover() |
||||
|
if (stepId === "WEBHOOK") { |
||||
|
webhookModal.show() |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<div class="tab-container"> |
||||
|
{#each tabs as tab, idx} |
||||
|
<div |
||||
|
bind:this={anchors[idx]} |
||||
|
class="tab" |
||||
|
class:disabled={tab.disabled} |
||||
|
on:click={tab.disabled ? null : () => onChangeTab(idx)} |
||||
|
class:active={idx === selectedIndex}> |
||||
|
{#if tab.icon}<i class={tab.icon} />{/if} |
||||
|
<span>{tab.label}</span> |
||||
|
<i class="ri-arrow-down-s-line arrow" /> |
||||
|
</div> |
||||
|
{/each} |
||||
|
</div> |
||||
|
<DropdownMenu |
||||
|
on:close={() => (selectedIndex = null)} |
||||
|
bind:this={popover} |
||||
|
{anchor} |
||||
|
align="left"> |
||||
|
<DropdownContainer> |
||||
|
{#each blocks as [stepId, blockDefinition]} |
||||
|
<DropdownItem |
||||
|
icon={blockDefinition.icon} |
||||
|
title={blockDefinition.name} |
||||
|
subtitle={blockDefinition.description} |
||||
|
on:click={() => addBlockToAutomation(stepId, blockDefinition)} /> |
||||
|
{/each} |
||||
|
</DropdownContainer> |
||||
|
</DropdownMenu> |
||||
|
<Modal bind:this={webhookModal} width="30%"> |
||||
|
<CreateWebhookModal /> |
||||
|
</Modal> |
||||
|
|
||||
|
<style> |
||||
|
.tab-container { |
||||
|
display: flex; |
||||
|
flex-direction: row; |
||||
|
justify-content: flex-start; |
||||
|
align-items: center; |
||||
|
gap: var(--spacing-l); |
||||
|
min-height: 24px; |
||||
|
} |
||||
|
|
||||
|
.tab { |
||||
|
color: var(--grey-7); |
||||
|
display: flex; |
||||
|
flex-direction: row; |
||||
|
justify-content: flex-start; |
||||
|
align-items: center; |
||||
|
gap: var(--spacing-xs); |
||||
|
font-size: var(--font-size-xs); |
||||
|
} |
||||
|
.tab span { |
||||
|
font-weight: 500; |
||||
|
user-select: none; |
||||
|
} |
||||
|
.tab.active, |
||||
|
.tab:not(.disabled):hover { |
||||
|
color: var(--ink); |
||||
|
cursor: pointer; |
||||
|
} |
||||
|
.tab.disabled { |
||||
|
color: var(--grey-5); |
||||
|
} |
||||
|
.tab i:not(:last-child) { |
||||
|
font-size: 16px; |
||||
|
} |
||||
|
</style> |
||||
|
Before Width: | Height: | Size: 303 B After Width: | Height: | Size: 303 B |
@ -0,0 +1,34 @@ |
|||||
|
<script> |
||||
|
import { onMount } from "svelte" |
||||
|
import { automationStore } from "builderStore" |
||||
|
import NavItem from "components/common/NavItem.svelte" |
||||
|
import EditAutomationPopover from "./EditAutomationPopover.svelte" |
||||
|
|
||||
|
$: selectedAutomationId = $automationStore.selectedAutomation?.automation?._id |
||||
|
|
||||
|
onMount(() => { |
||||
|
automationStore.actions.fetch() |
||||
|
}) |
||||
|
</script> |
||||
|
|
||||
|
<div class="automations-list"> |
||||
|
{#each $automationStore.automations as automation, idx} |
||||
|
<NavItem |
||||
|
border={idx > 0} |
||||
|
icon="ri-stackshare-line" |
||||
|
text={automation.name} |
||||
|
selected={automation._id === selectedAutomationId} |
||||
|
on:click={() => automationStore.actions.select(automation)}> |
||||
|
<EditAutomationPopover {automation} /> |
||||
|
</NavItem> |
||||
|
{/each} |
||||
|
</div> |
||||
|
|
||||
|
<style> |
||||
|
.automations-list { |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
justify-content: flex-start; |
||||
|
align-items: stretch; |
||||
|
} |
||||
|
</style> |
||||
@ -1,79 +0,0 @@ |
|||||
<script> |
|
||||
import { onMount } from "svelte" |
|
||||
import { automationStore } from "builderStore" |
|
||||
import CreateAutomationModal from "./CreateAutomationModal.svelte" |
|
||||
import { Button, Modal } from "@budibase/bbui" |
|
||||
|
|
||||
let modal |
|
||||
|
|
||||
$: selectedAutomationId = $automationStore.selectedAutomation?.automation?._id |
|
||||
|
|
||||
onMount(() => { |
|
||||
automationStore.actions.fetch() |
|
||||
}) |
|
||||
</script> |
|
||||
|
|
||||
<section> |
|
||||
<Button primary wide on:click={modal.show}>Create New Automation</Button> |
|
||||
<ul> |
|
||||
{#each $automationStore.automations as automation} |
|
||||
<li |
|
||||
class="automation-item" |
|
||||
class:selected={automation._id === selectedAutomationId} |
|
||||
on:click={() => automationStore.actions.select(automation)}> |
|
||||
<i class="ri-stackshare-line" class:live={automation.live} /> |
|
||||
{automation.name} |
|
||||
</li> |
|
||||
{/each} |
|
||||
</ul> |
|
||||
</section> |
|
||||
<Modal bind:this={modal}> |
|
||||
<CreateAutomationModal /> |
|
||||
</Modal> |
|
||||
|
|
||||
<style> |
|
||||
section { |
|
||||
display: flex; |
|
||||
flex-direction: column; |
|
||||
} |
|
||||
|
|
||||
ul { |
|
||||
list-style-type: none; |
|
||||
padding: 0; |
|
||||
margin: var(--spacing-xl) 0 0 0; |
|
||||
flex: 1; |
|
||||
} |
|
||||
|
|
||||
i { |
|
||||
color: var(--grey-6); |
|
||||
} |
|
||||
i.live { |
|
||||
color: var(--ink); |
|
||||
} |
|
||||
|
|
||||
li { |
|
||||
font-size: 14px; |
|
||||
} |
|
||||
|
|
||||
.automation-item { |
|
||||
display: flex; |
|
||||
flex-direction: row; |
|
||||
justify-content: flex-start; |
|
||||
align-items: center; |
|
||||
border-radius: var(--border-radius-m); |
|
||||
padding: var(--spacing-s) var(--spacing-m); |
|
||||
margin-bottom: var(--spacing-xs); |
|
||||
color: var(--ink); |
|
||||
} |
|
||||
.automation-item i { |
|
||||
font-size: 24px; |
|
||||
margin-right: var(--spacing-m); |
|
||||
} |
|
||||
.automation-item:hover { |
|
||||
cursor: pointer; |
|
||||
background: var(--grey-1); |
|
||||
} |
|
||||
.automation-item.selected { |
|
||||
background: var(--grey-2); |
|
||||
} |
|
||||
</style> |
|
||||
@ -1,82 +0,0 @@ |
|||||
<script> |
|
||||
import { backendUiStore, automationStore } from "builderStore" |
|
||||
import CreateWebookModal from "../../Shared/CreateWebhookModal.svelte" |
|
||||
import analytics from "analytics" |
|
||||
import { Modal } from "@budibase/bbui" |
|
||||
|
|
||||
export let blockDefinition |
|
||||
export let stepId |
|
||||
export let blockType |
|
||||
|
|
||||
let modal |
|
||||
|
|
||||
$: instanceId = $backendUiStore.selectedDatabase._id |
|
||||
$: automation = $automationStore.selectedAutomation?.automation |
|
||||
|
|
||||
function addBlockToAutomation() { |
|
||||
automationStore.actions.addBlockToAutomation({ |
|
||||
...blockDefinition, |
|
||||
inputs: blockDefinition.inputs || {}, |
|
||||
stepId, |
|
||||
type: blockType, |
|
||||
}) |
|
||||
if (stepId === "WEBHOOK") { |
|
||||
modal.show() |
|
||||
} |
|
||||
analytics.captureEvent("Added Automation Block", { |
|
||||
name: blockDefinition.name, |
|
||||
}) |
|
||||
} |
|
||||
</script> |
|
||||
|
|
||||
<div |
|
||||
class="automation-block hoverable" |
|
||||
on:click={addBlockToAutomation} |
|
||||
data-cy={stepId}> |
|
||||
<div><i class={blockDefinition.icon} /></div> |
|
||||
<div class="automation-text"> |
|
||||
<h4>{blockDefinition.name}</h4> |
|
||||
<p>{blockDefinition.description}</p> |
|
||||
</div> |
|
||||
</div> |
|
||||
<Modal bind:this={modal} width="30%"> |
|
||||
<CreateWebookModal /> |
|
||||
</Modal> |
|
||||
|
|
||||
<style> |
|
||||
.automation-block { |
|
||||
display: grid; |
|
||||
grid-template-columns: 20px auto; |
|
||||
align-items: center; |
|
||||
margin-top: var(--spacing-s); |
|
||||
padding: var(--spacing-m); |
|
||||
border-radius: var(--border-radius-m); |
|
||||
} |
|
||||
.automation-block:hover { |
|
||||
background-color: var(--grey-1); |
|
||||
} |
|
||||
.automation-block:first-child { |
|
||||
margin-top: 0; |
|
||||
} |
|
||||
|
|
||||
i { |
|
||||
color: var(--grey-7); |
|
||||
font-size: 20px; |
|
||||
} |
|
||||
|
|
||||
.automation-text { |
|
||||
margin-left: 16px; |
|
||||
} |
|
||||
.automation-text h4 { |
|
||||
font-size: 14px; |
|
||||
font-weight: 500; |
|
||||
margin-bottom: 5px; |
|
||||
margin-top: 0; |
|
||||
} |
|
||||
.automation-text p { |
|
||||
font-size: 12px; |
|
||||
line-height: 1.4; |
|
||||
color: var(--grey-7); |
|
||||
margin: 0; |
|
||||
} |
|
||||
</style> |
|
||||
@ -1,48 +0,0 @@ |
|||||
<script> |
|
||||
import { sortBy } from "lodash/fp" |
|
||||
import { automationStore } from "builderStore" |
|
||||
import AutomationBlock from "./AutomationBlock.svelte" |
|
||||
import FlatButtonGroup from "components/userInterface/FlatButtonGroup.svelte" |
|
||||
|
|
||||
let selectedTab = "TRIGGER" |
|
||||
let buttonProps = [] |
|
||||
$: blocks = sortBy(entry => entry[1].name)( |
|
||||
Object.entries($automationStore.blockDefinitions[selectedTab]) |
|
||||
) |
|
||||
|
|
||||
$: { |
|
||||
if ($automationStore.selectedAutomation.hasTrigger()) { |
|
||||
buttonProps = [ |
|
||||
{ value: "ACTION", text: "Action" }, |
|
||||
{ value: "LOGIC", text: "Logic" }, |
|
||||
] |
|
||||
if (selectedTab === "TRIGGER") { |
|
||||
selectedTab = "ACTION" |
|
||||
} |
|
||||
} else { |
|
||||
buttonProps = [{ value: "TRIGGER", text: "Trigger" }] |
|
||||
if (selectedTab !== "TRIGGER") { |
|
||||
selectedTab = "TRIGGER" |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
function onChangeTab(tab) { |
|
||||
selectedTab = tab |
|
||||
} |
|
||||
</script> |
|
||||
|
|
||||
<section> |
|
||||
<FlatButtonGroup value={selectedTab} {buttonProps} onChange={onChangeTab} /> |
|
||||
<div id="blocklist"> |
|
||||
{#each blocks as [stepId, blockDefinition]} |
|
||||
<AutomationBlock {blockDefinition} {stepId} blockType={selectedTab} /> |
|
||||
{/each} |
|
||||
</div> |
|
||||
</section> |
|
||||
|
|
||||
<style> |
|
||||
#blocklist { |
|
||||
margin-top: var(--spacing-xl); |
|
||||
} |
|
||||
</style> |
|
||||
@ -0,0 +1,88 @@ |
|||||
|
<script> |
||||
|
import { automationStore, backendUiStore } from "builderStore" |
||||
|
import { notifier } from "builderStore/store/notifications" |
||||
|
import { DropdownMenu } from "@budibase/bbui" |
||||
|
import { DropdownContainer, DropdownItem } from "components/common/Dropdowns" |
||||
|
import ConfirmDialog from "components/common/ConfirmDialog.svelte" |
||||
|
|
||||
|
export let automation |
||||
|
|
||||
|
let anchor |
||||
|
let dropdown |
||||
|
let confirmDeleteDialog |
||||
|
$: instanceId = $backendUiStore.selectedDatabase._id |
||||
|
|
||||
|
function showModal() { |
||||
|
dropdown.hide() |
||||
|
confirmDeleteDialog.show() |
||||
|
} |
||||
|
|
||||
|
async function deleteAutomation() { |
||||
|
await automationStore.actions.delete({ |
||||
|
instanceId, |
||||
|
automation, |
||||
|
}) |
||||
|
notifier.success("Automation deleted.") |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<div on:click|stopPropagation> |
||||
|
<div bind:this={anchor} class="icon" on:click={dropdown.show}> |
||||
|
<i class="ri-more-line" /> |
||||
|
</div> |
||||
|
<DropdownMenu align="left" {anchor} bind:this={dropdown}> |
||||
|
<DropdownContainer> |
||||
|
<DropdownItem |
||||
|
icon="ri-delete-bin-line" |
||||
|
title="Delete" |
||||
|
on:click={showModal} /> |
||||
|
</DropdownContainer> |
||||
|
</DropdownMenu> |
||||
|
</div> |
||||
|
<ConfirmDialog |
||||
|
bind:this={confirmDeleteDialog} |
||||
|
okText="Delete Automation" |
||||
|
onOk={deleteAutomation} |
||||
|
title="Confirm Deletion"> |
||||
|
Are you sure you wish to delete the automation |
||||
|
<i>{automation.name}?</i> |
||||
|
This action cannot be undone. |
||||
|
</ConfirmDialog> |
||||
|
|
||||
|
<style> |
||||
|
div.icon { |
||||
|
display: flex; |
||||
|
flex-direction: row; |
||||
|
justify-content: flex-end; |
||||
|
align-items: center; |
||||
|
} |
||||
|
|
||||
|
div.icon i { |
||||
|
font-size: 16px; |
||||
|
} |
||||
|
|
||||
|
ul { |
||||
|
list-style: none; |
||||
|
margin: 0; |
||||
|
padding: var(--spacing-s) 0; |
||||
|
} |
||||
|
|
||||
|
li { |
||||
|
display: flex; |
||||
|
font-family: var(--font-sans); |
||||
|
font-size: var(--font-size-xs); |
||||
|
color: var(--ink); |
||||
|
padding: var(--spacing-s) var(--spacing-m); |
||||
|
margin: auto 0; |
||||
|
align-items: center; |
||||
|
cursor: pointer; |
||||
|
} |
||||
|
|
||||
|
li:hover { |
||||
|
background-color: var(--grey-2); |
||||
|
} |
||||
|
|
||||
|
li:active { |
||||
|
color: var(--blue); |
||||
|
} |
||||
|
</style> |
||||
@ -1,3 +0,0 @@ |
|||||
export { default as AutomationPanel } from "./AutomationPanel.svelte" |
|
||||
export { default as BlockList } from "./BlockList/BlockList.svelte" |
|
||||
export { default as AutomationList } from "./AutomationList/AutomationList.svelte" |
|
||||
@ -1 +0,0 @@ |
|||||
export { default as SetupPanel } from "./SetupPanel.svelte" |
|
||||
@ -1,3 +0,0 @@ |
|||||
export { default as AutomationBuilder } from "./AutomationBuilder/AutomationBuilder.svelte" |
|
||||
export { default as SetupPanel } from "./SetupPanel/SetupPanel.svelte" |
|
||||
export { default as AutomationPanel } from "./AutomationPanel/AutomationPanel.svelte" |
|
||||
@ -1,70 +0,0 @@ |
|||||
<script> |
|
||||
export let disabled = false |
|
||||
export let hidden = false |
|
||||
export let secondary = false |
|
||||
export let primary = true |
|
||||
export let cancel = false |
|
||||
export let alert = false |
|
||||
export let warning = false |
|
||||
</script> |
|
||||
|
|
||||
<button |
|
||||
on:click |
|
||||
class="button" |
|
||||
class:hidden |
|
||||
class:secondary |
|
||||
class:primary |
|
||||
class:alert |
|
||||
class:cancel |
|
||||
class:warning |
|
||||
{disabled}> |
|
||||
<slot /> |
|
||||
</button> |
|
||||
|
|
||||
<style> |
|
||||
.primary { |
|
||||
color: #ffffff; |
|
||||
background: var(--blue); |
|
||||
border: solid 1px var(--blue); |
|
||||
} |
|
||||
|
|
||||
.alert { |
|
||||
color: white; |
|
||||
background: #e26d69; |
|
||||
border: solid 1px #e26d69; |
|
||||
} |
|
||||
|
|
||||
.cancel { |
|
||||
color: var(--secondary40); |
|
||||
background: none; |
|
||||
} |
|
||||
|
|
||||
.secondary { |
|
||||
color: var(--ink); |
|
||||
border: solid 1px var(--grey-4); |
|
||||
background: white; |
|
||||
} |
|
||||
|
|
||||
.button { |
|
||||
font-size: 14px; |
|
||||
font-weight: 500; |
|
||||
border-radius: 3px; |
|
||||
padding: 10px 20px; |
|
||||
height: 40px; |
|
||||
} |
|
||||
|
|
||||
.button:hover { |
|
||||
cursor: pointer; |
|
||||
filter: saturate(90%); |
|
||||
} |
|
||||
|
|
||||
.button:disabled { |
|
||||
color: rgba(22, 48, 87, 0.2); |
|
||||
cursor: default; |
|
||||
background: transparent; |
|
||||
} |
|
||||
|
|
||||
.hidden { |
|
||||
visibility: hidden; |
|
||||
} |
|
||||
</style> |
|
||||
@ -1,171 +0,0 @@ |
|||||
<script> |
|
||||
export let color = "primary" |
|
||||
export let className = "" |
|
||||
export let style = "" |
|
||||
export let groupPosition = "" |
|
||||
export let grouped = false |
|
||||
|
|
||||
$: borderClass = grouped ? "" : "border-normal" |
|
||||
</script> |
|
||||
|
|
||||
<button |
|
||||
class="{color} |
|
||||
{className} |
|
||||
{borderClass} |
|
||||
{grouped ? 'grouped' : ''}" |
|
||||
{style} |
|
||||
on:click> |
|
||||
<slot /> |
|
||||
</button> |
|
||||
|
|
||||
<style> |
|
||||
.border-normal { |
|
||||
border-radius: var(--borderradiusall); |
|
||||
} |
|
||||
.border-left { |
|
||||
border-radius: var(--borderradius) 0 0 var(--borderradius); |
|
||||
} |
|
||||
.border-right { |
|
||||
border-radius: 0 var(--borderradius) var(--borderradius) 0; |
|
||||
} |
|
||||
.border-middle { |
|
||||
border-radius: 0; |
|
||||
} |
|
||||
|
|
||||
button { |
|
||||
border-style: solid; |
|
||||
padding: 7.5px 15px; |
|
||||
cursor: pointer; |
|
||||
margin: 5px; |
|
||||
border-radius: 5px; |
|
||||
} |
|
||||
|
|
||||
/* ---- PRIMARY ----*/ |
|
||||
.primary { |
|
||||
background-color: var(--blue); |
|
||||
border-color: var(--blue); |
|
||||
color: var(--white); |
|
||||
} |
|
||||
|
|
||||
.primary:hover { |
|
||||
background-color: var(--blue); |
|
||||
border-color: var(--blue); |
|
||||
} |
|
||||
|
|
||||
.primary:active { |
|
||||
background-color: var(--primarydark); |
|
||||
border-color: var(--primarydark); |
|
||||
} |
|
||||
|
|
||||
.primary-outline { |
|
||||
background-color: var(--white); |
|
||||
border-color: var(--blue); |
|
||||
color: var(--blue); |
|
||||
} |
|
||||
|
|
||||
.primary-outline:hover { |
|
||||
background-color: var(--primary10); |
|
||||
} |
|
||||
|
|
||||
.primary-outline:pressed { |
|
||||
background-color: var(--primary25); |
|
||||
} |
|
||||
|
|
||||
/* ---- secondary ----*/ |
|
||||
|
|
||||
.secondary { |
|
||||
background-color: var(--ink); |
|
||||
border-color: var(--ink); |
|
||||
color: var(--white); |
|
||||
} |
|
||||
|
|
||||
.secondary:hover { |
|
||||
background-color: var(--secondary75); |
|
||||
border-color: var(--secondary75); |
|
||||
} |
|
||||
|
|
||||
.secondary:pressed { |
|
||||
background-color: var(--secondarydark); |
|
||||
border-color: var(--secondarydark); |
|
||||
} |
|
||||
|
|
||||
.secondary-outline { |
|
||||
background-color: var(--white); |
|
||||
border-color: var(--ink); |
|
||||
color: var(--ink); |
|
||||
} |
|
||||
|
|
||||
.secondary-outline:hover { |
|
||||
background-color: var(--secondary10); |
|
||||
} |
|
||||
|
|
||||
.secondary-outline:pressed { |
|
||||
background-color: var(--secondary25); |
|
||||
} |
|
||||
|
|
||||
/* ---- success ----*/ |
|
||||
.success { |
|
||||
background-color: var(--success100); |
|
||||
border-color: var(--success100); |
|
||||
color: var(--white); |
|
||||
} |
|
||||
|
|
||||
.success:hover { |
|
||||
background-color: var(--success75); |
|
||||
border-color: var(--success75); |
|
||||
} |
|
||||
|
|
||||
.success:pressed { |
|
||||
background-color: var(--successdark); |
|
||||
border-color: var(--successdark); |
|
||||
} |
|
||||
|
|
||||
.success-outline { |
|
||||
background-color: var(--white); |
|
||||
border-color: var(--success100); |
|
||||
color: var(--success100); |
|
||||
} |
|
||||
|
|
||||
.success-outline:hover { |
|
||||
background-color: var(--success10); |
|
||||
} |
|
||||
|
|
||||
.success-outline:pressed { |
|
||||
background-color: var(--success25); |
|
||||
} |
|
||||
|
|
||||
/* ---- deletion ----*/ |
|
||||
.deletion { |
|
||||
background-color: var(--red); |
|
||||
border-color: var(--red); |
|
||||
color: var(--white); |
|
||||
} |
|
||||
|
|
||||
.deletion:hover { |
|
||||
background-color: var(--red-light); |
|
||||
border-color: var(--red); |
|
||||
color: var(--red); |
|
||||
} |
|
||||
|
|
||||
.deletion:pressed { |
|
||||
background-color: var(--red-dark); |
|
||||
border-color: var(--red-dark); |
|
||||
color: var(--white); |
|
||||
} |
|
||||
|
|
||||
.deletion-outline { |
|
||||
background-color: var(--white); |
|
||||
border-color: var(--red); |
|
||||
color: var(--red); |
|
||||
} |
|
||||
|
|
||||
.deletion-outline:hover { |
|
||||
background-color: var(--red-light); |
|
||||
color: var(--red); |
|
||||
} |
|
||||
|
|
||||
.deletion-outline:pressed { |
|
||||
background-color: var(--red-dark); |
|
||||
color: var(--white); |
|
||||
} |
|
||||
</style> |
|
||||
@ -1,15 +0,0 @@ |
|||||
<script> |
|
||||
export let style = "" |
|
||||
</script> |
|
||||
|
|
||||
<div class="root" {style}> |
|
||||
<slot /> |
|
||||
</div> |
|
||||
|
|
||||
<style> |
|
||||
.root { |
|
||||
display: flex; |
|
||||
flex-direction: row; |
|
||||
justify-content: space-between; |
|
||||
} |
|
||||
</style> |
|
||||
@ -0,0 +1,13 @@ |
|||||
|
<div class="dropdown-container" on:click> |
||||
|
<slot /> |
||||
|
</div> |
||||
|
|
||||
|
<style> |
||||
|
.dropdown-container { |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
justify-content: flex-start; |
||||
|
align-items: stretch; |
||||
|
padding: var(--spacing-s) 0; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,70 @@ |
|||||
|
<script> |
||||
|
export let icon |
||||
|
export let title |
||||
|
export let subtitle |
||||
|
export let disabled |
||||
|
</script> |
||||
|
|
||||
|
<div |
||||
|
class="dropdown-item" |
||||
|
class:disabled |
||||
|
on:click |
||||
|
class:big={subtitle != null} |
||||
|
{...$$restProps}> |
||||
|
{#if icon}<i class={icon} />{/if} |
||||
|
<div class="content"> |
||||
|
<div class="title">{title}</div> |
||||
|
{#if subtitle != null} |
||||
|
<div class="subtitle">{subtitle}</div> |
||||
|
{/if} |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<style> |
||||
|
.dropdown-item { |
||||
|
display: flex; |
||||
|
flex-direction: row; |
||||
|
justify-content: flex-start; |
||||
|
align-items: center; |
||||
|
gap: var(--spacing-m); |
||||
|
padding: var(--spacing-xs) var(--spacing-l); |
||||
|
color: var(--ink); |
||||
|
} |
||||
|
.dropdown-item.disabled, |
||||
|
.dropdown-item.disabled .subtitle { |
||||
|
pointer-events: none; |
||||
|
color: var(--grey-5); |
||||
|
} |
||||
|
.dropdown-item.big { |
||||
|
padding: var(--spacing-s) var(--spacing-l); |
||||
|
} |
||||
|
.dropdown-item:not(.disabled):hover { |
||||
|
background-color: var(--grey-2); |
||||
|
cursor: pointer; |
||||
|
} |
||||
|
|
||||
|
.content { |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
justify-content: center; |
||||
|
align-items: flex-start; |
||||
|
} |
||||
|
|
||||
|
.title, |
||||
|
.subtitle { |
||||
|
font-size: var(--font-size-xs); |
||||
|
} |
||||
|
|
||||
|
.title { |
||||
|
font-weight: 400; |
||||
|
} |
||||
|
|
||||
|
.subtitle { |
||||
|
color: var(--grey-7); |
||||
|
font-weight: 400; |
||||
|
} |
||||
|
|
||||
|
i { |
||||
|
font-size: 16px; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,2 @@ |
|||||
|
export { default as DropdownContainer } from "./DropdownContainer.svelte" |
||||
|
export { default as DropdownItem } from "./DropdownItem.svelte" |
||||
@ -0,0 +1,109 @@ |
|||||
|
<script> |
||||
|
export let icon |
||||
|
export let withArrow = false |
||||
|
export let withActions = true |
||||
|
export let indentLevel = 0 |
||||
|
export let text |
||||
|
export let border = true |
||||
|
export let selected = false |
||||
|
export let opened = false |
||||
|
export let draggable = false |
||||
|
</script> |
||||
|
|
||||
|
<div |
||||
|
class="nav-item" |
||||
|
class:border |
||||
|
class:selected |
||||
|
style={`padding-left: ${indentLevel * 18}px`} |
||||
|
{draggable} |
||||
|
on:dragend |
||||
|
on:dragstart |
||||
|
on:dragover |
||||
|
on:drop |
||||
|
on:click |
||||
|
ondragover="return false" |
||||
|
ondragenter="return false"> |
||||
|
<div class="content"> |
||||
|
{#if withArrow} |
||||
|
<div class:opened class="icon arrow"> |
||||
|
<i class="ri-arrow-right-s-line" /> |
||||
|
</div> |
||||
|
{/if} |
||||
|
{#if icon} |
||||
|
<div class="icon"><i class={icon} /></div> |
||||
|
{/if} |
||||
|
<div class="text">{text}</div> |
||||
|
{#if withActions} |
||||
|
<div class="actions"> |
||||
|
<slot /> |
||||
|
</div> |
||||
|
{/if} |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<style> |
||||
|
.nav-item { |
||||
|
border-radius: var(--border-radius-m); |
||||
|
cursor: pointer; |
||||
|
color: var(--grey-7); |
||||
|
} |
||||
|
.nav-item.border { |
||||
|
border-top: 1px solid var(--grey-1); |
||||
|
} |
||||
|
.nav-item.selected { |
||||
|
background-color: var(--grey-2); |
||||
|
color: var(--ink); |
||||
|
} |
||||
|
.nav-item:hover { |
||||
|
background-color: var(--grey-1); |
||||
|
} |
||||
|
.nav-item:hover .actions { |
||||
|
display: flex; |
||||
|
} |
||||
|
|
||||
|
.content { |
||||
|
padding: 0 var(--spacing-m); |
||||
|
height: 32px; |
||||
|
display: flex; |
||||
|
flex-direction: row; |
||||
|
justify-content: flex-start; |
||||
|
align-items: center; |
||||
|
gap: var(--spacing-xs); |
||||
|
} |
||||
|
|
||||
|
.icon { |
||||
|
font-size: 16px; |
||||
|
flex: 0 0 20px; |
||||
|
display: flex; |
||||
|
flex-direction: row; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
} |
||||
|
.icon.arrow { |
||||
|
margin: 0 -2px 0 -6px; |
||||
|
font-size: 12px; |
||||
|
} |
||||
|
.icon.arrow.opened { |
||||
|
transform: rotate(90deg); |
||||
|
} |
||||
|
.icon + .icon { |
||||
|
margin-left: -4px; |
||||
|
} |
||||
|
|
||||
|
.text { |
||||
|
flex: 1 1 auto; |
||||
|
font-weight: 500; |
||||
|
font-size: var(--font-size-xs); |
||||
|
} |
||||
|
|
||||
|
.actions { |
||||
|
display: none; |
||||
|
width: 20px; |
||||
|
height: 20px; |
||||
|
cursor: pointer; |
||||
|
position: relative; |
||||
|
flex-direction: row; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
} |
||||
|
</style> |
||||
@ -1,36 +0,0 @@ |
|||||
<script> |
|
||||
export let value |
|
||||
export let label |
|
||||
|
|
||||
const inputChanged = ev => { |
|
||||
try { |
|
||||
value = Number(ev.target.value) |
|
||||
} catch (_) { |
|
||||
value = null |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
let numberText = value === null || value === undefined ? "" : value.toString() |
|
||||
</script> |
|
||||
|
|
||||
<div class="numberbox"> |
|
||||
<label>{label}</label> |
|
||||
<input |
|
||||
class="budibase__input" |
|
||||
type="number" |
|
||||
{value} |
|
||||
on:change={inputChanged} /> |
|
||||
</div> |
|
||||
|
|
||||
<style> |
|
||||
.numberbox { |
|
||||
display: grid; |
|
||||
align-items: center; |
|
||||
} |
|
||||
|
|
||||
label { |
|
||||
font-size: 14px; |
|
||||
font-weight: 500; |
|
||||
margin-bottom: 8px; |
|
||||
} |
|
||||
</style> |
|
||||
@ -1,76 +0,0 @@ |
|||||
<script> |
|
||||
export let tabs = [] |
|
||||
export const selectTab = tabName => { |
|
||||
selected = tabName |
|
||||
selectedIndex = tabs.indexOf(selected) |
|
||||
} |
|
||||
|
|
||||
let selected = tabs.length > 0 && tabs[0] |
|
||||
let selectedIndex = 0 |
|
||||
|
|
||||
const isSelected = tab => selected === tab |
|
||||
</script> |
|
||||
|
|
||||
<div class="root"> |
|
||||
<div class="switcher"> |
|
||||
{#each tabs as tab} |
|
||||
<button class:selected={selected === tab} on:click={() => selectTab(tab)}> |
|
||||
{tab} |
|
||||
</button> |
|
||||
{/each} |
|
||||
</div> |
|
||||
|
|
||||
<div class="panel"> |
|
||||
{#if selectedIndex === 0} |
|
||||
<slot name="0" /> |
|
||||
{:else if selectedIndex === 1} |
|
||||
<slot name="1" /> |
|
||||
{:else if selectedIndex === 2} |
|
||||
<slot name="2" /> |
|
||||
{:else if selectedIndex === 3} |
|
||||
<slot name="3" /> |
|
||||
{/if} |
|
||||
</div> |
|
||||
</div> |
|
||||
|
|
||||
<style> |
|
||||
.root { |
|
||||
height: 100%; |
|
||||
display: flex; |
|
||||
flex-direction: column; |
|
||||
padding: 20px 20px; |
|
||||
border-left: solid 1px var(--grey-2); |
|
||||
box-sizing: border-box; |
|
||||
} |
|
||||
|
|
||||
.switcher { |
|
||||
display: flex; |
|
||||
margin: 0px 20px 20px 0px; |
|
||||
box-sizing: border-box; |
|
||||
} |
|
||||
|
|
||||
.switcher > button { |
|
||||
display: inline-block; |
|
||||
border: none; |
|
||||
margin: 0; |
|
||||
padding: 0; |
|
||||
cursor: pointer; |
|
||||
font-size: 18px; |
|
||||
font-weight: 600; |
|
||||
color: var(--grey-5); |
|
||||
margin-right: 20px; |
|
||||
background: none; |
|
||||
outline: none; |
|
||||
font-family: Inter; |
|
||||
} |
|
||||
|
|
||||
.switcher > .selected { |
|
||||
color: var(--ink); |
|
||||
} |
|
||||
|
|
||||
.panel { |
|
||||
min-height: 0; |
|
||||
height: 100%; |
|
||||
overflow-y: auto; |
|
||||
} |
|
||||
</style> |
|
||||
@ -1,3 +0,0 @@ |
|||||
import { flow } from "lodash/fp" |
|
||||
|
|
||||
export const pipe = (arg, funcs) => flow(funcs)(arg) |
|
||||
@ -1,13 +0,0 @@ |
|||||
import { eventHandlers } from "../../../../client/src/state/eventHandlers" |
|
||||
export { EVENT_TYPE_MEMBER_NAME } from "../../../../client/src/state/eventHandlers" |
|
||||
|
|
||||
export const allHandlers = () => { |
|
||||
const handlersObj = eventHandlers() |
|
||||
|
|
||||
const handlers = Object.keys(handlersObj).map(name => ({ |
|
||||
name, |
|
||||
...handlersObj[name], |
|
||||
})) |
|
||||
|
|
||||
return handlers |
|
||||
} |
|
||||
@ -1,32 +1,36 @@ |
|||||
<script> |
<script> |
||||
import SettingsModal from "./SettingsModal.svelte" |
import SettingsModal from "./SettingsModal.svelte" |
||||
import { SettingsIcon } from "components/common/Icons/" |
|
||||
import { Modal } from "@budibase/bbui" |
import { Modal } from "@budibase/bbui" |
||||
|
|
||||
let modal |
let modal |
||||
</script> |
</script> |
||||
|
|
||||
<span class="topnavitemright settings" on:click={modal.show}> |
<div class="topnavitemright settings" on:click={modal.show}> |
||||
<SettingsIcon /> |
<i class="ri-settings-3-line" /> |
||||
</span> |
</div> |
||||
<Modal bind:this={modal} width="600px"> |
<Modal bind:this={modal} width="600px"> |
||||
<SettingsModal /> |
<SettingsModal /> |
||||
</Modal> |
</Modal> |
||||
|
|
||||
<style> |
<style> |
||||
span:first-letter { |
i { |
||||
text-transform: capitalize; |
font-size: 18px; |
||||
|
color: var(--grey-7); |
||||
} |
} |
||||
.topnavitemright { |
.topnavitemright { |
||||
cursor: pointer; |
cursor: pointer; |
||||
color: var(--grey-7); |
color: var(--grey-7); |
||||
margin: 0 20px 0 0; |
margin: 0 12px 0 0; |
||||
font-weight: 500; |
font-weight: 500; |
||||
font-size: 1rem; |
font-size: 1rem; |
||||
height: 100%; |
|
||||
display: flex; |
display: flex; |
||||
flex: 1; |
flex-direction: row; |
||||
|
justify-content: center; |
||||
align-items: center; |
align-items: center; |
||||
box-sizing: border-box; |
height: 24px; |
||||
|
width: 24px; |
||||
|
} |
||||
|
.topnavitemright:hover i { |
||||
|
color: var(--ink); |
||||
} |
} |
||||
</style> |
</style> |
||||
|
|||||
@ -1,13 +0,0 @@ |
|||||
<h3> |
|
||||
<slot /> |
|
||||
</h3> |
|
||||
|
|
||||
<style> |
|
||||
h3 { |
|
||||
font-size: 18px; |
|
||||
font-weight: bold; |
|
||||
color: var(--ink); |
|
||||
margin-top: 40px; |
|
||||
margin-bottom: 20px; |
|
||||
} |
|
||||
</style> |
|
||||
@ -1,291 +1,291 @@ |
|||||
<script> |
<script> |
||||
import { DropdownMenu, Button, Input } from "@budibase/bbui" |
import { DropdownMenu, Button, Input } from "@budibase/bbui" |
||||
import { createEventDispatcher, tick } from "svelte" |
import { createEventDispatcher, tick } from "svelte" |
||||
|
|
||||
import icons from "./icons.js" |
import icons from "./icons.js" |
||||
|
|
||||
const dispatch = createEventDispatcher() |
const dispatch = createEventDispatcher() |
||||
|
|
||||
export let value = "" |
export let value = "" |
||||
export let maxIconsPerPage = 30 |
export let maxIconsPerPage = 30 |
||||
|
|
||||
let searchTerm = "" |
let searchTerm = "" |
||||
let selectedLetter = "A" |
let selectedLetter = "A" |
||||
|
|
||||
let currentPage = 1 |
let currentPage = 1 |
||||
let filteredIcons = findIconByTerm(selectedLetter) |
let filteredIcons = findIconByTerm(selectedLetter) |
||||
|
|
||||
$: dispatch("change", value) |
$: dispatch("change", value) |
||||
|
|
||||
const alphabet = [ |
const alphabet = [ |
||||
"A", |
"A", |
||||
"B", |
"B", |
||||
"C", |
"C", |
||||
"D", |
"D", |
||||
"E", |
"E", |
||||
"F", |
"F", |
||||
"G", |
"G", |
||||
"H", |
"H", |
||||
"I", |
"I", |
||||
"J", |
"J", |
||||
"K", |
"K", |
||||
"L", |
"L", |
||||
"M", |
"M", |
||||
"N", |
"N", |
||||
"O", |
"O", |
||||
"P", |
"P", |
||||
"Q", |
"Q", |
||||
"R", |
"R", |
||||
"S", |
"S", |
||||
"T", |
"T", |
||||
"U", |
"U", |
||||
"V", |
"V", |
||||
"W", |
"W", |
||||
"X", |
"X", |
||||
"Y", |
"Y", |
||||
"Z", |
"Z", |
||||
] |
] |
||||
let buttonAnchor, dropdown |
let buttonAnchor, dropdown |
||||
let loading = false |
let loading = false |
||||
|
|
||||
function findIconByTerm(term) { |
function findIconByTerm(term) { |
||||
const r = new RegExp(`\^${term}`, "i") |
const r = new RegExp(`\^${term}`, "i") |
||||
return icons.filter(i => r.test(i.label)) |
return icons.filter(i => r.test(i.label)) |
||||
} |
} |
||||
|
|
||||
async function switchLetter(letter) { |
async function switchLetter(letter) { |
||||
currentPage = 1 |
currentPage = 1 |
||||
searchTerm = "" |
searchTerm = "" |
||||
loading = true |
loading = true |
||||
selectedLetter = letter |
selectedLetter = letter |
||||
filteredIcons = findIconByTerm(letter) |
filteredIcons = findIconByTerm(letter) |
||||
await tick() //svg icons do not update without tick |
await tick() //svg icons do not update without tick |
||||
loading = false |
loading = false |
||||
} |
} |
||||
|
|
||||
async function findIconOnPage() { |
async function findIconOnPage() { |
||||
loading = true |
loading = true |
||||
const iconIdx = filteredIcons.findIndex(i => i.value === value) |
const iconIdx = filteredIcons.findIndex(i => i.value === value) |
||||
if (iconIdx !== -1) { |
if (iconIdx !== -1) { |
||||
currentPage = Math.ceil(iconIdx / maxIconsPerPage) |
currentPage = Math.ceil(iconIdx / maxIconsPerPage) |
||||
} |
} |
||||
await tick() //svg icons do not update without tick |
await tick() //svg icons do not update without tick |
||||
loading = false |
loading = false |
||||
} |
} |
||||
|
|
||||
async function setSelectedUI() { |
async function setSelectedUI() { |
||||
if (value) { |
if (value) { |
||||
const letter = displayValue.substring(0, 1) |
const letter = displayValue.substring(0, 1) |
||||
await switchLetter(letter) |
await switchLetter(letter) |
||||
await findIconOnPage() |
await findIconOnPage() |
||||
} |
} |
||||
} |
} |
||||
|
|
||||
async function pageClick(next) { |
async function pageClick(next) { |
||||
loading = true |
loading = true |
||||
if (next && currentPage < totalPages) { |
if (next && currentPage < totalPages) { |
||||
currentPage++ |
currentPage++ |
||||
} else if (!next && currentPage > 1) { |
} else if (!next && currentPage > 1) { |
||||
currentPage-- |
currentPage-- |
||||
} |
} |
||||
await tick() //svg icons do not update without tick |
await tick() //svg icons do not update without tick |
||||
loading = false |
loading = false |
||||
} |
} |
||||
|
|
||||
async function searchForIcon(e) { |
async function searchForIcon(e) { |
||||
currentPage = 1 |
currentPage = 1 |
||||
loading = true |
loading = true |
||||
filteredIcons = findIconByTerm(searchTerm) |
filteredIcons = findIconByTerm(searchTerm) |
||||
await tick() //svg icons do not update without tick |
await tick() //svg icons do not update without tick |
||||
loading = false |
loading = false |
||||
} |
} |
||||
|
|
||||
$: displayValue = value ? value.substring(7) : "Pick Icon" |
$: displayValue = value ? value.substring(7) : "Pick Icon" |
||||
|
|
||||
$: totalPages = Math.ceil(filteredIcons.length / maxIconsPerPage) |
$: totalPages = Math.ceil(filteredIcons.length / maxIconsPerPage) |
||||
$: pageEndIdx = maxIconsPerPage * currentPage |
$: pageEndIdx = maxIconsPerPage * currentPage |
||||
$: pagedIcons = filteredIcons.slice(pageEndIdx - maxIconsPerPage, pageEndIdx) |
$: pagedIcons = filteredIcons.slice(pageEndIdx - maxIconsPerPage, pageEndIdx) |
||||
|
|
||||
$: pagerText = `Page ${currentPage} of ${totalPages}` |
$: pagerText = `Page ${currentPage} of ${totalPages}` |
||||
</script> |
</script> |
||||
|
|
||||
<div bind:this={buttonAnchor}> |
<div bind:this={buttonAnchor}> |
||||
<Button secondary on:click={dropdown.show}>{displayValue}</Button> |
<Button secondary small on:click={dropdown.show}>{displayValue}</Button> |
||||
</div> |
</div> |
||||
<DropdownMenu |
<DropdownMenu |
||||
bind:this={dropdown} |
bind:this={dropdown} |
||||
on:open={setSelectedUI} |
on:open={setSelectedUI} |
||||
anchor={buttonAnchor}> |
anchor={buttonAnchor}> |
||||
<div class="container"> |
<div class="container"> |
||||
<div class="search-area"> |
<div class="search-area"> |
||||
<div class="alphabet-area"> |
<div class="alphabet-area"> |
||||
{#each alphabet as letter, idx} |
{#each alphabet as letter, idx} |
||||
<span |
<span |
||||
class="letter" |
class="letter" |
||||
class:letter-selected={letter === selectedLetter} |
class:letter-selected={letter === selectedLetter} |
||||
on:click={() => switchLetter(letter)}> |
on:click={() => switchLetter(letter)}> |
||||
{letter} |
{letter} |
||||
</span> |
</span> |
||||
{#if idx !== alphabet.length - 1}<span>-</span>{/if} |
{#if idx !== alphabet.length - 1}<span>-</span>{/if} |
||||
{/each} |
{/each} |
||||
</div> |
</div> |
||||
<div class="search-input"> |
<div class="search-input"> |
||||
<div class="input-wrapper"> |
<div class="input-wrapper"> |
||||
<Input bind:value={searchTerm} thin placeholder="Search Icon" /> |
<Input bind:value={searchTerm} thin placeholder="Search Icon" /> |
||||
</div> |
</div> |
||||
<Button secondary on:click={searchForIcon}>Search</Button> |
<Button secondary on:click={searchForIcon}>Search</Button> |
||||
</div> |
</div> |
||||
<div class="page-area"> |
<div class="page-area"> |
||||
<div class="pager"> |
<div class="pager"> |
||||
<span on:click={() => pageClick(false)}> |
<span on:click={() => pageClick(false)}> |
||||
<i class="page-btn fas fa-chevron-left" /> |
<i class="page-btn fas fa-chevron-left" /> |
||||
</span> |
</span> |
||||
<span>{pagerText}</span> |
<span>{pagerText}</span> |
||||
<span on:click={() => pageClick(true)}> |
<span on:click={() => pageClick(true)}> |
||||
<i class="page-btn fas fa-chevron-right" /> |
<i class="page-btn fas fa-chevron-right" /> |
||||
</span> |
</span> |
||||
</div> |
</div> |
||||
</div> |
</div> |
||||
</div> |
</div> |
||||
{#if pagedIcons.length > 0} |
{#if pagedIcons.length > 0} |
||||
<div class="icon-area"> |
<div class="icon-area"> |
||||
{#if !loading} |
{#if !loading} |
||||
{#each pagedIcons as icon} |
{#each pagedIcons as icon} |
||||
<div |
<div |
||||
class="icon-container" |
class="icon-container" |
||||
class:selected={value === icon.value} |
class:selected={value === icon.value} |
||||
on:click={() => (value = icon.value)}> |
on:click={() => (value = icon.value)}> |
||||
<div class="icon-preview"> |
<div class="icon-preview"> |
||||
<i class={`${icon.value} fa-3x`} /> |
<i class={`${icon.value} fa-3x`} /> |
||||
</div> |
</div> |
||||
<div class="icon-label">{icon.label}</div> |
<div class="icon-label">{icon.label}</div> |
||||
</div> |
</div> |
||||
{/each} |
{/each} |
||||
{/if} |
{/if} |
||||
</div> |
</div> |
||||
{:else} |
{:else} |
||||
<div class="no-icons"> |
<div class="no-icons"> |
||||
<h5> |
<h5> |
||||
{`There is no icons for this ${searchTerm ? 'search' : 'page'}`} |
{`There is no icons for this ${searchTerm ? 'search' : 'page'}`} |
||||
</h5> |
</h5> |
||||
</div> |
</div> |
||||
{/if} |
{/if} |
||||
</div> |
</div> |
||||
</DropdownMenu> |
</DropdownMenu> |
||||
|
|
||||
<style> |
<style> |
||||
.container { |
.container { |
||||
width: 610px; |
width: 610px; |
||||
height: 350px; |
height: 350px; |
||||
display: flex; |
display: flex; |
||||
flex-direction: column; |
flex-direction: column; |
||||
padding: 10px 0px 10px 15px; |
padding: 10px 0px 10px 15px; |
||||
overflow-x: hidden; |
overflow-x: hidden; |
||||
} |
} |
||||
|
|
||||
.search-area { |
.search-area { |
||||
flex: 0 0 80px; |
flex: 0 0 80px; |
||||
display: flex; |
display: flex; |
||||
flex-direction: column; |
flex-direction: column; |
||||
} |
} |
||||
|
|
||||
.icon-area { |
.icon-area { |
||||
flex: 1; |
flex: 1; |
||||
display: grid; |
display: grid; |
||||
grid-template-columns: repeat(5, 1fr); |
grid-template-columns: repeat(5, 1fr); |
||||
grid-gap: 5px; |
grid-gap: 5px; |
||||
justify-content: flex-start; |
justify-content: flex-start; |
||||
overflow-y: auto; |
overflow-y: auto; |
||||
overflow-x: hidden; |
overflow-x: hidden; |
||||
padding-right: 10px; |
padding-right: 10px; |
||||
} |
} |
||||
|
|
||||
.no-icons { |
.no-icons { |
||||
display: flex; |
display: flex; |
||||
justify-content: center; |
justify-content: center; |
||||
align-items: center; |
align-items: center; |
||||
} |
} |
||||
|
|
||||
.alphabet-area { |
.alphabet-area { |
||||
display: flex; |
display: flex; |
||||
flex-flow: row wrap; |
flex-flow: row wrap; |
||||
padding-bottom: 10px; |
padding-bottom: 10px; |
||||
padding-right: 15px; |
padding-right: 15px; |
||||
justify-content: space-around; |
justify-content: space-around; |
||||
} |
} |
||||
|
|
||||
.loading-container { |
.loading-container { |
||||
display: flex; |
display: flex; |
||||
justify-content: center; |
justify-content: center; |
||||
align-items: center; |
align-items: center; |
||||
} |
} |
||||
|
|
||||
.search-input { |
.search-input { |
||||
display: flex; |
display: flex; |
||||
flex-flow: row nowrap; |
flex-flow: row nowrap; |
||||
width: 100%; |
width: 100%; |
||||
padding-right: 15px; |
padding-right: 15px; |
||||
} |
} |
||||
|
|
||||
.input-wrapper { |
.input-wrapper { |
||||
width: 510px; |
width: 510px; |
||||
margin-right: 5px; |
margin-right: 5px; |
||||
} |
} |
||||
|
|
||||
.page-area { |
.page-area { |
||||
padding: 10px; |
padding: 10px; |
||||
display: flex; |
display: flex; |
||||
justify-content: center; |
justify-content: center; |
||||
} |
} |
||||
|
|
||||
.letter { |
.letter { |
||||
color: var(--blue); |
color: var(--blue); |
||||
} |
} |
||||
|
|
||||
.letter:hover { |
.letter:hover { |
||||
cursor: pointer; |
cursor: pointer; |
||||
text-decoration: underline; |
text-decoration: underline; |
||||
} |
} |
||||
|
|
||||
.letter-selected { |
.letter-selected { |
||||
text-decoration: underline; |
text-decoration: underline; |
||||
} |
} |
||||
|
|
||||
.icon-container { |
.icon-container { |
||||
height: 100px; |
height: 100px; |
||||
display: flex; |
display: flex; |
||||
justify-content: center; |
justify-content: center; |
||||
flex-direction: column; |
flex-direction: column; |
||||
border: var(--border-dark); |
border: var(--border-dark); |
||||
} |
} |
||||
|
|
||||
.icon-container:hover { |
.icon-container:hover { |
||||
cursor: pointer; |
cursor: pointer; |
||||
background: var(--grey-2); |
background: var(--grey-2); |
||||
} |
} |
||||
|
|
||||
.selected { |
.selected { |
||||
background: var(--grey-3); |
background: var(--grey-3); |
||||
} |
} |
||||
|
|
||||
.icon-preview { |
.icon-preview { |
||||
flex: 1; |
flex: 1; |
||||
display: flex; |
display: flex; |
||||
justify-content: center; |
justify-content: center; |
||||
align-items: center; |
align-items: center; |
||||
} |
} |
||||
|
|
||||
.icon-label { |
.icon-label { |
||||
flex: 0 0 20px; |
flex: 0 0 20px; |
||||
text-align: center; |
text-align: center; |
||||
font-size: 12px; |
font-size: 12px; |
||||
} |
} |
||||
|
|
||||
.page-btn { |
.page-btn { |
||||
color: var(--blue); |
color: var(--blue); |
||||
} |
} |
||||
|
|
||||
.page-btn:hover { |
.page-btn:hover { |
||||
cursor: pointer; |
cursor: pointer; |
||||
} |
} |
||||
</style> |
</style> |
||||
|
|||||
@ -1,50 +0,0 @@ |
|||||
<script> |
|
||||
export let item |
|
||||
</script> |
|
||||
|
|
||||
<div data-cy={item.name} class="item-item" on:click> |
|
||||
<div class="item-icon"><i class={item.icon} /></div> |
|
||||
<div class="item-text"> |
|
||||
<div class="item-name">{item.name}</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
|
|
||||
<style> |
|
||||
.item-item { |
|
||||
display: flex; |
|
||||
flex-direction: column; |
|
||||
cursor: pointer; |
|
||||
padding: 12px 16px 16px 16px; |
|
||||
height: 80px; |
|
||||
justify-content: center; |
|
||||
align-items: center; |
|
||||
background-color: var(--grey-1); |
|
||||
border-radius: 5px; |
|
||||
} |
|
||||
|
|
||||
.item-item:hover { |
|
||||
background: var(--grey-2); |
|
||||
transition: all 0.3s; |
|
||||
} |
|
||||
|
|
||||
.item-icon { |
|
||||
border-radius: 3px; |
|
||||
display: flex; |
|
||||
} |
|
||||
|
|
||||
.item-text { |
|
||||
display: flex; |
|
||||
flex-direction: column; |
|
||||
} |
|
||||
|
|
||||
.item-name { |
|
||||
font-size: 14px; |
|
||||
font-weight: 400; |
|
||||
text-align: center; |
|
||||
} |
|
||||
|
|
||||
i { |
|
||||
font-size: 24px; |
|
||||
color: var(--grey-7); |
|
||||
} |
|
||||
</style> |
|
||||
@ -1,52 +0,0 @@ |
|||||
<script> |
|
||||
import { createEventDispatcher } from "svelte" |
|
||||
import { store } from "builderStore" |
|
||||
import Item from "./Item.svelte" |
|
||||
|
|
||||
const dispatch = createEventDispatcher() |
|
||||
|
|
||||
export let list |
|
||||
|
|
||||
let category = list |
|
||||
|
|
||||
const handleClick = item => { |
|
||||
if (item.children && item.children.length > 0) { |
|
||||
list = item |
|
||||
} else { |
|
||||
dispatch("selectItem", item) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
const goBack = () => { |
|
||||
list = category |
|
||||
} |
|
||||
</script> |
|
||||
|
|
||||
{#if !list.isCategory} |
|
||||
<button class="back-button" on:click={goBack}>Back</button> |
|
||||
{/if} |
|
||||
{#each list.children as item} |
|
||||
{#if !item.showOnPages || item.showOnPages.includes($store.currentPageName)} |
|
||||
<Item {item} on:click={() => handleClick(item)} /> |
|
||||
{/if} |
|
||||
{/each} |
|
||||
|
|
||||
<style> |
|
||||
.back-button { |
|
||||
grid-column: 1 / span 2; |
|
||||
font-size: 14px; |
|
||||
text-align: center; |
|
||||
height: 36px; |
|
||||
border-radius: 5px; |
|
||||
border: solid 1px var(--grey-3); |
|
||||
background: white; |
|
||||
cursor: pointer; |
|
||||
font-weight: 500; |
|
||||
font-family: Inter; |
|
||||
transition: all 0.3ms; |
|
||||
} |
|
||||
|
|
||||
.back-button:hover { |
|
||||
background: var(--grey-1); |
|
||||
} |
|
||||
</style> |
|
||||
@ -1,88 +1,94 @@ |
|||||
<script> |
<script> |
||||
import { DataList } from "@budibase/bbui" |
import { DataList } from "@budibase/bbui" |
||||
import { createEventDispatcher } from "svelte" |
import { createEventDispatcher } from "svelte" |
||||
import { store, backendUiStore } from "builderStore" |
import { store, backendUiStore } from "builderStore" |
||||
import fetchBindableProperties from "builderStore/fetchBindableProperties" |
import fetchBindableProperties from "builderStore/fetchBindableProperties" |
||||
|
|
||||
const dispatch = createEventDispatcher() |
const dispatch = createEventDispatcher() |
||||
|
|
||||
export let value = "" |
export let value = "" |
||||
|
|
||||
$: urls = getUrls() |
$: urls = getUrls() |
||||
|
|
||||
const handleBlur = () => dispatch("change", value) |
const handleBlur = () => dispatch("change", value) |
||||
|
|
||||
// this will get urls of all screens, but only |
// this will get urls of all screens, but only |
||||
// choose detail screens that are usable in the current context |
// choose detail screens that are usable in the current context |
||||
// and substitute the :id param for the actual {{ ._id }} binding |
// and substitute the :id param for the actual {{ ._id }} binding |
||||
const getUrls = () => { |
const getUrls = () => { |
||||
const urls = [ |
const urls = [ |
||||
...$store.screens |
...$store.screens |
||||
.filter(screen => !screen.props._component.endsWith("/rowdetail")) |
.filter(screen => !screen.props._component.endsWith("/rowdetail")) |
||||
.map(screen => ({ |
.map(screen => ({ |
||||
name: screen.props._instanceName, |
name: screen.props._instanceName, |
||||
url: screen.route, |
url: screen.route, |
||||
sort: screen.props._component, |
sort: screen.props._component, |
||||
})), |
})), |
||||
] |
] |
||||
|
|
||||
const bindableProperties = fetchBindableProperties({ |
const bindableProperties = fetchBindableProperties({ |
||||
componentInstanceId: $store.currentComponentInfo._id, |
componentInstanceId: $store.currentComponentInfo._id, |
||||
components: $store.components, |
components: $store.components, |
||||
screen: $store.currentPreviewItem, |
screen: $store.currentPreviewItem, |
||||
tables: $backendUiStore.tables, |
tables: $backendUiStore.tables, |
||||
}) |
}) |
||||
|
|
||||
const detailScreens = $store.screens.filter(screen => |
const detailScreens = $store.screens.filter(screen => |
||||
screen.props._component.endsWith("/rowdetail") |
screen.props._component.endsWith("/rowdetail") |
||||
) |
) |
||||
|
|
||||
for (let detailScreen of detailScreens) { |
for (let detailScreen of detailScreens) { |
||||
const idBinding = bindableProperties.find(p => { |
const idBinding = bindableProperties.find(p => { |
||||
if ( |
if ( |
||||
p.type === "context" && |
p.type === "context" && |
||||
p.runtimeBinding.endsWith("._id") && |
p.runtimeBinding.endsWith("._id") && |
||||
p.table |
p.table |
||||
) { |
) { |
||||
const tableId = |
const tableId = |
||||
typeof p.table === "string" ? p.table : p.table.tableId |
typeof p.table === "string" ? p.table : p.table.tableId |
||||
return tableId === detailScreen.props.table |
return tableId === detailScreen.props.table |
||||
} |
} |
||||
return false |
return false |
||||
}) |
}) |
||||
|
|
||||
if (idBinding) { |
if (idBinding) { |
||||
urls.push({ |
urls.push({ |
||||
name: detailScreen.props._instanceName, |
name: detailScreen.props._instanceName, |
||||
url: detailScreen.route.replace( |
url: detailScreen.route.replace( |
||||
":id", |
":id", |
||||
`{{ ${idBinding.runtimeBinding} }}` |
`{{ ${idBinding.runtimeBinding} }}` |
||||
), |
), |
||||
sort: detailScreen.props._component, |
sort: detailScreen.props._component, |
||||
}) |
}) |
||||
} |
} |
||||
} |
} |
||||
|
|
||||
return urls |
return urls |
||||
} |
} |
||||
</script> |
</script> |
||||
|
|
||||
<div> |
<div> |
||||
<DataList editable secondary thin on:blur={handleBlur} on:change bind:value> |
<DataList |
||||
<option value="" /> |
editable |
||||
{#each urls as url} |
secondary |
||||
<option value={url.url}>{url.name}</option> |
extraThin |
||||
{/each} |
on:blur={handleBlur} |
||||
</DataList> |
on:change |
||||
</div> |
bind:value> |
||||
|
<option value="" /> |
||||
<style> |
{#each urls as url} |
||||
div { |
<option value={url.url}>{url.name}</option> |
||||
flex: 1 1 auto; |
{/each} |
||||
display: flex; |
</DataList> |
||||
flex-direction: row; |
</div> |
||||
} |
|
||||
div :global(> div) { |
<style> |
||||
flex: 1 1 auto; |
div { |
||||
} |
flex: 1 1 auto; |
||||
</style> |
display: flex; |
||||
|
flex-direction: row; |
||||
|
} |
||||
|
div :global(> div) { |
||||
|
flex: 1 1 auto; |
||||
|
} |
||||
|
</style> |
||||
|
|||||
@ -1,163 +1,163 @@ |
|||||
<script> |
<script> |
||||
import { Button, Icon, DropdownMenu, Spacer, Heading } from "@budibase/bbui" |
import { Button, Icon, DropdownMenu, Spacer, Heading } from "@budibase/bbui" |
||||
import { createEventDispatcher } from "svelte" |
import { createEventDispatcher } from "svelte" |
||||
import { store, backendUiStore } from "builderStore" |
import { store, backendUiStore } from "builderStore" |
||||
import fetchBindableProperties from "../../builderStore/fetchBindableProperties" |
import fetchBindableProperties from "../../builderStore/fetchBindableProperties" |
||||
|
|
||||
const dispatch = createEventDispatcher() |
const dispatch = createEventDispatcher() |
||||
let anchorRight, dropdownRight |
let anchorRight, dropdownRight |
||||
|
|
||||
export let value = {} |
export let value = {} |
||||
|
|
||||
function handleSelected(selected) { |
function handleSelected(selected) { |
||||
dispatch("change", selected) |
dispatch("change", selected) |
||||
dropdownRight.hide() |
dropdownRight.hide() |
||||
} |
} |
||||
|
|
||||
$: tables = $backendUiStore.tables.map(m => ({ |
$: tables = $backendUiStore.tables.map(m => ({ |
||||
label: m.name, |
label: m.name, |
||||
name: `all_${m._id}`, |
name: `all_${m._id}`, |
||||
tableId: m._id, |
tableId: m._id, |
||||
type: "table", |
type: "table", |
||||
})) |
})) |
||||
|
|
||||
$: views = $backendUiStore.tables.reduce((acc, cur) => { |
$: views = $backendUiStore.tables.reduce((acc, cur) => { |
||||
let viewsArr = Object.entries(cur.views).map(([key, value]) => ({ |
let viewsArr = Object.entries(cur.views).map(([key, value]) => ({ |
||||
label: key, |
label: key, |
||||
name: key, |
name: key, |
||||
...value, |
...value, |
||||
type: "view", |
type: "view", |
||||
})) |
})) |
||||
return [...acc, ...viewsArr] |
return [...acc, ...viewsArr] |
||||
}, []) |
}, []) |
||||
|
|
||||
$: bindableProperties = fetchBindableProperties({ |
$: bindableProperties = fetchBindableProperties({ |
||||
componentInstanceId: $store.currentComponentInfo._id, |
componentInstanceId: $store.currentComponentInfo._id, |
||||
components: $store.components, |
components: $store.components, |
||||
screen: $store.currentPreviewItem, |
screen: $store.currentPreviewItem, |
||||
tables: $backendUiStore.tables, |
tables: $backendUiStore.tables, |
||||
}) |
}) |
||||
|
|
||||
$: links = bindableProperties |
$: links = bindableProperties |
||||
.filter(x => x.fieldSchema.type === "link") |
.filter(x => x.fieldSchema?.type === "link") |
||||
.map(property => ({ |
.map(property => ({ |
||||
label: property.readableBinding, |
label: property.readableBinding, |
||||
fieldName: property.fieldSchema.name, |
fieldName: property.fieldSchema.name, |
||||
name: `all_${property.fieldSchema.tableId}`, |
name: `all_${property.fieldSchema.tableId}`, |
||||
tableId: property.fieldSchema.tableId, |
tableId: property.fieldSchema.tableId, |
||||
type: "link", |
type: "link", |
||||
})) |
})) |
||||
</script> |
</script> |
||||
|
|
||||
<div |
<div |
||||
class="dropdownbutton" |
class="dropdownbutton" |
||||
bind:this={anchorRight} |
bind:this={anchorRight} |
||||
on:click={dropdownRight.show}> |
on:click={dropdownRight.show}> |
||||
<span>{value.label ? value.label : 'Table / View'}</span> |
<span>{value.label ? value.label : 'Table / View'}</span> |
||||
<Icon name="arrowdown" /> |
<Icon name="arrowdown" /> |
||||
</div> |
</div> |
||||
<DropdownMenu bind:this={dropdownRight} anchor={anchorRight}> |
<DropdownMenu bind:this={dropdownRight} anchor={anchorRight}> |
||||
<div class="dropdown"> |
<div class="dropdown"> |
||||
<div class="title"> |
<div class="title"> |
||||
<Heading extraSmall>Tables</Heading> |
<Heading extraSmall>Tables</Heading> |
||||
</div> |
</div> |
||||
<ul> |
<ul> |
||||
{#each tables as table} |
{#each tables as table} |
||||
<li |
<li |
||||
class:selected={value === table} |
class:selected={value === table} |
||||
on:click={() => handleSelected(table)}> |
on:click={() => handleSelected(table)}> |
||||
{table.label} |
{table.label} |
||||
</li> |
</li> |
||||
{/each} |
{/each} |
||||
</ul> |
</ul> |
||||
<hr /> |
<hr /> |
||||
<div class="title"> |
<div class="title"> |
||||
<Heading extraSmall>Views</Heading> |
<Heading extraSmall>Views</Heading> |
||||
</div> |
</div> |
||||
<ul> |
<ul> |
||||
{#each views as view} |
{#each views as view} |
||||
<li |
<li |
||||
class:selected={value === view} |
class:selected={value === view} |
||||
on:click={() => handleSelected(view)}> |
on:click={() => handleSelected(view)}> |
||||
{view.label} |
{view.label} |
||||
</li> |
</li> |
||||
{/each} |
{/each} |
||||
</ul> |
</ul> |
||||
<hr /> |
<hr /> |
||||
<div class="title"> |
<div class="title"> |
||||
<Heading extraSmall>Relationships</Heading> |
<Heading extraSmall>Relationships</Heading> |
||||
</div> |
</div> |
||||
<ul> |
<ul> |
||||
{#each links as link} |
{#each links as link} |
||||
<li |
<li |
||||
class:selected={value === link} |
class:selected={value === link} |
||||
on:click={() => handleSelected(link)}> |
on:click={() => handleSelected(link)}> |
||||
{link.label} |
{link.label} |
||||
</li> |
</li> |
||||
{/each} |
{/each} |
||||
</ul> |
</ul> |
||||
</div> |
</div> |
||||
</DropdownMenu> |
</DropdownMenu> |
||||
|
|
||||
<style> |
<style> |
||||
.dropdownbutton { |
.dropdownbutton { |
||||
background-color: var(--grey-2); |
background-color: var(--grey-2); |
||||
border: var(--border-transparent); |
border: var(--border-transparent); |
||||
padding: var(--spacing-m); |
padding: var(--spacing-s) var(--spacing-m); |
||||
border-radius: var(--border-radius-m); |
border-radius: var(--border-radius-m); |
||||
display: flex; |
display: flex; |
||||
flex-direction: row; |
flex-direction: row; |
||||
justify-content: space-between; |
justify-content: space-between; |
||||
align-items: center; |
align-items: center; |
||||
overflow: hidden; |
overflow: hidden; |
||||
flex: 1 1 auto; |
flex: 1 1 auto; |
||||
} |
} |
||||
.dropdownbutton:hover { |
.dropdownbutton:hover { |
||||
cursor: pointer; |
cursor: pointer; |
||||
background-color: var(--grey-3); |
background-color: var(--grey-3); |
||||
} |
} |
||||
.dropdownbutton span { |
.dropdownbutton span { |
||||
white-space: nowrap; |
white-space: nowrap; |
||||
text-overflow: ellipsis; |
text-overflow: ellipsis; |
||||
overflow: hidden; |
overflow: hidden; |
||||
flex: 1 1 auto; |
flex: 1 1 auto; |
||||
text-align: left; |
text-align: left; |
||||
font-size: var(--font-size-xs); |
font-size: var(--font-size-xs); |
||||
} |
} |
||||
.dropdownbutton :global(svg) { |
.dropdownbutton :global(svg) { |
||||
margin: -4px 0; |
margin: -4px 0; |
||||
} |
} |
||||
|
|
||||
.dropdown { |
.dropdown { |
||||
padding: var(--spacing-m) 0; |
padding: var(--spacing-m) 0; |
||||
z-index: 99999999; |
z-index: 99999999; |
||||
} |
} |
||||
.title { |
.title { |
||||
padding: 0 var(--spacing-m) var(--spacing-xs) var(--spacing-m); |
padding: 0 var(--spacing-m) var(--spacing-xs) var(--spacing-m); |
||||
} |
} |
||||
|
|
||||
hr { |
hr { |
||||
margin: var(--spacing-m) 0 var(--spacing-xl) 0; |
margin: var(--spacing-m) 0 var(--spacing-xl) 0; |
||||
} |
} |
||||
|
|
||||
ul { |
ul { |
||||
list-style: none; |
list-style: none; |
||||
padding-left: 0px; |
padding-left: 0px; |
||||
margin: 0px; |
margin: 0px; |
||||
} |
} |
||||
|
|
||||
li { |
li { |
||||
cursor: pointer; |
cursor: pointer; |
||||
margin: 0px; |
margin: 0px; |
||||
padding: var(--spacing-s) var(--spacing-m); |
padding: var(--spacing-s) var(--spacing-m); |
||||
font-size: var(--font-size-xs); |
font-size: var(--font-size-xs); |
||||
} |
} |
||||
|
|
||||
.selected { |
.selected { |
||||
background-color: var(--grey-4); |
background-color: var(--grey-4); |
||||
} |
} |
||||
|
|
||||
li:hover { |
li:hover { |
||||
background-color: var(--grey-4); |
background-color: var(--grey-4); |
||||
} |
} |
||||
</style> |
</style> |
||||
|
|||||
@ -1,193 +0,0 @@ |
|||||
<script> |
|
||||
import ComponentsHierarchy from "./ComponentsHierarchy.svelte" |
|
||||
import ComponentsHierarchyChildren from "./ComponentsHierarchyChildren.svelte" |
|
||||
import PageLayout from "./PageLayout.svelte" |
|
||||
import PagesList from "./PagesList.svelte" |
|
||||
import { store } from "builderStore" |
|
||||
import NewScreenModal from "./NewScreenModal.svelte" |
|
||||
import CurrentItemPreview from "./AppPreview/CurrentItemPreview.svelte" |
|
||||
import SettingsView from "./SettingsView.svelte" |
|
||||
import ComponentsPaneSwitcher from "./ComponentsPaneSwitcher.svelte" |
|
||||
import ConfirmDialog from "components/common/ConfirmDialog.svelte" |
|
||||
import { last } from "lodash/fp" |
|
||||
import { AddIcon } from "components/common/Icons" |
|
||||
import { Modal } from "@budibase/bbui" |
|
||||
|
|
||||
let newScreenPicker |
|
||||
let confirmDeleteDialog |
|
||||
let componentToDelete = "" |
|
||||
let settingsView |
|
||||
let modal |
|
||||
|
|
||||
const settings = () => { |
|
||||
settingsView.show() |
|
||||
} |
|
||||
|
|
||||
const lastPartOfName = c => (c ? last(c.split("/")) : "") |
|
||||
</script> |
|
||||
|
|
||||
<div class="root"> |
|
||||
<div class="ui-nav"> |
|
||||
<div class="pages-list-container"> |
|
||||
<div class="nav-header"> |
|
||||
<span class="navigator-title">Navigator</span> |
|
||||
<span class="components-nav-page">Pages</span> |
|
||||
</div> |
|
||||
<div class="nav-items-container"> |
|
||||
<PagesList /> |
|
||||
</div> |
|
||||
</div> |
|
||||
<PageLayout layout={$store.pages[$store.currentPageName]} /> |
|
||||
<div class="components-list-container"> |
|
||||
<div class="nav-group-header"> |
|
||||
<span class="components-nav-header" style="margin-top: 0;"> |
|
||||
Screens |
|
||||
</span> |
|
||||
<div> |
|
||||
<button on:click={modal.show}> |
|
||||
<AddIcon /> |
|
||||
</button> |
|
||||
</div> |
|
||||
</div> |
|
||||
<div class="nav-items-container"> |
|
||||
<ComponentsHierarchy screens={$store.screens} /> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
<div class="preview-pane"> |
|
||||
<CurrentItemPreview /> |
|
||||
</div> |
|
||||
{#if $store.currentFrontEndType === 'screen' || $store.currentFrontEndType === 'page'} |
|
||||
<div class="components-pane"> |
|
||||
<ComponentsPaneSwitcher /> |
|
||||
</div> |
|
||||
{/if} |
|
||||
</div> |
|
||||
|
|
||||
<Modal bind:this={modal}> |
|
||||
<NewScreenModal /> |
|
||||
</Modal> |
|
||||
|
|
||||
<SettingsView bind:this={settingsView} /> |
|
||||
|
|
||||
<style> |
|
||||
button { |
|
||||
cursor: pointer; |
|
||||
outline: none; |
|
||||
border: none; |
|
||||
border-radius: 5px; |
|
||||
width: 20px; |
|
||||
padding-bottom: 10px; |
|
||||
display: flex; |
|
||||
justify-content: center; |
|
||||
align-items: center; |
|
||||
padding: 0; |
|
||||
} |
|
||||
|
|
||||
.root { |
|
||||
display: grid; |
|
||||
grid-template-columns: 300px 1fr 300px; |
|
||||
height: 100%; |
|
||||
width: 100%; |
|
||||
background: var(--grey-1); |
|
||||
} |
|
||||
|
|
||||
.ui-nav { |
|
||||
grid-column: 1; |
|
||||
background-color: var(--white); |
|
||||
height: calc(100vh - 49px); |
|
||||
padding: 0; |
|
||||
display: flex; |
|
||||
flex-direction: column; |
|
||||
} |
|
||||
|
|
||||
.preview-pane { |
|
||||
grid-column: 2; |
|
||||
margin: 40px; |
|
||||
background: #fff; |
|
||||
border-radius: 5px; |
|
||||
box-shadow: 0 0px 6px rgba(0, 0, 0, 0.05); |
|
||||
} |
|
||||
|
|
||||
.components-pane { |
|
||||
grid-column: 3; |
|
||||
background-color: var(--white); |
|
||||
height: calc(100vh - 49px); |
|
||||
} |
|
||||
|
|
||||
.components-nav-page { |
|
||||
font-size: 13px; |
|
||||
color: var(--ink); |
|
||||
text-transform: uppercase; |
|
||||
padding-left: 20px; |
|
||||
margin-top: 20px; |
|
||||
font-weight: 600; |
|
||||
opacity: 0.4; |
|
||||
} |
|
||||
|
|
||||
.components-nav-header { |
|
||||
font-size: 13px; |
|
||||
color: var(--ink); |
|
||||
text-transform: uppercase; |
|
||||
margin-top: 20px; |
|
||||
font-weight: 600; |
|
||||
opacity: 0.4; |
|
||||
} |
|
||||
|
|
||||
.nav-header { |
|
||||
display: flex; |
|
||||
flex-direction: column; |
|
||||
margin-top: 20px; |
|
||||
} |
|
||||
|
|
||||
.nav-items-container { |
|
||||
padding: 1rem 0rem 0rem 0rem; |
|
||||
} |
|
||||
|
|
||||
.nav-group-header { |
|
||||
display: flex; |
|
||||
padding: 0px 20px 0px 20px; |
|
||||
font-size: 0.9rem; |
|
||||
font-weight: bold; |
|
||||
justify-content: space-between; |
|
||||
align-items: center; |
|
||||
} |
|
||||
|
|
||||
.nav-group-header > div:nth-child(1) { |
|
||||
padding: 0rem 0.5rem 0rem 0rem; |
|
||||
vertical-align: bottom; |
|
||||
grid-column-start: icon; |
|
||||
margin-right: 5px; |
|
||||
} |
|
||||
|
|
||||
.nav-group-header > span:nth-child(3) { |
|
||||
margin-left: 5px; |
|
||||
vertical-align: bottom; |
|
||||
grid-column-start: title; |
|
||||
margin-top: auto; |
|
||||
} |
|
||||
|
|
||||
.nav-group-header > div:nth-child(3) { |
|
||||
vertical-align: bottom; |
|
||||
grid-column-start: button; |
|
||||
cursor: pointer; |
|
||||
color: var(--blue); |
|
||||
} |
|
||||
|
|
||||
.nav-group-header > div:nth-child(3):hover { |
|
||||
color: var(--blue); |
|
||||
} |
|
||||
|
|
||||
.navigator-title { |
|
||||
font-size: 14px; |
|
||||
color: var(--ink); |
|
||||
font-weight: 600; |
|
||||
text-transform: uppercase; |
|
||||
padding: 0 20px 20px 20px; |
|
||||
line-height: 1rem !important; |
|
||||
} |
|
||||
|
|
||||
.components-list-container { |
|
||||
padding: 10px 0px 0 0; |
|
||||
} |
|
||||
</style> |
|
||||
File diff suppressed because it is too large
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue