From cff9e067be6461d90b116ac558a28b02030f98a9 Mon Sep 17 00:00:00 2001 From: vamsikrishnamathala Date: Mon, 25 Aug 2025 16:40:03 +0530 Subject: [PATCH 1/5] chore: added collapsibl to propel --- .../propel/src/collapsible/collapsible.tsx | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 packages/propel/src/collapsible/collapsible.tsx diff --git a/packages/propel/src/collapsible/collapsible.tsx b/packages/propel/src/collapsible/collapsible.tsx new file mode 100644 index 00000000000..4d9c26e40ea --- /dev/null +++ b/packages/propel/src/collapsible/collapsible.tsx @@ -0,0 +1,51 @@ +import React, { useState, useEffect, useCallback } from "react"; +import { Collapsible as BaseCollapsible } from "@base-ui-components/react/collapsible"; +import { cn } from "../utils"; + +export type TCollapsibleProps = { + title: string | React.ReactNode; + children: React.ReactNode; + buttonRef?: React.RefObject; + className?: string; + buttonClassName?: string; + isOpen?: boolean; + onToggle?: () => void; + defaultOpen?: boolean; +}; + +export const Collapsible: React.FC = (props) => { + const { title, children, buttonRef, className, buttonClassName, isOpen, onToggle, defaultOpen } = props; + // state + const [localIsOpen, setLocalIsOpen] = useState(isOpen || defaultOpen ? true : false); + + useEffect(() => { + if (isOpen !== undefined) { + setLocalIsOpen(isOpen); + } + }, [isOpen]); + + // handlers + const handleOnClick = useCallback(() => { + if (isOpen !== undefined) { + if (onToggle) onToggle(); + } else { + setLocalIsOpen((prev) => !prev); + } + }, [isOpen, onToggle]); + + return ( + + + {title} + + + {children} + + + ); +}; From e2d89d3e4061550a10c910387cd3e04fc0092202 Mon Sep 17 00:00:00 2001 From: vamsikrishnamathala Date: Mon, 25 Aug 2025 17:25:54 +0530 Subject: [PATCH 2/5] fix: export path --- packages/propel/package.json | 3 ++- packages/propel/src/collapsible/collapsible.tsx | 4 ++-- packages/propel/src/collapsible/index.ts | 1 + 3 files changed, 5 insertions(+), 3 deletions(-) create mode 100644 packages/propel/src/collapsible/index.ts diff --git a/packages/propel/package.json b/packages/propel/package.json index b18bd730c65..c8bc2d77db0 100644 --- a/packages/propel/package.json +++ b/packages/propel/package.json @@ -23,7 +23,8 @@ "./combobox": "./src/combobox/index.ts", "./tooltip": "./src/tooltip/index.ts", "./styles/fonts": "./src/styles/fonts/index.css", - "./switch": "./src/switch/index.ts" + "./switch": "./src/switch/index.ts", + "./collapsible": "./src/collapsible/index.ts" }, "dependencies": { "@base-ui-components/react": "^1.0.0-beta.2", diff --git a/packages/propel/src/collapsible/collapsible.tsx b/packages/propel/src/collapsible/collapsible.tsx index 4d9c26e40ea..b1b2271ae17 100644 --- a/packages/propel/src/collapsible/collapsible.tsx +++ b/packages/propel/src/collapsible/collapsible.tsx @@ -1,6 +1,6 @@ import React, { useState, useEffect, useCallback } from "react"; import { Collapsible as BaseCollapsible } from "@base-ui-components/react/collapsible"; -import { cn } from "../utils"; +import clsx from "clsx"; export type TCollapsibleProps = { title: string | React.ReactNode; @@ -35,7 +35,7 @@ export const Collapsible: React.FC = (props) => { return ( Date: Thu, 28 Aug 2025 13:55:40 +0530 Subject: [PATCH 3/5] chore: made collapsible a compound component --- .../propel/src/collapsible/collapsible.tsx | 109 +++++++++++++----- 1 file changed, 80 insertions(+), 29 deletions(-) diff --git a/packages/propel/src/collapsible/collapsible.tsx b/packages/propel/src/collapsible/collapsible.tsx index b1b2271ae17..912514df2df 100644 --- a/packages/propel/src/collapsible/collapsible.tsx +++ b/packages/propel/src/collapsible/collapsible.tsx @@ -1,51 +1,102 @@ -import React, { useState, useEffect, useCallback } from "react"; +import React, { useState, useEffect, useCallback, createContext, useContext } from "react"; import { Collapsible as BaseCollapsible } from "@base-ui-components/react/collapsible"; import clsx from "clsx"; -export type TCollapsibleProps = { - title: string | React.ReactNode; +// Types +type CollapsibleContextType = { + isOpen: boolean; + onToggle: () => void; +}; + +type RootProps = { children: React.ReactNode; - buttonRef?: React.RefObject; className?: string; - buttonClassName?: string; isOpen?: boolean; onToggle?: () => void; defaultOpen?: boolean; }; -export const Collapsible: React.FC = (props) => { - const { title, children, buttonRef, className, buttonClassName, isOpen, onToggle, defaultOpen } = props; - // state - const [localIsOpen, setLocalIsOpen] = useState(isOpen || defaultOpen ? true : false); +type TriggerProps = { + children: React.ReactNode; + className?: string; + buttonRef?: React.RefObject; +}; + +type ContentProps = { + children: React.ReactNode; + className?: string; +}; + +// Context +const CollapsibleContext = createContext(undefined); + +// Hook +const useCollapsible = () => { + const context = useContext(CollapsibleContext); + if (!context) { + throw new Error("Collapsible compound components cannot be rendered outside the Collapsible component"); + } + return context; +}; + +// Components +const Root: React.FC = ({ children, className, isOpen: controlledIsOpen, onToggle, defaultOpen }) => { + const [localIsOpen, setLocalIsOpen] = useState(controlledIsOpen || defaultOpen || false); useEffect(() => { - if (isOpen !== undefined) { - setLocalIsOpen(isOpen); + if (controlledIsOpen !== undefined) { + setLocalIsOpen(controlledIsOpen); } - }, [isOpen]); + }, [controlledIsOpen]); - // handlers - const handleOnClick = useCallback(() => { - if (isOpen !== undefined) { - if (onToggle) onToggle(); + const handleToggle = useCallback(() => { + if (controlledIsOpen !== undefined) { + onToggle?.(); } else { setLocalIsOpen((prev) => !prev); } - }, [isOpen, onToggle]); + }, [controlledIsOpen, onToggle]); return ( - - - {title} - - + + {children} - - + + + ); +}; + +const Trigger: React.FC = ({ children, className, buttonRef }) => { + const { isOpen } = useCollapsible(); + + return ( + + {children} + ); }; + +const Content: React.FC = ({ children, className }) => { + return ( + + {children} + + ); +}; + +// Compound Component +export const Collapsible = { + CollapsibleRoot: Root, + CollapsibleTrigger: Trigger, + CollapsibleContent: Content, +}; From 1d2e06d5f4bd93dc439b02f57461fd408a525d8c Mon Sep 17 00:00:00 2001 From: vamsikrishnamathala Date: Thu, 28 Aug 2025 14:21:50 +0530 Subject: [PATCH 4/5] fix: lint and format errors --- packages/propel/package.json | 2 +- .../propel/src/collapsible/collapsible.tsx | 22 +++++++++---------- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/packages/propel/package.json b/packages/propel/package.json index 4ce826834eb..26b0683a8ec 100644 --- a/packages/propel/package.json +++ b/packages/propel/package.json @@ -26,7 +26,7 @@ "./tooltip": "./src/tooltip/index.ts", "./styles/fonts": "./src/styles/fonts/index.css", "./switch": "./src/switch/index.ts", - "./collapsible": "./src/collapsible/index.ts" + "./collapsible": "./src/collapsible/index.ts", "./card": "./src/card/index.ts" }, "dependencies": { diff --git a/packages/propel/src/collapsible/collapsible.tsx b/packages/propel/src/collapsible/collapsible.tsx index 912514df2df..ec40775156f 100644 --- a/packages/propel/src/collapsible/collapsible.tsx +++ b/packages/propel/src/collapsible/collapsible.tsx @@ -81,18 +81,16 @@ const Trigger: React.FC = ({ children, className, buttonRef }) => ); }; -const Content: React.FC = ({ children, className }) => { - return ( - - {children} - - ); -}; +const Content: React.FC = ({ children, className }) => ( + + {children} + +); // Compound Component export const Collapsible = { From ab10fa1843d99a30deb75e30a97eba7c944d1c3e Mon Sep 17 00:00:00 2001 From: vamsikrishnamathala Date: Tue, 9 Sep 2025 13:48:55 +0530 Subject: [PATCH 5/5] chore: updated propel exports order and added collapsible to tsdown config --- packages/propel/package.json | 4 ++-- packages/propel/tsdown.config.ts | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/propel/package.json b/packages/propel/package.json index 1aa55cad250..c9e1cf1c192 100644 --- a/packages/propel/package.json +++ b/packages/propel/package.json @@ -20,6 +20,7 @@ "./avatar": "./dist/avatar/index.js", "./card": "./dist/card/index.js", "./charts/*": "./dist/charts/*/index.js", + "./collapsible": "./dist/collapsible/index.js", "./combobox": "./dist/combobox/index.js", "./command": "./dist/command/index.js", "./dialog": "./dist/dialog/index.js", @@ -33,8 +34,7 @@ "./table": "./dist/table/index.js", "./tabs": "./dist/tabs/index.js", "./tooltip": "./dist/tooltip/index.js", - "./utils": "./dist/utils/index.js", - "./collapsible": "./dist/collapsible/index.js" + "./utils": "./dist/utils/index.js" }, "dependencies": { "@base-ui-components/react": "^1.0.0-beta.2", diff --git a/packages/propel/tsdown.config.ts b/packages/propel/tsdown.config.ts index 8f23c301cb8..05ac15adeb2 100644 --- a/packages/propel/tsdown.config.ts +++ b/packages/propel/tsdown.config.ts @@ -6,6 +6,7 @@ export default defineConfig({ "src/avatar/index.ts", "src/card/index.ts", "src/charts/*/index.ts", + "src/collapsible/index.ts", "src/combobox/index.ts", "src/command/index.ts", "src/dialog/index.ts",