Merge branch 'develop' of github.com:Budibase/budibase into feature/multi-tenants
This commit is contained in:
commit
1b40740709
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"version": "0.9.79-alpha.4",
|
||||
"version": "0.9.80-alpha.9",
|
||||
"npmClient": "yarn",
|
||||
"packages": [
|
||||
"packages/*"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@budibase/auth",
|
||||
"version": "0.9.79-alpha.4",
|
||||
"version": "0.9.80-alpha.9",
|
||||
"description": "Authentication middlewares for budibase builder and apps",
|
||||
"main": "src/index.js",
|
||||
"author": "Budibase",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "@budibase/bbui",
|
||||
"description": "A UI solution used in the different Budibase projects.",
|
||||
"version": "0.9.79-alpha.4",
|
||||
"version": "0.9.80-alpha.9",
|
||||
"license": "AGPL-3.0",
|
||||
"svelte": "src/index.js",
|
||||
"module": "dist/bbui.es.js",
|
||||
|
|
|
@ -115,7 +115,6 @@
|
|||
}
|
||||
|
||||
.spectrum-Modal {
|
||||
background: var(--background);
|
||||
overflow: visible;
|
||||
max-height: none;
|
||||
margin: 40px 0;
|
||||
|
|
|
@ -88,7 +88,6 @@
|
|||
display: grid;
|
||||
position: relative;
|
||||
gap: var(--spacing-xl);
|
||||
color: var(--ink);
|
||||
}
|
||||
|
||||
.spectrum-Dialog-content {
|
||||
|
@ -106,13 +105,8 @@
|
|||
position: absolute;
|
||||
top: 15px;
|
||||
right: 15px;
|
||||
color: var(--ink);
|
||||
font-size: var(--font-size-m);
|
||||
}
|
||||
.close-icon:hover {
|
||||
color: var(--grey-6);
|
||||
cursor: pointer;
|
||||
}
|
||||
.close-icon :global(svg) {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@budibase/builder",
|
||||
"version": "0.9.79-alpha.4",
|
||||
"version": "0.9.80-alpha.9",
|
||||
"license": "AGPL-3.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
@ -65,10 +65,10 @@
|
|||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@budibase/bbui": "^0.9.79-alpha.4",
|
||||
"@budibase/client": "^0.9.79-alpha.4",
|
||||
"@budibase/bbui": "^0.9.80-alpha.9",
|
||||
"@budibase/client": "^0.9.80-alpha.9",
|
||||
"@budibase/colorpicker": "1.1.2",
|
||||
"@budibase/string-templates": "^0.9.79-alpha.4",
|
||||
"@budibase/string-templates": "^0.9.80-alpha.9",
|
||||
"@sentry/browser": "5.19.1",
|
||||
"@spectrum-css/page": "^3.0.1",
|
||||
"@spectrum-css/vars": "^3.0.1",
|
||||
|
|
|
@ -530,6 +530,11 @@ export const getFrontendStore = () => {
|
|||
selected._styles = { normal: {}, hover: {}, active: {} }
|
||||
await store.actions.preview.saveSelected()
|
||||
},
|
||||
updateConditions: async conditions => {
|
||||
const selected = get(selectedComponent)
|
||||
selected._conditions = conditions
|
||||
await store.actions.preview.saveSelected()
|
||||
},
|
||||
updateProp: async (name, value) => {
|
||||
let component = get(selectedComponent)
|
||||
if (!name || !component) {
|
||||
|
|
|
@ -6,8 +6,10 @@
|
|||
import ErrorsBox from "components/common/ErrorsBox.svelte"
|
||||
import { roles } from "stores/backend"
|
||||
|
||||
const BASE_ROLE = { _id: "", inherits: "BASIC", permissionId: "Read/Write" }
|
||||
|
||||
let basePermissions = []
|
||||
let selectedRole = {}
|
||||
let selectedRole = BASE_ROLE
|
||||
let errors = []
|
||||
let builtInRoles = ["Admin", "Power", "Basic", "Public"]
|
||||
// Don't allow editing of public role
|
||||
|
@ -15,6 +17,11 @@
|
|||
$: selectedRoleId = selectedRole._id
|
||||
$: otherRoles = editableRoles.filter(role => role._id !== selectedRoleId)
|
||||
$: isCreating = selectedRoleId == null || selectedRoleId === ""
|
||||
$: valid =
|
||||
selectedRole.name &&
|
||||
selectedRole.inherits &&
|
||||
selectedRole.permissionId &&
|
||||
!builtInRoles.includes(selectedRole.name)
|
||||
|
||||
const fetchBasePermissions = async () => {
|
||||
const permissionsResponse = await api.get("/api/permission/builtin")
|
||||
|
@ -32,7 +39,7 @@
|
|||
permissionId: role.permissionId ?? "",
|
||||
}
|
||||
} else {
|
||||
selectedRole = { _id: "", inherits: "", permissionId: "" }
|
||||
selectedRole = BASE_ROLE
|
||||
}
|
||||
errors = []
|
||||
}
|
||||
|
@ -88,6 +95,7 @@
|
|||
title="Edit Roles"
|
||||
confirmText={isCreating ? "Create" : "Save"}
|
||||
onConfirm={saveRole}
|
||||
disabled={!valid}
|
||||
>
|
||||
{#if errors.length}
|
||||
<ErrorsBox {errors} />
|
||||
|
@ -115,7 +123,7 @@
|
|||
options={otherRoles}
|
||||
getOptionValue={role => role._id}
|
||||
getOptionLabel={role => role.name}
|
||||
placeholder="None"
|
||||
disabled={builtInRoles.includes(selectedRole.name)}
|
||||
/>
|
||||
<Select
|
||||
label="Base Permissions"
|
||||
|
@ -123,7 +131,7 @@
|
|||
options={basePermissions}
|
||||
getOptionValue={x => x._id}
|
||||
getOptionLabel={x => x.name}
|
||||
placeholder="Choose permissions"
|
||||
disabled={builtInRoles.includes(selectedRole.name)}
|
||||
/>
|
||||
{/if}
|
||||
<div slot="footer">
|
||||
|
|
|
@ -1,32 +1,12 @@
|
|||
<script>
|
||||
import { isEmpty } from "lodash/fp"
|
||||
import { Checkbox, Input, Select, DetailSummary } from "@budibase/bbui"
|
||||
import { Input, DetailSummary } from "@budibase/bbui"
|
||||
import { store } from "builderStore"
|
||||
import PropertyControl from "./PropertyControls/PropertyControl.svelte"
|
||||
import LayoutSelect from "./PropertyControls/LayoutSelect.svelte"
|
||||
import RoleSelect from "./PropertyControls/RoleSelect.svelte"
|
||||
import TableSelect from "./PropertyControls/TableSelect.svelte"
|
||||
import DataSourceSelect from "./PropertyControls/DataSourceSelect.svelte"
|
||||
import DataProviderSelect from "./PropertyControls/DataProviderSelect.svelte"
|
||||
import FieldSelect from "./PropertyControls/FieldSelect.svelte"
|
||||
import MultiFieldSelect from "./PropertyControls/MultiFieldSelect.svelte"
|
||||
import SchemaSelect from "./PropertyControls/SchemaSelect.svelte"
|
||||
import SectionSelect from "./PropertyControls/SectionSelect.svelte"
|
||||
import NavigationEditor from "./PropertyControls/NavigationEditor/NavigationEditor.svelte"
|
||||
import EventsEditor from "./PropertyControls/EventsEditor"
|
||||
import FilterEditor from "./PropertyControls/FilterEditor/FilterEditor.svelte"
|
||||
import { IconSelect } from "./PropertyControls/IconSelect"
|
||||
import StringFieldSelect from "./PropertyControls/StringFieldSelect.svelte"
|
||||
import NumberFieldSelect from "./PropertyControls/NumberFieldSelect.svelte"
|
||||
import OptionsFieldSelect from "./PropertyControls/OptionsFieldSelect.svelte"
|
||||
import BooleanFieldSelect from "./PropertyControls/BooleanFieldSelect.svelte"
|
||||
import LongFormFieldSelect from "./PropertyControls/LongFormFieldSelect.svelte"
|
||||
import DateTimeFieldSelect from "./PropertyControls/DateTimeFieldSelect.svelte"
|
||||
import AttachmentFieldSelect from "./PropertyControls/AttachmentFieldSelect.svelte"
|
||||
import RelationshipFieldSelect from "./PropertyControls/RelationshipFieldSelect.svelte"
|
||||
import ResetFieldsButton from "./PropertyControls/ResetFieldsButton.svelte"
|
||||
import ColorPicker from "./PropertyControls/ColorPicker.svelte"
|
||||
import URLSelect from "./PropertyControls/URLSelect.svelte"
|
||||
import { getComponentForSettingType } from "./PropertyControls/componentSettings"
|
||||
|
||||
export let componentDefinition
|
||||
export let componentInstance
|
||||
|
@ -45,40 +25,9 @@
|
|||
$: assetDefinition = isLayout ? layoutDefinition : screenDefinition
|
||||
|
||||
const updateProp = store.actions.components.updateProp
|
||||
const controlMap = {
|
||||
text: Input,
|
||||
select: Select,
|
||||
dataSource: DataSourceSelect,
|
||||
dataProvider: DataProviderSelect,
|
||||
boolean: Checkbox,
|
||||
number: Input,
|
||||
event: EventsEditor,
|
||||
table: TableSelect,
|
||||
color: ColorPicker,
|
||||
icon: IconSelect,
|
||||
field: FieldSelect,
|
||||
multifield: MultiFieldSelect,
|
||||
schema: SchemaSelect,
|
||||
section: SectionSelect,
|
||||
navigation: NavigationEditor,
|
||||
filter: FilterEditor,
|
||||
url: URLSelect,
|
||||
"field/string": StringFieldSelect,
|
||||
"field/number": NumberFieldSelect,
|
||||
"field/options": OptionsFieldSelect,
|
||||
"field/boolean": BooleanFieldSelect,
|
||||
"field/longform": LongFormFieldSelect,
|
||||
"field/datetime": DateTimeFieldSelect,
|
||||
"field/attachment": AttachmentFieldSelect,
|
||||
"field/link": RelationshipFieldSelect,
|
||||
}
|
||||
|
||||
const getControl = type => {
|
||||
return controlMap[type]
|
||||
}
|
||||
|
||||
const canRenderControl = setting => {
|
||||
const control = getControl(setting?.type)
|
||||
const control = getComponentForSettingType(setting?.type)
|
||||
if (!control) {
|
||||
return false
|
||||
}
|
||||
|
@ -101,11 +50,11 @@
|
|||
/>
|
||||
{/if}
|
||||
{#if settings && settings.length > 0}
|
||||
{#each settings as setting (`${componentInstance._id}-${setting.key}`)}
|
||||
{#each settings as setting}
|
||||
{#if canRenderControl(setting)}
|
||||
<PropertyControl
|
||||
type={setting.type}
|
||||
control={getControl(setting.type)}
|
||||
control={getComponentForSettingType(setting.type)}
|
||||
label={setting.label}
|
||||
key={setting.key}
|
||||
value={componentInstance[setting.key] ??
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
<script>
|
||||
import { DetailSummary, ActionButton, Drawer, Button } from "@budibase/bbui"
|
||||
import { store } from "builderStore"
|
||||
import ConditionalUIDrawer from "./PropertyControls/ConditionalUIDrawer.svelte"
|
||||
|
||||
export let componentInstance
|
||||
|
||||
let tempValue
|
||||
let drawer
|
||||
|
||||
const openDrawer = () => {
|
||||
tempValue = JSON.parse(JSON.stringify(componentInstance?._conditions ?? []))
|
||||
drawer.show()
|
||||
}
|
||||
|
||||
const save = () => {
|
||||
store.actions.components.updateConditions(tempValue)
|
||||
drawer.hide()
|
||||
}
|
||||
</script>
|
||||
|
||||
<DetailSummary
|
||||
name={`Conditions${componentInstance?._conditions ? " *" : ""}`}
|
||||
collapsible={false}
|
||||
>
|
||||
<div>
|
||||
<ActionButton on:click={openDrawer}>Configure conditions</ActionButton>
|
||||
</div>
|
||||
</DetailSummary>
|
||||
<Drawer bind:this={drawer} title="Conditions">
|
||||
<svelte:fragment slot="description">
|
||||
Show, hide and update components in response to conditions being met.
|
||||
</svelte:fragment>
|
||||
<Button cta slot="buttons" on:click={() => save()}>Save</Button>
|
||||
<ConditionalUIDrawer slot="body" bind:conditions={tempValue} />
|
||||
</Drawer>
|
|
@ -5,6 +5,7 @@
|
|||
import ComponentSettingsSection from "./ComponentSettingsSection.svelte"
|
||||
import DesignSection from "./DesignSection.svelte"
|
||||
import CustomStylesSection from "./CustomStylesSection.svelte"
|
||||
import ConditionalUISection from "./ConditionalUISection.svelte"
|
||||
|
||||
$: componentInstance = $selectedComponent
|
||||
$: componentDefinition = store.actions.components.getDefinition(
|
||||
|
@ -15,10 +16,13 @@
|
|||
<Tabs selected="Settings" noPadding>
|
||||
<Tab title="Settings">
|
||||
<div class="container">
|
||||
{#key componentInstance?._id}
|
||||
<ScreenSettingsSection {componentInstance} {componentDefinition} />
|
||||
<ComponentSettingsSection {componentInstance} {componentDefinition} />
|
||||
<DesignSection {componentInstance} {componentDefinition} />
|
||||
<CustomStylesSection {componentInstance} {componentDefinition} />
|
||||
<ConditionalUISection {componentInstance} {componentDefinition} />
|
||||
{/key}
|
||||
</div>
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
|
|
@ -0,0 +1,290 @@
|
|||
<script>
|
||||
import {
|
||||
Button,
|
||||
Body,
|
||||
Icon,
|
||||
DrawerContent,
|
||||
Layout,
|
||||
Select,
|
||||
DatePicker,
|
||||
} from "@budibase/bbui"
|
||||
import { flip } from "svelte/animate"
|
||||
import { dndzone } from "svelte-dnd-action"
|
||||
import { generate } from "shortid"
|
||||
import DrawerBindableInput from "components/common/bindings/DrawerBindableInput.svelte"
|
||||
import { OperatorOptions, getValidOperatorsForType } from "helpers/lucene"
|
||||
import { getBindableProperties } from "builderStore/dataBinding"
|
||||
import { currentAsset, selectedComponent, store } from "builderStore"
|
||||
import { getComponentForSettingType } from "./componentSettings"
|
||||
import PropertyControl from "./PropertyControl.svelte"
|
||||
|
||||
export let conditions = []
|
||||
|
||||
const flipDurationMs = 150
|
||||
const actionOptions = [
|
||||
{
|
||||
label: "Hide component",
|
||||
value: "hide",
|
||||
},
|
||||
{
|
||||
label: "Show component",
|
||||
value: "show",
|
||||
},
|
||||
{
|
||||
label: "Update setting",
|
||||
value: "update",
|
||||
},
|
||||
]
|
||||
const valueTypeOptions = [
|
||||
{
|
||||
value: "string",
|
||||
label: "Binding",
|
||||
},
|
||||
{
|
||||
value: "number",
|
||||
label: "Number",
|
||||
},
|
||||
{
|
||||
value: "datetime",
|
||||
label: "Date",
|
||||
},
|
||||
{
|
||||
value: "boolean",
|
||||
label: "Boolean",
|
||||
},
|
||||
]
|
||||
|
||||
let dragDisabled = true
|
||||
$: definition = store.actions.components.getDefinition(
|
||||
$selectedComponent?._component
|
||||
)
|
||||
$: settings = (definition?.settings ?? []).map(setting => {
|
||||
return {
|
||||
label: setting.label,
|
||||
value: setting.key,
|
||||
}
|
||||
})
|
||||
$: bindableProperties = getBindableProperties(
|
||||
$currentAsset,
|
||||
$store.selectedComponentId
|
||||
)
|
||||
$: conditions.forEach(link => {
|
||||
if (!link.id) {
|
||||
link.id = generate()
|
||||
}
|
||||
})
|
||||
|
||||
const getSettingDefinition = key => {
|
||||
return definition?.settings?.find(setting => {
|
||||
return setting.key === key
|
||||
})
|
||||
}
|
||||
|
||||
const getComponentForSetting = key => {
|
||||
const settingDefinition = getSettingDefinition(key)
|
||||
return getComponentForSettingType(settingDefinition?.type || "text")
|
||||
}
|
||||
|
||||
const addCondition = () => {
|
||||
conditions = [
|
||||
...conditions,
|
||||
{
|
||||
valueType: "string",
|
||||
id: generate(),
|
||||
action: "hide",
|
||||
operator: OperatorOptions.Equals.value,
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
const removeCondition = id => {
|
||||
conditions = conditions.filter(link => link.id !== id)
|
||||
}
|
||||
|
||||
const handleFinalize = e => {
|
||||
updateConditions(e)
|
||||
dragDisabled = true
|
||||
}
|
||||
|
||||
const updateConditions = e => {
|
||||
conditions = e.detail.items
|
||||
}
|
||||
|
||||
const getOperatorOptions = condition => {
|
||||
return getValidOperatorsForType(condition.valueType)
|
||||
}
|
||||
|
||||
const onOperatorChange = (condition, newOperator) => {
|
||||
const noValueOptions = [
|
||||
OperatorOptions.Empty.value,
|
||||
OperatorOptions.NotEmpty.value,
|
||||
]
|
||||
condition.noValue = noValueOptions.includes(newOperator)
|
||||
if (condition.noValue) {
|
||||
condition.referenceValue = null
|
||||
condition.valueType = "string"
|
||||
}
|
||||
}
|
||||
|
||||
const onValueTypeChange = (condition, newType) => {
|
||||
condition.referenceValue = null
|
||||
|
||||
// Ensure a valid operator is set
|
||||
const validOperators = getValidOperatorsForType(newType).map(x => x.value)
|
||||
if (!validOperators.includes(condition.operator)) {
|
||||
condition.operator = validOperators[0] ?? OperatorOptions.Equals.value
|
||||
onOperatorChange(condition, condition.operator)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<DrawerContent>
|
||||
<div class="container">
|
||||
<Layout noPadding>
|
||||
{#if conditions?.length}
|
||||
<div
|
||||
class="conditions"
|
||||
use:dndzone={{
|
||||
items: conditions,
|
||||
flipDurationMs,
|
||||
dropTargetStyle: { outline: "none" },
|
||||
dragDisabled,
|
||||
}}
|
||||
on:finalize={handleFinalize}
|
||||
on:consider={updateConditions}
|
||||
>
|
||||
{#each conditions as condition (condition.id)}
|
||||
<div
|
||||
class="condition"
|
||||
class:update={condition.action === "update"}
|
||||
animate:flip={{ duration: flipDurationMs }}
|
||||
>
|
||||
<div
|
||||
class="handle"
|
||||
aria-label="drag-handle"
|
||||
style={dragDisabled ? "cursor: grab" : "cursor: grabbing"}
|
||||
on:mousedown={() => (dragDisabled = false)}
|
||||
>
|
||||
<Icon name="DragHandle" size="XL" />
|
||||
</div>
|
||||
<Select
|
||||
placeholder={null}
|
||||
options={actionOptions}
|
||||
bind:value={condition.action}
|
||||
/>
|
||||
{#if condition.action === "update"}
|
||||
<Select options={settings} bind:value={condition.setting} />
|
||||
<div>TO</div>
|
||||
{#if getSettingDefinition(condition.setting)}
|
||||
<PropertyControl
|
||||
type={getSettingDefinition(condition.setting).type}
|
||||
control={getComponentForSetting(condition.setting)}
|
||||
key={getSettingDefinition(condition.setting).key}
|
||||
value={condition.settingValue}
|
||||
componentInstance={$selectedComponent}
|
||||
onChange={val => (condition.settingValue = val)}
|
||||
props={{
|
||||
options: getSettingDefinition(condition.setting).options,
|
||||
placeholder: getSettingDefinition(condition.setting)
|
||||
.placeholder,
|
||||
}}
|
||||
/>
|
||||
{:else}
|
||||
<Select disabled placeholder=" " />
|
||||
{/if}
|
||||
{/if}
|
||||
<div>IF</div>
|
||||
<DrawerBindableInput
|
||||
bindings={bindableProperties}
|
||||
placeholder="Value"
|
||||
value={condition.newValue}
|
||||
on:change={e => (condition.newValue = e.detail)}
|
||||
/>
|
||||
<Select
|
||||
placeholder={null}
|
||||
options={getOperatorOptions(condition)}
|
||||
bind:value={condition.operator}
|
||||
on:change={e => onOperatorChange(condition, e.detail)}
|
||||
/>
|
||||
<Select
|
||||
disabled={condition.noValue}
|
||||
options={valueTypeOptions}
|
||||
bind:value={condition.valueType}
|
||||
placeholder={null}
|
||||
on:change={e => onValueTypeChange(condition, e.detail)}
|
||||
/>
|
||||
{#if ["string", "number"].includes(condition.valueType)}
|
||||
<DrawerBindableInput
|
||||
disabled={condition.noValue}
|
||||
bindings={bindableProperties}
|
||||
placeholder="Value"
|
||||
value={condition.referenceValue}
|
||||
on:change={e => (condition.referenceValue = e.detail)}
|
||||
/>
|
||||
{:else if condition.valueType === "datetime"}
|
||||
<DatePicker
|
||||
placeholder="Value"
|
||||
disabled={condition.noValue}
|
||||
bind:value={condition.referenceValue}
|
||||
/>
|
||||
{:else if condition.valueType === "boolean"}
|
||||
<Select
|
||||
placeholder="Value"
|
||||
disabled={condition.noValue}
|
||||
options={["True", "False"]}
|
||||
bind:value={condition.referenceValue}
|
||||
/>
|
||||
{/if}
|
||||
<Icon
|
||||
name="Close"
|
||||
hoverable
|
||||
size="S"
|
||||
on:click={() => removeCondition(condition.id)}
|
||||
/>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{:else}
|
||||
<Body size="S">Add your first condition to get started.</Body>
|
||||
{/if}
|
||||
<div>
|
||||
<Button secondary icon="Add" on:click={addCondition}>
|
||||
Add condition
|
||||
</Button>
|
||||
</div>
|
||||
</Layout>
|
||||
</div>
|
||||
</DrawerContent>
|
||||
|
||||
<style>
|
||||
.container {
|
||||
width: 100%;
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.conditions {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
align-items: stretch;
|
||||
gap: var(--spacing-m);
|
||||
}
|
||||
.condition {
|
||||
gap: var(--spacing-l);
|
||||
display: grid;
|
||||
align-items: center;
|
||||
grid-template-columns: auto 1fr auto 1fr 1fr 1fr 1fr auto;
|
||||
border-radius: var(--border-radius-s);
|
||||
transition: background-color ease-in-out 130ms;
|
||||
}
|
||||
.condition.update {
|
||||
grid-template-columns: auto 1fr 1fr auto 1fr auto 1fr 1fr 1fr 1fr auto;
|
||||
}
|
||||
.condition:hover {
|
||||
background-color: var(--spectrum-global-color-gray-100);
|
||||
}
|
||||
.handle {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
}
|
||||
</style>
|
|
@ -11,44 +11,11 @@
|
|||
import { getBindableProperties } from "builderStore/dataBinding"
|
||||
import DrawerBindableInput from "components/common/bindings/DrawerBindableInput.svelte"
|
||||
import { generate } from "shortid"
|
||||
import { OperatorOptions, getValidOperatorsForType } from "helpers/lucene"
|
||||
|
||||
export let schemaFields
|
||||
export let value
|
||||
|
||||
const OperatorOptions = {
|
||||
Equals: {
|
||||
value: "equal",
|
||||
label: "Equals",
|
||||
},
|
||||
NotEquals: {
|
||||
value: "notEqual",
|
||||
label: "Not equals",
|
||||
},
|
||||
Empty: {
|
||||
value: "empty",
|
||||
label: "Is empty",
|
||||
},
|
||||
NotEmpty: {
|
||||
value: "notEmpty",
|
||||
label: "Is not empty",
|
||||
},
|
||||
StartsWith: {
|
||||
value: "string",
|
||||
label: "Starts with",
|
||||
},
|
||||
Like: {
|
||||
value: "fuzzy",
|
||||
label: "Like",
|
||||
},
|
||||
MoreThan: {
|
||||
value: "rangeLow",
|
||||
label: "More than",
|
||||
},
|
||||
LessThan: {
|
||||
value: "rangeHigh",
|
||||
label: "Less than",
|
||||
},
|
||||
}
|
||||
const BannedTypes = ["link", "attachment"]
|
||||
$: bindableProperties = getBindableProperties(
|
||||
$currentAsset,
|
||||
|
@ -75,61 +42,16 @@
|
|||
value = value.filter(field => field.id !== id)
|
||||
}
|
||||
|
||||
const getValidOperatorsForType = type => {
|
||||
const Op = OperatorOptions
|
||||
if (type === "string") {
|
||||
return [
|
||||
Op.Equals,
|
||||
Op.NotEquals,
|
||||
Op.StartsWith,
|
||||
Op.Like,
|
||||
Op.Empty,
|
||||
Op.NotEmpty,
|
||||
]
|
||||
} else if (type === "number") {
|
||||
return [
|
||||
Op.Equals,
|
||||
Op.NotEquals,
|
||||
Op.MoreThan,
|
||||
Op.LessThan,
|
||||
Op.Empty,
|
||||
Op.NotEmpty,
|
||||
]
|
||||
} else if (type === "options") {
|
||||
return [Op.Equals, Op.NotEquals, Op.Empty, Op.NotEmpty]
|
||||
} else if (type === "boolean") {
|
||||
return [Op.Equals, Op.NotEquals, Op.Empty, Op.NotEmpty]
|
||||
} else if (type === "longform") {
|
||||
return [
|
||||
Op.Equals,
|
||||
Op.NotEquals,
|
||||
Op.StartsWith,
|
||||
Op.Like,
|
||||
Op.Empty,
|
||||
Op.NotEmpty,
|
||||
]
|
||||
} else if (type === "datetime") {
|
||||
return [
|
||||
Op.Equals,
|
||||
Op.NotEquals,
|
||||
Op.MoreThan,
|
||||
Op.LessThan,
|
||||
Op.Empty,
|
||||
Op.NotEmpty,
|
||||
]
|
||||
}
|
||||
return []
|
||||
}
|
||||
|
||||
const onFieldChange = (expression, field) => {
|
||||
// Update the field type
|
||||
expression.type = schemaFields.find(x => x.name === field)?.type
|
||||
|
||||
// Ensure a valid operator is set
|
||||
const validOperators = getValidOperatorsForType(expression.type)
|
||||
const validOperators = getValidOperatorsForType(expression.type).map(
|
||||
x => x.value
|
||||
)
|
||||
if (!validOperators.includes(expression.operator)) {
|
||||
expression.operator =
|
||||
validOperators[0]?.value ?? OperatorOptions.Equals.value
|
||||
expression.operator = validOperators[0] ?? OperatorOptions.Equals.value
|
||||
onOperatorChange(expression, expression.operator)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,13 +15,13 @@
|
|||
export let links = []
|
||||
|
||||
const flipDurationMs = 150
|
||||
let dragDisabled = true
|
||||
|
||||
$: links.forEach(link => {
|
||||
if (!link.id) {
|
||||
link.id = generate()
|
||||
}
|
||||
})
|
||||
|
||||
$: urlOptions = $store.screens
|
||||
.map(screen => screen.routing?.route)
|
||||
.filter(x => x != null)
|
||||
|
@ -37,6 +37,11 @@
|
|||
const updateLinks = e => {
|
||||
links = e.detail.items
|
||||
}
|
||||
|
||||
const handleFinalize = e => {
|
||||
updateLinks(e)
|
||||
dragDisabled = true
|
||||
}
|
||||
</script>
|
||||
|
||||
<DrawerContent>
|
||||
|
@ -49,13 +54,21 @@
|
|||
items: links,
|
||||
flipDurationMs,
|
||||
dropTargetStyle: { outline: "none" },
|
||||
dragDisabled,
|
||||
}}
|
||||
on:finalize={updateLinks}
|
||||
on:finalize={handleFinalize}
|
||||
on:consider={updateLinks}
|
||||
>
|
||||
{#each links as link (link.id)}
|
||||
<div class="link" animate:flip={{ duration: flipDurationMs }}>
|
||||
<div
|
||||
class="handle"
|
||||
aria-label="drag-handle"
|
||||
style={dragDisabled ? "cursor: grab" : "cursor: grabbing"}
|
||||
on:mousedown={() => (dragDisabled = false)}
|
||||
>
|
||||
<Icon name="DragHandle" size="XL" />
|
||||
</div>
|
||||
<Input bind:value={link.text} placeholder="Text" />
|
||||
<Combobox
|
||||
bind:value={link.url}
|
||||
|
@ -90,7 +103,7 @@
|
|||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
align-items: stretch;
|
||||
gap: var(--spacing-s);
|
||||
gap: var(--spacing-m);
|
||||
}
|
||||
.link {
|
||||
gap: var(--spacing-l);
|
||||
|
@ -108,4 +121,8 @@
|
|||
flex: 1 1 auto;
|
||||
width: 0;
|
||||
}
|
||||
.handle {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -70,7 +70,7 @@
|
|||
</script>
|
||||
|
||||
<div class="property-control" bind:this={anchor} data-cy={`setting-${key}`}>
|
||||
{#if type !== "boolean"}
|
||||
{#if type !== "boolean" && label}
|
||||
<div class="label">
|
||||
<Label>{label}</Label>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
import { Checkbox, Input, Select } from "@budibase/bbui"
|
||||
import DataSourceSelect from "./DataSourceSelect.svelte"
|
||||
import DataProviderSelect from "./DataProviderSelect.svelte"
|
||||
import EventsEditor from "./EventsEditor"
|
||||
import TableSelect from "./TableSelect.svelte"
|
||||
import ColorPicker from "./ColorPicker.svelte"
|
||||
import { IconSelect } from "./IconSelect"
|
||||
import FieldSelect from "./FieldSelect.svelte"
|
||||
import MultiFieldSelect from "./MultiFieldSelect.svelte"
|
||||
import SchemaSelect from "./SchemaSelect.svelte"
|
||||
import SectionSelect from "./SectionSelect.svelte"
|
||||
import NavigationEditor from "./NavigationEditor/NavigationEditor.svelte"
|
||||
import FilterEditor from "./FilterEditor/FilterEditor.svelte"
|
||||
import URLSelect from "./URLSelect.svelte"
|
||||
import StringFieldSelect from "./StringFieldSelect.svelte"
|
||||
import NumberFieldSelect from "./NumberFieldSelect.svelte"
|
||||
import OptionsFieldSelect from "./OptionsFieldSelect.svelte"
|
||||
import BooleanFieldSelect from "./BooleanFieldSelect.svelte"
|
||||
import LongFormFieldSelect from "./LongFormFieldSelect.svelte"
|
||||
import DateTimeFieldSelect from "./DateTimeFieldSelect.svelte"
|
||||
import AttachmentFieldSelect from "./AttachmentFieldSelect.svelte"
|
||||
import RelationshipFieldSelect from "./RelationshipFieldSelect.svelte"
|
||||
|
||||
const componentMap = {
|
||||
text: Input,
|
||||
select: Select,
|
||||
dataSource: DataSourceSelect,
|
||||
dataProvider: DataProviderSelect,
|
||||
boolean: Checkbox,
|
||||
number: Input,
|
||||
event: EventsEditor,
|
||||
table: TableSelect,
|
||||
color: ColorPicker,
|
||||
icon: IconSelect,
|
||||
field: FieldSelect,
|
||||
multifield: MultiFieldSelect,
|
||||
schema: SchemaSelect,
|
||||
section: SectionSelect,
|
||||
navigation: NavigationEditor,
|
||||
filter: FilterEditor,
|
||||
url: URLSelect,
|
||||
"field/string": StringFieldSelect,
|
||||
"field/number": NumberFieldSelect,
|
||||
"field/options": OptionsFieldSelect,
|
||||
"field/boolean": BooleanFieldSelect,
|
||||
"field/longform": LongFormFieldSelect,
|
||||
"field/datetime": DateTimeFieldSelect,
|
||||
"field/attachment": AttachmentFieldSelect,
|
||||
"field/link": RelationshipFieldSelect,
|
||||
}
|
||||
|
||||
export const getComponentForSettingType = type => {
|
||||
return componentMap[type]
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
export const OperatorOptions = {
|
||||
Equals: {
|
||||
value: "equal",
|
||||
label: "Equals",
|
||||
},
|
||||
NotEquals: {
|
||||
value: "notEqual",
|
||||
label: "Not equals",
|
||||
},
|
||||
Empty: {
|
||||
value: "empty",
|
||||
label: "Is empty",
|
||||
},
|
||||
NotEmpty: {
|
||||
value: "notEmpty",
|
||||
label: "Is not empty",
|
||||
},
|
||||
StartsWith: {
|
||||
value: "string",
|
||||
label: "Starts with",
|
||||
},
|
||||
Like: {
|
||||
value: "fuzzy",
|
||||
label: "Like",
|
||||
},
|
||||
MoreThan: {
|
||||
value: "rangeLow",
|
||||
label: "More than",
|
||||
},
|
||||
LessThan: {
|
||||
value: "rangeHigh",
|
||||
label: "Less than",
|
||||
},
|
||||
}
|
||||
|
||||
export const getValidOperatorsForType = type => {
|
||||
const Op = OperatorOptions
|
||||
if (type === "string") {
|
||||
return [
|
||||
Op.Equals,
|
||||
Op.NotEquals,
|
||||
Op.StartsWith,
|
||||
Op.Like,
|
||||
Op.Empty,
|
||||
Op.NotEmpty,
|
||||
]
|
||||
} else if (type === "number") {
|
||||
return [
|
||||
Op.Equals,
|
||||
Op.NotEquals,
|
||||
Op.MoreThan,
|
||||
Op.LessThan,
|
||||
Op.Empty,
|
||||
Op.NotEmpty,
|
||||
]
|
||||
} else if (type === "options") {
|
||||
return [Op.Equals, Op.NotEquals, Op.Empty, Op.NotEmpty]
|
||||
} else if (type === "boolean") {
|
||||
return [Op.Equals, Op.NotEquals, Op.Empty, Op.NotEmpty]
|
||||
} else if (type === "longform") {
|
||||
return [
|
||||
Op.Equals,
|
||||
Op.NotEquals,
|
||||
Op.StartsWith,
|
||||
Op.Like,
|
||||
Op.Empty,
|
||||
Op.NotEmpty,
|
||||
]
|
||||
} else if (type === "datetime") {
|
||||
return [
|
||||
Op.Equals,
|
||||
Op.NotEquals,
|
||||
Op.MoreThan,
|
||||
Op.LessThan,
|
||||
Op.Empty,
|
||||
Op.NotEmpty,
|
||||
]
|
||||
}
|
||||
return []
|
||||
}
|
|
@ -58,5 +58,6 @@
|
|||
align-items: stretch;
|
||||
gap: var(--spacing-l);
|
||||
background-color: var(--background);
|
||||
overflow-y: auto;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -19,7 +19,8 @@
|
|||
import { fetchData } from "helpers"
|
||||
import { users, auth } from "stores/portal"
|
||||
|
||||
import TagsRenderer from "./_components/TagsTableRenderer.svelte"
|
||||
import TagsRenderer from "./_components/RolesTagsTableRenderer.svelte"
|
||||
|
||||
import UpdateRolesModal from "./_components/UpdateRolesModal.svelte"
|
||||
import ForceResetPasswordModal from "./_components/ForceResetPasswordModal.svelte"
|
||||
|
||||
|
@ -36,7 +37,8 @@
|
|||
$: defaultRoleId = $userFetch?.data?.builder?.global ? "ADMIN" : "BASIC"
|
||||
// Merge the Apps list and the roles response to get something that makes sense for the table
|
||||
$: appList = Object.keys($apps?.data).map(id => {
|
||||
const role = $userFetch?.data?.roles?.[id] || defaultRoleId
|
||||
const roleId = $userFetch?.data?.roles?.[id] || defaultRoleId
|
||||
const role = $apps?.data?.[id].roles.find(role => role._id === roleId)
|
||||
return {
|
||||
...$apps?.data?.[id],
|
||||
_id: id,
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
<script>
|
||||
import TagsTableRenderer from "./TagsTableRenderer.svelte"
|
||||
export let value
|
||||
|
||||
$: roles = value?.filter(role => role != null).map(role => role.name) ?? []
|
||||
</script>
|
||||
|
||||
<TagsTableRenderer value={roles} />
|
|
@ -4,9 +4,9 @@
|
|||
|
||||
const displayLimit = 5
|
||||
|
||||
$: roles = value?.filter(role => role != null) ?? []
|
||||
$: tags = roles.slice(0, displayLimit)
|
||||
$: leftover = roles.length - tags.length
|
||||
$: values = value?.filter(value => value != null) ?? []
|
||||
$: tags = values.slice(0, displayLimit)
|
||||
$: leftover = values.length - tags.length
|
||||
</script>
|
||||
|
||||
<div class="tag-renderer">
|
||||
|
|
|
@ -10,8 +10,8 @@
|
|||
|
||||
const roles = app.roles
|
||||
let options = roles
|
||||
.filter(role => role._id !== "PUBLIC")
|
||||
.map(role => ({ value: role._id, label: role.name }))
|
||||
.filter(role => role.value !== "PUBLIC")
|
||||
let selectedRole = user?.roles?.[app?._id]
|
||||
|
||||
async function updateUserRoles() {
|
||||
|
@ -48,5 +48,7 @@
|
|||
on:change
|
||||
{options}
|
||||
label="Role"
|
||||
getOptionLabel={role => role.name}
|
||||
getOptionValue={role => role._id}
|
||||
/>
|
||||
</ModalContent>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@budibase/cli",
|
||||
"version": "0.9.79-alpha.4",
|
||||
"version": "0.9.80-alpha.9",
|
||||
"description": "Budibase CLI, for developers, self hosting and migrations.",
|
||||
"main": "src/index.js",
|
||||
"bin": {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@budibase/client",
|
||||
"version": "0.9.79-alpha.4",
|
||||
"version": "0.9.80-alpha.9",
|
||||
"license": "MPL-2.0",
|
||||
"module": "dist/budibase-client.js",
|
||||
"main": "dist/budibase-client.js",
|
||||
|
@ -18,9 +18,9 @@
|
|||
"dev:builder": "rollup -cw"
|
||||
},
|
||||
"dependencies": {
|
||||
"@budibase/bbui": "^0.9.79-alpha.4",
|
||||
"@budibase/standard-components": "^0.9.79-alpha.4",
|
||||
"@budibase/string-templates": "^0.9.79-alpha.4",
|
||||
"@budibase/bbui": "^0.9.80-alpha.9",
|
||||
"@budibase/standard-components": "^0.9.80-alpha.9",
|
||||
"@budibase/string-templates": "^0.9.80-alpha.9",
|
||||
"regexparam": "^1.3.0",
|
||||
"shortid": "^2.2.15",
|
||||
"svelte-spa-router": "^3.0.5"
|
||||
|
|
|
@ -8,11 +8,18 @@
|
|||
import { hashString } from "../utils/hash"
|
||||
import Manifest from "@budibase/standard-components/manifest.json"
|
||||
import { Placeholder } from "@budibase/standard-components"
|
||||
import {
|
||||
getActiveConditions,
|
||||
reduceConditionActions,
|
||||
} from "../utils/conditions"
|
||||
|
||||
export let instance = {}
|
||||
|
||||
// Props that will be passed to the component instance
|
||||
let componentProps
|
||||
// The enriched component settings
|
||||
let enrichedSettings
|
||||
|
||||
// Any prop overrides that need to be applied due to conditional UI
|
||||
let conditionalSettings
|
||||
|
||||
// Props are hashed when inside the builder preview and used as a key, so that
|
||||
// components fully remount whenever any props change
|
||||
|
@ -28,6 +35,9 @@
|
|||
let lastContextKey
|
||||
let lastInstanceKey
|
||||
|
||||
// Visibility flag used by conditional UI
|
||||
let visible = true
|
||||
|
||||
// Get contexts
|
||||
const context = getContext("context")
|
||||
const insideScreenslot = !!getContext("screenslot")
|
||||
|
@ -54,6 +64,8 @@
|
|||
$builderStore.inBuilder &&
|
||||
$builderStore.selectedComponentId === instance._id
|
||||
$: interactive = $builderStore.previewType === "layout" || insideScreenslot
|
||||
$: evaluateConditions(enrichedSettings?._conditions)
|
||||
$: componentSettings = { ...enrichedSettings, ...conditionalSettings }
|
||||
|
||||
// Update component context
|
||||
$: componentStore.set({
|
||||
|
@ -62,14 +74,14 @@
|
|||
styles: { ...instance._styles, id, empty, interactive },
|
||||
empty,
|
||||
selected,
|
||||
props: componentProps,
|
||||
props: componentSettings,
|
||||
name,
|
||||
})
|
||||
|
||||
const getRawProps = instance => {
|
||||
let validProps = {}
|
||||
Object.entries(instance)
|
||||
.filter(([name]) => !name.startsWith("_"))
|
||||
.filter(([name]) => name === "_conditions" || !name.startsWith("_"))
|
||||
.forEach(([key, value]) => {
|
||||
validProps[key] = value
|
||||
})
|
||||
|
@ -123,34 +135,55 @@
|
|||
return
|
||||
}
|
||||
let propsChanged = false
|
||||
if (!componentProps) {
|
||||
componentProps = {}
|
||||
if (!enrichedSettings) {
|
||||
enrichedSettings = {}
|
||||
propsChanged = true
|
||||
}
|
||||
Object.keys(enrichedProps).forEach(key => {
|
||||
if (!propsAreSame(enrichedProps[key], componentProps[key])) {
|
||||
if (!propsAreSame(enrichedProps[key], enrichedSettings[key])) {
|
||||
propsChanged = true
|
||||
componentProps[key] = enrichedProps[key]
|
||||
enrichedSettings[key] = enrichedProps[key]
|
||||
}
|
||||
})
|
||||
|
||||
// Update the hash if we're in the builder so we can fully remount this
|
||||
// component
|
||||
if (get(builderStore).inBuilder && propsChanged) {
|
||||
propsHash = hashString(JSON.stringify(componentProps))
|
||||
propsHash = hashString(JSON.stringify(enrichedSettings))
|
||||
}
|
||||
}
|
||||
|
||||
const evaluateConditions = conditions => {
|
||||
if (!conditions?.length) {
|
||||
return
|
||||
}
|
||||
|
||||
// Default visible to false if there is a show condition
|
||||
let nextVisible = !conditions.find(condition => condition.action === "show")
|
||||
|
||||
// Execute conditions and determine settings and visibility changes
|
||||
const activeConditions = getActiveConditions(conditions)
|
||||
const result = reduceConditionActions(activeConditions)
|
||||
if (result.visible != null) {
|
||||
nextVisible = result.visible
|
||||
}
|
||||
|
||||
// Update state from condition results
|
||||
conditionalSettings = result.settingUpdates
|
||||
visible = nextVisible
|
||||
}
|
||||
</script>
|
||||
|
||||
<div
|
||||
{#key propsHash}
|
||||
{#if constructor && componentSettings && visible}
|
||||
<div
|
||||
class={`component ${id}`}
|
||||
data-type={interactive ? "component" : ""}
|
||||
data-id={id}
|
||||
data-name={name}
|
||||
>
|
||||
{#key propsHash}
|
||||
{#if constructor && componentProps}
|
||||
<svelte:component this={constructor} {...componentProps}>
|
||||
class:hidden={!visible}
|
||||
>
|
||||
<svelte:component this={constructor} {...componentSettings}>
|
||||
{#if children.length}
|
||||
{#each children as child (child._id)}
|
||||
<svelte:self instance={child} />
|
||||
|
@ -159,9 +192,9 @@
|
|||
<Placeholder />
|
||||
{/if}
|
||||
</svelte:component>
|
||||
</div>
|
||||
{/if}
|
||||
{/key}
|
||||
</div>
|
||||
{/key}
|
||||
|
||||
<style>
|
||||
.component {
|
||||
|
|
|
@ -6,8 +6,15 @@ const createAppStore = () => {
|
|||
|
||||
// Fetches the app definition including screens, layouts and theme
|
||||
const fetchAppDefinition = async () => {
|
||||
const appDefinition = await API.fetchAppPackage(get(store).appId)
|
||||
store.set(appDefinition)
|
||||
const appId = get(store)?.appId
|
||||
if (!appId) {
|
||||
throw "Cannot fetch app definition without app ID set"
|
||||
}
|
||||
const appDefinition = await API.fetchAppPackage(appId)
|
||||
store.set({
|
||||
...appDefinition,
|
||||
appId: appDefinition?.application?.appId,
|
||||
})
|
||||
}
|
||||
|
||||
// Sets the initial app ID
|
||||
|
|
|
@ -10,9 +10,14 @@ const createAuthStore = () => {
|
|||
store.set(user)
|
||||
}
|
||||
|
||||
const logOut = async () => {
|
||||
window.document.cookie = `budibase:auth=; budibase:currentapp=; Path=/; Expires=Thu, 01 Jan 1970 00:00:01 GMT;`
|
||||
window.location = "/builder/auth/login"
|
||||
}
|
||||
|
||||
return {
|
||||
subscribe: store.subscribe,
|
||||
actions: { fetchUser },
|
||||
actions: { fetchUser, logOut },
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,15 @@
|
|||
import { get } from "svelte/store"
|
||||
import { routeStore, builderStore, confirmationStore } from "../store"
|
||||
import {
|
||||
routeStore,
|
||||
builderStore,
|
||||
confirmationStore,
|
||||
authStore,
|
||||
} from "../store"
|
||||
import { saveRow, deleteRow, executeQuery, triggerAutomation } from "../api"
|
||||
import { ActionTypes } from "../constants"
|
||||
|
||||
const saveRowHandler = async (action, context) => {
|
||||
const { fields, providerId } = action.parameters
|
||||
const { fields, providerId, tableId } = action.parameters
|
||||
if (providerId) {
|
||||
let draft = context[providerId]
|
||||
if (fields) {
|
||||
|
@ -12,6 +17,9 @@ const saveRowHandler = async (action, context) => {
|
|||
draft[field] = value
|
||||
}
|
||||
}
|
||||
if (tableId) {
|
||||
draft.tableId = tableId
|
||||
}
|
||||
await saveRow(draft)
|
||||
}
|
||||
}
|
||||
|
@ -74,6 +82,10 @@ const refreshDatasourceHandler = async (action, context) => {
|
|||
)
|
||||
}
|
||||
|
||||
const logoutHandler = async () => {
|
||||
await authStore.actions.logOut()
|
||||
}
|
||||
|
||||
const handlerMap = {
|
||||
["Save Row"]: saveRowHandler,
|
||||
["Delete Row"]: deleteRowHandler,
|
||||
|
@ -82,6 +94,7 @@ const handlerMap = {
|
|||
["Trigger Automation"]: triggerAutomationHandler,
|
||||
["Validate Form"]: validateFormHandler,
|
||||
["Refresh Datasource"]: refreshDatasourceHandler,
|
||||
["Log Out"]: logoutHandler,
|
||||
}
|
||||
|
||||
const confirmTextMap = {
|
||||
|
|
|
@ -43,5 +43,17 @@ export const enrichProps = (props, context) => {
|
|||
)
|
||||
}
|
||||
|
||||
// Enrich any click actions in conditions
|
||||
if (enrichedProps._conditions) {
|
||||
enrichedProps._conditions.forEach(condition => {
|
||||
if (condition.setting === "onClick") {
|
||||
condition.settingValue = enrichButtonActions(
|
||||
condition.settingValue,
|
||||
totalContext
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return enrichedProps
|
||||
}
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
import {
|
||||
buildLuceneQuery,
|
||||
luceneQuery,
|
||||
} from "../../../standard-components/src/lucene"
|
||||
|
||||
export const getActiveConditions = conditions => {
|
||||
if (!conditions?.length) {
|
||||
return []
|
||||
}
|
||||
|
||||
return conditions.filter(condition => {
|
||||
// Parse values into correct types
|
||||
if (condition.valueType === "number") {
|
||||
condition.referenceValue = parseFloat(condition.referenceValue)
|
||||
condition.newValue = parseFloat(condition.newValue)
|
||||
} else if (condition.valueType === "datetime") {
|
||||
if (condition.referenceValue) {
|
||||
condition.referenceValue = new Date(
|
||||
condition.referenceValue
|
||||
).toISOString()
|
||||
}
|
||||
if (condition.newValue) {
|
||||
condition.newValue = new Date(condition.newValue).toISOString()
|
||||
}
|
||||
} else if (condition.valueType === "boolean") {
|
||||
condition.referenceValue =
|
||||
`${condition.referenceValue}`.toLowerCase() === "true"
|
||||
condition.newValue = `${condition.newValue}`.toLowerCase() === "true"
|
||||
}
|
||||
|
||||
// Build lucene compatible condition expression
|
||||
const luceneCondition = {
|
||||
...condition,
|
||||
type: condition.valueType,
|
||||
field: "newValue",
|
||||
value: condition.referenceValue,
|
||||
}
|
||||
|
||||
const query = buildLuceneQuery([luceneCondition])
|
||||
const result = luceneQuery([luceneCondition], query)
|
||||
return result.length > 0
|
||||
})
|
||||
}
|
||||
|
||||
export const reduceConditionActions = conditions => {
|
||||
let settingUpdates = {}
|
||||
let visible = null
|
||||
|
||||
conditions?.forEach(condition => {
|
||||
if (condition.action === "show") {
|
||||
visible = true
|
||||
} else if (condition.action === "hide") {
|
||||
visible = false
|
||||
} else if (condition.setting) {
|
||||
settingUpdates[condition.setting] = condition.settingValue
|
||||
}
|
||||
})
|
||||
|
||||
return { settingUpdates, visible }
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "@budibase/server",
|
||||
"email": "hi@budibase.com",
|
||||
"version": "0.9.79-alpha.4",
|
||||
"version": "0.9.80-alpha.9",
|
||||
"description": "Budibase Web Server",
|
||||
"main": "src/index.js",
|
||||
"repository": {
|
||||
|
@ -9,7 +9,7 @@
|
|||
"url": "https://github.com/Budibase/budibase.git"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "rm -rf dist/ && tsc && mv dist/src/* dist/ && rmdir dist/src/ && yarn postbuild",
|
||||
"build": "rimraf dist/ && tsc && mv dist/src/* dist/ && rmdir dist/src/ && yarn postbuild",
|
||||
"postbuild": "copyfiles -u 1 src/**/*.svelte dist/ && copyfiles -u 1 src/**/*.hbs dist/ && copyfiles -u 1 src/**/*.json dist/",
|
||||
"test": "jest --coverage --maxWorkers=2",
|
||||
"test:watch": "jest --watch",
|
||||
|
@ -62,9 +62,9 @@
|
|||
"author": "Budibase",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"@budibase/auth": "^0.9.79-alpha.4",
|
||||
"@budibase/client": "^0.9.79-alpha.4",
|
||||
"@budibase/string-templates": "^0.9.79-alpha.4",
|
||||
"@budibase/auth": "^0.9.80-alpha.9",
|
||||
"@budibase/client": "^0.9.80-alpha.9",
|
||||
"@budibase/string-templates": "^0.9.80-alpha.9",
|
||||
"@elastic/elasticsearch": "7.10.0",
|
||||
"@koa/router": "8.0.0",
|
||||
"@sendgrid/mail": "7.1.1",
|
||||
|
@ -117,7 +117,7 @@
|
|||
"devDependencies": {
|
||||
"@babel/core": "^7.14.3",
|
||||
"@babel/preset-env": "^7.14.4",
|
||||
"@budibase/standard-components": "^0.9.79-alpha.4",
|
||||
"@budibase/standard-components": "^0.9.80-alpha.9",
|
||||
"@jest/test-sequencer": "^24.8.0",
|
||||
"@types/bull": "^3.15.1",
|
||||
"@types/jest": "^26.0.23",
|
||||
|
@ -134,6 +134,7 @@
|
|||
"nodemon": "^2.0.4",
|
||||
"pouchdb-adapter-memory": "^7.2.1",
|
||||
"prettier": "^2.3.1",
|
||||
"rimraf": "^3.0.2",
|
||||
"supertest": "^4.0.2",
|
||||
"ts-jest": "^27.0.3",
|
||||
"ts-node": "^10.0.0",
|
||||
|
|
|
@ -29,11 +29,11 @@
|
|||
"keywords": [
|
||||
"svelte"
|
||||
],
|
||||
"version": "0.9.79-alpha.4",
|
||||
"version": "0.9.80-alpha.9",
|
||||
"license": "MIT",
|
||||
"gitHead": "d1836a898cab3f8ab80ee6d8f42be1a9eed7dcdc",
|
||||
"dependencies": {
|
||||
"@budibase/bbui": "^0.9.79-alpha.4",
|
||||
"@budibase/bbui": "^0.9.80-alpha.9",
|
||||
"@spectrum-css/card": "^3.0.3",
|
||||
"@spectrum-css/link": "^3.1.3",
|
||||
"@spectrum-css/page": "^3.0.1",
|
||||
|
|
|
@ -23,7 +23,7 @@ export const buildLuceneQuery = filter => {
|
|||
value = parseFloat(value)
|
||||
}
|
||||
if (type === "boolean") {
|
||||
value = value?.toLowerCase() === "true"
|
||||
value = `${value}`?.toLowerCase() === "true"
|
||||
}
|
||||
if (operator.startsWith("range")) {
|
||||
if (!query.range[field]) {
|
||||
|
@ -91,6 +91,11 @@ export const luceneQuery = (docs, query) => {
|
|||
return !doc[key] || !doc[key].startsWith(value)
|
||||
})
|
||||
|
||||
// Process a fuzzy match (treat the same as starts with when running locally)
|
||||
const fuzzyMatch = match("fuzzy", (key, value, doc) => {
|
||||
return !doc[key] || !doc[key].startsWith(value)
|
||||
})
|
||||
|
||||
// Process a range match
|
||||
const rangeMatch = match("range", (key, value, doc) => {
|
||||
return !doc[key] || doc[key] < value.low || doc[key] > value.high
|
||||
|
@ -120,6 +125,7 @@ export const luceneQuery = (docs, query) => {
|
|||
const docMatch = doc => {
|
||||
return (
|
||||
stringMatch(doc) &&
|
||||
fuzzyMatch(doc) &&
|
||||
rangeMatch(doc) &&
|
||||
equalMatch(doc) &&
|
||||
notEqualMatch(doc) &&
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@budibase/string-templates",
|
||||
"version": "0.9.79-alpha.4",
|
||||
"version": "0.9.80-alpha.9",
|
||||
"description": "Handlebars wrapper for Budibase templating.",
|
||||
"main": "src/index.cjs",
|
||||
"module": "dist/bundle.mjs",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "@budibase/worker",
|
||||
"email": "hi@budibase.com",
|
||||
"version": "0.9.79-alpha.4",
|
||||
"version": "0.9.80-alpha.9",
|
||||
"description": "Budibase background service",
|
||||
"main": "src/index.js",
|
||||
"repository": {
|
||||
|
@ -23,8 +23,8 @@
|
|||
"author": "Budibase",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"@budibase/auth": "^0.9.79-alpha.4",
|
||||
"@budibase/string-templates": "^0.9.79-alpha.4",
|
||||
"@budibase/auth": "^0.9.80-alpha.9",
|
||||
"@budibase/string-templates": "^0.9.80-alpha.9",
|
||||
"@koa/router": "^8.0.0",
|
||||
"@techpass/passport-openidconnect": "^0.3.0",
|
||||
"aws-sdk": "^2.811.0",
|
||||
|
|
Loading…
Reference in New Issue