This commit is contained in:
Dean 2024-03-22 15:26:05 +00:00
parent 35e1114fac
commit 6b5938aef6
20 changed files with 379 additions and 2 deletions

View File

@ -104,7 +104,7 @@ const environment = {
JS_BCRYPT: process.env.JS_BCRYPT,
JWT_SECRET: process.env.JWT_SECRET,
JWT_SECRET_FALLBACK: process.env.JWT_SECRET_FALLBACK,
ENCRYPTION_KEY: process.env.ENCRYPTION_KEY,
ENCRYPTION_KEY: "wgeBLcM6sXQTmaH0UtjpO+hCQhCBDn/c7GQHlnqgPCc=",
API_ENCRYPTION_KEY: getAPIEncryptionKey(),
COUCH_DB_URL: process.env.COUCH_DB_URL || "http://localhost:4005",
COUCH_DB_SQL_URL: process.env.COUCH_DB_SQL_URL || "http://localhost:4984",

View File

@ -0,0 +1,140 @@
<!--
Should this just be Canvas.svelte?
A drawing zone?
Shift it somewhere else?
width, height, toBase64, toFileObj
-->
<script>
import { onMount } from "svelte"
export let canvasWidth
export let canvasHeight
let canvasRef
let canvas
let mouseDown = false
let lastOffsetX, lastOffsetY
let touching = false
let touchmove = false
let debug = true
let debugData
export function toDataUrl() {
return canvasRef.toDataURL()
}
export function clear() {
return canvas.clearRect(0, 0, canvas.width, canvas.height)
}
const updated = (x, y) => {
return lastOffsetX != x || lastOffsetY != y
}
// Needs touch handling
const handleDraw = e => {
// e.touches[0] there should ol
e.preventDefault()
var rect = canvasRef.getBoundingClientRect()
const canvasX = e.offsetX || e.targetTouches?.[0].pageX - rect.left
const canvasY = e.offsetY || e.targetTouches?.[0].pageY - rect.top
const coords = { x: Math.round(canvasX), y: Math.round(canvasY) }
draw(coords.x, coords.y)
debugData = {
coords,
t0x: Math.round(e.touches?.[0].clientX),
t0y: Math.round(e.touches?.[0].clientY),
mouseOffx: e.offsetX,
mouseOffy: e.offsetY,
}
}
const draw = (xPos, yPos) => {
if (mouseDown && updated(xPos, yPos)) {
canvas.miterLimit = 3
canvas.lineWidth = 3
canvas.lineJoin = "round"
canvas.lineCap = "round"
canvas.strokeStyle = "white"
canvas.stroke()
canvas.lineTo(xPos, yPos)
lastOffsetX = xPos
lastOffsetY = yPos
}
}
const stopTracking = () => {
mouseDown = false
lastOffsetX = null
lastOffsetY = null
}
const canvasMouseDown = e => {
// if (e.button != 0) {
// return
// }
mouseDown = true
canvas.moveTo(e.offsetX, e.offsetY)
canvas.beginPath()
}
onMount(() => {
canvas = canvasRef.getContext("2d")
canvas.imageSmoothingEnabled = true
canvas.imageSmoothingQuality = "high"
})
</script>
<div>
<div>{JSON.stringify(debugData, null, 2)}</div>
<canvas
id="canvas"
width={200}
height={220}
bind:this={canvasRef}
on:mousemove={handleDraw}
on:mousedown={canvasMouseDown}
on:mouseup={stopTracking}
on:mouseleave={stopTracking}
on:touchstart={e => {
touching = true
canvasMouseDown(e)
}}
on:touchend={e => {
touching = false
touchmove = false
stopTracking(e)
}}
on:touchmove={e => {
touchmove = true
handleDraw(e)
}}
on:touchleave={e => {
touching = false
touchmove = false
stopTracking(e)
}}
class:touching={touching && debug}
class:touchmove={touchmove && debug}
/>
</div>
<style>
#canvas {
border: 1px solid blueviolet;
}
#canvas.touching {
border-color: aquamarine;
}
#canvas.touchmove {
border-color: rgb(227, 102, 68);
}
</style>

View File

@ -14,3 +14,4 @@ export { default as CoreStepper } from "./Stepper.svelte"
export { default as CoreRichTextField } from "./RichTextField.svelte"
export { default as CoreSlider } from "./Slider.svelte"
export { default as CoreFile } from "./File.svelte"
export { default as CoreSignature } from "./Signature.svelte"

View File

@ -385,6 +385,7 @@
return [
FIELDS.STRING,
FIELDS.BARCODEQR,
FIELDS.SIGNATURE,
FIELDS.LONGFORM,
FIELDS.OPTIONS,
FIELDS.ARRAY,

View File

@ -72,6 +72,7 @@ const componentMap = {
"field/array": FormFieldSelect,
"field/json": FormFieldSelect,
"field/barcodeqr": FormFieldSelect,
"field/signature": FormFieldSelect,
"field/bb_reference": FormFieldSelect,
// Some validation types are the same as others, so not all types are
// explicitly listed here. e.g. options uses string validation

View File

@ -43,5 +43,6 @@ export const FieldTypeToComponentMap = {
link: "relationshipfield",
json: "jsonfield",
barcodeqr: "codescanner",
signature: "signaturefield",
bb_reference: "bbreferencefield",
}

View File

@ -114,6 +114,16 @@ export const FIELDS = {
presence: false,
},
},
// USE the single approach like Adrias update
SIGNATURE: {
name: "Signature",
type: FieldType.SIGNATURE,
icon: "AnnotatePen",
constraints: {
type: "array",
presence: false,
},
},
LINK: {
name: "Relationship",
type: FieldType.LINK,

View File

@ -72,6 +72,7 @@
"multifieldselect",
"s3upload",
"codescanner",
"signaturefield",
"bbreferencefield"
]
},

View File

@ -13,6 +13,7 @@ const fieldTypeToComponentMap = {
attachment: "attachmentfield",
link: "relationshipfield",
json: "jsonfield",
signature: "signaturefield",
barcodeqr: "codescanner",
}

View File

@ -4112,6 +4112,55 @@
}
]
},
"signaturefield": {
"name": "Signature",
"icon": "AnnotatePen",
"styles": ["size"],
"size": {
"width": 400,
"height": 50
},
"settings": [
{
"type": "field/signature",
"label": "Field",
"key": "field",
"required": true
},
{
"type": "text",
"label": "Label",
"key": "label"
},
{
"type": "text",
"label": "Help text",
"key": "helpText"
},
{
"type": "boolean",
"label": "Disabled",
"key": "disabled",
"defaultValue": false
},
{
"type": "event",
"label": "On change",
"key": "onChange",
"context": [
{
"label": "Field Value",
"key": "value"
}
]
},
{
"type": "validation/string",
"label": "Validation",
"key": "validation"
}
]
},
"embeddedmap": {
"name": "Embedded Map",
"icon": "Location",

View File

@ -19,6 +19,7 @@
link: "relationshipfield",
json: "jsonfield",
barcodeqr: "codescanner",
signature: "signaturefield",
bb_reference: "bbreferencefield",
}

View File

@ -0,0 +1,8 @@
<script>
import SignatureField from "./SignatureField.svelte"
</script>
<div>
Sig Wrap
<SignatureField />
</div>

View File

@ -0,0 +1,6 @@
<script>
import { CoreSignature } from "@budibase/bbui"
</script>
<div>SignatureField</div>
<CoreSignature />

View File

@ -15,4 +15,5 @@ export { default as formstep } from "./FormStep.svelte"
export { default as jsonfield } from "./JSONField.svelte"
export { default as s3upload } from "./S3Upload.svelte"
export { default as codescanner } from "./CodeScannerField.svelte"
export { default as signaturefield } from "./SignatureField.svelte"
export { default as bbreferencefield } from "./BBReferenceField.svelte"

View File

@ -0,0 +1,143 @@
<script>
import { onMount, getContext } from "svelte"
import { CoreSignature } from "@budibase/bbui"
export let value
export let focused = false
export let onChange
export let readonly = false
export let api
export let invertX = false
export let invertY = false
// export let schema
const { API, notifications } = getContext("grid")
let isOpen = false
let sigCanvas
$: editable = focused && !readonly
$: {
if (!focused) {
close()
}
}
const onKeyDown = () => {
return isOpen
}
const open = () => {
isOpen = true
}
const close = () => {
isOpen = false
}
onMount(() => {
api = {
focus: () => open(),
blur: () => close(),
isActive: () => isOpen,
onKeyDown,
}
})
</script>
<!-- svelte-ignore a11y-no-static-element-interactions -->
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div class="signature-cell" class:editable on:click={editable ? open : null}>
signature cell: open {isOpen}
</div>
{#if isOpen}
<div class="signature" class:invertX class:invertY>
<button
on:click={() => {
console.log(sigCanvas.toDataUrl())
}}
>
check
</button>
<div class="field-wrap">
<CoreSignature
bind:this={sigCanvas}
on:change={() => {
console.log("cell change")
}}
/>
</div>
</div>
{/if}
<style>
.signature-cell {
/* display: flex;
padding: var(--cell-padding);
overflow: hidden;
user-select: none;
position: relative; */
flex: 1 1 auto;
display: flex;
flex-direction: row;
align-items: stretch;
padding: var(--cell-padding);
flex-wrap: nowrap;
gap: var(--cell-spacing);
align-self: stretch;
overflow: hidden;
user-select: none;
}
.signature-cell.editable:hover {
cursor: pointer;
}
.signature {
position: absolute;
top: 100%;
left: 0;
width: 320px;
background: var(--grid-background-alt);
border: var(--cell-border);
padding: var(--cell-padding);
box-shadow: 0 0 20px -4px rgba(0, 0, 0, 0.15);
border-bottom-left-radius: 2px;
border-bottom-right-radius: 2px;
}
.field-wrap {
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
background-color: var(--spectrum-global-color-gray-50);
color: var(--spectrum-alias-text-color);
/* font-size: var(--spectrum-alias-item-text-size-m); */
box-sizing: border-box;
border: var(--spectrum-alias-border-size-thin)
var(--spectrum-alias-border-color) solid;
border-radius: var(--spectrum-alias-border-radius-regular);
position: relative;
}
.signature.invertX {
left: auto;
right: 0;
}
.signature.invertY {
transform: translateY(-100%);
top: 0;
}
/* .attachment-cell {
flex: 1 1 auto;
display: flex;
flex-direction: row;
align-items: stretch;
padding: var(--cell-padding);
flex-wrap: nowrap;
gap: var(--cell-spacing);
align-self: stretch;
overflow: hidden;
user-select: none;
}
*/
</style>

View File

@ -10,12 +10,14 @@ import FormulaCell from "../cells/FormulaCell.svelte"
import JSONCell from "../cells/JSONCell.svelte"
import AttachmentCell from "../cells/AttachmentCell.svelte"
import BBReferenceCell from "../cells/BBReferenceCell.svelte"
import SignatureCell from "../cells/SignatureCell.svelte"
const TypeComponentMap = {
text: TextCell,
options: OptionsCell,
datetime: DateCell,
barcodeqr: TextCell,
signature: SignatureCell,
longform: LongFormCell,
array: MultiSelectCell,
number: NumberCell,

View File

@ -10,6 +10,7 @@ const TypeIconMap = {
options: "Dropdown",
datetime: "Date",
barcodeqr: "Camera",
signature: "AnnotatePen",
longform: "TextAlignLeft",
array: "Dropdown",
number: "123",
@ -30,7 +31,6 @@ export const getColumnIcon = column => {
return "MagicWand"
}
const { type, subtype } = column.schema
const result =
typeof TypeIconMap[type] === "object" && subtype
? TypeIconMap[type][subtype]

View File

@ -113,6 +113,14 @@ export const TYPE_TRANSFORM_MAP: any = {
[undefined]: undefined,
parse: parseArrayString,
},
//Review this
[FieldType.SIGNATURE]: {
//@ts-ignore
[null]: [],
//@ts-ignore
[undefined]: undefined,
parse: parseArrayString,
},
[FieldType.BOOLEAN]: {
"": null,
//@ts-ignore

View File

@ -15,6 +15,7 @@ const allowDisplayColumnByType: Record<FieldType, boolean> = {
[FieldType.BOOLEAN]: false,
[FieldType.ARRAY]: false,
[FieldType.ATTACHMENT]: false,
[FieldType.SIGNATURE]: false,
[FieldType.LINK]: false,
[FieldType.JSON]: false,
[FieldType.BB_REFERENCE]: false,
@ -35,6 +36,7 @@ const allowSortColumnByType: Record<FieldType, boolean> = {
[FieldType.FORMULA]: false,
[FieldType.ATTACHMENT]: false,
[FieldType.SIGNATURE]: false,
[FieldType.ARRAY]: false,
[FieldType.LINK]: false,
[FieldType.BB_REFERENCE]: false,

View File

@ -15,6 +15,7 @@ export enum FieldType {
JSON = "json",
INTERNAL = "internal",
BARCODEQR = "barcodeqr",
SIGNATURE = "signature",
BIGINT = "bigint",
BB_REFERENCE = "bb_reference",
}