Skip to content

Commit

Permalink
Fix table content not rendering
Browse files Browse the repository at this point in the history
  • Loading branch information
mgmeyers committed Apr 19, 2024
1 parent f143eed commit cae948d
Show file tree
Hide file tree
Showing 5 changed files with 176 additions and 83 deletions.
1 change: 0 additions & 1 deletion src/components/Kanban.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,6 @@ export const Kanban = ({ view, stateManager }: KanbanProps) => {
{
'something-is-dragging': isAnythingDragging,
},
// TODO
...((boardData.data.frontmatter.cssclass || []) as string[]),
...((boardData.data.frontmatter.cssclasses || []) as string[]),
])}
Expand Down
26 changes: 21 additions & 5 deletions src/components/MarkdownRenderer/MarkdownRenderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
renderMarkdown,
} from '../../helpers/renderMarkdown';
import { usePreprocessedStr } from '../Editor/dateWidget';
import { KanbanContext, SearchContext, SortContext } from '../context';
import { IntersectionObserverContext, KanbanContext, SearchContext, SortContext } from '../context';
import { c, noop } from '../helpers';
import { DateColor, TagColor } from '../types';

Expand Down Expand Up @@ -444,15 +444,17 @@ export const MarkdownPreviewRenderer = memo(function MarkdownPreviewRenderer({
searchQuery,
...divProps
}: MarkdownPreviewRendererProps) {
const search = useContext(SearchContext);
const { view, stateManager, getDateColor, getTagColor } = useContext(KanbanContext);
const search = useContext(SearchContext);
const entityManager = useContext(EntityManagerContext);
const dndManager = useContext(DndManagerContext);
const sortContext = useContext(SortContext);
const intersectionContext = useContext(IntersectionObserverContext);

const markRef = useRef<Mark>();
const renderer = useRef<MarkdownRenderer>();
const elRef = useRef<HTMLDivElement>();

const entityManager = useContext(EntityManagerContext);
const dndManager = useContext(DndManagerContext);
const sortContext = useContext(SortContext);
const processed = usePreprocessedStr(stateManager, markdownString, getDateColor);

useEffect(() => {
Expand Down Expand Up @@ -591,6 +593,20 @@ export const MarkdownPreviewRenderer = memo(function MarkdownPreviewRenderer({
}
}

useEffect(() => {
if (!intersectionContext || !elRef.current) return;

intersectionContext.registerHandler(elRef.current, (entry) => {
if (entry.isIntersecting) renderer.current?.showChildren();
else renderer.current?.hideChildren();
});
return () => {
if (elRef.current) {
intersectionContext?.unregisterHandler(elRef.current);
}
};
}, []);

return (
<div
style={styles}
Expand Down
226 changes: 149 additions & 77 deletions src/components/Table/Table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,90 @@ import {
import classcat from 'classcat';
import update from 'immutability-helper';
import { useEffect, useMemo, useRef } from 'preact/compat';
import { IntersectionObserverHandler } from 'src/dnd/managers/ScrollManager';

import { StateManager } from '../../StateManager';
import { Icon } from '../Icon/Icon';
import { IntersectionObserverContext } from '../context';
import { c } from '../helpers';
import { Board } from '../types';
import { fuzzyAnyFilter, useTableColumns } from './helpers';

function useIntersectionObserver() {
const observerRef = useRef<IntersectionObserver>();
const targetRef = useRef<HTMLElement>();
const handlers = useRef<WeakMap<HTMLElement, IntersectionObserverHandler>>(new WeakMap());
const queueRef = useRef<HTMLElement[]>([]);

useEffect(() => {
return () => {
observerRef.current?.disconnect();
handlers.current = null;
queueRef.current.length = 0;
};
}, []);

const bindObserver = (el: HTMLElement) => {
if (!el) return;
if (targetRef.current === el) return;
if (observerRef.current) observerRef.current.disconnect();

const style = getComputedStyle(el);

observerRef.current = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (!handlers.current.has(entry.target as HTMLElement)) return;
const handler = handlers.current.get(entry.target as HTMLElement);
handler(entry);
});
},
{
root: el,
threshold: 0.01,
rootMargin: `${style.paddingTop} 0px ${style.paddingBottom} 0px`,
}
);

targetRef.current = el;
queueRef.current.forEach((el) => observerRef.current.observe(el));
queueRef.current.length = 0;
};

const context = useMemo(
() => ({
registerHandler: (el: HTMLElement, handler: IntersectionObserverHandler) => {
if (!el) return;
handlers.current.set(el, handler);
if (!observerRef.current) {
queueRef.current.push(el);
return;
}
observerRef.current.observe(el);
},
unregisterHandler: (el: HTMLElement) => {
if (!el) return;
handlers.current?.delete(el);
if (queueRef.current?.length) {
queueRef.current = queueRef.current.filter((q) => q !== el);
}
observerRef.current?.unobserve(el);
},
}),
[]
);

return { bindObserver, context };
}

export function TableView({
boardData,
stateManager,
}: {
boardData: Board;
stateManager: StateManager;
}) {
const { bindObserver, context } = useIntersectionObserver();
const { data, columns, state, setSorting } = useTableColumns(boardData, stateManager);
const table = useReactTable({
data,
Expand Down Expand Up @@ -72,86 +142,88 @@ export function TableView({
}, [tableWidth]);

return (
<div className={`markdown-rendered ${c('table-wrapper')}`}>
<table style={tableStyle}>
<thead>
{table.getHeaderGroups().map((headerGroup) => (
<tr key={headerGroup.id}>
{headerGroup.headers.map((header) => {
const sort = header.column.getIsSorted();
return (
<th key={header.id} className="mod-has-icon">
<div
className={c('table-cell-wrapper')}
style={{
width: header.getSize(),
}}
>
{header.isPlaceholder ? null : (
<div
className={c('table-header')}
onClick={header.column.getToggleSortingHandler()}
>
<div>
{flexRender(header.column.columnDef.header, header.getContext())}
</div>
<div className={c('table-header-sort')}>
{sort === 'asc' ? (
<Icon name="lucide-chevron-up" />
) : sort === 'desc' ? (
<Icon name="lucide-chevron-down" />
) : (
<Icon name="lucide-chevrons-up-down" />
)}
</div>
</div>
)}
<div className={`markdown-rendered ${c('table-wrapper')}`} ref={bindObserver}>
<IntersectionObserverContext.Provider value={context}>
<table style={tableStyle}>
<thead>
{table.getHeaderGroups().map((headerGroup) => (
<tr key={headerGroup.id}>
{headerGroup.headers.map((header) => {
const sort = header.column.getIsSorted();
return (
<th key={header.id} className="mod-has-icon">
<div
{...{
onDoubleClick: () => header.column.resetSize(),
onMouseDown: header.getResizeHandler(),
onTouchStart: header.getResizeHandler(),
className: `resizer ${table.options.columnResizeDirection} ${
header.column.getIsResizing() ? 'isResizing' : ''
}`,
className={c('table-cell-wrapper')}
style={{
width: header.getSize(),
}}
/>
</div>
</th>
);
})}
</tr>
))}
</thead>
<tbody>
{table.getRowModel().rows.map((row) => (
<tr key={row.id}>
{row.getVisibleCells().map((cell) => {
return (
<td
key={cell.id}
className={classcat({
'mod-has-icon': cell.column.id === 'lane',
'mod-search-match': row.columnFiltersMeta[cell.column.id]
? (row.columnFiltersMeta[cell.column.id] as any).itemRank.passed
: false,
})}
>
<div
className={c('table-cell-wrapper')}
style={{
width: cell.column.getSize(),
}}
>
{header.isPlaceholder ? null : (
<div
className={c('table-header')}
onClick={header.column.getToggleSortingHandler()}
>
<div>
{flexRender(header.column.columnDef.header, header.getContext())}
</div>
<div className={c('table-header-sort')}>
{sort === 'asc' ? (
<Icon name="lucide-chevron-up" />
) : sort === 'desc' ? (
<Icon name="lucide-chevron-down" />
) : (
<Icon name="lucide-chevrons-up-down" />
)}
</div>
</div>
)}
<div
{...{
onDoubleClick: () => header.column.resetSize(),
onMouseDown: header.getResizeHandler(),
onTouchStart: header.getResizeHandler(),
className: `resizer ${table.options.columnResizeDirection} ${
header.column.getIsResizing() ? 'isResizing' : ''
}`,
}}
/>
</div>
</th>
);
})}
</tr>
))}
</thead>
<tbody>
{table.getRowModel().rows.map((row) => (
<tr key={row.id}>
{row.getVisibleCells().map((cell) => {
return (
<td
key={cell.id}
className={classcat({
'mod-has-icon': cell.column.id === 'lane',
'mod-search-match': row.columnFiltersMeta[cell.column.id]
? (row.columnFiltersMeta[cell.column.id] as any).itemRank.passed
: false,
})}
>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</div>
</td>
);
})}
</tr>
))}
</tbody>
</table>
<div
className={c('table-cell-wrapper')}
style={{
width: cell.column.getSize(),
}}
>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</div>
</td>
);
})}
</tr>
))}
</tbody>
</table>
</IntersectionObserverContext.Provider>
</div>
);
}
5 changes: 5 additions & 0 deletions src/components/context.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { createContext } from 'preact/compat';
import { KanbanView } from 'src/KanbanView';
import { StateManager } from 'src/StateManager';
import { IntersectionObserverHandler } from 'src/dnd/managers/ScrollManager';

import { BoardModifiers } from '../helpers/boardModifiers';
import { DateColor, Item, Lane, LaneSort, TagColor } from './types';
Expand All @@ -25,3 +26,7 @@ export interface SearchContextProps {

export const SearchContext = createContext<SearchContextProps | null>(null);
export const SortContext = createContext<LaneSort | null>(null);
export const IntersectionObserverContext = createContext<{
registerHandler: (el: HTMLElement, handler: IntersectionObserverHandler) => void;
unregisterHandler: (el: HTMLElement) => void;
} | null>(null);
1 change: 1 addition & 0 deletions src/styles.less
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 +518,7 @@ div.kanban-plugin__lane-title-count.wip-exceeded {
}

.kanban-plugin__table-cell-wrapper .kanban-plugin__lane-menu {
color: var(--text-faint);
margin-inline-start: 2px;
margin-inline-end: 0px;
}
Expand Down

0 comments on commit cae948d

Please sign in to comment.