diff options
author | Sebastian <sebasjm@gmail.com> | 2022-06-24 11:42:21 -0300 |
---|---|---|
committer | Sebastian <sebasjm@gmail.com> | 2022-06-24 11:42:21 -0300 |
commit | 50379a1d5b89822dd32618c32433cf12d1a1779e (patch) | |
tree | f6851306ed7bb39429f5336ece0151fee128631f | |
parent | b06ae62de00a536525eac342c3dcb99d45c9eb86 (diff) |
mui menu, select input inprogress
6 files changed, 376 insertions, 12 deletions
diff --git a/packages/taler-wallet-webextension/src/mui/Button.tsx b/packages/taler-wallet-webextension/src/mui/Button.tsx index 7afa3643c..a7657ae29 100644 --- a/packages/taler-wallet-webextension/src/mui/Button.tsx +++ b/packages/taler-wallet-webextension/src/mui/Button.tsx @@ -16,11 +16,11 @@ import { ComponentChildren, h, VNode, JSX } from "preact"; import { css } from "@linaria/core"; // eslint-disable-next-line import/extensions -import { theme, ripple, Colors, rippleOutlined } from "./style"; +import { theme, Colors, rippleEnabled, rippleEnabledOutlined } from "./style"; // eslint-disable-next-line import/extensions import { alpha } from "./colors/manipulation"; -const buttonBaseStyle = css` +export const buttonBaseStyle = css` display: inline-flex; align-items: center; justify-content: center; @@ -318,6 +318,7 @@ interface BaseProps extends JSX.HTMLAttributes<HTMLButtonElement> { onClick?: () => Promise<void>; containedRipple?: boolean; children?: ComponentChildren; + svg?: any; } function ButtonBase({ @@ -325,7 +326,7 @@ function ButtonBase({ children, containedRipple, onClick, - dangerouslySetInnerHTML, + svg, ...rest }: BaseProps): VNode { function doClick(): void { @@ -334,14 +335,14 @@ function ButtonBase({ const classNames = [ buttonBaseStyle, _class, - containedRipple ? ripple : rippleOutlined, + containedRipple ? rippleEnabled : rippleEnabledOutlined, ].join(" "); - if (dangerouslySetInnerHTML) { + if (svg) { return ( <button onClick={doClick} class={classNames} - dangerouslySetInnerHTML={dangerouslySetInnerHTML} + dangerouslySetInnerHTML={{ __html: svg }} {...rest} /> ); @@ -375,7 +376,7 @@ export function IconButton({ fill: currentColor; `, ].join(" ")} - dangerouslySetInnerHTML={{ __html: svg }} + svg={svg} /> ); } diff --git a/packages/taler-wallet-webextension/src/mui/Menu.stories.tsx b/packages/taler-wallet-webextension/src/mui/Menu.stories.tsx new file mode 100644 index 000000000..60e373be4 --- /dev/null +++ b/packages/taler-wallet-webextension/src/mui/Menu.stories.tsx @@ -0,0 +1,172 @@ +/* + This file is part of GNU Taler + (C) 2022 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + +/** + * + * @author Sebastian Javier Marchano (sebasjm) + */ + +import { h, VNode } from "preact"; +import { useState } from "preact/hooks"; +import { Menu, MenuItem } from "./Menu.jsx"; +import { Paper } from "./Paper.js"; + +export default { + title: "mui/menu", + component: Menu, +}; + +export const BasicExample = (): VNode => { + const [open, setOpen] = useState(false); + async function handleClose(): Promise<void> { + setOpen(false); + } + async function handleClick(): Promise<void> { + setOpen(true); + } + return ( + <Menu open={open} onClose={handleClose} onClick={handleClick}> + <MenuItem onClick={handleClose}>Profile</MenuItem> + <MenuItem onClick={handleClose}>My account</MenuItem> + <MenuItem onClick={handleClose}>Logout</MenuItem> + </Menu> + ); +}; + +import { styled } from "@linaria/react"; +// eslint-disable-next-line import/extensions +import { theme } from "./style"; +import { Typography } from "./Typography.js"; +import { Divider } from "./Divider.js"; + +const ListItemIcon = styled.div` + min-width: 36px; + color: ${theme.palette.action.active}; + flex-shrink: 0; + display: inline-flex; +`; + +const IconCut = (): VNode => ( + <svg + xmlns="http://www.w3.org/2000/svg" + viewBox="0 0 24 24" + aria-hidden="true" + focusable="false" + style={{ + width: "1em", + height: "1em", + }} + > + <path d="M9.64 7.64c.23-.5.36-1.05.36-1.64 0-2.21-1.79-4-4-4S2 3.79 2 6s1.79 4 4 4c.59 0 1.14-.13 1.64-.36L10 12l-2.36 2.36C7.14 14.13 6.59 14 6 14c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4c0-.59-.13-1.14-.36-1.64L12 14l7 7h3v-1L9.64 7.64zM6 8c-1.1 0-2-.89-2-2s.9-2 2-2 2 .89 2 2-.9 2-2 2zm0 12c-1.1 0-2-.89-2-2s.9-2 2-2 2 .89 2 2-.9 2-2 2zm6-7.5c-.28 0-.5-.22-.5-.5s.22-.5.5-.5.5.22.5.5-.22.5-.5.5zM19 3l-6 6 2 2 7-7V3z" /> + </svg> +); + +const IconCopy = (): VNode => ( + <svg + xmlns="http://www.w3.org/2000/svg" + viewBox="0 0 24 24" + aria-hidden="true" + focusable="false" + style={{ + width: "1em", + height: "1em", + }} + > + <path d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z" /> + </svg> +); + +const IconPaste = (): VNode => ( + <svg + xmlns="http://www.w3.org/2000/svg" + viewBox="0 0 24 24" + aria-hidden="true" + focusable="false" + style={{ + width: "1em", + height: "1em", + }} + > + <path d="M19 2h-4.18C14.4.84 13.3 0 12 0c-1.3 0-2.4.84-2.82 2H5c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-7 0c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zm7 18H5V4h2v3h10V4h2v16z" /> + </svg> +); + +const IconCloud = (): VNode => ( + <svg + xmlns="http://www.w3.org/2000/svg" + viewBox="0 0 24 24" + aria-hidden="true" + focusable="false" + style={{ + width: "1em", + height: "1em", + }} + > + <path d="M19.35 10.04C18.67 6.59 15.64 4 12 4 9.11 4 6.6 5.64 5.35 8.04 2.34 8.36 0 10.91 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96z" /> + </svg> +); + +const ListItemText = styled.div` + flex: 1 1 auto; + min-width: 0px; + margin-top: 4px; + margin-bottom: 4px; +`; + +export function IconMenu(): VNode { + return ( + <div style={{ width: 320 }}> + <Paper> + <ul> + <MenuItem> + <ListItemIcon> + <IconCut /> + </ListItemIcon> + <ListItemText>Cut</ListItemText> + <Typography variant="body2" /* color="text.secondary" */> + ⌘X + </Typography> + </MenuItem> + <MenuItem> + <ListItemIcon> + <IconCopy /> + </ListItemIcon> + <ListItemText>Copy</ListItemText> + <Typography variant="body2" /* color="text.secondary" */> + ⌘C + </Typography> + </MenuItem> + <MenuItem> + <ListItemIcon> + <IconPaste /> + </ListItemIcon> + <ListItemText>Paste</ListItemText> + <Typography variant="body2" /* color="text.secondary" */> + ⌘V + </Typography> + </MenuItem> + <Divider /> + <MenuItem> + <ListItemIcon> + <IconCloud /> + </ListItemIcon> + <ListItemText>Web Clipboard</ListItemText> + </MenuItem> + </ul> + </Paper> + </div> + ); +} diff --git a/packages/taler-wallet-webextension/src/mui/Menu.tsx b/packages/taler-wallet-webextension/src/mui/Menu.tsx new file mode 100644 index 000000000..941abfee4 --- /dev/null +++ b/packages/taler-wallet-webextension/src/mui/Menu.tsx @@ -0,0 +1,135 @@ +/* + This file is part of GNU Taler + (C) 2022 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ +import { css } from "@linaria/core"; +import { h, VNode, Fragment, ComponentChildren } from "preact"; +import { buttonBaseStyle } from "./Button.js"; +import { alpha } from "./colors/manipulation.js"; +import { Paper } from "./Paper.js"; +// eslint-disable-next-line import/extensions +import { Colors, ripple, theme } from "./style"; + +interface Props { + children: ComponentChildren; + onClose: () => Promise<void>; + onClick: () => Promise<void>; + open?: boolean; +} + +const menuPaper = css` + max-height: calc(100% - 96px); + -webkit-overflow-scrolling: touch; +`; + +const menuList = css` + outline: 0px; +`; + +export function Menu({ children, onClose, onClick, open }: Props): VNode { + return ( + <Popover class={menuPaper}> + <ul class={menuList}>{children}</ul> + </Popover> + ); +} + +const popoverRoot = css``; + +const popoverPaper = css` + position: absolute; + overflow-y: auto; + overflow-x: hidden; + min-width: 16px; + min-height: 16px; + max-width: calc(100% - 32px); + max-height: calc(100% - 32px); + outline: 0; +`; + +function Popover({ children }: any): VNode { + return ( + <div class={popoverRoot}> + <Paper class={popoverPaper}>{children}</Paper> + </div> + ); +} + +const root = css` + display: flex; + justify-content: flex-start; + align-items: center; + position: relative; + text-decoration: none; + min-height: 48px; + padding-top: 6px; + padding-bottom: 6px; + box-sizing: border-box; + white-space: nowrap; + appearance: none; + + &:not([data-disableGutters]) { + padding-left: 16px; + padding-right: 16px; + } + + [data-dividers] { + border-bottom: 1px solid ${theme.palette.divider}; + background-clip: padding-box; + } + &:hover { + text-decoration: none; + background-color: var(--color-main-alpha-half); + @media (hover: none) { + background-color: transparent; + } + } +`; + +export function MenuItem({ + children, + onClick, + color = "primary", +}: { + children: ComponentChildren; + onClick?: () => Promise<void>; + color?: Colors; +}): VNode { + function doClick(): void { + // if (onClick) onClick(); + return; + } + return ( + <li + onClick={doClick} + disabled={false} + role="menuitem" + class={[buttonBaseStyle, root, ripple].join(" ")} + style={{ + "--color-main": theme.palette[color].main, + "--color-dark": theme.palette[color].dark, + "--color-grey-or-dark": !color + ? theme.palette.grey.A100 + : theme.palette[color].dark, + "--color-main-alpha-half": alpha(theme.palette[color].main, 0.7), + "--color-main-alpha-opacity": alpha( + theme.palette[color].main, + theme.palette.action.hoverOpacity, + ), + }} + > + {children} + </li> + ); +} diff --git a/packages/taler-wallet-webextension/src/mui/index.stories.tsx b/packages/taler-wallet-webextension/src/mui/index.stories.tsx index ca332f4ea..b70b56698 100644 --- a/packages/taler-wallet-webextension/src/mui/index.stories.tsx +++ b/packages/taler-wallet-webextension/src/mui/index.stories.tsx @@ -24,5 +24,6 @@ import * as a3 from "./Grid.stories.js"; import * as a4 from "./Paper.stories.js"; import * as a5 from "./TextField.stories.js"; import * as a6 from "./Alert.stories.js"; +import * as a7 from "./Menu.stories.js"; -export default [a1, a3, a4, a5, a6]; +export default [a1, a3, a4, a5, a6, a7]; diff --git a/packages/taler-wallet-webextension/src/mui/input/SelectStandard.tsx b/packages/taler-wallet-webextension/src/mui/input/SelectStandard.tsx index d9d9b02ca..6fb2823c7 100644 --- a/packages/taler-wallet-webextension/src/mui/input/SelectStandard.tsx +++ b/packages/taler-wallet-webextension/src/mui/input/SelectStandard.tsx @@ -13,8 +13,46 @@ You should have received a copy of the GNU General Public License along with GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ -import { h, VNode } from "preact"; +import { css } from "@linaria/core"; +import { h, VNode, Fragment } from "preact"; -export function SelectStandard(): VNode { - return <div />; +const SelectSelect = css` + height: "auto"; + min-height: "1.4374em"; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; +`; + +const SelectIcon = css``; + +const SelectNativeInput = css` + bottom: 0px; + left: 0px; + position: "absolute"; + opacity: 0px; + pointer-events: "none"; + width: 100%; + box-sizing: border-box; +`; + +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">​</span> + ) : ( + value + )} + <input + class={SelectNativeInput} + aria-hidden + tabIndex={-1} + value={Array.isArray(value) ? value.join(",") : value} + /> + </div> + </Fragment> + ); } diff --git a/packages/taler-wallet-webextension/src/mui/style.tsx b/packages/taler-wallet-webextension/src/mui/style.tsx index 3f0a61c86..32fa412e5 100644 --- a/packages/taler-wallet-webextension/src/mui/style.tsx +++ b/packages/taler-wallet-webextension/src/mui/style.tsx @@ -63,6 +63,23 @@ export const ripple = css` transition: background 0.2s; + &:hover { + background: var(--color-main) + radial-gradient(circle, transparent 1%, var(--color-dark) 1%) + center/15000%; + } + &:active { + background-color: var(--color-main); + background-size: 100%; + transition: background 0s; + } +`; + +export const rippleEnabled = css` + background-position: center; + + transition: background 0.2s; + &:hover:enabled { background: var(--color-main) radial-gradient(circle, transparent 1%, var(--color-dark) 1%) @@ -75,7 +92,7 @@ export const ripple = css` } `; -export const rippleOutlined = css` +export const rippleEnabledOutlined = css` background-position: center; transition: background 0.2s; |