@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,32 @@
1
+ /**
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
+ */
9
+
10
+ import { Mark } from 'tiptap'
11
+ import { toggleMark } from 'tiptap-commands'
12
+
13
+ export default class EditorCustomInsert extends Mark {
14
+ get name () {
15
+ return 'insert'
16
+ }
17
+
18
+ get schema () {
19
+ return {
20
+ parseDOM: [
21
+ {
22
+ tag: 'ins'
23
+ }
24
+ ],
25
+ toDOM: () => ['ins', { title: Translator.trans('text.inserted') }, 0]
26
+ }
27
+ }
28
+
29
+ commands ({ type }) {
30
+ return () => toggleMark(type)
31
+ }
32
+ }
@@ -0,0 +1,46 @@
1
+ /**
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
+ */
9
+
10
+ import { Link } from 'tiptap-extensions'
11
+
12
+ export default class EditorCustomLink extends Link {
13
+ get defaultOptions () {
14
+ return {
15
+ openOnClick: false,
16
+ target: null
17
+ }
18
+ }
19
+
20
+ get schema () {
21
+ return {
22
+ attrs: {
23
+ href: {
24
+ default: null
25
+ },
26
+ target: {
27
+ default: null
28
+ }
29
+ },
30
+ inclusive: false,
31
+ parseDOM: [
32
+ {
33
+ tag: 'a[href]',
34
+ getAttrs: dom => ({
35
+ href: dom.getAttribute('href'),
36
+ target: dom.getAttribute('target')
37
+ })
38
+ }
39
+ ],
40
+ toDOM: node => ['a', {
41
+ ...node.attrs,
42
+ rel: 'noopener noreferrer nofollow'
43
+ }, 0]
44
+ }
45
+ }
46
+ }
@@ -0,0 +1,32 @@
1
+ /**
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
+ */
9
+
10
+ import { Mark } from 'tiptap'
11
+ import { toggleMark } from 'tiptap-commands'
12
+
13
+ export default class EditorCustomMark extends Mark {
14
+ get name () {
15
+ return 'mark'
16
+ }
17
+
18
+ get schema () {
19
+ return {
20
+ parseDOM: [
21
+ {
22
+ tag: 'mark'
23
+ }
24
+ ],
25
+ toDOM: () => ['mark', { title: Translator.trans('text.mark') }, 0]
26
+ }
27
+ }
28
+
29
+ commands ({ type }) {
30
+ return () => toggleMark(type)
31
+ }
32
+ }
@@ -0,0 +1,41 @@
1
+ /**
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
+ */
9
+
10
+ import { DOMParser } from 'prosemirror-model'
11
+ import { Node } from 'tiptap'
12
+
13
+ export default class EditorInsertAtCursorPos extends Node {
14
+ get name () {
15
+ return 'insertHTML'
16
+ }
17
+
18
+ get schema () {
19
+ return {
20
+ inline: false,
21
+ attrs: {},
22
+ group: 'block',
23
+ draggable: true,
24
+ parseDOM: []
25
+ }
26
+ }
27
+
28
+ commands ({ type }) {
29
+ return {
30
+ insertHTML: string =>
31
+ (state, dispatch) => {
32
+ const { selection } = state
33
+ const element = document.createElement('div')
34
+ element.innerHTML = string.trim()
35
+ const slice = DOMParser.fromSchema(state.schema).parseSlice(element)
36
+ const transaction = state.tr.insert(selection.anchor, slice.content)
37
+ dispatch(transaction)
38
+ }
39
+ }
40
+ }
41
+ }
@@ -0,0 +1,60 @@
1
+ /**
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
+ */
9
+
10
+ /*
11
+ * This is the obscure-extension for tiptap, built on the basis of tiptap bold-extension.
12
+ * On mark-obscure in tiptap, we wrap up the marked content in <span class='u-obscure'></span> tags, and then before
13
+ * saving the changes we convert them to <dp-obscure>, so that they are correctly saved in BE. But to display the
14
+ * <span class='u-obscure'> tags in the editor we need to use the toDOM function provided by tiptap/prosemirror.
15
+ *
16
+ * InputRules and pasteRules help to handle diverse behaviour when we want to obscure only part of words or we want to
17
+ * use more than one tool (e.g. obscure and bold) simultaneously, etc.
18
+ */
19
+
20
+ import { markInputRule, markPasteRule, toggleMark } from 'tiptap-commands'
21
+ import { Mark } from 'tiptap'
22
+
23
+ export default class EditorObscure extends Mark {
24
+ get name () {
25
+ return 'obscure'
26
+ }
27
+
28
+ get schema () {
29
+ return {
30
+ parseDOM: [
31
+ { tag: '.u-obscure' },
32
+ { tag: 'dp-obscure' }
33
+ ],
34
+ toDOM: () => ['span', { class: (hasPermission('feature_obscure_text') ? 'u-obscure' : '') }, 0]
35
+ }
36
+ }
37
+ /*
38
+ * Keys ({ type }) {
39
+ * return {
40
+ * 'Mod-obscure': toggleMark(type)
41
+ * }
42
+ * }
43
+ */
44
+
45
+ commands ({ type }) {
46
+ return () => toggleMark(type)
47
+ }
48
+
49
+ inputRules ({ type }) {
50
+ return [
51
+ markInputRule(/(?:<o>)([^<o>]+)(?:<o>)$/, type)
52
+ ]
53
+ }
54
+
55
+ pasteRules ({ type }) {
56
+ return [
57
+ markPasteRule(/(?:<o>)([^<o>]+)(?:<o>)/g, type)
58
+ ]
59
+ }
60
+ }
@@ -0,0 +1,56 @@
1
+ /**
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
+ */
9
+
10
+ /*
11
+ * This is the anonymize-extension for tiptap, built on the basis of tiptap bold-extension.
12
+ * On mark-anonymize in tiptap, we wrap up the marked content in <span class='anonymize'></span> tags, and then before
13
+ * saving the changes we convert them to <dp-obscure>, so that they are correctly saved in BE. But to display the
14
+ * <span class='u-obscure'> tags in the editor we need to use the toDOM function provided by tiptap/prosemirror.
15
+ *
16
+ * InputRules and pasteRules help to handle diverse behaviour when we want to obscure only part of words or we want to
17
+ * use more than one tool (e.g. obscure and bold) simultaneously, etc.
18
+ */
19
+
20
+ import { markInputRule, markPasteRule, toggleMark } from 'tiptap-commands'
21
+ import { Mark } from 'tiptap'
22
+
23
+ export default class EditorUnAnonymize extends Mark {
24
+ get name () {
25
+ return 'unanonymize'
26
+ }
27
+
28
+ get schema () {
29
+ return {
30
+ parseDOM: [{
31
+ tag: '.unanonymized'
32
+ }],
33
+ toDOM: () => {
34
+ return ['span', {
35
+ class: 'unanonymized'
36
+ }, 0]
37
+ }
38
+ }
39
+ }
40
+
41
+ commands ({ type }) {
42
+ return () => toggleMark(type)
43
+ }
44
+
45
+ inputRules ({ type }) {
46
+ return [
47
+ markInputRule(/(?:<o>)([^<o>]+)(?:<o>)$/, type)
48
+ ]
49
+ }
50
+
51
+ pasteRules ({ type }) {
52
+ return [
53
+ markPasteRule(/(?:<o>)([^<o>]+)(?:<o>)/g, type)
54
+ ]
55
+ }
56
+ }
@@ -0,0 +1,360 @@
1
+ /**
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
+ */
9
+
10
+ import { v4 as uuid } from 'uuid'
11
+
12
+ /**
13
+ * This parser should manage to detect if an clipbaord content comes from microsoft word (mso) or office365 (o365)
14
+ * And them clean and transform the data into something, that tiptap can display nested (un)orderen lists
15
+ *
16
+ * Because Microsoft is quite special the way they store data in their XML, its likely that this functions
17
+ * have to be adjusted to optimize special cases and not tested versions.
18
+ *
19
+ * The main method "handleWordContent" is designed to increase and in the future may handle images or tables as well
20
+ *
21
+ */
22
+
23
+ /**
24
+ * Try to determin if the snippet from the clipboard comes from MS Office
25
+ *
26
+ * @param snippet{string}
27
+ *
28
+ * @return {boolean}
29
+ */
30
+ function checkIfMso (snippet) {
31
+ return (/class="?Mso|style="[^"]*\bmso-|style='[^']*\bmso-|w:WordDocument|office:wo/i).test(snippet)
32
+ }
33
+
34
+ /**
35
+ * Try to determine if the snippet from the clipboard comes from Office365.
36
+ *
37
+ * @param snippet{string}
38
+ *
39
+ * @return {boolean}
40
+ */
41
+ function checkIfOffice365 (snippet) {
42
+ return (/class="OutlineElement/).test(snippet)
43
+ }
44
+
45
+ /**
46
+ * Transforms a list item from office365 to a readable internal exchange Object
47
+ *
48
+ * Office365 stores the information for lists on the li items with a bunch of "data" and "data-aria" attributes
49
+ * instead of nesting them.
50
+ * To make the data handleable, we axtract that infos to a "Plain old Javascript Object"
51
+ *
52
+ * @param li{Element}
53
+ *
54
+ * @returns {Object<listId, indent, type, content>}
55
+ */
56
+ function createItemFrom365List (li) {
57
+ const indent = parseInt(li.getAttribute('data-aria-level')) // Current level of indentation
58
+ const listId = parseInt(li.getAttribute('data-listid')) // List ID
59
+ const type = li.parentElement.nodeName // Can be 'ul' or 'ol'
60
+ const content = li.innerHTML.replace(/&nbsp;/gmi, ' ')
61
+
62
+ return { listId, indent, type, content }
63
+ }
64
+
65
+ /**
66
+ * Transforms a list item from Mso to a readable and manageable object
67
+ *
68
+ * Microsoft Office stores all items in a kind of plain structure, List items too.
69
+ * The information, if an item is a list item is defined by the class name "[class^=MsoList]".
70
+ * Level and List-Id is put in the style attribute.
71
+ * To determin if its an ordered list, we have to check the leading listStyleType.
72
+ *
73
+ * @param li{Element}
74
+ *
75
+ * @returns {Object<listId, indent, type, content, listStyleType>}
76
+ */
77
+ function createItemFromMsoList (li) {
78
+ // "listInfo" is expected to be something something like "mso-list: l1 level1 lfo1"
79
+ const listInfo = li.getAttribute('style').match(/mso-list:([^'|^"|^;]*)/i)[0] // Find ListInfo
80
+ const indent = parseInt(listInfo.match(/level\d+/i)[0].slice(5)) // Strip "level" // current level of indentation
81
+ const listId = listInfo.match(/lfo\d+/i)[0] || '' // List ID
82
+ /*
83
+ * To define if its an OL or UL, we first have to create the whole List.
84
+ * Defining the type will be managed in `redefineMsoOrderedLists()` later in the transformation process
85
+ */
86
+ const type = 'ul'
87
+ const listStyleType = li.querySelector('[data-val-supportLists]')?.getAttribute('data-val-supportLists') || ''
88
+ const content = li.innerHTML
89
+
90
+ return { listId, indent, type, content, listStyleType }
91
+ }
92
+
93
+ /**
94
+ * Because Ms Word doesn't stores the information about the list type in such a way, that we can easily access it,
95
+ * we have to handle it manually.
96
+ * The approach is to take the structured list and step through each "sublist" (nested list).
97
+ * Within each list we compare the "listStyleTypes" of two elements to find out if they have the same style or not.
98
+ * If so, the list have to be unordered.
99
+ * For lists with just one element an additional check is implemented.
100
+ *
101
+ * @param listItemObjects
102
+ *
103
+ * @return {Object<listItemObjects>}
104
+ */
105
+ function redefineMsoOrderedLists (listItemObjects) {
106
+ Object.values(listItemObjects).forEach(list => {
107
+ let sublistId = uuid()
108
+ const sublistIdsAtPosition = [sublistId]
109
+ const sublistIds = [sublistId]
110
+ let pos = 0
111
+
112
+ /*
113
+ * Group sublists by uuid.
114
+ * Due to the nesting, we can't know all siblings by just checking the previous, next items
115
+ * To make them groupable, lets add a sublistId to each element.
116
+ */
117
+ listItemObjects[list[0].listId].forEach((li, i) => {
118
+ /*
119
+ * If we "dive" into the next nesting level, we need a new uuid.
120
+ * But if we already have been in this level, but in another sublist,
121
+ * We have to keep the ID from the previous list.
122
+ * Therefore we have to store the IDs in two different ways.
123
+ */
124
+ if (i > 0 && list[i - 1].indent < list[i].indent) {
125
+ sublistId = uuid()
126
+ pos++
127
+ sublistIdsAtPosition[pos] = sublistId
128
+ sublistIds.push(sublistId)
129
+ }
130
+
131
+ if (i > 0 && list[i - 1].indent > list[i].indent) {
132
+ pos--
133
+ }
134
+
135
+ li.sublistId = sublistIdsAtPosition[pos]
136
+ li.idx = i
137
+ })
138
+
139
+ // Check for each sublist (again) if its (un-)ordered
140
+ sublistIds.forEach(sublistId => {
141
+ const sublist = list.filter(el => el.sublistId === sublistId)
142
+ if (sublist.length > 1) {
143
+ const mappedItems = sublist
144
+ .map(el => {
145
+ // We assume, that if two items in one List have the same listStyleType, it is an unordered list
146
+ const type = sublist[0].listStyleType === sublist[1].listStyleType ? 'ul' : 'ol'
147
+ return { ...el, type: type }
148
+ })
149
+
150
+ mappedItems.forEach(li => {
151
+ listItemObjects[list[0].listId][li.idx] = li
152
+ })
153
+ }
154
+
155
+ /*
156
+ * If we have just one list item we assume that a number or letter indicates an ordred list
157
+ * Only if listStyleType is an "o". Than we assume that its more likely that its an
158
+ * unordered list, than an ordered one because mso uses the "o" a outlined bullit.
159
+ *
160
+ * since the default is 'ul', we just have to change it to 'ol' if neccesary
161
+ */
162
+ if (sublist.length === 1 &&
163
+ (/(\d+|\w+)/i).test(sublist[0].listStyleType) &&
164
+ sublist[0].listStyleType !== 'o') {
165
+ listItemObjects[list[0].listId][sublist[0].idx].type = 'ol'
166
+ }
167
+ })
168
+ })
169
+
170
+ return listItemObjects
171
+ }
172
+
173
+ /**
174
+ * Create objects that hold the information about the list items
175
+ * to be used for creating a nested list from that data.
176
+ *
177
+ * @param parsedDom{DOMTokenList}
178
+ * @param isMso{Boolean}
179
+ *
180
+ * @return {Object}
181
+ */
182
+ function createLists (parsedDom, isMso) {
183
+ let listItemObjects = {}
184
+
185
+ const listItems = isMso
186
+ ? parsedDom.querySelectorAll('[class^=MsoList]')
187
+ : parsedDom.querySelectorAll('li')
188
+
189
+ listItems.forEach(li => {
190
+ const item = isMso ? createItemFromMsoList(li) : createItemFrom365List(li)
191
+
192
+ if (Object.keys(listItemObjects).includes(item.listId.toString()) === false) {
193
+ listItemObjects[item.listId] = []
194
+ }
195
+
196
+ // Set data-attribute as Hook to find it back later on
197
+ li.setAttribute('data-list-indicator', item.listId)
198
+
199
+ listItemObjects[item.listId].push(item)
200
+ })
201
+
202
+ /*
203
+ * Dirty part to determine if (mso) lists are ordered or not
204
+ */
205
+ if (isMso) {
206
+ listItemObjects = redefineMsoOrderedLists(listItemObjects)
207
+ }
208
+
209
+ return listItemObjects
210
+ }
211
+
212
+ /**
213
+ * Takes the flat list of list item objects and creates a HTML-String for that list
214
+ *
215
+ * @param list{Array<Object{indent, type, content, ?listStyleType}>}
216
+ *
217
+ * @return {string}
218
+ */
219
+ function buildListAsHtmlString (list) {
220
+ /*
221
+ * `nesting` holds the type of lists for nested lists
222
+ * It starts with the first indentation level and may be filled if there are lists in lists
223
+ */
224
+ const nesting = [list[0].type]
225
+ let htmlList = `<${list[0].type}>`
226
+
227
+ for (let i = 0; i < list.length; i++) {
228
+ const item = list[i]
229
+ htmlList += `<li>${item.content}`
230
+
231
+ const hasChildren = i + 1 < list.length && list[i + 1].indent > item.indent
232
+ const isLastChild = i + 1 < list.length && list[i + 1].indent < item.indent
233
+ const isLastElement = i + 1 === list.length
234
+
235
+ if (hasChildren) {
236
+ // If the next Element is a child-Element the closing Tag follows when closing the Child List
237
+ htmlList += `<${list[i + 1].type}>`
238
+ nesting.unshift(list[i + 1].type)
239
+ } else {
240
+ htmlList += '</li>'
241
+ }
242
+
243
+ if (isLastChild) {
244
+ // Close the List and the Parent, for following elements that are intended above
245
+ let c = 0
246
+ while (list[i + 1].indent + c < item.indent) {
247
+ htmlList += `</${nesting[0]}></li>`
248
+ nesting.shift()
249
+ c++
250
+ }
251
+ }
252
+ if (isLastElement) {
253
+ // Close all open Tags if its the last Element
254
+ for (let h = item.indent; h > 1; h--) {
255
+ htmlList += `</${nesting[0]}></li>`
256
+ nesting.shift()
257
+ }
258
+ }
259
+ }
260
+
261
+ htmlList += `</${list[0].type}>`
262
+
263
+ return htmlList
264
+ }
265
+
266
+ /**
267
+ * Remove code which would make the DomParser fail
268
+ * and store the "bullet" tht it can be checked later on.
269
+ *
270
+ * @param slice{string}
271
+ *
272
+ * @return {string}
273
+ */
274
+ function prepareDataBeforeParsingMso (slice) {
275
+ return slice
276
+ // Strip line breaks
277
+ .replace(/(\r|\n)/gmi, '')
278
+ // Strip head
279
+ .replace(/<head>(.|\n|\r)*?<\/head>/mi, '')
280
+ // Strip html wrapper and remove conentless and non html like elements "<o:p>"
281
+ .replace(/<(\/)?(html|o:p)[^>]*>/gmi, '')
282
+ /*
283
+ * Remove Microsoft IF comments and replace it with empty spans holding the data, that it can be processed later on
284
+ * to determine if that one listStyleType may be part of an ordered or unordered list.
285
+ */
286
+ .replace(/<!\[if !(.*?)\]>(.*?)<span(.*?)style='mso-list:Ignore'>(.*?)<!\[endif\]>/gmi, (match, p1, p2, p3, p4) => {
287
+ // Try to remove spacing spans
288
+ const listStyleType = p4
289
+ .replace(/<span[^>]*>/gmi, '')
290
+ .replace(/<\/span>/gmi, '')
291
+ .replace(/(&nbsp;|\s|\\r|\\n|\r|\n)/gmi, '')
292
+ return `<span data-val-${p1}="${listStyleType}" />`
293
+ })
294
+ }
295
+
296
+ /**
297
+ * Parses Clippboard content from Word to fix List ordering.
298
+ *
299
+ * @param slice{String}
300
+ *
301
+ * @return {string}
302
+ */
303
+ function handleWordPaste (slice) {
304
+ const isMso = checkIfMso(slice)
305
+ const isOffice365 = checkIfOffice365(slice)
306
+
307
+ if ((isMso || isOffice365) === false) {
308
+ return slice
309
+ }
310
+
311
+ // Strip meta though its not closed and would break the parser
312
+ slice = slice.replace(/<meta[^>]*>/mi, '')
313
+
314
+ // If its Mso-Content: Before the parser can be applied, some noise has to be reduced
315
+ if (isMso) {
316
+ slice = prepareDataBeforeParsingMso(slice)
317
+ }
318
+ // For a better handling all the Stuff gets converted to html
319
+ const parser = new DOMParser()
320
+ const parsedDom = parser.parseFromString(slice, 'text/html')
321
+
322
+ const listItemObjects = createLists(parsedDom, isMso)
323
+
324
+ /**
325
+ * After collecting the list, we go to each list and add a well formatted list before the found one.
326
+ * Until that we need the old list as dom hook to paste the new one, afterwards it is removed.
327
+ */
328
+ Object.keys(listItemObjects).forEach(listId => {
329
+ // Get the first Element of current List
330
+ const context = parsedDom.querySelector('[data-list-indicator="' + listId + '"]')
331
+
332
+ // Instert new List before the original one
333
+ if (context.parentElement.nodeName.toLowerCase() === 'ul' || context.parentElement.nodeName.toLowerCase() === 'ol') {
334
+ context.parentElement.before(buildListAsHtmlString(listItemObjects[listId]))
335
+ } else {
336
+ context.before(buildListAsHtmlString(listItemObjects[listId]))
337
+ }
338
+
339
+ // Remove old Elements
340
+ parsedDom
341
+ .querySelectorAll('[data-list-indicator="' + listId + '"]')
342
+ .forEach(el => {
343
+ if (el.parentElement.nodeName.toLowerCase() === 'ul' || el.parentElement.nodeName.toLowerCase() === 'ol') {
344
+ el.parentElement.remove()
345
+ } else {
346
+ el.remove()
347
+ }
348
+ })
349
+ })
350
+
351
+ // Clear List item collection for the next run.
352
+ return parsedDom.documentElement.outerHTML
353
+ .replace(/&gt;/gmi, '>')
354
+ .replace(/&lt;/gmi, '<')
355
+ // Remove empty list wrapper
356
+ .replace(/<div[^>]*?class="ListContainerWrapper[^>]*?><ul[^>]*?><\/ul><\/div>/gm, '')
357
+ .replace(/<ul[^>]*?><\/ul>/gm, '')
358
+ }
359
+
360
+ export { handleWordPaste }
@@ -0,0 +1,31 @@
1
+ /**
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
+ */
9
+
10
+ import { Extension, Plugin } from 'tiptap'
11
+
12
+ export default class preventDrop extends Extension {
13
+ get name () {
14
+ return 'PreventDrop'
15
+ }
16
+
17
+ get plugins () {
18
+ return [
19
+ new Plugin({
20
+ props: {
21
+ handleDOMEvents: {
22
+ drop (view, event) {
23
+ event.preventDefault()
24
+ return true
25
+ }
26
+ }
27
+ }
28
+ })
29
+ ]
30
+ }
31
+ }
@@ -0,0 +1,27 @@
1
+ /**
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
+ */
9
+
10
+ import { Extension, Plugin } from 'tiptap'
11
+
12
+ export default class preventKeyboardInput extends Extension {
13
+ get name () {
14
+ return 'PreventDrop'
15
+ }
16
+
17
+ get plugins () {
18
+ return [
19
+ new Plugin({
20
+ props: {
21
+ handleKeyDown: (view, event) => true,
22
+ handleTextInput: (view, event) => true
23
+ }
24
+ })
25
+ ]
26
+ }
27
+ }