Skip to content

Commit 94393f7

Browse files
Merge pull request #453 from 1771-Technologies/lb/skills-docs-enhancement-1
Skills Docs Enhancement Cells and Cell Editing
2 parents 4d39415 + 34b773c commit 94393f7

2 files changed

Lines changed: 60 additions & 37 deletions

File tree

skills/lytenyte-grid/refs/cell-editing.md

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,17 @@ Make individual columns editable with `editable` and `editRenderer`:
2323

2424
## Edit Renderers
2525

26-
An edit renderer is a **standard React component** — LyteNyte Grid provides the editing state and wiring props (`editValue`, `changeValue`, `commit`, `cancel`), and you bring whatever input or UI element you need. This means you can use:
26+
An edit renderer is a **standard React component** — LyteNyte Grid provides the editing state and wiring props
27+
(`editValue`, `changeValue`, `commit`, `cancel`), and you bring whatever input or
28+
UI element you need. This means you can use:
2729

2830
- Native HTML elements: `<input type="text">`, `<input type="number">`, `<input type="date">`, `<select>`, `<textarea>`
2931
- Existing form components already in the project (e.g. a `NumberInput` or `DatePicker` the user already has)
3032
- Third-party libraries: React Select, Radix UI, shadcn/ui inputs, etc.
3133
- Custom components built from scratch
3234

33-
**The pattern is always the same:** read `editValue` for the initial value, call `changeValue(newVal)` when the value changes. LyteNyte Grid handles the rest.
35+
**The pattern is always the same:** read `editValue` for the initial value, call `changeValue(newVal)` when the value changes.
36+
LyteNyte Grid handles the rest.
3437

3538
```tsx
3639
// Text editor — native input
@@ -111,7 +114,8 @@ function PriceEditor({ editValue, changeValue }: Grid.T.EditParams<GridSpec>) {
111114

112115
### Popover Editors
113116

114-
Use a React portal to avoid grid cell clipping. Disable `editOnPrintable` if your component uses character keys:
117+
- Use a React portal to avoid grid cell clipping.
118+
- Disable `editOnPrintable` if your component uses character keys:
115119

116120
```ts
117121
{ id: "product", editable: true, editRenderer: SmartSelectEditor, editOnPrintable: false }
@@ -384,16 +388,18 @@ const ds = useTreeDataSource({
384388
- **`editMutateCommit` fires for every column when any cell is committed** — a common mistake is writing `editMutateCommit` on a column that converts strings to numbers, but forgetting it fires even when a _different_ column is edited. The hook receives the full `editData`, so each column's `editMutateCommit` should only transform its own field and leave others untouched.
385389
- **`editSetter` must return the full `editData` object** — returning only the changed field (e.g. `{ price: newValue }`) replaces the entire row data with a partial object. Always spread: `{ ...editData, price: newValue }`.
386390
- **Controlled date inputs cause "jumping" bugs** — for date inputs, use `defaultValue` (uncontrolled) rather than `value` (controlled). A controlled date input can enter invalid intermediate states as the user types (e.g. `"2024-"` mid-entry), causing the input to jump. See the date editor example above.
387-
- **Popover editors: set `editOnPrintable: false`** — if your editor component responds to keyboard input (e.g. opens a dropdown on keypress), the grid would otherwise open editing on every printable key typed while the cell is focused.
388-
- **`editUpdate` runs through the full validation pipeline** — if validation fails, the update is rejected and the map is not partially applied. Check the return value.
391+
- **Popover editors: set `editOnPrintable: false`** — if your editor component responds to keyboard input (e.g. opens a dropdown on keypress),
392+
the grid would otherwise open editing on every printable key typed while the cell is focused.
393+
- **`editUpdate` runs through the full validation pipeline** — if validation fails, the update is rejected and
394+
the map is not partially applied. Check the return value.
389395

390396
## Full Docs
391397

392-
- [Cell Editing](/docs/cell-editing)
393-
- [Cell Edit Renderers](/docs/cell-editing-renderers)
394-
- [Cell Editing Validation](/docs/cell-editing-validation)
395-
- [Full Row Editing](/docs/cell-editing-full-row)
396-
- [Linked Cell Edits](/docs/cell-editing-linked-cell-edits)
397-
- [Bulk Cell Editing](/docs/cell-editing-bulk-editing)
398-
- [Server Data Editing](/docs/server-data-loading-cell-editing)
399-
- [Tree Data Editing](/docs/tree-source-data-editing)
398+
- [Cell Editing](https://www.1771technologies.com/docs/cell-editing)
399+
- [Cell Edit Renderers](https://www.1771technologies.com/docs/cell-editing-renderers)
400+
- [Cell Editing Validation](https://www.1771technologies.com/docs/cell-editing-validation)
401+
- [Full Row Editing](https://www.1771technologies.com/docs/cell-editing-full-row)
402+
- [Linked Cell Edits](https://www.1771technologies.com/docs/cell-editing-linked-cell-edits)
403+
- [Bulk Cell Editing](https://www.1771technologies.com/docs/cell-editing-bulk-editing)
404+
- [Server Data Editing](https://www.1771technologies.com/docs/server-data-loading-cell-editing)
405+
- [Tree Data Editing](https://www.1771technologies.com/docs/tree-source-data-editing)

skills/lytenyte-grid/refs/cells.md

Lines changed: 41 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22

33
## Cell Renderers
44

5-
A cell renderer is a standard React component assigned to a column's `cellRenderer` property. It receives `Grid.T.CellRendererParams<GridSpec>` as props:
5+
A cell renderer is a standard React component assigned to a column's `cellRenderer` property.
6+
It receives `Grid.T.CellRendererParams<GridSpec>` as props:
67

78
```tsx
89
function ProductCell({ api, row, column }: Grid.T.CellRendererParams<GridSpec>) {
@@ -56,7 +57,9 @@ function PriceCell({ api, column, row }: Grid.T.CellRendererParams<GridSpec>) {
5657

5758
### State & Virtualization Warning
5859

59-
LyteNyte Grid virtualizes rows — cells unmount/remount as they scroll in/out of view. **Do not use `useState` inside cell renderers for persistent state** — it resets on unmount.
60+
LyteNyte Grid virtualizes rows — cells unmount/remount as they scroll in/out of view.
61+
62+
**Do not use `useState` inside cell renderers for persistent state** — it resets on unmount.
6063

6164
Instead, lift state above the grid:
6265

@@ -98,7 +101,8 @@ function SymbolCell({ api, row }: Grid.T.CellRendererParams<GridSpec>) {
98101

99102
### Popover on Cell Focus (Alternative Pattern)
100103

101-
Instead of embedding a popover in every cell renderer, listen to cell focus events and render a single shared popover as a grid sibling:
104+
Instead of embedding a popover in every cell renderer, listen to cell focus
105+
events and render a single shared popover as a grid sibling:
102106

103107
```tsx
104108
const [anchor, setAnchor] = useState<HTMLElement | null>(null);
@@ -181,9 +185,12 @@ Display the direction and magnitude of the change alongside the value:
181185
## Gotchas
182186

183187
- **`api.columnField(column, row)` is the safe way to read values** — it handles all row types (leaf, group, aggregated) uniformly. Direct `row.data[column.id]` fails for group rows (no `data`) and ignores custom `field` definitions.
184-
- **Popover/tooltip libraries need portals to escape grid clipping** — the grid clips its cells. A tooltip anchored to a cell element will be clipped by the cell's overflow boundary unless it renders in a portal (e.g. `Tooltip.Portal` in Radix UI).
185-
- **Cell renderers receive new props on every render** — do not use deep equality checks or memoize based on props inside renderers. The grid controls when renderers re-render; hook into that rather than trying to suppress it.
186-
- **`useRef` for diff tracking, not `useState`** — for diff flashing, tracking the previous value with `useRef` works across virtualization remounts. `useState` would reset on unmount and produce false "new value" flashes for rows that just scrolled back into view.
188+
- **Popover/tooltip libraries need portals to escape grid clipping** — the grid clips its cells. A tooltip anchored to a cell element will
189+
be clipped by the cell's overflow boundary unless it renders in a portal (e.g. `Tooltip.Portal` in Radix UI).
190+
- **Cell renderers receive new props on every render** — do not use deep equality checks or memoize based on props inside renderers.
191+
The grid controls when renderers re-render; hook into that rather than trying to suppress it.
192+
- **`useRef` for diff tracking, not `useState`** — for diff flashing, tracking the previous value with `useRef` works across
193+
virtualization remounts. `useState` would reset on unmount and produce false "new value" flashes for rows that just scrolled back into view.
187194

188195
## Cell Range Selection
189196

@@ -197,7 +204,8 @@ Set `cellSelectionMode` on the grid:
197204
<Grid cellSelectionMode="none" ... /> // disabled (default)
198205
```
199206

200-
Users click and drag to select. In `"multi-range"` mode, hold **Ctrl** or **Cmd** to add or deselect ranges.
207+
Users click and drag to select. In `"multi-range"` mode,
208+
hold **Ctrl** or **Cmd** to add or deselect ranges.
201209

202210
### Reading the Selection (Uncontrolled)
203211

@@ -211,16 +219,18 @@ const rects = api.cellSelections(); // DataRect[] | null
211219

212220
```ts
213221
interface DataRect {
214-
readonly rowStart: number; // inclusive
215-
readonly rowEnd: number; // exclusive
222+
readonly rowStart: number; // inclusive
223+
readonly rowEnd: number; // exclusive
216224
readonly columnStart: number; // inclusive
217-
readonly columnEnd: number; // exclusive
225+
readonly columnEnd: number; // exclusive
218226
}
219227
```
220228

221229
### Copy to Clipboard (Ctrl+C / Cmd+C)
222230

223-
Listen for the keyboard shortcut in the `viewport.keyDown` event, read the selection rect, export the data, and write to the clipboard. The `viewport` parameter is the grid's DOM element — add/remove a CSS class on it for copy-flash visual feedback.
231+
Listen for the keyboard shortcut in the `viewport.keyDown` event, read the selection rect, export the data,
232+
and write to the clipboard. The `viewport` parameter is the grid's DOM element,
233+
add/remove a CSS class on it for copy-flash visual feedback.
224234

225235
```tsx
226236
<Grid
@@ -236,9 +246,7 @@ Listen for the keyboard shortcut in the `viewport.keyDown` event, read the selec
236246
const exported = await api.exportData({ rect });
237247

238248
// Tab-separated rows for spreadsheet paste (Excel, Google Sheets)
239-
const text = exported.data
240-
.map((row) => row.map((cell) => `${cell ?? ""}`).join("\t"))
241-
.join("\n");
249+
const text = exported.data.map((row) => row.map((cell) => `${cell ?? ""}`).join("\t")).join("\n");
242250

243251
await navigator.clipboard.writeText(text);
244252

@@ -258,9 +266,15 @@ Add the CSS animation targeting the selection overlay element:
258266

259267
```css
260268
@keyframes copy-flash {
261-
0% { background-color: var(--ln-primary-10); }
262-
50% { background-color: var(--ln-primary-30); }
263-
100% { background-color: var(--ln-primary-10); }
269+
0% {
270+
background-color: var(--ln-primary-10);
271+
}
272+
50% {
273+
background-color: var(--ln-primary-30);
274+
}
275+
100% {
276+
background-color: var(--ln-primary-10);
277+
}
264278
}
265279

266280
.copy-flash [data-ln-cell-selection-rect] {
@@ -269,12 +283,14 @@ Add the CSS animation targeting the selection overlay element:
269283
```
270284

271285
**Key points:**
286+
272287
- `api.exportData({ rect })` is `async` — always `await` it before writing to clipboard
273288
- `exported.data` is a 2D array (row-major): `data[rowIndex][colIndex]`
274289
- Use `\t` between cells and `\n` between rows for Excel/Google Sheets paste compatibility
275290
- For `"multi-range"` mode, `api.cellSelections()` returns multiple rects — use `[0]` for the primary selection or implement merge logic
276291
- `navigator.clipboard.writeText` requires HTTPS or localhost
277-
```
292+
293+
````
278294
279295
### Controlled Cell Selection
280296
@@ -284,7 +300,7 @@ Manage selection state in React when you need to react to changes (e.g., show a
284300
const [cellSelections, setCellSelections] = useState<Grid.T.DataRect[]>([]);
285301
286302
<Grid cellSelectionMode="range" cellSelections={cellSelections} onCellSelectionChange={setCellSelections} />;
287-
```
303+
````
288304

289305
> **Warning:** `cellSelections` is both a grid **prop** (controlled state) and an **API method** (`api.cellSelections()`). In controlled mode, the API method returns the same value you passed in. In uncontrolled mode, the API method reads internal state.
290306
@@ -346,7 +362,8 @@ LyteNyte Grid renders inert `div` overlays to visualize selections. They are uns
346362
}
347363
```
348364

349-
Header cells of columns containing selected cells get `data-ln-cell-selected="true"`. Rows containing selected cells also get this attribute:
365+
Header cells of columns containing selected cells get `data-ln-cell-selected="true"`.
366+
Rows containing selected cells also get this attribute:
350367

351368
```css
352369
[data-ln-header-cell="true"][data-ln-cell-selected="true"] {
@@ -386,7 +403,7 @@ Even with this enabled, the marker column still occupies column index 0 — so t
386403

387404
## Full Docs
388405

389-
- [Cell Renderers](/docs/cell-renderers)
390-
- [Cell Tooltips & Popovers](/docs/cell-tooltips)
391-
- [Cell Diff Flashing](/docs/cell-diff-flashing)
392-
- [Cell Range Selection](/docs/cell-selection)
406+
- [Cell Renderers](https://www.1771technologies.com/docs/cell-renderers)
407+
- [Cell Tooltips & Popovers](https://www.1771technologies.com/docs/cell-tooltips)
408+
- [Cell Diff Flashing](https://www.1771technologies.com/docs/cell-diff-flashing)
409+
- [Cell Range Selection](https://www.1771technologies.com/docs/cell-selection)

0 commit comments

Comments
 (0)