@mmlogic/components 0.1.0

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 (159) hide show
  1. package/README.md +221 -0
  2. package/dist/cjs/app-globals-V2Kpy_OQ.js +5 -0
  3. package/dist/cjs/format-CDw-zie_.js +82 -0
  4. package/dist/cjs/index-OvnIRO4Y.js +1523 -0
  5. package/dist/cjs/index.cjs.js +32 -0
  6. package/dist/cjs/loader.cjs.js +13 -0
  7. package/dist/cjs/mosterdcomponents.cjs.js +25 -0
  8. package/dist/cjs/mrd-boolean-field_16.cjs.entry.js +1185 -0
  9. package/dist/cjs/mrd-table.cjs.entry.js +322 -0
  10. package/dist/cjs/quill-DmFfnC1f.js +16272 -0
  11. package/dist/collection/collection-manifest.json +29 -0
  12. package/dist/collection/components/mrd-boolean-field/mrd-boolean-field.js +199 -0
  13. package/dist/collection/components/mrd-boolean-field/mrd-boolean-field.scss +77 -0
  14. package/dist/collection/components/mrd-currency-field/mrd-currency-field.js +248 -0
  15. package/dist/collection/components/mrd-currency-field/mrd-currency-field.scss +100 -0
  16. package/dist/collection/components/mrd-date-field/mrd-date-field.js +206 -0
  17. package/dist/collection/components/mrd-date-field/mrd-date-field.scss +66 -0
  18. package/dist/collection/components/mrd-datetime-field/mrd-datetime-field.js +206 -0
  19. package/dist/collection/components/mrd-datetime-field/mrd-datetime-field.scss +66 -0
  20. package/dist/collection/components/mrd-email-field/mrd-email-field.js +230 -0
  21. package/dist/collection/components/mrd-email-field/mrd-email-field.scss +69 -0
  22. package/dist/collection/components/mrd-field/mrd-field.js +187 -0
  23. package/dist/collection/components/mrd-file-field/mrd-file-field.js +273 -0
  24. package/dist/collection/components/mrd-file-field/mrd-file-field.scss +140 -0
  25. package/dist/collection/components/mrd-form/mrd-form.js +245 -0
  26. package/dist/collection/components/mrd-form/mrd-form.scss +116 -0
  27. package/dist/collection/components/mrd-hyperlink-field/mrd-hyperlink-field.js +230 -0
  28. package/dist/collection/components/mrd-hyperlink-field/mrd-hyperlink-field.scss +69 -0
  29. package/dist/collection/components/mrd-image-field/mrd-image-field.js +287 -0
  30. package/dist/collection/components/mrd-image-field/mrd-image-field.scss +166 -0
  31. package/dist/collection/components/mrd-list-field/mrd-list-field.js +311 -0
  32. package/dist/collection/components/mrd-list-field/mrd-list-field.scss +109 -0
  33. package/dist/collection/components/mrd-number-field/mrd-number-field.js +316 -0
  34. package/dist/collection/components/mrd-number-field/mrd-number-field.scss +77 -0
  35. package/dist/collection/components/mrd-relation-field/mrd-relation-field.js +490 -0
  36. package/dist/collection/components/mrd-relation-field/mrd-relation-field.scss +266 -0
  37. package/dist/collection/components/mrd-table/mrd-table.js +522 -0
  38. package/dist/collection/components/mrd-table/mrd-table.scss +158 -0
  39. package/dist/collection/components/mrd-text-field/mrd-text-field.js +227 -0
  40. package/dist/collection/components/mrd-text-field/mrd-text-field.scss +69 -0
  41. package/dist/collection/components/mrd-textarea-field/mrd-textarea-field.js +267 -0
  42. package/dist/collection/components/mrd-textarea-field/mrd-textarea-field.scss +79 -0
  43. package/dist/collection/components/mrd-time-field/mrd-time-field.js +206 -0
  44. package/dist/collection/components/mrd-time-field/mrd-time-field.scss +66 -0
  45. package/dist/collection/index.js +1 -0
  46. package/dist/collection/types/client-layout.js +30 -0
  47. package/dist/collection/types/index.js +1 -0
  48. package/dist/collection/utils/cell-renderer.js +57 -0
  49. package/dist/collection/utils/format.js +72 -0
  50. package/dist/collection/utils/i18n.js +80 -0
  51. package/dist/collection/utils/validation.js +46 -0
  52. package/dist/components/client-layout.js +1 -0
  53. package/dist/components/format.js +1 -0
  54. package/dist/components/i18n.js +1 -0
  55. package/dist/components/index.d.ts +35 -0
  56. package/dist/components/index.js +1 -0
  57. package/dist/components/mrd-boolean-field.d.ts +11 -0
  58. package/dist/components/mrd-boolean-field.js +1 -0
  59. package/dist/components/mrd-boolean-field2.js +1 -0
  60. package/dist/components/mrd-currency-field.d.ts +11 -0
  61. package/dist/components/mrd-currency-field.js +1 -0
  62. package/dist/components/mrd-currency-field2.js +1 -0
  63. package/dist/components/mrd-date-field.d.ts +11 -0
  64. package/dist/components/mrd-date-field.js +1 -0
  65. package/dist/components/mrd-date-field2.js +1 -0
  66. package/dist/components/mrd-datetime-field.d.ts +11 -0
  67. package/dist/components/mrd-datetime-field.js +1 -0
  68. package/dist/components/mrd-datetime-field2.js +1 -0
  69. package/dist/components/mrd-email-field.d.ts +11 -0
  70. package/dist/components/mrd-email-field.js +1 -0
  71. package/dist/components/mrd-email-field2.js +1 -0
  72. package/dist/components/mrd-field.d.ts +11 -0
  73. package/dist/components/mrd-field.js +1 -0
  74. package/dist/components/mrd-field2.js +1 -0
  75. package/dist/components/mrd-file-field.d.ts +11 -0
  76. package/dist/components/mrd-file-field.js +1 -0
  77. package/dist/components/mrd-file-field2.js +1 -0
  78. package/dist/components/mrd-form.d.ts +11 -0
  79. package/dist/components/mrd-form.js +1 -0
  80. package/dist/components/mrd-hyperlink-field.d.ts +11 -0
  81. package/dist/components/mrd-hyperlink-field.js +1 -0
  82. package/dist/components/mrd-hyperlink-field2.js +1 -0
  83. package/dist/components/mrd-image-field.d.ts +11 -0
  84. package/dist/components/mrd-image-field.js +1 -0
  85. package/dist/components/mrd-image-field2.js +1 -0
  86. package/dist/components/mrd-list-field.d.ts +11 -0
  87. package/dist/components/mrd-list-field.js +1 -0
  88. package/dist/components/mrd-list-field2.js +1 -0
  89. package/dist/components/mrd-number-field.d.ts +11 -0
  90. package/dist/components/mrd-number-field.js +1 -0
  91. package/dist/components/mrd-number-field2.js +1 -0
  92. package/dist/components/mrd-relation-field.d.ts +11 -0
  93. package/dist/components/mrd-relation-field.js +1 -0
  94. package/dist/components/mrd-relation-field2.js +1 -0
  95. package/dist/components/mrd-table.d.ts +11 -0
  96. package/dist/components/mrd-table.js +1 -0
  97. package/dist/components/mrd-text-field.d.ts +11 -0
  98. package/dist/components/mrd-text-field.js +1 -0
  99. package/dist/components/mrd-text-field2.js +1 -0
  100. package/dist/components/mrd-textarea-field.d.ts +11 -0
  101. package/dist/components/mrd-textarea-field.js +1 -0
  102. package/dist/components/mrd-textarea-field2.js +1 -0
  103. package/dist/components/mrd-time-field.d.ts +11 -0
  104. package/dist/components/mrd-time-field.js +1 -0
  105. package/dist/components/mrd-time-field2.js +1 -0
  106. package/dist/components/quill.js +1 -0
  107. package/dist/components/validation.js +1 -0
  108. package/dist/esm/app-globals-DQuL1Twl.js +3 -0
  109. package/dist/esm/format-Dt-aHxkM.js +74 -0
  110. package/dist/esm/index-DQ_he8te.js +1514 -0
  111. package/dist/esm/index.js +32 -0
  112. package/dist/esm/loader.js +11 -0
  113. package/dist/esm/mosterdcomponents.js +21 -0
  114. package/dist/esm/mrd-boolean-field_16.entry.js +1168 -0
  115. package/dist/esm/mrd-table.entry.js +320 -0
  116. package/dist/esm/quill-CiuCgGz_.js +16266 -0
  117. package/dist/index.cjs.js +1 -0
  118. package/dist/index.js +1 -0
  119. package/dist/mosterdcomponents/index.esm.js +1 -0
  120. package/dist/mosterdcomponents/mosterdcomponents.css +1 -0
  121. package/dist/mosterdcomponents/mosterdcomponents.esm.js +1 -0
  122. package/dist/mosterdcomponents/p-88cd0930.entry.js +1 -0
  123. package/dist/mosterdcomponents/p-926ed331.entry.js +1 -0
  124. package/dist/mosterdcomponents/p-CiuCgGz_.js +1 -0
  125. package/dist/mosterdcomponents/p-DQ_he8te.js +2 -0
  126. package/dist/mosterdcomponents/p-DQuL1Twl.js +1 -0
  127. package/dist/mosterdcomponents/p-Dt-aHxkM.js +1 -0
  128. package/dist/types/components/mrd-boolean-field/mrd-boolean-field.d.ts +22 -0
  129. package/dist/types/components/mrd-currency-field/mrd-currency-field.d.ts +26 -0
  130. package/dist/types/components/mrd-date-field/mrd-date-field.d.ts +21 -0
  131. package/dist/types/components/mrd-datetime-field/mrd-datetime-field.d.ts +21 -0
  132. package/dist/types/components/mrd-email-field/mrd-email-field.d.ts +22 -0
  133. package/dist/types/components/mrd-field/mrd-field.d.ts +23 -0
  134. package/dist/types/components/mrd-file-field/mrd-file-field.d.ts +30 -0
  135. package/dist/types/components/mrd-form/mrd-form.d.ts +24 -0
  136. package/dist/types/components/mrd-hyperlink-field/mrd-hyperlink-field.d.ts +22 -0
  137. package/dist/types/components/mrd-image-field/mrd-image-field.d.ts +31 -0
  138. package/dist/types/components/mrd-list-field/mrd-list-field.d.ts +28 -0
  139. package/dist/types/components/mrd-number-field/mrd-number-field.d.ts +29 -0
  140. package/dist/types/components/mrd-relation-field/mrd-relation-field.d.ts +44 -0
  141. package/dist/types/components/mrd-table/mrd-table.d.ts +72 -0
  142. package/dist/types/components/mrd-text-field/mrd-text-field.d.ts +22 -0
  143. package/dist/types/components/mrd-textarea-field/mrd-textarea-field.d.ts +25 -0
  144. package/dist/types/components/mrd-time-field/mrd-time-field.d.ts +21 -0
  145. package/dist/types/components.d.ts +1609 -0
  146. package/dist/types/index.d.ts +1 -0
  147. package/dist/types/stencil-public-runtime.d.ts +1860 -0
  148. package/dist/types/types/client-layout.d.ts +76 -0
  149. package/dist/types/types/index.d.ts +1 -0
  150. package/dist/types/utils/cell-renderer.d.ts +14 -0
  151. package/dist/types/utils/format.d.ts +7 -0
  152. package/dist/types/utils/i18n.d.ts +1 -0
  153. package/dist/types/utils/validation.d.ts +5 -0
  154. package/loader/cdn.js +1 -0
  155. package/loader/index.cjs.js +1 -0
  156. package/loader/index.d.ts +24 -0
  157. package/loader/index.es2017.js +1 -0
  158. package/loader/index.js +2 -0
  159. package/package.json +54 -0
@@ -0,0 +1,522 @@
1
+ import { h, Host } from "@stencil/core";
2
+ import { CellRenderer } from "../../utils/cell-renderer";
3
+ const BUFFER = 10;
4
+ /** Wacht deze tijd (ms) na het laatste scroll-event voordat pagina's worden
5
+ * aangevraagd. Pagina's die de gebruiker snel voorbij scrollt worden zo geskipt. */
6
+ const REQUEST_DEBOUNCE_MS = 150;
7
+ export class MrdTable {
8
+ constructor() {
9
+ // ── Debounce internals (geen @State — triggert geen re-render) ─────────────
10
+ /** Pagina's die wachten op debounce-flush. */
11
+ this.pendingPages = new Set();
12
+ /** Handle van de actieve debounce-timer. */
13
+ this.debounceTimer = null;
14
+ // ── Props ──────────────────────────────────────────────────────────────────
15
+ this.columns = [];
16
+ /** Direct rows (non-paginated mode, used when totalElements === 0). */
17
+ this.rows = [];
18
+ this.locale = navigator.language;
19
+ /** Total number of records across all pages. 0 = non-paginated mode. */
20
+ this.totalElements = 0;
21
+ /** Records per page (must match the API page size). */
22
+ this.pageSize = 20;
23
+ /** Row height in px — used for spacer and scroll-position maths. */
24
+ this.rowHeight = 36;
25
+ /** Height of the scroll container in px. */
26
+ this.tableHeight = 500;
27
+ /** Initial sort applied on load, e.g. "timestamp,desc" or "name".
28
+ * Parsed by init() into sortField + sortDir. */
29
+ this.defaultSort = '';
30
+ // ── Internal state ─────────────────────────────────────────────────────────
31
+ /** Pages injected via setPage(). Always replaced by a new Map to trigger re-render. */
32
+ this.loadedPages = new Map();
33
+ /** Pages already requested via mrdLoadPage (to avoid duplicate events). */
34
+ this.requestedPages = new Set();
35
+ /** Absolute index of the first row currently in the render window. */
36
+ this.renderStart = 0;
37
+ /** Absolute index of the last row currently in the render window. */
38
+ this.renderEnd = 0;
39
+ /** Locked column widths (px) — measured after first page renders, then fixed. */
40
+ this.colWidths = [];
41
+ /** Column currently used for sorting (empty = no sort). */
42
+ this.sortField = '';
43
+ /** Sort direction for sortField. */
44
+ this.sortDir = 'asc';
45
+ this.handleScroll = (e) => {
46
+ const scroller = e.currentTarget;
47
+ const scrollTop = scroller.scrollTop;
48
+ const total = this.totalElements;
49
+ const visStart = Math.floor(scrollTop / this.rowHeight);
50
+ const visEnd = Math.min(visStart + this.visibleCount(), total - 1);
51
+ this.renderStart = Math.max(0, visStart - BUFFER);
52
+ this.renderEnd = Math.min(total - 1, visEnd + BUFFER);
53
+ this.requestPagesForWindow(this.renderStart, this.renderEnd);
54
+ };
55
+ }
56
+ // ── Public API ─────────────────────────────────────────────────────────────
57
+ /**
58
+ * Initialise (or reset) the virtual scroll.
59
+ * Call after setting all props and registering the mrdLoadPage listener,
60
+ * but before calling setPage(0, rows).
61
+ */
62
+ async init() {
63
+ var _a;
64
+ if (this.debounceTimer !== null) {
65
+ clearTimeout(this.debounceTimer);
66
+ this.debounceTimer = null;
67
+ }
68
+ this.pendingPages.clear();
69
+ this.loadedPages = new Map();
70
+ this.requestedPages = new Set();
71
+ this.colWidths = [];
72
+ // Apply defaultSort prop as the initial sort state.
73
+ if (this.defaultSort) {
74
+ const parts = this.defaultSort.split(',');
75
+ this.sortField = parts[0].trim();
76
+ this.sortDir = ((_a = parts[1]) === null || _a === void 0 ? void 0 : _a.trim()) === 'desc' ? 'desc' : 'asc';
77
+ }
78
+ else {
79
+ this.sortField = '';
80
+ this.sortDir = 'asc';
81
+ }
82
+ this.renderStart = 0;
83
+ this.renderEnd = Math.max(0, Math.min(this.visibleCount() + BUFFER, this.totalElements - 1));
84
+ // Scroll the container back to the top when switching datasets.
85
+ const scroller = this.el.querySelector('.mrd-table__scroll');
86
+ if (scroller)
87
+ scroller.scrollTop = 0;
88
+ // Do NOT emit mrdLoadPage here — the host injects page 0 via setPage().
89
+ }
90
+ /**
91
+ * Inject the rows for a given page (0-based).
92
+ * Creates a new Map reference so Stencil detects the state change.
93
+ */
94
+ async setPage(pageNumber, rows) {
95
+ const next = new Map(this.loadedPages);
96
+ next.set(pageNumber, rows);
97
+ this.loadedPages = next;
98
+ }
99
+ // ── Private helpers ────────────────────────────────────────────────────────
100
+ visibleCount() {
101
+ return Math.ceil(this.tableHeight / this.rowHeight);
102
+ }
103
+ /** Returns the current sort value for use in ?sort= query params. */
104
+ sortParam() {
105
+ if (!this.sortField)
106
+ return '';
107
+ return this.sortDir === 'desc' ? `${this.sortField},desc` : this.sortField;
108
+ }
109
+ /** Called when a header cell is clicked. Toggles direction or sets a new column. */
110
+ handleSortClick(col) {
111
+ if (this.sortField === col.name) {
112
+ this.sortDir = this.sortDir === 'asc' ? 'desc' : 'asc';
113
+ }
114
+ else {
115
+ this.sortField = col.name;
116
+ this.sortDir = 'asc';
117
+ }
118
+ // Cancel any pending scroll debounce.
119
+ if (this.debounceTimer !== null) {
120
+ clearTimeout(this.debounceTimer);
121
+ this.debounceTimer = null;
122
+ }
123
+ this.pendingPages.clear();
124
+ // Wipe all loaded data so the new sort order is fetched fresh.
125
+ this.loadedPages = new Map();
126
+ this.requestedPages = new Set();
127
+ this.colWidths = [];
128
+ this.renderStart = 0;
129
+ this.renderEnd = Math.max(0, Math.min(this.visibleCount() + BUFFER, this.totalElements - 1));
130
+ const scroller = this.el.querySelector('.mrd-table__scroll');
131
+ if (scroller)
132
+ scroller.scrollTop = 0;
133
+ // Emit immediately — no debounce for intentional sort clicks.
134
+ this.emitPagesForWindow(this.renderStart, this.renderEnd);
135
+ }
136
+ /** Emits mrdLoadPage immediately for all missing pages in [start, end]. */
137
+ emitPagesForWindow(start, end) {
138
+ const firstPage = Math.floor(start / this.pageSize);
139
+ const lastPage = Math.floor(end / this.pageSize);
140
+ const next = new Set(this.requestedPages);
141
+ let changed = false;
142
+ for (let p = firstPage; p <= lastPage; p++) {
143
+ if (!this.loadedPages.has(p) && !next.has(p)) {
144
+ next.add(p);
145
+ this.mrdLoadPage.emit({ page: p, sort: this.sortParam() });
146
+ changed = true;
147
+ }
148
+ }
149
+ if (changed)
150
+ this.requestedPages = next;
151
+ }
152
+ getRow(i) {
153
+ var _a;
154
+ const page = this.loadedPages.get(Math.floor(i / this.pageSize));
155
+ return (_a = page === null || page === void 0 ? void 0 : page[i % this.pageSize]) !== null && _a !== void 0 ? _a : null;
156
+ }
157
+ requestPagesForWindow(start, end) {
158
+ const firstPage = Math.floor(start / this.pageSize);
159
+ const lastPage = Math.floor(end / this.pageSize);
160
+ let anyNew = false;
161
+ for (let p = firstPage; p <= lastPage; p++) {
162
+ if (!this.loadedPages.has(p) && !this.requestedPages.has(p) && !this.pendingPages.has(p)) {
163
+ this.pendingPages.add(p);
164
+ anyNew = true;
165
+ }
166
+ }
167
+ if (!anyNew)
168
+ return;
169
+ // Reset de timer: wacht tot het scrollen even stopt.
170
+ if (this.debounceTimer !== null)
171
+ clearTimeout(this.debounceTimer);
172
+ this.debounceTimer = setTimeout(() => this.flushPendingPages(), REQUEST_DEBOUNCE_MS);
173
+ }
174
+ /** Emitteert mrdLoadPage alleen voor pagina's die na de debounce-wachttijd
175
+ * nog steeds binnen het huidige render-venster vallen. */
176
+ flushPendingPages() {
177
+ this.debounceTimer = null;
178
+ if (this.pendingPages.size === 0)
179
+ return;
180
+ const next = new Set(this.requestedPages);
181
+ let changed = false;
182
+ for (const page of this.pendingPages) {
183
+ // Sla over als pagina inmiddels geladen of al aangevraagd is.
184
+ if (this.loadedPages.has(page) || next.has(page))
185
+ continue;
186
+ // Sla over als de pagina buiten het huidige venster is geraakt.
187
+ const pageStart = page * this.pageSize;
188
+ const pageEnd = pageStart + this.pageSize - 1;
189
+ if (pageEnd < this.renderStart || pageStart > this.renderEnd)
190
+ continue;
191
+ next.add(page);
192
+ this.mrdLoadPage.emit({ page, sort: this.sortParam() });
193
+ changed = true;
194
+ }
195
+ this.pendingPages.clear();
196
+ if (changed)
197
+ this.requestedPages = next;
198
+ }
199
+ // ── Lifecycle ──────────────────────────────────────────────────────────────
200
+ /** After the first page of data renders, lock column widths so subsequent
201
+ * page loads don't cause layout shifts. */
202
+ componentDidRender() {
203
+ if (this.colWidths.length === 0 && this.loadedPages.size > 0 && this.totalElements > 0) {
204
+ const ths = this.el.querySelectorAll('.mrd-table__header');
205
+ if (ths.length > 0) {
206
+ this.colWidths = Array.from(ths).map(th => th.offsetWidth);
207
+ }
208
+ }
209
+ }
210
+ // ── Render ─────────────────────────────────────────────────────────────────
211
+ render() {
212
+ var _a, _b;
213
+ if (!((_a = this.columns) === null || _a === void 0 ? void 0 : _a.length))
214
+ return null;
215
+ const numericTypes = new Set(['INTEGER', 'DECIMAL', 'PERCENTAGE', 'CURRENCY']);
216
+ // ── Non-paginated mode ──────────────────────────────────────────────────
217
+ if (this.totalElements === 0) {
218
+ return (h(Host, null, h("div", { class: "mrd-table" }, h("table", { class: "mrd-table__table" }, h("thead", null, h("tr", null, this.columns.map(col => (h("th", { class: "mrd-table__header" }, col.label))))), h("tbody", null, (_b = this.rows) === null || _b === void 0 ? void 0 : _b.map((row, i) => (h("tr", { class: "mrd-table__row", style: { background: i % 2 === 0 ? '' : 'var(--mrd-color-neutral-100)' } }, this.columns.map(col => {
219
+ var _a;
220
+ const value = CellRenderer.render(col, row, this.locale);
221
+ const isNumeric = col.type === 'FIELD' && numericTypes.has((_a = col.dataType) !== null && _a !== void 0 ? _a : '');
222
+ return (h("td", { class: `mrd-table__cell${isNumeric ? ' mrd-table__cell--numeric' : ''}` }, value));
223
+ })))))), (!this.rows || this.rows.length === 0) && (h("p", { class: "mrd-table__empty" }, "Geen resultaten gevonden.")))));
224
+ }
225
+ // ── Paginated / virtual-scroll mode ────────────────────────────────────
226
+ const total = this.totalElements;
227
+ const colCount = this.columns.length;
228
+ const topSpacerHeight = this.renderStart * this.rowHeight;
229
+ const bottomSpacerHeight = Math.max(0, (total - 1 - this.renderEnd) * this.rowHeight);
230
+ const renderedRows = [];
231
+ for (let i = this.renderStart; i <= this.renderEnd; i++) {
232
+ const row = this.getRow(i);
233
+ if (row === null) {
234
+ renderedRows.push(h("tr", { class: "mrd-table__row mrd-table__row--loading" }, h("td", { class: "mrd-table__cell--placeholder", colSpan: colCount }, h("span", { class: "mrd-table__placeholder-bar" }))));
235
+ }
236
+ else {
237
+ renderedRows.push(h("tr", { class: "mrd-table__row", style: { background: i % 2 === 0 ? '' : 'var(--mrd-color-neutral-100)' } }, this.columns.map(col => {
238
+ var _a;
239
+ const value = CellRenderer.render(col, row, this.locale);
240
+ const isNumeric = col.type === 'FIELD' && numericTypes.has((_a = col.dataType) !== null && _a !== void 0 ? _a : '');
241
+ return (h("td", { class: `mrd-table__cell${isNumeric ? ' mrd-table__cell--numeric' : ''}` }, value));
242
+ })));
243
+ }
244
+ }
245
+ const tableStyle = this.colWidths.length > 0
246
+ ? { tableLayout: 'fixed' }
247
+ : undefined;
248
+ return (h(Host, null, h("div", { class: "mrd-table__scroll", style: { height: `${this.tableHeight}px` }, onScroll: this.handleScroll }, h("table", { class: "mrd-table__table", style: tableStyle }, h("thead", null, h("tr", null, this.columns.map((col, idx) => {
249
+ const isActive = this.sortField === col.name;
250
+ const cls = `mrd-table__header mrd-table__header--sortable${isActive ? ` mrd-table__header--sorted-${this.sortDir}` : ''}`;
251
+ return (h("th", { class: cls, style: this.colWidths[idx] ? { width: `${this.colWidths[idx]}px` } : undefined, onClick: () => this.handleSortClick(col) }, h("span", { class: "mrd-table__header-label" }, col.label), h("span", { class: "mrd-table__sort-icon", "aria-hidden": "true" }, isActive ? (this.sortDir === 'asc' ? '▲' : '▼') : '⇅')));
252
+ }))), h("tbody", null, topSpacerHeight > 0 && (h("tr", { class: "mrd-table__spacer", style: { height: `${topSpacerHeight}px` } }, h("td", { colSpan: colCount }))), renderedRows, bottomSpacerHeight > 0 && (h("tr", { class: "mrd-table__spacer", style: { height: `${bottomSpacerHeight}px` } }, h("td", { colSpan: colCount })))))), total === 0 && (h("p", { class: "mrd-table__empty" }, "Geen resultaten gevonden."))));
253
+ }
254
+ static get is() { return "mrd-table"; }
255
+ static get encapsulation() { return "scoped"; }
256
+ static get originalStyleUrls() {
257
+ return {
258
+ "$": ["mrd-table.scss"]
259
+ };
260
+ }
261
+ static get styleUrls() {
262
+ return {
263
+ "$": ["mrd-table.css"]
264
+ };
265
+ }
266
+ static get properties() {
267
+ return {
268
+ "columns": {
269
+ "type": "unknown",
270
+ "mutable": false,
271
+ "complexType": {
272
+ "original": "TableColumn[]",
273
+ "resolved": "TableColumn[]",
274
+ "references": {
275
+ "TableColumn": {
276
+ "location": "import",
277
+ "path": "../../utils/cell-renderer",
278
+ "id": "src/utils/cell-renderer.ts::TableColumn",
279
+ "referenceLocation": "TableColumn"
280
+ }
281
+ }
282
+ },
283
+ "required": false,
284
+ "optional": false,
285
+ "docs": {
286
+ "tags": [],
287
+ "text": ""
288
+ },
289
+ "getter": false,
290
+ "setter": false,
291
+ "defaultValue": "[]"
292
+ },
293
+ "rows": {
294
+ "type": "unknown",
295
+ "mutable": false,
296
+ "complexType": {
297
+ "original": "Record<string, any>[]",
298
+ "resolved": "Record<string, any>[]",
299
+ "references": {
300
+ "Record": {
301
+ "location": "global",
302
+ "id": "global::Record"
303
+ }
304
+ }
305
+ },
306
+ "required": false,
307
+ "optional": false,
308
+ "docs": {
309
+ "tags": [],
310
+ "text": "Direct rows (non-paginated mode, used when totalElements === 0)."
311
+ },
312
+ "getter": false,
313
+ "setter": false,
314
+ "defaultValue": "[]"
315
+ },
316
+ "locale": {
317
+ "type": "string",
318
+ "mutable": false,
319
+ "complexType": {
320
+ "original": "string",
321
+ "resolved": "string",
322
+ "references": {}
323
+ },
324
+ "required": false,
325
+ "optional": false,
326
+ "docs": {
327
+ "tags": [],
328
+ "text": ""
329
+ },
330
+ "getter": false,
331
+ "setter": false,
332
+ "reflect": false,
333
+ "attribute": "locale",
334
+ "defaultValue": "navigator.language"
335
+ },
336
+ "totalElements": {
337
+ "type": "number",
338
+ "mutable": false,
339
+ "complexType": {
340
+ "original": "number",
341
+ "resolved": "number",
342
+ "references": {}
343
+ },
344
+ "required": false,
345
+ "optional": false,
346
+ "docs": {
347
+ "tags": [],
348
+ "text": "Total number of records across all pages. 0 = non-paginated mode."
349
+ },
350
+ "getter": false,
351
+ "setter": false,
352
+ "reflect": false,
353
+ "attribute": "total-elements",
354
+ "defaultValue": "0"
355
+ },
356
+ "pageSize": {
357
+ "type": "number",
358
+ "mutable": false,
359
+ "complexType": {
360
+ "original": "number",
361
+ "resolved": "number",
362
+ "references": {}
363
+ },
364
+ "required": false,
365
+ "optional": false,
366
+ "docs": {
367
+ "tags": [],
368
+ "text": "Records per page (must match the API page size)."
369
+ },
370
+ "getter": false,
371
+ "setter": false,
372
+ "reflect": false,
373
+ "attribute": "page-size",
374
+ "defaultValue": "20"
375
+ },
376
+ "rowHeight": {
377
+ "type": "number",
378
+ "mutable": false,
379
+ "complexType": {
380
+ "original": "number",
381
+ "resolved": "number",
382
+ "references": {}
383
+ },
384
+ "required": false,
385
+ "optional": false,
386
+ "docs": {
387
+ "tags": [],
388
+ "text": "Row height in px \u2014 used for spacer and scroll-position maths."
389
+ },
390
+ "getter": false,
391
+ "setter": false,
392
+ "reflect": false,
393
+ "attribute": "row-height",
394
+ "defaultValue": "36"
395
+ },
396
+ "tableHeight": {
397
+ "type": "number",
398
+ "mutable": false,
399
+ "complexType": {
400
+ "original": "number",
401
+ "resolved": "number",
402
+ "references": {}
403
+ },
404
+ "required": false,
405
+ "optional": false,
406
+ "docs": {
407
+ "tags": [],
408
+ "text": "Height of the scroll container in px."
409
+ },
410
+ "getter": false,
411
+ "setter": false,
412
+ "reflect": false,
413
+ "attribute": "table-height",
414
+ "defaultValue": "500"
415
+ },
416
+ "defaultSort": {
417
+ "type": "string",
418
+ "mutable": false,
419
+ "complexType": {
420
+ "original": "string",
421
+ "resolved": "string",
422
+ "references": {}
423
+ },
424
+ "required": false,
425
+ "optional": false,
426
+ "docs": {
427
+ "tags": [],
428
+ "text": "Initial sort applied on load, e.g. \"timestamp,desc\" or \"name\".\nParsed by init() into sortField + sortDir."
429
+ },
430
+ "getter": false,
431
+ "setter": false,
432
+ "reflect": false,
433
+ "attribute": "default-sort",
434
+ "defaultValue": "''"
435
+ }
436
+ };
437
+ }
438
+ static get states() {
439
+ return {
440
+ "loadedPages": {},
441
+ "requestedPages": {},
442
+ "renderStart": {},
443
+ "renderEnd": {},
444
+ "colWidths": {},
445
+ "sortField": {},
446
+ "sortDir": {}
447
+ };
448
+ }
449
+ static get events() {
450
+ return [{
451
+ "method": "mrdLoadPage",
452
+ "name": "mrdLoadPage",
453
+ "bubbles": true,
454
+ "cancelable": true,
455
+ "composed": true,
456
+ "docs": {
457
+ "tags": [],
458
+ "text": "Fired when a page needs to be fetched. Host fetches and calls setPage().\n`sort` is the raw query-param value, e.g. \"name\" or \"name,desc\"."
459
+ },
460
+ "complexType": {
461
+ "original": "{ page: number; sort: string }",
462
+ "resolved": "{ page: number; sort: string; }",
463
+ "references": {}
464
+ }
465
+ }];
466
+ }
467
+ static get methods() {
468
+ return {
469
+ "init": {
470
+ "complexType": {
471
+ "signature": "() => Promise<void>",
472
+ "parameters": [],
473
+ "references": {
474
+ "Promise": {
475
+ "location": "global",
476
+ "id": "global::Promise"
477
+ },
478
+ "HTMLElement": {
479
+ "location": "global",
480
+ "id": "global::HTMLElement"
481
+ }
482
+ },
483
+ "return": "Promise<void>"
484
+ },
485
+ "docs": {
486
+ "text": "Initialise (or reset) the virtual scroll.\nCall after setting all props and registering the mrdLoadPage listener,\nbut before calling setPage(0, rows).",
487
+ "tags": []
488
+ }
489
+ },
490
+ "setPage": {
491
+ "complexType": {
492
+ "signature": "(pageNumber: number, rows: Record<string, any>[]) => Promise<void>",
493
+ "parameters": [{
494
+ "name": "pageNumber",
495
+ "type": "number",
496
+ "docs": ""
497
+ }, {
498
+ "name": "rows",
499
+ "type": "Record<string, any>[]",
500
+ "docs": ""
501
+ }],
502
+ "references": {
503
+ "Promise": {
504
+ "location": "global",
505
+ "id": "global::Promise"
506
+ },
507
+ "Record": {
508
+ "location": "global",
509
+ "id": "global::Record"
510
+ }
511
+ },
512
+ "return": "Promise<void>"
513
+ },
514
+ "docs": {
515
+ "text": "Inject the rows for a given page (0-based).\nCreates a new Map reference so Stencil detects the state change.",
516
+ "tags": []
517
+ }
518
+ }
519
+ };
520
+ }
521
+ static get elementRef() { return "el"; }
522
+ }
@@ -0,0 +1,158 @@
1
+ :host {
2
+ display: block;
3
+ width: 100%;
4
+ }
5
+
6
+ /* ── Scroll container (paginated mode) ─────────────────────────────────── */
7
+ .mrd-table__scroll {
8
+ overflow-y: auto;
9
+ overflow-x: auto;
10
+ border: 1px solid var(--mrd-border-color);
11
+ border-radius: var(--mrd-border-radius);
12
+ /* Voorkomt dat de browser scrollTop automatisch aanpast wanneer de
13
+ top-spacer groeit (scroll anchoring feedbackloop). */
14
+ overflow-anchor: none;
15
+ }
16
+
17
+ /* ── Inner wrapper: horizontal scroll in non-paginated mode ────────────── */
18
+ /* Only used in non-paginated path; paginated mode scrolls via .mrd-table__scroll */
19
+ .mrd-table {
20
+ overflow-x: auto;
21
+ }
22
+
23
+ /* ── Table ─────────────────────────────────────────────────────────────── */
24
+ .mrd-table__table {
25
+ width: auto;
26
+ min-width: 100%;
27
+ border-collapse: collapse;
28
+ font-size: var(--mrd-font-size-sm);
29
+ color: var(--mrd-color-neutral-900);
30
+ }
31
+
32
+ /* When table-layout: fixed is applied (after col widths are locked), allow
33
+ horizontal overflow so the scroll container can scroll. */
34
+ .mrd-table__scroll .mrd-table__table {
35
+ min-width: max-content;
36
+ }
37
+
38
+ /* ── Sticky header ─────────────────────────────────────────────────────── */
39
+ .mrd-table__header {
40
+ position: sticky;
41
+ top: 0;
42
+ z-index: 1;
43
+ background: var(--mrd-color-white);
44
+ text-align: left;
45
+ padding: var(--mrd-space-2) var(--mrd-space-4);
46
+ border-bottom: 2px solid var(--mrd-border-color);
47
+ color: var(--mrd-color-neutral-600);
48
+ font-weight: var(--mrd-font-weight-medium);
49
+ white-space: nowrap;
50
+ font-size: var(--mrd-font-size-xs);
51
+ text-transform: uppercase;
52
+ letter-spacing: 0.04em;
53
+ }
54
+
55
+ .mrd-table__header--sortable {
56
+ cursor: pointer;
57
+ user-select: none;
58
+ }
59
+
60
+ .mrd-table__header--sortable:hover {
61
+ background: var(--mrd-color-neutral-50);
62
+ color: var(--mrd-color-neutral-800);
63
+ }
64
+
65
+ .mrd-table__header--sorted-asc,
66
+ .mrd-table__header--sorted-desc {
67
+ color: var(--mrd-color-primary);
68
+ border-bottom-color: var(--mrd-color-primary);
69
+ }
70
+
71
+ .mrd-table__header-label {
72
+ margin-right: var(--mrd-space-1);
73
+ }
74
+
75
+ .mrd-table__sort-icon {
76
+ font-size: 0.65rem;
77
+ opacity: 0.4;
78
+ vertical-align: middle;
79
+ }
80
+
81
+ .mrd-table__header--sorted-asc .mrd-table__sort-icon,
82
+ .mrd-table__header--sorted-desc .mrd-table__sort-icon {
83
+ opacity: 1;
84
+ color: var(--mrd-color-primary);
85
+ }
86
+
87
+ /* ── Data rows ─────────────────────────────────────────────────────────── */
88
+ /* Background is set via inline style based on absolute row index,
89
+ so zebra striping stays correct even with virtual spacer rows. */
90
+ .mrd-table__row {
91
+ border-bottom: 1px solid var(--mrd-border-color);
92
+ }
93
+
94
+ /* !important overrides the inline zebra background on hover */
95
+ .mrd-table__row:hover {
96
+ background: var(--mrd-color-neutral-200) !important;
97
+ }
98
+
99
+ /* ── Spacer rows (virtual scroll padding above/below render window) ─────── */
100
+ .mrd-table__spacer {
101
+ border: none;
102
+ }
103
+
104
+ .mrd-table__spacer td {
105
+ padding: 0;
106
+ border: none;
107
+ }
108
+
109
+ /* ── Cells ─────────────────────────────────────────────────────────────── */
110
+ .mrd-table__cell {
111
+ padding: var(--mrd-space-2) var(--mrd-space-4);
112
+ vertical-align: top;
113
+ white-space: nowrap;
114
+ }
115
+
116
+ .mrd-table__cell--numeric {
117
+ text-align: right;
118
+ font-variant-numeric: tabular-nums;
119
+ }
120
+
121
+ /* ── Loading placeholder row ───────────────────────────────────────────── */
122
+ .mrd-table__row--loading {
123
+ background: transparent;
124
+ }
125
+
126
+ .mrd-table__cell--placeholder {
127
+ padding: var(--mrd-space-2) var(--mrd-space-4);
128
+ border-bottom: 1px solid var(--mrd-border-color);
129
+ }
130
+
131
+ .mrd-table__placeholder-bar {
132
+ display: block;
133
+ height: 0.75rem;
134
+ width: 55%;
135
+ border-radius: var(--mrd-border-radius-sm);
136
+ background: linear-gradient(
137
+ 90deg,
138
+ var(--mrd-color-neutral-200) 25%,
139
+ var(--mrd-color-neutral-100) 50%,
140
+ var(--mrd-color-neutral-200) 75%
141
+ );
142
+ background-size: 200% 100%;
143
+ animation: mrd-shimmer 1.4s ease infinite;
144
+ }
145
+
146
+ @keyframes mrd-shimmer {
147
+ 0% { background-position: 200% 0; }
148
+ 100% { background-position: -200% 0; }
149
+ }
150
+
151
+ /* ── Empty state ───────────────────────────────────────────────────────── */
152
+ .mrd-table__empty {
153
+ padding: var(--mrd-space-4) var(--mrd-space-3);
154
+ color: var(--mrd-color-neutral-500);
155
+ font-size: var(--mrd-font-size-sm);
156
+ text-align: center;
157
+ margin: 0;
158
+ }