@guiexpert/table 1.1.104 → 1.1.106

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.
@@ -0,0 +1,10 @@
1
+ /**
2
+ * macOS or not – the one place where the table asks for the platform (⌘ shortcuts, ⌥+letter types text,
3
+ * `aria-keyshortcuts`). Reads `navigator.platform` ("MacIntel", also iPadOS Safari with a keyboard); without a
4
+ * `navigator` (SSR, Node) `false`. Tests switch the platform here with `vi.spyOn(navigator, "platform", "get")`.
5
+ *
6
+ * `navigator.userAgentData.platform` is not used on purpose: only Chromium has it, and the result would be the same.
7
+ *
8
+ * @internal
9
+ */
10
+ export declare function isMacPlatform(): boolean;
@@ -7,7 +7,22 @@ import { SortItem } from '../../common/sort-item.js';
7
7
  import { AreaIdent } from '../area-ident.type.js';
8
8
  import { SorterService } from '../../../service/sorter.service.js';
9
9
 
10
- /** @category Area models */
10
+ /**
11
+ * Body of a cover grid: `filteredItems[row * columns + column]` is the item of a tile (`getValueAt`), `columns` =
12
+ * `floor(width / coverWidth)`. `items` = all items, `filteredItems` = shown items (filter and `sort()` work on it).
13
+ * Change `coverWidth`/`coverHeight` or the items and call `tableApi.repaintHard()`.
14
+ *
15
+ * @example
16
+ * ```ts
17
+ * declare const grid: CoverGridTableModel<string>;
18
+ * const tiles = grid.bodyModel;
19
+ * tiles.items = ["new-1.jpg", "new-2.jpg"];
20
+ * tiles.filteredItems = tiles.items; // or tableApi.externalFilterChanged() with a filter
21
+ * tableApi.repaintHard();
22
+ * ```
23
+ *
24
+ * @category Area models
25
+ */
11
26
  export declare class CoverGridBodyModel<T> implements AreaModelIf {
12
27
  items: T[];
13
28
  cellRenderer: CellRendererIf;
@@ -9,7 +9,20 @@ import { ColumnDefIf } from './column/column-def.if.js';
9
9
  import { Padding } from './padding.js';
10
10
  import { SelectionModelIf } from '../../selection/selection-model.if.js';
11
11
 
12
- /** @category Table model */
12
+ /**
13
+ * Table model of a cover grid (created by {@link CoverGridFactory.createCoverGridModel}): items as tiles, the number of
14
+ * columns follows the width (`floor(width / coverWidth)`), header and footer are hidden. The data and the tile size
15
+ * live in {@link CoverGridBodyModel} (`tableModel.bodyModel`).
16
+ *
17
+ * @example
18
+ * ```ts
19
+ * declare const grid: CoverGridTableModel<{ title: string }>;
20
+ * const columns = grid.getColumnCount();
21
+ * tableApi.scrollToIndex(0, Math.floor(500 / columns)); // the row of item 500
22
+ * ```
23
+ *
24
+ * @category Table model
25
+ */
13
26
  export declare class CoverGridTableModel<T> implements TableModelIf {
14
27
  bodyModel: CoverGridBodyModel<T>;
15
28
  paddingTop: number;
@@ -39,6 +52,7 @@ export declare class CoverGridTableModel<T> implements TableModelIf {
39
52
  setColumnWidth(_columnIndex: number, width: number): void;
40
53
  recalcSize(_clientWidth: number): void;
41
54
  recalcPadding(): void;
55
+ /** No-op: the height follows from the item count, the column count and `coverHeight` – `repaintHard()` is enough. */
42
56
  recalcHeightAndPadding(): void;
43
57
  getPadding(): Padding;
44
58
  getContentHeightInPixel(): number;
@@ -47,6 +47,16 @@ export declare class EleScope {
47
47
  protected hoverColumn: HTMLDivElement;
48
48
  protected draggingColumn: HTMLDivElement;
49
49
  protected scrollTop: number;
50
+ /**
51
+ * Height of the horizontal scroll bar of {@link scrollViewport} in px, measured in {@link adjustContainersAndRows}.
52
+ * 0 without a horizontal scroll bar and with overlay scroll bars (macOS default, mobile) – they take no space.
53
+ */
54
+ protected horizontalScrollBarHeight: number;
55
+ /**
56
+ * Width of the vertical scroll bar of {@link scrollViewport} in px (at the end of the rows: right, in right-to-left
57
+ * on the left), measured in {@link adjustContainersAndRows}. 0 without one and with overlay scroll bars.
58
+ */
59
+ protected verticalScrollBarWidth: number;
50
60
  protected areaBodyWestGeo: GeoData;
51
61
  protected areaBodyCenterGeo: GeoData;
52
62
  protected areaBodyEastGeo: GeoData;
@@ -135,6 +145,29 @@ export declare class EleScope {
135
145
  * @return {void}
136
146
  */
137
147
  adjustContainersAndRows(): void;
148
+ /**
149
+ * The height the table lays out header, body and footer in: the host height minus a horizontal scroll bar that
150
+ * takes space (see {@link horizontalScrollBarHeight}).
151
+ *
152
+ * @returns {number} px
153
+ * @since 1.1.104
154
+ */
155
+ getLayoutHeight(): number;
156
+ /**
157
+ * The width the table lays out header, body and footer in: the host width minus a vertical scroll bar that takes
158
+ * space (see {@link verticalScrollBarWidth}). A logical width – in right-to-left the bar is on the left.
159
+ *
160
+ * @returns {number} px
161
+ * @since 1.1.104
162
+ */
163
+ getLayoutWidth(): number;
164
+ /**
165
+ * Height of the horizontal scroll bar of the scroll viewport: offsetHeight − clientHeight (the viewport has no
166
+ * border, see the constructor).
167
+ */
168
+ protected measureHorizontalScrollBarHeight(): number;
169
+ /** Width of the vertical scroll bar of the scroll viewport: offsetWidth − clientWidth. */
170
+ protected measureVerticalScrollBarWidth(): number;
138
171
  /**
139
172
  * Position and size of the detail layer (master/detail) in the host – overwritten in {@link RenderScope}.
140
173
  *
@@ -2,8 +2,73 @@ import { CoverGridTableModel } from '../data/tablemodel/cover-grid-table-model.j
2
2
  import { CellRendererIf } from '../renderer/cell-render.if.js';
3
3
  import { TableOptionsIf } from '../data/options/table-options.if.js';
4
4
 
5
- /** @category Table model */
5
+ /**
6
+ * Creates cover grids: a flat array of items shown as tiles (book covers, photos, products) – as many per row as fit
7
+ * into the width of the table (`floor(width / coverWidth)`), without header and footer. Only the visible rows are
8
+ * rendered, like in every GuiExpert Table; filter (`externalFilterFunction`), `sort()` and the scroll position work as
9
+ * usual. Guide: instructions.md 5.10, showcase https://gui.expert/showcase/covergrid/run.
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * interface Book { title: string; }
14
+ * declare const books: Book[];
15
+ *
16
+ * const renderer: CellRendererIf = {
17
+ * render(cellDiv: HTMLDivElement, _r: number, _c: number, _a: AreaIdent, _m: AreaModelIf, book: any) {
18
+ * if (book) cellDiv.textContent = (book as Book).title; // the item of this tile (undefined after the last one)
19
+ * return undefined;
20
+ * }
21
+ * };
22
+ * const gridModel = CoverGridFactory.createCoverGridModel(books, renderer, 160, 240);
23
+ * const gridOptions = CoverGridFactory.createCoverGrisDefaultOptions(240, () => "books");
24
+ * const gridApi = new TableScope(host, gridModel, new SimpleDomService(), gridOptions, new EventAdapter())
25
+ * .firstInit()
26
+ * .getApi();
27
+ * ```
28
+ *
29
+ * @category Table model
30
+ */
6
31
  export declare class CoverGridFactory {
32
+ /**
33
+ * Creates the model of a cover grid.
34
+ *
35
+ * @param items - the items, one per tile (the renderer gets the item as `cellValue`)
36
+ * @param cellRenderer - draws one tile into the cell `<div>`
37
+ * @param coverWidth - width of a tile in px (default 200) – change it later via `bodyModel.coverWidth` + `repaintHard()`
38
+ * @param coverHeight - height of a tile in px (default 300)
39
+ * @param width - initial width of the grid; the table sets the width of its host on start and after a resize
40
+ * @param firstRowPaddingTop - extra height of the first row in px (room above the first tiles, e.g. for a toolbar)
41
+ * @returns the table model; `tableModel.bodyModel` holds `items`, `filteredItems`, `coverWidth`, `coverHeight`
42
+ *
43
+ * @example
44
+ * ```ts
45
+ * declare const renderer: CellRendererIf;
46
+ * const gridModel = CoverGridFactory.createCoverGridModel(["a.jpg", "b.jpg", "c.jpg"], renderer, 120, 180);
47
+ * // bigger tiles – no new model:
48
+ * gridModel.bodyModel.coverWidth = 200;
49
+ * gridModel.bodyModel.coverHeight = 300;
50
+ * tableApi.repaintHard();
51
+ * ```
52
+ */
7
53
  static createCoverGridModel<T>(items: T[], cellRenderer: CellRendererIf, coverWidth?: number, coverHeight?: number, width?: number, firstRowPaddingTop?: number): CoverGridTableModel<T>;
54
+ /**
55
+ * Options for a cover grid: no borders and separators, no row or column hover, header and footer height 0, and the
56
+ * scroll position restored under the key of `getStorageKeyFn` (`localStorage`). (The name has a typo – kept for
57
+ * compatibility.)
58
+ *
59
+ * @param defaultRowHeight - the height of a tile (`coverHeight`)
60
+ * @param getStorageKeyFn - storage key of the scroll position (default `"CoverGrid"`)
61
+ * @returns new options – add your own fields with a spread
62
+ *
63
+ * @example
64
+ * ```ts
65
+ * let photoFilter = "";
66
+ * const gridOptions: TableOptionsIf = {
67
+ * ...CoverGridFactory.createCoverGrisDefaultOptions(240, () => "photos"),
68
+ * externalFilterFunction: (photo: { name: string }) => photo.name.includes(photoFilter),
69
+ * shortcutActionsDisabled: true
70
+ * };
71
+ * ```
72
+ */
8
73
  static createCoverGrisDefaultOptions(defaultRowHeight: number, getStorageKeyFn?: () => string): TableOptionsIf;
9
74
  }
@@ -27,6 +27,10 @@ export declare class MouseHandler {
27
27
  private mouseDown;
28
28
  private repaintAfterMouseUp;
29
29
  private dragging;
30
+ /** Counts the drags: a frame callback of a drag that is over (a new one started) does nothing. */
31
+ private dragGeneration;
32
+ /** The end of the last drag, until its frame ran – a new mousedown handles it at once (see {@link flushDraggingEnd}). */
33
+ private pendingDraggingEnd?;
30
34
  constructor(tableScope: TableScope);
31
35
  /**
32
36
  * Handles the "contextmenu" event.
@@ -76,7 +80,8 @@ export declare class MouseHandler {
76
80
  private onMouseMove;
77
81
  private onMouseUp;
78
82
  private mouseDraggingOnFrame;
79
- private mouseDraggingEndOnFrame;
83
+ /** Handles the end of the last drag now (in its frame, or earlier when the next mousedown comes first). */
84
+ private flushDraggingEnd;
80
85
  private mouseMoveOnFrame;
81
86
  /** A mouse event on a blocking loading overlay ({@link TableOverlay.isEventBlocked}): the table ignores it. */
82
87
  private isBlocked;
@@ -312,6 +312,18 @@ export declare class RenderScope extends EleScope {
312
312
  * @return {undefined}
313
313
  */
314
314
  recalcColumnWidths(clientWidth?: number): void;
315
+ /** `TableModelIf.setParentWidth` – remembers the width for {@link adjustRelativeColumnWidthsToScrollBar}. */
316
+ protected setParentWidth(widthInPixel: number): void;
317
+ /** True if a column has a % or weight width (it depends on the width of the host). */
318
+ protected hasRelativeColumnWidths(): boolean;
319
+ adjustContainersAndRows(): void;
320
+ /**
321
+ * % and weight columns share the width next to the vertical scroll bar. The bar appears (or disappears) only after
322
+ * the rows are laid out – the first layout, rows added or removed, a filter: then the columns were computed for the
323
+ * wrong width – too wide by the width of the bar, so a horizontal scroll bar appeared and covered the last row. Here
324
+ * they are computed again (once – the flag stops a second pass) with the width that is available now.
325
+ */
326
+ protected adjustRelativeColumnWidthsToScrollBar(): void;
315
327
  /**
316
328
  * The width that % and weight columns share: host width minus the fixed west columns and a vertical scroll bar.
317
329
  *
@@ -590,6 +602,10 @@ export declare class RenderScope extends EleScope {
590
602
  private rowNumberWidth?;
591
603
  /** true while the containers are adjusted to a new row number width (no second change in that nested pass). */
592
604
  private adjustingRowNumberWidth;
605
+ /** Guards {@link adjustRelativeColumnWidthsToScrollBar} against re-entry. */
606
+ private adjustingRelativeWidths;
607
+ /** The width % and weight columns were last computed for (`TableModelIf.setParentWidth`), -1 = unknown. */
608
+ protected relativeWidthsBase: number;
593
609
  /** the approximate first width is measured again once (see adjustAfterScrolling). */
594
610
  private rowNumberRemeasureScheduled;
595
611
  /**
@@ -17,6 +17,8 @@ import { ValueLabelIf } from '../../data/common/value-label.if.js';
17
17
  export declare class SelectCellEditor implements CellEditorIf {
18
18
  protected readonly options: ValueLabelIf[];
19
19
  private select?;
20
+ /** The text of the cell when the editor opened: "nothing chosen" counts as unchanged (since 1.1.106). */
21
+ private startText;
20
22
  private readonly abort;
21
23
  constructor(options: ValueLabelIf[]);
22
24
  /**
@@ -28,6 +30,12 @@ export declare class SelectCellEditor implements CellEditorIf {
28
30
  static create(values: string[]): SelectCellEditor;
29
31
  open(container: HTMLDivElement, ctx: CellEditorContextIf): void;
30
32
  getValue(): unknown;
33
+ /**
34
+ * The chosen value as text; with nothing chosen (the cell's value is not in the list) the start text, so that
35
+ * Enter without a choice writes nothing (before 1.1.106 it emptied the cell).
36
+ *
37
+ * @returns {string} the text
38
+ */
31
39
  getText(): string;
32
40
  focus(): void;
33
41
  getFocusElement(): HTMLElement | undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@guiexpert/table",
3
- "version": "1.1.104",
3
+ "version": "1.1.106",
4
4
  "license": "MIT",
5
5
  "author": "Dr. Marc Kronberg <info@gui.expert> (https://gui.expert)",
6
6
  "repository": {
@@ -69,7 +69,8 @@
69
69
  "docs:check": "node scripts/check-doc-examples.mjs",
70
70
  "test": "vitest run",
71
71
  "test:unit": "vitest run --project unit",
72
- "test:browser": "vitest run --project browser",
72
+ "test:browser": "vitest run --project browser --project browser-scrollbars",
73
+ "test:browser:scrollbars": "GE_ALL_SCROLLBARS=1 vitest run --project browser-scrollbars",
73
74
  "test:watch": "vitest --project unit",
74
75
  "test:coverage": "vitest run --coverage",
75
76
  "test:types": "tsc -p tsconfig.spec.json --noEmit",