Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
te6-in committed Dec 3, 2024
1 parent 163f9a6 commit a7144ca
Show file tree
Hide file tree
Showing 15 changed files with 370 additions and 51 deletions.
1 change: 1 addition & 0 deletions docs/components/example/index.json
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@
"inline-banner-with-icon": "import { InlineBanner } from \"seed-design/ui/inline-banner\";\nimport { IconILowercaseSerifCircleFill } from \"@daangn/react-monochrome-icon\";\n\nexport default function InlineBannerWithIcon() {\n return (\n <InlineBanner variant=\"informativeWeak\" icon={<IconILowercaseSerifCircleFill />}>\n 다른 사람과 예약된 물품이 있어요.\n </InlineBanner>\n );\n}",
"inline-banner-with-link": "import { InlineBanner } from \"seed-design/ui/inline-banner\";\n\nexport default function InlineBannerWithLink() {\n return (\n <InlineBanner variant=\"informativeWeak\" link={{ label: \"자세히 보기\", onClick: () => {} }}>\n 다른 사람과 예약된 물품이 있어요.\n </InlineBanner>\n );\n}",
"inline-banner-with-title-text": "import { InlineBanner } from \"seed-design/ui/inline-banner\";\n\nexport default function InlineBannerWithTitleText() {\n return (\n <InlineBanner variant=\"informativeWeak\" titleText=\"예약\">\n 다른 사람과 예약된 물품이 있어요.\n </InlineBanner>\n );\n}",
"multiline-text-field-preview": "import { useState } from \"react\";\nimport { MultilineTextField } from \"seed-design/ui/multiline-text-field\";\n\nexport default function TextFieldPreview() {\n const [value, setValue] = useState(\"\");\n\n return (\n <div className=\"flex flex-col items-center w-full\">\n <MultilineTextField autoFocus value={value} onValueChange={setValue} />\n <p className=\"text-center\">현재 값: {value}</p>\n </div>\n );\n}",
"segmented-control-disabled": "import { SegmentedControl, Segment } from \"seed-design/ui/segmented-control\";\n\nexport default function SegmentedControlPreview() {\n return (\n <SegmentedControl defaultValue=\"Hot\" disabled>\n <Segment value=\"Hot\">Hot</Segment>\n <Segment value=\"New\">New</Segment>\n </SegmentedControl>\n );\n}",
"segmented-control-fixed-width": "import { SegmentedControl, Segment } from \"seed-design/ui/segmented-control\";\n\nexport default function SegmentedControlFixedWidth() {\n return (\n <SegmentedControl defaultValue=\"new\">\n <Segment value=\"new\">New</Segment>\n <Segment value=\"hot\">Hot</Segment>\n </SegmentedControl>\n );\n}",
"segmented-control-long-label-fixed-width": "import { SegmentedControl, Segment } from \"seed-design/ui/segmented-control\";\n\nexport default function SegmentedControlLongLabelFixedWidth() {\n return (\n <SegmentedControl defaultValue=\"price\">\n <Segment value=\"price\">가격 높은 순</Segment>\n <Segment value=\"discount\">할인율 높은 순</Segment>\n <Segment value=\"popularity\">인기 많은 순</Segment>\n </SegmentedControl>\n );\n}",
Expand Down
15 changes: 15 additions & 0 deletions docs/components/example/multiline-text-field-preview.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
"use client";

import { useState } from "react";
import { MultilineTextField } from "seed-design/ui/multiline-text-field";

export default function TextFieldPreview() {
const [value, setValue] = useState("");

return (
<div className="flex flex-col items-center w-full">
<MultilineTextField autoFocus value={value} onValueChange={setValue} />
<p className="text-center">현재 값: {value}</p>
</div>
);
}
5 changes: 5 additions & 0 deletions docs/content/docs/react/components/text-fields/meta.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"title": "Text Fields",
"pages": ["..."],
"defaultOpen": true
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
---
title: Multiline Text Field
description: 사용자가 입력할 수 있는 텍스트를 받는 컴포넌트에요. 여러 줄의 텍스트를 입력할 수 있고 높이가 자동으로 조절돼요.
---

<ComponentExample name="multiline-text-field-preview" />

## 설치

<Installation name="multiline-text-field" />

## Props

<AutoTypeTable
path="./registry/ui/multiline-text-field.tsx"
name="MultilineTextFieldProps"
/>

## 예제

### Status

#### Enabled

<ComponentExample name="multiline-text-field-enabled" />

#### Disabled

<ComponentExample name="multiline-text-field-disabled" />

#### Read Only

<ComponentExample name="multiline-text-field-read-only" />

### Customizable Parts

#### Required Indicator

<ComponentExample name="multiline-text-field-required" />

#### Optional Indicator

<ComponentExample name="multiline-text-field-optional" />

#### Grapheme Count

<ComponentExample name="multiline-text-field-grapheme-count" />

### Size

#### XLarge

<ComponentExample name="multiline-text-field-xlarge" />

#### Large

<ComponentExample name="multiline-text-field-large" />

#### Medium

<ComponentExample name="multiline-text-field-medium" />
10 changes: 10 additions & 0 deletions docs/public/__registry__/ui/index.json
Original file line number Diff line number Diff line change
Expand Up @@ -195,5 +195,15 @@
"files": [
"ui:text-field.tsx"
]
},
{
"name": "multiline-text-field",
"dependencies": [
"@seed-design/react-text-field",
"@daangn/react-monochrome-icon"
],
"files": [
"ui:multiline-text-field.tsx"
]
}
]
14 changes: 14 additions & 0 deletions docs/public/__registry__/ui/multiline-text-field.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"name": "multiline-text-field",
"dependencies": [
"@seed-design/react-text-field",
"@daangn/react-monochrome-icon"
],
"registries": [
{
"name": "multiline-text-field.tsx",
"type": "ui",
"content": "\"use client\";\n\nimport \"@seed-design/stylesheet/textField.css\";\n\nimport * as React from \"react\";\nimport clsx from \"clsx\";\nimport {\n textField,\n type TextFieldVariantProps,\n} from \"@seed-design/recipe/textField\";\nimport { IconExclamationmarkCircleFill } from \"@daangn/react-monochrome-icon\";\nimport {\n useTextField,\n type UseTextFieldProps,\n} from \"@seed-design/react-text-field\";\nimport type { Assign } from \"../util/types\";\n\nexport interface MultilineTextFieldProps\n extends UseTextFieldProps,\n TextFieldVariantProps {\n label?: string;\n requiredIndicator?: string;\n optionalIndicator?: string;\n\n description?: string;\n errorMessage?: string;\n\n maxGraphemeCount?: number;\n hideGraphemeCount?: boolean;\n}\n\ntype ReactMultilineTextFieldProps = Assign<\n Omit<\n React.TextareaHTMLAttributes<HTMLTextAreaElement>,\n \"children\" | \"maxLength\"\n >,\n MultilineTextFieldProps\n>;\n\nexport const MultilineTextField = React.forwardRef<\n HTMLTextAreaElement,\n ReactMultilineTextFieldProps\n>(\n (\n {\n size = \"medium\",\n label,\n requiredIndicator,\n optionalIndicator,\n hideGraphemeCount,\n ...restProps\n },\n ref,\n ) => {\n const {\n rootProps: { className: rootClassName, ...rootProps },\n inputProps: { className: inputClassName, ...inputProps },\n labelProps: { className: labelClassName, ...labelProps },\n descriptionProps,\n errorMessageProps,\n stateProps,\n restProps: restInternalProps,\n isInvalid,\n isRequired,\n graphemes,\n } = useTextField({ ...restProps });\n\n const { description, errorMessage, maxGraphemeCount } = restProps;\n\n const classNames = textField({ size });\n\n const indicator = isRequired ? requiredIndicator : optionalIndicator;\n\n const renderDescription = !isInvalid && description;\n const renderErrorMessage = isInvalid && !!errorMessage;\n const renderCharacterCount = !hideGraphemeCount && maxGraphemeCount;\n\n return (\n <div\n className={clsx(classNames.root, rootClassName)}\n {...rootProps}\n {...stateProps}\n >\n {label && (\n // XXX\n // biome-ignore lint/a11y/noLabelWithoutControl: <explanation>\n <label {...labelProps} className={classNames.header}>\n <span className={clsx(classNames.label, labelClassName)}>\n {label}\n </span>\n {indicator && (\n <span className={classNames.indicator}>{indicator}</span>\n )}\n </label>\n )}\n <textarea\n rows={3}\n ref={ref}\n className={clsx(\n classNames.inputText,\n classNames.input,\n inputClassName,\n )}\n {...inputProps}\n {...restInternalProps}\n />\n {(renderDescription || renderErrorMessage || renderCharacterCount) && (\n <div className={classNames.footer}>\n {renderDescription && (\n <div {...descriptionProps} className={classNames.description}>\n {description}\n </div>\n )}\n {renderErrorMessage && (\n <div {...errorMessageProps} className={classNames.errorMessage}>\n <IconExclamationmarkCircleFill\n className={classNames.errorIcon}\n />\n <div>{errorMessage}</div>\n </div>\n )}\n {renderCharacterCount && (\n <div className={classNames.graphemeCount}>\n <span\n {...stateProps}\n className={classNames.currentGraphemeCount}\n >\n {graphemes.length}\n </span>\n <span className={classNames.maxGraphemeCount}>\n /{maxGraphemeCount}\n </span>\n </div>\n )}\n </div>\n )}\n </div>\n );\n },\n);\nMultilineTextField.displayName = \"MultilineTextField\";\n"
}
]
}
8 changes: 8 additions & 0 deletions docs/registry/registry-ui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,4 +127,12 @@ export const registryUI: RegistryUI = [
],
files: ["ui:text-field.tsx"],
},
{
name: "multiline-text-field",
dependencies: [
"@seed-design/react-text-field",
"@daangn/react-monochrome-icon",
],
files: ["ui:multiline-text-field.tsx"],
},
];
139 changes: 139 additions & 0 deletions docs/registry/ui/multiline-text-field.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
"use client";

import "@seed-design/stylesheet/textField.css";

import * as React from "react";
import clsx from "clsx";
import {
textField,
type TextFieldVariantProps,
} from "@seed-design/recipe/textField";
import { IconExclamationmarkCircleFill } from "@daangn/react-monochrome-icon";
import {
useTextField,
type UseTextFieldProps,
} from "@seed-design/react-text-field";
import type { Assign } from "../util/types";

export interface MultilineTextFieldProps
extends UseTextFieldProps,
TextFieldVariantProps {
label?: string;
requiredIndicator?: string;
optionalIndicator?: string;

description?: string;
errorMessage?: string;

maxGraphemeCount?: number;
hideGraphemeCount?: boolean;
}

type ReactMultilineTextFieldProps = Assign<
Omit<
React.TextareaHTMLAttributes<HTMLTextAreaElement>,
"children" | "maxLength"
>,
MultilineTextFieldProps
>;

export const MultilineTextField = React.forwardRef<
HTMLTextAreaElement,
ReactMultilineTextFieldProps
>(
(
{
size = "medium",
label,
requiredIndicator,
optionalIndicator,
hideGraphemeCount,
...restProps
},
ref,
) => {
const {
rootProps: { className: rootClassName, ...rootProps },
textareaProps: { className: textareaClassName, ...textareaProps },
labelProps: { className: labelClassName, ...labelProps },
descriptionProps,
errorMessageProps,
stateProps,
restProps: restInternalProps,
isInvalid,
isRequired,
graphemes,
} = useTextField({ elementType: "textarea", ...restProps });

const { description, errorMessage, maxGraphemeCount } = restProps;

const classNames = textField({ size });

const indicator = isRequired ? requiredIndicator : optionalIndicator;

const renderDescription = !isInvalid && description;
const renderErrorMessage = isInvalid && !!errorMessage;
const renderCharacterCount = !hideGraphemeCount && maxGraphemeCount;

return (
<div
className={clsx(classNames.root, rootClassName)}
{...rootProps}
{...stateProps}
>
{label && (
// XXX
// biome-ignore lint/a11y/noLabelWithoutControl: <explanation>
<label {...labelProps} className={classNames.header}>
<span className={clsx(classNames.label, labelClassName)}>
{label}
</span>
{indicator && (
<span className={classNames.indicator}>{indicator}</span>
)}
</label>
)}
<div className={classNames.input}>
<textarea
rows={3}
ref={ref}
className={clsx(classNames.inputText, textareaClassName)}
{...textareaProps}
{...restInternalProps}
/>
</div>
{(renderDescription || renderErrorMessage || renderCharacterCount) && (
<div className={classNames.footer}>
{renderDescription && (
<div {...descriptionProps} className={classNames.description}>
{description}
</div>
)}
{renderErrorMessage && (
<div {...errorMessageProps} className={classNames.errorMessage}>
<IconExclamationmarkCircleFill
className={classNames.errorIcon}
/>
<div>{errorMessage}</div>
</div>
)}
{renderCharacterCount && (
<div className={classNames.graphemeCount}>
<span
{...stateProps}
className={classNames.currentGraphemeCount}
>
{graphemes.length}
</span>
<span className={classNames.maxGraphemeCount}>
/{maxGraphemeCount}
</span>
</div>
)}
</div>
)}
</div>
);
},
);
MultilineTextField.displayName = "MultilineTextField";
Loading

0 comments on commit a7144ca

Please sign in to comment.