diff --git a/src/form/uploader.js b/src/form/uploader.js index 307e3e5..987a443 100644 --- a/src/form/uploader.js +++ b/src/form/uploader.js @@ -4,16 +4,21 @@ * @date 2023/04/28 16:14:10 */ -import { css, html, Component, bind, unbind } from 'wkit' -import '../base/button.js' +import { css, html, Component, bind, unbind, styleMap } from 'wkit' +import '../base/icon.js' + +function uuid() { + return Math.random().toString(16).slice(2) +} class Uploader extends Component { static props = { value: [], tips: 'str!', - allow: '*/*', + accept: '*/*', maxSize: 0, limit: 0, + grid: false, drag: false, multiple: false, disabled: false @@ -24,23 +29,177 @@ class Uploader extends Component { :host { display: flex; width: 100%; + font-size: 14px; } .container { position: relative; + width: 100%; + padding: 6px 0; -webkit-user-select: none; user-select: none; + + --wc-icon-size: 14px; + } + + .tips { + display: block; + line-height: 2; + font-size: 12px; + font-style: normal; + } + .upload-button { + position: relative; + display: inline-flex; + justify-content: center; + align-items: center; + width: 108px; + height: 32px; + gap: 4px; + border: 1px solid var(--color-grey-2); + border-radius: 3px; + cursor: pointer; + transition: background 0.1s linear, border-color 0.1s linear; + + &:hover { + border-color: var(--color-grey-1); + } + &:active { + background: var(--color-plain-1); + } + } + .hidden-input { + position: absolute; + left: 0; + top: 0; + display: block; + width: 100%; + height: 100%; + opacity: 0; + cursor: inherit; + + &::-webkit-file-upload-button { + display: none; + } + } + `, + css` + .file-list { + .item { + display: flex; + align-items: center; + padding: 0 8px; + line-height: 24px; + gap: 4px; + transition: background 0.15s linear; + + &:hover { + background: var(--color-plain-1); + + wc-icon.del { + opacity: 1; + } + } + + .name { + flex: 1; + } + wc-icon { + flex-shrink: 0; + transition: opacity 0.15s linear; + + &.del { + opacity: 0; + cursor: pointer; + color: var(--color-red-1); + } + } + } } `, // grid css` :host([grid]) { + .container { + display: flex; + flex-flow: row-reverse; + justify-content: start; + gap: 12px; + + --size: var(--wc-uploader-size, 96px); + --border-color: var(--wc-uploader-border-color, var(--color-grey-2)); + } + + .tips { + display: none; + } + + .upload-button { + width: var(--size); + height: var(--size); + border-radius: 6px; + --wc-icon-size: 18px; + } + + .file-list { + display: flex; + gap: 12px; + + .item { + justify-content: center; + width: var(--size); + height: var(--size); + gap: 6px; + border: 1px solid var(--border-color); + border-radius: 6px; + background: none no-repeat center; + background-size: contain; + + --wc-icon-size: 20px; + + .name { + display: none; + } + + wc-icon { + padding: 3px; + border: 1px solid rgba(255, 255, 255, 0.25); + border-radius: 50%; + background: rgba(64, 64, 64, 0.75); + color: #fff; + opacity: 0; + cursor: pointer; + + &:hover { + border-color: rgba(255, 255, 255, 0.5); + } + } + + &:hover { + wc-icon { + opacity: 1; + } + } + } + } } `, // drag css` :host([drag]) { + .upload-button { + flex-direction: column; + width: 100%; + height: 180px; + border-style: dashed; + --wc-icon-size: 64px; + + &.in { + border-color: var(--color-orange-1); + background: var(--color-drag-background); + } + } } `, // thumb @@ -59,15 +218,125 @@ class Uploader extends Component { ` ] + #files = [] + + #fileChange(ev) { + let files = [...ev.target.files] + let { limit } = this + if (limit > 0) { + files = files.slice(0, limit - this.#files.length) + } + + files = files.map(file => ({ file, id: uuid(), url: '', stat: 0 })) + + this.#files = this.#files.concat(files) + + this.$emit('upload', { + files, + send: args => { + for (let it of args) { + it.stat = 1 + } + this.value = this.#files.map(it => it.url) + } + }) + + ev.target.value = '' + this.$requestUpdate() + } + + #remove(ev, it, id) { + for (let i in this.#files) { + if (it === this.#files[i]) { + this.#files.splice(i, 1) + break + } + } + this.$requestUpdate() + } + + #dragover() { + this.$refs.drag.classList.toggle('in', true) + } + #dragleave() { + this.$refs.drag.classList.toggle('in', false) + } + + #drop(ev) { + this.$refs.drag.classList.toggle('in', false) + + // let files = await resolveFiles(ev.dataTransfer.items) + + // for (let it of files) { + // if (it.file.type.startsWith('image/')) { + // this.img = it.file + // this.init() + // break + // } + // } + } + + mounted() {} + render() { + let { grid, drag } = this + let limited = this.limit > 0 && this.#files.length >= this.limit + return html`
-
-
- +
+
+ + ${grid ? '' : '选择文件'} +
+ ${this.tips}
+ +
` }