@nxgt/material-core 0.3.1 → 0.4.0

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.
package/CHANGELOG.md ADDED
@@ -0,0 +1,156 @@
1
+ # @nxgt/material-core
2
+
3
+ ## 0.4.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#431](https://github.com/softistx/nxgt-material/pull/431) [`bd58a6f`](https://github.com/softistx/nxgt-material/commit/bd58a6f67bdc25d4b4d0d7d88c52784869ad1529) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `DataTable` renders server-side data: one page from a loader, with the query in the URL. Everything is opt-in; a table that sets none of the new props renders and behaves as before.
8
+
9
+ - `manual`: `data` is one server page, rendered as given — no client paging, sorting or filtering — and the footer becomes `DataTableServerFooter` (exported).
10
+ - `pagination={{ totalCount, offset, previous, next }}`: the server footer's range ("41–60 of 133", `material.table.pagination.range`) and Previous / Next, each an href (a real link, `rel="prev"` / `rel="next"`, that works with no JavaScript) or a callback (a button); disabled when absent and while `loading`.
11
+ - Controlled sorting: `sorting` + `onSortingChange(sorting)` in React, `v-model:sorting` in Vue. The headers show the sorting they are given; the callback receives the new `SortingState`, not an updater.
12
+ - `getRowId(row, index)` and controlled selection by id: `rowSelection` + `onRowSelectionChange(selection)` in React, `v-model:row-selection` in Vue. Ids of other pages are kept, and the server footer counts them all.
13
+ - `getRowHref(row)`: the first data cell becomes a link to the row's page, and a click anywhere else on the row (but on a control, in a portal, or at the end of a text selection) follows it. `onRowClick(row, event)` (`@row-click` in Vue) makes the row focusable and activatable with Enter.
14
+ - `linkComponent`: the link rendered for row and page links, given `href` (wrap React Router's `Link` or vue-router's `RouterLink`, which take `to`). Default: a plain `<a>`.
15
+ - `footer`: a render function (React, Vue) or the `#footer` slot (Vue) replaces the footer; `footer={false}` removes it.
16
+ - New `material.table.pagination.*` strings, in English and French: `label`, `range`, `range-open`, `total`, `selected`.
17
+
18
+ `@nxgt/material-core` gains `/row-activation` (`isRowActivation`, `followRowLink`, `ROW_CONTROL_SELECTOR`, `ROW_LINK_SELECTOR`, `RowClick`): the clickable-row rule both libraries' `DataTable` use.
19
+
20
+ ## 0.3.2
21
+
22
+ ### Patch Changes
23
+
24
+ - [#429](https://github.com/softistx/nxgt-material/pull/429) [`13c3eda`](https://github.com/softistx/nxgt-material/commit/13c3edad01f08656ca196aca7c37a21e8c003466) Thanks [@SteveGT96](https://github.com/SteveGT96)! - The package ships its `CHANGELOG.md` (now in `files`), so the notes of every
25
+ version are in `node_modules/<package>/CHANGELOG.md` next to the docs, for
26
+ reading offline and for the upgrade agents of the Claude Code plugins.
27
+ The getting-started pages of `@nxgt/material` and `@nxgt/material-vue`
28
+ list it among the tarball's contents.
29
+
30
+ ## 0.3.1
31
+
32
+ ### Patch Changes
33
+
34
+ - [#427](https://github.com/softistx/nxgt-material/pull/427) [`d4e953f`](https://github.com/softistx/nxgt-material/commit/d4e953fd4fc147b366c5d9d70ebfb1aa3a3c0ba7) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Docs: the roadmap lists what 0.3.0 shipped (`/date-range`, `/file-preview`, `/slider-ticks`) under Shipped, newest first. Troubleshooting has entries for `FilePreviewHttpError` (`Could not read <url>: HTTP <status>`) and `FilePreviewUnsafeUrlError` (`Refused to preview a URL with an unsafe scheme`), so every message the package prints is there again. The README's `/pdf-viewer` section documents `pinToPage`'s `onRelease` and `pinHolds`, with an example.
35
+
36
+ ## 0.3.0
37
+
38
+ ### Minor Changes
39
+
40
+ - [#424](https://github.com/softistx/nxgt-material/pull/424) [`d90a68b`](https://github.com/softistx/nxgt-material/commit/d90a68b8f16759b44929effc426b6c3964e92a4c) Thanks [@SteveGT96](https://github.com/SteveGT96)! - New subpath `/date-range`, the pure half of both libraries' `DateRangeField`
41
+ presets and times: `DATE_RANGE_PRESET_IDS` (`today`, `yesterday`,
42
+ `last-7-days`, `last-30-days`, `this-month`, `last-month`, `this-year`),
43
+ `presetRange(id, now?)`, `resolvePresetRange(preset, now?, { withTime })`,
44
+ `isSameDayRange`, `isRangeDisabled(range, { min, max, isDayDisabled })`
45
+ (also true for a range whose end comes before its start),
46
+ `withTimeOf`, `endOfDayTime`, `clampRangeEnd`, `keepRangeTimes` (a new end
47
+ at 23:59), and the types
48
+ `DayRange`, `PartialDayRange`, `DateRangePreset`, `DateRangePresetId`,
49
+ `DateRangePresetOption` and `DateRangeRules`.
50
+
51
+ - [#426](https://github.com/softistx/nxgt-material/pull/426) [`812c101`](https://github.com/softistx/nxgt-material/commit/812c10115278480852852084ddf06b016239e6d4) Thanks [@SteveGT96](https://github.com/SteveGT96)! - New subpath `/file-preview`, the pure half of both libraries' `FilePreview`:
52
+ `fileKind({ type, name, url })` picks a `FileKind` (`image`, `pdf`, `video`,
53
+ `audio`, `text`, `json`, `csv`, `markdown` or `other`) from the MIME type
54
+ when it names one (a `data:` URL's included), else from the extension of the
55
+ name, then of the URL; `describeFile(file, { type, name })` does it for a
56
+ `File` / `Blob` or a URL and returns its name, type and size;
57
+ `safePreviewUrl(url)` returns the URL when it may reach an `href` or a `src`
58
+ (`http(s):`, relative, `blob:`, or a `data:` URL of an image, video, audio
59
+ file, plain text, CSV, Markdown, JSON or PDF) and `null` for anything else
60
+ (`javascript:`, `data:text/html`, and also a `data:` URL of XML, TSV or
61
+ YAML), with `FilePreviewUnsafeUrlError` ("Refused to preview a URL with an unsafe scheme") for the one refused;
62
+ `readTextPreview(source, { maxBytes, signal })` reads the first `maxBytes`
63
+ (`TEXT_PREVIEW_MAX_BYTES`, 256 KB, by default) of a `Blob` or of what a URL
64
+ answers, decoded with the `charset` of the `Content-Type` (or of the `Blob`'s
65
+ `type`) when `TextDecoder` supports it and as UTF-8 otherwise, stopping the
66
+ download past the cap; it throws `FilePreviewHttpError` (with its `status`)
67
+ on an HTTP error, and rejects with the signal's reason once aborted, for a
68
+ `Blob` too. Also
69
+ `fileExtension`, `fileNameFromUrl`, `dataUrlType`, `kindOfMimeType`,
70
+ `normaliseMimeType`, `isTextFileKind`, `TEXT_FILE_KINDS` and the types
71
+ `FileKind`, `FileKindInput`, `DescribedFile`, `FilePreviewSource` and
72
+ `TextPreview`. Nothing runs at import.
73
+
74
+ - [#420](https://github.com/softistx/nxgt-material/pull/420) [`f4ca69b`](https://github.com/softistx/nxgt-material/commit/f4ca69ba5dd7b80f92cdd5374cf6fd01f155784d) Thanks [@SteveGT96](https://github.com/SteveGT96)! - New subpath `/slider-ticks`: `tickValues(min, max, ticks, step?)`, the values a slider's tick marks sit at. Both libraries' `SliderField` (`ticks`) now read it from here instead of keeping a copy each.
75
+
76
+ ### Patch Changes
77
+
78
+ - [#419](https://github.com/softistx/nxgt-material/pull/419) [`99878c9`](https://github.com/softistx/nxgt-material/commit/99878c9bdb0d4c33fd6a777970c4f3735bd5ef3b) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `PdfReader`: opening on an `initialPage` no longer reports another page on a
79
+ slow device. The placement scroll could land after the scroll spy's hold had
80
+ run out, and near the end of the document it read as the last page. While
81
+ the view stays where the opening page's pin put it (that page at the top,
82
+ or the page area at its end with that page not above the top), a scroll is the reader's own and is not
83
+ reported; a scroll that moves the view elsewhere — the user's, find in page,
84
+ a screen reader's cursor — lets go of the pin and is reported. A wheel,
85
+ touch, pointer or key in the page area also ends the hold on the reader's
86
+ own scroll, so the scrolling after it is reported at once. In
87
+ `@nxgt/material-core/pdf-viewer`, `pinToPage` takes an optional `onRelease`,
88
+ called once when the pin lets go, and `pinHolds(node)` tells whether the view
89
+ is still where the pin puts it.
90
+
91
+ ## 0.2.0
92
+
93
+ ### Minor Changes
94
+
95
+ - [#406](https://github.com/softistx/nxgt-material/pull/406) [`b890457`](https://github.com/softistx/nxgt-material/commit/b8904573f18dc6d0cdd16f62146183ff0b7aef18) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Two new subpaths, each the one copy of code `@nxgt/material` and
96
+ `@nxgt/material-vue` used to keep twice:
97
+
98
+ - **`@nxgt/material-core/format-file-size`**: `formatFileSize(bytes)`, the
99
+ byte count both libraries' `FileList`, `NetworkUploadField`, `FileCard` and
100
+ chat attachments show (`'1.5 KB'`; `''` for a negative, `NaN` or infinite
101
+ size).
102
+ - **`@nxgt/material-core/pdfkit-fonts`** (Node only): `pdfkitStandardFontDirs`,
103
+ `pdfkitStandardFontFiles` and `PDFKIT_STANDARD_FONTS` find the real files of
104
+ pdfkit's standard fonts as an app loads them, through `@react-pdf/renderer`
105
+ and `@react-pdf/font`, for a server tracer (`@nxgt/material/next`, the Nuxt
106
+ module of `@nxgt/material-vue`). Without `@react-pdf/renderer` they return
107
+ `[]` silently; any other failure reaches your `warn` callback once per
108
+ call. It is the only subpath that imports `node:` built-ins: never import it
109
+ from browser code.
110
+
111
+ ```ts
112
+ import { formatFileSize } from '@nxgt/material-core/format-file-size';
113
+ import { pdfkitStandardFontDirs } from '@nxgt/material-core/pdfkit-fonts';
114
+
115
+ formatFileSize(1536); // '1.5 KB'
116
+ pdfkitStandardFontDirs(process.cwd(), console.warn); // ['/app/node_modules/pdfkit/js/standard-fonts']
117
+ ```
118
+
119
+ ### Patch Changes
120
+
121
+ - [#407](https://github.com/softistx/nxgt-material/pull/407) [`bf43a14`](https://github.com/softistx/nxgt-material/commit/bf43a14abb147e6dcd2bcac5f1e0e359b0aae6a2) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `pdfkitStandardFontDirs` and `pdfkitStandardFontFiles` (`/pdfkit-fonts`) keep
122
+ only the first line of each error in the one warning they give. Node's
123
+ `Cannot find module` error carries a multi-line `Require stack:`, and joined
124
+ whole into the `; `-separated message it made one warning dozens of lines
125
+ long; it now reads `could not resolve @react-pdf/font: Error: Cannot find
126
+ module '@react-pdf/font'; could not resolve pdfkit's fonts for …`.
127
+
128
+ ## 0.1.1
129
+
130
+ ### Patch Changes
131
+
132
+ - [#399](https://github.com/softistx/nxgt-material/pull/399) [`1abb908`](https://github.com/softistx/nxgt-material/commit/1abb90800ec213d8ec98abd7df5dda9f65c14204) Thanks [@SteveGT96](https://github.com/SteveGT96)! - The package manifest no longer carries a `repository` field. The source
133
+ repository is private, so the link npm showed for it led to a 404; the
134
+ README and the `docs/` shipped in the tarball remain the public reference.
135
+
136
+ - [#398](https://github.com/softistx/nxgt-material/pull/398) [`b4e5770`](https://github.com/softistx/nxgt-material/commit/b4e5770922ad4582a1ad2dcca439f7c8c999a0c7) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Docs: the roadmap's Shipped list now has 0.1.0 — the first release, holding the framework-free code `@nxgt/material` and `@nxgt/material-vue` share.
137
+
138
+ ## 0.1.0
139
+
140
+ ### Minor Changes
141
+
142
+ - [#391](https://github.com/softistx/nxgt-material/pull/391) [`deaf0c4`](https://github.com/softistx/nxgt-material/commit/deaf0c49cbc2268fd20e9e2272104dbd27e4ce7f) Thanks [@SteveGT96](https://github.com/SteveGT96)! - First release of `@nxgt/material-core`: the framework-free core that
143
+ `@nxgt/material` (React) and `@nxgt/material-vue` (Vue) share, as one copy.
144
+ The PDF document model (`/pdf/model`) and its react-pdf renderer
145
+ (`/pdf/render`, both also from `/pdf`), the print formatters (`/print-format`, `/print-value`,
146
+ `/print-table`, `/print-fraction-digits`), the PDF file helpers
147
+ (`/pdf-source`, `/pdf-file-name`, `/pdf-download`), serving a PDF
148
+ (`/pdf/http`, `/pdf/file-loader`), the PDF viewer's framework-free modules
149
+ (`/pdf-viewer`: page geometry, page sizes, scroll spy, text search), the
150
+ currency field's decimals (`/currency-digits`), the fields' blur layers
151
+ (`/field-blur-layers`) and the pdfkit fonts plugin (`/vite`).
152
+ `react` and `@react-pdf/renderer` are optional peers, needed only to render;
153
+ `vite` (6, 7 or 8) is one too, for the types of `/vite`.
154
+ Most apps get it through `@nxgt/material` or `@nxgt/material-vue`; a server
155
+ bundle that inlines either library (`ssr.noExternal`) must list
156
+ `@nxgt/material-core` beside it.
package/README.md CHANGED
@@ -70,6 +70,7 @@ built-in.
70
70
  | `/pdf-viewer` | The PDF viewer's pure half: page geometry and fit (`readerFitScale`, `placeholderSize`, `rotatedSize`, `PdfPageSize`, `FitMode`), page sizes (`readPageSizes`, `createPageSizesLoader`), the scroll spy (`createScrollSpy`, `revealHit`, `scrollIntoArea`), the opening page's pin (`pinToPage`, whose optional `onRelease` runs once when it lets go, and `pinHolds`, whether the view is still where it puts the page), text search (`pageTextFromItems`, `findTextMatches`, `searchDocument`), `escapeHtml`, `mapPdfOutline` |
71
71
  | `/currency-digits` | `currencyFractionDigits` — the decimals `CurrencyField` shows |
72
72
  | `/field-blur-layers` | `layerOf`, `openerOf`, `FieldLayers` — whose overlay a layer of the page is, for a field's blur |
73
+ | `/row-activation` | `isRowActivation`, `followRowLink`, `ROW_CONTROL_SELECTOR`, `ROW_LINK_SELECTOR`, `RowClick` — whether a click on a table row follows its link, and following it |
73
74
  | `/format-file-size` | `formatFileSize` — a byte count as `'1.5 KB'` |
74
75
  | `/slider-ticks` | `tickValues` — where a slider's tick marks sit |
75
76
  | `/date-range` | `DATE_RANGE_PRESET_IDS`, `presetRange`, `resolvePresetRange`, `isSameDayRange`, `isRangeDisabled`, `withTimeOf`, `endOfDayTime`, `clampRangeEnd`, `keepRangeTimes` and the types `DayRange`, `PartialDayRange`, `DateRangePreset`, `DateRangePresetId`, `DateRangePresetOption`, `DateRangeRules` — `DateRangeField`'s presets and times |
@@ -465,6 +466,24 @@ field.addEventListener('focusout', (event) => {
465
466
  });
466
467
  ```
467
468
 
469
+ ### A clickable table row: `/row-activation`
470
+
471
+ A row whose first cell links to its page should follow that link on a click
472
+ anywhere else on the row — but not on its checkbox or its buttons, not from
473
+ a menu its cell opened into `<body>`, and not when the click ends a text
474
+ selection. `isRowActivation` says whether a click is one; `followRowLink`
475
+ clicks the row's link (`ROW_LINK_SELECTOR`) with the click's modifier keys,
476
+ so a router's link navigates as it does on its own click. Both read the DOM
477
+ when called, never at import.
478
+
479
+ ```ts
480
+ import { followRowLink, isRowActivation } from '@nxgt/material-core/row-activation';
481
+
482
+ row.addEventListener('click', (event) => {
483
+ if (isRowActivation(event, row)) followRowLink(row, event);
484
+ });
485
+ ```
486
+
468
487
  ### Bundling pdfkit's fonts: `/vite`
469
488
 
470
489
  An SSR server bundled into a single file with no `node_modules` needs
@@ -0,0 +1,30 @@
1
+ /** What a click inside a row must not activate the row from: its controls
2
+ * (checkboxes, buttons, links, menus, fields) and anything marked
3
+ * `data-row-click-ignore`. */
4
+ export declare const ROW_CONTROL_SELECTOR: string;
5
+ /** The row's own link, which a click on the row follows: the element marked
6
+ * `data-slot="data-table-row-link"`, else the first link of the cell marked
7
+ * `data-row-link-cell` (for a link component that drops the `data-slot` it
8
+ * is given). */
9
+ export declare const ROW_LINK_SELECTOR = "[data-slot=\"data-table-row-link\"],[data-row-link-cell] a[href]";
10
+ /** The parts of a click this module reads. */
11
+ export type RowClick = {
12
+ target: EventTarget | null;
13
+ button: number;
14
+ defaultPrevented: boolean;
15
+ ctrlKey?: boolean;
16
+ metaKey?: boolean;
17
+ shiftKey?: boolean;
18
+ altKey?: boolean;
19
+ };
20
+ /** Whether `event`, a click that reached `row`, activates the row: a primary
21
+ * click, not handled yet, inside the row's own DOM (not in a portal its
22
+ * cells opened), not on one of its controls, and not the end of a text
23
+ * selection in it. */
24
+ export declare function isRowActivation(event: RowClick, row: Element): boolean;
25
+ /** Follows the row's link (`ROW_LINK_SELECTOR`) as if it had been clicked,
26
+ * with the modifier keys of `event`: a router's link navigates as it does on
27
+ * its own click, Ctrl/Cmd+click opens a tab where the browser allows it.
28
+ * `false` when the row has no link. */
29
+ export declare function followRowLink(row: Element, event: RowClick): boolean;
30
+ //# sourceMappingURL=row-activation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"row-activation.d.ts","sourceRoot":"","sources":["../../src/lib/row-activation.ts"],"names":[],"mappings":"AAIA;;8BAE8B;AAC9B,eAAO,MAAM,oBAAoB,QAgBtB,CAAC;AAEZ;;;gBAGgB;AAChB,eAAO,MAAM,iBAAiB,qEACmC,CAAC;AAElE,8CAA8C;AAC9C,MAAM,MAAM,QAAQ,GAAG;IACtB,MAAM,EAAE,WAAW,GAAG,IAAI,CAAC;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,gBAAgB,EAAE,OAAO,CAAC;IAC1B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;CACjB,CAAC;AAMF;;;sBAGsB;AACtB,wBAAgB,eAAe,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,EAAE,OAAO,GAAG,OAAO,CAgBtE;AAED;;;uCAGuC;AACvC,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,GAAG,OAAO,CAgBpE"}
@@ -0,0 +1,62 @@
1
+ // src/lib/row-activation.ts
2
+ var ROW_CONTROL_SELECTOR = [
3
+ "a[href]",
4
+ "button",
5
+ "input",
6
+ "select",
7
+ "textarea",
8
+ "label",
9
+ "summary",
10
+ '[contenteditable="true"]',
11
+ '[role="button"]',
12
+ '[role="checkbox"]',
13
+ '[role="link"]',
14
+ '[role="menuitem"]',
15
+ '[role="switch"]',
16
+ '[role="combobox"]',
17
+ "[data-row-click-ignore]"
18
+ ].join(",");
19
+ var ROW_LINK_SELECTOR = '[data-slot="data-table-row-link"],[data-row-link-cell] a[href]';
20
+ function isElement(target) {
21
+ return target !== null && typeof target.closest === "function";
22
+ }
23
+ function isRowActivation(event, row) {
24
+ if (event.defaultPrevented || event.button !== 0)
25
+ return false;
26
+ const { target } = event;
27
+ if (!isElement(target) || !row.contains(target))
28
+ return false;
29
+ const control = target.closest(ROW_CONTROL_SELECTOR);
30
+ if (control && control !== row && row.contains(control))
31
+ return false;
32
+ const selection = row.ownerDocument?.getSelection?.();
33
+ if (selection && !selection.isCollapsed && selection.anchorNode && row.contains(selection.anchorNode)) {
34
+ return false;
35
+ }
36
+ return true;
37
+ }
38
+ function followRowLink(row, event) {
39
+ const link = row.querySelector(ROW_LINK_SELECTOR);
40
+ const view = row.ownerDocument?.defaultView;
41
+ if (!link || !view)
42
+ return false;
43
+ link.dispatchEvent(new view.MouseEvent("click", {
44
+ bubbles: true,
45
+ cancelable: true,
46
+ button: 0,
47
+ ctrlKey: event.ctrlKey,
48
+ metaKey: event.metaKey,
49
+ shiftKey: event.shiftKey,
50
+ altKey: event.altKey
51
+ }));
52
+ return true;
53
+ }
54
+ export {
55
+ ROW_CONTROL_SELECTOR,
56
+ ROW_LINK_SELECTOR,
57
+ followRowLink,
58
+ isRowActivation
59
+ };
60
+
61
+ //# debugId=B5C72BCA21047D6B64756E2164756E21
62
+ //# sourceMappingURL=row-activation.js.map
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/lib/row-activation.ts"],
4
+ "sourcesContent": [
5
+ "// A clickable table row: a click anywhere on the row, but on a control of its\n// own, does what the row's link (or its click handler) does. Shared by both\n// libraries' `DataTable`. Reads the DOM only when called, never at import.\n\n/** What a click inside a row must not activate the row from: its controls\n * (checkboxes, buttons, links, menus, fields) and anything marked\n * `data-row-click-ignore`. */\nexport const ROW_CONTROL_SELECTOR = [\n\t'a[href]',\n\t'button',\n\t'input',\n\t'select',\n\t'textarea',\n\t'label',\n\t'summary',\n\t'[contenteditable=\"true\"]',\n\t'[role=\"button\"]',\n\t'[role=\"checkbox\"]',\n\t'[role=\"link\"]',\n\t'[role=\"menuitem\"]',\n\t'[role=\"switch\"]',\n\t'[role=\"combobox\"]',\n\t'[data-row-click-ignore]',\n].join(',');\n\n/** The row's own link, which a click on the row follows: the element marked\n * `data-slot=\"data-table-row-link\"`, else the first link of the cell marked\n * `data-row-link-cell` (for a link component that drops the `data-slot` it\n * is given). */\nexport const ROW_LINK_SELECTOR =\n\t'[data-slot=\"data-table-row-link\"],[data-row-link-cell] a[href]';\n\n/** The parts of a click this module reads. */\nexport type RowClick = {\n\ttarget: EventTarget | null;\n\tbutton: number;\n\tdefaultPrevented: boolean;\n\tctrlKey?: boolean;\n\tmetaKey?: boolean;\n\tshiftKey?: boolean;\n\taltKey?: boolean;\n};\n\nfunction isElement(target: EventTarget | null): target is Element {\n\treturn target !== null && typeof (target as Element).closest === 'function';\n}\n\n/** Whether `event`, a click that reached `row`, activates the row: a primary\n * click, not handled yet, inside the row's own DOM (not in a portal its\n * cells opened), not on one of its controls, and not the end of a text\n * selection in it. */\nexport function isRowActivation(event: RowClick, row: Element): boolean {\n\tif (event.defaultPrevented || event.button !== 0) return false;\n\tconst { target } = event;\n\tif (!isElement(target) || !row.contains(target)) return false;\n\tconst control = target.closest(ROW_CONTROL_SELECTOR);\n\tif (control && control !== row && row.contains(control)) return false;\n\tconst selection = row.ownerDocument?.getSelection?.();\n\tif (\n\t\tselection &&\n\t\t!selection.isCollapsed &&\n\t\tselection.anchorNode &&\n\t\trow.contains(selection.anchorNode)\n\t) {\n\t\treturn false;\n\t}\n\treturn true;\n}\n\n/** Follows the row's link (`ROW_LINK_SELECTOR`) as if it had been clicked,\n * with the modifier keys of `event`: a router's link navigates as it does on\n * its own click, Ctrl/Cmd+click opens a tab where the browser allows it.\n * `false` when the row has no link. */\nexport function followRowLink(row: Element, event: RowClick): boolean {\n\tconst link = row.querySelector(ROW_LINK_SELECTOR);\n\tconst view = row.ownerDocument?.defaultView;\n\tif (!link || !view) return false;\n\tlink.dispatchEvent(\n\t\tnew view.MouseEvent('click', {\n\t\t\tbubbles: true,\n\t\t\tcancelable: true,\n\t\t\tbutton: 0,\n\t\t\tctrlKey: event.ctrlKey,\n\t\t\tmetaKey: event.metaKey,\n\t\t\tshiftKey: event.shiftKey,\n\t\t\taltKey: event.altKey,\n\t\t}),\n\t);\n\treturn true;\n}\n"
6
+ ],
7
+ "mappings": ";AAOO,IAAM,uBAAuB;AAAA,EACnC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD,EAAE,KAAK,GAAG;AAMH,IAAM,oBACZ;AAaD,SAAS,SAAS,CAAC,QAA+C;AAAA,EACjE,OAAO,WAAW,QAAQ,OAAQ,OAAmB,YAAY;AAAA;AAO3D,SAAS,eAAe,CAAC,OAAiB,KAAuB;AAAA,EACvE,IAAI,MAAM,oBAAoB,MAAM,WAAW;AAAA,IAAG,OAAO;AAAA,EACzD,QAAQ,WAAW;AAAA,EACnB,IAAI,CAAC,UAAU,MAAM,KAAK,CAAC,IAAI,SAAS,MAAM;AAAA,IAAG,OAAO;AAAA,EACxD,MAAM,UAAU,OAAO,QAAQ,oBAAoB;AAAA,EACnD,IAAI,WAAW,YAAY,OAAO,IAAI,SAAS,OAAO;AAAA,IAAG,OAAO;AAAA,EAChE,MAAM,YAAY,IAAI,eAAe,eAAe;AAAA,EACpD,IACC,aACA,CAAC,UAAU,eACX,UAAU,cACV,IAAI,SAAS,UAAU,UAAU,GAChC;AAAA,IACD,OAAO;AAAA,EACR;AAAA,EACA,OAAO;AAAA;AAOD,SAAS,aAAa,CAAC,KAAc,OAA0B;AAAA,EACrE,MAAM,OAAO,IAAI,cAAc,iBAAiB;AAAA,EAChD,MAAM,OAAO,IAAI,eAAe;AAAA,EAChC,IAAI,CAAC,QAAQ,CAAC;AAAA,IAAM,OAAO;AAAA,EAC3B,KAAK,cACJ,IAAI,KAAK,WAAW,SAAS;AAAA,IAC5B,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,QAAQ;AAAA,IACR,SAAS,MAAM;AAAA,IACf,SAAS,MAAM;AAAA,IACf,UAAU,MAAM;AAAA,IAChB,QAAQ,MAAM;AAAA,EACf,CAAC,CACF;AAAA,EACA,OAAO;AAAA;",
8
+ "debugId": "B5C72BCA21047D6B64756E2164756E21",
9
+ "names": []
10
+ }
package/docs/roadmap.md CHANGED
@@ -31,6 +31,10 @@ Nothing in progress right now; see Next.
31
31
 
32
32
  ## Shipped
33
33
 
34
+ - **Clickable table rows** (`/row-activation`) — whether a click on a row
35
+ should follow its link (not on a control, not in a portal, not the end of
36
+ a text selection) and following it with the click's modifier keys, for
37
+ both libraries' `DataTable`. 0.4.0.
34
38
  - **Date range presets and times** (`/date-range`) — the built-in presets'
35
39
  ranges, whether a field's rules rule a range out, and the time of day on
36
40
  each end, for both libraries' `DateRangeField`. 0.3.0.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nxgt/material-core",
3
- "version": "0.3.1",
3
+ "version": "0.4.0",
4
4
  "description": "The framework-free core of @nxgt/material and @nxgt/material-vue: the PDF document model and its react-pdf renderer, print formatters, PDF file helpers, the PDF viewer's pure modules, the currency digits, the file-size formatter, the field-blur layers, the date range presets, the file preview's kind detection and text reader, the pdfkit fonts Vite plugin and the Node-only pdfkit fonts resolution for Next.js and Nuxt builds",
5
5
  "license": "MIT",
6
6
  "bugs": {
@@ -13,6 +13,7 @@
13
13
  "dist",
14
14
  "docs",
15
15
  "README.md",
16
+ "CHANGELOG.md",
16
17
  "package.json",
17
18
  "LICENSE"
18
19
  ],
@@ -92,6 +93,11 @@
92
93
  "import": "./dist/lib/field-blur-layers.js",
93
94
  "default": "./dist/lib/field-blur-layers.js"
94
95
  },
96
+ "./row-activation": {
97
+ "types": "./dist/lib/row-activation.d.ts",
98
+ "import": "./dist/lib/row-activation.js",
99
+ "default": "./dist/lib/row-activation.js"
100
+ },
95
101
  "./format-file-size": {
96
102
  "types": "./dist/lib/format-file-size.d.ts",
97
103
  "import": "./dist/lib/format-file-size.js",
@@ -171,6 +177,7 @@
171
177
  "src/lib/pdf-download.ts",
172
178
  "src/lib/currency-digits.ts",
173
179
  "src/lib/field-blur-layers.ts",
180
+ "src/lib/row-activation.ts",
174
181
  "src/lib/format-file-size.ts",
175
182
  "src/lib/slider-ticks.ts",
176
183
  "src/lib/date-range.ts",