Skip to content

Commit

Permalink
Merge branch 'error-notices'
Browse files Browse the repository at this point in the history
* Branch commit log:
  ui/b/icon.js: remove 'important' from CSS display
  ui/b/menubar.js: fix Quit->Save option for web UIs
  ui/b/crawlerdialog.js: use Dom.show_modal()
  ui/b/aboutdialog.js: use Dom.show_modal()
  ui/b/contextmenu.js: use Dom.show_modal()
  ui/dom.js: add show_modal() for dialogs
	* Add show_modal() for dialogs.
	* Properly close dialog on backdrop clicks.
	* Do not focus entire dialog on backdrop clicks.
  ui/b/app.js: turn one-button error dialog into notice
  ui/b/menubar.js: turn one-button error dialog into notice
  ui/b/devicepanel.js: consolidate CSS

Signed-off-by: Tim Janik <[email protected]>
  • Loading branch information
tim-janik committed Jun 11, 2024
2 parents 4ebbc09 + 6c5e7e5 commit da20031
Show file tree
Hide file tree
Showing 8 changed files with 79 additions and 44 deletions.
14 changes: 4 additions & 10 deletions ui/b/aboutdialog.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,19 @@

import { LitComponent, html, render, noChange, JsExtract, docs, ref } from '../little.js';
import * as Util from "../util.js";
import * as Dom from "../dom.js";

// == HTML ==
const HTML = (t, d) => html`
<dialog class="floating-dialog [&:not([open])]:hidden" ${ref (h => t.dialog = h)} @cancel=${t.close_dialog} @pointerdown=${t.backdrop_click}>
<dialog class="floating-dialog [&:not([open])]:hidden" ${ref (h => t.dialog = h)} @close=${t.close_dialog}>
<div class="dialog-header">
About ANKLANG
</div>
<c-grid class="max-w-full">
${INFOS_HTML (t, d)}
</c-grid>
<div class="dialog-footer">
<button class="button-xl" autofocus @click=${t.close_dialog} > Close </button>
<button class="button-xl" autofocus @click=${e => t.dialog.close()} > Close </button>
</div>
</dialog>`;
const INFOS_HTML = (t, d) =>
Expand Down Expand Up @@ -61,7 +62,7 @@ export class BAboutDialog extends LitComponent {
}
if (this.shown && !this.dialog.open && this.info_pairs.length > 0) {
document.startViewTransition (async () => {
this.dialog.showModal();
Dom.show_modal (this.dialog);
await Promise.all ([this.updateComplete, info_promise]);
});
}
Expand All @@ -81,13 +82,6 @@ export class BAboutDialog extends LitComponent {
this.dialog.close();
super.disconnectedCallback();
}
backdrop_click (event)
{
if (event.target === event.currentTarget && // target must be DIALOG or backdrop
(event.offsetX < 0 || event.offsetX > event.target.offsetWidth || // click must be outside
event.offsetY < 0 || event.offsetY > event.target.offsetHeight)) // which is backdrop
this.close_dialog (event);
}
}
customElements.define ('b-aboutdialog', BAboutDialog);

Expand Down
14 changes: 7 additions & 7 deletions ui/b/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,13 +99,13 @@ export class AppClass {
}
async load_project_checked (project_or_path) {
const err = await this.load_project (project_or_path);
if (err !== Ase.Error.NONE)
App.async_button_dialog ("Project Loading Error",
"Failed to open project.\n" +
displayfs (project_or_path) + ":\n" +
await Ase.server.error_blurb (err), [
{ label: 'Dismiss', autofocus: true },
], 'ERROR');
if (err !== Ase.Error.NONE) {
let errblurb = Ase.server.error_blurb (err);
let msg = '# File IO Error\n \n \n';
msg += 'Failed to load project:\n\n';
msg += '`' + displayfs (project_or_path) + ": " + await errblurb + '`';
App.show_notice (msg);
}
return err;
}
async load_project (project_or_path) {
Expand Down
3 changes: 2 additions & 1 deletion ui/b/contextmenu.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ import { LitComponent, html, render, noChange, JsExtract, docs, ref } from '../l
import * as Util from "../util.js";
import * as Kbd from '../kbd.js';
import { text_content, get_uri, valid_uri, has_uri } from '../dom.js';
import * as Dom from "../dom.js";

// == STYLE ==
JsExtract.css`
Expand Down Expand Up @@ -362,7 +363,7 @@ class BContextMenu extends LitComponent {
return (async () => {
await this.updateComplete; // needed to access this.dialog
this.reposition = true;
this.dialog.showModal();
Dom.show_modal (this.dialog);
this.blur();
App.zmove(); // force changes to be picked up
// check items (and this used to handle auto-focus)
Expand Down
3 changes: 2 additions & 1 deletion ui/b/crawlerdialog.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { Signal, State, Computed, Watcher, tracking_wrapper } from "../signal.js
import { get_uri } from '../dom.js';
import * as Util from "../util.js";
import * as Kbd from "../kbd.js";
import * as Dom from "../dom.js";

// == STYLE ==
JsExtract.css`
Expand Down Expand Up @@ -195,7 +196,7 @@ class BCrawlerDialog extends LitComponent {
if (!this.shown && this.dialog.open)
this.dialog.close();
if (this.shown && !this.dialog.open)
this.dialog.showModal();
Dom.show_modal (this.dialog);
}
connectedCallback()
{
Expand Down
5 changes: 2 additions & 3 deletions ui/b/devicepanel.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,9 @@ b-devicepanel {
}
position: relative;
&::after {
position: absolute; left:0; top:0; right:0; bottom:0;
content: ' '; pointer-events: none;
@apply absolute pointer-events-none inset-0;
content: ' '; z-index: 9; //* raise above scrolled siblings */
box-shadow: inset -10px 0 7px -7px #000, inset 10px 0 7px -7px #000;
z-index: 9; //* raise above scrolled siblings */
}
}`;

Expand Down
2 changes: 1 addition & 1 deletion ui/b/icon.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import * as Dom from "../dom.js";
// == STYLE ==
JsExtract.css`
b-icon { // not using shadow-root here
display: inline-flex !important;
display: inline-flex;
place-content: center center;
flex-wrap: wrap; /* needed for align-content:center */
&[hflip] { transform: scaleX(-1); }
Expand Down
39 changes: 18 additions & 21 deletions ui/b/menubar.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ const HTML = (t, d) => html`
<b-icon ic="bc-folder" ></b-icon>
<b-icon ic="bc-menumore" ></b-icon>
</div>
<b-contextmenu ${ref (h => t.filemenu = h)} id="g-filemenu" .activate=${activate} .isactive=${isactive} startfocus1 >
<b-contextmenu ${ref (h => t.filemenu = h)} id="g-filemenu" .activate=${activate} .isactive=${isactive} >
<button ic="fa-file-o" kbd="Ctrl+N" uri="loadnew" > New Project </button>
<button ic="fa-file-audio-o" kbd="Ctrl+O" uri="load" > Open Project… </button>
<button ic="mi-save_alt" kbd="Ctrl+S" uri="save" > Save Project </button>
Expand All @@ -57,7 +57,7 @@ const HTML = (t, d) => html`
<b-icon ic="mi-draw" ></b-icon>
<b-icon ic="bc-menumore" ></b-icon>
</div>
<b-contextmenu ${ref (h => t.editmenu = h)} id="g-editmenu" .activate=${activate} .isactive=${isactive} startfocus >
<b-contextmenu ${ref (h => t.editmenu = h)} id="g-editmenu" .activate=${activate} .isactive=${isactive} >
<button ic="mi-undo" .disabled=${!true} kbd="Ctrl+Z" uri="undo"> Undo </button>
<button ic="mi-redo" .disabled=${!true} kbd="Shift+Ctrl+Z" uri="redo"> Redo </button>
</b-contextmenu>
Expand All @@ -69,7 +69,7 @@ const HTML = (t, d) => html`
<b-icon ic="fa-eye" ></b-icon>
<b-icon ic="bc-menumore" ></b-icon>
</div>
<b-contextmenu ${ref (h => t.viewmenu = h)} id="g-viewmenu" .activate=${activate} .isactive=${isactive} startfocus >
<b-contextmenu ${ref (h => t.viewmenu = h)} id="g-viewmenu" .activate=${activate} .isactive=${isactive} >
<button ic="mi-fullscreen" .disabled=${!document.fullscreenEnabled}
kbd="F11" uri="fullscreen"> Toggle Fullscreen </button>
${ELECTRON_MENUITEMS (t)}
Expand All @@ -92,7 +92,7 @@ const HTML = (t, d) => html`
<b-icon ic="fa-life-ring" ></b-icon>
<b-icon ic="bc-menumore" ></b-icon>
</div>
<b-contextmenu ${ref (h => t.helpmenu = h)} id="g-helpmenu" .activate=${activate} .isactive=${isactive} startfocus >
<b-contextmenu ${ref (h => t.helpmenu = h)} id="g-helpmenu" .activate=${activate} .isactive=${isactive} >
<button ic="mi-chrome_reader_mode" uri="user-manual"> Anklang Manual… </button>
<button ic="mi-chrome_reader_mode" uri="dev-manual"> Development Reference… </button>
<b-menuseparator></b-menuseparator>
Expand Down Expand Up @@ -185,7 +185,7 @@ async function activate (uri, event)
(Electron ?
[ 'Discard Changes', { label: 'Cancel', autofocus: true }, 'Save' ] :
[ 'Discard Changes', { label: 'Cancel', autofocus: true },
{ label: 'Save', disabled: true } ]),
{ label: 'Save', disabled: false } ]),
'QUESTION');
v = await v;
if (v == 0)
Expand Down Expand Up @@ -301,21 +301,18 @@ async function save_project (asnew = false) {
if (replace != 1)
return false;
}
const err = await App.save_project (filename);
if (err === Ase.Error.NONE)
{
filename = await Data.project.saved_filename();
let msg = '### Project Saved\n';
msg += ' \n \nProject saved to: ``' + displayfs (filename) + '``\n';
App.show_notice (msg);
return true;
}
await App.async_button_dialog ("File IO Error",
"Failed to save project.\n" +
displayfs (filename) + ": " +
await Ase.server.error_blurb (err), [
{ label: 'Dismiss', autofocus: true }
], 'ERROR');
return false;
let msg, err = await App.save_project (filename);
if (err === Ase.Error.NONE) {
filename = await Data.project.saved_filename(); // get canonicalized form
msg = '### Project Saved\n \n \n';
msg += 'Project successfully saved to:\n\n`' + displayfs (filename) + '`\n';
} else {
let errblurb = Ase.server.error_blurb (err);
msg = '# File IO Error\n \n \n';
msg += 'Failed to save project:\n\n';
msg += '`' + displayfs (filename) + ": " + await errblurb + '`';
}
App.show_notice (msg);
return err === Ase.Error.NONE;
}
let save_project_last_dir = "~MUSIC";
43 changes: 43 additions & 0 deletions ui/dom.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,46 @@ export function text_content (element, with_children = true)
s += element.childNodes[i].textContent;
return s;
}

/// Show a `dialog` via showModal() and close it on backdrop clicks.
export function show_modal (dialog)
{
if (dialog.open) return;
// close dialog on backdrop clicks, but:
// - avoid matching text-select drags that end up on backdrop area
// - avoid matching Enter-click event coordinates from input-submit with clientX*clientY==0
// - avoid matching an outside popup click, after a previous pointerdown+Escape combination
// - avoid re-popup by clicking on outside menu button and closing early on pointerdown
let pointer_outside = false; // must reset on every dialog.showModal()
const pointerdown = event => {
pointer_outside = (event.buttons && event.target === dialog && // backdrop has target==dialog
(event.offsetX < 0 || event.offsetX >= event.target.offsetWidth ||
event.offsetY < 0 || event.offsetY >= event.target.offsetHeight));
};
const pointerup = event => {
if (pointer_outside && event.target === dialog && // backdrop as target is dialog
(event.offsetX < 0 || event.offsetX >= event.target.offsetWidth ||
event.offsetY < 0 || event.offsetY >= event.target.offsetHeight))
dialog.close();
else
pointer_outside = false;
};
const mousedown = event => {
// prevent focus on the dialog itself
if (event.buttons)
event.preventDefault();
};
const capture = { capture: true };
const close = event => {
dialog.removeEventListener ('pointerdown', pointerdown, capture);
dialog.removeEventListener ('pointerup', pointerup);
dialog.removeEventListener ('mousedown', mousedown);
dialog.removeEventListener ('close', close);
};
dialog.addEventListener ('pointerdown', pointerdown, capture);
dialog.addEventListener ('pointerup', pointerup);
dialog.addEventListener ('mousedown', mousedown);
dialog.addEventListener ('close', close);
dialog.showModal();
return dialog;
}

0 comments on commit da20031

Please sign in to comment.