summaryrefslogtreecommitdiff
path: root/packages/taler-wallet-webextension/src/mui
diff options
context:
space:
mode:
Diffstat (limited to 'packages/taler-wallet-webextension/src/mui')
-rw-r--r--packages/taler-wallet-webextension/src/mui/Alert.stories.tsx86
-rw-r--r--packages/taler-wallet-webextension/src/mui/Alert.tsx160
-rw-r--r--packages/taler-wallet-webextension/src/mui/Button.tsx129
-rw-r--r--packages/taler-wallet-webextension/src/mui/Paper.tsx4
-rw-r--r--packages/taler-wallet-webextension/src/mui/Typography.tsx29
-rw-r--r--packages/taler-wallet-webextension/src/mui/index.stories.tsx3
-rw-r--r--packages/taler-wallet-webextension/src/mui/style.tsx4
7 files changed, 376 insertions, 39 deletions
diff --git a/packages/taler-wallet-webextension/src/mui/Alert.stories.tsx b/packages/taler-wallet-webextension/src/mui/Alert.stories.tsx
new file mode 100644
index 000000000..12b2a8625
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/mui/Alert.stories.tsx
@@ -0,0 +1,86 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 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 { css } from "@linaria/core";
+import { ComponentChildren, Fragment, h, VNode } from "preact";
+import { Alert } from "./Alert.jsx";
+
+export default {
+ title: "mui/alert",
+ component: Alert,
+};
+
+function Wrapper({ children }: { children: ComponentChildren }): VNode {
+ return (
+ <div
+ class={css`
+ & > * {
+ margin: 2em;
+ }
+ `}
+ >
+ {children}
+ </div>
+ );
+}
+
+export const BasicExample = (): VNode => (
+ <Wrapper>
+ <Alert severity="warning">this is an warning</Alert>
+ <Alert severity="error">this is an error</Alert>
+ <Alert severity="success">this is an success</Alert>
+ <Alert severity="info">this is an info</Alert>
+ </Wrapper>
+);
+
+export const WithTitle = (): VNode => (
+ <Wrapper>
+ <Alert title="Warning" severity="warning">
+ this is an warning
+ </Alert>
+ <Alert title="Error" severity="error">
+ this is an error
+ </Alert>
+ <Alert title="Success" severity="success">
+ this is an success
+ </Alert>
+ <Alert title="Info" severity="info">
+ this is an info
+ </Alert>
+ </Wrapper>
+);
+
+export const WithAction = (): VNode => (
+ <Wrapper>
+ <Alert title="Warning" severity="warning" onClose={() => alert("closed")}>
+ this is an warning
+ </Alert>
+ <Alert title="Error" severity="error" onClose={() => alert("closed")}>
+ this is an error
+ </Alert>
+ <Alert title="Success" severity="success" onClose={() => alert("closed")}>
+ this is an success
+ </Alert>
+ <Alert title="Info" severity="info" onClose={() => alert("closed")}>
+ this is an info
+ </Alert>
+ </Wrapper>
+);
diff --git a/packages/taler-wallet-webextension/src/mui/Alert.tsx b/packages/taler-wallet-webextension/src/mui/Alert.tsx
new file mode 100644
index 000000000..7d0ce55d0
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/mui/Alert.tsx
@@ -0,0 +1,160 @@
+import { css } from "@linaria/core";
+import { ComponentChildren, h, VNode } from "preact";
+// eslint-disable-next-line import/extensions
+import CloseIcon from "../svg/close_24px.svg";
+import ErrorOutlineIcon from "../svg/error_outline_outlined_24px.svg";
+import InfoOutlinedIcon from "../svg/info_outlined_24px.svg";
+import ReportProblemOutlinedIcon from "../svg/report_problem_outlined_24px.svg";
+import SuccessOutlinedIcon from "../svg/success_outlined_24px.svg";
+import { IconButton } from "./Button.js";
+// eslint-disable-next-line import/extensions
+import { darken, lighten } from "./colors/manipulation";
+import { Paper } from "./Paper.js";
+// eslint-disable-next-line import/extensions
+import { theme } from "./style";
+import { Typography } from "./Typography.js";
+
+const defaultIconMapping = {
+ success: SuccessOutlinedIcon,
+ warning: ReportProblemOutlinedIcon,
+ error: ErrorOutlineIcon,
+ info: InfoOutlinedIcon,
+};
+
+const baseStyle = css`
+ background-color: transparent;
+ display: flex;
+ padding: 6px 16px;
+`;
+
+const colorVariant = {
+ standard: css`
+ color: var(--color-light-06);
+ background-color: var(--color-background-light-09);
+ `,
+ outlined: css`
+ color: var(--color-light-06);
+ border-width: 1px;
+ border-style: solid;
+ border-color: var(--color-light);
+ `,
+ filled: css`
+ color: "#fff";
+ font-weight: ${theme.typography.fontWeightMedium};
+ background-color: var(--color-main);
+ `,
+};
+
+interface Props {
+ title?: string;
+ variant?: "filled" | "outlined" | "standard";
+ role?: string;
+ onClose?: () => void;
+ // icon: VNode;
+ severity?: "info" | "warning" | "success" | "error";
+ children: ComponentChildren;
+ icon?: boolean;
+}
+
+const getColor = theme.palette.mode === "light" ? darken : lighten;
+const getBackgroundColor = theme.palette.mode === "light" ? lighten : darken;
+
+function Icon({ svg }: { svg: VNode }): VNode {
+ return (
+ <div
+ class={css`
+ margin-right: 12px;
+ padding: 7px 0px;
+ display: flex;
+ font-size: 22px;
+ opacity: 0.9;
+ fill: currentColor;
+ `}
+ dangerouslySetInnerHTML={{ __html: svg as any }}
+ ></div>
+ );
+}
+
+function Action({ children }: { children: ComponentChildren }): VNode {
+ return (
+ <div
+ class={css`
+ display: flex;
+ align-items: flex-start;
+ padding: 4px 0px 0px 16px;
+ margin-left: auto;
+ margin-right: -8px;
+ `}
+ >
+ {children}
+ </div>
+ );
+}
+
+function Message({
+ title,
+ children,
+}: {
+ title?: string;
+ children: ComponentChildren;
+}): VNode {
+ return (
+ <div
+ class={css`
+ padding: 8px 0px;
+ width: 100%;
+ `}
+ >
+ {title && (
+ <Typography
+ class={css`
+ font-weight: ${theme.typography.fontWeightMedium};
+ `}
+ gutterBottom
+ >
+ {title}
+ </Typography>
+ )}
+ {children}
+ </div>
+ );
+}
+
+export function Alert({
+ variant = "standard",
+ severity = "success",
+ title,
+ children,
+ icon,
+ onClose,
+ ...rest
+}: Props): VNode {
+ return (
+ <Paper
+ class={[
+ theme.typography.body2,
+ baseStyle,
+ severity && colorVariant[variant],
+ ].join(" ")}
+ style={{
+ "--color-light-06": getColor(theme.palette[severity].light, 0.6),
+ "--color-background-light-09": getBackgroundColor(
+ theme.palette[severity].light,
+ 0.9,
+ ),
+ "--color-main": theme.palette[severity].main,
+ "--color-light": theme.palette[severity].light,
+ // ...(style as any),
+ }}
+ elevation={1}
+ >
+ {icon != false ? <Icon svg={defaultIconMapping[severity]} /> : null}
+ <Message title={title}>{children}</Message>
+ {onClose && (
+ <Action>
+ <IconButton svg={CloseIcon} onClick={onClose} />
+ </Action>
+ )}
+ </Paper>
+ );
+}
diff --git a/packages/taler-wallet-webextension/src/mui/Button.tsx b/packages/taler-wallet-webextension/src/mui/Button.tsx
index 083bbea0d..451b1d48d 100644
--- a/packages/taler-wallet-webextension/src/mui/Button.tsx
+++ b/packages/taler-wallet-webextension/src/mui/Button.tsx
@@ -1,26 +1,11 @@
-import { ComponentChildren, h, VNode } from "preact";
+import { ComponentChildren, h, VNode, JSX } from "preact";
import { css } from "@linaria/core";
// eslint-disable-next-line import/extensions
import { theme, ripple, Colors } from "./style";
// eslint-disable-next-line import/extensions
import { alpha } from "./colors/manipulation";
-interface Props {
- children?: ComponentChildren;
- disabled?: boolean;
- disableElevation?: boolean;
- disableFocusRipple?: boolean;
- endIcon?: VNode;
- fullWidth?: boolean;
- href?: string;
- size?: "small" | "medium" | "large";
- startIcon?: VNode | string;
- variant?: "contained" | "outlined" | "text";
- color?: Colors;
- onClick?: () => void;
-}
-
-const baseStyle = css`
+const buttonBaseStyle = css`
display: inline-flex;
align-items: center;
justify-content: center;
@@ -30,7 +15,7 @@ const baseStyle = css`
outline: 0;
border: 0;
margin: 0;
- /* border-radius: 0; */
+ border-radius: 0;
padding: 0;
cursor: pointer;
user-select: none;
@@ -39,6 +24,21 @@ const baseStyle = css`
color: inherit;
`;
+interface Props {
+ children?: ComponentChildren;
+ disabled?: boolean;
+ disableElevation?: boolean;
+ disableFocusRipple?: boolean;
+ endIcon?: string | VNode;
+ fullWidth?: boolean;
+ href?: string;
+ size?: "small" | "medium" | "large";
+ startIcon?: VNode | string;
+ variant?: "contained" | "outlined" | "text";
+ color?: Colors;
+ onClick?: () => void;
+}
+
const button = css`
min-width: 64px;
&:hover {
@@ -54,13 +54,13 @@ const button = css`
`;
const colorIconVariant = {
outlined: css`
- background-color: var(--color-main);
+ fill: var(--color-main);
`,
contained: css`
- background-color: var(--color-contrastText);
+ fill: var(--color-contrastText);
`,
text: css`
- background-color: var(--color-main);
+ fill: var(--color-main);
`,
};
@@ -68,6 +68,7 @@ const colorVariant = {
outlined: css`
color: var(--color-main);
border: 1px solid var(--color-main-alpha-half);
+ background-color: var(--color-contrastText);
&:hover {
border: 1px solid var(--color-main);
background-color: var(--color-main-alpha-opacity);
@@ -81,7 +82,7 @@ const colorVariant = {
background-color: var(--color-main);
box-shadow: ${theme.shadows[2]};
&:hover {
- background-color: var(--color-dark);
+ background-color: var(--color-grey-or-dark);
}
&:active {
box-shadow: ${theme.shadows[8]};
@@ -186,11 +187,16 @@ const sizeVariant = {
},
};
+const fullWidthStyle = css`
+ width: 100%;
+`;
+
export function Button({
children,
disabled,
startIcon: sip,
endIcon: eip,
+ fullWidth,
variant = "text",
size = "medium",
color = "primary",
@@ -198,8 +204,8 @@ export function Button({
}: Props): VNode {
const style = css`
user-select: none;
- width: 1em;
- height: 1em;
+ width: 24px;
+ height: 24px;
display: inline-block;
fill: currentColor;
flex-shrink: 0;
@@ -222,8 +228,9 @@ export function Button({
sizeIconVariant[variant][size],
style,
].join(" ")}
+ //FIXME: check when sip can be a vnode
+ dangerouslySetInnerHTML={{ __html: sip as string }}
style={{
- "--image": `url("${sip}")`,
"--color-main": theme.palette[color].main,
"--color-contrastText": theme.palette[color].contrastText,
}}
@@ -241,8 +248,8 @@ export function Button({
sizeIconVariant[variant][size],
style,
].join(" ")}
+ dangerouslySetInnerHTML={{ __html: eip as string }}
style={{
- "--image": `url("${eip}")`,
"--color-main": theme.palette[color].main,
"--color-contrastText": theme.palette[color].contrastText,
"--color-dark": theme.palette[color].dark,
@@ -250,17 +257,17 @@ export function Button({
/>
);
return (
- <button
+ <ButtonBase
disabled={disabled}
class={[
theme.typography.button,
theme.shape.roundBorder,
- ripple,
- baseStyle,
button,
+ fullWidth && fullWidthStyle,
colorVariant[variant],
sizeVariant[variant][size],
].join(" ")}
+ onClick={onClick}
style={{
"--color-main": theme.palette[color].main,
"--color-contrastText": theme.palette[color].contrastText,
@@ -274,11 +281,75 @@ export function Button({
theme.palette.text.primary,
theme.palette.action.hoverOpacity,
),
+ "--color-grey-or-dark": !color
+ ? theme.palette.grey.A100
+ : theme.palette[color].dark,
}}
>
{startIcon}
{children}
{endIcon}
+ </ButtonBase>
+ );
+}
+
+interface BaseProps extends JSX.HTMLAttributes<HTMLButtonElement> {
+ class: string;
+ onClick?: () => void;
+ children?: ComponentChildren;
+}
+
+function ButtonBase({
+ class: _class,
+ children,
+ onClick,
+ dangerouslySetInnerHTML,
+ ...rest
+}: BaseProps): VNode {
+ function doClick(): void {
+ if (onClick) onClick();
+ }
+ const classNames = [buttonBaseStyle, _class, ripple].join(" ");
+ if (dangerouslySetInnerHTML) {
+ return (
+ <button
+ onClick={doClick}
+ class={classNames}
+ dangerouslySetInnerHTML={dangerouslySetInnerHTML}
+ {...rest}
+ />
+ );
+ }
+ return (
+ <button onClick={doClick} class={classNames} {...rest}>
+ {children}
</button>
);
}
+
+export function IconButton({
+ svg,
+ onClick,
+}: {
+ svg: any;
+ onClick?: () => void;
+}): VNode {
+ return (
+ <ButtonBase
+ onClick={onClick}
+ class={[
+ css`
+ text-align: center;
+ flex: 0 0 auto;
+ font-size: ${theme.typography.pxToRem(24)};
+ padding: 8px;
+ border-radius: 50%;
+ overflow: visible;
+ color: "inherit";
+ fill: currentColor;
+ `,
+ ].join(" ")}
+ dangerouslySetInnerHTML={{ __html: svg }}
+ />
+ );
+}
diff --git a/packages/taler-wallet-webextension/src/mui/Paper.tsx b/packages/taler-wallet-webextension/src/mui/Paper.tsx
index eeb4083d4..7d36c32d1 100644
--- a/packages/taler-wallet-webextension/src/mui/Paper.tsx
+++ b/packages/taler-wallet-webextension/src/mui/Paper.tsx
@@ -34,11 +34,14 @@ export function Paper({
square,
variant = "elevation",
children,
+ class: _class,
+ style,
...rest
}: Props): VNode {
return (
<div
class={[
+ _class,
baseStyle,
!square && theme.shape.roundBorder,
borderVariant[variant],
@@ -49,6 +52,7 @@ export function Paper({
"#fff",
getOverlayAlpha(elevation),
)}, ${alpha("#fff", getOverlayAlpha(elevation))})`,
+ ...(style as any),
}}
{...rest}
>
diff --git a/packages/taler-wallet-webextension/src/mui/Typography.tsx b/packages/taler-wallet-webextension/src/mui/Typography.tsx
index ecd2b8ec3..1610a1c66 100644
--- a/packages/taler-wallet-webextension/src/mui/Typography.tsx
+++ b/packages/taler-wallet-webextension/src/mui/Typography.tsx
@@ -1,5 +1,6 @@
import { css } from "@linaria/core";
import { ComponentChildren, h, VNode } from "preact";
+import { useTranslationContext } from "../context/translation.js";
// eslint-disable-next-line import/extensions
import { theme } from "./style";
@@ -22,10 +23,13 @@ type VariantEnum =
interface Props {
align?: "center" | "inherit" | "justify" | "left" | "right";
gutterBottom?: boolean;
+ bold?: boolean;
+ inline?: boolean;
noWrap?: boolean;
paragraph?: boolean;
variant?: VariantEnum;
- children?: ComponentChildren;
+ children: string[] | string;
+ class?: string;
}
const defaultVariantMapping = {
@@ -57,6 +61,9 @@ const gutterBottomStyle = css`
const paragraphStyle = css`
margin-bottom: 16px;
`;
+const boldStyle = css`
+ font-weight: bold;
+`;
export function Typography({
align,
@@ -64,9 +71,16 @@ export function Typography({
noWrap = false,
paragraph = false,
variant = "body1",
+ bold,
+ inline,
children,
+ class: _class,
}: Props): VNode {
- const cmp = paragraph
+ const { i18n } = useTranslationContext();
+
+ const Component = inline
+ ? "span"
+ : paragraph === true
? "p"
: defaultVariantMapping[variant as "h1"] || "span";
@@ -76,20 +90,21 @@ export function Typography({
: {
textAlign: align,
};
+
return h(
- cmp,
+ Component,
{
class: [
+ _class,
root,
noWrap && noWrapStyle,
gutterBottom && gutterBottomStyle,
paragraph && paragraphStyle,
+ bold && boldStyle,
theme.typography[variant as "button"], //FIXME: implement the rest of the typography and remove the casting
].join(" "),
- style: {
- ...alignStyle,
- },
+ style: alignStyle,
},
- children,
+ <i18n.Translate>{children}</i18n.Translate>,
);
}
diff --git a/packages/taler-wallet-webextension/src/mui/index.stories.tsx b/packages/taler-wallet-webextension/src/mui/index.stories.tsx
index 302114ed5..41b3a7bd9 100644
--- a/packages/taler-wallet-webextension/src/mui/index.stories.tsx
+++ b/packages/taler-wallet-webextension/src/mui/index.stories.tsx
@@ -23,5 +23,6 @@ import * as a1 from "./Button.stories.js";
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";
-export default [a1, a3, a4, a5];
+export default [a1, a3, a4, a5, a6];
diff --git a/packages/taler-wallet-webextension/src/mui/style.tsx b/packages/taler-wallet-webextension/src/mui/style.tsx
index 5be978794..652a71d46 100644
--- a/packages/taler-wallet-webextension/src/mui/style.tsx
+++ b/packages/taler-wallet-webextension/src/mui/style.tsx
@@ -48,11 +48,11 @@ export const ripple = css`
transition: background 0.5s;
&:hover {
- background: #47a7f5 radial-gradient(circle, transparent 1%, #47a7f5 1%)
+ background: #eeeeee radial-gradient(circle, transparent 1%, #eeeeee 1%)
center/15000%;
}
&:active {
- background-color: #6eb9f7;
+ background-color: currentColor;
background-size: 100%;
transition: background 0s;
}