@mmlogic/components 0.5.13 → 0.5.15

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.
Files changed (74) hide show
  1. package/dist/cjs/{cell-renderer-BBebjBVz.js → cell-renderer-CLVKTVVQ.js} +113 -1
  2. package/dist/cjs/{client-layout-CqVuJumR.js → client-layout-Eg2VwW-7.js} +2 -0
  3. package/dist/cjs/index.cjs.js +2 -1
  4. package/dist/cjs/loader.cjs.js +1 -1
  5. package/dist/cjs/mosterdcomponents.cjs.js +1 -1
  6. package/dist/cjs/{mrd-boolean-field_25.cjs.entry.js → mrd-boolean-field_28.cjs.entry.js} +1163 -356
  7. package/dist/cjs/mrd-merge-view.cjs.entry.js +4 -4
  8. package/dist/collection/collection-manifest.json +3 -0
  9. package/dist/collection/components/fields/mrd-textarea-field/mrd-textarea-field.js +47 -2
  10. package/dist/collection/components/form/mrd-form/mrd-form.css +29 -0
  11. package/dist/collection/components/form/mrd-form/mrd-form.js +44 -8
  12. package/dist/collection/components/layout/mrd-layout-section/mrd-layout-section.js +126 -13
  13. package/dist/collection/components/memo/mrd-memo-container/mrd-memo-container.css +204 -0
  14. package/dist/collection/components/memo/mrd-memo-container/mrd-memo-container.js +694 -0
  15. package/dist/collection/components/memo/mrd-memo-list/mrd-memo-list.css +150 -0
  16. package/dist/collection/components/memo/mrd-memo-list/mrd-memo-list.js +511 -0
  17. package/dist/collection/components/memo/mrd-memo-view/mrd-memo-view.css +108 -0
  18. package/dist/collection/components/memo/mrd-memo-view/mrd-memo-view.js +204 -0
  19. package/dist/collection/components/merge/mrd-merge-view/mrd-merge-view.js +2 -2
  20. package/dist/collection/components/table/mrd-table/mrd-table.css +36 -0
  21. package/dist/collection/components/table/mrd-table/mrd-table.js +146 -31
  22. package/dist/collection/components/table/mrd-table/table-parts.js +6 -2
  23. package/dist/collection/components/table/mrd-table/table-query.js +6 -0
  24. package/dist/collection/dev/app.js +17 -0
  25. package/dist/collection/types/client-layout.js +1 -0
  26. package/dist/collection/utils/i18n.js +112 -0
  27. package/dist/collection/utils/query-params.js +9 -0
  28. package/dist/components/client-layout.js +1 -1
  29. package/dist/components/i18n.js +1 -1
  30. package/dist/components/index.js +1 -1
  31. package/dist/components/mrd-form.js +1 -1
  32. package/dist/components/mrd-layout-section.js +1 -1
  33. package/dist/components/mrd-memo-container.d.ts +11 -0
  34. package/dist/components/mrd-memo-container.js +1 -0
  35. package/dist/components/mrd-memo-container2.js +1 -0
  36. package/dist/components/mrd-memo-list.d.ts +11 -0
  37. package/dist/components/mrd-memo-list.js +1 -0
  38. package/dist/components/mrd-memo-list2.js +1 -0
  39. package/dist/components/mrd-memo-view.d.ts +11 -0
  40. package/dist/components/mrd-memo-view.js +1 -0
  41. package/dist/components/mrd-memo-view2.js +1 -0
  42. package/dist/components/mrd-merge-view.js +1 -1
  43. package/dist/components/mrd-table2.js +1 -1
  44. package/dist/components/mrd-textarea-field2.js +1 -1
  45. package/dist/components/query-params.js +1 -1
  46. package/dist/components/table-query.js +1 -0
  47. package/dist/esm/{cell-renderer-CC8-MtvQ.js → cell-renderer-4NKEzZWw.js} +113 -1
  48. package/dist/esm/{client-layout-Bc42CfJB.js → client-layout-BJkVeSq6.js} +2 -1
  49. package/dist/esm/index.js +1 -1
  50. package/dist/esm/loader.js +1 -1
  51. package/dist/esm/mosterdcomponents.js +1 -1
  52. package/dist/esm/{mrd-boolean-field_25.entry.js → mrd-boolean-field_28.entry.js} +1161 -357
  53. package/dist/esm/mrd-merge-view.entry.js +4 -4
  54. package/dist/mosterdcomponents/index.esm.js +1 -1
  55. package/dist/mosterdcomponents/mosterdcomponents.esm.js +1 -1
  56. package/dist/mosterdcomponents/{p-Bc42CfJB.js → p-BJkVeSq6.js} +1 -1
  57. package/dist/mosterdcomponents/p-BMx6VAJ6.js +1 -0
  58. package/dist/mosterdcomponents/p-d64ba1f2.entry.js +3 -0
  59. package/dist/mosterdcomponents/{p-a7e8f6a3.entry.js → p-e68dfbb0.entry.js} +1 -1
  60. package/dist/types/components/fields/mrd-textarea-field/mrd-textarea-field.d.ts +10 -0
  61. package/dist/types/components/form/mrd-form/mrd-form.d.ts +6 -0
  62. package/dist/types/components/layout/mrd-layout-section/mrd-layout-section.d.ts +20 -1
  63. package/dist/types/components/memo/mrd-memo-container/mrd-memo-container.d.ts +152 -0
  64. package/dist/types/components/memo/mrd-memo-list/mrd-memo-list.d.ts +79 -0
  65. package/dist/types/components/memo/mrd-memo-view/mrd-memo-view.d.ts +33 -0
  66. package/dist/types/components/table/mrd-table/mrd-table.d.ts +48 -1
  67. package/dist/types/components/table/mrd-table/table-parts.d.ts +15 -0
  68. package/dist/types/components/table/mrd-table/table-query.d.ts +6 -0
  69. package/dist/types/components.d.ts +528 -1
  70. package/dist/types/types/client-layout.d.ts +1 -0
  71. package/dist/types/utils/query-params.d.ts +16 -0
  72. package/package.json +1 -1
  73. package/dist/mosterdcomponents/p-07b148b2.entry.js +0 -3
  74. package/dist/mosterdcomponents/p-D5ZPfX3-.js +0 -1
@@ -0,0 +1,108 @@
1
+ @charset "UTF-8";
2
+ :host {
3
+ display: block;
4
+ }
5
+
6
+ .mrd-memo-view {
7
+ max-width: 720px;
8
+ display: flex;
9
+ flex-direction: column;
10
+ gap: var(--mrd-space-3);
11
+ }
12
+ .mrd-memo-view__header {
13
+ display: flex;
14
+ align-items: baseline;
15
+ flex-wrap: wrap;
16
+ gap: var(--mrd-space-3);
17
+ }
18
+ .mrd-memo-view__title {
19
+ font-size: var(--mrd-font-size-2xl);
20
+ font-weight: var(--mrd-font-weight-bold);
21
+ color: var(--mrd-color-neutral-900);
22
+ letter-spacing: -0.02em;
23
+ margin: 0;
24
+ }
25
+ .mrd-memo-view__important {
26
+ display: inline-flex;
27
+ align-items: center;
28
+ gap: var(--mrd-space-1);
29
+ font-size: var(--mrd-font-size-xs);
30
+ font-weight: var(--mrd-font-weight-semibold);
31
+ color: var(--mrd-color-warning);
32
+ text-transform: uppercase;
33
+ letter-spacing: 0.04em;
34
+ }
35
+ .mrd-memo-view__important svg {
36
+ width: 0.875rem;
37
+ height: 0.875rem;
38
+ }
39
+ .mrd-memo-view__meta {
40
+ display: flex;
41
+ align-items: center;
42
+ gap: var(--mrd-space-2);
43
+ font-size: var(--mrd-font-size-sm);
44
+ color: var(--mrd-color-neutral-400);
45
+ }
46
+ .mrd-memo-view__meta-author {
47
+ background: none;
48
+ border: none;
49
+ padding: 0;
50
+ font-family: var(--mrd-font-family);
51
+ font-size: inherit;
52
+ font-weight: var(--mrd-font-weight-medium);
53
+ color: var(--mrd-color-neutral-500);
54
+ cursor: pointer;
55
+ }
56
+ .mrd-memo-view__meta-author:hover {
57
+ color: var(--mrd-color-primary);
58
+ text-decoration: underline;
59
+ }
60
+ .mrd-memo-view__meta-date::before {
61
+ content: "·";
62
+ margin-right: var(--mrd-space-2);
63
+ }
64
+ .mrd-memo-view__meta-author + .mrd-memo-view__meta-date::before {
65
+ content: "·";
66
+ }
67
+ .mrd-memo-view__labels {
68
+ display: flex;
69
+ flex-wrap: wrap;
70
+ gap: var(--mrd-space-2);
71
+ }
72
+ .mrd-memo-view__badge {
73
+ display: inline-flex;
74
+ align-items: center;
75
+ height: 1.375rem;
76
+ padding: 0 var(--mrd-space-2);
77
+ border-radius: var(--mrd-border-radius-full, 999px);
78
+ font-size: var(--mrd-font-size-xs);
79
+ font-weight: var(--mrd-font-weight-medium);
80
+ }
81
+ .mrd-memo-view__badge--plain {
82
+ background: var(--mrd-color-neutral-100);
83
+ color: var(--mrd-color-neutral-600);
84
+ }
85
+ .mrd-memo-view__container-link {
86
+ align-self: flex-start;
87
+ background: none;
88
+ border: none;
89
+ padding: 0;
90
+ font-family: var(--mrd-font-family);
91
+ font-size: var(--mrd-font-size-sm);
92
+ font-weight: var(--mrd-font-weight-semibold);
93
+ color: var(--mrd-color-primary);
94
+ cursor: pointer;
95
+ }
96
+ .mrd-memo-view__container-link:hover {
97
+ text-decoration: underline;
98
+ }
99
+ .mrd-memo-view__divider {
100
+ height: var(--mrd-border-width);
101
+ background: var(--mrd-color-neutral-200);
102
+ }
103
+ .mrd-memo-view__body {
104
+ font-size: var(--mrd-font-size-base);
105
+ color: var(--mrd-color-neutral-800);
106
+ line-height: var(--mrd-line-height-relaxed);
107
+ word-break: break-word;
108
+ }
@@ -0,0 +1,204 @@
1
+ import { Host, h } from "@stencil/core";
2
+ import DOMPurify from "dompurify";
3
+ import { t } from "../../../utils/i18n";
4
+ import { formatDateTime } from "../../../utils/format";
5
+ /**
6
+ * Archetype-driven single-memo view. Given a `commons.memo` binding and a data
7
+ * record, renders the memo the way a note should read: the title and body are
8
+ * the prominent content, author and timestamp sit below as a subtle metadata
9
+ * line — not the other way around.
10
+ */
11
+ export class MrdMemoView {
12
+ constructor() {
13
+ /** The data record from the /data endpoint. Fields keyed by name; relations under `_links`. */
14
+ this.data = {};
15
+ this.locale = navigator.language;
16
+ /** listItems of the FIELD bound to the `labels` slot, resolved by mrd-layout-section
17
+ * from its own items array (archetype metadata does not publish listItems), so the
18
+ * label badges get a real label/color instead of the raw stored key. */
19
+ this.labelsListItems = null;
20
+ }
21
+ /** Value of a value-slot: reads data[field] via the slot→field map. */
22
+ slotValue(slot) {
23
+ var _a, _b;
24
+ const field = (_b = (_a = this.archetype) === null || _a === void 0 ? void 0 : _a.slots) === null || _b === void 0 ? void 0 : _b[slot];
25
+ if (!field)
26
+ return undefined;
27
+ return this.data[field];
28
+ }
29
+ /** Relation link for a relation-slot: reads data._links[field]. */
30
+ slotLink(slot) {
31
+ var _a, _b, _c, _d;
32
+ const field = (_b = (_a = this.archetype) === null || _a === void 0 ? void 0 : _a.slots) === null || _b === void 0 ? void 0 : _b[slot];
33
+ if (!field)
34
+ return undefined;
35
+ const links = ((_d = (_c = this.data) === null || _c === void 0 ? void 0 : _c._links) !== null && _d !== void 0 ? _d : {});
36
+ return links[field];
37
+ }
38
+ renderAuthor() {
39
+ const link = this.slotLink('author');
40
+ if (!(link === null || link === void 0 ? void 0 : link.href) || !link.name)
41
+ return null;
42
+ return (h("button", { type: "button", class: "mrd-memo-view__meta-author", onClick: () => this.mrdNavigate.emit({ href: link.href, label: link.name }) }, link.name));
43
+ }
44
+ renderContainer() {
45
+ const link = this.slotLink('container');
46
+ if (!(link === null || link === void 0 ? void 0 : link.href) || !link.name)
47
+ return null;
48
+ return (h("button", { type: "button", class: "mrd-memo-view__container-link", onClick: () => this.mrdNavigate.emit({ href: link.href, label: link.name }) }, link.name));
49
+ }
50
+ renderLabels() {
51
+ const raw = this.slotValue('labels');
52
+ const keys = Array.isArray(raw) ? raw.map(String) : (raw != null && raw !== '' ? [String(raw)] : []);
53
+ if (!keys.length)
54
+ return null;
55
+ return (h("div", { class: "mrd-memo-view__labels" }, keys.map(key => {
56
+ var _a, _b;
57
+ const listItem = ((_a = this.labelsListItems) !== null && _a !== void 0 ? _a : []).find(li => li.key === key);
58
+ const label = (_b = listItem === null || listItem === void 0 ? void 0 : listItem.label) !== null && _b !== void 0 ? _b : key;
59
+ if ((listItem === null || listItem === void 0 ? void 0 : listItem.color) && (listItem === null || listItem === void 0 ? void 0 : listItem.backgroundColor)) {
60
+ return (h("span", { key: key, class: "mrd-memo-view__badge", style: { color: listItem.color, backgroundColor: listItem.backgroundColor } }, label));
61
+ }
62
+ return h("span", { key: key, class: "mrd-memo-view__badge mrd-memo-view__badge--plain" }, label);
63
+ })));
64
+ }
65
+ renderBody() {
66
+ const bodyRaw = this.slotValue('body');
67
+ if (bodyRaw == null || bodyRaw === '')
68
+ return null;
69
+ return h("div", { class: "mrd-memo-view__body", innerHTML: DOMPurify.sanitize(String(bodyRaw)) });
70
+ }
71
+ render() {
72
+ const titleRaw = this.slotValue('title');
73
+ const title = titleRaw != null && titleRaw !== '' ? String(titleRaw) : null;
74
+ const dateRaw = this.slotValue('date');
75
+ const important = !!this.slotValue('important');
76
+ return (h(Host, { key: '4d8ed14c54449fbcfddc94a34dcfb220338f8bba' }, h("div", { key: '0e0456277715607580e5373ada10706db1067c7d', class: "mrd-memo-view" }, h("div", { key: 'd5c548617eca9a99eb34409b2082be025cfce2f4', class: "mrd-memo-view__header" }, title && h("h1", { key: '2ecd2eae3f8fdba310679515aa2fe76e02f0c2b2', class: "mrd-memo-view__title" }, title), important && (h("span", { key: 'e5e8284310ab6040f1677bc1241d04d65784c2da', class: "mrd-memo-view__important", title: t('memo_important', this.locale) }, h("svg", { key: 'babe987244b384330533721276b5392c9fe72c5a', viewBox: "0 0 24 24", fill: "currentColor", "aria-hidden": "true" }, h("path", { key: '511956710ae8e440328f9e477551ba1c2d478f1a', d: "M12 2l2.9 6.26L22 9.27l-5 4.87L18.18 21 12 17.27 5.82 21 7 14.14l-5-4.87 7.1-1.01z" })), t('memo_important', this.locale)))), h("div", { key: '41c25e5376d94078debaf62121b8bdf400511d16', class: "mrd-memo-view__meta" }, this.renderAuthor(), dateRaw != null && dateRaw !== '' && (h("span", { key: 'caff8047249745ca98da77b374438e58d150ad7f', class: "mrd-memo-view__meta-date" }, formatDateTime(dateRaw, this.locale)))), this.renderLabels(), this.renderContainer(), h("div", { key: 'bfa4c55458ea19fb920338df9478006b1883e606', class: "mrd-memo-view__divider" }), this.renderBody())));
77
+ }
78
+ static get is() { return "mrd-memo-view"; }
79
+ static get encapsulation() { return "scoped"; }
80
+ static get originalStyleUrls() {
81
+ return {
82
+ "$": ["mrd-memo-view.scss"]
83
+ };
84
+ }
85
+ static get styleUrls() {
86
+ return {
87
+ "$": ["mrd-memo-view.css"]
88
+ };
89
+ }
90
+ static get properties() {
91
+ return {
92
+ "archetype": {
93
+ "type": "unknown",
94
+ "mutable": false,
95
+ "complexType": {
96
+ "original": "ClientArchetype",
97
+ "resolved": "ClientArchetype",
98
+ "references": {
99
+ "ClientArchetype": {
100
+ "location": "import",
101
+ "path": "../../../types/client-layout",
102
+ "id": "src/types/client-layout.ts::ClientArchetype",
103
+ "referenceLocation": "ClientArchetype"
104
+ }
105
+ }
106
+ },
107
+ "required": true,
108
+ "optional": false,
109
+ "docs": {
110
+ "tags": [],
111
+ "text": "Resolved `commons.memo` binding: slot name \u2192 concrete field/relation name."
112
+ },
113
+ "getter": false,
114
+ "setter": false
115
+ },
116
+ "data": {
117
+ "type": "unknown",
118
+ "mutable": false,
119
+ "complexType": {
120
+ "original": "Record<string, unknown>",
121
+ "resolved": "string | unknown",
122
+ "references": {
123
+ "Record": {
124
+ "location": "global",
125
+ "id": "global::Record"
126
+ }
127
+ }
128
+ },
129
+ "required": false,
130
+ "optional": false,
131
+ "docs": {
132
+ "tags": [],
133
+ "text": "The data record from the /data endpoint. Fields keyed by name; relations under `_links`."
134
+ },
135
+ "getter": false,
136
+ "setter": false,
137
+ "defaultValue": "{}"
138
+ },
139
+ "locale": {
140
+ "type": "string",
141
+ "mutable": false,
142
+ "complexType": {
143
+ "original": "string",
144
+ "resolved": "string",
145
+ "references": {}
146
+ },
147
+ "required": false,
148
+ "optional": false,
149
+ "docs": {
150
+ "tags": [],
151
+ "text": ""
152
+ },
153
+ "getter": false,
154
+ "setter": false,
155
+ "reflect": false,
156
+ "attribute": "locale",
157
+ "defaultValue": "navigator.language"
158
+ },
159
+ "labelsListItems": {
160
+ "type": "unknown",
161
+ "mutable": false,
162
+ "complexType": {
163
+ "original": "ClientListValue[] | null",
164
+ "resolved": "ClientListValue[] | null",
165
+ "references": {
166
+ "ClientListValue": {
167
+ "location": "import",
168
+ "path": "../../../types/client-layout",
169
+ "id": "src/types/client-layout.ts::ClientListValue",
170
+ "referenceLocation": "ClientListValue"
171
+ }
172
+ }
173
+ },
174
+ "required": false,
175
+ "optional": false,
176
+ "docs": {
177
+ "tags": [],
178
+ "text": "listItems of the FIELD bound to the `labels` slot, resolved by mrd-layout-section\nfrom its own items array (archetype metadata does not publish listItems), so the\nlabel badges get a real label/color instead of the raw stored key."
179
+ },
180
+ "getter": false,
181
+ "setter": false,
182
+ "defaultValue": "null"
183
+ }
184
+ };
185
+ }
186
+ static get events() {
187
+ return [{
188
+ "method": "mrdNavigate",
189
+ "name": "mrdNavigate",
190
+ "bubbles": true,
191
+ "cancelable": true,
192
+ "composed": true,
193
+ "docs": {
194
+ "tags": [],
195
+ "text": "Navigate to the author or the container. Host resolves the href and routes."
196
+ },
197
+ "complexType": {
198
+ "original": "{ href?: string; label: string }",
199
+ "resolved": "{ href?: string | undefined; label: string; }",
200
+ "references": {}
201
+ }
202
+ }];
203
+ }
204
+ }
@@ -121,14 +121,14 @@ export class MrdMergeView {
121
121
  var _a, _b;
122
122
  let stepContent;
123
123
  if (this.step === 'search') {
124
- stepContent = (h(MergeSearchStep, { key: '3adb36fb46aa7260641a46a2cf27cc0ef320876f', locale: this.locale, searchQuery: this.searchQuery, searchResults: this.searchResults, isLoading: this.isLoading, onSearchInput: this.handleSearchInput, onSelect: this.handleSelect }));
124
+ stepContent = (h(MergeSearchStep, { key: '88ddac4f8f60a736bc6bc898b553e24771023b82', locale: this.locale, searchQuery: this.searchQuery, searchResults: this.searchResults, isLoading: this.isLoading, onSearchInput: this.handleSearchInput, onSelect: this.handleSelect }));
125
125
  }
126
126
  else {
127
127
  const diff = diffFields(this.fields, this.currentRecord, this.candidate);
128
128
  const bLabel = candidateLabel(this.candidate);
129
129
  stepContent = this.step === 'compare' ? (h(MergeCompareStep, { locale: this.locale, currentLabel: this.currentLabel, candidateLabel: bLabel, conflicts: diff.conflicts, autoFilled: diff.autoFilled, recordA: this.currentRecord, recordB: (_a = this.candidate) !== null && _a !== void 0 ? _a : {}, resolutions: this.resolutions, openHistoryKey: this.openHistoryKey, onResolve: this.handleResolve, onToggleHistory: this.handleToggleHistory, onChangeCandidate: this.handleChangeCandidate, onContinue: this.handleContinue })) : (h(MergeConfirmStep, { locale: this.locale, candidateLabel: bLabel, conflicts: diff.conflicts, autoFilled: diff.autoFilled, resolutions: this.resolutions, recordB: (_b = this.candidate) !== null && _b !== void 0 ? _b : {}, openHistoryKey: this.openHistoryKey, onToggleHistory: this.handleToggleHistory, onBack: this.handleBack, onConfirm: this.handleConfirm }));
130
130
  }
131
- return (h(Host, { key: 'e33a6d66a220b28cc9533da78dc274e2d7ec5c4a' }, h("div", { key: 'b5fb1390d86192f9f6f15e3ccb89e2d21d9c7c8b', class: "mrd-merge-view" }, stepContent)));
131
+ return (h(Host, { key: '3606faba4c86ae1d981b7c40e4f1cb9cde19cf50' }, h("div", { key: '9605009fec0a7c79a5cd32141d930ea04e5efaad', class: "mrd-merge-view" }, stepContent)));
132
132
  }
133
133
  static get is() { return "mrd-merge-view"; }
134
134
  static get encapsulation() { return "scoped"; }
@@ -321,6 +321,42 @@
321
321
  pointer-events: none;
322
322
  z-index: 10;
323
323
  }
324
+ .mrd-table__search-box {
325
+ display: inline-flex;
326
+ align-items: center;
327
+ gap: var(--mrd-space-1);
328
+ height: 2rem;
329
+ padding: 0 var(--mrd-space-2);
330
+ border: 1px solid var(--mrd-border-color-focus, var(--mrd-color-primary));
331
+ border-radius: var(--mrd-border-radius);
332
+ background: var(--mrd-color-white, #fff);
333
+ box-shadow: var(--mrd-shadow-focus, none);
334
+ color: var(--mrd-color-neutral-400);
335
+ }
336
+ .mrd-table__search-input {
337
+ border: none;
338
+ outline: none;
339
+ background: transparent;
340
+ font-size: var(--mrd-font-size-sm);
341
+ font-family: var(--mrd-font-family);
342
+ color: var(--mrd-color-neutral-800);
343
+ width: 12rem;
344
+ }
345
+ .mrd-table__search-clear {
346
+ display: inline-flex;
347
+ align-items: center;
348
+ justify-content: center;
349
+ background: none;
350
+ border: none;
351
+ padding: 0;
352
+ cursor: pointer;
353
+ color: var(--mrd-color-neutral-400);
354
+ font-size: var(--mrd-font-size-xs);
355
+ line-height: 1;
356
+ }
357
+ .mrd-table__search-clear:hover {
358
+ color: var(--mrd-color-neutral-700);
359
+ }
324
360
  .mrd-table__create-picker-wrap {
325
361
  position: relative;
326
362
  }
@@ -1,7 +1,7 @@
1
1
  import { h, Host, writeTask } from "@stencil/core";
2
2
  import { t } from "../../../utils/i18n";
3
- import { buildListPath } from "../../../utils/query-params";
4
- import { TEXT_TYPES, NON_INTERACTIVE_TYPES, parseDefaultSort, buildSortParam, buildTableQueryParams, buildAggregationQs, filterHasValue, normalizeDatetimeFilter, datetimeFilterToDisplay, } from "./table-query";
3
+ import { buildCreateActionDetail, buildListPath } from "../../../utils/query-params";
4
+ import { TEXT_TYPES, NON_INTERACTIVE_TYPES, SEARCH_DEBOUNCE_MS, parseDefaultSort, buildSortParam, buildTableQueryParams, buildAggregationQs, filterHasValue, normalizeDatetimeFilter, datetimeFilterToDisplay, } from "./table-query";
5
5
  import { FilterIcon, TableCell, TotalsRow, TextblockModal, JsonModal, Toolbar } from "./table-parts";
6
6
  import { FilterPopup } from "./table-filter-popup";
7
7
  import { REQUEST_DEBOUNCE_MS, REQUEST_TIMEOUT_MS, MAX_RETRY_ATTEMPTS, visibleCount, computeWindow, pageRange, getRowFromPages, computeEffectiveTotal, isTotalKnownFromPages, getStalePages, } from "./virtual-scroll";
@@ -27,6 +27,17 @@ export class MrdTable {
27
27
  this.keydownHandler = null;
28
28
  this.createPickerClickHandler = null;
29
29
  this.viewPopoverClickHandler = null;
30
+ /** Debounce timer for the free-text search box — separate from debounceTimer
31
+ * (pending-page requests), since typing and scrolling debounce independently. */
32
+ this.searchDebounceTimer = null;
33
+ /** The rendered search <input>, captured so it can be focused the instant it mounts. */
34
+ this.searchInputEl = null;
35
+ /** Set by toggleSearch() when the box just expanded; consumed and cleared in componentDidRender(). */
36
+ this.searchNeedsFocus = false;
37
+ /** Column filters stashed by stashFiltersForSearch() while the search box is open — the
38
+ * backend can't combine `q` with column filters. Restored by restoreFiltersAfterSearch()
39
+ * on close, unless the user applied a fresh filter of their own in the meantime. */
40
+ this.stashedFilters = null;
30
41
  /** Watches the host for gaining a box (e.g. its tab pane being shown) — see componentDidLoad. */
31
42
  this.resizeObserver = null;
32
43
  /** Guards against scheduling more than one column-width measurement per freeze. */
@@ -81,6 +92,10 @@ export class MrdTable {
81
92
  this.sortDir = 'asc';
82
93
  /** Active filters keyed by field name. */
83
94
  this.activeFilters = new Map();
95
+ /** True while the toolbar's magnifying glass is expanded into the search input. */
96
+ this.searchOpen = false;
97
+ /** Current free-text search query — sent as `q` on every mrdLoadPage/aggregations request. */
98
+ this.searchQuery = '';
84
99
  /** Field name of the currently open filter popup (null = closed). */
85
100
  this.openFilterCol = null;
86
101
  /** Filter state being edited in the open popup. */
@@ -134,6 +149,11 @@ export class MrdTable {
134
149
  this.colWidthMeasureScheduled = false;
135
150
  this.colWidthsUserSet = false;
136
151
  this.fixedContentMeasured = false;
152
+ // A new item is a different table (possibly not even a VIEW any more) — a leftover
153
+ // query from the old one should not silently keep filtering it.
154
+ this.searchOpen = false;
155
+ this.searchQuery = '';
156
+ this.stashedFilters = null;
137
157
  }
138
158
  // ── Lifecycle ──────────────────────────────────────────────────────────────
139
159
  componentWillLoad() {
@@ -241,7 +261,12 @@ export class MrdTable {
241
261
  if (pageNumber === 0 && this.aggregationsPending) {
242
262
  this.aggregationsPending = false;
243
263
  const hasAggColumns = this.columns.some(c => c.type === 'FIELD' && c.aggregate);
244
- if (!isLastPage || hasAggColumns) {
264
+ // The aggregations endpoint doesn't support the `q` free-text param, so a request
265
+ // sent while searching would silently return unfiltered totals. Leave aggregations
266
+ // (and aggregationsTotal) at null instead — TotalsRow already renders nothing when
267
+ // aggregations is null, so the totals row just disappears for the duration of the
268
+ // search rather than showing numbers that don't match the filtered rows.
269
+ if (!this.searchQuery && (!isLastPage || hasAggColumns)) {
245
270
  this.emitLoadAggregations();
246
271
  }
247
272
  }
@@ -280,10 +305,18 @@ export class MrdTable {
280
305
  document.removeEventListener('click', this.viewPopoverClickHandler);
281
306
  this.viewPopoverClickHandler = null;
282
307
  }
308
+ if (this.searchDebounceTimer !== null) {
309
+ clearTimeout(this.searchDebounceTimer);
310
+ this.searchDebounceTimer = null;
311
+ }
283
312
  }
284
313
  componentDidRender() {
285
314
  this.freezeColWidths();
286
315
  this.growToFixedContent();
316
+ if (this.searchNeedsFocus && this.searchInputEl) {
317
+ this.searchInputEl.focus();
318
+ this.searchNeedsFocus = false;
319
+ }
287
320
  }
288
321
  /** Freeze the column widths once the first data page has rendered.
289
322
  *
@@ -574,16 +607,19 @@ export class MrdTable {
574
607
  });
575
608
  }
576
609
  buildActionDetail(action, basicType) {
577
- var _a, _b, _c;
610
+ var _a;
578
611
  if (action === 'export') {
579
612
  return { action, path: this.buildExcelPath(), qs: this.buildQueryParams(0) };
580
613
  }
581
614
  if (action === 'create') {
582
615
  const v = this.allViews[this.activeViewIdx];
583
- const parentPath = ((_a = this.item) === null || _a === void 0 ? void 0 : _a.type) === 'RELATED_VIEW'
584
- ? `/${(_b = v === null || v === void 0 ? void 0 : v.fromClass) !== null && _b !== void 0 ? _b : ''}/${this.parentId}`
585
- : null;
586
- return Object.assign({ action, dataClass: (_c = v === null || v === void 0 ? void 0 : v.dataClass) !== null && _c !== void 0 ? _c : '', parentPath }, (basicType ? { basicType } : {}));
616
+ return buildCreateActionDetail({
617
+ isRelatedView: ((_a = this.item) === null || _a === void 0 ? void 0 : _a.type) === 'RELATED_VIEW',
618
+ fromClass: v === null || v === void 0 ? void 0 : v.fromClass,
619
+ parentId: this.parentId,
620
+ dataClass: v === null || v === void 0 ? void 0 : v.dataClass,
621
+ basicType,
622
+ });
587
623
  }
588
624
  return { action };
589
625
  }
@@ -602,7 +638,13 @@ export class MrdTable {
602
638
  dataClass: v.dataClass,
603
639
  });
604
640
  }
605
- /** Build query params for a page request from current sort, view filters, filterClass and active column filters. */
641
+ /** Build query params for a page request from current sort, view filters, filterClass and active column filters.
642
+ *
643
+ * `q` and column filters are mutually exclusive on the backend — combining them breaks
644
+ * the search. stashFiltersForSearch() already empties activeFilters for the duration of
645
+ * a search, but this guard is the actual guarantee: even if a filter is applied through
646
+ * a column header while the search box is still open (before the query is cleared),
647
+ * activeFilters is dropped from the request rather than sent alongside `q`. */
606
648
  buildQueryParams(page) {
607
649
  var _a;
608
650
  const v = this.allViews[this.activeViewIdx];
@@ -611,7 +653,8 @@ export class MrdTable {
611
653
  sort: this.sortParam(),
612
654
  filterClass: v === null || v === void 0 ? void 0 : v.filterClass,
613
655
  viewFilters: (_a = v === null || v === void 0 ? void 0 : v.view) === null || _a === void 0 ? void 0 : _a.filter,
614
- activeFilters: this.activeFilters.values(),
656
+ activeFilters: this.searchQuery ? [] : this.activeFilters.values(),
657
+ query: this.searchQuery,
615
658
  });
616
659
  }
617
660
  /** True when the table is in paginated / virtual-scroll mode rather than driven by the
@@ -917,6 +960,18 @@ export class MrdTable {
917
960
  const current = (_b = (_a = this.pendingFilter) === null || _a === void 0 ? void 0 : _a.values) !== null && _b !== void 0 ? _b : [];
918
961
  this.pendingFilter = Object.assign(Object.assign({}, this.pendingFilter), { values: checked ? [...current, key] : current.filter(k => k !== key) });
919
962
  }
963
+ /** Common reload after activeFilters or searchQuery changed: drop stale aggregations
964
+ * (the next setPage(0) re-requests them, guarded against `q` in setPage() itself) and
965
+ * re-fetch page 0. Shared by the filter popup, clear-all, and free-text search. */
966
+ reloadAfterQueryChange() {
967
+ this.aggregations = null;
968
+ this.aggregationsTotal = null;
969
+ this.aggregationsPending = true;
970
+ if (this.isPaginated) {
971
+ this.resetPages();
972
+ this.emitPagesForWindow(this.renderStart, this.renderEnd);
973
+ }
974
+ }
920
975
  applyFilter() {
921
976
  const f = this.pendingFilter;
922
977
  if (!(f === null || f === void 0 ? void 0 : f.field)) {
@@ -933,13 +988,7 @@ export class MrdTable {
933
988
  }
934
989
  this.activeFilters = next;
935
990
  this.closeFilterPopup();
936
- this.aggregations = null;
937
- this.aggregationsTotal = null;
938
- this.aggregationsPending = true;
939
- if (this.isPaginated) {
940
- this.resetPages();
941
- this.emitPagesForWindow(this.renderStart, this.renderEnd);
942
- }
991
+ this.reloadAfterQueryChange();
943
992
  }
944
993
  clearFilter() {
945
994
  const name = this.openFilterCol;
@@ -948,23 +997,80 @@ export class MrdTable {
948
997
  next.delete(name);
949
998
  this.activeFilters = next;
950
999
  this.closeFilterPopup();
951
- this.aggregations = null;
952
- this.aggregationsTotal = null;
953
- this.aggregationsPending = true;
954
- if (this.isPaginated) {
955
- this.resetPages();
956
- this.emitPagesForWindow(this.renderStart, this.renderEnd);
957
- }
1000
+ this.reloadAfterQueryChange();
958
1001
  }
959
1002
  clearAllFilters() {
960
1003
  this.activeFilters = new Map();
961
- this.aggregations = null;
962
- this.aggregationsTotal = null;
963
- this.aggregationsPending = true;
964
- if (this.isPaginated) {
965
- this.resetPages();
966
- this.emitPagesForWindow(this.renderStart, this.renderEnd);
1004
+ this.reloadAfterQueryChange();
1005
+ }
1006
+ // ── Free-text search ───────────────────────────────────────────────────────
1007
+ /** Search is a VIEW-only affordance — a RELATED_VIEW table is already scoped to
1008
+ * its parent record, so a free-text box there would invite confusion about what
1009
+ * it searches across. */
1010
+ get searchEnabled() {
1011
+ var _a;
1012
+ return ((_a = this.item) === null || _a === void 0 ? void 0 : _a.type) === 'VIEW';
1013
+ }
1014
+ toggleSearch() {
1015
+ if (this.searchOpen) {
1016
+ this.closeSearch();
1017
+ }
1018
+ else {
1019
+ this.searchOpen = true;
1020
+ this.searchNeedsFocus = true;
1021
+ this.stashFiltersForSearch();
1022
+ }
1023
+ }
1024
+ /** Collapses the box back to the icon. Clearing a non-empty query and restoring any
1025
+ * stashed filters can each independently require a reload — reloadAfterQueryChange()
1026
+ * is called at most once here regardless of how many of the two actually changed. */
1027
+ closeSearch() {
1028
+ this.searchOpen = false;
1029
+ if (this.searchDebounceTimer !== null) {
1030
+ clearTimeout(this.searchDebounceTimer);
1031
+ this.searchDebounceTimer = null;
967
1032
  }
1033
+ const hadQuery = this.searchQuery !== '';
1034
+ this.searchQuery = '';
1035
+ const filtersRestored = this.restoreFiltersAfterSearch();
1036
+ if (hadQuery || filtersRestored)
1037
+ this.reloadAfterQueryChange();
1038
+ }
1039
+ handleSearchInput(value) {
1040
+ this.searchQuery = value;
1041
+ if (this.searchDebounceTimer !== null)
1042
+ clearTimeout(this.searchDebounceTimer);
1043
+ this.searchDebounceTimer = setTimeout(() => this.applySearch(), SEARCH_DEBOUNCE_MS);
1044
+ }
1045
+ /** Reloads from page 0 with the current searchQuery, same reset as applyFilter(). */
1046
+ applySearch() {
1047
+ this.searchDebounceTimer = null;
1048
+ this.reloadAfterQueryChange();
1049
+ }
1050
+ /** Column filters and the free-text `q` param are mutually exclusive on the backend —
1051
+ * sending both breaks the search. Opening the search box stashes any active filters
1052
+ * and clears them (the header filter icons and the clear-all button disappear along
1053
+ * with activeFilters, so there is nothing extra to hide) and reloads unfiltered. */
1054
+ stashFiltersForSearch() {
1055
+ if (this.activeFilters.size === 0)
1056
+ return;
1057
+ this.stashedFilters = this.activeFilters;
1058
+ this.activeFilters = new Map();
1059
+ this.reloadAfterQueryChange();
1060
+ }
1061
+ /** Restores filters stashed by stashFiltersForSearch(), unless the user applied a fresh
1062
+ * filter of their own while the search box was open — that explicit choice wins over
1063
+ * reinstating the old one. Returns whether activeFilters actually changed, so the
1064
+ * caller can decide whether a reload is needed. */
1065
+ restoreFiltersAfterSearch() {
1066
+ if (!this.stashedFilters)
1067
+ return false;
1068
+ const stashed = this.stashedFilters;
1069
+ this.stashedFilters = null;
1070
+ if (this.activeFilters.size > 0)
1071
+ return false;
1072
+ this.activeFilters = stashed;
1073
+ return true;
968
1074
  }
969
1075
  // ── View switcher ──────────────────────────────────────────────────────────
970
1076
  handleViewSwitch(targetIdx) {
@@ -975,6 +1081,9 @@ export class MrdTable {
975
1081
  this.activeViewIdx = targetIdx;
976
1082
  this.applyDefaultSort((_b = (_a = target.view) === null || _a === void 0 ? void 0 : _a.defaultSort) !== null && _b !== void 0 ? _b : '');
977
1083
  this.activeFilters = new Map();
1084
+ this.searchOpen = false;
1085
+ this.searchQuery = '';
1086
+ this.stashedFilters = null;
978
1087
  this.init();
979
1088
  }
980
1089
  toggleViewPopover(e) {
@@ -997,7 +1106,7 @@ export class MrdTable {
997
1106
  // ── Render: toolbar ────────────────────────────────────────────────────────
998
1107
  renderToolbar() {
999
1108
  var _a;
1000
- return (h(Toolbar, { locale: this.locale, filterCount: this.activeFilters.size, actions: this.tableActions, views: this.allViews, activeViewIdx: this.activeViewIdx, viewPopoverOpen: this.viewPopoverOpen, createPickerOpen: this.createPickerOpen, createTypes: (_a = this.item) === null || _a === void 0 ? void 0 : _a.createTypes, readOnly: this.readOnly, cb: {
1109
+ return (h(Toolbar, { locale: this.locale, filterCount: this.activeFilters.size, actions: this.tableActions, views: this.allViews, activeViewIdx: this.activeViewIdx, viewPopoverOpen: this.viewPopoverOpen, createPickerOpen: this.createPickerOpen, createTypes: (_a = this.item) === null || _a === void 0 ? void 0 : _a.createTypes, readOnly: this.readOnly, searchEnabled: this.searchEnabled, searchOpen: this.searchOpen, searchQuery: this.searchQuery, cb: {
1001
1110
  onClearAllFilters: () => this.clearAllFilters(),
1002
1111
  onViewSwitch: idx => this.handleViewSwitch(idx),
1003
1112
  onToggleViewPopover: e => this.toggleViewPopover(e),
@@ -1008,6 +1117,10 @@ export class MrdTable {
1008
1117
  this.closeCreatePicker();
1009
1118
  },
1010
1119
  onAction: action => this.mrdAction.emit(this.buildActionDetail(action)),
1120
+ onToggleSearch: () => this.toggleSearch(),
1121
+ onSearchInput: value => this.handleSearchInput(value),
1122
+ onCloseSearch: () => this.closeSearch(),
1123
+ onSearchInputRef: el => (this.searchInputEl = el !== null && el !== void 0 ? el : null),
1011
1124
  } }));
1012
1125
  }
1013
1126
  // ── Render: filter popup ───────────────────────────────────────────────────
@@ -1378,6 +1491,8 @@ export class MrdTable {
1378
1491
  "sortField": {},
1379
1492
  "sortDir": {},
1380
1493
  "activeFilters": {},
1494
+ "searchOpen": {},
1495
+ "searchQuery": {},
1381
1496
  "openFilterCol": {},
1382
1497
  "pendingFilter": {},
1383
1498
  "popupPos": {},