@mmlogic/components 0.4.0 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/dist/cjs/index.cjs.js +8 -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_23.cjs.entry.js → mrd-boolean-field_24.cjs.entry.js} +348 -39
  5. package/dist/collection/collection-manifest.json +1 -0
  6. package/dist/collection/components/documents/mrd-document-container/mrd-document-container.css +14 -1
  7. package/dist/collection/components/documents/mrd-document-container/mrd-document-container.js +22 -7
  8. package/dist/collection/components/documents/mrd-document-list/mrd-document-list.css +8 -5
  9. package/dist/collection/components/documents/mrd-document-list/tree-view.js +13 -1
  10. package/dist/collection/components/documents/mrd-document-view/mrd-document-view.css +39 -29
  11. package/dist/collection/components/documents/mrd-document-view/mrd-document-view.js +109 -8
  12. package/dist/collection/components/documents/mrd-file-preview/file-preview-kind.js +30 -0
  13. package/dist/collection/components/documents/mrd-file-preview/mrd-file-preview.css +59 -0
  14. package/dist/collection/components/documents/mrd-file-preview/mrd-file-preview.js +212 -0
  15. package/dist/collection/components/fields/mrd-boolean-field/mrd-boolean-field.js +1 -1
  16. package/dist/collection/components/fields/mrd-currency-field/mrd-currency-field.js +1 -1
  17. package/dist/collection/components/fields/mrd-date-field/mrd-date-field.js +1 -1
  18. package/dist/collection/components/fields/mrd-datetime-field/mrd-datetime-field.js +1 -1
  19. package/dist/collection/components/fields/mrd-email-field/mrd-email-field.js +1 -1
  20. package/dist/collection/components/fields/mrd-file-field/mrd-file-field.js +1 -1
  21. package/dist/collection/components/fields/mrd-image-field/mrd-image-field.js +1 -1
  22. package/dist/collection/components/fields/mrd-longtext-field/mrd-longtext-field.js +1 -1
  23. package/dist/collection/components/fields/mrd-number-field/mrd-number-field.js +1 -1
  24. package/dist/collection/components/fields/mrd-secret-field/mrd-secret-field.js +2 -2
  25. package/dist/collection/components/fields/mrd-text-field/mrd-text-field.js +1 -1
  26. package/dist/collection/components/fields/mrd-textarea-field/mrd-textarea-field.js +1 -1
  27. package/dist/collection/components/fields/mrd-time-field/mrd-time-field.js +1 -1
  28. package/dist/collection/components/layout/mrd-layout-section/mrd-layout-section.css +5 -0
  29. package/dist/collection/components/layout/mrd-layout-section/mrd-layout-section.js +91 -8
  30. package/dist/collection/components/table/mrd-table/mrd-table.css +20 -0
  31. package/dist/collection/components/table/mrd-table/mrd-table.js +124 -5
  32. package/dist/collection/components/table/mrd-table/virtual-scroll.js +14 -0
  33. package/dist/collection/dev/api.js +9 -0
  34. package/dist/collection/dev/app.js +101 -3
  35. package/dist/collection/dev/example-data.js +24 -0
  36. package/dist/collection/types/client-layout.js +8 -0
  37. package/dist/collection/utils/i18n.js +40 -0
  38. package/dist/components/client-layout.js +1 -1
  39. package/dist/components/i18n.js +1 -1
  40. package/dist/components/index.js +1 -1
  41. package/dist/components/mrd-boolean-field2.js +1 -1
  42. package/dist/components/mrd-currency-field2.js +1 -1
  43. package/dist/components/mrd-date-field2.js +1 -1
  44. package/dist/components/mrd-datetime-field2.js +1 -1
  45. package/dist/components/mrd-document-container2.js +1 -1
  46. package/dist/components/mrd-document-list2.js +1 -1
  47. package/dist/components/mrd-document-view2.js +1 -1
  48. package/dist/components/mrd-email-field2.js +1 -1
  49. package/dist/components/mrd-file-field2.js +1 -1
  50. package/dist/components/mrd-file-preview.d.ts +11 -0
  51. package/dist/components/mrd-file-preview.js +1 -0
  52. package/dist/components/mrd-file-preview2.js +1 -0
  53. package/dist/components/mrd-image-field2.js +1 -1
  54. package/dist/components/mrd-layout-section.js +1 -1
  55. package/dist/components/mrd-longtext-field2.js +1 -1
  56. package/dist/components/mrd-number-field2.js +1 -1
  57. package/dist/components/mrd-secret-field2.js +1 -1
  58. package/dist/components/mrd-table2.js +1 -1
  59. package/dist/components/mrd-text-field2.js +1 -1
  60. package/dist/components/mrd-textarea-field2.js +1 -1
  61. package/dist/components/mrd-time-field2.js +1 -1
  62. package/dist/esm/index.js +9 -1
  63. package/dist/esm/loader.js +1 -1
  64. package/dist/esm/mosterdcomponents.js +1 -1
  65. package/dist/esm/{mrd-boolean-field_23.entry.js → mrd-boolean-field_24.entry.js} +349 -41
  66. package/dist/mosterdcomponents/cell-renderer-PbcEU4ph.js.map +1 -0
  67. package/dist/mosterdcomponents/client-layout-DCOAAH92.js.map +1 -0
  68. package/dist/mosterdcomponents/field-helpers-CASS3akr.js.map +1 -0
  69. package/dist/mosterdcomponents/file-upload-common-D1ndi6cN.js.map +1 -0
  70. package/dist/mosterdcomponents/format-BAfsQfy1.js.map +1 -0
  71. package/dist/mosterdcomponents/i18n-B1IIkRF9.js.map +1 -0
  72. package/dist/mosterdcomponents/index-AokUFVX0.js.map +1 -0
  73. package/dist/mosterdcomponents/index-CprJSlzK.js.map +1 -0
  74. package/dist/mosterdcomponents/index.esm.js +1 -1
  75. package/dist/mosterdcomponents/index.esm.js.map +1 -0
  76. package/dist/mosterdcomponents/mosterdcomponents.esm.js +1 -1
  77. package/dist/mosterdcomponents/mosterdcomponents.esm.js.map +1 -0
  78. package/dist/mosterdcomponents/p-aae8a9dc.entry.js +1 -0
  79. package/dist/mosterdcomponents/query-params-B1HfnMOl.js.map +1 -0
  80. package/dist/mosterdcomponents/quill-C9pgw_k-.js.map +1 -0
  81. package/dist/mosterdcomponents/validation-DbEViuiw.js.map +1 -0
  82. package/dist/types/components/documents/mrd-document-container/mrd-document-container.d.ts +6 -2
  83. package/dist/types/components/documents/mrd-document-view/mrd-document-view.d.ts +29 -1
  84. package/dist/types/components/documents/mrd-file-preview/file-preview-kind.d.ts +9 -0
  85. package/dist/types/components/documents/mrd-file-preview/mrd-file-preview.d.ts +41 -0
  86. package/dist/types/components/layout/mrd-layout-section/mrd-layout-section.d.ts +17 -1
  87. package/dist/types/components/table/mrd-table/mrd-table.d.ts +23 -0
  88. package/dist/types/components/table/mrd-table/virtual-scroll.d.ts +7 -0
  89. package/dist/types/components.d.ts +194 -7
  90. package/dist/types/types/client-layout.d.ts +10 -0
  91. package/package.json +1 -1
  92. package/dist/mosterdcomponents/p-9b3c0917.entry.js +0 -1
@@ -3,6 +3,7 @@
3
3
  "components/documents/mrd-document-container/mrd-document-container.js",
4
4
  "components/documents/mrd-document-list/mrd-document-list.js",
5
5
  "components/documents/mrd-document-view/mrd-document-view.js",
6
+ "components/documents/mrd-file-preview/mrd-file-preview.js",
6
7
  "components/fields/mrd-boolean-field/mrd-boolean-field.js",
7
8
  "components/fields/mrd-currency-field/mrd-currency-field.js",
8
9
  "components/fields/mrd-date-field/mrd-date-field.js",
@@ -7,10 +7,23 @@
7
7
  align-items: center;
8
8
  justify-content: flex-end;
9
9
  gap: var(--mrd-space-2);
10
- padding: var(--mrd-space-2) var(--mrd-space-4);
10
+ /* Horizontal padding intentionally 0: mrd-table__toolbar is flush with its
11
+ card body too, so the two sibling views' buttons line up on the same
12
+ left/right edge as their bodies below. */
13
+ padding: var(--mrd-space-2) 0;
11
14
  border-bottom: 1px solid var(--mrd-color-neutral-100);
12
15
  }
13
16
 
17
+ /* Card frame matching .mrd-table__scroll, so the tree view reads as an inset
18
+ panel like the sibling table view instead of bleeding to the tab edges.
19
+ The list mode doesn't need this: the embedded mrd-table already draws its
20
+ own .mrd-table__scroll frame. */
21
+ .mrd-document-container__body--tree {
22
+ border: 1px solid var(--mrd-border-color);
23
+ border-radius: var(--mrd-border-radius);
24
+ overflow: hidden;
25
+ }
26
+
14
27
  .mrd-document-container__action {
15
28
  display: inline-flex;
16
29
  align-items: center;
@@ -1,4 +1,5 @@
1
1
  import { Host, h } from "@stencil/core";
2
+ import { ClientLayoutNavigateBehaviour } from "../../../types/client-layout";
2
3
  import { t } from "../../../utils/i18n";
3
4
  /**
4
5
  * Owns a document collection view: a single toolbar (view switch + collection
@@ -72,7 +73,7 @@ export class MrdDocumentContainer {
72
73
  this.mrdLoadPage.emit(e.detail);
73
74
  }, onMrdNavigate: (e) => {
74
75
  e.stopPropagation();
75
- this.mrdNavigate.emit(e.detail);
76
+ this.mrdNavigate.emit(Object.assign(Object.assign({}, e.detail), { navigateBehaviour: ClientLayoutNavigateBehaviour.OVERLAY_PAGE }));
76
77
  }, onMrdCanCreate: (e) => {
77
78
  e.stopPropagation();
78
79
  this.canCreate = e.detail;
@@ -98,7 +99,11 @@ export class MrdDocumentContainer {
98
99
  var _a, _b, _c;
99
100
  e.stopPropagation();
100
101
  const row = e.detail;
101
- this.mrdNavigate.emit({ href: (_b = (_a = row === null || row === void 0 ? void 0 : row._links) === null || _a === void 0 ? void 0 : _a.self) === null || _b === void 0 ? void 0 : _b.href, label: (_c = row === null || row === void 0 ? void 0 : row.name) !== null && _c !== void 0 ? _c : '' });
102
+ this.mrdNavigate.emit({
103
+ href: (_b = (_a = row === null || row === void 0 ? void 0 : row._links) === null || _a === void 0 ? void 0 : _a.self) === null || _b === void 0 ? void 0 : _b.href,
104
+ label: (_c = row === null || row === void 0 ? void 0 : row.name) !== null && _c !== void 0 ? _c : '',
105
+ navigateBehaviour: ClientLayoutNavigateBehaviour.OVERLAY_PAGE,
106
+ });
102
107
  }, onMrdAction: (e) => {
103
108
  e.stopPropagation();
104
109
  this.mrdAction.emit(e.detail);
@@ -106,7 +111,10 @@ export class MrdDocumentContainer {
106
111
  }
107
112
  render() {
108
113
  const treeMode = this.mode === 'tree';
109
- return (h(Host, { key: '018c9eacd169fdf7d352a51472651348af2b45df' }, this.renderToolbar(), h("div", { key: '5ff2ac77d4d12369905696ff075f99785dc1c809', class: "mrd-document-container__body", style: treeMode ? { height: `${this.height}px`, overflowY: 'auto' } : {} }, treeMode ? this.renderTree() : this.renderList())));
114
+ return (h(Host, { key: 'e8d37d2f818b9b2260fb2ccd3d784393a28385c4' }, this.renderToolbar(), h("div", { key: 'fa80a1343e9b2f1e74f30317bfbfbab78ea63e5d', class: {
115
+ 'mrd-document-container__body': true,
116
+ 'mrd-document-container__body--tree': treeMode,
117
+ }, style: treeMode ? { height: `${this.height}px`, overflowY: 'auto' } : {} }, treeMode ? this.renderTree() : this.renderList())));
110
118
  }
111
119
  static get is() { return "mrd-document-container"; }
112
120
  static get encapsulation() { return "scoped"; }
@@ -333,12 +341,19 @@ export class MrdDocumentContainer {
333
341
  "composed": true,
334
342
  "docs": {
335
343
  "tags": [],
336
- "text": "A row / document was activated. Host routes to the object page."
344
+ "text": "A row / document was activated. Host routes to the object page. Documents\nare always the \"quick look\" use case, so navigateBehaviour is hardcoded\nto OVERLAY_PAGE here \u2014 unlike the generic VIEW/RELATED_VIEW table path in\nmrd-layout-section, which takes it from the item's configuration."
337
345
  },
338
346
  "complexType": {
339
- "original": "{ href?: string; label: string }",
340
- "resolved": "{ href?: string | undefined; label: string; }",
341
- "references": {}
347
+ "original": "{ href?: string; label: string; navigateBehaviour?: ClientLayoutNavigateBehaviour }",
348
+ "resolved": "{ href?: string | undefined; label: string; navigateBehaviour?: ClientLayoutNavigateBehaviour | undefined; }",
349
+ "references": {
350
+ "ClientLayoutNavigateBehaviour": {
351
+ "location": "import",
352
+ "path": "../../../types/client-layout",
353
+ "id": "src/types/client-layout.ts::ClientLayoutNavigateBehaviour",
354
+ "referenceLocation": "ClientLayoutNavigateBehaviour"
355
+ }
356
+ }
342
357
  }
343
358
  }, {
344
359
  "method": "mrdAction",
@@ -21,9 +21,12 @@
21
21
  .mrd-document-list__row {
22
22
  display: flex;
23
23
  align-items: center;
24
- gap: var(--mrd-space-2);
24
+ gap: var(--mrd-space-1);
25
25
  height: 2.125rem;
26
- padding: 0 var(--mrd-space-4);
26
+ /* Asymmetric: a narrow left gutter (Finder-style — the disclosure triangle
27
+ sits in the margin, not indented behind a full column) vs. a roomier
28
+ right side for the count/date columns. */
29
+ padding: 0 var(--mrd-space-4) 0 var(--mrd-space-2);
27
30
  cursor: pointer;
28
31
  user-select: none;
29
32
  font-size: var(--mrd-font-size-sm);
@@ -98,7 +101,7 @@
98
101
  }
99
102
 
100
103
  .mrd-document-list__chev {
101
- width: 1rem;
104
+ width: 0.875rem;
102
105
  height: 1rem;
103
106
  flex: none;
104
107
  display: grid;
@@ -165,9 +168,9 @@
165
168
  .mrd-document-list__loading {
166
169
  display: flex;
167
170
  align-items: center;
168
- gap: var(--mrd-space-2);
171
+ gap: var(--mrd-space-1);
169
172
  height: 1.875rem;
170
- padding: 0 var(--mrd-space-4);
173
+ padding: 0 var(--mrd-space-4) 0 var(--mrd-space-2);
171
174
  font-size: var(--mrd-font-size-xs);
172
175
  color: var(--mrd-color-neutral-400);
173
176
  }
@@ -13,9 +13,21 @@ function folderIcon() {
13
13
  function docIcon() {
14
14
  return (h("svg", { class: "mrd-document-list__icon mrd-document-list__icon--doc", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true" }, h("path", { d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" }), h("path", { d: "M14 2v6h6" })));
15
15
  }
16
+ /** Depth 0 renders no indent spacer at all — like Finder, the top-level
17
+ * disclosure triangle sits in the row's own left margin instead of adding
18
+ * an extra indent column before it. Nested levels still step in 18px each. */
16
19
  function indent(depth) {
20
+ if (depth === 0)
21
+ return null;
17
22
  return h("span", { class: "mrd-document-list__indent", style: { width: `${depth * 18}px` } });
18
23
  }
24
+ /** Pixel offset from the row's left edge to where the label text starts,
25
+ * used to align the inline folder-name-error text under the label above it.
26
+ * Mirrors the row's padding + (indent) + chevron + icon + gaps in tree-view.scss. */
27
+ function labelOffset(depth) {
28
+ const chevronAndIcon = 4 /* gap */ + 14 /* chevron */ + 4 /* gap */ + 17 /* icon */ + 4 /* gap */;
29
+ return depth === 0 ? 8 + chevronAndIcon : 8 + depth * 18 + chevronAndIcon;
30
+ }
19
31
  export function renderLoadingRow(ctx, depth) {
20
32
  return (h("div", { class: "mrd-document-list__loading", key: "loading" }, indent(depth), h("span", { class: "mrd-document-list__spinner" }), t('loading', ctx.locale)));
21
33
  }
@@ -30,7 +42,7 @@ function renderDocRow(ctx, doc, node, container, depth) {
30
42
  return (h("div", { class: "mrd-document-list__row mrd-document-list__row--doc", key: (_e = (_d = (_c = doc === null || doc === void 0 ? void 0 : doc._links) === null || _c === void 0 ? void 0 : _c.self) === null || _d === void 0 ? void 0 : _d.href) !== null && _e !== void 0 ? _e : label, draggable: true, onClick: () => ctx.on.openDoc(doc), onDragStart: (e) => ctx.on.docDragStart(e, doc, node), onDragEnd: ctx.on.docDragEnd, onDragOver: (e) => ctx.on.nodeDragOver(e, node), onDragLeave: () => ctx.on.nodeDragLeave(node), onDrop: (e) => ctx.on.nodeDrop(e, node, container) }, indent(depth), chevron(false, true), docIcon(), h("span", { class: "mrd-document-list__label" }, label), date && h("span", { class: "mrd-document-list__date" }, date)));
31
43
  }
32
44
  function renderFolderEditRow(ctx, node, depth) {
33
- return (h("div", { class: "mrd-document-list__node", key: node.id }, h("div", { class: "mrd-document-list__row mrd-document-list__row--edit" }, indent(depth), chevron(false, true), folderIcon(), h("input", { class: "mrd-document-list__name-input", type: "text", placeholder: t('folder_name_placeholder', ctx.locale), ref: el => ctx.on.setPendingInputEl(el), onInput: ctx.on.pendingInput, onKeyDown: ctx.on.pendingKeyDown, onBlur: () => ctx.on.cancelFolder() })), node.error && (h("div", { class: "mrd-document-list__error", style: { paddingLeft: `${depth * 18 + 52}px` } }, node.error))));
45
+ return (h("div", { class: "mrd-document-list__node", key: node.id }, h("div", { class: "mrd-document-list__row mrd-document-list__row--edit" }, indent(depth), chevron(false, true), folderIcon(), h("input", { class: "mrd-document-list__name-input", type: "text", placeholder: t('folder_name_placeholder', ctx.locale), ref: el => ctx.on.setPendingInputEl(el), onInput: ctx.on.pendingInput, onKeyDown: ctx.on.pendingKeyDown, onBlur: () => ctx.on.cancelFolder() })), node.error && (h("div", { class: "mrd-document-list__error", style: { paddingLeft: `${labelOffset(depth)}px` } }, node.error))));
34
46
  }
35
47
  export function renderNodeChildren(ctx, container, node, depth) {
36
48
  const out = [];
@@ -1,11 +1,28 @@
1
1
  :host {
2
2
  display: block;
3
+ container-type: inline-size;
4
+ container-name: mrd-document-view;
3
5
  }
4
6
 
5
7
  .mrd-document-view {
6
- max-width: 720px;
8
+ max-width: 1200px;
7
9
  }
8
10
 
11
+ .mrd-document-view__body {
12
+ display: grid;
13
+ grid-template-columns: 1fr;
14
+ gap: var(--mrd-space-6);
15
+ }
16
+
17
+ @container mrd-document-view (min-width: 640px) {
18
+ .mrd-document-view__body {
19
+ grid-template-columns: minmax(220px, 320px) 1fr !important;
20
+ align-items: stretch !important;
21
+ }
22
+ .mrd-document-view__crumb {
23
+ margin-bottom: var(--mrd-space-2) !important;
24
+ }
25
+ }
9
26
  .mrd-document-view__crumb {
10
27
  display: inline-flex;
11
28
  align-items: center;
@@ -46,47 +63,40 @@
46
63
  margin: 0 0 var(--mrd-space-6) 0;
47
64
  }
48
65
 
49
- .mrd-document-view__file {
66
+ .mrd-document-view__file-meta {
50
67
  display: flex;
51
68
  align-items: center;
52
69
  gap: var(--mrd-space-4);
53
- padding: var(--mrd-space-4);
54
- border: 1px solid var(--mrd-border-color);
55
- border-radius: var(--mrd-border-radius-md);
56
- background: var(--mrd-color-neutral-50);
57
- margin-bottom: var(--mrd-space-6);
58
- }
59
-
60
- .mrd-document-view__file-preview {
61
- width: 2.875rem;
62
- height: 3.625rem;
63
- flex-shrink: 0;
64
- display: grid;
65
- place-items: center;
66
- border-radius: var(--mrd-border-radius-sm);
67
- background: var(--mrd-color-white);
68
- border: 1px solid var(--mrd-border-color);
69
- color: var(--mrd-color-danger);
70
- box-shadow: var(--mrd-shadow-sm);
71
- }
72
-
73
- .mrd-document-view__file-preview svg {
74
- width: 1.5rem;
75
- height: 1.5rem;
70
+ margin-bottom: var(--mrd-space-2);
76
71
  }
77
72
 
78
- .mrd-document-view__file-meta {
73
+ .mrd-document-view__file-name {
79
74
  flex: 1;
80
75
  min-width: 0;
81
- }
82
-
83
- .mrd-document-view__file-name {
84
76
  font-weight: var(--mrd-font-weight-semibold);
85
77
  font-size: var(--mrd-font-size-sm);
86
78
  color: var(--mrd-color-neutral-800);
87
79
  word-break: break-word;
88
80
  }
89
81
 
82
+ .mrd-document-view__preview {
83
+ width: 100%;
84
+ overflow: hidden;
85
+ border: 1px solid var(--mrd-border-color);
86
+ border-radius: var(--mrd-border-radius-md);
87
+ background: var(--mrd-color-neutral-50);
88
+ }
89
+
90
+ .mrd-document-view__preview-loading {
91
+ display: flex;
92
+ align-items: center;
93
+ justify-content: center;
94
+ width: 100%;
95
+ height: 100%;
96
+ color: var(--mrd-color-neutral-500);
97
+ font-size: var(--mrd-font-size-sm);
98
+ }
99
+
90
100
  .mrd-document-view__download {
91
101
  display: inline-flex;
92
102
  align-items: center;
@@ -16,6 +16,32 @@ export class MrdDocumentView {
16
16
  /** The data record from the /data endpoint. Fields keyed by name; relations under `_links`. */
17
17
  this.data = {};
18
18
  this.locale = navigator.language;
19
+ /** Signed URL for the file's inline preview (mrd-file-preview), resolved by
20
+ * the host in response to mrdLoadPreview. Empty while unresolved — the
21
+ * preview area shows a loading state until the host injects a value. */
22
+ this.previewUrl = '';
23
+ /** Minimum height (px) of the inline preview area — it stretches to fill the
24
+ * available row height in the wide side-by-side layout (see mrd-document-view.scss),
25
+ * and falls back to this height when stacked. Overlay hosts typically pass a
26
+ * smaller value than the full-page default to fit available chrome. */
27
+ this.previewHeight = 640;
28
+ /** Tracks the href a preview was last requested for, so componentDidLoad and
29
+ * the data watcher don't emit duplicate requests for the same file. */
30
+ this.lastPreviewHref = null;
31
+ }
32
+ componentDidLoad() {
33
+ this.requestPreviewIfNeeded();
34
+ }
35
+ dataChanged() {
36
+ this.requestPreviewIfNeeded();
37
+ }
38
+ requestPreviewIfNeeded() {
39
+ var _a, _b;
40
+ const href = (_b = (_a = this.slotValue('file')) === null || _a === void 0 ? void 0 : _a.href) !== null && _b !== void 0 ? _b : null;
41
+ if (href && href !== this.lastPreviewHref) {
42
+ this.lastPreviewHref = href;
43
+ this.mrdLoadPreview.emit({ href });
44
+ }
19
45
  }
20
46
  /** Value of a value-slot: reads data[field] via the slot→field map. */
21
47
  slotValue(slot) {
@@ -40,23 +66,37 @@ export class MrdDocumentView {
40
66
  return null;
41
67
  return (h("button", { class: "mrd-document-view__crumb", onClick: () => this.mrdNavigate.emit({ href: link.href, label: link.name }) }, h("svg", { class: "mrd-document-view__crumb-icon", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true" }, h("path", { d: "M19 12H5M12 19l-7-7 7-7" })), link.name));
42
68
  }
43
- renderFile() {
44
- var _a, _b;
45
- const raw = this.slotValue('file');
46
- if (!raw)
69
+ /** Filename + download button — part of the metadata column, not the preview
70
+ * panel, so it stays next to the title/summary in the wide side-by-side layout. */
71
+ renderFileMeta(fileName, href) {
72
+ return (h("div", { class: "mrd-document-view__file-meta" }, h("span", { class: "mrd-document-view__file-name" }, fileName), href && (h("button", { type: "button", class: "mrd-document-view__download", onClick: () => this.mrdDownload.emit({ href, fileName }) }, h("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true" }, h("path", { d: "M12 3v12m0 0l-4-4m4 4l4-4M4 21h16" })), t('download', this.locale)))));
73
+ }
74
+ /** The preview panel itself (iframe/image/loading state) — its own grid
75
+ * column in the wide layout, so it can take the full available height. */
76
+ renderPreviewPanel(fileName, href, mimeType) {
77
+ if (!href)
47
78
  return null;
48
- const href = (_a = raw === null || raw === void 0 ? void 0 : raw.href) !== null && _a !== void 0 ? _a : '';
49
- const fileName = (_b = raw === null || raw === void 0 ? void 0 : raw.fileName) !== null && _b !== void 0 ? _b : String(raw);
50
- return (h("div", { class: "mrd-document-view__file" }, h("div", { class: "mrd-document-view__file-preview", "aria-hidden": "true" }, h("svg", { viewBox: "0 0 24 24", fill: "currentColor" }, h("path", { d: "M6 2a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8l-6-6H6zm7 1.5L18.5 9H13V3.5z" }))), h("div", { class: "mrd-document-view__file-meta" }, h("span", { class: "mrd-document-view__file-name" }, fileName)), href && (h("button", { class: "mrd-document-view__download", onClick: () => this.mrdDownload.emit({ href, fileName }) }, h("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true" }, h("path", { d: "M12 3v12m0 0l-4-4m4 4l4-4M4 21h16" })), t('download', this.locale)))));
79
+ return (h("div", { class: "mrd-document-view__preview", style: { minHeight: `${this.previewHeight}px` } }, this.previewUrl ? (h("mrd-file-preview", { src: this.previewUrl, mimeType: mimeType, fileName: fileName, href: href, locale: this.locale, onMrdDownload: (e) => {
80
+ e.stopPropagation();
81
+ this.mrdDownload.emit(e.detail);
82
+ }, onMrdPreviewReload: (e) => {
83
+ e.stopPropagation();
84
+ this.mrdLoadPreview.emit({ href });
85
+ } })) : (h("div", { class: "mrd-document-view__preview-loading" }, t('loading', this.locale)))));
51
86
  }
52
87
  render() {
88
+ var _a, _b, _c, _d;
53
89
  const titleRaw = this.slotValue('title');
54
90
  const title = titleRaw != null && titleRaw !== '' ? String(titleRaw) : null;
55
91
  const dateRaw = this.slotValue('date');
56
92
  const date = dateRaw != null && dateRaw !== '' ? formatDate(dateRaw, this.locale) : null;
57
93
  const summaryRaw = this.slotValue('summary');
58
94
  const summary = summaryRaw != null && summaryRaw !== '' ? String(summaryRaw) : null;
59
- return (h(Host, { key: '0c64c72eaf5e1eefc2ca9aebb2543081eb1eae32' }, h("div", { key: '70cce81b089fee81a4c163a6ba5fbcc8cb46131c', class: "mrd-document-view" }, this.renderContainer(), title && h("h1", { key: '50f85b32a6740d6cf57fdf1bb0bcc597b5e97b31', class: "mrd-document-view__title" }, title), date && h("p", { key: 'b3c8fb3b4382104858d49ef9a1597034665122bb', class: "mrd-document-view__date" }, date), this.renderFile(), summary && (h("div", { key: '2046c1eca7601549115de0c95ff778abaf5faf46', class: "mrd-document-view__summary" }, h("div", { key: '875863d3e438eee3d4df10473a93f09370fde08c', class: "mrd-document-view__summary-body", innerHTML: summary }))))));
95
+ const fileRaw = this.slotValue('file');
96
+ const fileHref = (_a = fileRaw === null || fileRaw === void 0 ? void 0 : fileRaw.href) !== null && _a !== void 0 ? _a : '';
97
+ const fileName = (_b = fileRaw === null || fileRaw === void 0 ? void 0 : fileRaw.fileName) !== null && _b !== void 0 ? _b : (fileRaw ? String(fileRaw) : '');
98
+ const mimeType = (_d = (_c = fileRaw === null || fileRaw === void 0 ? void 0 : fileRaw.mimeType) !== null && _c !== void 0 ? _c : fileRaw === null || fileRaw === void 0 ? void 0 : fileRaw.contentType) !== null && _d !== void 0 ? _d : '';
99
+ return (h(Host, { key: '6023c8c6ca3d780fd4a9cca41189cb05a151221e' }, h("div", { key: 'd7d39d62964583da0ca6c1494e75321e6bab9ac8', class: "mrd-document-view" }, h("div", { key: '9aa14569c9ffc785ffb4f2b3d5930cf3e822e246', class: "mrd-document-view__body" }, h("div", { key: 'e00f3b55551429d8522fb97e7ccb43d56e98042d', class: "mrd-document-view__meta" }, this.renderContainer(), title && h("h1", { key: '9860273878b245fc757eb690c6cd7006b13045c4', class: "mrd-document-view__title" }, title), date && h("p", { key: '84c60b0b6feb6678d661000c26050e42eaaa8399', class: "mrd-document-view__date" }, date), fileRaw && this.renderFileMeta(fileName, fileHref), summary && (h("div", { key: 'd1cff0cc1b9376409c8e04a785deba93c986ba47', class: "mrd-document-view__summary" }, h("div", { key: 'fc9c4b986afc8c4f50486caf85bf58860d641a2b', class: "mrd-document-view__summary-body", innerHTML: summary })))), fileRaw && this.renderPreviewPanel(fileName, fileHref, mimeType)))));
60
100
  }
61
101
  static get is() { return "mrd-document-view"; }
62
102
  static get encapsulation() { return "scoped"; }
@@ -138,6 +178,46 @@ export class MrdDocumentView {
138
178
  "reflect": false,
139
179
  "attribute": "locale",
140
180
  "defaultValue": "navigator.language"
181
+ },
182
+ "previewUrl": {
183
+ "type": "string",
184
+ "mutable": false,
185
+ "complexType": {
186
+ "original": "string",
187
+ "resolved": "string",
188
+ "references": {}
189
+ },
190
+ "required": false,
191
+ "optional": false,
192
+ "docs": {
193
+ "tags": [],
194
+ "text": "Signed URL for the file's inline preview (mrd-file-preview), resolved by\nthe host in response to mrdLoadPreview. Empty while unresolved \u2014 the\npreview area shows a loading state until the host injects a value."
195
+ },
196
+ "getter": false,
197
+ "setter": false,
198
+ "reflect": false,
199
+ "attribute": "preview-url",
200
+ "defaultValue": "''"
201
+ },
202
+ "previewHeight": {
203
+ "type": "number",
204
+ "mutable": false,
205
+ "complexType": {
206
+ "original": "number",
207
+ "resolved": "number",
208
+ "references": {}
209
+ },
210
+ "required": false,
211
+ "optional": false,
212
+ "docs": {
213
+ "tags": [],
214
+ "text": "Minimum height (px) of the inline preview area \u2014 it stretches to fill the\navailable row height in the wide side-by-side layout (see mrd-document-view.scss),\nand falls back to this height when stacked. Overlay hosts typically pass a\nsmaller value than the full-page default to fit available chrome."
215
+ },
216
+ "getter": false,
217
+ "setter": false,
218
+ "reflect": false,
219
+ "attribute": "preview-height",
220
+ "defaultValue": "640"
141
221
  }
142
222
  };
143
223
  }
@@ -172,6 +252,27 @@ export class MrdDocumentView {
172
252
  "resolved": "{ href: string; fileName: string; }",
173
253
  "references": {}
174
254
  }
255
+ }, {
256
+ "method": "mrdLoadPreview",
257
+ "name": "mrdLoadPreview",
258
+ "bubbles": true,
259
+ "cancelable": true,
260
+ "composed": true,
261
+ "docs": {
262
+ "tags": [],
263
+ "text": "The file slot has an href and needs a signed URL for inline preview.\nFired once on load and again whenever the file href changes (e.g. the\noverlay is reused for a different document via prev/next). Host resolves\nit and calls back with `previewUrl` \u2014 the same pattern as mrdLoadImage /\nsetImagePreview on mrd-layout-section."
264
+ },
265
+ "complexType": {
266
+ "original": "{ href: string }",
267
+ "resolved": "{ href: string; }",
268
+ "references": {}
269
+ }
270
+ }];
271
+ }
272
+ static get watchers() {
273
+ return [{
274
+ "propName": "data",
275
+ "methodName": "dataChanged"
175
276
  }];
176
277
  }
177
278
  }
@@ -0,0 +1,30 @@
1
+ /** Pure classification logic for mrd-file-preview — no Stencil dependency. */
2
+ const IMAGE_EXTENSIONS = new Set(['png', 'jpg', 'jpeg', 'gif', 'webp', 'svg', 'bmp']);
3
+ function extensionOf(fileName) {
4
+ if (!fileName)
5
+ return null;
6
+ const match = /\.([a-z0-9]+)$/i.exec(fileName);
7
+ return match ? match[1].toLowerCase() : null;
8
+ }
9
+ /**
10
+ * Decide how to render a file given its declared mime type and/or file name.
11
+ * A declared mime type is authoritative when present (a mismatched file name
12
+ * extension is ignored); the file name extension is only consulted as a
13
+ * fallback when no mime type is known.
14
+ */
15
+ export function detectPreviewKind(mimeType, fileName) {
16
+ const mime = (mimeType !== null && mimeType !== void 0 ? mimeType : '').toLowerCase().trim();
17
+ if (mime) {
18
+ if (mime === 'application/pdf')
19
+ return 'pdf';
20
+ if (mime.startsWith('image/'))
21
+ return 'image';
22
+ return 'unsupported';
23
+ }
24
+ const ext = extensionOf(fileName);
25
+ if (ext === 'pdf')
26
+ return 'pdf';
27
+ if (ext && IMAGE_EXTENSIONS.has(ext))
28
+ return 'image';
29
+ return 'unsupported';
30
+ }
@@ -0,0 +1,59 @@
1
+ :host {
2
+ display: block;
3
+ width: 100%;
4
+ height: 100%;
5
+ }
6
+
7
+ .mrd-file-preview__frame {
8
+ display: block;
9
+ width: 100%;
10
+ height: 100%;
11
+ border: none;
12
+ }
13
+ .mrd-file-preview__image {
14
+ display: block;
15
+ max-width: 100%;
16
+ max-height: 100%;
17
+ margin: 0 auto;
18
+ object-fit: contain;
19
+ }
20
+ .mrd-file-preview__error, .mrd-file-preview__unsupported {
21
+ display: flex;
22
+ flex-direction: column;
23
+ align-items: center;
24
+ justify-content: center;
25
+ gap: var(--mrd-space-3);
26
+ width: 100%;
27
+ height: 100%;
28
+ min-height: 8rem;
29
+ padding: var(--mrd-space-6);
30
+ color: var(--mrd-color-neutral-500);
31
+ font-size: var(--mrd-font-size-sm);
32
+ text-align: center;
33
+ }
34
+ .mrd-file-preview__error-text {
35
+ color: var(--mrd-color-danger);
36
+ }
37
+ .mrd-file-preview__retry, .mrd-file-preview__download {
38
+ display: inline-flex;
39
+ align-items: center;
40
+ gap: var(--mrd-space-2);
41
+ height: 2rem;
42
+ padding: 0 var(--mrd-space-3);
43
+ border: var(--mrd-border-width) solid var(--mrd-color-neutral-300);
44
+ border-radius: var(--mrd-border-radius);
45
+ background: var(--mrd-color-white);
46
+ color: var(--mrd-color-neutral-700);
47
+ font-family: var(--mrd-font-family);
48
+ font-size: var(--mrd-font-size-sm);
49
+ font-weight: var(--mrd-font-weight-medium);
50
+ cursor: pointer;
51
+ }
52
+ .mrd-file-preview__retry:hover, .mrd-file-preview__download:hover {
53
+ background: var(--mrd-color-neutral-100);
54
+ color: var(--mrd-color-neutral-900);
55
+ }
56
+ .mrd-file-preview__download svg {
57
+ width: 0.9375rem;
58
+ height: 0.9375rem;
59
+ }