https://chakra-ui.com/community/recipes/floating-labels
Chakra UI - A simple, modular and accessible component library that gives you the building blocks you need to build your React a
Simple, Modular and Accessible UI Components for your React Applications. Built with Styled System
chakra-ui.com
์ด ๋์์ธ์ ๋ก๊ทธ์ธํ๋ฉด์ input์ผ๋ก ๋ฃ๊ณ ์ถ์๋ค.
๊ทธ๋์ ํ๋์ ์ปดํฌ๋ํธ๋ก ๋ง๋ค์ด์, ๊ณ์ ์ฌ์ฌ์ฉํ ์ ์๋๋ก ๋ง๋ค์ด๋ณด์๋ค.
provider.tsx
"use client";
import * as React from "react";
import { ChakraProvider } from "@chakra-ui/react";
import theme from "../theme";
export default function Provider({ children }: { children: React.ReactNode }) {
return <ChakraProvider theme={theme}>{children}</ChakraProvider>;
}
ํน์ ์ด๋ ๊ฒ ํด์ ์๋๋ฉด, chakra-ui/next-js๋ฅผ ๊น๊ณ ์๋์ฒ๋ผ CacheProvider๋ฅผ ๊ฐ์ธ์ฃผ๋๊ฒ๋ ๋ฐฉ๋ฒ์ด๋ค.
๊ทผ๋ฐ ๋๋ ์ ์ฝ๋ ๋ง์ผ๋ก๋ ๋์์.
"use client";
import * as React from "react";
import { CacheProvider } from "@chakra-ui/next-js";
import { ChakraProvider } from "@chakra-ui/react";
import theme from "../theme";
export default function Provider({ children }: { children: React.ReactNode }) {
return (
<CacheProvider>
<ChakraProvider theme={theme}>{children}</ChakraProvider>;
</CacheProvider>
);
}
layout.tsx
import Provider from "@/utils/provider";
import type { Metadata } from "next";
import "./globals.css";
export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="ko">
<body className={inter.className}>
<Provider>{children}</Provider>
</body>
</html>
);
}
layout ํ์ผ์ ์์ provider๋ฅผ ๊ฐ์ธ์ฃผ๋ฉด ์ผ๋จ ์ค๋น ๋์ด๋ค!
์์ chakra ui ๋งํฌ์ ์๋ ์ฝ๋๋ฅผ theme.ts๋ก ๋๊ณ ์ค์ฅ
theme.ts
import { extendTheme, Input, Select, Textarea } from "@chakra-ui/react";
const activeLabelStyles = {
transform: "scale(0.85) translateY(-24px)",
};
const theme = extendTheme({
components: {
Form: {
variants: {
floating: {
container: {
_focusWithin: {
label: {
...activeLabelStyles,
},
},
"input:not(:placeholder-shown) + label, .chakra-select__wrapper + label, textarea:not(:placeholder-shown) ~ label":
{
...activeLabelStyles,
},
label: {
top: 0,
left: 0,
zIndex: 2,
position: "absolute",
backgroundColor: "transparent",
pointerEvents: "none",
mx: 0,
px: 1,
my: 2,
transformOrigin: "left top",
},
},
},
},
},
},
});
Input.defaultProps = { ...Input.defaultProps, focusBorderColor: "#3eb134" };
Select.defaultProps = { ...Select.defaultProps, focusBorderColor: "#3eb134" };
Textarea.defaultProps = { ...Textarea.defaultProps, focusBorderColor: "#3eb134" };
export default theme;
๋ง์ง๋ง defaultProps ์๋ ์ค์, ์ด ํ๋ก์ ํธ๊ฐ ์ ๋ฐ์ ์ผ๋ก ์ด๋ก์์ ๋๊ณ ์์ด์
input, select, textarea์์์ ์ด๋ก์ ๊ณ์ด๋ก ๋ง์ถฐ์ค๋ถ๋ถ์ด๋ค
FloatingLabel.tsx
import React from "react";
import {
FormControl,
FormErrorMessage,
FormHelperText,
FormLabel,
Input,
Select,
} from "@chakra-ui/react";
interface FloatingInputProps {
id?: string;
placeholder?: string;
type?: string;
label?: string;
helperText?: string;
errorMessage?: string;
children?: React.ReactNode;
disabled?: boolean;
}
const FloatingInput = ({
id,
placeholder,
type,
label = "",
helperText,
errorMessage,
children,
disabled,
...rest
}: FloatingInputProps) => {
return (
<FormControl variant="floating" id={id} isInvalid={!!errorMessage}>
<Input
variant="flushed"
type={type}
placeholder={placeholder}
disabled={disabled}
textAlign={type === "date" ? "right" : "left"}
pl={"5px"}
/>
{type === "date" ? (
<FormLabel color={disabled ? "gray.300" : "gray.800"} fontSize="19px" pt={"20px"}>
{label}
</FormLabel>
) : (
<FormLabel color={disabled ? "gray.300" : "gray.800"}>{label}</FormLabel>
)}
<FormHelperText>{helperText}</FormHelperText>
<FormErrorMessage>{errorMessage}</FormErrorMessage>
</FormControl>
);
};
export default FloatingInput;
์ด์ ๋ถ๋ฌ์์ ์จ์ฃผ๋ฉด ๋๋ค.
type ์ด date์ธ ๊ฒ์ ๋ฐ๋ก ์กฐ๊ฑด์ฒ๋ฆฌ ํด์ค ์ด์ ๋, date์ผ๋ label์ด ์๋จ์ ๊ณ ์ ์ด ๋ผ์ ๋์์ธ์ ๋ฐ๊ฟ์ค์ผํด์ ๊ทธ๋ ๋ค.
export const userLoginInputFields: IInputFields[] = [
{
id: "userId",
placeholder: " ",
type: "input",
label: "์์ด๋",
},
{
id: "passwd",
placeholder: " ",
type: "password",
label: "๋น๋ฐ๋ฒํธ",
},
];
์ด๋ฐ์์ผ๋ก ๊ฐ์ ํ์ด์ง์์ ์ธ input list๋ค์ ํ๊ณณ์ ๋ด์๋๊ณ ,
Login.tsx (์์ ํ์ด์ง- ์ค์ ๋ก ์ฌ์ฉํ ํ์ด์ง)
<LoginBox>
<form onSubmit={handleSubmit(onLogin)}>
<Flex
w={"full"}
h={"130px"}
flexDirection={"column"}
justifyContent="space-between"
mt={"30px"}
px="10px"
>
{userLoginInputFields.map((el: any) => (
<FloatingInput
key={el.id}
{...el}
type={el.type}
{...register(el.id, { required: true })}
/>
))}
</Flex>
<MainBtn onClick={() => router.push("/")} title={"LOGIN"} size={"sm"} />
</form>
<Box w={"full"} mt="30px">
{userLoginLinkList.map((el: any) => (
<MainLinks url={el.url} content={el.content} />
))}
</Box>
</LoginBox>
์ด๋ฐ์์ผ๋ก ์จ์ฃผ๋ฉด ๋๋ค.
์ด๋ ๊ฒ ์ ๋ฐ์๋ ๊ฒ์ ๋ณผ ์ ์์!
'Coding Diary' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
๋ค์ ์ฃผ์ ๊ฒ์ api ์ด์ฉํด์ next ์ ์ ์ฉํ๊ธฐ (+๋ชจ๋ฌ) (0) | 2024.04.02 |
---|---|
[Next] SNS๋ณ๋ก ๋์ ๋ผ์ฐํ (Dynamic Routes) ์ง์ ํ๊ธฐ (1) | 2024.03.05 |
[JS, React] ํ์ด์ง ์ฒ๋ฆฌ ์ ์ธ๋ฑ์ค๋ฅผ ์ญ์์ผ๋ก ์ฒ๋ฆฌ (2) | 2023.11.27 |
Ag-grid์ ์ซ์๋ง ์ ๋ ฅ๋๊ฒ ํ๊ธฐ : agNumberColumnFilter (0) | 2023.10.02 |
Check Box ์ด์ฉํด์ Select option ๊ฐ ๋ณ๊ฒฝํ๊ธฐ (0) | 2023.09.22 |