@mmlogic/components 0.5.16 → 0.5.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/loader.cjs.js +1 -1
- package/dist/cjs/mosterdcomponents.cjs.js +1 -1
- package/dist/cjs/mrd-boolean-field_28.cjs.entry.js +244 -21
- package/dist/collection/collection-manifest.json +2 -2
- package/dist/collection/components/documents/mrd-document-container/mrd-document-container.js +31 -4
- package/dist/collection/components/layout/mrd-layout-section/mrd-layout-section.js +196 -11
- package/dist/collection/components/memo/mrd-memo-container/mrd-memo-container.js +28 -4
- package/dist/collection/components/table/mrd-table/mrd-table.js +193 -4
- package/dist/collection/dev/api.js +19 -0
- package/dist/collection/dev/app.js +75 -0
- package/dist/components/mrd-document-container2.js +1 -1
- package/dist/components/mrd-layout-section.js +1 -1
- package/dist/components/mrd-memo-container2.js +1 -1
- package/dist/components/mrd-table2.js +1 -1
- package/dist/esm/loader.js +1 -1
- package/dist/esm/mosterdcomponents.js +1 -1
- package/dist/esm/mrd-boolean-field_28.entry.js +244 -21
- package/dist/mosterdcomponents/mosterdcomponents.esm.js +1 -1
- package/dist/mosterdcomponents/p-29d6dcbc.entry.js +3 -0
- package/dist/types/components/documents/mrd-document-container/mrd-document-container.d.ts +7 -0
- package/dist/types/components/layout/mrd-layout-section/mrd-layout-section.d.ts +69 -0
- package/dist/types/components/memo/mrd-memo-container/mrd-memo-container.d.ts +4 -0
- package/dist/types/components/table/mrd-table/mrd-table.d.ts +75 -0
- package/dist/types/components.d.ts +79 -0
- package/package.json +1 -1
- package/dist/mosterdcomponents/p-8a7fa81d.entry.js +0 -3
|
@@ -365,6 +365,13 @@ const MrdDocumentContainer = class {
|
|
|
365
365
|
* (with a tooltip explaining why) and the embedded table/list get it passed
|
|
366
366
|
* through so their own create/upload/drag-drop paths are disabled too. */
|
|
367
367
|
this.readOnly = false;
|
|
368
|
+
/** Whether the current user may create a document here — set by mrd-layout-section from
|
|
369
|
+
* its generic capability check (TASK-0299/FINDING-0155), the same answer the embedded
|
|
370
|
+
* table gets in list mode. Named `mayCreate` (not `canCreate`) to avoid colliding with
|
|
371
|
+
* the existing `canCreate` state below, which tracks something unrelated: whether the
|
|
372
|
+
* folder tree currently has a valid drop target selected. Defaults to false (hidden)
|
|
373
|
+
* until confirmed, same as mrd-table. */
|
|
374
|
+
this.mayCreate = false;
|
|
368
375
|
/** Active body: folder tree or flat table. */
|
|
369
376
|
this.mode = 'tree';
|
|
370
377
|
/** Whether the folder tree currently has a valid target for "New folder". */
|
|
@@ -379,7 +386,7 @@ const MrdDocumentContainer = class {
|
|
|
379
386
|
};
|
|
380
387
|
this.pickFile = () => {
|
|
381
388
|
var _a;
|
|
382
|
-
if (this.readOnly)
|
|
389
|
+
if (this.readOnly || !this.mayCreate)
|
|
383
390
|
return;
|
|
384
391
|
(_a = this.fileInputEl) === null || _a === void 0 ? void 0 : _a.click();
|
|
385
392
|
};
|
|
@@ -417,7 +424,7 @@ const MrdDocumentContainer = class {
|
|
|
417
424
|
}
|
|
418
425
|
// ── Toolbar ────────────────────────────────────────────────────────────────
|
|
419
426
|
renderToolbar() {
|
|
420
|
-
return (h("div", { class: "mrd-document-container__toolbar" }, this.mode === 'tree' && (h("button", { type: "button", class: "mrd-document-container__action", disabled: this.readOnly || !this.canCreate, title: this.readOnly ? t('readonly_access', this.locale) : undefined, onClick: this.createFolder }, h("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true" }, h("path", { d: "M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" }), h("path", { d: "M12 11v4M10 13h4" })), t('new_folder', this.locale))), this.mode === 'tree' && (h("button", { type: "button", class: "mrd-document-container__action", disabled: this.readOnly || !this.canCreate, title: this.readOnly ? t('readonly_access', this.locale) : (this.targetLabel ? `${t('doc_upload_to', this.locale)} ${this.targetLabel}` : undefined), onClick: this.pickFile }, h("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true" }, h("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }), h("path", { d: "M12 3v13M7 8l5-5 5 5" })), t('doc_upload', this.locale))), h("input", { type: "file", class: "mrd-document-container__file-input", ref: el => (this.fileInputEl = el), onChange: this.onFilePicked }), h("div", { class: "mrd-document-container__seg", role: "group", "aria-label": t('doc_view_switch', this.locale) }, h("button", { class: "mrd-document-container__seg-btn", "aria-pressed": String(this.mode === 'tree'), title: t('doc_view_tree', this.locale), "aria-label": t('doc_view_tree', this.locale), onClick: () => this.setMode('tree') }, h("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true" }, h("path", { d: "M3 5h6M3 12h9M3 19h6" }), h("circle", { cx: "18", cy: "5", r: "1.6" }), h("circle", { cx: "18", cy: "19", r: "1.6" }), h("path", { d: "M16 12h4" }))), h("button", { class: "mrd-document-container__seg-btn", "aria-pressed": String(this.mode === 'list'), title: t('doc_view_list', this.locale), "aria-label": t('doc_view_list', this.locale), onClick: () => this.setMode('list') }, h("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true" }, h("path", { d: "M8 6h13M8 12h13M8 18h13M3 6h.01M3 12h.01M3 18h.01" }))))));
|
|
427
|
+
return (h("div", { class: "mrd-document-container__toolbar" }, this.mode === 'tree' && this.mayCreate && (h("button", { type: "button", class: "mrd-document-container__action", disabled: this.readOnly || !this.canCreate, title: this.readOnly ? t('readonly_access', this.locale) : undefined, onClick: this.createFolder }, h("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true" }, h("path", { d: "M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" }), h("path", { d: "M12 11v4M10 13h4" })), t('new_folder', this.locale))), this.mode === 'tree' && this.mayCreate && (h("button", { type: "button", class: "mrd-document-container__action", disabled: this.readOnly || !this.canCreate, title: this.readOnly ? t('readonly_access', this.locale) : (this.targetLabel ? `${t('doc_upload_to', this.locale)} ${this.targetLabel}` : undefined), onClick: this.pickFile }, h("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true" }, h("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }), h("path", { d: "M12 3v13M7 8l5-5 5 5" })), t('doc_upload', this.locale))), h("input", { type: "file", class: "mrd-document-container__file-input", ref: el => (this.fileInputEl = el), onChange: this.onFilePicked }), h("div", { class: "mrd-document-container__seg", role: "group", "aria-label": t('doc_view_switch', this.locale) }, h("button", { class: "mrd-document-container__seg-btn", "aria-pressed": String(this.mode === 'tree'), title: t('doc_view_tree', this.locale), "aria-label": t('doc_view_tree', this.locale), onClick: () => this.setMode('tree') }, h("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true" }, h("path", { d: "M3 5h6M3 12h9M3 19h6" }), h("circle", { cx: "18", cy: "5", r: "1.6" }), h("circle", { cx: "18", cy: "19", r: "1.6" }), h("path", { d: "M16 12h4" }))), h("button", { class: "mrd-document-container__seg-btn", "aria-pressed": String(this.mode === 'list'), title: t('doc_view_list', this.locale), "aria-label": t('doc_view_list', this.locale), onClick: () => this.setMode('list') }, h("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true" }, h("path", { d: "M8 6h13M8 12h13M8 18h13M3 6h.01M3 12h.01M3 18h.01" }))))));
|
|
421
428
|
}
|
|
422
429
|
// ── Bodies ───────────────────────────────────────────────────────────────
|
|
423
430
|
renderTree() {
|
|
@@ -448,7 +455,7 @@ const MrdDocumentContainer = class {
|
|
|
448
455
|
} }));
|
|
449
456
|
}
|
|
450
457
|
renderList() {
|
|
451
|
-
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) => {
|
|
458
|
+
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.mayCreate, onMrdLoadPage: (e) => {
|
|
452
459
|
e.stopPropagation();
|
|
453
460
|
this.mrdLoadPage.emit(e.detail);
|
|
454
461
|
}, onMrdLoadAggregations: (e) => {
|
|
@@ -472,7 +479,7 @@ const MrdDocumentContainer = class {
|
|
|
472
479
|
}
|
|
473
480
|
render() {
|
|
474
481
|
const treeMode = this.mode === 'tree';
|
|
475
|
-
return (h(Host, { key: '
|
|
482
|
+
return (h(Host, { key: '4b674a508ac7ddf6af80a40836b72dc65f41c741' }, this.renderToolbar(), h("div", { key: '19968916f685a2e3cd075dabf170f67fb104251c', class: {
|
|
476
483
|
'mrd-document-container__body': true,
|
|
477
484
|
'mrd-document-container__body--tree': treeMode,
|
|
478
485
|
}, style: treeMode ? { height: `${this.height}px`, overflowY: 'auto' } : {} }, treeMode ? this.renderTree() : this.renderList())));
|
|
@@ -5441,6 +5448,7 @@ const MrdLayoutSection = class {
|
|
|
5441
5448
|
this.mrdViewAction = createEvent(this, "mrdViewAction");
|
|
5442
5449
|
this.mrdLoadViewAggregations = createEvent(this, "mrdLoadViewAggregations");
|
|
5443
5450
|
this.mrdLoadViewDistinct = createEvent(this, "mrdLoadViewDistinct");
|
|
5451
|
+
this.mrdViewCheckCapabilities = createEvent(this, "mrdViewCheckCapabilities");
|
|
5444
5452
|
this.mrdUpdateObject = createEvent(this, "mrdUpdateObject");
|
|
5445
5453
|
this.mrdDeleteObject = createEvent(this, "mrdDeleteObject");
|
|
5446
5454
|
this.mrdViewUpload = createEvent(this, "mrdViewUpload");
|
|
@@ -5461,6 +5469,12 @@ const MrdLayoutSection = class {
|
|
|
5461
5469
|
* create ("+") action and the documents view's New folder / Upload are disabled
|
|
5462
5470
|
* with a tooltip explaining why, so it's clear at a glance the user can't mutate data. */
|
|
5463
5471
|
this.readOnly = false;
|
|
5472
|
+
/** When false, skips the mrdViewCheckCapabilities round trip for every VIEW/RELATED_VIEW
|
|
5473
|
+
* binding and hands mrd-table/mrd-memo-container/mrd-document-container their create
|
|
5474
|
+
* affordance as allowed immediately — for a tenant with no access control configured,
|
|
5475
|
+
* the answer would always be mayCreate: true anyway (FINDING-0171 / TASK-0322). Default
|
|
5476
|
+
* `true` keeps today's ask-and-hide behaviour unchanged. */
|
|
5477
|
+
this.accessControlEnabled = true;
|
|
5464
5478
|
/** The active account's linked person record (userProfile.links.person), if any.
|
|
5465
5479
|
* Threaded to mrd-memo-container so a top-level memo VIEW (no parent record to
|
|
5466
5480
|
* attach a new memo to) can still offer "New memo" by falling back to the
|
|
@@ -5471,7 +5485,22 @@ const MrdLayoutSection = class {
|
|
|
5471
5485
|
this.imagePreviewUrl = null;
|
|
5472
5486
|
this.imagePreviews = {};
|
|
5473
5487
|
this.openHistoryField = null;
|
|
5488
|
+
/** Answers to mrdViewCheckCapabilities, keyed by view key — see emitCapabilitiesChecks().
|
|
5489
|
+
* Passed straight down as a `canCreate`/`mayCreate` prop to whichever body (mrd-table,
|
|
5490
|
+
* mrd-memo-container, mrd-document-container) is mounted for that key; a missing entry
|
|
5491
|
+
* means "not answered yet" and is treated as false (hidden), same default as mrd-table's
|
|
5492
|
+
* own standalone behaviour (TASK-0299). */
|
|
5493
|
+
this.canCreateMap = {};
|
|
5494
|
+
/** Companion to canCreateMap — concrete types the mrdViewCheckCapabilities answer allows,
|
|
5495
|
+
* when the API reports them; passed to mrd-table's `allowedTypes` prop to narrow its
|
|
5496
|
+
* create-type picker down to types a create POST would actually accept (FINDING-0176 /
|
|
5497
|
+
* TASK-0314). A missing entry means "not answered yet / not reported" — mrd-table then
|
|
5498
|
+
* leaves `item.createTypes` unfiltered, same as before this existed. */
|
|
5499
|
+
this.allowedTypesMap = {};
|
|
5474
5500
|
this.historyClickOutside = null;
|
|
5501
|
+
/** Signature last asked per view key (see emitCapabilitiesChecks()) — avoids re-emitting
|
|
5502
|
+
* mrdViewCheckCapabilities on every data/items update when the binding hasn't changed. */
|
|
5503
|
+
this.capabilitiesAsked = new Map();
|
|
5475
5504
|
this.searchTimers = {};
|
|
5476
5505
|
this.handleViewLoadPage = (e, name) => {
|
|
5477
5506
|
e.stopPropagation();
|
|
@@ -5491,6 +5520,10 @@ const MrdLayoutSection = class {
|
|
|
5491
5520
|
};
|
|
5492
5521
|
}
|
|
5493
5522
|
componentDidLoad() {
|
|
5523
|
+
// No DOM/child-hydration dependency, unlike initEmbeddedTables()/emitLoadImages() below —
|
|
5524
|
+
// run it immediately rather than deferring, so the host's answer (setViewCanCreate) can
|
|
5525
|
+
// land before the child bodies even mount.
|
|
5526
|
+
this.emitCapabilitiesChecks();
|
|
5494
5527
|
setTimeout(() => {
|
|
5495
5528
|
this.initEmbeddedTables();
|
|
5496
5529
|
this.emitLoadImages();
|
|
@@ -5509,9 +5542,15 @@ const MrdLayoutSection = class {
|
|
|
5509
5542
|
}
|
|
5510
5543
|
dataChanged(newVal) {
|
|
5511
5544
|
if (newVal && Object.keys(newVal).length > 0) {
|
|
5545
|
+
this.emitCapabilitiesChecks();
|
|
5512
5546
|
setTimeout(() => this.initEmbeddedTables(), 0);
|
|
5513
5547
|
}
|
|
5514
5548
|
}
|
|
5549
|
+
/** A host can swap `items` (e.g. a different dashboard layout) without `data` changing —
|
|
5550
|
+
* dataChanged() alone would miss that binding change. */
|
|
5551
|
+
itemsChanged() {
|
|
5552
|
+
this.emitCapabilitiesChecks();
|
|
5553
|
+
}
|
|
5515
5554
|
async initEmbeddedTables() {
|
|
5516
5555
|
const tables = this.el.querySelectorAll('mrd-table[data-view]');
|
|
5517
5556
|
for (const table of Array.from(tables)) {
|
|
@@ -5539,6 +5578,70 @@ const MrdLayoutSection = class {
|
|
|
5539
5578
|
}
|
|
5540
5579
|
}
|
|
5541
5580
|
}
|
|
5581
|
+
/** Parent record id extracted from `data._links.self.href` — the same value every
|
|
5582
|
+
* VIEW/RELATED_VIEW item on this record shares, used for RELATED_VIEW path construction
|
|
5583
|
+
* (renderRelatedView) and capability checks (emitCapabilitiesChecks). */
|
|
5584
|
+
computeParentId() {
|
|
5585
|
+
var _a, _b, _c, _d, _e;
|
|
5586
|
+
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 : '';
|
|
5587
|
+
return (_e = selfHref.split('/').filter(Boolean).pop()) !== null && _e !== void 0 ? _e : '';
|
|
5588
|
+
}
|
|
5589
|
+
/** Asks, once per VIEW/RELATED_VIEW binding, whether the current user may create a record
|
|
5590
|
+
* there — the single generic place for this check (TASK-0299/FINDING-0155), so mrd-table,
|
|
5591
|
+
* mrd-memo-container and mrd-document-container all get the answer the same way: as a plain
|
|
5592
|
+
* `canCreate`/`mayCreate` prop from this component, computed here and handed down in
|
|
5593
|
+
* renderTable()/renderMemoContainer()/renderDocumentContainer() — none of the three leaf
|
|
5594
|
+
* components need to ask on their own when rendered through mrd-layout-section (mrd-table's
|
|
5595
|
+
* own self-ask only kicks in for standalone use, i.e. its `canCreate` prop left undefined).
|
|
5596
|
+
*
|
|
5597
|
+
* The route/path variables mirror what each renderer already uses for its own rows —
|
|
5598
|
+
* `type` = `dataClass`, `refType`/`refId` = `fromClass`/parentId for a RELATED_VIEW, and
|
|
5599
|
+
* `filterClass` when the view is scoped to one subtype (the same value sent as `?type=` on
|
|
5600
|
+
* the row fetch). A signature (not just the key) is tracked per binding so a genuine route
|
|
5601
|
+
* change re-asks and hides again, without re-asking on every unrelated data/items update. */
|
|
5602
|
+
emitCapabilitiesChecks() {
|
|
5603
|
+
var _a, _b, _c, _d;
|
|
5604
|
+
if (!this.accessControlEnabled)
|
|
5605
|
+
return;
|
|
5606
|
+
const parentId = this.computeParentId();
|
|
5607
|
+
for (const item of flattenItems(this.items)) {
|
|
5608
|
+
if (item.type !== ClientLayoutItemType.VIEW && item.type !== ClientLayoutItemType.RELATED_VIEW)
|
|
5609
|
+
continue;
|
|
5610
|
+
const key = viewKeyFor(item);
|
|
5611
|
+
if (!key || !item.view)
|
|
5612
|
+
continue;
|
|
5613
|
+
const isRelatedView = item.type === ClientLayoutItemType.RELATED_VIEW;
|
|
5614
|
+
const type = (_b = (_a = item.dataClass) !== null && _a !== void 0 ? _a : item.relatedClass) !== null && _b !== void 0 ? _b : key;
|
|
5615
|
+
const refType = isRelatedView ? ((_c = item.fromClass) !== null && _c !== void 0 ? _c : '') : undefined;
|
|
5616
|
+
const refId = isRelatedView ? parentId : undefined;
|
|
5617
|
+
const filterClass = (_d = item.filterClass) !== null && _d !== void 0 ? _d : undefined;
|
|
5618
|
+
// A host binding `items` and `data` as separate properties (e.g. Angular's
|
|
5619
|
+
// `[items]="…" [data]="…"` on <mrd-layout-section>) can apply them in either
|
|
5620
|
+
// order — itemsChanged() can fire while `data` still holds its default `{}`,
|
|
5621
|
+
// before the real record (and its self href) has landed. Asking here would
|
|
5622
|
+
// emit refId: '' for a RELATED_VIEW, which a host's `refType && refId` check
|
|
5623
|
+
// reads as "no parent" and answers via the plain, unscoped capabilities route
|
|
5624
|
+
// instead of the parent-scoped one (FINDING-0175 / TASK-0316). Skip without
|
|
5625
|
+
// marking capabilitiesAsked, so the real ask fires once dataChanged() lands.
|
|
5626
|
+
if (isRelatedView && !refId)
|
|
5627
|
+
continue;
|
|
5628
|
+
const signature = JSON.stringify([type, refType, refId, filterClass]);
|
|
5629
|
+
if (this.capabilitiesAsked.get(key) === signature)
|
|
5630
|
+
continue;
|
|
5631
|
+
this.capabilitiesAsked.set(key, signature);
|
|
5632
|
+
if (key in this.canCreateMap) {
|
|
5633
|
+
const next = Object.assign({}, this.canCreateMap);
|
|
5634
|
+
delete next[key];
|
|
5635
|
+
this.canCreateMap = next;
|
|
5636
|
+
}
|
|
5637
|
+
if (key in this.allowedTypesMap) {
|
|
5638
|
+
const next = Object.assign({}, this.allowedTypesMap);
|
|
5639
|
+
delete next[key];
|
|
5640
|
+
this.allowedTypesMap = next;
|
|
5641
|
+
}
|
|
5642
|
+
this.mrdViewCheckCapabilities.emit(Object.assign(Object.assign({ name: key, type }, (refType !== undefined ? { refType, refId } : {})), (filterClass ? { filterClass } : {})));
|
|
5643
|
+
}
|
|
5644
|
+
}
|
|
5542
5645
|
/** Inject search results. Pass dataClass to target a specific SEARCH item; omit when there is only one. */
|
|
5543
5646
|
async setSearchResults(results, dataClass) {
|
|
5544
5647
|
const key = dataClass !== null && dataClass !== void 0 ? dataClass : this.resolveSearchKey();
|
|
@@ -5580,6 +5683,28 @@ const MrdLayoutSection = class {
|
|
|
5580
5683
|
return;
|
|
5581
5684
|
await table.setAggregations(data);
|
|
5582
5685
|
}
|
|
5686
|
+
/** Host answer to mrdViewCheckCapabilities — whether the current user may create a record
|
|
5687
|
+
* for the given view's bound route. `name` is the view key from the event detail. Stored in
|
|
5688
|
+
* canCreateMap and passed down as a plain prop to whichever body is mounted for that key
|
|
5689
|
+
* (mrd-table, mrd-memo-container or mrd-document-container) — no DOM lookup needed, unlike
|
|
5690
|
+
* setViewPage/setViewAggregations, since the prop flows through the next render. */
|
|
5691
|
+
async setViewCanCreate(name, value, allowedTypes) {
|
|
5692
|
+
this.canCreateMap = Object.assign(Object.assign({}, this.canCreateMap), { [name]: value });
|
|
5693
|
+
this.allowedTypesMap = Object.assign(Object.assign({}, this.allowedTypesMap), { [name]: allowedTypes });
|
|
5694
|
+
}
|
|
5695
|
+
/** `canCreateMap[key]` when access control is configured (default); always allowed
|
|
5696
|
+
* when `accessControlEnabled` is false, since emitCapabilitiesChecks() never asked
|
|
5697
|
+
* and canCreateMap[key] would otherwise stay permanently unanswered. */
|
|
5698
|
+
effectiveCanCreate(key) {
|
|
5699
|
+
var _a;
|
|
5700
|
+
return this.accessControlEnabled ? ((_a = this.canCreateMap[key]) !== null && _a !== void 0 ? _a : false) : true;
|
|
5701
|
+
}
|
|
5702
|
+
/** `allowedTypesMap[key]` when access control is configured; `undefined` (don't filter)
|
|
5703
|
+
* when disabled, since emitCapabilitiesChecks() never asked and the map entry would
|
|
5704
|
+
* otherwise stay permanently unanswered — see effectiveCanCreate(). */
|
|
5705
|
+
effectiveAllowedTypes(key) {
|
|
5706
|
+
return this.accessControlEnabled ? this.allowedTypesMap[key] : undefined;
|
|
5707
|
+
}
|
|
5583
5708
|
/** Inject distinct values into an embedded mrd-document-list for a document view. */
|
|
5584
5709
|
async setViewDistinct(name, nodeId, values) {
|
|
5585
5710
|
const list = this.el.querySelector(`mrd-document-list[data-doclist="${name}"]`);
|
|
@@ -5667,20 +5792,19 @@ const MrdLayoutSection = class {
|
|
|
5667
5792
|
return (h("div", { class: "mrd-layout-section__search", key: `search-${dataClass}` }, h("div", { class: "mrd-layout-section__search-wrap" }, h("svg", { class: "mrd-layout-section__search-icon", xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 20 20", fill: "currentColor", "aria-hidden": "true" }, h("path", { "fill-rule": "evenodd", d: "M9 3.5a5.5 5.5 0 100 11 5.5 5.5 0 000-11zM2 9a7 7 0 1112.452 4.391l3.328 3.329a.75.75 0 11-1.06 1.06l-3.329-3.328A7 7 0 012 9z", "clip-rule": "evenodd" })), h("input", { class: "mrd-layout-section__search-input", type: "text", value: query, placeholder: (_c = item.label) !== null && _c !== void 0 ? _c : '', onInput: e => this.handleSearchInput(dataClass, e.target.value) })), results.length > 0 && (h("ul", { class: "mrd-layout-section__search-results" }, results.map(r => (h("li", { key: r.id, class: "mrd-layout-section__search-result" }, h("button", { class: "mrd-layout-section__search-result-btn", onClick: () => this.mrdNavigate.emit({ href: r.id, label: r.label }) }, h("span", { class: "mrd-layout-section__search-result-label" }, r.label), r.description && h("span", { class: "mrd-layout-section__search-result-desc" }, r.description)))))))));
|
|
5668
5793
|
}
|
|
5669
5794
|
renderRelatedView(item) {
|
|
5670
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k
|
|
5795
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
|
5671
5796
|
const key = viewKeyFor(item);
|
|
5672
5797
|
if (!key)
|
|
5673
5798
|
return null;
|
|
5674
5799
|
if (!item.view)
|
|
5675
5800
|
return null;
|
|
5676
5801
|
const showTitle = (_a = item.showTitle) !== null && _a !== void 0 ? _a : false;
|
|
5677
|
-
// Extract parentId from data._links.self.href for RELATED_VIEW path construction
|
|
5678
5802
|
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 : '';
|
|
5679
|
-
const parentId =
|
|
5803
|
+
const parentId = this.computeParentId();
|
|
5680
5804
|
// The API publishes `archetypes` on the VIEW/RELATED_VIEW item itself
|
|
5681
5805
|
// (sibling of `view`), not inside `view`.
|
|
5682
|
-
const docArchetype = resolveArchetype((
|
|
5683
|
-
const memoArchetype = !docArchetype ? resolveArchetype((
|
|
5806
|
+
const docArchetype = resolveArchetype((_f = item.archetypes) !== null && _f !== void 0 ? _f : (_g = item.view) === null || _g === void 0 ? void 0 : _g.archetypes, ARCHETYPE_DOCUMENT);
|
|
5807
|
+
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;
|
|
5684
5808
|
// Canonical parent href for single-container seeding: use the RELATED_VIEW's
|
|
5685
5809
|
// fromClass (base class) rather than the route class, so polymorphic subtypes
|
|
5686
5810
|
// resolve to the same container the documents actually reference.
|
|
@@ -5691,7 +5815,7 @@ const MrdLayoutSection = class {
|
|
|
5691
5815
|
// back to this parent; only when it matches the container slot does the
|
|
5692
5816
|
// parent's own href double as the memo's container.
|
|
5693
5817
|
const memoContainerRelated = item.type !== ClientLayoutItemType.RELATED_VIEW
|
|
5694
|
-
|| item.inverseRelation === ((
|
|
5818
|
+
|| item.inverseRelation === ((_k = memoArchetype === null || memoArchetype === void 0 ? void 0 : memoArchetype.slots) === null || _k === void 0 ? void 0 : _k.container);
|
|
5695
5819
|
const memoContainerHref = memoContainerRelated ? containerHref : '';
|
|
5696
5820
|
// Distinct from memoContainerRelated: only a genuine container-scoped
|
|
5697
5821
|
// RELATED_VIEW (a company's own memo tab) makes every card's container
|
|
@@ -5704,7 +5828,7 @@ const MrdLayoutSection = class {
|
|
|
5704
5828
|
* swaps between the folder tree and the flat table. Events are re-emitted to
|
|
5705
5829
|
* the host with the view key, exactly as the raw list/table events were. */
|
|
5706
5830
|
renderDocumentContainer(item, key, parentId, archetype, containerHref) {
|
|
5707
|
-
return (h("mrd-document-container", { item: item, archetype: archetype, parentId: parentId, containerHref: containerHref, viewKey: key, locale: this.locale, readOnly: this.readOnly, onMrdLoadDistinct: (e) => {
|
|
5831
|
+
return (h("mrd-document-container", { item: item, archetype: archetype, parentId: parentId, containerHref: containerHref, viewKey: key, locale: this.locale, readOnly: this.readOnly, mayCreate: this.effectiveCanCreate(key), onMrdLoadDistinct: (e) => {
|
|
5708
5832
|
e.stopPropagation();
|
|
5709
5833
|
this.mrdLoadViewDistinct.emit(Object.assign({ name: key }, e.detail));
|
|
5710
5834
|
}, onMrdLoadPage: (e) => {
|
|
@@ -5741,7 +5865,7 @@ const MrdLayoutSection = class {
|
|
|
5741
5865
|
renderMemoContainer(item, key, parentId, archetype, containerHref, containerRelated, showContainerLink) {
|
|
5742
5866
|
var _a, _b, _c, _d;
|
|
5743
5867
|
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;
|
|
5744
|
-
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, me: this.me, labelsListItems: labelsListItems, onMrdNavigate: (e) => {
|
|
5868
|
+
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: this.effectiveCanCreate(key), me: this.me, labelsListItems: labelsListItems, onMrdNavigate: (e) => {
|
|
5745
5869
|
var _a;
|
|
5746
5870
|
e.stopPropagation();
|
|
5747
5871
|
this.mrdNavigate.emit(Object.assign(Object.assign({}, e.detail), { navigateBehaviour: (_a = item.navigateBehaviour) !== null && _a !== void 0 ? _a : ClientLayoutNavigateBehaviour.PAGE }));
|
|
@@ -5768,7 +5892,7 @@ const MrdLayoutSection = class {
|
|
|
5768
5892
|
} }));
|
|
5769
5893
|
}
|
|
5770
5894
|
renderTable(item, key, parentId) {
|
|
5771
|
-
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) => {
|
|
5895
|
+
return (h("mrd-table", { "data-view": key, item: item, parentId: parentId, locale: this.locale, readOnly: this.readOnly, canCreate: this.effectiveCanCreate(key), allowedTypes: this.effectiveAllowedTypes(key), accessControlEnabled: this.accessControlEnabled, onMrdLoadPage: (e) => this.handleViewLoadPage(e, key), onMrdLoadAggregations: (e) => {
|
|
5772
5896
|
var _a;
|
|
5773
5897
|
e.stopPropagation();
|
|
5774
5898
|
this.mrdLoadViewAggregations.emit(Object.assign({ name: key, dataClass: (_a = item.dataClass) !== null && _a !== void 0 ? _a : key }, e.detail));
|
|
@@ -5906,12 +6030,15 @@ const MrdLayoutSection = class {
|
|
|
5906
6030
|
const emailArchetype = !docArchetype && !invoiceArchetype ? resolveArchetype(this.archetypes, ARCHETYPE_EMAIL) : undefined;
|
|
5907
6031
|
const memoArchetype = !docArchetype && !invoiceArchetype && !emailArchetype ? resolveArchetype(this.archetypes, ARCHETYPE_MEMO) : undefined;
|
|
5908
6032
|
const anyArchetype = docArchetype || invoiceArchetype || emailArchetype || memoArchetype;
|
|
5909
|
-
return (h(Host, { key: '
|
|
6033
|
+
return (h(Host, { key: '97351c57865614662e888b3c0278a61d866a74e1' }, h("div", { key: '43e8f0fda8fcd3300b879d05ac692ce331c896d5', 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()));
|
|
5910
6034
|
}
|
|
5911
6035
|
get el() { return getElement(this); }
|
|
5912
6036
|
static get watchers() { return {
|
|
5913
6037
|
"data": [{
|
|
5914
6038
|
"dataChanged": 0
|
|
6039
|
+
}],
|
|
6040
|
+
"items": [{
|
|
6041
|
+
"itemsChanged": 0
|
|
5915
6042
|
}]
|
|
5916
6043
|
}; }
|
|
5917
6044
|
};
|
|
@@ -6073,6 +6200,10 @@ const MrdMemoContainer = class {
|
|
|
6073
6200
|
* tooltip, cards/rows are not clickable, and the embedded table's own
|
|
6074
6201
|
* create action is disabled too. */
|
|
6075
6202
|
this.readOnly = false;
|
|
6203
|
+
/** Whether the current user may create a memo here — set by mrd-layout-section from its
|
|
6204
|
+
* generic capability check (TASK-0299/FINDING-0155), the same answer the embedded table
|
|
6205
|
+
* gets in table mode. Defaults to false (hidden) until confirmed, same as mrd-table. */
|
|
6206
|
+
this.canCreate = false;
|
|
6076
6207
|
/** The active account's linked person record (userProfile.links.person), if any.
|
|
6077
6208
|
* Used both to gate "New memo" (see effectiveContainerHref) and, on submit, to
|
|
6078
6209
|
* fill the author slot. */
|
|
@@ -6305,7 +6436,7 @@ const MrdMemoContainer = class {
|
|
|
6305
6436
|
* the dialog), so without it there is nothing valid to create — regardless
|
|
6306
6437
|
* of whether a parent record already supplies a container. */
|
|
6307
6438
|
get canAdd() {
|
|
6308
|
-
return !this.readOnly && !!this.meHref() && !!this.effectiveContainerHref;
|
|
6439
|
+
return this.canCreate && !this.readOnly && !!this.meHref() && !!this.effectiveContainerHref;
|
|
6309
6440
|
}
|
|
6310
6441
|
/** Path for creating a new memo: `/{dataClass}` for a top-level VIEW, or
|
|
6311
6442
|
* `/{fromClass}/{parentId}/{dataClass}` when nested under a parent record. */
|
|
@@ -6319,7 +6450,7 @@ const MrdMemoContainer = class {
|
|
|
6319
6450
|
});
|
|
6320
6451
|
}
|
|
6321
6452
|
renderToolbar() {
|
|
6322
|
-
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" }))))));
|
|
6453
|
+
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" }))))));
|
|
6323
6454
|
}
|
|
6324
6455
|
renderDeleteConfirm() {
|
|
6325
6456
|
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)))));
|
|
@@ -6356,7 +6487,7 @@ const MrdMemoContainer = class {
|
|
|
6356
6487
|
} }));
|
|
6357
6488
|
}
|
|
6358
6489
|
renderTable() {
|
|
6359
|
-
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) => {
|
|
6490
|
+
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) => {
|
|
6360
6491
|
e.stopPropagation();
|
|
6361
6492
|
this.mrdLoadPage.emit(e.detail);
|
|
6362
6493
|
}, onMrdLoadAggregations: (e) => {
|
|
@@ -6371,7 +6502,7 @@ const MrdMemoContainer = class {
|
|
|
6371
6502
|
} }));
|
|
6372
6503
|
}
|
|
6373
6504
|
render() {
|
|
6374
|
-
return (h(Host, { key: '
|
|
6505
|
+
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()));
|
|
6375
6506
|
}
|
|
6376
6507
|
static get watchers() { return {
|
|
6377
6508
|
"dialogOpen": [{
|
|
@@ -7503,6 +7634,7 @@ const MrdTable = class {
|
|
|
7503
7634
|
this.mrdAction = createEvent(this, "mrdAction");
|
|
7504
7635
|
this.mrdDownload = createEvent(this, "mrdDownload");
|
|
7505
7636
|
this.mrdLoadAggregations = createEvent(this, "mrdLoadAggregations");
|
|
7637
|
+
this.mrdCheckCapabilities = createEvent(this, "mrdCheckCapabilities");
|
|
7506
7638
|
// ── Non-state internals ────────────────────────────────────────────────────
|
|
7507
7639
|
this.pendingPages = new Set();
|
|
7508
7640
|
this.debounceTimer = null;
|
|
@@ -7564,6 +7696,12 @@ const MrdTable = class {
|
|
|
7564
7696
|
* explaining why) so a viewer-role user can see at a glance that this dashboard
|
|
7565
7697
|
* is read-only. Sorting, filtering and export stay available — those don't mutate data. */
|
|
7566
7698
|
this.readOnly = false;
|
|
7699
|
+
/** When false, skips the standalone mrdCheckCapabilities round trip entirely and renders
|
|
7700
|
+
* the create action immediately as allowed — for a tenant that has no access control
|
|
7701
|
+
* configured, the answer would always be true anyway (FINDING-0171 / TASK-0322). Ignored
|
|
7702
|
+
* while `canCreate` is set explicitly (the host already controls the answer directly in
|
|
7703
|
+
* that mode). Default `true` keeps today's ask-and-hide behaviour unchanged. */
|
|
7704
|
+
this.accessControlEnabled = true;
|
|
7567
7705
|
// ── Internal state ─────────────────────────────────────────────────────────
|
|
7568
7706
|
/** Index into allViews[] for the currently displayed view. 0 = primary, 1+ = alternatives. */
|
|
7569
7707
|
this.activeViewIdx = 0;
|
|
@@ -7608,6 +7746,13 @@ const MrdTable = class {
|
|
|
7608
7746
|
this.aggregationsPending = false;
|
|
7609
7747
|
/** Lower bound on total derived from setPage() hasNext info; grows as pages load. */
|
|
7610
7748
|
this.minKnownTotal = 0;
|
|
7749
|
+
/** Self-asked answer for standalone use (the `canCreate` prop is undefined) — defaults to
|
|
7750
|
+
* hidden (TASK-0299) until the host answers mrdCheckCapabilities via setCanCreate(). Ignored
|
|
7751
|
+
* when the host controls `canCreate` directly; see effectiveCanCreate(). */
|
|
7752
|
+
this.selfCanCreate = false;
|
|
7753
|
+
/** Self-asked companion to selfCanCreate — the concrete types setCanCreate() reported allowed,
|
|
7754
|
+
* or `undefined` while unanswered / when the host's mrdCheckCapabilities answer omitted it. */
|
|
7755
|
+
this.selfAllowedTypes = undefined;
|
|
7611
7756
|
this.handleScroll = (e) => {
|
|
7612
7757
|
const scroller = e.currentTarget;
|
|
7613
7758
|
const { renderStart, renderEnd } = computeWindow(scroller.scrollTop, this.rowHeight, this.tableHeight, this.baseTotal);
|
|
@@ -7642,6 +7787,14 @@ const MrdTable = class {
|
|
|
7642
7787
|
this.searchOpen = false;
|
|
7643
7788
|
this.searchQuery = '';
|
|
7644
7789
|
this.stashedFilters = null;
|
|
7790
|
+
this.emitCapabilitiesCheck();
|
|
7791
|
+
}
|
|
7792
|
+
/** A parent-record navigation can reuse the same `item` reference while only `parentId`
|
|
7793
|
+
* changes (e.g. clicking a different dossier for the same RELATED_VIEW layout) — itemChanged()
|
|
7794
|
+
* would not fire for that. Re-ask so a stale canCreate answer from the previous parent never
|
|
7795
|
+
* leaks into the new one. */
|
|
7796
|
+
parentIdChanged() {
|
|
7797
|
+
this.emitCapabilitiesCheck();
|
|
7645
7798
|
}
|
|
7646
7799
|
// ── Lifecycle ──────────────────────────────────────────────────────────────
|
|
7647
7800
|
componentWillLoad() {
|
|
@@ -7649,6 +7802,7 @@ const MrdTable = class {
|
|
|
7649
7802
|
this.applyDefaultSort((_c = (_b = (_a = this.item) === null || _a === void 0 ? void 0 : _a.view) === null || _b === void 0 ? void 0 : _b.defaultSort) !== null && _c !== void 0 ? _c : '');
|
|
7650
7803
|
}
|
|
7651
7804
|
componentDidLoad() {
|
|
7805
|
+
this.emitCapabilitiesCheck();
|
|
7652
7806
|
this.staleCheckTimer = setInterval(() => this.checkStalePages(), STALE_CHECK_INTERVAL_MS);
|
|
7653
7807
|
// A table inside a hidden tab pane renders and loads page 0 without ever having a box, so
|
|
7654
7808
|
// the width freeze cannot measure anything. ResizeObserver fires the moment the pane is
|
|
@@ -7767,6 +7921,15 @@ const MrdTable = class {
|
|
|
7767
7921
|
// Fresh totals can be wider than the ones the columns were checked against.
|
|
7768
7922
|
this.fixedContentMeasured = false;
|
|
7769
7923
|
}
|
|
7924
|
+
/** Host answer to mrdCheckCapabilities — whether the current user may create a record for
|
|
7925
|
+
* the table's currently bound route (standalone use only; ignored while the `canCreate`
|
|
7926
|
+
* prop is set, since the host controls it directly in that mode). Call it any time after
|
|
7927
|
+
* the event fires; a stale answer for an old binding is discarded automatically
|
|
7928
|
+
* (selfCanCreate is reset to false before every new mrdCheckCapabilities emission). */
|
|
7929
|
+
async setCanCreate(value, allowedTypes) {
|
|
7930
|
+
this.selfCanCreate = value;
|
|
7931
|
+
this.selfAllowedTypes = allowedTypes;
|
|
7932
|
+
}
|
|
7770
7933
|
// ── Lifecycle ──────────────────────────────────────────────────────────────
|
|
7771
7934
|
disconnectedCallback() {
|
|
7772
7935
|
if (this.resizeObserver) {
|
|
@@ -8126,6 +8289,33 @@ const MrdTable = class {
|
|
|
8126
8289
|
dataClass: v.dataClass,
|
|
8127
8290
|
});
|
|
8128
8291
|
}
|
|
8292
|
+
/** Asks the host whether creating is allowed for the table's current binding (item, parent
|
|
8293
|
+
* and active view) — only in standalone use (`canCreate` prop undefined). A host that
|
|
8294
|
+
* controls `canCreate` directly already knows the answer, so the table never asks on its
|
|
8295
|
+
* own in that mode. Resets selfCanCreate to false first so the add button stays hidden for
|
|
8296
|
+
* the new binding until the host answers — it never inherits the previous binding's answer. */
|
|
8297
|
+
emitCapabilitiesCheck() {
|
|
8298
|
+
var _a, _b;
|
|
8299
|
+
if (this.canCreate !== undefined)
|
|
8300
|
+
return;
|
|
8301
|
+
if (!this.accessControlEnabled)
|
|
8302
|
+
return;
|
|
8303
|
+
this.selfCanCreate = false;
|
|
8304
|
+
this.selfAllowedTypes = undefined;
|
|
8305
|
+
const v = this.allViews[this.activeViewIdx];
|
|
8306
|
+
if (!(v === null || v === void 0 ? void 0 : v.dataClass))
|
|
8307
|
+
return;
|
|
8308
|
+
const isRelatedView = ((_a = this.item) === null || _a === void 0 ? void 0 : _a.type) === 'RELATED_VIEW';
|
|
8309
|
+
// A host binding `item` and `parentId` as separate properties (e.g. Angular) can apply
|
|
8310
|
+
// them in either order — this can run while `parentId` still holds its default `''`,
|
|
8311
|
+
// before the real parent record has landed. Asking here would emit refId: '' for a
|
|
8312
|
+
// RELATED_VIEW, which a host's `refType && refId` check reads as "no parent" and answers
|
|
8313
|
+
// via the plain, unscoped capabilities route instead of the parent-scoped one
|
|
8314
|
+
// (FINDING-0175 / TASK-0316). Skip; parentIdChanged() re-asks once it lands.
|
|
8315
|
+
if (isRelatedView && !this.parentId)
|
|
8316
|
+
return;
|
|
8317
|
+
this.mrdCheckCapabilities.emit(Object.assign(Object.assign({ type: v.dataClass }, (isRelatedView ? { refType: (_b = v.fromClass) !== null && _b !== void 0 ? _b : '', refId: this.parentId } : {})), (v.filterClass ? { filterClass: v.filterClass } : {})));
|
|
8318
|
+
}
|
|
8129
8319
|
/** Build query params for a page request from current sort, view filters, filterClass and active column filters.
|
|
8130
8320
|
*
|
|
8131
8321
|
* `q` and column filters are mutually exclusive on the backend — combining them breaks
|
|
@@ -8160,11 +8350,41 @@ const MrdTable = class {
|
|
|
8160
8350
|
var _a, _b, _c;
|
|
8161
8351
|
return ((_c = (_b = (_a = this.allViews[this.activeViewIdx]) === null || _a === void 0 ? void 0 : _a.view) === null || _b === void 0 ? void 0 : _b.values) !== null && _c !== void 0 ? _c : []);
|
|
8162
8352
|
}
|
|
8353
|
+
/** `canCreate` when the host controls it directly; else, when access control is disabled,
|
|
8354
|
+
* always allowed; else the table's own self-asked answer. */
|
|
8355
|
+
get effectiveCanCreate() {
|
|
8356
|
+
if (this.canCreate !== undefined)
|
|
8357
|
+
return this.canCreate;
|
|
8358
|
+
if (!this.accessControlEnabled)
|
|
8359
|
+
return true;
|
|
8360
|
+
return this.selfCanCreate;
|
|
8361
|
+
}
|
|
8362
|
+
/** `allowedTypes` when the host controls it directly, else the table's own self-asked
|
|
8363
|
+
* answer. `undefined` means "unknown — don't filter", not "nothing allowed". */
|
|
8364
|
+
get effectiveAllowedTypes() {
|
|
8365
|
+
if (this.allowedTypes !== undefined)
|
|
8366
|
+
return this.allowedTypes;
|
|
8367
|
+
return this.selfAllowedTypes;
|
|
8368
|
+
}
|
|
8369
|
+
/** `item.createTypes` intersected with effectiveAllowedTypes when that is known, so the
|
|
8370
|
+
* create-type picker only offers concrete types a create POST would actually accept
|
|
8371
|
+
* (FINDING-0176 / TASK-0314). Unfiltered when the answer isn't known (undefined) — an
|
|
8372
|
+
* older host/API that never reports allowedTypes keeps today's unfiltered behaviour. */
|
|
8373
|
+
get effectiveCreateTypes() {
|
|
8374
|
+
var _a;
|
|
8375
|
+
const all = (_a = this.item) === null || _a === void 0 ? void 0 : _a.createTypes;
|
|
8376
|
+
const allowed = this.effectiveAllowedTypes;
|
|
8377
|
+
if (!all || allowed == null)
|
|
8378
|
+
return all;
|
|
8379
|
+
return all.filter(ct => allowed.includes(ct.type));
|
|
8380
|
+
}
|
|
8163
8381
|
get tableActions() {
|
|
8164
8382
|
var _a, _b;
|
|
8165
8383
|
const raw = (_b = (_a = this.item) === null || _a === void 0 ? void 0 : _a.actions) !== null && _b !== void 0 ? _b : [];
|
|
8166
8384
|
return (raw !== null && raw !== void 0 ? raw : []).reduce((acc, a) => {
|
|
8167
|
-
|
|
8385
|
+
// Hidden (not just disabled) until creating is confirmed allowed for this binding —
|
|
8386
|
+
// see effectiveCanCreate() / emitCapabilitiesCheck() (TASK-0299).
|
|
8387
|
+
if (a === 'NEW' && this.effectiveCanCreate)
|
|
8168
8388
|
acc.push({ action: 'create', label: t('table_new_record', this.locale), icon: 'assets/sprites.svg#icon-plus', variant: 'primary', disabled: this.readOnly });
|
|
8169
8389
|
if (a === 'EXPORT')
|
|
8170
8390
|
acc.push({ action: 'export', label: t('table_export_excel', this.locale), icon: 'assets/sprites.svg#icon-file-excel' });
|
|
@@ -8572,6 +8792,7 @@ const MrdTable = class {
|
|
|
8572
8792
|
this.searchOpen = false;
|
|
8573
8793
|
this.searchQuery = '';
|
|
8574
8794
|
this.stashedFilters = null;
|
|
8795
|
+
this.emitCapabilitiesCheck();
|
|
8575
8796
|
this.init();
|
|
8576
8797
|
}
|
|
8577
8798
|
toggleViewPopover(e) {
|
|
@@ -8593,8 +8814,7 @@ const MrdTable = class {
|
|
|
8593
8814
|
}
|
|
8594
8815
|
// ── Render: toolbar ────────────────────────────────────────────────────────
|
|
8595
8816
|
renderToolbar() {
|
|
8596
|
-
|
|
8597
|
-
return (h(Toolbar, { locale: this.locale, filterCount: this.activeFilters.size, actions: this.tableActions, views: this.allViews, activeViewIdx: this.activeViewIdx, viewPopoverOpen: this.viewPopoverOpen, createPickerOpen: this.createPickerOpen, createTypes: (_a = this.item) === null || _a === void 0 ? void 0 : _a.createTypes, readOnly: this.readOnly, searchEnabled: this.searchEnabled, searchOpen: this.searchOpen, searchQuery: this.searchQuery, cb: {
|
|
8817
|
+
return (h(Toolbar, { locale: this.locale, filterCount: this.activeFilters.size, actions: this.tableActions, views: this.allViews, activeViewIdx: this.activeViewIdx, viewPopoverOpen: this.viewPopoverOpen, createPickerOpen: this.createPickerOpen, createTypes: this.effectiveCreateTypes, readOnly: this.readOnly, searchEnabled: this.searchEnabled, searchOpen: this.searchOpen, searchQuery: this.searchQuery, cb: {
|
|
8598
8818
|
onClearAllFilters: () => this.clearAllFilters(),
|
|
8599
8819
|
onViewSwitch: idx => this.handleViewSwitch(idx),
|
|
8600
8820
|
onToggleViewPopover: e => this.toggleViewPopover(e),
|
|
@@ -8749,6 +8969,9 @@ const MrdTable = class {
|
|
|
8749
8969
|
}],
|
|
8750
8970
|
"item": [{
|
|
8751
8971
|
"itemChanged": 0
|
|
8972
|
+
}],
|
|
8973
|
+
"parentId": [{
|
|
8974
|
+
"parentIdChanged": 0
|
|
8752
8975
|
}]
|
|
8753
8976
|
}; }
|
|
8754
8977
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{p as e,b as a}from"./p-CDiBvvJU.js";export{s as setNonce}from"./p-CDiBvvJU.js";import{g as l}from"./p-DQuL1Twl.js";(()=>{const a=import.meta.url,l={};return""!==a&&(l.resourcesUrl=new URL(".",a).href),e(l)})().then((async e=>(await l(),a([["p-e68dfbb0",[[2,"mrd-merge-view",{locale:[1],basicType:[1,"basic-type"],mostSignificantClass:[1,"most-significant-class"],fields:[16],currentHref:[1,"current-href"],currentRecord:[16],currentLabel:[1,"current-label"],step:[32],searchQuery:[32],searchResults:[32],isLoading:[32],candidate:[32],resolutions:[32],openHistoryKey:[32],setSearchResults:[64],setLoading:[64]}]]],["p-
|
|
1
|
+
import{p as e,b as a}from"./p-CDiBvvJU.js";export{s as setNonce}from"./p-CDiBvvJU.js";import{g as l}from"./p-DQuL1Twl.js";(()=>{const a=import.meta.url,l={};return""!==a&&(l.resourcesUrl=new URL(".",a).href),e(l)})().then((async e=>(await l(),a([["p-e68dfbb0",[[2,"mrd-merge-view",{locale:[1],basicType:[1,"basic-type"],mostSignificantClass:[1,"most-significant-class"],fields:[16],currentHref:[1,"current-href"],currentRecord:[16],currentLabel:[1,"current-label"],step:[32],searchQuery:[32],searchResults:[32],isLoading:[32],candidate:[32],resolutions:[32],openHistoryKey:[32],setSearchResults:[64],setLoading:[64]}]]],["p-29d6dcbc",[[2,"mrd-form",{layout:[16],locale:[1],values:[16],referenceHref:[1,"reference-href"],referenceClass:[1,"reference-class"],showCancel:[4,"show-cancel"],showDelete:[4,"show-delete"],me:[16],formValues:[32],errors:[32],submitted:[32],setFieldValue:[64]},null,{values:[{valuesChanged:0}],me:[{meChanged:0}]}],[2,"mrd-layout-section",{items:[16],data:[16],views:[16],links:[16],locale:[1],archetypes:[16],readOnly:[4,"read-only"],accessControlEnabled:[4,"access-control-enabled"],me:[16],searchQueryMap:[32],searchResultsMap:[32],imagePreviewUrl:[32],imagePreviews:[32],openHistoryField:[32],canCreateMap:[32],allowedTypesMap:[32],setSearchResults:[64],setViewPage:[64],setViewAggregations:[64],setViewCanCreate:[64],setViewDistinct:[64],setViewFileReference:[64],setViewCreatedObject:[64],setImagePreview:[64],openImagePreview:[64]},null,{data:[{dataChanged:0}],items:[{itemsChanged:0}]}],[2,"mrd-field",{item:[16],locale:[1],value:[16],historyEntries:[32],currentValue:[32],multiValues:[32],multiError:[32]},null,{value:[{valueChanged:0}]}],[2,"mrd-memo-container",{item:[16],archetype:[16],parentId:[1,"parent-id"],containerHref:[1,"container-href"],containerRelated:[4,"container-related"],showContainerLink:[4,"show-container-link"],viewKey:[1,"view-key"],locale:[1],labelsListItems:[16],readOnly:[4,"read-only"],canCreate:[4,"can-create"],me:[16],mode:[32],dialogOpen:[32],editingHref:[32],dialogBody:[32],dialogTitle:[32],dialogLabels:[32],dialogImportant:[32],dialogTouched:[32],deleteConfirmOpen:[32],refresh:[64]},null,{dialogOpen:[{dialogOpenChanged:0}]}],[2,"mrd-document-container",{item:[16],archetype:[16],parentId:[1,"parent-id"],containerHref:[1,"container-href"],viewKey:[1,"view-key"],height:[2],locale:[1],readOnly:[4,"read-only"],mayCreate:[4,"may-create"],mode:[32],canCreate:[32],targetLabel:[32]}],[2,"mrd-document-view",{archetype:[16],data:[16],locale:[1]},null,{data:[{dataChanged:0}]}],[2,"mrd-email-view",{archetype:[16],data:[16],locale:[1]},null,{data:[{dataChanged:0}]}],[262,"mrd-invoice-view",{archetype:[16],data:[16],locale:[1],statusListItems:[16]},null,{data:[{dataChanged:0}]}],[2,"mrd-memo-view",{archetype:[16],data:[16],locale:[1],labelsListItems:[16]}],[2,"mrd-currency-field",{name:[1],label:[1],value:[16],required:[4],disabled:[4],locale:[1],amountDisplay:[32],currency:[32],error:[32]},null,{value:[{valueChanged:0}],locale:[{localeChanged:0}]}],[2,"mrd-date-field",{name:[1],label:[1],value:[1],required:[4],disabled:[4],locale:[1],error:[32]}],[2,"mrd-datetime-field",{name:[1],label:[1],value:[1],required:[4],disabled:[4],locale:[1],error:[32],localValue:[32]},null,{value:[{valueChanged:0}]}],[2,"mrd-document-list",{item:[16],archetype:[16],parentId:[1,"parent-id"],containerHref:[1,"container-href"],locale:[1],readOnly:[4,"read-only"],containers:[32],rootLoading:[32],singleContainer:[32],selectedId:[32],dropTargetId:[32],uploadingNodeId:[32],rootDropActive:[32],setDistinct:[64],setDocuments:[64],createFolder:[64],uploadFile:[64],setFileReference:[64],setCreatedHref:[64]}],[2,"mrd-email-field",{name:[1],label:[1],value:[1],placeholder:[1],required:[4],disabled:[4],locale:[1],error:[32]}],[2,"mrd-file-field",{name:[1],label:[1],value:[16],required:[4],disabled:[4],locale:[1],accept:[1],maxSize:[2,"max-size"],multiple:[4],entries:[32],isDragging:[32],error:[32]},null,{value:[{valueChanged:0}]}],[2,"mrd-hyperlink-field",{name:[1],label:[1],value:[1],placeholder:[1],required:[4],disabled:[4],locale:[1],internalHref:[32],internalName:[32],error:[32]},null,{value:[{valueChanged:0}]}],[2,"mrd-image-field",{name:[1],label:[1],value:[16],required:[4],disabled:[4],locale:[1],accept:[1],maxSize:[2,"max-size"],multiple:[4],entries:[32],isDragging:[32],error:[32]},null,{value:[{valueChanged:0}]}],[2,"mrd-longtext-field",{name:[1],label:[1],value:[1],placeholder:[1],required:[4],disabled:[4],locale:[1],error:[32]}],[2,"mrd-memo-list",{item:[16],archetype:[16],parentId:[1,"parent-id"],pageSize:[2,"page-size"],locale:[1],readOnly:[4,"read-only"],labelsListItems:[16],showContainerLink:[4,"show-container-link"],rows:[32],loading:[32],exhausted:[32],started:[32],expandedHrefs:[32],init:[64],setPage:[64],patchRow:[64],removeRow:[64]}],[2,"mrd-number-field",{name:[1],label:[1],value:[2],placeholder:[1],required:[4],disabled:[4],locale:[1],dataType:[1,"data-type"],decimalPrecision:[2,"decimal-precision"],displayValue:[32],error:[32]},null,{value:[{valueChanged:0}],locale:[{localeChanged:0}]}],[2,"mrd-relation-field",{name:[1],label:[1],required:[4],disabled:[4],locale:[1],relatedClass:[1,"related-class"],mostSignificantClass:[1,"most-significant-class"],displayType:[1,"display-type"],editBehavior:[1,"edit-behavior"],commonRelation:[1,"common-relation"],multiple:[4],dropdownValues:[16],value:[1],searchQuery:[32],searchResults:[32],allRecords:[32],isLoading:[32],selectedItems:[32],showResults:[32],error:[32],highlightedIndex:[32],setAllRecords:[64],setSearchResults:[64],setLoading:[64]},null,{allRecords:[{allRecordsChanged:0}],value:[{valueChanged:0}]}],[2,"mrd-secret-field",{name:[1],label:[1],value:[1],placeholder:[1],required:[4],disabled:[4],locale:[1],error:[32]}],[2,"mrd-time-field",{name:[1],label:[1],value:[1],required:[4],disabled:[4],locale:[1],error:[32]}],[2,"mrd-table",{item:[16],parentId:[1,"parent-id"],rows:[16],locale:[1],totalElements:[2,"total-elements"],pageSize:[2,"page-size"],rowHeight:[2,"row-height"],tableHeight:[2,"table-height"],requestTimeoutMs:[2,"request-timeout-ms"],readOnly:[4,"read-only"],canCreate:[4,"can-create"],allowedTypes:[16],accessControlEnabled:[4,"access-control-enabled"],activeViewIdx:[32],loadedPages:[32],requestedPages:[32],failedPages:[32],renderStart:[32],renderEnd:[32],colWidths:[32],resizingCol:[32],sortField:[32],sortDir:[32],activeFilters:[32],searchOpen:[32],searchQuery:[32],openFilterCol:[32],pendingFilter:[32],popupPos:[32],createPickerOpen:[32],viewPopoverOpen:[32],scrollTop:[32],textblockModal:[32],jsonModal:[32],aggregations:[32],aggregationsTotal:[32],minKnownTotal:[32],selfCanCreate:[32],selfAllowedTypes:[32],init:[64],setPage:[64],setAggregations:[64],setCanCreate:[64]},null,{totalElements:[{totalElementsChanged:0}],item:[{itemChanged:0}],parentId:[{parentIdChanged:0}]}],[2,"mrd-boolean-field",{name:[1],label:[1],value:[4],required:[4],disabled:[4],locale:[1],checked:[32]}],[2,"mrd-list-field",{name:[1],label:[1],value:[1],required:[4],disabled:[4],multiple:[4],locale:[1],listItems:[16],error:[32],selected:[32]}],[2,"mrd-text-field",{name:[1],label:[1],value:[1],placeholder:[1],required:[4],disabled:[4],locale:[1],error:[32]}],[2,"mrd-textarea-field",{name:[1],label:[1],value:[1],placeholder:[1],required:[4],disabled:[4],locale:[1],autofocus:[4],error:[32],editorReady:[32]},null,{value:[{valueChanged:0}]}]]]],e))));
|