@1771technologies/lytenyte-pro 1.0.12 → 1.0.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/dist/+types.d.ts +21 -2
  2. package/dist/__play__/grid-navigation/cell-spans-large.play.d.ts +3 -0
  3. package/dist/__play__/grid-navigation/cell-spans-large.play.js +26 -0
  4. package/dist/__play__/grid-navigation/cell-spans-with-pins.play.d.ts +3 -0
  5. package/dist/__play__/grid-navigation/cell-spans-with-pins.play.js +27 -0
  6. package/dist/__play__/grid-navigation/cell-spans.play.d.ts +3 -0
  7. package/dist/__play__/grid-navigation/cell-spans.play.js +52 -0
  8. package/dist/__play__/grid-navigation/cell-with-tabbables.play.d.ts +3 -0
  9. package/dist/__play__/grid-navigation/cell-with-tabbables.play.js +47 -0
  10. package/dist/__play__/grid-navigation/column-groups.play.d.ts +3 -0
  11. package/dist/__play__/grid-navigation/column-groups.play.js +24 -0
  12. package/dist/__play__/grid-navigation/full-width-rows.play.d.ts +6 -0
  13. package/dist/__play__/grid-navigation/full-width-rows.play.js +67 -0
  14. package/dist/__play__/grid-navigation/horizontal-navigation.pt.d.ts +1 -0
  15. package/dist/__play__/grid-navigation/horizontal-navigation.pt.js +877 -0
  16. package/dist/__play__/grid-navigation/normal-layout.play.d.ts +10 -0
  17. package/dist/__play__/grid-navigation/normal-layout.play.js +72 -0
  18. package/dist/__play__/grid-navigation/pinned-gap-layout.play.d.ts +3 -0
  19. package/dist/__play__/grid-navigation/pinned-gap-layout.play.js +11 -0
  20. package/dist/__play__/grid-navigation/row-detail-with-spans.play.d.ts +6 -0
  21. package/dist/__play__/grid-navigation/row-detail-with-spans.play.js +79 -0
  22. package/dist/__play__/grid-navigation/row-detail.play.d.ts +6 -0
  23. package/dist/__play__/grid-navigation/row-detail.play.js +78 -0
  24. package/dist/__play__/grid-navigation/row-detail.pt.d.ts +1 -0
  25. package/dist/__play__/grid-navigation/row-detail.pt.js +68 -0
  26. package/dist/__play__/grid-navigation/vertical-navigation.pt.d.ts +1 -0
  27. package/dist/__play__/grid-navigation/vertical-navigation.pt.js +176 -0
  28. package/dist/__play__/test-utils/bank-data-smaller.d.ts +19 -0
  29. package/dist/__play__/test-utils/bank-data-smaller.js +252 -0
  30. package/dist/__play__/test-utils/row-handler.d.ts +6 -0
  31. package/dist/__play__/test-utils/row-handler.js +33 -0
  32. package/dist/cells/+types.cell.d.ts +1 -1
  33. package/dist/cells/cell-default.d.ts +1 -1
  34. package/dist/cells/cell-editor.js +19 -12
  35. package/dist/cells/cell-spacer.js +9 -4
  36. package/dist/cells/cell.js +4 -3
  37. package/dist/context.d.ts +3 -3
  38. package/dist/header/use-header-cell-renderer.js +1 -0
  39. package/dist/icons/index.js +1 -3
  40. package/dist/root/root.js +39 -56
  41. package/dist/rows/row/context.d.ts +2 -1
  42. package/dist/rows/row/context.js +1 -0
  43. package/dist/rows/row/row.d.ts +1 -1
  44. package/dist/rows/row/row.js +1 -1
  45. package/dist/rows/row/use-row-context-value.d.ts +2 -2
  46. package/dist/rows/row/use-row-context-value.js +3 -2
  47. package/dist/rows/row-detail-row.js +4 -87
  48. package/dist/rows/row-full-width.js +1 -1
  49. package/dist/state/api/cell-root.js +4 -3
  50. package/dist/state/api/focus-cell.d.ts +1 -1
  51. package/dist/state/api/focus-cell.js +19 -12
  52. package/dist/state/helpers/column-marker.js +2 -0
  53. package/dist/state/helpers/get-span-callback.js +1 -1
  54. package/dist/state/helpers/row-layout/row-layout.js +8 -28
  55. package/dist/state/use-lytenyte.js +3 -1
  56. package/dist/viewport/viewport.js +22 -15
  57. package/package.json +7 -7
@@ -3,7 +3,7 @@ import { useCallback, useEffect, useState } from "react";
3
3
  import { getTabbables } from "@1771technologies/lytenyte-dom-utils";
4
4
  import { useGridRoot } from "../context.js";
5
5
  import { editOnChange } from "../state/helpers/edit-on-change.js";
6
- import { handleNavigationKeys } from "@1771technologies/lytenyte-shared";
6
+ import { handleNavigation } from "@1771technologies/lytenyte-shared";
7
7
  export function CellEditor({ cell }) {
8
8
  const [el, setEl] = useState(null);
9
9
  const grid = useGridRoot().grid;
@@ -41,20 +41,27 @@ export function CellEditor({ cell }) {
41
41
  : Object.keys(editValidation).length >= 1) {
42
42
  return;
43
43
  }
44
- handleNavigationKeys({
45
- ctrlKey: false,
46
- metaKey: false,
47
- key: "ArrowDown",
48
- preventDefault: () => { },
49
- stopPropagation: () => { },
50
- }, {
51
- vp: grid.state.viewport.get(),
44
+ handleNavigation({
45
+ event: {
46
+ key: "ArrowDown",
47
+ ctrlKey: false,
48
+ metaKey: false,
49
+ preventDefault: () => { },
50
+ stopPropagation: () => { },
51
+ },
52
+ topCount: grid.state.rowDataStore.rowTopCount.get(),
53
+ centerCount: grid.state.rowDataStore.rowCenterCount.get(),
54
+ isRowDetailExpanded: (r) => {
55
+ const row = grid.api.rowByIndex(r);
56
+ if (!row)
57
+ return false;
58
+ return grid.api.rowDetailIsExpanded(row);
59
+ },
60
+ viewport: grid.state.viewport.get(),
52
61
  rowCount: ds.rowCount.get(),
53
- topCount: ds.rowTopCount.get(),
54
- centerCount: ds.rowCenterCount.get(),
55
62
  columnCount: grid.state.columnMeta.get().columnsVisible.length,
56
63
  focusActive: grid.internal.focusActive,
57
- id: grid.state.gridId.get(),
64
+ gridId: grid.state.gridId.get(),
58
65
  getRootCell: grid.api.cellRoot,
59
66
  rtl: grid.state.rtl.get(),
60
67
  scrollIntoView: grid.api.scrollIntoView,
@@ -1,12 +1,15 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { useGridRoot } from "../context.js";
3
+ import { useRowMeta } from "../rows/row/context.js";
3
4
  export function CellSpacePinStart({ xPositions: x }) {
5
+ const { layout, colBounds } = useRowMeta();
4
6
  const ctx = useGridRoot().grid;
5
7
  const bounds = ctx.state.viewBounds.useValue();
6
- const offset = x[bounds.colCenterStart] - x[bounds.colStartEnd];
7
8
  const meta = ctx.state.columnMeta.useValue();
8
- if (meta.columnVisibleStartCount === 0)
9
+ if (meta.columnVisibleStartCount === 0 || layout.kind === "full-width")
9
10
  return null;
11
+ const colOffset = layout.cells.findIndex((c) => !c.colPin && c.colIndex + c.colSpan - 1 >= colBounds[0] && !c.isDeadCol);
12
+ const offset = x[colOffset] - x[bounds.colStartEnd];
10
13
  return _jsx("div", { style: { display: "inline-block", width: offset, height: 0 } });
11
14
  }
12
15
  export function CellSpacerPinEnd({ xPositions: x }) {
@@ -24,11 +27,13 @@ export function CellSpacerPinEnd({ xPositions: x }) {
24
27
  return _jsx("div", { style: { display: "inline-block", width: offset, height: 0 } });
25
28
  }
26
29
  export function CellSpacerNoPin({ xPositions: x }) {
30
+ const { layout, colBounds } = useRowMeta();
27
31
  const ctx = useGridRoot().grid;
28
32
  const bounds = ctx.state.viewBounds.useValue();
29
- const offset = x[bounds.colCenterStart - bounds.colStartEnd];
30
33
  const meta = ctx.state.columnMeta.useValue();
31
- if (meta.columnVisibleStartCount > 0)
34
+ if (meta.columnVisibleStartCount > 0 || layout.kind === "full-width")
32
35
  return null;
36
+ const colOffset = layout.cells.findIndex((c) => c.colIndex + c.colSpan - 1 >= colBounds[0] && !c.isDeadCol);
37
+ const offset = x[colOffset - bounds.colStartEnd];
33
38
  return _jsx("div", { style: { display: "inline-block", width: offset, height: 0 } });
34
39
  }
@@ -10,12 +10,13 @@ import { useCellStyle } from "./use-cell-style.js";
10
10
  export const Cell = forwardRef(function Cell(props, forwarded) {
11
11
  const { colBounds: [start, end], ...rowMeta } = useRowMeta();
12
12
  // This enforces our column virtualization.
13
- if (props.cell.colPin == null && (props.cell.colIndex >= end || props.cell.colIndex < start)) {
13
+ if (props.cell.colPin == null &&
14
+ (props.cell.colIndex >= end || props.cell.colIndex + props.cell.colSpan - 1 < start)) {
14
15
  return null;
15
16
  }
16
17
  return _jsx(CellImpl, { ...props, ref: forwarded, ...rowMeta });
17
18
  });
18
- const CellImpl = memo(forwardRef(function Cell({ cell, row, selected, indeterminate, xPositions, yPositions, base, renderers, rtl, ...props }, forwarded) {
19
+ const CellImpl = memo(forwardRef(function Cell({ cell, row, selected, indeterminate, xPositions, yPositions, base, renderers, rtl, layout: _, ...props }, forwarded) {
19
20
  const grid = useGridRoot().grid;
20
21
  const providedRenderer = cell.column.cellRenderer ?? base.cellRenderer;
21
22
  const Renderer = providedRenderer
@@ -39,5 +40,5 @@ const CellImpl = memo(forwardRef(function Cell({ cell, row, selected, indetermin
39
40
  return _jsx("div", { style: { width: sizeFromCoord(cell.colIndex, xPositions) } });
40
41
  if (!row || cell.isDeadCol)
41
42
  return null;
42
- return (_jsxs(_Fragment, { children: [cell.colFirstEndPin && _jsx(CellSpacerPinEnd, { xPositions: xPositions }), _jsxs("div", { ...props, ref: forwarded, role: "gridcell", "data-ln-rowindex": cell.rowIndex, "data-ln-colindex": cell.colIndex, "data-ln-colspan": cell.colSpan, "data-ln-rowspan": cell.rowSpan, "data-ln-pin": cell.colPin ?? "center", "data-ln-cell": true, "data-ln-last-top-pin": cell.rowLastPinTop, "data-ln-first-bottom-pin": cell.rowFirstPinBottom, "data-ln-last-start-pin": cell.colLastStartPin, "data-ln-first-end-pin": cell.colFirstEndPin, tabIndex: isEditing ? -1 : 0, style: style, children: [isEditing && _jsx(CellEditor, { cell: cell }), !isEditing && (_jsx(Renderer, { column: cell.column, rowSelected: selected, rowIndeterminate: indeterminate, row: row, grid: grid, rowIndex: cell.rowIndex, colIndex: cell.colIndex, rowPin: cell.rowPin }))] }), cell.colLastStartPin && _jsx(CellSpacePinStart, { xPositions: xPositions })] }));
43
+ return (_jsxs(_Fragment, { children: [cell.colFirstEndPin && _jsx(CellSpacerPinEnd, { xPositions: xPositions }), _jsxs("div", { ...props, ref: forwarded, role: "gridcell", "data-ln-rowindex": cell.rowIndex, "data-ln-colindex": cell.colIndex, "data-ln-colspan": cell.colSpan, "data-ln-rowspan": cell.rowSpan, "data-ln-pin": cell.colPin ?? "center", "data-ln-cell": true, "data-ln-last-top-pin": cell.rowLastPinTop, "data-ln-first-bottom-pin": cell.rowFirstPinBottom, "data-ln-last-start-pin": cell.colLastStartPin, "data-ln-first-end-pin": cell.colFirstEndPin, tabIndex: isEditing ? -1 : 0, style: { ...style, ...props.style }, children: [isEditing && _jsx(CellEditor, { cell: cell }), !isEditing && (_jsx(Renderer, { column: cell.column, rowSelected: selected, rowIndeterminate: indeterminate, row: row, grid: grid, rowIndex: cell.rowIndex, colIndex: cell.colIndex, rowPin: cell.rowPin }))] }), cell.colLastStartPin && _jsx(CellSpacePinStart, { xPositions: xPositions })] }));
43
44
  }));
package/dist/context.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- import type { Grid } from "./+types";
2
- import type { InternalAtoms } from "./state/+types";
1
+ import type { Grid } from "./+types.js";
2
+ import type { InternalAtoms } from "./state/+types.js";
3
3
  export interface GridRootContext {
4
- readonly ref: (el: HTMLElement | SVGElement | null) => void;
4
+ readonly ref: (el: HTMLElement | null) => void;
5
5
  readonly grid: Grid<any> & {
6
6
  internal: InternalAtoms;
7
7
  };
@@ -1,6 +1,7 @@
1
1
  import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
2
2
  import { useMemo } from "react";
3
3
  import { useGridRoot } from "../context.js";
4
+ // eslint-disable-next-line react-refresh/only-export-components
4
5
  function DefaultRenderer(p) {
5
6
  return _jsx(_Fragment, { children: p.column.name ?? p.column.id });
6
7
  }
@@ -1,3 +1 @@
1
- export { AddIcon, AddSmallIcon, AggregateIcon, AggregationIcon, ArchiveIcon, ArrowDownIcon, ArrowLeftIcon, ArrowRightCurvedIcon, ArrowRightIcon, ArrowUpIcon, AutosizeIcon, BackIcon, BookmarkIcon, BooleanIcon, CalendarIcon, ChartIcon, ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, Close2Icon, CloseIcon, CollapseGroupIcon, Columns2Icon, ColumnsIcon, CopyIcon, CornerRadiusIcon, CustomizeThemeIcon, CutIcon, CsvIcon, DeleteIcon, DocsIcon, DownloadIcon, Drag2Icon, DragDotsIcon, DragDotsSmallIcon, DragIcon, DuplicateIcon, EditIcon, EmailIcon, ExcelIcon, ExpandGroupIcon, ExpandIcon, Expression2Icon, ExpressionIcon, ExternalLinkIcon, File2Icon, FileIcon, FilterIcon, FolderIcon, GlobeIcon, GridViewIcon, GroupByColIcon, HamburgerIcon, HelpIcon, HiddenIcon, HistoryIcon, ImportIcon, LinkIcon, ListViewIcon, LoadingIcon, LocationIcon, ModalIcon, MoreIcon, MoreVerticalIcon, MultilineIcon, NumberIcon, PasteIcon, PieIcon, PinColumnIcon, RefreshIcon, SearchIcon, SettingsIcon, ShareIcon, SingleLineIcon, Sort2Icon, SortIcon, SpacingHIcon, SpacingIcon, StickColumnIcon, SwapIcon, TagIcon, TaskIcon, TextIcon, ThemesIcon, Tickmark2Icon, TickmarkIcon, TimeIcon, UploadIcon, UserIcon, ViewIcon,
2
- // eslint-disable-next-line import/extensions
3
- } from "@1771technologies/lytenyte-core/icons";
1
+ export { AddIcon, AddSmallIcon, AggregateIcon, AggregationIcon, ArchiveIcon, ArrowDownIcon, ArrowLeftIcon, ArrowRightCurvedIcon, ArrowRightIcon, ArrowUpIcon, AutosizeIcon, BackIcon, BookmarkIcon, BooleanIcon, CalendarIcon, ChartIcon, ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, Close2Icon, CloseIcon, CollapseGroupIcon, Columns2Icon, ColumnsIcon, CopyIcon, CornerRadiusIcon, CustomizeThemeIcon, CutIcon, CsvIcon, DeleteIcon, DocsIcon, DownloadIcon, Drag2Icon, DragDotsIcon, DragDotsSmallIcon, DragIcon, DuplicateIcon, EditIcon, EmailIcon, ExcelIcon, ExpandGroupIcon, ExpandIcon, Expression2Icon, ExpressionIcon, ExternalLinkIcon, File2Icon, FileIcon, FilterIcon, FolderIcon, GlobeIcon, GridViewIcon, GroupByColIcon, HamburgerIcon, HelpIcon, HiddenIcon, HistoryIcon, ImportIcon, LinkIcon, ListViewIcon, LoadingIcon, LocationIcon, ModalIcon, MoreIcon, MoreVerticalIcon, MultilineIcon, NumberIcon, PasteIcon, PieIcon, PinColumnIcon, RefreshIcon, SearchIcon, SettingsIcon, ShareIcon, SingleLineIcon, Sort2Icon, SortIcon, SpacingHIcon, SpacingIcon, StickColumnIcon, SwapIcon, TagIcon, TaskIcon, TextIcon, ThemesIcon, Tickmark2Icon, TickmarkIcon, TimeIcon, UploadIcon, UserIcon, ViewIcon, } from "@1771technologies/lytenyte-core/icons";
package/dist/root/root.js CHANGED
@@ -1,6 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useEffect, useLayoutEffect, useMemo } from "react";
3
- import { useEvent, useMeasure } from "@1771technologies/lytenyte-react-hooks";
2
+ import { useEffect, useMemo, useState } from "react";
4
3
  import { RootProvider } from "../context.js";
5
4
  import { DialogDriver } from "./dialog-driver.js";
6
5
  import { PopoverDriver } from "./popover-driver.js";
@@ -33,13 +32,6 @@ export function Root({ grid, children, ...events }) {
33
32
  document.body.appendChild(invalidLicenseWatermark);
34
33
  return () => invalidLicenseWatermark.remove();
35
34
  }, []);
36
- const onViewportChange = useEvent((bounds) => {
37
- grid.state.viewport.set(element ?? null);
38
- grid.state.viewportHeightOuter.set(bounds.height);
39
- grid.state.viewportWidthOuter.set(bounds.width);
40
- grid.state.viewportWidthInner.set(element?.clientWidth ?? 0);
41
- grid.state.viewportHeightInner.set(element?.clientHeight ?? 0);
42
- });
43
35
  // Add event listeners in the standard react way
44
36
  useEffect(() => {
45
37
  const ev = Object.entries(events).map(([onName, fn]) => {
@@ -51,53 +43,7 @@ export function Root({ grid, children, ...events }) {
51
43
  return () => ev.forEach((c) => c?.());
52
44
  // eslint-disable-next-line react-hooks/exhaustive-deps
53
45
  }, [Object.values(events)]);
54
- const [ref, bounds, , element] = useMeasure({
55
- onChange: onViewportChange,
56
- });
57
- // Add css variables so that we can avoid re-renders
58
- useEffect(() => {
59
- return grid.state.widthTotal.watch(() => {
60
- const vp = grid.state.viewport.get();
61
- const width = grid.state.widthTotal.get();
62
- if (!vp)
63
- return;
64
- vp.style.setProperty("--lng-vp-total-width", `${width}px`);
65
- });
66
- }, [grid]);
67
46
  const internal = grid.internal;
68
- // Listen to size changes of the window
69
- useLayoutEffect(() => {
70
- if (!element)
71
- return;
72
- const obs = new ResizeObserver(() => {
73
- grid.state.viewport.set(element ?? null);
74
- grid.state.viewportHeightOuter.set(bounds.height);
75
- grid.state.viewportWidthOuter.set(bounds.width);
76
- grid.state.viewportWidthInner.set(element?.clientWidth ?? 0);
77
- grid.state.viewportHeightInner.set(element?.clientHeight ?? 0);
78
- });
79
- obs.observe(element);
80
- const controller = new AbortController();
81
- element.addEventListener("scroll", () => {
82
- internal.xScroll.set(Math.abs(element.scrollLeft));
83
- internal.yScroll.set(element.scrollTop);
84
- }, { signal: controller.signal });
85
- return () => {
86
- obs.disconnect();
87
- controller.abort();
88
- };
89
- }, [
90
- bounds.height,
91
- bounds.width,
92
- element,
93
- grid.state.viewport,
94
- grid.state.viewportHeightInner,
95
- grid.state.viewportHeightOuter,
96
- grid.state.viewportWidthInner,
97
- grid.state.viewportWidthOuter,
98
- internal.xScroll,
99
- internal.yScroll,
100
- ]);
101
47
  useEffect(() => {
102
48
  return internal.focusActive.watch(() => {
103
49
  const editActive = internal.editActivePos.get();
@@ -114,12 +60,49 @@ export function Root({ grid, children, ...events }) {
114
60
  }
115
61
  });
116
62
  }, [grid.api, internal.editActivePos, internal.focusActive]);
63
+ const [vp, ref] = useState(null);
64
+ useEffect(() => {
65
+ if (!vp)
66
+ return;
67
+ const controller = new AbortController();
68
+ vp.addEventListener("scroll", () => {
69
+ internal.xScroll.set(Math.abs(vp.scrollLeft));
70
+ internal.yScroll.set(vp.scrollTop);
71
+ }, { signal: controller.signal });
72
+ return () => controller.abort();
73
+ }, [internal.xScroll, internal.yScroll, vp]);
74
+ useEffect(() => {
75
+ grid.state.viewport.set(vp);
76
+ if (!vp)
77
+ return;
78
+ const obs = new ResizeObserver(() => {
79
+ grid.state.viewportHeightOuter.set(vp.offsetHeight);
80
+ grid.state.viewportWidthOuter.set(vp.offsetWidth);
81
+ grid.state.viewportWidthInner.set(vp.clientWidth);
82
+ grid.state.viewportHeightInner.set(vp.clientHeight);
83
+ });
84
+ grid.state.viewportHeightOuter.set(vp.offsetHeight);
85
+ grid.state.viewportWidthOuter.set(vp.offsetWidth);
86
+ grid.state.viewportWidthInner.set(vp.clientWidth);
87
+ grid.state.viewportHeightInner.set(vp.clientHeight);
88
+ obs.observe(vp);
89
+ return () => {
90
+ obs.disconnect();
91
+ };
92
+ }, [
93
+ grid.state.viewport,
94
+ grid.state.viewportHeightInner,
95
+ grid.state.viewportHeightOuter,
96
+ grid.state.viewportWidthInner,
97
+ grid.state.viewportWidthOuter,
98
+ vp,
99
+ ]);
117
100
  const value = useMemo(() => {
118
101
  return {
119
102
  ref,
120
103
  grid: grid,
121
104
  };
122
- }, [grid, ref]);
105
+ }, [grid]);
123
106
  const cellSelectionMode = grid.state.cellSelectionMode.useValue();
124
107
  return (_jsxs(RootProvider, { value: value, children: [_jsx(DialogDriver, {}), _jsx(PopoverDriver, {}), cellSelectionMode !== "none" && _jsx(CellSelectionDriver, {}), children] }));
125
108
  }
@@ -1,9 +1,10 @@
1
- import type { CellRendererFn, ColumnBase, RowNode } from "../../+types";
1
+ import type { CellRendererFn, ColumnBase, RowLayout, RowNode } from "../../+types";
2
2
  export interface RowMetaData {
3
3
  readonly selected: boolean;
4
4
  readonly indeterminate: boolean;
5
5
  readonly colBounds: [number, number];
6
6
  readonly row: RowNode<any> | null;
7
+ readonly layout: RowLayout<any>;
7
8
  readonly xPositions: Uint32Array;
8
9
  readonly yPositions: Uint32Array;
9
10
  readonly base: ColumnBase<any>;
@@ -4,6 +4,7 @@ export const RowContext = createContext({
4
4
  indeterminate: false,
5
5
  colBounds: [0, 0],
6
6
  row: null,
7
+ layout: null,
7
8
  xPositions: new Uint32Array(),
8
9
  yPositions: new Uint32Array(),
9
10
  base: null,
@@ -1,4 +1,4 @@
1
- import type { RowNormalRowLayout } from "../../+types.js";
1
+ import type { RowNormalRowLayout } from "../../+types";
2
2
  import { type DropWrapProps } from "@1771technologies/lytenyte-shared";
3
3
  export interface RowProps extends Omit<DropWrapProps, "accepted"> {
4
4
  readonly row: RowNormalRowLayout<any>;
@@ -11,7 +11,7 @@ const empty = [];
11
11
  const RowImpl = forwardRef(function Rows({ row, ...props }, forwarded) {
12
12
  const ctx = useGridRoot().grid;
13
13
  const yPos = ctx.state.yPositions.useValue();
14
- const rowMeta = useRowContextValue(ctx, row.row, yPos);
14
+ const rowMeta = useRowContextValue(ctx, row, yPos);
15
15
  const hasSpans = ctx.internal.hasSpans.useValue();
16
16
  const accepted = props.accepted ?? empty;
17
17
  return (_jsx(RowContext.Provider, { value: rowMeta, children: _jsxs(RowReact, { ...props, ref: forwarded, accepted: accepted, gridId: ctx.state.gridId.useValue(), rowIndex: row.rowIndex, rowFirstPinBottom: row.rowFirstPinBottom, rowLastPinTop: row.rowLastPinTop, rowIsFocusRow: row.rowIsFocusRow ?? false, rowPin: row.rowPin, topOffset: ctx.view.useValue().rows.rowTopTotalHeight, yPositions: yPos, hasSpans: hasSpans, "data-ln-row-selected": rowMeta.selected, children: [_jsx(CellSpacerNoPin, { xPositions: rowMeta.xPositions }), props.children, _jsx(RowDetailRow, { layout: row })] }) }));
@@ -1,6 +1,6 @@
1
- import type { Grid, GridAtomReadonlyUnwatchable, RowNode } from "../../+types.js";
1
+ import type { Grid, RowLayout } from "../../+types.js";
2
2
  import type { InternalAtoms } from "../../state/+types.js";
3
3
  import type { RowMetaData } from "./context.js";
4
4
  export declare function useRowContextValue(grid: Grid<any> & {
5
5
  internal: InternalAtoms;
6
- }, row: GridAtomReadonlyUnwatchable<RowNode<any> | null>, yPositions: Uint32Array): RowMetaData;
6
+ }, row: RowLayout<any>, yPositions: Uint32Array): RowMetaData;
@@ -1,6 +1,6 @@
1
1
  import { useEffect, useMemo, useState } from "react";
2
2
  export function useRowContextValue(grid, row, yPositions) {
3
- const r = row.useValue();
3
+ const r = row.row.useValue();
4
4
  const xPositions = grid.state.xPositions.useValue();
5
5
  const [indeterminate, setIndeterminate] = useState(false);
6
6
  const [selected, setSelected] = useState(false);
@@ -33,12 +33,13 @@ export function useRowContextValue(grid, row, yPositions) {
33
33
  indeterminate,
34
34
  colBounds,
35
35
  row: r,
36
+ layout: row,
36
37
  xPositions,
37
38
  yPositions,
38
39
  rtl,
39
40
  base,
40
41
  renderers,
41
42
  };
42
- }, [base, colBounds, indeterminate, r, renderers, rtl, selected, xPositions, yPositions]);
43
+ }, [base, colBounds, indeterminate, r, renderers, row, rtl, selected, xPositions, yPositions]);
43
44
  return value;
44
45
  }
@@ -1,8 +1,7 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { getNearestRow, getRowIndexFromEl, SCROLL_WIDTH_VARIABLE_USE, sizeFromCoord, } from "@1771technologies/lytenyte-shared";
2
+ import { SCROLL_WIDTH_VARIABLE_USE, sizeFromCoord } from "@1771technologies/lytenyte-shared";
3
3
  import { useGridRoot } from "../context.js";
4
4
  import { useEffect, useState } from "react";
5
- import { getTabbables, isHTMLElement } from "@1771technologies/lytenyte-dom-utils";
6
5
  export function RowDetailRow({ layout, }) {
7
6
  const cx = useGridRoot();
8
7
  const row = layout.row.useValue();
@@ -17,89 +16,6 @@ function RowDetailImpl({ row, rowIndex }) {
17
16
  const rowHeight = sizeFromCoord(rowIndex, cx.grid.state.yPositions.get()) - height;
18
17
  const Renderer = cx.grid.state.rowDetailRenderer.useValue().fn;
19
18
  const [ref, setRef] = useState(null);
20
- useEffect(() => {
21
- const parent = ref?.parentElement;
22
- const first = ref?.firstElementChild;
23
- if (!parent || !first)
24
- return;
25
- const controller = new AbortController();
26
- let focusTime = false;
27
- const vp = cx.grid.state.viewport.get();
28
- vp?.addEventListener("keydown", () => {
29
- focusTime = true;
30
- setTimeout(() => {
31
- focusTime = false;
32
- }, 20);
33
- }, { capture: true, signal: controller.signal });
34
- parent.addEventListener("focusin", (e) => {
35
- if (focusTime && e.relatedTarget && isHTMLElement(e.relatedTarget)) {
36
- const row = getNearestRow(e.relatedTarget);
37
- const gridId = cx.grid.state.gridId.get();
38
- if (gridId !== row?.getAttribute("data-ln-gridid"))
39
- return;
40
- const prevIndex = getRowIndexFromEl(row);
41
- if (prevIndex - 1 === rowIndex) {
42
- first.focus();
43
- }
44
- }
45
- }, { signal: controller.signal });
46
- parent.addEventListener("keydown", (e) => {
47
- if (e.key === "ArrowDown" && document.activeElement !== first) {
48
- e.stopPropagation();
49
- e.preventDefault();
50
- first.focus();
51
- }
52
- }, { signal: controller.signal });
53
- first.addEventListener("keydown", (e) => {
54
- const next = rtl ? "ArrowLeft" : "ArrowRight";
55
- const prev = rtl ? "ArrowRight" : "ArrowLeft";
56
- if (e.key === "Escape") {
57
- first.focus();
58
- return;
59
- }
60
- if (e.key === "ArrowUp") {
61
- e.stopPropagation();
62
- e.preventDefault();
63
- cx.grid.api.focusCell({
64
- row: rowIndex,
65
- column: cx.grid.internal.focusActive.get()?.colIndex ?? 0,
66
- });
67
- }
68
- if (e.key === next) {
69
- e.preventDefault();
70
- e.stopPropagation();
71
- const focusables = getTabbables(first);
72
- const index = focusables.indexOf(document.activeElement);
73
- if (index === -1)
74
- focusables.at(0)?.focus();
75
- else
76
- focusables.at(index + 1)?.focus();
77
- }
78
- if (e.key === prev) {
79
- e.preventDefault();
80
- e.stopPropagation();
81
- const focusables = getTabbables(first);
82
- const index = focusables.indexOf(document.activeElement);
83
- if (index === -1)
84
- focusables.at(-1)?.focus();
85
- else
86
- focusables[index - 1]?.focus();
87
- }
88
- }, { signal: controller.signal });
89
- return () => {
90
- controller.abort();
91
- };
92
- }, [
93
- cx.grid.api,
94
- cx.grid.internal.focusActive,
95
- cx.grid.internal.focusPrevRowIndex,
96
- cx.grid.state.gridId,
97
- cx.grid.state.rtl,
98
- cx.grid.state.viewport,
99
- ref,
100
- rowIndex,
101
- rtl,
102
- ]);
103
19
  useEffect(() => {
104
20
  const first = ref?.firstElementChild;
105
21
  if (!first)
@@ -115,12 +31,13 @@ function RowDetailImpl({ row, rowIndex }) {
115
31
  }, [cx.grid.internal.rowDetailAutoHeightCache, ref?.firstElementChild, rowIndex]);
116
32
  const isAuto = cx.grid.state.rowDetailHeight.useValue() === "auto";
117
33
  return (_jsx("div", { ref: setRef, role: "gridcell", style: {
118
- pointerEvents: "all",
34
+ pointerEvents: "none",
119
35
  position: "absolute",
120
36
  left: 0,
121
37
  width: SCROLL_WIDTH_VARIABLE_USE,
122
- }, children: _jsx("div", { tabIndex: -1, "data-ln-row-detail": true, style: {
38
+ }, children: _jsx("div", { tabIndex: 0, "data-ln-gridid": cx.grid.state.gridId.get(), "data-ln-row-detail": true, "data-ln-rowindex": rowIndex, style: {
123
39
  position: "sticky",
40
+ pointerEvents: "all",
124
41
  right: rtl ? "0px" : undefined,
125
42
  left: rtl ? undefined : "0px",
126
43
  marginTop: rowHeight,
@@ -12,7 +12,7 @@ const RowFullWidthImpl = forwardRef(function RowFullWidth({ row: layout, space,
12
12
  const row = layout.row.useValue();
13
13
  const yPositions = grid.state.yPositions.useValue();
14
14
  const hasSpans = grid.internal.hasSpans.useValue();
15
- const meta = useRowContextValue(grid, layout.row, yPositions);
15
+ const meta = useRowContextValue(grid, layout, yPositions);
16
16
  return (_jsx(RowFullWidthReact, { ...props, ref: forwarded, accepted: props.accepted ?? empty, detail: _jsx(RowDetailRow, { layout: layout }), detailHeight: grid.api.rowDetailRenderedHeight(row ?? ""), gridId: grid.state.gridId.useValue(), rtl: meta.rtl, hasSpans: hasSpans, rowFirstPinBottom: layout.rowFirstPinBottom, rowLastPinTop: layout.rowLastPinTop, rowIndex: layout.rowIndex, rowIsFocusRow: layout.rowIsFocusRow ?? false, rowPin: layout.rowPin, topOffset: grid.view.useValue().rows.rowTopTotalHeight, yPositions: yPositions, space: space, "data-ln-row-selected": meta.selected, children: children ??
17
17
  (row ? (_jsx(Renderer, { grid: grid, row: row, rowIndex: layout.rowIndex, rowSelected: meta.selected, rowIndeterminate: meta.indeterminate })) : null) }));
18
18
  });
@@ -45,13 +45,14 @@ export const makeCellRoot = (grid) => {
45
45
  if (status === CONTAINS_DEAD_CELLS) {
46
46
  const spec = l.lookup.get(row);
47
47
  // This cell is not covered
48
- if (!spec || spec[column * 4] !== 0) {
48
+ if (!spec || spec[column * 4] > 0) {
49
49
  return { kind: "cell", rowIndex: row, colIndex: column, root: null };
50
50
  }
51
- const rowSpan = spec[column * 4];
52
- const colSpan = spec[column * 4 + 1];
53
51
  const rowIndex = spec[column * 4 + 2];
54
52
  const colIndex = spec[column * 4 + 3];
53
+ const rootSpec = l.lookup.get(rowIndex);
54
+ const rowSpan = rootSpec[colIndex * 4];
55
+ const colSpan = rootSpec[colIndex * 4 + 1];
55
56
  return {
56
57
  kind: "cell",
57
58
  rowIndex: row,
@@ -1,5 +1,5 @@
1
1
  import type { Grid, GridApi } from "../../+types";
2
- import type { InternalAtoms } from "../+types.js";
2
+ import type { InternalAtoms } from "../+types";
3
3
  export declare const makeFocusCell: (grid: Grid<any> & {
4
4
  internal: InternalAtoms;
5
5
  }) => GridApi<any>["focusCell"];
@@ -1,4 +1,4 @@
1
- import { focusCell, getHeaderRows, handleNavigationKeys, isColumnFloatingHeader, } from "@1771technologies/lytenyte-shared";
1
+ import { focusCell, getHeaderRows, handleNavigation, isColumnFloatingHeader, } from "@1771technologies/lytenyte-shared";
2
2
  import { clamp } from "@1771technologies/lytenyte-js-utils";
3
3
  export const makeFocusCell = (grid) => {
4
4
  return (position) => {
@@ -18,20 +18,27 @@ export const makeFocusCell = (grid) => {
18
18
  if (!key)
19
19
  return false;
20
20
  const ds = grid.state.rowDataStore;
21
- handleNavigationKeys({
22
- key,
23
- ctrlKey: false,
24
- metaKey: false,
25
- preventDefault: () => { },
26
- stopPropagation: () => { },
27
- }, {
28
- vp,
21
+ handleNavigation({
22
+ event: {
23
+ key,
24
+ ctrlKey: false,
25
+ metaKey: false,
26
+ preventDefault: () => { },
27
+ stopPropagation: () => { },
28
+ },
29
+ topCount: grid.state.rowDataStore.rowTopCount.get(),
30
+ centerCount: grid.state.rowDataStore.rowCenterCount.get(),
31
+ isRowDetailExpanded: (r) => {
32
+ const row = grid.api.rowByIndex(r);
33
+ if (!row)
34
+ return false;
35
+ return grid.api.rowDetailIsExpanded(row);
36
+ },
37
+ viewport: vp,
29
38
  rowCount: ds.rowCount.get(),
30
- topCount: ds.rowTopCount.get(),
31
- centerCount: ds.rowCenterCount.get(),
32
39
  columnCount: grid.state.columnMeta.get().columnsVisible.length,
33
40
  focusActive: grid.internal.focusActive,
34
- id: grid.state.gridId.get(),
41
+ gridId: grid.state.gridId.get(),
35
42
  getRootCell: grid.api.cellRoot,
36
43
  rtl: grid.state.rtl.get(),
37
44
  scrollIntoView: grid.api.scrollIntoView,
@@ -35,9 +35,11 @@ export function columnHandleMarker({ columns, marker, markerEnabled, }) {
35
35
  }
36
36
  return columns;
37
37
  }
38
+ // eslint-disable-next-line react-refresh/only-export-components
38
39
  function HeaderRenderer() {
39
40
  return (_jsx(_Fragment, { children: _jsx("div", { style: { overflow: "hidden", opacity: 0, width: "100%", height: "100%" }, children: "Marker column default" }) }));
40
41
  }
42
+ // eslint-disable-next-line react-refresh/only-export-components
41
43
  function FloatingRenderer() {
42
44
  return _jsx(_Fragment, {});
43
45
  }
@@ -11,7 +11,7 @@ export function getSpanFn(rds, grid, visibleColumns, span) {
11
11
  return 1;
12
12
  if (typeof spanFn === "number") {
13
13
  if (span === "col") {
14
- return c % spanFn === 0 ? spanFn : 1;
14
+ return spanFn;
15
15
  }
16
16
  else {
17
17
  return r % spanFn === 0 ? spanFn : 1;
@@ -44,31 +44,15 @@ export function makeRowLayout({ view: n, viewCache, layout, rds, columns }) {
44
44
  * BOTTOM ROW LAYOUT END
45
45
  */
46
46
  return {
47
- top: top.map((t) => partition(n, t)),
48
- center: center.map((t) => partition(n, t)),
49
- bottom: bottom.map((t) => partition(n, t)),
47
+ top: top,
48
+ center: center,
49
+ bottom: bottom,
50
50
  };
51
51
  }
52
- const partition = (n, row) => {
53
- if (row.kind === "full-width")
54
- return row;
55
- let found = false;
56
- const cells = [
57
- ...row.cells.slice(0, n.colStartEnd),
58
- ...row.cells.slice(n.colCenterStart, n.colCenterEnd).map((c) => {
59
- if (!found && c.isDeadCol && !c.isDeadRow)
60
- return { ...c, isDeadCol: false, isDeadRow: true };
61
- found = true;
62
- return c;
63
- }),
64
- ...row.cells.slice(n.colEndStart, n.colEndEnd),
65
- ];
66
- return {
67
- ...row,
68
- cells,
69
- };
70
- };
71
52
  function handleViewLayout({ columns, spanLayout: n, rowStart, rowEnd, rowPin, layout, viewCache, container, rowForIndex, }) {
53
+ /**
54
+ * TOP ROW LAYOUT START
55
+ */
72
56
  for (let r = rowStart; r < rowEnd; r++) {
73
57
  const status = layout.special[r];
74
58
  const computed = layout.computed[r];
@@ -84,9 +68,7 @@ function handleViewLayout({ columns, spanLayout: n, rowStart, rowEnd, rowPin, la
84
68
  const rowLastPinTop = n.rowTopEnd - 1 === r ? true : undefined;
85
69
  if (status === FULL_WIDTH) {
86
70
  const row = {
87
- get id() {
88
- return node.get()?.id ?? `${r}`;
89
- },
71
+ id: node.get()?.id ?? `${r}`,
90
72
  rowIndex: r,
91
73
  kind: "full-width",
92
74
  rowPin,
@@ -165,9 +147,7 @@ function handleViewLayout({ columns, spanLayout: n, rowStart, rowEnd, rowPin, la
165
147
  });
166
148
  }
167
149
  const row = {
168
- get id() {
169
- return node.get()?.id ?? `${r}`;
170
- },
150
+ id: node.get()?.id ?? `${r}`,
171
151
  rowIndex: r,
172
152
  kind: "row",
173
153
  cells: cellLayout,
@@ -194,6 +194,7 @@ export function makeLyteNyte(p) {
194
194
  rowDataStore.rowCenterCount.$();
195
195
  rowDataStore.rowTopCount.$();
196
196
  rowDataSource();
197
+ rowDetailExpansions();
197
198
  rowGroupModel();
198
199
  sortModel();
199
200
  filterModel();
@@ -430,6 +431,7 @@ export function makeLyteNyte(p) {
430
431
  const topCount = rowDataStore.rowTopCount.$();
431
432
  const botCount = rowDataStore.rowBottomCount.$();
432
433
  const rowCount = rowDataStore.rowCount.$();
434
+ const expansions = rowDetailExpansions();
433
435
  const fullWidthPredicate = rowFullWidthPredicate().fn;
434
436
  updateFull({
435
437
  topCount,
@@ -442,7 +444,7 @@ export function makeLyteNyte(p) {
442
444
  isFullWidth: fullWidthPredicate ? getFullWidthCallback(rds, fullWidthPredicate, grid) : null,
443
445
  isRowCutoff: (r) => {
444
446
  const row = rds.rowByIndex(r);
445
- return !row || row.kind === "branch";
447
+ return !row || row.kind === "branch" || expansions.has(row.id);
446
448
  },
447
449
  rowScanDistance: rowScan,
448
450
  rowStart: n.rowCenterStart,