feat: add DeesInputFileupload and DeesInputRichtext components

- Implemented DeesInputFileupload component with file upload functionality, including drag-and-drop support, file previews, and clear all option.
- Developed DeesInputRichtext component featuring a rich text editor with a formatting toolbar, link management, and word count display.
- Created demo for DeesInputRichtext showcasing various use cases including basic editing, placeholder text, different heights, and disabled state.
- Added styles for both components to ensure a consistent and user-friendly interface.
- Introduced types for toolbar buttons in the rich text editor for better type safety and maintainability.
This commit is contained in:
2025-09-19 15:26:21 +00:00
parent 3ba673282a
commit 987ae70e7a
34 changed files with 3167 additions and 3085 deletions

View File

@@ -0,0 +1,84 @@
import { html, type TemplateResult } from '@design.estate/dees-element';
import type { DeesInputFileupload } from './component.js';
export const renderFileupload = (component: DeesInputFileupload): TemplateResult => {
const hasFiles = component.value.length > 0;
const showClearAll = hasFiles && component.value.length > 1;
return html`
<div class="input-wrapper">
${component.label ? html`
<dees-label .label=${component.label}></dees-label>
` : ''}
<div class="hidden">
<input
type="file"
?multiple=${component.multiple}
accept="${component.accept}"
>
</div>
<div class="maincontainer ${component.state === 'dragOver' ? 'dragOver' : ''}">
${hasFiles ? html`
${showClearAll ? html`
<div class="clear-all-button">
<button @click=${component.clearAll}>Clear All</button>
</div>
` : ''}
<div class="files-container">
${component.value.map((fileArg) => {
const fileType = component.getFileType(fileArg);
const isImage = fileType === 'image';
return html`
<div class="uploadCandidate ${fileType}-file">
<div class="icon">
${isImage && component.canShowPreview(fileArg) ? html`
<img class="image-preview" src="${URL.createObjectURL(fileArg)}" alt="${fileArg.name}">
` : html`
<dees-icon .icon=${component.getFileIcon(fileArg)}></dees-icon>
`}
</div>
<div class="info">
<div class="filename" title="${fileArg.name}">${fileArg.name}</div>
<div class="filesize">${component.formatFileSize(fileArg.size)}</div>
</div>
<div class="actions">
<button
class="remove-button"
@click=${() => component.removeFile(fileArg)}
title="Remove file"
>
<dees-icon .icon=${'lucide:x'}></dees-icon>
</button>
</div>
</div>
`;
})}
</div>
` : html`
<div class="drop-hint">
<dees-icon .icon=${'lucide:cloud-upload'}></dees-icon>
<div>Drag files here or click to browse</div>
</div>
`}
<div class="uploadButton ${component.isLoading ? 'loading' : ''}" @click=${component.openFileSelector}>
<div class="button-content">
${component.isLoading ? html`
<div class="loading-spinner"></div>
<span>Opening...</span>
` : html`
<dees-icon .icon=${'lucide:upload'}></dees-icon>
${component.buttonText}
`}
</div>
</div>
</div>
${component.description ? html`
<div class="description-text">${component.description}</div>
` : ''}
${component.validationState === 'invalid' && component.validationMessage ? html`
<div class="validation-message">${component.validationMessage}</div>
` : ''}
</div>
`;
};