dees-catalog/ts_web/elements/dees-input-fileupload.ts
2024-01-18 02:08:19 +01:00

267 lines
6.8 KiB
TypeScript

import * as colors from './00colors.js';
import * as plugins from './00plugins.js';
import { DeesContextmenu } from './dees-contextmenu.js';
import {
customElement,
DeesElement,
type TemplateResult,
property,
html,
css,
unsafeCSS,
cssManager,
type CSSResult,
domtools,
} from '@design.estate/dees-element';
declare global {
interface HTMLElementTagNameMap {
'dees-input-fileupload': DeesInputFileupload;
}
}
@customElement('dees-input-fileupload')
export class DeesInputFileupload extends DeesElement {
public static demo = () =>
html`<dees-input-fileupload .label=${'Attachments'}></dees-input-fileupload>`;
// INSTANCE
public changeSubject = new domtools.plugins.smartrx.rxjs.Subject();
@property({
type: String,
})
public label: string = null;
@property({
type: String,
reflect: true,
})
public key: string;
@property({
attribute: false,
})
public value: File[] = [];
@property()
public state: 'idle' | 'dragOver' | 'dropped' | 'uploading' | 'completed' = 'idle';
@property({
type: Boolean,
})
public required: boolean = false;
@property({
type: Boolean,
})
public disabled: boolean = false;
@property({
type: String,
})
public buttonText: string = 'Upload File...';
constructor() {
super();
}
public static styles = [
cssManager.defaultStyles,
css`
:host {
position: relative;
display: grid;
margin: 10px 0px;
margin-bottom: 24px;
color: ${cssManager.bdTheme('#333', '#ccc')};
}
.hidden {
display: none;
}
.maincontainer {
position: relative;
border-radius: 3px;
padding: 8px;
background: ${cssManager.bdTheme('#fafafa', '#222222')};
color: ${cssManager.bdTheme('#333', '#ccc')};
border-top: 1px solid #ffffff10;
}
.maincontainer::after {
top: 2px;
right: 2px;
left: 2px;
bottom: 2px;
transform: scale3d(0.98, 0.9, 1);
position: absolute;
content: '';
display: block;
border: 2px dashed rgba(255, 255, 255, 0);
transition: all 0.2s;
pointer-events: none;
background: #00000000;
}
.maincontainer.dragOver::after {
transform: scale3d(1, 1, 1);
border: 2px dashed rgba(255, 255, 255, 0.3);
background: #00000080;
}
.label {
font-size: 14px;
margin-bottom: 8px;
}
.uploadButton {
position: relative;
padding: 8px;
max-width: 600px;
background: ${cssManager.bdTheme('#fafafa', '#333333')};
border-radius: 3px;
text-align: center;
font-size: 14px;
cursor: default;
}
.uploadButton:hover {
color: #fff;
background: ${unsafeCSS(colors.dark.blue)};
}
.uploadCandidate {
display: grid;
grid-template-columns: 48px auto;
background: #333;
padding: 8px 8px 8px 0px;
margin-bottom: 8px;
text-align: left;
border-radius: 3px;
color: ${cssManager.bdTheme('#666', '#ccc')};
font-family: 'Roboto', 'Inter', sans-serif;
cursor: default;
transition: all 0.2s;
border-top: 1px solid #ffffff10;
}
.uploadCandidate:last-child {
margin-bottom: 8px;
}
.uploadCandidate .icon {
display: flex;
align-items: center;
justify-content: center;
font-size: 16px;
}
.uploadCandidate:hover {
background: #393939;
}
.uploadCandidate .description {
font-size: 14px;
border-left: 1px solid #ffffff10;
padding-left: 8px;
}
`,
];
public render(): TemplateResult {
return html`
<div class="hidden">
<input type="file"></div>
</div>
${this.label ? html`<div class="label">${this.label}</div>` : null}
<div class="maincontainer ${this.state === 'dragOver' ? 'dragOver' : ''}">
${this.value.map(
(fileArg) => html`
<div class="uploadCandidate" @contextmenu=${eventArg => {
DeesContextmenu.openContextMenuWithOptions(eventArg, [{
iconName: 'trash',
name: 'Remove',
action: async () => {
this.value.splice(this.value.indexOf(fileArg), 1);
this.requestUpdate();
}
}]);
}}>
<div class="icon">
<dees-icon .iconFA=${'paperclip'}></dees-icon>
</div>
<div class="description">
<span style="font-weight: 600">${fileArg.name}</span><br />
<span style="font-weight: 400">${fileArg.size}</span>
</div>
</div> `
)}
<div class="uploadButton" @click=${
this.openFileSelector
}>
${this.buttonText}
</div>
</div>
`;
}
public async openFileSelector() {
const inputFile: HTMLInputElement = this.shadowRoot.querySelector('input[type="file"]');
inputFile.click();
this.state = 'idle';
this.buttonText = 'Upload more files...';
}
public async updateValue(eventArg: Event) {
const target: any = eventArg.target;
this.value = target.value;
this.changeSubject.next(this);
}
public firstUpdated() {
const inputFile: HTMLInputElement = this.shadowRoot.querySelector('input[type="file"]');
inputFile.addEventListener('change', (event: Event) => {
const target = event.target as HTMLInputElement;
for (const file of Array.from(target.files)) {
this.value.push(file);
}
this.requestUpdate();
console.log(`Got ${this.value.length} files!`);
// Reset the input value to allow selecting the same file again if needed
target.value = '';
});
// lets handle drag and drop
const dropArea = this.shadowRoot.querySelector('.maincontainer');
const handlerFunction = (eventArg: DragEvent) => {
eventArg.preventDefault();
switch (eventArg.type) {
case 'dragover':
this.state = 'dragOver';
this.buttonText = 'release to upload file...';
break;
case 'dragleave':
this.state = 'idle';
this.buttonText = 'Upload File...';
break;
case 'drop':
this.state = 'idle';
this.buttonText = 'Upload more files...';
}
console.log(eventArg);
for (const file of Array.from(eventArg.dataTransfer.files)) {
this.value.push(file);
this.requestUpdate();
}
console.log(`Got ${this.value.length} files!`);
};
dropArea.addEventListener('dragenter', handlerFunction, false);
dropArea.addEventListener('dragleave', handlerFunction, false);
dropArea.addEventListener('dragover', handlerFunction, false);
dropArea.addEventListener('drop', handlerFunction, false);
}
}