use bbui component for dropzone

This commit is contained in:
Martin McKeaveney 2020-09-24 15:50:51 +01:00
parent 31c6450f5e
commit 6e9c238054
5 changed files with 22 additions and 556 deletions

View File

@ -63,7 +63,7 @@
} }
}, },
"dependencies": { "dependencies": {
"@budibase/bbui": "^1.33.0", "@budibase/bbui": "^1.34.6",
"@budibase/client": "^0.1.21", "@budibase/client": "^0.1.21",
"@budibase/colorpicker": "^1.0.1", "@budibase/colorpicker": "^1.0.1",
"@fortawesome/fontawesome-free": "^5.14.0", "@fortawesome/fontawesome-free": "^5.14.0",

View File

@ -1,40 +1,20 @@
<script> <script>
import { notifier } from "builderStore/store/notifications" import { notifier } from "builderStore/store/notifications"
import { Heading, Body, Button } from "@budibase/bbui" import { Heading, Body, Button, Dropzone } from "@budibase/bbui"
import { FILE_TYPES } from "constants/backend"
import api from "builderStore/api" import api from "builderStore/api"
const BYTES_IN_KB = 1000
const BYTES_IN_MB = 1000000
export let files = [] export let files = []
export let fileSizeLimit = BYTES_IN_MB * 20
let selectedImageIdx = 0 function handleFileTooLarge() {
let fileDragged = false
$: selectedImage = files[selectedImageIdx]
function determineFileIcon(extension) {
const ext = extension.toLowerCase()
if (FILE_TYPES.IMAGE.includes(ext)) return "ri-image-2-line"
if (FILE_TYPES.CODE.includes(ext)) return "ri-terminal-box-line"
return "ri-file-line"
}
async function processFiles(fileList) {
const fileArray = Array.from(fileList)
if (fileArray.some(file => file.size >= fileSizeLimit)) {
notifier.danger( notifier.danger(
`Files cannot exceed ${fileSizeLimit / `Files cannot exceed ${fileSizeLimit /
BYTES_IN_MB}MB. Please try again with smaller files.` BYTES_IN_MB}MB. Please try again with smaller files.`
) )
return
} }
async function processFiles(fileList) {
const fileArray = Array.from(fileList)
const filesToProcess = fileArray.map(({ name, path, size, type }) => ({ const filesToProcess = fileArray.map(({ name, path, size, type }) => ({
name, name,
path, path,
@ -45,256 +25,8 @@
const response = await api.post(`/api/attachments/process`, { const response = await api.post(`/api/attachments/process`, {
files: filesToProcess, files: filesToProcess,
}) })
const processedFiles = await response.json() return await response.json()
files = [...processedFiles, ...files]
selectedImageIdx = 0
}
async function removeFile() {
files.splice(selectedImageIdx, 1)
files = files
selectedImageIdx = 0
}
function navigateLeft() {
selectedImageIdx -= 1
}
function navigateRight() {
selectedImageIdx += 1
}
function handleFile(evt) {
processFiles(evt.target.files)
}
function handleDragOver(evt) {
evt.preventDefault()
fileDragged = true
}
function handleDragLeave(evt) {
evt.preventDefault()
fileDragged = false
}
function handleDrop(evt) {
evt.preventDefault()
processFiles(evt.dataTransfer.files)
fileDragged = false
} }
</script> </script>
<div <Dropzone bind:files {processFiles} {handleFileTooLarge} />
class="dropzone"
on:dragover={handleDragOver}
on:dragleave={handleDragLeave}
on:dragenter={handleDragOver}
on:drop={handleDrop}
class:fileDragged>
<ul>
{#if selectedImage}
<li>
<header>
<div>
<i
class={`file-icon ${determineFileIcon(selectedImage.extension)}`} />
<span class="filename">{selectedImage.name}</span>
</div>
<p>
{#if selectedImage.size <= BYTES_IN_MB}
{selectedImage.size / BYTES_IN_KB}KB
{:else}{selectedImage.size / BYTES_IN_MB}MB{/if}
</p>
</header>
<div class="delete-button" on:click={removeFile}>
<i class="ri-close-line" />
</div>
{#if selectedImageIdx !== 0}
<div class="nav left" on:click={navigateLeft}>
<i class="ri-arrow-left-line" />
</div>
{/if}
<img src={selectedImage.url} />
{#if selectedImageIdx !== files.length - 1}
<div class="nav right" on:click={navigateRight}>
<i class="ri-arrow-right-line" />
</div>
{/if}
</li>
{/if}
</ul>
<i class="ri-folder-upload-line" />
<input id="file-upload" type="file" multiple on:change={handleFile} />
<label for="file-upload">Upload</label>
</div>
<style>
.dropzone {
padding: var(--spacing-l);
border: 2px dashed var(--grey-7);
text-align: center;
display: flex;
align-items: center;
flex-direction: column;
border-radius: 10px;
transition: all 0.3s;
}
.fileDragged {
border: 2px dashed var(--grey-7);
transform: scale(1.03);
background: var(--blue-light);
}
input[type="file"] {
display: none;
}
label {
font-family: var(--font-sans);
cursor: pointer;
font-weight: 600;
box-sizing: border-box;
overflow: hidden;
border-radius: var(--border-radius-s);
color: var(--white);
padding: var(--spacing-s) var(--spacing-l);
transition: all 0.2s ease 0s;
display: inline-flex;
text-rendering: optimizeLegibility;
min-width: auto;
outline: none;
font-feature-settings: "case" 1, "rlig" 1, "calt" 0;
-webkit-box-align: center;
user-select: none;
flex-shrink: 0;
align-items: center;
justify-content: center;
margin-top: 10px;
width: 100%;
border: solid 1.5px var(--ink);
background-color: var(--ink);
}
div.nav {
position: absolute;
background: black;
color: var(--white);
display: flex;
align-items: center;
bottom: var(--spacing-s);
border-radius: 10px;
transition: 0.2s transform;
}
.nav:hover {
cursor: pointer;
transform: scale(1.1);
}
.left {
left: var(--spacing-s);
}
.right {
right: var(--spacing-s);
}
li {
position: relative;
height: 300px;
background: var(--grey-7);
display: flex;
justify-content: center;
border-radius: 10px;
}
img {
border-radius: 10px;
width: 100%;
box-shadow: 0 var(--spacing-s) 12px rgba(0, 0, 0, 0.15);
object-fit: contain;
}
i {
font-size: 3em;
}
.file-icon {
color: var(--white);
font-size: 2em;
margin-right: var(--spacing-s);
}
ul {
padding: 0;
display: grid;
grid-gap: var(--spacing-s);
list-style-type: none;
width: 100%;
}
header {
display: flex;
align-items: center;
justify-content: space-between;
position: absolute;
background: linear-gradient(
180deg,
rgba(12, 12, 12, 1),
rgba(60, 60, 60, 0)
);
width: 100%;
border-top-left-radius: 10px;
border-top-right-radius: 10px;
height: 60px;
}
header > div {
color: var(--white);
display: flex;
align-items: center;
font-size: 15px;
margin-left: var(--spacing-m);
width: 60%;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.filename {
overflow: hidden;
text-overflow: ellipsis;
}
header > p {
color: var(--grey-5);
margin-right: var(--spacing-m);
}
.delete-button {
position: absolute;
top: var(--spacing-s);
right: var(--spacing-s);
padding: var(--spacing-s);
border-radius: 10px;
opacity: 0;
transition: all 0.3s;
color: var(--white);
}
.delete-button i {
font-size: 2em;
}
.delete-button:hover {
opacity: 1;
cursor: pointer;
background: linear-gradient(
to top right,
rgba(60, 60, 60, 0),
rgba(255, 0, 0, 0.2)
);
}
</style>

View File

@ -688,10 +688,10 @@
lodash "^4.17.13" lodash "^4.17.13"
to-fast-properties "^2.0.0" to-fast-properties "^2.0.0"
"@budibase/bbui@^1.33.0": "@budibase/bbui@^1.34.6":
version "1.33.0" version "1.34.6"
resolved "https://registry.yarnpkg.com/@budibase/bbui/-/bbui-1.33.0.tgz#216b24dd815f45880e9795e66b04848329b0390f" resolved "https://registry.yarnpkg.com/@budibase/bbui/-/bbui-1.34.6.tgz#d94a9c0af52244ded20dfcd7c93dbd6b184460dc"
integrity sha512-Rrt5eLbea014TIfAbT40kP0D0AWNUi8Q0kDr3UZO6Aq4UXgjc0f53ZuJ7Kb66YRDWrqiucjf1FtvOUs3/YaD6g== integrity sha512-FLYKst1WDjQWpZPOm5w31M5mpdc4FZaHNT5UPyE+LTOtVJquUPycyS1Y/lhGjt/QjwP/Gn8wSvwwsD0gCNJvvg==
dependencies: dependencies:
sirv-cli "^0.4.6" sirv-cli "^0.4.6"
svelte-flatpickr "^2.4.0" svelte-flatpickr "^2.4.0"

View File

@ -36,7 +36,7 @@
"gitHead": "284cceb9b703c38566c6e6363c022f79a08d5691", "gitHead": "284cceb9b703c38566c6e6363c022f79a08d5691",
"dependencies": { "dependencies": {
"@beyonk/svelte-googlemaps": "^2.2.0", "@beyonk/svelte-googlemaps": "^2.2.0",
"@budibase/bbui": "^1.32.0", "@budibase/bbui": "^1.34.6",
"@fortawesome/fontawesome-free": "^5.14.0", "@fortawesome/fontawesome-free": "^5.14.0",
"britecharts": "^2.16.1", "britecharts": "^2.16.1",
"d3-selection": "^1.4.2", "d3-selection": "^1.4.2",

View File

@ -1,26 +1,16 @@
<script> <script>
import { Heading, Body, Button } from "@budibase/bbui" import { Heading, Body, Button, Dropzone } from "@budibase/bbui"
import { FILE_TYPES } from "./fileTypes" import { FILE_TYPES } from "./fileTypes"
import api from "../api"
const BYTES_IN_KB = 1000 const BYTES_IN_KB = 1000
const BYTES_IN_MB = 1000000
export let files = [] export let files = []
export let fileSizeLimit = BYTES_IN_MB * 20
let selectedImageIdx = 0 function handleFileTooLarge(fileSizeLimit) {
let fileDragged = false alert(
`Files cannot exceed ${fileSizeLimit /
$: selectedImage = files ? files[selectedImageIdx] : null BYTES_IN_MB}MB. Please try again with smaller files.`
)
function determineFileIcon(extension) {
const ext = extension.toLowerCase()
if (FILE_TYPES.IMAGE.includes(ext)) return "fas fa-file-image"
if (FILE_TYPES.CODE.includes(ext)) return "fas fa-file-code"
return "fas fa-file"
} }
async function processFiles(fileList) { async function processFiles(fileList) {
@ -29,14 +19,6 @@
data.append("file", fileList[i]) data.append("file", fileList[i])
} }
if (Array.from(fileList).some(file => file.size >= fileSizeLimit)) {
alert(
`Files cannot exceed ${fileSizeLimit /
BYTES_IN_MB}MB. Please try again with smaller files.`
)
return
}
const response = await fetch("/api/attachments/upload", { const response = await fetch("/api/attachments/upload", {
method: "POST", method: "POST",
body: data, body: data,
@ -46,256 +28,8 @@
}) })
const processedFiles = await response.json() const processedFiles = await response.json()
files = [...processedFiles, ...files] return processedFiles
selectedImageIdx = 0
}
async function removeFile() {
files.splice(selectedImageIdx, 1)
files = files
selectedImageIdx = 0
}
function navigateLeft() {
selectedImageIdx -= 1
}
function navigateRight() {
selectedImageIdx += 1
}
function handleFile(evt) {
processFiles(evt.target.files)
}
function handleDragOver(evt) {
evt.preventDefault()
fileDragged = true
}
function handleDragLeave(evt) {
evt.preventDefault()
fileDragged = false
}
function handleDrop(evt) {
evt.preventDefault()
processFiles(evt.dataTransfer.files)
fileDragged = false
} }
</script> </script>
<div <Dropzone bind:files {processFiles} {handleFileTooLarge} />
class="dropzone"
on:dragover={handleDragOver}
on:dragleave={handleDragLeave}
on:dragenter={handleDragOver}
on:drop={handleDrop}
class:fileDragged>
<ul>
{#if selectedImage}
<li>
<header>
<div>
<i class={determineFileIcon(selectedImage.extension)} />
<span class="filename">{selectedImage.name}</span>
</div>
<p>
{#if selectedImage.size <= BYTES_IN_MB}
{selectedImage.size / BYTES_IN_KB}KB
{:else}{selectedImage.size / BYTES_IN_MB}MB{/if}
</p>
</header>
<div class="delete-button" on:click={removeFile}>
<i class="fas fa-times" />
</div>
{#if selectedImageIdx !== 0}
<div class="nav left" on:click={navigateLeft}>
<i class="fas fa-arrow-left" />
</div>
{/if}
<img src={selectedImage.url} />
{#if selectedImageIdx !== files.length - 1}
<div class="nav right" on:click={navigateRight}>
<i class="fas fa-arrow-right" />
</div>
{/if}
</li>
{/if}
</ul>
<i class="fas fa-upload" />
<input id="file-upload" type="file" multiple on:change={handleFile} />
<label for="file-upload">Upload</label>
</div>
<style>
.dropzone {
padding: var(--spacing-l);
border: 2px dashed var(--grey-7);
text-align: center;
display: flex;
align-items: center;
flex-direction: column;
border-radius: 10px;
transition: all 0.3s;
}
.fileDragged {
border: 2px dashed var(--grey-7);
transform: scale(1.03);
background: var(--blue-light);
}
input[type="file"] {
display: none;
}
label {
font-family: var(--font-sans);
cursor: pointer;
font-weight: 600;
box-sizing: border-box;
overflow: hidden;
border-radius: var(--border-radius-s);
color: var(--white);
padding: var(--spacing-s) var(--spacing-l);
transition: all 0.2s ease 0s;
display: inline-flex;
text-rendering: optimizeLegibility;
min-width: auto;
outline: none;
font-feature-settings: "case" 1, "rlig" 1, "calt" 0;
-webkit-box-align: center;
user-select: none;
flex-shrink: 0;
align-items: center;
justify-content: center;
margin-top: 10px;
width: 100%;
border: solid 1.5px var(--ink);
background-color: var(--ink);
}
div.nav {
padding: var(--spacing-xs);
position: absolute;
background: black;
color: var(--white);
display: flex;
align-items: center;
bottom: var(--spacing-s);
border-radius: 5px;
transition: 0.2s transform;
}
.nav:hover {
cursor: pointer;
transform: scale(1.1);
}
.left {
left: var(--spacing-s);
}
.right {
right: var(--spacing-s);
}
li {
position: relative;
height: 300px;
background: var(--grey-7);
display: flex;
justify-content: center;
border-radius: 10px;
}
img {
border-radius: 10px;
width: 100%;
box-shadow: 0 var(--spacing-s) 12px rgba(0, 0, 0, 0.15);
object-fit: contain;
}
i {
font-size: 3em;
}
.file-icon {
color: var(--white);
font-size: 2em;
margin-right: var(--spacing-s);
}
ul {
padding: 0;
display: grid;
grid-gap: var(--spacing-s);
list-style-type: none;
width: 100%;
}
header {
display: flex;
align-items: center;
justify-content: space-between;
position: absolute;
background: linear-gradient(
180deg,
rgba(12, 12, 12, 1),
rgba(60, 60, 60, 0)
);
width: 100%;
border-top-left-radius: 10px;
border-top-right-radius: 10px;
height: 60px;
}
header > div {
color: var(--white);
display: flex;
align-items: center;
font-size: 15px;
margin-left: var(--spacing-m);
width: 60%;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.filename {
overflow: hidden;
margin-left: 5px;
text-overflow: ellipsis;
}
header > p {
color: var(--grey-5);
margin-right: var(--spacing-m);
}
.delete-button {
position: absolute;
top: var(--spacing-s);
right: var(--spacing-s);
padding: var(--spacing-s);
border-radius: 10px;
opacity: 0;
transition: all 0.3s;
color: var(--white);
}
.delete-button i {
font-size: 2em;
}
.delete-button:hover {
opacity: 1;
cursor: pointer;
background: linear-gradient(
to top right,
rgba(60, 60, 60, 0),
rgba(255, 0, 0, 0.2)
);
}
</style>