From e3a3de42970e8bb6c80e1cdda98e642909e8678c Mon Sep 17 00:00:00 2001 From: Pranay Kothapalli Date: Tue, 18 Jun 2024 20:00:15 +0530 Subject: [PATCH 1/6] refactor: types --- .../ui/Tabs/context/TabsContext.tsx | 5 ++++ .../ui/Tabs/segments/TabContent.tsx | 6 ++--- src/components/ui/Tabs/segments/TabList.tsx | 4 ++-- src/components/ui/Tabs/segments/TabRoot.tsx | 24 +++++++++---------- .../ui/Tabs/segments/TabTrigger.tsx | 10 ++++---- src/components/ui/Tabs/types.ts | 5 ---- src/components/ui/Tabs/types/TabProps.tsx | 9 +++++++ src/components/ui/Tabs/types/TabRootProps.tsx | 11 +++++++++ src/components/ui/Tabs/types/index.tsx | 5 ++++ 9 files changed, 52 insertions(+), 27 deletions(-) create mode 100644 src/components/ui/Tabs/context/TabsContext.tsx delete mode 100644 src/components/ui/Tabs/types.ts create mode 100644 src/components/ui/Tabs/types/TabProps.tsx create mode 100644 src/components/ui/Tabs/types/TabRootProps.tsx create mode 100644 src/components/ui/Tabs/types/index.tsx diff --git a/src/components/ui/Tabs/context/TabsContext.tsx b/src/components/ui/Tabs/context/TabsContext.tsx new file mode 100644 index 00000000..c473009e --- /dev/null +++ b/src/components/ui/Tabs/context/TabsContext.tsx @@ -0,0 +1,5 @@ +import {createContext} from "react"; + +const TabRootContext = createContext(null); + +export default TabRootContext \ No newline at end of file diff --git a/src/components/ui/Tabs/segments/TabContent.tsx b/src/components/ui/Tabs/segments/TabContent.tsx index b7265729..0cfab3f9 100644 --- a/src/components/ui/Tabs/segments/TabContent.tsx +++ b/src/components/ui/Tabs/segments/TabContent.tsx @@ -1,13 +1,13 @@ 'use client'; import React from 'react'; import {customClassSwitcher} from '~/core'; -import {Tab} from '../types'; +import {TabProps} from '../types'; const COMPONENT_NAME = 'TabContent'; export type TabContentProps ={ - tabs?: Tab[] - activeTab: Tab + tabs?: TabProps[] + activeTab: TabProps className?: string; customRootClass?: string; } diff --git a/src/components/ui/Tabs/segments/TabList.tsx b/src/components/ui/Tabs/segments/TabList.tsx index 470ae5eb..803c8f49 100644 --- a/src/components/ui/Tabs/segments/TabList.tsx +++ b/src/components/ui/Tabs/segments/TabList.tsx @@ -3,7 +3,7 @@ import React from 'react'; import {customClassSwitcher} from '~/core'; import TabTrigger from './TabTrigger'; -import {Tab} from '../types'; +import { TabProps } from '../types'; const COMPONENT_NAME = 'TabList'; @@ -12,7 +12,7 @@ export type TabListProps = { className?: string; customRootClass?: string; setActiveTab: React.Dispatch; - activeTab: Tab; + activeTab: TabProps; } const TabList = ({tabs = [], className='', customRootClass='', setActiveTab, activeTab}: TabListProps) => { diff --git a/src/components/ui/Tabs/segments/TabRoot.tsx b/src/components/ui/Tabs/segments/TabRoot.tsx index d41a0d30..11d4a0cc 100644 --- a/src/components/ui/Tabs/segments/TabRoot.tsx +++ b/src/components/ui/Tabs/segments/TabRoot.tsx @@ -3,25 +3,23 @@ import React from 'react'; import {customClassSwitcher} from '~/core'; -const COMPONENT_NAME = 'TabRoot'; +import TabsContext from '../context/TabsContext'; +import {TabRootProps} from "../types" + +const COMPONENT_NAME = 'Tabs'; -export type TabRootProps = { - children: React.ReactNode; - customRootClass?: string; - className?: string; - color?: string; - props?: Record[]; -} -const TabRoot = ({children, customRootClass, className, color, ...props}: TabRootProps) => { - const rootClass = customClassSwitcher(customRootClass, COMPONENT_NAME); +const TabRoot = ({children, customRootClass, className, color, ...props}: TabRootProps) => { + const rootClass = customClassSwitcher(customRootClass, COMPONENT_NAME); return ( -
- {children} -
+ +
+ {children} +
+
); }; diff --git a/src/components/ui/Tabs/segments/TabTrigger.tsx b/src/components/ui/Tabs/segments/TabTrigger.tsx index 306ca0d1..de960eab 100644 --- a/src/components/ui/Tabs/segments/TabTrigger.tsx +++ b/src/components/ui/Tabs/segments/TabTrigger.tsx @@ -1,14 +1,14 @@ 'use client'; import React from 'react'; import {customClassSwitcher} from '~/core'; -import {Tab} from '../types'; +import {TabProps} from '../types'; const COMPONENT_NAME = 'TabTrigger'; export type TabTriggerProps = { - tab: Tab; + tab: TabProps; setActiveTab: React.Dispatch; - activeTab: Tab; + activeTab: TabProps; className?: string; customRootClass?: string; index: number; @@ -25,7 +25,9 @@ const TabTrigger = ({tab, setActiveTab, activeTab, className, customRootClass, i }; return ( - - ); -}; - -TabTrigger.displayName = 'TabTrigger'; - -export default TabTrigger; diff --git a/src/components/ui/Tabs/segments/TabContent.tsx b/src/components/ui/Tabs/shards/TabContent.tsx similarity index 73% rename from src/components/ui/Tabs/segments/TabContent.tsx rename to src/components/ui/Tabs/shards/TabContent.tsx index 0cfab3f9..14ae04eb 100644 --- a/src/components/ui/Tabs/segments/TabContent.tsx +++ b/src/components/ui/Tabs/shards/TabContent.tsx @@ -1,8 +1,9 @@ 'use client'; -import React from 'react'; +import React, {useContext} from 'react'; import {customClassSwitcher} from '~/core'; import {TabProps} from '../types'; const COMPONENT_NAME = 'TabContent'; +import TabsRootContext from '../context/TabsRootContext'; export type TabContentProps ={ @@ -12,9 +13,12 @@ export type TabContentProps ={ customRootClass?: string; } -const TabContent = ({tabs = [], activeTab, className, customRootClass}: TabContentProps) => { +const TabContent = ({ className, customRootClass}: TabContentProps) => { const rootClass = customClassSwitcher(customRootClass, COMPONENT_NAME); + const {tabs, activeTab, setActiveTab} = useContext(TabsRootContext); + + return
{tabs.map((tab, index) => { if (tab.value === activeTab) { diff --git a/src/components/ui/Tabs/segments/TabList.tsx b/src/components/ui/Tabs/shards/TabList.tsx similarity index 82% rename from src/components/ui/Tabs/segments/TabList.tsx rename to src/components/ui/Tabs/shards/TabList.tsx index e2b1e669..525546f7 100644 --- a/src/components/ui/Tabs/segments/TabList.tsx +++ b/src/components/ui/Tabs/shards/TabList.tsx @@ -1,9 +1,10 @@ 'use client'; -import React from 'react'; +import React, {useContext} from 'react'; import {customClassSwitcher} from '~/core'; import TabTrigger from './TabTrigger'; import {TabProps} from '../types'; +import TabsRootContext from '../context/TabsRootContext'; const COMPONENT_NAME = 'TabList'; @@ -15,8 +16,9 @@ export type TabListProps = { activeTab: TabProps; } -const TabList = ({tabs = [], className='', customRootClass='', setActiveTab, activeTab}: TabListProps) => { +const TabList = ({className='', customRootClass=''}: TabListProps) => { const rootClass = customClassSwitcher(customRootClass, COMPONENT_NAME); + const {tabs, activeTab, setActiveTab} = useContext(TabsRootContext); // TODO: in the previous return value of // {tabs.map((tab, index) => { diff --git a/src/components/ui/Tabs/shards/TabRoot.tsx b/src/components/ui/Tabs/shards/TabRoot.tsx new file mode 100644 index 00000000..22c72b1a --- /dev/null +++ b/src/components/ui/Tabs/shards/TabRoot.tsx @@ -0,0 +1,52 @@ + +'use client'; +import React, {useState} from 'react'; +import {customClassSwitcher} from '~/core'; + +import TabsRootContext from '../context/TabsRootContext'; + +import {TabRootProps} from '../types'; + +const COMPONENT_NAME = 'Tabs'; + + +const TabRoot = ({children, defaultTab='', customRootClass, tabs=[], className, color, ...props}: TabRootProps) => { + const rootClass = customClassSwitcher(customRootClass, COMPONENT_NAME); + + const [activeTab, setActiveTab] = useState(defaultTab || tabs[0].value || ''); + + const nextTab = ()=>{ + const currentIndex = tabs.findIndex(tab => tab.value === activeTab); + const nextIndex = currentIndex + 1; + if (nextIndex < tabs.length) { + setActiveTab(tabs[nextIndex].value); + } + } + + const previousTab = ()=>{ + const currentIndex = tabs.findIndex(tab => tab.value === activeTab); + const previousIndex = currentIndex - 1; + if (previousIndex >= 0) { + setActiveTab(tabs[previousIndex].value); + } + } + + + return ( + +
+ {children} +
+
+ ); +}; + +TabRoot.displayName = COMPONENT_NAME; + +export default TabRoot; diff --git a/src/components/ui/Tabs/shards/TabTrigger.tsx b/src/components/ui/Tabs/shards/TabTrigger.tsx new file mode 100644 index 00000000..a65756a9 --- /dev/null +++ b/src/components/ui/Tabs/shards/TabTrigger.tsx @@ -0,0 +1,59 @@ +'use client'; +import React, {useContext} from 'react'; +import {customClassSwitcher} from '~/core'; +import {TabProps} from '../types'; + +import TabsRootContext from '../context/TabsRootContext'; + + +const COMPONENT_NAME = 'TabTrigger'; + +export type TabTriggerProps = { + tab: TabProps; + setActiveTab: React.Dispatch; + activeTab: TabProps; + className?: string; + customRootClass?: string; + index: number; + props?: Record[] +} + +const TabTrigger = ({tab, setActiveTab, activeTab, className, customRootClass, index, ...props}: TabTriggerProps) => { + + // use context + const {tabs, previousTab, nextTab} = useContext(TabsRootContext); + console.log(tabs); + + + const rootClass = customClassSwitcher(customRootClass, COMPONENT_NAME); + + const isActive = activeTab === tab.value; + + const handleClick = (tab: Tab) => { + setActiveTab(tab.value); + }; + + const handleKeyDownEvent = (e: React.KeyboardEvent) => { + console.log(e.key); + if(e.key=="ArrowLeft"){ + previousTab(); + } + if(e.key=="ArrowRight"){ + nextTab(); + } + } + + return ( + + ); +}; + +TabTrigger.displayName = 'TabTrigger'; + +export default TabTrigger; diff --git a/src/components/ui/Tabs/types/TabRootProps.tsx b/src/components/ui/Tabs/types/TabRootProps.tsx index f037f4fd..9903e965 100644 --- a/src/components/ui/Tabs/types/TabRootProps.tsx +++ b/src/components/ui/Tabs/types/TabRootProps.tsx @@ -5,7 +5,7 @@ type TabRootProps = { color?: string; props?: Record[]; tabs: []; - activeTab: any + defaultTab: string; } export default TabRootProps; From ebde2e9e71fad86a52d1c7a3a152ff17a0ff0e17 Mon Sep 17 00:00:00 2001 From: Pranay Kothapalli Date: Wed, 19 Jun 2024 12:10:41 +0530 Subject: [PATCH 4/6] chore: lint --- src/components/ui/Tabs/shards/TabContent.tsx | 2 +- src/components/ui/Tabs/shards/TabRoot.tsx | 27 ++++++++++---------- src/components/ui/Tabs/shards/TabTrigger.tsx | 21 ++++++++------- 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/components/ui/Tabs/shards/TabContent.tsx b/src/components/ui/Tabs/shards/TabContent.tsx index 14ae04eb..98d95a44 100644 --- a/src/components/ui/Tabs/shards/TabContent.tsx +++ b/src/components/ui/Tabs/shards/TabContent.tsx @@ -13,7 +13,7 @@ export type TabContentProps ={ customRootClass?: string; } -const TabContent = ({ className, customRootClass}: TabContentProps) => { +const TabContent = ({className, customRootClass}: TabContentProps) => { const rootClass = customClassSwitcher(customRootClass, COMPONENT_NAME); const {tabs, activeTab, setActiveTab} = useContext(TabsRootContext); diff --git a/src/components/ui/Tabs/shards/TabRoot.tsx b/src/components/ui/Tabs/shards/TabRoot.tsx index 22c72b1a..f9128a29 100644 --- a/src/components/ui/Tabs/shards/TabRoot.tsx +++ b/src/components/ui/Tabs/shards/TabRoot.tsx @@ -15,31 +15,32 @@ const TabRoot = ({children, defaultTab='', customRootClass, tabs=[], className, const [activeTab, setActiveTab] = useState(defaultTab || tabs[0].value || ''); - const nextTab = ()=>{ - const currentIndex = tabs.findIndex(tab => tab.value === activeTab); + const nextTab = () => { + const currentIndex = tabs.findIndex((tab) => tab.value === activeTab); const nextIndex = currentIndex + 1; if (nextIndex < tabs.length) { setActiveTab(tabs[nextIndex].value); } - } + }; - const previousTab = ()=>{ - const currentIndex = tabs.findIndex(tab => tab.value === activeTab); + const previousTab = () => { + const currentIndex = tabs.findIndex((tab) => tab.value === activeTab); const previousIndex = currentIndex - 1; if (previousIndex >= 0) { setActiveTab(tabs[previousIndex].value); } - } + }; return ( - +
{children}
diff --git a/src/components/ui/Tabs/shards/TabTrigger.tsx b/src/components/ui/Tabs/shards/TabTrigger.tsx index a65756a9..e76cbfdc 100644 --- a/src/components/ui/Tabs/shards/TabTrigger.tsx +++ b/src/components/ui/Tabs/shards/TabTrigger.tsx @@ -19,7 +19,6 @@ export type TabTriggerProps = { } const TabTrigger = ({tab, setActiveTab, activeTab, className, customRootClass, index, ...props}: TabTriggerProps) => { - // use context const {tabs, previousTab, nextTab} = useContext(TabsRootContext); console.log(tabs); @@ -35,22 +34,22 @@ const TabTrigger = ({tab, setActiveTab, activeTab, className, customRootClass, i const handleKeyDownEvent = (e: React.KeyboardEvent) => { console.log(e.key); - if(e.key=="ArrowLeft"){ + if (e.key=='ArrowLeft') { previousTab(); } - if(e.key=="ArrowRight"){ + if (e.key=='ArrowRight') { nextTab(); } - } + }; return ( - + ); }; From 9a75a757b9867c4f722f75b5bb2fe65f870562a2 Mon Sep 17 00:00:00 2001 From: Pranay Kothapalli Date: Wed, 19 Jun 2024 12:18:27 +0530 Subject: [PATCH 5/6] add: focus state --- src/components/ui/Tabs/shards/TabTrigger.tsx | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/components/ui/Tabs/shards/TabTrigger.tsx b/src/components/ui/Tabs/shards/TabTrigger.tsx index e76cbfdc..680aab03 100644 --- a/src/components/ui/Tabs/shards/TabTrigger.tsx +++ b/src/components/ui/Tabs/shards/TabTrigger.tsx @@ -1,5 +1,5 @@ 'use client'; -import React, {useContext} from 'react'; +import React, {useContext, useEffect, useRef} from 'react'; import {customClassSwitcher} from '~/core'; import {TabProps} from '../types'; @@ -21,7 +21,21 @@ export type TabTriggerProps = { const TabTrigger = ({tab, setActiveTab, activeTab, className, customRootClass, index, ...props}: TabTriggerProps) => { // use context const {tabs, previousTab, nextTab} = useContext(TabsRootContext); - console.log(tabs); + const ref = useRef(null); + + const handleFocusTabEvent = ()=>{ + // focus on the active tab + if (activeTab === tab.value) { + ref.current.focus(); + } + + } + + useEffect(() => { + + handleFocusTabEvent(); + } + , [activeTab]); const rootClass = customClassSwitcher(customRootClass, COMPONENT_NAME); @@ -33,7 +47,6 @@ const TabTrigger = ({tab, setActiveTab, activeTab, className, customRootClass, i }; const handleKeyDownEvent = (e: React.KeyboardEvent) => { - console.log(e.key); if (e.key=='ArrowLeft') { previousTab(); } @@ -44,6 +57,7 @@ const TabTrigger = ({tab, setActiveTab, activeTab, className, customRootClass, i return (