refactor(file-upload): SSR-first state, click-to-replace, cleaner preview
- Server renders initial file-upload state (src, alt, label text, has-current class) so there is no flash of broken-image icon while JS boots. JS no longer duplicates that work — it only reacts to user input (select, clear, drag-and-drop) and uses data-current-src solely to restore the server file after a blob preview. - Clicking the current preview image now opens the file dialog, mirrors the Replace button, with hover affordance (cursor + primary border). - Drop the transparency checker pattern in the preview — the admin UI does not need Figma-style transparency semantics. Preview is now a flat theme-aware surface (--app-preview-bg) that keeps white or black logos visible against a neutral gray. - Move tenant favicon out of the aside into the Master-data tab as its own details block next to the Tenant-logos section. Uses the same barrier-form pattern (HTML5 form attribute + app-file-upload.phtml) for a consistent instant-upload UX. - Tenant-logo slot label is a native <label> element — picks up the global form-label typography and spacing, no custom muted-color class needed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -68,27 +68,16 @@ export function initFileUpload(root) {
|
||||
const filenameEl = root.querySelector('.app-file-upload-filename');
|
||||
const filesizeEl = root.querySelector('.app-file-upload-filesize');
|
||||
const clearButton = root.querySelector('.app-file-upload-clear');
|
||||
const currentImage = root.querySelector('.app-file-upload-current-image');
|
||||
const currentLabel = root.querySelector('.app-file-upload-current-label');
|
||||
const replaceButton = root.querySelector('.app-file-upload-replace-button');
|
||||
const currentImage = root.querySelector('.app-file-upload-current-image');
|
||||
|
||||
const cleanupFns = [];
|
||||
let dragCounter = 0;
|
||||
|
||||
// ── Initialize current file state from data attributes ──
|
||||
// Server rendered the initial state (img src + label + has-current class).
|
||||
// `data-current-src` is kept so clearFile() can restore the server file
|
||||
// after a user previewed a different selection.
|
||||
const currentSrc = (root.dataset.currentSrc || '').trim();
|
||||
const currentLabelText = (root.dataset.currentLabel || '').trim();
|
||||
|
||||
if (currentSrc) {
|
||||
root.classList.add('has-current');
|
||||
if (currentImage) {
|
||||
currentImage.src = currentSrc;
|
||||
currentImage.alt = currentLabelText;
|
||||
}
|
||||
if (currentLabel) {
|
||||
currentLabel.textContent = currentLabelText;
|
||||
}
|
||||
}
|
||||
|
||||
// ── Show pending file (new selection) ──
|
||||
const showFile = (file) => {
|
||||
@@ -175,15 +164,19 @@ export function initFileUpload(root) {
|
||||
cleanupFns.push(() => clearButton.removeEventListener('click', onClear));
|
||||
}
|
||||
|
||||
// ── Replace button → open file dialog ──
|
||||
// ── Replace button + click on current preview → open file dialog ──
|
||||
const openPicker = (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
input.click();
|
||||
};
|
||||
if (replaceButton) {
|
||||
const onReplace = (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
input.click();
|
||||
};
|
||||
replaceButton.addEventListener('click', onReplace);
|
||||
cleanupFns.push(() => replaceButton.removeEventListener('click', onReplace));
|
||||
replaceButton.addEventListener('click', openPicker);
|
||||
cleanupFns.push(() => replaceButton.removeEventListener('click', openPicker));
|
||||
}
|
||||
if (currentImage) {
|
||||
currentImage.addEventListener('click', openPicker);
|
||||
cleanupFns.push(() => currentImage.removeEventListener('click', openPicker));
|
||||
}
|
||||
|
||||
// ── Drag and drop ──
|
||||
|
||||
Reference in New Issue
Block a user