@mmlogic/components 0.5.15 → 0.5.17

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 (40) hide show
  1. package/dist/cjs/loader.cjs.js +1 -1
  2. package/dist/cjs/mosterdcomponents.cjs.js +1 -1
  3. package/dist/cjs/mrd-boolean-field_28.cjs.entry.js +361 -56
  4. package/dist/collection/collection-manifest.json +2 -2
  5. package/dist/collection/components/documents/mrd-document-container/mrd-document-container.js +31 -4
  6. package/dist/collection/components/form/mrd-field/mrd-field.css +69 -0
  7. package/dist/collection/components/form/mrd-field/mrd-field.js +95 -25
  8. package/dist/collection/components/layout/mrd-layout-section/field-value.js +19 -2
  9. package/dist/collection/components/layout/mrd-layout-section/layout-helpers.js +10 -0
  10. package/dist/collection/components/layout/mrd-layout-section/mrd-layout-section.css +4 -3
  11. package/dist/collection/components/layout/mrd-layout-section/mrd-layout-section.js +174 -17
  12. package/dist/collection/components/memo/mrd-memo-container/mrd-memo-container.js +105 -6
  13. package/dist/collection/components/memo/mrd-memo-list/mrd-memo-list.css +14 -0
  14. package/dist/collection/components/memo/mrd-memo-list/mrd-memo-list.js +50 -2
  15. package/dist/collection/components/table/mrd-table/mrd-table.js +105 -2
  16. package/dist/collection/dev/api.js +17 -0
  17. package/dist/collection/dev/app.js +49 -15
  18. package/dist/collection/dev/example-data.js +15 -1
  19. package/dist/components/mrd-document-container2.js +1 -1
  20. package/dist/components/mrd-field2.js +1 -1
  21. package/dist/components/mrd-layout-section.js +1 -1
  22. package/dist/components/mrd-memo-container2.js +1 -1
  23. package/dist/components/mrd-memo-list2.js +1 -1
  24. package/dist/components/mrd-table2.js +1 -1
  25. package/dist/esm/loader.js +1 -1
  26. package/dist/esm/mosterdcomponents.js +1 -1
  27. package/dist/esm/mrd-boolean-field_28.entry.js +361 -56
  28. package/dist/mosterdcomponents/mosterdcomponents.esm.js +1 -1
  29. package/dist/mosterdcomponents/p-f72668e9.entry.js +3 -0
  30. package/dist/types/components/documents/mrd-document-container/mrd-document-container.d.ts +7 -0
  31. package/dist/types/components/form/mrd-field/mrd-field.d.ts +16 -0
  32. package/dist/types/components/layout/mrd-layout-section/field-value.d.ts +4 -0
  33. package/dist/types/components/layout/mrd-layout-section/layout-helpers.d.ts +3 -0
  34. package/dist/types/components/layout/mrd-layout-section/mrd-layout-section.d.ts +52 -0
  35. package/dist/types/components/memo/mrd-memo-container/mrd-memo-container.d.ts +26 -1
  36. package/dist/types/components/memo/mrd-memo-list/mrd-memo-list.d.ts +12 -0
  37. package/dist/types/components/table/mrd-table/mrd-table.d.ts +45 -0
  38. package/dist/types/components.d.ts +92 -0
  39. package/package.json +1 -1
  40. package/dist/mosterdcomponents/p-d64ba1f2.entry.js +0 -3
@@ -1,8 +1,8 @@
1
1
  import { Host, h } from "@stencil/core";
2
2
  import { ClientLayoutItemType, ClientLayoutItemFieldDataType, ClientLayoutNavigateBehaviour, resolveArchetype, ARCHETYPE_DOCUMENT, ARCHETYPE_INVOICE, ARCHETYPE_EMAIL, ARCHETYPE_INVOICE_LINE, ARCHETYPE_MEMO, } from "../../../types/client-layout";
3
3
  import { t } from "../../../utils/i18n";
4
- import { viewKeyFor, flattenItems, buildContainerHref, showsEmpty } from "./layout-helpers";
5
- import { renderField } from "./field-value";
4
+ import { viewKeyFor, flattenItems, buildContainerHref, showsEmpty, hasRelationValue } from "./layout-helpers";
5
+ import { renderField, isFieldVisible } from "./field-value";
6
6
  export class MrdLayoutSection {
7
7
  constructor() {
8
8
  /** Items from one layout entry in ClientDashboardMetadata.layouts[]. */
@@ -31,7 +31,16 @@ export class MrdLayoutSection {
31
31
  this.imagePreviewUrl = null;
32
32
  this.imagePreviews = {};
33
33
  this.openHistoryField = null;
34
+ /** Answers to mrdViewCheckCapabilities, keyed by view key — see emitCapabilitiesChecks().
35
+ * Passed straight down as a `canCreate`/`mayCreate` prop to whichever body (mrd-table,
36
+ * mrd-memo-container, mrd-document-container) is mounted for that key; a missing entry
37
+ * means "not answered yet" and is treated as false (hidden), same default as mrd-table's
38
+ * own standalone behaviour (TASK-0299). */
39
+ this.canCreateMap = {};
34
40
  this.historyClickOutside = null;
41
+ /** Signature last asked per view key (see emitCapabilitiesChecks()) — avoids re-emitting
42
+ * mrdViewCheckCapabilities on every data/items update when the binding hasn't changed. */
43
+ this.capabilitiesAsked = new Map();
35
44
  this.searchTimers = {};
36
45
  this.handleViewLoadPage = (e, name) => {
37
46
  e.stopPropagation();
@@ -51,6 +60,10 @@ export class MrdLayoutSection {
51
60
  };
52
61
  }
53
62
  componentDidLoad() {
63
+ // No DOM/child-hydration dependency, unlike initEmbeddedTables()/emitLoadImages() below —
64
+ // run it immediately rather than deferring, so the host's answer (setViewCanCreate) can
65
+ // land before the child bodies even mount.
66
+ this.emitCapabilitiesChecks();
54
67
  setTimeout(() => {
55
68
  this.initEmbeddedTables();
56
69
  this.emitLoadImages();
@@ -69,9 +82,15 @@ export class MrdLayoutSection {
69
82
  }
70
83
  dataChanged(newVal) {
71
84
  if (newVal && Object.keys(newVal).length > 0) {
85
+ this.emitCapabilitiesChecks();
72
86
  setTimeout(() => this.initEmbeddedTables(), 0);
73
87
  }
74
88
  }
89
+ /** A host can swap `items` (e.g. a different dashboard layout) without `data` changing —
90
+ * dataChanged() alone would miss that binding change. */
91
+ itemsChanged() {
92
+ this.emitCapabilitiesChecks();
93
+ }
75
94
  async initEmbeddedTables() {
76
95
  const tables = this.el.querySelectorAll('mrd-table[data-view]');
77
96
  for (const table of Array.from(tables)) {
@@ -99,6 +118,53 @@ export class MrdLayoutSection {
99
118
  }
100
119
  }
101
120
  }
121
+ /** Parent record id extracted from `data._links.self.href` — the same value every
122
+ * VIEW/RELATED_VIEW item on this record shares, used for RELATED_VIEW path construction
123
+ * (renderRelatedView) and capability checks (emitCapabilitiesChecks). */
124
+ computeParentId() {
125
+ var _a, _b, _c, _d, _e;
126
+ const selfHref = (_d = (_c = (_b = (_a = this.data) === null || _a === void 0 ? void 0 : _a._links) === null || _b === void 0 ? void 0 : _b.self) === null || _c === void 0 ? void 0 : _c.href) !== null && _d !== void 0 ? _d : '';
127
+ return (_e = selfHref.split('/').filter(Boolean).pop()) !== null && _e !== void 0 ? _e : '';
128
+ }
129
+ /** Asks, once per VIEW/RELATED_VIEW binding, whether the current user may create a record
130
+ * there — the single generic place for this check (TASK-0299/FINDING-0155), so mrd-table,
131
+ * mrd-memo-container and mrd-document-container all get the answer the same way: as a plain
132
+ * `canCreate`/`mayCreate` prop from this component, computed here and handed down in
133
+ * renderTable()/renderMemoContainer()/renderDocumentContainer() — none of the three leaf
134
+ * components need to ask on their own when rendered through mrd-layout-section (mrd-table's
135
+ * own self-ask only kicks in for standalone use, i.e. its `canCreate` prop left undefined).
136
+ *
137
+ * The route/path variables mirror what each renderer already uses for its own rows —
138
+ * `type` = `dataClass`, `refType`/`refId` = `fromClass`/parentId for a RELATED_VIEW, and
139
+ * `filterClass` when the view is scoped to one subtype (the same value sent as `?type=` on
140
+ * the row fetch). A signature (not just the key) is tracked per binding so a genuine route
141
+ * change re-asks and hides again, without re-asking on every unrelated data/items update. */
142
+ emitCapabilitiesChecks() {
143
+ var _a, _b, _c, _d;
144
+ const parentId = this.computeParentId();
145
+ for (const item of flattenItems(this.items)) {
146
+ if (item.type !== ClientLayoutItemType.VIEW && item.type !== ClientLayoutItemType.RELATED_VIEW)
147
+ continue;
148
+ const key = viewKeyFor(item);
149
+ if (!key || !item.view)
150
+ continue;
151
+ const isRelatedView = item.type === ClientLayoutItemType.RELATED_VIEW;
152
+ const type = (_b = (_a = item.dataClass) !== null && _a !== void 0 ? _a : item.relatedClass) !== null && _b !== void 0 ? _b : key;
153
+ const refType = isRelatedView ? ((_c = item.fromClass) !== null && _c !== void 0 ? _c : '') : undefined;
154
+ const refId = isRelatedView ? parentId : undefined;
155
+ const filterClass = (_d = item.filterClass) !== null && _d !== void 0 ? _d : undefined;
156
+ const signature = JSON.stringify([type, refType, refId, filterClass]);
157
+ if (this.capabilitiesAsked.get(key) === signature)
158
+ continue;
159
+ this.capabilitiesAsked.set(key, signature);
160
+ if (key in this.canCreateMap) {
161
+ const next = Object.assign({}, this.canCreateMap);
162
+ delete next[key];
163
+ this.canCreateMap = next;
164
+ }
165
+ this.mrdViewCheckCapabilities.emit(Object.assign(Object.assign({ name: key, type }, (refType !== undefined ? { refType, refId } : {})), (filterClass ? { filterClass } : {})));
166
+ }
167
+ }
102
168
  /** Inject search results. Pass dataClass to target a specific SEARCH item; omit when there is only one. */
103
169
  async setSearchResults(results, dataClass) {
104
170
  const key = dataClass !== null && dataClass !== void 0 ? dataClass : this.resolveSearchKey();
@@ -140,6 +206,14 @@ export class MrdLayoutSection {
140
206
  return;
141
207
  await table.setAggregations(data);
142
208
  }
209
+ /** Host answer to mrdViewCheckCapabilities — whether the current user may create a record
210
+ * for the given view's bound route. `name` is the view key from the event detail. Stored in
211
+ * canCreateMap and passed down as a plain prop to whichever body is mounted for that key
212
+ * (mrd-table, mrd-memo-container or mrd-document-container) — no DOM lookup needed, unlike
213
+ * setViewPage/setViewAggregations, since the prop flows through the next render. */
214
+ async setViewCanCreate(name, value) {
215
+ this.canCreateMap = Object.assign(Object.assign({}, this.canCreateMap), { [name]: value });
216
+ }
143
217
  /** Inject distinct values into an embedded mrd-document-list for a document view. */
144
218
  async setViewDistinct(name, nodeId, values) {
145
219
  const list = this.el.querySelector(`mrd-document-list[data-doclist="${name}"]`);
@@ -212,7 +286,7 @@ export class MrdLayoutSection {
212
286
  else if (link === null || link === void 0 ? void 0 : link.name) {
213
287
  valueContent = makeBtn(link.href, link.name);
214
288
  }
215
- const isEmpty = !valueContent;
289
+ const isEmpty = !hasRelationValue(item, this.data);
216
290
  if (isEmpty && !showsEmpty(item))
217
291
  return null;
218
292
  return (h("div", { class: `mrd-layout-section__field${isEmpty ? ' mrd-layout-section__field--empty' : ''}`, key: item.name }, h("span", { class: "mrd-layout-section__field-label" }, item.label), h("span", { class: "mrd-layout-section__field-value" }, isEmpty ? t('value_empty', this.locale) : valueContent)));
@@ -234,24 +308,37 @@ export class MrdLayoutSection {
234
308
  if (!item.view)
235
309
  return null;
236
310
  const showTitle = (_a = item.showTitle) !== null && _a !== void 0 ? _a : false;
237
- // Extract parentId from data._links.self.href for RELATED_VIEW path construction
238
311
  const selfHref = (_e = (_d = (_c = (_b = this.data) === null || _b === void 0 ? void 0 : _b._links) === null || _c === void 0 ? void 0 : _c.self) === null || _d === void 0 ? void 0 : _d.href) !== null && _e !== void 0 ? _e : '';
239
- const parentId = (_f = selfHref.split('/').filter(Boolean).pop()) !== null && _f !== void 0 ? _f : '';
312
+ const parentId = this.computeParentId();
240
313
  // The API publishes `archetypes` on the VIEW/RELATED_VIEW item itself
241
314
  // (sibling of `view`), not inside `view`.
242
- const docArchetype = resolveArchetype((_g = item.archetypes) !== null && _g !== void 0 ? _g : (_h = item.view) === null || _h === void 0 ? void 0 : _h.archetypes, ARCHETYPE_DOCUMENT);
243
- const memoArchetype = !docArchetype ? resolveArchetype((_j = item.archetypes) !== null && _j !== void 0 ? _j : (_k = item.view) === null || _k === void 0 ? void 0 : _k.archetypes, ARCHETYPE_MEMO) : undefined;
315
+ const docArchetype = resolveArchetype((_f = item.archetypes) !== null && _f !== void 0 ? _f : (_g = item.view) === null || _g === void 0 ? void 0 : _g.archetypes, ARCHETYPE_DOCUMENT);
316
+ const memoArchetype = !docArchetype ? resolveArchetype((_h = item.archetypes) !== null && _h !== void 0 ? _h : (_j = item.view) === null || _j === void 0 ? void 0 : _j.archetypes, ARCHETYPE_MEMO) : undefined;
244
317
  // Canonical parent href for single-container seeding: use the RELATED_VIEW's
245
318
  // fromClass (base class) rather than the route class, so polymorphic subtypes
246
319
  // resolve to the same container the documents actually reference.
247
320
  const containerHref = buildContainerHref(item, selfHref, parentId);
248
- return (h("div", { class: "mrd-layout-section__related-view", key: `view-${key}` }, showTitle && item.label && h("h3", { class: "mrd-layout-section__related-view-title" }, item.label), docArchetype && this.renderDocumentContainer(item, key, parentId, docArchetype, containerHref), memoArchetype && this.renderMemoContainer(item, key, parentId, memoArchetype, containerHref), !docArchetype && !memoArchetype && this.renderTable(item, key, parentId)));
321
+ // A memo RELATED_VIEW isn't necessarily scoped by the container relation
322
+ // e.g. a person's dashboard can show "memos I authored" via the author
323
+ // relation instead. inverseRelation names which relation on the memo points
324
+ // back to this parent; only when it matches the container slot does the
325
+ // parent's own href double as the memo's container.
326
+ const memoContainerRelated = item.type !== ClientLayoutItemType.RELATED_VIEW
327
+ || item.inverseRelation === ((_k = memoArchetype === null || memoArchetype === void 0 ? void 0 : memoArchetype.slots) === null || _k === void 0 ? void 0 : _k.container);
328
+ const memoContainerHref = memoContainerRelated ? containerHref : '';
329
+ // Distinct from memoContainerRelated: only a genuine container-scoped
330
+ // RELATED_VIEW (a company's own memo tab) makes every card's container
331
+ // obvious from the page itself. A top-level VIEW or an author-scoped
332
+ // RELATED_VIEW both still need the per-card container link.
333
+ const memoContainerImplied = item.type === ClientLayoutItemType.RELATED_VIEW && memoContainerRelated;
334
+ return (h("div", { class: "mrd-layout-section__related-view", key: `view-${key}` }, showTitle && item.label && h("h3", { class: "mrd-layout-section__related-view-title" }, item.label), docArchetype && this.renderDocumentContainer(item, key, parentId, docArchetype, containerHref), memoArchetype && this.renderMemoContainer(item, key, parentId, memoArchetype, memoContainerHref, memoContainerRelated, !memoContainerImplied), !docArchetype && !memoArchetype && this.renderTable(item, key, parentId)));
249
335
  }
250
336
  /** Document collection view: owns the view-switch + collection actions and
251
337
  * swaps between the folder tree and the flat table. Events are re-emitted to
252
338
  * the host with the view key, exactly as the raw list/table events were. */
253
339
  renderDocumentContainer(item, key, parentId, archetype, containerHref) {
254
- return (h("mrd-document-container", { item: item, archetype: archetype, parentId: parentId, containerHref: containerHref, viewKey: key, locale: this.locale, readOnly: this.readOnly, onMrdLoadDistinct: (e) => {
340
+ var _a;
341
+ return (h("mrd-document-container", { item: item, archetype: archetype, parentId: parentId, containerHref: containerHref, viewKey: key, locale: this.locale, readOnly: this.readOnly, mayCreate: (_a = this.canCreateMap[key]) !== null && _a !== void 0 ? _a : false, onMrdLoadDistinct: (e) => {
255
342
  e.stopPropagation();
256
343
  this.mrdLoadViewDistinct.emit(Object.assign({ name: key }, e.detail));
257
344
  }, onMrdLoadPage: (e) => {
@@ -285,10 +372,14 @@ export class MrdLayoutSection {
285
372
  }
286
373
  /** Memo collection view: owns the view-switch + "New memo" and swaps between
287
374
  * the card list and the flat table, mirroring renderDocumentContainer(). */
288
- renderMemoContainer(item, key, parentId, archetype, containerHref) {
289
- var _a, _b, _c, _d;
375
+ renderMemoContainer(item, key, parentId, archetype, containerHref, containerRelated, showContainerLink) {
376
+ var _a, _b, _c, _d, _e;
290
377
  const labelsListItems = (_d = (_c = (_b = (_a = item.view) === null || _a === void 0 ? void 0 : _a.values) === null || _b === void 0 ? void 0 : _b.find(v => { var _a; return v.name === ((_a = archetype.slots) === null || _a === void 0 ? void 0 : _a.labels); })) === null || _c === void 0 ? void 0 : _c.listItems) !== null && _d !== void 0 ? _d : null;
291
- return (h("mrd-memo-container", { "data-view": key, item: item, archetype: archetype, parentId: parentId, containerHref: containerHref, viewKey: key, locale: this.locale, readOnly: this.readOnly, me: this.me, labelsListItems: labelsListItems, onMrdLoadPage: (e) => {
378
+ return (h("mrd-memo-container", { "data-view": key, item: item, archetype: archetype, parentId: parentId, containerHref: containerHref, containerRelated: containerRelated, showContainerLink: showContainerLink, viewKey: key, locale: this.locale, readOnly: this.readOnly, canCreate: (_e = this.canCreateMap[key]) !== null && _e !== void 0 ? _e : false, me: this.me, labelsListItems: labelsListItems, onMrdNavigate: (e) => {
379
+ var _a;
380
+ e.stopPropagation();
381
+ this.mrdNavigate.emit(Object.assign(Object.assign({}, e.detail), { navigateBehaviour: (_a = item.navigateBehaviour) !== null && _a !== void 0 ? _a : ClientLayoutNavigateBehaviour.PAGE }));
382
+ }, onMrdLoadPage: (e) => {
292
383
  e.stopPropagation();
293
384
  this.mrdLoadViewPage.emit(Object.assign({ name: key }, e.detail));
294
385
  }, onMrdLoadAggregations: (e) => {
@@ -311,7 +402,8 @@ export class MrdLayoutSection {
311
402
  } }));
312
403
  }
313
404
  renderTable(item, key, parentId) {
314
- return (h("mrd-table", { "data-view": key, item: item, parentId: parentId, locale: this.locale, readOnly: this.readOnly, onMrdLoadPage: (e) => this.handleViewLoadPage(e, key), onMrdLoadAggregations: (e) => {
405
+ var _a;
406
+ return (h("mrd-table", { "data-view": key, item: item, parentId: parentId, locale: this.locale, readOnly: this.readOnly, canCreate: (_a = this.canCreateMap[key]) !== null && _a !== void 0 ? _a : false, onMrdLoadPage: (e) => this.handleViewLoadPage(e, key), onMrdLoadAggregations: (e) => {
315
407
  var _a;
316
408
  e.stopPropagation();
317
409
  this.mrdLoadViewAggregations.emit(Object.assign({ name: key, dataClass: (_a = item.dataClass) !== null && _a !== void 0 ? _a : key }, e.detail));
@@ -339,7 +431,26 @@ export class MrdLayoutSection {
339
431
  });
340
432
  } }));
341
433
  }
342
- renderItem(item) {
434
+ /** Whether every FIELD/RELATION item following a HEADER (up to the next
435
+ * HEADER/SECTION/GROUP or the end of `siblings`) would itself render
436
+ * nothing — i.e. the HEADER would introduce an empty block. Other item
437
+ * types (VIEW, NAVIGATE, TEXT, ...) always count as visible. */
438
+ isHeaderBlockEmpty(siblings, index) {
439
+ const ctx = this.fieldCtx();
440
+ for (let i = index + 1; i < siblings.length; i++) {
441
+ const sib = siblings[i];
442
+ if (sib.type === ClientLayoutItemType.HEADER || sib.type === ClientLayoutItemType.SECTION || sib.type === ClientLayoutItemType.GROUP)
443
+ break;
444
+ if (sib.type === ClientLayoutItemType.FIELD && isFieldVisible(ctx, sib))
445
+ return false;
446
+ if (sib.type === ClientLayoutItemType.RELATION && (hasRelationValue(sib, this.data) || showsEmpty(sib)))
447
+ return false;
448
+ if (sib.type !== ClientLayoutItemType.FIELD && sib.type !== ClientLayoutItemType.RELATION)
449
+ return false;
450
+ }
451
+ return true;
452
+ }
453
+ renderItem(item, siblings = this.items, index = -1) {
343
454
  var _a, _b;
344
455
  switch (item.type) {
345
456
  case ClientLayoutItemType.FIELD:
@@ -347,6 +458,8 @@ export class MrdLayoutSection {
347
458
  case ClientLayoutItemType.RELATION:
348
459
  return this.renderRelation(item);
349
460
  case ClientLayoutItemType.HEADER:
461
+ if (index >= 0 && this.isHeaderBlockEmpty(siblings, index))
462
+ return null;
350
463
  return (h("h2", { class: "mrd-layout-section__header", key: `header-${item.label}` }, item.label));
351
464
  case ClientLayoutItemType.TEXT:
352
465
  return h("div", { class: "mrd-layout-section__text", key: `text-${item.label}`, innerHTML: (_a = item.label) !== null && _a !== void 0 ? _a : '' });
@@ -356,7 +469,7 @@ export class MrdLayoutSection {
356
469
  return this.renderSearch(item);
357
470
  case ClientLayoutItemType.SECTION:
358
471
  case ClientLayoutItemType.GROUP:
359
- return (h("div", { class: "mrd-layout-section__group", key: `group-${item.label}` }, item.label && h("h3", { class: "mrd-layout-section__group-title" }, item.label), ((_b = item.items) !== null && _b !== void 0 ? _b : []).map(child => this.renderItem(child))));
472
+ return (h("div", { class: "mrd-layout-section__group", key: `group-${item.label}` }, item.label && h("h3", { class: "mrd-layout-section__group-title" }, item.label), ((_b = item.items) !== null && _b !== void 0 ? _b : []).map((child, i) => { var _a; return this.renderItem(child, (_a = item.items) !== null && _a !== void 0 ? _a : [], i); })));
360
473
  case ClientLayoutItemType.RELATED_VIEW:
361
474
  case ClientLayoutItemType.VIEW:
362
475
  return this.renderRelatedView(item);
@@ -428,7 +541,7 @@ export class MrdLayoutSection {
428
541
  const emailArchetype = !docArchetype && !invoiceArchetype ? resolveArchetype(this.archetypes, ARCHETYPE_EMAIL) : undefined;
429
542
  const memoArchetype = !docArchetype && !invoiceArchetype && !emailArchetype ? resolveArchetype(this.archetypes, ARCHETYPE_MEMO) : undefined;
430
543
  const anyArchetype = docArchetype || invoiceArchetype || emailArchetype || memoArchetype;
431
- return (h(Host, { key: '730654d745992b9607f15fcaf2bb54a725429e4d' }, h("div", { key: '72a2e7bb767992aa6f206a10f8b2a3a572e86115', class: "mrd-layout-section" }, docArchetype && this.renderDocumentObject(docArchetype), invoiceArchetype && this.renderInvoiceObject(invoiceArchetype), emailArchetype && this.renderEmailObject(emailArchetype), memoArchetype && this.renderMemoObject(memoArchetype), !anyArchetype && this.items.map(item => this.renderItem(item))), this.renderImageModal()));
544
+ return (h(Host, { key: '508ca481c777d65d1739f7b5f30f5f3ad8722655' }, h("div", { key: 'c02630235a2a21511b391ff7b5bebeca1bcc6903', class: "mrd-layout-section" }, docArchetype && this.renderDocumentObject(docArchetype), invoiceArchetype && this.renderInvoiceObject(invoiceArchetype), emailArchetype && this.renderEmailObject(emailArchetype), memoArchetype && this.renderMemoObject(memoArchetype), !anyArchetype && this.items.map((item, i) => this.renderItem(item, this.items, i))), this.renderImageModal()));
432
545
  }
433
546
  static get is() { return "mrd-layout-section"; }
434
547
  static get encapsulation() { return "scoped"; }
@@ -642,7 +755,8 @@ export class MrdLayoutSection {
642
755
  "searchResultsMap": {},
643
756
  "imagePreviewUrl": {},
644
757
  "imagePreviews": {},
645
- "openHistoryField": {}
758
+ "openHistoryField": {},
759
+ "canCreateMap": {}
646
760
  };
647
761
  }
648
762
  static get events() {
@@ -813,6 +927,21 @@ export class MrdLayoutSection {
813
927
  "resolved": "{ name: string; nodeId: string; distinct: string; path: string; qs: string; }",
814
928
  "references": {}
815
929
  }
930
+ }, {
931
+ "method": "mrdViewCheckCapabilities",
932
+ "name": "mrdViewCheckCapabilities",
933
+ "bubbles": true,
934
+ "cancelable": true,
935
+ "composed": true,
936
+ "docs": {
937
+ "tags": [],
938
+ "text": "Emitted once per VIEW/RELATED_VIEW binding \u2014 computed here directly, not relayed from a\nchild (see emitCapabilitiesChecks()), so it covers mrd-table, mrd-memo-container and\nmrd-document-container uniformly. `name` is the view key (viewKeyFor(item)) \u2014 pass it\nback to setViewCanCreate() to answer it."
939
+ },
940
+ "complexType": {
941
+ "original": "{ name: string; type: string; refType?: string; refId?: string; filterClass?: string }",
942
+ "resolved": "{ name: string; type: string; refType?: string | undefined; refId?: string | undefined; filterClass?: string | undefined; }",
943
+ "references": {}
944
+ }
816
945
  }, {
817
946
  "method": "mrdUpdateObject",
818
947
  "name": "mrdUpdateObject",
@@ -1005,6 +1134,31 @@ export class MrdLayoutSection {
1005
1134
  "tags": []
1006
1135
  }
1007
1136
  },
1137
+ "setViewCanCreate": {
1138
+ "complexType": {
1139
+ "signature": "(name: string, value: boolean) => Promise<void>",
1140
+ "parameters": [{
1141
+ "name": "name",
1142
+ "type": "string",
1143
+ "docs": ""
1144
+ }, {
1145
+ "name": "value",
1146
+ "type": "boolean",
1147
+ "docs": ""
1148
+ }],
1149
+ "references": {
1150
+ "Promise": {
1151
+ "location": "global",
1152
+ "id": "global::Promise"
1153
+ }
1154
+ },
1155
+ "return": "Promise<void>"
1156
+ },
1157
+ "docs": {
1158
+ "text": "Host answer to mrdViewCheckCapabilities \u2014 whether the current user may create a record\nfor the given view's bound route. `name` is the view key from the event detail. Stored in\ncanCreateMap and passed down as a plain prop to whichever body is mounted for that key\n(mrd-table, mrd-memo-container or mrd-document-container) \u2014 no DOM lookup needed, unlike\nsetViewPage/setViewAggregations, since the prop flows through the next render.",
1159
+ "tags": []
1160
+ }
1161
+ },
1008
1162
  "setViewDistinct": {
1009
1163
  "complexType": {
1010
1164
  "signature": "(name: string, nodeId: string, values: DistinctValue[]) => Promise<void>",
@@ -1159,6 +1313,9 @@ export class MrdLayoutSection {
1159
1313
  return [{
1160
1314
  "propName": "data",
1161
1315
  "methodName": "dataChanged"
1316
+ }, {
1317
+ "propName": "items",
1318
+ "methodName": "itemsChanged"
1162
1319
  }];
1163
1320
  }
1164
1321
  }
@@ -29,6 +29,20 @@ export class MrdMemoContainer {
29
29
  /** Canonical parent href — set when this is a RELATED_VIEW embedded under a
30
30
  * parent record, which already gives a new memo something to attach to. */
31
31
  this.containerHref = '';
32
+ /** False when this RELATED_VIEW's parent is related via a relation other than
33
+ * the archetype's container slot (e.g. a person's "memos I authored" list,
34
+ * related via `author`) — mrd-layout-section resolves this from the item's
35
+ * `inverseRelation`. In that case the parent is not a valid container for a
36
+ * new memo, and — unlike a top-level VIEW with no context at all — falling
37
+ * back to `me` would be wrong too (creating a memo "on myself" from someone
38
+ * else's authored-memos list makes no sense), so "New memo" is hidden outright. */
39
+ this.containerRelated = true;
40
+ /** True unless this is a container-scoped RELATED_VIEW (a company's own memo
41
+ * tab, say) where every card's container is obviously the page you're
42
+ * already on — forwarded to mrd-memo-list to show/hide its per-card
43
+ * container link. A top-level VIEW or an author-scoped RELATED_VIEW both
44
+ * still need it, since the container isn't implied by the page context. */
45
+ this.showContainerLink = true;
32
46
  /** Lookup key used for the embedded `data-view` attribute. */
33
47
  this.viewKey = '';
34
48
  this.locale = navigator.language;
@@ -39,6 +53,10 @@ export class MrdMemoContainer {
39
53
  * tooltip, cards/rows are not clickable, and the embedded table's own
40
54
  * create action is disabled too. */
41
55
  this.readOnly = false;
56
+ /** Whether the current user may create a memo here — set by mrd-layout-section from its
57
+ * generic capability check (TASK-0299/FINDING-0155), the same answer the embedded table
58
+ * gets in table mode. Defaults to false (hidden) until confirmed, same as mrd-table. */
59
+ this.canCreate = false;
42
60
  /** The active account's linked person record (userProfile.links.person), if any.
43
61
  * Used both to gate "New memo" (see effectiveContainerHref) and, on submit, to
44
62
  * fill the author slot. */
@@ -260,15 +278,18 @@ export class MrdMemoContainer {
260
278
  return !!((_b = (_a = this.archetype) === null || _a === void 0 ? void 0 : _a.slots) === null || _b === void 0 ? void 0 : _b.important);
261
279
  }
262
280
  /** A parent record already gives a new memo somewhere to attach to (RELATED_VIEW);
263
- * otherwise fall back to the account's own linked person (top-level VIEW). */
281
+ * otherwise fall back to the account's own linked person (top-level VIEW) — but
282
+ * only when containerRelated allows it (see its doc comment). */
264
283
  get effectiveContainerHref() {
284
+ if (!this.containerRelated)
285
+ return '';
265
286
  return this.containerHref || this.meHref();
266
287
  }
267
288
  /** The author slot is always filled from `me` (there is no author picker in
268
289
  * the dialog), so without it there is nothing valid to create — regardless
269
290
  * of whether a parent record already supplies a container. */
270
291
  get canAdd() {
271
- return !this.readOnly && !!this.meHref() && !!this.effectiveContainerHref;
292
+ return this.canCreate && !this.readOnly && !!this.meHref() && !!this.effectiveContainerHref;
272
293
  }
273
294
  /** Path for creating a new memo: `/{dataClass}` for a top-level VIEW, or
274
295
  * `/{fromClass}/{parentId}/{dataClass}` when nested under a parent record. */
@@ -282,7 +303,7 @@ export class MrdMemoContainer {
282
303
  });
283
304
  }
284
305
  renderToolbar() {
285
- return (h("div", { class: "mrd-memo-container__toolbar" }, this.mode === 'cards' && (h("button", { type: "button", class: "mrd-memo-container__action", disabled: !this.canAdd, title: this.readOnly ? t('readonly_access', this.locale) : undefined, onClick: this.openCreate }, 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 5v14M5 12h14" })), t('memo_new', this.locale))), h("div", { class: "mrd-memo-container__seg", role: "group", "aria-label": t('doc_view_switch', this.locale) }, h("button", { class: "mrd-memo-container__seg-btn", "aria-pressed": String(this.mode === 'cards'), title: t('memo_view_cards', this.locale), "aria-label": t('memo_view_cards', this.locale), onClick: () => this.setMode('cards') }, h("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true" }, h("rect", { x: "3", y: "4", width: "7", height: "7", rx: "1" }), h("rect", { x: "14", y: "4", width: "7", height: "7", rx: "1" }), h("rect", { x: "3", y: "15", width: "7", height: "5", rx: "1" }), h("rect", { x: "14", y: "15", width: "7", height: "5", rx: "1" }))), h("button", { class: "mrd-memo-container__seg-btn", "aria-pressed": String(this.mode === 'table'), title: t('memo_view_table', this.locale), "aria-label": t('memo_view_table', this.locale), onClick: () => this.setMode('table') }, h("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true" }, h("path", { d: "M8 6h13M8 12h13M8 18h13M3 6h.01M3 12h.01M3 18h.01" }))))));
306
+ return (h("div", { class: "mrd-memo-container__toolbar" }, this.mode === 'cards' && this.canCreate && (h("button", { type: "button", class: "mrd-memo-container__action", disabled: !this.canAdd, title: this.readOnly ? t('readonly_access', this.locale) : undefined, onClick: this.openCreate }, 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 5v14M5 12h14" })), t('memo_new', this.locale))), h("div", { class: "mrd-memo-container__seg", role: "group", "aria-label": t('doc_view_switch', this.locale) }, h("button", { class: "mrd-memo-container__seg-btn", "aria-pressed": String(this.mode === 'cards'), title: t('memo_view_cards', this.locale), "aria-label": t('memo_view_cards', this.locale), onClick: () => this.setMode('cards') }, h("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true" }, h("rect", { x: "3", y: "4", width: "7", height: "7", rx: "1" }), h("rect", { x: "14", y: "4", width: "7", height: "7", rx: "1" }), h("rect", { x: "3", y: "15", width: "7", height: "5", rx: "1" }), h("rect", { x: "14", y: "15", width: "7", height: "5", rx: "1" }))), h("button", { class: "mrd-memo-container__seg-btn", "aria-pressed": String(this.mode === 'table'), title: t('memo_view_table', this.locale), "aria-label": t('memo_view_table', this.locale), onClick: () => this.setMode('table') }, h("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true" }, h("path", { d: "M8 6h13M8 12h13M8 18h13M3 6h.01M3 12h.01M3 18h.01" }))))));
286
307
  }
287
308
  renderDeleteConfirm() {
288
309
  return (h("div", null, h("h2", { class: "mrd-memo-container__modal-title" }, t('memo_delete_confirm_title', this.locale)), h("p", { class: "mrd-memo-container__modal-warning" }, t('memo_delete_confirm_body', this.locale)), h("div", { class: "mrd-memo-container__modal-actions" }, h("button", { type: "button", class: "mrd-memo-container__modal-cancel", onClick: this.cancelDeleteConfirm }, t('cancel', this.locale)), h("button", { type: "button", class: "mrd-memo-container__modal-delete-confirm", onClick: this.confirmDelete }, t('delete', this.locale)))));
@@ -307,16 +328,19 @@ export class MrdMemoContainer {
307
328
  return (h("div", { class: "mrd-memo-container__modal-backdrop", onClick: this.closeDialog }, h("div", { class: "mrd-memo-container__modal", onClick: (e) => e.stopPropagation() }, this.deleteConfirmOpen ? this.renderDeleteConfirm() : this.renderForm())));
308
329
  }
309
330
  renderCards() {
310
- return (h("mrd-memo-list", { "data-view": this.viewKey, ref: el => (this.listEl = el), item: this.item, archetype: this.archetype, parentId: this.parentId, locale: this.locale, readOnly: this.readOnly, labelsListItems: this.labelsListItems, onMrdLoadPage: (e) => {
331
+ return (h("mrd-memo-list", { "data-view": this.viewKey, ref: el => (this.listEl = el), item: this.item, archetype: this.archetype, parentId: this.parentId, locale: this.locale, readOnly: this.readOnly, labelsListItems: this.labelsListItems, showContainerLink: this.showContainerLink, onMrdLoadPage: (e) => {
311
332
  e.stopPropagation();
312
333
  this.mrdLoadPage.emit(e.detail);
313
334
  }, onMrdEdit: (e) => {
314
335
  e.stopPropagation();
315
336
  this.openEdit(e.detail.row);
337
+ }, onMrdNavigate: (e) => {
338
+ e.stopPropagation();
339
+ this.mrdNavigate.emit(e.detail);
316
340
  } }));
317
341
  }
318
342
  renderTable() {
319
- return (h("mrd-table", { "data-view": this.viewKey, ref: el => (this.tableEl = el), item: this.item, parentId: this.parentId, locale: this.locale, readOnly: this.readOnly, onMrdLoadPage: (e) => {
343
+ return (h("mrd-table", { "data-view": this.viewKey, ref: el => (this.tableEl = el), item: this.item, parentId: this.parentId, locale: this.locale, readOnly: this.readOnly, canCreate: this.canCreate, onMrdLoadPage: (e) => {
320
344
  e.stopPropagation();
321
345
  this.mrdLoadPage.emit(e.detail);
322
346
  }, onMrdLoadAggregations: (e) => {
@@ -331,7 +355,7 @@ export class MrdMemoContainer {
331
355
  } }));
332
356
  }
333
357
  render() {
334
- return (h(Host, { key: 'cf5e9a23e1e8496d061c00218029ec49d92700fe' }, this.renderToolbar(), h("div", { key: '5a15a60ea4cc68c77a8928068f7d6d6c3cb91434', class: "mrd-memo-container__body" }, this.mode === 'cards' ? this.renderCards() : this.renderTable()), this.renderDialog()));
358
+ return (h(Host, { key: 'b0479aad6dc49935442183fcbdf30c07c2c490ff' }, this.renderToolbar(), h("div", { key: '40d0fb882186001ad4984c1ba64c5a5c529f4d76', class: "mrd-memo-container__body" }, this.mode === 'cards' ? this.renderCards() : this.renderTable()), this.renderDialog()));
335
359
  }
336
360
  static get is() { return "mrd-memo-container"; }
337
361
  static get encapsulation() { return "scoped"; }
@@ -436,6 +460,46 @@ export class MrdMemoContainer {
436
460
  "attribute": "container-href",
437
461
  "defaultValue": "''"
438
462
  },
463
+ "containerRelated": {
464
+ "type": "boolean",
465
+ "mutable": false,
466
+ "complexType": {
467
+ "original": "boolean",
468
+ "resolved": "boolean",
469
+ "references": {}
470
+ },
471
+ "required": false,
472
+ "optional": false,
473
+ "docs": {
474
+ "tags": [],
475
+ "text": "False when this RELATED_VIEW's parent is related via a relation other than\nthe archetype's container slot (e.g. a person's \"memos I authored\" list,\nrelated via `author`) \u2014 mrd-layout-section resolves this from the item's\n`inverseRelation`. In that case the parent is not a valid container for a\nnew memo, and \u2014 unlike a top-level VIEW with no context at all \u2014 falling\nback to `me` would be wrong too (creating a memo \"on myself\" from someone\nelse's authored-memos list makes no sense), so \"New memo\" is hidden outright."
476
+ },
477
+ "getter": false,
478
+ "setter": false,
479
+ "reflect": false,
480
+ "attribute": "container-related",
481
+ "defaultValue": "true"
482
+ },
483
+ "showContainerLink": {
484
+ "type": "boolean",
485
+ "mutable": false,
486
+ "complexType": {
487
+ "original": "boolean",
488
+ "resolved": "boolean",
489
+ "references": {}
490
+ },
491
+ "required": false,
492
+ "optional": false,
493
+ "docs": {
494
+ "tags": [],
495
+ "text": "True unless this is a container-scoped RELATED_VIEW (a company's own memo\ntab, say) where every card's container is obviously the page you're\nalready on \u2014 forwarded to mrd-memo-list to show/hide its per-card\ncontainer link. A top-level VIEW or an author-scoped RELATED_VIEW both\nstill need it, since the container isn't implied by the page context."
496
+ },
497
+ "getter": false,
498
+ "setter": false,
499
+ "reflect": false,
500
+ "attribute": "show-container-link",
501
+ "defaultValue": "true"
502
+ },
439
503
  "viewKey": {
440
504
  "type": "string",
441
505
  "mutable": false,
@@ -521,6 +585,26 @@ export class MrdMemoContainer {
521
585
  "attribute": "read-only",
522
586
  "defaultValue": "false"
523
587
  },
588
+ "canCreate": {
589
+ "type": "boolean",
590
+ "mutable": false,
591
+ "complexType": {
592
+ "original": "boolean",
593
+ "resolved": "boolean",
594
+ "references": {}
595
+ },
596
+ "required": false,
597
+ "optional": false,
598
+ "docs": {
599
+ "tags": [],
600
+ "text": "Whether the current user may create a memo here \u2014 set by mrd-layout-section from its\ngeneric capability check (TASK-0299/FINDING-0155), the same answer the embedded table\ngets in table mode. Defaults to false (hidden) until confirmed, same as mrd-table."
601
+ },
602
+ "getter": false,
603
+ "setter": false,
604
+ "reflect": false,
605
+ "attribute": "can-create",
606
+ "defaultValue": "false"
607
+ },
524
608
  "me": {
525
609
  "type": "unknown",
526
610
  "mutable": false,
@@ -592,6 +676,21 @@ export class MrdMemoContainer {
592
676
  "resolved": "{ path: string; qs: string; aggQs: string; }",
593
677
  "references": {}
594
678
  }
679
+ }, {
680
+ "method": "mrdNavigate",
681
+ "name": "mrdNavigate",
682
+ "bubbles": true,
683
+ "cancelable": true,
684
+ "composed": true,
685
+ "docs": {
686
+ "tags": [],
687
+ "text": "A card's container link was clicked \u2014 real navigation, unrelated to the\nquick-edit dialog (mrdEdit is internal, handled by this component itself)."
688
+ },
689
+ "complexType": {
690
+ "original": "{ href?: string; label: string }",
691
+ "resolved": "{ href?: string | undefined; label: string; }",
692
+ "references": {}
693
+ }
595
694
  }, {
596
695
  "method": "mrdAction",
597
696
  "name": "mrdAction",
@@ -121,6 +121,20 @@
121
121
  content: "·";
122
122
  margin-right: var(--mrd-space-2);
123
123
  }
124
+ .mrd-memo-list__card-container {
125
+ align-self: flex-start;
126
+ background: none;
127
+ border: none;
128
+ padding: 0;
129
+ font-family: var(--mrd-font-family);
130
+ font-size: var(--mrd-font-size-xs);
131
+ font-weight: var(--mrd-font-weight-semibold);
132
+ color: var(--mrd-color-primary);
133
+ cursor: pointer;
134
+ }
135
+ .mrd-memo-list__card-container:hover {
136
+ text-decoration: underline;
137
+ }
124
138
  .mrd-memo-list__empty {
125
139
  padding: var(--mrd-space-6);
126
140
  text-align: center;