@mmlogic/components 0.1.23 → 0.1.25

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 (57) hide show
  1. package/dist/cjs/index.cjs.js +2 -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_19.cjs.entry.js +3150 -0
  5. package/dist/collection/collection-manifest.json +2 -0
  6. package/dist/collection/components/mrd-field/mrd-field.js +5 -1
  7. package/dist/collection/components/mrd-layout-section/mrd-layout-section.js +715 -0
  8. package/dist/collection/components/mrd-layout-section/mrd-layout-section.scss +339 -0
  9. package/dist/collection/components/mrd-longtext-field/mrd-longtext-field.js +227 -0
  10. package/dist/collection/components/mrd-longtext-field/mrd-longtext-field.scss +78 -0
  11. package/dist/collection/components/mrd-number-field/mrd-number-field.js +2 -2
  12. package/dist/collection/components/mrd-table/mrd-table.js +41 -4
  13. package/dist/collection/components/mrd-table/mrd-table.scss +23 -0
  14. package/dist/collection/components/mrd-text-field/mrd-text-field.js +1 -1
  15. package/dist/collection/components/mrd-textarea-field/mrd-textarea-field.js +1 -1
  16. package/dist/collection/components/mrd-time-field/mrd-time-field.js +1 -1
  17. package/dist/collection/dev/app.js +109 -3
  18. package/dist/collection/dev/example-data.js +324 -0
  19. package/dist/collection/types/client-layout.js +2 -0
  20. package/dist/collection/utils/cell-renderer.js +32 -0
  21. package/dist/components/client-layout.js +1 -1
  22. package/dist/components/mrd-field2.js +1 -1
  23. package/dist/components/mrd-form.js +1 -1
  24. package/dist/components/mrd-layout-section.d.ts +11 -0
  25. package/dist/components/mrd-layout-section.js +1 -0
  26. package/dist/components/mrd-longtext-field.d.ts +11 -0
  27. package/dist/components/mrd-longtext-field.js +1 -0
  28. package/dist/components/mrd-longtext-field2.js +1 -0
  29. package/dist/components/mrd-number-field2.js +1 -1
  30. package/dist/components/mrd-table.js +1 -1
  31. package/dist/components/mrd-table2.js +1 -0
  32. package/dist/components/mrd-text-field2.js +1 -1
  33. package/dist/components/mrd-textarea-field2.js +1 -1
  34. package/dist/components/mrd-time-field2.js +1 -1
  35. package/dist/esm/index.js +2 -0
  36. package/dist/esm/loader.js +1 -1
  37. package/dist/esm/mosterdcomponents.js +1 -1
  38. package/dist/esm/mrd-boolean-field_19.entry.js +3130 -0
  39. package/dist/mosterdcomponents/index.esm.js +1 -1
  40. package/dist/mosterdcomponents/mosterdcomponents.esm.js +1 -1
  41. package/dist/mosterdcomponents/p-a3255fc4.entry.js +1 -0
  42. package/dist/types/components/mrd-layout-section/mrd-layout-section.d.ts +92 -0
  43. package/dist/types/components/mrd-longtext-field/mrd-longtext-field.d.ts +22 -0
  44. package/dist/types/components/mrd-table/mrd-table.d.ts +5 -0
  45. package/dist/types/components.d.ts +224 -8
  46. package/dist/types/types/client-layout.d.ts +22 -1
  47. package/dist/types/utils/cell-renderer.d.ts +9 -1
  48. package/package.json +1 -1
  49. package/dist/cjs/format-DExY8_nu.js +0 -328
  50. package/dist/cjs/mrd-boolean-field_16.cjs.entry.js +0 -1512
  51. package/dist/cjs/mrd-table.cjs.entry.js +0 -882
  52. package/dist/esm/format-CcRjWvcb.js +0 -319
  53. package/dist/esm/mrd-boolean-field_16.entry.js +0 -1495
  54. package/dist/esm/mrd-table.entry.js +0 -880
  55. package/dist/mosterdcomponents/p-16dcbcdf.entry.js +0 -1
  56. package/dist/mosterdcomponents/p-17e5a251.entry.js +0 -1
  57. package/dist/mosterdcomponents/p-CcRjWvcb.js +0 -1
@@ -12,6 +12,8 @@ const TEXT_TYPES = new Set(['TEXT', 'TEXTBLOCK', 'EMAIL', 'HYPERLINK']);
12
12
  const NUMERIC_TYPES = new Set(['INTEGER', 'DECIMAL', 'PERCENTAGE', 'CURRENCY']);
13
13
  const DATE_TYPES = new Set(['DATE', 'DATETIME', 'TIME']);
14
14
  const NO_FILTER_TYPES = new Set(['FILE', 'IMAGE']);
15
+ /** Column types that cannot be sorted or filtered (not stored in PostgreSQL). */
16
+ const NON_INTERACTIVE_TYPES = new Set(['LONGTEXT', 'JSON']);
15
17
  export class MrdTable {
16
18
  constructor() {
17
19
  // ── Non-state internals ────────────────────────────────────────────────────
@@ -64,6 +66,8 @@ export class MrdTable {
64
66
  this.scrollTop = 0;
65
67
  /** Full text shown in the TEXTBLOCK expand modal (null = closed). */
66
68
  this.textblockModal = null;
69
+ /** Syntax-highlighted JSON HTML shown in the JSON expand modal (null = closed). */
70
+ this.jsonModal = null;
67
71
  /** Aggregation totals received from the host via setAggregations(). Null = not yet loaded. */
68
72
  this.aggregations = null;
69
73
  /** Whether the view switcher dropdown is open. */
@@ -401,6 +405,23 @@ export class MrdTable {
401
405
  this.keydownHandler = null;
402
406
  }
403
407
  }
408
+ openJsonModal(html) {
409
+ this.jsonModal = html;
410
+ if (this.keydownHandler)
411
+ document.removeEventListener('keydown', this.keydownHandler);
412
+ this.keydownHandler = (ev) => {
413
+ if (ev.key === 'Escape')
414
+ this.closeJsonModal();
415
+ };
416
+ document.addEventListener('keydown', this.keydownHandler);
417
+ }
418
+ closeJsonModal() {
419
+ this.jsonModal = null;
420
+ if (this.keydownHandler) {
421
+ document.removeEventListener('keydown', this.keydownHandler);
422
+ this.keydownHandler = null;
423
+ }
424
+ }
404
425
  setPending(key, val) {
405
426
  this.pendingFilter = Object.assign(Object.assign({}, this.pendingFilter), { [key]: val });
406
427
  }
@@ -680,7 +701,7 @@ export class MrdTable {
680
701
  }
681
702
  // ── Render: cell ──────────────────────────────────────────────────────────
682
703
  renderCell(col, row) {
683
- var _a, _b, _c, _d;
704
+ var _a, _b, _c, _d, _e, _f;
684
705
  const numericTypes = new Set(['INTEGER', 'DECIMAL', 'PERCENTAGE', 'CURRENCY']);
685
706
  const dataType = (_b = (_a = col.field) === null || _a === void 0 ? void 0 : _a.dataType) !== null && _b !== void 0 ? _b : '';
686
707
  const isNumeric = col.type === 'FIELD' && numericTypes.has(dataType);
@@ -695,6 +716,15 @@ export class MrdTable {
695
716
  this.mrdDownload.emit({ href, fileName });
696
717
  } }, h("svg", { class: "mrd-table__file-icon", viewBox: "0 0 24 24", "aria-hidden": "true" }, h("path", { fill: "currentColor", d: "M14 2H6c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V8l-6-6zm-1 7V3.5L18.5 9H13zm-3 8l-3-3 1.41-1.41L10 14.17l4.59-4.58L16 11l-6 6z" })), t('download', this.locale))) : ''));
697
718
  }
719
+ if (dataType === 'JSON') {
720
+ const name = (_f = (_e = col.field) === null || _e === void 0 ? void 0 : _e.name) !== null && _f !== void 0 ? _f : '';
721
+ const rawValue = name ? row === null || row === void 0 ? void 0 : row[name] : undefined;
722
+ if (rawValue == null || rawValue === '')
723
+ return h("td", { class: "mrd-table__cell" });
724
+ const compactHtml = CellRenderer.formatJson(rawValue, true);
725
+ const prettyHtml = CellRenderer.formatJson(rawValue, false);
726
+ return (h("td", { class: "mrd-table__cell" }, h("span", { class: "mrd-table__json-preview", innerHTML: compactHtml }), h("button", { class: "mrd-table__textblock-btn", onClick: (e) => { e.stopPropagation(); this.openJsonModal(prettyHtml); }, "aria-label": t('textblock_show_more', this.locale) }, "\u22EF")));
727
+ }
698
728
  const TEXTBLOCK_MAX = 200;
699
729
  if (dataType === 'TEXTBLOCK') {
700
730
  const full = CellRenderer.render(col, row, this.locale);
@@ -737,7 +767,7 @@ export class MrdTable {
737
767
  this.filterMode ? 'mrd-table__header--sortable' : '',
738
768
  ].filter(Boolean).join(' ');
739
769
  return (h("th", { class: cls, onClick: this.filterMode ? (e) => this.handleFilterOpen(col, e) : undefined }, h("span", { class: "mrd-table__header-label" }, (_d = (_b = (_a = col.field) === null || _a === void 0 ? void 0 : _a.label) !== null && _b !== void 0 ? _b : (_c = col.relation) === null || _c === void 0 ? void 0 : _c.label) !== null && _d !== void 0 ? _d : ''), isFiltered && this.renderFilterIcon()));
740
- }))), h("tbody", null, (_b = this.rows) === null || _b === void 0 ? void 0 : _b.map((row, i) => (h("tr", { class: "mrd-table__row mrd-table__row--clickable", style: { background: i % 2 === 0 ? '' : 'var(--mrd-color-neutral-100)' }, onClick: () => this.mrdRowClick.emit(row) }, this.columns.map(col => this.renderCell(col, row)))))), this.renderTotalsRow()), (!this.rows || this.rows.length === 0) && (h("p", { class: "mrd-table__empty" }, t('no_results', this.locale)))), this.renderFooter((_c = this.rows) === null || _c === void 0 ? void 0 : _c.length), this.renderFilterPopup(), this.renderTextblockModal()));
770
+ }))), h("tbody", null, (_b = this.rows) === null || _b === void 0 ? void 0 : _b.map((row, i) => (h("tr", { class: "mrd-table__row mrd-table__row--clickable", style: { background: i % 2 === 0 ? '' : 'var(--mrd-color-neutral-100)' }, onClick: () => this.mrdRowClick.emit(row) }, this.columns.map(col => this.renderCell(col, row)))))), this.renderTotalsRow()), (!this.rows || this.rows.length === 0) && (h("p", { class: "mrd-table__empty" }, t('no_results', this.locale)))), this.renderFooter((_c = this.rows) === null || _c === void 0 ? void 0 : _c.length), this.renderFilterPopup(), this.renderTextblockModal(), this.renderJsonModal()));
741
771
  }
742
772
  // ── Paginated / virtual-scroll mode ────────────────────────────────────
743
773
  // Derive the authoritative row count from loaded pages:
@@ -773,13 +803,14 @@ export class MrdTable {
773
803
  const name = this.colName(col);
774
804
  const isActive = this.sortField === name;
775
805
  const isFiltered = this.activeFilters.has(name);
806
+ const isInteractive = !NON_INTERACTIVE_TYPES.has(this.colDataType(col));
776
807
  const cls = [
777
808
  'mrd-table__header',
778
- 'mrd-table__header--sortable',
809
+ isInteractive ? 'mrd-table__header--sortable' : '',
779
810
  isActive ? `mrd-table__header--sorted-${this.sortDir}` : '',
780
811
  isFiltered ? 'mrd-table__header--filtered' : '',
781
812
  ].filter(Boolean).join(' ');
782
- return (h("th", { class: cls, style: this.colWidths[idx] ? { width: `${this.colWidths[idx]}px` } : undefined, onClick: (e) => this.filterMode ? this.handleFilterOpen(col, e) : this.handleSortClick(col) }, h("span", { class: "mrd-table__header-label" }, (_d = (_b = (_a = col.field) === null || _a === void 0 ? void 0 : _a.label) !== null && _b !== void 0 ? _b : (_c = col.relation) === null || _c === void 0 ? void 0 : _c.label) !== null && _d !== void 0 ? _d : ''), isActive && (h("span", { class: "mrd-table__sort-icon", "aria-hidden": "true" }, this.sortDir === 'asc' ? '▲' : '▼')), !isActive && !this.filterMode && (h("span", { class: "mrd-table__sort-icon", "aria-hidden": "true" }, "\u21C5")), isFiltered && this.renderFilterIcon()));
813
+ return (h("th", { class: cls, style: this.colWidths[idx] ? { width: `${this.colWidths[idx]}px` } : undefined, onClick: isInteractive ? ((e) => this.filterMode ? this.handleFilterOpen(col, e) : this.handleSortClick(col)) : undefined }, h("span", { class: "mrd-table__header-label" }, (_d = (_b = (_a = col.field) === null || _a === void 0 ? void 0 : _a.label) !== null && _b !== void 0 ? _b : (_c = col.relation) === null || _c === void 0 ? void 0 : _c.label) !== null && _d !== void 0 ? _d : ''), isInteractive && isActive && (h("span", { class: "mrd-table__sort-icon", "aria-hidden": "true" }, this.sortDir === 'asc' ? '▲' : '▼')), isInteractive && !isActive && !this.filterMode && (h("span", { class: "mrd-table__sort-icon", "aria-hidden": "true" }, "\u21C5")), isInteractive && isFiltered && this.renderFilterIcon()));
783
814
  }))), h("tbody", null, topSpacerHeight > 0 && (h("tr", { class: "mrd-table__spacer", style: { height: `${topSpacerHeight}px` } }, h("td", { colSpan: colCount }))), renderedRows, bottomSpacerHeight > 0 && (h("tr", { class: "mrd-table__spacer", style: { height: `${bottomSpacerHeight}px` } }, h("td", { colSpan: colCount })))), this.renderTotalsRow())), effectiveTotal === 0 && this.loadedPages.has(0) && (h("p", { class: "mrd-table__empty" }, t('no_results', this.locale))), effectiveTotal > 0 && this.renderFooter(undefined, effectiveTotal), this.renderFilterPopup(), this.renderTextblockModal()));
784
815
  }
785
816
  renderFilterIcon() {
@@ -790,6 +821,11 @@ export class MrdTable {
790
821
  return null;
791
822
  return (h("div", { class: "mrd-table__modal-backdrop", onClick: () => this.closeTextblockModal(), role: "dialog", "aria-modal": "true" }, h("div", { class: "mrd-table__modal", onClick: (e) => e.stopPropagation() }, h("button", { class: "mrd-table__modal-close", onClick: () => this.closeTextblockModal(), "aria-label": t('close', this.locale) }, "\u2715"), h("p", { class: "mrd-table__modal-text" }, this.textblockModal))));
792
823
  }
824
+ renderJsonModal() {
825
+ if (this.jsonModal === null)
826
+ return null;
827
+ return (h("div", { class: "mrd-table__modal-backdrop", onClick: () => this.closeJsonModal(), role: "dialog", "aria-modal": "true" }, h("div", { class: "mrd-table__modal", onClick: (e) => e.stopPropagation() }, h("button", { class: "mrd-table__modal-close", onClick: () => this.closeJsonModal(), "aria-label": t('close', this.locale) }, "\u2715"), h("pre", { class: "mrd-table__modal-json", innerHTML: this.jsonModal }))));
828
+ }
793
829
  static get is() { return "mrd-table"; }
794
830
  static get encapsulation() { return "scoped"; }
795
831
  static get originalStyleUrls() {
@@ -1060,6 +1096,7 @@ export class MrdTable {
1060
1096
  "popupPos": {},
1061
1097
  "scrollTop": {},
1062
1098
  "textblockModal": {},
1099
+ "jsonModal": {},
1063
1100
  "aggregations": {},
1064
1101
  "viewSwitcherOpen": {}
1065
1102
  };
@@ -746,3 +746,26 @@
746
746
  line-height: 1.6;
747
747
  }
748
748
 
749
+ .mrd-table__json-preview {
750
+ font-family: var(--mrd-font-family-mono);
751
+ font-size: var(--mrd-font-size-xs);
752
+ overflow: hidden;
753
+ white-space: nowrap;
754
+ text-overflow: ellipsis;
755
+ display: inline-block;
756
+ max-width: calc(100% - 1.5rem);
757
+ vertical-align: middle;
758
+ }
759
+
760
+ .mrd-table__modal-json {
761
+ margin: 0;
762
+ padding-right: var(--mrd-space-6);
763
+ font-family: var(--mrd-font-family-mono);
764
+ font-size: var(--mrd-font-size-xs);
765
+ white-space: pre-wrap;
766
+ word-break: break-word;
767
+ line-height: 1.6;
768
+ background: none;
769
+ border: none;
770
+ }
771
+
@@ -28,7 +28,7 @@ export class MrdTextField {
28
28
  }
29
29
  render() {
30
30
  const hasError = !!this.error;
31
- return (h(Host, { key: '4ddbcb2f8915235631c6f66449b28e664c24c0a4' }, h("div", { key: '91dbb52ed715c92d4ef2bc0924fd378d0dc839a8', class: "mrd-text-field" }, this.label && (h("label", { key: 'cf3278503107d313c08034273916a4b84a16febd', class: `mrd-text-field__label${this.required ? ' mrd-text-field__label--required' : ''}` }, this.label)), h("input", { key: '9ab992213bfaa372e453331e1b1e12a853c3ae6e', class: `mrd-text-field__input${hasError ? ' mrd-text-field__input--error' : ''}`, type: "text", name: this.name, value: this.value, placeholder: this.placeholder, required: this.required, disabled: this.disabled, onInput: this.handleInput, onBlur: this.handleBlur }), hasError && h("span", { key: 'a4ff2fdd2490f644eb97f66f15edc5af0b22e823', class: "mrd-text-field__error" }, this.error))));
31
+ return (h(Host, { key: 'b1ea3dce848a4ad41b60599804c69a35ccd35570' }, h("div", { key: '2e327824843fe02c5851beae96d52f9eb9ee67ce', class: "mrd-text-field" }, this.label && (h("label", { key: '99026834b30a53d7cf715c59099895211ff6ddb7', class: `mrd-text-field__label${this.required ? ' mrd-text-field__label--required' : ''}` }, this.label)), h("input", { key: '0e56c9115b2016ad3cb3ce8e734297354c70c7e2', class: `mrd-text-field__input${hasError ? ' mrd-text-field__input--error' : ''}`, type: "text", name: this.name, value: this.value, placeholder: this.placeholder, required: this.required, disabled: this.disabled, onInput: this.handleInput, onBlur: this.handleBlur }), hasError && h("span", { key: '0430c32b083484747962147e167b11ed7a1597cc', class: "mrd-text-field__error" }, this.error))));
32
32
  }
33
33
  static get is() { return "mrd-text-field"; }
34
34
  static get encapsulation() { return "scoped"; }
@@ -66,7 +66,7 @@ export class MrdTextareaField {
66
66
  }
67
67
  }
68
68
  render() {
69
- return (h(Host, { key: '8f7544745341ea2eca4450296db53ed8b41af0e2' }, h("div", { key: '62937448dd685071e0500b8f7f53fe22b10ffa15', class: "mrd-textarea-field" }, this.label && (h("label", { key: '4a7d807d78c2a9abeaf3ff94ee5e71b167475e25', class: `mrd-textarea-field__label${this.required ? ' mrd-textarea-field__label--required' : ''}` }, this.label)), h("div", { key: '599e28a45b1c161d7cfec3de138e386bf997f9b9', class: `mrd-textarea-field__container${this.error ? ' mrd-textarea-field__container--error' : ''}` }, h("div", { key: '5824a225fa6ea29bf02dfd8e4b27a71e853f059e', class: "mrd-textarea-field__editor" })), this.error && h("span", { key: '1c7693269df8f3fe112a6df419e6cbace75bdaa9', class: "mrd-textarea-field__error" }, this.error))));
69
+ return (h(Host, { key: '7bc05fae8d48c43b56bde892537930552605727a' }, h("div", { key: '3416e9c5fdf6362602715de8427c5d2ccfa52925', class: "mrd-textarea-field" }, this.label && (h("label", { key: 'c6bd35076738b1a700b53092fb0754250c73e085', class: `mrd-textarea-field__label${this.required ? ' mrd-textarea-field__label--required' : ''}` }, this.label)), h("div", { key: 'ed0483a25a62399d1ac8ee14bc59e3eb44b91814', class: `mrd-textarea-field__container${this.error ? ' mrd-textarea-field__container--error' : ''}` }, h("div", { key: 'd17c6997602b81e8b7f66f1e5cfb12b4836cb892', class: "mrd-textarea-field__editor" })), this.error && h("span", { key: '30a068872e73ca78781c9a5e1fac2c9e3ce30765', class: "mrd-textarea-field__error" }, this.error))));
70
70
  }
71
71
  static get is() { return "mrd-textarea-field"; }
72
72
  static get encapsulation() { return "scoped"; }
@@ -27,7 +27,7 @@ export class MrdTimeField {
27
27
  }
28
28
  render() {
29
29
  const hasError = !!this.error;
30
- return (h(Host, { key: '0a6e3047eb409b50a7dfa46c724e0d9824bdbad9' }, h("div", { key: '1fdcaa9cc0e44edd514d3453df0cfb264989cbca', class: "mrd-time-field" }, this.label && (h("label", { key: 'ae53864761258ae56e29474c2da68c852fe8db57', class: `mrd-time-field__label${this.required ? ' mrd-time-field__label--required' : ''}` }, this.label)), h("input", { key: '60336f497545b61656756790cfb5ec096034ecf7', class: `mrd-time-field__input${hasError ? ' mrd-time-field__input--error' : ''}`, type: "time", name: this.name, value: this.value, required: this.required, disabled: this.disabled, onChange: this.handleChange, onBlur: this.handleBlur }), hasError && h("span", { key: 'f8409b690c67873f6e4fe10f1d404b57005f0557', class: "mrd-time-field__error" }, this.error))));
30
+ return (h(Host, { key: '6a6828d9c5224ddfcc5d17124523037512f4970c' }, h("div", { key: '468d5295170cefc93b1e2d49b7189c8366d306b7', class: "mrd-time-field" }, this.label && (h("label", { key: '52ea233ea0a7e8563145c326c4ef9d27557b6dc8', class: `mrd-time-field__label${this.required ? ' mrd-time-field__label--required' : ''}` }, this.label)), h("input", { key: 'e9f0f75517638bf4e4ead95bf69530b5c395f8bc', class: `mrd-time-field__input${hasError ? ' mrd-time-field__input--error' : ''}`, type: "time", name: this.name, value: this.value, required: this.required, disabled: this.disabled, onChange: this.handleChange, onBlur: this.handleBlur }), hasError && h("span", { key: '4f734c3373444df4f686a7ae6bde84a2ee39d9f4', class: "mrd-time-field__error" }, this.error))));
31
31
  }
32
32
  static get is() { return "mrd-time-field"; }
33
33
  static get encapsulation() { return "scoped"; }
@@ -41,12 +41,97 @@ function logEvent(type, data) {
41
41
  ===================================================================== */
42
42
 
43
43
  function showTab(name) {
44
+ const names = ['embedded', 'field-types', 'detail-view', 'live-api'];
44
45
  document.querySelectorAll('.tab-btn').forEach((btn, i) => {
45
- const names = ['embedded', 'live-api'];
46
46
  btn.classList.toggle('active', names[i] === name);
47
47
  });
48
- document.getElementById('tab-embedded').classList.toggle('active', name === 'embedded');
49
- document.getElementById('tab-live-api').classList.toggle('active', name === 'live-api');
48
+ names.forEach(n => {
49
+ document.getElementById('tab-' + n).classList.toggle('active', n === name);
50
+ });
51
+ if (name === 'detail-view') initDetailViewTab();
52
+ }
53
+
54
+ /* =====================================================================
55
+ DETAIL VIEW TAB
56
+ ===================================================================== */
57
+
58
+ let _detailViewInitialized = false;
59
+
60
+ function initDetailViewTab() {
61
+ if (_detailViewInitialized) return;
62
+ _detailViewInitialized = true;
63
+
64
+ const table = document.getElementById('companies-table');
65
+ const meta = window.EXAMPLE_COMPANY_METADATA;
66
+
67
+ table.columns = window.EXAMPLE_COMPANY_COLUMNS;
68
+ table.totalElements = window.EXAMPLE_COMPANIES.length;
69
+ table.pageSize = 20;
70
+ table.locale = 'nl-NL';
71
+
72
+ table.addEventListener('mrdLoadPage', async (e) => {
73
+ await table.setPage(e.detail.page, window.EXAMPLE_COMPANIES);
74
+ });
75
+
76
+ table.addEventListener('mrdRowClick', (e) => {
77
+ showCompanyDetail(e.detail);
78
+ });
79
+
80
+ table.init().then(() => table.setPage(0, window.EXAMPLE_COMPANIES));
81
+ }
82
+
83
+ function showCompanyDetail(row) {
84
+ document.getElementById('detail-table-panel').style.display = 'none';
85
+ document.getElementById('detail-record-panel').style.display = 'block';
86
+
87
+ // Use the static example company (in a real app: fetch /data/:tenant/companies/:id)
88
+ const company = window.EXAMPLE_COMPANY;
89
+ const meta = window.EXAMPLE_COMPANY_METADATA;
90
+ const container = document.getElementById('detail-sections');
91
+ container.innerHTML = '';
92
+
93
+ meta.layouts.forEach((layout, idx) => {
94
+ const wrapper = document.createElement('div');
95
+ wrapper.style.marginBottom = '2rem';
96
+
97
+ if (layout.label) {
98
+ const heading = document.createElement('h3');
99
+ heading.style.cssText = 'font-size:1.1rem;font-weight:600;margin:0 0 .75rem;padding-bottom:.5rem;border-bottom:2px solid #e5e7eb';
100
+ heading.textContent = layout.label;
101
+ wrapper.appendChild(heading);
102
+ }
103
+
104
+ const section = document.createElement('mrd-layout-section');
105
+ section.items = layout.items;
106
+ section.data = company;
107
+ section.views = meta.views;
108
+ section.locale = 'nl-NL';
109
+
110
+ section.addEventListener('mrdNavigate', (e) => {
111
+ logEvent('mrdNavigate', e.detail);
112
+ });
113
+
114
+ section.addEventListener('mrdLoadView', async (e) => {
115
+ const { name, viewConfig } = e.detail;
116
+ const mockRows = name === 'view0' ? window.EXAMPLE_APPOINTMENTS : window.EXAMPLE_SHARE_CLASSES;
117
+ await section.setViewPage(name, 0, mockRows, mockRows.length);
118
+ });
119
+
120
+ section.addEventListener('mrdLoadViewPage', async (e) => {
121
+ // No more pages in the mock — just set the same rows again
122
+ const { name } = e.detail;
123
+ const mockRows = name === 'view0' ? window.EXAMPLE_APPOINTMENTS : window.EXAMPLE_SHARE_CLASSES;
124
+ await section.setViewPage(name, e.detail.page, mockRows);
125
+ });
126
+
127
+ wrapper.appendChild(section);
128
+ container.appendChild(wrapper);
129
+ });
130
+ }
131
+
132
+ function showCompanyList() {
133
+ document.getElementById('detail-record-panel').style.display = 'none';
134
+ document.getElementById('detail-table-panel').style.display = 'block';
50
135
  }
51
136
 
52
137
  /* =====================================================================
@@ -204,6 +289,7 @@ function renderTable(columns, totalElements, pageSize, page0Rows, dataHref, defa
204
289
 
205
290
  // Mutable closure state — reassigned on view switch so subsequent handlers see new values
206
291
  let _activeFilters = [];
292
+ let _pageLinks = {}; // _links from the most recent page 0 response (self, excel, etc.)
207
293
  let viewLabel = initialViewLabel;
208
294
  let alternativeViews = initialAlternativeViews;
209
295
  let viewFilter = initialViewFilter; // static filters from view definition
@@ -639,6 +725,26 @@ document.getElementById('btn-inject-results').addEventListener('click', () => {
639
725
  ===================================================================== */
640
726
 
641
727
  document.addEventListener('DOMContentLoaded', async () => {
728
+ // Initialize Field Types tab (static, no auth needed)
729
+ const ftSection = document.getElementById('field-types-section');
730
+ ftSection.items = window.EXAMPLE_FIELD_TYPES_ITEMS;
731
+ ftSection.data = window.EXAMPLE_FIELD_TYPES_DATA;
732
+ ftSection.locale = 'nl-NL';
733
+
734
+ // Simulate host resolving a signed URL for IMAGE fields (thumbnail on load).
735
+ ftSection.addEventListener('mrdLoadImage', (e) => {
736
+ const staticUrl = 'https://picsum.photos/seed/mosterd/800/600';
737
+ ftSection.setImagePreview(e.detail.fieldName, staticUrl);
738
+ });
739
+
740
+ ftSection.addEventListener('mrdNavigate', (e) => {
741
+ logEvent('mrdNavigate', e.detail);
742
+ });
743
+
744
+ ftSection.addEventListener('mrdDownload', (e) => {
745
+ logEvent('mrdDownload', e.detail);
746
+ });
747
+
642
748
  authRestoreSession();
643
749
  await authHandleCallback();
644
750
 
@@ -254,3 +254,327 @@ window.EXAMPLE_VALUES = {
254
254
  startDate: "2026-01-15",
255
255
  contactEmail: "team@mosterd.nl"
256
256
  };
257
+
258
+ // ─── Companies detail view demo ────────────────────────────────────────────────
259
+
260
+ /** Column definitions for the companies list table. */
261
+ window.EXAMPLE_COMPANY_COLUMNS = [
262
+ { type: 'FIELD', label: 'Company name', field: { name: 'name', dataType: 'TEXT', required: true } },
263
+ { type: 'FIELD', label: 'Reg. number', field: { name: 'registrationNumber', dataType: 'TEXT', required: false } },
264
+ { type: 'FIELD', label: 'Legal form', field: { name: 'legalForm', dataType: 'TEXT', required: false } },
265
+ { type: 'FIELD', label: 'Start date', field: { name: 'startDate', dataType: 'DATE', required: false } },
266
+ { type: 'FIELD', label: 'Employees', field: { name: 'totalEmployees', dataType: 'INTEGER', required: false } },
267
+ ];
268
+
269
+ /** Rows for the companies list table. */
270
+ window.EXAMPLE_COMPANIES = [
271
+ {
272
+ name: 'Alpha Holding B.V.',
273
+ registrationNumber: '10000001',
274
+ legalForm: 'Besloten Vennootschap',
275
+ startDate: '2001-03-01',
276
+ totalEmployees: 1,
277
+ _links: { self: { href: '/data/demo/companies/company-1' } },
278
+ },
279
+ {
280
+ name: 'Beta Services N.V.',
281
+ registrationNumber: '20000002',
282
+ legalForm: 'Naamloze Vennootschap',
283
+ startDate: '2010-07-15',
284
+ totalEmployees: 45,
285
+ _links: { self: { href: '/data/demo/companies/company-2' } },
286
+ },
287
+ {
288
+ name: 'Gamma & Partners B.V.',
289
+ registrationNumber: '30000003',
290
+ legalForm: 'Besloten Vennootschap',
291
+ startDate: '2008-11-01',
292
+ totalEmployees: 12,
293
+ _links: { self: { href: '/data/demo/companies/company-3' } },
294
+ },
295
+ {
296
+ name: 'Delta Vastgoed B.V.',
297
+ registrationNumber: '40000004',
298
+ legalForm: 'Besloten Vennootschap',
299
+ startDate: '2020-06-15',
300
+ totalEmployees: 3,
301
+ _links: { self: { href: '/data/demo/companies/company-4' } },
302
+ },
303
+ ];
304
+
305
+ /** Single company data object (result of GET /data/:tenant/companies/:id). */
306
+ window.EXAMPLE_COMPANY = {
307
+ name: 'Alpha Holding B.V.',
308
+ legalName: 'Alpha Holding B.V.',
309
+ registrationNumber: '10000001',
310
+ registrationDate: null,
311
+ legalForm: 'Besloten Vennootschap',
312
+ legalFormExtended: 'Besloten Vennootschap',
313
+ rsin: '100000001',
314
+ startDate: '2001-03-01',
315
+ endDate: null,
316
+ primaryActivityDescription: 'Activiteiten van houdstermaatschappijen',
317
+ primaryActivityCode: '64100',
318
+ totalEmployees: 1,
319
+ noMailing: false,
320
+ verificationDate: '2024-01-15T09:00:00.000Z',
321
+ visitAddress: 'Voorbeeldstraat 1',
322
+ visitPostalCode: '1234AB',
323
+ visitCity: 'Amsterdam',
324
+ mailingAddress: null,
325
+ mailingPostalCode: null,
326
+ mailingCity: null,
327
+ _links: {
328
+ self: { href: '/data/demo/companies/company-1' },
329
+ country: { href: '/data/demo/countries/nl', name: 'Nederland' },
330
+ appointments: { href: '/data/demo/companies/company-1/appointments' },
331
+ shareClasses: { href: '/data/demo/companies/company-1/shareClasses' },
332
+ shareAllocations: { href: '/data/demo/shareholders/company-1/shareAllocations' },
333
+ },
334
+ };
335
+
336
+ /** Dashboard metadata (GET /metadata/:tenant/dataObject?types=companies,...). */
337
+ window.EXAMPLE_COMPANY_METADATA = {
338
+ layoutType: 'OBJECT_DASHBOARD',
339
+ layouts: [
340
+ {
341
+ type: 'OBJECT_DASHBOARD',
342
+ label: '',
343
+ tabPage: false,
344
+ items: [
345
+ { type: 'FIELD', label: 'company name', field: { name: 'name', dataType: 'TEXT', required: true, header: true } },
346
+ { type: 'FIELD', label: 'legal name', field: { name: 'legalName', dataType: 'TEXT', required: true } },
347
+ { type: 'FIELD', label: 'registration number', field: { name: 'registrationNumber', dataType: 'TEXT', required: false } },
348
+ { type: 'FIELD', label: 'registration date', field: { name: 'registrationDate', dataType: 'DATE', required: false } },
349
+ // ACTION items are silently ignored by mrd-layout-section
350
+ { type: 'ACTION', label: 'company register', name: 'CompanyRegister' },
351
+ ],
352
+ },
353
+ {
354
+ type: 'OBJECT_DASHBOARD',
355
+ label: 'Registration details',
356
+ tabPage: true,
357
+ items: [
358
+ { type: 'FIELD', label: 'legal form', field: { name: 'legalForm', dataType: 'TEXT', required: false } },
359
+ { type: 'FIELD', label: 'legal form extended', field: { name: 'legalFormExtended', dataType: 'TEXT', required: false } },
360
+ { type: 'FIELD', label: 'rsin', field: { name: 'rsin', dataType: 'TEXT', required: false } },
361
+ { type: 'FIELD', label: 'start date', field: { name: 'startDate', dataType: 'DATE', required: false } },
362
+ { type: 'FIELD', label: 'end date', field: { name: 'endDate', dataType: 'DATE', required: false } },
363
+ { type: 'FIELD', label: 'primary activity description', field: { name: 'primaryActivityDescription', dataType: 'TEXT', required: false } },
364
+ { type: 'FIELD', label: 'primary activity code', field: { name: 'primaryActivityCode', dataType: 'TEXT', required: false } },
365
+ { type: 'FIELD', label: 'total employees', field: { name: 'totalEmployees', dataType: 'INTEGER', required: false } },
366
+ { type: 'FIELD', label: 'no mailing', field: { name: 'noMailing', dataType: 'BOOLEAN', required: false } },
367
+ { type: 'FIELD', label: 'verification date', field: { name: 'verificationDate', dataType: 'DATETIME', required: false } },
368
+ ],
369
+ },
370
+ {
371
+ type: 'OBJECT_DASHBOARD',
372
+ label: 'Contact information',
373
+ tabPage: true,
374
+ items: [
375
+ { type: 'FIELD', label: 'visit address', field: { name: 'visitAddress', dataType: 'TEXT', required: false } },
376
+ { type: 'FIELD', label: 'visit postal code', field: { name: 'visitPostalCode', dataType: 'TEXT', required: false } },
377
+ { type: 'FIELD', label: 'visit city', field: { name: 'visitCity', dataType: 'TEXT', required: false } },
378
+ { type: 'FIELD', label: 'mailing address', field: { name: 'mailingAddress', dataType: 'TEXT', required: false } },
379
+ { type: 'FIELD', label: 'mailing postal code', field: { name: 'mailingPostalCode', dataType: 'TEXT', required: false } },
380
+ { type: 'FIELD', label: 'mailing city', field: { name: 'mailingCity', dataType: 'TEXT', required: false } },
381
+ {
382
+ type: 'RELATION',
383
+ label: 'country',
384
+ relation: { name: 'country', label: 'country', relatedClass: 'commons.country', mostSignificantClass: 'countries', required: false },
385
+ },
386
+ ],
387
+ },
388
+ {
389
+ type: 'OBJECT_DASHBOARD',
390
+ label: 'Appointments',
391
+ tabPage: true,
392
+ items: [
393
+ {
394
+ type: 'RELATED_VIEW',
395
+ label: 'Appointments',
396
+ name: 'view0',
397
+ relatedView: { name: 'view0', relatedClass: 'appointments', showTitle: false },
398
+ },
399
+ ],
400
+ },
401
+ {
402
+ type: 'OBJECT_DASHBOARD',
403
+ label: 'Share classes',
404
+ tabPage: true,
405
+ items: [
406
+ {
407
+ type: 'RELATED_VIEW',
408
+ label: 'Share classes',
409
+ name: 'view1',
410
+ relatedView: { name: 'view1', relatedClass: 'shareClasses', showTitle: false },
411
+ },
412
+ ],
413
+ },
414
+ ],
415
+ views: {
416
+ view0: {
417
+ name: null,
418
+ defaultView: true,
419
+ defaultSort: 'name',
420
+ values: [
421
+ { type: 'FIELD', label: 'name', field: { name: 'name', dataType: 'TEXT', required: true } },
422
+ {
423
+ type: 'RELATION', label: 'Officer',
424
+ relation: { name: 'officer', label: 'Officer', relatedClass: 'housekeeping.officer', mostSignificantClass: 'entities', required: true },
425
+ },
426
+ {
427
+ type: 'FIELD', label: 'Authority',
428
+ field: {
429
+ name: 'authority', dataType: 'LIST', required: false,
430
+ listItems: [
431
+ { key: 'SOLE', label: 'Sole authority' },
432
+ { key: 'JOINT', label: 'Joint authority' },
433
+ ],
434
+ },
435
+ },
436
+ { type: 'FIELD', label: 'start date', field: { name: 'activeDate', dataType: 'DATE', required: false } },
437
+ { type: 'FIELD', label: 'end date', field: { name: 'inactiveDate', dataType: 'DATE', required: false } },
438
+ ],
439
+ type: 'appointments',
440
+ singularLabel: 'Appointment',
441
+ pluralLabel: 'Appointments',
442
+ },
443
+ view1: {
444
+ name: null,
445
+ defaultView: true,
446
+ defaultSort: 'name',
447
+ values: [
448
+ { type: 'FIELD', label: 'name', field: { name: 'name', dataType: 'TEXT', required: true } },
449
+ { type: 'FIELD', label: 'Nominal value', field: { name: 'nominalValue', dataType: 'TEXT', required: false } },
450
+ { type: 'FIELD', label: 'Currency', field: { name: 'currency', dataType: 'TEXT', required: false } },
451
+ { type: 'FIELD', label: 'Authorised shares', field: { name: 'authorizedShares', dataType: 'INTEGER', required: false } },
452
+ ],
453
+ type: 'shareClasses',
454
+ singularLabel: 'Share class',
455
+ pluralLabel: 'Share classes',
456
+ },
457
+ },
458
+ _links: {
459
+ view0: { href: '/data/demo/appointments' },
460
+ view1: { href: '/data/demo/shareClasses' },
461
+ },
462
+ };
463
+
464
+ /** Mock appointment rows for the embedded table. */
465
+ window.EXAMPLE_APPOINTMENTS = [
466
+ {
467
+ name: 'Directeur',
468
+ authority: 'SOLE',
469
+ activeDate: '2001-03-01',
470
+ inactiveDate: null,
471
+ _links: {
472
+ self: { href: '/data/demo/appointments/apt-1' },
473
+ officer: { href: '/data/demo/entities/person-1', name: 'A. Jansen' },
474
+ },
475
+ },
476
+ {
477
+ name: 'Commissaris',
478
+ authority: 'JOINT',
479
+ activeDate: '2018-03-01',
480
+ inactiveDate: null,
481
+ _links: {
482
+ self: { href: '/data/demo/appointments/apt-2' },
483
+ officer: { href: '/data/demo/entities/person-2', name: 'B. de Wit' },
484
+ },
485
+ },
486
+ ];
487
+
488
+ /** Mock share class rows for the embedded table. */
489
+ window.EXAMPLE_SHARE_CLASSES = [
490
+ {
491
+ name: 'Gewone aandelen',
492
+ nominalValue: '1.00',
493
+ currency: 'EUR',
494
+ authorizedShares: 1800,
495
+ _links: { self: { href: '/data/demo/shareClasses/sc-1' } },
496
+ },
497
+ {
498
+ name: 'Preferente aandelen',
499
+ nominalValue: '1.00',
500
+ currency: 'EUR',
501
+ authorizedShares: 200,
502
+ _links: { self: { href: '/data/demo/shareClasses/sc-2' } },
503
+ },
504
+ ];
505
+
506
+ // ─── Field Types demo ─────────────────────────────────────────────────────────
507
+
508
+ window.EXAMPLE_FIELD_TYPES_ITEMS = [
509
+ { type: 'FIELD', label: 'Name (header)', field: { name: 'fHeader', dataType: 'TEXT', required: false, header: true } },
510
+ { type: 'FIELD', label: 'Text', field: { name: 'fText', dataType: 'TEXT', required: false } },
511
+ { type: 'FIELD', label: 'Email', field: { name: 'fEmail', dataType: 'EMAIL', required: false } },
512
+ { type: 'FIELD', label: 'Hyperlink', field: { name: 'fHyperlink', dataType: 'HYPERLINK', required: false } },
513
+ { type: 'FIELD', label: 'Integer', field: { name: 'fInteger', dataType: 'INTEGER', required: false } },
514
+ { type: 'FIELD', label: 'Decimal', field: { name: 'fDecimal', dataType: 'DECIMAL', required: false } },
515
+ { type: 'FIELD', label: 'Percentage', field: { name: 'fPercentage', dataType: 'PERCENTAGE', required: false } },
516
+ { type: 'FIELD', label: 'Currency', field: { name: 'fCurrency', dataType: 'CURRENCY', required: false } },
517
+ { type: 'FIELD', label: 'Boolean (true)', field: { name: 'fBoolTrue', dataType: 'BOOLEAN', required: false } },
518
+ { type: 'FIELD', label: 'Boolean (false)', field: { name: 'fBoolFalse', dataType: 'BOOLEAN', required: false } },
519
+ { type: 'FIELD', label: 'Date', field: { name: 'fDate', dataType: 'DATE', required: false } },
520
+ { type: 'FIELD', label: 'DateTime', field: { name: 'fDatetime', dataType: 'DATETIME', required: false } },
521
+ { type: 'FIELD', label: 'Time', field: { name: 'fTime', dataType: 'TIME', required: false } },
522
+ {
523
+ type: 'FIELD', label: 'List (no color)', field: {
524
+ name: 'fListPlain', dataType: 'LIST', required: false,
525
+ listItems: [{ key: 'a', label: 'Option A' }, { key: 'b', label: 'Option B' }],
526
+ },
527
+ },
528
+ {
529
+ type: 'FIELD', label: 'List (color only)', field: {
530
+ name: 'fListColor', dataType: 'LIST', required: false,
531
+ listItems: [
532
+ { key: 'active', label: 'Active', color: '#16a34a' },
533
+ { key: 'inactive', label: 'Inactive', color: '#dc2626' },
534
+ ],
535
+ },
536
+ },
537
+ {
538
+ type: 'FIELD', label: 'List (color + bg)', field: {
539
+ name: 'fListBadge', dataType: 'LIST', required: false,
540
+ listItems: [
541
+ { key: 'approved', label: 'Approved', color: '#14532d', backgroundColor: '#dcfce7' },
542
+ { key: 'rejected', label: 'Rejected', color: '#7f1d1d', backgroundColor: '#fee2e2' },
543
+ { key: 'pending', label: 'Pending', color: '#78350f', backgroundColor: '#fef3c7' },
544
+ ],
545
+ },
546
+ },
547
+ { type: 'RELATION', label: 'Relation (single)', relation: { name: 'fRelation', label: 'Relation', relatedClass: 'demo.entity', required: false } },
548
+ { type: 'FIELD', label: 'File', field: { name: 'fFile', dataType: 'FILE', required: false } },
549
+ { type: 'FIELD', label: 'Image', field: { name: 'fImage', dataType: 'IMAGE', required: false } },
550
+ { type: 'FIELD', label: 'Textblock', field: { name: 'fTextblock', dataType: 'TEXTBLOCK', required: false } },
551
+ { type: 'FIELD', label: 'Longtext', field: { name: 'fLongtext', dataType: 'LONGTEXT', required: false } },
552
+ { type: 'FIELD', label: 'JSON', field: { name: 'fJson', dataType: 'JSON', required: false } },
553
+ ];
554
+
555
+ window.EXAMPLE_FIELD_TYPES_DATA = {
556
+ fHeader: 'Voorbeeld Bedrijf B.V.',
557
+ fText: 'Voorbeeld tekst',
558
+ fEmail: 'info@example.com',
559
+ fHyperlink: { href: 'https://example.com', text: 'example.com' },
560
+ fInteger: 42,
561
+ fDecimal: 3.14159,
562
+ fPercentage: 0.7825,
563
+ fCurrency: { amount: 1250.50, currency: 'EUR' },
564
+ fBoolTrue: true,
565
+ fBoolFalse: false,
566
+ fDate: '2024-03-15',
567
+ fDatetime: '2024-03-15T14:30:00.000Z',
568
+ fTime: '14:30:00',
569
+ fListPlain: 'a',
570
+ fListColor: 'active',
571
+ fListBadge: 'approved',
572
+ fFile: { href: '/api/files/example-document', fileName: 'voorbeeld-document.pdf' },
573
+ fImage: { href: '/api/files/example-photo', fileName: 'voorbeeld-foto.jpg' },
574
+ fTextblock: '<p>Dit is een <strong>opgemaakte</strong> tekst met <em>html</em>-inhoud. Geschikt voor langere beschrijvingen met opmaak.</p>',
575
+ fLongtext: 'Dit is een langere stuk platte tekst zonder opmaak.\nHet kan meerdere regels bevatten en wordt getoond in een pre-blok zodat witruimte bewaard blijft.',
576
+ fJson: { id: 1, status: 'active', tags: ['alpha', 'beta'], meta: { created: '2024-01-01', valid: true } },
577
+ _links: {
578
+ fRelation: { href: '/data/demo/entities/person-1', name: 'A. Jansen' },
579
+ },
580
+ };
@@ -45,6 +45,8 @@ export var ClientLayoutItemFieldDataType;
45
45
  ClientLayoutItemFieldDataType["LIST"] = "LIST";
46
46
  ClientLayoutItemFieldDataType["FILE"] = "FILE";
47
47
  ClientLayoutItemFieldDataType["IMAGE"] = "IMAGE";
48
+ ClientLayoutItemFieldDataType["LONGTEXT"] = "LONGTEXT";
49
+ ClientLayoutItemFieldDataType["JSON"] = "JSON";
48
50
  })(ClientLayoutItemFieldDataType || (ClientLayoutItemFieldDataType = {}));
49
51
  // ─── Relation display / edit behaviour ───────────────────────────────────────
50
52
  export var ClientLayoutItemRelationDisplayType;