@mmlogic/components 0.5.11 → 0.5.13
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/dist/cjs/{cell-renderer-pwUGEDql.js → cell-renderer-BBebjBVz.js} +8 -0
- package/dist/cjs/loader.cjs.js +1 -1
- package/dist/cjs/mosterdcomponents.cjs.js +1 -1
- package/dist/cjs/mrd-boolean-field_25.cjs.entry.js +161 -24
- package/dist/cjs/mrd-merge-view.cjs.entry.js +1 -1
- package/dist/collection/components/documents/mrd-document-container/mrd-document-container.js +30 -4
- package/dist/collection/components/documents/mrd-document-list/mrd-document-list.js +55 -6
- package/dist/collection/components/layout/mrd-layout-section/mrd-layout-section.js +27 -3
- package/dist/collection/components/table/mrd-table/mrd-table.js +120 -6
- package/dist/collection/components/table/mrd-table/table-parts.js +9 -5
- package/dist/collection/dev/app.js +24 -0
- package/dist/collection/utils/i18n.js +8 -0
- package/dist/components/i18n.js +1 -1
- package/dist/components/mrd-document-container2.js +1 -1
- package/dist/components/mrd-document-list2.js +1 -1
- package/dist/components/mrd-layout-section.js +1 -1
- package/dist/components/mrd-table2.js +1 -1
- package/dist/esm/{cell-renderer-B8rJpnq8.js → cell-renderer-CC8-MtvQ.js} +8 -0
- package/dist/esm/loader.js +1 -1
- package/dist/esm/mosterdcomponents.js +1 -1
- package/dist/esm/mrd-boolean-field_25.entry.js +161 -24
- package/dist/esm/mrd-merge-view.entry.js +1 -1
- package/dist/mosterdcomponents/mosterdcomponents.esm.js +1 -1
- package/dist/mosterdcomponents/p-07b148b2.entry.js +3 -0
- package/dist/mosterdcomponents/p-D5ZPfX3-.js +1 -0
- package/dist/mosterdcomponents/{p-e540de6a.entry.js → p-a7e8f6a3.entry.js} +1 -1
- package/dist/types/components/documents/mrd-document-container/mrd-document-container.d.ts +4 -0
- package/dist/types/components/documents/mrd-document-list/mrd-document-list.d.ts +6 -1
- package/dist/types/components/layout/mrd-layout-section/mrd-layout-section.d.ts +4 -0
- package/dist/types/components/table/mrd-table/mrd-table.d.ts +30 -0
- package/dist/types/components/table/mrd-table/table-parts.d.ts +3 -0
- package/dist/types/components.d.ts +45 -1
- package/package.json +1 -1
- package/dist/mosterdcomponents/cell-renderer-B8u5BCK-.js.map +0 -1
- package/dist/mosterdcomponents/client-layout-Bc42CfJB.js.map +0 -1
- package/dist/mosterdcomponents/document-attachments-BufPmMRv.js.map +0 -1
- package/dist/mosterdcomponents/field-helpers-CUJsHr1V.js.map +0 -1
- package/dist/mosterdcomponents/file-upload-common-DdTDuxEU.js.map +0 -1
- package/dist/mosterdcomponents/format-gcecItcD.js.map +0 -1
- package/dist/mosterdcomponents/i18n-Ba7_jO9I.js.map +0 -1
- package/dist/mosterdcomponents/index-1ayBojyN.js.map +0 -1
- package/dist/mosterdcomponents/index-DhffneUF.js.map +0 -1
- package/dist/mosterdcomponents/index.esm.js.map +0 -1
- package/dist/mosterdcomponents/mosterdcomponents.esm.js.map +0 -1
- package/dist/mosterdcomponents/p-eee4be8b.entry.js +0 -3
- package/dist/mosterdcomponents/p-rTM1tU_M.js +0 -1
- package/dist/mosterdcomponents/purify.es-_duHfPgC.js.map +0 -1
- package/dist/mosterdcomponents/query-params-BZbQoeYF.js.map +0 -1
- package/dist/mosterdcomponents/quill-C9pgw_k-.js.map +0 -1
- package/dist/mosterdcomponents/validation-CjjTt66G.js.map +0 -1
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
var index = require('./index-DVbdkYO3.js');
|
|
4
4
|
var clientLayout = require('./client-layout-CqVuJumR.js');
|
|
5
|
-
var cellRenderer = require('./cell-renderer-
|
|
5
|
+
var cellRenderer = require('./cell-renderer-BBebjBVz.js');
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Whether a layout item can appear in the merge diff at all. m-n relations
|
package/dist/collection/components/documents/mrd-document-container/mrd-document-container.js
CHANGED
|
@@ -28,6 +28,10 @@ export class MrdDocumentContainer {
|
|
|
28
28
|
* on top, Finder-style. The table manages its own height. */
|
|
29
29
|
this.height = 460;
|
|
30
30
|
this.locale = navigator.language;
|
|
31
|
+
/** When true, this dashboard is read-only: New folder and Upload are disabled
|
|
32
|
+
* (with a tooltip explaining why) and the embedded table/list get it passed
|
|
33
|
+
* through so their own create/upload/drag-drop paths are disabled too. */
|
|
34
|
+
this.readOnly = false;
|
|
31
35
|
/** Active body: folder tree or flat table. */
|
|
32
36
|
this.mode = 'tree';
|
|
33
37
|
/** Whether the folder tree currently has a valid target for "New folder". */
|
|
@@ -42,6 +46,8 @@ export class MrdDocumentContainer {
|
|
|
42
46
|
};
|
|
43
47
|
this.pickFile = () => {
|
|
44
48
|
var _a;
|
|
49
|
+
if (this.readOnly)
|
|
50
|
+
return;
|
|
45
51
|
(_a = this.fileInputEl) === null || _a === void 0 ? void 0 : _a.click();
|
|
46
52
|
};
|
|
47
53
|
/** One file per upload, matching the drop handler. Reset the input afterwards
|
|
@@ -78,11 +84,11 @@ export class MrdDocumentContainer {
|
|
|
78
84
|
}
|
|
79
85
|
// ── Toolbar ────────────────────────────────────────────────────────────────
|
|
80
86
|
renderToolbar() {
|
|
81
|
-
return (h("div", { class: "mrd-document-container__toolbar" }, this.mode === 'tree' && (h("button", { type: "button", class: "mrd-document-container__action", disabled: !this.canCreate, onClick: this.createFolder }, h("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true" }, h("path", { d: "M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" }), h("path", { d: "M12 11v4M10 13h4" })), t('new_folder', this.locale))), this.mode === 'tree' && (h("button", { type: "button", class: "mrd-document-container__action", disabled: !this.canCreate, title: this.targetLabel ? `${t('doc_upload_to', this.locale)} ${this.targetLabel}` : undefined, onClick: this.pickFile }, h("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true" }, h("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }), h("path", { d: "M12 3v13M7 8l5-5 5 5" })), t('doc_upload', this.locale))), h("input", { type: "file", class: "mrd-document-container__file-input", ref: el => (this.fileInputEl = el), onChange: this.onFilePicked }), h("div", { class: "mrd-document-container__seg", role: "group", "aria-label": t('doc_view_switch', this.locale) }, h("button", { class: "mrd-document-container__seg-btn", "aria-pressed": String(this.mode === 'tree'), title: t('doc_view_tree', this.locale), "aria-label": t('doc_view_tree', this.locale), onClick: () => this.setMode('tree') }, h("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true" }, h("path", { d: "M3 5h6M3 12h9M3 19h6" }), h("circle", { cx: "18", cy: "5", r: "1.6" }), h("circle", { cx: "18", cy: "19", r: "1.6" }), h("path", { d: "M16 12h4" }))), h("button", { class: "mrd-document-container__seg-btn", "aria-pressed": String(this.mode === 'list'), title: t('doc_view_list', this.locale), "aria-label": t('doc_view_list', this.locale), onClick: () => this.setMode('list') }, h("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true" }, h("path", { d: "M8 6h13M8 12h13M8 18h13M3 6h.01M3 12h.01M3 18h.01" }))))));
|
|
87
|
+
return (h("div", { class: "mrd-document-container__toolbar" }, this.mode === 'tree' && (h("button", { type: "button", class: "mrd-document-container__action", disabled: this.readOnly || !this.canCreate, title: this.readOnly ? t('readonly_access', this.locale) : undefined, onClick: this.createFolder }, h("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true" }, h("path", { d: "M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" }), h("path", { d: "M12 11v4M10 13h4" })), t('new_folder', this.locale))), this.mode === 'tree' && (h("button", { type: "button", class: "mrd-document-container__action", disabled: this.readOnly || !this.canCreate, title: this.readOnly ? t('readonly_access', this.locale) : (this.targetLabel ? `${t('doc_upload_to', this.locale)} ${this.targetLabel}` : undefined), onClick: this.pickFile }, h("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true" }, h("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }), h("path", { d: "M12 3v13M7 8l5-5 5 5" })), t('doc_upload', this.locale))), h("input", { type: "file", class: "mrd-document-container__file-input", ref: el => (this.fileInputEl = el), onChange: this.onFilePicked }), h("div", { class: "mrd-document-container__seg", role: "group", "aria-label": t('doc_view_switch', this.locale) }, h("button", { class: "mrd-document-container__seg-btn", "aria-pressed": String(this.mode === 'tree'), title: t('doc_view_tree', this.locale), "aria-label": t('doc_view_tree', this.locale), onClick: () => this.setMode('tree') }, h("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true" }, h("path", { d: "M3 5h6M3 12h9M3 19h6" }), h("circle", { cx: "18", cy: "5", r: "1.6" }), h("circle", { cx: "18", cy: "19", r: "1.6" }), h("path", { d: "M16 12h4" }))), h("button", { class: "mrd-document-container__seg-btn", "aria-pressed": String(this.mode === 'list'), title: t('doc_view_list', this.locale), "aria-label": t('doc_view_list', this.locale), onClick: () => this.setMode('list') }, h("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true" }, h("path", { d: "M8 6h13M8 12h13M8 18h13M3 6h.01M3 12h.01M3 18h.01" }))))));
|
|
82
88
|
}
|
|
83
89
|
// ── Bodies ───────────────────────────────────────────────────────────────
|
|
84
90
|
renderTree() {
|
|
85
|
-
return (h("mrd-document-list", { "data-doclist": this.viewKey, ref: el => (this.listEl = el), item: this.item, archetype: this.archetype, parentId: this.parentId, containerHref: this.containerHref, locale: this.locale, onMrdLoadDistinct: (e) => {
|
|
91
|
+
return (h("mrd-document-list", { "data-doclist": this.viewKey, ref: el => (this.listEl = el), item: this.item, archetype: this.archetype, parentId: this.parentId, containerHref: this.containerHref, locale: this.locale, readOnly: this.readOnly, onMrdLoadDistinct: (e) => {
|
|
86
92
|
e.stopPropagation();
|
|
87
93
|
this.mrdLoadDistinct.emit(e.detail);
|
|
88
94
|
}, onMrdLoadPage: (e) => {
|
|
@@ -109,7 +115,7 @@ export class MrdDocumentContainer {
|
|
|
109
115
|
} }));
|
|
110
116
|
}
|
|
111
117
|
renderList() {
|
|
112
|
-
return (h("mrd-table", { "data-view": this.viewKey, ref: el => (this.tableEl = el), item: this.item, parentId: this.parentId, locale: this.locale, onMrdLoadPage: (e) => {
|
|
118
|
+
return (h("mrd-table", { "data-view": this.viewKey, ref: el => (this.tableEl = el), item: this.item, parentId: this.parentId, locale: this.locale, readOnly: this.readOnly, onMrdLoadPage: (e) => {
|
|
113
119
|
e.stopPropagation();
|
|
114
120
|
this.mrdLoadPage.emit(e.detail);
|
|
115
121
|
}, onMrdLoadAggregations: (e) => {
|
|
@@ -133,7 +139,7 @@ export class MrdDocumentContainer {
|
|
|
133
139
|
}
|
|
134
140
|
render() {
|
|
135
141
|
const treeMode = this.mode === 'tree';
|
|
136
|
-
return (h(Host, { key: '
|
|
142
|
+
return (h(Host, { key: '6792d49f08dd19f6c51e4b656df06bbd8bc14f3e' }, this.renderToolbar(), h("div", { key: '702f4c288f8c40bd2c5999beb392b9dfd022b0bc', class: {
|
|
137
143
|
'mrd-document-container__body': true,
|
|
138
144
|
'mrd-document-container__body--tree': treeMode,
|
|
139
145
|
}, style: treeMode ? { height: `${this.height}px`, overflowY: 'auto' } : {} }, treeMode ? this.renderTree() : this.renderList())));
|
|
@@ -300,6 +306,26 @@ export class MrdDocumentContainer {
|
|
|
300
306
|
"reflect": false,
|
|
301
307
|
"attribute": "locale",
|
|
302
308
|
"defaultValue": "navigator.language"
|
|
309
|
+
},
|
|
310
|
+
"readOnly": {
|
|
311
|
+
"type": "boolean",
|
|
312
|
+
"mutable": false,
|
|
313
|
+
"complexType": {
|
|
314
|
+
"original": "boolean",
|
|
315
|
+
"resolved": "boolean",
|
|
316
|
+
"references": {}
|
|
317
|
+
},
|
|
318
|
+
"required": false,
|
|
319
|
+
"optional": false,
|
|
320
|
+
"docs": {
|
|
321
|
+
"tags": [],
|
|
322
|
+
"text": "When true, this dashboard is read-only: New folder and Upload are disabled\n(with a tooltip explaining why) and the embedded table/list get it passed\nthrough so their own create/upload/drag-drop paths are disabled too."
|
|
323
|
+
},
|
|
324
|
+
"getter": false,
|
|
325
|
+
"setter": false,
|
|
326
|
+
"reflect": false,
|
|
327
|
+
"attribute": "read-only",
|
|
328
|
+
"defaultValue": "false"
|
|
303
329
|
}
|
|
304
330
|
};
|
|
305
331
|
}
|
|
@@ -34,6 +34,11 @@ export class MrdDocumentList {
|
|
|
34
34
|
* accepts file drops and folder creation. */
|
|
35
35
|
this.containerHref = '';
|
|
36
36
|
this.locale = navigator.language;
|
|
37
|
+
/** When true, folder creation, file upload (button and drag-drop) and moving a
|
|
38
|
+
* document between folders are all disabled — a viewer-role user can browse
|
|
39
|
+
* but not mutate anything. Drives mrdCanCreate(false) so the host toolbar's
|
|
40
|
+
* New folder / Upload buttons stay disabled too. */
|
|
41
|
+
this.readOnly = false;
|
|
37
42
|
this.containers = [];
|
|
38
43
|
this.rootLoading = true;
|
|
39
44
|
/** True when the single-container level is collapsed away (dossier context). */
|
|
@@ -76,6 +81,8 @@ export class MrdDocumentList {
|
|
|
76
81
|
/** The optimistic document row awaiting its self href from the create POST. */
|
|
77
82
|
this.lastOptimisticDoc = null;
|
|
78
83
|
this.startCreateFolder = () => {
|
|
84
|
+
if (this.readOnly)
|
|
85
|
+
return;
|
|
79
86
|
const resolved = this.resolveTarget();
|
|
80
87
|
if (!resolved || this.pendingNode)
|
|
81
88
|
return;
|
|
@@ -115,6 +122,10 @@ export class MrdDocumentList {
|
|
|
115
122
|
// ── Drag & drop: move a document into another folder / container ────────────
|
|
116
123
|
this.onDocDragStart = (e, doc, node) => {
|
|
117
124
|
var _a, _b, _c;
|
|
125
|
+
if (this.readOnly) {
|
|
126
|
+
e.preventDefault();
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
118
129
|
this.dragDoc = doc;
|
|
119
130
|
this.dragFromId = node.id;
|
|
120
131
|
if (e.dataTransfer) {
|
|
@@ -134,6 +145,11 @@ export class MrdDocumentList {
|
|
|
134
145
|
* root drop-zone; a different node is highlighted (its own folder = no-op). */
|
|
135
146
|
this.onNodeDragOver = (e, node) => {
|
|
136
147
|
const file = this.isFileDrag(e);
|
|
148
|
+
if (this.readOnly) {
|
|
149
|
+
if (file)
|
|
150
|
+
e.preventDefault(); // swallow so the browser doesn't navigate to the dropped file
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
137
153
|
if (!this.dragDoc && !file)
|
|
138
154
|
return;
|
|
139
155
|
e.preventDefault();
|
|
@@ -149,6 +165,10 @@ export class MrdDocumentList {
|
|
|
149
165
|
this.dropTargetId = null;
|
|
150
166
|
};
|
|
151
167
|
this.onNodeDrop = (e, node, container) => {
|
|
168
|
+
if (this.readOnly) {
|
|
169
|
+
e.preventDefault();
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
152
172
|
if (this.isFileDrag(e)) {
|
|
153
173
|
e.preventDefault();
|
|
154
174
|
e.stopPropagation();
|
|
@@ -167,6 +187,11 @@ export class MrdDocumentList {
|
|
|
167
187
|
if (!this.singleContainer)
|
|
168
188
|
return;
|
|
169
189
|
const file = this.isFileDrag(e);
|
|
190
|
+
if (this.readOnly) {
|
|
191
|
+
if (file)
|
|
192
|
+
e.preventDefault(); // swallow so the browser doesn't navigate to the dropped file
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
170
195
|
if (!this.dragDoc && !file)
|
|
171
196
|
return;
|
|
172
197
|
e.preventDefault();
|
|
@@ -184,6 +209,10 @@ export class MrdDocumentList {
|
|
|
184
209
|
};
|
|
185
210
|
this.onRootDrop = (e) => {
|
|
186
211
|
this.rootDropActive = false;
|
|
212
|
+
if (this.readOnly) {
|
|
213
|
+
e.preventDefault();
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
187
216
|
if (!this.singleContainer)
|
|
188
217
|
return;
|
|
189
218
|
const only = this.containers[0];
|
|
@@ -454,10 +483,10 @@ export class MrdDocumentList {
|
|
|
454
483
|
return null;
|
|
455
484
|
}
|
|
456
485
|
get canCreateFolder() {
|
|
457
|
-
return !this.pendingNode && this.resolveTarget() !== null;
|
|
486
|
+
return !this.readOnly && !this.pendingNode && this.resolveTarget() !== null;
|
|
458
487
|
}
|
|
459
488
|
/** Start creating a folder under the current target. Called by the host toolbar
|
|
460
|
-
* (mrd-document-container). No-op when there is no valid target. */
|
|
489
|
+
* (mrd-document-container). No-op when there is no valid target, or read-only. */
|
|
461
490
|
async createFolder() {
|
|
462
491
|
this.startCreateFolder();
|
|
463
492
|
}
|
|
@@ -543,7 +572,7 @@ export class MrdDocumentList {
|
|
|
543
572
|
* (mrd-document-container). No-op without a target, or while another upload
|
|
544
573
|
* is still awaiting its setFileReference. */
|
|
545
574
|
async uploadFile(file) {
|
|
546
|
-
if (!file || this.pendingUpload)
|
|
575
|
+
if (this.readOnly || !file || this.pendingUpload)
|
|
547
576
|
return;
|
|
548
577
|
const resolved = this.resolveTarget();
|
|
549
578
|
if (!resolved)
|
|
@@ -684,7 +713,7 @@ export class MrdDocumentList {
|
|
|
684
713
|
body = renderLoadingRow(ctx, 0);
|
|
685
714
|
}
|
|
686
715
|
else if (this.containers.length === 0) {
|
|
687
|
-
body = h("div", { key: '
|
|
716
|
+
body = h("div", { key: '5d473ec3a894361325a71deb14e7b52d0abc5f06', class: "mrd-document-list__empty" }, t('no_results', this.locale));
|
|
688
717
|
}
|
|
689
718
|
else if (this.singleContainer) {
|
|
690
719
|
// Dossier context: render the single container's folder tree at the root.
|
|
@@ -702,7 +731,7 @@ export class MrdDocumentList {
|
|
|
702
731
|
else {
|
|
703
732
|
body = this.containers.map(c => renderContainerNode(ctx, c, 0));
|
|
704
733
|
}
|
|
705
|
-
return (h(Host, { key: '
|
|
734
|
+
return (h(Host, { key: '757babf3ccabbb113ab2a24d632b2b9a02d3c08b' }, h("div", { key: '606c212687482efdc5666333678e2ea406709e75', class: 'mrd-document-list' + (this.rootDropActive ? ' mrd-document-list--drop-active' : ''), onDragOver: this.onRootDragOver, onDragLeave: this.onRootDragLeave, onDrop: this.onRootDrop }, body)));
|
|
706
735
|
}
|
|
707
736
|
static get is() { return "mrd-document-list"; }
|
|
708
737
|
static get encapsulation() { return "scoped"; }
|
|
@@ -826,6 +855,26 @@ export class MrdDocumentList {
|
|
|
826
855
|
"reflect": false,
|
|
827
856
|
"attribute": "locale",
|
|
828
857
|
"defaultValue": "navigator.language"
|
|
858
|
+
},
|
|
859
|
+
"readOnly": {
|
|
860
|
+
"type": "boolean",
|
|
861
|
+
"mutable": false,
|
|
862
|
+
"complexType": {
|
|
863
|
+
"original": "boolean",
|
|
864
|
+
"resolved": "boolean",
|
|
865
|
+
"references": {}
|
|
866
|
+
},
|
|
867
|
+
"required": false,
|
|
868
|
+
"optional": false,
|
|
869
|
+
"docs": {
|
|
870
|
+
"tags": [],
|
|
871
|
+
"text": "When true, folder creation, file upload (button and drag-drop) and moving a\ndocument between folders are all disabled \u2014 a viewer-role user can browse\nbut not mutate anything. Drives mrdCanCreate(false) so the host toolbar's\nNew folder / Upload buttons stay disabled too."
|
|
872
|
+
},
|
|
873
|
+
"getter": false,
|
|
874
|
+
"setter": false,
|
|
875
|
+
"reflect": false,
|
|
876
|
+
"attribute": "read-only",
|
|
877
|
+
"defaultValue": "false"
|
|
829
878
|
}
|
|
830
879
|
};
|
|
831
880
|
}
|
|
@@ -1068,7 +1117,7 @@ export class MrdDocumentList {
|
|
|
1068
1117
|
"return": "Promise<void>"
|
|
1069
1118
|
},
|
|
1070
1119
|
"docs": {
|
|
1071
|
-
"text": "Start creating a folder under the current target. Called by the host toolbar\n(mrd-document-container). No-op when there is no valid target.",
|
|
1120
|
+
"text": "Start creating a folder under the current target. Called by the host toolbar\n(mrd-document-container). No-op when there is no valid target, or read-only.",
|
|
1072
1121
|
"tags": []
|
|
1073
1122
|
}
|
|
1074
1123
|
},
|
|
@@ -17,6 +17,10 @@ export class MrdLayoutSection {
|
|
|
17
17
|
/** Top-level archetypes on the object dashboard. When it contains commons.document,
|
|
18
18
|
* the record is rendered as a single-document view instead of generic fields. */
|
|
19
19
|
this.archetypes = [];
|
|
20
|
+
/** When true, this dashboard is read-only (viewer role): the embedded mrd-table's
|
|
21
|
+
* create ("+") action and the documents view's New folder / Upload are disabled
|
|
22
|
+
* with a tooltip explaining why, so it's clear at a glance the user can't mutate data. */
|
|
23
|
+
this.readOnly = false;
|
|
20
24
|
this.searchQueryMap = {};
|
|
21
25
|
this.searchResultsMap = {};
|
|
22
26
|
this.imagePreviewUrl = null;
|
|
@@ -222,7 +226,7 @@ export class MrdLayoutSection {
|
|
|
222
226
|
* swaps between the folder tree and the flat table. Events are re-emitted to
|
|
223
227
|
* the host with the view key, exactly as the raw list/table events were. */
|
|
224
228
|
renderDocumentContainer(item, key, parentId, archetype, containerHref) {
|
|
225
|
-
return (h("mrd-document-container", { item: item, archetype: archetype, parentId: parentId, containerHref: containerHref, viewKey: key, locale: this.locale, onMrdLoadDistinct: (e) => {
|
|
229
|
+
return (h("mrd-document-container", { item: item, archetype: archetype, parentId: parentId, containerHref: containerHref, viewKey: key, locale: this.locale, readOnly: this.readOnly, onMrdLoadDistinct: (e) => {
|
|
226
230
|
e.stopPropagation();
|
|
227
231
|
this.mrdLoadViewDistinct.emit(Object.assign({ name: key }, e.detail));
|
|
228
232
|
}, onMrdLoadPage: (e) => {
|
|
@@ -255,7 +259,7 @@ export class MrdLayoutSection {
|
|
|
255
259
|
} }));
|
|
256
260
|
}
|
|
257
261
|
renderTable(item, key, parentId) {
|
|
258
|
-
return (h("mrd-table", { "data-view": key, item: item, parentId: parentId, locale: this.locale, onMrdLoadPage: (e) => this.handleViewLoadPage(e, key), onMrdLoadAggregations: (e) => {
|
|
262
|
+
return (h("mrd-table", { "data-view": key, item: item, parentId: parentId, locale: this.locale, readOnly: this.readOnly, onMrdLoadPage: (e) => this.handleViewLoadPage(e, key), onMrdLoadAggregations: (e) => {
|
|
259
263
|
var _a;
|
|
260
264
|
e.stopPropagation();
|
|
261
265
|
this.mrdLoadViewAggregations.emit(Object.assign({ name: key, dataClass: (_a = item.dataClass) !== null && _a !== void 0 ? _a : key }, e.detail));
|
|
@@ -359,7 +363,7 @@ export class MrdLayoutSection {
|
|
|
359
363
|
const docArchetype = resolveArchetype(this.archetypes, ARCHETYPE_DOCUMENT);
|
|
360
364
|
const invoiceArchetype = !docArchetype ? resolveArchetype(this.archetypes, ARCHETYPE_INVOICE) : undefined;
|
|
361
365
|
const emailArchetype = !docArchetype && !invoiceArchetype ? resolveArchetype(this.archetypes, ARCHETYPE_EMAIL) : undefined;
|
|
362
|
-
return (h(Host, { key: '
|
|
366
|
+
return (h(Host, { key: '07b1d61b170a5ae971ba54eaf775e1787dea1642' }, h("div", { key: 'd146e0542b1de11bd130eb3d36d54e4faf055309', class: "mrd-layout-section" }, docArchetype && this.renderDocumentObject(docArchetype), invoiceArchetype && this.renderInvoiceObject(invoiceArchetype), emailArchetype && this.renderEmailObject(emailArchetype), !docArchetype && !invoiceArchetype && !emailArchetype && this.items.map(item => this.renderItem(item))), this.renderImageModal()));
|
|
363
367
|
}
|
|
364
368
|
static get is() { return "mrd-layout-section"; }
|
|
365
369
|
static get encapsulation() { return "scoped"; }
|
|
@@ -519,6 +523,26 @@ export class MrdLayoutSection {
|
|
|
519
523
|
"getter": false,
|
|
520
524
|
"setter": false,
|
|
521
525
|
"defaultValue": "[]"
|
|
526
|
+
},
|
|
527
|
+
"readOnly": {
|
|
528
|
+
"type": "boolean",
|
|
529
|
+
"mutable": false,
|
|
530
|
+
"complexType": {
|
|
531
|
+
"original": "boolean",
|
|
532
|
+
"resolved": "boolean",
|
|
533
|
+
"references": {}
|
|
534
|
+
},
|
|
535
|
+
"required": false,
|
|
536
|
+
"optional": false,
|
|
537
|
+
"docs": {
|
|
538
|
+
"tags": [],
|
|
539
|
+
"text": "When true, this dashboard is read-only (viewer role): the embedded mrd-table's\ncreate (\"+\") action and the documents view's New folder / Upload are disabled\nwith a tooltip explaining why, so it's clear at a glance the user can't mutate data."
|
|
540
|
+
},
|
|
541
|
+
"getter": false,
|
|
542
|
+
"setter": false,
|
|
543
|
+
"reflect": false,
|
|
544
|
+
"attribute": "read-only",
|
|
545
|
+
"defaultValue": "false"
|
|
522
546
|
}
|
|
523
547
|
};
|
|
524
548
|
}
|
|
@@ -31,6 +31,10 @@ export class MrdTable {
|
|
|
31
31
|
this.resizeObserver = null;
|
|
32
32
|
/** Guards against scheduling more than one column-width measurement per freeze. */
|
|
33
33
|
this.colWidthMeasureScheduled = false;
|
|
34
|
+
/** True once the header/totals widths have been checked against the current totals row. */
|
|
35
|
+
this.fixedContentMeasured = false;
|
|
36
|
+
/** Guards against scheduling more than one grow pass. */
|
|
37
|
+
this.growScheduled = false;
|
|
34
38
|
/** Drag origin of the running column resize (null = no drag in progress). */
|
|
35
39
|
this.resizeOrigin = null;
|
|
36
40
|
/** True once the user has dragged (or auto-fitted) a column — see colWidthsUserSet usages. */
|
|
@@ -57,6 +61,10 @@ export class MrdTable {
|
|
|
57
61
|
/** Time (ms) to wait for setPage() before retrying a page request; after
|
|
58
62
|
* MAX_RETRY_ATTEMPTS retries the page is marked failed (see failedPages). */
|
|
59
63
|
this.requestTimeoutMs = REQUEST_TIMEOUT_MS;
|
|
64
|
+
/** When true, the toolbar's create ("+") action is disabled (with a tooltip
|
|
65
|
+
* explaining why) so a viewer-role user can see at a glance that this dashboard
|
|
66
|
+
* is read-only. Sorting, filtering and export stay available — those don't mutate data. */
|
|
67
|
+
this.readOnly = false;
|
|
60
68
|
// ── Internal state ─────────────────────────────────────────────────────────
|
|
61
69
|
/** Index into allViews[] for the currently displayed view. 0 = primary, 1+ = alternatives. */
|
|
62
70
|
this.activeViewIdx = 0;
|
|
@@ -125,6 +133,7 @@ export class MrdTable {
|
|
|
125
133
|
this.colWidths = [];
|
|
126
134
|
this.colWidthMeasureScheduled = false;
|
|
127
135
|
this.colWidthsUserSet = false;
|
|
136
|
+
this.fixedContentMeasured = false;
|
|
128
137
|
}
|
|
129
138
|
// ── Lifecycle ──────────────────────────────────────────────────────────────
|
|
130
139
|
componentWillLoad() {
|
|
@@ -168,6 +177,7 @@ export class MrdTable {
|
|
|
168
177
|
this.colWidths = [];
|
|
169
178
|
this.colWidthMeasureScheduled = false;
|
|
170
179
|
this.colWidthsUserSet = false;
|
|
180
|
+
this.fixedContentMeasured = false;
|
|
171
181
|
this.scrollTop = 0;
|
|
172
182
|
this.renderStart = 0;
|
|
173
183
|
// Always fill the visible viewport on init — totalElements may be stale from a
|
|
@@ -241,6 +251,8 @@ export class MrdTable {
|
|
|
241
251
|
if (data.total != null)
|
|
242
252
|
this.aggregationsTotal = data.total;
|
|
243
253
|
this.aggregations = data;
|
|
254
|
+
// Fresh totals can be wider than the ones the columns were checked against.
|
|
255
|
+
this.fixedContentMeasured = false;
|
|
244
256
|
}
|
|
245
257
|
// ── Lifecycle ──────────────────────────────────────────────────────────────
|
|
246
258
|
disconnectedCallback() {
|
|
@@ -271,6 +283,7 @@ export class MrdTable {
|
|
|
271
283
|
}
|
|
272
284
|
componentDidRender() {
|
|
273
285
|
this.freezeColWidths();
|
|
286
|
+
this.growToFixedContent();
|
|
274
287
|
}
|
|
275
288
|
/** Freeze the column widths once the first data page has rendered.
|
|
276
289
|
*
|
|
@@ -324,6 +337,38 @@ export class MrdTable {
|
|
|
324
337
|
const content = scrollWidth > clientWidth ? scrollWidth + paddingRight : clientWidth;
|
|
325
338
|
return Math.ceil(content + borders);
|
|
326
339
|
}
|
|
340
|
+
/** Width a header cell needs, measured from its children instead of the cell's own
|
|
341
|
+
* scrollWidth. A header is not just text: it holds the label plus a sort icon (and a filter
|
|
342
|
+
* icon once the column is filtered), with the drag handle absolutely positioned on top of
|
|
343
|
+
* the right padding. Summing the in-flow children makes the icons part of the width by
|
|
344
|
+
* construction — and a child's own rect is never clipped by the header's `overflow: hidden`,
|
|
345
|
+
* so it stays correct even when the header is already truncated on screen.
|
|
346
|
+
* Returns 0 when nothing can be measured, so callers fall back to neededWidth(). */
|
|
347
|
+
neededHeaderWidth(th) {
|
|
348
|
+
const px = (v) => (typeof v === 'number' && Number.isFinite(v) ? v : 0);
|
|
349
|
+
const size = (v) => px(parseFloat(v || '0'));
|
|
350
|
+
const style = getComputedStyle(th);
|
|
351
|
+
const paddingLeft = size(style.paddingLeft);
|
|
352
|
+
const paddingRight = size(style.paddingRight);
|
|
353
|
+
const borders = Math.max(0, px(th.offsetWidth) - px(th.clientWidth));
|
|
354
|
+
let inflow = 0;
|
|
355
|
+
let handle = 0;
|
|
356
|
+
Array.from(th.children).forEach(child => {
|
|
357
|
+
const el = child;
|
|
358
|
+
const cs = getComputedStyle(el);
|
|
359
|
+
const width = px(el.getBoundingClientRect().width) + size(cs.marginLeft) + size(cs.marginRight);
|
|
360
|
+
// The drag handle is absolutely positioned: it takes no room in the flow, it overlays it.
|
|
361
|
+
if (cs.position === 'absolute')
|
|
362
|
+
handle = Math.max(handle, width);
|
|
363
|
+
else
|
|
364
|
+
inflow += width;
|
|
365
|
+
});
|
|
366
|
+
if (inflow <= 0)
|
|
367
|
+
return 0;
|
|
368
|
+
// The handle sits over the right padding; only reserve what would stick out past it,
|
|
369
|
+
// so the label never ends up underneath the grab area.
|
|
370
|
+
return Math.ceil(inflow + paddingLeft + paddingRight + Math.max(0, handle - paddingRight) + borders);
|
|
371
|
+
}
|
|
327
372
|
/** Width each column needs for its header and its rendered page-0 cells — what the columns
|
|
328
373
|
* are locked to, so they never end up narrower than their content.
|
|
329
374
|
*
|
|
@@ -337,11 +382,11 @@ export class MrdTable {
|
|
|
337
382
|
if (ths.length === 0 || ths.length !== this.columns.length)
|
|
338
383
|
return null;
|
|
339
384
|
const needed = Array.from(ths).map((th, idx) => {
|
|
340
|
-
let widest = this.neededWidth(th);
|
|
385
|
+
let widest = Math.max(this.neededWidth(th), this.neededHeaderWidth(th));
|
|
341
386
|
// .mrd-table__cell only matches real data cells — the shimmer/retry rows render a single
|
|
342
387
|
// colSpan cell (without that class) which would measure as the full table width.
|
|
343
388
|
this.el
|
|
344
|
-
.querySelectorAll(
|
|
389
|
+
.querySelectorAll(this.columnCellSelector(idx))
|
|
345
390
|
.forEach(cell => { widest = Math.max(widest, this.neededWidth(cell)); });
|
|
346
391
|
return widest;
|
|
347
392
|
});
|
|
@@ -351,6 +396,50 @@ export class MrdTable {
|
|
|
351
396
|
return null;
|
|
352
397
|
return needed.map(w => Math.min(Math.max(w, MIN_COL_WIDTH), MAX_AUTOFIT_WIDTH));
|
|
353
398
|
}
|
|
399
|
+
/** Data and totals cells of one column. The totals row is included everywhere the data
|
|
400
|
+
* cells are: it is fixed, always-visible content, so it must never be clipped. */
|
|
401
|
+
columnCellSelector(idx) {
|
|
402
|
+
return `.mrd-table__cell:nth-child(${idx + 1}), .mrd-table__totals-cell:nth-child(${idx + 1})`;
|
|
403
|
+
}
|
|
404
|
+
/** Width the header and totals cells of each column need. Null when nothing has a box. */
|
|
405
|
+
measureFixedContentWidths() {
|
|
406
|
+
const ths = this.el.querySelectorAll('.mrd-table__header');
|
|
407
|
+
if (ths.length === 0 || ths.length !== this.columns.length)
|
|
408
|
+
return null;
|
|
409
|
+
const needed = Array.from(ths).map((th, idx) => {
|
|
410
|
+
let widest = Math.max(this.neededWidth(th), this.neededHeaderWidth(th));
|
|
411
|
+
this.el
|
|
412
|
+
.querySelectorAll(`.mrd-table__totals-cell:nth-child(${idx + 1})`)
|
|
413
|
+
.forEach(cell => { widest = Math.max(widest, this.neededWidth(cell)); });
|
|
414
|
+
return widest;
|
|
415
|
+
});
|
|
416
|
+
return needed.every(w => w <= 0) ? null : needed;
|
|
417
|
+
}
|
|
418
|
+
/** Widen columns whose header or totals cell does not fit. Data cells may be ellipsised —
|
|
419
|
+
* that is what the lock is for — but the header and the totals row are fixed content that
|
|
420
|
+
* is always on screen, so clipping them just hides information with no way to reveal it.
|
|
421
|
+
*
|
|
422
|
+
* It runs as a second pass because the totals only arrive with setAggregations(), long
|
|
423
|
+
* after the widths were frozen on page 0. Grow-only and once per totals change, so it can
|
|
424
|
+
* never turn into the per-page re-proportioning the lock exists to prevent. Hand-set
|
|
425
|
+
* widths are left alone. */
|
|
426
|
+
growToFixedContent() {
|
|
427
|
+
if (this.colWidths.length === 0 || this.colWidthsUserSet || this.fixedContentMeasured || this.growScheduled)
|
|
428
|
+
return;
|
|
429
|
+
this.growScheduled = true;
|
|
430
|
+
writeTask(() => {
|
|
431
|
+
this.growScheduled = false;
|
|
432
|
+
if (this.colWidths.length === 0 || this.colWidthsUserSet)
|
|
433
|
+
return;
|
|
434
|
+
const needed = this.measureFixedContentWidths();
|
|
435
|
+
if (!needed)
|
|
436
|
+
return;
|
|
437
|
+
this.fixedContentMeasured = true;
|
|
438
|
+
const grown = this.colWidths.map((w, i) => Math.min(Math.max(w, needed[i]), MAX_AUTOFIT_WIDTH));
|
|
439
|
+
if (grown.some((w, i) => w !== this.colWidths[i]))
|
|
440
|
+
this.colWidths = grown;
|
|
441
|
+
});
|
|
442
|
+
}
|
|
354
443
|
/** Re-reads the rendered header widths into colWidths so a drag starts from what is
|
|
355
444
|
* actually on screen — and locks the columns if they weren't locked yet.
|
|
356
445
|
*
|
|
@@ -409,9 +498,14 @@ export class MrdTable {
|
|
|
409
498
|
return;
|
|
410
499
|
// .mrd-table__cell only matches real data cells — the shimmer/retry rows render a single
|
|
411
500
|
// colSpan cell (without that class) which would otherwise measure as the full table width.
|
|
412
|
-
const cells = this.el.querySelectorAll(`.mrd-table__header:nth-child(${idx + 1}), .
|
|
501
|
+
const cells = this.el.querySelectorAll(`.mrd-table__header:nth-child(${idx + 1}), ${this.columnCellSelector(idx)}`);
|
|
413
502
|
let widest = 0;
|
|
414
|
-
cells.forEach(cell => {
|
|
503
|
+
cells.forEach(cell => {
|
|
504
|
+
const needed = cell.classList.contains('mrd-table__header')
|
|
505
|
+
? Math.max(this.neededWidth(cell), this.neededHeaderWidth(cell))
|
|
506
|
+
: this.neededWidth(cell);
|
|
507
|
+
widest = Math.max(widest, needed);
|
|
508
|
+
});
|
|
415
509
|
if (widest > 0)
|
|
416
510
|
this.setColWidth(idx, Math.min(widest, MAX_AUTOFIT_WIDTH));
|
|
417
511
|
}
|
|
@@ -540,7 +634,7 @@ export class MrdTable {
|
|
|
540
634
|
const raw = (_b = (_a = this.item) === null || _a === void 0 ? void 0 : _a.actions) !== null && _b !== void 0 ? _b : [];
|
|
541
635
|
return (raw !== null && raw !== void 0 ? raw : []).reduce((acc, a) => {
|
|
542
636
|
if (a === 'NEW')
|
|
543
|
-
acc.push({ action: 'create', label: t('table_new_record', this.locale), icon: 'assets/sprites.svg#icon-plus', variant: 'primary' });
|
|
637
|
+
acc.push({ action: 'create', label: t('table_new_record', this.locale), icon: 'assets/sprites.svg#icon-plus', variant: 'primary', disabled: this.readOnly });
|
|
544
638
|
if (a === 'EXPORT')
|
|
545
639
|
acc.push({ action: 'export', label: t('table_export_excel', this.locale), icon: 'assets/sprites.svg#icon-file-excel' });
|
|
546
640
|
return acc;
|
|
@@ -903,7 +997,7 @@ export class MrdTable {
|
|
|
903
997
|
// ── Render: toolbar ────────────────────────────────────────────────────────
|
|
904
998
|
renderToolbar() {
|
|
905
999
|
var _a;
|
|
906
|
-
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, cb: {
|
|
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: {
|
|
907
1001
|
onClearAllFilters: () => this.clearAllFilters(),
|
|
908
1002
|
onViewSwitch: idx => this.handleViewSwitch(idx),
|
|
909
1003
|
onToggleViewPopover: e => this.toggleViewPopover(e),
|
|
@@ -1248,6 +1342,26 @@ export class MrdTable {
|
|
|
1248
1342
|
"reflect": false,
|
|
1249
1343
|
"attribute": "request-timeout-ms",
|
|
1250
1344
|
"defaultValue": "15000"
|
|
1345
|
+
},
|
|
1346
|
+
"readOnly": {
|
|
1347
|
+
"type": "boolean",
|
|
1348
|
+
"mutable": false,
|
|
1349
|
+
"complexType": {
|
|
1350
|
+
"original": "boolean",
|
|
1351
|
+
"resolved": "boolean",
|
|
1352
|
+
"references": {}
|
|
1353
|
+
},
|
|
1354
|
+
"required": false,
|
|
1355
|
+
"optional": false,
|
|
1356
|
+
"docs": {
|
|
1357
|
+
"tags": [],
|
|
1358
|
+
"text": "When true, the toolbar's create (\"+\") action is disabled (with a tooltip\nexplaining why) so a viewer-role user can see at a glance that this dashboard\nis read-only. Sorting, filtering and export stay available \u2014 those don't mutate data."
|
|
1359
|
+
},
|
|
1360
|
+
"getter": false,
|
|
1361
|
+
"setter": false,
|
|
1362
|
+
"reflect": false,
|
|
1363
|
+
"attribute": "read-only",
|
|
1364
|
+
"defaultValue": "false"
|
|
1251
1365
|
}
|
|
1252
1366
|
};
|
|
1253
1367
|
}
|
|
@@ -87,7 +87,7 @@ export const JsonModal = ({ html, locale, onClose }) => {
|
|
|
87
87
|
return null;
|
|
88
88
|
return (h("div", { class: "mrd-table__modal-backdrop", onClick: onClose, role: "dialog", "aria-modal": "true" }, h("div", { class: "mrd-table__modal", onClick: (e) => e.stopPropagation() }, h("button", { class: "mrd-table__modal-close", onClick: onClose, "aria-label": t('close', locale) }, "\u2715"), h("pre", { class: "mrd-table__modal-json", innerHTML: html }))));
|
|
89
89
|
};
|
|
90
|
-
export const Toolbar = ({ locale, filterCount, actions, views, activeViewIdx, viewPopoverOpen, createPickerOpen, createTypes, cb }) => {
|
|
90
|
+
export const Toolbar = ({ locale, filterCount, actions, views, activeViewIdx, viewPopoverOpen, createPickerOpen, createTypes, readOnly, cb }) => {
|
|
91
91
|
var _a;
|
|
92
92
|
const hasActions = actions.length > 0;
|
|
93
93
|
const hasViewSwitcher = views.length > 1;
|
|
@@ -98,13 +98,17 @@ export const Toolbar = ({ locale, filterCount, actions, views, activeViewIdx, vi
|
|
|
98
98
|
} }, v.label)))))), h("button", { class: "mrd-table__view-arrow", "aria-label": "Next view", onClick: () => cb.onViewSwitch((activeViewIdx + 1) % views.length) }, "\u25B6")))), hasActions && (h("div", { class: "mrd-table__toolbar-right" }, actions.map(a => {
|
|
99
99
|
var _a, _b;
|
|
100
100
|
const pickerTypes = a.action === 'create' ? createTypes : null;
|
|
101
|
+
// readOnly only ever disables 'create' (see mrd-table.tsx tableActions) — export,
|
|
102
|
+
// sorting and filtering don't mutate data and stay available.
|
|
103
|
+
const disabled = a.disabled || (readOnly && a.action === 'create');
|
|
104
|
+
const tooltip = disabled && readOnly ? t('readonly_access', locale) : a.label;
|
|
101
105
|
if (pickerTypes === null || pickerTypes === void 0 ? void 0 : pickerTypes.length) {
|
|
102
|
-
return (h("div", { class: "mrd-table__create-picker-wrap", key: `action-${a.action}` }, h("button", { class: `mrd-table__action mrd-table__action--${(_a = a.variant) !== null && _a !== void 0 ? _a : 'secondary'}`, onClick: cb.onToggleCreatePicker }, a.icon
|
|
106
|
+
return (h("div", { class: "mrd-table__create-picker-wrap", key: `action-${a.action}` }, h("button", { class: `mrd-table__action mrd-table__action--${(_a = a.variant) !== null && _a !== void 0 ? _a : 'secondary'}`, disabled: disabled, onClick: cb.onToggleCreatePicker }, a.icon
|
|
103
107
|
? h("svg", { class: "mrd-table__action-icon", "aria-hidden": "true" }, h("use", { href: a.icon }))
|
|
104
|
-
: a.label, h("span", { class: "mrd-table__action-tooltip" },
|
|
108
|
+
: a.label, h("span", { class: "mrd-table__action-tooltip" }, tooltip)), createPickerOpen && (h("div", { class: "mrd-table__create-picker" }, pickerTypes.map(ct => (h("button", { key: ct.type, class: "mrd-table__create-picker-item", onClick: () => cb.onCreate(ct.type) }, ct.label)))))));
|
|
105
109
|
}
|
|
106
|
-
return (h("button", { key: `action-${a.action}`, class: `mrd-table__action mrd-table__action--${(_b = a.variant) !== null && _b !== void 0 ? _b : 'secondary'}`, disabled:
|
|
110
|
+
return (h("button", { key: `action-${a.action}`, class: `mrd-table__action mrd-table__action--${(_b = a.variant) !== null && _b !== void 0 ? _b : 'secondary'}`, disabled: disabled, onClick: () => cb.onAction(a.action) }, a.icon
|
|
107
111
|
? h("svg", { class: "mrd-table__action-icon", "aria-hidden": "true" }, h("use", { href: a.icon }))
|
|
108
|
-
: a.label, h("span", { class: "mrd-table__action-tooltip" },
|
|
112
|
+
: a.label, h("span", { class: "mrd-table__action-tooltip" }, tooltip)));
|
|
109
113
|
})))));
|
|
110
114
|
};
|
|
@@ -14,6 +14,8 @@ let _dashboardType = 'class'; // 'class' | 'general' | 'navigation' | 'objec
|
|
|
14
14
|
let _sectionGeneration = 0; // incremented on each renderSection(); prevents stale fetches
|
|
15
15
|
let _navHistory = []; // stack van { dashboardData, dashboardRecord, activeLayoutIndex }
|
|
16
16
|
let _meUser = null; // { id: href, label: name } — resolved once after login via /accounts/me
|
|
17
|
+
let _detailReadOnly = false; // Detail View tab: readOnly toggle (TASK-0248 demo)
|
|
18
|
+
let _liveApiReadOnly = false; // Live API tab: readOnly toggle (TASK-0248 demo)
|
|
17
19
|
|
|
18
20
|
/* =====================================================================
|
|
19
21
|
UTILITIES
|
|
@@ -135,6 +137,7 @@ async function initDetailViewTab() {
|
|
|
135
137
|
table.totalElements = window.EXAMPLE_COMPANIES.length;
|
|
136
138
|
table.pageSize = 20;
|
|
137
139
|
table.locale = _locale;
|
|
140
|
+
table.readOnly = _detailReadOnly;
|
|
138
141
|
|
|
139
142
|
// Register before init(): init() emits mrdLoadPage for page 0 itself.
|
|
140
143
|
table.addEventListener('mrdLoadPage', async (e) => {
|
|
@@ -175,6 +178,7 @@ function showCompanyDetail(row) {
|
|
|
175
178
|
section.items = layout.items;
|
|
176
179
|
section.data = company;
|
|
177
180
|
section.locale = _locale;
|
|
181
|
+
section.readOnly = _detailReadOnly;
|
|
178
182
|
|
|
179
183
|
section.addEventListener('mrdNavigate', (e) => {
|
|
180
184
|
logEvent('mrdNavigate', e.detail);
|
|
@@ -199,6 +203,17 @@ function showCompanyList() {
|
|
|
199
203
|
document.getElementById('detail-table-panel').style.display = 'block';
|
|
200
204
|
}
|
|
201
205
|
|
|
206
|
+
/** readOnly demo (TASK-0248): flips the companies table and any rendered
|
|
207
|
+
* detail sections without re-fetching — readOnly is a pure render-time prop. */
|
|
208
|
+
function onDetailViewReadOnlyToggle(checked) {
|
|
209
|
+
_detailReadOnly = checked;
|
|
210
|
+
const table = document.getElementById('companies-table');
|
|
211
|
+
if (table) table.readOnly = checked;
|
|
212
|
+
document.querySelectorAll('#detail-sections mrd-layout-section').forEach((section) => {
|
|
213
|
+
section.readOnly = checked;
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
|
|
202
217
|
/* =====================================================================
|
|
203
218
|
DOCUMENT PREVIEW TAB
|
|
204
219
|
===================================================================== */
|
|
@@ -494,6 +509,7 @@ async function renderSection(index) {
|
|
|
494
509
|
section.items = layout.items;
|
|
495
510
|
section.data = _dashboardRecord ?? { _links: _dashboardData._links ?? {} };
|
|
496
511
|
section.locale = _locale;
|
|
512
|
+
section.readOnly = _liveApiReadOnly;
|
|
497
513
|
// Dashboard-level archetypes describe the object itself → single-document view.
|
|
498
514
|
// Only apply to the main object layout, not tab-page (related) layouts.
|
|
499
515
|
section.archetypes = (!layout.tabPage && _dashboardData.archetypes) ? _dashboardData.archetypes : [];
|
|
@@ -670,6 +686,14 @@ async function renderSection(index) {
|
|
|
670
686
|
}
|
|
671
687
|
|
|
672
688
|
|
|
689
|
+
/** readOnly demo (TASK-0248): flips the currently rendered section without
|
|
690
|
+
* re-fetching — readOnly is a pure render-time prop, no reload needed. */
|
|
691
|
+
function onLiveApiReadOnlyToggle(checked) {
|
|
692
|
+
_liveApiReadOnly = checked;
|
|
693
|
+
const section = document.querySelector('#sections-container mrd-layout-section');
|
|
694
|
+
if (section) section.readOnly = checked;
|
|
695
|
+
}
|
|
696
|
+
|
|
673
697
|
function toggleJsonViewer() {
|
|
674
698
|
const viewer = document.getElementById('json-viewer');
|
|
675
699
|
const btn = document.getElementById('btn-json-toggle');
|