From 0159f2364cd8b5fcd99a3306fa4d9724f22e535e Mon Sep 17 00:00:00 2001 From: robo-mop Date: Fri, 9 Jun 2023 17:21:46 +0530 Subject: [PATCH 01/23] Add modal skeleton --- src/client/lazy-app/Compress/Modal/index.tsx | 67 +++++++++++ .../lazy-app/Compress/Modal/modal-context.ts | 13 +++ src/client/lazy-app/Compress/Modal/style.css | 86 +++++++++++++++ src/client/lazy-app/Compress/index.tsx | 104 ++++++++++-------- src/client/lazy-app/icons/index.tsx | 11 ++ 5 files changed, 237 insertions(+), 44 deletions(-) create mode 100644 src/client/lazy-app/Compress/Modal/index.tsx create mode 100644 src/client/lazy-app/Compress/Modal/modal-context.ts create mode 100644 src/client/lazy-app/Compress/Modal/style.css diff --git a/src/client/lazy-app/Compress/Modal/index.tsx b/src/client/lazy-app/Compress/Modal/index.tsx new file mode 100644 index 000000000..8b73560a3 --- /dev/null +++ b/src/client/lazy-app/Compress/Modal/index.tsx @@ -0,0 +1,67 @@ +import { h, Component } from 'preact'; +import * as style from './style.css'; +import 'add-css:./style.css'; +import { shallowEqual, isSafari } from '../../util'; +// import { * } from '../../icons'; +import { linkRef } from 'shared/prerendered-app/util'; +import { ModalInfoIcon } from 'client/lazy-app/icons'; + +interface Props {} + +export interface ModalMessage { + type: 'info' | 'error' | 'update'; + title: string; + content: string; +} + +interface State { + message: ModalMessage; + shown: boolean; +} + +export default class Modal extends Component { + state: State = { + message: { + type: 'info', + title: 'default', + content: 'default', + }, + shown: false, + }; + + showModal(message: ModalMessage) { + this.setState({ + message: message, + shown: true, + }); + } + + hideModal() { + this.setState({ + message: { ...this.state.message }, + shown: false, + }); + } + + render({}: Props, { message, shown }: State) { + return ( +
+
+
this.hideModal()}> +
+ {message.type === 'info' ? : } + {message.title} +
+
+
+
+
+
+
+
+ ); + } +} diff --git a/src/client/lazy-app/Compress/Modal/modal-context.ts b/src/client/lazy-app/Compress/Modal/modal-context.ts new file mode 100644 index 000000000..b38b63565 --- /dev/null +++ b/src/client/lazy-app/Compress/Modal/modal-context.ts @@ -0,0 +1,13 @@ +import { Context, createContext } from 'preact'; +import { ModalMessage } from '.'; + +export type ModalContextType = (_: ModalMessage) => void; + +const ModalContext: Context = createContext( + (modalMessage) => { + // Will be shown only if there is no ModalContext Provider + console.log('Using default context'); + }, +); + +export default ModalContext; diff --git a/src/client/lazy-app/Compress/Modal/style.css b/src/client/lazy-app/Compress/Modal/style.css new file mode 100644 index 000000000..dd1882313 --- /dev/null +++ b/src/client/lazy-app/Compress/Modal/style.css @@ -0,0 +1,86 @@ +.modal-overlay { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + + display: flex; + justify-content: center; + align-items: center; + + backdrop-filter: blur(5px); + z-index: 1; + + user-select: none; + pointer-events: none; + opacity: 0; + + transition: opacity 250ms ease; +} + +.modal-shown { + user-select: auto; + pointer-events: auto; + + opacity: 1; +} + +.modal-overlay > * { + box-sizing: border-box; +} + +.modal { + width: 70vw; + max-width: 60ch; + max-height: 70vh; + + box-shadow: 0 0 5px 2px rgba(0, 0, 0, 0.1); + + display: grid; + grid-template-rows: auto 1fr auto; + + font-size: 1.5em; + color: white; + + border-radius: 10px; + overflow: hidden; + + background-color: rgb(40, 40, 40); +} + +.header, +.footer { + max-height: 100%; + padding: 1em; + + background-color: rgba(0, 0, 0, 0.5); +} + +.header { + font-weight: 700; +} + +.modal-title { + font-size: 1.5em; + display: flex; + align-items: center; + + svg { + font-size: 1em; + height: 1em; + width: 1em; + margin-right: 0.5em; + } +} + +.content-container { + max-height: 60vh; + overflow-y: auto; +} + +.content { + font-size: 1em; + + padding: 0 1em; +} diff --git a/src/client/lazy-app/Compress/index.tsx b/src/client/lazy-app/Compress/index.tsx index 008ff17e8..b05d95c7c 100644 --- a/src/client/lazy-app/Compress/index.tsx +++ b/src/client/lazy-app/Compress/index.tsx @@ -32,6 +32,9 @@ 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, { ModalMessage } from './Modal'; +import ModalContext from './Modal/modal-context'; +import { linkRef } from 'shared/prerendered-app/util'; export type OutputType = EncoderType | 'identity'; @@ -324,6 +327,8 @@ export default class Compress extends Component { /** For debouncing calls to updateImage for each side. */ private updateImageTimeout?: number; + private modal?: Modal; + constructor(props: Props) { super(props); this.widthQuery.addListener(this.onMobileWidthChange); @@ -915,6 +920,12 @@ export default class Compress extends Component { }); } + private showModal(modalMessage: ModalMessage) { + if (!this.modal) return; + + this.modal.showModal(modalMessage); + } + render( { onBack }: Props, { loading, sides, source, mobileView, preprocessorState }: State, @@ -968,50 +979,55 @@ export default class Compress extends Component { return (
- - - {mobileView ? ( -
- -
{results[0]}
-
{options[0]}
-
{results[1]}
-
{options[1]}
-
-
- ) : ( - [ -
- {options[0]} - {results[0]} -
, -
- {options[1]} - {results[1]} -
, - ] - )} + this.showModal(message)} + > + + + {mobileView ? ( +
+ +
{results[0]}
+
{options[0]}
+
{results[1]}
+
{options[1]}
+
+
+ ) : ( + [ +
+ {options[0]} + {results[0]} +
, +
+ {options[1]} + {results[1]} +
, + ] + )} +
+
); } diff --git a/src/client/lazy-app/icons/index.tsx b/src/client/lazy-app/icons/index.tsx index c3a640d9e..ae50da5ef 100644 --- a/src/client/lazy-app/icons/index.tsx +++ b/src/client/lazy-app/icons/index.tsx @@ -11,6 +11,17 @@ const Icon = (props: preact.JSX.HTMLAttributes) => ( /> ); +export const ModalInfoIcon = (props: preact.JSX.HTMLAttributes) => ( + + + + + +); + export const ToggleAliasingIcon = (props: preact.JSX.HTMLAttributes) => ( Date: Tue, 13 Jun 2023 00:42:01 +0530 Subject: [PATCH 02/23] Improve the modal component * Improvements to Modal UI * Modal is now keyboard accessible * Added new/better info, error and update icons --- src/client/lazy-app/Compress/Modal/index.tsx | 101 +++++++++++++++-- src/client/lazy-app/Compress/Modal/style.css | 110 +++++++++++++++++-- src/client/lazy-app/icons/index.tsx | 27 ++++- 3 files changed, 211 insertions(+), 27 deletions(-) diff --git a/src/client/lazy-app/Compress/Modal/index.tsx b/src/client/lazy-app/Compress/Modal/index.tsx index 8b73560a3..530c8217b 100644 --- a/src/client/lazy-app/Compress/Modal/index.tsx +++ b/src/client/lazy-app/Compress/Modal/index.tsx @@ -1,10 +1,12 @@ import { h, Component } from 'preact'; import * as style from './style.css'; import 'add-css:./style.css'; -import { shallowEqual, isSafari } from '../../util'; -// import { * } from '../../icons'; import { linkRef } from 'shared/prerendered-app/util'; -import { ModalInfoIcon } from 'client/lazy-app/icons'; +import { + InfoIcon, + DiamondStarIcon, + ModalErrorIcon, +} from 'client/lazy-app/icons'; interface Props {} @@ -29,11 +31,25 @@ export default class Modal extends Component { shown: false, }; + private modal?: HTMLElement; + private returnFocusElement?: HTMLElement | null; + showModal(message: ModalMessage) { + if (this.state.shown) return; + if (!this.modal) return; + + // Set element to return focus to after hiding + this.returnFocusElement = document.activeElement as HTMLElement; + + this.modal.style.display = ''; this.setState({ message: message, shown: true, }); + // Wait for the 'display' reset to take place, then focus + setTimeout(() => { + this.modal?.querySelector('button')?.focus(); + }, 0); } hideModal() { @@ -41,17 +57,82 @@ export default class Modal extends Component { message: { ...this.state.message }, shown: false, }); + setTimeout(() => { + this.modal && (this.modal.style.display = 'none'); + this.returnFocusElement?.focus(); + }, 250); + } + + private _getCloseButton() { + return this.modal!.querySelector('button')!; + } + + private _getLastFocusable() { + const focusables = this.modal!.querySelectorAll('button, a'); + return focusables[focusables.length - 1] as HTMLElement; + } + + private _onKeyDown(e: KeyboardEvent) { + // If Escape, hide modal + if (e.key === 'Escape' || e.keyCode == 27) { + this.hideModal(); + e.preventDefault(); + e.stopImmediatePropagation(); + return; + } + + let isTabPressed = e.key === 'Tab' || e.keyCode === 9; + + if (!isTabPressed) return; + + if (e.shiftKey) { + // If SHIFT + TAB was pressed on the first focusable element + // Move focus to the last focusable element + if (document.activeElement === this._getCloseButton()) { + this._getLastFocusable().focus(); + e.preventDefault(); + e.stopImmediatePropagation(); + } + } else { + // If TAB was pressed on the last focusable element + // Move focus to the first focusable element + if (document.activeElement === this._getLastFocusable()) { + this._getCloseButton().focus(); + e.preventDefault(); + e.stopImmediatePropagation(); + } + } } render({}: Props, { message, shown }: State) { return ( -
-
-
this.hideModal()}> -
- {message.type === 'info' ? : } - {message.title} -
+
this._onKeyDown(e)} + tabIndex={shown ? 0 : -1} + > +
+
+ + {message.type === 'info' ? ( + + ) : message.type === 'error' ? ( + + ) : ( + + )} + + {message.title} +
* { - box-sizing: border-box; -} - .modal { width: 70vw; max-width: 60ch; max-height: 70vh; - box-shadow: 0 0 5px 2px rgba(0, 0, 0, 0.1); + border: 4px solid black; display: grid; grid-template-rows: auto 1fr auto; font-size: 1.5em; - color: white; border-radius: 10px; overflow: hidden; - background-color: rgb(40, 40, 40); + background-color: hsl(250deg, 100%, 90%); +} + +@media (max-width: 720px) { + .modal { + width: 90%; + max-width: none; + font-size: 1.25em; + } +} + +@media (max-width: 480px) { + .modal { + font-size: 1em; + } } .header, @@ -54,23 +64,69 @@ max-height: 100%; padding: 1em; - background-color: rgba(0, 0, 0, 0.5); + background-color: rgba(0, 0, 0, 6%); } .header { - font-weight: 700; + display: grid; + grid-template-columns: auto 1fr auto; + align-items: center; + gap: 0.5em; + border-bottom: 1px solid rgba(0, 0, 0, 8%); + + & > * { + height: 100%; + font-size: 1.5em; + } +} + +.footer { + border-top: 1px solid rgba(0, 0, 0, 8%); } .modal-title { - font-size: 1.5em; + text-align: center; + + /* Overflowing text gets ellipse-d */ + overflow: hidden; + text-overflow: ellipsis; + width: 100%; + white-space: nowrap; +} + +.modal-type-icon { display: flex; + justify-content: center; align-items: center; + opacity: 0.5; + svg { font-size: 1em; height: 1em; width: 1em; - margin-right: 0.5em; + } +} + +.close-button { + composes: unbutton from global; + flex: 0 0 auto; + height: 1em; + width: 1em; + background-color: rgb(255, 105, 118); + border-radius: 5px; + + path { + stroke: black; + } + + /* Don't show focus ring on mobile */ + @media (min-width: 720px) { + &:focus { + outline-width: 2px; + outline-style: solid; + outline-offset: 2px; + } } } @@ -81,6 +137,38 @@ .content { font-size: 1em; + line-height: 1.5; padding: 0 1em; + + img { + max-width: 100%; + max-height: 500px; + } + + figcaption { + font-size: 0.8em; + text-align: center; + font-style: italic; + } + + a { + &:link { + color: hsl(220, 100%, 50%); + } + &:visited { + color: rgb(255, 0, 103); + } + } + + pre, + code { + background-color: rgba(0, 0, 0, 10%); + padding: 2px 4px; + border-radius: 3px; + } + + pre { + overflow-x: auto; + } } diff --git a/src/client/lazy-app/icons/index.tsx b/src/client/lazy-app/icons/index.tsx index ae50da5ef..9071aac34 100644 --- a/src/client/lazy-app/icons/index.tsx +++ b/src/client/lazy-app/icons/index.tsx @@ -11,14 +11,29 @@ const Icon = (props: preact.JSX.HTMLAttributes) => ( /> ); -export const ModalInfoIcon = (props: preact.JSX.HTMLAttributes) => ( - - +export const InfoIcon = (props: preact.JSX.HTMLAttributes) => ( + + + +); + +export const ModalErrorIcon = (props: preact.JSX.HTMLAttributes) => ( + - + +); + +export const DiamondStarIcon = (props: preact.JSX.HTMLAttributes) => ( + + ); From 2b4d8936f4a6aa566f949eb775d178eeb9acaa7e Mon Sep 17 00:00:00 2001 From: robo-mop Date: Tue, 13 Jun 2023 01:06:10 +0530 Subject: [PATCH 03/23] Change Modal directory + color scheme --- .../lazy-app/Compress/Modal/modal-context.ts | 13 --------- src/client/lazy-app/Compress/index.tsx | 4 +-- .../lazy-app/{Compress => }/Modal/index.tsx | 0 .../lazy-app/{Compress => }/Modal/style.css | 28 ++++++++++++++----- 4 files changed, 23 insertions(+), 22 deletions(-) delete mode 100644 src/client/lazy-app/Compress/Modal/modal-context.ts rename src/client/lazy-app/{Compress => }/Modal/index.tsx (100%) rename src/client/lazy-app/{Compress => }/Modal/style.css (81%) diff --git a/src/client/lazy-app/Compress/Modal/modal-context.ts b/src/client/lazy-app/Compress/Modal/modal-context.ts deleted file mode 100644 index b38b63565..000000000 --- a/src/client/lazy-app/Compress/Modal/modal-context.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Context, createContext } from 'preact'; -import { ModalMessage } from '.'; - -export type ModalContextType = (_: ModalMessage) => void; - -const ModalContext: Context = createContext( - (modalMessage) => { - // Will be shown only if there is no ModalContext Provider - console.log('Using default context'); - }, -); - -export default ModalContext; diff --git a/src/client/lazy-app/Compress/index.tsx b/src/client/lazy-app/Compress/index.tsx index b05d95c7c..d9edbf398 100644 --- a/src/client/lazy-app/Compress/index.tsx +++ b/src/client/lazy-app/Compress/index.tsx @@ -32,8 +32,8 @@ 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, { ModalMessage } from './Modal'; -import ModalContext from './Modal/modal-context'; +import Modal, { ModalMessage } from '../Modal'; +import ModalContext from '../Modal/modal-context'; import { linkRef } from 'shared/prerendered-app/util'; export type OutputType = EncoderType | 'identity'; diff --git a/src/client/lazy-app/Compress/Modal/index.tsx b/src/client/lazy-app/Modal/index.tsx similarity index 100% rename from src/client/lazy-app/Compress/Modal/index.tsx rename to src/client/lazy-app/Modal/index.tsx diff --git a/src/client/lazy-app/Compress/Modal/style.css b/src/client/lazy-app/Modal/style.css similarity index 81% rename from src/client/lazy-app/Compress/Modal/style.css rename to src/client/lazy-app/Modal/style.css index 4e50e9752..5a08373ab 100644 --- a/src/client/lazy-app/Compress/Modal/style.css +++ b/src/client/lazy-app/Modal/style.css @@ -32,7 +32,7 @@ max-width: 60ch; max-height: 70vh; - border: 4px solid black; + box-shadow: 0 0 20px 0 rgba(0, 0, 0, 20%); display: grid; grid-template-rows: auto 1fr auto; @@ -42,7 +42,8 @@ border-radius: 10px; overflow: hidden; - background-color: hsl(250deg, 100%, 90%); + background-color: var(--off-black); + color: var(--light-gray); } @media (max-width: 720px) { @@ -64,7 +65,7 @@ max-height: 100%; padding: 1em; - background-color: rgba(0, 0, 0, 6%); + background-color: var(--dark-gray); } .header { @@ -113,8 +114,11 @@ flex: 0 0 auto; height: 1em; width: 1em; - background-color: rgb(255, 105, 118); + background-color: var(--pink); border-radius: 5px; + outline-color: var( + --medium-light-gray + ) !important; /* Overwrite default black outline */ path { stroke: black; @@ -141,6 +145,10 @@ padding: 0 1em; + h1 { + color: var(--white); + } + img { max-width: 100%; max-height: 500px; @@ -154,21 +162,27 @@ a { &:link { - color: hsl(220, 100%, 50%); + color: var(--blue); } &:visited { - color: rgb(255, 0, 103); + color: var(--pink); } } pre, code { - background-color: rgba(0, 0, 0, 10%); + background-color: var(--black); padding: 2px 4px; border-radius: 3px; } pre { overflow-x: auto; + padding: 1rem 2rem; + } + + hr { + border: none; + border-bottom: 1px solid var(--dim-text); } } From 89f94f756341fbd5931e41e8ca3b0152262bc8e2 Mon Sep 17 00:00:00 2001 From: robo-mop Date: Sat, 1 Jul 2023 17:30:35 +0530 Subject: [PATCH 04/23] Bump TS version for `` support TS 4.8.3 onwards supports `showModal()`. Also bumped @types/node. --- package-lock.json | 28 ++++++++++++++-------------- package.json | 8 ++++---- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/package-lock.json b/package-lock.json index f4ed52c56..a5cf3d9e6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,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", @@ -42,7 +42,7 @@ "rollup": "^2.38.0", "rollup-plugin-terser": "^7.0.2", "serve": "^11.3.2", - "typescript": "^4.4.4", + "typescript": "^4.8.3", "wasm-feature-detect": "^1.2.11", "which": "^2.0.2" } @@ -326,9 +326,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "16.11.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.1.tgz", - "integrity": "sha512-PYGcJHL9mwl1Ek3PLiYgyEKtwTMmkMw4vbiyz/ps3pfdRYLVv+SN7qHVAImrjdAXxgluDEw6Ph4lyv+m9UpRmA==", + "version": "16.18.38", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.38.tgz", + "integrity": "sha512-6sfo1qTulpVbkxECP+AVrHV9OoJqhzCsfTNp5NIG+enM4HyM3HvZCO798WShIXBN0+QtDIcutJCjsVYnQP5rIQ==", "dev": true }, "node_modules/@types/parse-json": { @@ -8470,9 +8470,9 @@ } }, "node_modules/typescript": { - "version": "4.4.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.4.tgz", - "integrity": "sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA==", + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -8915,9 +8915,9 @@ "dev": true }, "@types/node": { - "version": "16.11.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.1.tgz", - "integrity": "sha512-PYGcJHL9mwl1Ek3PLiYgyEKtwTMmkMw4vbiyz/ps3pfdRYLVv+SN7qHVAImrjdAXxgluDEw6Ph4lyv+m9UpRmA==", + "version": "16.18.38", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.38.tgz", + "integrity": "sha512-6sfo1qTulpVbkxECP+AVrHV9OoJqhzCsfTNp5NIG+enM4HyM3HvZCO798WShIXBN0+QtDIcutJCjsVYnQP5rIQ==", "dev": true }, "@types/parse-json": { @@ -15677,9 +15677,9 @@ "dev": true }, "typescript": { - "version": "4.4.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.4.tgz", - "integrity": "sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA==", + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", "dev": true }, "uniq": { diff --git a/package.json b/package.json index c8eb30787..39ae6987b 100644 --- a/package.json +++ b/package.json @@ -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", @@ -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", From 4e98cab9cf938854b5f74eb9c7db956c576223a3 Mon Sep 17 00:00:00 2001 From: robo-mop Date: Sun, 2 Jul 2023 02:34:25 +0530 Subject: [PATCH 05/23] Refactor modal to use `` --- src/client/lazy-app/Modal/index.tsx | 153 +++++++++++++--------------- src/client/lazy-app/Modal/style.css | 80 ++++++++------- 2 files changed, 117 insertions(+), 116 deletions(-) diff --git a/src/client/lazy-app/Modal/index.tsx b/src/client/lazy-app/Modal/index.tsx index 530c8217b..fc3b9fc3b 100644 --- a/src/client/lazy-app/Modal/index.tsx +++ b/src/client/lazy-app/Modal/index.tsx @@ -31,118 +31,107 @@ export default class Modal extends Component { shown: false, }; - private modal?: HTMLElement; - private returnFocusElement?: HTMLElement | null; + private modal?: HTMLDialogElement; + + 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.modal?.addEventListener( + 'transitionend', + this._closeOnTransitionEnd.bind(this), + ); + this.modal?.setAttribute('inert', 'enabled'); + } + private _closeOnTransitionEnd() { + // If modal does not exist + // Or if it's not being closed at the moment + if (!this.modal || !this.modal.classList.contains(style.modalClosing)) + return; + + this.modal.close(); + this.modal.classList.remove(style.modalClosing); + this.modal.setAttribute('inert', 'enabled'); + } + + /** + * Function to set up the modal and show it + */ showModal(message: ModalMessage) { - if (this.state.shown) return; if (!this.modal) return; - // Set element to return focus to after hiding - this.returnFocusElement = document.activeElement as HTMLElement; - - this.modal.style.display = ''; this.setState({ message: message, shown: true, }); - // Wait for the 'display' reset to take place, then focus - setTimeout(() => { - this.modal?.querySelector('button')?.focus(); - }, 0); + + // Actually show the modal + this.modal.removeAttribute('inert'); + this.modal.showModal(); } + /** + * Function to hide the modal with a fade-out transition + * Adds the `modal--closing` class which is removed on transition end + */ hideModal() { + if (!this.modal || !this.modal.open) return; + + // Make the modal fade out + this.modal.classList.add(style.modalClosing); + this.setState({ message: { ...this.state.message }, shown: false, }); - setTimeout(() => { - this.modal && (this.modal.style.display = 'none'); - this.returnFocusElement?.focus(); - }, 250); - } - - private _getCloseButton() { - return this.modal!.querySelector('button')!; - } - - private _getLastFocusable() { - const focusables = this.modal!.querySelectorAll('button, a'); - return focusables[focusables.length - 1] as HTMLElement; } private _onKeyDown(e: KeyboardEvent) { - // If Escape, hide modal + // Default behaviour of closes it instantly when you press Esc + // So we hijack it to smoothly hide the modal if (e.key === 'Escape' || e.keyCode == 27) { this.hideModal(); e.preventDefault(); e.stopImmediatePropagation(); - return; - } - - let isTabPressed = e.key === 'Tab' || e.keyCode === 9; - - if (!isTabPressed) return; - - if (e.shiftKey) { - // If SHIFT + TAB was pressed on the first focusable element - // Move focus to the last focusable element - if (document.activeElement === this._getCloseButton()) { - this._getLastFocusable().focus(); - e.preventDefault(); - e.stopImmediatePropagation(); - } - } else { - // If TAB was pressed on the last focusable element - // Move focus to the first focusable element - if (document.activeElement === this._getLastFocusable()) { - this._getCloseButton().focus(); - e.preventDefault(); - e.stopImmediatePropagation(); - } } } render({}: Props, { message, shown }: State) { return ( -
this._onKeyDown(e)} - tabIndex={shown ? 0 : -1} > -
-
- - {message.type === 'info' ? ( - - ) : message.type === 'error' ? ( - - ) : ( - - )} - - {message.title} - -
-
-
-
-
+
+ + {message.type === 'info' ? ( + + ) : message.type === 'error' ? ( + + ) : ( + + )} + + {message.title} + +
+
+
-
+
+ ); } } diff --git a/src/client/lazy-app/Modal/style.css b/src/client/lazy-app/Modal/style.css index 5a08373ab..d95841b1d 100644 --- a/src/client/lazy-app/Modal/style.css +++ b/src/client/lazy-app/Modal/style.css @@ -1,33 +1,5 @@ -.modal-overlay { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - - display: flex; - justify-content: center; - align-items: center; - - backdrop-filter: blur(5px); - -webkit-backdrop-filter: blur(5px); - z-index: 1; - - user-select: none; - pointer-events: none; - opacity: 0; - - transition: opacity 250ms ease; -} - -.modal-shown { - user-select: auto; - pointer-events: auto; - - opacity: 1; -} - -.modal { +dialog { + padding: 0; width: 70vw; max-width: 60ch; max-height: 70vh; @@ -38,16 +10,55 @@ grid-template-rows: auto 1fr auto; font-size: 1.5em; + color: var(--light-gray); + border: none; border-radius: 10px; overflow: hidden; background-color: var(--off-black); - color: var(--light-gray); + + opacity: 0; + transform: translateY(50px); + transition: 250ms ease; + + /* backdrop can't be transitioned easily, so we must */ + + &::backdrop { + background-color: rgba(0, 0, 0, 30%); + } + + &[open]::backdrop { + animation: backdrop-fade-in 250ms linear; + + @keyframes backdrop-fade-in { + from { + opacity: 0; + } + to { + opacity: 1; + } + } + } + + &.modal--closing::backdrop { + transition: opacity 250ms linear; + opacity: 0; + } +} + +dialog[open] { + opacity: 1; + transform: translateY(0px); +} + +dialog.modal--closing { + opacity: 0; + transform: translateY(50px); } @media (max-width: 720px) { - .modal { + dialog { width: 90%; max-width: none; font-size: 1.25em; @@ -55,7 +66,7 @@ } @media (max-width: 480px) { - .modal { + dialog { font-size: 1em; } } @@ -74,9 +85,10 @@ align-items: center; gap: 0.5em; border-bottom: 1px solid rgba(0, 0, 0, 8%); + align-content: stretch; + /* Apply font size only to children so that padding is unaffected */ & > * { - height: 100%; font-size: 1.5em; } } From d1337d24d58be9bb2f4e2a84d93353a900e267d7 Mon Sep 17 00:00:00 2001 From: robo-mop Date: Sun, 2 Jul 2023 02:59:07 +0530 Subject: [PATCH 06/23] Modal - use VNode icon and content --- src/client/lazy-app/Modal/index.tsx | 30 +++++++---------------------- src/client/lazy-app/Modal/style.css | 2 +- 2 files changed, 8 insertions(+), 24 deletions(-) diff --git a/src/client/lazy-app/Modal/index.tsx b/src/client/lazy-app/Modal/index.tsx index fc3b9fc3b..0d8d891b1 100644 --- a/src/client/lazy-app/Modal/index.tsx +++ b/src/client/lazy-app/Modal/index.tsx @@ -1,19 +1,14 @@ -import { h, Component } from 'preact'; +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 { - InfoIcon, - DiamondStarIcon, - ModalErrorIcon, -} from 'client/lazy-app/icons'; interface Props {} export interface ModalMessage { - type: 'info' | 'error' | 'update'; + icon: VNode; title: string; - content: string; + content: VNode; } interface State { @@ -24,9 +19,9 @@ interface State { export default class Modal extends Component { state: State = { message: { - type: 'info', + icon: , title: 'default', - content: 'default', + content: , }, shown: false, }; @@ -103,15 +98,7 @@ export default class Modal extends Component { onKeyDown={(e) => this._onKeyDown(e)} >
- - {message.type === 'info' ? ( - - ) : message.type === 'error' ? ( - - ) : ( - - )} - + {message.icon} {message.title}
-
+
{message.content}
diff --git a/src/client/lazy-app/Modal/style.css b/src/client/lazy-app/Modal/style.css index d95841b1d..d56656a40 100644 --- a/src/client/lazy-app/Modal/style.css +++ b/src/client/lazy-app/Modal/style.css @@ -107,7 +107,7 @@ dialog.modal--closing { white-space: nowrap; } -.modal-type-icon { +.modal-icon { display: flex; justify-content: center; align-items: center; From e9e2c30025b889552bc4baf1fd31213af0cddcf1 Mon Sep 17 00:00:00 2001 From: robo-mop Date: Wed, 5 Jul 2023 17:35:54 +0530 Subject: [PATCH 07/23] Replace Context with shared functions --- src/client/lazy-app/Compress/index.tsx | 106 +++++++++++-------------- src/client/lazy-app/Modal/index.tsx | 64 ++++++++------- 2 files changed, 81 insertions(+), 89 deletions(-) diff --git a/src/client/lazy-app/Compress/index.tsx b/src/client/lazy-app/Compress/index.tsx index d9edbf398..672516471 100644 --- a/src/client/lazy-app/Compress/index.tsx +++ b/src/client/lazy-app/Compress/index.tsx @@ -32,9 +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, { ModalMessage } from '../Modal'; -import ModalContext from '../Modal/modal-context'; -import { linkRef } from 'shared/prerendered-app/util'; +import Modal from '../Modal'; export type OutputType = EncoderType | 'identity'; @@ -327,8 +325,6 @@ export default class Compress extends Component { /** For debouncing calls to updateImage for each side. */ private updateImageTimeout?: number; - private modal?: Modal; - constructor(props: Props) { super(props); this.widthQuery.addListener(this.onMobileWidthChange); @@ -920,12 +916,6 @@ export default class Compress extends Component { }); } - private showModal(modalMessage: ModalMessage) { - if (!this.modal) return; - - this.modal.showModal(modalMessage); - } - render( { onBack }: Props, { loading, sides, source, mobileView, preprocessorState }: State, @@ -979,55 +969,51 @@ export default class Compress extends Component { return (
- this.showModal(message)} - > - - - {mobileView ? ( -
- -
{results[0]}
-
{options[0]}
-
{results[1]}
-
{options[1]}
-
-
- ) : ( - [ -
- {options[0]} - {results[0]} -
, -
- {options[1]} - {results[1]} -
, - ] - )} -
- + + + {mobileView ? ( +
+ +
{results[0]}
+
{options[0]}
+
{results[1]}
+
{options[1]}
+
+
+ ) : ( + [ +
+ {options[0]} + {results[0]} +
, +
+ {options[1]} + {results[1]} +
, + ] + )} +
); } diff --git a/src/client/lazy-app/Modal/index.tsx b/src/client/lazy-app/Modal/index.tsx index 0d8d891b1..38beeb86d 100644 --- a/src/client/lazy-app/Modal/index.tsx +++ b/src/client/lazy-app/Modal/index.tsx @@ -2,6 +2,7 @@ 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 {} @@ -26,34 +27,45 @@ export default class Modal extends Component { shown: false, }; - private modal?: HTMLDialogElement; + 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.modal?.addEventListener( + this.dialogElement.addEventListener( 'transitionend', this._closeOnTransitionEnd.bind(this), ); - this.modal?.setAttribute('inert', 'enabled'); + 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.modal || !this.modal.classList.contains(style.modalClosing)) + if ( + !this.dialogElement || + !this.dialogElement.classList.contains(style.modalClosing) + ) return; - this.modal.close(); - this.modal.classList.remove(style.modalClosing); - this.modal.setAttribute('inert', 'enabled'); + this.dialogElement.close(); + this.dialogElement.classList.remove(style.modalClosing); + this.dialogElement.setAttribute('inert', 'enabled'); + } + + static showModal(message: ModalMessage) { + Modal.modalInstance?._showModal(message); } - /** - * Function to set up the modal and show it - */ - showModal(message: ModalMessage) { - if (!this.modal) return; + static hideModal() { + Modal.modalInstance?._hideModal(); + } + + private _showModal(message: ModalMessage) { + if (!this.dialogElement) throw Error('Modal missing'); this.setState({ message: message, @@ -61,31 +73,25 @@ export default class Modal extends Component { }); // Actually show the modal - this.modal.removeAttribute('inert'); - this.modal.showModal(); + this.dialogElement.removeAttribute('inert'); + this.dialogElement.showModal(); } - /** - * Function to hide the modal with a fade-out transition - * Adds the `modal--closing` class which is removed on transition end - */ - hideModal() { - if (!this.modal || !this.modal.open) return; + private _hideModal() { + if (!this.dialogElement || !this.dialogElement.open) + throw Error('Modal missing / hidden'); // Make the modal fade out - this.modal.classList.add(style.modalClosing); + this.dialogElement.classList.add(style.modalClosing); - this.setState({ - message: { ...this.state.message }, - shown: false, - }); + this.setState(cleanSet(this.state, 'shown', false)); } private _onKeyDown(e: KeyboardEvent) { // Default behaviour of closes it instantly when you press Esc // So we hijack it to smoothly hide the modal - if (e.key === 'Escape' || e.keyCode == 27) { - this.hideModal(); + if (e.key === 'Escape') { + this._hideModal(); e.preventDefault(); e.stopImmediatePropagation(); } @@ -94,13 +100,13 @@ export default class Modal extends Component { render({}: Props, { message, shown }: State) { return ( this._onKeyDown(e)} >
{message.icon} {message.title} -
, ] )} -
); } diff --git a/src/client/lazy-app/Modal/index.tsx b/src/client/lazy-app/Modal/index.tsx index 38beeb86d..8a0ca0fac 100644 --- a/src/client/lazy-app/Modal/index.tsx +++ b/src/client/lazy-app/Modal/index.tsx @@ -4,45 +4,37 @@ import 'add-css:./style.css'; import { linkRef } from 'shared/prerendered-app/util'; import { cleanSet } from '../util/clean-modify'; -interface Props {} - -export interface ModalMessage { +interface Props { icon: VNode; title: string; content: VNode; } interface State { - message: ModalMessage; shown: boolean; } export default class Modal extends Component { - state: State = { - message: { - icon: , - title: 'default', - content: , - }, - shown: false, - }; - - private dialogElement!: HTMLDialogElement; - static modalInstance?: Modal | undefined; + private dialogElement?: HTMLDialogElement; componentDidMount() { + if (!this.dialogElement) throw new Error('Modal missing'); // 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'); + this.dialogElement.addEventListener('transitionend', (event) => { + event.stopPropagation(); + this._closeOnTransitionEnd(); + }); + this.dialogElement.inert = true; - Modal.modalInstance = this; + // Prevent events from leaking through the dialog + this.dialogElement.onclick = (event) => { + event.preventDefault(); + event.stopImmediatePropagation(); + }; } - private _closeOnTransitionEnd() { + private _closeOnTransitionEnd = () => { // If modal does not exist // Or if it's not being closed at the moment if ( @@ -54,27 +46,16 @@ export default class Modal extends Component { 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({ shown: false }); + }; - this.setState({ - message: message, - shown: true, - }); + showModal() { + if (!this.dialogElement || this.dialogElement.open) + throw Error('Modal missing / already shown'); - // Actually show the modal - this.dialogElement.removeAttribute('inert'); + this.dialogElement.inert = false; this.dialogElement.showModal(); + this.setState({ shown: true }); } private _hideModal() { @@ -83,8 +64,6 @@ export default class Modal extends Component { // Make the modal fade out this.dialogElement.classList.add(style.modalClosing); - - this.setState(cleanSet(this.state, 'shown', false)); } private _onKeyDown(e: KeyboardEvent) { @@ -97,30 +76,37 @@ export default class Modal extends Component { } } - render({}: Props, { message, shown }: State) { + render({ title, icon, content }: Props, { shown }: State) { return ( this._onKeyDown(e)} > -
- {message.icon} - {message.title} - -
-
-
{message.content}
-
-
+ {shown && ( + +
+ {icon} + {title} + +
+
+
{content}
+
+
+
+ )}
); } diff --git a/src/client/lazy-app/Modal/style.css b/src/client/lazy-app/Modal/style.css index d56656a40..59639e060 100644 --- a/src/client/lazy-app/Modal/style.css +++ b/src/client/lazy-app/Modal/style.css @@ -3,13 +3,14 @@ dialog { width: 70vw; max-width: 60ch; max-height: 70vh; + cursor: auto; box-shadow: 0 0 20px 0 rgba(0, 0, 0, 20%); display: grid; grid-template-rows: auto 1fr auto; - font-size: 1.5em; + font-size: 1.5rem; color: var(--light-gray); border: none; @@ -22,7 +23,7 @@ dialog { transform: translateY(50px); transition: 250ms ease; - /* backdrop can't be transitioned easily, so we must */ + /* ::backdrop is created when dialog is opened, so animating it needs trickery */ &::backdrop { background-color: rgba(0, 0, 0, 30%); @@ -61,13 +62,13 @@ dialog.modal--closing { dialog { width: 90%; max-width: none; - font-size: 1.25em; + font-size: 1.25rem; } } @media (max-width: 480px) { dialog { - font-size: 1em; + font-size: 1rem; } } @@ -128,6 +129,7 @@ dialog.modal--closing { width: 1em; background-color: var(--pink); border-radius: 5px; + display: grid; outline-color: var( --medium-light-gray ) !important; /* Overwrite default black outline */ From b66ec294fabd7e82251d69293179e616b74617c1 Mon Sep 17 00:00:00 2001 From: robo-mop Date: Wed, 26 Jul 2023 13:49:37 +0530 Subject: [PATCH 09/23] Add ModalHint --- src/client/lazy-app/Modal/ModalHint/index.tsx | 56 +++++++++++++++++++ src/client/lazy-app/Modal/ModalHint/style.css | 31 ++++++++++ src/client/lazy-app/icons/index.tsx | 6 +- 3 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 src/client/lazy-app/Modal/ModalHint/index.tsx create mode 100644 src/client/lazy-app/Modal/ModalHint/style.css diff --git a/src/client/lazy-app/Modal/ModalHint/index.tsx b/src/client/lazy-app/Modal/ModalHint/index.tsx new file mode 100644 index 000000000..46614e865 --- /dev/null +++ b/src/client/lazy-app/Modal/ModalHint/index.tsx @@ -0,0 +1,56 @@ +import { h, Component, VNode } from 'preact'; +import Modal from '..'; +import { InfoIcon } from 'client/lazy-app/icons'; +import { linkRef } from 'shared/prerendered-app/util'; + +import * as style from './style.css'; +import 'add-css:./style.css'; + +interface Props { + modalTitle: string; + content: VNode; + text?: string; +} + +interface State {} + +export default class ModalHint extends Component { + private modalComponent?: Modal; + + private onclick = (event: Event) => { + if (!this.modalComponent) + throw new Error('ModalHint is missing a modalComponent'); + + // Stop bubbled events from triggering the modal + if (!(event.currentTarget as Element).matches('button')) return; + + this.modalComponent.showModal(); + }; + + render({ modalTitle, content }: Props) { + return ( + { + event.preventDefault(); + event.stopImmediatePropagation(); + }} + > + + } + title={modalTitle} + content={content} + > + + ); + } +} diff --git a/src/client/lazy-app/Modal/ModalHint/style.css b/src/client/lazy-app/Modal/ModalHint/style.css new file mode 100644 index 000000000..417e2a8a1 --- /dev/null +++ b/src/client/lazy-app/Modal/ModalHint/style.css @@ -0,0 +1,31 @@ +span.modal-hint { + display: inline-block; + vertical-align: bottom; +} + +.modal-button { + composes: unbutton from global; + + color: inherit; + + display: flex; + align-items: center; + gap: 0.5rem; + + border-radius: 2px; +} + +.modal-button:hover { + text-decoration: underline solid 1px currentColor; + text-underline-offset: 0.1em; +} + +.modal-button:focus { + outline: white solid 1px; + outline-offset: 0.25em; +} + +.modal-button > svg { + width: 1em; + height: 1em; +} diff --git a/src/client/lazy-app/icons/index.tsx b/src/client/lazy-app/icons/index.tsx index 9071aac34..153535ae7 100644 --- a/src/client/lazy-app/icons/index.tsx +++ b/src/client/lazy-app/icons/index.tsx @@ -13,7 +13,11 @@ const Icon = (props: preact.JSX.HTMLAttributes) => ( export const InfoIcon = (props: preact.JSX.HTMLAttributes) => ( - + ); From fefe3d3545aa497f78b24bb372945e54beecd00c Mon Sep 17 00:00:00 2001 From: robo-mop Date: Wed, 26 Jul 2023 14:40:58 +0530 Subject: [PATCH 10/23] Basic `ModalHint` demo --- .../encoders/mozJPEG/client/index.tsx | 55 +++++++++++++++++- src/static-build/assets/link.jpg | Bin 0 -> 72189 bytes 2 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 src/static-build/assets/link.jpg diff --git a/src/features/encoders/mozJPEG/client/index.tsx b/src/features/encoders/mozJPEG/client/index.tsx index 780e5cb3c..36684b5c6 100644 --- a/src/features/encoders/mozJPEG/client/index.tsx +++ b/src/features/encoders/mozJPEG/client/index.tsx @@ -1,6 +1,6 @@ import { EncodeOptions, MozJpegColorSpace } from '../shared/meta'; import type WorkerBridge from 'client/lazy-app/worker-bridge'; -import { h, Component } from 'preact'; +import { h, Component, Fragment } from 'preact'; import { inputFieldChecked, inputFieldValueAsNumber, @@ -13,6 +13,8 @@ import Checkbox from 'client/lazy-app/Compress/Options/Checkbox'; import Expander from 'client/lazy-app/Compress/Options/Expander'; import Select from 'client/lazy-app/Compress/Options/Select'; import Revealer from 'client/lazy-app/Compress/Options/Revealer'; +import ModalHint from 'client/lazy-app/Modal/ModalHint'; +import * as linkImage from 'img-url:static-build/assets/link.jpg'; export function encode( signal: AbortSignal, @@ -23,6 +25,53 @@ export function encode( return workerBridge.mozjpegEncode(signal, imageData, options); } +function sampleContent() { + return ( + +

Test Suite (h1)

+

+ Let's say, hypothetically, this paragraph links to{' '} + WASSUP}> + Trellis Multipass + + . Lorem ipsum dolor sit amet consectetur adipisicing elit. Quaerat ad + maiores iure suscipit numquam voluptate. +

+

+ This is another paragraph. Notice how another uses + italics. Oh wait, I just used the <b> tag. Oh, and escaped HTML + unicode thingies. Now I'm just rambling so that this is long enough to + be multi-line, and you can see the line-height. +

+
+
+        # Which is the best programming language?
+        
+ print("Python is!") +
+ Oh and this is all in <pre> +
+

+ This text is strong. And this is in + <code>. +

+
+ Alt Text +
+ Source: Nintendo EDP / Nintendo (and this is a figcaption) +
+
+

+ That's a link.{' '} + + This is also a link + + . +

+
+ ); +} + interface Props { options: EncodeOptions; onChange(newOptions: EncodeOptions): void; @@ -114,7 +163,9 @@ export class Options extends Component { value={options.quality} onInput={this.onChange} > - Quality: + + Quality: +
-
-
{content}
-
-
- - )} +
+
+ {icon} + {title} + +
+
+
{content}
+
+
+
); } diff --git a/src/client/lazy-app/Modal/style.css b/src/client/lazy-app/Modal/style.css index e2aa6e59c..76213d96e 100644 --- a/src/client/lazy-app/Modal/style.css +++ b/src/client/lazy-app/Modal/style.css @@ -1,4 +1,4 @@ -dialog { +.modal-dialog { padding: 0; width: 70vw; max-width: 60ch; @@ -7,9 +7,6 @@ dialog { box-shadow: 0 0 20px 0 rgba(0, 0, 0, 20%); - display: grid; - grid-template-rows: auto 1fr auto; - font-size: 1.5rem; color: var(--light-gray); @@ -28,7 +25,7 @@ dialog { } @media (max-width: 720px) { - dialog { + .modal-dialog { width: 90%; max-width: none; font-size: 1.25rem; @@ -36,11 +33,16 @@ dialog { } @media (max-width: 480px) { - dialog { + .modal-dialog { font-size: 1rem; } } +.article { + display: grid; + grid-template-rows: auto 1fr auto; +} + .header, .footer { max-height: 100%; From a25f09385c146e52c27d7ca44c92e9d06b4a79d0 Mon Sep 17 00:00:00 2001 From: robo-mop Date: Wed, 13 Sep 2023 11:41:21 +0530 Subject: [PATCH 18/23] Add RoundedCrossIcon to icons --- src/client/lazy-app/Modal/index.tsx | 10 ++-------- src/client/lazy-app/Modal/style.css | 6 ++---- src/client/lazy-app/icons/index.tsx | 21 ++++++++++++++++++--- 3 files changed, 22 insertions(+), 15 deletions(-) diff --git a/src/client/lazy-app/Modal/index.tsx b/src/client/lazy-app/Modal/index.tsx index 56014de73..49d376d72 100644 --- a/src/client/lazy-app/Modal/index.tsx +++ b/src/client/lazy-app/Modal/index.tsx @@ -4,6 +4,7 @@ import 'add-css:./style.css'; import { linkRef } from 'shared/prerendered-app/util'; import { cleanSet } from '../util/clean-modify'; import { animateTo } from '../util'; +import { RoundedCrossIcon } from '../icons'; interface Props { icon: VNode; @@ -101,14 +102,7 @@ export default class Modal extends Component { {icon} {title}
diff --git a/src/client/lazy-app/Modal/style.css b/src/client/lazy-app/Modal/style.css index 76213d96e..7bb927493 100644 --- a/src/client/lazy-app/Modal/style.css +++ b/src/client/lazy-app/Modal/style.css @@ -103,14 +103,12 @@ display: grid; outline-color: var(--medium-light-gray); + color: black; /* Influences currentColor of the icon */ + &:focus { outline: var(--medium-light-gray); /* Overwrite default black outline */ } - path { - stroke: black; - } - /* Don't show focus ring on mobile */ @media (min-width: 720px) { &:focus { diff --git a/src/client/lazy-app/icons/index.tsx b/src/client/lazy-app/icons/index.tsx index 5dfda1a15..90d85728e 100644 --- a/src/client/lazy-app/icons/index.tsx +++ b/src/client/lazy-app/icons/index.tsx @@ -1,4 +1,4 @@ -import { h } from 'preact'; +import preact, { h } from 'preact'; const Icon = (props: preact.JSX.HTMLAttributes) => ( // @ts-ignore - TS bug https://github.com/microsoft/TypeScript/issues/16019 @@ -11,9 +11,24 @@ const Icon = (props: preact.JSX.HTMLAttributes) => ( /> ); +/* Cross with rounded linecaps */ +export const RoundedCrossIcon = (props: preact.JSX.HTMLAttributes) => ( + + + +); + export const InfoIcon = (props: preact.JSX.HTMLAttributes) => ( - + ); @@ -28,7 +43,7 @@ export const ModalErrorIcon = (props: preact.JSX.HTMLAttributes) => ( export const DiamondStarIcon = (props: preact.JSX.HTMLAttributes) => ( - + ); From d9391d01d4a62ccb271a408dc08ee008acacdd9e Mon Sep 17 00:00:00 2001 From: robo-mop Date: Wed, 13 Sep 2023 15:16:32 +0530 Subject: [PATCH 19/23] Refactor ModalHint ModalHint now takes the modal content as its children, and the optional text through props --- src/client/lazy-app/Modal/ModalHint/index.tsx | 7 +++---- src/client/lazy-app/Modal/style.css | 10 ++++------ 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/client/lazy-app/Modal/ModalHint/index.tsx b/src/client/lazy-app/Modal/ModalHint/index.tsx index 43e63a638..718f6821b 100644 --- a/src/client/lazy-app/Modal/ModalHint/index.tsx +++ b/src/client/lazy-app/Modal/ModalHint/index.tsx @@ -8,7 +8,6 @@ import 'add-css:./style.css'; interface Props { modalTitle: string; - content: VNode; text?: string; } @@ -27,7 +26,7 @@ export default class ModalHint extends Component { this.modalComponent.showModal(); }; - render({ modalTitle, content }: Props) { + render({ modalTitle, text }: Props) { return ( { title="Learn more" > - {this.props.children} + {text} } title={modalTitle} - content={content} + content={this.props.children as any} > ); diff --git a/src/client/lazy-app/Modal/style.css b/src/client/lazy-app/Modal/style.css index 7bb927493..f8adf7dd4 100644 --- a/src/client/lazy-app/Modal/style.css +++ b/src/client/lazy-app/Modal/style.css @@ -110,12 +110,10 @@ } /* Don't show focus ring on mobile */ - @media (min-width: 720px) { - &:focus { - outline-width: 2px; - outline-style: solid; - outline-offset: 2px; - } + &:focus-visible { + outline-width: 2px; + outline-style: solid; + outline-offset: 2px; } } From 8511b8f7f1c7d469e5c4ec16c4e21c1cdebc8da7 Mon Sep 17 00:00:00 2001 From: robo-mop Date: Wed, 13 Sep 2023 15:46:22 +0530 Subject: [PATCH 20/23] Improved icons --- src/client/lazy-app/icons/index.tsx | 32 ++++++++++++++++------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/src/client/lazy-app/icons/index.tsx b/src/client/lazy-app/icons/index.tsx index 90d85728e..e0c49291a 100644 --- a/src/client/lazy-app/icons/index.tsx +++ b/src/client/lazy-app/icons/index.tsx @@ -24,20 +24,24 @@ export const RoundedCrossIcon = (props: preact.JSX.HTMLAttributes) => ( ); export const InfoIcon = (props: preact.JSX.HTMLAttributes) => ( - - - -); - -export const ModalErrorIcon = (props: preact.JSX.HTMLAttributes) => ( - - + + + + + + + + + ); From 313d06fee4db16d8411928c90ad7573406af388d Mon Sep 17 00:00:00 2001 From: robo-mop Date: Wed, 13 Sep 2023 17:50:33 +0530 Subject: [PATCH 21/23] Use `animateFrom` to show modal --- src/client/lazy-app/Modal/index.tsx | 16 ++++++++++------ src/client/lazy-app/util/index.ts | 11 +++++++++++ 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/src/client/lazy-app/Modal/index.tsx b/src/client/lazy-app/Modal/index.tsx index 49d376d72..5429c31a6 100644 --- a/src/client/lazy-app/Modal/index.tsx +++ b/src/client/lazy-app/Modal/index.tsx @@ -3,7 +3,7 @@ import * as style from './style.css'; import 'add-css:./style.css'; import { linkRef } from 'shared/prerendered-app/util'; import { cleanSet } from '../util/clean-modify'; -import { animateTo } from '../util'; +import { animateFrom, animateTo } from '../util'; import { RoundedCrossIcon } from '../icons'; interface Props { @@ -45,11 +45,15 @@ export default class Modal extends Component { // animate modal::backdrop // some browsers don't support ::backdrop, catch those errors try { - animateTo(this.dialogElement, [{ opacity: 0 }, { opacity: 1 }], { - duration: 250, - easing: 'ease', - pseudoElement: '::backdrop', - }); + animateFrom( + this.dialogElement, + { opacity: 0 }, + { + duration: 250, + easing: 'ease', + pseudoElement: '::backdrop', + }, + ); } catch (e) {} } diff --git a/src/client/lazy-app/util/index.ts b/src/client/lazy-app/util/index.ts index 8fc020c03..9a7a0fbc1 100644 --- a/src/client/lazy-app/util/index.ts +++ b/src/client/lazy-app/util/index.ts @@ -28,6 +28,17 @@ export function animateTo( return anim; } +export function animateFrom( + element: HTMLElement, + from: PropertyIndexedKeyframes, + options: KeyframeAnimationOptions, +) { + return element.animate( + { ...from, offset: 0 }, + { ...options, fill: 'backwards' }, + ); +} + /** If render engine is Safari */ export const isSafari = /Safari\//.test(navigator.userAgent) && From 84c19ca5c9e3bbb58b9e3fe33dba4377b5e956ed Mon Sep 17 00:00:00 2001 From: robo-mop Date: Wed, 13 Sep 2023 17:51:16 +0530 Subject: [PATCH 22/23] (REMOVE) Update modal demo --- src/features/encoders/mozJPEG/client/index.tsx | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/features/encoders/mozJPEG/client/index.tsx b/src/features/encoders/mozJPEG/client/index.tsx index 36684b5c6..db37f38a0 100644 --- a/src/features/encoders/mozJPEG/client/index.tsx +++ b/src/features/encoders/mozJPEG/client/index.tsx @@ -31,8 +31,8 @@ function sampleContent() {

Test Suite (h1)

Let's say, hypothetically, this paragraph links to{' '} - WASSUP}> - Trellis Multipass + +

WASSUP

. Lorem ipsum dolor sit amet consectetur adipisicing elit. Quaerat ad maiores iure suscipit numquam voluptate. @@ -163,8 +163,8 @@ export class Options extends Component { value={options.quality} onInput={this.onChange} > - - Quality: + + {sampleContent()}
@@ -194,7 +194,12 @@ export class Options extends Component { {options.color_space === MozJpegColorSpace.YCbCr ? (