-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
164 additions
and
137 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
import React, { | ||
CSSProperties, | ||
forwardRef, | ||
useCallback, | ||
useEffect, | ||
useMemo, | ||
useRef, | ||
} from "react"; | ||
import ChromeTabsClz, { TabProperties } from "./chrome-tabs"; | ||
|
||
type Listeners = { | ||
onTabActivated: (tabId: string) => void; | ||
onTabClosed: (tabId: string) => void; | ||
onTabReorder: (tabId: string, fromIdex: number, toIndex: number) => void; | ||
}; | ||
|
||
const ChromeTabsWrapper = forwardRef<HTMLDivElement, any>((props, ref) => { | ||
return ( | ||
<div | ||
ref={ref} | ||
className="chrome-tabs" | ||
style={{ "--tab-content-margin": "9px" } as CSSProperties} | ||
> | ||
<div className="chrome-tabs-content"></div> | ||
</div> | ||
); | ||
}); | ||
|
||
export function useChromeTabs(listeners: Listeners) { | ||
const ref = useRef<HTMLDivElement>(null); | ||
const chromeTabsRef = useRef<ChromeTabsClz | null>(null); | ||
const listenersRef = useRef<Listeners>(listeners); | ||
|
||
useCallback(() => { | ||
listenersRef.current = { ...listeners }; | ||
}, [listeners.onTabActivated, listeners.onTabClosed, listeners.onTabReorder]); | ||
|
||
useEffect(() => { | ||
const chromeTabs = new ChromeTabsClz(); | ||
chromeTabsRef.current = chromeTabs; | ||
chromeTabs.init(ref.current as HTMLDivElement); | ||
chromeTabs.el.addEventListener("activeTabChange", ({ detail }: any) => { | ||
const tabEle = detail.tabEl as HTMLDivElement; | ||
const tabId = tabEle.getAttribute( | ||
"data-tab-id" | ||
) as string; | ||
listenersRef.current.onTabActivated(tabId); | ||
}); | ||
|
||
chromeTabs.el.addEventListener("tabClose", ({ detail }: any) => { | ||
const tabEle = detail.tabEl as HTMLDivElement; | ||
const tabId = tabEle.getAttribute( | ||
"data-tab-id" | ||
) as string; | ||
listenersRef.current.onTabClosed(tabId); | ||
}); | ||
|
||
chromeTabs.el.addEventListener("tabReorder", ({ detail }: any) => { | ||
const { tabEl: tabEle, originIndex, destinationIndex } = detail; | ||
const tabId = tabEle.getAttribute( | ||
"data-tab-id" | ||
) as string; | ||
listenersRef.current.onTabReorder(tabId, originIndex, destinationIndex); | ||
}); | ||
}, []); | ||
|
||
const addTab = useCallback((tab: TabProperties) => { | ||
chromeTabsRef.current?.addTab(tab); | ||
}, []); | ||
|
||
const removeTab = useCallback((tabId: string) => { | ||
const ele = ref.current?.querySelector( | ||
`[data-tab-id="${tabId}"]` | ||
) as HTMLDivElement; | ||
if (ele) { | ||
chromeTabsRef.current?.removeTab(ele); | ||
} | ||
}, []); | ||
|
||
const activeTab = useCallback((tabId: string) => { | ||
const ele = ref.current?.querySelector( | ||
`[data-tab-id="${tabId}"]` | ||
) as HTMLDivElement; | ||
if (ele) { | ||
chromeTabsRef.current?.setCurrentTab(ele); | ||
} | ||
}, []); | ||
|
||
const updateTab = useCallback((tabId: string, tab: TabProperties) => { | ||
const ele = ref.current?.querySelector( | ||
`[data-tab-id="${tabId}"]` | ||
) as HTMLDivElement; | ||
if (ele) { | ||
chromeTabsRef.current?.updateTab(ele, { ...tab, id: tabId }); | ||
} | ||
}, []); | ||
|
||
const ChromeTabs = useCallback(() => { | ||
return <ChromeTabsWrapper ref={ref} />; | ||
}, []); | ||
|
||
return { | ||
ChromeTabs, | ||
addTab, | ||
updateTab, | ||
removeTab, | ||
activeTab | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,122 +1 @@ | ||
import React, { | ||
CSSProperties, | ||
memo, | ||
useCallback, | ||
useEffect, | ||
useMemo, | ||
useRef, | ||
} from "react"; | ||
import ChromeTabsJS, { TabProperties } from "./chrome-tabs"; | ||
|
||
export interface ChromeTabsProps { | ||
tabs: TabProperties[]; | ||
style?: CSSProperties; | ||
onTabsChange( | ||
tabs: TabProperties[], | ||
detail: { | ||
reason: "close" | "reorder" | "active"; | ||
tab: TabProperties; | ||
originIndex?: number; | ||
destIndex?: number; | ||
} | ||
): void; | ||
} | ||
|
||
export const ChromeTabs = memo<ChromeTabsProps>((props) => { | ||
const ref = useRef<HTMLDivElement>(null); | ||
const tabsRef = useRef<ChromeTabsJS>(); | ||
const onChangeRef = useRef(props.onTabsChange); | ||
|
||
const map = useMemo(() => { | ||
return new Map<HTMLElement, TabProperties>(); | ||
}, []); | ||
|
||
useEffect(() => { | ||
onChangeRef.current = props.onTabsChange; | ||
}, [props.onTabsChange]); | ||
|
||
const updateTabs = useCallback((tabs: TabProperties[]) => { | ||
const chromeTabs = tabsRef.current!; | ||
const tabEls = chromeTabs.tabEls; | ||
|
||
tabEls.forEach((ele, index) => { | ||
const tab = tabs[index]; | ||
if (tab) { | ||
chromeTabs.updateTab(ele, tab); | ||
map.set(ele, tab); | ||
if (tab.active) { | ||
chromeTabs.setCurrentTab(ele); | ||
} | ||
} else { | ||
chromeTabs.removeTab(ele); | ||
map.delete(ele); | ||
} | ||
}); | ||
|
||
// console.log(tabs); | ||
tabs.slice(tabEls.length).forEach((tab) => { | ||
const ele = chromeTabs.addTab(tab, { background: true }); | ||
map.set(ele, tab); | ||
if (tab.active) { | ||
chromeTabs.setCurrentTab(ele); | ||
} | ||
}); | ||
}, []); | ||
|
||
useEffect(() => { | ||
const chromeTabs = new ChromeTabsJS(); | ||
chromeTabs.init(ref.current!); | ||
tabsRef.current = chromeTabs; | ||
chromeTabs.el.addEventListener("activeTabChange", ({ detail }: any) => { | ||
const tabEl = detail.tabEl; | ||
const tabEls = [...chromeTabs.tabEls]; | ||
const newTabs = tabEls | ||
.map((el) => { | ||
return { ...map.get(el)!, active: el === tabEl }!; | ||
}) | ||
.filter((tab) => !!tab); | ||
onChangeRef.current(newTabs, { reason: "active", tab: map.get(tabEl)! }); | ||
}); | ||
// chromeTabs.el.addEventListener("tabClick", ({ detail }: any) => { | ||
// const tabEl = detail.tabEl; | ||
// const tabEls = [...chromeTabs.tabEls]; | ||
// const newTabs = tabEls | ||
// .map((el) => { | ||
// return { ...map.get(el)!, active: el === tabEl }!; | ||
// }) | ||
// .filter((tab) => !!tab); | ||
// onChangeRef.current(newTabs, { reason: "active", tab: map.get(tabEl)! }); | ||
// }); | ||
|
||
chromeTabs.el.addEventListener("tabClose", ({ detail }: any) => { | ||
const tabEl = detail.tabEl; | ||
const tabEls = [...chromeTabs.tabEls]; | ||
const index = tabEls.indexOf(tabEl); | ||
tabEls.splice(index, 1); | ||
const newTabs = tabEls.map((el) => map.get(el)!).filter((tab) => !!tab); | ||
onChangeRef.current(newTabs, { reason: "close", tab: map.get(tabEl)! }); | ||
// props.onTabsChange([...props.tabs]) | ||
}); | ||
|
||
chromeTabs.el.addEventListener("tabReorder", ({ detail }: any) => { | ||
const tabEl = detail.tabEl; | ||
const tabEls = [...chromeTabs.tabEls]; | ||
const newTabs = tabEls.filter((tab) => !!tab); | ||
onChangeRef.current(newTabs, { reason: "reorder", tab: map.get(tabEl)! }); | ||
}); | ||
}, []); | ||
|
||
useEffect(() => { | ||
updateTabs(props.tabs); | ||
}, [props.tabs]); | ||
|
||
return ( | ||
<div | ||
ref={ref} | ||
className="chrome-tabs" | ||
style={{ "--tab-content-margin": "9px" } as CSSProperties} | ||
> | ||
<div className="chrome-tabs-content"></div> | ||
</div> | ||
); | ||
}); | ||
export { useChromeTabs } from './hooks' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters