Add new long form text cell
This commit is contained in:
parent
aefdfabe39
commit
125febdd5a
|
@ -2,23 +2,36 @@
|
||||||
import { getContext, onMount } from "svelte"
|
import { getContext, onMount } from "svelte"
|
||||||
import { get } from "svelte/store"
|
import { get } from "svelte/store"
|
||||||
|
|
||||||
const { rows, rand, selectedCellId, columns, selectedCellRow, stickyColumn, selectedCellAPI } =
|
const {
|
||||||
getContext("sheet")
|
rows,
|
||||||
|
selectedCellId,
|
||||||
|
columns,
|
||||||
|
selectedCellRow,
|
||||||
|
stickyColumn,
|
||||||
|
selectedCellAPI,
|
||||||
|
} = getContext("sheet")
|
||||||
|
|
||||||
const handleKeyDown = e => {
|
const handleKeyDown = e => {
|
||||||
const api = get(selectedCellAPI)
|
const api = get(selectedCellAPI)
|
||||||
|
if (!api) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Always capture escape and blur any selected cell
|
// Always intercept certain key presses
|
||||||
if (e.key === "Escape") {
|
if (e.key === "Escape") {
|
||||||
api?.blur()
|
api.blur()
|
||||||
|
} else if (e.key === "Tab") {
|
||||||
|
api.blur()
|
||||||
|
changeSelectedColumn(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pass the key event to the selected cell and let it decide whether to
|
// Pass the key event to the selected cell and let it decide whether to
|
||||||
// capture it or not
|
// capture it or not
|
||||||
const handled = api?.onKeyDown?.(e)
|
const handled = api.onKeyDown?.(e)
|
||||||
if (handled) {
|
if (handled) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
e.preventDefault()
|
||||||
|
|
||||||
// Handle the key ourselves
|
// Handle the key ourselves
|
||||||
switch (e.key) {
|
switch (e.key) {
|
||||||
|
@ -39,7 +52,7 @@
|
||||||
break
|
break
|
||||||
case "Enter":
|
case "Enter":
|
||||||
focusSelectedCell()
|
focusSelectedCell()
|
||||||
break;
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -46,6 +46,7 @@
|
||||||
|
|
||||||
// Handles a wheel even and updates the scroll offsets
|
// Handles a wheel even and updates the scroll offsets
|
||||||
const handleWheel = e => {
|
const handleWheel = e => {
|
||||||
|
console.log("wheel scrol!")
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
const modifier = e.ctrlKey || e.metaKey
|
const modifier = e.ctrlKey || e.metaKey
|
||||||
let x = modifier ? e.deltaY : e.deltaX
|
let x = modifier ? e.deltaY : e.deltaX
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
<div>[MISSING]</div>
|
|
@ -2,7 +2,7 @@
|
||||||
import { getContext } from "svelte"
|
import { getContext } from "svelte"
|
||||||
import SheetCell from "./SheetCell.svelte"
|
import SheetCell from "./SheetCell.svelte"
|
||||||
import { Icon, Popover, Menu, MenuItem } from "@budibase/bbui"
|
import { Icon, Popover, Menu, MenuItem } from "@budibase/bbui"
|
||||||
import { getIconForField } from "../utils"
|
import { getColumnIcon } from "../utils"
|
||||||
|
|
||||||
export let column
|
export let column
|
||||||
export let idx
|
export let idx
|
||||||
|
@ -17,7 +17,7 @@
|
||||||
renderedColumns,
|
renderedColumns,
|
||||||
dispatch,
|
dispatch,
|
||||||
config,
|
config,
|
||||||
ui
|
ui,
|
||||||
} = getContext("sheet")
|
} = getContext("sheet")
|
||||||
|
|
||||||
let anchor
|
let anchor
|
||||||
|
@ -99,7 +99,7 @@
|
||||||
>
|
>
|
||||||
<Icon
|
<Icon
|
||||||
size="S"
|
size="S"
|
||||||
name={getIconForField(column)}
|
name={getColumnIcon(column)}
|
||||||
color={`var(--spectrum-global-color-gray-600)`}
|
color={`var(--spectrum-global-color-gray-600)`}
|
||||||
/>
|
/>
|
||||||
<div class="name">
|
<div class="name">
|
||||||
|
|
|
@ -0,0 +1,101 @@
|
||||||
|
<script>
|
||||||
|
import { onMount, tick } from "svelte"
|
||||||
|
|
||||||
|
export let value
|
||||||
|
export let selected = false
|
||||||
|
export let onChange
|
||||||
|
export let readonly = false
|
||||||
|
export let api
|
||||||
|
|
||||||
|
let textarea
|
||||||
|
let isOpen = false
|
||||||
|
|
||||||
|
$: editable = selected && !readonly
|
||||||
|
$: {
|
||||||
|
if (!selected) {
|
||||||
|
isOpen = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleChange = e => {
|
||||||
|
onChange(e.target.value)
|
||||||
|
console.log(e.target.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
const onKeyDown = () => {
|
||||||
|
return isOpen
|
||||||
|
}
|
||||||
|
|
||||||
|
const open = async () => {
|
||||||
|
isOpen = true
|
||||||
|
await tick()
|
||||||
|
textarea.focus()
|
||||||
|
textarea.setSelectionRange(0, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
const close = () => {
|
||||||
|
textarea?.blur()
|
||||||
|
isOpen = false
|
||||||
|
}
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
api = {
|
||||||
|
focus: () => open(),
|
||||||
|
blur: () => close(),
|
||||||
|
onKeyDown,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if isOpen}
|
||||||
|
<textarea
|
||||||
|
bind:this={textarea}
|
||||||
|
value={value || ""}
|
||||||
|
on:change={handleChange}
|
||||||
|
on:wheel|stopPropagation
|
||||||
|
/>
|
||||||
|
{:else}
|
||||||
|
<div class="long-form-cell" on:click={editable ? open : null} class:editable>
|
||||||
|
<div class="value">
|
||||||
|
{value || ""}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.long-form-cell {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
padding: 0 var(--cell-padding);
|
||||||
|
align-self: stretch;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.long-form-cell.editable:hover {
|
||||||
|
cursor: text;
|
||||||
|
}
|
||||||
|
.value {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
textarea {
|
||||||
|
padding: var(--cell-padding);
|
||||||
|
margin: 0;
|
||||||
|
border: none;
|
||||||
|
background: var(--cell-background);
|
||||||
|
font-size: var(--cell-font-size);
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
color: inherit;
|
||||||
|
position: absolute;
|
||||||
|
top: -1px;
|
||||||
|
left: -1px;
|
||||||
|
width: calc(100% + 100px);
|
||||||
|
height: calc(5 * var(--cell-height) + 1px);
|
||||||
|
border: var(--cell-border);
|
||||||
|
box-shadow: 0 0 8px 4px rgba(0, 0, 0, 0.15);
|
||||||
|
}
|
||||||
|
textarea:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -183,17 +183,18 @@
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
.options {
|
.options {
|
||||||
min-width: 100%;
|
min-width: calc(100% + 2px);
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: -1px;
|
||||||
left: 0;
|
left: -1px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
box-shadow: 0 0 8px 4px rgba(0, 0, 0, 0.15);
|
box-shadow: 0 0 8px 4px rgba(0, 0, 0, 0.15);
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
max-height: calc(6 * var(--cell-height) - 1px);
|
max-height: calc(5 * var(--cell-height) + 1px);
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
border: var(--cell-border);
|
||||||
}
|
}
|
||||||
.option {
|
.option {
|
||||||
flex: 0 0 var(--cell-height);
|
flex: 0 0 var(--cell-height);
|
||||||
|
|
|
@ -4,13 +4,22 @@ import MultiSelectCell from "./cells/MultiSelectCell.svelte"
|
||||||
import NumberCell from "./cells/NumberCell.svelte"
|
import NumberCell from "./cells/NumberCell.svelte"
|
||||||
import RelationshipCell from "./cells/RelationshipCell.svelte"
|
import RelationshipCell from "./cells/RelationshipCell.svelte"
|
||||||
import TextCell from "./cells/TextCell.svelte"
|
import TextCell from "./cells/TextCell.svelte"
|
||||||
|
import BlankCell from "./cells/BlankCell.svelte"
|
||||||
|
import LongFormCell from "./cells/LongFormCell.svelte"
|
||||||
|
|
||||||
const TypeComponentMap = {
|
const TypeComponentMap = {
|
||||||
|
text: TextCell,
|
||||||
options: OptionsCell,
|
options: OptionsCell,
|
||||||
datetime: DateCell,
|
datetime: DateCell,
|
||||||
|
barcodeqr: BlankCell,
|
||||||
|
longform: LongFormCell,
|
||||||
array: MultiSelectCell,
|
array: MultiSelectCell,
|
||||||
number: NumberCell,
|
number: NumberCell,
|
||||||
|
boolean: BlankCell,
|
||||||
|
attachment: BlankCell,
|
||||||
link: RelationshipCell,
|
link: RelationshipCell,
|
||||||
|
formula: BlankCell,
|
||||||
|
json: BlankCell,
|
||||||
}
|
}
|
||||||
export const getCellRenderer = column => {
|
export const getCellRenderer = column => {
|
||||||
return TypeComponentMap[column?.schema?.type] || TextCell
|
return TypeComponentMap[column?.schema?.type] || TextCell
|
||||||
|
|
|
@ -5,10 +5,10 @@ export const getColor = (idx, opacity = 0.3) => {
|
||||||
return `hsla(${((idx + 1) * 222) % 360}, 90%, 75%, ${opacity})`
|
return `hsla(${((idx + 1) * 222) % 360}, 90%, 75%, ${opacity})`
|
||||||
}
|
}
|
||||||
|
|
||||||
const DataTypeIconMap = {
|
const TypeIconMap = {
|
||||||
|
text: "Text",
|
||||||
options: "Dropdown",
|
options: "Dropdown",
|
||||||
datetime: "Date",
|
datetime: "Date",
|
||||||
text: "Text",
|
|
||||||
barcodeqr: "Camera",
|
barcodeqr: "Camera",
|
||||||
longform: "TextAlignLeft",
|
longform: "TextAlignLeft",
|
||||||
array: "Dropdown",
|
array: "Dropdown",
|
||||||
|
@ -20,7 +20,7 @@ const DataTypeIconMap = {
|
||||||
json: "Brackets",
|
json: "Brackets",
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getIconForField = field => {
|
export const getColumnIcon = column => {
|
||||||
const type = field.schema.type
|
const type = column.schema.type
|
||||||
return DataTypeIconMap[type] || "Text"
|
return TypeIconMap[type] || "Text"
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue