mui menu, select input inprogress
This commit is contained in:
parent
b06ae62de0
commit
50379a1d5b
@ -16,11 +16,11 @@
|
|||||||
import { ComponentChildren, h, VNode, JSX } from "preact";
|
import { ComponentChildren, h, VNode, JSX } from "preact";
|
||||||
import { css } from "@linaria/core";
|
import { css } from "@linaria/core";
|
||||||
// eslint-disable-next-line import/extensions
|
// 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
|
// eslint-disable-next-line import/extensions
|
||||||
import { alpha } from "./colors/manipulation";
|
import { alpha } from "./colors/manipulation";
|
||||||
|
|
||||||
const buttonBaseStyle = css`
|
export const buttonBaseStyle = css`
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
@ -318,6 +318,7 @@ interface BaseProps extends JSX.HTMLAttributes<HTMLButtonElement> {
|
|||||||
onClick?: () => Promise<void>;
|
onClick?: () => Promise<void>;
|
||||||
containedRipple?: boolean;
|
containedRipple?: boolean;
|
||||||
children?: ComponentChildren;
|
children?: ComponentChildren;
|
||||||
|
svg?: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
function ButtonBase({
|
function ButtonBase({
|
||||||
@ -325,7 +326,7 @@ function ButtonBase({
|
|||||||
children,
|
children,
|
||||||
containedRipple,
|
containedRipple,
|
||||||
onClick,
|
onClick,
|
||||||
dangerouslySetInnerHTML,
|
svg,
|
||||||
...rest
|
...rest
|
||||||
}: BaseProps): VNode {
|
}: BaseProps): VNode {
|
||||||
function doClick(): void {
|
function doClick(): void {
|
||||||
@ -334,14 +335,14 @@ function ButtonBase({
|
|||||||
const classNames = [
|
const classNames = [
|
||||||
buttonBaseStyle,
|
buttonBaseStyle,
|
||||||
_class,
|
_class,
|
||||||
containedRipple ? ripple : rippleOutlined,
|
containedRipple ? rippleEnabled : rippleEnabledOutlined,
|
||||||
].join(" ");
|
].join(" ");
|
||||||
if (dangerouslySetInnerHTML) {
|
if (svg) {
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
onClick={doClick}
|
onClick={doClick}
|
||||||
class={classNames}
|
class={classNames}
|
||||||
dangerouslySetInnerHTML={dangerouslySetInnerHTML}
|
dangerouslySetInnerHTML={{ __html: svg }}
|
||||||
{...rest}
|
{...rest}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@ -375,7 +376,7 @@ export function IconButton({
|
|||||||
fill: currentColor;
|
fill: currentColor;
|
||||||
`,
|
`,
|
||||||
].join(" ")}
|
].join(" ")}
|
||||||
dangerouslySetInnerHTML={{ __html: svg }}
|
svg={svg}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
172
packages/taler-wallet-webextension/src/mui/Menu.stories.tsx
Normal file
172
packages/taler-wallet-webextension/src/mui/Menu.stories.tsx
Normal file
@ -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>
|
||||||
|
);
|
||||||
|
}
|
135
packages/taler-wallet-webextension/src/mui/Menu.tsx
Normal file
135
packages/taler-wallet-webextension/src/mui/Menu.tsx
Normal file
@ -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>
|
||||||
|
);
|
||||||
|
}
|
@ -24,5 +24,6 @@ import * as a3 from "./Grid.stories.js";
|
|||||||
import * as a4 from "./Paper.stories.js";
|
import * as a4 from "./Paper.stories.js";
|
||||||
import * as a5 from "./TextField.stories.js";
|
import * as a5 from "./TextField.stories.js";
|
||||||
import * as a6 from "./Alert.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];
|
||||||
|
@ -13,8 +13,46 @@
|
|||||||
You should have received a copy of the GNU General Public License along with
|
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/>
|
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 {
|
const SelectSelect = css`
|
||||||
return <div />;
|
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>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
@ -63,6 +63,23 @@ export const ripple = css`
|
|||||||
|
|
||||||
transition: background 0.2s;
|
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 {
|
&:hover:enabled {
|
||||||
background: var(--color-main)
|
background: var(--color-main)
|
||||||
radial-gradient(circle, transparent 1%, var(--color-dark) 1%)
|
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;
|
background-position: center;
|
||||||
|
|
||||||
transition: background 0.2s;
|
transition: background 0.2s;
|
||||||
|
Loading…
Reference in New Issue
Block a user