UX/UI updates and rollup config changes to exclude common warnings
This commit is contained in:
parent
644d0f2bdd
commit
e060db2929
|
@ -3168,24 +3168,39 @@
|
||||||
"section"
|
"section"
|
||||||
],
|
],
|
||||||
"settings": [
|
"settings": [
|
||||||
{
|
|
||||||
"type": "text",
|
|
||||||
"label": "Label",
|
|
||||||
"key": "label",
|
|
||||||
"defaultValue": true
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"type": "field/code",
|
"type": "field/code",
|
||||||
"label": "Field",
|
"label": "Field",
|
||||||
"key": "field",
|
"key": "field",
|
||||||
"required": true
|
"required": true
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"label": "Label",
|
||||||
|
"key": "label"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"label": "Button",
|
||||||
|
"key": "scanButtonText"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"label": "Default value",
|
||||||
|
"key": "defaultValue"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"label": "Disabled",
|
"label": "Disabled",
|
||||||
"key": "disabled",
|
"key": "disabled",
|
||||||
"defaultValue": false
|
"defaultValue": false
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "boolean",
|
||||||
|
"label": "Allow manual entry",
|
||||||
|
"key": "allowManualEntry",
|
||||||
|
"defaultValue": false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "validation/string",
|
"type": "validation/string",
|
||||||
"label": "Validation",
|
"label": "Validation",
|
||||||
|
|
|
@ -27,6 +27,15 @@ export default {
|
||||||
file: `./dist/budibase-client.js`,
|
file: `./dist/budibase-client.js`,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
onwarn(warning, warn) {
|
||||||
|
if (
|
||||||
|
warning.code === "THIS_IS_UNDEFINED" ||
|
||||||
|
warning.code === "CIRCULAR_DEPENDENCY"
|
||||||
|
) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
warn(warning)
|
||||||
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
alias({
|
alias({
|
||||||
entries: [
|
entries: [
|
||||||
|
|
|
@ -1,123 +1,189 @@
|
||||||
<script>
|
<script>
|
||||||
import { ModalContent, Modal, Select } from "@budibase/bbui"
|
import { ModalContent, Modal, Icon, ActionButton } from "@budibase/bbui"
|
||||||
import { Input, Button, StatusLight } from "@budibase/bbui"
|
import { Input, Button, StatusLight } from "@budibase/bbui"
|
||||||
import { Html5Qrcode } from "html5-qrcode"
|
import { Html5Qrcode } from "html5-qrcode"
|
||||||
|
|
||||||
export let code = ""
|
export let value
|
||||||
|
export let disabled = false
|
||||||
|
export let allowManualEntry = false
|
||||||
|
export let scanButtonText = "Scan Code"
|
||||||
|
|
||||||
|
import { createEventDispatcher } from "svelte"
|
||||||
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
let videoEle
|
let videoEle
|
||||||
let camModal
|
let camModal
|
||||||
let manualMode = false
|
let manualMode = false
|
||||||
let enabled = false
|
let cameraEnabled
|
||||||
let cameraInit = false
|
let cameraStarted = false
|
||||||
let html5QrCode
|
let html5QrCode
|
||||||
let cameraId
|
let cameraSetting = { facingMode: "environment" }
|
||||||
let cameraDevices = []
|
let cameraConfig = {
|
||||||
let selectedCam
|
fps: 25,
|
||||||
|
qrbox: { width: 250, height: 250 },
|
||||||
|
}
|
||||||
|
const onScanSuccess = decodedText => {
|
||||||
|
if (value != decodedText) {
|
||||||
|
dispatch("change", decodedText)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const initReader = async () => {
|
||||||
|
if (html5QrCode) {
|
||||||
|
html5QrCode.stop()
|
||||||
|
}
|
||||||
|
html5QrCode = new Html5Qrcode("reader")
|
||||||
|
return new Promise(resolve => {
|
||||||
|
html5QrCode
|
||||||
|
.start(cameraSetting, cameraConfig, onScanSuccess)
|
||||||
|
.then(() => {
|
||||||
|
resolve({ initialised: true })
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.log("There was a problem scanning the image", err)
|
||||||
|
resolve({ initialised: false })
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const checkCamera = async () => {
|
const checkCamera = async () => {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
Html5Qrcode.getCameras()
|
Html5Qrcode.getCameras()
|
||||||
.then(devices => {
|
.then(devices => {
|
||||||
if (devices && devices.length) {
|
if (devices && devices.length) {
|
||||||
cameraDevices = devices
|
|
||||||
cameraId = devices[0].id
|
|
||||||
resolve({ enabled: true })
|
resolve({ enabled: true })
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(e => {
|
||||||
|
console.error(e)
|
||||||
resolve({ enabled: false })
|
resolve({ enabled: false })
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
$: if (enabled && videoEle && !cameraInit) {
|
const start = async () => {
|
||||||
html5QrCode = new Html5Qrcode("reader")
|
const status = await initReader()
|
||||||
html5QrCode
|
cameraStarted = status.initialised
|
||||||
.start(
|
|
||||||
cameraId,
|
|
||||||
{
|
|
||||||
fps: 25,
|
|
||||||
qrbox: { width: 250, height: 250 },
|
|
||||||
},
|
|
||||||
(decodedText, decodedResult) => {
|
|
||||||
code = decodedText
|
|
||||||
console.log(decodedText, decodedResult)
|
|
||||||
}
|
}
|
||||||
)
|
|
||||||
.catch(err => {
|
$: if (cameraEnabled && videoEle && !cameraStarted) {
|
||||||
console.log("There was a problem scanning the image", err)
|
start()
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const showReaderModal = async () => {
|
const showReaderModal = async () => {
|
||||||
camModal.show()
|
camModal.show()
|
||||||
const camStatus = await checkCamera()
|
const camStatus = await checkCamera()
|
||||||
enabled = camStatus.enabled
|
cameraEnabled = camStatus.enabled
|
||||||
}
|
}
|
||||||
|
|
||||||
const hideReaderModal = async () => {
|
const hideReaderModal = async () => {
|
||||||
camModal.hide()
|
cameraEnabled = undefined
|
||||||
|
cameraStarted = false
|
||||||
|
if (html5QrCode) {
|
||||||
await html5QrCode.stop()
|
await html5QrCode.stop()
|
||||||
|
html5QrCode = undefined
|
||||||
|
}
|
||||||
|
camModal.hide()
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="scanner-video-wrapper">
|
<div class="scanner-video-wrapper">
|
||||||
{#if code}
|
{#if value && !manualMode}
|
||||||
<div class="scanner-value">
|
<div class="scanner-value field-display">
|
||||||
<StatusLight positive />
|
<StatusLight positive />
|
||||||
{code}
|
{value}
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
<Button primary icon="Camera" on:click={showReaderModal}>Scan Code</Button>
|
|
||||||
|
{#if allowManualEntry && manualMode}
|
||||||
|
<div class="manual-input">
|
||||||
|
<Input
|
||||||
|
bind:value
|
||||||
|
on:change={() => {
|
||||||
|
dispatch("change", value)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{#if value}
|
||||||
|
<ActionButton
|
||||||
|
on:click={() => {
|
||||||
|
dispatch("change", "")
|
||||||
|
}}
|
||||||
|
{disabled}
|
||||||
|
>
|
||||||
|
Clear
|
||||||
|
</ActionButton>
|
||||||
|
{:else}
|
||||||
|
<ActionButton
|
||||||
|
icon="Camera"
|
||||||
|
on:click={() => {
|
||||||
|
showReaderModal()
|
||||||
|
}}
|
||||||
|
{disabled}
|
||||||
|
>
|
||||||
|
{scanButtonText}
|
||||||
|
</ActionButton>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="modal-wrap">
|
<div class="modal-wrap">
|
||||||
<Select
|
|
||||||
on:change={e => console.log(e)}
|
|
||||||
value={selectedCam}
|
|
||||||
options={cameraDevices}
|
|
||||||
getOptionLabel={() => cameraDevices}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Modal bind:this={camModal} on:hide={hideReaderModal}>
|
<Modal bind:this={camModal} on:hide={hideReaderModal}>
|
||||||
<ModalContent
|
<ModalContent
|
||||||
title="Scan Code"
|
title={scanButtonText}
|
||||||
showCancelButton={false}
|
|
||||||
showConfirmButton={false}
|
showConfirmButton={false}
|
||||||
|
showCancelButton={false}
|
||||||
>
|
>
|
||||||
<div id="reader" bind:this={videoEle} />
|
<div id="reader" class="container" bind:this={videoEle}>
|
||||||
<div class="code-wrap">
|
<div class="camera-placeholder">
|
||||||
{#if manualMode}
|
<Icon size="XXL" name="Camera" />
|
||||||
<Input label="Enter" bind:value={code} />
|
{#if cameraEnabled === false}
|
||||||
|
<div>Your camera is disabled.</div>
|
||||||
{/if}
|
{/if}
|
||||||
{#if code}
|
</div>
|
||||||
|
</div>
|
||||||
|
{#if cameraEnabled === true}
|
||||||
|
<div class="code-wrap">
|
||||||
|
{#if value}
|
||||||
<div class="scanner-value">
|
<div class="scanner-value">
|
||||||
<StatusLight positive />
|
<StatusLight positive />
|
||||||
{code}
|
{value}
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{:else}
|
||||||
{#if !code && enabled && videoEle && cameraInit}
|
|
||||||
<div class="scanner-value">
|
<div class="scanner-value">
|
||||||
<StatusLight neutral />
|
<StatusLight neutral />
|
||||||
Searching for code...
|
Searching for code...
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
<div slot="footer">
|
<div slot="footer">
|
||||||
<div class="footer-buttons">
|
<div class="footer-buttons">
|
||||||
|
{#if allowManualEntry}
|
||||||
<Button
|
<Button
|
||||||
group
|
group
|
||||||
secondary
|
secondary
|
||||||
newStyles
|
newStyles
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
manualMode = !manualMode
|
manualMode = !manualMode
|
||||||
|
camModal.hide()
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Enter Manually
|
Enter Manually
|
||||||
</Button>
|
</Button>
|
||||||
|
{/if}
|
||||||
|
|
||||||
<Button group cta disabled={!code}>Confirm</Button>
|
<Button
|
||||||
|
group
|
||||||
|
cta
|
||||||
|
on:click={() => {
|
||||||
|
camModal.hide()
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Confirm
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
|
@ -125,17 +191,45 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
#reader :global(video) {
|
||||||
|
border-radius: 4px;
|
||||||
|
border: var(--border-light-2);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.field-display :global(.spectrum-Tags-item) {
|
||||||
|
margin: 0px;
|
||||||
|
}
|
||||||
.footer-buttons {
|
.footer-buttons {
|
||||||
display: flex;
|
display: flex;
|
||||||
grid-area: buttonGroup;
|
grid-area: buttonGroup;
|
||||||
gap: var(--spectrum-global-dimension-static-size-200);
|
gap: var(--spectrum-global-dimension-static-size-200);
|
||||||
}
|
}
|
||||||
.scanner-value {
|
.scanner-value {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.field-display {
|
||||||
padding-top: var(
|
padding-top: var(
|
||||||
--spectrum-fieldlabel-side-m-padding-top,
|
--spectrum-fieldlabel-side-m-padding-top,
|
||||||
var(--spectrum-global-dimension-size-100)
|
var(--spectrum-global-dimension-size-100)
|
||||||
);
|
);
|
||||||
display: flex;
|
|
||||||
margin-bottom: var(--spacing-m);
|
margin-bottom: var(--spacing-m);
|
||||||
}
|
}
|
||||||
|
.camera-placeholder {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: var(--border-light-2);
|
||||||
|
background-color: var(--spectrum-global-color-gray-200);
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--spectrum-global-dimension-static-size-200);
|
||||||
|
}
|
||||||
|
.container,
|
||||||
|
.camera-placeholder {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 240px;
|
||||||
|
}
|
||||||
|
.manual-input {
|
||||||
|
padding-bottom: var(--spacing-m);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -9,34 +9,18 @@
|
||||||
export let validation
|
export let validation
|
||||||
export let defaultValue = ""
|
export let defaultValue = ""
|
||||||
export let onChange
|
export let onChange
|
||||||
|
export let allowManualEntry
|
||||||
|
export let scanButtonText
|
||||||
|
|
||||||
let fieldState
|
let fieldState
|
||||||
let fieldApi
|
let fieldApi
|
||||||
|
|
||||||
let scannedCode
|
const handleUpdate = e => {
|
||||||
let loaded = false
|
const changed = fieldApi.setValue(e.detail)
|
||||||
|
|
||||||
const handleInput = () => {
|
|
||||||
const changed = fieldApi.setValue(scannedCode)
|
|
||||||
if (onChange && changed) {
|
if (onChange && changed) {
|
||||||
onChange({ value: scannedCode })
|
onChange({ value: e.detail })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$: if (!loaded && !scannedCode && fieldState?.value) {
|
|
||||||
scannedCode = fieldState.value + ""
|
|
||||||
loaded = true
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
QR Nimiq has rollup issues?
|
|
||||||
QR qrcodejs 12b bundle?
|
|
||||||
https://github.com/davidshimjs/qrcodejs
|
|
||||||
BOTH html5-qrcode has a 330k bundle
|
|
||||||
https://github.com/mebjas/html5-qrcode
|
|
||||||
BOTH zxing 360k bundle size
|
|
||||||
https://github.com/zxing-js/library
|
|
||||||
*/
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Field
|
<Field
|
||||||
|
@ -50,6 +34,12 @@
|
||||||
bind:fieldApi
|
bind:fieldApi
|
||||||
>
|
>
|
||||||
{#if fieldState}
|
{#if fieldState}
|
||||||
<CodeScanner bind:code={scannedCode} on:input={handleInput} />
|
<CodeScanner
|
||||||
|
value={fieldState.value}
|
||||||
|
on:change={handleUpdate}
|
||||||
|
disabled={fieldState.disabled}
|
||||||
|
{allowManualEntry}
|
||||||
|
{scanButtonText}
|
||||||
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
</Field>
|
</Field>
|
||||||
|
|
Loading…
Reference in New Issue