From ede3369632ba2801b32f964125a4db18a97b67ef Mon Sep 17 00:00:00 2001 From: Rabithua <1289770378@qq.com> Date: Sun, 30 Mar 2025 12:32:05 +0800 Subject: [PATCH 1/4] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8Ddropdown.tsx?= =?UTF-8?q?=E4=B8=AD=E7=9A=84=E5=8B=BE=E9=80=89=E6=A0=B7=E5=BC=8F=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/ui/components/ui/dropdown.tsx | 117 +++++++++++++++++------------ 1 file changed, 70 insertions(+), 47 deletions(-) diff --git a/apps/ui/components/ui/dropdown.tsx b/apps/ui/components/ui/dropdown.tsx index f551d28..4dd22f8 100644 --- a/apps/ui/components/ui/dropdown.tsx +++ b/apps/ui/components/ui/dropdown.tsx @@ -1,25 +1,25 @@ -"use client" +"use client"; -import { cn } from "@/utils/classes" -import { IconCheck } from "justd-icons" +import { cn } from "@/utils/classes"; +import { IconCheck } from "justd-icons"; import type { ListBoxItemProps, SectionProps, SeparatorProps, TextProps, -} from "react-aria-components" +} from "react-aria-components"; import { Collection, + composeRenderProps, Header, ListBoxItem as ListBoxItemPrimitive, ListBoxSection, Separator, Text, - composeRenderProps, -} from "react-aria-components" -import { tv } from "tailwind-variants" -import { Keyboard } from "./keyboard" -import { Typography } from "./typography" +} from "react-aria-components"; +import { tv } from "tailwind-variants"; +import { Keyboard } from "./keyboard"; +import { Typography } from "./typography"; const dropdownItemStyles = tv({ base: [ @@ -36,7 +36,8 @@ const dropdownItemStyles = tv({ true: "text-muted-fg forced-colors:text-[GrayText]", }, isSelected: { - true: "**:data-[slot=avatar]:*:hidden **:data-[slot=avatar]:hidden **:data-[slot=icon]:hidden", + true: + "**:data-[slot=avatar]:*:hidden **:data-[slot=avatar]:hidden **:data-[slot=icon]:hidden", }, isFocused: { false: "data-danger:text-danger", @@ -48,58 +49,66 @@ const dropdownItemStyles = tv({ ], }, }, -}) +}); const dropdownSectionStyles = tv({ slots: { section: "col-span-full grid grid-cols-[auto_1fr]", - header: "col-span-full px-2.5 py-1 font-medium text-muted-fg text-sm sm:text-xs", + header: + "col-span-full px-2.5 py-1 font-medium text-muted-fg text-sm sm:text-xs", }, -}) +}); -const { section, header } = dropdownSectionStyles() +const { section, header } = dropdownSectionStyles(); interface DropdownSectionProps extends SectionProps { - title?: string + title?: string; } -const DropdownSection = ({ className, ...props }: DropdownSectionProps) => { +const DropdownSection = ( + { className, ...props }: DropdownSectionProps, +) => { return ( {"title" in props &&
{props.title}
} {props.children}
- ) -} + ); +}; -type DropdownItemProps = ListBoxItemProps +type DropdownItemProps = ListBoxItemProps; const DropdownItem = ({ className, ...props }: DropdownItemProps) => { return ( - dropdownItemStyles({ ...renderProps, className }), - )} + dropdownItemStyles({ ...renderProps, className }))} {...props} > {composeRenderProps(props.children, (children, { isSelected }) => ( <> - {isSelected && } - {typeof children === "string" ? {children} : children} + {typeof children === "string" + ? {children} + : children} + {isSelected && ( + + )} ))} - ) -} + ); +}; interface DropdownItemDetailProps extends TextProps { - label?: TextProps["children"] - description?: TextProps["children"] + label?: TextProps["children"]; + description?: TextProps["children"]; classNames?: { - label?: TextProps["className"] - description?: TextProps["className"] - } + label?: TextProps["className"]; + description?: TextProps["className"]; + }; } const DropdownItemDetails = ({ @@ -108,7 +117,7 @@ const DropdownItemDetails = ({ classNames, ...props }: DropdownItemDetailProps) => { - const { slot, children, title, ...restProps } = props + const { slot, children, title, ...restProps } = props; return (
- ) -} + ); +}; interface DropdownLabelProps extends TextProps { - ref?: React.Ref + ref?: React.Ref; } const DropdownLabel = ({ className, ref, ...props }: DropdownLabelProps) => ( - + -) +); const DropdownSeparator = ({ className, ...props }: SeparatorProps) => ( ( className={cn("-mx-1 col-span-full my-1 h-px bg-border", className)} {...props} /> -) +); -const DropdownKeyboard = ({ className, ...props }: React.ComponentProps) => { - return -} +const DropdownKeyboard = ( + { className, ...props }: React.ComponentProps, +) => { + return ( + + ); +}; /** * Note: This is not exposed component, but it's used in other components to render dropdowns. * @internal */ -export type { DropdownSectionProps, DropdownLabelProps, DropdownItemProps, DropdownItemDetailProps } +export type { + DropdownItemDetailProps, + DropdownItemProps, + DropdownLabelProps, + DropdownSectionProps, +}; export { - DropdownSeparator, DropdownItem, - DropdownLabel, - DropdownKeyboard, - dropdownItemStyles, DropdownItemDetails, + dropdownItemStyles, + DropdownKeyboard, + DropdownLabel, DropdownSection, dropdownSectionStyles, -} + DropdownSeparator, +}; From 87dd344449de664ecc3e733e30ee042da154e88e Mon Sep 17 00:00:00 2001 From: Rabithua <1289770378@qq.com> Date: Sun, 30 Mar 2025 16:17:24 +0800 Subject: [PATCH 2/4] =?UTF-8?q?refactor:=20=E4=BC=98=E5=8C=96calendar.tsx?= =?UTF-8?q?=E4=B8=AD=E7=9A=84=E4=BB=A3=E7=A0=81=E6=A0=BC=E5=BC=8F=E5=92=8C?= =?UTF-8?q?=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/ui/components/ui/calendar.tsx | 144 +++++++++++++++++------------ 1 file changed, 85 insertions(+), 59 deletions(-) diff --git a/apps/ui/components/ui/calendar.tsx b/apps/ui/components/ui/calendar.tsx index c047a61..8a72f40 100644 --- a/apps/ui/components/ui/calendar.tsx +++ b/apps/ui/components/ui/calendar.tsx @@ -1,37 +1,46 @@ -"use client" +"use client"; -import { IconChevronLgLeft, IconChevronLgRight } from "justd-icons" -import { CalendarStateContext } from "react-aria-components" -import type { CalendarProps as CalendarPrimitiveProps, DateValue } from "react-aria-components" +import { IconChevronLgLeft, IconChevronLgRight } from "justd-icons"; +import { CalendarStateContext } from "react-aria-components"; +import type { + CalendarProps as CalendarPrimitiveProps, + DateValue, +} from "react-aria-components"; import { + Calendar as CalendarPrimitive, CalendarCell, CalendarGrid, CalendarGridBody, CalendarGridHeader as CalendarGridHeaderPrimitive, CalendarHeaderCell, - Calendar as CalendarPrimitive, + composeRenderProps, Heading, Text, - composeRenderProps, useLocale, -} from "react-aria-components" +} from "react-aria-components"; -import { cn } from "@/utils/classes" -import { type CalendarDate, getLocalTimeZone, today } from "@internationalized/date" -import { useDateFormatter } from "@react-aria/i18n" -import type { CalendarState } from "@react-stately/calendar" -import { use } from "react" -import { Button } from "./button" -import { Select } from "./select" +import { cn } from "@/utils/classes"; +import { + type CalendarDate, + getLocalTimeZone, + today, +} from "@internationalized/date"; +import { useDateFormatter } from "@react-aria/i18n"; +import type { CalendarState } from "@react-stately/calendar"; +import { use } from "react"; +import { Button } from "./button"; +import { Select } from "./select"; interface CalendarProps extends Omit, "visibleDuration"> { - errorMessage?: string - className?: string + errorMessage?: string; + className?: string; } -const Calendar = ({ errorMessage, className, ...props }: CalendarProps) => { - const now = today(getLocalTimeZone()) +const Calendar = ( + { errorMessage, className, ...props }: CalendarProps, +) => { + const now = today(getLocalTimeZone()); return ( @@ -42,16 +51,18 @@ const Calendar = ({ errorMessage, className, ...props }: Ca {(date) => ( - cn( - "relative flex size-10 cursor-default items-center justify-center rounded-lg text-fg tabular-nums outline-hidden hover:bg-secondary-fg/15 sm:size-9 sm:text-sm/6 forced-colors:text-[ButtonText] forced-colors:outline-0", - isSelected && - "bg-primary pressed:bg-primary text-primary-fg hover:bg-primary/90 data-invalid:bg-danger data-invalid:text-danger-fg forced-colors:bg-[Highlight] forced-colors:text-[Highlight] forced-colors:data-invalid:bg-[Mark]", - isDisabled && "text-muted-fg forced-colors:text-[GrayText]", - date.compare(now) === 0 && - "after:-translate-x-1/2 after:pointer-events-none after:absolute after:start-1/2 after:bottom-1 after:z-10 after:size-[3px] after:rounded-full after:bg-primary selected:after:bg-primary-fg data-focus-visible:after:bg-primary-fg", - className, - ), + className={composeRenderProps( + className, + (className, { isSelected, isDisabled }) => + cn( + "relative flex size-10 cursor-default items-center justify-center rounded-lg text-fg tabular-nums outline-hidden hover:bg-secondary-fg/15 sm:size-9 sm:text-sm/6 forced-colors:text-[ButtonText] forced-colors:outline-0", + isSelected && + "bg-primary pressed:bg-primary text-primary-fg hover:bg-primary/90 data-invalid:bg-danger data-invalid:text-danger-fg forced-colors:bg-[Highlight] forced-colors:text-[Highlight] forced-colors:data-invalid:bg-[Mark]", + isDisabled && "text-muted-fg forced-colors:text-[GrayText]", + date.compare(now) === 0 && + "after:-translate-x-1/2 after:pointer-events-none after:absolute after:start-1/2 after:bottom-1 after:z-10 after:size-[3px] after:rounded-full after:bg-primary selected:after:bg-primary-fg data-focus-visible:after:bg-primary-fg", + className, + ), )} /> )} @@ -63,21 +74,24 @@ const Calendar = ({ errorMessage, className, ...props }: Ca )} - ) -} + ); +}; const CalendarHeader = ({ isRange, className, ...props }: React.ComponentProps<"header"> & { isRange?: boolean }) => { - const { direction } = useLocale() - const state = use(CalendarStateContext)! + const { direction } = useLocale(); + const state = use(CalendarStateContext)!; return (
{!isRange && ( @@ -114,68 +128,80 @@ const CalendarHeader = ({
- ) -} + ); +}; const SelectMonth = ({ state }: { state: CalendarState }) => { - const months = [] + const months = []; const formatter = useDateFormatter({ month: "long", timeZone: state.timeZone, - }) + }); - const numMonths = state.focusedDate.calendar.getMonthsInYear(state.focusedDate) + const numMonths = state.focusedDate.calendar.getMonthsInYear( + state.focusedDate, + ); for (let i = 1; i <= numMonths; i++) { - const date = state.focusedDate.set({ month: i }) - months.push(formatter.format(date.toDate(state.timeZone))) + const date = state.focusedDate.set({ month: i }); + months.push(formatter.format(date.toDate(state.timeZone))); } return ( - ) -} + ); +}; const SelectYear = ({ state }: { state: CalendarState }) => { - const years: { value: CalendarDate; formatted: string }[] = [] + const years: { value: CalendarDate; formatted: string }[] = []; const formatter = useDateFormatter({ year: "numeric", timeZone: state.timeZone, - }) + }); for (let i = -20; i <= 20; i++) { - const date = state.focusedDate.add({ years: i }) + const date = state.focusedDate.add({ years: i }); years.push({ value: date, formatted: formatter.format(date.toDate(state.timeZone)), - }) + }); } return ( - ) -} + ); +}; const CalendarGridHeader = () => { return ( @@ -195,8 +221,8 @@ const CalendarGridHeader = () => { )} - ) -} + ); +}; -export type { CalendarProps } -export { Calendar, CalendarHeader, CalendarGridHeader } \ No newline at end of file +export type { CalendarProps }; +export { Calendar, CalendarGridHeader, CalendarHeader }; From b385d6d0cc2e473f05886dc27fc0c8a828762003 Mon Sep 17 00:00:00 2001 From: Rabithua <1289770378@qq.com> Date: Sun, 30 Mar 2025 16:19:25 +0800 Subject: [PATCH 3/4] =?UTF-8?q?refactor:=20=E4=BC=98=E5=8C=96=E5=A4=9A?= =?UTF-8?q?=E4=B8=AA=E7=BB=84=E4=BB=B6=E4=B8=AD=E7=9A=84=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E5=92=8C=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/ui/components/navbar.tsx | 2 +- apps/ui/components/ui/button.tsx | 2 +- apps/ui/components/ui/calendar.tsx | 143 ++++++++++------------- apps/ui/components/ui/date-picker.tsx | 18 +-- apps/ui/components/ui/dropdown.tsx | 109 +++++++----------- apps/ui/components/ui/field.tsx | 16 +-- apps/ui/components/ui/keyboard.tsx | 2 +- apps/ui/components/ui/list-box.tsx | 2 +- apps/ui/components/ui/select.tsx | 18 +-- apps/ui/components/ui/text-field.tsx | 156 +++++++++++++------------- apps/ui/components/ui/textarea.tsx | 18 +-- 11 files changed, 220 insertions(+), 266 deletions(-) diff --git a/apps/ui/components/navbar.tsx b/apps/ui/components/navbar.tsx index bba71e5..6e6f40e 100644 --- a/apps/ui/components/navbar.tsx +++ b/apps/ui/components/navbar.tsx @@ -126,7 +126,7 @@ export function Navbar() { intent="outline" className="h-9 items-center" > -
+
Search... diff --git a/apps/ui/components/ui/button.tsx b/apps/ui/components/ui/button.tsx index c52b24b..cf259a7 100644 --- a/apps/ui/components/ui/button.tsx +++ b/apps/ui/components/ui/button.tsx @@ -9,7 +9,7 @@ import { import { tv } from "tailwind-variants" import { focusButtonStyles } from "./primitive" -import { Typography } from "./typography"; +import { Typography } from "./typography" const buttonStyles = tv({ extend: focusButtonStyles, diff --git a/apps/ui/components/ui/calendar.tsx b/apps/ui/components/ui/calendar.tsx index 8a72f40..a06bfed 100644 --- a/apps/ui/components/ui/calendar.tsx +++ b/apps/ui/components/ui/calendar.tsx @@ -1,46 +1,37 @@ -"use client"; +"use client" -import { IconChevronLgLeft, IconChevronLgRight } from "justd-icons"; -import { CalendarStateContext } from "react-aria-components"; -import type { - CalendarProps as CalendarPrimitiveProps, - DateValue, -} from "react-aria-components"; +import { IconChevronLgLeft, IconChevronLgRight } from "justd-icons" +import { CalendarStateContext } from "react-aria-components" +import type { CalendarProps as CalendarPrimitiveProps, DateValue } from "react-aria-components" import { - Calendar as CalendarPrimitive, CalendarCell, CalendarGrid, CalendarGridBody, CalendarGridHeader as CalendarGridHeaderPrimitive, CalendarHeaderCell, - composeRenderProps, + Calendar as CalendarPrimitive, Heading, Text, + composeRenderProps, useLocale, -} from "react-aria-components"; +} from "react-aria-components" -import { cn } from "@/utils/classes"; -import { - type CalendarDate, - getLocalTimeZone, - today, -} from "@internationalized/date"; -import { useDateFormatter } from "@react-aria/i18n"; -import type { CalendarState } from "@react-stately/calendar"; -import { use } from "react"; -import { Button } from "./button"; -import { Select } from "./select"; +import { cn } from "@/utils/classes" +import { type CalendarDate, getLocalTimeZone, today } from "@internationalized/date" +import { useDateFormatter } from "@react-aria/i18n" +import type { CalendarState } from "@react-stately/calendar" +import { use } from "react" +import { Button } from "./button" +import { Select } from "./select" interface CalendarProps extends Omit, "visibleDuration"> { - errorMessage?: string; - className?: string; + errorMessage?: string + className?: string } -const Calendar = ( - { errorMessage, className, ...props }: CalendarProps, -) => { - const now = today(getLocalTimeZone()); +const Calendar = ({ errorMessage, className, ...props }: CalendarProps) => { + const now = today(getLocalTimeZone()) return ( @@ -51,18 +42,16 @@ const Calendar = ( {(date) => ( - cn( - "relative flex size-10 cursor-default items-center justify-center rounded-lg text-fg tabular-nums outline-hidden hover:bg-secondary-fg/15 sm:size-9 sm:text-sm/6 forced-colors:text-[ButtonText] forced-colors:outline-0", - isSelected && - "bg-primary pressed:bg-primary text-primary-fg hover:bg-primary/90 data-invalid:bg-danger data-invalid:text-danger-fg forced-colors:bg-[Highlight] forced-colors:text-[Highlight] forced-colors:data-invalid:bg-[Mark]", - isDisabled && "text-muted-fg forced-colors:text-[GrayText]", - date.compare(now) === 0 && - "after:-translate-x-1/2 after:pointer-events-none after:absolute after:start-1/2 after:bottom-1 after:z-10 after:size-[3px] after:rounded-full after:bg-primary selected:after:bg-primary-fg data-focus-visible:after:bg-primary-fg", - className, - ), + className={composeRenderProps(className, (className, { isSelected, isDisabled }) => + cn( + "relative flex size-10 cursor-default items-center justify-center rounded-lg text-fg tabular-nums outline-hidden hover:bg-secondary-fg/15 sm:size-9 sm:text-sm/6 forced-colors:text-[ButtonText] forced-colors:outline-0", + isSelected && + "bg-primary pressed:bg-primary text-primary-fg hover:bg-primary/90 data-invalid:bg-danger data-invalid:text-danger-fg forced-colors:bg-[Highlight] forced-colors:text-[Highlight] forced-colors:data-invalid:bg-[Mark]", + isDisabled && "text-muted-fg forced-colors:text-[GrayText]", + date.compare(now) === 0 && + "after:-translate-x-1/2 after:pointer-events-none after:absolute after:start-1/2 after:bottom-1 after:z-10 after:size-[3px] after:rounded-full after:bg-primary selected:after:bg-primary-fg data-focus-visible:after:bg-primary-fg", + className, + ), )} /> )} @@ -74,24 +63,21 @@ const Calendar = ( )} - ); -}; + ) +} const CalendarHeader = ({ isRange, className, ...props }: React.ComponentProps<"header"> & { isRange?: boolean }) => { - const { direction } = useLocale(); - const state = use(CalendarStateContext)!; + const { direction } = useLocale() + const state = use(CalendarStateContext)! return (
{!isRange && ( @@ -128,80 +114,67 @@ const CalendarHeader = ({
- ); -}; + ) +} const SelectMonth = ({ state }: { state: CalendarState }) => { - const months = []; + const months = [] const formatter = useDateFormatter({ month: "long", timeZone: state.timeZone, - }); + }) - const numMonths = state.focusedDate.calendar.getMonthsInYear( - state.focusedDate, - ); + const numMonths = state.focusedDate.calendar.getMonthsInYear(state.focusedDate) for (let i = 1; i <= numMonths; i++) { - const date = state.focusedDate.set({ month: i }); - months.push(formatter.format(date.toDate(state.timeZone))); + const date = state.focusedDate.set({ month: i }) + months.push(formatter.format(date.toDate(state.timeZone))) } return ( - ); -}; + ) +} const SelectYear = ({ state }: { state: CalendarState }) => { - const years: { value: CalendarDate; formatted: string }[] = []; + const years: { value: CalendarDate; formatted: string }[] = [] const formatter = useDateFormatter({ year: "numeric", timeZone: state.timeZone, - }); + }) for (let i = -20; i <= 20; i++) { - const date = state.focusedDate.add({ years: i }); + const date = state.focusedDate.add({ years: i }) years.push({ value: date, formatted: formatter.format(date.toDate(state.timeZone)), - }); + }) } return ( - ); -}; + ) +} const CalendarGridHeader = () => { return ( @@ -221,8 +194,8 @@ const CalendarGridHeader = () => { )} - ); -}; + ) +} -export type { CalendarProps }; -export { Calendar, CalendarGridHeader, CalendarHeader }; +export type { CalendarProps } +export { Calendar, CalendarGridHeader, CalendarHeader } diff --git a/apps/ui/components/ui/date-picker.tsx b/apps/ui/components/ui/date-picker.tsx index 453992d..37e35d9 100644 --- a/apps/ui/components/ui/date-picker.tsx +++ b/apps/ui/components/ui/date-picker.tsx @@ -97,33 +97,33 @@ const DatePicker = ({ {...props} className={composeTailwindRenderProps(className, "group flex w-full flex-col gap-y-1")} > - {label && + {label && ( - } - + )} + - {description && + {description && ( {description} - } - + )} + {(leftDescription || errorMessage || rightDescription) && ( -
+
{errorMessage ? {errorMessage} : leftDescription}
{rightDescription}
)} - + ) } export type { DatePickerProps, DateValue, ValidationResult } -export { DatePicker, DatePickerIcon, DatePickerOverlay } \ No newline at end of file +export { DatePicker, DatePickerIcon, DatePickerOverlay } diff --git a/apps/ui/components/ui/dropdown.tsx b/apps/ui/components/ui/dropdown.tsx index 4dd22f8..a5844d4 100644 --- a/apps/ui/components/ui/dropdown.tsx +++ b/apps/ui/components/ui/dropdown.tsx @@ -1,25 +1,25 @@ -"use client"; +"use client" -import { cn } from "@/utils/classes"; -import { IconCheck } from "justd-icons"; +import { cn } from "@/utils/classes" +import { IconCheck } from "justd-icons" import type { ListBoxItemProps, SectionProps, SeparatorProps, TextProps, -} from "react-aria-components"; +} from "react-aria-components" import { Collection, - composeRenderProps, Header, ListBoxItem as ListBoxItemPrimitive, ListBoxSection, Separator, Text, -} from "react-aria-components"; -import { tv } from "tailwind-variants"; -import { Keyboard } from "./keyboard"; -import { Typography } from "./typography"; + composeRenderProps, +} from "react-aria-components" +import { tv } from "tailwind-variants" +import { Keyboard } from "./keyboard" +import { Typography } from "./typography" const dropdownItemStyles = tv({ base: [ @@ -36,8 +36,7 @@ const dropdownItemStyles = tv({ true: "text-muted-fg forced-colors:text-[GrayText]", }, isSelected: { - true: - "**:data-[slot=avatar]:*:hidden **:data-[slot=avatar]:hidden **:data-[slot=icon]:hidden", + true: "**:data-[slot=avatar]:*:hidden **:data-[slot=avatar]:hidden **:data-[slot=icon]:hidden", }, isFocused: { false: "data-danger:text-danger", @@ -49,66 +48,58 @@ const dropdownItemStyles = tv({ ], }, }, -}); +}) const dropdownSectionStyles = tv({ slots: { section: "col-span-full grid grid-cols-[auto_1fr]", - header: - "col-span-full px-2.5 py-1 font-medium text-muted-fg text-sm sm:text-xs", + header: "col-span-full px-2.5 py-1 font-medium text-muted-fg text-sm sm:text-xs", }, -}); +}) -const { section, header } = dropdownSectionStyles(); +const { section, header } = dropdownSectionStyles() interface DropdownSectionProps extends SectionProps { - title?: string; + title?: string } -const DropdownSection = ( - { className, ...props }: DropdownSectionProps, -) => { +const DropdownSection = ({ className, ...props }: DropdownSectionProps) => { return ( {"title" in props &&
{props.title}
} {props.children}
- ); -}; + ) +} -type DropdownItemProps = ListBoxItemProps; +type DropdownItemProps = ListBoxItemProps const DropdownItem = ({ className, ...props }: DropdownItemProps) => { return ( - dropdownItemStyles({ ...renderProps, className }))} + dropdownItemStyles({ ...renderProps, className }), + )} {...props} > {composeRenderProps(props.children, (children, { isSelected }) => ( <> - {typeof children === "string" - ? {children} - : children} - {isSelected && ( - - )} + {typeof children === "string" ? {children} : children} + {isSelected && } ))} - ); -}; + ) +} interface DropdownItemDetailProps extends TextProps { - label?: TextProps["children"]; - description?: TextProps["children"]; + label?: TextProps["children"] + description?: TextProps["children"] classNames?: { - label?: TextProps["className"]; - description?: TextProps["className"]; - }; + label?: TextProps["className"] + description?: TextProps["className"] + } } const DropdownItemDetails = ({ @@ -117,7 +108,7 @@ const DropdownItemDetails = ({ classNames, ...props }: DropdownItemDetailProps) => { - const { slot, children, title, ...restProps } = props; + const { slot, children, title, ...restProps } = props return (
- ); -}; + ) +} interface DropdownLabelProps extends TextProps { - ref?: React.Ref; + ref?: React.Ref } const DropdownLabel = ({ className, ref, ...props }: DropdownLabelProps) => ( - + -); +) const DropdownSeparator = ({ className, ...props }: SeparatorProps) => ( ( className={cn("-mx-1 col-span-full my-1 h-px bg-border", className)} {...props} /> -); +) -const DropdownKeyboard = ( - { className, ...props }: React.ComponentProps, -) => { - return ( - - ); -}; +const DropdownKeyboard = ({ className, ...props }: React.ComponentProps) => { + return +} /** * Note: This is not exposed component, but it's used in other components to render dropdowns. * @internal */ -export type { - DropdownItemDetailProps, - DropdownItemProps, - DropdownLabelProps, - DropdownSectionProps, -}; +export type { DropdownItemDetailProps, DropdownItemProps, DropdownLabelProps, DropdownSectionProps } export { DropdownItem, DropdownItemDetails, @@ -202,4 +179,4 @@ export { DropdownSection, dropdownSectionStyles, DropdownSeparator, -}; +} diff --git a/apps/ui/components/ui/field.tsx b/apps/ui/components/ui/field.tsx index feb32dd..e834ad8 100644 --- a/apps/ui/components/ui/field.tsx +++ b/apps/ui/components/ui/field.tsx @@ -20,7 +20,7 @@ import { import { tv } from "tailwind-variants" import { composeTailwindRenderProps, focusStyles } from "./primitive" -import { Typography } from "./typography"; +import { Typography } from "./typography" interface FieldProps { label?: string @@ -116,13 +116,13 @@ const FieldGroup = ({ className, ...props }: GroupProps) => { return ( - fieldGroupStyles({ - ...renderProps, - className, - }), - )} + {...props} + className={composeRenderProps(className, (className, renderProps) => + fieldGroupStyles({ + ...renderProps, + className, + }), + )} /> ) diff --git a/apps/ui/components/ui/keyboard.tsx b/apps/ui/components/ui/keyboard.tsx index ae82764..078e4fc 100644 --- a/apps/ui/components/ui/keyboard.tsx +++ b/apps/ui/components/ui/keyboard.tsx @@ -5,7 +5,7 @@ import { tv } from "tailwind-variants" const keyboardStyles = tv({ slots: { - base: "hidden text-current/70 group-hover:text-fg group-disabled:opacity-50 group-data-focused:text-fg group-data-focused:opacity-90 lg:inline-flex items-center forced-colors:group-data-focused:text-[HighlightText]", + base: "hidden items-center text-current/70 group-hover:text-fg group-disabled:opacity-50 group-data-focused:text-fg group-data-focused:opacity-90 lg:inline-flex forced-colors:group-data-focused:text-[HighlightText]", kbd: "inline-grid min-h-5 min-w-[2ch] place-content-center rounded text-center font-sans text-[.75rem] uppercase", }, }) diff --git a/apps/ui/components/ui/list-box.tsx b/apps/ui/components/ui/list-box.tsx index 56f1a03..92338df 100644 --- a/apps/ui/components/ui/list-box.tsx +++ b/apps/ui/components/ui/list-box.tsx @@ -20,7 +20,7 @@ const ListBox = ({ className, ...props }: ListBoxProps) => {...props} className={composeTailwindRenderProps( className, - "flex grid max-h-96 w-full min-w-56 grid-cols-[auto_1fr] flex-col gap-y-1 overflow-auto overflow-y-auto rounded-xl border p-1 shadow-lg outline-hidden [scrollbar-width:thin] [&::-webkit-scrollbar]:size-0.5 *:[[role='group']+[role=group]]:mt-4 *:[[role='group']+[role=separator]]:mt-1", + "grid max-h-96 w-full min-w-56 grid-cols-[auto_1fr] flex-col gap-y-1 overflow-auto overflow-y-auto rounded-xl border p-1 shadow-lg outline-hidden [scrollbar-width:thin] [&::-webkit-scrollbar]:size-0.5 *:[[role='group']+[role=group]]:mt-4 *:[[role='group']+[role=separator]]:mt-1", )} /> ) diff --git a/apps/ui/components/ui/select.tsx b/apps/ui/components/ui/select.tsx index 115c818..d7c8fc5 100644 --- a/apps/ui/components/ui/select.tsx +++ b/apps/ui/components/ui/select.tsx @@ -68,22 +68,24 @@ const Select = ({ > {(values) => ( <> - {label && + {label && ( - } + )} {typeof props.children === "function" ? props.children(values) : props.children} - {description && + {description && ( {description} - } - + )} + {(leftDescription || errorMessage || rightDescription) && ( -
-
{errorMessage ? {errorMessage} : leftDescription}
+
+
+ {errorMessage ? {errorMessage} : leftDescription} +
{rightDescription}
@@ -169,4 +171,4 @@ Select.Trigger = SelectTrigger Select.List = SelectList export type { SelectProps, SelectTriggerProps } -export { Select } \ No newline at end of file +export { Select } diff --git a/apps/ui/components/ui/text-field.tsx b/apps/ui/components/ui/text-field.tsx index 0e53f08..70c2f57 100644 --- a/apps/ui/components/ui/text-field.tsx +++ b/apps/ui/components/ui/text-field.tsx @@ -10,11 +10,11 @@ import { type TextFieldProps as TextFieldPrimitiveProps, } from "react-aria-components" -import {FieldError, FieldProps} from "./field" +import { FieldError, type FieldProps } from "./field" import { Description, FieldGroup, Input, Label } from "./field" import { Loader } from "./loader" import { composeTailwindRenderProps } from "./primitive" -import {Typography} from "./typography"; +import { Typography } from "./typography" type InputType = Exclude @@ -40,20 +40,20 @@ interface NonRevealableTextFieldProps extends BaseTextFieldProps { type TextFieldProps = RevealableTextFieldProps | NonRevealableTextFieldProps const TextField = ({ - placeholder, - label, - description, - errorMessage, - prefix, - suffix, - isPending, - className, - isRevealable, - type, - leftDescription, - rightDescription, - ...props - }: TextFieldProps) => { + placeholder, + label, + description, + errorMessage, + prefix, + suffix, + isPending, + className, + isRevealable, + type, + leftDescription, + rightDescription, + ...props +}: TextFieldProps) => { const [isPasswordVisible, setIsPasswordVisible] = useState(false) const [autoError, setAutoError] = useState(null) @@ -76,68 +76,70 @@ const TextField = ({ } return ( - - {!props.children ? ( - <> - {label && - - - - } - + {!props.children ? ( + <> + {label && ( + + + + )} + + {prefix && typeof prefix === "string" ? ( + + {prefix} + + ) : ( + {prefix} + )} + + {isRevealable ? ( + - {prefix && typeof prefix === "string" ? ( - {prefix} - ) : ( - {prefix} - )} - - {isRevealable ? ( - - {isPasswordVisible ? : } - - ) : isPending ? ( - - ) : suffix ? ( - typeof suffix === "string" ? ( - {suffix} - ) : ( - suffix - ) - ) : null} - - - {description && {description}} - - {(leftDescription || autoError || rightDescription) && ( - -
-
{autoError ? {autoError} : leftDescription}
-
{rightDescription}
-
-
- )} - - ) : ( - props.children - )} -
+ {isPasswordVisible ? : } + + ) : isPending ? ( + + ) : suffix ? ( + typeof suffix === "string" ? ( + {suffix} + ) : ( + suffix + ) + ) : null} + + + {description && ( + + {description} + + )} + + {(leftDescription || autoError || rightDescription) && ( + +
+
{autoError ? {autoError} : leftDescription}
+
{rightDescription}
+
+
+ )} + + ) : ( + props.children + )} + ) } diff --git a/apps/ui/components/ui/textarea.tsx b/apps/ui/components/ui/textarea.tsx index bf91c1c..ef4311d 100644 --- a/apps/ui/components/ui/textarea.tsx +++ b/apps/ui/components/ui/textarea.tsx @@ -1,5 +1,6 @@ "use client" +import { useEffect, useState } from "react" import { TextArea as TextAreaPrimitive, TextField as TextFieldPrimitive, @@ -8,7 +9,6 @@ import { composeRenderProps, } from "react-aria-components" import { tv } from "tailwind-variants" -import { useState, useEffect } from "react" import { Description, FieldError, Label } from "./field" import { composeTailwindRenderProps, focusStyles } from "./primitive" @@ -16,7 +16,7 @@ import { Typography } from "./typography" const textareaStyles = tv({ extend: focusStyles, - base: "field-sizing-content max-h-96 min-h-16 w-full min-w-0 rounded-lg border border-input px-2.5 py-2 pb-6 text-base shadow-xs outline-hidden transition duration-200 disabled:opacity-50 sm:text-sm break-words whitespace-pre-wrap overflow-wrap-anywhere", + base: "field-sizing-content overflow-wrap-anywhere max-h-96 min-h-16 w-full min-w-0 whitespace-pre-wrap break-words rounded-lg border border-input px-2.5 py-2 pb-6 text-base shadow-xs outline-hidden transition duration-200 disabled:opacity-50 sm:text-sm", }) interface TextareaProps extends TextFieldPrimitiveProps { @@ -42,18 +42,18 @@ const Textarea = ({ maxLength, ...props }: TextareaProps) => { - const [currentLength, setCurrentLength] = useState(props.value?.toString().length || 0); + const [currentLength, setCurrentLength] = useState(props.value?.toString().length || 0) useEffect(() => { - setCurrentLength(props.value?.toString().length || 0); - }, [props.value]); + setCurrentLength(props.value?.toString().length || 0) + }, [props.value]) const getCountText = () => { if (maxLength) { - return `${currentLength}/${maxLength}`; + return `${currentLength}/${maxLength}` } - return `${currentLength}字`; - }; + return `${currentLength}字` + } return ( {showCharacterCount && currentLength > 0 && ( -
+
{getCountText()}
)} From 71490e45be104ad3de7718aa12243043c607caae Mon Sep 17 00:00:00 2001 From: Rabithua <1289770378@qq.com> Date: Sun, 30 Mar 2025 16:22:46 +0800 Subject: [PATCH 4/4] =?UTF-8?q?refactor:=20=E4=BC=98=E5=8C=96textarea.tsx?= =?UTF-8?q?=E5=92=8Ctypography.tsx=E4=B8=AD=E7=9A=84=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E5=92=8C=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/ui/components/ui/textarea.tsx | 71 +++++++++++++--------------- apps/ui/components/ui/typography.tsx | 8 +++- 2 files changed, 40 insertions(+), 39 deletions(-) diff --git a/apps/ui/components/ui/textarea.tsx b/apps/ui/components/ui/textarea.tsx index 0e8a0b7..e53b942 100644 --- a/apps/ui/components/ui/textarea.tsx +++ b/apps/ui/components/ui/textarea.tsx @@ -1,35 +1,34 @@ -"use client"; +"use client" -import { useEffect, useState } from "react"; +import { useEffect, useState } from "react" import { - composeRenderProps, TextArea as TextAreaPrimitive, TextField as TextFieldPrimitive, type TextFieldProps as TextFieldPrimitiveProps, type ValidationResult, -} from "react-aria-components"; -import { tv } from "tailwind-variants"; + composeRenderProps, +} from "react-aria-components" +import { tv } from "tailwind-variants" -import { Description, FieldError, Label } from "./field"; -import { composeTailwindRenderProps, focusStyles } from "./primitive"; +import { Description, FieldError, Label } from "./field" +import { composeTailwindRenderProps, focusStyles } from "./primitive" const textareaStyles = tv({ extend: focusStyles, - base: - "field-sizing-content max-h-96 min-h-16 w-full min-w-0 rounded-lg border border-input px-2.5 py-2 pb-6 text-base shadow-xs outline-hidden transition duration-200 disabled:opacity-50 sm:text-sm break-words whitespace-pre-wrap overflow-wrap-anywhere", -}); + base: "field-sizing-content overflow-wrap-anywhere max-h-96 min-h-16 w-full min-w-0 whitespace-pre-wrap break-words rounded-lg border border-input px-2.5 py-2 pb-6 text-base shadow-xs outline-hidden transition duration-200 disabled:opacity-50 sm:text-sm", +}) interface TextareaProps extends TextFieldPrimitiveProps { - autoSize?: boolean; - label?: string; - placeholder?: string; - description?: string; - errorMessage?: string | ((validation: ValidationResult) => string); - className?: string; - ref?: React.Ref; - showCharacterCount?: boolean; - maxLength?: number; - height?: string | number; + autoSize?: boolean + label?: string + placeholder?: string + description?: string + errorMessage?: string | ((validation: ValidationResult) => string) + className?: string + ref?: React.Ref + showCharacterCount?: boolean + maxLength?: number + height?: string | number } const Textarea = ({ @@ -44,29 +43,24 @@ const Textarea = ({ height, ...props }: TextareaProps) => { - const [currentLength, setCurrentLength] = useState( - props.value?.toString().length || 0, - ); + const [currentLength, setCurrentLength] = useState(props.value?.toString().length || 0) useEffect(() => { - setCurrentLength(props.value?.toString().length || 0); - }, [props.value]); + setCurrentLength(props.value?.toString().length || 0) + }, [props.value]) const getCountText = () => { if (maxLength) { - return `${currentLength}/${maxLength}`; + return `${currentLength}/${maxLength}` } - return `${currentLength}字`; - }; + return `${currentLength}字` + } return ( {label && }
@@ -77,7 +71,8 @@ const Textarea = ({ textareaStyles({ ...renderProps, className, - }))} + }), + )} style={{ height: height ?? undefined, wordWrap: "break-word", @@ -86,7 +81,7 @@ const Textarea = ({ }} /> {showCharacterCount && currentLength > 0 && ( -
+
{getCountText()}
)} @@ -94,8 +89,8 @@ const Textarea = ({ {description && {description}} {errorMessage} - ); -}; + ) +} -export type { TextareaProps }; -export { Textarea }; +export type { TextareaProps } +export { Textarea } diff --git a/apps/ui/components/ui/typography.tsx b/apps/ui/components/ui/typography.tsx index 2428d6c..52c69bd 100644 --- a/apps/ui/components/ui/typography.tsx +++ b/apps/ui/components/ui/typography.tsx @@ -27,7 +27,13 @@ type TypographyProps = { as?: "p" | "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "span" | "div" } -const Typography = ({ className, variant, as: Tag = "div", children, ...props }: TypographyProps) => { +const Typography = ({ + className, + variant, + as: Tag = "div", + children, + ...props +}: TypographyProps) => { return ( {children}