aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSebastian <sebasjm@gmail.com>2022-08-15 21:36:53 -0300
committerSebastian <sebasjm@gmail.com>2022-08-15 21:36:53 -0300
commit0798aa5cedad2a599829f2b13d4573c64a081c29 (patch)
tree191ce51f0569c34e124dd20a4a23acecc5f9eae7
parentcdc8e9afdfb93bd8a90d1e6cf0ea9aa20159e43a (diff)
modal, popover and portal for select input
-rw-r--r--packages/taler-wallet-webextension/src/mui/Modal.tsx132
-rw-r--r--packages/taler-wallet-webextension/src/mui/ModalManager.ts310
-rw-r--r--packages/taler-wallet-webextension/src/mui/Popover.tsx59
-rw-r--r--packages/taler-wallet-webextension/src/mui/Portal.tsx113
-rw-r--r--packages/taler-wallet-webextension/src/mui/TextField.stories.tsx18
-rw-r--r--packages/taler-wallet-webextension/src/mui/input/SelectStandard.tsx164
-rw-r--r--packages/taler-wallet-webextension/src/mui/style.tsx11
-rw-r--r--packages/taler-wallet-webextension/src/stories.tsx1
8 files changed, 782 insertions, 26 deletions
diff --git a/packages/taler-wallet-webextension/src/mui/Modal.tsx b/packages/taler-wallet-webextension/src/mui/Modal.tsx
new file mode 100644
index 000000000..5a30bcf26
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/mui/Modal.tsx
@@ -0,0 +1,132 @@
+import { css } from "@linaria/core";
+import { h, JSX, VNode, ComponentChildren } from "preact";
+import { useCallback, useEffect, useRef, useState } from "preact/hooks";
+// eslint-disable-next-line import/extensions
+import { alpha } from "./colors/manipulation";
+import { ModalManager } from "./ModalManager";
+import { Portal } from "./Portal.js";
+// eslint-disable-next-line import/extensions
+import { theme } from "./style";
+
+const baseStyle = css`
+ position: fixed;
+ z-index: ${theme.zIndex.modal};
+ right: 0px;
+ bottom: 0px;
+ top: 0px;
+ left: 0px;
+`;
+
+interface Props {
+ class: string;
+ children: ComponentChildren;
+ open?: boolean;
+ exited?: boolean;
+ container?: HTMLElement;
+}
+
+const defaultManager = new ModalManager();
+const manager = defaultManager;
+
+export function Modal({
+ open,
+ // exited,
+ class: _class,
+ children,
+
+ container,
+ ...rest
+}: Props): VNode {
+ const [exited, setExited] = useState(true);
+ const mountNodeRef = useRef<HTMLElement | undefined>(undefined);
+
+ const isTopModal = useCallback(
+ () => manager.isTopModal(getModal()),
+ [manager],
+ );
+
+ const handlePortalRef = useEventCallback<HTMLElement[], void>((node) => {
+ mountNodeRef.current = node;
+
+ if (!node) {
+ return;
+ }
+
+ if (open && isTopModal()) {
+ handleMounted();
+ } else {
+ ariaHidden(modalRef.current, true);
+ }
+ });
+
+ return (
+ <Portal
+ ref={handlePortalRef}
+ container={container}
+ disablePortal={disablePortal}
+ >
+ <div
+ class={[_class, baseStyle].join(" ")}
+ style={{
+ visibility: !open && exited ? "hidden" : "visible",
+ }}
+ >
+ {children}
+ </div>
+ </Portal>
+ );
+}
+
+function getOffsetTop(rect: any, vertical: any): number {
+ let offset = 0;
+
+ if (typeof vertical === "number") {
+ offset = vertical;
+ } else if (vertical === "center") {
+ offset = rect.height / 2;
+ } else if (vertical === "bottom") {
+ offset = rect.height;
+ }
+
+ return offset;
+}
+
+function getOffsetLeft(rect: any, horizontal: any): number {
+ let offset = 0;
+
+ if (typeof horizontal === "number") {
+ offset = horizontal;
+ } else if (horizontal === "center") {
+ offset = rect.width / 2;
+ } else if (horizontal === "right") {
+ offset = rect.width;
+ }
+
+ return offset;
+}
+
+function getTransformOriginValue(transformOrigin): string {
+ return [transformOrigin.horizontal, transformOrigin.vertical]
+ .map((n) => (typeof n === "number" ? `${n}px` : n))
+ .join(" ");
+}
+
+function resolveAnchorEl(anchorEl: any): any {
+ return typeof anchorEl === "function" ? anchorEl() : anchorEl;
+}
+
+function useEventCallback<Args extends unknown[], Return>(
+ fn: (...args: Args) => Return,
+): (...args: Args) => Return {
+ const ref = useRef(fn);
+ useEffect(() => {
+ ref.current = fn;
+ });
+ return useCallback(
+ (...args: Args) =>
+ // @ts-expect-error hide `this`
+ // tslint:disable-next-line:ban-comma-operator
+ (0, ref.current!)(...args),
+ [],
+ );
+}
diff --git a/packages/taler-wallet-webextension/src/mui/ModalManager.ts b/packages/taler-wallet-webextension/src/mui/ModalManager.ts
new file mode 100644
index 000000000..2894ffa7a
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/mui/ModalManager.ts
@@ -0,0 +1,310 @@
+////////////////////
+function ownerDocument(node: Node | null | undefined): Document {
+ return (node && node.ownerDocument) || document;
+}
+function ownerWindow(node: Node | undefined): Window {
+ const doc = ownerDocument(node);
+ return doc.defaultView || window;
+}
+// A change of the browser zoom change the scrollbar size.
+// Credit https://github.com/twbs/bootstrap/blob/488fd8afc535ca3a6ad4dc581f5e89217b6a36ac/js/src/util/scrollbar.js#L14-L18
+function getScrollbarSize(doc: Document): number {
+ // https://developer.mozilla.org/en-US/docs/Web/API/Window/innerWidth#usage_notes
+ const documentWidth = doc.documentElement.clientWidth;
+ return Math.abs(window.innerWidth - documentWidth);
+}
+
+/////////////////////
+
+export interface ManagedModalProps {
+ disableScrollLock?: boolean;
+}
+
+// Is a vertical scrollbar displayed?
+function isOverflowing(container: Element): boolean {
+ const doc = ownerDocument(container);
+
+ if (doc.body === container) {
+ return ownerWindow(container).innerWidth > doc.documentElement.clientWidth;
+ }
+
+ return container.scrollHeight > container.clientHeight;
+}
+
+export function ariaHidden(element: Element, show: boolean): void {
+ if (show) {
+ element.setAttribute("aria-hidden", "true");
+ } else {
+ element.removeAttribute("aria-hidden");
+ }
+}
+
+function getPaddingRight(element: Element): number {
+ return (
+ parseInt(ownerWindow(element).getComputedStyle(element).paddingRight, 10) ||
+ 0
+ );
+}
+
+function ariaHiddenSiblings(
+ container: Element,
+ mountElement: Element,
+ currentElement: Element,
+ elementsToExclude: readonly Element[] = [],
+ show: boolean,
+): void {
+ const blacklist = [mountElement, currentElement, ...elementsToExclude];
+ const blacklistTagNames = ["TEMPLATE", "SCRIPT", "STYLE"];
+
+ [].forEach.call(container.children, (element: Element) => {
+ if (
+ blacklist.indexOf(element) === -1 &&
+ blacklistTagNames.indexOf(element.tagName) === -1
+ ) {
+ ariaHidden(element, show);
+ }
+ });
+}
+
+function findIndexOf<T>(
+ items: readonly T[],
+ callback: (item: T) => boolean,
+): number {
+ let idx = -1;
+ items.some((item, index) => {
+ if (callback(item)) {
+ idx = index;
+ return true;
+ }
+ return false;
+ });
+ return idx;
+}
+
+function handleContainer(containerInfo: Container, props: ManagedModalProps) {
+ const restoreStyle: Array<{
+ /**
+ * CSS property name (HYPHEN CASE) to be modified.
+ */
+ property: string;
+ el: HTMLElement | SVGElement;
+ value: string;
+ }> = [];
+ const container = containerInfo.container;
+
+ if (!props.disableScrollLock) {
+ if (isOverflowing(container)) {
+ // Compute the size before applying overflow hidden to avoid any scroll jumps.
+ const scrollbarSize = getScrollbarSize(ownerDocument(container));
+
+ restoreStyle.push({
+ value: container.style.paddingRight,
+ property: "padding-right",
+ el: container,
+ });
+ // Use computed style, here to get the real padding to add our scrollbar width.
+ container.style.paddingRight = `${getPaddingRight(container) + scrollbarSize
+ }px`;
+
+ // .mui-fixed is a global helper.
+ const fixedElements =
+ ownerDocument(container).querySelectorAll(".mui-fixed");
+ [].forEach.call(fixedElements, (element: HTMLElement | SVGElement) => {
+ restoreStyle.push({
+ value: element.style.paddingRight,
+ property: "padding-right",
+ el: element,
+ });
+ element.style.paddingRight = `${getPaddingRight(element) + scrollbarSize
+ }px`;
+ });
+ }
+
+ // Improve Gatsby support
+ // https://css-tricks.com/snippets/css/force-vertical-scrollbar/
+ const parent = container.parentElement;
+ const containerWindow = ownerWindow(container);
+ const scrollContainer =
+ parent?.nodeName === "HTML" &&
+ containerWindow.getComputedStyle(parent).overflowY === "scroll"
+ ? parent
+ : container;
+
+ // Block the scroll even if no scrollbar is visible to account for mobile keyboard
+ // screensize shrink.
+ restoreStyle.push(
+ {
+ value: scrollContainer.style.overflow,
+ property: "overflow",
+ el: scrollContainer,
+ },
+ {
+ value: scrollContainer.style.overflowX,
+ property: "overflow-x",
+ el: scrollContainer,
+ },
+ {
+ value: scrollContainer.style.overflowY,
+ property: "overflow-y",
+ el: scrollContainer,
+ },
+ );
+
+ scrollContainer.style.overflow = "hidden";
+ }
+
+ const restore = () => {
+ restoreStyle.forEach(({ value, el, property }) => {
+ if (value) {
+ el.style.setProperty(property, value);
+ } else {
+ el.style.removeProperty(property);
+ }
+ });
+ };
+
+ return restore;
+}
+
+function getHiddenSiblings(container: Element) {
+ const hiddenSiblings: Element[] = [];
+ [].forEach.call(container.children, (element: Element) => {
+ if (element.getAttribute("aria-hidden") === "true") {
+ hiddenSiblings.push(element);
+ }
+ });
+ return hiddenSiblings;
+}
+
+interface Modal {
+ mount: Element;
+ modalRef: Element;
+}
+
+interface Container {
+ container: HTMLElement;
+ hiddenSiblings: Element[];
+ modals: Modal[];
+ restore: null | (() => void);
+}
+
+export class ModalManager {
+ private containers: Container[];
+
+ private modals: Modal[];
+
+ constructor() {
+ this.modals = [];
+ this.containers = [];
+ }
+
+ add(modal: Modal, container: HTMLElement): number {
+ let modalIndex = this.modals.indexOf(modal);
+ if (modalIndex !== -1) {
+ return modalIndex;
+ }
+
+ modalIndex = this.modals.length;
+ this.modals.push(modal);
+
+ // If the modal we are adding is already in the DOM.
+ if (modal.modalRef) {
+ ariaHidden(modal.modalRef, false);
+ }
+
+ const hiddenSiblings = getHiddenSiblings(container);
+ ariaHiddenSiblings(
+ container,
+ modal.mount,
+ modal.modalRef,
+ hiddenSiblings,
+ true,
+ );
+
+ const containerIndex = findIndexOf(
+ this.containers,
+ (item) => item.container === container,
+ );
+ if (containerIndex !== -1) {
+ this.containers[containerIndex].modals.push(modal);
+ return modalIndex;
+ }
+
+ this.containers.push({
+ modals: [modal],
+ container,
+ restore: null,
+ hiddenSiblings,
+ });
+
+ return modalIndex;
+ }
+
+ mount(modal: Modal, props: ManagedModalProps): void {
+ const containerIndex = findIndexOf(
+ this.containers,
+ (item) => item.modals.indexOf(modal) !== -1,
+ );
+ const containerInfo = this.containers[containerIndex];
+
+ if (!containerInfo.restore) {
+ containerInfo.restore = handleContainer(containerInfo, props);
+ }
+ }
+
+ remove(modal: Modal): number {
+ const modalIndex = this.modals.indexOf(modal);
+
+ if (modalIndex === -1) {
+ return modalIndex;
+ }
+
+ const containerIndex = findIndexOf(
+ this.containers,
+ (item) => item.modals.indexOf(modal) !== -1,
+ );
+ const containerInfo = this.containers[containerIndex];
+
+ containerInfo.modals.splice(containerInfo.modals.indexOf(modal), 1);
+ this.modals.splice(modalIndex, 1);
+
+ // If that was the last modal in a container, clean up the container.
+ if (containerInfo.modals.length === 0) {
+ // The modal might be closed before it had the chance to be mounted in the DOM.
+ if (containerInfo.restore) {
+ containerInfo.restore();
+ }
+
+ if (modal.modalRef) {
+ // In case the modal wasn't in the DOM yet.
+ ariaHidden(modal.modalRef, true);
+ }
+
+ ariaHiddenSiblings(
+ containerInfo.container,
+ modal.mount,
+ modal.modalRef,
+ containerInfo.hiddenSiblings,
+ false,
+ );
+ this.containers.splice(containerIndex, 1);
+ } else {
+ // Otherwise make sure the next top modal is visible to a screen reader.
+ const nextTop = containerInfo.modals[containerInfo.modals.length - 1];
+ // as soon as a modal is adding its modalRef is undefined. it can't set
+ // aria-hidden because the dom element doesn't exist either
+ // when modal was unmounted before modalRef gets null
+ if (nextTop.modalRef) {
+ ariaHidden(nextTop.modalRef, false);
+ }
+ }
+
+ return modalIndex;
+ }
+
+ isTopModal(modal: Modal): boolean {
+ return (
+ this.modals.length > 0 && this.modals[this.modals.length - 1] === modal
+ );
+ }
+}
diff --git a/packages/taler-wallet-webextension/src/mui/Popover.tsx b/packages/taler-wallet-webextension/src/mui/Popover.tsx
new file mode 100644
index 000000000..408f87987
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/mui/Popover.tsx
@@ -0,0 +1,59 @@
+import { css } from "@linaria/core";
+import { h, JSX, VNode, ComponentChildren } from "preact";
+// eslint-disable-next-line import/extensions
+import { alpha } from "./colors/manipulation";
+// eslint-disable-next-line import/extensions
+import { theme } from "./style";
+
+const baseStyle = css``;
+
+interface Props {
+ class: string;
+ children: ComponentChildren;
+}
+
+export function Popover({ class: _class, children, ...rest }: Props): VNode {
+ return (
+ <div class={[_class, baseStyle].join(" ")} style={{}} {...rest}>
+ {children}
+ </div>
+ );
+}
+
+function getOffsetTop(rect: any, vertical: any): number {
+ let offset = 0;
+
+ if (typeof vertical === "number") {
+ offset = vertical;
+ } else if (vertical === "center") {
+ offset = rect.height / 2;
+ } else if (vertical === "bottom") {
+ offset = rect.height;
+ }
+
+ return offset;
+}
+
+function getOffsetLeft(rect: any, horizontal: any): number {
+ let offset = 0;
+
+ if (typeof horizontal === "number") {
+ offset = horizontal;
+ } else if (horizontal === "center") {
+ offset = rect.width / 2;
+ } else if (horizontal === "right") {
+ offset = rect.width;
+ }
+
+ return offset;
+}
+
+function getTransformOriginValue(transformOrigin): string {
+ return [transformOrigin.horizontal, transformOrigin.vertical]
+ .map((n) => (typeof n === "number" ? `${n}px` : n))
+ .join(" ");
+}
+
+function resolveAnchorEl(anchorEl: any): any {
+ return typeof anchorEl === "function" ? anchorEl() : anchorEl;
+}
diff --git a/packages/taler-wallet-webextension/src/mui/Portal.tsx b/packages/taler-wallet-webextension/src/mui/Portal.tsx
new file mode 100644
index 000000000..828a574fd
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/mui/Portal.tsx
@@ -0,0 +1,113 @@
+import { css } from "@linaria/core";
+import { createPortal, forwardRef } from "preact/compat";
+import {
+ h,
+ JSX,
+ VNode,
+ ComponentChildren,
+ RefObject,
+ isValidElement,
+ cloneElement,
+ Fragment,
+} from "preact";
+import { Ref, useEffect, useMemo, useState } from "preact/hooks";
+// eslint-disable-next-line import/extensions
+import { alpha } from "./colors/manipulation";
+// eslint-disable-next-line import/extensions
+import { theme } from "./style";
+
+const baseStyle = css`
+ position: fixed;
+ z-index: ${theme.zIndex.modal};
+ right: 0px;
+ bottom: 0px;
+ top: 0px;
+ left: 0px;
+`;
+
+interface Props {
+ class: string;
+ children: ComponentChildren;
+ disablePortal?: boolean;
+ container?: VNode;
+}
+
+export const Portal = forwardRef(function Portal(
+ { container, disablePortal, children }: Props,
+ ref: Ref<any>,
+): VNode {
+ const [mountNode, setMountNode] = useState<HTMLElement | undefined>(
+ undefined,
+ );
+ const handleRef = useForkRef(
+ isValidElement(children) ? children.ref : null,
+ ref,
+ );
+
+ useEffect(() => {
+ if (!disablePortal) {
+ setMountNode(getContainer(container) || document.body);
+ }
+ }, [container, disablePortal]);
+
+ useEffect(() => {
+ if (mountNode && !disablePortal) {
+ setRef(ref, mountNode);
+ return () => {
+ setRef(ref, null);
+ };
+ }
+
+ return undefined;
+ }, [ref, mountNode, disablePortal]);
+
+ if (disablePortal) {
+ if (isValidElement(children)) {
+ return cloneElement(children, {
+ ref: handleRef,
+ });
+ }
+ return <Fragment>{children}</Fragment>;
+ }
+
+ return mountNode ? (
+ createPortal(<Fragment>{children}</Fragment>, mountNode)
+ ) : (
+ <Fragment />
+ );
+});
+
+function getContainer(container: any): any {
+ return typeof container === "function" ? container() : container;
+}
+
+function useForkRef<Instance>(
+ refA: React.Ref<Instance> | null | undefined,
+ refB: React.Ref<Instance> | null | undefined,
+): React.Ref<Instance> | null {
+ /**
+ * This will create a new function if the ref props change and are defined.
+ * This means react will call the old forkRef with `null` and the new forkRef
+ * with the ref. Cleanup naturally emerges from this behavior.
+ */
+ return useMemo(() => {
+ if (refA == null && refB == null) {
+ return null;
+ }
+ return (refValue) => {
+ setRef(refA, refValue);
+ setRef(refB, refValue);
+ };
+ }, [refA, refB]);
+}
+
+function setRef<T>(
+ ref: RefObject<T | null> | ((instance: T | null) => void) | null | undefined,
+ value: T | null,
+): void {
+ if (typeof ref === "function") {
+ ref(value);
+ } else if (ref) {
+ ref.current = value;
+ }
+}
diff --git a/packages/taler-wallet-webextension/src/mui/TextField.stories.tsx b/packages/taler-wallet-webextension/src/mui/TextField.stories.tsx
index fb044acbc..a409f09f0 100644
--- a/packages/taler-wallet-webextension/src/mui/TextField.stories.tsx
+++ b/packages/taler-wallet-webextension/src/mui/TextField.stories.tsx
@@ -118,20 +118,20 @@ const Multiline = (variant: Props["variant"]): VNode => {
label="Multiline"
variant={variant}
multiline
+ maxRows={4}
/>
<TextField
{...{ value, onChange }}
label="Max row 4"
variant={variant}
multiline
- maxRows={4}
+ rows={10}
/>
<TextField
{...{ value, onChange }}
label="Row 10"
variant={variant}
multiline
- rows={10}
/>
</Container>
);
@@ -145,19 +145,7 @@ export const Select = (): VNode => {
<Container>
<TextField
{...{ value, onChange }}
- label="Multiline"
- variant="standard"
- select
- />
- <TextField
- {...{ value, onChange }}
- label="Max row 4"
- variant="standard"
- select
- />
- <TextField
- {...{ value, onChange }}
- label="Row 10"
+ label="select"
variant="standard"
select
/>
diff --git a/packages/taler-wallet-webextension/src/mui/input/SelectStandard.tsx b/packages/taler-wallet-webextension/src/mui/input/SelectStandard.tsx
index 6fb2823c7..b0474a80b 100644
--- a/packages/taler-wallet-webextension/src/mui/input/SelectStandard.tsx
+++ b/packages/taler-wallet-webextension/src/mui/input/SelectStandard.tsx
@@ -15,6 +15,12 @@
*/
import { css } from "@linaria/core";
import { h, VNode, Fragment } from "preact";
+import { useRef } from "preact/hooks";
+import { Paper } from "../Paper.js";
+
+function hasValue(value: any): boolean {
+ return value != null && !(Array.isArray(value) && value.length === 0);
+}
const SelectSelect = css`
height: "auto";
@@ -36,23 +42,159 @@ const SelectNativeInput = css`
box-sizing: border-box;
`;
-export function SelectStandard({ value }: any): VNode {
+// export function SelectStandard({ value }: any): VNode {
+// return (
+// <Fragment>
+// <div class={SelectSelect} role="button">
+// {!value ? (
+// // notranslate needed while Google Translate will not fix zero-width space issue
+// <span className="notranslate">&#8203;</span>
+// ) : (
+// value
+// )}
+// <input
+// class={SelectNativeInput}
+// aria-hidden
+// tabIndex={-1}
+// value={Array.isArray(value) ? value.join(",") : value}
+// />
+// </div>
+// </Fragment>
+// );
+// }
+function isFilled(obj: any, SSR = false): boolean {
+ return (
+ obj &&
+ ((hasValue(obj.value) && obj.value !== "") ||
+ (SSR && hasValue(obj.defaultValue) && obj.defaultValue !== ""))
+ );
+}
+function isEmpty(display: any): boolean {
+ return display == null || (typeof display === "string" && !display.trim());
+}
+
+export function SelectStandard({
+ value,
+ multiple,
+ displayEmpty,
+ onBlur,
+ onChange,
+ onClose,
+ onFocus,
+ onOpen,
+ renderValue,
+ menuMinWidthState,
+}: any): VNode {
+ const inputRef = useRef(null);
+ const displayRef = useRef(null);
+
+ let display;
+ let computeDisplay = false;
+ let foundMatch = false;
+ let displaySingle;
+ const displayMultiple: any[] = [];
+ if (isFilled({ value }) || displayEmpty) {
+ if (renderValue) {
+ display = renderValue(value);
+ } else {
+ computeDisplay = true;
+ }
+ }
+ if (computeDisplay) {
+ if (multiple) {
+ if (displayMultiple.length === 0) {
+ display = null;
+ } else {
+ display = displayMultiple.reduce((output, child, index) => {
+ output.push(child);
+ if (index < displayMultiple.length - 1) {
+ output.push(", ");
+ }
+ return output;
+ }, []);
+ }
+ } else {
+ display = displaySingle;
+ }
+ }
+
+ // Avoid performing a layout computation in the render method.
+ let menuMinWidth = menuMinWidthState;
+
+ // if (!autoWidth && isOpenControlled && displayNode) {
+ // menuMinWidth = displayNode.clientWidth;
+ // }
+
+ // let tabIndex;
+ // if (typeof tabIndexProp !== "undefined") {
+ // tabIndex = tabIndexProp;
+ // } else {
+ // tabIndex = disabled ? null : 0;
+ // }
+ const update = (open: any, event: any) => {
+ if (open) {
+ if (onOpen) {
+ onOpen(event);
+ }
+ } else if (onClose) {
+ onClose(event);
+ }
+
+ // if (!isOpenControlled) {
+ // setMenuMinWidthState(autoWidth ? null : displayNode.clientWidth);
+ // setOpenState(open);
+ // }
+ };
+
+ const handleMouseDown = (event: any) => {
+ // Ignore everything but left-click
+ if (event.button !== 0) {
+ return;
+ }
+ // Hijack the default focus behavior.
+ event.preventDefault();
+ // displayRef.current.focus();
+
+ update(true, event);
+ };
return (
<Fragment>
- <div class={SelectSelect} role="button">
- {!value ? (
+ <div
+ class={css`
+ height: auto;
+ min-height: 14375em;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ overflow: hidden;
+ `}
+ >
+ {isEmpty(display) ? (
// notranslate needed while Google Translate will not fix zero-width space issue
- <span className="notranslate">&#8203;</span>
+ <span class="notranslate">&#8203;</span>
) : (
- value
+ display
)}
- <input
- class={SelectNativeInput}
- aria-hidden
- tabIndex={-1}
- value={Array.isArray(value) ? value.join(",") : value}
- />
</div>
+ <input
+ class={css`
+ bottom: 0px;
+ left: 0px;
+ position: "absolute";
+ opacity: 0;
+ pointer-events: none;
+ width: 100%;
+ box-sizing: border-box;
+ `}
+ />
+ <svg />
</Fragment>
);
}
+
+// function Popover(): VNode {
+// return;
+// }
+
+// function Menu(): VNode {
+// return <Paper></Paper>;
+// }
diff --git a/packages/taler-wallet-webextension/src/mui/style.tsx b/packages/taler-wallet-webextension/src/mui/style.tsx
index 32fa412e5..c3071b314 100644
--- a/packages/taler-wallet-webextension/src/mui/style.tsx
+++ b/packages/taler-wallet-webextension/src/mui/style.tsx
@@ -58,6 +58,16 @@ export interface Spacing {
export const theme = createTheme();
+const zIndex = {
+ mobileStepper: 1000,
+ speedDial: 1050,
+ appBar: 1100,
+ drawer: 1200,
+ modal: 1300,
+ snackbar: 1400,
+ tooltip: 1500,
+};
+
export const ripple = css`
background-position: center;
@@ -859,5 +869,6 @@ function createTheme() {
breakpoints,
spacing,
pxToRem,
+ zIndex,
};
}
diff --git a/packages/taler-wallet-webextension/src/stories.tsx b/packages/taler-wallet-webextension/src/stories.tsx
index 4a090e52e..b4c209a5f 100644
--- a/packages/taler-wallet-webextension/src/stories.tsx
+++ b/packages/taler-wallet-webextension/src/stories.tsx
@@ -223,6 +223,7 @@ function ExampleList({
e.preventDefault();
location.hash = `#${eId}`;
onSelectStory(r, eId);
+ history.pushState({}, "", `#${eId}`);
}}
>
{r.name}