@mmlogic/components 0.5.16 → 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 (42) 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 +158 -20
  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/layout/mrd-layout-section/mrd-layout-section.js +131 -12
  7. package/dist/collection/components/memo/mrd-memo-container/mrd-memo-container.js +28 -4
  8. package/dist/collection/components/table/mrd-table/mrd-table.js +105 -2
  9. package/dist/collection/dev/api.js +17 -0
  10. package/dist/collection/dev/app.js +36 -0
  11. package/dist/components/mrd-document-container2.js +1 -1
  12. package/dist/components/mrd-layout-section.js +1 -1
  13. package/dist/components/mrd-memo-container2.js +1 -1
  14. package/dist/components/mrd-table2.js +1 -1
  15. package/dist/esm/loader.js +1 -1
  16. package/dist/esm/mosterdcomponents.js +1 -1
  17. package/dist/esm/mrd-boolean-field_28.entry.js +158 -20
  18. package/dist/mosterdcomponents/mosterdcomponents.esm.js +1 -1
  19. package/dist/mosterdcomponents/p-f72668e9.entry.js +3 -0
  20. package/dist/types/components/documents/mrd-document-container/mrd-document-container.d.ts +7 -0
  21. package/dist/types/components/layout/mrd-layout-section/mrd-layout-section.d.ts +47 -0
  22. package/dist/types/components/memo/mrd-memo-container/mrd-memo-container.d.ts +4 -0
  23. package/dist/types/components/table/mrd-table/mrd-table.d.ts +45 -0
  24. package/dist/types/components.d.ts +49 -0
  25. package/package.json +1 -1
  26. package/dist/mosterdcomponents/cell-renderer-CMUxFREW.js.map +0 -1
  27. package/dist/mosterdcomponents/client-layout-BJkVeSq6.js.map +0 -1
  28. package/dist/mosterdcomponents/document-attachments-BufPmMRv.js.map +0 -1
  29. package/dist/mosterdcomponents/field-helpers-BWAp5fD8.js.map +0 -1
  30. package/dist/mosterdcomponents/file-upload-common-DDWuC9-3.js.map +0 -1
  31. package/dist/mosterdcomponents/format-gcecItcD.js.map +0 -1
  32. package/dist/mosterdcomponents/i18n-OzbszktW.js.map +0 -1
  33. package/dist/mosterdcomponents/index-1ayBojyN.js.map +0 -1
  34. package/dist/mosterdcomponents/index-CkcFwdPc.js.map +0 -1
  35. package/dist/mosterdcomponents/index.esm.js.map +0 -1
  36. package/dist/mosterdcomponents/mosterdcomponents.esm.js.map +0 -1
  37. package/dist/mosterdcomponents/p-8a7fa81d.entry.js +0 -3
  38. package/dist/mosterdcomponents/purify.es-_duHfPgC.js.map +0 -1
  39. package/dist/mosterdcomponents/query-params-3TXd5CUO.js.map +0 -1
  40. package/dist/mosterdcomponents/quill-C9pgw_k-.js.map +0 -1
  41. package/dist/mosterdcomponents/table-query-DJq1uKqJ.js.map +0 -1
  42. package/dist/mosterdcomponents/validation-B_0Lx9DE.js.map +0 -1
@@ -33,6 +33,13 @@ export declare class MrdDocumentContainer {
33
33
  * (with a tooltip explaining why) and the embedded table/list get it passed
34
34
  * through so their own create/upload/drag-drop paths are disabled too. */
35
35
  readOnly: boolean;
36
+ /** Whether the current user may create a document here — set by mrd-layout-section from
37
+ * its generic capability check (TASK-0299/FINDING-0155), the same answer the embedded
38
+ * table gets in list mode. Named `mayCreate` (not `canCreate`) to avoid colliding with
39
+ * the existing `canCreate` state below, which tracks something unrelated: whether the
40
+ * folder tree currently has a valid drop target selected. Defaults to false (hidden)
41
+ * until confirmed, same as mrd-table. */
42
+ mayCreate: boolean;
36
43
  /** Re-emitted from the embedded list/table. `nodeId` present for a folder load. */
37
44
  mrdLoadPage: EventEmitter<{
38
45
  page: number;
@@ -32,7 +32,16 @@ export declare class MrdLayoutSection {
32
32
  private imagePreviewUrl;
33
33
  private imagePreviews;
34
34
  private openHistoryField;
35
+ /** Answers to mrdViewCheckCapabilities, keyed by view key — see emitCapabilitiesChecks().
36
+ * Passed straight down as a `canCreate`/`mayCreate` prop to whichever body (mrd-table,
37
+ * mrd-memo-container, mrd-document-container) is mounted for that key; a missing entry
38
+ * means "not answered yet" and is treated as false (hidden), same default as mrd-table's
39
+ * own standalone behaviour (TASK-0299). */
40
+ private canCreateMap;
35
41
  private historyClickOutside;
42
+ /** Signature last asked per view key (see emitCapabilitiesChecks()) — avoids re-emitting
43
+ * mrdViewCheckCapabilities on every data/items update when the binding hasn't changed. */
44
+ private capabilitiesAsked;
36
45
  /** `attachments`, when present, prefetches the clicked document row's file slot
37
46
  * (see TASK-0169 / FINDING-0111) so the host can render a preview immediately,
38
47
  * without waiting for the navigated-to page's own mrdAttachments. Only set for
@@ -101,6 +110,17 @@ export declare class MrdLayoutSection {
101
110
  path: string;
102
111
  qs: string;
103
112
  }>;
113
+ /** Emitted once per VIEW/RELATED_VIEW binding — computed here directly, not relayed from a
114
+ * child (see emitCapabilitiesChecks()), so it covers mrd-table, mrd-memo-container and
115
+ * mrd-document-container uniformly. `name` is the view key (viewKeyFor(item)) — pass it
116
+ * back to setViewCanCreate() to answer it. */
117
+ mrdViewCheckCapabilities: EventEmitter<{
118
+ name: string;
119
+ type: string;
120
+ refType?: string;
121
+ refId?: string;
122
+ filterClass?: string;
123
+ }>;
104
124
  /** Generic "update an existing object" request from an embedded document view
105
125
  * (a document dragged into another folder). Host PATCHes `href` with `values`. */
106
126
  mrdUpdateObject: EventEmitter<{
@@ -129,8 +149,29 @@ export declare class MrdLayoutSection {
129
149
  componentDidLoad(): void;
130
150
  disconnectedCallback(): void;
131
151
  dataChanged(newVal: Record<string, unknown>): void;
152
+ /** A host can swap `items` (e.g. a different dashboard layout) without `data` changing —
153
+ * dataChanged() alone would miss that binding change. */
154
+ itemsChanged(): void;
132
155
  private initEmbeddedTables;
133
156
  private emitLoadImages;
157
+ /** Parent record id extracted from `data._links.self.href` — the same value every
158
+ * VIEW/RELATED_VIEW item on this record shares, used for RELATED_VIEW path construction
159
+ * (renderRelatedView) and capability checks (emitCapabilitiesChecks). */
160
+ private computeParentId;
161
+ /** Asks, once per VIEW/RELATED_VIEW binding, whether the current user may create a record
162
+ * there — the single generic place for this check (TASK-0299/FINDING-0155), so mrd-table,
163
+ * mrd-memo-container and mrd-document-container all get the answer the same way: as a plain
164
+ * `canCreate`/`mayCreate` prop from this component, computed here and handed down in
165
+ * renderTable()/renderMemoContainer()/renderDocumentContainer() — none of the three leaf
166
+ * components need to ask on their own when rendered through mrd-layout-section (mrd-table's
167
+ * own self-ask only kicks in for standalone use, i.e. its `canCreate` prop left undefined).
168
+ *
169
+ * The route/path variables mirror what each renderer already uses for its own rows —
170
+ * `type` = `dataClass`, `refType`/`refId` = `fromClass`/parentId for a RELATED_VIEW, and
171
+ * `filterClass` when the view is scoped to one subtype (the same value sent as `?type=` on
172
+ * the row fetch). A signature (not just the key) is tracked per binding so a genuine route
173
+ * change re-asks and hides again, without re-asking on every unrelated data/items update. */
174
+ private emitCapabilitiesChecks;
134
175
  /** Inject search results. Pass dataClass to target a specific SEARCH item; omit when there is only one. */
135
176
  setSearchResults(results: RelationSearchResult[], dataClass?: string): Promise<void>;
136
177
  /**
@@ -142,6 +183,12 @@ export declare class MrdLayoutSection {
142
183
  setViewPage(name: string, page: number, rows: any[], totalElements?: number, hasNext?: boolean, nodeId?: string): Promise<void>;
143
184
  /** Inject aggregation totals into an embedded mrd-table for a VIEW or RELATED_VIEW item. */
144
185
  setViewAggregations(name: string, data: any): Promise<void>;
186
+ /** Host answer to mrdViewCheckCapabilities — whether the current user may create a record
187
+ * for the given view's bound route. `name` is the view key from the event detail. Stored in
188
+ * canCreateMap and passed down as a plain prop to whichever body is mounted for that key
189
+ * (mrd-table, mrd-memo-container or mrd-document-container) — no DOM lookup needed, unlike
190
+ * setViewPage/setViewAggregations, since the prop flows through the next render. */
191
+ setViewCanCreate(name: string, value: boolean): Promise<void>;
145
192
  /** Inject distinct values into an embedded mrd-document-list for a document view. */
146
193
  setViewDistinct(name: string, nodeId: string, values: DistinctValue[]): Promise<void>;
147
194
  /** Inject the uploaded file reference for a dropped file, so the document view
@@ -52,6 +52,10 @@ export declare class MrdMemoContainer {
52
52
  * tooltip, cards/rows are not clickable, and the embedded table's own
53
53
  * create action is disabled too. */
54
54
  readOnly: boolean;
55
+ /** Whether the current user may create a memo here — set by mrd-layout-section from its
56
+ * generic capability check (TASK-0299/FINDING-0155), the same answer the embedded table
57
+ * gets in table mode. Defaults to false (hidden) until confirmed, same as mrd-table. */
58
+ canCreate: boolean;
55
59
  /** The active account's linked person record (userProfile.links.person), if any.
56
60
  * Used both to gate "New memo" (see effectiveContainerHref) and, on submit, to
57
61
  * fill the author slot. */
@@ -63,6 +63,13 @@ export declare class MrdTable {
63
63
  * explaining why) so a viewer-role user can see at a glance that this dashboard
64
64
  * is read-only. Sorting, filtering and export stay available — those don't mutate data. */
65
65
  readOnly: boolean;
66
+ /** Whether the current user may create a record for the table's bound route. Leave
67
+ * `undefined` for standalone use — the table then asks on its own via mrdCheckCapabilities
68
+ * and hides the create button until setCanCreate() answers. Set it explicitly when a host
69
+ * already knows the answer (e.g. mrd-layout-section, which asks once per binding and hands
70
+ * the answer to whichever body it mounts) — the table then trusts it directly and never
71
+ * asks on its own (TASK-0299). */
72
+ canCreate?: boolean;
66
73
  /** Clamp renderEnd when totalElements shrinks (e.g. after a filter is applied).
67
74
  * 0 means "unknown" (hosts that rely on minKnownTotal never set it, and some reset it
68
75
  * while a filter is being applied) — clamping to it would collapse the window to a
@@ -70,6 +77,11 @@ export declare class MrdTable {
70
77
  totalElementsChanged(newVal: number): void;
71
78
  /** Reset to primary view when the item prop is replaced from outside. */
72
79
  itemChanged(newVal: ClientLayoutItem | null): void;
80
+ /** A parent-record navigation can reuse the same `item` reference while only `parentId`
81
+ * changes (e.g. clicking a different dossier for the same RELATED_VIEW layout) — itemChanged()
82
+ * would not fire for that. Re-ask so a stale canCreate answer from the previous parent never
83
+ * leaks into the new one. */
84
+ parentIdChanged(): void;
73
85
  /** Index into allViews[] for the currently displayed view. 0 = primary, 1+ = alternatives. */
74
86
  private activeViewIdx;
75
87
  loadedPages: Map<number, Record<string, any>[]>;
@@ -116,6 +128,10 @@ export declare class MrdTable {
116
128
  private aggregationsPending;
117
129
  /** Lower bound on total derived from setPage() hasNext info; grows as pages load. */
118
130
  private minKnownTotal;
131
+ /** Self-asked answer for standalone use (the `canCreate` prop is undefined) — defaults to
132
+ * hidden (TASK-0299) until the host answers mrdCheckCapabilities via setCanCreate(). Ignored
133
+ * when the host controls `canCreate` directly; see effectiveCanCreate(). */
134
+ private selfCanCreate;
119
135
  /** Fired when a page needs to be fetched. Host fetches and calls setPage().
120
136
  * `sort` is the raw query-param value, e.g. "name" or "name,desc". */
121
137
  mrdLoadPage: EventEmitter<{
@@ -160,6 +176,21 @@ export declare class MrdTable {
160
176
  qs: string;
161
177
  aggQs: string;
162
178
  }>;
179
+ /** Standalone use only (never fires while the `canCreate` prop is set — see there). Fired
180
+ * once per table binding (initial load, and whenever the bound item, parent record or active
181
+ * view changes) to ask the host whether the current user may create a record here. Carries
182
+ * the same route the table already uses for its own rows, unconstructed — VIEW:
183
+ * GET /data/{tenant}/{type}/capabilities; RELATED_VIEW: GET /data/{tenant}/{refType}/{refId}/{type}/capabilities.
184
+ * `filterClass`, when the view declares one, is the same subtype the row fetch sends as
185
+ * `?type=` — e.g. a "contents" RELATED_VIEW filtered to `document` asks specifically whether
186
+ * a document may be created here, not any content type.
187
+ * The add button stays hidden until the host calls setCanCreate() (FINDING-0155 / TASK-0299). */
188
+ mrdCheckCapabilities: EventEmitter<{
189
+ type: string;
190
+ refType?: string;
191
+ refId?: string;
192
+ filterClass?: string;
193
+ }>;
163
194
  componentWillLoad(): void;
164
195
  componentDidLoad(): void;
165
196
  private applyDefaultSort;
@@ -183,6 +214,12 @@ export declare class MrdTable {
183
214
  setPage(pageNumber: number, rows: Record<string, any>[], hasNext?: boolean): Promise<void>;
184
215
  /** Inject aggregation totals returned by the /aggregations endpoint. */
185
216
  setAggregations(data: AggregationResult): Promise<void>;
217
+ /** Host answer to mrdCheckCapabilities — whether the current user may create a record for
218
+ * the table's currently bound route (standalone use only; ignored while the `canCreate`
219
+ * prop is set, since the host controls it directly in that mode). Call it any time after
220
+ * the event fires; a stale answer for an old binding is discarded automatically
221
+ * (selfCanCreate is reset to false before every new mrdCheckCapabilities emission). */
222
+ setCanCreate(value: boolean): Promise<void>;
186
223
  disconnectedCallback(): void;
187
224
  componentDidRender(): void;
188
225
  /** Freeze the column widths once the first data page has rendered.
@@ -273,6 +310,12 @@ export declare class MrdTable {
273
310
  * VIEW: /{dataClass}
274
311
  * RELATED_VIEW: /{fromClass}/{parentId}/{dataClass} */
275
312
  private buildDataPath;
313
+ /** Asks the host whether creating is allowed for the table's current binding (item, parent
314
+ * and active view) — only in standalone use (`canCreate` prop undefined). A host that
315
+ * controls `canCreate` directly already knows the answer, so the table never asks on its
316
+ * own in that mode. Resets selfCanCreate to false first so the add button stays hidden for
317
+ * the new binding until the host answers — it never inherits the previous binding's answer. */
318
+ private emitCapabilitiesCheck;
276
319
  /** Build query params for a page request from current sort, view filters, filterClass and active column filters.
277
320
  *
278
321
  * `q` and column filters are mutually exclusive on the backend — combining them breaks
@@ -291,6 +334,8 @@ export declare class MrdTable {
291
334
  * mode must not flip back to the rows prop (and its "no results") while page 0 is in flight. */
292
335
  private get isPaginated();
293
336
  private get columns();
337
+ /** `canCreate` when the host controls it directly, else the table's own self-asked answer. */
338
+ private get effectiveCanCreate();
294
339
  private get tableActions();
295
340
  private toggleCreatePicker;
296
341
  private closeCreatePicker;
@@ -156,6 +156,11 @@ export namespace Components {
156
156
  * @default navigator.language
157
157
  */
158
158
  "locale": string;
159
+ /**
160
+ * Whether the current user may create a document here — set by mrd-layout-section from its generic capability check (TASK-0299/FINDING-0155), the same answer the embedded table gets in list mode. Named `mayCreate` (not `canCreate`) to avoid colliding with the existing `canCreate` state below, which tracks something unrelated: whether the folder tree currently has a valid drop target selected. Defaults to false (hidden) until confirmed, same as mrd-table.
161
+ * @default false
162
+ */
163
+ "mayCreate": boolean;
159
164
  /**
160
165
  * Parent record id — required for RELATED_VIEW path building.
161
166
  * @default ''
@@ -566,6 +571,10 @@ export namespace Components {
566
571
  * Inject aggregation totals into an embedded mrd-table for a VIEW or RELATED_VIEW item.
567
572
  */
568
573
  "setViewAggregations": (name: string, data: any) => Promise<void>;
574
+ /**
575
+ * Host answer to mrdViewCheckCapabilities — whether the current user may create a record for the given view's bound route. `name` is the view key from the event detail. Stored in canCreateMap and passed down as a plain prop to whichever body is mounted for that key (mrd-table, mrd-memo-container or mrd-document-container) — no DOM lookup needed, unlike setViewPage/setViewAggregations, since the prop flows through the next render.
576
+ */
577
+ "setViewCanCreate": (name: string, value: boolean) => Promise<void>;
569
578
  /**
570
579
  * Inject the created document's href after a file-drop create, so the optimistic row becomes navigable (see mrdCreateObject). For a memo quick-create there is no optimistic row to resolve — simply reload the container's current body so the new memo shows up.
571
580
  */
@@ -674,6 +683,11 @@ export namespace Components {
674
683
  * Resolved `commons.memo` archetype binding.
675
684
  */
676
685
  "archetype": ClientArchetype;
686
+ /**
687
+ * Whether the current user may create a memo here — set by mrd-layout-section from its generic capability check (TASK-0299/FINDING-0155), the same answer the embedded table gets in table mode. Defaults to false (hidden) until confirmed, same as mrd-table.
688
+ * @default false
689
+ */
690
+ "canCreate": boolean;
677
691
  /**
678
692
  * Canonical parent href — set when this is a RELATED_VIEW embedded under a parent record, which already gives a new memo something to attach to.
679
693
  * @default ''
@@ -997,6 +1011,10 @@ export namespace Components {
997
1011
  "value": string;
998
1012
  }
999
1013
  interface MrdTable {
1014
+ /**
1015
+ * Whether the current user may create a record for the table's bound route. Leave `undefined` for standalone use — the table then asks on its own via mrdCheckCapabilities and hides the create button until setCanCreate() answers. Set it explicitly when a host already knows the answer (e.g. mrd-layout-section, which asks once per binding and hands the answer to whichever body it mounts) — the table then trusts it directly and never asks on its own (TASK-0299).
1016
+ */
1017
+ "canCreate"?: boolean;
1000
1018
  /**
1001
1019
  * Initialise (or reset) the virtual scroll. Call after setting all props and registering the mrdLoadPage listener, but before calling setPage(0, rows).
1002
1020
  */
@@ -1044,6 +1062,10 @@ export namespace Components {
1044
1062
  * Inject aggregation totals returned by the /aggregations endpoint.
1045
1063
  */
1046
1064
  "setAggregations": (data: AggregationResult) => Promise<void>;
1065
+ /**
1066
+ * Host answer to mrdCheckCapabilities — whether the current user may create a record for the table's currently bound route (standalone use only; ignored while the `canCreate` prop is set, since the host controls it directly in that mode). Call it any time after the event fires; a stale answer for an old binding is discarded automatically (selfCanCreate is reset to false before every new mrdCheckCapabilities emission).
1067
+ */
1068
+ "setCanCreate": (value: boolean) => Promise<void>;
1047
1069
  /**
1048
1070
  * Inject the rows for a given page (0-based). Creates a new Map reference so Stencil detects the state change. When the page contains fewer rows than pageSize it is the last page. renderEnd is clamped immediately so no loading-placeholder rows appear beyond the actual data — without requiring the host to update totalElements. Pass hasNext (from _links.next in the API response) for accurate last-page detection even when rows.length === pageSize (exact multiple of page size).
1049
1071
  */
@@ -1638,6 +1660,7 @@ declare global {
1638
1660
  "mrdViewAction": { name: string; action: string; dataClass: string; path?: string; qs?: string; parentPath?: string | null; basicType?: string };
1639
1661
  "mrdLoadViewAggregations": { name: string; dataClass: string; path: string; qs: string; aggQs: string };
1640
1662
  "mrdLoadViewDistinct": { name: string; nodeId: string; distinct: string; path: string; qs: string };
1663
+ "mrdViewCheckCapabilities": { name: string; type: string; refType?: string; refId?: string; filterClass?: string };
1641
1664
  "mrdUpdateObject": { href: string; values: Record<string, unknown> };
1642
1665
  "mrdDeleteObject": { href: string };
1643
1666
  "mrdViewUpload": { name: string; file: File };
@@ -1879,6 +1902,7 @@ declare global {
1879
1902
  "mrdAction": { action: string; path?: string; qs?: string; dataClass?: string; parentPath?: string | null; basicType?: string };
1880
1903
  "mrdDownload": { href: string; fileName: string };
1881
1904
  "mrdLoadAggregations": { path: string; qs: string; aggQs: string };
1905
+ "mrdCheckCapabilities": { type: string; refType?: string; refId?: string; filterClass?: string };
1882
1906
  }
1883
1907
  interface HTMLMrdTableElement extends Components.MrdTable, HTMLStencilElement {
1884
1908
  addEventListener<K extends keyof HTMLMrdTableElementEventMap>(type: K, listener: (this: HTMLMrdTableElement, ev: MrdTableCustomEvent<HTMLMrdTableElementEventMap[K]>) => any, options?: boolean | AddEventListenerOptions): void;
@@ -2129,6 +2153,11 @@ declare namespace LocalJSX {
2129
2153
  * @default navigator.language
2130
2154
  */
2131
2155
  "locale"?: string;
2156
+ /**
2157
+ * Whether the current user may create a document here — set by mrd-layout-section from its generic capability check (TASK-0299/FINDING-0155), the same answer the embedded table gets in list mode. Named `mayCreate` (not `canCreate`) to avoid colliding with the existing `canCreate` state below, which tracks something unrelated: whether the folder tree currently has a valid drop target selected. Defaults to false (hidden) until confirmed, same as mrd-table.
2158
+ * @default false
2159
+ */
2160
+ "mayCreate"?: boolean;
2132
2161
  /**
2133
2162
  * A table toolbar action was triggered.
2134
2163
  */
@@ -2659,6 +2688,10 @@ declare namespace LocalJSX {
2659
2688
  * Re-emitted from an embedded mrd-table's mrdAction; host builds the target URL from dataClass.
2660
2689
  */
2661
2690
  "onMrdViewAction"?: (event: MrdLayoutSectionCustomEvent<{ name: string; action: string; dataClass: string; path?: string; qs?: string; parentPath?: string | null; basicType?: string }>) => void;
2691
+ /**
2692
+ * Emitted once per VIEW/RELATED_VIEW binding — computed here directly, not relayed from a child (see emitCapabilitiesChecks()), so it covers mrd-table, mrd-memo-container and mrd-document-container uniformly. `name` is the view key (viewKeyFor(item)) — pass it back to setViewCanCreate() to answer it.
2693
+ */
2694
+ "onMrdViewCheckCapabilities"?: (event: MrdLayoutSectionCustomEvent<{ name: string; type: string; refType?: string; refId?: string; filterClass?: string }>) => void;
2662
2695
  /**
2663
2696
  * An external file was dropped on a document view; host uploads and calls setViewFileReference(name, uri).
2664
2697
  */
@@ -2764,6 +2797,11 @@ declare namespace LocalJSX {
2764
2797
  * Resolved `commons.memo` archetype binding.
2765
2798
  */
2766
2799
  "archetype": ClientArchetype;
2800
+ /**
2801
+ * Whether the current user may create a memo here — set by mrd-layout-section from its generic capability check (TASK-0299/FINDING-0155), the same answer the embedded table gets in table mode. Defaults to false (hidden) until confirmed, same as mrd-table.
2802
+ * @default false
2803
+ */
2804
+ "canCreate"?: boolean;
2767
2805
  /**
2768
2806
  * Canonical parent href — set when this is a RELATED_VIEW embedded under a parent record, which already gives a new memo something to attach to.
2769
2807
  * @default ''
@@ -3107,6 +3145,10 @@ declare namespace LocalJSX {
3107
3145
  "value"?: string;
3108
3146
  }
3109
3147
  interface MrdTable {
3148
+ /**
3149
+ * Whether the current user may create a record for the table's bound route. Leave `undefined` for standalone use — the table then asks on its own via mrdCheckCapabilities and hides the create button until setCanCreate() answers. Set it explicitly when a host already knows the answer (e.g. mrd-layout-section, which asks once per binding and hands the answer to whichever body it mounts) — the table then trusts it directly and never asks on its own (TASK-0299).
3150
+ */
3151
+ "canCreate"?: boolean;
3110
3152
  /**
3111
3153
  * The VIEW or RELATED_VIEW layout item. Contains view config, dataClass, fromClass, actions etc.
3112
3154
  * @default null
@@ -3120,6 +3162,10 @@ declare namespace LocalJSX {
3120
3162
  * Fired when a toolbar action button is clicked. For 'export': includes `path` (relative excel path) and `qs` (current sort+filter params). For 'create': includes `dataClass` (target type) and `parentPath` (e.g. /buyers/123 for RELATED_VIEW).
3121
3163
  */
3122
3164
  "onMrdAction"?: (event: MrdTableCustomEvent<{ action: string; path?: string; qs?: string; dataClass?: string; parentPath?: string | null; basicType?: string }>) => void;
3165
+ /**
3166
+ * Standalone use only (never fires while the `canCreate` prop is set — see there). Fired once per table binding (initial load, and whenever the bound item, parent record or active view changes) to ask the host whether the current user may create a record here. Carries the same route the table already uses for its own rows, unconstructed — VIEW: GET /data/{tenant}/{type}/capabilities; RELATED_VIEW: GET /data/{tenant}/{refType}/{refId}/{type}/capabilities. `filterClass`, when the view declares one, is the same subtype the row fetch sends as `?type=` — e.g. a "contents" RELATED_VIEW filtered to `document` asks specifically whether a document may be created here, not any content type. The add button stays hidden until the host calls setCanCreate() (FINDING-0155 / TASK-0299).
3167
+ */
3168
+ "onMrdCheckCapabilities"?: (event: MrdTableCustomEvent<{ type: string; refType?: string; refId?: string; filterClass?: string }>) => void;
3123
3169
  /**
3124
3170
  * Fired when a FILE or IMAGE cell is clicked. Host resolves the href to a signed download URL.
3125
3171
  */
@@ -3317,6 +3363,7 @@ declare namespace LocalJSX {
3317
3363
  "height": number;
3318
3364
  "locale": string;
3319
3365
  "readOnly": boolean;
3366
+ "mayCreate": boolean;
3320
3367
  }
3321
3368
  interface MrdDocumentListAttributes {
3322
3369
  "parentId": string;
@@ -3411,6 +3458,7 @@ declare namespace LocalJSX {
3411
3458
  "viewKey": string;
3412
3459
  "locale": string;
3413
3460
  "readOnly": boolean;
3461
+ "canCreate": boolean;
3414
3462
  }
3415
3463
  interface MrdMemoListAttributes {
3416
3464
  "parentId": string;
@@ -3472,6 +3520,7 @@ declare namespace LocalJSX {
3472
3520
  "tableHeight": number;
3473
3521
  "requestTimeoutMs": number;
3474
3522
  "readOnly": boolean;
3523
+ "canCreate": boolean;
3475
3524
  }
3476
3525
  interface MrdTextFieldAttributes {
3477
3526
  "name": string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mmlogic/components",
3
- "version": "0.5.16",
3
+ "version": "0.5.17",
4
4
  "description": "Stencil.js web component library for dynamic forms and virtual-scroll data tables",
5
5
  "license": "MIT",
6
6
  "author": "Mosterd",
@@ -1 +0,0 @@
1
- {"version":3,"file":"cell-renderer-CMUxFREW.js","sources":["src/utils/cell-renderer.ts"],"sourcesContent":["import { formatNumber, formatPercentage, formatCurrency, formatDate, formatDateTime, formatTime } from './format';\nimport { ClientLayoutItem, ClientLayoutItemType } from '../types/client-layout';\n\n/** TableColumn is an alias for ClientLayoutItem — used for table column definitions. */\nexport type TableColumn = ClientLayoutItem;\n\n/** One entry in the alternativeViews array for mrd-table. */\nexport interface AlternativeView {\n /** Named view identifier, emitted in mrdSwitchView. */\n name: string;\n /** Display text; falls back to name when absent. */\n label?: string;\n /** Optional class override (TypeFQN); emitted in mrdSwitchView. */\n class?: string;\n}\n\n/** Defines a toolbar action button for mrd-table. */\nexport interface TableAction {\n /** Machine-readable identifier, e.g. 'create' | 'export'. Emitted in mrdAction event. */\n action: string;\n /** Tooltip text (and label fallback when no icon is provided). */\n label: string;\n /** SVG sprite href, e.g. \"assets/sprites.svg#icon-plus\". Optional. */\n icon?: string;\n /** Button style variant. Defaults to 'secondary'. */\n variant?: 'primary' | 'secondary' | 'danger';\n disabled?: boolean;\n basicType?: string;\n}\n\n/** Represents an active filter on a single column. */\nexport interface ColumnFilter {\n field: string;\n dataType: string;\n /** Text operator — only relevant for text-like data types. */\n operator?: 'contains' | 'startsWith' | 'equals' | 'isEmpty' | 'isNotEmpty';\n /** Single value (boolean, exact text/number/date). */\n value?: string | number | boolean | null;\n /** Range start (≥) — future API support. */\n from?: string | number | null;\n /** Range end (≤) — future API support. */\n to?: string | number | null;\n /** LIST: keys of selected items to include. */\n values?: string[];\n}\n\nexport class CellRenderer {\n static render(column: TableColumn, row: Record<string, any>, locale: string): string {\n if (column.type === ClientLayoutItemType.RELATION) {\n const name = column.name ?? '';\n const link = row?._links?.[name];\n if (!link) return '';\n if (Array.isArray(link)) return link.map((l: any) => l.name ?? '').filter(Boolean).join(', ');\n return link.name ?? '';\n }\n\n if (column.type !== ClientLayoutItemType.FIELD) return '';\n\n const name = column.name ?? '';\n const dataType = column.dataType;\n const listItems = column.listItems;\n let raw = row?.[name];\n // History-enabled fields are stored as { current, history: [...] } — render\n // the current value, same as mrd-layout-section's field rendering.\n if (column.historyEnabled && raw != null && typeof raw === 'object' && !Array.isArray(raw) && 'current' in raw) {\n raw = (raw as any).current;\n }\n if (raw == null || raw === '') return '';\n\n const values = Array.isArray(raw) ? raw : [raw];\n return values\n .map(v => CellRenderer.renderValue(dataType ?? 'TEXT', v, listItems ?? [], locale))\n .filter(s => s !== '')\n .join(', ');\n }\n\n static renderValue(\n dataType: string,\n value: any,\n listItems: Array<{ key: string; label: string }>,\n locale: string,\n ): string {\n switch (dataType) {\n case 'INTEGER':\n return formatNumber(Number(value), locale, { maximumFractionDigits: 0 });\n case 'DECIMAL':\n return formatNumber(Number(value), locale);\n case 'PERCENTAGE':\n return formatPercentage(Number(value), locale);\n case 'CURRENCY': {\n const { amount, currency } = typeof value === 'object' && value !== null\n ? value\n : { amount: value, currency: '' };\n return currency\n ? formatCurrency(Number(amount), currency, locale)\n : formatNumber(Number(amount), locale);\n }\n case 'DATE':\n return formatDate(value, locale);\n case 'DATETIME':\n return formatDateTime(value, locale);\n case 'TIME':\n return formatTime(value, locale);\n case 'BOOLEAN':\n return value ? '✓' : '';\n case 'FILE':\n case 'IMAGE':\n return typeof value === 'object' && value !== null ? (value.fileName ?? '') : '';\n case 'LIST': {\n const item = listItems.find(li => li.key === String(value));\n return item?.label ?? String(value);\n }\n case 'TEXTBLOCK': {\n const stripped = String(value).replace(/<[^>]*>/g, '');\n const txt = document.createElement('textarea');\n txt.innerHTML = stripped;\n return txt.value.trim();\n }\n case 'LONGTEXT':\n return String(value).replace(/[\\r\\n]+/g, ' ').trim();\n case 'JSON': {\n const str = typeof value === 'object' ? JSON.stringify(value) : String(value);\n return str.replace(/[\\r\\n]+/g, ' ').trim();\n }\n case 'SECRET':\n return value ? '••••••••' : '';\n default:\n return String(value);\n }\n }\n\n /**\n * Returns syntax-highlighted JSON as an HTML string with inline styles.\n * compact=true → single line (no indentation); compact=false → pretty-printed.\n */\n static formatJson(value: any, compact = false): string {\n try {\n const parsed = typeof value === 'string' ? JSON.parse(value) : value;\n const json = compact ? JSON.stringify(parsed) : JSON.stringify(parsed, null, 2);\n return json.replace(\n /(\"(\\\\u[a-zA-Z0-9]{4}|\\\\[^u]|[^\\\\\"])*\"(\\s*:)?|\\b(true|false|null)\\b|-?\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d+)?)/g,\n match => {\n let style = 'color:#2aa198';\n if (/^\"/.test(match)) {\n style = /:$/.test(match) ? 'color:#881391' : 'color:#268bd2';\n } else if (/true|false/.test(match)) {\n style = 'color:#b58900';\n } else if (/null/.test(match)) {\n style = 'color:#dc322f';\n }\n return `<span style=\"${style}\">${match}</span>`;\n },\n );\n } catch {\n return String(value);\n }\n }\n}\n"],"names":[],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"client-layout-BJkVeSq6.js","sources":["src/types/client-layout.ts"],"sourcesContent":["// ─── Layout type ──────────────────────────────────────────────────────────────\n\nexport enum ClientLayoutType {\n OBJECT_FORM_DASHBOARD = 'OBJECT_FORM_DASHBOARD',\n CLASS_DASHBOARD = 'CLASS_DASHBOARD',\n GENERAL_DASHBOARD = 'GENERAL_DASHBOARD',\n NAVIGATION_PANE = 'NAVIGATION_PANE',\n}\n\n// ─── Item types ───────────────────────────────────────────────────────────────\n\nexport enum ClientLayoutItemType {\n // Form / data input\n FIELD = 'FIELD',\n RELATION = 'RELATION',\n // Layout structure\n SECTION = 'SECTION',\n GROUP = 'GROUP',\n // Read-only / display\n HEADER = 'HEADER',\n TEXT = 'TEXT',\n // Navigation / actions\n NAVIGATE = 'NAVIGATE',\n ACTION = 'ACTION',\n SEARCH = 'SEARCH',\n // Dashboard views\n VIEW = 'VIEW',\n RELATED_VIEW = 'RELATED_VIEW',\n RELATED_OBJECT = 'RELATED_OBJECT',\n}\n\n// ─── Field data types ─────────────────────────────────────────────────────────\n\nexport enum ClientLayoutItemFieldDataType {\n TEXT = 'TEXT',\n TEXTBLOCK = 'TEXTBLOCK',\n INTEGER = 'INTEGER',\n DECIMAL = 'DECIMAL',\n PERCENTAGE = 'PERCENTAGE',\n CURRENCY = 'CURRENCY',\n BOOLEAN = 'BOOLEAN',\n DATE = 'DATE',\n DATETIME = 'DATETIME',\n TIME = 'TIME',\n EMAIL = 'EMAIL',\n HYPERLINK = 'HYPERLINK',\n LIST = 'LIST',\n FILE = 'FILE',\n IMAGE = 'IMAGE',\n LONGTEXT = 'LONGTEXT',\n JSON = 'JSON',\n SECRET = 'SECRET',\n}\n\n// ─── Relation display / edit behaviour ───────────────────────────────────────\n\nexport enum ClientLayoutItemRelationDisplayType {\n SEARCH = 'SEARCH',\n DROPDOWN = 'DROPDOWN',\n CHECKBOX = 'CHECKBOX',\n}\n\nexport enum ClientLayoutItemRelationEditBehavior {\n SEARCH = 'SEARCH',\n DROPDOWN = 'DROPDOWN',\n CHECKBOX = 'CHECKBOX',\n}\n\n// ─── VIEW / RELATED_VIEW navigate behaviour ──────────────────────────────────\n\n/** How navigating from a row/document in this VIEW or RELATED_VIEW should open\n * its target: a full page navigation, or an overlay on top of the current page. */\nexport enum ClientLayoutNavigateBehaviour {\n PAGE = 'PAGE',\n OVERLAY_PAGE = 'OVERLAY_PAGE',\n}\n\n// ─── Data object link types (API _links shape) ───────────────────────────────\n\n/** A single sub-link within a multi-value relation link. */\nexport interface DataObjectSubLink {\n href: string;\n name: string;\n}\n\n/** Link object as returned by the API on a data object's `_links` map. */\nexport interface DataObjectLink {\n href: string;\n name?: string;\n values?: DataObjectSubLink[];\n}\n\n// ─── Shared value types ───────────────────────────────────────────────────────\n\n/** A selectable list item as returned by the API. */\nexport interface ClientListValue {\n /** Raw key stored in the database (API uses `key`, older code used `value`). */\n key: string;\n label: string;\n color?: string | null;\n backgroundColor?: string | null;\n}\n\nexport interface CurrencyValue {\n amount: number | null;\n currency: string;\n decimals?: number;\n}\n\nexport interface HyperlinkValue {\n href: string;\n name?: string;\n}\n\nexport interface RelationSearchResult {\n id: string;\n label: string;\n description?: string;\n}\n\n/** A single file belonging to a `commons.document`-archetype record. The host\n * resolves `href` to a signed URL and decides how (or whether) to render it\n * inline — the component only ever hands over the reference. */\nexport interface DocumentAttachment {\n href: string;\n fileName: string;\n mimeType?: string;\n}\n\n// ─── Archetypes (semantic type contracts) ────────────────────────────────────\n\n/**\n * A resolved archetype binding as published on a dashboard.\n * `archetype` is the platform-defined id (e.g. `commons.document`); `slots` maps\n * each slot name to the concrete field/relation name on this type. The client\n * keys on the archetype id and reads values from the data endpoint via the map;\n * the slot kind (value vs relation) is known from the archetype id itself and is\n * not published.\n */\nexport interface ClientArchetype {\n archetype: string;\n slots: Record<string, string>;\n}\n\n/** Well-known archetype ids the client renders specially. */\nexport const ARCHETYPE_DOCUMENT = 'commons.document';\nexport const ARCHETYPE_INVOICE = 'commons.invoice';\nexport const ARCHETYPE_EMAIL = 'commons.email';\n/** Bound by a `commons.invoice`'s `lines` relation target class. Used to find the\n * RELATED_VIEW item for the line items generically, by archetype id rather than\n * by name/class string. */\nexport const ARCHETYPE_INVOICE_LINE = 'commons.invoiceLine';\nexport const ARCHETYPE_MEMO = 'commons.memo';\n\n/**\n * Find a binding for `archetypeId` in a published `archetypes` list.\n * Returns `undefined` when the archetype is absent — callers fall back to the\n * generic table/field rendering.\n */\nexport function resolveArchetype(\n archetypes: ClientArchetype[] | null | undefined,\n archetypeId: string,\n): ClientArchetype | undefined {\n return (archetypes ?? []).find(a => a.archetype === archetypeId);\n}\n\n// ─── Item definitions ─────────────────────────────────────────────────────────\n\nexport interface ClientLayoutItemField {\n name: string;\n label: string;\n dataType: ClientLayoutItemFieldDataType;\n required: boolean;\n disabled?: boolean;\n multiple?: boolean;\n header?: boolean;\n defaultValue?: string | null;\n listItems?: ClientListValue[] | null;\n // UI hints — optional, not sent by API\n placeholder?: string;\n accept?: string;\n maxSize?: number;\n decimalPrecision?: number;\n currencyCode?: string;\n /** Aggregate function to render in the table totals row. */\n aggregate?: 'sum' | 'avg' | 'count' | null;\n historyEnabled?: boolean;\n /** Render the item on an OBJECT_DASHBOARD even when it has no value. */\n showEmpty?: boolean;\n}\n\n/** Response shape from the /aggregations endpoint. */\nexport interface AggregationResult {\n total?: number;\n count?: Record<string, number>;\n sum?: Record<string, number>;\n avg?: Record<string, number>;\n}\n\nexport interface ClientLayoutItemRelation {\n name: string;\n label: string;\n relatedClass: string;\n mostSignificantClass?: string;\n displayType?: ClientLayoutItemRelationDisplayType;\n editBehavior?: ClientLayoutItemRelationEditBehavior | null;\n commonRelation?: string;\n required: boolean;\n disabled?: boolean;\n multiple?: boolean;\n defaultValue?: string | null;\n dropdownValues?: ClientListValue[];\n /** Render the item on an OBJECT_DASHBOARD even when it has no value. */\n showEmpty?: boolean;\n}\n\nexport interface ClientLayoutItemNavigate {\n dataClass: string;\n icon?: string | null;\n navigationType?: string | null;\n}\n\nexport interface ClientLayoutItemRelatedView {\n name: string;\n fromClass?: string | null;\n relatedClass: string;\n filterClass?: string | null;\n showTitle: boolean;\n}\n\nexport interface ClientLayoutItemRelatedObject {\n name: string;\n relatedClass: string;\n}\n\nexport interface ClientLayoutItemSearch {\n dataClass: string;\n showTitle: boolean;\n}\n\n// ─── Generic item ─────────────────────────────────────────────────────────────\n\nexport interface ClientCreateType {\n type: string;\n label: string;\n}\n\n/**\n * A single item in a ClientLayout. The `type` discriminator determines which\n * optional fields are present. Components should only act on types they\n * understand and ignore others.\n */\nexport interface ClientLayoutItem {\n type: ClientLayoutItemType;\n label?: string;\n\n // FIELD\n field?: ClientLayoutItemField;\n\n // RELATION\n relation?: ClientLayoutItemRelation;\n\n // SECTION / GROUP — nested items\n items?: ClientLayoutItem[];\n\n // NAVIGATE\n navigate?: ClientLayoutItemNavigate;\n\n // VIEW — embedded view config (new flat format)\n view?: ClientViewMetadata;\n\n // RELATED_VIEW\n relatedView?: ClientLayoutItemRelatedView;\n\n // RELATED_OBJECT\n relatedObject?: ClientLayoutItemRelatedObject;\n\n // SEARCH\n search?: ClientLayoutItemSearch;\n\n /**\n * Archetype bindings for this item. The API publishes them on the item itself\n * (sibling of `view`), for two cases:\n * - object dashboard top-level: the type of the object itself → mrd-layout-section\n * renders a recognisable single-document view instead of generic fields;\n * - a VIEW / RELATED_VIEW item: → mrd-layout-section offers the document\n * collection view (folder tree) alongside the generic table.\n */\n archetypes?: ClientArchetype[];\n\n // ─── Flat API fields ──────────────────────────────────────────────────────\n // Shared / ACTION / VIEW\n name?: string;\n showTitle?: boolean;\n /** Embedded alternative view configs (new flat format). */\n alternativeViews?: Array<{ label?: string; dataClass?: string; fromClass?: string | null; filterClass?: string | null; view?: ClientViewMetadata }>;\n /** Toolbar buttons for embedded views. null/omitted = [\"NEW\",\"EXPORT\"]; [] = none. */\n actions?: string[] | null;\n /** Concrete subtypes available for create. When set, the NEW button expands into one button per entry. */\n createTypes?: ClientCreateType[] | null;\n\n // FIELD (flat)\n dataType?: ClientLayoutItemFieldDataType;\n required?: boolean;\n disabled?: boolean;\n multiple?: boolean;\n header?: boolean;\n defaultValue?: string | null;\n listItems?: ClientListValue[] | null;\n aggregate?: 'sum' | 'avg' | 'count' | null;\n placeholder?: string;\n accept?: string;\n maxSize?: number;\n decimalPrecision?: number;\n currencyCode?: string;\n historyEnabled?: boolean;\n /** FIELD / RELATION — render the item on an OBJECT_DASHBOARD even when it has\n * no value, instead of hiding it (the default). */\n showEmpty?: boolean;\n\n // RELATION (flat)\n relatedClass?: string;\n mostSignificantClass?: string;\n displayType?: ClientLayoutItemRelationDisplayType;\n editBehavior?: ClientLayoutItemRelationEditBehavior | null;\n commonRelation?: string;\n dropdownValues?: ClientListValue[];\n\n // VIEW / NAVIGATE / SEARCH (flat) — API sends 'dataClass' (renamed from reserved 'class')\n dataClass?: string;\n\n // RELATED_VIEW (flat)\n fromClass?: string | null;\n filterClass?: string | null;\n /** Name of the relation on the related object that points back to the\n * `fromClass` parent (e.g. a document's `dossier` relation). When it equals\n * the `commons.document` archetype's `container` slot, the view is a single\n * container context (see mrd-document-list seeding). */\n inverseRelation?: string | null;\n /** How navigating out of this VIEW/RELATED_VIEW opens its target. Defaults to\n * `PAGE` (full navigation) when absent. mrd-layout-section reads this off the\n * item and includes it in the mrdNavigate event detail it relays upward. */\n navigateBehaviour?: ClientLayoutNavigateBehaviour;\n\n // NAVIGATE (flat)\n icon?: string | null;\n navigationType?: string | null;\n\n // ACTION (flat)\n actionType?: string;\n}\n\n// ─── Layout ───────────────────────────────────────────────────────────────────\n\nexport interface ClientLayout {\n type?: ClientLayoutType;\n label?: string;\n /** Used as the form title (mrd-form). */\n title?: string;\n tabPage?: boolean;\n items: ClientLayoutItem[];\n}\n\n// ─── ViewMetadata (from DashboardMetadata.views) ─────────────────────────────\n\nexport interface ViewFilter {\n name: string;\n value?: string | null;\n operator?: 'EQUALS' | 'FROM' | 'TO' | 'STARTS_WITH' | 'NOT_EMPTY' | 'EMPTY' | null;\n}\n\nexport interface ClientViewMetadata {\n name: string | null;\n defaultView: boolean;\n defaultSort?: string | null;\n /** Column definitions — each item is a FIELD or RELATION item. */\n values: ClientLayoutItem[];\n /** Static query-param filters baked into the view definition. */\n filter?: ViewFilter[] | null;\n type?: string;\n referenceType?: string | null;\n filterType?: string | null;\n singularLabel?: string;\n pluralLabel?: string;\n /**\n * Archetype bindings published on this VIEW / RELATED_VIEW item. When it\n * contains `commons.document`, mrd-layout-section offers the document\n * collection view (folder tree) alongside the generic table.\n */\n archetypes?: ClientArchetype[];\n}\n\n// ─── DashboardMetadata ────────────────────────────────────────────────────────\n\nexport interface ClientDashboardMetadata {\n layouts: ClientLayout[];\n /** Top-level archetypes on an object dashboard (the object's own type). Drives the\n * single-document view; passed to the main layout's mrd-layout-section. */\n archetypes?: ClientArchetype[];\n /** Legacy: view metadata map keyed by generated name. Absent in new flat format. */\n views?: Record<string, ClientViewMetadata>;\n layoutType: ClientLayoutType;\n _links?: Record<string, { href: string }>;\n}\n"],"names":[],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"document-attachments-BufPmMRv.js","sources":["src/utils/document-attachments.ts"],"sourcesContent":["import { ClientArchetype, DocumentAttachment } from '../types/client-layout';\n\n/**\n * Extract an archetype's file slot from a data record as a `DocumentAttachment[]` —\n * empty when the slot is unmapped or has no value. `slot` defaults to `'file'` (the\n * commons.document single-file slot); pass `'attachments'` for the multi-file slots\n * on commons.email / commons.invoice. A single-object slot value yields a one-entry\n * array, an array-valued slot yields one entry per item.\n * Shared by mrd-document-view / mrd-invoice-view / mrd-email-view (on load/data-change)\n * and mrd-document-container (row-click prefetch on mrdNavigate), so all report the\n * exact same shape.\n */\nexport function attachmentsFromRecord(\n archetype: ClientArchetype | null | undefined,\n record: Record<string, unknown> | null | undefined,\n slot: string = 'file',\n): DocumentAttachment[] {\n const field = archetype?.slots?.[slot];\n if (!field) return [];\n const raw = (record as any)?.[field];\n if (!raw) return [];\n const items = Array.isArray(raw) ? raw : [raw];\n return items.filter(Boolean).map(item => ({\n href: item.href,\n fileName: item.fileName ?? String(item),\n mimeType: item.mimeType ?? item.contentType ?? undefined,\n }));\n}\n\n/** Dedup key for the componentDidLoad/@Watch('data') \"emit mrdAttachments only on\n * change\" guard shared by mrd-document-view / mrd-invoice-view / mrd-email-view. */\nexport function attachmentsKey(attachments: DocumentAttachment[]): string {\n return attachments.map(a => a.href).join('|');\n}\n"],"names":[],"mappings":"AAEA;;;;;;;;;AASG;AACG,SAAU,qBAAqB,CACnC,SAA6C,EAC7C,MAAkD,EAClD,OAAe,MAAM,EAAA;;IAErB,MAAM,KAAK,GAAG,CAAA,EAAA,GAAA,SAAS,KAAA,IAAA,IAAT,SAAS,KAAA,KAAA,CAAA,GAAA,KAAA,CAAA,GAAT,SAAS,CAAE,KAAK,MAAA,IAAA,IAAA,EAAA,KAAA,KAAA,CAAA,GAAA,KAAA,CAAA,GAAA,EAAA,CAAG,IAAI,CAAC;AACtC,IAAA,IAAI,CAAC,KAAK;AAAE,QAAA,OAAO,EAAE;IACrB,MAAM,GAAG,GAAI,MAAc,KAAA,IAAA,IAAd,MAAM,uBAAN,MAAM,CAAW,KAAK,CAAC;AACpC,IAAA,IAAI,CAAC,GAAG;AAAE,QAAA,OAAO,EAAE;AACnB,IAAA,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC;IAC9C,OAAO,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,IAAI,IAAG;;AAAC,QAAA,QAAC;YACxC,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,QAAQ,EAAE,MAAA,IAAI,CAAC,QAAQ,MAAA,IAAA,IAAA,EAAA,KAAA,KAAA,CAAA,GAAA,EAAA,GAAI,MAAM,CAAC,IAAI,CAAC;AACvC,YAAA,QAAQ,EAAE,CAAA,EAAA,GAAA,CAAA,EAAA,GAAA,IAAI,CAAC,QAAQ,MAAA,IAAA,IAAA,EAAA,KAAA,KAAA,CAAA,GAAA,EAAA,GAAI,IAAI,CAAC,WAAW,MAAA,IAAA,IAAA,EAAA,KAAA,KAAA,CAAA,GAAA,EAAA,GAAI,SAAS;AACzD,SAAA;AAAC,KAAA,CAAC;AACL;AAEA;AACqF;AAC/E,SAAU,cAAc,CAAC,WAAiC,EAAA;AAC9D,IAAA,OAAO,WAAW,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;AAC/C;;;;"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"field-helpers-BWAp5fD8.js","sources":["src/components/fields/field-helpers.tsx"],"sourcesContent":["import { FunctionalComponent, h } from '@stencil/core';\nimport { t } from '../../utils/i18n';\nimport { validateRequired } from '../../utils/validation';\n\n/**\n * Shared building blocks for the leaf field components.\n * `base` is the BEM block name, e.g. 'mrd-text-field'.\n */\n\nexport const FieldLabel: FunctionalComponent<{ base: string; label: string; required: boolean }> = ({ base, label, required }) =>\n label ? <label class={`${base}__label${required ? ` ${base}__label--required` : ''}`}>{label}</label> : null;\n\nexport const FieldError: FunctionalComponent<{ base: string; error: string }> = ({ base, error }) =>\n error ? <span class={`${base}__error`}>{error}</span> : null;\n\n/** Class string for the form control, with the --error modifier when applicable. */\nexport function controlClass(base: string, hasError: boolean, element: string = 'input'): string {\n return `${base}__${element}${hasError ? ` ${base}__${element}--error` : ''}`;\n}\n\n/** Returns the localised required-error, or '' when the value passes. */\nexport function requiredError(value: unknown, required: boolean, locale: string): string {\n return required && !validateRequired(value) ? t('required', locale) : '';\n}\n"],"names":[],"mappings":";;;;AAIA;;;AAGG;MAEU,UAAU,GAA4E,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,KAC3H,KAAK,GAAG,aAAO,KAAK,EAAE,CAAA,EAAG,IAAI,CAAA,OAAA,EAAU,QAAQ,GAAG,CAAA,CAAA,EAAI,IAAI,CAAA,iBAAA,CAAmB,GAAG,EAAE,CAAA,CAAE,IAAG,KAAK,CAAS,GAAG;AAEnG,MAAM,UAAU,GAAyD,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,KAC9F,KAAK,GAAG,CAAA,CAAA,MAAA,EAAA,EAAM,KAAK,EAAE,GAAG,IAAI,CAAA,OAAA,CAAS,EAAA,EAAG,KAAK,CAAQ,GAAG;AAE1D;AACM,SAAU,YAAY,CAAC,IAAY,EAAE,QAAiB,EAAE,UAAkB,OAAO,EAAA;AACrF,IAAA,OAAO,GAAG,IAAI,CAAA,EAAA,EAAK,OAAO,CAAA,EAAG,QAAQ,GAAG,IAAI,IAAI,CAAA,EAAA,EAAK,OAAO,CAAA,OAAA,CAAS,GAAG,EAAE,EAAE;AAC9E;AAEA;SACgB,aAAa,CAAC,KAAc,EAAE,QAAiB,EAAE,MAAc,EAAA;IAC7E,OAAO,QAAQ,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,MAAM,CAAC,GAAG,EAAE;AAC1E;;;;"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"file-upload-common-DDWuC9-3.js","sources":["src/components/fields/file-upload-common.ts"],"sourcesContent":["import { t } from '../../utils/i18n';\n\n/**\n * Shared upload behaviour for mrd-file-field and mrd-image-field:\n * file validation, drop-zone class building and drag-drop wiring.\n */\n\n/** A resolved file/image value, matching the shape the API returns for an already-persisted\n * file (mrd-table cell renderer, mrd-document-view, mrd-layout-section all read `.href` /\n * `.fileName` the same way) — also what a freshly-completed upload resolves to (host builds\n * `{ href: uri, fileName: file.name }` client-side). May carry extra metadata (`contentType`,\n * `size`, …) that this library ignores. */\nexport interface UploadedFileRef {\n href: string;\n fileName: string;\n [key: string]: unknown;\n}\n\n/** A single field entry: a pending `File` (upload in progress), a resolved `UploadedFileRef`,\n * or a bare href string (legacy shape — still supported so older hosts keep working unchanged). */\nexport type FileEntry = File | UploadedFileRef | string;\n\n/** True while the entry is still a local `File` awaiting upload resolution. */\nexport function isPending(entry: FileEntry): boolean {\n return entry instanceof File;\n}\n\n/** Display name for an entry: the `File`'s own name, the resolved `fileName`, or — for the\n * legacy bare-href shape — a best-effort basename of the URI. */\nexport function entryLabel(entry: FileEntry): string {\n if (entry instanceof File) return entry.name;\n if (typeof entry !== 'string') return entry.fileName || entry.href;\n const base = entry.split('/').pop() ?? entry;\n try {\n return decodeURIComponent(base);\n } catch {\n return base;\n }\n}\n\n/** Validates a selected/dropped file; returns a localised error message or '' when accepted. */\nexport function validateUploadFile(file: File, maxSize: number, locale: string, requireImage = false): string {\n if (maxSize > 0 && file.size > maxSize) return t('file_too_large', locale);\n if (requireImage && !file.type.startsWith('image/')) return t('invalid_image', locale);\n return '';\n}\n\n/** BEM class string for the drop zone, e.g. base 'mrd-file-field' → 'mrd-file-field__zone …'. */\nexport function uploadZoneClass(base: string, opts: { dragging: boolean; error: boolean; disabled: boolean }): string {\n return [\n `${base}__zone`,\n opts.dragging ? `${base}__zone--dragging` : '',\n opts.error ? `${base}__zone--error` : '',\n opts.disabled ? `${base}__zone--disabled` : '',\n ].filter(Boolean).join(' ');\n}\n\n/** Drag-drop handlers for the drop zone; the component owns the dragging state and file handling. */\nexport function makeDragHandlers(setDragging: (dragging: boolean) => void, onFiles: (files: File[]) => void) {\n return {\n onDragOver: (e: DragEvent) => {\n e.preventDefault();\n setDragging(true);\n },\n onDragLeave: () => setDragging(false),\n onDrop: (e: DragEvent) => {\n e.preventDefault();\n setDragging(false);\n onFiles(Array.from(e.dataTransfer?.files ?? []));\n },\n };\n}\n"],"names":[],"mappings":";;AAsBA;AACM,SAAU,SAAS,CAAC,KAAgB,EAAA;IACxC,OAAO,KAAK,YAAY,IAAI;AAC9B;AAEA;AACkE;AAC5D,SAAU,UAAU,CAAC,KAAgB,EAAA;;IACzC,IAAI,KAAK,YAAY,IAAI;QAAE,OAAO,KAAK,CAAC,IAAI;IAC5C,IAAI,OAAO,KAAK,KAAK,QAAQ;AAAE,QAAA,OAAO,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,IAAI;AAClE,IAAA,MAAM,IAAI,GAAG,CAAA,EAAA,GAAA,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,MAAA,IAAA,IAAA,EAAA,KAAA,KAAA,CAAA,GAAA,EAAA,GAAI,KAAK;AAC5C,IAAA,IAAI;AACF,QAAA,OAAO,kBAAkB,CAAC,IAAI,CAAC;;IAC/B,OAAA,EAAA,EAAM;AACN,QAAA,OAAO,IAAI;;AAEf;AAEA;AACM,SAAU,kBAAkB,CAAC,IAAU,EAAE,OAAe,EAAE,MAAc,EAAE,YAAY,GAAG,KAAK,EAAA;IAClG,IAAI,OAAO,GAAG,CAAC,IAAI,IAAI,CAAC,IAAI,GAAG,OAAO;AAAE,QAAA,OAAO,CAAC,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAC1E,IAAI,YAAY,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;AAAE,QAAA,OAAO,CAAC,CAAC,eAAe,EAAE,MAAM,CAAC;AACtF,IAAA,OAAO,EAAE;AACX;AAEA;AACM,SAAU,eAAe,CAAC,IAAY,EAAE,IAA8D,EAAA;IAC1G,OAAO;AACL,QAAA,CAAA,EAAG,IAAI,CAAA,MAAA,CAAQ;QACf,IAAI,CAAC,QAAQ,GAAG,CAAA,EAAG,IAAI,CAAA,gBAAA,CAAkB,GAAG,EAAE;QAC9C,IAAI,CAAC,KAAK,GAAG,CAAA,EAAG,IAAI,CAAA,aAAA,CAAe,GAAG,EAAE;QACxC,IAAI,CAAC,QAAQ,GAAG,CAAA,EAAG,IAAI,CAAA,gBAAA,CAAkB,GAAG,EAAE;KAC/C,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;AAC7B;AAEA;AACM,SAAU,gBAAgB,CAAC,WAAwC,EAAE,OAAgC,EAAA;IACzG,OAAO;AACL,QAAA,UAAU,EAAE,CAAC,CAAY,KAAI;YAC3B,CAAC,CAAC,cAAc,EAAE;YAClB,WAAW,CAAC,IAAI,CAAC;SAClB;AACD,QAAA,WAAW,EAAE,MAAM,WAAW,CAAC,KAAK,CAAC;AACrC,QAAA,MAAM,EAAE,CAAC,CAAY,KAAI;;YACvB,CAAC,CAAC,cAAc,EAAE;YAClB,WAAW,CAAC,KAAK,CAAC;YAClB,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,MAAA,CAAA,EAAA,GAAA,CAAC,CAAC,YAAY,0CAAE,KAAK,MAAA,IAAA,IAAA,EAAA,KAAA,KAAA,CAAA,GAAA,EAAA,GAAI,EAAE,CAAC,CAAC;SACjD;KACF;AACH;;;;"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"format-gcecItcD.js","sources":["src/utils/format.ts"],"sourcesContent":["export function formatNumber(value: number, locale: string, options?: Intl.NumberFormatOptions): string {\n if (value === null || value === undefined || isNaN(value)) return '';\n return new Intl.NumberFormat(locale, options).format(value);\n}\n\nexport function formatCurrency(amount: number, currency: string, locale: string): string {\n if (amount === null || amount === undefined || isNaN(amount)) return '';\n return new Intl.NumberFormat(locale, {\n style: 'currency',\n currency,\n minimumFractionDigits: 2,\n maximumFractionDigits: 2,\n }).format(amount);\n}\n\nexport function formatPercentage(value: number, locale: string, decimalPrecision = 2): string {\n if (value === null || value === undefined || isNaN(value)) return '';\n return new Intl.NumberFormat(locale, {\n style: 'percent',\n minimumFractionDigits: decimalPrecision,\n maximumFractionDigits: decimalPrecision,\n }).format(value);\n}\n\nexport function formatDate(value: string | Date, locale: string): string {\n if (!value) return '';\n const date = typeof value === 'string' ? new Date(value) : value;\n if (isNaN(date.getTime())) return '';\n return new Intl.DateTimeFormat(locale, { year: 'numeric', month: '2-digit', day: '2-digit' }).format(date);\n}\n\nexport function formatDateTime(value: string | Date, locale: string): string {\n if (!value) return '';\n const date = typeof value === 'string' ? new Date(value) : value;\n if (isNaN(date.getTime())) return '';\n return new Intl.DateTimeFormat(locale, {\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n hour: '2-digit',\n minute: '2-digit',\n }).format(date);\n}\n\nexport function formatTime(value: string, locale: string): string {\n if (!value) return '';\n // value is HH:MM or HH:MM:SS\n const [hours, minutes] = value.split(':').map(Number);\n const date = new Date();\n date.setHours(hours, minutes, 0, 0);\n return new Intl.DateTimeFormat(locale, { hour: '2-digit', minute: '2-digit' }).format(date);\n}\n\nexport function parseLocalizedNumber(str: string, locale: string): number | null {\n if (!str || !str.trim()) return null;\n // Detect decimal and thousands separators from the locale\n const parts = new Intl.NumberFormat(locale).formatToParts(1234567.89);\n const groupSep = parts.find((p: Intl.NumberFormatPart) => p.type === 'group')?.value ?? ',';\n const decimalSep = parts.find((p: Intl.NumberFormatPart) => p.type === 'decimal')?.value ?? '.';\n\n // Remove group separators, replace decimal separator with '.'\n const normalized = str\n .replace(new RegExp(`\\\\${groupSep}`, 'g'), '')\n .replace(new RegExp(`\\\\${decimalSep}`), '.')\n .replace('%', '')\n .trim();\n\n const parsed = parseFloat(normalized);\n return isNaN(parsed) ? null : parsed;\n}\n"],"names":[],"mappings":"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"}