feat(web): componentize frontend and replace native delete confirm
- Split monolithic app.js/style.css into Alpine.js components (icons, store, fileSection, confirm)
- Add central Alpine.store('files') as single source of truth for file lists and actions
- Replace duplicated Inbox/Outbox markup with reusable fileSection component (props: dir, canShare)
- Add shared window.Icons module to remove duplicated inline SVGs
- Replace browser-native confirm() for file deletion with a styled, accessible modal dialog
- Split CSS into design tokens, base, and component layers; modernize visuals
- Update web.go embed and server static routes for the new component/style dirs
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
/**
|
||||
* Confirmation dialog (a promise-based modal store + UI component).
|
||||
*
|
||||
* Replaces the browser-native `confirm()` with a styled, accessible modal.
|
||||
* Components request confirmation via `Alpine.store('confirm').show(opts)`,
|
||||
* which resolves `true`/`false` when the user acts — mirroring a UI-library
|
||||
* dialog API.
|
||||
*/
|
||||
(function () {
|
||||
function register() {
|
||||
Alpine.store('confirm', {
|
||||
open: false,
|
||||
title: '',
|
||||
message: '',
|
||||
danger: false,
|
||||
_resolve: null,
|
||||
|
||||
show(opts) {
|
||||
this.title = opts.title || 'Confirm';
|
||||
this.message = opts.message || '';
|
||||
this.danger = opts.danger || false;
|
||||
this.open = true;
|
||||
return new Promise((resolve) => {
|
||||
this._resolve = resolve;
|
||||
});
|
||||
},
|
||||
|
||||
accept() {
|
||||
this._close(true);
|
||||
},
|
||||
|
||||
cancel() {
|
||||
this._close(false);
|
||||
},
|
||||
|
||||
_close(result) {
|
||||
this.open = false;
|
||||
const resolve = this._resolve;
|
||||
this._resolve = null;
|
||||
if (resolve) resolve(result);
|
||||
},
|
||||
});
|
||||
|
||||
Alpine.data('confirmDialog', () => ({
|
||||
get store() {
|
||||
return Alpine.store('confirm');
|
||||
},
|
||||
get open() {
|
||||
return this.store.open;
|
||||
},
|
||||
get title() {
|
||||
return this.store.title;
|
||||
},
|
||||
get message() {
|
||||
return this.store.message;
|
||||
},
|
||||
get danger() {
|
||||
return this.store.danger;
|
||||
},
|
||||
|
||||
accept() {
|
||||
this.store.accept();
|
||||
},
|
||||
cancel() {
|
||||
this.store.cancel();
|
||||
},
|
||||
onEscape() {
|
||||
if (this.open) this.cancel();
|
||||
},
|
||||
|
||||
init() {
|
||||
this.$watch('open', (visible) => {
|
||||
if (visible) {
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.accept) this.$refs.accept.focus();
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
}));
|
||||
}
|
||||
|
||||
if (window.Alpine) register();
|
||||
else document.addEventListener('alpine:init', register);
|
||||
})();
|
||||
Reference in New Issue
Block a user