mirror of
https://github.com/spacebarchat/client.git
synced 2024-11-25 03:32:54 +01:00
button component, input error changes
This commit is contained in:
parent
b3ea83a561
commit
ac6ded3387
91
src/components/Button.tsx
Normal file
91
src/components/Button.tsx
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
import styled from "styled-components";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
variant?: "primary" | "secondary" | "danger" | "success" | "warning";
|
||||||
|
outlined?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default styled.button<Props>`
|
||||||
|
background: ${(props) => {
|
||||||
|
if (props.outlined) return "transparent";
|
||||||
|
switch (props.variant) {
|
||||||
|
case "primary":
|
||||||
|
return "var(--button-primary)";
|
||||||
|
case "secondary":
|
||||||
|
return "var(--button-secondary)";
|
||||||
|
case "danger":
|
||||||
|
return "var(--button-danger)";
|
||||||
|
case "success":
|
||||||
|
return "var(--button-success)";
|
||||||
|
case "warning":
|
||||||
|
return "var(--button-warning)";
|
||||||
|
default:
|
||||||
|
return "var(--button-primary)";
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
|
||||||
|
border: ${(props) => {
|
||||||
|
if (!props.outlined) return "none";
|
||||||
|
switch (props.variant) {
|
||||||
|
case "primary":
|
||||||
|
return "1px solid var(--button-primary)";
|
||||||
|
case "secondary":
|
||||||
|
return "1px solid var(--button-secondary)";
|
||||||
|
case "danger":
|
||||||
|
return "1px solid var(--button-danger)";
|
||||||
|
case "success":
|
||||||
|
return "1px solid var(--button-success)";
|
||||||
|
case "warning":
|
||||||
|
return "1px solid var(--button-warning)";
|
||||||
|
default:
|
||||||
|
return "1px solid var(--button-primary)";
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
|
||||||
|
color: var(--text);
|
||||||
|
padding: 8px 16px;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 700;
|
||||||
|
cursor: pointer;
|
||||||
|
outline: none;
|
||||||
|
transition: background 0.2s ease-in-out;
|
||||||
|
pointer-events:${(props) => (props.disabled ? "none" : null)};
|
||||||
|
opacity: ${(props) => (props.disabled ? 0.5 : 1)};
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: ${(props) => {
|
||||||
|
switch (props.variant) {
|
||||||
|
case "primary":
|
||||||
|
return "var(--button-primary-hover)";
|
||||||
|
case "secondary":
|
||||||
|
return "var(--button-secondary-hover)";
|
||||||
|
case "danger":
|
||||||
|
return "var(--button-danger-hover)";
|
||||||
|
case "success":
|
||||||
|
return "var(--button-success-hover)";
|
||||||
|
case "warning":
|
||||||
|
return "var(--button-warning-hover)";
|
||||||
|
default:
|
||||||
|
return "var(--button-primary-hover)";
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
background: ${(props) => {
|
||||||
|
switch (props.variant) {
|
||||||
|
case "primary":
|
||||||
|
return "var(--button-primary-active)";
|
||||||
|
case "secondary":
|
||||||
|
return "var(--button-secondary-active)";
|
||||||
|
case "danger":
|
||||||
|
return "var(--button-danger-active)";
|
||||||
|
case "success":
|
||||||
|
return "var(--button-success-active)";
|
||||||
|
case "warning":
|
||||||
|
return "var(--button-warning-active)";
|
||||||
|
default:
|
||||||
|
return "var(--button-primary-active)";
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
`;
|
@ -11,7 +11,23 @@ export type ThemeVariables =
|
|||||||
| "tertiary"
|
| "tertiary"
|
||||||
| "text"
|
| "text"
|
||||||
| "textMuted"
|
| "textMuted"
|
||||||
| "inputBackground";
|
| "inputBackground"
|
||||||
|
| "error"
|
||||||
|
| "buttonPrimary"
|
||||||
|
| "buttonPrimaryHover"
|
||||||
|
| "buttonPrimaryActive"
|
||||||
|
| "buttonSecondary"
|
||||||
|
| "buttonSecondaryHover"
|
||||||
|
| "buttonSecondaryActive"
|
||||||
|
| "buttonDanger"
|
||||||
|
| "buttonDangerHover"
|
||||||
|
| "buttonDangerActive"
|
||||||
|
| "buttonSuccess"
|
||||||
|
| "buttonSuccessHover"
|
||||||
|
| "buttonSuccessActive"
|
||||||
|
| "buttonWarning"
|
||||||
|
| "buttonWarningHover"
|
||||||
|
| "buttonWarningActive";
|
||||||
|
|
||||||
export type Overrides = {
|
export type Overrides = {
|
||||||
[variable in ThemeVariables]: string;
|
[variable in ThemeVariables]: string;
|
||||||
@ -32,6 +48,22 @@ export const ThemePresets: Record<string, Theme> = {
|
|||||||
text: "#000000",
|
text: "#000000",
|
||||||
textMuted: "#232120",
|
textMuted: "#232120",
|
||||||
inputBackground: "#757575",
|
inputBackground: "#757575",
|
||||||
|
error: "#e83f36",
|
||||||
|
buttonPrimary: "",
|
||||||
|
buttonPrimaryHover: "",
|
||||||
|
buttonPrimaryActive: "",
|
||||||
|
buttonSecondary: "",
|
||||||
|
buttonSecondaryHover: "",
|
||||||
|
buttonSecondaryActive: "",
|
||||||
|
buttonDanger: "",
|
||||||
|
buttonDangerHover: "",
|
||||||
|
buttonDangerActive: "",
|
||||||
|
buttonSuccess: "",
|
||||||
|
buttonSuccessHover: "",
|
||||||
|
buttonSuccessActive: "",
|
||||||
|
buttonWarning: "",
|
||||||
|
buttonWarningHover: "",
|
||||||
|
buttonWarningActive: "",
|
||||||
},
|
},
|
||||||
dark: {
|
dark: {
|
||||||
brandPrimary: "#FF5F00",
|
brandPrimary: "#FF5F00",
|
||||||
@ -43,6 +75,23 @@ export const ThemePresets: Record<string, Theme> = {
|
|||||||
text: "#e9e2e1",
|
text: "#e9e2e1",
|
||||||
textMuted: "#85898f",
|
textMuted: "#85898f",
|
||||||
inputBackground: "#121212",
|
inputBackground: "#121212",
|
||||||
|
error: "#e83f36",
|
||||||
|
// buttons
|
||||||
|
buttonPrimary: "#FF5F00",
|
||||||
|
buttonPrimaryHover: "#ff3d00",
|
||||||
|
buttonPrimaryActive: "#BA4500",
|
||||||
|
buttonSecondary: "#4a4544",
|
||||||
|
buttonSecondaryHover: "#746d69",
|
||||||
|
buttonSecondaryActive: "#5f5a59",
|
||||||
|
buttonDanger: "#ff3a3b",
|
||||||
|
buttonDangerHover: "#ff2d2f",
|
||||||
|
buttonDangerActive: "#ff2425",
|
||||||
|
buttonSuccess: "#34af65",
|
||||||
|
buttonSuccessHover: "#31a660",
|
||||||
|
buttonSuccessActive: "#2d9657",
|
||||||
|
buttonWarning: "#faa61a",
|
||||||
|
buttonWarningHover: "#e69105",
|
||||||
|
buttonWarningActive: "#c27b04",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -52,6 +101,9 @@ const GlobalTheme = createGlobalStyle<{ theme: Theme }>`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const toDashed = (str: string) =>
|
||||||
|
str.replace(/[A-Z]/g, (m) => "-" + m.toLowerCase());
|
||||||
|
|
||||||
export const generateVariables = (theme: Theme) => {
|
export const generateVariables = (theme: Theme) => {
|
||||||
return (Object.keys(theme) as ThemeVariables[]).map((key) => {
|
return (Object.keys(theme) as ThemeVariables[]).map((key) => {
|
||||||
const colour = theme[key];
|
const colour = theme[key];
|
||||||
@ -59,9 +111,11 @@ export const generateVariables = (theme: Theme) => {
|
|||||||
const r = parseInt(colour.substring(1, 3), 16);
|
const r = parseInt(colour.substring(1, 3), 16);
|
||||||
const g = parseInt(colour.substring(3, 5), 16);
|
const g = parseInt(colour.substring(3, 5), 16);
|
||||||
const b = parseInt(colour.substring(5, 7), 16);
|
const b = parseInt(colour.substring(5, 7), 16);
|
||||||
return `--${key}: ${theme[key]}; --${key}-rgb: rgb(${r}, ${g}, ${b});`;
|
return `--${toDashed(key)}: ${theme[key]}; --${toDashed(
|
||||||
|
key
|
||||||
|
)}-rgb: rgb(${r}, ${g}, ${b});`;
|
||||||
} catch {
|
} catch {
|
||||||
return `--${key}: ${theme[key]};`;
|
return `--${toDashed(key)}: ${theme[key]};`;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
|
import Button from "../components/Button";
|
||||||
import Container from "../components/Container";
|
import Container from "../components/Container";
|
||||||
|
|
||||||
const Wrapper = styled(Container)`
|
const Wrapper = styled(Container)`
|
||||||
@ -11,10 +12,10 @@ const Wrapper = styled(Container)`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
const LoginBox = styled(Container)`
|
const LoginBox = styled(Container)`
|
||||||
background-color: var(--primaryAlt);
|
background-color: var(--primary-alt);
|
||||||
padding: 32px;
|
padding: 32px;
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
color: var(--textMuted);
|
color: var(--text-muted);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@ -43,7 +44,7 @@ const Header = styled.h1`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
const SubHeader = styled.h2`
|
const SubHeader = styled.h2`
|
||||||
color: var(--textMuted);
|
color: var(--text-muted);
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
`;
|
`;
|
||||||
@ -59,9 +60,20 @@ const InputContainer = styled.h1<{ marginBottom: boolean }>`
|
|||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const InputLabel = styled.label`
|
const LabelWrapper = styled.div<{ error?: boolean }>`
|
||||||
color: #b1b5bc;
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
|
color: ${(props) => (props.error ? "var(--error)" : "#b1b5bc")};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const InputErrorText = styled.label`
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 400;
|
||||||
|
font-style: italic;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const InputLabel = styled.label`
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
`;
|
`;
|
||||||
@ -71,7 +83,7 @@ const InputWrapper = styled.div`
|
|||||||
display: flex;
|
display: flex;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const Input = styled.input<{invalid?: boolean}>`
|
const Input = styled.input<{ error?: boolean }>`
|
||||||
outline: none;
|
outline: none;
|
||||||
background: var(--secondary);
|
background: var(--secondary);
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
@ -80,8 +92,8 @@ const Input = styled.input<{invalid?: boolean}>`
|
|||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
margin: 0;
|
margin: 0;
|
||||||
border: ${(props) => props.invalid ? "1px solid red" : "none"};
|
border: none;
|
||||||
aria-invalid: ${(props) => props.invalid ? "true" : "false"};
|
aria-invalid: ${(props) => (props.error ? "true" : "false")};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const PasswordResetLink = styled.a`
|
const PasswordResetLink = styled.a`
|
||||||
@ -93,16 +105,11 @@ const PasswordResetLink = styled.a`
|
|||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const Button = styled.button`
|
const LoginButton = styled(Button)`
|
||||||
background: var(--brandPrimary);
|
|
||||||
color: var(--text);
|
|
||||||
font-size: 16px;
|
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-width: 130px;
|
min-width: 130px;
|
||||||
min-height: 44px;
|
min-height: 44px;
|
||||||
border-radius: 8px;
|
|
||||||
border: none;
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const RegisterContainer = styled.div`
|
const RegisterContainer = styled.div`
|
||||||
@ -123,11 +130,18 @@ const RegisterLink = styled.a`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
function LoginPage() {
|
const Divider = styled.span`
|
||||||
const { register, handleSubmit, watch, formState: { errors } } = useForm();
|
padding: 0 4px;
|
||||||
const onSubmit = (data: any) => console.log(data);
|
`;
|
||||||
|
|
||||||
console.log(errors)
|
function LoginPage() {
|
||||||
|
const {
|
||||||
|
register,
|
||||||
|
handleSubmit,
|
||||||
|
watch,
|
||||||
|
formState: { errors },
|
||||||
|
} = useForm();
|
||||||
|
const onSubmit = (data: any) => console.log(data);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Wrapper>
|
<Wrapper>
|
||||||
@ -139,21 +153,46 @@ function LoginPage() {
|
|||||||
|
|
||||||
<FormContainer onSubmit={handleSubmit(onSubmit)}>
|
<FormContainer onSubmit={handleSubmit(onSubmit)}>
|
||||||
<InputContainer marginBottom={true} style={{ marginTop: 0 }}>
|
<InputContainer marginBottom={true} style={{ marginTop: 0 }}>
|
||||||
<InputLabel>Email</InputLabel>
|
<LabelWrapper error={!!errors.email}>
|
||||||
|
<InputLabel>Email</InputLabel>
|
||||||
|
{errors.email && (
|
||||||
|
<InputErrorText>
|
||||||
|
<Divider>-</Divider>Email error here
|
||||||
|
</InputErrorText>
|
||||||
|
)}
|
||||||
|
</LabelWrapper>
|
||||||
<InputWrapper>
|
<InputWrapper>
|
||||||
<Input type="email" autoFocus {...register("email", {required: true})} invalid={!!errors.email} />
|
<Input
|
||||||
|
type="email"
|
||||||
|
autoFocus
|
||||||
|
{...register("email", { required: true })}
|
||||||
|
error={!!errors.email}
|
||||||
|
/>
|
||||||
</InputWrapper>
|
</InputWrapper>
|
||||||
</InputContainer>
|
</InputContainer>
|
||||||
|
|
||||||
<InputContainer marginBottom={false}>
|
<InputContainer marginBottom={false}>
|
||||||
<InputLabel>Password</InputLabel>
|
<LabelWrapper error={!!errors.password}>
|
||||||
|
<InputLabel>Password</InputLabel>
|
||||||
|
{errors.password && (
|
||||||
|
<InputErrorText>
|
||||||
|
<Divider>-</Divider>Password error here
|
||||||
|
</InputErrorText>
|
||||||
|
)}
|
||||||
|
</LabelWrapper>
|
||||||
<InputWrapper>
|
<InputWrapper>
|
||||||
<Input type="password" {...register("password", {required: true})} invalid={!!errors.password} />
|
<Input
|
||||||
|
type="password"
|
||||||
|
{...register("password", { required: true })}
|
||||||
|
error={!!errors.password}
|
||||||
|
/>
|
||||||
</InputWrapper>
|
</InputWrapper>
|
||||||
</InputContainer>
|
</InputContainer>
|
||||||
|
|
||||||
<PasswordResetLink href="#">Forgot your password?</PasswordResetLink>
|
<PasswordResetLink href="#">Forgot your password?</PasswordResetLink>
|
||||||
<Button type="submit">Log In</Button>
|
<LoginButton variant="primary" type="submit">
|
||||||
|
Log In
|
||||||
|
</LoginButton>
|
||||||
|
|
||||||
<RegisterContainer>
|
<RegisterContainer>
|
||||||
<RegisterLabel>Don't have an account? </RegisterLabel>
|
<RegisterLabel>Don't have an account? </RegisterLabel>
|
||||||
|
Loading…
Reference in New Issue
Block a user