@nxgt/material-core 0.3.2 → 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 CHANGED
@@ -1,5 +1,22 @@
1
1
  # @nxgt/material-core
2
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
+
3
20
  ## 0.3.2
4
21
 
5
22
  ### Patch Changes
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.2",
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": {
@@ -93,6 +93,11 @@
93
93
  "import": "./dist/lib/field-blur-layers.js",
94
94
  "default": "./dist/lib/field-blur-layers.js"
95
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
+ },
96
101
  "./format-file-size": {
97
102
  "types": "./dist/lib/format-file-size.d.ts",
98
103
  "import": "./dist/lib/format-file-size.js",
@@ -172,6 +177,7 @@
172
177
  "src/lib/pdf-download.ts",
173
178
  "src/lib/currency-digits.ts",
174
179
  "src/lib/field-blur-layers.ts",
180
+ "src/lib/row-activation.ts",
175
181
  "src/lib/format-file-size.ts",
176
182
  "src/lib/slider-ticks.ts",
177
183
  "src/lib/date-range.ts",