@mmlogic/components 0.5.12 → 0.5.14

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 (53) hide show
  1. package/dist/cjs/{cell-renderer-pwUGEDql.js → cell-renderer-mjS630f1.js} +16 -0
  2. package/dist/cjs/loader.cjs.js +1 -1
  3. package/dist/cjs/mosterdcomponents.cjs.js +1 -1
  4. package/dist/cjs/mrd-boolean-field_25.cjs.entry.js +212 -45
  5. package/dist/cjs/mrd-merge-view.cjs.entry.js +1 -1
  6. package/dist/collection/components/documents/mrd-document-container/mrd-document-container.js +30 -4
  7. package/dist/collection/components/documents/mrd-document-list/mrd-document-list.js +55 -6
  8. package/dist/collection/components/layout/mrd-layout-section/mrd-layout-section.js +27 -3
  9. package/dist/collection/components/table/mrd-table/mrd-table.css +36 -0
  10. package/dist/collection/components/table/mrd-table/mrd-table.js +162 -26
  11. package/dist/collection/components/table/mrd-table/table-parts.js +14 -6
  12. package/dist/collection/components/table/mrd-table/table-query.js +6 -0
  13. package/dist/collection/dev/app.js +24 -0
  14. package/dist/collection/utils/i18n.js +16 -0
  15. package/dist/components/i18n.js +1 -1
  16. package/dist/components/mrd-document-container2.js +1 -1
  17. package/dist/components/mrd-document-list2.js +1 -1
  18. package/dist/components/mrd-layout-section.js +1 -1
  19. package/dist/components/mrd-table2.js +1 -1
  20. package/dist/esm/{cell-renderer-B8rJpnq8.js → cell-renderer-BDY8UXkH.js} +16 -0
  21. package/dist/esm/loader.js +1 -1
  22. package/dist/esm/mosterdcomponents.js +1 -1
  23. package/dist/esm/mrd-boolean-field_25.entry.js +212 -45
  24. package/dist/esm/mrd-merge-view.entry.js +1 -1
  25. package/dist/mosterdcomponents/cell-renderer-B8u5BCK-.js.map +1 -0
  26. package/dist/mosterdcomponents/client-layout-Bc42CfJB.js.map +1 -0
  27. package/dist/mosterdcomponents/document-attachments-BufPmMRv.js.map +1 -0
  28. package/dist/mosterdcomponents/field-helpers-CNQcI3Hf.js.map +1 -0
  29. package/dist/mosterdcomponents/file-upload-common-C9wp47CB.js.map +1 -0
  30. package/dist/mosterdcomponents/format-gcecItcD.js.map +1 -0
  31. package/dist/mosterdcomponents/i18n-ydNM87-Q.js.map +1 -0
  32. package/dist/mosterdcomponents/index-1ayBojyN.js.map +1 -0
  33. package/dist/mosterdcomponents/index-DhffneUF.js.map +1 -0
  34. package/dist/mosterdcomponents/index.esm.js.map +1 -0
  35. package/dist/mosterdcomponents/mosterdcomponents.esm.js +1 -1
  36. package/dist/mosterdcomponents/mosterdcomponents.esm.js.map +1 -0
  37. package/dist/mosterdcomponents/p-Dkm4WxMy.js +1 -0
  38. package/dist/mosterdcomponents/p-c834951a.entry.js +3 -0
  39. package/dist/mosterdcomponents/{p-e540de6a.entry.js → p-e8b0880f.entry.js} +1 -1
  40. package/dist/mosterdcomponents/purify.es-_duHfPgC.js.map +1 -0
  41. package/dist/mosterdcomponents/query-params-BZbQoeYF.js.map +1 -0
  42. package/dist/mosterdcomponents/quill-C9pgw_k-.js.map +1 -0
  43. package/dist/mosterdcomponents/validation-CjjTt66G.js.map +1 -0
  44. package/dist/types/components/documents/mrd-document-container/mrd-document-container.d.ts +4 -0
  45. package/dist/types/components/documents/mrd-document-list/mrd-document-list.d.ts +6 -1
  46. package/dist/types/components/layout/mrd-layout-section/mrd-layout-section.d.ts +4 -0
  47. package/dist/types/components/table/mrd-table/mrd-table.d.ts +52 -1
  48. package/dist/types/components/table/mrd-table/table-parts.d.ts +18 -0
  49. package/dist/types/components/table/mrd-table/table-query.d.ts +6 -0
  50. package/dist/types/components.d.ts +45 -1
  51. package/package.json +1 -1
  52. package/dist/mosterdcomponents/p-eb322d5c.entry.js +0 -3
  53. package/dist/mosterdcomponents/p-rTM1tU_M.js +0 -1
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var index = require('./index-DVbdkYO3.js');
4
- var cellRenderer = require('./cell-renderer-pwUGEDql.js');
4
+ var cellRenderer = require('./cell-renderer-mjS630f1.js');
5
5
  var clientLayout = require('./client-layout-CqVuJumR.js');
6
6
 
7
7
  const mrdBooleanFieldCss = () => `.sc-mrd-boolean-field-h{display:block}.mrd-boolean-field.sc-mrd-boolean-field{display:flex;align-items:center;width:100%}.mrd-boolean-field__toggle-label.sc-mrd-boolean-field{display:flex;align-items:center;gap:var(--mrd-space-3);cursor:pointer;user-select:none}.mrd-boolean-field__checkbox.sc-mrd-boolean-field{position:absolute;opacity:0;width:0;height:0;pointer-events:none}.mrd-boolean-field__checkbox.sc-mrd-boolean-field:checked+.mrd-boolean-field__toggle.sc-mrd-boolean-field{background-color:var(--mrd-color-primary)}.mrd-boolean-field__checkbox.sc-mrd-boolean-field:checked+.mrd-boolean-field__toggle.sc-mrd-boolean-field::after{transform:translateX(20px)}.mrd-boolean-field__checkbox.sc-mrd-boolean-field:focus+.mrd-boolean-field__toggle.sc-mrd-boolean-field{box-shadow:var(--mrd-shadow-focus)}.mrd-boolean-field__checkbox.sc-mrd-boolean-field:disabled+.mrd-boolean-field__toggle.sc-mrd-boolean-field{opacity:0.5;cursor:not-allowed}.mrd-boolean-field__toggle.sc-mrd-boolean-field{position:relative;display:inline-block;width:44px;height:24px;background-color:var(--mrd-color-neutral-300);border-radius:var(--mrd-border-radius-full);transition:background-color var(--mrd-transition);flex-shrink:0}.mrd-boolean-field__toggle.sc-mrd-boolean-field::after{content:"";position:absolute;top:2px;left:2px;width:20px;height:20px;background-color:var(--mrd-color-white);border-radius:50%;transition:transform var(--mrd-transition);box-shadow:var(--mrd-shadow-sm)}.mrd-boolean-field__text.sc-mrd-boolean-field{font-family:var(--mrd-font-family);font-size:var(--mrd-font-size-base);color:var(--mrd-color-neutral-800)}.mrd-boolean-field__text--required.sc-mrd-boolean-field::after{content:" *";color:var(--mrd-color-danger)}`;
@@ -363,6 +363,10 @@ const MrdDocumentContainer = class {
363
363
  * on top, Finder-style. The table manages its own height. */
364
364
  this.height = 460;
365
365
  this.locale = navigator.language;
366
+ /** When true, this dashboard is read-only: New folder and Upload are disabled
367
+ * (with a tooltip explaining why) and the embedded table/list get it passed
368
+ * through so their own create/upload/drag-drop paths are disabled too. */
369
+ this.readOnly = false;
366
370
  /** Active body: folder tree or flat table. */
367
371
  this.mode = 'tree';
368
372
  /** Whether the folder tree currently has a valid target for "New folder". */
@@ -377,6 +381,8 @@ const MrdDocumentContainer = class {
377
381
  };
378
382
  this.pickFile = () => {
379
383
  var _a;
384
+ if (this.readOnly)
385
+ return;
380
386
  (_a = this.fileInputEl) === null || _a === void 0 ? void 0 : _a.click();
381
387
  };
382
388
  /** One file per upload, matching the drop handler. Reset the input afterwards
@@ -413,11 +419,11 @@ const MrdDocumentContainer = class {
413
419
  }
414
420
  // ── Toolbar ────────────────────────────────────────────────────────────────
415
421
  renderToolbar() {
416
- return (index.h("div", { class: "mrd-document-container__toolbar" }, this.mode === 'tree' && (index.h("button", { type: "button", class: "mrd-document-container__action", disabled: !this.canCreate, onClick: this.createFolder }, index.h("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true" }, index.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" }), index.h("path", { d: "M12 11v4M10 13h4" })), cellRenderer.t('new_folder', this.locale))), this.mode === 'tree' && (index.h("button", { type: "button", class: "mrd-document-container__action", disabled: !this.canCreate, title: this.targetLabel ? `${cellRenderer.t('doc_upload_to', this.locale)} ${this.targetLabel}` : undefined, onClick: this.pickFile }, index.h("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true" }, index.h("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }), index.h("path", { d: "M12 3v13M7 8l5-5 5 5" })), cellRenderer.t('doc_upload', this.locale))), index.h("input", { type: "file", class: "mrd-document-container__file-input", ref: el => (this.fileInputEl = el), onChange: this.onFilePicked }), index.h("div", { class: "mrd-document-container__seg", role: "group", "aria-label": cellRenderer.t('doc_view_switch', this.locale) }, index.h("button", { class: "mrd-document-container__seg-btn", "aria-pressed": String(this.mode === 'tree'), title: cellRenderer.t('doc_view_tree', this.locale), "aria-label": cellRenderer.t('doc_view_tree', this.locale), onClick: () => this.setMode('tree') }, index.h("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true" }, index.h("path", { d: "M3 5h6M3 12h9M3 19h6" }), index.h("circle", { cx: "18", cy: "5", r: "1.6" }), index.h("circle", { cx: "18", cy: "19", r: "1.6" }), index.h("path", { d: "M16 12h4" }))), index.h("button", { class: "mrd-document-container__seg-btn", "aria-pressed": String(this.mode === 'list'), title: cellRenderer.t('doc_view_list', this.locale), "aria-label": cellRenderer.t('doc_view_list', this.locale), onClick: () => this.setMode('list') }, index.h("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true" }, index.h("path", { d: "M8 6h13M8 12h13M8 18h13M3 6h.01M3 12h.01M3 18h.01" }))))));
422
+ return (index.h("div", { class: "mrd-document-container__toolbar" }, this.mode === 'tree' && (index.h("button", { type: "button", class: "mrd-document-container__action", disabled: this.readOnly || !this.canCreate, title: this.readOnly ? cellRenderer.t('readonly_access', this.locale) : undefined, onClick: this.createFolder }, index.h("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true" }, index.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" }), index.h("path", { d: "M12 11v4M10 13h4" })), cellRenderer.t('new_folder', this.locale))), this.mode === 'tree' && (index.h("button", { type: "button", class: "mrd-document-container__action", disabled: this.readOnly || !this.canCreate, title: this.readOnly ? cellRenderer.t('readonly_access', this.locale) : (this.targetLabel ? `${cellRenderer.t('doc_upload_to', this.locale)} ${this.targetLabel}` : undefined), onClick: this.pickFile }, index.h("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true" }, index.h("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }), index.h("path", { d: "M12 3v13M7 8l5-5 5 5" })), cellRenderer.t('doc_upload', this.locale))), index.h("input", { type: "file", class: "mrd-document-container__file-input", ref: el => (this.fileInputEl = el), onChange: this.onFilePicked }), index.h("div", { class: "mrd-document-container__seg", role: "group", "aria-label": cellRenderer.t('doc_view_switch', this.locale) }, index.h("button", { class: "mrd-document-container__seg-btn", "aria-pressed": String(this.mode === 'tree'), title: cellRenderer.t('doc_view_tree', this.locale), "aria-label": cellRenderer.t('doc_view_tree', this.locale), onClick: () => this.setMode('tree') }, index.h("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true" }, index.h("path", { d: "M3 5h6M3 12h9M3 19h6" }), index.h("circle", { cx: "18", cy: "5", r: "1.6" }), index.h("circle", { cx: "18", cy: "19", r: "1.6" }), index.h("path", { d: "M16 12h4" }))), index.h("button", { class: "mrd-document-container__seg-btn", "aria-pressed": String(this.mode === 'list'), title: cellRenderer.t('doc_view_list', this.locale), "aria-label": cellRenderer.t('doc_view_list', this.locale), onClick: () => this.setMode('list') }, index.h("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true" }, index.h("path", { d: "M8 6h13M8 12h13M8 18h13M3 6h.01M3 12h.01M3 18h.01" }))))));
417
423
  }
418
424
  // ── Bodies ───────────────────────────────────────────────────────────────
419
425
  renderTree() {
420
- return (index.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) => {
426
+ return (index.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) => {
421
427
  e.stopPropagation();
422
428
  this.mrdLoadDistinct.emit(e.detail);
423
429
  }, onMrdLoadPage: (e) => {
@@ -444,7 +450,7 @@ const MrdDocumentContainer = class {
444
450
  } }));
445
451
  }
446
452
  renderList() {
447
- return (index.h("mrd-table", { "data-view": this.viewKey, ref: el => (this.tableEl = el), item: this.item, parentId: this.parentId, locale: this.locale, onMrdLoadPage: (e) => {
453
+ return (index.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) => {
448
454
  e.stopPropagation();
449
455
  this.mrdLoadPage.emit(e.detail);
450
456
  }, onMrdLoadAggregations: (e) => {
@@ -468,7 +474,7 @@ const MrdDocumentContainer = class {
468
474
  }
469
475
  render() {
470
476
  const treeMode = this.mode === 'tree';
471
- return (index.h(index.Host, { key: 'ae948e2458d9087179e850b527802c489067d93f' }, this.renderToolbar(), index.h("div", { key: 'a04e1a78413aa794d0bda551aeb9c6245f1aafa2', class: {
477
+ return (index.h(index.Host, { key: '6792d49f08dd19f6c51e4b656df06bbd8bc14f3e' }, this.renderToolbar(), index.h("div", { key: '702f4c288f8c40bd2c5999beb392b9dfd022b0bc', class: {
472
478
  'mrd-document-container__body': true,
473
479
  'mrd-document-container__body--tree': treeMode,
474
480
  }, style: treeMode ? { height: `${this.height}px`, overflowY: 'auto' } : {} }, treeMode ? this.renderTree() : this.renderList())));
@@ -735,6 +741,11 @@ const MrdDocumentList = class {
735
741
  * accepts file drops and folder creation. */
736
742
  this.containerHref = '';
737
743
  this.locale = navigator.language;
744
+ /** When true, folder creation, file upload (button and drag-drop) and moving a
745
+ * document between folders are all disabled — a viewer-role user can browse
746
+ * but not mutate anything. Drives mrdCanCreate(false) so the host toolbar's
747
+ * New folder / Upload buttons stay disabled too. */
748
+ this.readOnly = false;
738
749
  this.containers = [];
739
750
  this.rootLoading = true;
740
751
  /** True when the single-container level is collapsed away (dossier context). */
@@ -777,6 +788,8 @@ const MrdDocumentList = class {
777
788
  /** The optimistic document row awaiting its self href from the create POST. */
778
789
  this.lastOptimisticDoc = null;
779
790
  this.startCreateFolder = () => {
791
+ if (this.readOnly)
792
+ return;
780
793
  const resolved = this.resolveTarget();
781
794
  if (!resolved || this.pendingNode)
782
795
  return;
@@ -816,6 +829,10 @@ const MrdDocumentList = class {
816
829
  // ── Drag & drop: move a document into another folder / container ────────────
817
830
  this.onDocDragStart = (e, doc, node) => {
818
831
  var _a, _b, _c;
832
+ if (this.readOnly) {
833
+ e.preventDefault();
834
+ return;
835
+ }
819
836
  this.dragDoc = doc;
820
837
  this.dragFromId = node.id;
821
838
  if (e.dataTransfer) {
@@ -835,6 +852,11 @@ const MrdDocumentList = class {
835
852
  * root drop-zone; a different node is highlighted (its own folder = no-op). */
836
853
  this.onNodeDragOver = (e, node) => {
837
854
  const file = this.isFileDrag(e);
855
+ if (this.readOnly) {
856
+ if (file)
857
+ e.preventDefault(); // swallow so the browser doesn't navigate to the dropped file
858
+ return;
859
+ }
838
860
  if (!this.dragDoc && !file)
839
861
  return;
840
862
  e.preventDefault();
@@ -850,6 +872,10 @@ const MrdDocumentList = class {
850
872
  this.dropTargetId = null;
851
873
  };
852
874
  this.onNodeDrop = (e, node, container) => {
875
+ if (this.readOnly) {
876
+ e.preventDefault();
877
+ return;
878
+ }
853
879
  if (this.isFileDrag(e)) {
854
880
  e.preventDefault();
855
881
  e.stopPropagation();
@@ -868,6 +894,11 @@ const MrdDocumentList = class {
868
894
  if (!this.singleContainer)
869
895
  return;
870
896
  const file = this.isFileDrag(e);
897
+ if (this.readOnly) {
898
+ if (file)
899
+ e.preventDefault(); // swallow so the browser doesn't navigate to the dropped file
900
+ return;
901
+ }
871
902
  if (!this.dragDoc && !file)
872
903
  return;
873
904
  e.preventDefault();
@@ -885,6 +916,10 @@ const MrdDocumentList = class {
885
916
  };
886
917
  this.onRootDrop = (e) => {
887
918
  this.rootDropActive = false;
919
+ if (this.readOnly) {
920
+ e.preventDefault();
921
+ return;
922
+ }
888
923
  if (!this.singleContainer)
889
924
  return;
890
925
  const only = this.containers[0];
@@ -1155,10 +1190,10 @@ const MrdDocumentList = class {
1155
1190
  return null;
1156
1191
  }
1157
1192
  get canCreateFolder() {
1158
- return !this.pendingNode && this.resolveTarget() !== null;
1193
+ return !this.readOnly && !this.pendingNode && this.resolveTarget() !== null;
1159
1194
  }
1160
1195
  /** Start creating a folder under the current target. Called by the host toolbar
1161
- * (mrd-document-container). No-op when there is no valid target. */
1196
+ * (mrd-document-container). No-op when there is no valid target, or read-only. */
1162
1197
  async createFolder() {
1163
1198
  this.startCreateFolder();
1164
1199
  }
@@ -1244,7 +1279,7 @@ const MrdDocumentList = class {
1244
1279
  * (mrd-document-container). No-op without a target, or while another upload
1245
1280
  * is still awaiting its setFileReference. */
1246
1281
  async uploadFile(file) {
1247
- if (!file || this.pendingUpload)
1282
+ if (this.readOnly || !file || this.pendingUpload)
1248
1283
  return;
1249
1284
  const resolved = this.resolveTarget();
1250
1285
  if (!resolved)
@@ -1385,7 +1420,7 @@ const MrdDocumentList = class {
1385
1420
  body = renderLoadingRow(ctx, 0);
1386
1421
  }
1387
1422
  else if (this.containers.length === 0) {
1388
- body = index.h("div", { key: '63604a99fc62d9108f61b8efc126c0cd3e8fc5ed', class: "mrd-document-list__empty" }, cellRenderer.t('no_results', this.locale));
1423
+ body = index.h("div", { key: '5d473ec3a894361325a71deb14e7b52d0abc5f06', class: "mrd-document-list__empty" }, cellRenderer.t('no_results', this.locale));
1389
1424
  }
1390
1425
  else if (this.singleContainer) {
1391
1426
  // Dossier context: render the single container's folder tree at the root.
@@ -1403,7 +1438,7 @@ const MrdDocumentList = class {
1403
1438
  else {
1404
1439
  body = this.containers.map(c => renderContainerNode(ctx, c, 0));
1405
1440
  }
1406
- return (index.h(index.Host, { key: '2a092e3a7483deb1fb30753b0c0428aaf75c303a' }, index.h("div", { key: '2b716c912a272b925a95133de5b1ff2abedd9dbc', class: 'mrd-document-list' + (this.rootDropActive ? ' mrd-document-list--drop-active' : ''), onDragOver: this.onRootDragOver, onDragLeave: this.onRootDragLeave, onDrop: this.onRootDrop }, body)));
1441
+ return (index.h(index.Host, { key: '757babf3ccabbb113ab2a24d632b2b9a02d3c08b' }, index.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)));
1407
1442
  }
1408
1443
  get el() { return index.getElement(this); }
1409
1444
  };
@@ -5318,6 +5353,10 @@ const MrdLayoutSection = class {
5318
5353
  /** Top-level archetypes on the object dashboard. When it contains commons.document,
5319
5354
  * the record is rendered as a single-document view instead of generic fields. */
5320
5355
  this.archetypes = [];
5356
+ /** When true, this dashboard is read-only (viewer role): the embedded mrd-table's
5357
+ * create ("+") action and the documents view's New folder / Upload are disabled
5358
+ * with a tooltip explaining why, so it's clear at a glance the user can't mutate data. */
5359
+ this.readOnly = false;
5321
5360
  this.searchQueryMap = {};
5322
5361
  this.searchResultsMap = {};
5323
5362
  this.imagePreviewUrl = null;
@@ -5523,7 +5562,7 @@ const MrdLayoutSection = class {
5523
5562
  * swaps between the folder tree and the flat table. Events are re-emitted to
5524
5563
  * the host with the view key, exactly as the raw list/table events were. */
5525
5564
  renderDocumentContainer(item, key, parentId, archetype, containerHref) {
5526
- return (index.h("mrd-document-container", { item: item, archetype: archetype, parentId: parentId, containerHref: containerHref, viewKey: key, locale: this.locale, onMrdLoadDistinct: (e) => {
5565
+ return (index.h("mrd-document-container", { item: item, archetype: archetype, parentId: parentId, containerHref: containerHref, viewKey: key, locale: this.locale, readOnly: this.readOnly, onMrdLoadDistinct: (e) => {
5527
5566
  e.stopPropagation();
5528
5567
  this.mrdLoadViewDistinct.emit(Object.assign({ name: key }, e.detail));
5529
5568
  }, onMrdLoadPage: (e) => {
@@ -5556,7 +5595,7 @@ const MrdLayoutSection = class {
5556
5595
  } }));
5557
5596
  }
5558
5597
  renderTable(item, key, parentId) {
5559
- return (index.h("mrd-table", { "data-view": key, item: item, parentId: parentId, locale: this.locale, onMrdLoadPage: (e) => this.handleViewLoadPage(e, key), onMrdLoadAggregations: (e) => {
5598
+ return (index.h("mrd-table", { "data-view": key, item: item, parentId: parentId, locale: this.locale, readOnly: this.readOnly, onMrdLoadPage: (e) => this.handleViewLoadPage(e, key), onMrdLoadAggregations: (e) => {
5560
5599
  var _a;
5561
5600
  e.stopPropagation();
5562
5601
  this.mrdLoadViewAggregations.emit(Object.assign({ name: key, dataClass: (_a = item.dataClass) !== null && _a !== void 0 ? _a : key }, e.detail));
@@ -5660,7 +5699,7 @@ const MrdLayoutSection = class {
5660
5699
  const docArchetype = clientLayout.resolveArchetype(this.archetypes, clientLayout.ARCHETYPE_DOCUMENT);
5661
5700
  const invoiceArchetype = !docArchetype ? clientLayout.resolveArchetype(this.archetypes, clientLayout.ARCHETYPE_INVOICE) : undefined;
5662
5701
  const emailArchetype = !docArchetype && !invoiceArchetype ? clientLayout.resolveArchetype(this.archetypes, clientLayout.ARCHETYPE_EMAIL) : undefined;
5663
- return (index.h(index.Host, { key: 'ccae8e82b4dd549ab24239fe685a6830a508061d' }, index.h("div", { key: '4072cc4ecdd2b087c54a5515e653f7f713b2150f', 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()));
5702
+ return (index.h(index.Host, { key: '07b1d61b170a5ae971ba54eaf775e1787dea1642' }, index.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()));
5664
5703
  }
5665
5704
  get el() { return index.getElement(this); }
5666
5705
  static get watchers() { return {
@@ -6184,6 +6223,10 @@ const DATE_TYPES = new Set(['DATE', 'DATETIME', 'TIME']);
6184
6223
  const NO_FILTER_TYPES = new Set(['FILE', 'IMAGE']);
6185
6224
  /** Column types that cannot be sorted or filtered (not stored in PostgreSQL). */
6186
6225
  const NON_INTERACTIVE_TYPES = new Set(['LONGTEXT', 'JSON', 'FILE', 'IMAGE']);
6226
+ /** Wait this long (ms) after the user stops typing in the free-text search box
6227
+ * before firing the search request — mirrors REQUEST_DEBOUNCE_MS's purpose
6228
+ * (skip intermediate keystrokes) but is tuned for typing, not scrolling. */
6229
+ const SEARCH_DEBOUNCE_MS = 400;
6187
6230
  function parseDefaultSort(defaultSort) {
6188
6231
  var _a;
6189
6232
  if (!defaultSort)
@@ -6208,6 +6251,8 @@ function buildTableQueryParams(opts) {
6208
6251
  if (opts.filterClass)
6209
6252
  p.set('type', opts.filterClass);
6210
6253
  applyViewFilters(p, opts.viewFilters);
6254
+ if (opts.query)
6255
+ p.set('q', opts.query);
6211
6256
  for (const f of opts.activeFilters) {
6212
6257
  if (f.operator === 'isEmpty') {
6213
6258
  p.set(f.field, '');
@@ -6357,6 +6402,7 @@ function datetimeFilterToDisplay(existing) {
6357
6402
  * component; these components receive plain values and callbacks.
6358
6403
  */
6359
6404
  const FilterIcon = () => (index.h("span", { class: "mrd-table__filter-icon", "aria-hidden": "true" }, index.h("svg", { viewBox: "0 0 24 24", width: "14", height: "14", fill: "currentColor" }, index.h("path", { d: "M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z" }))));
6405
+ const SearchIcon = ({ class: cls }) => (index.h("svg", { class: cls, viewBox: "0 0 24 24", width: "16", height: "16", "aria-hidden": "true" }, index.h("path", { fill: "currentColor", d: "M15.5 14h-.79l-.28-.27a6.5 6.5 0 1 0-.7.7l.27.28v.79l5 4.99L20.49 19zM9.5 14A4.5 4.5 0 1 1 14 9.5 4.5 4.5 0 0 1 9.5 14" })));
6360
6406
  const TEXTBLOCK_MAX = 200;
6361
6407
  const TableCell = ({ col, row, locale, onDownload, onOpenTextblock, onOpenJson }) => {
6362
6408
  var _a, _b, _c;
@@ -6436,25 +6482,32 @@ const JsonModal = ({ html, locale, onClose }) => {
6436
6482
  return null;
6437
6483
  return (index.h("div", { class: "mrd-table__modal-backdrop", onClick: onClose, role: "dialog", "aria-modal": "true" }, index.h("div", { class: "mrd-table__modal", onClick: (e) => e.stopPropagation() }, index.h("button", { class: "mrd-table__modal-close", onClick: onClose, "aria-label": cellRenderer.t('close', locale) }, "\u2715"), index.h("pre", { class: "mrd-table__modal-json", innerHTML: html }))));
6438
6484
  };
6439
- const Toolbar = ({ locale, filterCount, actions, views, activeViewIdx, viewPopoverOpen, createPickerOpen, createTypes, cb }) => {
6485
+ const Toolbar = ({ locale, filterCount, actions, views, activeViewIdx, viewPopoverOpen, createPickerOpen, createTypes, readOnly, searchEnabled, searchOpen, searchQuery, cb }) => {
6440
6486
  var _a;
6441
6487
  const hasActions = actions.length > 0;
6442
6488
  const hasViewSwitcher = views.length > 1;
6443
- return (index.h("div", { class: "mrd-table__toolbar" }, index.h("div", { class: "mrd-table__toolbar-left" }, filterCount > 0 && (index.h("button", { class: "mrd-table__action mrd-table__action--secondary mrd-table__filter-clear", onClick: cb.onClearAllFilters }, index.h("svg", { class: "mrd-table__action-icon", viewBox: "0 0 24 24", "aria-hidden": "true" }, index.h("path", { fill: "currentColor", d: "M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z" })), index.h("span", { class: "mrd-table__filter-clear-label" }, cellRenderer.t('table_filter_clear_all', locale), " (", filterCount, ")")))), hasViewSwitcher && (index.h("div", { class: "mrd-table__toolbar-center" }, index.h("div", { class: "mrd-table__view-switcher" }, index.h("button", { class: "mrd-table__view-arrow", "aria-label": "Previous view", onClick: () => cb.onViewSwitch((activeViewIdx - 1 + views.length) % views.length) }, "\u25C0"), index.h("div", { class: "mrd-table__view-title-wrap" }, index.h("button", { class: "mrd-table__view-title", onClick: (e) => cb.onToggleViewPopover(e) }, (_a = views[activeViewIdx]) === null || _a === void 0 ? void 0 : _a.label), viewPopoverOpen && (index.h("div", { class: "mrd-table__view-popover", onClick: (e) => e.stopPropagation() }, views.map((v, i) => (index.h("button", { key: String(i), class: `mrd-table__view-popover-item${i === activeViewIdx ? ' mrd-table__view-popover-item--active' : ''}`, onClick: () => {
6489
+ return (index.h("div", { class: "mrd-table__toolbar" }, index.h("div", { class: "mrd-table__toolbar-left" }, searchEnabled && (searchOpen ? (index.h("span", { class: "mrd-table__search-box" }, index.h(SearchIcon, null), index.h("input", { type: "text", class: "mrd-table__search-input", value: searchQuery !== null && searchQuery !== void 0 ? searchQuery : '', placeholder: cellRenderer.t('search_placeholder', locale), ref: cb.onSearchInputRef, onInput: (e) => cb.onSearchInput(e.target.value), onKeyDown: (e) => {
6490
+ if (e.key === 'Escape')
6491
+ cb.onCloseSearch();
6492
+ } }), index.h("button", { type: "button", class: "mrd-table__search-clear", "aria-label": cellRenderer.t('clear', locale), onClick: cb.onCloseSearch }, "\u2715"))) : (index.h("button", { type: "button", class: "mrd-table__action mrd-table__action--secondary mrd-table__search-toggle", onClick: cb.onToggleSearch }, index.h(SearchIcon, { class: "mrd-table__action-icon" }), index.h("span", { class: "mrd-table__action-tooltip" }, cellRenderer.t('table_search', locale))))), filterCount > 0 && (index.h("button", { class: "mrd-table__action mrd-table__action--secondary mrd-table__filter-clear", onClick: cb.onClearAllFilters }, index.h("svg", { class: "mrd-table__action-icon", viewBox: "0 0 24 24", "aria-hidden": "true" }, index.h("path", { fill: "currentColor", d: "M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z" })), index.h("span", { class: "mrd-table__filter-clear-label" }, cellRenderer.t('table_filter_clear_all', locale), " (", filterCount, ")")))), hasViewSwitcher && (index.h("div", { class: "mrd-table__toolbar-center" }, index.h("div", { class: "mrd-table__view-switcher" }, index.h("button", { class: "mrd-table__view-arrow", "aria-label": "Previous view", onClick: () => cb.onViewSwitch((activeViewIdx - 1 + views.length) % views.length) }, "\u25C0"), index.h("div", { class: "mrd-table__view-title-wrap" }, index.h("button", { class: "mrd-table__view-title", onClick: (e) => cb.onToggleViewPopover(e) }, (_a = views[activeViewIdx]) === null || _a === void 0 ? void 0 : _a.label), viewPopoverOpen && (index.h("div", { class: "mrd-table__view-popover", onClick: (e) => e.stopPropagation() }, views.map((v, i) => (index.h("button", { key: String(i), class: `mrd-table__view-popover-item${i === activeViewIdx ? ' mrd-table__view-popover-item--active' : ''}`, onClick: () => {
6444
6493
  if (i !== activeViewIdx)
6445
6494
  cb.onViewSwitch(i);
6446
6495
  cb.onCloseViewPopover();
6447
6496
  } }, v.label)))))), index.h("button", { class: "mrd-table__view-arrow", "aria-label": "Next view", onClick: () => cb.onViewSwitch((activeViewIdx + 1) % views.length) }, "\u25B6")))), hasActions && (index.h("div", { class: "mrd-table__toolbar-right" }, actions.map(a => {
6448
6497
  var _a, _b;
6449
6498
  const pickerTypes = a.action === 'create' ? createTypes : null;
6499
+ // readOnly only ever disables 'create' (see mrd-table.tsx tableActions) — export,
6500
+ // sorting and filtering don't mutate data and stay available.
6501
+ const disabled = a.disabled || (readOnly && a.action === 'create');
6502
+ const tooltip = disabled && readOnly ? cellRenderer.t('readonly_access', locale) : a.label;
6450
6503
  if (pickerTypes === null || pickerTypes === void 0 ? void 0 : pickerTypes.length) {
6451
- return (index.h("div", { class: "mrd-table__create-picker-wrap", key: `action-${a.action}` }, index.h("button", { class: `mrd-table__action mrd-table__action--${(_a = a.variant) !== null && _a !== void 0 ? _a : 'secondary'}`, onClick: cb.onToggleCreatePicker }, a.icon
6504
+ return (index.h("div", { class: "mrd-table__create-picker-wrap", key: `action-${a.action}` }, index.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
6452
6505
  ? index.h("svg", { class: "mrd-table__action-icon", "aria-hidden": "true" }, index.h("use", { href: a.icon }))
6453
- : a.label, index.h("span", { class: "mrd-table__action-tooltip" }, a.label)), createPickerOpen && (index.h("div", { class: "mrd-table__create-picker" }, pickerTypes.map(ct => (index.h("button", { key: ct.type, class: "mrd-table__create-picker-item", onClick: () => cb.onCreate(ct.type) }, ct.label)))))));
6506
+ : a.label, index.h("span", { class: "mrd-table__action-tooltip" }, tooltip)), createPickerOpen && (index.h("div", { class: "mrd-table__create-picker" }, pickerTypes.map(ct => (index.h("button", { key: ct.type, class: "mrd-table__create-picker-item", onClick: () => cb.onCreate(ct.type) }, ct.label)))))));
6454
6507
  }
6455
- return (index.h("button", { key: `action-${a.action}`, class: `mrd-table__action mrd-table__action--${(_b = a.variant) !== null && _b !== void 0 ? _b : 'secondary'}`, disabled: a.disabled, onClick: () => cb.onAction(a.action) }, a.icon
6508
+ return (index.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
6456
6509
  ? index.h("svg", { class: "mrd-table__action-icon", "aria-hidden": "true" }, index.h("use", { href: a.icon }))
6457
- : a.label, index.h("span", { class: "mrd-table__action-tooltip" }, a.label)));
6510
+ : a.label, index.h("span", { class: "mrd-table__action-tooltip" }, tooltip)));
6458
6511
  })))));
6459
6512
  };
6460
6513
 
@@ -6610,7 +6663,7 @@ function getStalePages(requestedAt, now, timeoutMs) {
6610
6663
  return stale;
6611
6664
  }
6612
6665
 
6613
- const mrdTableCss = () => `.sc-mrd-table-h{display:block;width:100%}.mrd-table.sc-mrd-table{overflow-x:auto}.mrd-table__scroll.sc-mrd-table{overflow-y:auto;overflow-x:auto;border:1px solid var(--mrd-border-color);border-radius:var(--mrd-border-radius);overflow-anchor:none}.mrd-table__table.sc-mrd-table{width:auto;min-width:100%;border-collapse:collapse;font-size:var(--mrd-font-size-sm);color:var(--mrd-color-neutral-900)}.mrd-table__scroll.sc-mrd-table .mrd-table__table.sc-mrd-table{min-width:max-content}.mrd-table__header.sc-mrd-table{position:sticky;top:0;z-index:1;background:var(--mrd-color-white);text-align:left;padding:var(--mrd-space-2) var(--mrd-space-4);border-bottom:2px solid var(--mrd-border-color);color:var(--mrd-color-neutral-600);font-weight:var(--mrd-font-weight-medium);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-size:var(--mrd-font-size-xs);text-transform:uppercase;letter-spacing:0.04em}.mrd-table__header--sortable.sc-mrd-table{cursor:pointer;user-select:none}.mrd-table__header--sortable.sc-mrd-table:hover{background:var(--mrd-color-neutral-50);color:var(--mrd-color-neutral-800)}.mrd-table__header--sorted-asc.sc-mrd-table,.mrd-table__header--sorted-desc.sc-mrd-table{color:var(--mrd-color-primary);border-bottom-color:var(--mrd-color-primary)}.mrd-table__header--filtered.sc-mrd-table{color:var(--mrd-color-primary);border-bottom-color:var(--mrd-color-primary)}.mrd-table__header-label.sc-mrd-table{margin-right:var(--mrd-space-1)}.mrd-table__col-resizer.sc-mrd-table{position:absolute;top:0;right:0;width:9px;height:100%;cursor:col-resize;touch-action:none;user-select:none}.mrd-table__col-resizer.sc-mrd-table::after{content:"";position:absolute;top:25%;right:4px;width:1px;height:50%;background:var(--mrd-border-color)}.mrd-table__col-resizer.sc-mrd-table:hover::after{width:2px;right:3px;background:var(--mrd-color-primary)}.mrd-table__col-resizer--active.sc-mrd-table::after{width:2px;right:3px;height:100%;top:0;background:var(--mrd-color-primary)}.mrd-table--resizing.sc-mrd-table{cursor:col-resize;user-select:none}.mrd-table__sort-icon.sc-mrd-table{font-size:0.85rem;opacity:0.4;vertical-align:middle}.mrd-table__header--sorted-asc.sc-mrd-table .mrd-table__sort-icon.sc-mrd-table,.mrd-table__header--sorted-desc.sc-mrd-table .mrd-table__sort-icon.sc-mrd-table{opacity:1;color:var(--mrd-color-primary)}.mrd-table__filter-icon.sc-mrd-table{display:inline-flex;align-items:center;vertical-align:middle;margin-left:var(--mrd-space-1);color:var(--mrd-color-primary)}.mrd-table__row.sc-mrd-table{border-bottom:1px solid var(--mrd-border-color)}.mrd-table__row.sc-mrd-table:hover{background:var(--mrd-color-neutral-200) !important}.mrd-table__row--clickable.sc-mrd-table{cursor:pointer}.mrd-table__row--loading.sc-mrd-table{background:transparent}.mrd-table__row--failed.sc-mrd-table{background:transparent}.mrd-table__spacer.sc-mrd-table{border:none}.mrd-table__spacer.sc-mrd-table td.sc-mrd-table{padding:0;border:none}.mrd-table__cell.sc-mrd-table{padding:var(--mrd-space-2) var(--mrd-space-4);vertical-align:top;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.mrd-table__cell--numeric.sc-mrd-table{text-align:right;font-variant-numeric:tabular-nums}.mrd-table__cell--placeholder.sc-mrd-table{padding:var(--mrd-space-2) var(--mrd-space-4);border-bottom:1px solid var(--mrd-border-color)}.mrd-table__cell--failed.sc-mrd-table{padding:var(--mrd-space-2) var(--mrd-space-4);border-bottom:1px solid var(--mrd-border-color)}.mrd-table__placeholder-bar.sc-mrd-table{display:block;height:0.75rem;width:55%;border-radius:var(--mrd-border-radius-sm);background:linear-gradient(90deg, var(--mrd-color-neutral-200) 25%, var(--mrd-color-neutral-100) 50%, var(--mrd-color-neutral-200) 75%);background-size:200% 100%;animation:mrd-shimmer 1.4s ease infinite}.mrd-table__retry-button.sc-mrd-table{border:none;background:none;padding:0;color:var(--mrd-color-danger);font-size:inherit;cursor:pointer;text-decoration:underline}.mrd-table__toolbar.sc-mrd-table{display:flex;align-items:center;justify-content:space-between;padding-bottom:var(--mrd-space-2)}.mrd-table__toolbar-left.sc-mrd-table,.mrd-table__toolbar-right.sc-mrd-table{display:flex;gap:var(--mrd-space-2);align-items:center}.mrd-table__toolbar-center.sc-mrd-table{flex:1;display:flex;justify-content:center;align-items:center;gap:var(--mrd-space-1)}.mrd-table__view-switcher.sc-mrd-table{display:flex;align-items:center;gap:var(--mrd-space-1)}.mrd-table__view-arrow.sc-mrd-table{background:none;border:none;cursor:pointer;padding:0 var(--mrd-space-1);color:var(--mrd-color-neutral-500);font-size:var(--mrd-font-size-xs);line-height:1}.mrd-table__view-arrow.sc-mrd-table:hover{color:var(--mrd-color-neutral-800)}.mrd-table__view-title-wrap.sc-mrd-table{position:relative}.mrd-table__view-title.sc-mrd-table{font-size:var(--mrd-font-size-sm);font-weight:600;color:var(--mrd-color-neutral-800);font-family:var(--mrd-font-family);background:none;border:none;outline:none;cursor:pointer;padding:0 var(--mrd-space-1);max-width:220px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.mrd-table__view-title.sc-mrd-table:hover{color:var(--mrd-color-primary)}.mrd-table__view-popover.sc-mrd-table{position:absolute;top:calc(100% + var(--mrd-space-1));left:50%;transform:translateX(-50%);background:var(--mrd-color-white);border:1px solid var(--mrd-color-neutral-200);border-radius:var(--mrd-radius-md);box-shadow:var(--mrd-shadow-md);z-index:100;min-width:160px;padding:var(--mrd-space-1) 0}.mrd-table__view-popover-item.sc-mrd-table{display:block;width:100%;text-align:left;background:none;border:none;cursor:pointer;padding:var(--mrd-space-2) var(--mrd-space-3);font-size:var(--mrd-font-size-sm);font-family:var(--mrd-font-family);color:var(--mrd-color-neutral-700);white-space:nowrap}.mrd-table__view-popover-item.sc-mrd-table:hover{background:var(--mrd-color-neutral-50)}.mrd-table__view-popover-item--active.sc-mrd-table{font-weight:600;color:var(--mrd-color-primary)}.mrd-table__action.sc-mrd-table{position:relative;display:inline-flex;align-items:center;justify-content:center;width:2rem;height:2rem;padding:0;background:transparent;border:1px solid transparent;border-radius:var(--mrd-border-radius);cursor:pointer;color:var(--mrd-color-neutral-400);transition:background-color 0.15s, border-color 0.15s, color 0.15s}.mrd-table__action.sc-mrd-table:hover{background-color:var(--mrd-color-neutral-100);border-color:var(--mrd-color-neutral-300);color:var(--mrd-color-neutral-700)}.mrd-table__action.sc-mrd-table:hover .mrd-table__action-tooltip.sc-mrd-table{display:block}.mrd-table__action.sc-mrd-table:disabled{opacity:0.4;cursor:not-allowed}.mrd-table__action--primary.sc-mrd-table{color:var(--mrd-color-neutral-500)}.mrd-table__action--primary.sc-mrd-table:hover{background:var(--mrd-color-primary);border-color:var(--mrd-color-primary);color:var(--mrd-color-white)}.mrd-table__action--danger.sc-mrd-table{color:var(--mrd-color-error)}.mrd-table__action--danger.sc-mrd-table:hover{background-color:var(--mrd-color-error-light, #fef2f2);border-color:var(--mrd-color-error)}.mrd-table__action-icon.sc-mrd-table{width:1.25rem;height:1.25rem;pointer-events:none;fill:currentColor}.mrd-table__action-tooltip.sc-mrd-table{display:none;position:absolute;bottom:calc(100% + 6px);right:0;padding:var(--mrd-space-1) var(--mrd-space-2);font-size:var(--mrd-font-size-xs);white-space:nowrap;background:var(--mrd-color-tooltip, #fffce1);color:var(--mrd-color-neutral-900);border:1px solid var(--mrd-border-color);border-radius:var(--mrd-border-radius-sm, var(--mrd-border-radius));pointer-events:none;z-index:10}.mrd-table__create-picker-wrap.sc-mrd-table{position:relative}.mrd-table__create-picker.sc-mrd-table{position:absolute;right:0;top:calc(100% + 4px);background:var(--mrd-color-white, #fff);border:1px solid var(--mrd-border-color);border-radius:var(--mrd-border-radius);box-shadow:var(--mrd-shadow-md, 0 4px 12px rgba(0, 0, 0, 0.12));z-index:10;min-width:10rem;padding:var(--mrd-space-1) 0}.mrd-table__create-picker-item.sc-mrd-table{display:block;width:100%;text-align:left;padding:var(--mrd-space-2) var(--mrd-space-3);background:none;border:none;cursor:pointer;font-size:var(--mrd-font-size-sm);font-family:var(--mrd-font-family);color:var(--mrd-color-neutral-800)}.mrd-table__create-picker-item.sc-mrd-table:hover{background:var(--mrd-color-neutral-50);color:var(--mrd-color-neutral-900)}.mrd-table__filter-clear.sc-mrd-table{width:auto;gap:var(--mrd-space-1);padding:0 var(--mrd-space-2);color:var(--mrd-color-primary);border-color:var(--mrd-color-primary)}.mrd-table__filter-clear.sc-mrd-table:hover{color:var(--mrd-color-primary);border-color:var(--mrd-color-primary)}.mrd-table__filter-clear-label.sc-mrd-table{font-size:var(--mrd-font-size-sm);white-space:nowrap}.mrd-table__filter-popup.sc-mrd-table{position:fixed;width:280px;background:var(--mrd-color-white);border:1px solid var(--mrd-border-color);border-radius:var(--mrd-border-radius);box-shadow:var(--mrd-shadow-md, 0 4px 12px rgba(0, 0, 0, 0.12));z-index:var(--mrd-z-dropdown, 200);font-size:var(--mrd-font-size-sm)}.mrd-table__filter-popup-header.sc-mrd-table{display:flex;align-items:center;justify-content:space-between;padding:var(--mrd-space-2) var(--mrd-space-3);border-bottom:1px solid var(--mrd-border-color)}.mrd-table__filter-popup-title.sc-mrd-table{font-weight:var(--mrd-font-weight-medium);color:var(--mrd-color-neutral-800);font-size:var(--mrd-font-size-sm)}.mrd-table__filter-close.sc-mrd-table{background:transparent;border:none;cursor:pointer;color:var(--mrd-color-neutral-500);font-size:0.9rem;padding:2px 4px;border-radius:3px;line-height:1}.mrd-table__filter-close.sc-mrd-table:hover{background:var(--mrd-color-neutral-100);color:var(--mrd-color-neutral-800)}.mrd-table__filter-section.sc-mrd-table{padding:var(--mrd-space-2) var(--mrd-space-3)}.mrd-table__filter-section-label.sc-mrd-table{font-size:var(--mrd-font-size-xs);font-weight:var(--mrd-font-weight-medium);text-transform:uppercase;letter-spacing:0.04em;color:var(--mrd-color-neutral-500);margin-bottom:var(--mrd-space-2)}.mrd-table__filter-sort-buttons.sc-mrd-table{display:flex;gap:var(--mrd-space-2)}.mrd-table__filter-sort-btn.sc-mrd-table{flex:1;padding:var(--mrd-space-1) var(--mrd-space-2);background:transparent;border:1px solid var(--mrd-border-color);border-radius:var(--mrd-border-radius);cursor:pointer;font-size:var(--mrd-font-size-xs);color:var(--mrd-color-neutral-700)}.mrd-table__filter-sort-btn.sc-mrd-table:hover{background:var(--mrd-color-neutral-100)}.mrd-table__filter-sort-btn--active.sc-mrd-table{background:var(--mrd-color-primary);border-color:var(--mrd-color-primary);color:var(--mrd-color-white)}.mrd-table__filter-divider.sc-mrd-table{height:1px;background:var(--mrd-border-color);margin:0}.mrd-table__filter-editor.sc-mrd-table{display:flex;flex-direction:column;gap:var(--mrd-space-2)}.mrd-table__filter-select.sc-mrd-table,.mrd-table__filter-input.sc-mrd-table{width:100%;padding:var(--mrd-space-1) var(--mrd-space-2);border:1px solid var(--mrd-border-color);border-radius:var(--mrd-border-radius);font-size:var(--mrd-font-size-sm);color:var(--mrd-color-neutral-900);background:var(--mrd-color-white);box-sizing:border-box}.mrd-table__filter-select.sc-mrd-table:focus,.mrd-table__filter-input.sc-mrd-table:focus{outline:none;border-color:var(--mrd-color-primary);box-shadow:0 0 0 2px rgba(0, 0, 0, 0.06)}.mrd-table__filter-range.sc-mrd-table{display:flex;align-items:center;gap:var(--mrd-space-1)}.mrd-table__filter-range.sc-mrd-table .mrd-table__filter-input.sc-mrd-table{flex:1;min-width:0}.mrd-table__filter-range--stacked.sc-mrd-table{flex-direction:column;align-items:stretch;gap:var(--mrd-space-2)}.mrd-table__filter-range-sep.sc-mrd-table{color:var(--mrd-color-neutral-400);flex-shrink:0}.mrd-table__filter-range-label.sc-mrd-table{font-size:var(--mrd-font-size-xs);color:var(--mrd-color-neutral-500);margin-bottom:2px}.mrd-table__filter-radio-group.sc-mrd-table{display:flex;flex-direction:column;gap:var(--mrd-space-1)}.mrd-table__filter-radio-group--inline.sc-mrd-table{flex-direction:row;gap:var(--mrd-space-3)}.mrd-table__filter-radio-label.sc-mrd-table{display:flex;align-items:center;gap:var(--mrd-space-1);cursor:pointer;font-size:var(--mrd-font-size-sm);color:var(--mrd-color-neutral-800)}.mrd-table__filter-list.sc-mrd-table{display:flex;flex-direction:column;gap:var(--mrd-space-1);max-height:180px;overflow-y:auto}.mrd-table__filter-list-controls.sc-mrd-table{display:flex;gap:var(--mrd-space-2);margin-bottom:var(--mrd-space-1)}.mrd-table__filter-list-btn.sc-mrd-table{font-size:var(--mrd-font-size-xs);color:var(--mrd-color-primary);background:transparent;border:none;cursor:pointer;padding:0;text-decoration:underline}.mrd-table__filter-checkbox-label.sc-mrd-table{display:flex;align-items:center;gap:var(--mrd-space-1);cursor:pointer;font-size:var(--mrd-font-size-sm);color:var(--mrd-color-neutral-800)}.mrd-table__filter-no-support.sc-mrd-table{font-size:var(--mrd-font-size-sm);color:var(--mrd-color-neutral-500);margin:0;font-style:italic}.mrd-table__filter-popup-footer.sc-mrd-table{display:flex;justify-content:flex-end;gap:var(--mrd-space-2);padding:var(--mrd-space-2) var(--mrd-space-3);border-top:1px solid var(--mrd-border-color)}.mrd-table__filter-btn.sc-mrd-table{padding:var(--mrd-space-1) var(--mrd-space-3);border-radius:var(--mrd-border-radius);border:1px solid var(--mrd-border-color);font-size:var(--mrd-font-size-sm);cursor:pointer}.mrd-table__filter-btn--clear.sc-mrd-table{background:transparent;color:var(--mrd-color-neutral-600)}.mrd-table__filter-btn--clear.sc-mrd-table:hover{background:var(--mrd-color-neutral-100)}.mrd-table__filter-btn--apply.sc-mrd-table{background:var(--mrd-color-primary);border-color:var(--mrd-color-primary);color:var(--mrd-color-white)}.mrd-table__filter-btn--apply.sc-mrd-table:hover{background:var(--mrd-color-primary-dark, var(--mrd-color-primary));border-color:var(--mrd-color-primary-dark, var(--mrd-color-primary))}.mrd-table__totals-row.sc-mrd-table{border-top:2px solid var(--mrd-border-color)}.mrd-table__totals-cell.sc-mrd-table{position:sticky;bottom:0;z-index:2;padding:var(--mrd-space-2) var(--mrd-space-4);background:var(--mrd-color-white);font-weight:var(--mrd-font-weight-medium);font-variant-numeric:tabular-nums;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;border-top:2px solid var(--mrd-border-color)}.mrd-table__totals-cell--numeric.sc-mrd-table{text-align:right}.mrd-table__footer.sc-mrd-table{padding:var(--mrd-space-1) var(--mrd-space-2);font-size:var(--mrd-font-size-xs);color:var(--mrd-color-neutral-500);text-align:right}.mrd-table__empty.sc-mrd-table{padding:var(--mrd-space-4) var(--mrd-space-3);color:var(--mrd-color-neutral-500);font-size:var(--mrd-font-size-sm);text-align:center;margin:0}.mrd-table__file-btn.sc-mrd-table{display:inline-flex;align-items:center;gap:var(--mrd-space-1);background:none;border:none;padding:0;cursor:pointer;color:var(--mrd-color-primary);font-size:var(--mrd-font-size-sm);font-family:inherit;max-width:100%;overflow:hidden}.mrd-table__file-btn.sc-mrd-table:hover{text-decoration:underline;color:var(--mrd-color-primary-dark)}.mrd-table__file-icon.sc-mrd-table{flex-shrink:0;width:1rem;height:1rem}.mrd-table__textblock-btn.sc-mrd-table{display:inline;background:none;border:none;padding:0 0 0 var(--mrd-space-1);cursor:pointer;color:var(--mrd-color-primary);font-size:var(--mrd-font-size-sm);font-family:inherit;line-height:inherit;vertical-align:middle}.mrd-table__textblock-btn.sc-mrd-table:hover{color:var(--mrd-color-primary-dark)}.mrd-table__modal-backdrop.sc-mrd-table{position:fixed;inset:0;background:rgba(0, 0, 0, 0.4);z-index:var(--mrd-z-modal, 300);display:flex;align-items:center;justify-content:center}.mrd-table__modal.sc-mrd-table{background:#fff;border-radius:var(--mrd-radius-md, 0.5rem);padding:var(--mrd-space-6);max-width:min(600px, 90vw);max-height:70vh;overflow-y:auto;position:relative;box-shadow:var(--mrd-shadow-lg)}.mrd-table__modal-close.sc-mrd-table{position:absolute;top:var(--mrd-space-3);right:var(--mrd-space-3);background:none;border:none;cursor:pointer;font-size:1.25rem;line-height:1;color:var(--mrd-color-text-muted, #6b7280);padding:0}.mrd-table__modal-close.sc-mrd-table:hover{color:var(--mrd-color-text, #111827)}.mrd-table__modal-text.sc-mrd-table{margin:0;padding-right:var(--mrd-space-6);white-space:pre-wrap;word-break:break-word;font-size:var(--mrd-font-size-sm);line-height:1.6}.mrd-table__json-preview.sc-mrd-table{font-family:var(--mrd-font-family-mono);font-size:var(--mrd-font-size-xs);overflow:hidden;white-space:nowrap;text-overflow:ellipsis;display:inline-block;max-width:calc(100% - 1.5rem);vertical-align:middle}.mrd-table__modal-json.sc-mrd-table{margin:0;padding-right:var(--mrd-space-6);font-family:var(--mrd-font-family-mono);font-size:var(--mrd-font-size-xs);white-space:pre-wrap;word-break:break-word;line-height:1.6;background:none;border:none}@keyframes mrd-shimmer{0%{background-position:200% 0}100%{background-position:-200% 0}}`;
6666
+ const mrdTableCss = () => `.sc-mrd-table-h{display:block;width:100%}.mrd-table.sc-mrd-table{overflow-x:auto}.mrd-table__scroll.sc-mrd-table{overflow-y:auto;overflow-x:auto;border:1px solid var(--mrd-border-color);border-radius:var(--mrd-border-radius);overflow-anchor:none}.mrd-table__table.sc-mrd-table{width:auto;min-width:100%;border-collapse:collapse;font-size:var(--mrd-font-size-sm);color:var(--mrd-color-neutral-900)}.mrd-table__scroll.sc-mrd-table .mrd-table__table.sc-mrd-table{min-width:max-content}.mrd-table__header.sc-mrd-table{position:sticky;top:0;z-index:1;background:var(--mrd-color-white);text-align:left;padding:var(--mrd-space-2) var(--mrd-space-4);border-bottom:2px solid var(--mrd-border-color);color:var(--mrd-color-neutral-600);font-weight:var(--mrd-font-weight-medium);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-size:var(--mrd-font-size-xs);text-transform:uppercase;letter-spacing:0.04em}.mrd-table__header--sortable.sc-mrd-table{cursor:pointer;user-select:none}.mrd-table__header--sortable.sc-mrd-table:hover{background:var(--mrd-color-neutral-50);color:var(--mrd-color-neutral-800)}.mrd-table__header--sorted-asc.sc-mrd-table,.mrd-table__header--sorted-desc.sc-mrd-table{color:var(--mrd-color-primary);border-bottom-color:var(--mrd-color-primary)}.mrd-table__header--filtered.sc-mrd-table{color:var(--mrd-color-primary);border-bottom-color:var(--mrd-color-primary)}.mrd-table__header-label.sc-mrd-table{margin-right:var(--mrd-space-1)}.mrd-table__col-resizer.sc-mrd-table{position:absolute;top:0;right:0;width:9px;height:100%;cursor:col-resize;touch-action:none;user-select:none}.mrd-table__col-resizer.sc-mrd-table::after{content:"";position:absolute;top:25%;right:4px;width:1px;height:50%;background:var(--mrd-border-color)}.mrd-table__col-resizer.sc-mrd-table:hover::after{width:2px;right:3px;background:var(--mrd-color-primary)}.mrd-table__col-resizer--active.sc-mrd-table::after{width:2px;right:3px;height:100%;top:0;background:var(--mrd-color-primary)}.mrd-table--resizing.sc-mrd-table{cursor:col-resize;user-select:none}.mrd-table__sort-icon.sc-mrd-table{font-size:0.85rem;opacity:0.4;vertical-align:middle}.mrd-table__header--sorted-asc.sc-mrd-table .mrd-table__sort-icon.sc-mrd-table,.mrd-table__header--sorted-desc.sc-mrd-table .mrd-table__sort-icon.sc-mrd-table{opacity:1;color:var(--mrd-color-primary)}.mrd-table__filter-icon.sc-mrd-table{display:inline-flex;align-items:center;vertical-align:middle;margin-left:var(--mrd-space-1);color:var(--mrd-color-primary)}.mrd-table__row.sc-mrd-table{border-bottom:1px solid var(--mrd-border-color)}.mrd-table__row.sc-mrd-table:hover{background:var(--mrd-color-neutral-200) !important}.mrd-table__row--clickable.sc-mrd-table{cursor:pointer}.mrd-table__row--loading.sc-mrd-table{background:transparent}.mrd-table__row--failed.sc-mrd-table{background:transparent}.mrd-table__spacer.sc-mrd-table{border:none}.mrd-table__spacer.sc-mrd-table td.sc-mrd-table{padding:0;border:none}.mrd-table__cell.sc-mrd-table{padding:var(--mrd-space-2) var(--mrd-space-4);vertical-align:top;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.mrd-table__cell--numeric.sc-mrd-table{text-align:right;font-variant-numeric:tabular-nums}.mrd-table__cell--placeholder.sc-mrd-table{padding:var(--mrd-space-2) var(--mrd-space-4);border-bottom:1px solid var(--mrd-border-color)}.mrd-table__cell--failed.sc-mrd-table{padding:var(--mrd-space-2) var(--mrd-space-4);border-bottom:1px solid var(--mrd-border-color)}.mrd-table__placeholder-bar.sc-mrd-table{display:block;height:0.75rem;width:55%;border-radius:var(--mrd-border-radius-sm);background:linear-gradient(90deg, var(--mrd-color-neutral-200) 25%, var(--mrd-color-neutral-100) 50%, var(--mrd-color-neutral-200) 75%);background-size:200% 100%;animation:mrd-shimmer 1.4s ease infinite}.mrd-table__retry-button.sc-mrd-table{border:none;background:none;padding:0;color:var(--mrd-color-danger);font-size:inherit;cursor:pointer;text-decoration:underline}.mrd-table__toolbar.sc-mrd-table{display:flex;align-items:center;justify-content:space-between;padding-bottom:var(--mrd-space-2)}.mrd-table__toolbar-left.sc-mrd-table,.mrd-table__toolbar-right.sc-mrd-table{display:flex;gap:var(--mrd-space-2);align-items:center}.mrd-table__toolbar-center.sc-mrd-table{flex:1;display:flex;justify-content:center;align-items:center;gap:var(--mrd-space-1)}.mrd-table__view-switcher.sc-mrd-table{display:flex;align-items:center;gap:var(--mrd-space-1)}.mrd-table__view-arrow.sc-mrd-table{background:none;border:none;cursor:pointer;padding:0 var(--mrd-space-1);color:var(--mrd-color-neutral-500);font-size:var(--mrd-font-size-xs);line-height:1}.mrd-table__view-arrow.sc-mrd-table:hover{color:var(--mrd-color-neutral-800)}.mrd-table__view-title-wrap.sc-mrd-table{position:relative}.mrd-table__view-title.sc-mrd-table{font-size:var(--mrd-font-size-sm);font-weight:600;color:var(--mrd-color-neutral-800);font-family:var(--mrd-font-family);background:none;border:none;outline:none;cursor:pointer;padding:0 var(--mrd-space-1);max-width:220px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.mrd-table__view-title.sc-mrd-table:hover{color:var(--mrd-color-primary)}.mrd-table__view-popover.sc-mrd-table{position:absolute;top:calc(100% + var(--mrd-space-1));left:50%;transform:translateX(-50%);background:var(--mrd-color-white);border:1px solid var(--mrd-color-neutral-200);border-radius:var(--mrd-radius-md);box-shadow:var(--mrd-shadow-md);z-index:100;min-width:160px;padding:var(--mrd-space-1) 0}.mrd-table__view-popover-item.sc-mrd-table{display:block;width:100%;text-align:left;background:none;border:none;cursor:pointer;padding:var(--mrd-space-2) var(--mrd-space-3);font-size:var(--mrd-font-size-sm);font-family:var(--mrd-font-family);color:var(--mrd-color-neutral-700);white-space:nowrap}.mrd-table__view-popover-item.sc-mrd-table:hover{background:var(--mrd-color-neutral-50)}.mrd-table__view-popover-item--active.sc-mrd-table{font-weight:600;color:var(--mrd-color-primary)}.mrd-table__action.sc-mrd-table{position:relative;display:inline-flex;align-items:center;justify-content:center;width:2rem;height:2rem;padding:0;background:transparent;border:1px solid transparent;border-radius:var(--mrd-border-radius);cursor:pointer;color:var(--mrd-color-neutral-400);transition:background-color 0.15s, border-color 0.15s, color 0.15s}.mrd-table__action.sc-mrd-table:hover{background-color:var(--mrd-color-neutral-100);border-color:var(--mrd-color-neutral-300);color:var(--mrd-color-neutral-700)}.mrd-table__action.sc-mrd-table:hover .mrd-table__action-tooltip.sc-mrd-table{display:block}.mrd-table__action.sc-mrd-table:disabled{opacity:0.4;cursor:not-allowed}.mrd-table__action--primary.sc-mrd-table{color:var(--mrd-color-neutral-500)}.mrd-table__action--primary.sc-mrd-table:hover{background:var(--mrd-color-primary);border-color:var(--mrd-color-primary);color:var(--mrd-color-white)}.mrd-table__action--danger.sc-mrd-table{color:var(--mrd-color-error)}.mrd-table__action--danger.sc-mrd-table:hover{background-color:var(--mrd-color-error-light, #fef2f2);border-color:var(--mrd-color-error)}.mrd-table__action-icon.sc-mrd-table{width:1.25rem;height:1.25rem;pointer-events:none;fill:currentColor}.mrd-table__action-tooltip.sc-mrd-table{display:none;position:absolute;bottom:calc(100% + 6px);right:0;padding:var(--mrd-space-1) var(--mrd-space-2);font-size:var(--mrd-font-size-xs);white-space:nowrap;background:var(--mrd-color-tooltip, #fffce1);color:var(--mrd-color-neutral-900);border:1px solid var(--mrd-border-color);border-radius:var(--mrd-border-radius-sm, var(--mrd-border-radius));pointer-events:none;z-index:10}.mrd-table__search-box.sc-mrd-table{display:inline-flex;align-items:center;gap:var(--mrd-space-1);height:2rem;padding:0 var(--mrd-space-2);border:1px solid var(--mrd-border-color-focus, var(--mrd-color-primary));border-radius:var(--mrd-border-radius);background:var(--mrd-color-white, #fff);box-shadow:var(--mrd-shadow-focus, none);color:var(--mrd-color-neutral-400)}.mrd-table__search-input.sc-mrd-table{border:none;outline:none;background:transparent;font-size:var(--mrd-font-size-sm);font-family:var(--mrd-font-family);color:var(--mrd-color-neutral-800);width:12rem}.mrd-table__search-clear.sc-mrd-table{display:inline-flex;align-items:center;justify-content:center;background:none;border:none;padding:0;cursor:pointer;color:var(--mrd-color-neutral-400);font-size:var(--mrd-font-size-xs);line-height:1}.mrd-table__search-clear.sc-mrd-table:hover{color:var(--mrd-color-neutral-700)}.mrd-table__create-picker-wrap.sc-mrd-table{position:relative}.mrd-table__create-picker.sc-mrd-table{position:absolute;right:0;top:calc(100% + 4px);background:var(--mrd-color-white, #fff);border:1px solid var(--mrd-border-color);border-radius:var(--mrd-border-radius);box-shadow:var(--mrd-shadow-md, 0 4px 12px rgba(0, 0, 0, 0.12));z-index:10;min-width:10rem;padding:var(--mrd-space-1) 0}.mrd-table__create-picker-item.sc-mrd-table{display:block;width:100%;text-align:left;padding:var(--mrd-space-2) var(--mrd-space-3);background:none;border:none;cursor:pointer;font-size:var(--mrd-font-size-sm);font-family:var(--mrd-font-family);color:var(--mrd-color-neutral-800)}.mrd-table__create-picker-item.sc-mrd-table:hover{background:var(--mrd-color-neutral-50);color:var(--mrd-color-neutral-900)}.mrd-table__filter-clear.sc-mrd-table{width:auto;gap:var(--mrd-space-1);padding:0 var(--mrd-space-2);color:var(--mrd-color-primary);border-color:var(--mrd-color-primary)}.mrd-table__filter-clear.sc-mrd-table:hover{color:var(--mrd-color-primary);border-color:var(--mrd-color-primary)}.mrd-table__filter-clear-label.sc-mrd-table{font-size:var(--mrd-font-size-sm);white-space:nowrap}.mrd-table__filter-popup.sc-mrd-table{position:fixed;width:280px;background:var(--mrd-color-white);border:1px solid var(--mrd-border-color);border-radius:var(--mrd-border-radius);box-shadow:var(--mrd-shadow-md, 0 4px 12px rgba(0, 0, 0, 0.12));z-index:var(--mrd-z-dropdown, 200);font-size:var(--mrd-font-size-sm)}.mrd-table__filter-popup-header.sc-mrd-table{display:flex;align-items:center;justify-content:space-between;padding:var(--mrd-space-2) var(--mrd-space-3);border-bottom:1px solid var(--mrd-border-color)}.mrd-table__filter-popup-title.sc-mrd-table{font-weight:var(--mrd-font-weight-medium);color:var(--mrd-color-neutral-800);font-size:var(--mrd-font-size-sm)}.mrd-table__filter-close.sc-mrd-table{background:transparent;border:none;cursor:pointer;color:var(--mrd-color-neutral-500);font-size:0.9rem;padding:2px 4px;border-radius:3px;line-height:1}.mrd-table__filter-close.sc-mrd-table:hover{background:var(--mrd-color-neutral-100);color:var(--mrd-color-neutral-800)}.mrd-table__filter-section.sc-mrd-table{padding:var(--mrd-space-2) var(--mrd-space-3)}.mrd-table__filter-section-label.sc-mrd-table{font-size:var(--mrd-font-size-xs);font-weight:var(--mrd-font-weight-medium);text-transform:uppercase;letter-spacing:0.04em;color:var(--mrd-color-neutral-500);margin-bottom:var(--mrd-space-2)}.mrd-table__filter-sort-buttons.sc-mrd-table{display:flex;gap:var(--mrd-space-2)}.mrd-table__filter-sort-btn.sc-mrd-table{flex:1;padding:var(--mrd-space-1) var(--mrd-space-2);background:transparent;border:1px solid var(--mrd-border-color);border-radius:var(--mrd-border-radius);cursor:pointer;font-size:var(--mrd-font-size-xs);color:var(--mrd-color-neutral-700)}.mrd-table__filter-sort-btn.sc-mrd-table:hover{background:var(--mrd-color-neutral-100)}.mrd-table__filter-sort-btn--active.sc-mrd-table{background:var(--mrd-color-primary);border-color:var(--mrd-color-primary);color:var(--mrd-color-white)}.mrd-table__filter-divider.sc-mrd-table{height:1px;background:var(--mrd-border-color);margin:0}.mrd-table__filter-editor.sc-mrd-table{display:flex;flex-direction:column;gap:var(--mrd-space-2)}.mrd-table__filter-select.sc-mrd-table,.mrd-table__filter-input.sc-mrd-table{width:100%;padding:var(--mrd-space-1) var(--mrd-space-2);border:1px solid var(--mrd-border-color);border-radius:var(--mrd-border-radius);font-size:var(--mrd-font-size-sm);color:var(--mrd-color-neutral-900);background:var(--mrd-color-white);box-sizing:border-box}.mrd-table__filter-select.sc-mrd-table:focus,.mrd-table__filter-input.sc-mrd-table:focus{outline:none;border-color:var(--mrd-color-primary);box-shadow:0 0 0 2px rgba(0, 0, 0, 0.06)}.mrd-table__filter-range.sc-mrd-table{display:flex;align-items:center;gap:var(--mrd-space-1)}.mrd-table__filter-range.sc-mrd-table .mrd-table__filter-input.sc-mrd-table{flex:1;min-width:0}.mrd-table__filter-range--stacked.sc-mrd-table{flex-direction:column;align-items:stretch;gap:var(--mrd-space-2)}.mrd-table__filter-range-sep.sc-mrd-table{color:var(--mrd-color-neutral-400);flex-shrink:0}.mrd-table__filter-range-label.sc-mrd-table{font-size:var(--mrd-font-size-xs);color:var(--mrd-color-neutral-500);margin-bottom:2px}.mrd-table__filter-radio-group.sc-mrd-table{display:flex;flex-direction:column;gap:var(--mrd-space-1)}.mrd-table__filter-radio-group--inline.sc-mrd-table{flex-direction:row;gap:var(--mrd-space-3)}.mrd-table__filter-radio-label.sc-mrd-table{display:flex;align-items:center;gap:var(--mrd-space-1);cursor:pointer;font-size:var(--mrd-font-size-sm);color:var(--mrd-color-neutral-800)}.mrd-table__filter-list.sc-mrd-table{display:flex;flex-direction:column;gap:var(--mrd-space-1);max-height:180px;overflow-y:auto}.mrd-table__filter-list-controls.sc-mrd-table{display:flex;gap:var(--mrd-space-2);margin-bottom:var(--mrd-space-1)}.mrd-table__filter-list-btn.sc-mrd-table{font-size:var(--mrd-font-size-xs);color:var(--mrd-color-primary);background:transparent;border:none;cursor:pointer;padding:0;text-decoration:underline}.mrd-table__filter-checkbox-label.sc-mrd-table{display:flex;align-items:center;gap:var(--mrd-space-1);cursor:pointer;font-size:var(--mrd-font-size-sm);color:var(--mrd-color-neutral-800)}.mrd-table__filter-no-support.sc-mrd-table{font-size:var(--mrd-font-size-sm);color:var(--mrd-color-neutral-500);margin:0;font-style:italic}.mrd-table__filter-popup-footer.sc-mrd-table{display:flex;justify-content:flex-end;gap:var(--mrd-space-2);padding:var(--mrd-space-2) var(--mrd-space-3);border-top:1px solid var(--mrd-border-color)}.mrd-table__filter-btn.sc-mrd-table{padding:var(--mrd-space-1) var(--mrd-space-3);border-radius:var(--mrd-border-radius);border:1px solid var(--mrd-border-color);font-size:var(--mrd-font-size-sm);cursor:pointer}.mrd-table__filter-btn--clear.sc-mrd-table{background:transparent;color:var(--mrd-color-neutral-600)}.mrd-table__filter-btn--clear.sc-mrd-table:hover{background:var(--mrd-color-neutral-100)}.mrd-table__filter-btn--apply.sc-mrd-table{background:var(--mrd-color-primary);border-color:var(--mrd-color-primary);color:var(--mrd-color-white)}.mrd-table__filter-btn--apply.sc-mrd-table:hover{background:var(--mrd-color-primary-dark, var(--mrd-color-primary));border-color:var(--mrd-color-primary-dark, var(--mrd-color-primary))}.mrd-table__totals-row.sc-mrd-table{border-top:2px solid var(--mrd-border-color)}.mrd-table__totals-cell.sc-mrd-table{position:sticky;bottom:0;z-index:2;padding:var(--mrd-space-2) var(--mrd-space-4);background:var(--mrd-color-white);font-weight:var(--mrd-font-weight-medium);font-variant-numeric:tabular-nums;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;border-top:2px solid var(--mrd-border-color)}.mrd-table__totals-cell--numeric.sc-mrd-table{text-align:right}.mrd-table__footer.sc-mrd-table{padding:var(--mrd-space-1) var(--mrd-space-2);font-size:var(--mrd-font-size-xs);color:var(--mrd-color-neutral-500);text-align:right}.mrd-table__empty.sc-mrd-table{padding:var(--mrd-space-4) var(--mrd-space-3);color:var(--mrd-color-neutral-500);font-size:var(--mrd-font-size-sm);text-align:center;margin:0}.mrd-table__file-btn.sc-mrd-table{display:inline-flex;align-items:center;gap:var(--mrd-space-1);background:none;border:none;padding:0;cursor:pointer;color:var(--mrd-color-primary);font-size:var(--mrd-font-size-sm);font-family:inherit;max-width:100%;overflow:hidden}.mrd-table__file-btn.sc-mrd-table:hover{text-decoration:underline;color:var(--mrd-color-primary-dark)}.mrd-table__file-icon.sc-mrd-table{flex-shrink:0;width:1rem;height:1rem}.mrd-table__textblock-btn.sc-mrd-table{display:inline;background:none;border:none;padding:0 0 0 var(--mrd-space-1);cursor:pointer;color:var(--mrd-color-primary);font-size:var(--mrd-font-size-sm);font-family:inherit;line-height:inherit;vertical-align:middle}.mrd-table__textblock-btn.sc-mrd-table:hover{color:var(--mrd-color-primary-dark)}.mrd-table__modal-backdrop.sc-mrd-table{position:fixed;inset:0;background:rgba(0, 0, 0, 0.4);z-index:var(--mrd-z-modal, 300);display:flex;align-items:center;justify-content:center}.mrd-table__modal.sc-mrd-table{background:#fff;border-radius:var(--mrd-radius-md, 0.5rem);padding:var(--mrd-space-6);max-width:min(600px, 90vw);max-height:70vh;overflow-y:auto;position:relative;box-shadow:var(--mrd-shadow-lg)}.mrd-table__modal-close.sc-mrd-table{position:absolute;top:var(--mrd-space-3);right:var(--mrd-space-3);background:none;border:none;cursor:pointer;font-size:1.25rem;line-height:1;color:var(--mrd-color-text-muted, #6b7280);padding:0}.mrd-table__modal-close.sc-mrd-table:hover{color:var(--mrd-color-text, #111827)}.mrd-table__modal-text.sc-mrd-table{margin:0;padding-right:var(--mrd-space-6);white-space:pre-wrap;word-break:break-word;font-size:var(--mrd-font-size-sm);line-height:1.6}.mrd-table__json-preview.sc-mrd-table{font-family:var(--mrd-font-family-mono);font-size:var(--mrd-font-size-xs);overflow:hidden;white-space:nowrap;text-overflow:ellipsis;display:inline-block;max-width:calc(100% - 1.5rem);vertical-align:middle}.mrd-table__modal-json.sc-mrd-table{margin:0;padding-right:var(--mrd-space-6);font-family:var(--mrd-font-family-mono);font-size:var(--mrd-font-size-xs);white-space:pre-wrap;word-break:break-word;line-height:1.6;background:none;border:none}@keyframes mrd-shimmer{0%{background-position:200% 0}100%{background-position:-200% 0}}`;
6614
6667
 
6615
6668
  /** How often (ms) to scan outstanding page requests for staleness. */
6616
6669
  const STALE_CHECK_INTERVAL_MS = 2000;
@@ -6641,6 +6694,17 @@ const MrdTable = class {
6641
6694
  this.keydownHandler = null;
6642
6695
  this.createPickerClickHandler = null;
6643
6696
  this.viewPopoverClickHandler = null;
6697
+ /** Debounce timer for the free-text search box — separate from debounceTimer
6698
+ * (pending-page requests), since typing and scrolling debounce independently. */
6699
+ this.searchDebounceTimer = null;
6700
+ /** The rendered search <input>, captured so it can be focused the instant it mounts. */
6701
+ this.searchInputEl = null;
6702
+ /** Set by toggleSearch() when the box just expanded; consumed and cleared in componentDidRender(). */
6703
+ this.searchNeedsFocus = false;
6704
+ /** Column filters stashed by stashFiltersForSearch() while the search box is open — the
6705
+ * backend can't combine `q` with column filters. Restored by restoreFiltersAfterSearch()
6706
+ * on close, unless the user applied a fresh filter of their own in the meantime. */
6707
+ this.stashedFilters = null;
6644
6708
  /** Watches the host for gaining a box (e.g. its tab pane being shown) — see componentDidLoad. */
6645
6709
  this.resizeObserver = null;
6646
6710
  /** Guards against scheduling more than one column-width measurement per freeze. */
@@ -6675,6 +6739,10 @@ const MrdTable = class {
6675
6739
  /** Time (ms) to wait for setPage() before retrying a page request; after
6676
6740
  * MAX_RETRY_ATTEMPTS retries the page is marked failed (see failedPages). */
6677
6741
  this.requestTimeoutMs = REQUEST_TIMEOUT_MS;
6742
+ /** When true, the toolbar's create ("+") action is disabled (with a tooltip
6743
+ * explaining why) so a viewer-role user can see at a glance that this dashboard
6744
+ * is read-only. Sorting, filtering and export stay available — those don't mutate data. */
6745
+ this.readOnly = false;
6678
6746
  // ── Internal state ─────────────────────────────────────────────────────────
6679
6747
  /** Index into allViews[] for the currently displayed view. 0 = primary, 1+ = alternatives. */
6680
6748
  this.activeViewIdx = 0;
@@ -6691,6 +6759,10 @@ const MrdTable = class {
6691
6759
  this.sortDir = 'asc';
6692
6760
  /** Active filters keyed by field name. */
6693
6761
  this.activeFilters = new Map();
6762
+ /** True while the toolbar's magnifying glass is expanded into the search input. */
6763
+ this.searchOpen = false;
6764
+ /** Current free-text search query — sent as `q` on every mrdLoadPage/aggregations request. */
6765
+ this.searchQuery = '';
6694
6766
  /** Field name of the currently open filter popup (null = closed). */
6695
6767
  this.openFilterCol = null;
6696
6768
  /** Filter state being edited in the open popup. */
@@ -6744,6 +6816,11 @@ const MrdTable = class {
6744
6816
  this.colWidthMeasureScheduled = false;
6745
6817
  this.colWidthsUserSet = false;
6746
6818
  this.fixedContentMeasured = false;
6819
+ // A new item is a different table (possibly not even a VIEW any more) — a leftover
6820
+ // query from the old one should not silently keep filtering it.
6821
+ this.searchOpen = false;
6822
+ this.searchQuery = '';
6823
+ this.stashedFilters = null;
6747
6824
  }
6748
6825
  // ── Lifecycle ──────────────────────────────────────────────────────────────
6749
6826
  componentWillLoad() {
@@ -6851,7 +6928,12 @@ const MrdTable = class {
6851
6928
  if (pageNumber === 0 && this.aggregationsPending) {
6852
6929
  this.aggregationsPending = false;
6853
6930
  const hasAggColumns = this.columns.some(c => c.type === 'FIELD' && c.aggregate);
6854
- if (!isLastPage || hasAggColumns) {
6931
+ // The aggregations endpoint doesn't support the `q` free-text param, so a request
6932
+ // sent while searching would silently return unfiltered totals. Leave aggregations
6933
+ // (and aggregationsTotal) at null instead — TotalsRow already renders nothing when
6934
+ // aggregations is null, so the totals row just disappears for the duration of the
6935
+ // search rather than showing numbers that don't match the filtered rows.
6936
+ if (!this.searchQuery && (!isLastPage || hasAggColumns)) {
6855
6937
  this.emitLoadAggregations();
6856
6938
  }
6857
6939
  }
@@ -6890,10 +6972,18 @@ const MrdTable = class {
6890
6972
  document.removeEventListener('click', this.viewPopoverClickHandler);
6891
6973
  this.viewPopoverClickHandler = null;
6892
6974
  }
6975
+ if (this.searchDebounceTimer !== null) {
6976
+ clearTimeout(this.searchDebounceTimer);
6977
+ this.searchDebounceTimer = null;
6978
+ }
6893
6979
  }
6894
6980
  componentDidRender() {
6895
6981
  this.freezeColWidths();
6896
6982
  this.growToFixedContent();
6983
+ if (this.searchNeedsFocus && this.searchInputEl) {
6984
+ this.searchInputEl.focus();
6985
+ this.searchNeedsFocus = false;
6986
+ }
6897
6987
  }
6898
6988
  /** Freeze the column widths once the first data page has rendered.
6899
6989
  *
@@ -7212,7 +7302,13 @@ const MrdTable = class {
7212
7302
  dataClass: v.dataClass,
7213
7303
  });
7214
7304
  }
7215
- /** Build query params for a page request from current sort, view filters, filterClass and active column filters. */
7305
+ /** Build query params for a page request from current sort, view filters, filterClass and active column filters.
7306
+ *
7307
+ * `q` and column filters are mutually exclusive on the backend — combining them breaks
7308
+ * the search. stashFiltersForSearch() already empties activeFilters for the duration of
7309
+ * a search, but this guard is the actual guarantee: even if a filter is applied through
7310
+ * a column header while the search box is still open (before the query is cleared),
7311
+ * activeFilters is dropped from the request rather than sent alongside `q`. */
7216
7312
  buildQueryParams(page) {
7217
7313
  var _a;
7218
7314
  const v = this.allViews[this.activeViewIdx];
@@ -7221,7 +7317,8 @@ const MrdTable = class {
7221
7317
  sort: this.sortParam(),
7222
7318
  filterClass: v === null || v === void 0 ? void 0 : v.filterClass,
7223
7319
  viewFilters: (_a = v === null || v === void 0 ? void 0 : v.view) === null || _a === void 0 ? void 0 : _a.filter,
7224
- activeFilters: this.activeFilters.values(),
7320
+ activeFilters: this.searchQuery ? [] : this.activeFilters.values(),
7321
+ query: this.searchQuery,
7225
7322
  });
7226
7323
  }
7227
7324
  /** True when the table is in paginated / virtual-scroll mode rather than driven by the
@@ -7244,7 +7341,7 @@ const MrdTable = class {
7244
7341
  const raw = (_b = (_a = this.item) === null || _a === void 0 ? void 0 : _a.actions) !== null && _b !== void 0 ? _b : [];
7245
7342
  return (raw !== null && raw !== void 0 ? raw : []).reduce((acc, a) => {
7246
7343
  if (a === 'NEW')
7247
- acc.push({ action: 'create', label: cellRenderer.t('table_new_record', this.locale), icon: 'assets/sprites.svg#icon-plus', variant: 'primary' });
7344
+ acc.push({ action: 'create', label: cellRenderer.t('table_new_record', this.locale), icon: 'assets/sprites.svg#icon-plus', variant: 'primary', disabled: this.readOnly });
7248
7345
  if (a === 'EXPORT')
7249
7346
  acc.push({ action: 'export', label: cellRenderer.t('table_export_excel', this.locale), icon: 'assets/sprites.svg#icon-file-excel' });
7250
7347
  return acc;
@@ -7527,6 +7624,18 @@ const MrdTable = class {
7527
7624
  const current = (_b = (_a = this.pendingFilter) === null || _a === void 0 ? void 0 : _a.values) !== null && _b !== void 0 ? _b : [];
7528
7625
  this.pendingFilter = Object.assign(Object.assign({}, this.pendingFilter), { values: checked ? [...current, key] : current.filter(k => k !== key) });
7529
7626
  }
7627
+ /** Common reload after activeFilters or searchQuery changed: drop stale aggregations
7628
+ * (the next setPage(0) re-requests them, guarded against `q` in setPage() itself) and
7629
+ * re-fetch page 0. Shared by the filter popup, clear-all, and free-text search. */
7630
+ reloadAfterQueryChange() {
7631
+ this.aggregations = null;
7632
+ this.aggregationsTotal = null;
7633
+ this.aggregationsPending = true;
7634
+ if (this.isPaginated) {
7635
+ this.resetPages();
7636
+ this.emitPagesForWindow(this.renderStart, this.renderEnd);
7637
+ }
7638
+ }
7530
7639
  applyFilter() {
7531
7640
  const f = this.pendingFilter;
7532
7641
  if (!(f === null || f === void 0 ? void 0 : f.field)) {
@@ -7543,13 +7652,7 @@ const MrdTable = class {
7543
7652
  }
7544
7653
  this.activeFilters = next;
7545
7654
  this.closeFilterPopup();
7546
- this.aggregations = null;
7547
- this.aggregationsTotal = null;
7548
- this.aggregationsPending = true;
7549
- if (this.isPaginated) {
7550
- this.resetPages();
7551
- this.emitPagesForWindow(this.renderStart, this.renderEnd);
7552
- }
7655
+ this.reloadAfterQueryChange();
7553
7656
  }
7554
7657
  clearFilter() {
7555
7658
  const name = this.openFilterCol;
@@ -7558,24 +7661,81 @@ const MrdTable = class {
7558
7661
  next.delete(name);
7559
7662
  this.activeFilters = next;
7560
7663
  this.closeFilterPopup();
7561
- this.aggregations = null;
7562
- this.aggregationsTotal = null;
7563
- this.aggregationsPending = true;
7564
- if (this.isPaginated) {
7565
- this.resetPages();
7566
- this.emitPagesForWindow(this.renderStart, this.renderEnd);
7567
- }
7664
+ this.reloadAfterQueryChange();
7568
7665
  }
7569
7666
  clearAllFilters() {
7570
7667
  this.activeFilters = new Map();
7571
- this.aggregations = null;
7572
- this.aggregationsTotal = null;
7573
- this.aggregationsPending = true;
7574
- if (this.isPaginated) {
7575
- this.resetPages();
7576
- this.emitPagesForWindow(this.renderStart, this.renderEnd);
7668
+ this.reloadAfterQueryChange();
7669
+ }
7670
+ // ── Free-text search ───────────────────────────────────────────────────────
7671
+ /** Search is a VIEW-only affordance — a RELATED_VIEW table is already scoped to
7672
+ * its parent record, so a free-text box there would invite confusion about what
7673
+ * it searches across. */
7674
+ get searchEnabled() {
7675
+ var _a;
7676
+ return ((_a = this.item) === null || _a === void 0 ? void 0 : _a.type) === 'VIEW';
7677
+ }
7678
+ toggleSearch() {
7679
+ if (this.searchOpen) {
7680
+ this.closeSearch();
7681
+ }
7682
+ else {
7683
+ this.searchOpen = true;
7684
+ this.searchNeedsFocus = true;
7685
+ this.stashFiltersForSearch();
7577
7686
  }
7578
7687
  }
7688
+ /** Collapses the box back to the icon. Clearing a non-empty query and restoring any
7689
+ * stashed filters can each independently require a reload — reloadAfterQueryChange()
7690
+ * is called at most once here regardless of how many of the two actually changed. */
7691
+ closeSearch() {
7692
+ this.searchOpen = false;
7693
+ if (this.searchDebounceTimer !== null) {
7694
+ clearTimeout(this.searchDebounceTimer);
7695
+ this.searchDebounceTimer = null;
7696
+ }
7697
+ const hadQuery = this.searchQuery !== '';
7698
+ this.searchQuery = '';
7699
+ const filtersRestored = this.restoreFiltersAfterSearch();
7700
+ if (hadQuery || filtersRestored)
7701
+ this.reloadAfterQueryChange();
7702
+ }
7703
+ handleSearchInput(value) {
7704
+ this.searchQuery = value;
7705
+ if (this.searchDebounceTimer !== null)
7706
+ clearTimeout(this.searchDebounceTimer);
7707
+ this.searchDebounceTimer = setTimeout(() => this.applySearch(), SEARCH_DEBOUNCE_MS);
7708
+ }
7709
+ /** Reloads from page 0 with the current searchQuery, same reset as applyFilter(). */
7710
+ applySearch() {
7711
+ this.searchDebounceTimer = null;
7712
+ this.reloadAfterQueryChange();
7713
+ }
7714
+ /** Column filters and the free-text `q` param are mutually exclusive on the backend —
7715
+ * sending both breaks the search. Opening the search box stashes any active filters
7716
+ * and clears them (the header filter icons and the clear-all button disappear along
7717
+ * with activeFilters, so there is nothing extra to hide) and reloads unfiltered. */
7718
+ stashFiltersForSearch() {
7719
+ if (this.activeFilters.size === 0)
7720
+ return;
7721
+ this.stashedFilters = this.activeFilters;
7722
+ this.activeFilters = new Map();
7723
+ this.reloadAfterQueryChange();
7724
+ }
7725
+ /** Restores filters stashed by stashFiltersForSearch(), unless the user applied a fresh
7726
+ * filter of their own while the search box was open — that explicit choice wins over
7727
+ * reinstating the old one. Returns whether activeFilters actually changed, so the
7728
+ * caller can decide whether a reload is needed. */
7729
+ restoreFiltersAfterSearch() {
7730
+ if (!this.stashedFilters)
7731
+ return false;
7732
+ const stashed = this.stashedFilters;
7733
+ this.stashedFilters = null;
7734
+ if (this.activeFilters.size > 0)
7735
+ return false;
7736
+ this.activeFilters = stashed;
7737
+ return true;
7738
+ }
7579
7739
  // ── View switcher ──────────────────────────────────────────────────────────
7580
7740
  handleViewSwitch(targetIdx) {
7581
7741
  var _a, _b;
@@ -7585,6 +7745,9 @@ const MrdTable = class {
7585
7745
  this.activeViewIdx = targetIdx;
7586
7746
  this.applyDefaultSort((_b = (_a = target.view) === null || _a === void 0 ? void 0 : _a.defaultSort) !== null && _b !== void 0 ? _b : '');
7587
7747
  this.activeFilters = new Map();
7748
+ this.searchOpen = false;
7749
+ this.searchQuery = '';
7750
+ this.stashedFilters = null;
7588
7751
  this.init();
7589
7752
  }
7590
7753
  toggleViewPopover(e) {
@@ -7607,7 +7770,7 @@ const MrdTable = class {
7607
7770
  // ── Render: toolbar ────────────────────────────────────────────────────────
7608
7771
  renderToolbar() {
7609
7772
  var _a;
7610
- return (index.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: {
7773
+ return (index.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: {
7611
7774
  onClearAllFilters: () => this.clearAllFilters(),
7612
7775
  onViewSwitch: idx => this.handleViewSwitch(idx),
7613
7776
  onToggleViewPopover: e => this.toggleViewPopover(e),
@@ -7618,6 +7781,10 @@ const MrdTable = class {
7618
7781
  this.closeCreatePicker();
7619
7782
  },
7620
7783
  onAction: action => this.mrdAction.emit(this.buildActionDetail(action)),
7784
+ onToggleSearch: () => this.toggleSearch(),
7785
+ onSearchInput: value => this.handleSearchInput(value),
7786
+ onCloseSearch: () => this.closeSearch(),
7787
+ onSearchInputRef: el => (this.searchInputEl = el !== null && el !== void 0 ? el : null),
7621
7788
  } }));
7622
7789
  }
7623
7790
  // ── Render: filter popup ───────────────────────────────────────────────────