From 2918e97c22228fe5a3379517ba56ead115725dc9 Mon Sep 17 00:00:00 2001 From: Viraj-10 Date: Wed, 31 Jan 2024 09:58:07 +0530 Subject: [PATCH 1/4] feat: story for collapsible --- .../Collapsible/Collapsible.stories.tsx | 18 +++++ .../DataDisplay/Collapsible/Collapsible.tsx | 79 +++++++++++++++++++ .../toggle/src/useToggle.web.ts | 34 ++++---- 3 files changed, 114 insertions(+), 17 deletions(-) create mode 100644 example/storybook/src/ui/components/DataDisplay/Collapsible/Collapsible.stories.tsx create mode 100644 example/storybook/src/ui/components/DataDisplay/Collapsible/Collapsible.tsx diff --git a/example/storybook/src/ui/components/DataDisplay/Collapsible/Collapsible.stories.tsx b/example/storybook/src/ui/components/DataDisplay/Collapsible/Collapsible.stories.tsx new file mode 100644 index 0000000000..8585ec0568 --- /dev/null +++ b/example/storybook/src/ui/components/DataDisplay/Collapsible/Collapsible.stories.tsx @@ -0,0 +1,18 @@ +import type { ComponentMeta } from '@storybook/react-native'; +import Collapsible from './Collapsible'; + +const CollapsibleMeta: ComponentMeta = { + title: 'stories/DATA DISPLAY/Collapsible', + component: Collapsible, + // metaInfo is required for figma generation + // @ts-ignore + metaInfo: { + componentDescription: '', + }, + argTypes: {}, + args: {}, +}; + +export default CollapsibleMeta; + +export { Collapsible }; diff --git a/example/storybook/src/ui/components/DataDisplay/Collapsible/Collapsible.tsx b/example/storybook/src/ui/components/DataDisplay/Collapsible/Collapsible.tsx new file mode 100644 index 0000000000..345c536df5 --- /dev/null +++ b/example/storybook/src/ui/components/DataDisplay/Collapsible/Collapsible.tsx @@ -0,0 +1,79 @@ +import React, { useEffect } from 'react'; + +import { Pressable, Text, View } from '@gluestack-ui/themed'; +import { useToggleState } from 'react-stately'; +import { createContext } from 'react'; +const CollapsibleContext = createContext({ + isExpanded: false, + setIsExpanded: () => {}, +}); + +const Root = (props: any) => { + const [isExpanded, setIsExpanded] = React.useState(false); + const contextValue = React.useMemo(() => { + return { + isExpanded, + setIsExpanded, + }; + }, [isExpanded, setIsExpanded]); + return ( + + + + ); +}; + +const Trigger = ({ children, defaultValue, value, ...props }: any) => { + const { isExpanded, setIsExpanded } = React.useContext(CollapsibleContext); + const state = useToggleState({ + defaultSelected: !(defaultValue === null || defaultValue === undefined) + ? defaultValue + : !(value === null || value === undefined) + ? value + : false, + }); + + return ( + { + setIsExpanded(!isExpanded); + state.toggle(); + }} + > + {children} + + ); +}; +const TriggerText = (props: any) => { + return ; +}; + +const Content = (props: any) => { + const { isExpanded } = React.useContext(CollapsibleContext); + + return <>{isExpanded || (props.forceMount && )}; +}; +const ContentText = (props: any) => { + return ; +}; +const CollapsibleBasic = ({}: any) => { + return ( + + + Collapsible + + + Content + + + ); +}; + +CollapsibleBasic.description = + 'This is a basic component example. The component lets you quickly and easily add status indicators to your interface for improved usability. They are designed to be attention-grabbing and quickly convey important information.'; + +export default CollapsibleBasic; diff --git a/packages/react-native-aria/toggle/src/useToggle.web.ts b/packages/react-native-aria/toggle/src/useToggle.web.ts index f5b44e336e..467a61cef5 100644 --- a/packages/react-native-aria/toggle/src/useToggle.web.ts +++ b/packages/react-native-aria/toggle/src/useToggle.web.ts @@ -10,12 +10,12 @@ * governing permissions and limitations under the License. */ -import { AriaToggleProps } from "@react-types/checkbox"; -import { filterDOMProps, mergeProps } from "@react-aria/utils"; -import { InputHTMLAttributes, RefObject } from "react"; -import { ToggleState } from "@react-stately/toggle"; -import { useFocusable } from "@react-aria/focus"; -import { usePress } from "@react-native-aria/interactions"; +import { AriaToggleProps } from '@react-types/checkbox'; +import { filterDOMProps, mergeProps } from '@react-aria/utils'; +import { InputHTMLAttributes, RefObject } from 'react'; +import { ToggleState } from '@react-stately/toggle'; +import { useFocusable } from '@react-aria/focus'; +import { usePress } from '@react-native-aria/interactions'; export interface ToggleAria { /** @@ -39,9 +39,9 @@ export function useToggle( value, name, children, - "aria-label": ariaLabel, - "aria-labelledby": ariaLabelledby, - validationState = "valid", + 'aria-label': ariaLabel, + 'aria-labelledby': ariaLabelledby, + validationState = 'valid', } = props; let onChange = (e: any) => { @@ -55,7 +55,7 @@ export function useToggle( let hasAriaLabel = ariaLabel != null || ariaLabelledby != null; if (!hasChildren && !hasAriaLabel) { console.warn( - "If you do not provide children, you must specify an aria-label for accessibility" + 'If you do not provide children, you must specify an aria-label for accessibility' ); } @@ -70,16 +70,16 @@ export function useToggle( return { inputProps: mergeProps(domProps, { - "aria-invalid": validationState === "invalid" || undefined, - "aria-errormessage": props["aria-errormessage"], - "aria-controls": props["aria-controls"], - "aria-readonly": isReadOnly || undefined, + 'aria-invalid': validationState === 'invalid' || undefined, + 'aria-errormessage': props['aria-errormessage'], + 'aria-controls': props['aria-controls'], + 'aria-readonly': isReadOnly || undefined, onChange, - disabled: isDisabled, - required: isRequired, + 'disabled': isDisabled, + 'required': isRequired, value, name, - type: "checkbox", + 'type': 'checkbox', ...interactions, }), }; From 76b73c116360ee1b6ac7269f4de20cbca85b4828 Mon Sep 17 00:00:00 2001 From: Viraj-10 Date: Wed, 31 Jan 2024 10:31:23 +0530 Subject: [PATCH 2/4] feat: added working story with api --- .../DataDisplay/Collapsible/Collapsible.tsx | 103 ++++++++++++++++-- 1 file changed, 92 insertions(+), 11 deletions(-) diff --git a/example/storybook/src/ui/components/DataDisplay/Collapsible/Collapsible.tsx b/example/storybook/src/ui/components/DataDisplay/Collapsible/Collapsible.tsx index 345c536df5..5e49cd58bc 100644 --- a/example/storybook/src/ui/components/DataDisplay/Collapsible/Collapsible.tsx +++ b/example/storybook/src/ui/components/DataDisplay/Collapsible/Collapsible.tsx @@ -1,8 +1,16 @@ -import React, { useEffect } from 'react'; +import React from 'react'; import { Pressable, Text, View } from '@gluestack-ui/themed'; import { useToggleState } from 'react-stately'; import { createContext } from 'react'; +import { styled } from '@gluestack-style/react'; +const StyledView = styled( + View, + {}, + { + componentName: 'StyledView', + } +); const CollapsibleContext = createContext({ isExpanded: false, setIsExpanded: () => {}, @@ -16,9 +24,15 @@ const Root = (props: any) => { setIsExpanded, }; }, [isExpanded, setIsExpanded]); + return ( - + ); }; @@ -39,12 +53,17 @@ const Trigger = ({ children, defaultValue, value, ...props }: any) => { // @ts-ignore expanded: isExpanded, }} + {...props} onPress={() => { setIsExpanded(!isExpanded); state.toggle(); }} > - {children} + {typeof children === 'function' + ? children({ + expanded: isExpanded, + }) + : children} ); }; @@ -52,23 +71,85 @@ const TriggerText = (props: any) => { return ; }; -const Content = (props: any) => { +const Content = ({ forceMount = false, ...props }: any) => { const { isExpanded } = React.useContext(CollapsibleContext); - return <>{isExpanded || (props.forceMount && )}; + if (!forceMount && isExpanded) { + return null; + } + + return ; }; + const ContentText = (props: any) => { return ; }; const CollapsibleBasic = ({}: any) => { return ( - - - Collapsible - - - Content + + + + {` + + +
{children}
+
+
+ `} +
+ + { + // @ts-ignore + ({ expanded }) => { + return ( + + {expanded ? 'Expand' : 'Collapse'} + + ); + } + } +
); }; From 2cc2475836c92f5aa87c1e4ad30b2beeb629340d Mon Sep 17 00:00:00 2001 From: Viraj-10 Date: Thu, 1 Feb 2024 10:18:39 +0530 Subject: [PATCH 3/4] feat: added package for collapsible component --- packages/unstyled/collapsible/.npmignore | 20 ++++ packages/unstyled/collapsible/README.md | 63 +++++++++++ packages/unstyled/collapsible/babel.config.js | 24 +++++ packages/unstyled/collapsible/package.json | 102 ++++++++++++++++++ .../collapsible/src/AnimatedHeight.tsx | 67 ++++++++++++ .../unstyled/collapsible/src/Collapsible.tsx | 44 ++++++++ .../collapsible/src/CollapsibleContent.tsx | 16 +++ .../src/CollapsibleContentText.tsx | 6 ++ .../collapsible/src/CollapsibleTrigger.tsx | 88 +++++++++++++++ .../src/CollapsibleTriggerText.tsx | 5 + packages/unstyled/collapsible/src/context.ts | 5 + packages/unstyled/collapsible/src/index.tsx | 51 +++++++++ packages/unstyled/collapsible/src/types.ts | 79 ++++++++++++++ packages/unstyled/collapsible/tsconfig.json | 31 ++++++ 14 files changed, 601 insertions(+) create mode 100644 packages/unstyled/collapsible/.npmignore create mode 100644 packages/unstyled/collapsible/README.md create mode 100644 packages/unstyled/collapsible/babel.config.js create mode 100644 packages/unstyled/collapsible/package.json create mode 100644 packages/unstyled/collapsible/src/AnimatedHeight.tsx create mode 100644 packages/unstyled/collapsible/src/Collapsible.tsx create mode 100644 packages/unstyled/collapsible/src/CollapsibleContent.tsx create mode 100644 packages/unstyled/collapsible/src/CollapsibleContentText.tsx create mode 100644 packages/unstyled/collapsible/src/CollapsibleTrigger.tsx create mode 100644 packages/unstyled/collapsible/src/CollapsibleTriggerText.tsx create mode 100644 packages/unstyled/collapsible/src/context.ts create mode 100644 packages/unstyled/collapsible/src/index.tsx create mode 100644 packages/unstyled/collapsible/src/types.ts create mode 100644 packages/unstyled/collapsible/tsconfig.json diff --git a/packages/unstyled/collapsible/.npmignore b/packages/unstyled/collapsible/.npmignore new file mode 100644 index 0000000000..187790b632 --- /dev/null +++ b/packages/unstyled/collapsible/.npmignore @@ -0,0 +1,20 @@ +# Dotfiles +.babelrc +.eslintignore +.eslintrc.json +.gitattributes +_config.yml +.editorconfig + + +#Config files +babel.config.js + +# Documents +CONTRIBUTING.md +ISSUE_TEMPLATE.txt +img + +# Test cases +__tests__ +dist/__tests__ diff --git a/packages/unstyled/collapsible/README.md b/packages/unstyled/collapsible/README.md new file mode 100644 index 0000000000..eca8349c0d --- /dev/null +++ b/packages/unstyled/collapsible/README.md @@ -0,0 +1,63 @@ +# `@gluestack-ui/collapsible` + +The Collapsible component is a versatile and interactive user interface element, designed to efficiently organize and present content in a compact space. + +## Installation + +To install the component, run the following command in your terminal. This will add the component to your project's dependencies and allow you to use it in your project. + +```sh +npx install @gluestack-ui/collapsible +``` + +## Usage + +```jsx +// import the styles +import { + Root, + Trigger, + Content, + Icon, + TitleText, + ContentText, +} from '../components/core/collapsible/styled-components'; + +// import the createAccordion function +import { createAccordion } from '@gluestack-ui/collapsible'; + +// Understanding the API +const Collapsible = createCollapsible({ + Root, + Item, + Header, + Trigger, + Content, + Icon, + TitleText, + ContentText, +}); + +// Using the Collapsible component +export default () => ( + + + {({ open }: { open: boolean }) => { + return ( + + {open ? 'Expand' : 'Collapse'} + + ); + }} + + + + Lorem ipsum dolor sit amet consectetur, adipisicing elit. + + + +); +``` + +More guides on how to get started are available +[here](https://ui.gluestack.io/docs/). diff --git a/packages/unstyled/collapsible/babel.config.js b/packages/unstyled/collapsible/babel.config.js new file mode 100644 index 0000000000..c13591c49e --- /dev/null +++ b/packages/unstyled/collapsible/babel.config.js @@ -0,0 +1,24 @@ +const path = require('path'); + +module.exports = function (api) { + api.cache(true); + return { + presets: ['babel-preset-expo'], + plugins: [ + process.env.NODE_ENV !== 'production' + ? [ + 'module-resolver', + { + alias: { + ['@gluestack-ui/utils']: path.resolve( + __dirname, + '../utils/src' + ), + // For development, we want to alias the library to the source + }, + }, + ] + : ['babel-plugin-react-docgen-typescript', { exclude: 'node_modules' }], + ], + }; +}; diff --git a/packages/unstyled/collapsible/package.json b/packages/unstyled/collapsible/package.json new file mode 100644 index 0000000000..d0d4992999 --- /dev/null +++ b/packages/unstyled/collapsible/package.json @@ -0,0 +1,102 @@ +{ + "name": "@gluestack-ui/collapsible", + "description": "A universal headless collapsible component for React Native, Next.js & React", + "keywords": [ + "react", + "native", + "react-native", + "collapsible", + "gluestack-ui", + "universal", + "headless", + "typescript", + "component", + "android", + "ios", + "nextjs" + ], + "version": "1.0.0", + "main": "lib/commonjs/index", + "module": "lib/module/index", + "types": "lib/typescript/index.d.ts", + "react-native": "src/index", + "source": "src/index", + "typings": "lib/typescript/index.d.ts", + "scripts": { + "prepare": "bob build", + "release": "release-it", + "build": "bob build", + "clean": "rm -rf lib", + "dev:web": "cd example/native && yarn web --clear", + "storybook": "cd example/native/storybook && yarn web" + }, + "devDependencies": { + "@types/react": "^18.0.22", + "@types/react-native": "^0.72.3", + "babel-plugin-transform-remove-console": "^6.9.4", + "react": "^18.1.0", + "react-dom": "^18.1.0", + "react-native": "^0.72.4", + "react-native-builder-bob": "^0.20.1", + "react-native-web": "^0.19.9", + "tsconfig": "7", + "typescript": "^4.9.4" + }, + "dependencies": { + "@gluestack-ui/utils": "^0.1.12", + "@react-native-aria/focus": "^0.2.9", + "@react-native-aria/interactions": "^0.2.11" + }, + "peerDependencies": { + "react": ">=16", + "react-dom": ">=16" + }, + "react-native-builder-bob": { + "source": "src", + "output": "lib", + "targets": [ + "commonjs", + [ + "module" + ], + "typescript" + ] + }, + "files": [ + "lib/", + "src/" + ], + "jest": { + "preset": "jest-expo", + "transform": { + "^.+\\.js$": "/node_modules/react-native/jest/preprocessor.js" + }, + "modulePathIgnorePatterns": [ + "/example/*", + "/lib/" + ], + "transformIgnorePatterns": [ + "node_modules/(?!(@react-native|react-native|expo-asset|expo-constants|@unimodules|react-native-unimodules|expo-font|react-native-svg|@expo/vector-icons|react-native-vector-icons|@react-native-aria/checkbox|@react-native-aria/interactions|@react-native-aria/button|@react-native-aria/switch|@react-native-aria/toggle|@react-native-aria/utils|@react-native-aria/*))" + ], + "setupFiles": [ + "/src/jest/mock.ts" + ] + }, + "release-it": { + "git": { + "commitMessage": "chore: release ${version}", + "tagName": "v${version}" + }, + "npm": { + "publish": true + }, + "github": { + "release": true + }, + "plugins": { + "@release-it/conventional-changelog": { + "preset": "angular" + } + } + } +} diff --git a/packages/unstyled/collapsible/src/AnimatedHeight.tsx b/packages/unstyled/collapsible/src/AnimatedHeight.tsx new file mode 100644 index 0000000000..91ba0e32a5 --- /dev/null +++ b/packages/unstyled/collapsible/src/AnimatedHeight.tsx @@ -0,0 +1,67 @@ +import React, { useRef, useEffect, useState } from 'react'; +import { Animated, StyleSheet } from 'react-native'; + +const AnimatedHeight = ({ hide, extraHeight = 0, children }: any) => { + const [measuredHeight, setMeasuredHeight] = useState(0); + const opacityValue = useRef(new Animated.Value(hide ? 0 : 1)).current; + const heightValue = useRef( + new Animated.Value(hide ? 0 : measuredHeight + extraHeight) + ).current; + + useEffect(() => { + Animated.timing(opacityValue, { + toValue: hide ? 0 : 1, + duration: 200, // Set your transition duration here + useNativeDriver: false, + }).start(); + + Animated.timing(heightValue, { + toValue: hide ? 0 : 1, + duration: 200, + useNativeDriver: false, + }).start(); + }, [hide, measuredHeight, extraHeight, heightValue, opacityValue]); + + const animatedHeight = heightValue.interpolate({ + inputRange: [0, 1], + outputRange: [0, measuredHeight + extraHeight], + }); + + return ( + + { + const height = e.nativeEvent.layout.height; + setMeasuredHeight(height); + }} + > + {children} + + + ); +}; + +export default AnimatedHeight; + +const styles = StyleSheet.create({ + autoBottom: { + bottom: 'auto', + }, + hidden: { + overflow: 'hidden', + }, +}); diff --git a/packages/unstyled/collapsible/src/Collapsible.tsx b/packages/unstyled/collapsible/src/Collapsible.tsx new file mode 100644 index 0000000000..50977e1897 --- /dev/null +++ b/packages/unstyled/collapsible/src/Collapsible.tsx @@ -0,0 +1,44 @@ +import React, { forwardRef, useMemo } from 'react'; +import type { ICollapsibleProps } from './types'; +import { CollapsibleContext } from './context'; + +import { useControlledState } from '@react-stately/utils'; + +export const Collapsible = (StyledCollapsible: any) => + forwardRef( + ( + { + isOpen: isOpenProp, + isDisabled, + defaultIsOpen = false, + onOpenChange, + children, + ...props + }: T & ICollapsibleProps, + ref?: any + ) => { + const [isOpen, setIsOpen] = useControlledState( + isOpenProp, + defaultIsOpen, + (incomingValue: any) => { + onOpenChange && onOpenChange(incomingValue); + } + ); + + const contextValue = useMemo(() => { + return { + isOpen, + setIsOpen, + isDisabled, + }; + }, [isOpen, setIsOpen, isDisabled]); + + return ( + + + {children} + + + ); + } + ); diff --git a/packages/unstyled/collapsible/src/CollapsibleContent.tsx b/packages/unstyled/collapsible/src/CollapsibleContent.tsx new file mode 100644 index 0000000000..c12aac0c1f --- /dev/null +++ b/packages/unstyled/collapsible/src/CollapsibleContent.tsx @@ -0,0 +1,16 @@ +import React, { forwardRef } from 'react'; +import { CollapsibleContext } from './context'; +import AnimatedHeight from './AnimatedHeight'; + +export const CollapsibleContent = (StyledCollapsibleContent: any) => + forwardRef(({ children, forceMount, ...props }: any, ref?: any) => { + const { isOpen } = React.useContext(CollapsibleContext); + + return ( + + + {children} + + + ); + }); diff --git a/packages/unstyled/collapsible/src/CollapsibleContentText.tsx b/packages/unstyled/collapsible/src/CollapsibleContentText.tsx new file mode 100644 index 0000000000..cdecafd657 --- /dev/null +++ b/packages/unstyled/collapsible/src/CollapsibleContentText.tsx @@ -0,0 +1,6 @@ +import React, { forwardRef } from 'react'; + +export const CollapsibleContentText = (StyledCollapsibleContentText: any) => + forwardRef((props: any, ref?: any) => { + return ; + }); diff --git a/packages/unstyled/collapsible/src/CollapsibleTrigger.tsx b/packages/unstyled/collapsible/src/CollapsibleTrigger.tsx new file mode 100644 index 0000000000..d48963dcce --- /dev/null +++ b/packages/unstyled/collapsible/src/CollapsibleTrigger.tsx @@ -0,0 +1,88 @@ +import React, { forwardRef, useContext } from 'react'; +import { useHover, usePress } from '@react-native-aria/interactions'; +import { useFocusRing, useFocus } from '@react-native-aria/focus'; +import { composeEventHandlers } from '@gluestack-ui/utils'; +import { CollapsibleContext } from './context'; + +export const CollapsibleTrigger = (StyledCollapsibleTrigger: any) => + forwardRef( + ( + { + children, + isHovered: isHoveredProp, + isFocused: isFocusedProp, + isPressed: isPressedProp, + isFocusVisible: isFocusVisibleProp, + ...props + }: any, + ref?: any + ) => { + const { isDisabled, isOpen, setIsOpen } = useContext(CollapsibleContext); + + const { pressProps, isPressed } = usePress({ + isDisabled: isDisabled || props.disabled, + }); + + const { isHovered, hoverProps }: any = useHover(); + + const { isFocusVisible, focusProps: focusRingProps }: any = + useFocusRing(); + + const { isFocused, focusProps } = useFocus(); + + return ( + { + setIsOpen(!isOpen); + }, props.onPress)} + onPressIn={composeEventHandlers( + props?.onPressIn, + pressProps.onPressIn + )} + onPressOut={composeEventHandlers( + props?.onPressOut, + pressProps.onPressOut + )} + // @ts-ignore - web only + onHoverIn={composeEventHandlers( + props?.onHoverIn, + hoverProps.onHoverIn + )} + // @ts-ignore - web only + onHoverOut={composeEventHandlers( + props?.onHoverOut, + hoverProps.onHoverOut + )} + onFocus={composeEventHandlers( + composeEventHandlers(props?.onFocus, focusProps.onFocus), + focusRingProps.onFocus + )} + onBlur={composeEventHandlers( + composeEventHandlers(props?.onBlur, focusProps.onBlur), + focusRingProps.onBlur + )} + > + {typeof children === 'function' + ? children({ + hovered: isHovered, + focused: isFocused, + pressed: isPressed, + disabled: isDisabled || props.disabled, + focusVisible: isFocusVisible, + open: isOpen, + }) + : children} + + ); + } + ); diff --git a/packages/unstyled/collapsible/src/CollapsibleTriggerText.tsx b/packages/unstyled/collapsible/src/CollapsibleTriggerText.tsx new file mode 100644 index 0000000000..3c96cc8ad7 --- /dev/null +++ b/packages/unstyled/collapsible/src/CollapsibleTriggerText.tsx @@ -0,0 +1,5 @@ +import React, { forwardRef } from 'react'; +export const CollapsibleTriggerText = (StyledCollapsibleTriggerText: any) => + forwardRef((props, ref?: any) => { + return ; + }); diff --git a/packages/unstyled/collapsible/src/context.ts b/packages/unstyled/collapsible/src/context.ts new file mode 100644 index 0000000000..a41a317ff6 --- /dev/null +++ b/packages/unstyled/collapsible/src/context.ts @@ -0,0 +1,5 @@ +import { createContext, useContext } from 'react'; +export const CollapsibleContext = createContext({}); +export const useCollapsibleContext = () => { + return useContext(CollapsibleContext); +}; diff --git a/packages/unstyled/collapsible/src/index.tsx b/packages/unstyled/collapsible/src/index.tsx new file mode 100644 index 0000000000..f261d1ac6b --- /dev/null +++ b/packages/unstyled/collapsible/src/index.tsx @@ -0,0 +1,51 @@ +import type React from 'react'; +import { Collapsible as CollapsibleMain } from './Collapsible'; +import { CollapsibleTriggerText } from './CollapsibleTriggerText'; +import { CollapsibleContentText } from './CollapsibleContentText'; +import { CollapsibleTrigger } from './CollapsibleTrigger'; +import { CollapsibleContent } from './CollapsibleContent'; +import { ICollapsibleComponentType } from './types'; + +export function createCollapsible< + CollapsibleProps, + HeaderProps, + TriggerProps, + ContentProps, + IconProps, + TriggerTextProps, + ContentTextProps +>({ + Root, + Trigger, + Content, + TriggerText, + ContentText, +}: { + Root: React.ComponentType; + Header: React.ComponentType; + Trigger: React.ComponentType; + Content: React.ComponentType; + Icon: React.ComponentType; + TriggerText: React.ComponentType; + ContentText: React.ComponentType; +}) { + const Collapsible = CollapsibleMain(Root) as any; + Collapsible.Trigger = CollapsibleTrigger(Trigger); + Collapsible.TriggerText = CollapsibleTriggerText(TriggerText); + Collapsible.Content = CollapsibleContent(Content); + Collapsible.ContentText = CollapsibleContentText(ContentText); + + Collapsible.displayName = 'Collapsible'; + Collapsible.Trigger.displayName = 'Collapsible.Trigger'; + Collapsible.TriggerText.displayName = 'Collapsible.TriggerText'; + Collapsible.Content.displayName = 'Collapsible.Content'; + Collapsible.ContentText.displayName = 'Collapsible.ContentText'; + + return Collapsible as ICollapsibleComponentType< + CollapsibleProps, + TriggerProps, + TriggerTextProps, + ContentProps, + ContentTextProps + >; +} diff --git a/packages/unstyled/collapsible/src/types.ts b/packages/unstyled/collapsible/src/types.ts new file mode 100644 index 0000000000..415895379d --- /dev/null +++ b/packages/unstyled/collapsible/src/types.ts @@ -0,0 +1,79 @@ +import type { ViewProps } from 'react-native'; + +export interface ICollapsibleProps extends ViewProps { + /** + * When type is "single" or "multiple", allows closing content when clicking trigger for an open item. + */ + + isOpen?: boolean; + + /** + * The value of the item to expand when initially rendered when type is "single" or "multiple". + */ + + defaultIsOpen?: boolean; + + /** + * When true, prevents the user from interacting with the Collapsible and all its items. + */ + + isDisabled?: boolean; + + /** + * Event handler called when the expanded state of an item changes and type is "single" or "multiple". + */ + + onOpenChange?: (value: string[]) => void; +} + +export interface ICollapsibleContentProps { + /** + * If true, the content will be visible. + * @default false + */ + forceMount: boolean; +} + +export interface ICollapsibleTriggerProps { + /** + * If true, the button will be in pressed state. + */ + isPressed?: boolean; + + /** + * If true, the button will be in disabled state. + */ + isDisabled?: boolean; + + /** + * If true, the button will be in hovered state. + */ + isHovered?: boolean; + + /** + * If true, the button will be focused. + */ + isFocused?: boolean; + + /** + * If true, the button focus ring will be visible. + */ + isFocusVisible?: boolean; + + children: JSX.Element | Array | ((props: any) => JSX.Element); +} + +export type ICollapsibleComponentType< + CollapsibleProps, + TriggerProps, + TriggerTextProps, + ContentProps, + ContentTextProps +> = React.ForwardRefExoticComponent & { + Trigger: React.ForwardRefExoticComponent< + Omit & ICollapsibleTriggerProps + >; + TriggerText: React.ForwardRefExoticComponent; + Content: React.ForwardRefExoticComponent; + ContentText: React.ForwardRefExoticComponent; +}; diff --git a/packages/unstyled/collapsible/tsconfig.json b/packages/unstyled/collapsible/tsconfig.json new file mode 100644 index 0000000000..f1af89222c --- /dev/null +++ b/packages/unstyled/collapsible/tsconfig.json @@ -0,0 +1,31 @@ +{ + "include": ["src"], + "exclude": ["node_modules", "example"], + "paths": { + "@gluestack-ui/utils": ["../utils/src"] + }, + "compilerOptions": { + "emitDeclarationOnly": true, + "noEmit": false, + "baseUrl": "", + "declaration": true, + "allowUnreachableCode": false, + "allowUnusedLabels": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "jsx": "react", + "lib": ["esnext", "dom"], + "module": "esnext", + "moduleResolution": "node", + "noFallthroughCasesInSwitch": true, + "noImplicitReturns": true, + "noImplicitUseStrict": false, + "noStrictGenericChecks": false, + "noUnusedLocals": false, + "noUnusedParameters": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "strict": true, + "target": "esnext" + } +} From cdbad660a507966f4d1cc0b285dff44223929d57 Mon Sep 17 00:00:00 2001 From: Viraj-10 Date: Thu, 1 Feb 2024 10:39:14 +0530 Subject: [PATCH 4/4] feat: added package in theme --- packages/themed/package.json | 1 + .../src/components/Collapsible/config.json | 4 ++++ .../src/components/Collapsible/index.tsx | 24 +++++++++++++++++++ .../Collapsible/styled-components/Content.tsx | 6 +++++ .../styled-components/ContentText.tsx | 7 ++++++ .../Collapsible/styled-components/Root.tsx | 7 ++++++ .../Collapsible/styled-components/Trigger.tsx | 8 +++++++ .../styled-components/TriggerText.tsx | 7 ++++++ .../Collapsible/styled-components/index.tsx | 5 ++++ packages/themed/src/components/index.tsx | 1 + 10 files changed, 70 insertions(+) create mode 100644 packages/themed/src/components/Collapsible/config.json create mode 100644 packages/themed/src/components/Collapsible/index.tsx create mode 100644 packages/themed/src/components/Collapsible/styled-components/Content.tsx create mode 100644 packages/themed/src/components/Collapsible/styled-components/ContentText.tsx create mode 100644 packages/themed/src/components/Collapsible/styled-components/Root.tsx create mode 100644 packages/themed/src/components/Collapsible/styled-components/Trigger.tsx create mode 100644 packages/themed/src/components/Collapsible/styled-components/TriggerText.tsx create mode 100644 packages/themed/src/components/Collapsible/styled-components/index.tsx diff --git a/packages/themed/package.json b/packages/themed/package.json index 5d20012ff8..b2f927b65f 100644 --- a/packages/themed/package.json +++ b/packages/themed/package.json @@ -39,6 +39,7 @@ "@gluestack-ui/avatar": "0.1.15", "@gluestack-ui/button": "1.0.0", "@gluestack-ui/checkbox": "0.1.23", + "@gluestack-ui/collapsible": "0.0.1", "@gluestack-ui/divider": "0.1.8", "@gluestack-ui/fab": "0.1.17", "@gluestack-ui/form-control": "0.1.15", diff --git a/packages/themed/src/components/Collapsible/config.json b/packages/themed/src/components/Collapsible/config.json new file mode 100644 index 0000000000..864a23842c --- /dev/null +++ b/packages/themed/src/components/Collapsible/config.json @@ -0,0 +1,4 @@ +{ + "dependencies": { "@gluestack-ui/collapsible": "latest" }, + "keywords": ["components", "core"] +} diff --git a/packages/themed/src/components/Collapsible/index.tsx b/packages/themed/src/components/Collapsible/index.tsx new file mode 100644 index 0000000000..358fa776ae --- /dev/null +++ b/packages/themed/src/components/Collapsible/index.tsx @@ -0,0 +1,24 @@ +import { createCollapsible } from '@gluestack-ui/collapsible'; +import { + Root, + Trigger, + TriggerText, + ContentText, + Content, +} from './styled-components'; + +export const Collapsible = createCollapsible({ + Root, + Trigger, + TriggerText, + ContentText, + Content, +}); + +export const CollapsibleItem = Collapsible.Item; +export const CollapsibleHeader = Collapsible.Header; +export const CollapsibleTrigger = Collapsible.Trigger; +export const CollapsibleTitleText = Collapsible.TitleText; +export const CollapsibleContentText = Collapsible.ContentText; +export const CollapsibleIcon = Collapsible.Icon; +export const CollapsibleContent = Collapsible.Content; diff --git a/packages/themed/src/components/Collapsible/styled-components/Content.tsx b/packages/themed/src/components/Collapsible/styled-components/Content.tsx new file mode 100644 index 0000000000..d861cc0ba1 --- /dev/null +++ b/packages/themed/src/components/Collapsible/styled-components/Content.tsx @@ -0,0 +1,6 @@ +import { styled } from '@gluestack-style/react'; +import { View } from 'react-native'; + +export default styled(View, {}, { + componentName: 'CollapsibleContent', +} as const); diff --git a/packages/themed/src/components/Collapsible/styled-components/ContentText.tsx b/packages/themed/src/components/Collapsible/styled-components/ContentText.tsx new file mode 100644 index 0000000000..b000e79721 --- /dev/null +++ b/packages/themed/src/components/Collapsible/styled-components/ContentText.tsx @@ -0,0 +1,7 @@ +import { styled } from '@gluestack-style/react'; +import { Text } from 'react-native'; + +export default styled(Text, {}, { + componentName: 'CollapsibleContentText', + ancestorStyle: ['_contentText'], +} as const); diff --git a/packages/themed/src/components/Collapsible/styled-components/Root.tsx b/packages/themed/src/components/Collapsible/styled-components/Root.tsx new file mode 100644 index 0000000000..8759783503 --- /dev/null +++ b/packages/themed/src/components/Collapsible/styled-components/Root.tsx @@ -0,0 +1,7 @@ +import { styled } from '@gluestack-style/react'; +import { View } from 'react-native'; + +export default styled(View, {}, { + componentName: 'Collapsible', + descendantStyle: ['_trigger', '_triggerText', '_content', '_contentText'], +} as const); diff --git a/packages/themed/src/components/Collapsible/styled-components/Trigger.tsx b/packages/themed/src/components/Collapsible/styled-components/Trigger.tsx new file mode 100644 index 0000000000..7045611cf0 --- /dev/null +++ b/packages/themed/src/components/Collapsible/styled-components/Trigger.tsx @@ -0,0 +1,8 @@ +import { styled } from '@gluestack-style/react'; +import { Pressable } from 'react-native'; + +export default styled(Pressable, {}, { + componentName: 'AccordionTrigger', + descendantStyle: ['_triggerText'], + ancestorStyle: ['_trigger'], +} as const); diff --git a/packages/themed/src/components/Collapsible/styled-components/TriggerText.tsx b/packages/themed/src/components/Collapsible/styled-components/TriggerText.tsx new file mode 100644 index 0000000000..26e5e7b6a1 --- /dev/null +++ b/packages/themed/src/components/Collapsible/styled-components/TriggerText.tsx @@ -0,0 +1,7 @@ +import { styled } from '@gluestack-style/react'; +import { Text } from 'react-native'; + +export default styled(Text, {}, { + componentName: 'CollapsibleTriggerText', + ancestorStyle: ['_triggerText'], +} as const); diff --git a/packages/themed/src/components/Collapsible/styled-components/index.tsx b/packages/themed/src/components/Collapsible/styled-components/index.tsx new file mode 100644 index 0000000000..d76851713b --- /dev/null +++ b/packages/themed/src/components/Collapsible/styled-components/index.tsx @@ -0,0 +1,5 @@ +export { default as Root } from './Root'; +export { default as Trigger } from './Trigger'; +export { default as TriggerText } from './TriggerText'; +export { default as Content } from './Content'; +export { default as ContentText } from './ContentText'; diff --git a/packages/themed/src/components/index.tsx b/packages/themed/src/components/index.tsx index 3b41bd86b9..f82fcf95bb 100644 --- a/packages/themed/src/components/index.tsx +++ b/packages/themed/src/components/index.tsx @@ -10,6 +10,7 @@ export * from './Box'; export * from './FlatList'; export * from './Center'; export * from './Checkbox'; +export * from './Collapsible'; export * from './HStack'; export * from './Pressable'; export * from './Icons';