Skip to content

Commit f91349d

Browse files
authored
add drag to resize, save widths in options node (#461)
* add drag to resize, save widths in options node * handle plain setting * set column width on drag * th * 1.5.0
1 parent a9832cb commit f91349d

File tree

3 files changed

+143
-21
lines changed

3 files changed

+143
-21
lines changed

package-lock.json

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
"@types/mozilla-readability": "^0.2.0",
2424
"@types/turndown": "^5.0.1"
2525
},
26-
"version": "1.4.1",
26+
"version": "1.5.0",
2727
"samepage": {
2828
"extends": "node_modules/roamjs-components/package.json"
2929
}

src/features/table.tsx

+140-18
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
1-
import React, { useEffect, useMemo, useRef, useState } from "react";
1+
import React, {
2+
useCallback,
3+
useEffect,
4+
useMemo,
5+
useRef,
6+
useState,
7+
DragEvent,
8+
} from "react";
29
import {
310
HTMLTable,
411
EditableText,
@@ -79,6 +86,11 @@ const getSettings = (blockUid: string) => {
7986
(c) => c.text.toLowerCase() === "interactive"
8087
),
8188
};
89+
const widths = getSubTree({
90+
tree: optionsNode.children,
91+
key: "widths",
92+
parentUid: optionsNode.uid,
93+
}).children.map((c) => c.text);
8294

8395
// fold {{wb-table}} block
8496
if (!isLoaded)
@@ -96,6 +108,7 @@ const getSettings = (blockUid: string) => {
96108
styles,
97109
viewNode,
98110
view,
111+
widths,
99112
};
100113
};
101114
const Configuration = ({ blockUid, onSubmit }: ConfigurationProps) => {
@@ -264,11 +277,37 @@ const Configuration = ({ blockUid, onSubmit }: ConfigurationProps) => {
264277
</div>
265278
);
266279
};
280+
281+
const dragImage = document.createElement("img");
282+
dragImage.src =
283+
"data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
284+
267285
const DisplayTable = ({ blockUid, setIsEdit }: DisplayTableProps) => {
268286
const [settings, setSettings] = useState(() => getSettings(blockUid));
269287
const [isMenuOpen, setIsMenuOpen] = useState(false);
270288
const containerRef = useRef<HTMLDivElement>(null);
271289

290+
const columnWidths = useMemo(() => {
291+
const widths =
292+
typeof settings.widths === "string"
293+
? [settings.widths]
294+
: settings.widths || [];
295+
return Object.fromEntries(
296+
widths
297+
.map((w) => {
298+
const match = /^(.*) - ([^-]+)$/.exec(w);
299+
return match;
300+
})
301+
.filter((m): m is RegExpExecArray => !!m)
302+
.map((match) => {
303+
return [match[1], match[2]];
304+
})
305+
);
306+
}, [settings]);
307+
308+
const rows = settings.rowsNode.children;
309+
const headers = settings.headerNode.children;
310+
272311
const updateText = (uid: string, value: string) => {
273312
updateBlock({ uid, text: value });
274313
};
@@ -411,6 +450,61 @@ const DisplayTable = ({ blockUid, setIsEdit }: DisplayTableProps) => {
411450
/>
412451
);
413452
};
453+
454+
const [thRefs, setThRefs] = useState<
455+
React.RefObject<HTMLTableHeaderCellElement>[]
456+
>([]);
457+
useEffect(() => {
458+
setThRefs(
459+
headers.map((_) => React.createRef<HTMLTableHeaderCellElement>())
460+
);
461+
}, [headers]);
462+
const trRef = useRef<HTMLTableRowElement>(null);
463+
464+
const onDragStart = useCallback(
465+
(e) => {
466+
e.dataTransfer.setDragImage(dragImage, 0, 0);
467+
},
468+
[dragImage]
469+
);
470+
471+
const dragHandler = useCallback(
472+
(e: DragEvent<HTMLDivElement>) => {
473+
const delta = e.clientX - e.currentTarget.getBoundingClientRect().left;
474+
const cellWidth =
475+
e.currentTarget.parentElement?.getBoundingClientRect().width;
476+
if (typeof cellWidth === "undefined") return;
477+
if (cellWidth + delta <= 0) return;
478+
const rowWidth =
479+
e.currentTarget.parentElement?.parentElement?.getBoundingClientRect()
480+
.width;
481+
if (typeof rowWidth === "undefined") return;
482+
if (cellWidth + delta >= rowWidth) return;
483+
const column = e.currentTarget.getAttribute("data-column");
484+
const save = e.type === "dragend";
485+
if (trRef.current) {
486+
trRef.current.style.cursor = save ? "" : "ew-resize"; // Not working.
487+
}
488+
const newWidth = `${((cellWidth + delta) / rowWidth) * 100}%`;
489+
if (!column) return;
490+
const columnIndex = parseInt(column.split("-")[1]) - 1;
491+
const th = thRefs[columnIndex]?.current;
492+
if (!th) return;
493+
th.style.width = newWidth;
494+
if (!save) return;
495+
th.style.width = newWidth;
496+
setInputSettings({
497+
blockUid: settings.optionsNode.uid,
498+
key: "widths",
499+
values: thRefs
500+
.map((ref, index) => [index.toString(), ref.current?.style.width])
501+
.filter(([index, width]) => width)
502+
.map(([index, width]) => `${index} - ${width}`),
503+
});
504+
},
505+
[settings.optionsNode.uid, trRef, thRefs]
506+
);
507+
414508
return (
415509
<div className="relative" ref={containerRef}>
416510
<span className="absolute top-1 right-0">
@@ -427,46 +521,74 @@ const DisplayTable = ({ blockUid, setIsEdit }: DisplayTableProps) => {
427521
>
428522
<thead>
429523
<tr>
430-
{!!settings.headerNode.children.length &&
431-
settings.headerNode.children.map((c) => (
524+
{!!headers &&
525+
headers.map((header, i) => (
432526
<th
433-
key={c.uid}
434-
className={`wbt-header-${sanitizeClassName(c.text)}`}
527+
ref={thRefs[i]}
528+
key={header.uid}
529+
className={`wbt-header-${sanitizeClassName(
530+
header.text
531+
)} overflow-hidden`}
532+
style={{ width: columnWidths[i] }}
435533
>
436534
{settings.view === "embed" ? (
437-
<CellEmbed uid={c.uid} />
535+
<CellEmbed uid={header.uid} />
438536
) : (
439537
<EditableText
440538
placeholder=""
441-
defaultValue={c.text}
442-
onConfirm={(value) => updateText(c.uid, value)}
539+
defaultValue={header.text}
540+
onConfirm={(value) => updateText(header.uid, value)}
443541
/>
444542
)}
445543
</th>
446544
))}
447545
</tr>
448546
</thead>
449547
<tbody>
450-
{!!settings.rowsNode.children.length &&
451-
settings.rowsNode.children.map((c) => (
548+
{!!rows.length &&
549+
rows.map((row, i) => (
452550
<tr
453-
key={c.uid}
454-
className={`wbt-row-${sanitizeClassName(c.text)} w-full`}
551+
ref={trRef}
552+
key={row.uid}
553+
className={`wbt-row-${sanitizeClassName(row.text)} w-full`}
554+
data-column={`column-${i + 1}`}
455555
>
456-
{c.children.map((c) => (
457-
<td key={c.uid} className="overflow-hidden">
556+
{row.children.map((cell, i) => (
557+
<td key={cell.uid} className="overflow-hidden relative">
458558
{settings.view === "embed" ? (
459-
<CellEmbed uid={c.uid} />
559+
<CellEmbed uid={cell.uid} />
460560
) : (
461561
<EditableText
462562
placeholder=""
463-
defaultValue={c.text}
464-
onConfirm={(value) => updateText(c.uid, value)}
563+
defaultValue={cell.text}
564+
onConfirm={(value) => updateText(cell.uid, value)}
465565
className={`wbt-cell-${sanitizeClassName(
466-
c.text
566+
cell.text
467567
)} w-full`}
468568
/>
469569
)}
570+
<>
571+
{i < row.children.length - 1 && (
572+
<div
573+
style={{
574+
width: 11,
575+
cursor: "ew-resize",
576+
position: "absolute",
577+
top: 0,
578+
right: 0,
579+
bottom: 0,
580+
borderRight: `1px solid rgba(16,22,26,0.15)`,
581+
paddingLeft: 5,
582+
pointerEvents: "auto",
583+
}}
584+
data-column={`column-${i + 1}`}
585+
draggable
586+
onDragStart={onDragStart}
587+
onDrag={dragHandler}
588+
onDragEnd={dragHandler}
589+
/>
590+
)}
591+
</>
470592
</td>
471593
))}
472594
</tr>

0 commit comments

Comments
 (0)