@demos-europe/demosplan-ui 0.0.1

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 (133) hide show
  1. package/LICENSE +21 -0
  2. package/buildTokens.js +59 -0
  3. package/components/DpButton/DpButton.stories.mdx +136 -0
  4. package/components/DpButton/DpButton.vue +118 -0
  5. package/components/DpDetails/DpDetails.stories.mdx +55 -0
  6. package/components/DpDetails/DpDetails.vue +58 -0
  7. package/components/DpIcon/DpIcon.stories.mdx +396 -0
  8. package/components/DpIcon/DpIcon.vue +51 -0
  9. package/components/DpIcon/util/iconVariables.js +148 -0
  10. package/components/DpInput/DpInput.stories.mdx +127 -0
  11. package/components/DpInput/DpInput.vue +284 -0
  12. package/components/DpLabel/DpLabel.stories.mdx +103 -0
  13. package/components/DpLabel/DpLabel.vue +112 -0
  14. package/components/DpLoading/DpLoading.stories.mdx +63 -0
  15. package/components/DpLoading/DpLoading.vue +63 -0
  16. package/components/core/DpAccordion.vue +108 -0
  17. package/components/core/DpAnonymizeText.vue +136 -0
  18. package/components/core/DpAutocomplete.vue +133 -0
  19. package/components/core/DpBulkEditHeader.vue +53 -0
  20. package/components/core/DpButtonIcon.vue +47 -0
  21. package/components/core/DpButtonRow.vue +155 -0
  22. package/components/core/DpCard.vue +54 -0
  23. package/components/core/DpChangeStateAtDate.vue +223 -0
  24. package/components/core/DpCheckboxGroup.vue +93 -0
  25. package/components/core/DpContextualHelp.vue +54 -0
  26. package/components/core/DpCopyPasteButton.vue +47 -0
  27. package/components/core/DpDashboardTaskCard.vue +123 -0
  28. package/components/core/DpDataTable/DataTableSearch.js +44 -0
  29. package/components/core/DpDataTable/DpColumnSelector.vue +133 -0
  30. package/components/core/DpDataTable/DpDataTable.vue +647 -0
  31. package/components/core/DpDataTable/DpDataTableExtended.vue +377 -0
  32. package/components/core/DpDataTable/DpResizeHandle.vue +37 -0
  33. package/components/core/DpDataTable/DpSelectPageItemCount.vue +70 -0
  34. package/components/core/DpDataTable/DpTableHeader.vue +197 -0
  35. package/components/core/DpDataTable/DpTableRow.vue +355 -0
  36. package/components/core/DpDataTable/DpWrapTrigger.vue +48 -0
  37. package/components/core/DpDataTable/lib/ResizableColumns.js +83 -0
  38. package/components/core/DpEditableList.vue +161 -0
  39. package/components/core/DpEditor/DpBoilerPlate.vue +140 -0
  40. package/components/core/DpEditor/DpBoilerPlateModal.vue +166 -0
  41. package/components/core/DpEditor/DpEditor.vue +1281 -0
  42. package/components/core/DpEditor/DpLinkModal.vue +117 -0
  43. package/components/core/DpEditor/DpRecommendationModal/DpInsertableRecommendation.vue +137 -0
  44. package/components/core/DpEditor/DpRecommendationModal.vue +283 -0
  45. package/components/core/DpEditor/DpResizableImage.vue +121 -0
  46. package/components/core/DpEditor/DpUploadModal.vue +121 -0
  47. package/components/core/DpEditor/libs/Decoration.js +66 -0
  48. package/components/core/DpEditor/libs/SegmentRangeChangePlugin.js +35 -0
  49. package/components/core/DpEditor/libs/editorAnonymize.js +66 -0
  50. package/components/core/DpEditor/libs/editorBuildSuggestion.js +269 -0
  51. package/components/core/DpEditor/libs/editorCustomDelete.js +32 -0
  52. package/components/core/DpEditor/libs/editorCustomImage.js +100 -0
  53. package/components/core/DpEditor/libs/editorCustomInsert.js +32 -0
  54. package/components/core/DpEditor/libs/editorCustomLink.js +46 -0
  55. package/components/core/DpEditor/libs/editorCustomMark.js +32 -0
  56. package/components/core/DpEditor/libs/editorInsertAtCursorPos.js +41 -0
  57. package/components/core/DpEditor/libs/editorObscure.js +60 -0
  58. package/components/core/DpEditor/libs/editorUnAnonymize.js +56 -0
  59. package/components/core/DpEditor/libs/handleWordPaste.js +360 -0
  60. package/components/core/DpEditor/libs/preventDrop.js +31 -0
  61. package/components/core/DpEditor/libs/preventKeyboardInput.js +27 -0
  62. package/components/core/DpEditor/libs/preventPaste.js +28 -0
  63. package/components/core/DpFlyout.vue +119 -0
  64. package/components/core/DpInlineNotification.vue +116 -0
  65. package/components/core/DpModal.vue +208 -0
  66. package/components/core/DpObscure.vue +29 -0
  67. package/components/core/DpPager.vue +139 -0
  68. package/components/core/DpProgressBar.vue +67 -0
  69. package/components/core/DpRegisterFlyout.vue +58 -0
  70. package/components/core/DpResettableInput.vue +140 -0
  71. package/components/core/DpSkeletonBox.vue +32 -0
  72. package/components/core/DpSlidebar.vue +86 -0
  73. package/components/core/DpSlidingPagination.vue +45 -0
  74. package/components/core/DpSplitButton.vue +77 -0
  75. package/components/core/DpSwitcher.vue +62 -0
  76. package/components/core/DpTableCardList/DpTableCard.vue +61 -0
  77. package/components/core/DpTableCardList/DpTableCardListHeader.vue +83 -0
  78. package/components/core/DpTabs/DpTab.vue +52 -0
  79. package/components/core/DpTabs/DpTabs.vue +165 -0
  80. package/components/core/DpTextWrapper.vue +65 -0
  81. package/components/core/DpToggleForm.vue +72 -0
  82. package/components/core/DpTooltipIcon.vue +52 -0
  83. package/components/core/DpTransitionExpand.vue +87 -0
  84. package/components/core/DpTreeList/DpTreeList.vue +334 -0
  85. package/components/core/DpTreeList/DpTreeListCheckbox.vue +79 -0
  86. package/components/core/DpTreeList/DpTreeListNode.vue +348 -0
  87. package/components/core/DpTreeList/DpTreeListToggle.vue +71 -0
  88. package/components/core/DpTreeList/utils/constants.js +14 -0
  89. package/components/core/DpUpload/DpUpload.vue +223 -0
  90. package/components/core/DpUpload/DpUploadFiles.vue +269 -0
  91. package/components/core/DpUpload/DpUploadedFile.vue +80 -0
  92. package/components/core/DpUpload/DpUploadedFileList.vue +56 -0
  93. package/components/core/DpUpload/utils/GetFileIdsByHash.js +42 -0
  94. package/components/core/DpUpload/utils/UppyTranslations.js +31 -0
  95. package/components/core/DpVideoPlayer.vue +115 -0
  96. package/components/core/HeightLimit.vue +121 -0
  97. package/components/core/MultistepNav.vue +89 -0
  98. package/components/core/form/DpCheckbox.vue +108 -0
  99. package/components/core/form/DpDateRangePicker.vue +186 -0
  100. package/components/core/form/DpDatepicker.vue +160 -0
  101. package/components/core/form/DpDatetimePicker.vue +194 -0
  102. package/components/core/form/DpFormRow.vue +79 -0
  103. package/components/core/form/DpMultiselect.vue +164 -0
  104. package/components/core/form/DpRadio.vue +128 -0
  105. package/components/core/form/DpSearchField.vue +110 -0
  106. package/components/core/form/DpSelect.vue +149 -0
  107. package/components/core/form/DpTextArea.vue +152 -0
  108. package/components/core/form/DpTimePicker.vue +374 -0
  109. package/components/core/form/DpToggle.vue +78 -0
  110. package/components/core/index.js +132 -0
  111. package/components/core/notify/DpNotifyContainer.vue +122 -0
  112. package/components/core/notify/DpNotifyMessage.vue +95 -0
  113. package/components/core/shared/DpStickyElement.vue +95 -0
  114. package/components/index.js +24 -0
  115. package/components/shared/translations.js +15 -0
  116. package/directives/CleanHtml/CleanHtml.js +50 -0
  117. package/directives/CleanHtml/CleanHtml.stories.mdx +64 -0
  118. package/directives/Tooltip/Tooltip.js +40 -0
  119. package/directives/Tooltip/Tooltip.stories.mdx +42 -0
  120. package/directives/index.js +17 -0
  121. package/lib/index.js +14 -0
  122. package/lib/prefixClass.js +47 -0
  123. package/mixins/index.js +14 -0
  124. package/mixins/prefixClassMixin.js +22 -0
  125. package/package.json +52 -0
  126. package/shared/props.js +86 -0
  127. package/style/index.css +7 -0
  128. package/tailwind.config.js +24 -0
  129. package/tokens/color.json +358 -0
  130. package/tokens/color.stories.mdx +45 -0
  131. package/tokens/fontSize.json +100 -0
  132. package/tokens/space.json +33 -0
  133. package/utils/lengthHint.js +69 -0
@@ -0,0 +1,647 @@
1
+ <license>
2
+ (c) 2010-present DEMOS E-Partizipation GmbH.
3
+
4
+ This file is part of the package @demos-europe/demosplan-ui,
5
+ for more information see the license file.
6
+
7
+ All rights reserved
8
+ </license>
9
+
10
+ <documentation>
11
+ <!-- The most simple way to use DpDataTable is the following -->
12
+ <!-- NOTICE: the elements in the items array must have a key which matches a field in the header-fields array -->
13
+ <!-- The following example defines a table with two equally sized columns named "E-Mail" a,d "Name":
14
+ headerFields: [
15
+ {
16
+ field: 'mail',
17
+ label: 'E-Mail',
18
+ colClass: 'u-1-of-2', // can be omitted, renders a colgroup > col element
19
+ tooltip: 'longer.text.then.label' // can be omitted
20
+ },
21
+ {
22
+ field: 'name',
23
+ label: 'Name'
24
+ },
25
+ ],
26
+ items: [{mail: 'test@domain.dev', name: 'Testname'}]
27
+ -->
28
+ <usage>
29
+ <dp-data-table
30
+ has-flyout="true|false"
31
+ header-fields="[{field, label}]"
32
+ is-selectable="true|false"
33
+ items="[{fieldName}]"
34
+ search-string="String"
35
+ track-by="ID (used for checkbox identification)" />
36
+ </usage>
37
+ <!-- -->
38
+ <!-- -->
39
+ <!-- Advanced setup with scoped slots -->
40
+ <usage>
41
+ <dp-data-table
42
+ search-string="searchString"
43
+ header-fields="headerInput (see above)"
44
+ table-class="additional classes for the table element"
45
+ items="inputItems (see above)"
46
+ track-by="(see above)">
47
+ <!-- header slots -->
48
+ <template
49
+ v-for="element in headerInput /* iterate over the HeaderFields */"
50
+ v-slot:[`header-${element.field}`]="headerData">
51
+ <div :key="element.id">
52
+ {{ headerData.value }}
53
+ </div>
54
+ </template>
55
+ <!-- field slots -->
56
+ <template
57
+ v-for="(element, i) in inputItems"
58
+ v-slot:[element.field]="rowData">
59
+ <!-- For highlighting text in a slot we have to do something like this, where the `highlightText()` method
60
+ has to handle the highlighting from outside -->
61
+ <span
62
+ :key="`${element.field}:${i}`"
63
+ v-cleanhtml="highlightText(rowData[element.field])" />
64
+ </template>
65
+ </dp-data-table>
66
+ </usage>
67
+ </documentation>
68
+
69
+ <script>
70
+ import { CleanHtml } from 'demosplan-ui/directives'
71
+ import DomPurify from 'dompurify'
72
+ import { DpLoading } from 'demosplan-ui/components'
73
+ import DpTableHeader from './DpTableHeader'
74
+ import DpTableRow from './DpTableRow'
75
+ import draggable from 'vuedraggable'
76
+
77
+ export default {
78
+ name: 'DpDataTable',
79
+
80
+ components: {
81
+ DpLoading,
82
+ DpTableHeader,
83
+ DpTableRow,
84
+ draggable
85
+ },
86
+
87
+ directives: {
88
+ cleanhtml: CleanHtml
89
+ },
90
+
91
+ props: {
92
+ // Adds flyout menu
93
+ hasFlyout: {
94
+ type: Boolean,
95
+ required: false,
96
+ default: false
97
+ },
98
+
99
+ // The first table row (consisting of column headers) is being fixed to the top of the outer table element.
100
+ hasStickyHeader: {
101
+ type: Boolean,
102
+ required: false,
103
+ default: false
104
+ },
105
+
106
+ /**
107
+ * The header of every column of the table is defined here.
108
+ *
109
+ * Each column is represented by an object with a `field` key whose value should match
110
+ * a key of the objects inside `items`. The `label` key controls the header of the column.
111
+ * The header can also have a tooltip. To define the width the column is initially rendered with
112
+ * when `isResizable` is used, the keys `initialWidth`, `initialMaxWidth` and `initialMinWidth` take a px value.
113
+ */
114
+ headerFields: {
115
+ type: Array,
116
+ required: true
117
+ },
118
+
119
+ initSelectedItems: {
120
+ type: Array,
121
+ required: false,
122
+ default: () => []
123
+ },
124
+
125
+ isDraggable: {
126
+ type: Boolean,
127
+ required: false,
128
+ default: false
129
+ },
130
+
131
+ /**
132
+ * Rows may be expandable to show additional content inside another row.
133
+ * The `#expandedContent` slot can be utilized to style the content area.
134
+ */
135
+ isExpandable: {
136
+ type: Boolean,
137
+ required: false,
138
+ default: false
139
+ },
140
+
141
+ isLoading: {
142
+ type: Boolean,
143
+ required: false,
144
+ default: false
145
+ },
146
+
147
+ /**
148
+ * Make table columns resizable.
149
+ */
150
+ isResizable: {
151
+ type: Boolean,
152
+ required: false,
153
+ default: false
154
+ },
155
+
156
+ /**
157
+ * Display a checkbox in front of each row.
158
+ */
159
+ isSelectable: {
160
+ type: Boolean,
161
+ required: false,
162
+ default: false
163
+ },
164
+
165
+ /**
166
+ * If specified, the checkbox will have a "name" attr with this value, and a value of [trackBy].
167
+ */
168
+ isSelectableName: {
169
+ type: String,
170
+ required: false,
171
+ default: null
172
+ },
173
+
174
+ /**
175
+ * Make table rows truncatable.
176
+ * Cell content is truncated after 1 line, but expandable to its original size.
177
+ * To make this work, no custom css must be applied to the cells that hold the
178
+ * content to be truncated.
179
+ * At the moment, `isTruncatable` and `isExpandable` use the same icon as trigger visual.
180
+ * See https://yaits.demos-deutschland.de/T11301#413638 for possible advancement.
181
+ */
182
+ isTruncatable: {
183
+ type: Boolean,
184
+ required: false,
185
+ default: false
186
+ },
187
+
188
+ items: {
189
+ type: Array,
190
+ required: true
191
+ },
192
+
193
+ /**
194
+ * When selection on multiple pages is supported, this variable forces the "Check all" checkbox into a "checked"
195
+ * state, because instead of passing all checked items into here (which we can't in multi-page-selection suzenario),
196
+ * we just pass the info "all items are selected".
197
+ */
198
+ multiPageAllSelected: {
199
+ type: Boolean,
200
+ required: false,
201
+ default: false
202
+ },
203
+
204
+ /**
205
+ * Use a Boolean Property of the Item to set the Checkbox to a locked state.
206
+ * This should only be set if `isSelectable` is true.
207
+ */
208
+ lockCheckboxBy: {
209
+ type: String,
210
+ required: false,
211
+ default: null
212
+ },
213
+
214
+ /**
215
+ * When selection on multiple pages is supported, this variable holds number of items currently toggled.
216
+ * It is used for calculating indeterminate state of the "check all" checkbox.
217
+ */
218
+ multiPageSelectionItemsToggled: {
219
+ type: Number,
220
+ required: false,
221
+ default: 0
222
+ },
223
+
224
+ /**
225
+ * When selection on multiple pages is supported, this variable holds the absolute number of items available.
226
+ * It is used for calculating indeterminate state of the "check all" checkbox.
227
+ */
228
+ multiPageSelectionItemsTotal: {
229
+ type: Number,
230
+ required: false,
231
+ default: 0
232
+ },
233
+
234
+ searchString: {
235
+ type: [String, null],
236
+ required: false,
237
+ default: null
238
+ },
239
+
240
+ // This allows item selection to be forced from outside. It will override any internal selection state.
241
+ shouldBeSelectedItems: {
242
+ type: Object,
243
+ required: false,
244
+ default: () => ({})
245
+ },
246
+
247
+ tableClass: {
248
+ type: String,
249
+ required: false,
250
+ default: 'c-data-table'
251
+ },
252
+
253
+ trackBy: {
254
+ type: String,
255
+ required: true
256
+ },
257
+
258
+ translations: {
259
+ type: Object,
260
+ required: false,
261
+ default: () => ({})
262
+ }
263
+ },
264
+
265
+ data () {
266
+ return {
267
+ allExpanded: false,
268
+ allWrapped: false,
269
+ defaultTranslations: {
270
+ footerSelectedElement: Translator.trans('entry.selected'),
271
+ footerSelectedElements: Translator.trans('entries.selected'),
272
+ headerExpandHint: Translator.trans('aria.expand.all'),
273
+ headerSelectHint: Translator.trans('aria.select.all'),
274
+ lockedForSelection: Translator.trans('item.lockedForSelection'),
275
+ searchNoResults: (searchTerm) => Translator.trans('search.no.results', { searchterm: searchTerm }),
276
+ tableLoadingData: Translator.trans('loading.data'),
277
+ tableNoElements: Translator.trans('explanation.noentries')
278
+ },
279
+ elementSelections: {},
280
+ expandedElements: {},
281
+ mergedTranslations: {},
282
+ selectedElements: [],
283
+ tableEl: undefined,
284
+ wrappedElements: {}
285
+ }
286
+ },
287
+
288
+ computed: {
289
+ allSelected () {
290
+ if (this.multiPageSelectionItemsTotal > 0) {
291
+ return this.multiPageSelectionItemsToggled === this.multiPageSelectionItemsTotal || this.multiPageAllSelected
292
+ } else {
293
+ return this.items.filter(item => this.elementSelections[item[this.trackBy]]).length === this.items.length
294
+ }
295
+ },
296
+
297
+ indeterminate () {
298
+ if (this.isSelectable === false) {
299
+ return
300
+ }
301
+ if (this.multiPageSelectionItemsTotal > 0) {
302
+ return this.multiPageSelectionItemsToggled > 0 && !this.allSelected
303
+ } else {
304
+ return this.selectedElements.length > 0 && !this.allSelected
305
+ }
306
+ },
307
+
308
+ searchTerm () {
309
+ if (this.searchString === null || this.searchString.length < 1) {
310
+ return new RegExp()
311
+ }
312
+ const searchTerm = this.searchString.replace(/\s*/ig, '\\s*')
313
+ return new RegExp(searchTerm, 'ig')
314
+ }
315
+ },
316
+
317
+ watch: {
318
+ shouldBeSelectedItems () {
319
+ this.forceElementSelections(this.shouldBeSelectedItems)
320
+ },
321
+
322
+ indeterminate () {
323
+ this.setIndeterminate()
324
+ }
325
+ },
326
+
327
+ methods: {
328
+ extractTranslations (keys) {
329
+ return keys.reduce((acc, key) => {
330
+ const tmp = this.mergedTranslations[key] ? { [key]: this.mergedTranslations[key] } : {}
331
+ return { ...acc, ...tmp }
332
+ }, {})
333
+ },
334
+
335
+ /**
336
+ * Transforms and filters the list of selected items across all pages.
337
+ *
338
+ * Return only the `trackBy` of the items
339
+ *
340
+ * @returns {string[]}
341
+ */
342
+ filterElementSelections () {
343
+ return Object.entries(this.elementSelections)
344
+ .filter(selectedItem => selectedItem[1]) // True or false
345
+ .map(selectedItem => selectedItem[0]) // TrackBy of the item
346
+ },
347
+
348
+ forceElementSelections (itemsStatusObject) {
349
+ this.elementSelections = itemsStatusObject
350
+ this.selectedElements = this.filterElementSelections()
351
+ },
352
+
353
+ setIndeterminate () {
354
+ if (this.isSelectable) {
355
+ this.$refs.selectAll.indeterminate = this.indeterminate
356
+ }
357
+ },
358
+
359
+ resetSelection () {
360
+ this.toggleSelectAll(false)
361
+ },
362
+
363
+ setElementSelections (elements, status) {
364
+ return elements.reduce((acc, el) => {
365
+ return {
366
+ ...acc,
367
+ ...{ [el[this.trackBy]]: status }
368
+ }
369
+ }, this.elementSelections)
370
+ },
371
+
372
+ toggleExpand (id) {
373
+ this.expandedElements = { ...this.expandedElements, [id]: !this.expandedElements[id] }
374
+ },
375
+
376
+ toggleExpandAll (status = this.allExpanded === false) {
377
+ this.expandedElements = this.items.reduce((acc, item) => {
378
+ return {
379
+ ...acc,
380
+ ...{ [item[this.trackBy]]: status }
381
+ }
382
+ }, {})
383
+ this.allExpanded = status
384
+ },
385
+
386
+ toggleSelect (id) {
387
+ this.elementSelections = { ...this.elementSelections, ...{ [id]: !this.elementSelections[id] } }
388
+ this.selectedElements = this.filterElementSelections()
389
+
390
+ this.$emit('items-selected', this.selectedElements)
391
+ this.$emit('items-toggled', [{ id }], this.elementSelections[id])
392
+ },
393
+
394
+ toggleSelectAll (status = this.allSelected === false) {
395
+ this.elementSelections = this.setElementSelections(this.items, status)
396
+ this.selectedElements = this.filterElementSelections()
397
+ this.$emit('items-selected', this.selectedElements)
398
+ this.$emit('items-toggled', this.items.map(el => { return { id: el[this.trackBy] } }), status)
399
+
400
+ // Used by multi-page selection in SegmentsList to determine whether to track selected or deselected items.
401
+ this.$emit('select-all', status)
402
+ },
403
+
404
+ toggleWrap (id) {
405
+ this.wrappedElements = { ...this.wrappedElements, [id]: !this.wrappedElements[id] }
406
+ },
407
+
408
+ toggleWrapAll (status = this.allWrapped === false) {
409
+ this.wrappedElements = this.items.reduce((acc, item) => {
410
+ return {
411
+ ...acc,
412
+ ...{ [item[this.trackBy]]: status }
413
+ }
414
+ }, {})
415
+
416
+ this.allWrapped = status
417
+ }
418
+ },
419
+
420
+ created () {
421
+ this.elementSelections = this.setElementSelections(this.initSelectedItems, true)
422
+
423
+ // The searchNoResults translation key needs to be a function, therefore make it a function before merging with defaultTranslations
424
+ let tmpTranslations = { ...this.translations }
425
+ const noResults = this.translations.searchNoResults ? { searchNoResults: () => this.translations.searchNoResults } : {}
426
+ tmpTranslations = { ...tmpTranslations, ...noResults }
427
+
428
+ this.mergedTranslations = { ...this.defaultTranslations, ...tmpTranslations }
429
+ },
430
+
431
+ mounted () {
432
+ this.tableEl = this.$refs.tableEl
433
+
434
+ /**
435
+ * Why is this here you may ask?
436
+ * Tables and overflow are difficult to handle.
437
+ * When truncating cell we want a table cell to respect the max-width of our table.
438
+ * This can be achieved through table-layout: fixed;
439
+ * However, we want to have automatic cell sizing dependent on content.
440
+ * Therefore, we have a normal table, get the auto-sized cell width and set the layout to fixed afterwards.
441
+ */
442
+ if (this.isResizable || this.isTruncatable) {
443
+ const firstRow = this.tableEl.firstChild
444
+ const tableHeaders = Array.prototype.slice.call(firstRow.childNodes)
445
+ tableHeaders.forEach(tableHeader => {
446
+ const width = tableHeader.getBoundingClientRect().width
447
+ tableHeader.style.width = width + 'px'
448
+ })
449
+
450
+ this.tableEl.style.tableLayout = 'fixed'
451
+ this.tableEl.classList.add('is-fixed')
452
+
453
+ // Remove styles set by initialMaxWidth and initialWidth after copying rendered width into th styles
454
+ if (this.isResizable) {
455
+ const tableRows = Array.from(this.tableEl.children[1].children)
456
+ tableRows.forEach(tableRow => {
457
+ Array.from(tableRow.children).forEach(cell => {
458
+ cell.firstChild.style.width = null
459
+ cell.firstChild.style.maxWidth = null
460
+ cell.firstChild.style.minWidth = null
461
+ })
462
+ })
463
+ }
464
+ }
465
+
466
+ /**
467
+ * It makes no sense to have both props `isExpandable` and `isTruncatable` activated on an instance
468
+ * of DpDataTable from a UX perspective, since both do roughly the same, but based on a different kind
469
+ * of presentation.
470
+ */
471
+ if (this.isExpandable && this.isTruncatable) {
472
+ console.error('`isExpandable` and `isTruncatable` should not be activated at the same time when using DpDataTable.')
473
+ }
474
+
475
+ this.forceElementSelections(this.shouldBeSelectedItems)
476
+ this.setIndeterminate()
477
+ },
478
+
479
+ render: function (h) {
480
+ const self = this
481
+ const scopedSlots = this.$scopedSlots
482
+ const fields = self.headerFields.map(hf => hf.field)
483
+ const items = this.items
484
+ const headerTranslations = this.extractTranslations(['headerSelectHint'])
485
+
486
+ const rowItems = items.map((item, idx) => {
487
+ return h(DpTableRow, {
488
+ props: {
489
+ checked: self.elementSelections[item[self.trackBy]] || false,
490
+ expanded: self.expandedElements[item[self.trackBy]] || false,
491
+ fields: fields,
492
+ hasFlyout: self.hasFlyout,
493
+ headerFields: self.headerFields,
494
+ index: idx,
495
+ isDraggable: self.isDraggable,
496
+ isExpandable: self.isExpandable,
497
+ isLoading: self.isLoading && self.items.length > 0,
498
+ isLocked: self.lockCheckboxBy ? item[self.lockCheckboxBy] : false,
499
+ isLockedMessage: self.mergedTranslations.lockedForSelection,
500
+ isResizable: self.isResizable,
501
+ isSelectable: self.isSelectable,
502
+ isSelectableName: self.isSelectableName,
503
+ isTruncatable: self.isTruncatable,
504
+ item: item,
505
+ searchTerm: self.searchTerm,
506
+ trackBy: self.trackBy,
507
+ wrapped: self.wrappedElements[item[self.trackBy]] || false
508
+ },
509
+ on: {
510
+ toggleExpand: self.toggleExpand,
511
+ toggleSelect: self.toggleSelect,
512
+ toggleWrap: self.toggleWrap
513
+ },
514
+ scopedSlots: {
515
+ ...scopedSlots
516
+ }
517
+ })
518
+ })
519
+
520
+ const tableHeaderData = {
521
+ props: {
522
+ checked: self.allSelected,
523
+ hasFlyout: self.hasFlyout,
524
+ headerFields: self.headerFields,
525
+ indeterminate: self.indeterminate,
526
+ isDraggable: self.isDraggable,
527
+ isExpandable: self.isExpandable,
528
+ isResizable: self.isResizable,
529
+ isSelectable: self.isSelectable,
530
+ isSticky: self.hasStickyHeader,
531
+ isTruncatable: self.isTruncatable,
532
+ translations: headerTranslations
533
+ },
534
+ on: {
535
+ toggleExpandAll: self.toggleExpandAll,
536
+ toggleSelectAll: self.toggleSelectAll,
537
+ toggleWrapAll: self.toggleWrapAll
538
+ },
539
+ scopedSlots: {
540
+ ...scopedSlots
541
+ }
542
+ }
543
+
544
+ let noEntriesItem, noResultsItem
545
+
546
+ // Generate placeholder items if there are no other items to display
547
+ if (rowItems.length === 0) {
548
+ const noEntriesData = {}
549
+
550
+ noEntriesData.attrs = {
551
+ class: 'u-pt',
552
+ colspan: fields.length + (self.isSelectable && 1) || 0
553
+ }
554
+
555
+ const loadingEl = h(DpLoading, {
556
+ props: {
557
+ isLoading: true
558
+ },
559
+ attrs: {
560
+ class: 'u-mt',
561
+ colspan: fields.length + (self.isSelectable && 1) || 0
562
+ }
563
+ })
564
+
565
+ noEntriesItem = self.isLoading ? h('td', [loadingEl]) : h('td', noEntriesData, self.mergedTranslations.tableNoElements)
566
+
567
+ // If there is no searchTerm an empty RegexEp() object with source '(?:)' is returned
568
+ const searchTermSet = self.searchTerm.source !== '(?:)'
569
+ if (searchTermSet) {
570
+ const noResultsData = { ...noEntriesData }
571
+ noResultsData.domProps = {
572
+ // The searchNoResults translation has to be a function -> code in created() ensures that it will be a function
573
+ innerHTML: self.mergedTranslations.searchNoResults(DomPurify.sanitize('"' + this.searchString + '"'))
574
+ }
575
+
576
+ noResultsItem = h('td', noResultsData)
577
+ }
578
+ }
579
+
580
+ /**
581
+ * If self.headerFields include at least one item with a `colClass` property defined, this is treated as a Css class
582
+ * and rendered into a colgroup to enable equal column sizing of multiple instances of DpDataTable.
583
+ * Colgroup does not work in tandem with `is-resizable` if the class defines a width.
584
+ * Note that a very limited set of Css properties apply to columns (see https://www.w3.org/TR/CSS21/tables.html#columns).
585
+ */
586
+ let colGroup
587
+
588
+ if (self.headerFields.filter(field => field.colClass).length > 0) {
589
+ const cols = self.headerFields.map(field => {
590
+ return h('col', { attrs: { class: field.colClass } })
591
+ })
592
+
593
+ const emptyCol = h('col')
594
+
595
+ /*
596
+ * Prepend a col element for each of these props set to true, as they
597
+ * introduce additional td elements by themselves.
598
+ */
599
+ for (const condition of [self.isDraggable, self.isSelectable]) {
600
+ if (condition) {
601
+ cols.unshift(emptyCol)
602
+ }
603
+ }
604
+
605
+ /*
606
+ * Append a col element for each of these props set to true, as they
607
+ * introduce additional td elements by themselves.
608
+ */
609
+ for (const condition of [self.hasFlyout, self.isExpandable, self.isTruncatable]) {
610
+ if (condition) {
611
+ cols.push(emptyCol)
612
+ }
613
+ }
614
+
615
+ colGroup = h('colgroup', cols)
616
+ if (this.isResizable) {
617
+ console.warn('"isResizable" will not work with "colClass" property set in headerFields when applying width definitions.')
618
+ }
619
+ }
620
+
621
+ let bodyEl = 'tbody'
622
+ let bodyData = {}
623
+ if (self.isDraggable) {
624
+ bodyEl = draggable
625
+ bodyData = {
626
+ props: {
627
+ tag: 'tbody',
628
+ value: items,
629
+ handle: '[data-handle]'
630
+ },
631
+ on: {
632
+ change: (e) => self.$emit('changed-order', e)
633
+ }
634
+ }
635
+ }
636
+
637
+ return h('div',
638
+ [
639
+ h('table', { ref: 'tableEl', class: self.tableClass }, [
640
+ colGroup,
641
+ h(DpTableHeader, tableHeaderData),
642
+ h(bodyEl, bodyData, (rowItems.length && rowItems) || [noResultsItem || noEntriesItem])
643
+ ])
644
+ ])
645
+ }
646
+ }
647
+ </script>