From fd0be3d6d372a442e0cd38c3418bdc0977032a43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=ED=99=8D=EC=84=9C=ED=98=84?= Date: Mon, 10 Jun 2024 12:49:54 +0900 Subject: [PATCH 1/4] =?UTF-8?q?[Feature]=20TextField=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EA=B5=AC=ED=98=84=20(#40)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: TextField 컴포넌트 구현 * chore: 텍스트 필드 자잘한 수정 사항 반영 * feat: 텍스트 필드 컴포넌트 스토리 코드 작성 * fix: max length 관련 onChange 로직 수정 * test: 텍스트필드 컴포넌트 테스트 코드 작성 * chore: 토큰 변경 * feat: TextField 빌드 세팅 * fix: a11y violation, incomplete 사항 해결 * feat: 반응형 관련 breakpoint 추가 * chore: token에서 breakpoint 가져오도록 변경 * chore: label2 자간 수정사항 반영 * chore: breakpoint 네이밍 변경 * refactor: 텍스트필드 반응형 토큰 사용 * chore: 불필요한 useEffect 삭제 * chore: panda codegen 실행 * design: 글자 색 변경 * chore: useTextareaAutosize 훅에 useEffect 추가 * fix: 텍스트필드 색상 대조 관련 웹 접근성 테스트 끄도록 설정 변경 * chore: 포커스된 경우도 typing으로 처리하도록 변경 * chore: 스토리북 테스트 러너 a11y rule 관련 설정 추가 * chore: 변경된 빌드 설정 반영 * chore: placeholder 여러 줄인 경우도 수용하도록 수정 * fix: 머지 에러 해결 * refactor: 상수값 따로 분리 * chore: props style로 네이밍 변경 * chore: codegen 반영 --- apps/wow-docs/styled-system/tokens/index.js | 6 +- packages/theme/src/tokens/breakpoint.ts | 8 + packages/theme/src/tokens/index.ts | 2 + packages/wow-tokens/src/breakpoint.ts | 4 + packages/wow-tokens/src/color.ts | 6 +- packages/wow-tokens/src/index.ts | 1 + packages/wow-tokens/src/typography.ts | 1 + packages/wow-ui/package.json | 5 + packages/wow-ui/panda.config.ts | 3 +- packages/wow-ui/rollup.config.js | 1 + packages/wow-ui/src/components/Chip/index.tsx | 3 +- .../TextField/TextField.stories.tsx | 246 +++++++++++++ .../components/TextField/TextField.test.tsx | 157 ++++++++ .../wow-ui/src/components/TextField/index.tsx | 339 ++++++++++++++++++ .../wow-ui/src/hooks/useTextareaAutosize.ts | 42 +++ .../wow-ui/styled-system/css/conditions.js | 2 +- packages/wow-ui/styled-system/css/css.js | 2 +- .../wow-ui/styled-system/jsx/is-valid-prop.js | 2 +- packages/wow-ui/styled-system/tokens/index.js | 40 +-- .../wow-ui/styled-system/tokens/tokens.d.ts | 13 +- .../styled-system/types/conditions.d.ts | 62 ++-- 21 files changed, 864 insertions(+), 81 deletions(-) create mode 100644 packages/theme/src/tokens/breakpoint.ts create mode 100644 packages/wow-tokens/src/breakpoint.ts create mode 100644 packages/wow-ui/src/components/TextField/TextField.stories.tsx create mode 100644 packages/wow-ui/src/components/TextField/TextField.test.tsx create mode 100644 packages/wow-ui/src/components/TextField/index.tsx create mode 100644 packages/wow-ui/src/hooks/useTextareaAutosize.ts diff --git a/apps/wow-docs/styled-system/tokens/index.js b/apps/wow-docs/styled-system/tokens/index.js index f64f4759..94ca0846 100644 --- a/apps/wow-docs/styled-system/tokens/index.js +++ b/apps/wow-docs/styled-system/tokens/index.js @@ -384,11 +384,11 @@ const tokens = { variable: "var(--colors-primary)", }, "colors.success": { - value: "#34A853", + value: "#2A8642", variable: "var(--colors-success)", }, "colors.error": { - value: "#EA4335", + value: "#BB362A", variable: "var(--colors-error)", }, "colors.backgroundNormal": { @@ -404,7 +404,7 @@ const tokens = { variable: "var(--colors-background-dimmer)", }, "colors.sub": { - value: "#8F8F8F", + value: "#6B6B6B", variable: "var(--colors-sub)", }, "colors.outline": { diff --git a/packages/theme/src/tokens/breakpoint.ts b/packages/theme/src/tokens/breakpoint.ts new file mode 100644 index 00000000..4d348061 --- /dev/null +++ b/packages/theme/src/tokens/breakpoint.ts @@ -0,0 +1,8 @@ +import { breakpoint } from "wowds-tokens"; + +export const breakpoints = { + xs: breakpoint.xs, + sm: breakpoint.sm, + md: breakpoint.md, + lg: breakpoint.lg, +}; diff --git a/packages/theme/src/tokens/index.ts b/packages/theme/src/tokens/index.ts index 6f4d9c8f..55ddf098 100644 --- a/packages/theme/src/tokens/index.ts +++ b/packages/theme/src/tokens/index.ts @@ -1,5 +1,7 @@ +export * from "./breakpoint.ts"; export * from "./color.ts"; export * from "./typography.ts"; + import { defineTokens } from "@pandacss/dev"; import { colors, gradients } from "./color.ts"; diff --git a/packages/wow-tokens/src/breakpoint.ts b/packages/wow-tokens/src/breakpoint.ts new file mode 100644 index 00000000..dcfc17e2 --- /dev/null +++ b/packages/wow-tokens/src/breakpoint.ts @@ -0,0 +1,4 @@ +export const xs = "360px"; +export const sm = "600px"; +export const md = "900px"; +export const lg = "1280px"; diff --git a/packages/wow-tokens/src/color.ts b/packages/wow-tokens/src/color.ts index 4bf97d44..dbe30d4e 100644 --- a/packages/wow-tokens/src/color.ts +++ b/packages/wow-tokens/src/color.ts @@ -90,14 +90,14 @@ export const blackOpacity80 = "rgba(0, 0, 0, 0.8)"; // 시맨틱 토큰 export const primary = blue500; -export const success = green500; -export const error = red500; +export const success = green600; +export const error = red600; export const backgroundNormal = white; export const backgroundAlternative = mono50; export const backgroundDimmer = blackOpacity80; -export const sub = mono600; +export const sub = mono700; export const outline = mono400; export const textBlack = black; export const textWhite = white; diff --git a/packages/wow-tokens/src/index.ts b/packages/wow-tokens/src/index.ts index 430ffe95..2b62e05a 100644 --- a/packages/wow-tokens/src/index.ts +++ b/packages/wow-tokens/src/index.ts @@ -1,3 +1,4 @@ +export * as breakpoint from "./breakpoint.ts"; export * as color from "./color.ts"; export * as radius from "./radius.ts"; export * as space from "./space.ts"; diff --git a/packages/wow-tokens/src/typography.ts b/packages/wow-tokens/src/typography.ts index 0ed14205..04a17c02 100644 --- a/packages/wow-tokens/src/typography.ts +++ b/packages/wow-tokens/src/typography.ts @@ -64,6 +64,7 @@ export const label2 = { fontSize: "0.875rem", lineHeight: "100%", fontWeight: 600, + letterSpacing: "-0.01rem", }; export const label3 = { diff --git a/packages/wow-ui/package.json b/packages/wow-ui/package.json index a025674e..5a269302 100644 --- a/packages/wow-ui/package.json +++ b/packages/wow-ui/package.json @@ -40,6 +40,11 @@ "require": "./dist/Switch.cjs", "import": "./dist/Switch.js" }, + "./TextField": { + "types": "./dist/components/TextField/index.d.ts", + "require": "./dist/TextField.cjs", + "import": "./dist/TextField.js" + }, "./Chip": { "types": "./dist/components/Chip/index.d.ts", "require": "./dist/Chip.cjs", diff --git a/packages/wow-ui/panda.config.ts b/packages/wow-ui/panda.config.ts index 98f2c6b8..fb17f32f 100644 --- a/packages/wow-ui/panda.config.ts +++ b/packages/wow-ui/panda.config.ts @@ -1,5 +1,5 @@ import { defineConfig, defineGlobalStyles } from "@pandacss/dev"; -import { semanticTokens, textStyles, tokens } from "theme"; +import { semanticTokens, textStyles, tokens, breakpoints } from "theme"; import { removeUnusedCssVars, removeUnusedKeyframes } from "theme/utils"; const globalCss = defineGlobalStyles({ @@ -32,5 +32,6 @@ export default defineConfig({ tokens, textStyles, semanticTokens, + breakpoints, }, }); diff --git a/packages/wow-ui/rollup.config.js b/packages/wow-ui/rollup.config.js index 4db4d471..bac0b983 100644 --- a/packages/wow-ui/rollup.config.js +++ b/packages/wow-ui/rollup.config.js @@ -24,6 +24,7 @@ export default { Checkbox: "./src/components/Checkbox", Chip: "./src/components/Chip", Switch: "./src/components/Switch", + TextField: "./src/components/TextField", }, output: [ { diff --git a/packages/wow-ui/src/components/Chip/index.tsx b/packages/wow-ui/src/components/Chip/index.tsx index bd9d4145..5a3747f3 100644 --- a/packages/wow-ui/src/components/Chip/index.tsx +++ b/packages/wow-ui/src/components/Chip/index.tsx @@ -74,6 +74,7 @@ const Chip: ChipComponent & { displayName?: string } = forwardRef( isChecked: checkedProp = false, defaultChecked = false, disabled = false, + style, ...rest }: ChipProps, ref: any @@ -110,7 +111,7 @@ const Chip: ChipComponent & { displayName?: string } = forwardRef( aria-label={`chip ${isChecked ? "activated" : "inactivated"}`} data-selected={isChecked} ref={ref} - style={rest.customStyle} + style={style} className={chip({ clickable: disabled ? false : clickable, disabled: disabled, diff --git a/packages/wow-ui/src/components/TextField/TextField.stories.tsx b/packages/wow-ui/src/components/TextField/TextField.stories.tsx new file mode 100644 index 00000000..0df9f750 --- /dev/null +++ b/packages/wow-ui/src/components/TextField/TextField.stories.tsx @@ -0,0 +1,246 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { useState } from "react"; + +import TextField from "@/components/TextField"; + +const meta = { + title: "UI/TextField", + component: TextField, + tags: ["autodocs"], + parameters: { + componentSubtitle: "텍스트필드 컴포넌트", + a11y: { + config: { + rules: [{ id: "color-contrast", enabled: false }], + }, + }, + }, + argTypes: { + label: { + description: "텍스트필드의 라벨입니다.", + table: { + type: { summary: "string" }, + defaultValue: { summary: null }, + }, + control: { + type: "text", + }, + type: { + name: "string", + required: true, + }, + }, + placeholder: { + description: "텍스트필드의 플레이스홀더 텍스트입니다.", + table: { + type: { summary: "string" }, + defaultValue: { summary: null }, + }, + control: { + type: "text", + }, + }, + defaultValue: { + description: "텍스트필드의 기본 값입니다.", + table: { + type: { summary: "string" }, + defaultValue: { summary: null }, + }, + control: { + type: "text", + }, + }, + value: { + description: "외부에서 제어할 활성 상태입니다.", + table: { + type: { summary: "string" }, + defaultValue: { summary: null }, + }, + control: { + type: "text", + }, + }, + maxLength: { + description: "텍스트필드의 최대 입력 길이입니다.", + table: { + type: { summary: "number" }, + defaultValue: { summary: null }, + }, + control: { + type: "number", + }, + }, + error: { + description: "텍스트필드의 오류 상태 여부입니다.", + table: { + type: { summary: "boolean" }, + defaultValue: { summary: false }, + }, + control: { + type: "boolean", + }, + }, + success: { + description: "텍스트필드의 성공 상태 여부입니다.", + table: { + type: { summary: "boolean" }, + defaultValue: { summary: false }, + }, + control: { + type: "boolean", + }, + }, + helperText: { + description: "텍스트필드 아래 추가적인 텍스트입니다.", + table: { + type: { summary: "ReactNode" }, + defaultValue: { summary: null }, + }, + control: { + type: "text", + }, + }, + onChange: { + description: "외부 활성 상태가 변경될 때 호출될 콜백 함수입니다.", + table: { + type: { summary: "(value: string) => void" }, + defaultValue: { summary: null }, + control: { + type: "function", + }, + }, + }, + onBlur: { + description: "텍스트필드가 포커스를 잃을 때 호출될 콜백 함수입니다.", + table: { + type: { summary: "() => void" }, + defaultValue: { summary: null }, + control: { + type: "function", + }, + }, + }, + onFocus: { + description: "텍스트필드가 포커스됐을 때 호출될 콜백 함수입니다.", + table: { + type: { summary: "() => void" }, + defaultValue: { summary: null }, + control: { + type: "function", + }, + }, + }, + textareaProps: { + description: "텍스트필드에 전달할 추가 textarea 속성입니다.", + table: { + type: { summary: "TextareaHTMLAttributes" }, + defaultValue: { summary: null }, + control: { + type: "object", + }, + }, + }, + style: { + description: "텍스트필드의 커스텀 스타일 속성입니다.", + table: { + type: { summary: "CSSProperties" }, + defaultValue: { summary: null }, + control: { + type: "object", + }, + }, + }, + className: { + description: "텍스트필드에 전달하는 커스텀 클래스명입니다.", + table: { + type: { summary: "string" }, + defaultValue: { summary: null }, + control: { + type: "text", + }, + }, + }, + }, +} satisfies Meta; + +export default meta; + +type Story = StoryObj; + +export const Primary: Story = { + args: { + label: "Label", + }, +}; + +export const WithMaxLength: Story = { + args: { + label: "Label", + maxLength: 10, + }, +}; + +export const WithPlaceholder: Story = { + args: { + label: "Label", + placeholder: + "PlaceholderPlaceholderPlaceholderPlaceholderPlaceholderPlaceholderPlaceholder", + }, +}; + +export const WithDefaultValue: Story = { + args: { + label: "Label", + defaultValue: "Text", + }, +}; + +export const WithHelperText: Story = { + args: { + label: "Label", + helperText: "*Caption", + }, +}; + +export const WithMaxLengthAndHelperText: Story = { + args: { + label: "Label", + maxLength: 100, + helperText: "*Caption", + }, +}; + +export const Success: Story = { + args: { + label: "Label", + maxLength: 100, + helperText: "*Caption", + success: true, + }, +}; + +export const Error: Story = { + args: { + label: "Label", + maxLength: 100, + helperText: "*Caption", + error: true, + }, +}; + +const ControlledTextField = () => { + const [value, setValue] = useState(""); + + const handleChange = (value: string) => { + setValue(value); + }; + + return ; +}; + +export const ControlledState: Story = { + render: () => , + args: { + label: "Label", + }, +}; diff --git a/packages/wow-ui/src/components/TextField/TextField.test.tsx b/packages/wow-ui/src/components/TextField/TextField.test.tsx new file mode 100644 index 00000000..abdc2035 --- /dev/null +++ b/packages/wow-ui/src/components/TextField/TextField.test.tsx @@ -0,0 +1,157 @@ +import { fireEvent, render, waitFor } from "@testing-library/react"; +import { useState } from "react"; + +import TextField from "@/components/TextField"; + +describe("TextField component", () => { + it("should render label and placeholder", () => { + const { getByLabelText, getByPlaceholderText } = render( + + ); + const labelText = getByLabelText("Label"); + const placeholderText = getByPlaceholderText("Placeholder"); + + expect(labelText).toBeInTheDocument(); + expect(placeholderText).toBeInTheDocument(); + }); + + it("should render with default value", () => { + const { getByLabelText } = render( + + ); + const textField = getByLabelText("Label"); + + expect(textField).toHaveValue("textField"); + }); + + it("should render helperText", () => { + const { getByText } = render( + + ); + const helperText = getByText("HelperText"); + + expect(helperText).toBeInTheDocument(); + }); + + it("should handle max length correctly", async () => { + const { getByLabelText } = render( + + ); + const textField = getByLabelText("Label") as HTMLTextAreaElement; + + fireEvent.change(textField, { target: { value: "12345678910" } }); + + await waitFor(() => { + expect(textField.value).toHaveLength(5); + }); + }); + + it("should apply typing style while typing", async () => { + const { getByLabelText, getByText } = render(); + const textField = getByLabelText("Label"); + const label = getByText("Label"); + + fireEvent.change(textField, { target: { value: "12345" } }); + + await waitFor(() => { + expect(label).toHaveStyle("color: textBlack"); + expect(textField).toHaveStyle("borderColor: primary"); + expect(textField).toHaveStyle("color: textBlack"); + }); + }); + + it("should apply typed style after typing", async () => { + const { getByLabelText, getByText } = render(); + const textField = getByLabelText("Label"); + const label = getByText("Label"); + + fireEvent.change(textField, { target: { value: "12345" } }); + fireEvent.blur(textField); + + await waitFor(() => { + expect(label).toHaveStyle("color: textBlack"); + expect(textField).toHaveStyle("borderColor: sub"); + expect(textField).toHaveStyle("color: textBlack"); + }); + }); + + it("should apply success style if success is true", () => { + const { getByLabelText, getByText } = render( + + ); + const textField = getByLabelText("Label"); + const label = getByText("Label"); + + expect(label).toHaveStyle("color: textBlack"); + expect(textField).toHaveStyle("borderColor: success"); + expect(textField).toHaveStyle("color: textBlack"); + }); + + it("should apply error style if error is true", () => { + const { getByLabelText, getByText } = render( + + ); + const textField = getByLabelText("Label"); + const label = getByText("Label"); + + expect(label).toHaveStyle("color: textBlack"); + expect(textField).toHaveStyle("borderColor: error"); + expect(textField).toHaveStyle("color: textBlack"); + }); + + it("should display error message with proper aria-describedBy", () => { + const { getByLabelText } = render( + + ); + const textField = getByLabelText("Label"); + + expect(textField).toHaveAttribute( + "aria-describedby", + expect.stringContaining("error-message") + ); + }); + + it("should fire onFocus event when focused", () => { + const handleFocus = jest.fn(); + const { getByLabelText } = render( + + ); + const textField = getByLabelText("Label"); + + fireEvent.focus(textField); + + expect(handleFocus).toHaveBeenCalledTimes(1); + }); + + it("should fire onBlur event when focus is lost", () => { + const handleBlur = jest.fn(); + const { getByLabelText } = render( + + ); + const textField = getByLabelText("Label"); + + fireEvent.click(textField); + fireEvent.blur(textField); + + expect(handleBlur).toHaveBeenCalledTimes(1); + }); +}); + +describe("external control and events", () => { + it("should fire external onChange event", () => { + const Component = () => { + const [value, setValue] = useState("initial value"); + const handleChange = (newValue: string) => setValue(newValue); + + return ; + }; + const { getByLabelText } = render(); + const textField = getByLabelText("Label"); + + expect(textField).toHaveValue("initial value"); + + fireEvent.change(textField, { target: { value: "updated value" } }); + + expect(textField).toHaveValue("updated value"); + }); +}); diff --git a/packages/wow-ui/src/components/TextField/index.tsx b/packages/wow-ui/src/components/TextField/index.tsx new file mode 100644 index 00000000..b84d3da6 --- /dev/null +++ b/packages/wow-ui/src/components/TextField/index.tsx @@ -0,0 +1,339 @@ +"use client"; + +import { cva } from "@styled-system/css"; +import { Flex, styled } from "@styled-system/jsx"; +import type { + ChangeEvent, + CSSProperties, + FocusEvent, + ReactNode, + RefObject, + TextareaHTMLAttributes, +} from "react"; +import { forwardRef, useId, useLayoutEffect, useRef, useState } from "react"; + +import { useTextareaAutosize } from "@/hooks/useTextareaAutosize"; + +type VariantType = "default" | "typing" | "typed" | "success" | "error"; + +/** + * @description 사용자가 텍스트를 입력할 수 있는 텍스트필드 컴포넌트입니다. + * + * @param {string} label 텍스트필드의 라벨. + * @param {string} [placeholder] 텍스트필드의 플레이스홀더 텍스트. + * @param {string} [defaultValue] 텍스트필드의 기본 값. + * @param {string} [value] 외부에서 제어할 활성 상태. + * @param {number} [maxLength] 텍스트필드의 최대 입력 길이. + * @param {boolean} [error] 텍스트필드의 오류 상태 여부. + * @param {boolean} [success] 텍스트필드의 성공 상태 여부. + * @param {ReactNode} [helperText] 텍스트필드 아래 추가적인 텍스트. + * @param {(value: string) => void} [onChange] 외부 활성 상태가 변경될 때 호출될 콜백 함수. + * @param {() => void} [onBlur] 텍스트필드가 포커스를 잃을 때 호출될 콜백 함수. + * @param {() => void} [onFocus] 텍스트필드가 포커스됐을 때 호출될 콜백 함수. + * @param {TextareaHTMLAttributes} [textareaProps] 텍스트필드에 전달할 추가 textarea 속성. + * @param {CSSProperties} [style] 텍스트필드의 커스텀 스타일 속성. + * @param {string} [className] 텍스트필드에 전달하는 커스텀 클래스명. + * @param {ComponentPropsWithoutRef} rest 렌더링된 요소 또는 컴포넌트에 전달할 추가 props. + * @param {ComponentPropsWithRef["ref"]} ref 렌더링된 요소 또는 컴포넌트에 연결할 ref. + */ +export interface TextFieldProps { + label: string; + placeholder?: string; + defaultValue?: string; + value?: string; + maxLength?: number; + error?: boolean; + success?: boolean; + helperText?: ReactNode; + onChange?: (value: string) => void; + onBlur?: () => void; + onFocus?: () => void; + textareaProps?: TextareaHTMLAttributes; + style?: CSSProperties; + className?: string; +} + +const TextField = forwardRef( + ( + { + helperText, + label, + onBlur, + onChange, + onFocus, + placeholder = "", + defaultValue, + value: valueProp, + maxLength, + error = false, + success = false, + textareaProps, + ...rest + }, + ref + ) => { + const id = useId(); + const textareaId = textareaProps?.id || id; + const errorMessageId = `${textareaId}-error-message`; + const helperTextId = `${textareaId}-helper-text`; + const descriptionId = error ? `${errorMessageId}` : `${helperTextId}`; + + const textareaRef = useRef(null); + const textareaElementRef = ref || textareaRef; + + const [value, setValue] = useState(valueProp ?? defaultValue ?? ""); + const [variant, setVariant] = useState("default"); + + useLayoutEffect(() => { + if (success) { + setVariant("success"); + } else if (error) { + setVariant("error"); + } else if (defaultValue) { + setVariant("typed"); + } + }, [defaultValue, error, success]); + + useTextareaAutosize(textareaElementRef as RefObject); + + const handleChange = (e: ChangeEvent) => { + const textareaValue = e.target.value; + setVariant("typing"); + + if (maxLength && textareaValue.length > maxLength) { + setValue(textareaValue.slice(0, maxLength)); + } else { + setValue(textareaValue); + onChange?.(textareaValue); + } + }; + + const handleBlur = (e: FocusEvent) => { + const inputValue = e.target.value; + + if (variant !== "success" && variant !== "error") { + setVariant(inputValue ? "typed" : "default"); + } + onBlur?.(); + }; + + const handleFocus = () => { + if (variant !== "typing") { + setVariant("typing"); + } + onFocus?.(); + }; + + return ( + + + + {maxLength && ( + + )} + + + {helperText && {helperText}} + + ); + } +); + +TextField.displayName = "TextField"; +export default TextField; + +const Label = ({ + variant, + children, + textareaId, +}: { + variant: VariantType; + children: string; + textareaId: string; +}) => ( + + {children} + +); + +const TextLength = ({ + variant, + maxLength, + length, +}: { + variant: VariantType; + maxLength: number; + length: number; +}) => ( + + {length}/{maxLength} + +); + +const HelperText = ({ + variant, + children, +}: { + variant: VariantType; + children: ReactNode; +}) => ( + + {children} + +); + +const containerStyle = cva({ + base: { + lg: { + minWidth: "19.75rem", + maxWidth: "40.75rem", + }, + smDown: { + width: "100%", + }, + }, +}); + +const labelStyle = cva({ + base: { + textStyle: "label2", + }, + variants: { + type: { + default: { + color: "sub", + }, + focused: { + color: "textBlack", + }, + }, + }, +}); + +const textLengthStyle = cva({ + base: { + textStyle: "label3", + }, + variants: { + type: { + default: { + color: "sub", + }, + typing: { + color: "primary", + }, + typed: { + color: "sub", + }, + success: { + color: "success", + }, + error: { + color: "error", + }, + }, + }, +}); + +const textareaStyle = cva({ + base: { + borderRadius: "sm", + borderWidth: "button", + paddingX: "sm", + paddingY: "xs", + textStyle: "body1", + height: "2.625rem", + maxHeight: "7.5rem", + overflowY: "hidden", + resize: "none", + _placeholder: { + color: "outline", + }, + _focus: { + outline: "none", + }, + _scrollbar: { + width: "2px", + }, + _scrollbarThumb: { + width: "2px", + height: "65px", + borderRadius: "sm", + backgroundColor: "outline", + }, + _scrollbarTrack: { + marginTop: "2px", + marginBottom: "2px", + }, + }, + variants: { + type: { + default: { + borderColor: "outline", + color: "outline", + }, + typing: { + borderColor: "primary", + color: "textBlack", + }, + typed: { + borderColor: "sub", + color: "textBlack", + }, + success: { + borderColor: "success", + color: "textBlack", + }, + error: { + borderColor: "error", + color: "textBlack", + }, + }, + }, +}); + +const helperTextStyle = cva({ + base: { + textStyle: "body3", + }, + variants: { + type: { + default: { color: "sub" }, + typing: { color: "sub" }, + typed: { color: "sub" }, + success: { color: "success" }, + error: { color: "error" }, + }, + }, +}); diff --git a/packages/wow-ui/src/hooks/useTextareaAutosize.ts b/packages/wow-ui/src/hooks/useTextareaAutosize.ts new file mode 100644 index 00000000..5d472afd --- /dev/null +++ b/packages/wow-ui/src/hooks/useTextareaAutosize.ts @@ -0,0 +1,42 @@ +import { type RefObject, useEffect } from "react"; + +const TEXTAREA_LINE_HEIGHT = 2.625; +const MAX_TEXTAREA_HEIGHT = 120; + +export const useTextareaAutosize = (ref: RefObject) => { + useEffect(() => { + const textareaElement = ref.current; + if (!textareaElement) return; + + const placeholderLines = textareaElement.placeholder.split("\n").length; + const placeholderHeight = placeholderLines * TEXTAREA_LINE_HEIGHT; + + const setInitialHeight = () => { + textareaElement.style.height = `${placeholderHeight}rem`; + }; + + const handleResize = () => { + textareaElement.style.height = "auto"; + textareaElement.style.height = `${Math.max(placeholderHeight, textareaElement.scrollHeight)}px`; + }; + + const handleOverflow = () => { + textareaElement.style.overflowY = + textareaElement.scrollHeight > MAX_TEXTAREA_HEIGHT + ? "scroll" + : "hidden"; + }; + + setInitialHeight(); + handleResize(); + handleOverflow(); + + textareaElement.addEventListener("input", handleResize); + textareaElement.addEventListener("input", handleOverflow); + + return () => { + textareaElement.removeEventListener("input", handleResize); + textareaElement.removeEventListener("input", handleOverflow); + }; + }, [ref]); +}; diff --git a/packages/wow-ui/styled-system/css/conditions.js b/packages/wow-ui/styled-system/css/conditions.js index 33673f36..df6eed4e 100644 --- a/packages/wow-ui/styled-system/css/conditions.js +++ b/packages/wow-ui/styled-system/css/conditions.js @@ -1,7 +1,7 @@ import { withoutSpace } from "../helpers.js"; const conditionsStr = - "_hover,_focus,_focusWithin,_focusVisible,_disabled,_active,_visited,_target,_readOnly,_readWrite,_empty,_checked,_enabled,_expanded,_highlighted,_before,_after,_firstLetter,_firstLine,_marker,_selection,_file,_backdrop,_first,_last,_only,_even,_odd,_firstOfType,_lastOfType,_onlyOfType,_peerFocus,_peerHover,_peerActive,_peerFocusWithin,_peerFocusVisible,_peerDisabled,_peerChecked,_peerInvalid,_peerExpanded,_peerPlaceholderShown,_groupFocus,_groupHover,_groupActive,_groupFocusWithin,_groupFocusVisible,_groupDisabled,_groupChecked,_groupExpanded,_groupInvalid,_indeterminate,_required,_valid,_invalid,_autofill,_inRange,_outOfRange,_placeholder,_placeholderShown,_pressed,_selected,_default,_optional,_open,_closed,_fullscreen,_loading,_currentPage,_currentStep,_motionReduce,_motionSafe,_print,_landscape,_portrait,_dark,_light,_osDark,_osLight,_highContrast,_lessContrast,_moreContrast,_ltr,_rtl,_scrollbar,_scrollbarThumb,_scrollbarTrack,_horizontal,_vertical,_starting,sm,smOnly,smDown,md,mdOnly,mdDown,lg,lgOnly,lgDown,xl,xlOnly,xlDown,2xl,2xlOnly,2xlDown,smToMd,smToLg,smToXl,smTo2xl,mdToLg,mdToXl,mdTo2xl,lgToXl,lgTo2xl,xlTo2xl,@/xs,@/sm,@/md,@/lg,@/xl,@/2xl,@/3xl,@/4xl,@/5xl,@/6xl,@/7xl,@/8xl,base"; + "_hover,_focus,_focusWithin,_focusVisible,_disabled,_active,_visited,_target,_readOnly,_readWrite,_empty,_checked,_enabled,_expanded,_highlighted,_before,_after,_firstLetter,_firstLine,_marker,_selection,_file,_backdrop,_first,_last,_only,_even,_odd,_firstOfType,_lastOfType,_onlyOfType,_peerFocus,_peerHover,_peerActive,_peerFocusWithin,_peerFocusVisible,_peerDisabled,_peerChecked,_peerInvalid,_peerExpanded,_peerPlaceholderShown,_groupFocus,_groupHover,_groupActive,_groupFocusWithin,_groupFocusVisible,_groupDisabled,_groupChecked,_groupExpanded,_groupInvalid,_indeterminate,_required,_valid,_invalid,_autofill,_inRange,_outOfRange,_placeholder,_placeholderShown,_pressed,_selected,_default,_optional,_open,_closed,_fullscreen,_loading,_currentPage,_currentStep,_motionReduce,_motionSafe,_print,_landscape,_portrait,_dark,_light,_osDark,_osLight,_highContrast,_lessContrast,_moreContrast,_ltr,_rtl,_scrollbar,_scrollbarThumb,_scrollbarTrack,_horizontal,_vertical,_starting,xs,xsOnly,xsDown,sm,smOnly,smDown,md,mdOnly,mdDown,lg,lgOnly,lgDown,xsToSm,xsToMd,xsToLg,smToMd,smToLg,mdToLg,@/xs,@/sm,@/md,@/lg,@/xl,@/2xl,@/3xl,@/4xl,@/5xl,@/6xl,@/7xl,@/8xl,base"; const conditions = new Set(conditionsStr.split(",")); export function isCondition(value) { diff --git a/packages/wow-ui/styled-system/css/css.js b/packages/wow-ui/styled-system/css/css.js index 1a2225fe..26dbe225 100644 --- a/packages/wow-ui/styled-system/css/css.js +++ b/packages/wow-ui/styled-system/css/css.js @@ -28,7 +28,7 @@ const context = { conditions: { shift: sortConditions, finalize: finalizeConditions, - breakpoints: { keys: ["base", "sm", "md", "lg", "xl", "2xl"] }, + breakpoints: { keys: ["base", "xs", "sm", "md", "lg"] }, }, utility: { transform: (prop, value) => { diff --git a/packages/wow-ui/styled-system/jsx/is-valid-prop.js b/packages/wow-ui/styled-system/jsx/is-valid-prop.js index 0cb2bac4..7ea7b401 100644 --- a/packages/wow-ui/styled-system/jsx/is-valid-prop.js +++ b/packages/wow-ui/styled-system/jsx/is-valid-prop.js @@ -2,7 +2,7 @@ import { splitProps } from "../helpers.js"; import { memo } from "../helpers.js"; // src/index.ts var userGeneratedStr = - "css,pos,insetX,insetY,insetEnd,end,insetStart,start,flexDir,p,pl,pr,pt,pb,py,paddingY,paddingX,px,pe,paddingEnd,ps,paddingStart,ml,mr,mt,mb,m,my,marginY,mx,marginX,me,marginEnd,ms,marginStart,ringWidth,ringColor,ring,ringOffset,w,minW,maxW,h,minH,maxH,textShadowColor,bgPosition,bgPositionX,bgPositionY,bgAttachment,bgClip,bg,bgColor,bgOrigin,bgImage,bgRepeat,bgBlendMode,bgSize,bgGradient,rounded,roundedTopLeft,roundedTopRight,roundedBottomRight,roundedBottomLeft,roundedTop,roundedRight,roundedBottom,roundedLeft,roundedStartStart,roundedStartEnd,roundedStart,roundedEndStart,roundedEndEnd,roundedEnd,borderX,borderXWidth,borderXColor,borderY,borderYWidth,borderYColor,borderStart,borderStartWidth,borderStartColor,borderEnd,borderEndWidth,borderEndColor,shadow,shadowColor,x,y,z,scrollMarginY,scrollMarginX,scrollPaddingY,scrollPaddingX,aspectRatio,boxDecorationBreak,zIndex,boxSizing,objectPosition,objectFit,overscrollBehavior,overscrollBehaviorX,overscrollBehaviorY,position,top,left,insetInline,insetBlock,inset,insetBlockEnd,insetBlockStart,insetInlineEnd,insetInlineStart,right,bottom,float,visibility,display,hideFrom,hideBelow,flexBasis,flex,flexDirection,flexGrow,flexShrink,gridTemplateColumns,gridTemplateRows,gridColumn,gridRow,gridColumnStart,gridColumnEnd,gridAutoFlow,gridAutoColumns,gridAutoRows,gap,gridGap,gridRowGap,gridColumnGap,rowGap,columnGap,justifyContent,alignContent,alignItems,alignSelf,padding,paddingLeft,paddingRight,paddingTop,paddingBottom,paddingBlock,paddingBlockEnd,paddingBlockStart,paddingInline,paddingInlineEnd,paddingInlineStart,marginLeft,marginRight,marginTop,marginBottom,margin,marginBlock,marginBlockEnd,marginBlockStart,marginInline,marginInlineEnd,marginInlineStart,spaceX,spaceY,outlineWidth,outlineColor,outline,outlineOffset,divideX,divideY,divideColor,divideStyle,width,inlineSize,minWidth,minInlineSize,maxWidth,maxInlineSize,height,blockSize,minHeight,minBlockSize,maxHeight,maxBlockSize,color,fontFamily,fontSize,fontWeight,fontSmoothing,fontVariantNumeric,letterSpacing,lineHeight,textAlign,textDecoration,textDecorationColor,textEmphasisColor,textDecorationStyle,textDecorationThickness,textUnderlineOffset,textTransform,textIndent,textShadow,textOverflow,verticalAlign,wordBreak,textWrap,truncate,lineClamp,listStyleType,listStylePosition,listStyleImage,backgroundPosition,backgroundPositionX,backgroundPositionY,backgroundAttachment,backgroundClip,background,backgroundColor,backgroundOrigin,backgroundImage,backgroundRepeat,backgroundBlendMode,backgroundSize,backgroundGradient,textGradient,gradientFromPosition,gradientToPosition,gradientFrom,gradientTo,gradientVia,gradientViaPosition,borderRadius,borderTopLeftRadius,borderTopRightRadius,borderBottomRightRadius,borderBottomLeftRadius,borderTopRadius,borderRightRadius,borderBottomRadius,borderLeftRadius,borderStartStartRadius,borderStartEndRadius,borderStartRadius,borderEndStartRadius,borderEndEndRadius,borderEndRadius,border,borderWidth,borderTopWidth,borderLeftWidth,borderRightWidth,borderBottomWidth,borderColor,borderInline,borderInlineWidth,borderInlineColor,borderBlock,borderBlockWidth,borderBlockColor,borderLeft,borderLeftColor,borderInlineStart,borderInlineStartWidth,borderInlineStartColor,borderRight,borderRightColor,borderInlineEnd,borderInlineEndWidth,borderInlineEndColor,borderTop,borderTopColor,borderBottom,borderBottomColor,borderBlockEnd,borderBlockEndColor,borderBlockStart,borderBlockStartColor,opacity,boxShadow,boxShadowColor,mixBlendMode,filter,brightness,contrast,grayscale,hueRotate,invert,saturate,sepia,dropShadow,blur,backdropFilter,backdropBlur,backdropBrightness,backdropContrast,backdropGrayscale,backdropHueRotate,backdropInvert,backdropOpacity,backdropSaturate,backdropSepia,borderCollapse,borderSpacing,borderSpacingX,borderSpacingY,tableLayout,transitionTimingFunction,transitionDelay,transitionDuration,transitionProperty,transition,animation,animationName,animationTimingFunction,animationDuration,animationDelay,transformOrigin,rotate,rotateX,rotateY,rotateZ,scale,scaleX,scaleY,translate,translateX,translateY,translateZ,accentColor,caretColor,scrollBehavior,scrollbar,scrollMargin,scrollMarginLeft,scrollMarginRight,scrollMarginTop,scrollMarginBottom,scrollMarginBlock,scrollMarginBlockEnd,scrollMarginBlockStart,scrollMarginInline,scrollMarginInlineEnd,scrollMarginInlineStart,scrollPadding,scrollPaddingBlock,scrollPaddingBlockStart,scrollPaddingBlockEnd,scrollPaddingInline,scrollPaddingInlineEnd,scrollPaddingInlineStart,scrollPaddingLeft,scrollPaddingRight,scrollPaddingTop,scrollPaddingBottom,scrollSnapAlign,scrollSnapStop,scrollSnapType,scrollSnapStrictness,scrollSnapMargin,scrollSnapMarginTop,scrollSnapMarginBottom,scrollSnapMarginLeft,scrollSnapMarginRight,touchAction,userSelect,fill,stroke,strokeWidth,srOnly,debug,appearance,backfaceVisibility,clipPath,hyphens,mask,maskImage,maskSize,textSizeAdjust,container,containerName,containerType,colorPalette,_hover,_focus,_focusWithin,_focusVisible,_disabled,_active,_visited,_target,_readOnly,_readWrite,_empty,_checked,_enabled,_expanded,_highlighted,_before,_after,_firstLetter,_firstLine,_marker,_selection,_file,_backdrop,_first,_last,_only,_even,_odd,_firstOfType,_lastOfType,_onlyOfType,_peerFocus,_peerHover,_peerActive,_peerFocusWithin,_peerFocusVisible,_peerDisabled,_peerChecked,_peerInvalid,_peerExpanded,_peerPlaceholderShown,_groupFocus,_groupHover,_groupActive,_groupFocusWithin,_groupFocusVisible,_groupDisabled,_groupChecked,_groupExpanded,_groupInvalid,_indeterminate,_required,_valid,_invalid,_autofill,_inRange,_outOfRange,_placeholder,_placeholderShown,_pressed,_selected,_default,_optional,_open,_closed,_fullscreen,_loading,_currentPage,_currentStep,_motionReduce,_motionSafe,_print,_landscape,_portrait,_dark,_light,_osDark,_osLight,_highContrast,_lessContrast,_moreContrast,_ltr,_rtl,_scrollbar,_scrollbarThumb,_scrollbarTrack,_horizontal,_vertical,_starting,sm,smOnly,smDown,md,mdOnly,mdDown,lg,lgOnly,lgDown,xl,xlOnly,xlDown,2xl,2xlOnly,2xlDown,smToMd,smToLg,smToXl,smTo2xl,mdToLg,mdToXl,mdTo2xl,lgToXl,lgTo2xl,xlTo2xl,@/xs,@/sm,@/md,@/lg,@/xl,@/2xl,@/3xl,@/4xl,@/5xl,@/6xl,@/7xl,@/8xl,textStyle"; + "css,pos,insetX,insetY,insetEnd,end,insetStart,start,flexDir,p,pl,pr,pt,pb,py,paddingY,paddingX,px,pe,paddingEnd,ps,paddingStart,ml,mr,mt,mb,m,my,marginY,mx,marginX,me,marginEnd,ms,marginStart,ringWidth,ringColor,ring,ringOffset,w,minW,maxW,h,minH,maxH,textShadowColor,bgPosition,bgPositionX,bgPositionY,bgAttachment,bgClip,bg,bgColor,bgOrigin,bgImage,bgRepeat,bgBlendMode,bgSize,bgGradient,rounded,roundedTopLeft,roundedTopRight,roundedBottomRight,roundedBottomLeft,roundedTop,roundedRight,roundedBottom,roundedLeft,roundedStartStart,roundedStartEnd,roundedStart,roundedEndStart,roundedEndEnd,roundedEnd,borderX,borderXWidth,borderXColor,borderY,borderYWidth,borderYColor,borderStart,borderStartWidth,borderStartColor,borderEnd,borderEndWidth,borderEndColor,shadow,shadowColor,x,y,z,scrollMarginY,scrollMarginX,scrollPaddingY,scrollPaddingX,aspectRatio,boxDecorationBreak,zIndex,boxSizing,objectPosition,objectFit,overscrollBehavior,overscrollBehaviorX,overscrollBehaviorY,position,top,left,insetInline,insetBlock,inset,insetBlockEnd,insetBlockStart,insetInlineEnd,insetInlineStart,right,bottom,float,visibility,display,hideFrom,hideBelow,flexBasis,flex,flexDirection,flexGrow,flexShrink,gridTemplateColumns,gridTemplateRows,gridColumn,gridRow,gridColumnStart,gridColumnEnd,gridAutoFlow,gridAutoColumns,gridAutoRows,gap,gridGap,gridRowGap,gridColumnGap,rowGap,columnGap,justifyContent,alignContent,alignItems,alignSelf,padding,paddingLeft,paddingRight,paddingTop,paddingBottom,paddingBlock,paddingBlockEnd,paddingBlockStart,paddingInline,paddingInlineEnd,paddingInlineStart,marginLeft,marginRight,marginTop,marginBottom,margin,marginBlock,marginBlockEnd,marginBlockStart,marginInline,marginInlineEnd,marginInlineStart,spaceX,spaceY,outlineWidth,outlineColor,outline,outlineOffset,divideX,divideY,divideColor,divideStyle,width,inlineSize,minWidth,minInlineSize,maxWidth,maxInlineSize,height,blockSize,minHeight,minBlockSize,maxHeight,maxBlockSize,color,fontFamily,fontSize,fontWeight,fontSmoothing,fontVariantNumeric,letterSpacing,lineHeight,textAlign,textDecoration,textDecorationColor,textEmphasisColor,textDecorationStyle,textDecorationThickness,textUnderlineOffset,textTransform,textIndent,textShadow,textOverflow,verticalAlign,wordBreak,textWrap,truncate,lineClamp,listStyleType,listStylePosition,listStyleImage,backgroundPosition,backgroundPositionX,backgroundPositionY,backgroundAttachment,backgroundClip,background,backgroundColor,backgroundOrigin,backgroundImage,backgroundRepeat,backgroundBlendMode,backgroundSize,backgroundGradient,textGradient,gradientFromPosition,gradientToPosition,gradientFrom,gradientTo,gradientVia,gradientViaPosition,borderRadius,borderTopLeftRadius,borderTopRightRadius,borderBottomRightRadius,borderBottomLeftRadius,borderTopRadius,borderRightRadius,borderBottomRadius,borderLeftRadius,borderStartStartRadius,borderStartEndRadius,borderStartRadius,borderEndStartRadius,borderEndEndRadius,borderEndRadius,border,borderWidth,borderTopWidth,borderLeftWidth,borderRightWidth,borderBottomWidth,borderColor,borderInline,borderInlineWidth,borderInlineColor,borderBlock,borderBlockWidth,borderBlockColor,borderLeft,borderLeftColor,borderInlineStart,borderInlineStartWidth,borderInlineStartColor,borderRight,borderRightColor,borderInlineEnd,borderInlineEndWidth,borderInlineEndColor,borderTop,borderTopColor,borderBottom,borderBottomColor,borderBlockEnd,borderBlockEndColor,borderBlockStart,borderBlockStartColor,opacity,boxShadow,boxShadowColor,mixBlendMode,filter,brightness,contrast,grayscale,hueRotate,invert,saturate,sepia,dropShadow,blur,backdropFilter,backdropBlur,backdropBrightness,backdropContrast,backdropGrayscale,backdropHueRotate,backdropInvert,backdropOpacity,backdropSaturate,backdropSepia,borderCollapse,borderSpacing,borderSpacingX,borderSpacingY,tableLayout,transitionTimingFunction,transitionDelay,transitionDuration,transitionProperty,transition,animation,animationName,animationTimingFunction,animationDuration,animationDelay,transformOrigin,rotate,rotateX,rotateY,rotateZ,scale,scaleX,scaleY,translate,translateX,translateY,translateZ,accentColor,caretColor,scrollBehavior,scrollbar,scrollMargin,scrollMarginLeft,scrollMarginRight,scrollMarginTop,scrollMarginBottom,scrollMarginBlock,scrollMarginBlockEnd,scrollMarginBlockStart,scrollMarginInline,scrollMarginInlineEnd,scrollMarginInlineStart,scrollPadding,scrollPaddingBlock,scrollPaddingBlockStart,scrollPaddingBlockEnd,scrollPaddingInline,scrollPaddingInlineEnd,scrollPaddingInlineStart,scrollPaddingLeft,scrollPaddingRight,scrollPaddingTop,scrollPaddingBottom,scrollSnapAlign,scrollSnapStop,scrollSnapType,scrollSnapStrictness,scrollSnapMargin,scrollSnapMarginTop,scrollSnapMarginBottom,scrollSnapMarginLeft,scrollSnapMarginRight,touchAction,userSelect,fill,stroke,strokeWidth,srOnly,debug,appearance,backfaceVisibility,clipPath,hyphens,mask,maskImage,maskSize,textSizeAdjust,container,containerName,containerType,colorPalette,_hover,_focus,_focusWithin,_focusVisible,_disabled,_active,_visited,_target,_readOnly,_readWrite,_empty,_checked,_enabled,_expanded,_highlighted,_before,_after,_firstLetter,_firstLine,_marker,_selection,_file,_backdrop,_first,_last,_only,_even,_odd,_firstOfType,_lastOfType,_onlyOfType,_peerFocus,_peerHover,_peerActive,_peerFocusWithin,_peerFocusVisible,_peerDisabled,_peerChecked,_peerInvalid,_peerExpanded,_peerPlaceholderShown,_groupFocus,_groupHover,_groupActive,_groupFocusWithin,_groupFocusVisible,_groupDisabled,_groupChecked,_groupExpanded,_groupInvalid,_indeterminate,_required,_valid,_invalid,_autofill,_inRange,_outOfRange,_placeholder,_placeholderShown,_pressed,_selected,_default,_optional,_open,_closed,_fullscreen,_loading,_currentPage,_currentStep,_motionReduce,_motionSafe,_print,_landscape,_portrait,_dark,_light,_osDark,_osLight,_highContrast,_lessContrast,_moreContrast,_ltr,_rtl,_scrollbar,_scrollbarThumb,_scrollbarTrack,_horizontal,_vertical,_starting,xs,xsOnly,xsDown,sm,smOnly,smDown,md,mdOnly,mdDown,lg,lgOnly,lgDown,xsToSm,xsToMd,xsToLg,smToMd,smToLg,mdToLg,@/xs,@/sm,@/md,@/lg,@/xl,@/2xl,@/3xl,@/4xl,@/5xl,@/6xl,@/7xl,@/8xl,textStyle"; var userGenerated = userGeneratedStr.split(","); var cssPropertiesStr = "WebkitAppearance,WebkitBorderBefore,WebkitBorderBeforeColor,WebkitBorderBeforeStyle,WebkitBorderBeforeWidth,WebkitBoxReflect,WebkitLineClamp,WebkitMask,WebkitMaskAttachment,WebkitMaskClip,WebkitMaskComposite,WebkitMaskImage,WebkitMaskOrigin,WebkitMaskPosition,WebkitMaskPositionX,WebkitMaskPositionY,WebkitMaskRepeat,WebkitMaskRepeatX,WebkitMaskRepeatY,WebkitMaskSize,WebkitOverflowScrolling,WebkitTapHighlightColor,WebkitTextFillColor,WebkitTextStroke,WebkitTextStrokeColor,WebkitTextStrokeWidth,WebkitTouchCallout,WebkitUserModify,accentColor,alignContent,alignItems,alignSelf,alignTracks,all,animation,animationComposition,animationDelay,animationDirection,animationDuration,animationFillMode,animationIterationCount,animationName,animationPlayState,animationRange,animationRangeEnd,animationRangeStart,animationTimingFunction,animationTimeline,appearance,aspectRatio,azimuth,backdropFilter,backfaceVisibility,background,backgroundAttachment,backgroundBlendMode,backgroundClip,backgroundColor,backgroundImage,backgroundOrigin,backgroundPosition,backgroundPositionX,backgroundPositionY,backgroundRepeat,backgroundSize,blockSize,border,borderBlock,borderBlockColor,borderBlockStyle,borderBlockWidth,borderBlockEnd,borderBlockEndColor,borderBlockEndStyle,borderBlockEndWidth,borderBlockStart,borderBlockStartColor,borderBlockStartStyle,borderBlockStartWidth,borderBottom,borderBottomColor,borderBottomLeftRadius,borderBottomRightRadius,borderBottomStyle,borderBottomWidth,borderCollapse,borderColor,borderEndEndRadius,borderEndStartRadius,borderImage,borderImageOutset,borderImageRepeat,borderImageSlice,borderImageSource,borderImageWidth,borderInline,borderInlineEnd,borderInlineColor,borderInlineStyle,borderInlineWidth,borderInlineEndColor,borderInlineEndStyle,borderInlineEndWidth,borderInlineStart,borderInlineStartColor,borderInlineStartStyle,borderInlineStartWidth,borderLeft,borderLeftColor,borderLeftStyle,borderLeftWidth,borderRadius,borderRight,borderRightColor,borderRightStyle,borderRightWidth,borderSpacing,borderStartEndRadius,borderStartStartRadius,borderStyle,borderTop,borderTopColor,borderTopLeftRadius,borderTopRightRadius,borderTopStyle,borderTopWidth,borderWidth,bottom,boxAlign,boxDecorationBreak,boxDirection,boxFlex,boxFlexGroup,boxLines,boxOrdinalGroup,boxOrient,boxPack,boxShadow,boxSizing,breakAfter,breakBefore,breakInside,captionSide,caret,caretColor,caretShape,clear,clip,clipPath,color,colorScheme,columnCount,columnFill,columnGap,columnRule,columnRuleColor,columnRuleStyle,columnRuleWidth,columnSpan,columnWidth,columns,contain,containIntrinsicSize,containIntrinsicBlockSize,containIntrinsicHeight,containIntrinsicInlineSize,containIntrinsicWidth,container,containerName,containerType,content,contentVisibility,counterIncrement,counterReset,counterSet,cursor,direction,display,emptyCells,filter,flex,flexBasis,flexDirection,flexFlow,flexGrow,flexShrink,flexWrap,float,font,fontFamily,fontFeatureSettings,fontKerning,fontLanguageOverride,fontOpticalSizing,fontPalette,fontVariationSettings,fontSize,fontSizeAdjust,fontSmooth,fontStretch,fontStyle,fontSynthesis,fontSynthesisPosition,fontSynthesisSmallCaps,fontSynthesisStyle,fontSynthesisWeight,fontVariant,fontVariantAlternates,fontVariantCaps,fontVariantEastAsian,fontVariantEmoji,fontVariantLigatures,fontVariantNumeric,fontVariantPosition,fontWeight,forcedColorAdjust,gap,grid,gridArea,gridAutoColumns,gridAutoFlow,gridAutoRows,gridColumn,gridColumnEnd,gridColumnGap,gridColumnStart,gridGap,gridRow,gridRowEnd,gridRowGap,gridRowStart,gridTemplate,gridTemplateAreas,gridTemplateColumns,gridTemplateRows,hangingPunctuation,height,hyphenateCharacter,hyphenateLimitChars,hyphens,imageOrientation,imageRendering,imageResolution,imeMode,initialLetter,initialLetterAlign,inlineSize,inputSecurity,inset,insetBlock,insetBlockEnd,insetBlockStart,insetInline,insetInlineEnd,insetInlineStart,isolation,justifyContent,justifyItems,justifySelf,justifyTracks,left,letterSpacing,lineBreak,lineClamp,lineHeight,lineHeightStep,listStyle,listStyleImage,listStylePosition,listStyleType,margin,marginBlock,marginBlockEnd,marginBlockStart,marginBottom,marginInline,marginInlineEnd,marginInlineStart,marginLeft,marginRight,marginTop,marginTrim,mask,maskBorder,maskBorderMode,maskBorderOutset,maskBorderRepeat,maskBorderSlice,maskBorderSource,maskBorderWidth,maskClip,maskComposite,maskImage,maskMode,maskOrigin,maskPosition,maskRepeat,maskSize,maskType,masonryAutoFlow,mathDepth,mathShift,mathStyle,maxBlockSize,maxHeight,maxInlineSize,maxLines,maxWidth,minBlockSize,minHeight,minInlineSize,minWidth,mixBlendMode,objectFit,objectPosition,offset,offsetAnchor,offsetDistance,offsetPath,offsetPosition,offsetRotate,opacity,order,orphans,outline,outlineColor,outlineOffset,outlineStyle,outlineWidth,overflow,overflowAnchor,overflowBlock,overflowClipBox,overflowClipMargin,overflowInline,overflowWrap,overflowX,overflowY,overlay,overscrollBehavior,overscrollBehaviorBlock,overscrollBehaviorInline,overscrollBehaviorX,overscrollBehaviorY,padding,paddingBlock,paddingBlockEnd,paddingBlockStart,paddingBottom,paddingInline,paddingInlineEnd,paddingInlineStart,paddingLeft,paddingRight,paddingTop,page,pageBreakAfter,pageBreakBefore,pageBreakInside,paintOrder,perspective,perspectiveOrigin,placeContent,placeItems,placeSelf,pointerEvents,position,printColorAdjust,quotes,resize,right,rotate,rowGap,rubyAlign,rubyMerge,rubyPosition,scale,scrollbarColor,scrollbarGutter,scrollbarWidth,scrollBehavior,scrollMargin,scrollMarginBlock,scrollMarginBlockStart,scrollMarginBlockEnd,scrollMarginBottom,scrollMarginInline,scrollMarginInlineStart,scrollMarginInlineEnd,scrollMarginLeft,scrollMarginRight,scrollMarginTop,scrollPadding,scrollPaddingBlock,scrollPaddingBlockStart,scrollPaddingBlockEnd,scrollPaddingBottom,scrollPaddingInline,scrollPaddingInlineStart,scrollPaddingInlineEnd,scrollPaddingLeft,scrollPaddingRight,scrollPaddingTop,scrollSnapAlign,scrollSnapCoordinate,scrollSnapDestination,scrollSnapPointsX,scrollSnapPointsY,scrollSnapStop,scrollSnapType,scrollSnapTypeX,scrollSnapTypeY,scrollTimeline,scrollTimelineAxis,scrollTimelineName,shapeImageThreshold,shapeMargin,shapeOutside,tabSize,tableLayout,textAlign,textAlignLast,textCombineUpright,textDecoration,textDecorationColor,textDecorationLine,textDecorationSkip,textDecorationSkipInk,textDecorationStyle,textDecorationThickness,textEmphasis,textEmphasisColor,textEmphasisPosition,textEmphasisStyle,textIndent,textJustify,textOrientation,textOverflow,textRendering,textShadow,textSizeAdjust,textTransform,textUnderlineOffset,textUnderlinePosition,textWrap,timelineScope,top,touchAction,transform,transformBox,transformOrigin,transformStyle,transition,transitionBehavior,transitionDelay,transitionDuration,transitionProperty,transitionTimingFunction,translate,unicodeBidi,userSelect,verticalAlign,viewTimeline,viewTimelineAxis,viewTimelineInset,viewTimelineName,viewTransitionName,visibility,whiteSpace,whiteSpaceCollapse,widows,width,willChange,wordBreak,wordSpacing,wordWrap,writingMode,zIndex,zoom,alignmentBaseline,baselineShift,clipRule,colorInterpolation,colorRendering,dominantBaseline,fill,fillOpacity,fillRule,floodColor,floodOpacity,glyphOrientationVertical,lightingColor,marker,markerEnd,markerMid,markerStart,shapeRendering,stopColor,stopOpacity,stroke,strokeDasharray,strokeDashoffset,strokeLinecap,strokeLinejoin,strokeMiterlimit,strokeOpacity,strokeWidth,textAnchor,vectorEffect"; diff --git a/packages/wow-ui/styled-system/tokens/index.js b/packages/wow-ui/styled-system/tokens/index.js index f64f4759..13fd81a0 100644 --- a/packages/wow-ui/styled-system/tokens/index.js +++ b/packages/wow-ui/styled-system/tokens/index.js @@ -339,56 +339,48 @@ const tokens = { value: "1.2px", variable: "var(--border-widths-arrow)", }, + "breakpoints.xs": { + value: "360px", + variable: "var(--breakpoints-xs)", + }, "breakpoints.sm": { - value: "640px", + value: "600px", variable: "var(--breakpoints-sm)", }, "breakpoints.md": { - value: "768px", + value: "900px", variable: "var(--breakpoints-md)", }, "breakpoints.lg": { - value: "1024px", - variable: "var(--breakpoints-lg)", - }, - "breakpoints.xl": { value: "1280px", - variable: "var(--breakpoints-xl)", + variable: "var(--breakpoints-lg)", }, - "breakpoints.2xl": { - value: "1536px", - variable: "var(--breakpoints-2xl)", + "sizes.breakpoint-xs": { + value: "360px", + variable: "var(--sizes-breakpoint-xs)", }, "sizes.breakpoint-sm": { - value: "640px", + value: "600px", variable: "var(--sizes-breakpoint-sm)", }, "sizes.breakpoint-md": { - value: "768px", + value: "900px", variable: "var(--sizes-breakpoint-md)", }, "sizes.breakpoint-lg": { - value: "1024px", - variable: "var(--sizes-breakpoint-lg)", - }, - "sizes.breakpoint-xl": { value: "1280px", - variable: "var(--sizes-breakpoint-xl)", - }, - "sizes.breakpoint-2xl": { - value: "1536px", - variable: "var(--sizes-breakpoint-2xl)", + variable: "var(--sizes-breakpoint-lg)", }, "colors.primary": { value: "#368FF7", variable: "var(--colors-primary)", }, "colors.success": { - value: "#34A853", + value: "#2A8642", variable: "var(--colors-success)", }, "colors.error": { - value: "#EA4335", + value: "#BB362A", variable: "var(--colors-error)", }, "colors.backgroundNormal": { @@ -404,7 +396,7 @@ const tokens = { variable: "var(--colors-background-dimmer)", }, "colors.sub": { - value: "#8F8F8F", + value: "#6B6B6B", variable: "var(--colors-sub)", }, "colors.outline": { diff --git a/packages/wow-ui/styled-system/tokens/tokens.d.ts b/packages/wow-ui/styled-system/tokens/tokens.d.ts index 0b1546dc..73c94022 100644 --- a/packages/wow-ui/styled-system/tokens/tokens.d.ts +++ b/packages/wow-ui/styled-system/tokens/tokens.d.ts @@ -85,16 +85,14 @@ export type Token = | "radii.full" | "borderWidths.button" | "borderWidths.arrow" + | "breakpoints.xs" | "breakpoints.sm" | "breakpoints.md" | "breakpoints.lg" - | "breakpoints.xl" - | "breakpoints.2xl" + | "sizes.breakpoint-xs" | "sizes.breakpoint-sm" | "sizes.breakpoint-md" | "sizes.breakpoint-lg" - | "sizes.breakpoint-xl" - | "sizes.breakpoint-2xl" | "colors.primary" | "colors.success" | "colors.error" @@ -304,14 +302,13 @@ export type RadiusToken = "sm" | "md" | "full"; export type BorderWidthToken = "button" | "arrow"; -export type BreakpointToken = "sm" | "md" | "lg" | "xl" | "2xl"; +export type BreakpointToken = "xs" | "sm" | "md" | "lg"; export type SizeToken = + | "breakpoint-xs" | "breakpoint-sm" | "breakpoint-md" - | "breakpoint-lg" - | "breakpoint-xl" - | "breakpoint-2xl"; + | "breakpoint-lg"; export type Tokens = { colors: ColorToken; diff --git a/packages/wow-ui/styled-system/types/conditions.d.ts b/packages/wow-ui/styled-system/types/conditions.d.ts index b2e811bb..c31b9f52 100644 --- a/packages/wow-ui/styled-system/types/conditions.d.ts +++ b/packages/wow-ui/styled-system/types/conditions.d.ts @@ -180,56 +180,42 @@ export interface Conditions { _vertical: string; /** `@starting-style` */ _starting: string; - /** `@media screen and (min-width: 40rem)` */ + /** `@media screen and (min-width: 22.5rem)` */ + xs: string; + /** `@media screen and (min-width: 22.5rem) and (max-width: 37.4975rem)` */ + xsOnly: string; + /** `@media screen and (max-width: 22.4975rem)` */ + xsDown: string; + /** `@media screen and (min-width: 37.5rem)` */ sm: string; - /** `@media screen and (min-width: 40rem) and (max-width: 47.9975rem)` */ + /** `@media screen and (min-width: 37.5rem) and (max-width: 56.2475rem)` */ smOnly: string; - /** `@media screen and (max-width: 39.9975rem)` */ + /** `@media screen and (max-width: 37.4975rem)` */ smDown: string; - /** `@media screen and (min-width: 48rem)` */ + /** `@media screen and (min-width: 56.25rem)` */ md: string; - /** `@media screen and (min-width: 48rem) and (max-width: 63.9975rem)` */ + /** `@media screen and (min-width: 56.25rem) and (max-width: 79.9975rem)` */ mdOnly: string; - /** `@media screen and (max-width: 47.9975rem)` */ + /** `@media screen and (max-width: 56.2475rem)` */ mdDown: string; - /** `@media screen and (min-width: 64rem)` */ + /** `@media screen and (min-width: 80rem)` */ lg: string; - /** `@media screen and (min-width: 64rem) and (max-width: 79.9975rem)` */ - lgOnly: string; - /** `@media screen and (max-width: 63.9975rem)` */ - lgDown: string; /** `@media screen and (min-width: 80rem)` */ - xl: string; - /** `@media screen and (min-width: 80rem) and (max-width: 95.9975rem)` */ - xlOnly: string; + lgOnly: string; /** `@media screen and (max-width: 79.9975rem)` */ - xlDown: string; - /** `@media screen and (min-width: 96rem)` */ - "2xl": string; - /** `@media screen and (min-width: 96rem)` */ - "2xlOnly": string; - /** `@media screen and (max-width: 95.9975rem)` */ - "2xlDown": string; - /** `@media screen and (min-width: 40rem) and (max-width: 47.9975rem)` */ + lgDown: string; + /** `@media screen and (min-width: 22.5rem) and (max-width: 37.4975rem)` */ + xsToSm: string; + /** `@media screen and (min-width: 22.5rem) and (max-width: 56.2475rem)` */ + xsToMd: string; + /** `@media screen and (min-width: 22.5rem) and (max-width: 79.9975rem)` */ + xsToLg: string; + /** `@media screen and (min-width: 37.5rem) and (max-width: 56.2475rem)` */ smToMd: string; - /** `@media screen and (min-width: 40rem) and (max-width: 63.9975rem)` */ + /** `@media screen and (min-width: 37.5rem) and (max-width: 79.9975rem)` */ smToLg: string; - /** `@media screen and (min-width: 40rem) and (max-width: 79.9975rem)` */ - smToXl: string; - /** `@media screen and (min-width: 40rem) and (max-width: 95.9975rem)` */ - smTo2xl: string; - /** `@media screen and (min-width: 48rem) and (max-width: 63.9975rem)` */ + /** `@media screen and (min-width: 56.25rem) and (max-width: 79.9975rem)` */ mdToLg: string; - /** `@media screen and (min-width: 48rem) and (max-width: 79.9975rem)` */ - mdToXl: string; - /** `@media screen and (min-width: 48rem) and (max-width: 95.9975rem)` */ - mdTo2xl: string; - /** `@media screen and (min-width: 64rem) and (max-width: 79.9975rem)` */ - lgToXl: string; - /** `@media screen and (min-width: 64rem) and (max-width: 95.9975rem)` */ - lgTo2xl: string; - /** `@media screen and (min-width: 80rem) and (max-width: 95.9975rem)` */ - xlTo2xl: string; /** `@container (min-width: 20rem)` */ "@/xs": string; /** `@container (min-width: 24rem)` */ From df5a7e9638f78a606fd0b8f9c2637b50a128d80c Mon Sep 17 00:00:00 2001 From: SeieunYoo <101736358+SeieunYoo@users.noreply.github.com> Date: Mon, 10 Jun 2024 19:52:05 +0900 Subject: [PATCH 2/4] =?UTF-8?q?[Fix]=20:=20Checkbox=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20pressed,=20cursor=20=EA=B4=80=EB=A0=A8=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20(#47)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: data-pressed 수정, cursor 수정 * fix : pressed 값에 따라 스프레드 연산자 사용 --- packages/wow-ui/src/components/Checkbox/index.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/wow-ui/src/components/Checkbox/index.tsx b/packages/wow-ui/src/components/Checkbox/index.tsx index 1f723d4f..9ccf41f2 100644 --- a/packages/wow-ui/src/components/Checkbox/index.tsx +++ b/packages/wow-ui/src/components/Checkbox/index.tsx @@ -103,7 +103,7 @@ const Checkbox = forwardRef( aria-checked={checked} aria-disabled={disabled} aria-label={inputProps?.["aria-label"] ?? "checkbox"} - data-pressed={pressed} + {...(pressed && { "data-pressed": true })} id={id} ref={ref} tabIndex={0} @@ -148,6 +148,7 @@ const checkboxStyle = cva({ alignItems: "center", outline: "none", position: "relative", + cursor: "inherit", }, variants: { type: { From 954f40962d614b3413a8d00e1f93d03c232d9a83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=ED=98=84=EC=98=81?= <89445100+hamo-o@users.noreply.github.com> Date: Tue, 11 Jun 2024 10:33:45 +0900 Subject: [PATCH 3/4] =?UTF-8?q?[Feature]=20RadioGroup=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EA=B5=AC=ED=98=84=20(#39)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: radio button 컴포넌트 구현 * feat: radio button label 구현 * feat: add RadioContext * feat: add RadioGroup component * feat: RadioButton context 사용 로직 추가, 위치 이동 * feat: add RadioGrid component * docs: RadioButton 기본 story 작성 * docs: RadioGrid 기본 story 작성 * feat: RadioGrid 내보내기 추가 * feat: jsx 사용을 위한 jsxFramework 옵션 추가 * refactor: 기존 container를 panda css flex로 변환 * fix: Radiogroup label 삭제 * feat: disabled 상태 추가 * feat: 라디오버튼 웹 접근성 관련 속성 추가 * fix: RadioGrid 삭제, RadioGroup과 RadioButton을 함께 사용할 수 있도록 수정 * feat: disabled 속성 추가 및 cursor 변경 * refactor: context useMemo 처리 * feat: RadioButton, RadioGroup export * fix: input태그 기본기능 사용을 위한 css 속성 변경 * feat: RadioGroup, Context에 name 추가 * feat: 웹 접근성을 고려하여 라벨 태그 클릭 가능하도록 만들기 * feat: Conditional Styles 이용, pressed 속성 추가 * fix: label으로 pressed 될 수 있도록 임시 수정 * fix: 직접 hook을 이용해 컨트롤하지 않아도 RadioGroup 컴포넌트를 사용할 수 있도록 수정 * fix: add "use client" * feat: RadioButton 상태 정리 * feat: RadioButton 상태 사용자 제어 여부에 따라 분리 * feat: RadioButton 스토리 작성 * fix: onChange 누락 수정, aria-readonly 삭제 * feat: RadioButton, RadioGroup 통합 및 상태관리 변경 * feat: RadioGroup 스토리 작성 * chore: rollup config input 변경 및 docs 테스트 * refactor: styled 사용 및 css 이동 * fix: label 태그 press 시 이벤트 감지하도록 수정 * fix: 라디오 그룹의 중복 name 값 삭제 * docs: JSDoc 작성 * feat: RadioButton에 value값 직접 입력하도록 수정 * refactor: useState 타입 명시 * refactor: radius, gap 토큰 값으로 변경 * fix: docs 예시 삭제 * chore: 스타일 네이밍 변경 및 css 삭제 * fix: RadioContextProps 타입 변경 * chore: px to rem, gap 토큰 적용 * fix: RadioButton label optional 적용 * fix: 개별 RadioButton readonly 처리 * feat: RadioButton 커스텀 속성들 추가 * feat: RadioButton pressed 키보드 조작 * refactor: RadioContext onChange 함수 useCallback으로 재사용 * fix: RadioGroup 컴포넌트 onChange 이름 변경 * feat: forwardRef 추가 * fix: jsdoc property -> param 수정 * fix: 스토리북 템플릿 삭제 * fix: RadioButton background color token 이름 변경 * refactor: controlled 여부에 따라 함수 실행 * fix: 바뀐 빌드 스크립트에 따른 변경 * feat: 키보드 spacebar 선택 * chore: docs에서 기능 확인 --- apps/wow-docs/app/page.tsx | 15 ++ packages/wow-ui/package.json | 10 + packages/wow-ui/rollup.config.js | 3 + .../src/components/RadioGroup/RadioButton.tsx | 188 ++++++++++++++++++ .../components/RadioGroup/RadioContext.tsx | 27 +++ .../RadioGroup/RadioGroup.stories.tsx | 115 +++++++++++ .../src/components/RadioGroup/RadioGroup.tsx | 57 ++++++ .../wow-ui/src/components/RadioGroup/index.ts | 2 + packages/wow-ui/styled-system/styles.css | 1 + 9 files changed, 418 insertions(+) create mode 100644 packages/wow-ui/src/components/RadioGroup/RadioButton.tsx create mode 100644 packages/wow-ui/src/components/RadioGroup/RadioContext.tsx create mode 100644 packages/wow-ui/src/components/RadioGroup/RadioGroup.stories.tsx create mode 100644 packages/wow-ui/src/components/RadioGroup/RadioGroup.tsx create mode 100644 packages/wow-ui/src/components/RadioGroup/index.ts create mode 100644 packages/wow-ui/styled-system/styles.css diff --git a/apps/wow-docs/app/page.tsx b/apps/wow-docs/app/page.tsx index 5c430099..4536f4cc 100644 --- a/apps/wow-docs/app/page.tsx +++ b/apps/wow-docs/app/page.tsx @@ -1,5 +1,7 @@ +import { css } from "@styled-system/css/css"; import Checkbox from "wowds-ui/Checkbox"; import Chip from "wowds-ui/Chip"; +import { RadioButton, RadioGroup } from "wowds-ui/RadioGroup"; import Switch from "wowds-ui/Switch"; const Home = () => { @@ -8,6 +10,19 @@ const Home = () => { + + + + +
+

docs

+
); }; diff --git a/packages/wow-ui/package.json b/packages/wow-ui/package.json index 5a269302..7c174f30 100644 --- a/packages/wow-ui/package.json +++ b/packages/wow-ui/package.json @@ -49,6 +49,16 @@ "types": "./dist/components/Chip/index.d.ts", "require": "./dist/Chip.cjs", "import": "./dist/Chip.js" + }, + "./RadioButton": { + "types": "./dist/RadioButton/index.d.ts", + "require": "./dist/RadioButton.cjs", + "import": "./dist/RadioButton.js" + }, + "./RadioGroup": { + "types": "./dist/components/RadioGroup/index.d.ts", + "require": "./dist/RadioGroup.cjs", + "import": "./dist/RadioGroup.js" } }, "keywords": [], diff --git a/packages/wow-ui/rollup.config.js b/packages/wow-ui/rollup.config.js index bac0b983..8d89bd90 100644 --- a/packages/wow-ui/rollup.config.js +++ b/packages/wow-ui/rollup.config.js @@ -24,6 +24,9 @@ export default { Checkbox: "./src/components/Checkbox", Chip: "./src/components/Chip", Switch: "./src/components/Switch", + RadioButton: "./src/components/RadioButton", + RadioGroup: "./src/components/RadioGroup", + Switch: "./src/components/Switch", TextField: "./src/components/TextField", }, output: [ diff --git a/packages/wow-ui/src/components/RadioGroup/RadioButton.tsx b/packages/wow-ui/src/components/RadioGroup/RadioButton.tsx new file mode 100644 index 00000000..05c749d7 --- /dev/null +++ b/packages/wow-ui/src/components/RadioGroup/RadioButton.tsx @@ -0,0 +1,188 @@ +"use client"; + +import { cva } from "@styled-system/css/cva"; +import { styled } from "@styled-system/jsx"; +import type { CSSProperties, InputHTMLAttributes, KeyboardEvent } from "react"; +import { forwardRef, useCallback, useContext, useState } from "react"; + +import RadioContext from "@/components/RadioGroup/RadioContext"; + +/** + * @description 라디오 버튼 컴포넌트의 속성을 정의합니다. + * + * @param {boolean} [disabled] - 라디오 버튼이 비활성화되어 있는지 여부. + * @param {string} value - 라디오 버튼의 값. + * @param {string} [label] - 라디오 버튼의 라벨. + * @param {CSSProperties} [style] - 라디오 버튼의 커스텀 스타일. + * @param {string} [className] - 라디오 버튼에 전달하는 커스텀 클래스. + * @param {InputHTMLAttributes} [inputProps] - 라디오 버튼의 기본 input 요소에 전달할 추가 속성들. + * @param {ComponentPropsWithoutRef} rest 렌더링된 요소 또는 컴포넌트에 전달할 추가 props. + * @param {ComponentPropsWithRef["ref"]} ref 렌더링된 요소 또는 컴포넌트에 연결할 ref. + */ + +export interface RadioButtonProps { + disabled?: boolean; + value: string; + label?: string; + style?: CSSProperties; + className?: string; + inputProps?: InputHTMLAttributes; +} + +const RadioButton = forwardRef( + ({ disabled = false, value, label, inputProps, ...rest }, ref) => { + const group = useContext(RadioContext); + const selected = group.value === value; + + const [pressed, setPressed] = useState(false); + + const handleMouseDown = useCallback(() => { + if (!disabled && !group.disabled) setPressed(true); + }, [setPressed, disabled, group.disabled]); + + const handleMouseUp = useCallback(() => { + if (!disabled && !group.disabled) setPressed(false); + }, [setPressed, disabled, group.disabled]); + + const handleKeyDown = useCallback( + (event: KeyboardEvent) => { + if (event.key === " ") { + setPressed(true); + if (!selected) { + group.value = value; + } + } + }, + [setPressed, group, value, selected] + ); + + const handleKeyUp = useCallback( + (event: KeyboardEvent) => { + if (event.key === " ") { + setPressed(false); + } + }, + [setPressed] + ); + + return ( + + + {label && {label}} + + ); + } +); + +const radioButtonStyle = cva({ + base: { + appearance: "none", + + width: "1.25rem", + height: "1.25rem", + borderRadius: "full", + borderWidth: 1, + + display: "flex", + alignItems: "center", + justifyContent: "center", + cursor: "pointer", + + background: "Background", + borderColor: "darkDisabled", + }, + variants: { + state: { + active: { + base: { + background: "blueBackgroundPressed", + borderColor: "primary", + _before: { + content: `""`, + width: "0.625rem", + height: "0.625rem", + borderRadius: "full", + background: "primary", + }, + }, + "&[data-readonly=true]": { + background: "lightDisabled", + borderColor: "darkDisabled", + cursor: "not-allowed", + _before: { + background: "darkDisabled", + }, + }, + "&[data-pressed=true]": { + background: "blueBackgroundPressed", + borderColor: "bluePressed", + _before: { + background: "bluePressed", + }, + }, + }, + inactive: { + _disabled: { + background: "lightDisabled", + borderColor: "darkDisabled", + cursor: "not-allowed", + }, + "&[data-pressed=true]": { + background: "blueBackgroundPressed", + borderColor: "bluePressed", + }, + }, + }, + }, +}); + +const labelStyle = cva({ + base: { + width: "fit-content", + display: "flex", + alignItems: "center", + gap: "xs", + }, + variants: { + state: { + default: { + cursor: "pointer", + color: "textBlack", + }, + disabled: { + cursor: "not-allowed", + color: "sub", + }, + }, + }, +}); + +RadioButton.displayName = "RadioButton"; +export default RadioButton; diff --git a/packages/wow-ui/src/components/RadioGroup/RadioContext.tsx b/packages/wow-ui/src/components/RadioGroup/RadioContext.tsx new file mode 100644 index 00000000..4cd243cc --- /dev/null +++ b/packages/wow-ui/src/components/RadioGroup/RadioContext.tsx @@ -0,0 +1,27 @@ +import type { ChangeEvent } from "react"; +import { createContext } from "react"; + +/** + * @description RadioButton들 사이에 공유되는 RadioContext의 속성을 정의합니다. + * + * @param {string | undefined} name - 라디오 그룹의 이름. + * @param {string | undefined} [value] - 외부에서 제어할 활성된 라디오 버튼의 값. + * @param {(event: ChangeEvent) => void} [onChange] - 외부 활성 상태가 변경될 때 호출될 콜백 함수. + * @param {boolean} [disabled] - 라디오 그룹이 비활성화되어 있는지 여부. + */ + +export interface RadioContextProps { + name: string; + value?: string; + onChange?: (event: ChangeEvent) => void; + disabled?: boolean; +} + +const RadioContext = createContext({ + name: "RadioGroupName", + value: undefined, + onChange: undefined, + disabled: false, +}); + +export default RadioContext; diff --git a/packages/wow-ui/src/components/RadioGroup/RadioGroup.stories.tsx b/packages/wow-ui/src/components/RadioGroup/RadioGroup.stories.tsx new file mode 100644 index 00000000..a23051db --- /dev/null +++ b/packages/wow-ui/src/components/RadioGroup/RadioGroup.stories.tsx @@ -0,0 +1,115 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { useState } from "react"; + +import { RadioButton, RadioGroup } from "@/components/RadioGroup"; +import type { RadioGroupProps } from "@/components/RadioGroup/RadioGroup"; + +const meta = { + title: "UI/RadioGroup", + component: RadioGroup, + tags: ["autodocs"], + parameters: { + componentSubtitle: "라디오 그룹 컴포넌트", + }, + argTypes: { + defaultValue: { + description: "라디오 그룹의 초기 선택값을 나타냅니다.", + table: { + type: { summary: "string" }, + defaultValue: { summary: undefined }, + }, + }, + name: { + description: "라디오 그룹의 이름을 나타냅니다.", + table: { + type: { summary: "string" }, + defaultValue: { summary: undefined }, + }, + }, + children: { + description: "라디오 그룹의 자식 요소를 나타냅니다.", + table: { + type: { summary: "RadioButton" }, + defaultValue: { + summary: undefined, + }, + }, + type: { + name: "function", + required: true, + }, + }, + disabled: { + description: "라디오 그룹이 비활성화되어 있는지 여부를 나타냅니다.", + table: { + type: { summary: "boolean" }, + defaultValue: { summary: "false" }, + }, + }, + value: { + description: "외부에서 제어할 활성된 라디오 버튼의 값을 나타냅니다.", + table: { + type: { summary: "string" }, + defaultValue: { summary: undefined }, + }, + }, + onChange: { + description: "외부 활성 상태가 변경될 때 호출될 콜백 함수를 나타냅니다.", + table: { + type: { summary: "(event: ChangeEvent) => void" }, + defaultValue: { summary: undefined }, + }, + }, + }, +} satisfies Meta; + +export default meta; + +export const Default: StoryObj = { + args: { + defaultValue: "1학년", + name: "defaultgrade", + children: ( + <> + + + + + + ), + }, +}; + +export const ReadOnly: StoryObj = { + args: { + disabled: true, + defaultValue: "2학년", + name: "readonlygrade", + children: ( + <> + + + + + + ), + }, +}; + +export const Controlled = () => { + const [value, setState] = useState("1학년"); + + return ( + setState(e.target.value)} + > + + + + + + ); +}; diff --git a/packages/wow-ui/src/components/RadioGroup/RadioGroup.tsx b/packages/wow-ui/src/components/RadioGroup/RadioGroup.tsx new file mode 100644 index 00000000..b6ffc51d --- /dev/null +++ b/packages/wow-ui/src/components/RadioGroup/RadioGroup.tsx @@ -0,0 +1,57 @@ +"use client"; +import { Flex } from "@styled-system/jsx"; +import type { ChangeEvent, ReactNode } from "react"; +import { useCallback, useMemo, useState } from "react"; + +import type { RadioContextProps } from "./RadioContext"; +import RadioContext from "./RadioContext"; + +/** + * @description 라디오 버튼들을 하나의 그룹으로 묶는 라디오그룹 컴포넌트의 속성을 정의합니다. + * + * @param {ReactNode} children - 라디오 그룹의 자식 요소들. + * @param {string} defaultValue - 라디오 그룹의 초기 선택값. + */ + +export interface RadioGroupProps extends RadioContextProps { + children: ReactNode; + defaultValue: string; +} + +const RadioGroup = ({ + children, + name, + defaultValue, + onChange, + value, + disabled, +}: RadioGroupProps) => { + const [selected, setSelected] = useState(defaultValue); + + const handleChange = useCallback( + (event: ChangeEvent) => { + onChange ? onChange(event) : setSelected(event.target.value); + }, + [setSelected, onChange] + ); + + const contextValue = useMemo( + () => ({ + onChange: handleChange, + value: value ? value : selected, + name, + disabled, + }), + [handleChange, value, selected, name, disabled] + ); + + return ( + + + {children} + + + ); +}; + +export default RadioGroup; diff --git a/packages/wow-ui/src/components/RadioGroup/index.ts b/packages/wow-ui/src/components/RadioGroup/index.ts new file mode 100644 index 00000000..ebf2b348 --- /dev/null +++ b/packages/wow-ui/src/components/RadioGroup/index.ts @@ -0,0 +1,2 @@ +export { default as RadioButton } from "./RadioButton"; +export { default as RadioGroup } from "./RadioGroup"; diff --git a/packages/wow-ui/styled-system/styles.css b/packages/wow-ui/styled-system/styles.css new file mode 100644 index 00000000..35f3be89 --- /dev/null +++ b/packages/wow-ui/styled-system/styles.css @@ -0,0 +1 @@ +:host,html{--font-fallback:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,'Helvetica Neue',Arial,'Noto Sans',sans-serif,'Apple Color Emoji','Segoe UI Emoji','Segoe UI Symbol','Noto Color Emoji';-webkit-text-size-adjust:100%;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;-moz-tab-size:4;tab-size:4;-webkit-tap-highlight-color:transparent;line-height:1.5;font-family:var(--global-font-body,var(--font-fallback))}*,::backdrop,::file-selector-button,:after,:before{margin:0px;padding:0px;box-sizing:border-box;border-width:0px;border-style:solid;border-color:var(--global-color-border,currentColor)}hr{height:0px;color:inherit;border-top-width:1px}body{height:100%;line-height:inherit}img{border-style:none}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}h1,h2,h3,h4,h5,h6{text-wrap:balance;font-size:inherit;font-weight:inherit}h1,h2,h3,h4,h5,h6,p{overflow-wrap:break-word}menu,ol,ul{list-style:none}::file-selector-button,button,input:where([type=button],[type=reset],[type=submit]){appearance:button;-webkit-appearance:button}::file-selector-button,button,input,optgroup,select,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;background:transparent}::placeholder{opacity:1;--placeholder-fallback:color-mix(in srgb,currentColor 50%,transparent);color:var(--global-color-placeholder,var(--placeholder-fallback))}textarea{resize:vertical}table{text-indent:0px;border-collapse:collapse;border-color:inherit}summary{display:list-item}small{font-size:80%}sub,sup{position:relative;vertical-align:baseline;font-size:75%;line-height:0}sub{bottom:-0.25em}sup{top:-0.5em}dialog{padding:0px}a{color:inherit;text-decoration:inherit}abbr:where([title]){text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,pre,samp{--font-mono-fallback:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,'Liberation Mono','Courier New';font-feature-settings:normal;font-variation-settings:normal;font-family:var(--global-font-mono,var(--font-mono-fallback));font-size:1em}progress{vertical-align:baseline}::-webkit-search-cancel-button,::-webkit-search-decoration{-webkit-appearance:none}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}:-moz-ui-invalid{box-shadow:none}:-moz-focusring{outline:auto}[hidden]:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#){display:none!important}:where(:root,:host):not(#\#):not(#\#){--colors-red-400:#EE695D;--colors-red-500:#EA4335;--colors-blue-100:#D7E9FD;--colors-blue-400:#5EA5F9;--colors-primary:#368FF7;--colors-dark-disabled:#C2C2C2;--colors-blue-pressed:#5EA5F9;--colors-blue-background-pressed:#EBF4FE}.textStyle_body2:not(#\#):not(#\#):not(#\#):not(#\#){letter-spacing:-0.00875rem;font-size:0.875rem;line-height:160%;font-weight:500}.bg_blue\.100:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#){background:var(--colors-blue-100)}.px_4:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#){padding-inline:4px}.py_3:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#){padding-block:3px}.rounded_md:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#){border-radius:md}.bg_red\.400:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#){background:var(--colors-red-400)}.w_20:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#){width:20px}.h_20:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#){height:20px}.rounded_9999:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#){border-radius:9999px}.d_flex:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#){display:flex}.bg_blueBackgroundPressed:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#){background:var(--colors-blue-background-pressed)}.w_10:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#){width:10px}.h_10:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#){height:10px}.bg_primary:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#){background:var(--colors-primary)}.gap_8:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#){gap:8px}.d_none:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#){display:none}.gap_0\.5rem:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#){gap:0.5rem}.font_Inter:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#){font-family:Inter}.border-w_1:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#){border-width:1px}.items_center:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#){align-items:center}.justify_center:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#){justify-content:center}.border_primary:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#){border-color:var(--colors-primary)}.border_darkDisabled:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#){border-color:var(--colors-dark-disabled)}.border_bluePressed:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#){border-color:var(--colors-blue-pressed)}.flex_column:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#){flex-direction:column}.hover\:bg_blue\.400:is(:hover,[data-hover]):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#){background:var(--colors-blue-400)}.hover\:bg_red\.500:is(:hover,[data-hover]):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#){background:var(--colors-red-500)} \ No newline at end of file From 944cf6ee4a5bbacb0b288ec2458e240bc03a6789 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=ED=99=8D=EC=84=9C=ED=98=84?= Date: Wed, 12 Jun 2024 18:25:30 +0900 Subject: [PATCH 4/4] =?UTF-8?q?[Feature]=20=EC=8A=A4=ED=86=A0=EB=A6=AC=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=83=9D=EC=84=B1=20=EC=9E=90=EB=8F=99?= =?UTF-8?q?=ED=99=94=20(#52)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: story docs 자동화 추가 * docs: 스토리북 문서 자잘한 수정 * chore: button 타입 style로 네이밍 변경 * chore: codegen 시 validation 추가 * chore: 불필요한 부분 삭제 * docs: eslint 규칙 따르도록 double quote로 변경 --- packages/codegen/package.json | 13 + packages/codegen/plopfile.js | 33 ++ packages/codegen/templates/Story.tsx.hbs | 58 ++ packages/wow-ui/package.json | 4 +- .../components/Checkbox/Checkbox.stories.tsx | 12 +- .../src/components/Chip/Chip.stories.ts | 34 +- .../src/components/Switch/Switch.stories.tsx | 18 +- .../TextField/TextField.stories.tsx | 42 +- packages/wow-ui/src/types/button.ts | 2 +- pnpm-lock.yaml | 513 ++++++++++++++++++ 10 files changed, 667 insertions(+), 62 deletions(-) create mode 100644 packages/codegen/package.json create mode 100644 packages/codegen/plopfile.js create mode 100644 packages/codegen/templates/Story.tsx.hbs diff --git a/packages/codegen/package.json b/packages/codegen/package.json new file mode 100644 index 00000000..4b055c7d --- /dev/null +++ b/packages/codegen/package.json @@ -0,0 +1,13 @@ +{ + "name": "codegen", + "version": "1.0.0", + "description": "", + "main": "index.js", + "type": "module", + "keywords": [], + "author": "", + "license": "ISC", + "devDependencies": { + "plop": "^4.0.1" + } +} diff --git a/packages/codegen/plopfile.js b/packages/codegen/plopfile.js new file mode 100644 index 00000000..cbc5ea44 --- /dev/null +++ b/packages/codegen/plopfile.js @@ -0,0 +1,33 @@ +import { promises as fs } from "fs"; +import { exit } from "process"; + +export default async function async(plop) { + const COMPONENT_DIR = "../wow-ui/src/components"; + const files = await fs.readdir(COMPONENT_DIR); + + plop.setGenerator("Story", { + description: "Create a story file", + prompts: [ + { + type: "input", + name: "name", + message: "스토리를 작성할 컴포넌트 이름을 입력해주세요", + validate: (input) => { + if (!files.includes(input)) { + console.log("\n만들어진 컴포넌트 이름을 입력해주세요."); + exit(1); + } + + return true; + }, + }, + ], + actions: [ + { + type: "add", + path: "../wow-ui/src/components/{{pascalCase name}}/{{pascalCase name}}.stories.tsx", + templateFile: "templates/Story.tsx.hbs", + }, + ], + }); +} diff --git a/packages/codegen/templates/Story.tsx.hbs b/packages/codegen/templates/Story.tsx.hbs new file mode 100644 index 00000000..abb27659 --- /dev/null +++ b/packages/codegen/templates/Story.tsx.hbs @@ -0,0 +1,58 @@ +import type { Meta, StoryObj } from "@storybook/react"; + +import {{pascalCase name}} from "@/components/{{pascalCase name}}"; + +const meta = { + title: "UI/{{pascalCase name}}", + component: {{pascalCase name}}, + tags: ["autodocs"], + parameters: { + // 컴포넌트 부제목 작성 ex. 버튼 컴포넌트 + componentSubtitle: "", + // 색 대조 관련 웹 접근성 테스트 불가하게 하는 속성 + // 필요 없으면 지우기 + //a11y: { + // config: { + // rules: [{ id: "color-contrast", enabled: false }], + // }, + //}, + }, + argTypes: { + props1: { + description: "props1은 default 값이 있는 필수적인 속성입니다."", + table: { + // type, 필수가 아닐 경우 required 삭제 + type: { summary: "string", required: true }, + // defaultValue (Optional) + defaultValue: { summary: "기본 값" }, + }, + control: { + // Args Table에서 사용자가 조작할 수 있는 타입 + // select일 경우 options를 배열로 제공 + // 그 외 text, number, boolean, color, check, radio 등 존재 + // control 불가능하게 하고 싶을 경우 객체가 아니라 false로 설정 + // ex. control: false, + // 참고 : https://storybook.js.org/docs/react/essentials/controls + type: "select", + options: ["option1", "option2"], + }, + }, + }, + // decorators (Optional) + // 공통적으로 적용하고 싶은 컴포넌트 설정 + decorators: [ + (Story) => ( + <> + {Story()} + + ), + ], +} satisfies Meta; + +export default meta; + +type Story = StoryObj; + +export const Primary: Story = { + args: {}, +}; \ No newline at end of file diff --git a/packages/wow-ui/package.json b/packages/wow-ui/package.json index 7c174f30..2dbf251f 100644 --- a/packages/wow-ui/package.json +++ b/packages/wow-ui/package.json @@ -70,7 +70,8 @@ "build-storybook": "storybook build", "generate:build-config": "tsx ../scripts/generateBuildConfig.ts && prettier --write ./rollup.config.js ./package.json", "test-storybook": "test-storybook --browsers firefox chromium webkit", - "test": "jest" + "test": "jest", + "create:story-file": "plop --plopfile ../codegen/plopfile.js" }, "devDependencies": { "@chromatic-com/storybook": "^1.3.3", @@ -93,6 +94,7 @@ "chromatic": "^11.3.0", "eslint": "^8.57.0", "eslint-plugin-storybook": "^0.6.15", + "plop": "^4.0.1", "rollup-plugin-peer-deps-external": "^2.2.4", "storybook": "^7.5.3", "typescript": "^5.3.3" diff --git a/packages/wow-ui/src/components/Checkbox/Checkbox.stories.tsx b/packages/wow-ui/src/components/Checkbox/Checkbox.stories.tsx index 81b53aa3..31aadaeb 100644 --- a/packages/wow-ui/src/components/Checkbox/Checkbox.stories.tsx +++ b/packages/wow-ui/src/components/Checkbox/Checkbox.stories.tsx @@ -48,9 +48,7 @@ const meta = { type: { summary: "ReactNode" }, defaultValue: { summary: null }, }, - control: { - type: "text", - }, + control: false, }, onChange: { description: "외부 활성 상태가 변경될 때 호출될 콜백 함수를 나타냅니다.", @@ -122,9 +120,7 @@ const meta = { type: { summary: "InputHTMLAttributes" }, defaultValue: { summary: "{}" }, }, - control: { - type: "object", - }, + control: false, }, style: { description: "체크박스의 커스텀 스타일을 설정합니다.", @@ -132,9 +128,7 @@ const meta = { type: { summary: "CSSProperties" }, defaultValue: { summary: "{}" }, }, - control: { - type: "object", - }, + control: false, }, }, } satisfies Meta; diff --git a/packages/wow-ui/src/components/Chip/Chip.stories.ts b/packages/wow-ui/src/components/Chip/Chip.stories.ts index 28293883..bc4a63f2 100644 --- a/packages/wow-ui/src/components/Chip/Chip.stories.ts +++ b/packages/wow-ui/src/components/Chip/Chip.stories.ts @@ -14,7 +14,6 @@ const meta = { }, }, }, - argTypes: { disabled: { description: "disabled는 칩 버튼이 비활성화 상태인지 여부를 나타냅니다.", @@ -37,7 +36,6 @@ const meta = { type: "boolean", }, }, - isChecked: { description: "isChecked는 외부에서 제어할 활성 상태를 나타냅니다.", table: { @@ -61,11 +59,7 @@ const meta = { label: { description: "칩에 들어가게 될 텍스트입니다.", table: { - type: { summary: "string" }, - }, - type: { - name: "string", - required: true, + type: { summary: "string", required: true }, }, control: { type: "text", @@ -75,18 +69,18 @@ const meta = { description: "칩 클릭 시 동작할 이벤트입니다.", table: { type: { summary: "() => void" }, - control: { - type: "function", - }, + }, + control: { + type: "function", }, }, onDelete: { description: "칩에 대한 필터를 제거하기 위한 함수입니다.", table: { type: { summary: "() => void" }, - control: { - type: "function", - }, + }, + control: { + type: "function", }, }, onKeyDown: { @@ -94,10 +88,18 @@ const meta = { "칩이 포커스됐을 때 엔터 키 또는 스페이스 바를 눌렀을 때 동작할 이벤트입니다.", table: { type: { summary: "() => void" }, - control: { - type: "function", - }, }, + control: { + type: "function", + }, + }, + style: { + description: "칩의 커스텀 스타일을 설정합니다.", + table: { + type: { summary: "CSSProperties" }, + defaultValue: { summary: "{}" }, + }, + control: false, }, }, } satisfies Meta; diff --git a/packages/wow-ui/src/components/Switch/Switch.stories.tsx b/packages/wow-ui/src/components/Switch/Switch.stories.tsx index 7aca484d..6a5a89dd 100644 --- a/packages/wow-ui/src/components/Switch/Switch.stories.tsx +++ b/packages/wow-ui/src/components/Switch/Switch.stories.tsx @@ -58,9 +58,9 @@ const meta = { table: { type: { summary: "() => void" }, defaultValue: { summary: null }, - control: { - type: "function", - }, + }, + control: { + type: "function", }, }, onClick: { @@ -68,9 +68,9 @@ const meta = { table: { type: { summary: "() => void" }, defaultValue: { summary: null }, - control: { - type: "function", - }, + }, + control: { + type: "function", }, }, onKeyDown: { @@ -79,9 +79,9 @@ const meta = { table: { type: { summary: "() => void" }, defaultValue: { summary: null }, - control: { - type: "function", - }, + }, + control: { + type: "function", }, }, }, diff --git a/packages/wow-ui/src/components/TextField/TextField.stories.tsx b/packages/wow-ui/src/components/TextField/TextField.stories.tsx index 0df9f750..120ab62c 100644 --- a/packages/wow-ui/src/components/TextField/TextField.stories.tsx +++ b/packages/wow-ui/src/components/TextField/TextField.stories.tsx @@ -19,16 +19,12 @@ const meta = { label: { description: "텍스트필드의 라벨입니다.", table: { - type: { summary: "string" }, + type: { summary: "string", required: true }, defaultValue: { summary: null }, }, control: { type: "text", }, - type: { - name: "string", - required: true, - }, }, placeholder: { description: "텍스트필드의 플레이스홀더 텍스트입니다.", @@ -96,18 +92,16 @@ const meta = { type: { summary: "ReactNode" }, defaultValue: { summary: null }, }, - control: { - type: "text", - }, + control: false, }, onChange: { description: "외부 활성 상태가 변경될 때 호출될 콜백 함수입니다.", table: { type: { summary: "(value: string) => void" }, defaultValue: { summary: null }, - control: { - type: "function", - }, + }, + control: { + type: "function", }, }, onBlur: { @@ -115,9 +109,9 @@ const meta = { table: { type: { summary: "() => void" }, defaultValue: { summary: null }, - control: { - type: "function", - }, + }, + control: { + type: "function", }, }, onFocus: { @@ -125,9 +119,9 @@ const meta = { table: { type: { summary: "() => void" }, defaultValue: { summary: null }, - control: { - type: "function", - }, + }, + control: { + type: "function", }, }, textareaProps: { @@ -135,29 +129,25 @@ const meta = { table: { type: { summary: "TextareaHTMLAttributes" }, defaultValue: { summary: null }, - control: { - type: "object", - }, }, + control: false, }, style: { description: "텍스트필드의 커스텀 스타일 속성입니다.", table: { type: { summary: "CSSProperties" }, defaultValue: { summary: null }, - control: { - type: "object", - }, }, + control: false, }, className: { description: "텍스트필드에 전달하는 커스텀 클래스명입니다.", table: { type: { summary: "string" }, defaultValue: { summary: null }, - control: { - type: "text", - }, + }, + control: { + type: "text", }, }, }, diff --git a/packages/wow-ui/src/types/button.ts b/packages/wow-ui/src/types/button.ts index a5d71572..08999669 100644 --- a/packages/wow-ui/src/types/button.ts +++ b/packages/wow-ui/src/types/button.ts @@ -33,7 +33,7 @@ export interface BasicButtonProps { * wow-ds에서 지정하고 있는 스타일을 제외하고 사용자가 React 스타일 객체를 이용하여 커스텀하게 스타일을 적용할 수 있도록 합니다. */ - customStyle?: CSSProperties; + style?: CSSProperties; /** * 버튼이 클릭되었을때 호출되는 함수입니다. diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b577ca46..afd7db24 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -230,6 +230,12 @@ importers: specifier: ^5.3.3 version: 5.3.3 + packages/codegen: + devDependencies: + plop: + specifier: ^4.0.1 + version: 4.0.1 + packages/shared-config: {} packages/theme: @@ -314,6 +320,9 @@ importers: eslint-plugin-storybook: specifier: ^0.6.15 version: 0.6.15(eslint@8.57.0)(typescript@5.3.3) + plop: + specifier: ^4.0.1 + version: 4.0.1 rollup-plugin-peer-deps-external: specifier: ^2.2.4 version: 2.2.4(rollup@4.17.2) @@ -2564,6 +2573,11 @@ packages: resolution: {integrity: sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==} dev: true + /@inquirer/figures@1.0.3: + resolution: {integrity: sha512-ErXXzENMH5pJt5/ssXV0DfWUZqly8nGzf0UcBV9xTnP+KyffE2mqyxIMBrZ8ijQck2nU0TQm40EQB53YreyWHw==} + engines: {node: '>=18'} + dev: true + /@isaacs/cliui@8.0.2: resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} @@ -2874,6 +2888,13 @@ packages: resolution: {integrity: sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==} dev: true + /@ljharb/through@2.3.13: + resolution: {integrity: sha512-/gKJun8NNiWGZJkGzI/Ragc53cOdcLNdzjLaIa+GEjguQs0ulsurx8WN0jijdK9yPqDvziX995sMRLyLt1uZMQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + dev: true + /@manypkg/find-root@1.1.0: resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} dependencies: @@ -5640,6 +5661,10 @@ packages: resolution: {integrity: sha512-frsJrz2t/CeGifcu/6uRo4b+SzAwT4NYCVPu1GN8IB9XTzrpPkGuV0tmh9mN+/L0PklAlsC3u5Fxt0ju00LXIw==} dev: true + /@types/fined@1.1.5: + resolution: {integrity: sha512-2N93vadEGDFhASTIRbizbl4bNqpMOId5zZfj6hHqYZfEzEfO9onnU4Im8xvzo8uudySDveDHBOOSlTWf38ErfQ==} + dev: true + /@types/glob@7.2.0: resolution: {integrity: sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==} dependencies: @@ -5668,6 +5693,13 @@ packages: rxjs: 6.6.7 dev: true + /@types/inquirer@9.0.7: + resolution: {integrity: sha512-Q0zyBupO6NxGRZut/JdmqYKOnN95Eg5V8Csg3PGKkP+FnvsUZx1jAyK7fztIszxxMuoBA6E3KXWvdZVXIpx60g==} + dependencies: + '@types/through': 0.0.30 + rxjs: 7.8.1 + dev: true + /@types/istanbul-lib-coverage@2.0.6: resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} dev: true @@ -5711,6 +5743,13 @@ packages: resolution: {integrity: sha512-R5M+SYhMbwBeQcNXYWNCZkl09vkVfAtcPIaCGdzIkkbeaTrVbGQ7HVgi4s+EmM/M1K4ZuWQH0jGcvMvNePfxYA==} dev: true + /@types/liftoff@4.0.3: + resolution: {integrity: sha512-UgbL2kR5pLrWICvr8+fuSg0u43LY250q7ZMkC+XKC3E+rs/YBDEnQIzsnhU5dYsLlwMi3R75UvCL87pObP1sxw==} + dependencies: + '@types/fined': 1.1.5 + '@types/node': 20.11.24 + dev: true + /@types/lodash@4.17.1: resolution: {integrity: sha512-X+2qazGS3jxLAIz5JDXDzglAF3KpijdhFxlf/V1+hEsOUc+HnWi81L/uv/EvGuV90WY+7mPGFCUDGfQC3Gj95Q==} dev: true @@ -6453,6 +6492,14 @@ packages: indent-string: 4.0.0 dev: true + /aggregate-error@4.0.1: + resolution: {integrity: sha512-0poP0T7el6Vq3rstR8Mn4V/IQrpBLO6POkUSrN7RhyY+GF/InCFShQzsQ39T25gkHhLgSLByyAz+Kjb+c2L98w==} + engines: {node: '>=12'} + dependencies: + clean-stack: 4.2.0 + indent-string: 5.0.0 + dev: true + /ajv-formats@2.1.1(ajv@8.13.0): resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} peerDependencies: @@ -6626,6 +6673,11 @@ packages: is-array-buffer: 3.0.4 dev: true + /array-each@1.0.1: + resolution: {integrity: sha512-zHjL5SZa68hkKHBFBK6DJCTtr9sfTCPCaph/L7tMSLcTFgy+zX7E+6q5UArbtOtMBCtxdICpfTCspRse+ywyXA==} + engines: {node: '>=0.10.0'} + dev: true + /array-flatten@1.1.1: resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} dev: true @@ -6641,6 +6693,11 @@ packages: is-string: 1.0.7 dev: true + /array-slice@1.1.0: + resolution: {integrity: sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w==} + engines: {node: '>=0.10.0'} + dev: true + /array-union@2.1.0: resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} engines: {node: '>=8'} @@ -7423,6 +7480,14 @@ packages: /caniuse-lite@1.0.30001593: resolution: {integrity: sha512-UWM1zlo3cZfkpBysd7AS+z+v007q9G1+fLTUU42rQnY6t2axoogPW/xol6T7juU5EUoOhML4WgBIdG+9yYqAjQ==} + /capital-case@1.0.4: + resolution: {integrity: sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==} + dependencies: + no-case: 3.0.4 + tslib: 2.6.2 + upper-case-first: 2.0.2 + dev: true + /case-sensitive-paths-webpack-plugin@2.4.0: resolution: {integrity: sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw==} engines: {node: '>=4'} @@ -7493,6 +7558,23 @@ packages: upper-case-first: 1.1.2 dev: true + /change-case@4.1.2: + resolution: {integrity: sha512-bSxY2ws9OtviILG1EiY5K7NNxkqg/JnRnFxLtKQ96JaviiIxi7djMrSd0ECT9AC+lttClmYwKw53BWpOMblo7A==} + dependencies: + camel-case: 4.1.2 + capital-case: 1.0.4 + constant-case: 3.0.4 + dot-case: 3.0.4 + header-case: 2.0.4 + no-case: 3.0.4 + param-case: 3.0.4 + pascal-case: 3.1.2 + path-case: 3.0.4 + sentence-case: 3.0.4 + snake-case: 3.0.4 + tslib: 2.6.2 + dev: true + /char-regex@1.0.2: resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} engines: {node: '>=10'} @@ -7589,6 +7671,13 @@ packages: engines: {node: '>=6'} dev: true + /clean-stack@4.2.0: + resolution: {integrity: sha512-LYv6XPxoyODi36Dp976riBtSY27VmFo+MKqEU9QCCWyTrdEPDog+RWA7xQWHi6Vbp61j5c4cdzzX1NidnwtUWg==} + engines: {node: '>=12'} + dependencies: + escape-string-regexp: 5.0.0 + dev: true + /cli-cursor@3.1.0: resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} engines: {node: '>=8'} @@ -7608,6 +7697,11 @@ packages: engines: {node: '>=6'} dev: true + /cli-spinners@2.9.2: + resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} + engines: {node: '>=6'} + dev: true + /cli-table3@0.6.4: resolution: {integrity: sha512-Lm3L0p+/npIQWNIiyF/nAn7T5dnOwR3xNTHXYEBFBFVPXzCVNZ5lqEC/1eo/EVfpDsQ1I+TX4ORPQgp+UI0CRw==} engines: {node: 10.* || >= 12.*} @@ -7630,6 +7724,11 @@ packages: engines: {node: '>= 10'} dev: true + /cli-width@4.1.0: + resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} + engines: {node: '>= 12'} + dev: true + /client-only@0.0.1: resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} @@ -7830,6 +7929,14 @@ packages: upper-case: 1.1.3 dev: true + /constant-case@3.0.4: + resolution: {integrity: sha512-I2hSBi7Vvs7BEuJDr5dDHfzb/Ruj3FyvFyh7KLilAjNQw3Be+xgqUBA2W6scVEcL0hL1dwPRtIqEPVUCKkSsyQ==} + dependencies: + no-case: 3.0.4 + tslib: 2.6.2 + upper-case: 2.0.2 + dev: true + /constants-browserify@1.0.0: resolution: {integrity: sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==} dev: true @@ -8365,6 +8472,20 @@ packages: slash: 3.0.0 dev: true + /del@7.1.0: + resolution: {integrity: sha512-v2KyNk7efxhlyHpjEvfyxaAihKKK0nWCuf6ZtqZcFFpQRG0bJ12Qsr0RpvsICMjAAZ8DOVCxrlqpxISlMHC4Kg==} + engines: {node: '>=14.16'} + dependencies: + globby: 13.2.2 + graceful-fs: 4.2.11 + is-glob: 4.0.3 + is-path-cwd: 3.0.0 + is-path-inside: 4.0.0 + p-map: 5.5.0 + rimraf: 3.0.2 + slash: 4.0.0 + dev: true + /delayed-stream@1.0.0: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} @@ -8392,6 +8513,11 @@ packages: engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} dev: true + /detect-file@1.0.0: + resolution: {integrity: sha512-DtCOLG98P007x7wiiOmfI0fi3eIKyWiLTGJ2MDnVi/E04lWGbf+JzrRHMm0rgIIZJGtHpKpbVgLWHrv8xXpc3Q==} + engines: {node: '>=0.10.0'} + dev: true + /detect-indent@6.1.0: resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} engines: {node: '>=8'} @@ -9028,6 +9154,11 @@ packages: engines: {node: '>=10'} dev: true + /escape-string-regexp@5.0.0: + resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} + engines: {node: '>=12'} + dev: true + /escodegen@2.1.0: resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==} engines: {node: '>=6.0'} @@ -9464,6 +9595,13 @@ packages: os-homedir: 1.0.2 dev: true + /expand-tilde@2.0.2: + resolution: {integrity: sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==} + engines: {node: '>=0.10.0'} + dependencies: + homedir-polyfill: 1.0.3 + dev: true + /expect-playwright@0.8.0: resolution: {integrity: sha512-+kn8561vHAY+dt+0gMqqj1oY+g5xWrsuGMk4QGxotT2WS545nVqqjs37z6hrYfIuucwqthzwJfCJUEYqixyljg==} dev: true @@ -9768,6 +9906,32 @@ packages: pkg-dir: 4.2.0 dev: true + /findup-sync@5.0.0: + resolution: {integrity: sha512-MzwXju70AuyflbgeOhzvQWAvvQdo1XL0A9bVvlXsYcFEBM87WR4OakL4OfZq+QRmr+duJubio+UtNQCPsVESzQ==} + engines: {node: '>= 10.13.0'} + dependencies: + detect-file: 1.0.0 + is-glob: 4.0.3 + micromatch: 4.0.5 + resolve-dir: 1.0.1 + dev: true + + /fined@2.0.0: + resolution: {integrity: sha512-OFRzsL6ZMHz5s0JrsEr+TpdGNCtrVtnuG3x1yzGNiQHT0yaDnXAj8V/lWcpJVrnoDpcwXcASxAZYbuXda2Y82A==} + engines: {node: '>= 10.13.0'} + dependencies: + expand-tilde: 2.0.2 + is-plain-object: 5.0.0 + object.defaults: 1.1.0 + object.pick: 1.3.0 + parse-filepath: 1.0.2 + dev: true + + /flagged-respawn@2.0.0: + resolution: {integrity: sha512-Gq/a6YCi8zexmGHMuJwahTGzXlAZAOsbCVKduWXC6TlLCjjFRlExMJc4GC2NYPYZ0r/brw9P7CpRgQmlPVeOoA==} + engines: {node: '>= 10.13.0'} + dev: true + /flat-cache@3.0.4: resolution: {integrity: sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==} engines: {node: ^10.12.0 || >=12.0.0} @@ -9801,6 +9965,18 @@ packages: is-callable: 1.2.7 dev: true + /for-in@1.0.2: + resolution: {integrity: sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==} + engines: {node: '>=0.10.0'} + dev: true + + /for-own@1.0.0: + resolution: {integrity: sha512-0OABksIGrxKK8K4kynWkQ7y1zounQxP+CWnyclVwj81KW3vlLlGUx57DKGcP/LH216GzqnstnPocF16Nxs0Ycg==} + engines: {node: '>=0.10.0'} + dependencies: + for-in: 1.0.2 + dev: true + /foreground-child@2.0.0: resolution: {integrity: sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==} engines: {node: '>=8.0.0'} @@ -10157,6 +10333,15 @@ packages: is-windows: 0.2.0 dev: true + /global-modules@1.0.0: + resolution: {integrity: sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==} + engines: {node: '>=0.10.0'} + dependencies: + global-prefix: 1.0.2 + is-windows: 1.0.2 + resolve-dir: 1.0.1 + dev: true + /global-prefix@0.1.5: resolution: {integrity: sha512-gOPiyxcD9dJGCEArAhF4Hd0BAqvAe/JzERP7tYumE4yIkmIedPUVXcJFWbV3/p/ovIIvKjkrTk+f1UVkq7vvbw==} engines: {node: '>=0.10.0'} @@ -10167,6 +10352,17 @@ packages: which: 1.3.1 dev: true + /global-prefix@1.0.2: + resolution: {integrity: sha512-5lsx1NUDHtSjfg0eHlmYvZKv8/nVqX4ckFbM+FrGcQ+04KWcWFo9P5MxPZYSzUvyzmdTbI7Eix8Q4IbELDqzKg==} + engines: {node: '>=0.10.0'} + dependencies: + expand-tilde: 2.0.2 + homedir-polyfill: 1.0.3 + ini: 1.3.8 + is-windows: 1.0.2 + which: 1.3.1 + dev: true + /globals@11.12.0: resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} engines: {node: '>=4'} @@ -10211,6 +10407,17 @@ packages: slash: 3.0.0 dev: true + /globby@13.2.2: + resolution: {integrity: sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + dir-glob: 3.0.1 + fast-glob: 3.3.2 + ignore: 5.3.1 + merge2: 1.4.1 + slash: 4.0.0 + dev: true + /gopd@1.0.1: resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} dependencies: @@ -10378,6 +10585,13 @@ packages: upper-case: 1.1.3 dev: true + /header-case@2.0.4: + resolution: {integrity: sha512-H/vuk5TEEVZwrR0lp2zed9OCo1uAILMlx0JEMgC26rzyJJ3N1v6XkwHHXJQdR2doSjcGPM6OKPYoJgf0plJ11Q==} + dependencies: + capital-case: 1.0.4 + tslib: 2.6.2 + dev: true + /hmac-drbg@1.0.1: resolution: {integrity: sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==} dependencies: @@ -10628,6 +10842,11 @@ packages: engines: {node: '>=8'} dev: true + /indent-string@5.0.0: + resolution: {integrity: sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==} + engines: {node: '>=12'} + dev: true + /inflight@1.0.6: resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} dependencies: @@ -10683,6 +10902,27 @@ packages: wrap-ansi: 6.2.0 dev: true + /inquirer@9.2.23: + resolution: {integrity: sha512-kod5s+FBPIDM2xiy9fu+6wdU/SkK5le5GS9lh4FEBjBHqiMgD9lLFbCbuqFNAjNL2ZOy9Wd9F694IOzN9pZHBA==} + engines: {node: '>=18'} + dependencies: + '@inquirer/figures': 1.0.3 + '@ljharb/through': 2.3.13 + ansi-escapes: 4.3.2 + chalk: 5.3.0 + cli-cursor: 3.1.0 + cli-width: 4.1.0 + external-editor: 3.1.0 + lodash: 4.17.21 + mute-stream: 1.0.0 + ora: 5.4.1 + run-async: 3.0.0 + rxjs: 7.8.1 + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 6.2.0 + dev: true + /internal-slot@1.0.6: resolution: {integrity: sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg==} engines: {node: '>= 0.4'} @@ -10701,6 +10941,11 @@ packages: side-channel: 1.0.6 dev: true + /interpret@3.1.1: + resolution: {integrity: sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==} + engines: {node: '>=10.13.0'} + dev: true + /invariant@2.2.4: resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==} dependencies: @@ -10729,6 +10974,14 @@ packages: engines: {node: '>=8'} dev: true + /is-absolute@1.0.0: + resolution: {integrity: sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==} + engines: {node: '>=0.10.0'} + dependencies: + is-relative: 1.0.0 + is-windows: 1.0.2 + dev: true + /is-arguments@1.1.1: resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==} engines: {node: '>= 0.4'} @@ -10888,6 +11141,11 @@ packages: engines: {node: '>=8'} dev: true + /is-interactive@2.0.0: + resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==} + engines: {node: '>=12'} + dev: true + /is-lower-case@1.1.3: resolution: {integrity: sha512-+5A1e/WJpLLXZEDlgz4G//WYSHyQBD32qa4Jd3Lw06qQlv3fJHnp3YIHjTQSGzHMgzmVKz2ZP3rBxTHkPw/lxA==} dependencies: @@ -10937,11 +11195,21 @@ packages: engines: {node: '>=6'} dev: true + /is-path-cwd@3.0.0: + resolution: {integrity: sha512-kyiNFFLU0Ampr6SDZitD/DwUo4Zs1nSdnygUBqsu3LooL00Qvb5j+UnvApUn/TTj1J3OuE6BTdQ5rudKmU2ZaA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dev: true + /is-path-inside@3.0.3: resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} engines: {node: '>=8'} dev: true + /is-path-inside@4.0.0: + resolution: {integrity: sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==} + engines: {node: '>=12'} + dev: true + /is-plain-obj@1.1.0: resolution: {integrity: sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==} engines: {node: '>=0.10.0'} @@ -10977,6 +11245,13 @@ packages: has-tostringtag: 1.0.0 dev: true + /is-relative@1.0.0: + resolution: {integrity: sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==} + engines: {node: '>=0.10.0'} + dependencies: + is-unc-path: 1.0.0 + dev: true + /is-set@2.0.2: resolution: {integrity: sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==} dev: true @@ -11043,11 +11318,28 @@ packages: resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==} dev: true + /is-unc-path@1.0.0: + resolution: {integrity: sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==} + engines: {node: '>=0.10.0'} + dependencies: + unc-path-regex: 0.1.2 + dev: true + /is-unicode-supported@0.1.0: resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} engines: {node: '>=10'} dev: true + /is-unicode-supported@1.3.0: + resolution: {integrity: sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==} + engines: {node: '>=12'} + dev: true + + /is-unicode-supported@2.0.0: + resolution: {integrity: sha512-FRdAyx5lusK1iHG0TWpVtk9+1i+GjrzRffhDg4ovQ7mcidMQ6mj+MhKPmvh7Xwyv5gIS06ns49CA7Sqg7lC22Q==} + engines: {node: '>=18'} + dev: true + /is-upper-case@1.1.2: resolution: {integrity: sha512-GQYSJMgfeAmVwh9ixyk888l7OIhNAGKtY6QA+IrWlu9MDTCaXmeozOZ2S9Knj7bQwBO/H6J2kb+pbyTUiMNbsw==} dependencies: @@ -11106,6 +11398,11 @@ packages: engines: {node: '>= 8.0.0'} dev: true + /isbinaryfile@5.0.2: + resolution: {integrity: sha512-GvcjojwonMjWbTkfMpnVHVqXW/wKMYDfEpY94/8zy8HFMOqb/VL6oeONq9v87q4ttVlaTLnGXnJD4B5B1OTGIg==} + engines: {node: '>= 18.0.0'} + dev: true + /isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} dev: true @@ -11997,6 +12294,20 @@ packages: type-check: 0.4.0 dev: true + /liftoff@4.0.0: + resolution: {integrity: sha512-rMGwYF8q7g2XhG2ulBmmJgWv25qBsqRbDn5gH0+wnuyeFt7QBJlHJmtg5qEdn4pN6WVAUMgXnIxytMFRX9c1aA==} + engines: {node: '>=10.13.0'} + dependencies: + extend: 3.0.2 + findup-sync: 5.0.0 + fined: 2.0.0 + flagged-respawn: 2.0.0 + is-plain-object: 5.0.0 + object.map: 1.0.1 + rechoir: 0.8.0 + resolve: 1.22.8 + dev: true + /lightningcss-darwin-arm64@1.23.0: resolution: {integrity: sha512-kl4Pk3Q2lnE6AJ7Qaij47KNEfY2/UXRZBT/zqGA24B8qwkgllr/j7rclKOf1axcslNXvvUdztjo4Xqh39Yq1aA==} engines: {node: '>= 12.0.0'} @@ -12240,6 +12551,14 @@ packages: is-unicode-supported: 0.1.0 dev: true + /log-symbols@6.0.0: + resolution: {integrity: sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==} + engines: {node: '>=18'} + dependencies: + chalk: 5.3.0 + is-unicode-supported: 1.3.0 + dev: true + /log-update@6.0.0: resolution: {integrity: sha512-niTvB4gqvtof056rRIrTZvjNYE4rCUzO6X/X+kYjd7WFxXeJ0NwEFnRxX6ehkvv3jTwrXnNdtAak5XYZuIyPFw==} engines: {node: '>=18'} @@ -12348,12 +12667,24 @@ packages: resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} dev: true + /make-iterator@1.0.1: + resolution: {integrity: sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw==} + engines: {node: '>=0.10.0'} + dependencies: + kind-of: 6.0.3 + dev: true + /makeerror@1.0.12: resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} dependencies: tmpl: 1.0.5 dev: true + /map-cache@0.2.2: + resolution: {integrity: sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==} + engines: {node: '>=0.10.0'} + dev: true + /map-obj@1.0.1: resolution: {integrity: sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==} engines: {node: '>=0.10.0'} @@ -12655,6 +12986,11 @@ packages: resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==} dev: true + /mute-stream@1.0.0: + resolution: {integrity: sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + dev: true + /mylas@2.1.13: resolution: {integrity: sha512-+MrqnJRtxdF+xngFfUUkIMQrUUL0KsxbADUkn23Z/4ibGg192Q+z+CQyiYwvWTsYjJygmMR8+w3ZDa98Zh6ESg==} engines: {node: '>=12.0.0'} @@ -12804,6 +13140,25 @@ packages: resolve: 1.22.8 dev: true + /node-plop@0.32.0: + resolution: {integrity: sha512-lKFSRSRuDHhwDKMUobdsvaWCbbDRbV3jMUSMiajQSQux1aNUevAZVxUHc2JERI//W8ABPRbi3ebYuSuIzkNIpQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + '@types/inquirer': 9.0.7 + change-case: 4.1.2 + del: 7.1.0 + globby: 13.2.2 + handlebars: 4.7.8 + inquirer: 9.2.23 + isbinaryfile: 5.0.2 + lodash.get: 4.4.2 + lower-case: 2.0.2 + mkdirp: 3.0.1 + resolve: 1.22.8 + title-case: 3.0.3 + upper-case: 2.0.2 + dev: true + /node-polyfill-webpack-plugin@2.0.1(webpack@5.91.0): resolution: {integrity: sha512-ZUMiCnZkP1LF0Th2caY6J/eKKoA0TefpoVa68m/LQU1I/mE8rGt4fNYGgNuCcK+aG8P8P43nbeJ2RqJMOL/Y1A==} engines: {node: '>=12'} @@ -12981,6 +13336,16 @@ packages: object-keys: 1.1.1 dev: true + /object.defaults@1.1.0: + resolution: {integrity: sha512-c/K0mw/F11k4dEUBMW8naXUuBuhxRCfG7W+yFy8EcijU/rSmazOUd1XAEEe6bC0OuXY4HUKjTJv7xbxIMqdxrA==} + engines: {node: '>=0.10.0'} + dependencies: + array-each: 1.0.1 + array-slice: 1.1.0 + for-own: 1.0.0 + isobject: 3.0.1 + dev: true + /object.entries@1.1.7: resolution: {integrity: sha512-jCBs/0plmPsOnrKAfFQXRG2NFjlhZgjjcBLSmTnEhU8U6vVTsVe8ANeQJCHTl3gSsI4J+0emOoCgoKlmQPMgmA==} engines: {node: '>= 0.4'} @@ -13015,6 +13380,21 @@ packages: es-abstract: 1.22.3 dev: true + /object.map@1.0.1: + resolution: {integrity: sha512-3+mAJu2PLfnSVGHwIWubpOFLscJANBKuB/6A4CxBstc4aqwQY0FWcsppuy4jU5GSB95yES5JHSI+33AWuS4k6w==} + engines: {node: '>=0.10.0'} + dependencies: + for-own: 1.0.0 + make-iterator: 1.0.1 + dev: true + + /object.pick@1.3.0: + resolution: {integrity: sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==} + engines: {node: '>=0.10.0'} + dependencies: + isobject: 3.0.1 + dev: true + /object.values@1.1.7: resolution: {integrity: sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==} engines: {node: '>= 0.4'} @@ -13114,6 +13494,21 @@ packages: wcwidth: 1.0.1 dev: true + /ora@8.0.1: + resolution: {integrity: sha512-ANIvzobt1rls2BDny5fWZ3ZVKyD6nscLvfFRpQgfWsythlcsVUC9kL0zq6j2Z5z9wwp1kd7wpsD/T9qNPVLCaQ==} + engines: {node: '>=18'} + dependencies: + chalk: 5.3.0 + cli-cursor: 4.0.0 + cli-spinners: 2.9.2 + is-interactive: 2.0.0 + is-unicode-supported: 2.0.0 + log-symbols: 6.0.0 + stdin-discarder: 0.2.2 + string-width: 7.1.0 + strip-ansi: 7.1.0 + dev: true + /os-browserify@0.3.0: resolution: {integrity: sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==} dev: true @@ -13211,6 +13606,13 @@ packages: aggregate-error: 3.1.0 dev: true + /p-map@5.5.0: + resolution: {integrity: sha512-VFqfGDHlx87K66yZrNdI4YGtD70IRyd+zSvgks6mzHPRNkoKy+9EKP4SFC77/vTTQYmRmti7dvqC+m5jBrBAcg==} + engines: {node: '>=12'} + dependencies: + aggregate-error: 4.0.1 + dev: true + /p-try@2.2.0: resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} engines: {node: '>=6'} @@ -13291,6 +13693,15 @@ packages: safe-buffer: 5.2.1 dev: true + /parse-filepath@1.0.2: + resolution: {integrity: sha512-FwdRXKCohSVeXqwtYonZTXtbGJKrn+HNyWDYVcp5yuJlesTwNH4rsmRZ+GrKAPJ5bLpRxESMeS+Rl0VCHRvB2Q==} + engines: {node: '>=0.8'} + dependencies: + is-absolute: 1.0.0 + map-cache: 0.2.2 + path-root: 0.1.1 + dev: true + /parse-json@5.2.0: resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} engines: {node: '>=8'} @@ -13341,6 +13752,13 @@ packages: no-case: 2.3.2 dev: true + /path-case@3.0.4: + resolution: {integrity: sha512-qO4qCFjXqVTrcbPt/hQfhTQ+VhFsqNKOPtytgNKkKxSoEp3XPUQ8ObFuePylOIok5gjn69ry8XiULxCwot3Wfg==} + dependencies: + dot-case: 3.0.4 + tslib: 2.6.2 + dev: true + /path-exists@3.0.0: resolution: {integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==} engines: {node: '>=4'} @@ -13375,6 +13793,18 @@ packages: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} dev: true + /path-root-regex@0.1.2: + resolution: {integrity: sha512-4GlJ6rZDhQZFE0DPVKh0e9jmZ5egZfxTkp7bcRDuPlJXbAwhxcl2dINPUAsjLdejqaLsCeg8axcLjIbvBjN4pQ==} + engines: {node: '>=0.10.0'} + dev: true + + /path-root@0.1.1: + resolution: {integrity: sha512-QLcPegTHF11axjfojBIoDygmS2E3Lf+8+jI6wOVmNVenrKSo3mFdSGiIgdSHenczw3wPtlVMQaFVwGmM7BJdtg==} + engines: {node: '>=0.10.0'} + dependencies: + path-root-regex: 0.1.2 + dev: true + /path-scurry@1.10.1: resolution: {integrity: sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==} engines: {node: '>=16 || 14 >=14.17'} @@ -13518,6 +13948,21 @@ packages: queue-lit: 1.5.2 dev: true + /plop@4.0.1: + resolution: {integrity: sha512-5n8QU93kvL/ObOzBcPAB1siVFtAH1TZM6TntJ3JK5kXT0jIgnQV+j+uaOWWFJlg1cNkzLYm8klgASF65K36q9w==} + engines: {node: '>=18'} + hasBin: true + dependencies: + '@types/liftoff': 4.0.3 + chalk: 5.3.0 + interpret: 3.1.1 + liftoff: 4.0.0 + minimist: 1.2.8 + node-plop: 0.32.0 + ora: 8.0.1 + v8flags: 4.0.1 + dev: true + /pluralize@8.0.0: resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} engines: {node: '>=4'} @@ -14236,6 +14681,13 @@ packages: tslib: 2.6.2 dev: true + /rechoir@0.8.0: + resolution: {integrity: sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==} + engines: {node: '>= 10.13.0'} + dependencies: + resolve: 1.22.8 + dev: true + /redent@3.0.0: resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} engines: {node: '>=8'} @@ -14411,6 +14863,14 @@ packages: global-modules: 0.2.3 dev: true + /resolve-dir@1.0.1: + resolution: {integrity: sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg==} + engines: {node: '>=0.10.0'} + dependencies: + expand-tilde: 2.0.2 + global-modules: 1.0.0 + dev: true + /resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} @@ -14571,6 +15031,11 @@ packages: engines: {node: '>=0.12.0'} dev: true + /run-async@3.0.0: + resolution: {integrity: sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==} + engines: {node: '>=0.12.0'} + dev: true + /run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} dependencies: @@ -14740,6 +15205,14 @@ packages: upper-case-first: 1.1.2 dev: true + /sentence-case@3.0.4: + resolution: {integrity: sha512-8LS0JInaQMCRoQ7YUytAo/xUu5W2XnQxV2HI/6uM6U7CITS1RqPElr30V6uIqyMKM9lJGRVFy5/4CuzcixNYSg==} + dependencies: + no-case: 3.0.4 + tslib: 2.6.2 + upper-case-first: 2.0.2 + dev: true + /serialize-javascript@6.0.2: resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} dependencies: @@ -14956,6 +15429,13 @@ packages: no-case: 2.3.2 dev: true + /snake-case@3.0.4: + resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==} + dependencies: + dot-case: 3.0.4 + tslib: 2.6.2 + dev: true + /socks-proxy-agent@8.0.1: resolution: {integrity: sha512-59EjPbbgg8U3x62hhKOFVAmySQUcfRQ4C7Q/D5sEHnZTQRrQlNKINks44DMR1gwXp0p4LaVIeccX2KHTTcHVqQ==} engines: {node: '>= 14'} @@ -15084,6 +15564,11 @@ packages: engines: {node: '>= 0.8'} dev: true + /stdin-discarder@0.2.2: + resolution: {integrity: sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==} + engines: {node: '>=18'} + dev: true + /stop-iteration-iterator@1.0.0: resolution: {integrity: sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==} engines: {node: '>= 0.4'} @@ -15599,6 +16084,12 @@ packages: upper-case: 1.1.3 dev: true + /title-case@3.0.3: + resolution: {integrity: sha512-e1zGYRvbffpcHIrnuqT0Dh+gEJtDaxDSoG4JAIpq4oDFyooziLBIiYQv0GBT4FUAnUop5uZ1hiIAj7oAF6sOCA==} + dependencies: + tslib: 2.6.2 + dev: true + /tmp@0.0.33: resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} engines: {node: '>=0.6.0'} @@ -16162,6 +16653,11 @@ packages: which-boxed-primitive: 1.0.2 dev: true + /unc-path-regex@0.1.2: + resolution: {integrity: sha512-eXL4nmJT7oCpkZsHZUOJo8hcX3GbsiDOa0Qu9F646fi8dT3XuSVopVqAcEiVzSKKH7UoDti23wNX3qGFxcW5Qg==} + engines: {node: '>=0.10.0'} + dev: true + /undici-types@5.26.5: resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} dev: true @@ -16273,10 +16769,22 @@ packages: upper-case: 1.1.3 dev: true + /upper-case-first@2.0.2: + resolution: {integrity: sha512-514ppYHBaKwfJRK/pNC6c/OxfGa0obSnAl106u97Ed0I625Nin96KAjttZF6ZL3e1XLtphxnqrOi9iWgm+u+bg==} + dependencies: + tslib: 2.6.2 + dev: true + /upper-case@1.1.3: resolution: {integrity: sha512-WRbjgmYzgXkCV7zNVpy5YgrHgbBv126rMALQQMrmzOVC4GM2waQ9x7xtm8VU+1yF2kWyPzI9zbZ48n4vSxwfSA==} dev: true + /upper-case@2.0.2: + resolution: {integrity: sha512-KgdgDGJt2TpuwBUIjgG6lzw2GWFRCW9Qkfkiv0DxqHHLYJHmtmdUIKcZd8rHgFSjopVTlw6ggzCm1b8MFQwikg==} + dependencies: + tslib: 2.6.2 + dev: true + /uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} dependencies: @@ -16385,6 +16893,11 @@ packages: convert-source-map: 2.0.0 dev: true + /v8flags@4.0.1: + resolution: {integrity: sha512-fcRLaS4H/hrZk9hYwbdRM35D0U8IYMfEClhXxCivOojl+yTRAZH3Zy2sSy6qVCiGbV9YAtPssP6jaChqC9vPCg==} + engines: {node: '>= 10.13.0'} + dev: true + /validate-npm-package-license@3.0.4: resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} dependencies: