1
0
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:
Puyodead1 2023-03-24 14:59:30 -04:00
parent b3ea83a561
commit ac6ded3387
No known key found for this signature in database
GPG Key ID: A4FA4FEC0DD353FC
3 changed files with 210 additions and 26 deletions

91
src/components/Button.tsx Normal file
View 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)";
}
}};
`;

View File

@ -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]};`;
} }
}); });
}; };

View File

@ -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?&nbsp;</RegisterLabel> <RegisterLabel>Don't have an account?&nbsp;</RegisterLabel>