@myrmidon/paged-data-browsers 4.0.1 → 4.0.2

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/README.md CHANGED
@@ -21,74 +21,125 @@ So you need:
21
21
 
22
22
  ## Paged Tree
23
23
 
24
- ### Services
25
-
26
- The core of the tree is a `PagedTreeStore`.
27
-
28
- Tree **nodes** extend interface `TreeNode`, having:
29
-
30
- - `id`
31
- - `parentId`
32
- - `y` and `x`
33
- - `label`
34
- - `tag`
35
- - `hasChildren`
36
-
37
- The corresponding **node filter** (`TreeNodeFilter`) has `tags` and `parentId`, both optional. Your filters will extend this one, just like your nodes extend `TreeNode`.
38
-
39
- A **paged tree node** (`PagedTreeNode<F>` where `F` is `TreeNodeFilter` or any extension of it) extends a `TreeNode` by adding paging (`PagingInfo`: page number, count and total items), filtering, and expansion state. All the nodes entering the store are enriched with such data, thus becoming paged nodes.
24
+ ### Nodes
40
25
 
41
- The **service** which fetches nodes into the store is `PagedTreeStoreService<F>`. It provides a single method, `getNodes(filter, pageNumber, pageSize, hasMockRoot?)` to get the specified page of nodes.
26
+ A paged tree is based on **nodes** of type `TreeNode` or its derived types. This basic tree node contains:
42
27
 
43
- The **paged tree store** (`PagedTreeStore<E,F>` where `E`=paged tree node and `F`=node filter) is the local store for tree nodes. Its ideal hierarchy is:
28
+ - `id`: a unique numeric ID.
29
+ - `parentId`: the ID of the parent, or undefined if it's a root-level node.
30
+ - `y`: Y is the node depth, starting from 1. As we often need a single root node (for paging its children), unless we have a small number of root nodes, when your data source does not return a single root node you have the option of using a mock root. In this case, the mock root Y level will be 0 and all the other nodes will descend from it. Anyway you can also have many root level nodes, whose parent ID is undefined; but in this case all these nodes will appear at once without any paging, because paging is governed by the parent node.
31
+ - `x`: X is the ordinal number of the node among its siblings. The first child of a given node has X=1, the second has X=2, and so forth.
32
+ - `label`: a human-friendly label for the node.
33
+ - `tag`: a tag string for virtually categorizing or grouping the node in some way.
34
+ - `hasChildren`: true if the node has children, false if it has no children; undefined if this has not yet been determined.
44
35
 
45
- - *radix*: a single, static radix node (Y=0, X=1) including all the other nodes. This is like the ground where one or more trees can be planted. Each tree is identified by a node's tag.
46
- - *roots*: the direct children of the radix (Y=1, X=1-N).
47
- - other descendant nodes, each with a higher Y level.
36
+ A **paged tree node** type derives from `TreeNode` adding two essential components:
48
37
 
49
- The essential store data are:
38
+ - a _filter_ for its children. The basic `TreeNodeFilter` just refers to `TreeNode` properties, so it just has tag and parent ID. You can derive your own filter from this type.
39
+ - _paging information_ for its children. This is of type `PagingInfo` which has page number, page items count, and total items count.
50
40
 
51
- - flat list of paged nodes (`nodes$`).
52
- - list of tree tags (`tags$`).
53
- - global filter (`filter$`). This gets combined with (overridden by) node-specific filters, when specified. You can set it with `setFilter`. To set the filter for the children of a specific node use `setNodeFilter`.
54
- - page size (`pageSize`), get/set property.
41
+ The base type for a paged tree node is thus `PagedTreeNode<F>`, where `F` is the type of the filter used for filtering nodes; this adds:
55
42
 
56
- To initialize the store, you call `reset`, which loads root nodes (via its service's `getRootNodes`) and their direct children. When getting the children for each root node, an internal cache is used to minimize server fetches (via `getPageFromCacheOrServer`). The nodes got from the service are enriched with data required as paged tree nodes (paging, filtering, expansion).
43
+ - `paging`: paging information. This is required.
44
+ - `expanded`: for expanded/collapsed state.
45
+ - `filter`: filter object.
57
46
 
58
- >Methods related to the cache are `clearCache()` and `hasCachedPage(pageNumber, filter)`.
47
+ ### Services
59
48
 
60
- Nodes are managed in the tree with:
49
+ Your data must be provided by a service implementing interface `PagedTreeStoreService<F>`, where `F` is the tree node filter type. This requires you to implement a single function, `getNodes`, which returns a specific page of nodes, applying the specified filter.
50
+
51
+ >This service deals with `TreeNode`'s (or their derivations), returning a specific page of them. It is the store which extends these nodes with paging and filtering data using the `PagedTreeNode` type. Note that if you want a single root mock node, your implementation of this service must provide it (with Y=0).
52
+
53
+ The tree logic is then implemented by the `PagedTreeStore<E,F>`, where `E` is the element (node) type (a `PagedTreeNode<F>` or any derived type), and `F` is the filter type (a `TreeNodeFilter` or any derived type).
54
+
55
+ The store is used to load and manage a flat list of nodes. Every tree node in the list is extended with page number, page count and total items, plus expansion-related metadata. Users can expand and collapse nodes, browse through pages of children, and filter them.
56
+
57
+ The essential store **data** are:
58
+
59
+ - the flat _list of paged nodes_ (exposed in `nodes$`). This is populated by using an instance of `PagedTreeStoreService<F>`, injected in the store constructor together with its options (of type `PagedTreeStoreOptions`). Among other things, the options specify whether there must be a single mock root node, and the default page size. Once retrieved, pages can be fetched from an internal LRU cache (which is anyway cleared when calling `reset`).
60
+ - a list of tree _tags_ (exposed in `tags$`).
61
+ - a _global filter_ (exposed in `filter$`). This gets combined with (overridden by) node-specific filters, when specified. You can set it with `setFilter`. To set the filter for the children of a specific node use `setNodeFilter`.
62
+ - the _page size_ (`pageSize`), a get/set property, initially set by the options injected in the store constructor. Setting this property resets the store (like calling `reset`).
63
+
64
+ To initialize the store, you call `reset`, which loads root nodes (via its service's `getRootNodes`) and their direct children. You then expand or collapse nodes, change page, and set the global or node filters as desired.
65
+
66
+ The main **methods** are:
67
+
68
+ - `reset()`: reset the store, loading root node(s) and peeking at their direct children to determine whether they can be expanded.
69
+ - `clear()`: clear the whole store, emptying the cache and removing all the nodes.
70
+ - `clearCache()`: clears the pages cache.
71
+ - `hasCachedPage(pageNumber, filter)`: checks whether the specified page is cached.
72
+ - `isEmtpy()`: true if the list is empty.
73
+ - `getNodes()`: gets all the nodes in the list.
74
+ - `getRootNode()`: returns the root node, i.e. the first node in the list (unless this is empty).
75
+ - `getChildren(id)`: gets the children of the specified node.
76
+
77
+ - `expand(id)`: expand the specified node (if it has children and is collapsed).
78
+ - `expandAll(id)`: expand all the descendants of the specified node.
79
+ - `collapse(id)`: collapse the specified node if expanded.
80
+ - `collapseAll()`: collpase all the descendants of the specified node.
81
+
82
+ - `changePage(parentId, pageNumber)`: change the page of children nodes of the specified parent node.
83
+ - `setFilter(filter)`: sets the global filter, resetting the store.
84
+ - `setNodeFilter(id, filter)`: sets the node-filter for the specified node, resetting its children page number to 1.
85
+
86
+ The store is a plain class. If you want to persist it in the app, you can wrap it in a service and inject the service into your component. If you don't need this, just implement your data service to provide nodes via `getNodes`. Otherwise, you can wrap the store like e.g. here using a singleton for a single page of nodes in the demo app:
87
+
88
+ ```ts
89
+ @Injectable({
90
+ providedIn: 'root',
91
+ })
92
+ export class PagedTreeBrowserService {
93
+ public store: PagedTreeStore<MockTreeNode, MockTreeFilter>;
94
+
95
+ constructor() {
96
+ this.store = new PagedTreeStore<MockTreeNode, MockTreeFilter>(
97
+ new MockPagedTreeStoreService()
98
+ );
99
+ }
100
+
101
+ /**
102
+ * Set the mock root node for the paged list store. This is for testing
103
+ * purposes; in a real world scenario, this parameter would hardly change
104
+ * once set.
105
+ * @param on Whether to enable the mock root node.
106
+ */
107
+ public setHasMockRoot(on: boolean): void {
108
+ this.store = new PagedTreeStore<MockTreeNode, MockTreeFilter>(
109
+ new MockPagedTreeStoreService(),
110
+ {
111
+ ...DEFAULT_PAGED_LIST_STORE_OPTIONS,
112
+ hasMockRoot: on,
113
+ } as PagedTreeStoreOptions
114
+ );
115
+ }
116
+ }
117
+ ```
61
118
 
62
- - `expand(id)`
63
- - `expandAll(id)`
64
- - `getChildren(id)`
65
- - `getNodes()`
66
- - `getRootNode()`
67
- - `isEmtpy()`
68
- - `collapse(id)`
69
- - `collapseAll()`
70
- - `changePage(parentId, pageNumber)`
71
- - `setFilter(filter)`
72
- - `setNodeFilter(id, filter)`
73
- - `reset()`
119
+ ### Node Component
74
120
 
75
- ### Component
121
+ The only piece of UI provided by this library is for displaying a single node of the tree. The component for visualizing each single **node** of the paged tree is `BrowserTreeNodeComponent`. This wraps some HTML content providing a toggle button to expand/collapse the node, a paging control for the node's children, and a button to edit the node's filter.
76
122
 
77
- The component for visualizing each single **node** of the paged tree is `BrowserTreeNodeComponent`. This wraps some HTML content providing a toggle button to expand/collapse the node, a paging control for the node's children, and a button to edit the node's filter. You should then provide the HTML content to display the node's data inside this component, e.g.:
123
+ Unless you are satisfied with these essential data, you can then provide the HTML content to display more node's data inside this component, e.g.:
78
124
 
79
125
  ```html
80
126
  <pdb-browser-tree-node [node]="node">
127
+ <!-- add your data here... -->
81
128
  <your-node-view [node]="node" />
82
129
  <pdb-browser-tree-node>
83
130
  ```
84
131
 
85
132
  This component API has:
86
133
 
87
- - ➡️ `node` of type `PagedTreeNode`.
88
- - ➡️ `paging` (`PagingInfo`) with optional data about children nodes paging.
89
- - ➡️ `debug` (`boolean`) flag to toggle debug information in the view.
90
- - ➡️ `hideLabel` (`boolean`) flag to hide the node's loc and label. This is useful if you want to provide your own view for the node, between the expansion toggle and the filter edit button. In this case, in your consumer template provide your own view as the content of this component. If instead you are fine with the default loc and label, and just want to add more data to the view, then you can just add your own content to this component's template, without setting this property to true.
91
- - ➡️ `hidePaging` (`boolean`): true to hide the node's paging control unless hovered.
134
+ - ▶️ `node` (`PagedTreeNode`) to display.
135
+ - ▶️ `paging` (`PagingInfo`): optional paging information about node's children.
136
+ - ▶️ `debug` (`boolean`) flag to toggle debug information in the view.
137
+ - ▶️ `hideLabel` (`boolean`) flag to hide the node's loc and label. This is useful if you want to provide your own view for the node, between the expansion toggle and the filter edit button. In this case, in your consumer template provide your own view as the content of this component. If instead you are fine with the default loc and label, and just want to add more data to the view, then you can just add your own content to this component's template, without setting this property to true.
138
+ - ▶️ `hideLoc` (`boolean`): true to hide the node's location (X and Y).
139
+ - ▶️ `hideFilter` (`boolean`): true to hide the node's filter edit button. Do this when you do not want to allow users to apply per-node filters.
140
+ - ▶️ `hidePaging` (`boolean`): true to hide the node's paging control unless hovered.
141
+ - ▶️ `indentSize` (`number`): the indent size for the node's children.
142
+ - ▶️ `rangeWidth` (`number`): the width of the range view. This is a small horizontal bar showing you the location of the page in the set of nodes.
92
143
  - 🔥 `toggleExpandedRequest` (`PagedTreeNode`): emitted when the user wants to toggle the expanded state of the node.
93
144
  - 🔥 `changePageRequest` (`PageChangeRequest`): emitted when the user wants to change the page number of the node's children.
94
145
  - 🔥 `editNodeFilterRequest` (`PagedTreeNode`): emitted when the user wants to edit the node's filter for its children.
@@ -99,14 +150,3 @@ You should provide your components for:
99
150
  - the tree browser component. This combines:
100
151
  - a filters component for global filters. This dummy component gets a `filter$` and emits `filterChange`.
101
152
  - a tree view.
102
-
103
- The tree browser component uses a nested instance of `PagedTreeStore<N,F>`, typically injected via a middleman service like the sample `PagedTreeBrowserService`, which provides a new instance of the store to the component. The component orchestrates:
104
-
105
- - `filter$` (observable of filters). This simply binds to the global filter of the nested store.
106
- - `nodes$` (observable of paged tree nodes). Each of these nodes is displayed via a `BrowserTreeNodeComponent`. This simply binds to the nodes from the nested store, so these are all the nodes present in it. They are displayed in a list with various amounts of indentation representing the tree's hierarchy.
107
- - page change requests
108
- - filter change requests
109
- - expand/collapse requests
110
- - reset requests
111
-
112
- All the requests are accomplished by virtue of the nested store.
@@ -48,10 +48,10 @@ class CompactPagerComponent {
48
48
  });
49
49
  }
50
50
  }
51
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.0", ngImport: i0, type: CompactPagerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
52
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.0.0", type: CompactPagerComponent, isStandalone: true, selector: "pdb-compact-pager", inputs: { paging: { classPropertyName: "paging", publicName: "paging", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { pagingChange: "pagingChange" }, ngImport: i0, template: "@if (paging().pageCount) {\n <div class=\"form-row\">\n <span id=\"pages\">{{ paging().pageNumber }}/{{ paging().pageCount }}</span>\n <button\n type=\"button\"\n mat-icon-button\n (click)=\"onFirst()\"\n [disabled]=\"paging().pageNumber < 2\"\n >\n <mat-icon>first_page</mat-icon>\n </button>\n <button\n type=\"button\"\n mat-icon-button\n (click)=\"onPrevious()\"\n [disabled]=\"paging().pageNumber < 2\"\n >\n <mat-icon>navigate_before</mat-icon>\n </button>\n <button\n type=\"button\"\n mat-icon-button\n (click)=\"onNext()\"\n [disabled]=\"paging().pageNumber === paging().pageCount\"\n >\n <mat-icon>navigate_next</mat-icon>\n </button>\n <button\n type=\"button\"\n mat-icon-button\n (click)=\"onLast()\"\n [disabled]=\"paging().pageNumber === paging().pageCount\"\n >\n <mat-icon>last_page</mat-icon>\n </button>\n <span id=\"total\">{{ paging().total }} </span>\n </div>\n}\n", styles: ["#pages,#total{color:silver}.form-row{display:flex;gap:2px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }] }); }
51
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.5", ngImport: i0, type: CompactPagerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
52
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.0.5", type: CompactPagerComponent, isStandalone: true, selector: "pdb-compact-pager", inputs: { paging: { classPropertyName: "paging", publicName: "paging", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { pagingChange: "pagingChange" }, ngImport: i0, template: "@if (paging().pageCount) {\n <div class=\"form-row\">\n <span id=\"pages\">{{ paging().pageNumber }}/{{ paging().pageCount }}</span>\n <button\n type=\"button\"\n mat-icon-button\n (click)=\"onFirst()\"\n [disabled]=\"paging().pageNumber < 2\"\n >\n <mat-icon>first_page</mat-icon>\n </button>\n <button\n type=\"button\"\n mat-icon-button\n (click)=\"onPrevious()\"\n [disabled]=\"paging().pageNumber < 2\"\n >\n <mat-icon>navigate_before</mat-icon>\n </button>\n <button\n type=\"button\"\n mat-icon-button\n (click)=\"onNext()\"\n [disabled]=\"paging().pageNumber === paging().pageCount\"\n >\n <mat-icon>navigate_next</mat-icon>\n </button>\n <button\n type=\"button\"\n mat-icon-button\n (click)=\"onLast()\"\n [disabled]=\"paging().pageNumber === paging().pageCount\"\n >\n <mat-icon>last_page</mat-icon>\n </button>\n <span id=\"total\">{{ paging().total }} </span>\n </div>\n}\n", styles: ["#pages,#total{color:silver}.form-row{display:flex;gap:2px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }] }); }
53
53
  }
54
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.0", ngImport: i0, type: CompactPagerComponent, decorators: [{
54
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.5", ngImport: i0, type: CompactPagerComponent, decorators: [{
55
55
  type: Component,
56
56
  args: [{ selector: 'pdb-compact-pager', imports: [CommonModule, MatButtonModule, MatIconModule], template: "@if (paging().pageCount) {\n <div class=\"form-row\">\n <span id=\"pages\">{{ paging().pageNumber }}/{{ paging().pageCount }}</span>\n <button\n type=\"button\"\n mat-icon-button\n (click)=\"onFirst()\"\n [disabled]=\"paging().pageNumber < 2\"\n >\n <mat-icon>first_page</mat-icon>\n </button>\n <button\n type=\"button\"\n mat-icon-button\n (click)=\"onPrevious()\"\n [disabled]=\"paging().pageNumber < 2\"\n >\n <mat-icon>navigate_before</mat-icon>\n </button>\n <button\n type=\"button\"\n mat-icon-button\n (click)=\"onNext()\"\n [disabled]=\"paging().pageNumber === paging().pageCount\"\n >\n <mat-icon>navigate_next</mat-icon>\n </button>\n <button\n type=\"button\"\n mat-icon-button\n (click)=\"onLast()\"\n [disabled]=\"paging().pageNumber === paging().pageCount\"\n >\n <mat-icon>last_page</mat-icon>\n </button>\n <span id=\"total\">{{ paging().total }} </span>\n </div>\n}\n", styles: ["#pages,#total{color:silver}.form-row{display:flex;gap:2px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}\n"] }]
57
57
  }] });
@@ -87,10 +87,10 @@ class RangeViewComponent {
87
87
  ];
88
88
  });
89
89
  }
90
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.0", ngImport: i0, type: RangeViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
91
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "19.0.0", type: RangeViewComponent, isStandalone: true, selector: "pdb-range-view", inputs: { domain: { classPropertyName: "domain", publicName: "domain", isSignal: true, isRequired: false, transformFunction: null }, range: { classPropertyName: "range", publicName: "range", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<svg [attr.width]=\"width()\" [attr.height]=\"height()\">\n <rect id=\"rdomain\" [attr.width]=\"width()\" [attr.height]=\"height()\" />\n <rect\n id=\"rrange\"\n [attr.x]=\"scaledRange()[0]\"\n [attr.y]=\"0\"\n [attr.width]=\"scaledRange()[1] - scaledRange()[0]\"\n [attr.height]=\"height()\"\n />\n</svg>\n", styles: ["#rdomain{fill:#d3d3d3;stroke-width:3;stroke:#c1ba9b}#rrange{fill:#91aad3;stroke-width:3}\n"] }); }
90
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.5", ngImport: i0, type: RangeViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
91
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "19.0.5", type: RangeViewComponent, isStandalone: true, selector: "pdb-range-view", inputs: { domain: { classPropertyName: "domain", publicName: "domain", isSignal: true, isRequired: false, transformFunction: null }, range: { classPropertyName: "range", publicName: "range", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<svg [attr.width]=\"width()\" [attr.height]=\"height()\">\n <rect id=\"rdomain\" [attr.width]=\"width()\" [attr.height]=\"height()\" />\n <rect\n id=\"rrange\"\n [attr.x]=\"scaledRange()[0]\"\n [attr.y]=\"0\"\n [attr.width]=\"scaledRange()[1] - scaledRange()[0]\"\n [attr.height]=\"height()\"\n />\n</svg>\n", styles: ["#rdomain{fill:#d3d3d3;stroke-width:3;stroke:#c1ba9b}#rrange{fill:#91aad3;stroke-width:3}\n"] }); }
92
92
  }
93
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.0", ngImport: i0, type: RangeViewComponent, decorators: [{
93
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.5", ngImport: i0, type: RangeViewComponent, decorators: [{
94
94
  type: Component,
95
95
  args: [{ selector: 'pdb-range-view', template: "<svg [attr.width]=\"width()\" [attr.height]=\"height()\">\n <rect id=\"rdomain\" [attr.width]=\"width()\" [attr.height]=\"height()\" />\n <rect\n id=\"rrange\"\n [attr.x]=\"scaledRange()[0]\"\n [attr.y]=\"0\"\n [attr.width]=\"scaledRange()[1] - scaledRange()[0]\"\n [attr.height]=\"height()\"\n />\n</svg>\n", styles: ["#rdomain{fill:#d3d3d3;stroke-width:3;stroke:#c1ba9b}#rrange{fill:#91aad3;stroke-width:3}\n"] }]
96
96
  }], ctorParameters: () => [] });
@@ -178,14 +178,14 @@ class BrowserTreeNodeComponent {
178
178
  this.editNodeFilterRequest.emit(this.node());
179
179
  }
180
180
  }
181
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.0", ngImport: i0, type: BrowserTreeNodeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
182
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.0.0", type: BrowserTreeNodeComponent, isStandalone: true, selector: "pdb-browser-tree-node", inputs: { node: { classPropertyName: "node", publicName: "node", isSignal: true, isRequired: false, transformFunction: null }, paging: { classPropertyName: "paging", publicName: "paging", isSignal: true, isRequired: false, transformFunction: null }, debug: { classPropertyName: "debug", publicName: "debug", isSignal: true, isRequired: false, transformFunction: null }, hideLabel: { classPropertyName: "hideLabel", publicName: "hideLabel", isSignal: true, isRequired: false, transformFunction: null }, hideLoc: { classPropertyName: "hideLoc", publicName: "hideLoc", isSignal: true, isRequired: false, transformFunction: null }, hidePaging: { classPropertyName: "hidePaging", publicName: "hidePaging", isSignal: true, isRequired: false, transformFunction: null }, hideFilter: { classPropertyName: "hideFilter", publicName: "hideFilter", isSignal: true, isRequired: false, transformFunction: null }, indentSize: { classPropertyName: "indentSize", publicName: "indentSize", isSignal: true, isRequired: false, transformFunction: null }, rangeWidth: { classPropertyName: "rangeWidth", publicName: "rangeWidth", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { toggleExpandedRequest: "toggleExpandedRequest", changePageRequest: "changePageRequest", editNodeFilterRequest: "editNodeFilterRequest" }, ngImport: i0, template: "@if (node()) {\n<div id=\"node\" [style.margin-left.px]=\"(node()!.y - 1) * indentSize()\">\n <!-- pager -->\n @if (node()!.expanded && paging() && paging()!.pageCount > 1) {\n <div id=\"pager\" [style.display]=\"hidePaging() ? 'inherit' : 'block'\">\n <pdb-compact-pager\n [paging]=\"paging()!\"\n (pagingChange)=\"onPagingChange(node()!, $event)\"\n />\n <pdb-range-view\n [width]=\"rangeWidth()\"\n [domain]=\"[0, paging()!.pageCount]\"\n [range]=\"[paging()!.pageNumber - 1, paging()!.pageNumber]\"\n />\n </div>\n }\n <!-- node -->\n <div class=\"form-row\">\n <!-- expand/collapse button -->\n <button\n type=\"button\"\n mat-icon-button\n [matTooltip]=\"node()?.expanded ? 'Collapse' : 'Expand'\"\n i18n-matTooltip\n [disabled]=\"node()!.hasChildren === false\"\n (click)=\"onToggleExpanded()\"\n >\n <mat-icon class=\"mat-primary\" i18n>{{\n node()!.hasChildren === true || node()!.hasChildren === undefined\n ? node()?.expanded\n ? \"expand_less\"\n : \"expand_more\"\n : \"stop\"\n }}</mat-icon>\n </button>\n\n <!-- tag -->\n @if (!hideLabel()) {\n <span\n class=\"tag\"\n [ngStyle]=\"{\n 'background-color': (node()!.tag | stringToColor),\n color: node()!.tag | stringToColor | colorToContrast\n }\"\n >{{ node()!.tag }}</span\n >\n\n <!-- loc and label -->\n @if (!hideLoc()) {\n <span class=\"loc\">{{ node()!.y }}.{{ node()!.x }}</span> - }\n {{ node()!.label }}\n }\n\n <!-- PROJECTED NODE -->\n <ng-content></ng-content>\n\n <!-- debug -->\n @if (debug()) {\n <span class=\"debug\"\n >#{{ node()!.id }}\n <span\n >| {{ node()!.paging.pageNumber }}/{{ node()!.paging.pageCount }} ({{\n node()!.paging.total\n }})</span\n ></span\n >\n }\n\n <!-- filter -->\n @if (!hideFilter()){ @if (!node()?.filter && node()?.y) {\n <div class=\"muted\">\n <button\n type=\"button\"\n mat-icon-button\n matTooltip=\"Add filter\"\n i18n-matTooltip\n (click)=\"onEditFilter()\"\n >\n <mat-icon>filter_list</mat-icon>\n </button>\n </div>\n } @if (node()?.filter && node()?.y) {\n <div class=\"muted\">\n <button type=\"button\" mat-icon-button (click)=\"onEditFilter()\">\n <mat-icon [matBadge]=\"node()?.filter ? 'F' : ''\">filter_alt</mat-icon>\n </button>\n </div>\n } }\n </div>\n</div>\n}\n", styles: [":root{--browser-tree-node-margin-bottom: 4px;--browser-tree-node-padding: 4px 6px;--browser-tree-node-border: 1px solid #98a8d4;--browser-tree-node-border-radius: 6px;--browser-tree-node-hover-bg-color: #d6dee9}#node{margin-bottom:var(--browser-tree-node-margin-bottom);padding:var(--browser-tree-node-padding);border:var(--browser-tree-node-border);border-radius:var(--browser-tree-node-border-radius)}#node:hover{background-color:var(--browser-tree-node-hover-bg-color)}.form-row{display:flex;gap:8px;align-items:center}.form-row *{flex:0 0 auto}.form-row span{flex:0 1 auto;white-space:normal}#node #pager{display:none}#node:hover #pager{display:block}span.loc{font-size:.85em;color:#666;vertical-align:middle}span.tag{border:1px solid #aaa;border-radius:4px;padding:0 4px}fieldset{border:1px solid silver;border-radius:6px;padding:4px 6px;margin:4px 0}legend{color:silver}.muted{opacity:.3}.muted:hover{opacity:1}.debug{font-size:.85em;color:#9c3d3e}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: MatBadgeModule }, { kind: "directive", type: i2$1.MatBadge, selector: "[matBadge]", inputs: ["matBadgeColor", "matBadgeOverlap", "matBadgeDisabled", "matBadgePosition", "matBadge", "matBadgeDescription", "matBadgeSize", "matBadgeHidden"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i5.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "pipe", type:
181
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.5", ngImport: i0, type: BrowserTreeNodeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
182
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.0.5", type: BrowserTreeNodeComponent, isStandalone: true, selector: "pdb-browser-tree-node", inputs: { node: { classPropertyName: "node", publicName: "node", isSignal: true, isRequired: false, transformFunction: null }, paging: { classPropertyName: "paging", publicName: "paging", isSignal: true, isRequired: false, transformFunction: null }, debug: { classPropertyName: "debug", publicName: "debug", isSignal: true, isRequired: false, transformFunction: null }, hideLabel: { classPropertyName: "hideLabel", publicName: "hideLabel", isSignal: true, isRequired: false, transformFunction: null }, hideLoc: { classPropertyName: "hideLoc", publicName: "hideLoc", isSignal: true, isRequired: false, transformFunction: null }, hidePaging: { classPropertyName: "hidePaging", publicName: "hidePaging", isSignal: true, isRequired: false, transformFunction: null }, hideFilter: { classPropertyName: "hideFilter", publicName: "hideFilter", isSignal: true, isRequired: false, transformFunction: null }, indentSize: { classPropertyName: "indentSize", publicName: "indentSize", isSignal: true, isRequired: false, transformFunction: null }, rangeWidth: { classPropertyName: "rangeWidth", publicName: "rangeWidth", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { toggleExpandedRequest: "toggleExpandedRequest", changePageRequest: "changePageRequest", editNodeFilterRequest: "editNodeFilterRequest" }, ngImport: i0, template: "@if (node()) {\n<div id=\"node\" [style.margin-left.px]=\"(node()!.y - 1) * indentSize()\">\n <!-- pager -->\n @if (node()!.expanded && paging() && paging()!.pageCount > 1) {\n <div id=\"pager\" [style.display]=\"hidePaging() ? 'inherit' : 'block'\">\n <pdb-compact-pager\n [paging]=\"paging()!\"\n (pagingChange)=\"onPagingChange(node()!, $event)\"\n />\n <pdb-range-view\n [width]=\"rangeWidth()\"\n [domain]=\"[0, paging()!.pageCount]\"\n [range]=\"[paging()!.pageNumber - 1, paging()!.pageNumber]\"\n />\n </div>\n }\n <!-- node -->\n <div class=\"form-row\">\n <!-- expand/collapse button -->\n <button\n type=\"button\"\n mat-icon-button\n [matTooltip]=\"node()?.expanded ? 'Collapse' : 'Expand'\"\n i18n-matTooltip\n [disabled]=\"node()!.hasChildren === false\"\n (click)=\"onToggleExpanded()\"\n >\n <mat-icon class=\"mat-primary\" i18n>{{\n node()!.hasChildren === true || node()!.hasChildren === undefined\n ? node()?.expanded\n ? \"expand_less\"\n : \"expand_more\"\n : \"stop\"\n }}</mat-icon>\n </button>\n\n <!-- tag -->\n @if (!hideLabel()) {\n <span\n class=\"tag\"\n [ngStyle]=\"{\n 'background-color': (node()!.tag | stringToColor),\n color: node()!.tag | stringToColor | colorToContrast\n }\"\n >{{ node()!.tag }}</span\n >\n\n <!-- loc and label -->\n @if (!hideLoc()) {\n <span class=\"loc\">{{ node()!.y }}.{{ node()!.x }}</span> - }\n {{ node()!.label }}\n }\n\n <!-- PROJECTED NODE -->\n <ng-content></ng-content>\n\n <!-- debug -->\n @if (debug()) {\n <span class=\"debug\"\n >#{{ node()!.id }}\n <span\n >| {{ node()!.paging.pageNumber }}/{{ node()!.paging.pageCount }} ({{\n node()!.paging.total\n }})</span\n ></span\n >\n }\n\n <!-- filter -->\n @if (!hideFilter()){ @if (!node()?.filter && node()?.y) {\n <div class=\"muted\">\n <button\n type=\"button\"\n mat-icon-button\n matTooltip=\"Add filter\"\n i18n-matTooltip\n (click)=\"onEditFilter()\"\n >\n <mat-icon>filter_list</mat-icon>\n </button>\n </div>\n } @if (node()?.filter && node()?.y) {\n <div class=\"muted\">\n <button type=\"button\" mat-icon-button (click)=\"onEditFilter()\">\n <mat-icon [matBadge]=\"node()?.filter ? 'F' : ''\">filter_alt</mat-icon>\n </button>\n </div>\n } }\n </div>\n</div>\n}\n", styles: [":root{--browser-tree-node-margin-bottom: 4px;--browser-tree-node-padding: 4px 6px;--browser-tree-node-border: 1px solid #98a8d4;--browser-tree-node-border-radius: 6px;--browser-tree-node-hover-bg-color: #d6dee9}#node{margin-bottom:var(--browser-tree-node-margin-bottom);padding:var(--browser-tree-node-padding);border:var(--browser-tree-node-border);border-radius:var(--browser-tree-node-border-radius)}#node:hover{background-color:var(--browser-tree-node-hover-bg-color)}.form-row{display:flex;gap:8px;align-items:center}.form-row *{flex:0 0 auto}.form-row span{flex:0 1 auto;white-space:normal}#node #pager{display:none}#node:hover #pager{display:block}span.loc{font-size:.85em;color:#666;vertical-align:middle}span.tag{border:1px solid #aaa;border-radius:4px;padding:0 4px}fieldset{border:1px solid silver;border-radius:6px;padding:4px 6px;margin:4px 0}legend{color:silver}.muted{opacity:.3}.muted:hover{opacity:1}.debug{font-size:.85em;color:#9c3d3e}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: MatBadgeModule }, { kind: "directive", type: i2$1.MatBadge, selector: "[matBadge]", inputs: ["matBadgeColor", "matBadgeOverlap", "matBadgeDisabled", "matBadgePosition", "matBadge", "matBadgeDescription", "matBadgeSize", "matBadgeHidden"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i5.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "pipe", type:
183
183
  // ngx-tools
184
184
  ColorToContrastPipe, name: "colorToContrast" }, { kind: "pipe", type: StringToColorPipe, name: "stringToColor" }, { kind: "component", type:
185
185
  // local
186
186
  CompactPagerComponent, selector: "pdb-compact-pager", inputs: ["paging"], outputs: ["pagingChange"] }, { kind: "component", type: RangeViewComponent, selector: "pdb-range-view", inputs: ["domain", "range", "width", "height"] }] }); }
187
187
  }
188
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.0", ngImport: i0, type: BrowserTreeNodeComponent, decorators: [{
188
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.5", ngImport: i0, type: BrowserTreeNodeComponent, decorators: [{
189
189
  type: Component,
190
190
  args: [{ selector: 'pdb-browser-tree-node', imports: [
191
191
  CommonModule,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@myrmidon/paged-data-browsers",
3
- "version": "4.0.1",
3
+ "version": "4.0.2",
4
4
  "description": "Generic simple paged data browsers.",
5
5
  "keywords": [
6
6
  "data browsers"
@@ -18,7 +18,7 @@
18
18
  "@angular/core": "^19.0.0",
19
19
  "@angular/forms": "^19.0.0",
20
20
  "@angular/material": "^19.0.0",
21
- "@myrmidon/ngx-tools": "^0.0.1"
21
+ "@myrmidon/ngx-tools": "^1.0.0"
22
22
  },
23
23
  "dependencies": {
24
24
  "tslib": "^2.3.0"