1
0
Fork 0
mirror of synced 2024-09-17 01:38:40 +12:00

Add new long form text cell

This commit is contained in:
Andrew Kingston 2023-03-14 09:44:21 +00:00
parent aefdfabe39
commit 125febdd5a
8 changed files with 144 additions and 18 deletions

View file

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

View file

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

View file

@ -0,0 +1 @@
<div>[MISSING]</div>

View file

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

View file

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

View file

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

View file

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

View file

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