Merge pull request #13899 from Budibase/cheeks-fixes
Various QoL and table fixes
This commit is contained in:
commit
672d78159e
|
@ -334,7 +334,7 @@
|
||||||
// Add in defaults and initial definition
|
// Add in defaults and initial definition
|
||||||
const definition = fieldDefinitions[type?.toUpperCase()]
|
const definition = fieldDefinitions[type?.toUpperCase()]
|
||||||
if (definition?.constraints) {
|
if (definition?.constraints) {
|
||||||
editableColumn.constraints = definition.constraints
|
editableColumn.constraints = cloneDeep(definition.constraints)
|
||||||
}
|
}
|
||||||
|
|
||||||
editableColumn.type = definition.type
|
editableColumn.type = definition.type
|
||||||
|
|
|
@ -38,4 +38,5 @@
|
||||||
{processFiles}
|
{processFiles}
|
||||||
handleFileTooLarge={$admin.cloud ? handleFileTooLarge : null}
|
handleFileTooLarge={$admin.cloud ? handleFileTooLarge : null}
|
||||||
{fileSizeLimit}
|
{fileSizeLimit}
|
||||||
|
on:change
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -5,8 +5,6 @@
|
||||||
const { styleable, builderStore } = getContext("sdk")
|
const { styleable, builderStore } = getContext("sdk")
|
||||||
const component = getContext("component")
|
const component = getContext("component")
|
||||||
|
|
||||||
let handlingOnClick = false
|
|
||||||
|
|
||||||
export let disabled = false
|
export let disabled = false
|
||||||
export let text = ""
|
export let text = ""
|
||||||
export let onClick
|
export let onClick
|
||||||
|
@ -19,17 +17,9 @@
|
||||||
// For internal use only for now - not defined in the manifest
|
// For internal use only for now - not defined in the manifest
|
||||||
export let active = false
|
export let active = false
|
||||||
|
|
||||||
const handleOnClick = async () => {
|
|
||||||
handlingOnClick = true
|
|
||||||
|
|
||||||
if (onClick) {
|
|
||||||
await onClick()
|
|
||||||
}
|
|
||||||
|
|
||||||
handlingOnClick = false
|
|
||||||
}
|
|
||||||
|
|
||||||
let node
|
let node
|
||||||
|
let touched = false
|
||||||
|
let handlingOnClick = false
|
||||||
|
|
||||||
$: $component.editing && node?.focus()
|
$: $component.editing && node?.focus()
|
||||||
$: componentText = getComponentText(text, $builderStore, $component)
|
$: componentText = getComponentText(text, $builderStore, $component)
|
||||||
|
@ -42,8 +32,19 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateText = e => {
|
const updateText = e => {
|
||||||
|
if (touched) {
|
||||||
builderStore.actions.updateProp("text", e.target.textContent)
|
builderStore.actions.updateProp("text", e.target.textContent)
|
||||||
}
|
}
|
||||||
|
touched = false
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleOnClick = async () => {
|
||||||
|
handlingOnClick = true
|
||||||
|
if (onClick) {
|
||||||
|
await onClick()
|
||||||
|
}
|
||||||
|
handlingOnClick = false
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#key $component.editing}
|
{#key $component.editing}
|
||||||
|
@ -57,6 +58,7 @@
|
||||||
on:blur={$component.editing ? updateText : null}
|
on:blur={$component.editing ? updateText : null}
|
||||||
bind:this={node}
|
bind:this={node}
|
||||||
class:active
|
class:active
|
||||||
|
on:input={() => (touched = true)}
|
||||||
>
|
>
|
||||||
{#if icon}
|
{#if icon}
|
||||||
<i class="{icon} {size}" />
|
<i class="{icon} {size}" />
|
||||||
|
|
|
@ -90,9 +90,11 @@
|
||||||
columns.forEach((column, idx) => {
|
columns.forEach((column, idx) => {
|
||||||
overrides[column.field] = {
|
overrides[column.field] = {
|
||||||
displayName: column.label,
|
displayName: column.label,
|
||||||
width: column.width,
|
|
||||||
order: idx,
|
order: idx,
|
||||||
}
|
}
|
||||||
|
if (column.width) {
|
||||||
|
overrides[column.field].width = column.width
|
||||||
|
}
|
||||||
})
|
})
|
||||||
return overrides
|
return overrides
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
export let size
|
export let size
|
||||||
|
|
||||||
let node
|
let node
|
||||||
|
let touched = false
|
||||||
|
|
||||||
$: $component.editing && node?.focus()
|
$: $component.editing && node?.focus()
|
||||||
$: placeholder = $builderStore.inBuilder && !text && !$component.editing
|
$: placeholder = $builderStore.inBuilder && !text && !$component.editing
|
||||||
|
@ -47,8 +48,11 @@
|
||||||
|
|
||||||
// Convert contenteditable HTML to text and save
|
// Convert contenteditable HTML to text and save
|
||||||
const updateText = e => {
|
const updateText = e => {
|
||||||
|
if (touched) {
|
||||||
builderStore.actions.updateProp("text", e.target.textContent)
|
builderStore.actions.updateProp("text", e.target.textContent)
|
||||||
}
|
}
|
||||||
|
touched = false
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#key $component.editing}
|
{#key $component.editing}
|
||||||
|
@ -62,6 +66,7 @@
|
||||||
class:underline
|
class:underline
|
||||||
class="spectrum-Heading {sizeClass} {alignClass}"
|
class="spectrum-Heading {sizeClass} {alignClass}"
|
||||||
on:blur={$component.editing ? updateText : null}
|
on:blur={$component.editing ? updateText : null}
|
||||||
|
on:input={() => (touched = true)}
|
||||||
>
|
>
|
||||||
{componentText}
|
{componentText}
|
||||||
</h1>
|
</h1>
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
export let size
|
export let size
|
||||||
|
|
||||||
let node
|
let node
|
||||||
|
let touched = false
|
||||||
|
|
||||||
$: $component.editing && node?.focus()
|
$: $component.editing && node?.focus()
|
||||||
$: externalLink = url && typeof url === "string" && !url.startsWith("/")
|
$: externalLink = url && typeof url === "string" && !url.startsWith("/")
|
||||||
|
@ -62,8 +63,11 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateText = e => {
|
const updateText = e => {
|
||||||
|
if (touched) {
|
||||||
builderStore.actions.updateProp("text", e.target.textContent)
|
builderStore.actions.updateProp("text", e.target.textContent)
|
||||||
}
|
}
|
||||||
|
touched = false
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if $component.editing}
|
{#if $component.editing}
|
||||||
|
@ -76,6 +80,7 @@
|
||||||
class:underline
|
class:underline
|
||||||
class="align--{align || 'left'} size--{size || 'M'}"
|
class="align--{align || 'left'} size--{size || 'M'}"
|
||||||
on:blur={$component.editing ? updateText : null}
|
on:blur={$component.editing ? updateText : null}
|
||||||
|
on:input={() => (touched = true)}
|
||||||
>
|
>
|
||||||
{componentText}
|
{componentText}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
export let size
|
export let size
|
||||||
|
|
||||||
let node
|
let node
|
||||||
|
let touched = false
|
||||||
|
|
||||||
$: $component.editing && node?.focus()
|
$: $component.editing && node?.focus()
|
||||||
$: placeholder = $builderStore.inBuilder && !text && !$component.editing
|
$: placeholder = $builderStore.inBuilder && !text && !$component.editing
|
||||||
|
@ -46,8 +47,11 @@
|
||||||
|
|
||||||
// Convert contenteditable HTML to text and save
|
// Convert contenteditable HTML to text and save
|
||||||
const updateText = e => {
|
const updateText = e => {
|
||||||
|
if (touched) {
|
||||||
builderStore.actions.updateProp("text", e.target.textContent)
|
builderStore.actions.updateProp("text", e.target.textContent)
|
||||||
}
|
}
|
||||||
|
touched = false
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#key $component.editing}
|
{#key $component.editing}
|
||||||
|
@ -61,6 +65,7 @@
|
||||||
class:underline
|
class:underline
|
||||||
class="spectrum-Body {sizeClass} {alignClass}"
|
class="spectrum-Body {sizeClass} {alignClass}"
|
||||||
on:blur={$component.editing ? updateText : null}
|
on:blur={$component.editing ? updateText : null}
|
||||||
|
on:input={() => (touched = true)}
|
||||||
>
|
>
|
||||||
{componentText}
|
{componentText}
|
||||||
</p>
|
</p>
|
||||||
|
|
|
@ -26,6 +26,10 @@
|
||||||
// Register field with form
|
// Register field with form
|
||||||
const formApi = formContext?.formApi
|
const formApi = formContext?.formApi
|
||||||
const labelPos = fieldGroupContext?.labelPosition || "above"
|
const labelPos = fieldGroupContext?.labelPosition || "above"
|
||||||
|
|
||||||
|
let touched = false
|
||||||
|
let labelNode
|
||||||
|
|
||||||
$: formStep = formStepContext ? $formStepContext || 1 : 1
|
$: formStep = formStepContext ? $formStepContext || 1 : 1
|
||||||
$: formField = formApi?.registerField(
|
$: formField = formApi?.registerField(
|
||||||
field,
|
field,
|
||||||
|
@ -36,14 +40,12 @@
|
||||||
validation,
|
validation,
|
||||||
formStep
|
formStep
|
||||||
)
|
)
|
||||||
|
|
||||||
$: schemaType =
|
$: schemaType =
|
||||||
fieldSchema?.type !== "formula" && fieldSchema?.type !== "bigint"
|
fieldSchema?.type !== "formula" && fieldSchema?.type !== "bigint"
|
||||||
? fieldSchema?.type
|
? fieldSchema?.type
|
||||||
: "string"
|
: "string"
|
||||||
|
|
||||||
// Focus label when editing
|
// Focus label when editing
|
||||||
let labelNode
|
|
||||||
$: $component.editing && labelNode?.focus()
|
$: $component.editing && labelNode?.focus()
|
||||||
|
|
||||||
// Update form properties in parent component on every store change
|
// Update form properties in parent component on every store change
|
||||||
|
@ -57,8 +59,11 @@
|
||||||
$: labelClass = labelPos === "above" ? "" : `spectrum-FieldLabel--${labelPos}`
|
$: labelClass = labelPos === "above" ? "" : `spectrum-FieldLabel--${labelPos}`
|
||||||
|
|
||||||
const updateLabel = e => {
|
const updateLabel = e => {
|
||||||
|
if (touched) {
|
||||||
builderStore.actions.updateProp("label", e.target.textContent)
|
builderStore.actions.updateProp("label", e.target.textContent)
|
||||||
}
|
}
|
||||||
|
touched = false
|
||||||
|
}
|
||||||
|
|
||||||
onDestroy(() => {
|
onDestroy(() => {
|
||||||
fieldApi?.deregister()
|
fieldApi?.deregister()
|
||||||
|
@ -79,6 +84,7 @@
|
||||||
bind:this={labelNode}
|
bind:this={labelNode}
|
||||||
contenteditable={$component.editing}
|
contenteditable={$component.editing}
|
||||||
on:blur={$component.editing ? updateLabel : null}
|
on:blur={$component.editing ? updateLabel : null}
|
||||||
|
on:input={() => (touched = true)}
|
||||||
class:hidden={!label}
|
class:hidden={!label}
|
||||||
class:readonly
|
class:readonly
|
||||||
for={fieldState?.fieldId}
|
for={fieldState?.fieldId}
|
||||||
|
|
|
@ -81,6 +81,7 @@
|
||||||
}
|
}
|
||||||
input {
|
input {
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
|
width: 0;
|
||||||
border: none;
|
border: none;
|
||||||
padding: var(--cell-padding);
|
padding: var(--cell-padding);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
|
@ -116,8 +116,10 @@
|
||||||
{#each displayColumns as column}
|
{#each displayColumns as column}
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<Icon size="S" name={getColumnIcon(column)} />
|
<Icon size="S" name={getColumnIcon(column)} />
|
||||||
|
<div class="column-label" title={column.label}>
|
||||||
{column.label}
|
{column.label}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<ToggleActionButtonGroup
|
<ToggleActionButtonGroup
|
||||||
on:click={e => toggleColumn(column, e.detail)}
|
on:click={e => toggleColumn(column, e.detail)}
|
||||||
value={columnToPermissionOptions(column)}
|
value={columnToPermissionOptions(column)}
|
||||||
|
@ -139,7 +141,8 @@
|
||||||
display: grid;
|
display: grid;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
grid-template-columns: 1fr auto;
|
grid-template-columns: 1fr auto;
|
||||||
gap: 8px;
|
grid-row-gap: 8px;
|
||||||
|
grid-column-gap: 24px;
|
||||||
}
|
}
|
||||||
.columns :global(.spectrum-Switch) {
|
.columns :global(.spectrum-Switch) {
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
|
@ -148,4 +151,11 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
}
|
}
|
||||||
|
.column-label {
|
||||||
|
min-width: 80px;
|
||||||
|
max-width: 200px;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -29,7 +29,6 @@
|
||||||
.permissionPicker {
|
.permissionPicker {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: var(--spacing-xs);
|
gap: var(--spacing-xs);
|
||||||
padding-left: calc(var(--spacing-xl) * 2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.permissionPicker :global(.spectrum-Icon) {
|
.permissionPicker :global(.spectrum-Icon) {
|
||||||
|
|
|
@ -23,14 +23,24 @@
|
||||||
0
|
0
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const updateBounds = () => {
|
||||||
|
bounds.set(body.getBoundingClientRect())
|
||||||
|
}
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
// Observe and record the height of the body
|
// Observe and record the height of the body
|
||||||
const observer = new ResizeObserver(() => {
|
const resizeObserver = new ResizeObserver(updateBounds)
|
||||||
bounds.set(body.getBoundingClientRect())
|
resizeObserver.observe(body)
|
||||||
})
|
|
||||||
observer.observe(body)
|
// Capture any wheel events on the page to ensure our scroll offset is
|
||||||
|
// correct. We don't care about touch events as we only need this for
|
||||||
|
// hovering over rows with a mouse.
|
||||||
|
window.addEventListener("wheel", updateBounds, true)
|
||||||
|
|
||||||
|
// Clean up listeners
|
||||||
return () => {
|
return () => {
|
||||||
observer.disconnect()
|
resizeObserver.disconnect()
|
||||||
|
window.removeEventListener("wheel", updateBounds, true)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -94,6 +94,7 @@ export const createActions = context => {
|
||||||
nonPlus,
|
nonPlus,
|
||||||
schemaMutations,
|
schemaMutations,
|
||||||
schema,
|
schema,
|
||||||
|
notifications,
|
||||||
} = context
|
} = context
|
||||||
|
|
||||||
// Gets the appropriate API for the configured datasource type
|
// Gets the appropriate API for the configured datasource type
|
||||||
|
@ -125,16 +126,25 @@ export const createActions = context => {
|
||||||
// Saves the datasource definition
|
// Saves the datasource definition
|
||||||
const saveDefinition = async newDefinition => {
|
const saveDefinition = async newDefinition => {
|
||||||
// Update local state
|
// Update local state
|
||||||
|
const originalDefinition = get(definition)
|
||||||
definition.set(newDefinition)
|
definition.set(newDefinition)
|
||||||
|
|
||||||
// Update server
|
// Update server
|
||||||
if (get(config).canSaveSchema) {
|
if (get(config).canSaveSchema) {
|
||||||
|
try {
|
||||||
await getAPI()?.actions.saveDefinition(newDefinition)
|
await getAPI()?.actions.saveDefinition(newDefinition)
|
||||||
|
|
||||||
// Broadcast change so external state can be updated, as this change
|
// Broadcast change so external state can be updated, as this change
|
||||||
// will not be received by the builder websocket because we caused it
|
// will not be received by the builder websocket because we caused it
|
||||||
// ourselves
|
// ourselves
|
||||||
dispatch("updatedatasource", newDefinition)
|
dispatch("updatedatasource", newDefinition)
|
||||||
|
} catch (error) {
|
||||||
|
const msg = error?.message || error || "Unknown error"
|
||||||
|
get(notifications).error(`Error saving schema: ${msg}`)
|
||||||
|
|
||||||
|
// Reset the definition if saving failed
|
||||||
|
definition.set(originalDefinition)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue