Add inline editing of options fields
This commit is contained in:
parent
b19a2a3d3a
commit
6471464971
2 changed files with 115 additions and 14 deletions
|
@ -1,7 +1,12 @@
|
||||||
<script>
|
<script>
|
||||||
|
import { Icon } from "@budibase/bbui"
|
||||||
|
|
||||||
export let value
|
export let value
|
||||||
export let schema
|
export let schema
|
||||||
|
export let selected = false
|
||||||
|
export let onChange
|
||||||
|
|
||||||
|
const options = schema?.constraints?.inclusion || []
|
||||||
const colors = [
|
const colors = [
|
||||||
"rgb(207, 223, 255)",
|
"rgb(207, 223, 255)",
|
||||||
"rgb(208, 240, 253)",
|
"rgb(208, 240, 253)",
|
||||||
|
@ -14,26 +19,116 @@
|
||||||
"rgb(237, 226, 254)",
|
"rgb(237, 226, 254)",
|
||||||
]
|
]
|
||||||
|
|
||||||
$: idx = schema?.constraints?.inclusion?.indexOf(value)
|
let open = false
|
||||||
$: color = value && idx === -1 ? null : colors[idx % colors.length]
|
|
||||||
|
$: color = getColor(value)
|
||||||
|
$: {
|
||||||
|
// Close when deselected
|
||||||
|
if (!selected) {
|
||||||
|
open = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getColor = value => {
|
||||||
|
const index = options.indexOf(value)
|
||||||
|
if (!value || index === -1) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return colors[index % colors.length]
|
||||||
|
}
|
||||||
|
|
||||||
|
const toggle = () => {
|
||||||
|
open = !open
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div style="--color: {color}" class:valid={!!color}>
|
<div
|
||||||
{value || ""}
|
class="container"
|
||||||
|
class:selected
|
||||||
|
class:open
|
||||||
|
on:click={selected ? toggle : null}
|
||||||
|
>
|
||||||
|
{#if color}
|
||||||
|
<div class="badge text" style="--color: {color}">
|
||||||
|
{value}
|
||||||
|
</div>
|
||||||
|
{:else if value}
|
||||||
|
<div class="text">
|
||||||
|
{value}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{#if selected}
|
||||||
|
<Icon name="ChevronDown" />
|
||||||
|
{/if}
|
||||||
|
{#if open}
|
||||||
|
<div class="options">
|
||||||
|
<div class="option">
|
||||||
|
<div class="badge text" style="--color: {color}">
|
||||||
|
{value}
|
||||||
|
</div>
|
||||||
|
<Icon name="Checkmark" color="var(--spectrum-global-color-blue-400)" />
|
||||||
|
</div>
|
||||||
|
{#each options.filter(x => x !== value) as option}
|
||||||
|
<div class="option" on:click={() => onChange(option)}>
|
||||||
|
<div class="badge text" style="--color: {getColor(option)}">
|
||||||
|
{option}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
div {
|
.container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
padding: 0 8px;
|
padding: 0 8px;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
overflow: hidden;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
.container.selected:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.text {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
div.valid {
|
.badge {
|
||||||
margin: 0 8px;
|
|
||||||
padding: 2px 8px;
|
padding: 2px 8px;
|
||||||
background: var(--color);
|
background: var(--color);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
color: #333;
|
color: #2c2c2c;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
.options {
|
||||||
|
min-width: 100%;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: stretch;
|
||||||
|
box-shadow: 0 0 8px 4px rgba(0, 0, 0, 0.15);
|
||||||
|
border-radius: 4px;
|
||||||
|
max-height: 192px;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
.option {
|
||||||
|
flex: 0 0 32px;
|
||||||
|
padding: 0 8px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
background-color: var(--spectrum-global-color-gray-50);
|
||||||
|
}
|
||||||
|
.option:hover {
|
||||||
|
background-color: var(--spectrum-global-color-gray-100);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -106,17 +106,18 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleChange = async (rowId, field, value) => {
|
const handleChange = async (rowId, field, value) => {
|
||||||
selectedCell = null
|
|
||||||
let row = $fetch.rows.find(x => x._id === rowId)
|
let row = $fetch.rows.find(x => x._id === rowId)
|
||||||
if (!row) {
|
if (!row) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const newRow = {
|
if (row[field] === value) {
|
||||||
...row,
|
return
|
||||||
[field]: value,
|
|
||||||
}
|
}
|
||||||
changeCache[rowId] = { [field]: value }
|
changeCache[rowId] = { [field]: value }
|
||||||
await API.saveRow(newRow)
|
await API.saveRow({
|
||||||
|
...row,
|
||||||
|
...changeCache[rowId],
|
||||||
|
})
|
||||||
await fetch.refresh()
|
await fetch.refresh()
|
||||||
delete changeCache[rowId]
|
delete changeCache[rowId]
|
||||||
}
|
}
|
||||||
|
@ -237,6 +238,7 @@
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
border: 1px solid var(--spectrum-global-color-gray-400);
|
border: 1px solid var(--spectrum-global-color-gray-400);
|
||||||
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
.spreadsheet {
|
.spreadsheet {
|
||||||
display: grid;
|
display: grid;
|
||||||
|
@ -294,12 +296,13 @@
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
gap: 4px;
|
gap: 4px;
|
||||||
background: var(--spectrum-global-color-gray-50);
|
background: var(--spectrum-global-color-gray-50);
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
.cell.hovered {
|
.cell.hovered {
|
||||||
background: var(--spectrum-global-color-gray-100);
|
background: var(--spectrum-global-color-gray-100);
|
||||||
}
|
}
|
||||||
.cell.selected {
|
.cell.selected {
|
||||||
box-shadow: inset 0 0 0 2px rgb(89, 167, 246);
|
box-shadow: inset 0 0 0 2px var(--spectrum-global-color-blue-400);
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
.cell:hover {
|
.cell:hover {
|
||||||
|
@ -310,6 +313,9 @@
|
||||||
left: 50px;
|
left: 50px;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
}
|
}
|
||||||
|
.cell.sticky.selected {
|
||||||
|
z-index: 3;
|
||||||
|
}
|
||||||
.cell.row-selected {
|
.cell.row-selected {
|
||||||
background-color: rgb(224, 242, 255);
|
background-color: rgb(224, 242, 255);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue