@mmlogic/components 0.3.9 → 0.3.10

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 (59) 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_22.cjs.entry.js → mrd-boolean-field_23.cjs.entry.js} +591 -36
  4. package/dist/collection/collection-manifest.json +1 -0
  5. package/dist/collection/components/mrd-document-container/mrd-document-container.js +375 -0
  6. package/dist/collection/components/mrd-document-container/mrd-document-container.scss +79 -0
  7. package/dist/collection/components/mrd-document-list/mrd-document-list.js +562 -11
  8. package/dist/collection/components/mrd-document-list/mrd-document-list.scss +55 -0
  9. package/dist/collection/components/mrd-document-view/mrd-document-view.js +1 -1
  10. package/dist/collection/components/mrd-email-field/mrd-email-field.js +1 -1
  11. package/dist/collection/components/mrd-file-field/mrd-file-field.js +1 -1
  12. package/dist/collection/components/mrd-image-field/mrd-image-field.js +1 -1
  13. package/dist/collection/components/mrd-layout-section/mrd-layout-section.js +151 -15
  14. package/dist/collection/components/mrd-layout-section/mrd-layout-section.scss +0 -44
  15. package/dist/collection/components/mrd-longtext-field/mrd-longtext-field.js +1 -1
  16. package/dist/collection/components/mrd-number-field/mrd-number-field.js +1 -1
  17. package/dist/collection/components/mrd-secret-field/mrd-secret-field.js +2 -2
  18. package/dist/collection/components/mrd-text-field/mrd-text-field.js +1 -1
  19. package/dist/collection/components/mrd-textarea-field/mrd-textarea-field.js +1 -1
  20. package/dist/collection/components/mrd-time-field/mrd-time-field.js +1 -1
  21. package/dist/collection/dev/app.js +47 -0
  22. package/dist/collection/utils/i18n.js +10 -0
  23. package/dist/components/i18n.js +1 -1
  24. package/dist/components/mrd-document-container.d.ts +11 -0
  25. package/dist/components/mrd-document-container.js +1 -0
  26. package/dist/components/mrd-document-container2.js +1 -0
  27. package/dist/components/mrd-document-list2.js +1 -1
  28. package/dist/components/mrd-document-view2.js +1 -1
  29. package/dist/components/mrd-email-field2.js +1 -1
  30. package/dist/components/mrd-file-field2.js +1 -1
  31. package/dist/components/mrd-image-field2.js +1 -1
  32. package/dist/components/mrd-layout-section.js +1 -1
  33. package/dist/components/mrd-longtext-field2.js +1 -1
  34. package/dist/components/mrd-number-field2.js +1 -1
  35. package/dist/components/mrd-secret-field2.js +1 -1
  36. package/dist/components/mrd-text-field2.js +1 -1
  37. package/dist/components/mrd-textarea-field2.js +1 -1
  38. package/dist/components/mrd-time-field2.js +1 -1
  39. package/dist/esm/loader.js +1 -1
  40. package/dist/esm/mosterdcomponents.js +1 -1
  41. package/dist/esm/{mrd-boolean-field_22.entry.js → mrd-boolean-field_23.entry.js} +591 -37
  42. package/dist/mosterdcomponents/cell-renderer-B-AJDBcj.js.map +1 -0
  43. package/dist/mosterdcomponents/client-layout-ChqRA3AF.js.map +1 -0
  44. package/dist/mosterdcomponents/format-BAfsQfy1.js.map +1 -0
  45. package/dist/mosterdcomponents/i18n-s1XrKcD2.js.map +1 -0
  46. package/dist/mosterdcomponents/index-DkY-NQj0.js.map +1 -0
  47. package/dist/mosterdcomponents/index-DwF_Sp__.js.map +1 -0
  48. package/dist/mosterdcomponents/index.esm.js.map +1 -0
  49. package/dist/mosterdcomponents/mosterdcomponents.esm.js +1 -1
  50. package/dist/mosterdcomponents/mosterdcomponents.esm.js.map +1 -0
  51. package/dist/mosterdcomponents/p-69452249.entry.js +1 -0
  52. package/dist/mosterdcomponents/quill-C9pgw_k-.js.map +1 -0
  53. package/dist/mosterdcomponents/validation-Br9k2Ztw.js.map +1 -0
  54. package/dist/types/components/mrd-document-container/mrd-document-container.d.ts +94 -0
  55. package/dist/types/components/mrd-document-list/mrd-document-list.d.ts +102 -0
  56. package/dist/types/components/mrd-layout-section/mrd-layout-section.d.ts +29 -5
  57. package/dist/types/components.d.ts +217 -0
  58. package/package.json +1 -1
  59. package/dist/mosterdcomponents/p-b547291b.entry.js +0 -1
@@ -30,11 +30,168 @@ export class MrdDocumentList {
30
30
  this.rootLoading = true;
31
31
  /** True when the single-container level is collapsed away (dossier context). */
32
32
  this.singleContainer = false;
33
+ /** Currently selected node id — the target for "New folder" and (later) drops. */
34
+ this.selectedId = null;
35
+ /** Node id currently highlighted as a drag-drop target (null = none). */
36
+ this.dropTargetId = null;
37
+ /** Node id showing an "uploading…" indicator while a dropped file is processed. */
38
+ this.uploadingNodeId = null;
33
39
  this.nodeIndex = new Map();
40
+ // ── New-folder editing state ───────────────────────────────────────────────
41
+ /** The pending node whose inline name input is open (null = not creating). */
42
+ this.pendingNode = null;
43
+ /** The node the pending folder is being created under. */
44
+ this.pendingParent = null;
45
+ /** The container the pending folder belongs to. */
46
+ this.pendingContainer = null;
47
+ /** Current text in the inline name input. */
48
+ this.pendingName = '';
49
+ /** Ref to the inline input, so we can focus it after it mounts. */
50
+ this.pendingInputEl = null;
51
+ /** Set when a fresh input needs focus on the next render. */
52
+ this.pendingNeedsFocus = false;
53
+ /** Monotonic counter for unique temporary pending-node ids. */
54
+ this.newFolderSeq = 0;
55
+ /** Last emitted canCreateFolder value, to only emit mrdCanCreate on change. */
56
+ this.lastCanCreate = false;
57
+ // ── Drag & drop (move a document into another folder) ──────────────────────
58
+ /** The document row currently being dragged. */
59
+ this.dragDoc = null;
60
+ /** Id of the node the dragged document currently lives in. */
61
+ this.dragFromId = null;
62
+ /** External file dropped onto a node, awaiting its upload to complete. */
63
+ this.pendingUpload = null;
64
+ /** The optimistic document row awaiting its self href from the create POST. */
65
+ this.lastOptimisticDoc = null;
66
+ this.startCreateFolder = () => {
67
+ const resolved = this.resolveTarget();
68
+ if (!resolved || this.pendingNode)
69
+ return;
70
+ const { target, container } = resolved;
71
+ const tempId = `${container.id}/__new__${this.newFolderSeq++}`;
72
+ const node = this.makeFolderNode(tempId, '', container.id);
73
+ node.pending = true;
74
+ node.editing = true;
75
+ target.children = [...target.children, node];
76
+ target.open = true;
77
+ this.nodeIndex.set(node.id, node);
78
+ this.pendingNode = node;
79
+ this.pendingParent = target;
80
+ this.pendingContainer = container;
81
+ this.pendingName = '';
82
+ this.pendingNeedsFocus = true;
83
+ this.bump();
84
+ };
85
+ this.onPendingInput = (e) => {
86
+ var _a;
87
+ this.pendingName = e.target.value;
88
+ if ((_a = this.pendingNode) === null || _a === void 0 ? void 0 : _a.error) {
89
+ this.pendingNode.error = undefined;
90
+ this.bump();
91
+ }
92
+ };
93
+ this.onPendingKeyDown = (e) => {
94
+ if (e.key === 'Enter') {
95
+ e.preventDefault();
96
+ this.commitFolder();
97
+ }
98
+ else if (e.key === 'Escape') {
99
+ e.preventDefault();
100
+ this.cancelFolder();
101
+ }
102
+ };
103
+ // ── Drag & drop: move a document into another folder / container ────────────
104
+ this.onDocDragStart = (e, doc, node) => {
105
+ var _a, _b, _c;
106
+ this.dragDoc = doc;
107
+ this.dragFromId = node.id;
108
+ if (e.dataTransfer) {
109
+ e.dataTransfer.effectAllowed = 'move';
110
+ // A payload is required for a drag to start in some browsers.
111
+ e.dataTransfer.setData('text/plain', (_c = (_b = (_a = doc === null || doc === void 0 ? void 0 : doc._links) === null || _a === void 0 ? void 0 : _a.self) === null || _b === void 0 ? void 0 : _b.href) !== null && _c !== void 0 ? _c : '');
112
+ }
113
+ };
114
+ this.onDocDragEnd = () => {
115
+ this.dragDoc = null;
116
+ this.dragFromId = null;
117
+ if (this.dropTargetId !== null)
118
+ this.dropTargetId = null;
119
+ };
120
+ /** A folder/container node (or a document inside it) accepts an internal move
121
+ * or an external file drop. Always claims the event so it never bubbles to the
122
+ * root drop-zone; a different node is highlighted (its own folder = no-op). */
123
+ this.onNodeDragOver = (e, node) => {
124
+ const file = this.isFileDrag(e);
125
+ if (!this.dragDoc && !file)
126
+ return;
127
+ e.preventDefault();
128
+ e.stopPropagation();
129
+ if (e.dataTransfer)
130
+ e.dataTransfer.dropEffect = file ? 'copy' : 'move';
131
+ const target = !file && node.id === this.dragFromId ? null : node.id;
132
+ if (this.dropTargetId !== target)
133
+ this.dropTargetId = target;
134
+ };
135
+ this.onNodeDragLeave = (node) => {
136
+ if (this.dropTargetId === node.id)
137
+ this.dropTargetId = null;
138
+ };
139
+ this.onNodeDrop = (e, node, container) => {
140
+ if (this.isFileDrag(e)) {
141
+ e.preventDefault();
142
+ e.stopPropagation();
143
+ this.startFileUpload(e, node, container);
144
+ return;
145
+ }
146
+ if (!this.dragDoc)
147
+ return;
148
+ e.preventDefault();
149
+ e.stopPropagation();
150
+ this.moveDocument(node, container);
151
+ this.onDocDragEnd();
152
+ };
153
+ /** Root-zone drop (dossier context): target the single container's root. */
154
+ this.onRootDragOver = (e) => {
155
+ if (!this.singleContainer)
156
+ return;
157
+ const file = this.isFileDrag(e);
158
+ if (!this.dragDoc && !file)
159
+ return;
160
+ e.preventDefault();
161
+ if (e.dataTransfer)
162
+ e.dataTransfer.dropEffect = file ? 'copy' : 'move';
163
+ };
164
+ this.onRootDrop = (e) => {
165
+ if (!this.singleContainer)
166
+ return;
167
+ const only = this.containers[0];
168
+ if (this.isFileDrag(e)) {
169
+ e.preventDefault();
170
+ this.startFileUpload(e, only, only);
171
+ return;
172
+ }
173
+ if (!this.dragDoc)
174
+ return;
175
+ e.preventDefault();
176
+ this.moveDocument(only, only);
177
+ this.onDocDragEnd();
178
+ };
34
179
  }
35
180
  componentDidLoad() {
36
181
  this.emitContainerDistinct();
37
182
  }
183
+ componentDidRender() {
184
+ if (this.pendingNeedsFocus && this.pendingInputEl) {
185
+ this.pendingInputEl.focus();
186
+ this.pendingInputEl.select();
187
+ this.pendingNeedsFocus = false;
188
+ }
189
+ const can = this.canCreateFolder;
190
+ if (can !== this.lastCanCreate) {
191
+ this.lastCanCreate = can;
192
+ this.mrdCanCreate.emit(can);
193
+ }
194
+ }
38
195
  // ── Slot field names ─────────────────────────────────────────────────────
39
196
  get containerField() {
40
197
  var _a, _b, _c;
@@ -48,6 +205,10 @@ export class MrdDocumentList {
48
205
  var _a, _b, _c;
49
206
  return (_c = (_b = (_a = this.archetype) === null || _a === void 0 ? void 0 : _a.slots) === null || _b === void 0 ? void 0 : _b.title) !== null && _c !== void 0 ? _c : 'name';
50
207
  }
208
+ get fileField() {
209
+ var _a, _b, _c;
210
+ return (_c = (_b = (_a = this.archetype) === null || _a === void 0 ? void 0 : _a.slots) === null || _b === void 0 ? void 0 : _b.file) !== null && _c !== void 0 ? _c : 'file';
211
+ }
51
212
  get dateField() {
52
213
  var _a, _b;
53
214
  return (_b = (_a = this.archetype) === null || _a === void 0 ? void 0 : _a.slots) === null || _b === void 0 ? void 0 : _b.date;
@@ -200,7 +361,7 @@ export class MrdDocumentList {
200
361
  acc.push(seg);
201
362
  let child = node.children.find(c => c.label === seg);
202
363
  if (!child) {
203
- child = this.makeFolderNode(container.id + '/' + acc.join('/'), seg);
364
+ child = this.makeFolderNode(container.id + '/' + acc.join('/'), seg, container.id);
204
365
  node.children.push(child);
205
366
  this.nodeIndex.set(child.id, child);
206
367
  }
@@ -236,9 +397,9 @@ export class MrdDocumentList {
236
397
  open: false, loading: false, foldersLoaded: false, docsLoaded: false, docs: [],
237
398
  };
238
399
  }
239
- makeFolderNode(id, label) {
400
+ makeFolderNode(id, label, containerId) {
240
401
  return {
241
- id, kind: 'folder', label,
402
+ id, kind: 'folder', label, containerId,
242
403
  hasDocs: false, docCount: 0, totalCount: 0, children: [],
243
404
  open: false, loading: false, foldersLoaded: false, docsLoaded: false, docs: [],
244
405
  };
@@ -256,6 +417,7 @@ export class MrdDocumentList {
256
417
  }
257
418
  // ── Interaction ──────────────────────────────────────────────────────────
258
419
  toggleContainer(node) {
420
+ this.selectedId = node.id;
259
421
  node.open = !node.open;
260
422
  if (node.open && !node.foldersLoaded && !node.loading) {
261
423
  this.emitFolderDistinct(node);
@@ -263,12 +425,235 @@ export class MrdDocumentList {
263
425
  this.bump();
264
426
  }
265
427
  toggleFolder(container, node) {
428
+ this.selectedId = node.id;
266
429
  node.open = !node.open;
267
430
  if (node.open && node.hasDocs && !node.docsLoaded && !node.loading) {
268
431
  this.emitDocuments(container, node);
269
432
  }
270
433
  this.bump();
271
434
  }
435
+ // ── New folder (client-side pending, materialises on document drop) ─────────
436
+ /** Resolve the node a new folder should be created under, and its container.
437
+ * Uses the current selection; falls back to the single container (dossier). */
438
+ resolveTarget() {
439
+ if (this.selectedId) {
440
+ const n = this.nodeIndex.get(this.selectedId);
441
+ if (n && n.kind === 'container')
442
+ return { target: n, container: n };
443
+ if (n && n.kind === 'folder') {
444
+ const container = n.containerId ? this.nodeIndex.get(n.containerId) : undefined;
445
+ if (container)
446
+ return { target: n, container };
447
+ }
448
+ }
449
+ if (this.singleContainer && this.containers[0]) {
450
+ const only = this.containers[0];
451
+ return { target: only, container: only };
452
+ }
453
+ return null;
454
+ }
455
+ get canCreateFolder() {
456
+ return !this.pendingNode && this.resolveTarget() !== null;
457
+ }
458
+ /** Start creating a folder under the current target. Called by the host toolbar
459
+ * (mrd-document-container). No-op when there is no valid target. */
460
+ async createFolder() {
461
+ this.startCreateFolder();
462
+ }
463
+ /** Folder path segments of a node relative to its container (derived from its id). */
464
+ segmentsOf(node, container) {
465
+ if (node.kind === 'container')
466
+ return [];
467
+ const prefix = container.id + '/';
468
+ const rest = node.id.startsWith(prefix) ? node.id.slice(prefix.length) : '';
469
+ return rest ? rest.split('/') : [];
470
+ }
471
+ commitFolder() {
472
+ const node = this.pendingNode;
473
+ const parent = this.pendingParent;
474
+ const container = this.pendingContainer;
475
+ if (!node || !parent || !container)
476
+ return;
477
+ const name = this.pendingName.trim();
478
+ if (!name) {
479
+ this.cancelFolder();
480
+ return;
481
+ }
482
+ if (name.includes('/')) {
483
+ node.error = t('folder_no_slash', this.locale);
484
+ this.bump();
485
+ return;
486
+ }
487
+ const dup = parent.children.some(c => c !== node && c.label.toLowerCase() === name.toLowerCase());
488
+ if (dup) {
489
+ node.error = t('folder_duplicate', this.locale);
490
+ this.bump();
491
+ return;
492
+ }
493
+ // Finalise: swap the temp id for the real path-derived id so a later distinct
494
+ // value with the same folder string merges onto this node.
495
+ const segs = [...this.segmentsOf(parent, container), name];
496
+ this.nodeIndex.delete(node.id);
497
+ node.id = container.id + '/' + segs.join('/');
498
+ node.fullPath = '/' + segs.join('/');
499
+ node.label = name;
500
+ node.editing = false;
501
+ node.error = undefined;
502
+ node.open = true;
503
+ this.nodeIndex.set(node.id, node);
504
+ this.selectedId = node.id;
505
+ this.clearPending();
506
+ this.bump();
507
+ }
508
+ cancelFolder() {
509
+ const node = this.pendingNode;
510
+ const parent = this.pendingParent;
511
+ if (node && parent) {
512
+ parent.children = parent.children.filter(c => c !== node);
513
+ this.nodeIndex.delete(node.id);
514
+ }
515
+ this.clearPending();
516
+ this.bump();
517
+ }
518
+ clearPending() {
519
+ this.pendingNode = null;
520
+ this.pendingParent = null;
521
+ this.pendingContainer = null;
522
+ this.pendingName = '';
523
+ this.pendingInputEl = null;
524
+ this.pendingNeedsFocus = false;
525
+ }
526
+ /** True when the drag carries external files (an OS file drop, not an internal move). */
527
+ isFileDrag(e) {
528
+ return !this.dragDoc && !!e.dataTransfer && Array.from(e.dataTransfer.types).includes('Files');
529
+ }
530
+ // ── External file drop → upload → create document ──────────────────────────
531
+ /** Begin an upload: emit mrdUpload; the host uploads and calls setFileReference. */
532
+ startFileUpload(e, node, container) {
533
+ var _a, _b;
534
+ const file = (_b = (_a = e.dataTransfer) === null || _a === void 0 ? void 0 : _a.files) === null || _b === void 0 ? void 0 : _b[0]; // single file per drop
535
+ this.dropTargetId = null;
536
+ if (!file)
537
+ return;
538
+ this.pendingUpload = { file, node, container };
539
+ node.open = true;
540
+ this.uploadingNodeId = node.id;
541
+ this.bump();
542
+ this.mrdUpload.emit({ file });
543
+ }
544
+ /** Strip a single trailing extension for the document title. */
545
+ stripExtension(name) {
546
+ return name.replace(/\.[^./\\]+$/, '');
547
+ }
548
+ /** Host callback with the uploaded file reference. Builds the create payload
549
+ * from the archetype slots and requests the new document. */
550
+ async setFileReference(uri) {
551
+ const up = this.pendingUpload;
552
+ if (!up)
553
+ return;
554
+ const { file, node, container } = up;
555
+ const folder = this.folderStringOf(node, container);
556
+ const values = {
557
+ [this.fileField]: uri,
558
+ [this.titleField]: this.stripExtension(file.name),
559
+ [this.folderField]: folder,
560
+ [this.containerField]: container.href,
561
+ };
562
+ this.mrdCreateObject.emit({ path: this.buildDataPath(), values });
563
+ // Optimistically show the new document (no href until a reload).
564
+ const optimistic = { [this.titleField]: this.stripExtension(file.name), _optimistic: true };
565
+ const wasUnloadedEmpty = !node.docsLoaded && node.docCount === 0;
566
+ node.hasDocs = true;
567
+ node.pending = false;
568
+ node.docCount += 1;
569
+ if (wasUnloadedEmpty) {
570
+ node.docsLoaded = true;
571
+ node.docs = [optimistic];
572
+ }
573
+ else if (node.docsLoaded)
574
+ node.docs = [...node.docs, optimistic];
575
+ this.recomputeContainerTotals(container, +1);
576
+ this.lastOptimisticDoc = optimistic;
577
+ this.pendingUpload = null;
578
+ this.uploadingNodeId = null;
579
+ this.bump();
580
+ }
581
+ /** Host callback with the created document's self href, so the optimistic row
582
+ * becomes navigable without waiting for a reload. */
583
+ async setCreatedHref(href) {
584
+ var _a;
585
+ const doc = this.lastOptimisticDoc;
586
+ if (!doc || !href)
587
+ return;
588
+ doc._links = Object.assign(Object.assign({}, ((_a = doc._links) !== null && _a !== void 0 ? _a : {})), { self: { href } });
589
+ doc._optimistic = false;
590
+ this.lastOptimisticDoc = null;
591
+ this.bump();
592
+ }
593
+ /** Folder string a node represents: the raw distinct value when it holds
594
+ * documents, else derived from its path segments; null for a container root. */
595
+ folderStringOf(node, container) {
596
+ if (node.kind === 'container')
597
+ return null;
598
+ if (node.fullPath != null && node.fullPath !== '')
599
+ return node.fullPath;
600
+ const segs = this.segmentsOf(node, container);
601
+ return segs.length ? '/' + segs.join('/') : null;
602
+ }
603
+ moveDocument(toNode, toContainer) {
604
+ var _a, _b;
605
+ const doc = this.dragDoc;
606
+ const fromNode = this.dragFromId ? this.nodeIndex.get(this.dragFromId) : undefined;
607
+ if (!doc || !fromNode)
608
+ return;
609
+ const fromContainer = fromNode.kind === 'container'
610
+ ? fromNode
611
+ : (fromNode.containerId ? this.nodeIndex.get(fromNode.containerId) : undefined);
612
+ if (!fromContainer)
613
+ return;
614
+ const targetFolder = this.folderStringOf(toNode, toContainer);
615
+ const sourceFolder = this.folderStringOf(fromNode, fromContainer);
616
+ const sameContainer = toContainer === fromContainer;
617
+ // No-op: dropped back onto its own folder.
618
+ if (toNode === fromNode || (sameContainer && targetFolder === sourceFolder))
619
+ return;
620
+ // ── Optimistic tree update (fire-and-forget; no refetch) ──
621
+ fromNode.docs = fromNode.docs.filter(d => d !== doc);
622
+ fromNode.docCount = Math.max(0, fromNode.docCount - 1);
623
+ // A now-empty folder should stop showing an expand chevron (it disappears on
624
+ // the next reload anyway, since it is derived from document folder strings).
625
+ if (fromNode.docsLoaded)
626
+ fromNode.hasDocs = fromNode.docs.length > 0;
627
+ toNode.hasDocs = true;
628
+ toNode.pending = false; // a pending folder is now materialised
629
+ toNode.docCount += 1;
630
+ if (toNode.kind === 'folder')
631
+ toNode.fullPath = targetFolder !== null && targetFolder !== void 0 ? targetFolder : '';
632
+ if (toNode.docsLoaded)
633
+ toNode.docs = [...toNode.docs, doc];
634
+ this.recomputeContainerTotals(fromContainer, -1);
635
+ if (!sameContainer)
636
+ this.recomputeContainerTotals(toContainer, +1);
637
+ // ── Emit the generic update; host PATCHes the document ──
638
+ const href = (_b = (_a = doc === null || doc === void 0 ? void 0 : doc._links) === null || _a === void 0 ? void 0 : _a.self) === null || _b === void 0 ? void 0 : _b.href;
639
+ if (href) {
640
+ const values = { [this.folderField]: targetFolder };
641
+ if (!sameContainer)
642
+ values[this.containerField] = toContainer.href;
643
+ this.mrdUpdateObject.emit({ href, values });
644
+ }
645
+ this.bump();
646
+ }
647
+ /** Recompute a container's subtree totals from docCounts when its folders are
648
+ * loaded; otherwise just nudge the aggregate badge by the delta. */
649
+ recomputeContainerTotals(container, delta) {
650
+ if (container.foldersLoaded) {
651
+ this.computeTotals(container);
652
+ }
653
+ else {
654
+ container.totalCount = Math.max(0, container.totalCount + delta);
655
+ }
656
+ }
272
657
  openDoc(doc) {
273
658
  var _a, _b, _c, _d;
274
659
  const href = (_b = (_a = doc === null || doc === void 0 ? void 0 : doc._links) === null || _a === void 0 ? void 0 : _a.self) === null || _b === void 0 ? void 0 : _b.href;
@@ -291,12 +676,12 @@ export class MrdDocumentList {
291
676
  indent(depth) {
292
677
  return h("span", { class: "mrd-document-list__indent", style: { width: `${depth * 18}px` } });
293
678
  }
294
- renderDocRow(doc, depth) {
679
+ renderDocRow(doc, node, container, depth) {
295
680
  var _a, _b, _c, _d, _e;
296
681
  const label = (_b = (_a = doc === null || doc === void 0 ? void 0 : doc[this.titleField]) !== null && _a !== void 0 ? _a : doc === null || doc === void 0 ? void 0 : doc.name) !== null && _b !== void 0 ? _b : '';
297
682
  const dateRaw = this.dateField ? doc === null || doc === void 0 ? void 0 : doc[this.dateField] : undefined;
298
683
  const date = dateRaw ? formatDate(dateRaw, this.locale) : '';
299
- return (h("div", { class: "mrd-document-list__row mrd-document-list__row--doc", key: (_e = (_d = (_c = doc === null || doc === void 0 ? void 0 : doc._links) === null || _c === void 0 ? void 0 : _c.self) === null || _d === void 0 ? void 0 : _d.href) !== null && _e !== void 0 ? _e : label, onClick: () => this.openDoc(doc) }, this.indent(depth), this.renderChevron(false, true), this.renderDocIcon(), h("span", { class: "mrd-document-list__label" }, label), date && h("span", { class: "mrd-document-list__date" }, date)));
684
+ return (h("div", { class: "mrd-document-list__row mrd-document-list__row--doc", key: (_e = (_d = (_c = doc === null || doc === void 0 ? void 0 : doc._links) === null || _c === void 0 ? void 0 : _c.self) === null || _d === void 0 ? void 0 : _d.href) !== null && _e !== void 0 ? _e : label, draggable: true, onClick: () => this.openDoc(doc), onDragStart: (e) => this.onDocDragStart(e, doc, node), onDragEnd: this.onDocDragEnd, onDragOver: (e) => this.onNodeDragOver(e, node), onDragLeave: () => this.onNodeDragLeave(node), onDrop: (e) => this.onNodeDrop(e, node, container) }, this.indent(depth), this.renderChevron(false, true), this.renderDocIcon(), h("span", { class: "mrd-document-list__label" }, label), date && h("span", { class: "mrd-document-list__date" }, date)));
300
685
  }
301
686
  renderLoadingRow(depth) {
302
687
  return (h("div", { class: "mrd-document-list__loading", key: "loading" }, this.indent(depth), h("span", { class: "mrd-document-list__spinner" }), t('loading', this.locale)));
@@ -307,7 +692,7 @@ export class MrdDocumentList {
307
692
  if (node.hasDocs) {
308
693
  if (node.docsLoaded) {
309
694
  for (const doc of node.docs)
310
- out.push(this.renderDocRow(doc, depth));
695
+ out.push(this.renderDocRow(doc, node, container, depth));
311
696
  }
312
697
  else if (node.loading) {
313
698
  out.push(this.renderLoadingRow(depth));
@@ -317,14 +702,34 @@ export class MrdDocumentList {
317
702
  for (const child of node.children) {
318
703
  out.push(this.renderFolderNode(container, child, depth));
319
704
  }
705
+ // An uploading file dropped onto this node.
706
+ if (node.id === this.uploadingNodeId)
707
+ out.push(this.renderUploadingRow(depth));
320
708
  return out;
321
709
  }
710
+ renderUploadingRow(depth) {
711
+ return (h("div", { class: "mrd-document-list__loading", key: "uploading" }, this.indent(depth), h("span", { class: "mrd-document-list__spinner" }), t('upload_file', this.locale)));
712
+ }
713
+ renderFolderEditRow(node, depth) {
714
+ return (h("div", { class: "mrd-document-list__node", key: node.id }, h("div", { class: "mrd-document-list__row mrd-document-list__row--edit" }, this.indent(depth), this.renderChevron(false, true), this.renderFolderIcon(), h("input", { class: "mrd-document-list__name-input", type: "text", placeholder: t('folder_name_placeholder', this.locale), ref: el => (this.pendingInputEl = el), onInput: this.onPendingInput, onKeyDown: this.onPendingKeyDown, onBlur: () => this.cancelFolder() })), node.error && (h("div", { class: "mrd-document-list__error", style: { paddingLeft: `${depth * 18 + 52}px` } }, node.error))));
715
+ }
322
716
  renderFolderNode(container, node, depth) {
717
+ if (node.editing)
718
+ return this.renderFolderEditRow(node, depth);
323
719
  const leaf = node.children.length === 0 && !node.hasDocs;
324
- return (h("div", { class: "mrd-document-list__node", key: node.id }, h("div", { class: "mrd-document-list__row", onClick: () => this.toggleFolder(container, node) }, this.indent(depth), this.renderChevron(node.open, leaf), this.renderFolderIcon(), h("span", { class: "mrd-document-list__label" }, node.label), h("span", { class: "mrd-document-list__count" }, node.totalCount)), node.open && h("div", { class: "mrd-document-list__kids" }, this.renderChildren(container, node, depth + 1))));
720
+ const selected = node.id === this.selectedId;
721
+ const cls = 'mrd-document-list__row'
722
+ + (selected ? ' mrd-document-list__row--selected' : '')
723
+ + (node.pending ? ' mrd-document-list__row--pending' : '')
724
+ + (node.id === this.dropTargetId ? ' mrd-document-list__row--drop' : '');
725
+ return (h("div", { class: "mrd-document-list__node", key: node.id }, h("div", { class: cls, onClick: () => this.toggleFolder(container, node), onDragOver: (e) => this.onNodeDragOver(e, node), onDragLeave: () => this.onNodeDragLeave(node), onDrop: (e) => this.onNodeDrop(e, node, container) }, this.indent(depth), this.renderChevron(node.open, leaf), this.renderFolderIcon(), h("span", { class: "mrd-document-list__label" }, node.label), h("span", { class: "mrd-document-list__count" }, node.totalCount)), node.open && h("div", { class: "mrd-document-list__kids" }, this.renderChildren(container, node, depth + 1))));
325
726
  }
326
727
  renderContainerNode(node, depth) {
327
- return (h("div", { class: "mrd-document-list__node", key: node.id }, h("div", { class: "mrd-document-list__row mrd-document-list__row--dossier", onClick: () => this.toggleContainer(node) }, this.indent(depth), this.renderChevron(node.open, false), this.renderDossierIcon(), h("span", { class: "mrd-document-list__label" }, node.label), h("span", { class: "mrd-document-list__count" }, node.totalCount)), node.open && (h("div", { class: "mrd-document-list__kids" }, node.loading && !node.foldersLoaded
728
+ const selected = node.id === this.selectedId;
729
+ const cls = 'mrd-document-list__row mrd-document-list__row--dossier'
730
+ + (selected ? ' mrd-document-list__row--selected' : '')
731
+ + (node.id === this.dropTargetId ? ' mrd-document-list__row--drop' : '');
732
+ return (h("div", { class: "mrd-document-list__node", key: node.id }, h("div", { class: cls, onClick: () => this.toggleContainer(node), onDragOver: (e) => this.onNodeDragOver(e, node), onDragLeave: () => this.onNodeDragLeave(node), onDrop: (e) => this.onNodeDrop(e, node, node) }, this.indent(depth), this.renderChevron(node.open, false), this.renderDossierIcon(), h("span", { class: "mrd-document-list__label" }, node.label), h("span", { class: "mrd-document-list__count" }, node.totalCount)), node.open && (h("div", { class: "mrd-document-list__kids" }, node.loading && !node.foldersLoaded
328
733
  ? this.renderLoadingRow(depth + 1)
329
734
  : this.renderChildren(node, node, depth + 1)))));
330
735
  }
@@ -334,7 +739,7 @@ export class MrdDocumentList {
334
739
  body = this.renderLoadingRow(0);
335
740
  }
336
741
  else if (this.containers.length === 0) {
337
- body = h("div", { key: '0c4190bb86543e2d37cc1584b3495eb6eab5664c', class: "mrd-document-list__empty" }, t('no_results', this.locale));
742
+ body = h("div", { key: 'b8c3297cd7cff5bf79ce694d2ee1a64c570df5ce', class: "mrd-document-list__empty" }, t('no_results', this.locale));
338
743
  }
339
744
  else if (this.singleContainer) {
340
745
  // Dossier context: render the single container's folder tree at the root.
@@ -346,7 +751,7 @@ export class MrdDocumentList {
346
751
  else {
347
752
  body = this.containers.map(c => this.renderContainerNode(c, 0));
348
753
  }
349
- return (h(Host, { key: '4e38d9ef5a1df137a41516a47dc5debbea6b2f35' }, h("div", { key: '6d2f162624e5bbc8650a2978a3b045c21fe13ea3', class: "mrd-document-list" }, body)));
754
+ return (h(Host, { key: 'ff3b024a79ba2270aa28861923bc6406f98061aa' }, h("div", { key: '9db121e5336a4e3c82443257f6424db5ab4f0baf', class: "mrd-document-list", onDragOver: this.onRootDragOver, onDrop: this.onRootDrop }, body)));
350
755
  }
351
756
  static get is() { return "mrd-document-list"; }
352
757
  static get encapsulation() { return "scoped"; }
@@ -457,7 +862,10 @@ export class MrdDocumentList {
457
862
  return {
458
863
  "containers": {},
459
864
  "rootLoading": {},
460
- "singleContainer": {}
865
+ "singleContainer": {},
866
+ "selectedId": {},
867
+ "dropTargetId": {},
868
+ "uploadingNodeId": {}
461
869
  };
462
870
  }
463
871
  static get events() {
@@ -506,6 +914,81 @@ export class MrdDocumentList {
506
914
  "resolved": "{ href?: string | undefined; label: string; }",
507
915
  "references": {}
508
916
  }
917
+ }, {
918
+ "method": "mrdCanCreate",
919
+ "name": "mrdCanCreate",
920
+ "bubbles": true,
921
+ "cancelable": true,
922
+ "composed": true,
923
+ "docs": {
924
+ "tags": [],
925
+ "text": "Emitted whenever the \"can create a folder here\" state changes, so a host\ntoolbar (mrd-document-container) can enable/disable its New folder button."
926
+ },
927
+ "complexType": {
928
+ "original": "boolean",
929
+ "resolved": "boolean",
930
+ "references": {}
931
+ }
932
+ }, {
933
+ "method": "mrdUpdateObject",
934
+ "name": "mrdUpdateObject",
935
+ "bubbles": true,
936
+ "cancelable": true,
937
+ "composed": true,
938
+ "docs": {
939
+ "tags": [],
940
+ "text": "Generic \"update an existing object\" request \u2014 fired when a document is\ndragged into another folder/container. Host PATCHes `href` with `values`\n(no move/folder knowledge needed). `values` carries the new folder string\n(or null for the container root) and, on a cross-container move, the new\ncontainer relation href."
941
+ },
942
+ "complexType": {
943
+ "original": "{ href: string; values: Record<string, unknown> }",
944
+ "resolved": "{ href: string; values: Record<string, unknown>; }",
945
+ "references": {
946
+ "Record": {
947
+ "location": "global",
948
+ "id": "global::Record"
949
+ }
950
+ }
951
+ }
952
+ }, {
953
+ "method": "mrdUpload",
954
+ "name": "mrdUpload",
955
+ "bubbles": true,
956
+ "cancelable": true,
957
+ "composed": true,
958
+ "docs": {
959
+ "tags": [],
960
+ "text": "An external file was dropped onto a folder/container. Same flow as mrd-form:\nhost uploads the file and calls `setFileReference(uri)` with the result."
961
+ },
962
+ "complexType": {
963
+ "original": "{ file: File }",
964
+ "resolved": "{ file: File; }",
965
+ "references": {
966
+ "File": {
967
+ "location": "global",
968
+ "id": "global::File"
969
+ }
970
+ }
971
+ }
972
+ }, {
973
+ "method": "mrdCreateObject",
974
+ "name": "mrdCreateObject",
975
+ "bubbles": true,
976
+ "cancelable": true,
977
+ "composed": true,
978
+ "docs": {
979
+ "tags": [],
980
+ "text": "Fired once the dropped file is uploaded \u2014 requests creation of a new document.\nHost POSTs `values` to `path` (on a 400 it can open the create form itself)."
981
+ },
982
+ "complexType": {
983
+ "original": "{ path: string; values: Record<string, unknown> }",
984
+ "resolved": "{ path: string; values: Record<string, unknown>; }",
985
+ "references": {
986
+ "Record": {
987
+ "location": "global",
988
+ "id": "global::Record"
989
+ }
990
+ }
991
+ }
509
992
  }];
510
993
  }
511
994
  static get methods() {
@@ -569,6 +1052,74 @@ export class MrdDocumentList {
569
1052
  "text": "Inject the documents loaded for one folder node.",
570
1053
  "tags": []
571
1054
  }
1055
+ },
1056
+ "createFolder": {
1057
+ "complexType": {
1058
+ "signature": "() => Promise<void>",
1059
+ "parameters": [],
1060
+ "references": {
1061
+ "Promise": {
1062
+ "location": "global",
1063
+ "id": "global::Promise"
1064
+ }
1065
+ },
1066
+ "return": "Promise<void>"
1067
+ },
1068
+ "docs": {
1069
+ "text": "Start creating a folder under the current target. Called by the host toolbar\n(mrd-document-container). No-op when there is no valid target.",
1070
+ "tags": []
1071
+ }
1072
+ },
1073
+ "setFileReference": {
1074
+ "complexType": {
1075
+ "signature": "(uri: string) => Promise<void>",
1076
+ "parameters": [{
1077
+ "name": "uri",
1078
+ "type": "string",
1079
+ "docs": ""
1080
+ }],
1081
+ "references": {
1082
+ "Promise": {
1083
+ "location": "global",
1084
+ "id": "global::Promise"
1085
+ },
1086
+ "Record": {
1087
+ "location": "global",
1088
+ "id": "global::Record"
1089
+ },
1090
+ "DocRow": {
1091
+ "location": "local",
1092
+ "path": "/Users/michiel/Mosterd/mosterd-components/src/components/mrd-document-list/mrd-document-list.tsx",
1093
+ "id": "src/components/mrd-document-list/mrd-document-list.tsx::DocRow"
1094
+ }
1095
+ },
1096
+ "return": "Promise<void>"
1097
+ },
1098
+ "docs": {
1099
+ "text": "Host callback with the uploaded file reference. Builds the create payload\nfrom the archetype slots and requests the new document.",
1100
+ "tags": []
1101
+ }
1102
+ },
1103
+ "setCreatedHref": {
1104
+ "complexType": {
1105
+ "signature": "(href: string) => Promise<void>",
1106
+ "parameters": [{
1107
+ "name": "href",
1108
+ "type": "string",
1109
+ "docs": ""
1110
+ }],
1111
+ "references": {
1112
+ "Promise": {
1113
+ "location": "global",
1114
+ "id": "global::Promise"
1115
+ }
1116
+ },
1117
+ "return": "Promise<void>"
1118
+ },
1119
+ "docs": {
1120
+ "text": "Host callback with the created document's self href, so the optimistic row\nbecomes navigable without waiting for a reload.",
1121
+ "tags": []
1122
+ }
572
1123
  }
573
1124
  };
574
1125
  }