Merge branch 'develop' into pipeline/fail-if-changes-in-master
This commit is contained in:
commit
3bad6e331f
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"version": "2.8.29-alpha.4",
|
"version": "2.8.29-alpha.6",
|
||||||
"npmClient": "yarn",
|
"npmClient": "yarn",
|
||||||
"packages": [
|
"packages": [
|
||||||
"packages/*"
|
"packages/*"
|
||||||
|
|
|
@ -85,7 +85,8 @@
|
||||||
"dayjs": "^1.10.4",
|
"dayjs": "^1.10.4",
|
||||||
"easymde": "^2.16.1",
|
"easymde": "^2.16.1",
|
||||||
"svelte-flatpickr": "3.2.3",
|
"svelte-flatpickr": "3.2.3",
|
||||||
"svelte-portal": "^1.0.0"
|
"svelte-portal": "^1.0.0",
|
||||||
|
"svelte-dnd-action": "^0.9.8"
|
||||||
},
|
},
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
"loader-utils": "1.4.1"
|
"loader-utils": "1.4.1"
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
<script>
|
<script>
|
||||||
//import { createEventDispatcher } from "svelte"
|
|
||||||
import "@spectrum-css/popover/dist/index-vars.css"
|
import "@spectrum-css/popover/dist/index-vars.css"
|
||||||
import clickOutside from "../Actions/click_outside"
|
import clickOutside from "../Actions/click_outside"
|
||||||
import { fly } from "svelte/transition"
|
import { fly } from "svelte/transition"
|
||||||
|
|
|
@ -0,0 +1,252 @@
|
||||||
|
<script>
|
||||||
|
import { flip } from "svelte/animate"
|
||||||
|
import { dndzone } from "svelte-dnd-action"
|
||||||
|
import Icon from "../Icon/Icon.svelte"
|
||||||
|
import Popover from "../Popover/Popover.svelte"
|
||||||
|
import { onMount } from "svelte"
|
||||||
|
const flipDurationMs = 150
|
||||||
|
|
||||||
|
export let constraints
|
||||||
|
export let optionColors = {}
|
||||||
|
let options = []
|
||||||
|
|
||||||
|
let colorPopovers = []
|
||||||
|
let anchors = []
|
||||||
|
|
||||||
|
let colorsArray = [
|
||||||
|
"hsla(0, 90%, 75%, 0.3)",
|
||||||
|
"hsla(50, 80%, 75%, 0.3)",
|
||||||
|
"hsla(120, 90%, 75%, 0.3)",
|
||||||
|
"hsla(200, 90%, 75%, 0.3)",
|
||||||
|
"hsla(240, 90%, 75%, 0.3)",
|
||||||
|
"hsla(320, 90%, 75%, 0.3)",
|
||||||
|
]
|
||||||
|
$: {
|
||||||
|
if (constraints.inclusion.length) {
|
||||||
|
options = constraints.inclusion.map(value => ({
|
||||||
|
name: value,
|
||||||
|
id: Math.random(),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const removeInput = idx => {
|
||||||
|
delete optionColors[options[idx].name]
|
||||||
|
constraints.inclusion = constraints.inclusion.filter((e, i) => i !== idx)
|
||||||
|
options = options.filter((e, i) => i !== idx)
|
||||||
|
colorPopovers.pop(undefined)
|
||||||
|
anchors.pop(undefined)
|
||||||
|
}
|
||||||
|
|
||||||
|
const addNewInput = () => {
|
||||||
|
options = [
|
||||||
|
...options,
|
||||||
|
{ name: `Option ${constraints.inclusion.length + 1}`, id: Math.random() },
|
||||||
|
]
|
||||||
|
constraints.inclusion = [
|
||||||
|
...constraints.inclusion,
|
||||||
|
`Option ${constraints.inclusion.length + 1}`,
|
||||||
|
]
|
||||||
|
|
||||||
|
colorPopovers.push(undefined)
|
||||||
|
anchors.push(undefined)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleDndConsider = e => {
|
||||||
|
options = e.detail.items
|
||||||
|
}
|
||||||
|
const handleDndFinalize = e => {
|
||||||
|
options = e.detail.items
|
||||||
|
constraints.inclusion = options.map(option => option.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleColorChange = (optionName, color, idx) => {
|
||||||
|
optionColors[optionName] = color
|
||||||
|
colorPopovers[idx].hide()
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleNameChange = (optionName, idx, value) => {
|
||||||
|
constraints.inclusion[idx] = value
|
||||||
|
options[idx].name = value
|
||||||
|
optionColors[value] = optionColors[optionName]
|
||||||
|
delete optionColors[optionName]
|
||||||
|
}
|
||||||
|
|
||||||
|
const openColorPickerPopover = (optionIdx, target) => {
|
||||||
|
colorPopovers[optionIdx].show()
|
||||||
|
anchors[optionIdx] = target
|
||||||
|
}
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
// Initialize anchor arrays on mount, assuming 'options' is already populated
|
||||||
|
colorPopovers = constraints.inclusion.map(() => undefined)
|
||||||
|
anchors = constraints.inclusion.map(() => undefined)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="actions"
|
||||||
|
use:dndzone={{
|
||||||
|
items: options,
|
||||||
|
flipDurationMs,
|
||||||
|
dropTargetStyle: { outline: "none" },
|
||||||
|
}}
|
||||||
|
on:consider={handleDndConsider}
|
||||||
|
on:finalize={handleDndFinalize}
|
||||||
|
>
|
||||||
|
{#each options as option, idx (option.id)}
|
||||||
|
<div
|
||||||
|
class="no-border action-container"
|
||||||
|
animate:flip={{ duration: flipDurationMs }}
|
||||||
|
>
|
||||||
|
<div class="child drag-handle-spacing">
|
||||||
|
<Icon name="DragHandle" size="L" />
|
||||||
|
</div>
|
||||||
|
<div class="child color-picker">
|
||||||
|
<div
|
||||||
|
id="color-picker"
|
||||||
|
bind:this={anchors[idx]}
|
||||||
|
style="--color:{optionColors?.[option.name] ||
|
||||||
|
'hsla(0, 1%, 50%, 0.3)'}"
|
||||||
|
class="circle"
|
||||||
|
on:click={e => openColorPickerPopover(idx, e.target)}
|
||||||
|
>
|
||||||
|
<Popover
|
||||||
|
bind:this={colorPopovers[idx]}
|
||||||
|
anchor={anchors[idx]}
|
||||||
|
align="left"
|
||||||
|
offset={0}
|
||||||
|
style=""
|
||||||
|
popoverTarget={document.getElementById(`color-picker`)}
|
||||||
|
animate={false}
|
||||||
|
>
|
||||||
|
<div class="colors">
|
||||||
|
{#each colorsArray as color}
|
||||||
|
<div
|
||||||
|
on:click={() => handleColorChange(option.name, color, idx)}
|
||||||
|
style="--color:{color};"
|
||||||
|
class="circle circle-hover"
|
||||||
|
/>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</Popover>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="child">
|
||||||
|
<input
|
||||||
|
class="input-field"
|
||||||
|
type="text"
|
||||||
|
on:change={e => handleNameChange(option.name, idx, e.target.value)}
|
||||||
|
value={option.name}
|
||||||
|
placeholder="Option name"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="child">
|
||||||
|
<Icon name="Close" hoverable size="S" on:click={removeInput(idx)} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
<div on:click={addNewInput} class="add-option">
|
||||||
|
<Icon hoverable name="Add" />
|
||||||
|
<div>Add option</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.action-container {
|
||||||
|
background-color: var(--spectrum-alias-background-color-primary);
|
||||||
|
border-radius: 0px;
|
||||||
|
border: 1px solid var(--spectrum-global-color-gray-300);
|
||||||
|
transition: background-color 130ms ease-in-out, color 130ms ease-in-out,
|
||||||
|
border-color 130ms ease-in-out;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.no-border {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-container:last-child {
|
||||||
|
border-bottom: 1px solid var(--spectrum-global-color-gray-300) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.child {
|
||||||
|
height: 30px;
|
||||||
|
}
|
||||||
|
.child:hover,
|
||||||
|
.child:focus {
|
||||||
|
background: var(--spectrum-global-color-gray-200);
|
||||||
|
}
|
||||||
|
.add-option {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
padding: var(--spacing-m);
|
||||||
|
gap: var(--spacing-m);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-field {
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
background-color: transparent;
|
||||||
|
width: 100%;
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.child input[type="text"] {
|
||||||
|
padding-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-field:hover,
|
||||||
|
.input-field:focus {
|
||||||
|
background: var(--spectrum-global-color-gray-200);
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-container > :nth-child(1) {
|
||||||
|
flex-grow: 1;
|
||||||
|
justify-content: center;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-container > :nth-child(2) {
|
||||||
|
flex-grow: 1;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-container > :nth-child(3) {
|
||||||
|
flex-grow: 4;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.action-container > :nth-child(4) {
|
||||||
|
flex-grow: 1;
|
||||||
|
justify-content: center;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.circle {
|
||||||
|
height: 20px;
|
||||||
|
width: 20px;
|
||||||
|
background-color: var(--color);
|
||||||
|
border-radius: 50%;
|
||||||
|
display: inline-block;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.circle-hover:hover {
|
||||||
|
border: 1px solid var(--spectrum-global-color-blue-400);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.colors {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr 1fr;
|
||||||
|
gap: var(--spacing-xl);
|
||||||
|
justify-items: center;
|
||||||
|
margin: var(--spacing-m);
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -21,6 +21,7 @@
|
||||||
export let offset = 5
|
export let offset = 5
|
||||||
export let customHeight
|
export let customHeight
|
||||||
export let animate = true
|
export let animate = true
|
||||||
|
export let customZindex
|
||||||
|
|
||||||
$: target = portalTarget || getContext(Context.PopoverRoot) || ".spectrum"
|
$: target = portalTarget || getContext(Context.PopoverRoot) || ".spectrum"
|
||||||
|
|
||||||
|
@ -77,8 +78,9 @@
|
||||||
}}
|
}}
|
||||||
on:keydown={handleEscape}
|
on:keydown={handleEscape}
|
||||||
class="spectrum-Popover is-open"
|
class="spectrum-Popover is-open"
|
||||||
|
class:customZindex
|
||||||
role="presentation"
|
role="presentation"
|
||||||
style="height: {customHeight}"
|
style="height: {customHeight}; --customZindex: {customZindex};"
|
||||||
transition:fly|local={{ y: -20, duration: animate ? 200 : 0 }}
|
transition:fly|local={{ y: -20, duration: animate ? 200 : 0 }}
|
||||||
>
|
>
|
||||||
<slot />
|
<slot />
|
||||||
|
@ -92,4 +94,8 @@
|
||||||
border-color: var(--spectrum-global-color-gray-300);
|
border-color: var(--spectrum-global-color-gray-300);
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.customZindex {
|
||||||
|
z-index: var(--customZindex) !important;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -84,7 +84,7 @@ export { default as IconSideNavItem } from "./IconSideNav/IconSideNavItem.svelte
|
||||||
export { default as Slider } from "./Form/Slider.svelte"
|
export { default as Slider } from "./Form/Slider.svelte"
|
||||||
export { default as Accordion } from "./Accordion/Accordion.svelte"
|
export { default as Accordion } from "./Accordion/Accordion.svelte"
|
||||||
export { default as File } from "./Form/File.svelte"
|
export { default as File } from "./Form/File.svelte"
|
||||||
|
export { default as OptionSelectDnD } from "./OptionSelectDnD/OptionSelectDnD.svelte"
|
||||||
// Renderers
|
// Renderers
|
||||||
export { default as BoldRenderer } from "./Table/BoldRenderer.svelte"
|
export { default as BoldRenderer } from "./Table/BoldRenderer.svelte"
|
||||||
export { default as CodeRenderer } from "./Table/CodeRenderer.svelte"
|
export { default as CodeRenderer } from "./Table/CodeRenderer.svelte"
|
||||||
|
|
|
@ -64,6 +64,13 @@
|
||||||
<svelte:fragment slot="filter">
|
<svelte:fragment slot="filter">
|
||||||
<GridFilterButton />
|
<GridFilterButton />
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
|
<svelte:fragment slot="edit-column">
|
||||||
|
<GridEditColumnModal />
|
||||||
|
</svelte:fragment>
|
||||||
|
<svelte:fragment slot="add-column">
|
||||||
|
<GridAddColumnModal />
|
||||||
|
</svelte:fragment>
|
||||||
|
|
||||||
<svelte:fragment slot="controls">
|
<svelte:fragment slot="controls">
|
||||||
{#if isInternal}
|
{#if isInternal}
|
||||||
<GridCreateViewButton />
|
<GridCreateViewButton />
|
||||||
|
@ -77,9 +84,8 @@
|
||||||
{:else}
|
{:else}
|
||||||
<GridImportButton />
|
<GridImportButton />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<GridExportButton />
|
<GridExportButton />
|
||||||
<GridAddColumnModal />
|
|
||||||
<GridEditColumnModal />
|
|
||||||
{#if isUsersTable}
|
{#if isUsersTable}
|
||||||
<GridEditUserModal />
|
<GridEditUserModal />
|
||||||
{:else}
|
{:else}
|
||||||
|
|
|
@ -7,12 +7,12 @@
|
||||||
Toggle,
|
Toggle,
|
||||||
RadioGroup,
|
RadioGroup,
|
||||||
DatePicker,
|
DatePicker,
|
||||||
ModalContent,
|
|
||||||
Context,
|
|
||||||
Modal,
|
Modal,
|
||||||
notifications,
|
notifications,
|
||||||
|
OptionSelectDnD,
|
||||||
|
Layout,
|
||||||
} from "@budibase/bbui"
|
} from "@budibase/bbui"
|
||||||
import { createEventDispatcher } from "svelte"
|
import { createEventDispatcher, getContext } from "svelte"
|
||||||
import { cloneDeep } from "lodash/fp"
|
import { cloneDeep } from "lodash/fp"
|
||||||
import { tables, datasources } from "stores/backend"
|
import { tables, datasources } from "stores/backend"
|
||||||
import { TableNames, UNEDITABLE_USER_FIELDS } from "constants"
|
import { TableNames, UNEDITABLE_USER_FIELDS } from "constants"
|
||||||
|
@ -26,12 +26,10 @@
|
||||||
SWITCHABLE_TYPES,
|
SWITCHABLE_TYPES,
|
||||||
} from "constants/backend"
|
} from "constants/backend"
|
||||||
import { getAutoColumnInformation, buildAutoColumn } from "builderStore/utils"
|
import { getAutoColumnInformation, buildAutoColumn } from "builderStore/utils"
|
||||||
import ValuesList from "components/common/ValuesList.svelte"
|
|
||||||
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
|
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
|
||||||
import { truncate } from "lodash"
|
import { truncate } from "lodash"
|
||||||
import ModalBindableInput from "components/common/bindings/ModalBindableInput.svelte"
|
import ModalBindableInput from "components/common/bindings/ModalBindableInput.svelte"
|
||||||
import { getBindings } from "components/backend/DataTable/formula"
|
import { getBindings } from "components/backend/DataTable/formula"
|
||||||
import { getContext } from "svelte"
|
|
||||||
import JSONSchemaModal from "./JSONSchemaModal.svelte"
|
import JSONSchemaModal from "./JSONSchemaModal.svelte"
|
||||||
import { ValidColumnNameRegex } from "@budibase/shared-core"
|
import { ValidColumnNameRegex } from "@budibase/shared-core"
|
||||||
|
|
||||||
|
@ -45,11 +43,11 @@
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
const PROHIBITED_COLUMN_NAMES = ["type", "_id", "_rev", "tableId"]
|
const PROHIBITED_COLUMN_NAMES = ["type", "_id", "_rev", "tableId"]
|
||||||
const { hide } = getContext(Context.Modal)
|
const { dispatch: gridDispatch } = getContext("grid")
|
||||||
let fieldDefinitions = cloneDeep(FIELDS)
|
|
||||||
|
|
||||||
export let field
|
export let field
|
||||||
|
|
||||||
|
let fieldDefinitions = cloneDeep(FIELDS)
|
||||||
let originalName
|
let originalName
|
||||||
let linkEditDisabled
|
let linkEditDisabled
|
||||||
let primaryDisplay
|
let primaryDisplay
|
||||||
|
@ -61,11 +59,10 @@
|
||||||
let savingColumn
|
let savingColumn
|
||||||
let deleteColName
|
let deleteColName
|
||||||
let jsonSchemaModal
|
let jsonSchemaModal
|
||||||
|
let allowedTypes = []
|
||||||
let editableColumn = {
|
let editableColumn = {
|
||||||
type: "string",
|
type: "string",
|
||||||
constraints: fieldDefinitions.STRING.constraints,
|
constraints: fieldDefinitions.STRING.constraints,
|
||||||
|
|
||||||
// Initial value for column name in other table for linked records
|
// Initial value for column name in other table for linked records
|
||||||
fieldName: $tables.selected.name,
|
fieldName: $tables.selected.name,
|
||||||
}
|
}
|
||||||
|
@ -83,7 +80,23 @@
|
||||||
primaryDisplay =
|
primaryDisplay =
|
||||||
$tables.selected.primaryDisplay == null ||
|
$tables.selected.primaryDisplay == null ||
|
||||||
$tables.selected.primaryDisplay === editableColumn.name
|
$tables.selected.primaryDisplay === editableColumn.name
|
||||||
|
} else if (!savingColumn) {
|
||||||
|
let highestNumber = 0
|
||||||
|
Object.keys(table.schema).forEach(columnName => {
|
||||||
|
const columnNumber = extractColumnNumber(columnName)
|
||||||
|
if (columnNumber > highestNumber) {
|
||||||
|
highestNumber = columnNumber
|
||||||
|
}
|
||||||
|
return highestNumber
|
||||||
|
})
|
||||||
|
|
||||||
|
if (highestNumber >= 1) {
|
||||||
|
editableColumn.name = `Column 0${highestNumber + 1}`
|
||||||
|
} else {
|
||||||
|
editableColumn.name = "Column 01"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
allowedTypes = getAllowedTypes()
|
||||||
}
|
}
|
||||||
|
|
||||||
$: initialiseField(field, savingColumn)
|
$: initialiseField(field, savingColumn)
|
||||||
|
@ -182,6 +195,8 @@
|
||||||
indexes,
|
indexes,
|
||||||
})
|
})
|
||||||
dispatch("updatecolumns")
|
dispatch("updatecolumns")
|
||||||
|
gridDispatch("close-edit-column")
|
||||||
|
|
||||||
if (
|
if (
|
||||||
saveColumn.type === LINK_TYPE &&
|
saveColumn.type === LINK_TYPE &&
|
||||||
saveColumn.relationshipType === RelationshipType.MANY_TO_MANY
|
saveColumn.relationshipType === RelationshipType.MANY_TO_MANY
|
||||||
|
@ -203,6 +218,7 @@
|
||||||
|
|
||||||
function cancelEdit() {
|
function cancelEdit() {
|
||||||
editableColumn.name = originalName
|
editableColumn.name = originalName
|
||||||
|
gridDispatch("close-edit-column")
|
||||||
}
|
}
|
||||||
|
|
||||||
async function deleteColumn() {
|
async function deleteColumn() {
|
||||||
|
@ -214,8 +230,8 @@
|
||||||
await tables.deleteField(editableColumn)
|
await tables.deleteField(editableColumn)
|
||||||
notifications.success(`Column ${editableColumn.name} deleted`)
|
notifications.success(`Column ${editableColumn.name} deleted`)
|
||||||
confirmDeleteDialog.hide()
|
confirmDeleteDialog.hide()
|
||||||
hide()
|
|
||||||
dispatch("updatecolumns")
|
dispatch("updatecolumns")
|
||||||
|
gridDispatch("close-edit-column")
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
notifications.error(`Error deleting column: ${error.message}`)
|
notifications.error(`Error deleting column: ${error.message}`)
|
||||||
|
@ -251,14 +267,6 @@
|
||||||
required = req
|
required = req
|
||||||
}
|
}
|
||||||
|
|
||||||
function onChangePrimaryDisplay(e) {
|
|
||||||
const isPrimary = e.detail
|
|
||||||
// primary display is always required
|
|
||||||
if (isPrimary) {
|
|
||||||
editableColumn.constraints.presence = { allowEmpty: false }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function openJsonSchemaEditor() {
|
function openJsonSchemaEditor() {
|
||||||
jsonSchemaModal.show()
|
jsonSchemaModal.show()
|
||||||
}
|
}
|
||||||
|
@ -272,6 +280,11 @@
|
||||||
deleteColName = ""
|
deleteColName = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function extractColumnNumber(columnName) {
|
||||||
|
const match = columnName.match(/Column (\d+)/)
|
||||||
|
return match ? parseInt(match[1]) : 0
|
||||||
|
}
|
||||||
|
|
||||||
function getRelationshipOptions(field) {
|
function getRelationshipOptions(field) {
|
||||||
if (!field || !field.tableId) {
|
if (!field || !field.tableId) {
|
||||||
return null
|
return null
|
||||||
|
@ -402,15 +415,8 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<ModalContent
|
<Layout noPadding gap="S">
|
||||||
title={originalName ? "Edit Column" : "Create Column"}
|
|
||||||
confirmText="Save Column"
|
|
||||||
onConfirm={saveColumn}
|
|
||||||
onCancel={cancelEdit}
|
|
||||||
disabled={invalid}
|
|
||||||
>
|
|
||||||
<Input
|
<Input
|
||||||
label="Name"
|
|
||||||
bind:value={editableColumn.name}
|
bind:value={editableColumn.name}
|
||||||
disabled={uneditable ||
|
disabled={uneditable ||
|
||||||
(linkEditDisabled && editableColumn.type === LINK_TYPE)}
|
(linkEditDisabled && editableColumn.type === LINK_TYPE)}
|
||||||
|
@ -419,12 +425,12 @@
|
||||||
|
|
||||||
<Select
|
<Select
|
||||||
disabled={!typeEnabled}
|
disabled={!typeEnabled}
|
||||||
label="Type"
|
|
||||||
bind:value={editableColumn.type}
|
bind:value={editableColumn.type}
|
||||||
on:change={handleTypeChange}
|
on:change={handleTypeChange}
|
||||||
options={getAllowedTypes()}
|
options={allowedTypes}
|
||||||
getOptionLabel={field => field.name}
|
getOptionLabel={field => field.name}
|
||||||
getOptionValue={field => field.type}
|
getOptionValue={field => field.type}
|
||||||
|
getOptionIcon={field => field.icon}
|
||||||
isOptionEnabled={option => {
|
isOptionEnabled={option => {
|
||||||
if (option.type == AUTO_TYPE) {
|
if (option.type == AUTO_TYPE) {
|
||||||
return availableAutoColumnKeys?.length > 0
|
return availableAutoColumnKeys?.length > 0
|
||||||
|
@ -433,28 +439,6 @@
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{#if canBeRequired || canBeDisplay}
|
|
||||||
<div>
|
|
||||||
{#if canBeRequired}
|
|
||||||
<Toggle
|
|
||||||
value={required}
|
|
||||||
on:change={onChangeRequired}
|
|
||||||
disabled={primaryDisplay}
|
|
||||||
thin
|
|
||||||
text="Required"
|
|
||||||
/>
|
|
||||||
{/if}
|
|
||||||
{#if canBeDisplay}
|
|
||||||
<Toggle
|
|
||||||
bind:value={primaryDisplay}
|
|
||||||
on:change={onChangePrimaryDisplay}
|
|
||||||
thin
|
|
||||||
text="Use as table display column"
|
|
||||||
/>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
{#if editableColumn.type === "string"}
|
{#if editableColumn.type === "string"}
|
||||||
<Input
|
<Input
|
||||||
type="number"
|
type="number"
|
||||||
|
@ -462,9 +446,9 @@
|
||||||
bind:value={editableColumn.constraints.length.maximum}
|
bind:value={editableColumn.constraints.length.maximum}
|
||||||
/>
|
/>
|
||||||
{:else if editableColumn.type === "options"}
|
{:else if editableColumn.type === "options"}
|
||||||
<ValuesList
|
<OptionSelectDnD
|
||||||
label="Options (one per line)"
|
bind:constraints={editableColumn.constraints}
|
||||||
bind:values={editableColumn.constraints.inclusion}
|
bind:optionColors={editableColumn.optionColors}
|
||||||
/>
|
/>
|
||||||
{:else if editableColumn.type === "longform"}
|
{:else if editableColumn.type === "longform"}
|
||||||
<div>
|
<div>
|
||||||
|
@ -480,19 +464,28 @@
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{:else if editableColumn.type === "array"}
|
{:else if editableColumn.type === "array"}
|
||||||
<ValuesList
|
<OptionSelectDnD
|
||||||
label="Options (one per line)"
|
bind:constraints={editableColumn.constraints}
|
||||||
bind:values={editableColumn.constraints.inclusion}
|
bind:optionColors={editableColumn.optionColors}
|
||||||
/>
|
/>
|
||||||
{:else if editableColumn.type === "datetime" && !editableColumn.autocolumn}
|
{:else if editableColumn.type === "datetime" && !editableColumn.autocolumn}
|
||||||
<DatePicker
|
<div class="split-label">
|
||||||
label="Earliest"
|
<div class="label-length">
|
||||||
bind:value={editableColumn.constraints.datetime.earliest}
|
<Label size="M">Earliest</Label>
|
||||||
/>
|
</div>
|
||||||
<DatePicker
|
<div class="input-length">
|
||||||
label="Latest"
|
<DatePicker bind:value={editableColumn.constraints.datetime.earliest} />
|
||||||
bind:value={editableColumn.constraints.datetime.latest}
|
</div>
|
||||||
/>
|
</div>
|
||||||
|
|
||||||
|
<div class="split-label">
|
||||||
|
<div class="label-length">
|
||||||
|
<Label size="M">Latest</Label>
|
||||||
|
</div>
|
||||||
|
<div class="input-length">
|
||||||
|
<DatePicker bind:value={editableColumn.constraints.datetime.latest} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{#if datasource?.source !== "ORACLE" && datasource?.source !== "SQL_SERVER"}
|
{#if datasource?.source !== "ORACLE" && datasource?.source !== "SQL_SERVER"}
|
||||||
<div>
|
<div>
|
||||||
<Label
|
<Label
|
||||||
|
@ -509,16 +502,30 @@
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
{:else if editableColumn.type === "number" && !editableColumn.autocolumn}
|
{:else if editableColumn.type === "number" && !editableColumn.autocolumn}
|
||||||
<Input
|
<div class="split-label">
|
||||||
type="number"
|
<div class="label-length">
|
||||||
label="Min Value"
|
<Label size="M">Max Value</Label>
|
||||||
bind:value={editableColumn.constraints.numericality.greaterThanOrEqualTo}
|
</div>
|
||||||
/>
|
<div class="input-length">
|
||||||
<Input
|
<Input
|
||||||
type="number"
|
type="number"
|
||||||
label="Max Value"
|
bind:value={editableColumn.constraints.numericality
|
||||||
bind:value={editableColumn.constraints.numericality.lessThanOrEqualTo}
|
.greaterThanOrEqualTo}
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="split-label">
|
||||||
|
<div class="label-length">
|
||||||
|
<Label size="M">Max Value</Label>
|
||||||
|
</div>
|
||||||
|
<div class="input-length">
|
||||||
|
<Input
|
||||||
|
type="number"
|
||||||
|
bind:value={editableColumn.constraints.numericality.lessThanOrEqualTo}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{:else if editableColumn.type === "link"}
|
{:else if editableColumn.type === "link"}
|
||||||
<Select
|
<Select
|
||||||
label="Table"
|
label="Table"
|
||||||
|
@ -547,32 +554,44 @@
|
||||||
/>
|
/>
|
||||||
{:else if editableColumn.type === FORMULA_TYPE}
|
{:else if editableColumn.type === FORMULA_TYPE}
|
||||||
{#if !table.sql}
|
{#if !table.sql}
|
||||||
<Select
|
<div class="split-label">
|
||||||
label="Formula type"
|
<div class="label-length">
|
||||||
bind:value={editableColumn.formulaType}
|
<Label size="M">Formula Type</Label>
|
||||||
options={[
|
</div>
|
||||||
{ label: "Dynamic", value: "dynamic" },
|
<div class="input-length">
|
||||||
{ label: "Static", value: "static" },
|
<Select
|
||||||
]}
|
bind:value={editableColumn.formulaType}
|
||||||
getOptionLabel={option => option.label}
|
options={[
|
||||||
getOptionValue={option => option.value}
|
{ label: "Dynamic", value: "dynamic" },
|
||||||
tooltip="Dynamic formula are calculated when retrieved, but cannot be filtered or sorted by,
|
{ label: "Static", value: "static" },
|
||||||
|
]}
|
||||||
|
getOptionLabel={option => option.label}
|
||||||
|
getOptionValue={option => option.value}
|
||||||
|
tooltip="Dynamic formula are calculated when retrieved, but cannot be filtered or sorted by,
|
||||||
while static formula are calculated when the row is saved."
|
while static formula are calculated when the row is saved."
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
<ModalBindableInput
|
<div class="split-label">
|
||||||
title="Formula"
|
<div class="label-length">
|
||||||
label="Formula"
|
<Label size="M">Formula</Label>
|
||||||
value={editableColumn.formula}
|
</div>
|
||||||
on:change={e => {
|
<div class="input-length">
|
||||||
editableColumn = {
|
<ModalBindableInput
|
||||||
...editableColumn,
|
title="Formula"
|
||||||
formula: e.detail,
|
value={editableColumn.formula}
|
||||||
}
|
on:change={e => {
|
||||||
}}
|
editableColumn = {
|
||||||
bindings={getBindings({ table })}
|
...editableColumn,
|
||||||
allowJS
|
formula: e.detail,
|
||||||
/>
|
}
|
||||||
|
}}
|
||||||
|
bindings={getBindings({ table })}
|
||||||
|
allowJS
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{:else if editableColumn.type === JSON_TYPE}
|
{:else if editableColumn.type === JSON_TYPE}
|
||||||
<Button primary text on:click={openJsonSchemaEditor}
|
<Button primary text on:click={openJsonSchemaEditor}
|
||||||
>Open schema editor</Button
|
>Open schema editor</Button
|
||||||
|
@ -591,12 +610,28 @@
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<div slot="footer">
|
{#if canBeRequired || canBeDisplay}
|
||||||
{#if !uneditable && originalName != null}
|
<div>
|
||||||
<Button warning text on:click={confirmDelete}>Delete</Button>
|
{#if canBeRequired}
|
||||||
{/if}
|
<Toggle
|
||||||
</div>
|
value={required}
|
||||||
</ModalContent>
|
on:change={onChangeRequired}
|
||||||
|
disabled={primaryDisplay}
|
||||||
|
thin
|
||||||
|
text="Required"
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</Layout>
|
||||||
|
|
||||||
|
<div class="action-buttons">
|
||||||
|
{#if !uneditable && originalName != null}
|
||||||
|
<Button quiet warning text on:click={confirmDelete}>Delete</Button>
|
||||||
|
{/if}
|
||||||
|
<Button secondary newStyles on:click={cancelEdit}>Cancel</Button>
|
||||||
|
<Button disabled={invalid} newStyles cta on:click={saveColumn}>Save</Button>
|
||||||
|
</div>
|
||||||
<Modal bind:this={jsonSchemaModal}>
|
<Modal bind:this={jsonSchemaModal}>
|
||||||
<JSONSchemaModal
|
<JSONSchemaModal
|
||||||
schema={editableColumn.schema}
|
schema={editableColumn.schema}
|
||||||
|
@ -607,6 +642,7 @@
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
<ConfirmDialog
|
<ConfirmDialog
|
||||||
bind:this={confirmDeleteDialog}
|
bind:this={confirmDeleteDialog}
|
||||||
okText="Delete Column"
|
okText="Delete Column"
|
||||||
|
@ -622,3 +658,24 @@
|
||||||
</p>
|
</p>
|
||||||
<Input bind:value={deleteColName} placeholder={originalName} />
|
<Input bind:value={deleteColName} placeholder={originalName} />
|
||||||
</ConfirmDialog>
|
</ConfirmDialog>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.action-buttons {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
margin-top: var(--spacing-s);
|
||||||
|
gap: var(--spacing-l);
|
||||||
|
}
|
||||||
|
.split-label {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-length {
|
||||||
|
flex-basis: 40%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-length {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -1,15 +1,8 @@
|
||||||
<script>
|
<script>
|
||||||
import { getContext, onMount } from "svelte"
|
import { getContext } from "svelte"
|
||||||
import { Modal } from "@budibase/bbui"
|
|
||||||
import CreateEditColumn from "components/backend/DataTable/modals/CreateEditColumn.svelte"
|
import CreateEditColumn from "components/backend/DataTable/modals/CreateEditColumn.svelte"
|
||||||
|
|
||||||
const { rows, subscribe } = getContext("grid")
|
const { rows } = getContext("grid")
|
||||||
|
|
||||||
let modal
|
|
||||||
|
|
||||||
onMount(() => subscribe("add-column", modal.show))
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Modal bind:this={modal}>
|
<CreateEditColumn on:updatecolumns={rows.actions.refreshTableDefinition} />
|
||||||
<CreateEditColumn on:updatecolumns={rows.actions.refreshTableDefinition} />
|
|
||||||
</Modal>
|
|
||||||
|
|
|
@ -1,24 +1,19 @@
|
||||||
<script>
|
<script>
|
||||||
import { getContext, onMount } from "svelte"
|
import { getContext, onMount } from "svelte"
|
||||||
import { Modal } from "@budibase/bbui"
|
|
||||||
import CreateEditColumn from "../CreateEditColumn.svelte"
|
import CreateEditColumn from "../CreateEditColumn.svelte"
|
||||||
|
|
||||||
const { rows, subscribe } = getContext("grid")
|
const { rows, subscribe } = getContext("grid")
|
||||||
|
|
||||||
let editableColumn
|
let editableColumn
|
||||||
let editColumnModal
|
|
||||||
|
|
||||||
const editColumn = column => {
|
const editColumn = column => {
|
||||||
editableColumn = column
|
editableColumn = column
|
||||||
editColumnModal.show()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onMount(() => subscribe("edit-column", editColumn))
|
onMount(() => subscribe("edit-column", editColumn))
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Modal bind:this={editColumnModal}>
|
<CreateEditColumn
|
||||||
<CreateEditColumn
|
field={editableColumn}
|
||||||
field={editableColumn}
|
on:updatecolumns={rows.actions.refreshData}
|
||||||
on:updatecolumns={rows.actions.refreshData}
|
/>
|
||||||
/>
|
|
||||||
</Modal>
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ export const FIELDS = {
|
||||||
STRING: {
|
STRING: {
|
||||||
name: "Text",
|
name: "Text",
|
||||||
type: "string",
|
type: "string",
|
||||||
|
icon: "Text",
|
||||||
constraints: {
|
constraints: {
|
||||||
type: "string",
|
type: "string",
|
||||||
length: {},
|
length: {},
|
||||||
|
@ -11,6 +12,7 @@ export const FIELDS = {
|
||||||
BARCODEQR: {
|
BARCODEQR: {
|
||||||
name: "Barcode/QR",
|
name: "Barcode/QR",
|
||||||
type: "barcodeqr",
|
type: "barcodeqr",
|
||||||
|
icon: "Camera",
|
||||||
constraints: {
|
constraints: {
|
||||||
type: "string",
|
type: "string",
|
||||||
length: {},
|
length: {},
|
||||||
|
@ -20,6 +22,7 @@ export const FIELDS = {
|
||||||
LONGFORM: {
|
LONGFORM: {
|
||||||
name: "Long Form Text",
|
name: "Long Form Text",
|
||||||
type: "longform",
|
type: "longform",
|
||||||
|
icon: "TextAlignLeft",
|
||||||
constraints: {
|
constraints: {
|
||||||
type: "string",
|
type: "string",
|
||||||
length: {},
|
length: {},
|
||||||
|
@ -29,6 +32,7 @@ export const FIELDS = {
|
||||||
OPTIONS: {
|
OPTIONS: {
|
||||||
name: "Options",
|
name: "Options",
|
||||||
type: "options",
|
type: "options",
|
||||||
|
icon: "Dropdown",
|
||||||
constraints: {
|
constraints: {
|
||||||
type: "string",
|
type: "string",
|
||||||
presence: false,
|
presence: false,
|
||||||
|
@ -38,6 +42,7 @@ export const FIELDS = {
|
||||||
ARRAY: {
|
ARRAY: {
|
||||||
name: "Multi-select",
|
name: "Multi-select",
|
||||||
type: "array",
|
type: "array",
|
||||||
|
icon: "Duplicate",
|
||||||
constraints: {
|
constraints: {
|
||||||
type: "array",
|
type: "array",
|
||||||
presence: false,
|
presence: false,
|
||||||
|
@ -47,6 +52,7 @@ export const FIELDS = {
|
||||||
NUMBER: {
|
NUMBER: {
|
||||||
name: "Number",
|
name: "Number",
|
||||||
type: "number",
|
type: "number",
|
||||||
|
icon: "123",
|
||||||
constraints: {
|
constraints: {
|
||||||
type: "number",
|
type: "number",
|
||||||
presence: false,
|
presence: false,
|
||||||
|
@ -56,10 +62,12 @@ export const FIELDS = {
|
||||||
BIGINT: {
|
BIGINT: {
|
||||||
name: "BigInt",
|
name: "BigInt",
|
||||||
type: "bigint",
|
type: "bigint",
|
||||||
|
icon: "TagBold",
|
||||||
},
|
},
|
||||||
BOOLEAN: {
|
BOOLEAN: {
|
||||||
name: "Boolean",
|
name: "Boolean",
|
||||||
type: "boolean",
|
type: "boolean",
|
||||||
|
icon: "Boolean",
|
||||||
constraints: {
|
constraints: {
|
||||||
type: "boolean",
|
type: "boolean",
|
||||||
presence: false,
|
presence: false,
|
||||||
|
@ -68,6 +76,7 @@ export const FIELDS = {
|
||||||
DATETIME: {
|
DATETIME: {
|
||||||
name: "Date/Time",
|
name: "Date/Time",
|
||||||
type: "datetime",
|
type: "datetime",
|
||||||
|
icon: "Calendar",
|
||||||
constraints: {
|
constraints: {
|
||||||
type: "string",
|
type: "string",
|
||||||
length: {},
|
length: {},
|
||||||
|
@ -81,6 +90,7 @@ export const FIELDS = {
|
||||||
ATTACHMENT: {
|
ATTACHMENT: {
|
||||||
name: "Attachment",
|
name: "Attachment",
|
||||||
type: "attachment",
|
type: "attachment",
|
||||||
|
icon: "Folder",
|
||||||
constraints: {
|
constraints: {
|
||||||
type: "array",
|
type: "array",
|
||||||
presence: false,
|
presence: false,
|
||||||
|
@ -89,6 +99,7 @@ export const FIELDS = {
|
||||||
LINK: {
|
LINK: {
|
||||||
name: "Relationship",
|
name: "Relationship",
|
||||||
type: "link",
|
type: "link",
|
||||||
|
icon: "Link",
|
||||||
constraints: {
|
constraints: {
|
||||||
type: "array",
|
type: "array",
|
||||||
presence: false,
|
presence: false,
|
||||||
|
@ -97,11 +108,13 @@ export const FIELDS = {
|
||||||
FORMULA: {
|
FORMULA: {
|
||||||
name: "Formula",
|
name: "Formula",
|
||||||
type: "formula",
|
type: "formula",
|
||||||
|
icon: "Calculator",
|
||||||
constraints: {},
|
constraints: {},
|
||||||
},
|
},
|
||||||
JSON: {
|
JSON: {
|
||||||
name: "JSON",
|
name: "JSON",
|
||||||
type: "json",
|
type: "json",
|
||||||
|
icon: "Brackets",
|
||||||
constraints: {
|
constraints: {
|
||||||
type: "object",
|
type: "object",
|
||||||
presence: false,
|
presence: false,
|
||||||
|
|
|
@ -5420,11 +5420,7 @@
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"label": "Hide notifications",
|
"label": "Hide notifications",
|
||||||
"key": "notificationOverride",
|
"key": "notificationOverride",
|
||||||
"defaultValue": false,
|
"defaultValue": false
|
||||||
"dependsOn": {
|
|
||||||
"setting": "showSaveButton",
|
|
||||||
"value": true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,6 +83,7 @@
|
||||||
tableId: dataSource?.tableId,
|
tableId: dataSource?.tableId,
|
||||||
rowId: `{{ ${safe(repeaterId)}.${safe("_id")} }}`,
|
rowId: `{{ ${safe(repeaterId)}.${safe("_id")} }}`,
|
||||||
revId: `{{ ${safe(repeaterId)}.${safe("_rev")} }}`,
|
revId: `{{ ${safe(repeaterId)}.${safe("_rev")} }}`,
|
||||||
|
notificationOverride,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<script>
|
<script>
|
||||||
import { getContext } from "svelte"
|
import { getContext, onMount, tick } from "svelte"
|
||||||
import GridCell from "./GridCell.svelte"
|
import GridCell from "./GridCell.svelte"
|
||||||
import { Icon, Popover, Menu, MenuItem } from "@budibase/bbui"
|
import { Icon, Popover, Menu, MenuItem, clickOutside } from "@budibase/bbui"
|
||||||
import { getColumnIcon } from "../lib/utils"
|
import { getColumnIcon } from "../lib/utils"
|
||||||
|
|
||||||
export let column
|
export let column
|
||||||
|
@ -16,6 +16,7 @@
|
||||||
sort,
|
sort,
|
||||||
renderedColumns,
|
renderedColumns,
|
||||||
dispatch,
|
dispatch,
|
||||||
|
subscribe,
|
||||||
config,
|
config,
|
||||||
ui,
|
ui,
|
||||||
columns,
|
columns,
|
||||||
|
@ -32,7 +33,9 @@
|
||||||
|
|
||||||
let anchor
|
let anchor
|
||||||
let open = false
|
let open = false
|
||||||
|
let editIsOpen = false
|
||||||
let timeout
|
let timeout
|
||||||
|
let popover
|
||||||
|
|
||||||
$: sortedBy = column.name === $sort.column
|
$: sortedBy = column.name === $sort.column
|
||||||
$: canMoveLeft = orderable && idx > 0
|
$: canMoveLeft = orderable && idx > 0
|
||||||
|
@ -44,11 +47,16 @@
|
||||||
? "high-low"
|
? "high-low"
|
||||||
: "Z-A"
|
: "Z-A"
|
||||||
|
|
||||||
const editColumn = () => {
|
const editColumn = async () => {
|
||||||
|
editIsOpen = true
|
||||||
|
await tick()
|
||||||
dispatch("edit-column", column.schema)
|
dispatch("edit-column", column.schema)
|
||||||
open = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const cancelEdit = () => {
|
||||||
|
popover.hide()
|
||||||
|
editIsOpen = false
|
||||||
|
}
|
||||||
const onMouseDown = e => {
|
const onMouseDown = e => {
|
||||||
if (e.button === 0 && orderable) {
|
if (e.button === 0 && orderable) {
|
||||||
timeout = setTimeout(() => {
|
timeout = setTimeout(() => {
|
||||||
|
@ -109,6 +117,7 @@
|
||||||
columns.actions.saveChanges()
|
columns.actions.saveChanges()
|
||||||
open = false
|
open = false
|
||||||
}
|
}
|
||||||
|
onMount(() => subscribe("close-edit-column", cancelEdit))
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
|
@ -157,57 +166,74 @@
|
||||||
|
|
||||||
<Popover
|
<Popover
|
||||||
bind:open
|
bind:open
|
||||||
|
bind:this={popover}
|
||||||
{anchor}
|
{anchor}
|
||||||
align="right"
|
align="right"
|
||||||
offset={0}
|
offset={0}
|
||||||
popoverTarget={document.getElementById(`grid-${rand}`)}
|
popoverTarget={document.getElementById(`grid-${rand}`)}
|
||||||
animate={false}
|
animate={false}
|
||||||
|
customZindex={100}
|
||||||
>
|
>
|
||||||
<Menu>
|
{#if editIsOpen}
|
||||||
<MenuItem
|
<div
|
||||||
icon="Edit"
|
use:clickOutside={() => {
|
||||||
on:click={editColumn}
|
editIsOpen = false
|
||||||
disabled={!$config.allowSchemaChanges || column.schema.disabled}
|
}}
|
||||||
|
class="content"
|
||||||
>
|
>
|
||||||
Edit column
|
<slot />
|
||||||
</MenuItem>
|
</div>
|
||||||
<MenuItem
|
{:else}
|
||||||
icon="Label"
|
<Menu>
|
||||||
on:click={makeDisplayColumn}
|
<MenuItem
|
||||||
disabled={idx === "sticky" ||
|
icon="Edit"
|
||||||
!$config.allowSchemaChanges ||
|
on:click={editColumn}
|
||||||
bannedDisplayColumnTypes.includes(column.schema.type)}
|
disabled={!$config.allowSchemaChanges || column.schema.disabled}
|
||||||
>
|
>
|
||||||
Use as display column
|
Edit column
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
icon="SortOrderUp"
|
icon="Label"
|
||||||
on:click={sortAscending}
|
on:click={makeDisplayColumn}
|
||||||
disabled={column.name === $sort.column && $sort.order === "ascending"}
|
disabled={idx === "sticky" ||
|
||||||
>
|
!$config.allowSchemaChanges ||
|
||||||
Sort {ascendingLabel}
|
bannedDisplayColumnTypes.includes(column.schema.type)}
|
||||||
</MenuItem>
|
>
|
||||||
<MenuItem
|
Use as display column
|
||||||
icon="SortOrderDown"
|
</MenuItem>
|
||||||
on:click={sortDescending}
|
<MenuItem
|
||||||
disabled={column.name === $sort.column && $sort.order === "descending"}
|
icon="SortOrderUp"
|
||||||
>
|
on:click={sortAscending}
|
||||||
Sort {descendingLabel}
|
disabled={column.name === $sort.column && $sort.order === "ascending"}
|
||||||
</MenuItem>
|
>
|
||||||
<MenuItem disabled={!canMoveLeft} icon="ChevronLeft" on:click={moveLeft}>
|
Sort {ascendingLabel}
|
||||||
Move left
|
</MenuItem>
|
||||||
</MenuItem>
|
<MenuItem
|
||||||
<MenuItem disabled={!canMoveRight} icon="ChevronRight" on:click={moveRight}>
|
icon="SortOrderDown"
|
||||||
Move right
|
on:click={sortDescending}
|
||||||
</MenuItem>
|
disabled={column.name === $sort.column && $sort.order === "descending"}
|
||||||
<MenuItem
|
>
|
||||||
disabled={idx === "sticky" || !$config.showControls}
|
Sort {descendingLabel}
|
||||||
icon="VisibilityOff"
|
</MenuItem>
|
||||||
on:click={hideColumn}
|
<MenuItem disabled={!canMoveLeft} icon="ChevronLeft" on:click={moveLeft}>
|
||||||
>
|
Move left
|
||||||
Hide column
|
</MenuItem>
|
||||||
</MenuItem>
|
<MenuItem
|
||||||
</Menu>
|
disabled={!canMoveRight}
|
||||||
|
icon="ChevronRight"
|
||||||
|
on:click={moveRight}
|
||||||
|
>
|
||||||
|
Move right
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem
|
||||||
|
disabled={idx === "sticky" || !$config.showControls}
|
||||||
|
icon="VisibilityOff"
|
||||||
|
on:click={hideColumn}
|
||||||
|
>
|
||||||
|
Hide column
|
||||||
|
</MenuItem>
|
||||||
|
</Menu>
|
||||||
|
{/if}
|
||||||
</Popover>
|
</Popover>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
@ -255,4 +281,13 @@
|
||||||
.header-cell:hover .sort-indicator {
|
.header-cell:hover .sort-indicator {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
width: 300px;
|
||||||
|
padding: 20px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 20px;
|
||||||
|
background: var(--spectrum-alias-background-color-secondary);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
let focusedOptionIdx = null
|
let focusedOptionIdx = null
|
||||||
|
|
||||||
$: options = schema?.constraints?.inclusion || []
|
$: options = schema?.constraints?.inclusion || []
|
||||||
|
$: optionColors = schema?.optionColors || {}
|
||||||
$: editable = focused && !readonly
|
$: editable = focused && !readonly
|
||||||
$: values = Array.isArray(value) ? value : [value].filter(x => x != null)
|
$: values = Array.isArray(value) ? value : [value].filter(x => x != null)
|
||||||
$: {
|
$: {
|
||||||
|
@ -93,7 +94,7 @@
|
||||||
on:click={editable ? open : null}
|
on:click={editable ? open : null}
|
||||||
>
|
>
|
||||||
{#each values as val}
|
{#each values as val}
|
||||||
{@const color = getOptionColor(val)}
|
{@const color = optionColors[val] || getOptionColor(val)}
|
||||||
{#if color}
|
{#if color}
|
||||||
<div class="badge text" style="--color: {color}">
|
<div class="badge text" style="--color: {color}">
|
||||||
<span>
|
<span>
|
||||||
|
@ -121,7 +122,7 @@
|
||||||
use:clickOutside={close}
|
use:clickOutside={close}
|
||||||
>
|
>
|
||||||
{#each options as option, idx}
|
{#each options as option, idx}
|
||||||
{@const color = getOptionColor(option)}
|
{@const color = optionColors[option] || getOptionColor(option)}
|
||||||
<div
|
<div
|
||||||
class="option"
|
class="option"
|
||||||
on:click={() => toggleOption(option)}
|
on:click={() => toggleOption(option)}
|
||||||
|
|
|
@ -139,9 +139,20 @@
|
||||||
{#if $loaded}
|
{#if $loaded}
|
||||||
<div class="grid-data-outer" use:clickOutside={ui.actions.blur}>
|
<div class="grid-data-outer" use:clickOutside={ui.actions.blur}>
|
||||||
<div class="grid-data-inner">
|
<div class="grid-data-inner">
|
||||||
<StickyColumn />
|
<StickyColumn>
|
||||||
|
<svelte:fragment slot="edit-column">
|
||||||
|
<slot name="edit-column" />
|
||||||
|
</svelte:fragment>
|
||||||
|
</StickyColumn>
|
||||||
<div class="grid-data-content">
|
<div class="grid-data-content">
|
||||||
<HeaderRow />
|
<HeaderRow>
|
||||||
|
<svelte:fragment slot="add-column">
|
||||||
|
<slot name="add-column" />
|
||||||
|
</svelte:fragment>
|
||||||
|
<svelte:fragment slot="edit-column">
|
||||||
|
<slot name="edit-column" />
|
||||||
|
</svelte:fragment>
|
||||||
|
</HeaderRow>
|
||||||
<GridBody />
|
<GridBody />
|
||||||
</div>
|
</div>
|
||||||
{#if $canAddRows}
|
{#if $canAddRows}
|
||||||
|
|
|
@ -1,34 +1,22 @@
|
||||||
<script>
|
<script>
|
||||||
|
import NewColumnButton from "./NewColumnButton.svelte"
|
||||||
|
|
||||||
import { getContext } from "svelte"
|
import { getContext } from "svelte"
|
||||||
import GridScrollWrapper from "./GridScrollWrapper.svelte"
|
import GridScrollWrapper from "./GridScrollWrapper.svelte"
|
||||||
import HeaderCell from "../cells/HeaderCell.svelte"
|
import HeaderCell from "../cells/HeaderCell.svelte"
|
||||||
import { Icon, TempTooltip, TooltipType } from "@budibase/bbui"
|
import { TempTooltip, TooltipType } from "@budibase/bbui"
|
||||||
|
|
||||||
const {
|
const { renderedColumns, config, hasNonAutoColumn, tableId, loading } =
|
||||||
renderedColumns,
|
getContext("grid")
|
||||||
dispatch,
|
|
||||||
scroll,
|
|
||||||
hiddenColumnsWidth,
|
|
||||||
width,
|
|
||||||
config,
|
|
||||||
hasNonAutoColumn,
|
|
||||||
tableId,
|
|
||||||
loading,
|
|
||||||
} = getContext("grid")
|
|
||||||
|
|
||||||
$: columnsWidth = $renderedColumns.reduce(
|
|
||||||
(total, col) => total + col.width,
|
|
||||||
0
|
|
||||||
)
|
|
||||||
$: end = $hiddenColumnsWidth + columnsWidth - 1 - $scroll.left
|
|
||||||
$: left = Math.min($width - 40, end)
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<GridScrollWrapper scrollHorizontally>
|
<GridScrollWrapper scrollHorizontally>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
{#each $renderedColumns as column, idx}
|
{#each $renderedColumns as column, idx}
|
||||||
<HeaderCell {column} {idx} />
|
<HeaderCell {column} {idx}>
|
||||||
|
<slot name="edit-column" />
|
||||||
|
</HeaderCell>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
</GridScrollWrapper>
|
</GridScrollWrapper>
|
||||||
|
@ -39,13 +27,9 @@
|
||||||
type={TooltipType.Info}
|
type={TooltipType.Info}
|
||||||
condition={!$hasNonAutoColumn && !$loading}
|
condition={!$hasNonAutoColumn && !$loading}
|
||||||
>
|
>
|
||||||
<div
|
<NewColumnButton>
|
||||||
class="add"
|
<slot name="add-column" />
|
||||||
style="left:{left}px;"
|
</NewColumnButton>
|
||||||
on:click={() => dispatch("add-column")}
|
|
||||||
>
|
|
||||||
<Icon name="Add" />
|
|
||||||
</div>
|
|
||||||
</TempTooltip>
|
</TempTooltip>
|
||||||
{/key}
|
{/key}
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -61,21 +45,4 @@
|
||||||
.row {
|
.row {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
.add {
|
|
||||||
height: var(--default-row-height);
|
|
||||||
display: grid;
|
|
||||||
place-items: center;
|
|
||||||
width: 40px;
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
border-left: var(--cell-border);
|
|
||||||
border-right: var(--cell-border);
|
|
||||||
border-bottom: var(--cell-border);
|
|
||||||
background: var(--grid-background-alt);
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
.add:hover {
|
|
||||||
background: var(--spectrum-global-color-gray-200);
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -0,0 +1,79 @@
|
||||||
|
<script>
|
||||||
|
import { getContext, onMount } from "svelte"
|
||||||
|
import { Icon, Popover, clickOutside } from "@budibase/bbui"
|
||||||
|
|
||||||
|
const { renderedColumns, scroll, hiddenColumnsWidth, width, subscribe } =
|
||||||
|
getContext("grid")
|
||||||
|
|
||||||
|
let anchor
|
||||||
|
let open = false
|
||||||
|
$: columnsWidth = $renderedColumns.reduce(
|
||||||
|
(total, col) => (total += col.width),
|
||||||
|
0
|
||||||
|
)
|
||||||
|
$: end = $hiddenColumnsWidth + columnsWidth - 1 - $scroll.left
|
||||||
|
$: left = Math.min($width - 40, end)
|
||||||
|
|
||||||
|
const close = () => {
|
||||||
|
open = false
|
||||||
|
}
|
||||||
|
onMount(() => subscribe("close-edit-column", close))
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div
|
||||||
|
id="add-column-button"
|
||||||
|
bind:this={anchor}
|
||||||
|
class="add"
|
||||||
|
style="left:{left}px"
|
||||||
|
on:click={() => (open = true)}
|
||||||
|
>
|
||||||
|
<Icon name="Add" />
|
||||||
|
</div>
|
||||||
|
<Popover
|
||||||
|
bind:open
|
||||||
|
{anchor}
|
||||||
|
align="right"
|
||||||
|
offset={0}
|
||||||
|
popoverTarget={document.getElementById(`add-column-button`)}
|
||||||
|
animate={false}
|
||||||
|
customZindex={100}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
use:clickOutside={() => {
|
||||||
|
open = false
|
||||||
|
}}
|
||||||
|
class="content"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</Popover>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.add {
|
||||||
|
height: var(--default-row-height);
|
||||||
|
display: grid;
|
||||||
|
place-items: center;
|
||||||
|
width: 40px;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
border-left: var(--cell-border);
|
||||||
|
border-right: var(--cell-border);
|
||||||
|
border-bottom: var(--cell-border);
|
||||||
|
background: var(--grid-background-alt);
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
.add:hover {
|
||||||
|
background: var(--spectrum-global-color-gray-200);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
width: 300px;
|
||||||
|
padding: 20px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 20px;
|
||||||
|
z-index: 2;
|
||||||
|
background: var(--spectrum-alias-background-color-secondary);
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -57,7 +57,9 @@
|
||||||
disabled={!$renderedRows.length}
|
disabled={!$renderedRows.length}
|
||||||
/>
|
/>
|
||||||
{#if $stickyColumn}
|
{#if $stickyColumn}
|
||||||
<HeaderCell column={$stickyColumn} orderable={false} idx="sticky" />
|
<HeaderCell column={$stickyColumn} orderable={false} idx="sticky">
|
||||||
|
<slot name="edit-column" />
|
||||||
|
</HeaderCell>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue