From 49f543862661ced73be856969ca9b4b813ed9b80 Mon Sep 17 00:00:00 2001 From: Mohammad Date: Tue, 9 May 2023 06:24:51 -0600 Subject: [PATCH] =?UTF-8?q?feat(textfield):=20added=20Super=20cool=20text?= =?UTF-8?q?=20field=20=E2=80=BC=EF=B8=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fix #3 --- src/components/TextField.tsx | 169 +++++++++++++++++++++++++++++- src/stories/TextField.stories.tsx | 33 ++++++ 2 files changed, 201 insertions(+), 1 deletion(-) create mode 100644 src/stories/TextField.stories.tsx diff --git a/src/components/TextField.tsx b/src/components/TextField.tsx index 456d47e..e9aa271 100644 --- a/src/components/TextField.tsx +++ b/src/components/TextField.tsx @@ -1,3 +1,170 @@ import React from "react"; +import "./css/main.css"; +import styled from "styled-components"; +import { getThemeValue } from "./ThemeProvider"; -export const TextField = () => ; \ No newline at end of file +export type TextFieldProps = React.HTMLAttributes & { + children: React.ReactNode; + type?: "text" | "password" | "email" | "number"; + disabled?: boolean; + placeholder?: string; + regex?: RegExp; + value?: string; + setValue?: (value: string) => void; + error?: string; + success?: string; + info?: string; + divProps?: React.HTMLAttributes; + variant?: "underlined" | "outlined" | "none"; +}; + +export const TextField = (props: TextFieldProps) => { + const { regex, error, success, info } = props; + const [regexPassed, setRegexPassed] = React.useState(true); + const ref = React.useRef(null); + + React.useEffect(() => { + ref.current?.addEventListener("input", (event: any) => { + const e = event as React.ChangeEvent; + if (regex && e.target.value) { + setRegexPassed(regex.test(e.target.value ?? "")); + } else { + setRegexPassed(true); + } + }); + }, [regex, ref]); + + React.useEffect(() => { + if (regex && props.value) { + setRegexPassed(regex.test(props.value ?? "")); + } else { + setRegexPassed(true); + } + }, [regex, props.value]); + + return ( + + + 0) || + (!!success && typeof success === "string" && success.length > 0) || + (!!info && typeof info === "string" && info.length > 0) + } + error={!!error} + success={!!success} + info={!!info} + > + {error ?? success ?? info ?? ""} + + + ); +}; + +const TextFieldElement = styled.input` + font-family: "Poppins", sans-serif; + font-size: 1em; + color: ${(props) => props.theme.text ?? getThemeValue("text")}; + border: 2px solid ${(props) => props.theme.text ?? getThemeValue("text")}; + background-color: ${(props) => + props.theme.background ?? getThemeValue("background")}; + border-radius: 0.375em; + padding: 0.5em 1em; + outline: none; + transition: all 0.2s ease-in-out; + + :disabled { + border-color: ${(props) => + props.theme.disabled ?? getThemeValue("disabled")}; + color: ${(props) => props.theme.disabled ?? getThemeValue("disabled")}; + cursor: not-allowed; + } + + &:hover:not(:disabled) { + border-color: ${(props) => + props.theme.secondary ?? getThemeValue("secondary")}; + } + + &:active:not(:disabled), + &:focus:not(:disabled) { + border-color: ${(props) => props.theme.primary ?? getThemeValue("primary")}; + } + + &:not(:focus):not(:disabled) { + ${(props) => + props.info && + ` + border-color: ${props.theme.info ?? getThemeValue("info")}; + color: ${props.theme.info ?? getThemeValue("info")}; + ::placeholder { + color: ${props.theme.info ?? getThemeValue("info")}; + } + `} + ${(props) => + props.success && + ` + border-color: ${props.theme.success ?? getThemeValue("success")}; + color: ${props.theme.success ?? getThemeValue("success")}; + ::placeholder { + color: ${props.theme.success ?? getThemeValue("success")}; + } + `} + ${(props) => + props.error && + ` + border-color: ${props.theme.error ?? getThemeValue("error")}; + color: ${props.theme.error ?? getThemeValue("error")}; + ::placeholder { + color: ${props.theme.error ?? getThemeValue("error")}; + } + `} + } + + ${(props) => + props.variant === "underlined" && + ` + border: none; + border-bottom: 2px solid ${props.theme.text ?? getThemeValue("text")}; + border-radius: 0; + padding: 0.25em; + `} + + ${(props) => + props.variant === "none" && + ` + border: none; + padding: 0.25em; + `} +`; + +const HelperText = styled.p<{ + shown: boolean; + error?: boolean; + success?: boolean; + info?: boolean; +}>` + opacity: ${(props) => (props.shown ? 1 : 0)}; + font-family: "Poppins", sans-serif; + font-size: 0.75em; + color: ${(props) => + props.error + ? props.theme.error ?? getThemeValue("error") + : props.success + ? props.theme.success ?? getThemeValue("success") + : props.info + ? props.theme.info ?? getThemeValue("info") + : props.theme.text ?? getThemeValue("text")}; + margin: 0.25em 0 0 0.5em; + position: absolute; + bottom: ${(props) => (props.shown ? "-1.5em" : "-0.5em")}; + transition: all 0.2s ease-in-out; +`; + +const TextFieldContainer = styled.div` + display: flex; + position: relative; +`; diff --git a/src/stories/TextField.stories.tsx b/src/stories/TextField.stories.tsx new file mode 100644 index 0000000..0509692 --- /dev/null +++ b/src/stories/TextField.stories.tsx @@ -0,0 +1,33 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import { TextField } from "../components/TextField"; + +const meta: Meta = { + title: 'Example/TextField', + component: TextField, + tags: ['autodocs'], + parameters: { + controls: { hideNoControlsWarning: true }, + }, + argTypes: { + placeholder: { + defaultValue: "lorem ipsum", + control: { + type: "text", + }, + }, + }, + +}; + +export default meta; +type Story = StoryObj; + +export const TextStory: Story = { + args: { + type: 'text', + disabled: false, + placeholder: "lorem ipsum", + regex: /^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/g, + }, +}; \ No newline at end of file