mui text field, standard variation

This commit is contained in:
Sebastian 2022-03-18 17:52:46 -03:00
parent 98761a2b8d
commit 65eb64cd07
No known key found for this signature in database
GPG Key ID: BE4FF68352439FC1
21 changed files with 1356 additions and 186 deletions

View File

@ -37,9 +37,6 @@
"@babel/plugin-transform-react-jsx-source": "^7.12.13",
"@babel/preset-typescript": "^7.13.0",
"@gnu-taler/pogen": "workspace:*",
"@types/chai": "^4.3.0",
"chai": "^4.3.6",
"polished": "^4.1.4",
"@linaria/babel-preset": "3.0.0-beta.4",
"@linaria/core": "3.0.0-beta.4",
"@linaria/react": "3.0.0-beta.4",
@ -57,14 +54,17 @@
"@storybook/preact": "6.4.18",
"@testing-library/preact": "^2.0.1",
"@testing-library/preact-hooks": "^1.1.0",
"@types/chai": "^4.3.0",
"@types/chrome": "0.0.176",
"@types/history": "^4.7.8",
"@types/mocha": "^9.0.0",
"@types/node": "^17.0.8",
"babel-loader": "^8.2.3",
"babel-plugin-transform-react-jsx": "^6.24.1",
"chai": "^4.3.6",
"mocha": "^9.2.0",
"nyc": "^15.1.0",
"polished": "^4.1.4",
"preact-cli": "^3.3.5",
"preact-render-to-string": "^5.1.19",
"rimraf": "^3.0.2",

View File

@ -0,0 +1,133 @@
/*
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 { Button } from "./Button";
import { Fragment, h } from "preact";
import DeleteIcon from "../../static/img/delete_24px.svg";
import SendIcon from "../../static/img/send_24px.svg";
import { styled } from "@linaria/react";
export default {
title: "mui/button",
component: Button,
};
const Stack = styled.div`
display: flex;
flex-direction: column;
`;
export const BasicExample = () => (
<Fragment>
<Stack>
<Button size="small" variant="text">
Text
</Button>
<Button size="small" variant="contained">
Contained
</Button>
<Button size="small" variant="outlined">
Outlined
</Button>
</Stack>
<Stack>
<Button variant="text">Text</Button>
<Button variant="contained">Contained</Button>
<Button variant="outlined">Outlined</Button>
</Stack>
<Stack>
<Button size="large" variant="text">
Text
</Button>
<Button size="large" variant="contained">
Contained
</Button>
<Button size="large" variant="outlined">
Outlined
</Button>
</Stack>
</Fragment>
);
export const Others = () => (
<Fragment>
<p>colors</p>
<Stack>
<Button color="secondary">Secondary</Button>
<Button variant="contained" color="success">
Success
</Button>
<Button variant="outlined" color="error">
Error
</Button>
</Stack>
<p>disabled</p>
<Stack>
<Button disabled variant="text">
Text
</Button>
<Button disabled variant="contained">
Contained
</Button>
<Button disabled variant="outlined">
Outlined
</Button>
</Stack>
</Fragment>
);
export const WithIcons = () => (
<Fragment>
<Stack>
<Button variant="outlined" size="small" startIcon={DeleteIcon}>
Delete
</Button>
<Button variant="contained" size="small" endIcon={SendIcon}>
Send
</Button>
<Button variant="text" size="small" endIcon={SendIcon}>
Send
</Button>
</Stack>
<Stack>
<Button variant="outlined" startIcon={DeleteIcon}>
Delete
</Button>
<Button variant="contained" endIcon={SendIcon}>
Send
</Button>
<Button variant="text" endIcon={SendIcon}>
Send
</Button>
</Stack>
<Stack>
<Button variant="outlined" size="large" startIcon={DeleteIcon}>
Delete
</Button>
<Button variant="contained" size="large" endIcon={SendIcon}>
Send
</Button>
<Button variant="text" size="large" endIcon={SendIcon}>
Send
</Button>
</Stack>
</Fragment>
);

View File

@ -1,6 +1,6 @@
import { ComponentChildren, h, VNode } from "preact";
import { css } from "@linaria/core";
import { theme, ripple } from "./style";
import { theme, ripple, Colors } from "./style";
import { alpha } from "./colors/manipulation";
interface Props {
@ -12,9 +12,9 @@ interface Props {
fullWidth?: boolean;
href?: string;
size?: "small" | "medium" | "large";
startIcon?: VNode;
startIcon?: VNode | string;
variant?: "contained" | "outlined" | "text";
color?: "primary" | "secondary" | "success" | "error" | "info" | "warning";
color?: Colors;
onClick?: () => void;
}
@ -28,7 +28,7 @@ const baseStyle = css`
outline: 0;
border: 0;
margin: 0;
border-radius: 0;
/* border-radius: 0; */
padding: 0;
cursor: pointer;
user-select: none;
@ -50,6 +50,17 @@ const button = css`
color: ${theme.palette.action.disabled};
}
`;
const colorIconVariant = {
outlined: css`
background-color: var(--color-main);
`,
contained: css`
background-color: var(--color-contrastText);
`,
text: css`
background-color: var(--color-main);
`,
};
const colorVariant = {
outlined: css`
@ -90,6 +101,47 @@ const colorVariant = {
`,
};
const sizeIconVariant = {
outlined: {
small: css`
padding: 3px;
font-size: ${theme.pxToRem(7)};
`,
medium: css`
padding: 5px;
`,
large: css`
padding: 7px;
font-size: ${theme.pxToRem(10)};
`,
},
contained: {
small: css`
padding: 4px;
font-size: ${theme.pxToRem(13)};
`,
medium: css`
padding: 6px;
`,
large: css`
padding: 8px;
font-size: ${theme.pxToRem(10)};
`,
},
text: {
small: css`
padding: 4px;
font-size: ${theme.pxToRem(13)};
`,
medium: css`
padding: 6px;
`,
large: css`
padding: 8px;
font-size: ${theme.pxToRem(15)};
`,
},
};
const sizeVariant = {
outlined: {
small: css`
@ -162,12 +214,18 @@ export function Button({
css`
margin-right: 8px;
margin-left: -4px;
mask: var(--image) no-repeat center;
`,
colorIconVariant[variant],
sizeIconVariant[variant][size],
style,
].join(" ")}
>
{sip}
</span>
style={{
"--image": `url("${sip}")`,
"--color-main": theme.palette[color].main,
"--color-contrastText": theme.palette[color].contrastText,
}}
/>
);
const endIcon = eip && (
<span
@ -175,12 +233,19 @@ export function Button({
css`
margin-right: -4px;
margin-left: 8px;
mask: var(--image) no-repeat center;
`,
colorIconVariant[variant],
sizeIconVariant[variant][size],
style,
].join(" ")}
>
{eip}
</span>
style={{
"--image": `url("${eip}")`,
"--color-main": theme.palette[color].main,
"--color-contrastText": theme.palette[color].contrastText,
"--color-dark": theme.palette[color].dark,
}}
/>
);
return (
<button
@ -196,8 +261,8 @@ export function Button({
].join(" ")}
style={{
"--color-main": theme.palette[color].main,
"--color-main-alpha-half": alpha(theme.palette[color].main, 0.5),
"--color-contrastText": theme.palette[color].contrastText,
"--color-main-alpha-half": alpha(theme.palette[color].main, 0.5),
"--color-dark": theme.palette[color].dark,
"--color-main-alpha-opacity": alpha(
theme.palette[color].main,

View File

@ -0,0 +1,108 @@
/*
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 { styled } from "@linaria/react";
import { Fragment, h } from "preact";
import { useState } from "preact/hooks";
import { TextField, Props } from "./TextField";
export default {
title: "mui/TextField",
component: TextField,
};
const Container = styled.div`
display: flex;
flex-direction: column;
& > * {
margin: 20px;
}
`;
const BasicExample = (variant: Props["variant"]) => {
const [value, onChange] = useState("");
return (
<Container>
<TextField variant={variant} label="Name" {...{ value, onChange }} />
<TextField
variant={variant}
type="password"
label="Password"
{...{ value, onChange }}
/>
<TextField
disabled
variant={variant}
label="Country"
helperText="this is disabled"
value="disabled"
/>
<TextField
error
variant={variant}
label="Something"
{...{ value, onChange }}
/>
<TextField
error
disabled
variant={variant}
label="Disabled and Error"
value="disabled with error"
helperText="this field has an error"
/>
<TextField
variant={variant}
required
label="Name"
{...{ value, onChange }}
helperText="this field is required"
/>
</Container>
);
};
export const Standard = () => BasicExample("standard");
export const Filled = () => BasicExample("filled");
export const Outlined = () => BasicExample("outlined");
export const Color = () => (
<Container>
<TextField
variant="standard"
label="Outlined secondary"
color="secondary"
focused
/>
<TextField
label="Filled success"
variant="standard"
color="success"
focused
/>
<TextField
label="Standard warning"
variant="standard"
color="warning"
focused
/>
</Container>
);

View File

@ -0,0 +1,69 @@
import { ComponentChildren, h, VNode } from "preact";
import { FormControl } from "./input/FormControl";
import { FormHelperText } from "./input/FormHelperText";
import { InputFilled } from "./input/InputFilled";
import { InputLabel } from "./input/InputLabel";
import { InputOutlined } from "./input/InputOutlined";
import { InputStandard } from "./input/InputStandard";
import { SelectFilled } from "./input/SelectFilled";
import { SelectOutlined } from "./input/SelectOutlined";
import { SelectStandard } from "./input/SelectStandard";
import { Colors } from "./style";
export interface Props {
autoComplete?: string;
autoFocus?: boolean;
color?: Colors;
disabled?: boolean;
error?: boolean;
fullWidth?: boolean;
helperText?: VNode | string;
id?: string;
label?: VNode | string;
margin?: "dense" | "normal" | "none";
maxRows?: number;
minRows?: number;
multiline?: boolean;
onChange?: (s: string) => void;
placeholder?: string;
required?: boolean;
focused?: boolean;
rows?: number;
select?: boolean;
type?: string;
value?: string;
variant?: "filled" | "outlined" | "standard";
children?: ComponentChildren;
}
export function TextField({
label,
select,
helperText,
children,
variant = "standard",
...props
}: Props): VNode {
// htmlFor={id} id={inputLabelId}
const Input = select ? selectVariant[variant] : inputVariant[variant];
// console.log("variant", Input);
return (
<FormControl {...props}>
{label && <InputLabel>{label}</InputLabel>}
<Input {...props}>{children}</Input>
{helperText && <FormHelperText>{helperText}</FormHelperText>}
</FormControl>
);
}
const inputVariant = {
standard: InputStandard,
filled: InputFilled,
outlined: InputOutlined,
};
const selectVariant = {
standard: SelectStandard,
filled: SelectFilled,
outlined: SelectOutlined,
};

View File

@ -0,0 +1,156 @@
import { css } from "@linaria/core";
import { ComponentChildren, createContext, h } from "preact";
import { useContext, useState } from "preact/hooks";
import { Colors } from "../style";
export interface Props {
color: Colors;
disabled: boolean;
error: boolean;
focused: boolean;
fullWidth: boolean;
hiddenLabel: boolean;
required: boolean;
variant: "filled" | "outlined" | "standard";
margin: "none" | "normal" | "dense";
size: "medium" | "small";
children: ComponentChildren;
}
export const root = css`
display: inline-flex;
flex-direction: column;
position: relative;
min-width: 0px;
padding: 0px;
margin: 0px;
border: 0px;
vertical-align: top;
`;
const marginVariant = {
none: "",
normal: css`
margin-top: 16px;
margin-bottom: 8px;
`,
dense: css`
margin-top: 8px;
margin-bottom: 4px;
`,
};
const fullWidthStyle = css`
width: 100%;
`;
export function FormControl({
color = "primary",
disabled = false,
error = false,
focused: visuallyFocused,
fullWidth = false,
hiddenLabel = false,
margin = "none",
required = false,
size = "medium",
variant = "standard",
children,
}: Partial<Props>) {
const [filled, setFilled] = useState(false);
const [focusedState, setFocused] = useState(false);
const focused =
visuallyFocused !== undefined && !disabled ? visuallyFocused : focusedState;
const value: FCCProps = {
color,
disabled,
error,
filled,
focused,
fullWidth,
hiddenLabel,
size,
onBlur: () => {
setFocused(false);
},
onEmpty: () => {
setFilled(false);
},
onFilled: () => {
setFilled(true);
},
onFocus: () => {
setFocused(true);
},
required,
variant,
};
return (
<div
class={[
root,
marginVariant[margin],
fullWidth ? fullWidthStyle : "",
].join(" ")}
>
<FormControlContext.Provider value={value}>
{children}
</FormControlContext.Provider>
</div>
);
}
export interface FCCProps {
// adornedStart,
// setAdornedStart,
color: Colors;
disabled: boolean;
error: boolean;
filled: boolean;
focused: boolean;
fullWidth: boolean;
hiddenLabel: boolean;
size: "medium" | "small";
onBlur: () => void;
onEmpty: () => void;
onFilled: () => void;
onFocus: () => void;
// registerEffect,
required: boolean;
variant: "filled" | "outlined" | "standard";
}
export const FormControlContext = createContext<FCCProps | null>(null);
const defaultContextValue: FCCProps = {
color: "primary",
disabled: false,
error: false,
filled: false,
focused: false,
fullWidth: false,
hiddenLabel: false,
size: "medium",
onBlur: () => {},
onEmpty: () => {},
onFilled: () => {},
onFocus: () => {},
required: false,
variant: "outlined",
};
function withoutUndefinedProperties(obj: any) {
return Object.keys(obj).reduce((acc, key) => {
const _acc: any = acc;
if (obj[key] !== undefined) _acc[key] = obj[key];
return _acc;
}, {});
}
export function useFormControl(props: Partial<FCCProps> = {}): FCCProps {
const ctx = useContext(FormControlContext);
const cleanedProps = withoutUndefinedProperties(props);
if (!ctx) return { ...defaultContextValue, ...cleanedProps };
return { ...ctx, ...cleanedProps };
}

View File

@ -0,0 +1,54 @@
import { css } from "@linaria/core";
import { ComponentChildren, h } from "preact";
import { theme } from "../style";
import { useFormControl } from "./FormControl";
const root = css`
color: ${theme.palette.text.secondary};
text-align: left;
margin-top: 3px;
margin-bottom: 0px;
margin-right: 0px;
margin-left: 0px;
`;
const disabledStyle = css`
color: ${theme.palette.text.disabled};
`;
const errorStyle = css`
color: ${theme.palette.error.main};
`;
const sizeSmallStyle = css`
margin-top: 4px;
`;
const containedStyle = css`
margin-right: 14px;
margin-left: 14px;
`;
interface Props {
disabled?: boolean;
error?: boolean;
filled?: boolean;
focused?: boolean;
margin?: "dense";
required?: boolean;
children: ComponentChildren;
}
export function FormHelperText({ children, ...props }: Props) {
const fcs = useFormControl(props);
const contained = fcs.variant === "filled" || fcs.variant === "outlined";
return (
<p
class={[
root,
theme.typography.caption,
fcs.disabled && disabledStyle,
fcs.error && errorStyle,
fcs.size === "small" && sizeSmallStyle,
contained && containedStyle,
].join(" ")}
>
{children}
</p>
);
}

View File

@ -0,0 +1,67 @@
import { css } from "@linaria/core";
import { ComponentChildren, h } from "preact";
import { Colors, theme } from "../style";
import { useFormControl } from "./FormControl";
export interface Props {
class?: string;
disabled?: boolean;
error?: boolean;
filled?: boolean;
focused?: boolean;
required?: boolean;
color?: Colors;
children?: ComponentChildren;
}
const root = css`
color: ${theme.palette.text.secondary};
line-height: 1.4375em;
padding: 0px;
position: relative;
&[data-focused] {
color: var(--color-main);
}
&[data-disabled] {
color: ${theme.palette.text.disabled};
}
&[data-error] {
color: ${theme.palette.error.main};
}
`;
export function FormLabel({
disabled,
error,
filled,
focused,
required,
color,
class: _class,
children,
...rest
}: Props) {
const fcs = useFormControl({
disabled,
error,
filled,
focused,
required,
color,
});
return (
<label
data-focused={fcs.focused}
data-error={fcs.error}
data-disabled={fcs.disabled}
class={[_class, root, theme.typography.body1].join(" ")}
{...rest}
style={{
"--color-main": theme.palette[fcs.color].main,
}}
>
{children}
{fcs.required && <span data-error={fcs.error}>&thinsp;{"*"}</span>}
</label>
);
}

View File

@ -0,0 +1,258 @@
import { css } from "@linaria/core";
import { h, JSX } from "preact";
import { useEffect, useLayoutEffect, useState } from "preact/hooks";
import { theme } from "../style";
import { FormControlContext, useFormControl } from "./FormControl";
const rootStyle = css`
color: ${theme.palette.text.primary};
line-height: 1.4375em;
box-sizing: border-box;
position: relative;
cursor: text;
display: inline-flex;
align-items: center;
`;
const rootDisabledStyle = css`
color: ${theme.palette.text.disabled};
cursor: default;
`;
const rootMultilineStyle = css`
padding: 4px 0 5px;
`;
const fullWidthStyle = css`
width: "100%";
`;
export function InputBaseRoot({
class: _class,
disabled,
error,
multiline,
focused,
fullWidth,
children,
}: any) {
const fcs = useFormControl({});
return (
<div
data-disabled={disabled}
data-focused={focused}
data-error={error}
class={[
_class,
rootStyle,
theme.typography.body1,
disabled && rootDisabledStyle,
multiline && rootMultilineStyle,
fullWidth && fullWidthStyle,
].join(" ")}
style={{
"--color-main": theme.palette[fcs.color].main,
}}
>
{children}
</div>
);
}
const componentStyle = css`
font: inherit;
letter-spacing: inherit;
color: currentColor;
padding: 4px 0 5px;
border: 0px;
box-sizing: content-box;
background: none;
height: 1.4375em;
margin: 0px;
-webkit-tap-highlight-color: transparent;
display: block;
min-width: 0px;
width: 100%;
animation-name: "auto-fill-cancel";
animation-duration: 10ms;
@keyframes auto-fill {
from {
display: block;
}
}
@keyframes auto-fill-cancel {
from {
display: block;
}
}
&::placeholder {
color: "currentColor";
opacity: ${theme.palette.mode === "light" ? 0.42 : 0.5};
transition: ${theme.transitions.create("opacity", {
duration: theme.transitions.duration.shorter,
})};
}
&:focus {
outline: 0;
}
&:invalid {
box-shadow: none;
}
&::-webkit-search-decoration {
-webkit-appearance: none;
}
&:-webkit-autofill {
animation-duration: 5000s;
animation-name: auto-fill;
}
`;
const componentDisabledStyle = css`
opacity: 1;
--webkit-text-fill-color: ${theme.palette.text.disabled};
`;
const componentSmallStyle = css`
padding-top: 1px;
`;
const componentMultilineStyle = css`
height: auto;
resize: none;
padding: 0px;
padding-top: 0px;
`;
const searchStyle = css`
-moz-appearance: textfield;
-webkit-appearance: textfield;
`;
export function InputBaseComponent({
disabled,
size,
multiline,
type,
...props
}: any) {
return (
<input
disabled={disabled}
type={type}
class={[
componentStyle,
disabled && componentDisabledStyle,
size === "small" && componentSmallStyle,
multiline && componentMultilineStyle,
type === "search" && searchStyle,
].join(" ")}
{...props}
/>
);
}
export function InputBase({
Root = InputBaseRoot,
Input,
onChange,
name,
placeholder,
readOnly,
onKeyUp,
onKeyDown,
rows,
type = "text",
value,
onClick,
...props
}: any) {
const fcs = useFormControl(props);
// const [focused, setFocused] = useState(false);
useLayoutEffect(() => {
if (value && value !== "") {
fcs.onFilled();
} else {
fcs.onEmpty();
}
}, [value]);
const handleFocus = (event: JSX.TargetedFocusEvent<EventTarget>) => {
// Fix a bug with IE11 where the focus/blur events are triggered
// while the component is disabled.
if (fcs.disabled) {
event.stopPropagation();
return;
}
// if (onFocus) {
// onFocus(event);
// }
// if (inputPropsProp.onFocus) {
// inputPropsProp.onFocus(event);
// }
fcs.onFocus();
};
const handleBlur = () => {
// if (onBlur) {
// onBlur(event);
// }
// if (inputPropsProp.onBlur) {
// inputPropsProp.onBlur(event);
// }
fcs.onBlur();
};
const handleChange = (
event: JSX.TargetedEvent<HTMLElement & { value?: string }>,
) => {
// if (inputPropsProp.onChange) {
// inputPropsProp.onChange(event, ...args);
// }
// Perform in the willUpdate
if (onChange) {
onChange(event.currentTarget.value);
}
};
const handleClick = (
event: JSX.TargetedMouseEvent<HTMLElement & { value?: string }>,
) => {
// if (inputRef.current && event.currentTarget === event.target) {
// inputRef.current.focus();
// }
if (onClick) {
onClick(event.currentTarget.value);
}
};
if (!Input) {
Input = props.multiline ? TextareaAutoSize : InputBaseComponent;
}
return (
<Root {...fcs} onClick={handleClick}>
<FormControlContext.Provider value={null}>
<Input
aria-invalid={fcs.error}
// aria-describedby={}
disabled={fcs.disabled}
name={name}
placeholder={placeholder}
readOnly={readOnly}
required={fcs.required}
rows={rows}
value={value}
onKeyDown={onKeyDown}
onKeyUp={onKeyUp}
type={type}
onChange={handleChange}
onBlur={handleBlur}
onFocus={handleFocus}
/>
</FormControlContext.Provider>
</Root>
);
}
export function TextareaAutoSize() {
return <input onClick={(e) => null} />;
}

View File

@ -0,0 +1,5 @@
import { h, VNode } from "preact";
export function InputFilled(): VNode {
return <div />;
}

View File

@ -0,0 +1,98 @@
import { css } from "@linaria/core";
import { ComponentChildren, h } from "preact";
import { Colors, theme } from "../style";
import { useFormControl } from "./FormControl";
import { FormLabel } from "./FormLabel";
const root = css`
display: block;
transform-origin: top left;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 100%;
&[data-form-control] {
position: absolute;
left: 0px;
top: 0px;
transform: translate(0, 20px) scale(1);
}
&[data-size="small"] {
transform: translate(0, 17px) scale(1);
}
&[data-shrink] {
transform: translate(0, -1.5px) scale(0.75);
transform-origin: top left;
max-width: 133%;
}
&:not([data-disable-animation]) {
transition: ${theme.transitions.create(
["color", "transform", "max-width"],
{
duration: theme.transitions.duration.shorter,
easing: theme.transitions.easing.easeOut,
},
)};
}
&[data-variant="filled"] {
z-index: 1;
pointer-events: none;
transform: translate(12px, 16px) scale(1);
max-width: calc(100% - 24px);
&[data-size="small"] {
transform: translate(12px, 13px) scale(1);
}
&[data-shrink] {
user-select: none;
pointer-events: auto;
transform: translate(12px, 7px) scale(0.75);
max-width: calc(133% - 24px);
&[data-size="small"] {
transform: translate(12px, 4px) scale(0.75);
}
}
}
&[data-variant="outlined"] {
z-index: 1;
pointer-events: none;
transform: translate(14px, 16px) scale(1);
max-width: calc(100% - 24px);
&[data-size="small"] {
transform: translate(14px, 9px) scale(1);
}
&[data-shrink] {
user-select: none;
pointer-events: auto;
transform: translate(14px, -9px) scale(0.75);
max-width: calc(133% - 24px);
}
}
`;
interface InputLabelProps {
color: Colors;
disableAnimation: boolean;
disabled: boolean;
error: boolean;
focused: boolean;
margin: boolean;
required: boolean;
shrink: boolean;
variant: "filled" | "outlined" | "standard";
children: ComponentChildren;
}
export function InputLabel(props: Partial<InputLabelProps>) {
const fcs = useFormControl(props);
return (
<FormLabel
data-form-control={!!fcs}
data-size={fcs.size}
data-shrink={props.shrink || fcs.filled || fcs.focused}
data-disable-animation={props.disableAnimation}
data-variant={fcs.variant}
class={root}
{...props}
/>
);
}

View File

@ -0,0 +1,5 @@
import { h, VNode } from "preact";
export function InputOutlined(): VNode {
return <div />;
}

View File

@ -0,0 +1,124 @@
import { css } from "@linaria/core";
import { h, VNode } from "preact";
import { Colors, theme } from "../style";
import { useFormControl } from "./FormControl";
import { InputBase, InputBaseComponent, InputBaseRoot } from "./InputBase";
export interface Props {
autoComplete?: string;
autoFocus?: boolean;
color?: Colors;
defaultValue?: string;
disabled?: boolean;
disableUnderline?: boolean;
endAdornment?: VNode;
error?: boolean;
fullWidth?: boolean;
id?: string;
margin?: "dense" | "normal" | "none";
maxRows?: number;
minRows?: number;
multiline?: boolean;
name?: string;
onChange?: (s: string) => void;
placeholder?: string;
readOnly?: boolean;
required?: boolean;
rows?: number;
startAdornment?: VNode;
type?: string;
value?: string;
}
export function InputStandard({
type = "text",
multiline,
...props
}: Props): VNode {
const fcs = useFormControl(props);
return (
<InputBase
Root={Root}
Input={Input}
fullWidth={fcs.fullWidth}
multiline={multiline}
type={type}
{...props}
/>
);
}
const rootStyle = css`
position: relative;
`;
const formControlStyle = css`
label + & {
margin-top: 16px;
}
`;
const underlineStyle = css`
&:after {
border-bottom: 2px solid var(--color-main);
left: 0px;
bottom: 0px;
content: "";
position: absolute;
right: 0px;
transform: scaleX(0);
transition: ${theme.transitions.create("transform", {
duration: theme.transitions.duration.shorter,
easing: theme.transitions.easing.easeOut,
})};
pointer-events: none;
}
&[data-focused]:after {
transform: scaleX(1);
}
&[data-error]:after {
border-bottom-color: ${theme.palette.error.main};
transform: scaleY(1);
}
&:before {
border-bottom: 1px solid
${theme.palette.mode === "light"
? "rgba(0, 0, 0, 0.42)"
: "rgba(255, 255, 255, 0.7)"};
left: 0px;
bottom: 0px;
right: 0px;
content: "\\00a0";
position: absolute;
transition: ${theme.transitions.create("border-bottom-color", {
duration: theme.transitions.duration.shorter,
})};
pointer-events: none;
}
&:hover:not([data-disabled]:before) {
border-bottom: 2px solid var(--color-main);
@media (hover: none) {
border-bottom: 1px solid
${theme.palette.mode === "light"
? "rgba(0, 0, 0, 0.42)"
: "rgba(255, 255, 255, 0.7)"};
}
}
&[data-disabled]:before {
border-bottom-style: solid;
}
`;
function Root({ disabled, focused, error, children }: any) {
return (
<InputBaseRoot
disabled={disabled}
focused={focused}
error={error}
class={[rootStyle, formControlStyle, underlineStyle].join(" ")}
>
{children}
</InputBaseRoot>
);
}
function Input(props: any) {
return <InputBaseComponent {...props} />;
}

View File

@ -0,0 +1,5 @@
import { h, VNode } from "preact";
export function SelectFilled(): VNode {
return <div />;
}

View File

@ -0,0 +1,5 @@
import { h, VNode } from "preact";
export function SelectOutlined(): VNode {
return <div />;
}

View File

@ -0,0 +1,5 @@
import { h, VNode } from "preact";
export function SelectStandard(): VNode {
return <div />;
}

View File

@ -12,6 +12,14 @@ import {
} from "./colors/constants";
import { getContrastRatio } from "./colors/manipulation";
export type Colors =
| "primary"
| "secondary"
| "success"
| "error"
| "info"
| "warning";
export function round(value: number): number {
return Math.round(value * 1e5) / 1e5;
}
@ -386,6 +394,14 @@ function createTheme() {
`,
/* just of caseAllCaps */
// button: buildVariant(fontWeightMedium, 14, 1.75, 0.4, caseAllCaps),
caption: css`
font-family: "Roboto", "Helvetica", "Arial", sans-serif;
font-weight: ${fontWeightMedium};
font-size: ${pxToRem(12)};
line-height: 1.66;
letter-spacing: ${round(0.4 / 12)}em;
`,
// caption: buildVariant(fontWeightRegular, 12, 1.66, 0.4),
// overline: buildVariant(fontWeightRegular, 12, 2.66, 1, caseAllCaps),
};

View File

@ -57,7 +57,7 @@ function main(): void {
}
}
setupI18n("en-US", strings);
setupI18n("en", strings);
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", main);

View File

@ -73,7 +73,7 @@ function main(): void {
}
}
setupI18n("en-US", strings);
setupI18n("en", strings);
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", main);
@ -102,7 +102,6 @@ function Application(): VNode {
return (
<TranslationProvider>
<DevContextProvider>
{({ devMode }: { devMode: boolean }) => (
<IoCProviderForRuntime>
{/* <Match/> won't work in the first render if <Router /> is not called first */}
{/* https://github.com/preactjs/preact-router/issues/415 */}
@ -167,10 +166,7 @@ function Application(): VNode {
component={TransactionPage}
goToWalletHistory={(currency?: string) => {
route(
Pages.balance_history.replace(
":currency",
currency || "",
),
Pages.balance_history.replace(":currency", currency || ""),
);
}}
/>
@ -283,7 +279,6 @@ function Application(): VNode {
</Router>
</WalletBox>
</IoCProviderForRuntime>
)}
</DevContextProvider>
</TranslationProvider>
);

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M0 0h24v24H0z" fill="none"/><path d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z"/></svg>

After

Width:  |  Height:  |  Size: 192 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M0 0h24v24H0z" fill="none"/><path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"/></svg>

After

Width:  |  Height:  |  Size: 152 B