mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-06 20:10:12 +02:00
handle sticky elements on new layout
This commit is contained in:
+72
@@ -0,0 +1,72 @@
|
||||
import { useLayoutEffect, useState } from 'react';
|
||||
|
||||
import { useWindowSettings } from '/@/renderer/store/settings.store';
|
||||
import { Platform } from '/@/shared/types/types';
|
||||
|
||||
export interface ItemTableStickyLayoutOffsets {
|
||||
inViewMarginTop: number;
|
||||
stickyTop: number;
|
||||
}
|
||||
|
||||
export function useItemTableStickyLayoutOffsets(): ItemTableStickyLayoutOffsets {
|
||||
const { windowBarStyle } = useWindowSettings();
|
||||
const isWinMac = windowBarStyle === Platform.WINDOWS || windowBarStyle === Platform.MACOS;
|
||||
|
||||
const [offsets, setOffsets] = useState(() => ({
|
||||
inViewMarginTop: getFallbackInViewMargin(windowBarStyle),
|
||||
stickyTop: getFallbackStickyTop(windowBarStyle),
|
||||
}));
|
||||
|
||||
useLayoutEffect(() => {
|
||||
const read = () => {
|
||||
const topVar = isWinMac
|
||||
? '--item-table-sticky-top-win-mac'
|
||||
: '--item-table-sticky-top-default';
|
||||
const marginVar = isWinMac
|
||||
? '--item-table-sticky-inview-margin-win-mac'
|
||||
: '--item-table-sticky-inview-margin-default';
|
||||
setOffsets({
|
||||
inViewMarginTop: resolveRootCssMarginLeftVar(
|
||||
marginVar,
|
||||
getFallbackInViewMargin(windowBarStyle),
|
||||
),
|
||||
stickyTop: resolveRootCssWidthVar(topVar, getFallbackStickyTop(windowBarStyle)),
|
||||
});
|
||||
};
|
||||
|
||||
read();
|
||||
window.addEventListener('resize', read);
|
||||
return () => window.removeEventListener('resize', read);
|
||||
}, [isWinMac, windowBarStyle]);
|
||||
|
||||
return offsets;
|
||||
}
|
||||
|
||||
function getFallbackInViewMargin(windowBarStyle: Platform): number {
|
||||
return windowBarStyle === Platform.WINDOWS || windowBarStyle === Platform.MACOS ? -130 : -100;
|
||||
}
|
||||
|
||||
function getFallbackStickyTop(windowBarStyle: Platform): number {
|
||||
return windowBarStyle === Platform.WINDOWS || windowBarStyle === Platform.MACOS ? 95 : 65;
|
||||
}
|
||||
|
||||
function resolveRootCssMarginLeftVar(varName: string, fallback: number): number {
|
||||
if (typeof document === 'undefined') return fallback;
|
||||
const el = document.createElement('div');
|
||||
el.style.cssText = `position:fixed;left:0;top:0;margin-left:var(${varName});width:1px;height:0;margin-top:0;margin-right:0;margin-bottom:0;padding:0;border:none;visibility:hidden;pointer-events:none;`;
|
||||
document.body.appendChild(el);
|
||||
const raw = getComputedStyle(el).marginLeft;
|
||||
el.remove();
|
||||
const v = parseFloat(raw);
|
||||
return Number.isFinite(v) ? v : fallback;
|
||||
}
|
||||
|
||||
function resolveRootCssWidthVar(varName: string, fallback: number): number {
|
||||
if (typeof document === 'undefined') return fallback;
|
||||
const el = document.createElement('div');
|
||||
el.style.cssText = `position:fixed;left:-99999px;top:0;width:var(${varName});height:0;margin:0;padding:0;border:none;visibility:hidden;pointer-events:none;`;
|
||||
document.body.appendChild(el);
|
||||
const w = el.getBoundingClientRect().width;
|
||||
el.remove();
|
||||
return Number.isFinite(w) && w > 0 ? w : fallback;
|
||||
}
|
||||
+9
-12
@@ -1,9 +1,8 @@
|
||||
import type { ItemTableStickyLayoutOffsets } from '/@/renderer/components/item-list/item-table-list/hooks/use-item-table-sticky-layout-offsets';
|
||||
|
||||
import { useInView } from 'motion/react';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
|
||||
import { useWindowSettings } from '/@/renderer/store/settings.store';
|
||||
import { Platform } from '/@/shared/types/types';
|
||||
|
||||
export interface GroupRowInfo {
|
||||
groupIndex: number;
|
||||
rowIndex: number;
|
||||
@@ -18,6 +17,7 @@ export const useStickyTableGroupRows = ({
|
||||
mainGridRef,
|
||||
shouldShowStickyHeader,
|
||||
stickyHeaderTop,
|
||||
stickyLayout,
|
||||
}: {
|
||||
containerRef: React.RefObject<HTMLDivElement | null>;
|
||||
enabled: boolean;
|
||||
@@ -27,17 +27,14 @@ export const useStickyTableGroupRows = ({
|
||||
mainGridRef: React.RefObject<HTMLDivElement | null>;
|
||||
shouldShowStickyHeader?: boolean;
|
||||
stickyHeaderTop?: number;
|
||||
stickyLayout: ItemTableStickyLayoutOffsets;
|
||||
}) => {
|
||||
const { windowBarStyle } = useWindowSettings();
|
||||
const { inViewMarginTop, stickyTop: layoutStickyTop } = stickyLayout;
|
||||
const [stickyGroupIndex, setStickyGroupIndex] = useState<null | number>(null);
|
||||
|
||||
const topMargin =
|
||||
windowBarStyle === Platform.WINDOWS || windowBarStyle === Platform.MACOS
|
||||
? '-130px'
|
||||
: '-100px';
|
||||
|
||||
const groupRowsInViewMargin = `${inViewMarginTop}px 0px 0px 0px`;
|
||||
const isTableInView = useInView(containerRef, {
|
||||
margin: `${topMargin} 0px 0px 0px`,
|
||||
margin: groupRowsInViewMargin as NonNullable<Parameters<typeof useInView>[1]>['margin'],
|
||||
});
|
||||
|
||||
const stickyTop = useMemo(() => {
|
||||
@@ -46,8 +43,8 @@ export const useStickyTableGroupRows = ({
|
||||
if (shouldShowStickyHeader && stickyHeaderTop !== undefined) {
|
||||
return stickyHeaderTop + headerHeight + 1;
|
||||
}
|
||||
return windowBarStyle === Platform.WINDOWS || windowBarStyle === Platform.MACOS ? 95 : 65;
|
||||
}, [windowBarStyle, shouldShowStickyHeader, stickyHeaderTop, headerHeight]);
|
||||
return layoutStickyTop;
|
||||
}, [layoutStickyTop, shouldShowStickyHeader, stickyHeaderTop, headerHeight]);
|
||||
|
||||
// Calculate group row indexes
|
||||
const groupRowIndexes = useMemo(() => {
|
||||
|
||||
+12
-18
@@ -1,9 +1,8 @@
|
||||
import type { ItemTableStickyLayoutOffsets } from '/@/renderer/components/item-list/item-table-list/hooks/use-item-table-sticky-layout-offsets';
|
||||
|
||||
import { useInView } from 'motion/react';
|
||||
import { RefObject, useEffect, useMemo, useRef } from 'react';
|
||||
|
||||
import { useWindowSettings } from '/@/renderer/store/settings.store';
|
||||
import { Platform } from '/@/shared/types/types';
|
||||
|
||||
export const useStickyTableHeader = ({
|
||||
containerRef,
|
||||
enabled,
|
||||
@@ -12,6 +11,7 @@ export const useStickyTableHeader = ({
|
||||
pinnedLeftColumnRef,
|
||||
pinnedRightColumnRef,
|
||||
stickyHeaderMainRef,
|
||||
stickyLayout,
|
||||
}: {
|
||||
containerRef: RefObject<HTMLDivElement | null>;
|
||||
enabled: boolean;
|
||||
@@ -20,8 +20,9 @@ export const useStickyTableHeader = ({
|
||||
pinnedLeftColumnRef?: RefObject<HTMLDivElement | null>;
|
||||
pinnedRightColumnRef?: RefObject<HTMLDivElement | null>;
|
||||
stickyHeaderMainRef?: RefObject<HTMLDivElement | null>;
|
||||
stickyLayout: ItemTableStickyLayoutOffsets;
|
||||
}) => {
|
||||
const { windowBarStyle } = useWindowSettings();
|
||||
const { inViewMarginTop, stickyTop } = stickyLayout;
|
||||
const isScrollingRef = useRef({
|
||||
main: false,
|
||||
pinnedLeft: false,
|
||||
@@ -29,27 +30,20 @@ export const useStickyTableHeader = ({
|
||||
stickyHeader: false,
|
||||
});
|
||||
|
||||
const topMargin =
|
||||
windowBarStyle === Platform.WINDOWS || windowBarStyle === Platform.MACOS
|
||||
? '-130px'
|
||||
: '-100px';
|
||||
const inViewRootMargin = `${inViewMarginTop}px 0px 0px 0px`;
|
||||
|
||||
const isTableHeaderInView = useInView(headerRef, {
|
||||
margin: `${topMargin} 0px 0px 0px`,
|
||||
});
|
||||
const inViewOptions = { margin: inViewRootMargin } as {
|
||||
margin: NonNullable<Parameters<typeof useInView>[1]>['margin'];
|
||||
};
|
||||
|
||||
const isTableInView = useInView(containerRef, {
|
||||
margin: `${topMargin} 0px 0px 0px`,
|
||||
});
|
||||
const isTableHeaderInView = useInView(headerRef, inViewOptions);
|
||||
|
||||
const isTableInView = useInView(containerRef, inViewOptions);
|
||||
|
||||
const shouldShowStickyHeader = useMemo(() => {
|
||||
return enabled && !isTableHeaderInView && isTableInView;
|
||||
}, [enabled, isTableHeaderInView, isTableInView]);
|
||||
|
||||
const stickyTop = useMemo(() => {
|
||||
return windowBarStyle === Platform.WINDOWS || windowBarStyle === Platform.MACOS ? 95 : 65;
|
||||
}, [windowBarStyle]);
|
||||
|
||||
// Sync scroll between sticky header and main grid/pinned columns
|
||||
useEffect(() => {
|
||||
if (!shouldShowStickyHeader || !stickyHeaderMainRef?.current || !mainGridRef?.current) {
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
|
||||
.item-table-pinned-rows-grid-container.header-fixed {
|
||||
position: fixed !important;
|
||||
top: 65px;
|
||||
top: var(--item-table-sticky-top-default);
|
||||
z-index: 15;
|
||||
background-color: var(--theme-bg-primary);
|
||||
box-shadow: 0 -1px 0 0 var(--theme-colors-border);
|
||||
@@ -60,7 +60,7 @@
|
||||
}
|
||||
|
||||
.item-table-pinned-rows-grid-container.header-window-bar {
|
||||
top: 95px;
|
||||
top: var(--item-table-sticky-top-win-mac);
|
||||
}
|
||||
|
||||
.item-table-list-container.header-fixed-margin {
|
||||
|
||||
@@ -31,6 +31,7 @@ import { parseTableColumns } from '/@/renderer/components/item-list/helpers/pars
|
||||
import { useListHotkeys } from '/@/renderer/components/item-list/helpers/use-list-hotkeys';
|
||||
import { useContainerWidthTracking } from '/@/renderer/components/item-list/item-table-list/hooks/use-container-width-tracking';
|
||||
import { useRowInteractionDelegate } from '/@/renderer/components/item-list/item-table-list/hooks/use-row-interaction-delegate';
|
||||
import { useItemTableStickyLayoutOffsets } from '/@/renderer/components/item-list/item-table-list/hooks/use-item-table-sticky-layout-offsets';
|
||||
import { useStickyGroupRowPositioning } from '/@/renderer/components/item-list/item-table-list/hooks/use-sticky-group-row-positioning';
|
||||
import { useStickyHeaderPositioning } from '/@/renderer/components/item-list/item-table-list/hooks/use-sticky-header-positioning';
|
||||
import { useStickyTableGroupRows } from '/@/renderer/components/item-list/item-table-list/hooks/use-sticky-table-group-rows';
|
||||
@@ -829,6 +830,8 @@ const ItemTableListStickyUI = memo(
|
||||
const stickyHeaderMainRef = useRef<HTMLDivElement | null>(null);
|
||||
const stickyHeaderRightRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
const stickyLayout = useItemTableStickyLayoutOffsets();
|
||||
|
||||
const { shouldShowStickyHeader, stickyTop } = useStickyTableHeader({
|
||||
containerRef,
|
||||
enabled: enableHeader && enableStickyHeader,
|
||||
@@ -837,6 +840,7 @@ const ItemTableListStickyUI = memo(
|
||||
pinnedLeftColumnRef,
|
||||
pinnedRightColumnRef,
|
||||
stickyHeaderMainRef,
|
||||
stickyLayout,
|
||||
});
|
||||
|
||||
useStickyHeaderPositioning({
|
||||
@@ -858,6 +862,7 @@ const ItemTableListStickyUI = memo(
|
||||
mainGridRef: rowRef,
|
||||
shouldShowStickyHeader,
|
||||
stickyHeaderTop: stickyTop,
|
||||
stickyLayout,
|
||||
});
|
||||
|
||||
const shouldRenderStickyGroupRow = shouldShowStickyGroupRow;
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
.ag-header-fixed {
|
||||
position: fixed !important;
|
||||
top: 65px;
|
||||
z-index: 15;
|
||||
padding: 0 2rem;
|
||||
margin: 0 -2rem;
|
||||
box-shadow: 0 -1px 0 0 #181818;
|
||||
transition: position 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.ag-header-window-bar {
|
||||
top: 95px;
|
||||
}
|
||||
|
||||
.ag-header {
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
.window-frame {
|
||||
top: 95px;
|
||||
}
|
||||
|
||||
.ag-header-transparent {
|
||||
--ag-header-background-color: rgb(0 0 0 / 0%) !important;
|
||||
}
|
||||
|
||||
.ag-header-fixed-margin {
|
||||
margin-top: 36px !important;
|
||||
}
|
||||
|
||||
.ag-header-cell-comp-wrapper {
|
||||
margin: 0 0.5rem;
|
||||
}
|
||||
|
||||
.ag-header-cell,
|
||||
.ag-header-group-cell {
|
||||
padding-right: 0.5rem;
|
||||
padding-left: 0.5rem;
|
||||
}
|
||||
|
||||
.ag-header-cell-resize {
|
||||
background-color: transparent;
|
||||
}
|
||||
@@ -283,6 +283,10 @@ button {
|
||||
--theme-spacing-xs: var(--mantine-spacing-xs);
|
||||
--theme-spacing-sm: var(--mantine-spacing-sm);
|
||||
--theme-spacing-md: var(--mantine-spacing-md);
|
||||
--item-table-sticky-top-win-mac: calc(95px + 2 * var(--theme-spacing-md));
|
||||
--item-table-sticky-top-default: calc(65px + var(--theme-spacing-md));
|
||||
--item-table-sticky-inview-margin-win-mac: calc(-130px - 2 * var(--theme-spacing-md));
|
||||
--item-table-sticky-inview-margin-default: calc(-100px - var(--theme-spacing-md));
|
||||
--theme-spacing-lg: var(--mantine-spacing-lg);
|
||||
--theme-spacing-xl: var(--mantine-spacing-xl);
|
||||
--theme-spacing-2xl: var(--mantine-spacing-2xl);
|
||||
|
||||
Reference in New Issue
Block a user