Merge pull request #13899 from Budibase/cheeks-fixes

Various QoL and table fixes
This commit is contained in:
Andrew Kingston 2024-06-12 14:09:30 +02:00 committed by GitHub
commit 672d78159e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 90 additions and 34 deletions

View File

@ -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

View File

@ -38,4 +38,5 @@
{processFiles} {processFiles}
handleFileTooLarge={$admin.cloud ? handleFileTooLarge : null} handleFileTooLarge={$admin.cloud ? handleFileTooLarge : null}
{fileSizeLimit} {fileSizeLimit}
on:change
/> />

View File

@ -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}" />

View File

@ -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
} }

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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}

View File

@ -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;

View File

@ -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>

View File

@ -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) {

View File

@ -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>

View File

@ -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)
}
} }
} }