mirror of
https://github.com/EnixCoda/Gitako.git
synced 2026-03-11 08:54:44 +00:00
Merge branch 'feature/optimize-resize' into develop
This commit is contained in:
commit
73f9a8fcbe
10 changed files with 99 additions and 48 deletions
1
Safari/Gitako
Submodule
1
Safari/Gitako
Submodule
|
|
@ -0,0 +1 @@
|
|||
Subproject commit b82c8588b9cc5864c3738f903ac1930ceda5c996
|
||||
|
|
@ -58,9 +58,9 @@ Sentry.init(sentryOptions)
|
|||
|
||||
export const withErrorLog: Middleware = function withErrorLog(method, args) {
|
||||
return [
|
||||
async function (...args: any[]) {
|
||||
async function () {
|
||||
try {
|
||||
await method.apply(null, args)
|
||||
await method.apply(null, arguments as any)
|
||||
} catch (error) {
|
||||
raiseError(error)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,14 +46,23 @@ const RawFileExplorer: React.FC<Props & ConnectorState> = function RawFileExplor
|
|||
searched,
|
||||
} = props
|
||||
const {
|
||||
value: { accessToken, compressSingletonFolder, searchMode, commentToggle, restoreExpandedFolders },
|
||||
value: {
|
||||
accessToken,
|
||||
compressSingletonFolder,
|
||||
searchMode,
|
||||
commentToggle,
|
||||
restoreExpandedFolders,
|
||||
},
|
||||
} = useConfigs()
|
||||
|
||||
const onSearch = React.useCallback(
|
||||
(searchKey: string, searchMode: SearchMode) => {
|
||||
updateSearchKey(searchKey)
|
||||
if (visibleNodesGenerator) {
|
||||
visibleNodesGenerator.search(searchModes[searchMode].getSearchParams(searchKey), restoreExpandedFolders)
|
||||
visibleNodesGenerator.search(
|
||||
searchModes[searchMode].getSearchParams(searchKey),
|
||||
restoreExpandedFolders,
|
||||
)
|
||||
}
|
||||
},
|
||||
[updateSearchKey, visibleNodesGenerator, restoreExpandedFolders],
|
||||
|
|
@ -74,8 +83,8 @@ const RawFileExplorer: React.FC<Props & ConnectorState> = function RawFileExplor
|
|||
}, [setUpTree, metaData, compressSingletonFolder, accessToken])
|
||||
|
||||
React.useEffect(() => {
|
||||
if (visibleNodes?.focusedNode) focusFileExplorer()
|
||||
})
|
||||
focusFileExplorer()
|
||||
}, [])
|
||||
|
||||
const renderActions: ((node: TreeNode) => React.ReactNode) | undefined = React.useMemo(() => {
|
||||
const renderGoToButton = (node: TreeNode): React.ReactNode => (
|
||||
|
|
|
|||
|
|
@ -126,6 +126,7 @@ export function SideBar() {
|
|||
})}
|
||||
baseSize={baseSize}
|
||||
onLeave={sidebarToggleMode === 'float' ? () => setShowSideBar(false) : undefined}
|
||||
sizeVariableMountPoint={sidebarToggleMode === 'persistent' ? document.body : undefined}
|
||||
>
|
||||
<div className={'gitako-side-bar-body'}>
|
||||
<div
|
||||
|
|
|
|||
|
|
@ -4,25 +4,32 @@ import * as React from 'react'
|
|||
import { useDebounce, useWindowSize } from 'react-use'
|
||||
import { defaultConfigs } from 'utils/config/helper'
|
||||
import { cx } from 'utils/cx'
|
||||
import { setResizingState } from 'utils/DOMHelper'
|
||||
import { setCSSVariable } from 'utils/DOMHelper'
|
||||
import * as features from 'utils/features'
|
||||
import { useCSSVariable } from 'utils/hooks/useCSSVariable'
|
||||
|
||||
export type Size = number
|
||||
type Props = {
|
||||
baseSize: Size
|
||||
className?: string
|
||||
onLeave?: React.HTMLAttributes<HTMLElement>['onMouseLeave']
|
||||
sizeVariableMountPoint?: HTMLElement
|
||||
}
|
||||
|
||||
const MINIMAL_CONTENT_VIEWPORT_WIDTH = 100
|
||||
const MINIMAL_WIDTH = 240
|
||||
|
||||
function getSafeSize(size: number, width: number) {
|
||||
if (size > width - MINIMAL_CONTENT_VIEWPORT_WIDTH) return width - MINIMAL_CONTENT_VIEWPORT_WIDTH
|
||||
if (size < MINIMAL_WIDTH) return MINIMAL_WIDTH
|
||||
return size
|
||||
}
|
||||
|
||||
export function SideBarBodyWrapper({
|
||||
baseSize,
|
||||
className,
|
||||
children,
|
||||
onLeave,
|
||||
sizeVariableMountPoint,
|
||||
}: React.PropsWithChildren<Props>) {
|
||||
const [size, setSize] = React.useState(baseSize)
|
||||
const configContext = useConfigs()
|
||||
|
|
@ -34,27 +41,50 @@ export function SideBarBodyWrapper({
|
|||
|
||||
const { width } = useWindowSize()
|
||||
React.useEffect(() => {
|
||||
if (size > width - MINIMAL_CONTENT_VIEWPORT_WIDTH)
|
||||
setSize(width - MINIMAL_CONTENT_VIEWPORT_WIDTH)
|
||||
else if (size < MINIMAL_WIDTH) setSize(MINIMAL_WIDTH)
|
||||
const safeSize = getSafeSize(size, width)
|
||||
if (safeSize !== size) setSize(safeSize)
|
||||
}, [width, size])
|
||||
|
||||
React.useEffect(() => {
|
||||
setResizingState(true)
|
||||
const timer = setTimeout(() => setResizingState(false), 100)
|
||||
return () => clearTimeout(timer)
|
||||
}, [width, size])
|
||||
|
||||
useCSSVariable('--gitako-width', `${size}px`)
|
||||
const bodyWrapperRef = React.useRef<HTMLDivElement | null>(null)
|
||||
useDebounce(() => configContext.onChange({ sideBarWidth: size }), 100, [size])
|
||||
|
||||
const onResize = React.useCallback((size: number) => {
|
||||
// do NOT merge this with the above similar effect, side bar will jump otherwise
|
||||
if (size > width - MINIMAL_CONTENT_VIEWPORT_WIDTH)
|
||||
setSize(width - MINIMAL_CONTENT_VIEWPORT_WIDTH)
|
||||
else if (size < MINIMAL_WIDTH) setSize(MINIMAL_WIDTH)
|
||||
else setSize(size)
|
||||
}, [])
|
||||
function apply(sizeVariableMountPoint: HTMLElement | undefined, size: number) {
|
||||
if (sizeVariableMountPoint)
|
||||
setCSSVariable(
|
||||
'--gitako-width',
|
||||
sizeVariableMountPoint ? `${size}px` : undefined,
|
||||
sizeVariableMountPoint,
|
||||
)
|
||||
|
||||
if (bodyWrapperRef.current)
|
||||
setCSSVariable(
|
||||
'--gitako-width',
|
||||
sizeVariableMountPoint ? undefined : `${size}px`,
|
||||
bodyWrapperRef.current,
|
||||
)
|
||||
}
|
||||
|
||||
// Update size using useEffect would cause delay
|
||||
const onResize = React.useMemo(() => {
|
||||
let sizeToApply: number,
|
||||
applied = true
|
||||
return (size: number) => {
|
||||
// do NOT merge this with the above similar effect, side bar will jump otherwise
|
||||
sizeToApply = getSafeSize(size, width)
|
||||
setSize(sizeToApply)
|
||||
|
||||
if (applied) {
|
||||
applied = false
|
||||
requestAnimationFrame(() => {
|
||||
applied = true
|
||||
apply(sizeVariableMountPoint, sizeToApply)
|
||||
})
|
||||
}
|
||||
}
|
||||
}, [width, sizeVariableMountPoint])
|
||||
|
||||
React.useEffect(() => {
|
||||
apply(sizeVariableMountPoint, size)
|
||||
}, [sizeVariableMountPoint])
|
||||
|
||||
const onMouseLeave = React.useCallback(
|
||||
e => {
|
||||
|
|
@ -65,7 +95,11 @@ export function SideBarBodyWrapper({
|
|||
)
|
||||
|
||||
return (
|
||||
<div className={cx('gitako-side-bar-body-wrapper', className)} onMouseLeave={onMouseLeave}>
|
||||
<div
|
||||
ref={bodyWrapperRef}
|
||||
className={cx('gitako-side-bar-body-wrapper', className)}
|
||||
onMouseLeave={onMouseLeave}
|
||||
>
|
||||
<div className={'gitako-side-bar-body-wrapper-content'}>{children}</div>
|
||||
{features.resize && (
|
||||
<HorizontalResizeHandler
|
||||
|
|
|
|||
|
|
@ -264,8 +264,10 @@ export const toggleNodeExpansion: BoundMethodCreator<
|
|||
} = dispatch.get()
|
||||
if (!visibleNodesGenerator) return
|
||||
|
||||
visibleNodesGenerator.focusNode(node)
|
||||
await visibleNodesGenerator.toggleExpand(node, recursive)
|
||||
if (node.type === 'tree') {
|
||||
visibleNodesGenerator.focusNode(node)
|
||||
await visibleNodesGenerator.toggleExpand(node, recursive)
|
||||
}
|
||||
}
|
||||
|
||||
export const focusNode: BoundMethodCreator<[TreeNode | null]> =
|
||||
|
|
|
|||
|
|
@ -305,20 +305,29 @@ async function getPullRequestTreeData(
|
|||
}
|
||||
|
||||
const docs = await API.getPullPageDocuments(userName, repoName, pullId)
|
||||
// query all elements at once to make getFileElementHash run faster
|
||||
const elementsHavePath = docs.map(doc => doc.querySelectorAll(`[data-path]`))
|
||||
const getFileElementHash = (path: string) => {
|
||||
for (const doc of docs) {
|
||||
const id = doc.querySelector(`*[data-path^="${path}"]`)?.parentElement?.id
|
||||
if (id) return id
|
||||
let e
|
||||
for (const group of elementsHavePath) {
|
||||
for (let i = 0; i < group.length; i++) {
|
||||
const element = group[i]
|
||||
if (element.getAttribute('data-path')?.startsWith(path)) {
|
||||
e = element
|
||||
break
|
||||
}
|
||||
}
|
||||
if (e) break
|
||||
}
|
||||
return e?.parentElement?.id
|
||||
}
|
||||
|
||||
const urlMainPart = `https://${window.location.host}/${userName}/${repoName}/pull/${pullId}/files${window.location.search}`
|
||||
const nodes: TreeNode[] = treeData.map(item => ({
|
||||
path: item.filename || '',
|
||||
type: 'blob',
|
||||
name: item.filename?.replace(/^.*\//, '') || '',
|
||||
url: `https://${window.location.host}/${userName}/${repoName}/pull/${pullId}/files${
|
||||
window.location.search
|
||||
}${formatHash(getFileElementHash(item.filename))}`,
|
||||
url: `${urlMainPart}${formatHash(getFileElementHash(item.filename))}`,
|
||||
sha: item.sha,
|
||||
comments: commentData?.filter(comment => item.filename === comment.path).length,
|
||||
}))
|
||||
|
|
|
|||
|
|
@ -81,9 +81,6 @@ $minimal-z-index: max($github-header-z-index, $github-pull-request-float-header-
|
|||
}
|
||||
}
|
||||
|
||||
:root {
|
||||
--gitako-width: #{$side-bar-base-width};
|
||||
}
|
||||
|
||||
.#{$name}-ready {
|
||||
// github
|
||||
|
|
|
|||
|
|
@ -123,15 +123,13 @@ export function focusSearchInput() {
|
|||
})
|
||||
}
|
||||
|
||||
export function setResizingState(on: boolean) {
|
||||
const target = document.querySelector('.gitako-toggle-show-button-wrapper')
|
||||
if (!target) return
|
||||
if (on) target.classList.add('resizing')
|
||||
else target.classList.remove('resizing')
|
||||
}
|
||||
|
||||
export function findNodeElement(node:TreeNode, rootElement: HTMLElement): HTMLElement | null {
|
||||
export function findNodeElement(node: TreeNode, rootElement: HTMLElement): HTMLElement | null {
|
||||
const nodeElement = rootElement.querySelector(`a[href="${node.url}"]`)
|
||||
if (nodeElement instanceof HTMLElement) return nodeElement
|
||||
return null
|
||||
}
|
||||
|
||||
export function setCSSVariable(name: string, value: string | undefined, element: HTMLElement) {
|
||||
if (value === undefined) element.style.removeProperty(name)
|
||||
else element.style.setProperty(name, value)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -273,8 +273,8 @@ class FlattenLayer extends CompressLayer {
|
|||
const expand = !this.expandedNodes.has(node.path)
|
||||
await traverse(
|
||||
[node],
|
||||
async node => {
|
||||
await this.$setExpand(node, expand)
|
||||
node => {
|
||||
this.$setExpand(node, expand)
|
||||
return recursive
|
||||
},
|
||||
node => node.contents || [],
|
||||
|
|
|
|||
Loading…
Reference in a new issue