Skip to content

Commit

Permalink
Merge pull request #149 from 80000Coding/feat/input-components-with-n…
Browse files Browse the repository at this point in the history
…extui

🚧 nextui 를 활용한 input component 생성
  • Loading branch information
rkskekzzz authored Oct 18, 2023
2 parents 479dedf + 19aaf45 commit c58fce7
Show file tree
Hide file tree
Showing 9 changed files with 282 additions and 0 deletions.
16 changes: 16 additions & 0 deletions apps/web/next.config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,21 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
/**
*
* @param {import('webpack').Configuration} config
* @param {import('next/dist/server/config-shared').WebpackConfigContext} context
* @returns {import('webpack').Configuration}
*/
webpack: (config) => {
if (process.env.NEXT_OUTPUT_MODE !== 'export' || !config.module) {
return config
}
config.module.rules?.push({
test: /src\/app\/test/,
loader: 'ignore-loader',
})
return config
},
experimental: {
externalDir: true,
},
Expand Down
1 change: 1 addition & 0 deletions apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
"@types/react-dom": "18.2.6",
"chromatic": "^6.22.0",
"eslint-config-custom": "*",
"ignore-loader": "^0.1.2",
"storybook": "^7.1.0",
"storybook-dark-mode": "^3.0.1",
"storybook-tailwind-dark-mode": "^1.0.22",
Expand Down
27 changes: 27 additions & 0 deletions apps/web/src/app/test/input/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
'use client'
import { InputComment, InputSearch } from '@80000coding/ui'
import { useState } from 'react'

export default function Page() {
const [value, setValue] = useState('')
const [commentValue, setComment] = useState('')

return (
<div className='flex h-screen w-screen flex-col items-start gap-2 p-10'>
{/* <Input value={value} setValue={setValue} />
<Input value={value} setValue={setValue} label={'조직 이름'} description={'조직 이름을 입력해 주세요'} />
<Input value={value} setValue={setValue} isInvalid={value === '가나다라'} description='' displayLength={false}></Input>
<Input
value={value}
setValue={setValue}
isInvalid={false}
description=''
displayLength={false}
isCorrect={value === '가나다라'}
></Input> */}
<InputSearch value={value} setValue={setValue}></InputSearch>
<InputSearch value={value} setValue={setValue} isBackBtn={true}></InputSearch>
<InputComment value={commentValue} setValue={setComment}></InputComment>
</div>
)
}
7 changes: 7 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions packages/ui/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,8 @@ export * from './button'
export * from './chip'
export * from './dropdown'
export * from './icon'
export * from './input'
export * from './input/input-comment'
export * from './input/input-search'
export * from './notification'
export * from './toggle'
89 changes: 89 additions & 0 deletions packages/ui/src/input/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { StaticConfirmIcon } from '@80000coding/web-icons'
import { Input as Input$1 } from '@nextui-org/react'
import { Dispatch, SetStateAction, useState } from 'react'

export type InputProps = {
value: string
setValue: Dispatch<SetStateAction<string>>
displayLength?: boolean
isDescription?: boolean
isCorrect?: boolean
} & Omit<React.ComponentProps<typeof Input$1>, 'classNames'>

export function Input({
value,
setValue,
isInvalid = false,
placeholder = 'placeholder',
errorMessage: errorMessage$1 = 'Please enter a valid value',
displayLength = true,
description: description$1 = 'Please enter a value',
isDescription = true,
isCorrect = false,
label,
...rest
}: InputProps) {
const [isFocus, setIsFocus] = useState(false)
const description = displayLength ? (
<>
<span className='absolute top-[12px]'>{description$1}</span>
<span className='absolute right-[12px] top-[12px]'>{value.length + '/20'}</span>
</>
) : (
description$1
)

const errorMessage = displayLength ? (
<>
<span className='absolute top-[12px]'>{errorMessage$1}</span>
<span className='absolute right-[12px] top-[12px] text-gray-400'>{value.length + '/20'}</span>
</>
) : (
errorMessage$1
)

return (
<Input$1
/* strings */
value={value}
label={label}
placeholder={placeholder}
description={isDescription && description}
errorMessage={isInvalid && errorMessage}
/* status */
isInvalid={isInvalid}
isClearable={isFocus || !isCorrect}
/* actions */
onValueChange={setValue}
onFocusChange={(e) => {
setIsFocus(e)
}}
/* styles */
type='text'
radius='full'
labelPlacement='outside'
endContent={isCorrect && !isFocus && <StaticConfirmIcon className='pointer-events-none absolute right-[13px]' />}
classNames={{
label: ['mx-[12px]', 'body-2'],
description: ['w-full', 'text-gray-400', 'caption-2', 'left-[12px]'],
errorMessage: ['w-full', 'text-red-warning', 'caption-2', 'left-[12px]'],
input: ['!bg-white', 'text-black', 'placeholder:text-gray-300', 'body-3', 'h-100'],
innerWrapper: [],
inputWrapper: [
'items-start',
'!bg-white',
'border',
isInvalid ? 'border-red-warning' : isCorrect ? 'border-green' : 'border-gray-300',
isInvalid ? 'hover:border-red-warning' : 'hover:border-green',
isInvalid ? 'focus-within:border-red-warning' : 'focus-within:border-green',
'!cursor-text',
'rounded-[20px]',
'px-[20px]',
'py-[13px]',
],
mainWrapper: 'pb-[28px]', // 28px = padding 12px + lineheight 16px
}}
{...rest}
/>
)
}
63 changes: 63 additions & 0 deletions packages/ui/src/input/input-comment/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { Textarea as TextArea$1 } from '@nextui-org/react'
import { Dispatch, SetStateAction, useState } from 'react'

export type InputCommentProps = {
value: string
setValue: Dispatch<SetStateAction<string>>
displayLength?: boolean
isDescription?: boolean
isCorrect?: boolean
} & Omit<React.ComponentProps<typeof TextArea$1>, 'classNames'>

export function InputComment({
value,
setValue,
isInvalid = false,
placeholder = '댓글을 입력해주세요',
isCorrect = false,
label,
...rest
}: InputCommentProps) {
const [isFocus, setIsFocus] = useState(false)

return (
<TextArea$1
/* strings */
value={value}
label={label}
placeholder={placeholder}
/* status */
isInvalid={isInvalid}
isClearable={isFocus || !isCorrect}
/* actions */
onValueChange={setValue}
onFocusChange={(e) => {
setIsFocus(e)
}}
/* styles */
maxRows={3}
minRows={1}
type='text'
radius='full'
labelPlacement='outside'
classNames={{
label: ['mx-[12px]', 'body-2'],
input: ['!bg-white', 'text-black', 'placeholder:text-gray-300', 'body-3', 'px-[0px]', 'py-[0]'],
innerWrapper: [],
inputWrapper: [
'items-start',
'!bg-white',
'border',
isInvalid ? 'border-red-warning' : isCorrect ? 'border-green' : 'border-gray-300',
isInvalid ? 'hover:border-red-warning' : 'hover:border-green',
isInvalid ? 'focus-within:border-red-warning' : 'focus-within:border-green',
'!cursor-text',
'rounded-[20px]',
'px-[20px]',
'py-[10px]',
],
}}
{...rest}
/>
)
}
66 changes: 66 additions & 0 deletions packages/ui/src/input/input-search/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { DynamicBackIcon, DynamicDeleteIcon, DynamicSearchIcon } from '@80000coding/web-icons'
import { Input as Input$1 } from '@nextui-org/react'
import { Dispatch, SetStateAction, useState } from 'react'

export type InputSearchProps = {
value: string
setValue: Dispatch<SetStateAction<string>>
isBackBtn?: boolean
} & Omit<React.ComponentProps<typeof Input$1>, 'classNames'>

export function InputSearch({ value, setValue, isInvalid = false, isBackBtn = false, ...rest }: InputSearchProps) {
// const [value, setValue] = useState('')

const onClear = () => {
setValue('')
}

const onSearch = () => {
console.log('search')
}

const onClickGoBack = () => {
console.log('Go Back!')
}

const [isFocus, setIsFocus] = useState(false)

return (
<Input$1
labelPlacement='outside'
value={value}
startContent={isBackBtn && <DynamicBackIcon onClick={onClickGoBack} className='text-gray-500' />}
endContent={
<div className='flex-column flex'>
{value !== '' && <DynamicDeleteIcon className='text-gray-500' onClick={onClear}></DynamicDeleteIcon>}
<DynamicSearchIcon className='text-green ml-[16px]' onClick={onSearch}></DynamicSearchIcon>
</div>
}
onFocusChange={(e) => {
setIsFocus(e)
}}
onValueChange={setValue}
isInvalid={isInvalid}
type='text'
placeholder='검색어를 입력하세요'
radius='full'
classNames={{
label: ['mx-[12px]', 'body-2'],
description: ['mx-[12px]', 'caption-2'],
input: ['!bg-white', 'text-black', 'placeholder:text-gray-300', 'body-3', 'h-100'],
innerWrapper: [],
inputWrapper: [
'items-start',
'!bg-white',
'border',
'hover:border-green',
'focus-within:border-green',
'!cursor-text',
'rounded-[20px]',
'px-[20px]',
'py-[13px]',
],
}}
/>
)
}
10 changes: 10 additions & 0 deletions packages/web-icons/icons/dynamic/dynamic-delete.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit c58fce7

Please sign in to comment.