mirror of
https://github.com/EnixCoda/Gitako.git
synced 2026-03-11 08:54:44 +00:00
feat: toggle expand on hover&leave
This commit is contained in:
parent
87f40565f0
commit
968a5a46f3
5 changed files with 56 additions and 18 deletions
|
|
@ -5,10 +5,13 @@ import { Size } from './SideBarBodyWrapper'
|
|||
type Props = {
|
||||
size: Size
|
||||
onResize(size: Size): void
|
||||
onResizeStateChange?(state: ResizeState): void
|
||||
style?: React.CSSProperties
|
||||
}
|
||||
|
||||
export function HorizontalResizeHandler({ onResize, size, style }: Props) {
|
||||
type ResizeState = 'idle' | 'resizing'
|
||||
|
||||
export function HorizontalResizeHandler({ onResize, onResizeStateChange, size, style }: Props) {
|
||||
const pointerDown = React.useRef(false)
|
||||
const startX = React.useRef(0)
|
||||
const baseSize = React.useRef(size)
|
||||
|
|
@ -22,6 +25,7 @@ export function HorizontalResizeHandler({ onResize, size, style }: Props) {
|
|||
startX.current = clientX
|
||||
pointerDown.current = true
|
||||
baseSize.current = latestPropSize.current
|
||||
onResizeStateChange?.('resizing')
|
||||
}, [])
|
||||
|
||||
React.useEffect(() => {
|
||||
|
|
@ -39,6 +43,7 @@ export function HorizontalResizeHandler({ onResize, size, style }: Props) {
|
|||
if (pointerDown.current) {
|
||||
pointerDown.current = false
|
||||
baseSize.current = latestPropSize.current
|
||||
onResizeStateChange?.('idle')
|
||||
}
|
||||
}
|
||||
window.addEventListener('mouseup', onPointerUp)
|
||||
|
|
|
|||
|
|
@ -104,13 +104,21 @@ export function SideBar() {
|
|||
<Theme>
|
||||
<div className={'gitako-side-bar'}>
|
||||
<Portal into={$logoContainerElement.value}>
|
||||
{!shouldShow && <ToggleShowButton error={error} onClick={toggleShowSideBar} />}
|
||||
<ToggleShowButton
|
||||
error={error}
|
||||
className={cx({
|
||||
hidden: shouldShow,
|
||||
})}
|
||||
onHover={sidebarToggleMode === 'float' ? () => $shouldShow.onChange(true) : undefined}
|
||||
onClick={toggleShowSideBar}
|
||||
/>
|
||||
</Portal>
|
||||
<SideBarBodyWrapper
|
||||
className={cx(`toggle-mode-${sidebarToggleMode}`, {
|
||||
collapsed: error || !shouldShow,
|
||||
})}
|
||||
baseSize={baseSize}
|
||||
onLeave={() => $shouldShow.onChange(false)}
|
||||
>
|
||||
<div className={'gitako-side-bar-body'}>
|
||||
<div className={'close-side-bar-button-position'}>
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ export type Size = number
|
|||
type Props = {
|
||||
baseSize: Size
|
||||
className?: string
|
||||
onLeave?: React.HTMLAttributes<HTMLElement>['onMouseLeave']
|
||||
}
|
||||
|
||||
const MINIMAL_CONTENT_VIEWPORT_WIDTH = 100
|
||||
|
|
@ -20,9 +21,11 @@ export function SideBarBodyWrapper({
|
|||
baseSize,
|
||||
className,
|
||||
children,
|
||||
onLeave,
|
||||
}: React.PropsWithChildren<Props>) {
|
||||
const [size, setSize] = React.useState(baseSize)
|
||||
const configContext = useConfigs()
|
||||
const blockLeaveRef = React.useRef(false)
|
||||
|
||||
React.useEffect(() => {
|
||||
setSize(baseSize)
|
||||
|
|
@ -51,10 +54,27 @@ export function SideBarBodyWrapper({
|
|||
else if (size < MINIMAL_WIDTH) setSize(MINIMAL_WIDTH)
|
||||
else setSize(size)
|
||||
}, [])
|
||||
|
||||
const onMouseLeave = React.useCallback(
|
||||
e => {
|
||||
if (blockLeaveRef.current) return
|
||||
onLeave?.(e)
|
||||
},
|
||||
[onLeave],
|
||||
)
|
||||
|
||||
return (
|
||||
<div className={cx('gitako-side-bar-body-wrapper', className)}>
|
||||
<div className={cx('gitako-side-bar-body-wrapper', className)} onMouseLeave={onMouseLeave}>
|
||||
<div className={'gitako-side-bar-body-wrapper-content'}>{children}</div>
|
||||
{features.resize && <HorizontalResizeHandler onResize={onResize} size={size} />}
|
||||
{features.resize && (
|
||||
<HorizontalResizeHandler
|
||||
onResize={onResize}
|
||||
onResizeStateChange={state => {
|
||||
blockLeaveRef.current = state === 'resizing'
|
||||
}}
|
||||
size={size}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,9 +7,11 @@ import { Icon } from './Icon'
|
|||
|
||||
type Props = {
|
||||
error?: string | null
|
||||
className?: React.HTMLAttributes<HTMLButtonElement>['className']
|
||||
onHover?: React.HTMLAttributes<HTMLButtonElement>['onMouseEnter']
|
||||
} & Pick<React.HTMLAttributes<HTMLButtonElement>, 'onClick'>
|
||||
|
||||
export function ToggleShowButton({ error, onClick }: Props) {
|
||||
export function ToggleShowButton({ error, className, onClick, onHover }: Props) {
|
||||
const ref = React.useRef<HTMLDivElement>(null)
|
||||
const config = useConfigs()
|
||||
const [distance, setDistance] = React.useState(config.value.toggleButtonVerticalDistance)
|
||||
|
|
@ -36,12 +38,13 @@ export function ToggleShowButton({ error, onClick }: Props) {
|
|||
|
||||
const toggleIconMode = config.value.toggleButtonContent
|
||||
return (
|
||||
<div ref={ref} className={'gitako-toggle-show-button-wrapper'}>
|
||||
<div ref={ref} className={cx('gitako-toggle-show-button-wrapper', className)}>
|
||||
<button
|
||||
className={cx('gitako-toggle-show-button', {
|
||||
error,
|
||||
})}
|
||||
onClick={onClick}
|
||||
onMouseEnter={onHover}
|
||||
draggable
|
||||
onDragStart={event => {
|
||||
hideDragPreview(event)
|
||||
|
|
|
|||
|
|
@ -182,12 +182,19 @@ $minimal-z-index: max($github-header-z-index, $github-pull-request-float-header-
|
|||
left: 0px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
transition: left 0.25s linear;
|
||||
|
||||
&.hidden {
|
||||
left: -40px;
|
||||
}
|
||||
|
||||
.#{$name}-toggle-show-button {
|
||||
@include icon-button;
|
||||
background: transparent;
|
||||
border: none;
|
||||
padding: 0;
|
||||
position: relative;
|
||||
left: -8px;
|
||||
|
||||
&.error {
|
||||
cursor: not-allowed;
|
||||
|
|
@ -198,6 +205,7 @@ $minimal-z-index: max($github-header-z-index, $github-pull-request-float-header-
|
|||
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
padding: 4px;
|
||||
border-radius: 6px;
|
||||
border: 1px solid var(--gitako-border-tertiary);
|
||||
background: var(--gitako-bg-tertiary);
|
||||
|
|
@ -207,33 +215,27 @@ $minimal-z-index: max($github-header-z-index, $github-pull-request-float-header-
|
|||
}
|
||||
transition: all ease 0.3s;
|
||||
font-size: 16px;
|
||||
transform: translateX(0px);
|
||||
transform: translateX(8px);
|
||||
&:active,
|
||||
&:hover {
|
||||
transform: translateX(4px);
|
||||
transform: translateX(12px);
|
||||
}
|
||||
}
|
||||
.tentacle {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
object-fit: contain;
|
||||
transition: all ease 0.4s;
|
||||
transform: translateX(-16px);
|
||||
transform: translateX(-8px);
|
||||
filter: drop-shadow(0 0 1px var(--gitako-bg-backdrop));
|
||||
}
|
||||
&:active,
|
||||
&:hover {
|
||||
.tentacle {
|
||||
transform: translateX(-12px);
|
||||
transform: translateX(-4px);
|
||||
filter: drop-shadow(0 0 2px var(--gitako-bg-backdrop));
|
||||
}
|
||||
}
|
||||
|
||||
img {
|
||||
filter: drop-shadow(0 0 1px var(--gitako-bg-backdrop));
|
||||
transition: all ease 0.3s;
|
||||
width: 90%;
|
||||
height: 90%;
|
||||
object-fit: contain;
|
||||
}
|
||||
}
|
||||
|
||||
.error-message {
|
||||
|
|
|
|||
Loading…
Reference in a new issue