mirror of
https://github.com/coaidev/coai.git
synced 2025-05-19 21:10:18 +09:00
fix: fix paragraph item stacked height overflow issue (#99)
This commit is contained in:
parent
6f5dae5ca9
commit
82d0f3c4b1
@ -14,6 +14,7 @@
|
||||
"dependencies": {
|
||||
"@headlessui/react": "^1.7.18",
|
||||
"@headlessui/tailwindcss": "^0.2.0",
|
||||
"@radix-ui/react-accordion": "^1.1.2",
|
||||
"@radix-ui/react-alert-dialog": "^1.0.4",
|
||||
"@radix-ui/react-checkbox": "^1.0.4",
|
||||
"@radix-ui/react-context-menu": "^2.1.4",
|
||||
|
60
app/pnpm-lock.yaml
generated
60
app/pnpm-lock.yaml
generated
@ -11,6 +11,9 @@ dependencies:
|
||||
'@headlessui/tailwindcss':
|
||||
specifier: ^0.2.0
|
||||
version: 0.2.0(tailwindcss@3.3.5)
|
||||
'@radix-ui/react-accordion':
|
||||
specifier: ^1.1.2
|
||||
version: 1.1.2(@types/react-dom@18.2.14)(@types/react@18.2.33)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-alert-dialog':
|
||||
specifier: ^1.0.4
|
||||
version: 1.0.5(@types/react-dom@18.2.14)(@types/react@18.2.33)(react-dom@18.2.0)(react@18.2.0)
|
||||
@ -746,6 +749,35 @@ packages:
|
||||
'@babel/runtime': 7.23.2
|
||||
dev: false
|
||||
|
||||
/@radix-ui/react-accordion@1.1.2(@types/react-dom@18.2.14)(@types/react@18.2.33)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-fDG7jcoNKVjSK6yfmuAs0EnPDro0WMXIhMtXdTBWqEioVW206ku+4Lw07e+13lUkFkpoEQ2PdeMIAGpdqEAmDg==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
'@types/react-dom': '*'
|
||||
react: ^16.8 || ^17.0 || ^18.0
|
||||
react-dom: ^16.8 || ^17.0 || ^18.0
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
'@types/react-dom':
|
||||
optional: true
|
||||
dependencies:
|
||||
'@babel/runtime': 7.23.2
|
||||
'@radix-ui/primitive': 1.0.1
|
||||
'@radix-ui/react-collapsible': 1.0.3(@types/react-dom@18.2.14)(@types/react@18.2.33)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-collection': 1.0.3(@types/react-dom@18.2.14)(@types/react@18.2.33)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.33)(react@18.2.0)
|
||||
'@radix-ui/react-context': 1.0.1(@types/react@18.2.33)(react@18.2.0)
|
||||
'@radix-ui/react-direction': 1.0.1(@types/react@18.2.33)(react@18.2.0)
|
||||
'@radix-ui/react-id': 1.0.1(@types/react@18.2.33)(react@18.2.0)
|
||||
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.14)(@types/react@18.2.33)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.33)(react@18.2.0)
|
||||
'@types/react': 18.2.33
|
||||
'@types/react-dom': 18.2.14
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
/@radix-ui/react-alert-dialog@1.0.5(@types/react-dom@18.2.14)(@types/react@18.2.33)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-OrVIOcZL0tl6xibeuGt5/+UxoT2N27KCFOPjFyfXMnchxSHZ/OW7cCX2nGlIYJrbHK/fczPcFzAwvNBB6XBNMA==}
|
||||
peerDependencies:
|
||||
@ -821,6 +853,34 @@ packages:
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
/@radix-ui/react-collapsible@1.0.3(@types/react-dom@18.2.14)(@types/react@18.2.33)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-UBmVDkmR6IvDsloHVN+3rtx4Mi5TFvylYXpluuv0f37dtaz3H99bp8No0LGXRigVpl3UAT4l9j6bIchh42S/Gg==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
'@types/react-dom': '*'
|
||||
react: ^16.8 || ^17.0 || ^18.0
|
||||
react-dom: ^16.8 || ^17.0 || ^18.0
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
'@types/react-dom':
|
||||
optional: true
|
||||
dependencies:
|
||||
'@babel/runtime': 7.23.2
|
||||
'@radix-ui/primitive': 1.0.1
|
||||
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.33)(react@18.2.0)
|
||||
'@radix-ui/react-context': 1.0.1(@types/react@18.2.33)(react@18.2.0)
|
||||
'@radix-ui/react-id': 1.0.1(@types/react@18.2.33)(react@18.2.0)
|
||||
'@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.14)(@types/react@18.2.33)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.14)(@types/react@18.2.33)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.33)(react@18.2.0)
|
||||
'@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.33)(react@18.2.0)
|
||||
'@types/react': 18.2.33
|
||||
'@types/react-dom': 18.2.14
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
/@radix-ui/react-collection@1.0.3(@types/react-dom@18.2.14)(@types/react@18.2.33)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-3SzW+0PW7yBBoQlT8wNcGtaxaD0XSu0uLUFgrtHY08Acx05TaHaOmVLR73c0j/cqpDy53KBMO7s0dx2wmOIDIA==}
|
||||
peerDependencies:
|
||||
|
@ -152,7 +152,7 @@ input[type="number"] {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 0.5rem 0;
|
||||
padding: 1.5rem;
|
||||
padding: 1rem 1.5rem 0.25rem;
|
||||
border-radius: var(--radius);
|
||||
background: hsl(var(--background));
|
||||
color: hsl(var(--text));
|
||||
@ -191,7 +191,6 @@ input[type="number"] {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
margin-bottom: 1.5rem;
|
||||
align-items: center;
|
||||
transform: translateY(-0.25rem);
|
||||
}
|
||||
@ -205,6 +204,7 @@ input[type="number"] {
|
||||
line-height: 1.1rem;
|
||||
color: hsl(var(--text-secondary));
|
||||
transition: .25s;
|
||||
text-decoration: none !important;
|
||||
|
||||
&:before {
|
||||
content: '';
|
||||
@ -273,6 +273,10 @@ input[type="number"] {
|
||||
height: 0.25rem;
|
||||
}
|
||||
|
||||
.paragraph-content {
|
||||
transition: 1.5s ease-in-out;
|
||||
}
|
||||
|
||||
.paragraph-footer {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
@ -1,8 +1,13 @@
|
||||
import React from "react";
|
||||
import { ChevronDown, Info } from "lucide-react";
|
||||
import { Info } from "lucide-react";
|
||||
import { cn } from "@/components/ui/lib/utils.ts";
|
||||
import { Button } from "@/components/ui/button.tsx";
|
||||
import Markdown from "@/components/Markdown.tsx";
|
||||
import {
|
||||
Accordion,
|
||||
AccordionContent,
|
||||
AccordionItem,
|
||||
AccordionTrigger,
|
||||
} from "@/components/ui/accordion.tsx";
|
||||
|
||||
export type ParagraphProps = {
|
||||
title?: string;
|
||||
@ -10,8 +15,6 @@ export type ParagraphProps = {
|
||||
className?: string;
|
||||
configParagraph?: boolean;
|
||||
isCollapsed?: boolean;
|
||||
onCollapse?: () => void;
|
||||
defaultCollapsed?: boolean;
|
||||
};
|
||||
|
||||
function Paragraph({
|
||||
@ -20,51 +23,25 @@ function Paragraph({
|
||||
className,
|
||||
configParagraph,
|
||||
isCollapsed,
|
||||
onCollapse,
|
||||
defaultCollapsed,
|
||||
}: ParagraphProps) {
|
||||
const [collapsed, setCollapsed] = React.useState(defaultCollapsed ?? false);
|
||||
|
||||
React.useEffect(() => onCollapse && onCollapse(), [collapsed]);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
`paragraph`,
|
||||
configParagraph && `config-paragraph`,
|
||||
isCollapsed && `collapsable`,
|
||||
collapsed && `collapsed`,
|
||||
className,
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className={`paragraph-header`}
|
||||
onClick={() => setCollapsed(!collapsed)}
|
||||
>
|
||||
{title && <div className={`paragraph-title`}>{title}</div>}
|
||||
<div className={`grow`} />
|
||||
{isCollapsed && (
|
||||
<Button size={`icon`} variant={`ghost`} className={`w-8 h-8`}>
|
||||
<ChevronDown
|
||||
className={cn(
|
||||
`w-4 h-4 transition-transform duration-300`,
|
||||
collapsed && `transform rotate-180`,
|
||||
)}
|
||||
/>
|
||||
</Button>
|
||||
<Accordion type={`single`} collapsible={isCollapsed} defaultValue={"item"}>
|
||||
<AccordionItem
|
||||
value={`item`}
|
||||
className={cn(
|
||||
`paragraph`,
|
||||
configParagraph && `config-paragraph`,
|
||||
className,
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
className={`paragraph-content`}
|
||||
style={
|
||||
{
|
||||
"--max-height": collapsed ? "0px" : "1000px",
|
||||
} as React.CSSProperties
|
||||
}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
<AccordionTrigger className={`paragraph-header`}>
|
||||
<div className={`paragraph-title`}>{title ?? ""}</div>
|
||||
</AccordionTrigger>
|
||||
<AccordionContent className={`paragraph-content mt-2`}>
|
||||
{children}
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
);
|
||||
}
|
||||
|
||||
@ -88,6 +65,7 @@ type ParagraphDescriptionProps = {
|
||||
children: string;
|
||||
border?: boolean;
|
||||
};
|
||||
|
||||
export function ParagraphDescription({
|
||||
children,
|
||||
border,
|
||||
|
56
app/src/components/ui/accordion.tsx
Normal file
56
app/src/components/ui/accordion.tsx
Normal file
@ -0,0 +1,56 @@
|
||||
import * as React from "react";
|
||||
import * as AccordionPrimitive from "@radix-ui/react-accordion";
|
||||
import { ChevronDown } from "lucide-react";
|
||||
|
||||
import { cn } from "@/components/ui/lib/utils";
|
||||
|
||||
const Accordion = AccordionPrimitive.Root;
|
||||
|
||||
const AccordionItem = React.forwardRef<
|
||||
React.ElementRef<typeof AccordionPrimitive.Item>,
|
||||
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<AccordionPrimitive.Item
|
||||
ref={ref}
|
||||
className={cn("border-b", className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
AccordionItem.displayName = "AccordionItem";
|
||||
|
||||
const AccordionTrigger = React.forwardRef<
|
||||
React.ElementRef<typeof AccordionPrimitive.Trigger>,
|
||||
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<AccordionPrimitive.Header className="flex">
|
||||
<AccordionPrimitive.Trigger
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"flex flex-1 items-center justify-between py-4 font-medium transition-all [&[data-state=open]>svg]:rotate-180",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<ChevronDown className="h-4 w-4 shrink-0 transition-transform duration-200" />
|
||||
</AccordionPrimitive.Trigger>
|
||||
</AccordionPrimitive.Header>
|
||||
));
|
||||
AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName;
|
||||
|
||||
const AccordionContent = React.forwardRef<
|
||||
React.ElementRef<typeof AccordionPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<AccordionPrimitive.Content
|
||||
ref={ref}
|
||||
className="overflow-hidden text-sm transition-all data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down"
|
||||
{...props}
|
||||
>
|
||||
<div className={cn("pb-4 pt-0", className)}>{children}</div>
|
||||
</AccordionPrimitive.Content>
|
||||
));
|
||||
|
||||
AccordionContent.displayName = AccordionPrimitive.Content.displayName;
|
||||
|
||||
export { Accordion, AccordionItem, AccordionTrigger, AccordionContent };
|
@ -31,14 +31,21 @@ export interface FlexibleTextareaProps extends TextareaProps {
|
||||
const FlexibleTextarea = React.forwardRef<
|
||||
HTMLTextAreaElement,
|
||||
FlexibleTextareaProps
|
||||
>(({ rows = 1, ...props }, ref) => {
|
||||
>(({ rows = 1, className, ...props }, ref) => {
|
||||
const lines = useMemo(() => {
|
||||
const value = props.value?.toString() || "";
|
||||
const count = value.split("\n").length + 1;
|
||||
return Math.max(rows, count);
|
||||
}, [props.value]);
|
||||
|
||||
return <Textarea ref={ref} rows={lines} {...props} />;
|
||||
return (
|
||||
<Textarea
|
||||
className={cn("resize-none no-scrollbar", className)}
|
||||
ref={ref}
|
||||
rows={lines}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
FlexibleTextarea.displayName = "FlexibleTextarea";
|
||||
|
@ -45,7 +45,7 @@ import {
|
||||
import { DialogTitle } from "@radix-ui/react-dialog";
|
||||
import Require from "@/components/Require.tsx";
|
||||
import { Loader2, Settings2 } from "lucide-react";
|
||||
import { Textarea } from "@/components/ui/textarea.tsx";
|
||||
import { FlexibleTextarea } from "@/components/ui/textarea.tsx";
|
||||
import Tips from "@/components/Tips.tsx";
|
||||
import { cn } from "@/components/ui/lib/utils.ts";
|
||||
import { Switch } from "@/components/ui/switch.tsx";
|
||||
@ -527,7 +527,7 @@ function Site({ data, dispatch, onChange }: CompProps<SiteState>) {
|
||||
</ParagraphItem>
|
||||
<ParagraphItem rowLayout={true}>
|
||||
<Label>{t("admin.system.announcement")}</Label>
|
||||
<Textarea
|
||||
<FlexibleTextarea
|
||||
value={data.announcement}
|
||||
rows={12}
|
||||
onChange={(e) =>
|
||||
@ -541,7 +541,7 @@ function Site({ data, dispatch, onChange }: CompProps<SiteState>) {
|
||||
</ParagraphItem>
|
||||
<ParagraphItem rowLayout={true}>
|
||||
<Label>{t("admin.system.contact")}</Label>
|
||||
<Textarea
|
||||
<FlexibleTextarea
|
||||
value={data.contact}
|
||||
rows={6}
|
||||
onChange={(e) =>
|
||||
@ -556,9 +556,9 @@ function Site({ data, dispatch, onChange }: CompProps<SiteState>) {
|
||||
<ParagraphSpace />
|
||||
<ParagraphItem rowLayout={true}>
|
||||
<Label>{t("admin.system.footer")}</Label>
|
||||
<Textarea
|
||||
value={data.footer}
|
||||
<FlexibleTextarea
|
||||
rows={6}
|
||||
value={data.footer}
|
||||
onChange={(e) =>
|
||||
dispatch({
|
||||
type: "update:site.footer",
|
||||
|
Loading…
Reference in New Issue
Block a user