Skip to content
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 14 additions & 14 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"@surma/rollup-plugin-off-main-thread": "^2.2.2",
"@types/dedent": "^0.7.0",
"@types/mime-types": "^2.1.1",
"@types/node": "^16.11.1",
"@types/node": "^16.18.38",
"@web/rollup-plugin-import-meta-assets": "^1.0.6",
"comlink": "^4.3.0",
"cssnano": "^4.1.10",
Expand All @@ -45,9 +45,9 @@
"rollup": "^2.38.0",
"rollup-plugin-terser": "^7.0.2",
"serve": "^11.3.2",
"typescript": "^4.4.4",
"which": "^2.0.2",
"wasm-feature-detect": "^1.2.11"
"typescript": "^4.8.3",
"wasm-feature-detect": "^1.2.11",
"which": "^2.0.2"
},
"lint-staged": {
"*.{js,css,json,md,ts,tsx}": "prettier --write",
Expand Down
2 changes: 2 additions & 0 deletions src/client/lazy-app/Compress/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import WorkerBridge from '../worker-bridge';
import { resize } from 'features/processors/resize/client';
import type SnackBarElement from 'shared/custom-els/snack-bar';
import { drawableToImageData } from '../util/canvas';
import Modal from '../Modal';

export type OutputType = EncoderType | 'identity';

Expand Down Expand Up @@ -1012,6 +1013,7 @@ export default class Compress extends Component<Props, State> {
</div>,
]
)}
<Modal></Modal>
</div>
);
}
Expand Down
127 changes: 127 additions & 0 deletions src/client/lazy-app/Modal/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import { h, Component, VNode, Fragment } from 'preact';
import * as style from './style.css';
import 'add-css:./style.css';
import { linkRef } from 'shared/prerendered-app/util';
import { cleanSet } from '../util/clean-modify';

interface Props {}

export interface ModalMessage {
icon: VNode;
title: string;
content: VNode;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we decided that the content of the model should be the children of the modal?

}

interface State {
message: ModalMessage;
shown: boolean;
}

export default class Modal extends Component<Props, State> {
state: State = {
message: {
icon: <svg></svg>,
title: 'default',
content: <Fragment></Fragment>,
},
shown: false,
};

private dialogElement!: HTMLDialogElement;
static modalInstance?: Modal | undefined;

componentDidMount() {
// Once a transition ends, check if the modal should be closed (not just hidden)
// dialog.close() instantly hides the modal, so we call it AFTER fading it out i.e. on transition end
this.dialogElement.addEventListener(
'transitionend',
this._closeOnTransitionEnd.bind(this),
);
this.dialogElement.setAttribute('inert', 'enabled');

Modal.modalInstance = this;
}

private _closeOnTransitionEnd() {
// If modal does not exist
// Or if it's not being closed at the moment
if (
!this.dialogElement ||
!this.dialogElement.classList.contains(style.modalClosing)
)
return;

this.dialogElement.close();
this.dialogElement.classList.remove(style.modalClosing);
this.dialogElement.setAttribute('inert', 'enabled');
}

static showModal(message: ModalMessage) {
Modal.modalInstance?._showModal(message);
}

static hideModal() {
Modal.modalInstance?._hideModal();
}

private _showModal(message: ModalMessage) {
if (!this.dialogElement) throw Error('Modal missing');

this.setState({
message: message,
shown: true,
});

// Actually show the modal
this.dialogElement.removeAttribute('inert');
this.dialogElement.showModal();
}

private _hideModal() {
if (!this.dialogElement || !this.dialogElement.open)
throw Error('Modal missing / hidden');

// Make the modal fade out
this.dialogElement.classList.add(style.modalClosing);

this.setState(cleanSet(this.state, 'shown', false));
}

private _onKeyDown(e: KeyboardEvent) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Rename the arg to event. Leave minification to the minifier 😄

// Default behaviour of <dialog> closes it instantly when you press Esc
// So we hijack it to smoothly hide the modal
if (e.key === 'Escape') {
this._hideModal();
e.preventDefault();
e.stopImmediatePropagation();
}
}

render({}: Props, { message, shown }: State) {
return (
<dialog
ref={linkRef(this, 'dialogElement')}
onKeyDown={(e) => this._onKeyDown(e)}
>
<header class={style.header}>
<span class={style.modalIcon}>{message.icon}</span>
<span class={style.modalTitle}>{message.title}</span>
<button class={style.closeButton} onClick={() => this._hideModal()}>
<svg viewBox="0 0 480 480" fill="currentColor">
<path
d="M119.356 120L361 361M360.644 120L119 361"
stroke="#fff"
stroke-width="37"
stroke-linecap="round"
/>
</svg>
</button>
</header>
<div class={style.contentContainer}>
<article class={style.content}>{message.content}</article>
</div>
<footer class={style.footer}></footer>
</dialog>
);
}
}
Loading