@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,117 @@
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
+ <template>
11
+ <dp-modal
12
+ ref="linkModal"
13
+ content-classes="u-1-of-3"
14
+ data-dp-validate="linkModal"
15
+ @modal:toggled="setVisible">
16
+ <template>
17
+ <h3>
18
+ {{ Translator.trans(hasLink ? 'editor.link.edit' : 'editor.link.insert') }}
19
+ </h3>
20
+ <div class="space-stack-m">
21
+ <dp-input
22
+ id="link_text"
23
+ v-model="text"
24
+ :label="{
25
+ text: Translator.trans('link.text')
26
+ }"
27
+ :required="isVisible" />
28
+ <dp-input
29
+ id="link_url"
30
+ v-model="url"
31
+ :label="{
32
+ hint: Translator.trans('editor.link.url.formatHint'),
33
+ text: Translator.trans('url')
34
+ }"
35
+ :pattern="isVisible === true ? '(^https?://.*|^//.*)' : null"
36
+ :required="isVisible"
37
+ type="url" />
38
+ <dp-checkbox
39
+ id="newTab"
40
+ v-model="newTab"
41
+ :label="{
42
+ text: Translator.trans('open.in.new.tab')
43
+ }" />
44
+ <dp-button-row
45
+ class="u-mt"
46
+ primary
47
+ :primary-text="Translator.trans('insert')"
48
+ secondary
49
+ :secondary-text="Translator.trans('remove')"
50
+ @primary-action="dpValidateAction('linkModal', () => emitAndClose('insert'), false)"
51
+ @secondary-action="emitAndClose('remove')" />
52
+ </div>
53
+ </template>
54
+ </dp-modal>
55
+ </template>
56
+
57
+ <script>
58
+ import DpButtonRow from '../DpButtonRow'
59
+ import DpCheckbox from '../form/DpCheckbox'
60
+ import { DpInput } from 'demosplan-ui/components'
61
+ import DpModal from '../DpModal'
62
+ import { dpValidateMixin } from 'demosplan-utils/mixins'
63
+
64
+ export default {
65
+ name: 'DpLinkModal',
66
+
67
+ components: {
68
+ DpButtonRow,
69
+ DpCheckbox,
70
+ DpInput,
71
+ DpModal
72
+ },
73
+
74
+ mixins: [dpValidateMixin],
75
+
76
+ data () {
77
+ return {
78
+ initUrl: '',
79
+ isVisible: false,
80
+ newTab: false,
81
+ text: '',
82
+ url: ''
83
+ }
84
+ },
85
+
86
+ computed: {
87
+ hasLink () {
88
+ return this.initUrl !== ''
89
+ }
90
+ },
91
+
92
+ methods: {
93
+ emitAndClose (value) {
94
+ this.$emit('insert', (value === 'insert' ? this.url : null), this.newTab, this.text)
95
+ this.toggleModal()
96
+ },
97
+
98
+ setVisible (isOpenModal) {
99
+ this.isVisible = isOpenModal
100
+ },
101
+
102
+ toggleModal (initUrl, textSelection, newTab) {
103
+ this.$refs.linkModal.toggle()
104
+ if (this.isVisible) {
105
+ this.initUrl = initUrl
106
+ this.url = initUrl
107
+ this.text = textSelection
108
+ this.newTab = newTab === '_blank'
109
+ } else {
110
+ this.url = ''
111
+ this.text = ''
112
+ this.newTab = false
113
+ }
114
+ }
115
+ }
116
+ }
117
+ </script>
@@ -0,0 +1,137 @@
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
+ <template>
11
+ <li class="flex flex-items-start space-inline-xs u-pr-0_5">
12
+ <div class="min-width-m">
13
+ <i
14
+ v-if="fromOtherProcedure"
15
+ v-tooltip="{
16
+ boundariesElement: body,
17
+ content: Translator.trans('segment.recommendation.other.procedure') + ': ' + procedureName,
18
+ classes: 'u-z-super'
19
+ }"
20
+ :aria-label="Translator.trans('more.information')"
21
+ class="fa fa-info-circle" />
22
+ </div>
23
+ <div
24
+ class="flex-grow"
25
+ v-cleanhtml="recommendationText" />
26
+ <div class="flex flex-nowrap space-inline-s">
27
+ <button
28
+ class="btn--blank o-link--default"
29
+ :aria-label="Translator.trans(isExpanded ? 'dropdown.close' : 'dropdown.open')"
30
+ v-tooltip="{ boundariesElement: body, content: Translator.trans(isExpanded ? 'dropdown.close' : 'dropdown.open'), classes: 'u-z-super' }"
31
+ v-if="canExpand"
32
+ @click="toggleExpanded">
33
+ <i
34
+ aria-hidden="true"
35
+ class="fa"
36
+ :class="isExpanded ? 'fa-angle-up' : 'fa-angle-down'" />
37
+ </button>
38
+ <button
39
+ :aria-label="Translator.trans('segment.recommendation.paste')"
40
+ v-tooltip="{ boundariesElement: body, content: Translator.trans('segment.recommendation.paste'), classes: 'u-z-super' }"
41
+ class="btn--blank o-link--default"
42
+ @click="$emit('insert-recommendation')">
43
+ <i
44
+ class="fa fa-files-o"
45
+ aria-hidden="true" />
46
+ </button>
47
+ </div>
48
+ </li>
49
+ </template>
50
+
51
+ <script>
52
+ import { CleanHtml } from 'demosplan-ui/directives'
53
+
54
+ // This number is used to shorten long texts.
55
+ const SHORT_TEXT_CHAR_LENGTH = 300
56
+
57
+ export default {
58
+ name: 'DpInsertableRecommendation',
59
+
60
+ directives: {
61
+ cleanhtml: CleanHtml
62
+ },
63
+
64
+ props: {
65
+ fromOtherProcedure: {
66
+ type: Boolean,
67
+ required: true
68
+ },
69
+
70
+ recommendation: {
71
+ type: String,
72
+ required: true
73
+ },
74
+
75
+ searchTerm: {
76
+ type: String,
77
+ required: false,
78
+ default: ''
79
+ },
80
+
81
+ procedureName: {
82
+ type: String,
83
+ required: false,
84
+ default: ''
85
+ }
86
+ },
87
+
88
+ data () {
89
+ return {
90
+ isExpanded: false,
91
+ shortText: this.shortenHtmlText(this.recommendation)
92
+ }
93
+ },
94
+
95
+ computed: {
96
+ canExpand () {
97
+ return this.recommendation.length > SHORT_TEXT_CHAR_LENGTH
98
+ },
99
+
100
+ body () {
101
+ return document.body
102
+ },
103
+
104
+ recommendationText () {
105
+ const shouldTruncate = !this.isExpanded && this.canExpand
106
+
107
+ const shortDisplayText = this.searchTerm !== ''
108
+ ? this.shortText.replace(this.searchRegex, '<span style="background-color: yellow;">$&</span>') + '...'
109
+ : this.shortText
110
+
111
+ const fullText = this.searchTerm !== ''
112
+ ? this.recommendation.replace(this.searchRegex, '<span style="background-color: yellow;">$&</span>')
113
+ : this.recommendation
114
+
115
+ return shouldTruncate ? shortDisplayText : fullText
116
+ },
117
+
118
+ searchRegex () {
119
+ // Match the search term except when the term occurs within an html-tag
120
+ return new RegExp(this.searchTerm + '(?![^<]*>)', 'ig')
121
+ }
122
+ },
123
+
124
+ methods: {
125
+ shortenHtmlText (text) {
126
+ let textOnly = document.createElement('div')
127
+ textOnly.innerHTML = text
128
+ textOnly = textOnly.textContent.substring(0, SHORT_TEXT_CHAR_LENGTH)
129
+ return textOnly
130
+ },
131
+
132
+ toggleExpanded () {
133
+ this.isExpanded = !this.isExpanded
134
+ }
135
+ }
136
+ }
137
+ </script>
@@ -0,0 +1,283 @@
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
+ <!-- This component contains the UI for inserting recommendations into a textarea -->
12
+ </documentation>
13
+
14
+ <template>
15
+ <dp-modal
16
+ ref="recommendationModal"
17
+ content-classes="u-2-of-3">
18
+ <template>
19
+ <h3>{{ Translator.trans('segment.recommendation.insert.similar') }}</h3>
20
+ <div class="layout u-mb">
21
+ <div class="layout__item u-1-of-3">
22
+ <span class="display--block weight--bold">
23
+ {{ Translator.trans('segment.tags') }}
24
+ </span>
25
+ <div class="bg-color--grey-light-2 u-p-0_25">
26
+ <span
27
+ :key="id"
28
+ v-for="(id, idx) in tagIds">
29
+ {{ getTagTitle(id, idx) }}
30
+ </span>
31
+ </div>
32
+ </div><!--
33
+ --><div class="layout__item u-2-of-3">
34
+ <dp-label
35
+ :text="Translator.trans('search.text')"
36
+ for="searchField" />
37
+ <dp-search-field
38
+ @search="setSearchTerm"
39
+ @reset="setSearchTerm('')"
40
+ class="width--100p"
41
+ :placeholder="Translator.trans('search')" />
42
+ </div>
43
+ </div>
44
+
45
+ <dp-loading v-if="isLoading" />
46
+
47
+ <template v-else>
48
+ <ul
49
+ v-if="currentRecommendations.length > 0"
50
+ class="o-list space-stack-m u-pt-0_5 border--top height-50vh overflow-auto">
51
+ <dp-insertable-recommendation
52
+ class="o-list__item"
53
+ :from-other-procedure="recommendation.fromOtherProcedure"
54
+ :key="recommendation.id"
55
+ :procedure-name="recommendation.procedureName"
56
+ v-for="recommendation in currentRecommendations"
57
+ :search-term="searchTerm"
58
+ @insert-recommendation="toggleInsert(recommendation.attributes.recommendation)"
59
+ :recommendation="recommendation.attributes.recommendation" />
60
+ </ul>
61
+ <div
62
+ v-if="currentRecommendations.length === 0"
63
+ class="u-pt-0_5 border--top">
64
+ {{ Translator.trans('statement.list.empty') }}
65
+ </div>
66
+ </template>
67
+ </template>
68
+ </dp-modal>
69
+ </template>
70
+
71
+ <script>
72
+ import { DpLabel, DpLoading } from 'demosplan-ui/components'
73
+ import { mapMutations, mapState } from 'vuex'
74
+ import dataTableSearch from '../DpDataTable/DataTableSearch'
75
+ import { dpApi } from 'demosplan-utils'
76
+ import DpInsertableRecommendation from './DpRecommendationModal/DpInsertableRecommendation'
77
+ import DpModal from '../DpModal'
78
+ import DpSearchField from '../form/DpSearchField'
79
+
80
+ export default {
81
+ name: 'DpRecommendationModal',
82
+
83
+ components: {
84
+ DpInsertableRecommendation,
85
+ DpLabel,
86
+ DpLoading,
87
+ DpSearchField,
88
+ DpModal
89
+ },
90
+
91
+ // Array of procedureIds recommendations should be searched in.
92
+ inject: ['recommendationProcedureIds'],
93
+
94
+ props: {
95
+ procedureId: {
96
+ type: String,
97
+ required: false,
98
+ default: ''
99
+ },
100
+
101
+ segmentId: {
102
+ type: String,
103
+ required: true
104
+ }
105
+ },
106
+
107
+ data () {
108
+ return {
109
+ searchTerm: '',
110
+ similarRecommendations: [],
111
+ isLoading: true
112
+ }
113
+ },
114
+
115
+ computed: {
116
+ ...mapState('statementSegment', {
117
+ segments: 'items'
118
+ }),
119
+
120
+ ...mapState('tag', {
121
+ tags: 'items'
122
+ }),
123
+
124
+ currentRecommendations () {
125
+ return dataTableSearch(this.searchTerm, this.similarRecommendations, ['attributes.recommendation'])
126
+ },
127
+
128
+ segment () {
129
+ return this.segments[this.segmentId]
130
+ },
131
+
132
+ tagIds () {
133
+ return this.segment.hasRelationship('tags')
134
+ ? this.segment.relationships.tags.data.map(tag => tag.id)
135
+ : []
136
+ },
137
+
138
+ tagTitles () {
139
+ return this.segment.hasRelationship('tags')
140
+ ? this.segment.relationships.tags.data.map(tag => this.tags[tag.id].attributes.title)
141
+ : []
142
+ }
143
+ },
144
+
145
+ methods: {
146
+ ...mapMutations('statementSegment', ['setItem']),
147
+
148
+ getRecommendationProcedureName (recommendation, included) {
149
+ const recommendationProcedureId = this.getRecommendationProcedureId(recommendation, included)
150
+ const procedure = included.find(item => item.id === recommendationProcedureId)
151
+ return procedure.attributes.name
152
+ },
153
+
154
+ getRecommendationProcedureId (recommendation, included) {
155
+ const parentStatementId = recommendation.relationships?.parentStatement.data?.id || null
156
+ const parentStatement = included.find(item => item.id === parentStatementId)
157
+ return parentStatement.relationships.procedure.data.id
158
+ },
159
+
160
+ getTagTitle (id, idx) {
161
+ return (this.tags[id] && this.tags[id].attributes.title) + ((idx < this.tagIds.length - 1 && ',') || '')
162
+ },
163
+
164
+ isRecommendationFromOtherProcedure (recommendation, included) {
165
+ const recommendationProcedureId = this.getRecommendationProcedureId(recommendation, included)
166
+ return this.procedureId !== recommendationProcedureId
167
+ },
168
+
169
+ toggleInsert (recommendation) {
170
+ this.$emit('insert-recommendation', recommendation)
171
+ this.toggleModal()
172
+ },
173
+
174
+ toggleModal (open) {
175
+ if (open === 'open') {
176
+ this.isLoading = true
177
+ this.fetchSimilarRecommendations()
178
+ .then(({ data }) => {
179
+ this.similarRecommendations = data.data.map(recommendation => {
180
+ return {
181
+ ...recommendation,
182
+ fromOtherProcedure: this.isRecommendationFromOtherProcedure(recommendation, data.included),
183
+ procedureName: this.getRecommendationProcedureName(recommendation, data.included)
184
+ }
185
+ })
186
+
187
+ /*
188
+ * Recommendations from the current procedure are more relevant to the user than recommendations from another procedure.
189
+ * Therefore, we put all recommendations from the current procedure on top.
190
+ */
191
+ this.similarRecommendations.sort((a, b) => {
192
+ if (a.fromOtherProcedure && b.fromOtherProcedure === false) {
193
+ return 1
194
+ }
195
+ if (a.fromOtherProcedure === false && b.fromOtherProcedure) {
196
+ return -1
197
+ }
198
+
199
+ return 0
200
+ })
201
+
202
+ const uniqueRecommendationsText = []
203
+ const uniqueRecommendations = []
204
+
205
+ this.similarRecommendations.forEach((obj) => {
206
+ if (uniqueRecommendationsText.includes(obj.attributes.recommendation) === false) {
207
+ uniqueRecommendations.push(obj)
208
+ uniqueRecommendationsText.push(obj.attributes.recommendation)
209
+ }
210
+ })
211
+
212
+ this.similarRecommendations = uniqueRecommendations
213
+
214
+ this.isLoading = false
215
+ })
216
+ }
217
+ this.$refs.recommendationModal.toggle()
218
+ },
219
+
220
+ updateSegment (recommendation) {
221
+ const payload = JSON.parse(JSON.stringify(this.segment))
222
+ payload.attributes.recommendation = recommendation
223
+ this.setItem({ ...payload, id: payload.id, group: null })
224
+ },
225
+
226
+ fetchSimilarRecommendations () {
227
+ const url = Routing.generate('api_resource_list', { resourceType: 'StatementSegment' })
228
+ const params = {
229
+ include: 'parentStatement,parentStatement.procedure',
230
+ fields: {
231
+ StatementSegment: [
232
+ 'id',
233
+ 'recommendation',
234
+ 'text',
235
+ 'externId',
236
+ 'internId',
237
+ 'orderInProcedure',
238
+ 'parentStatement'
239
+ ].join(),
240
+ Statement: [
241
+ 'procedure'
242
+ ].join()
243
+ },
244
+ filter: {
245
+ tags: {
246
+ condition: {
247
+ path: 'tags.title',
248
+ value: this.tagTitles,
249
+ operator: 'IN'
250
+ }
251
+ },
252
+ notEmpty: {
253
+ condition: {
254
+ path: 'recommendation',
255
+ value: '',
256
+ operator: '<>'
257
+ }
258
+ },
259
+ notSelf: {
260
+ condition: {
261
+ path: 'id',
262
+ value: this.segmentId,
263
+ operator: '<>'
264
+ }
265
+ },
266
+ anyOfProcedures: {
267
+ condition: {
268
+ path: 'parentStatement.procedure.id',
269
+ value: this.recommendationProcedureIds,
270
+ memberOf: 'IN'
271
+ }
272
+ }
273
+ }
274
+ }
275
+ return dpApi.get(url, params, { serialize: true })
276
+ },
277
+
278
+ setSearchTerm (term) {
279
+ this.searchTerm = term
280
+ }
281
+ }
282
+ }
283
+ </script>
@@ -0,0 +1,121 @@
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
+ <template>
11
+ <span
12
+ class="resizable-image"
13
+ ref="imagewrapper"
14
+ tabindex="1"
15
+ @mouseup="() => updateImageDimensions(node.attrs.width)"
16
+ :style="`height: ${node.attrs.height}px; width: ${node.attrs.width}px`">
17
+ <img
18
+ @click.ctrl="$root.$emit('open-image-alt-modal', $event, id)"
19
+ :alt="node.attrs.alt"
20
+ ref="image"
21
+ :src="node.attrs.src"
22
+ :title="Translator.trans('image.alt.edit.hint')"
23
+ :width="node.attrs.width">
24
+ </span>
25
+ </template>
26
+
27
+ <script>
28
+ import { v4 as uuid } from 'uuid'
29
+
30
+ export default {
31
+ name: 'DpResizableImage',
32
+
33
+ props: {
34
+ getPos: {
35
+ type: Function,
36
+ required: false,
37
+ default: () => ({})
38
+ },
39
+
40
+ node: {
41
+ type: Object,
42
+ required: false,
43
+ default: () => ({})
44
+ },
45
+
46
+ options: {
47
+ type: Object,
48
+ required: false,
49
+ default: () => ({})
50
+ },
51
+
52
+ selected: {
53
+ type: Boolean,
54
+ required: false,
55
+ default: false
56
+ },
57
+
58
+ updateAttrs: {
59
+ type: Function,
60
+ required: false,
61
+ default: () => ({})
62
+ },
63
+
64
+ view: {
65
+ type: Object,
66
+ required: false,
67
+ default: () => ({})
68
+ }
69
+ },
70
+
71
+ data () {
72
+ return {
73
+ id: uuid(),
74
+ observer: null,
75
+ ratioFactor: 1
76
+ }
77
+ },
78
+
79
+ methods: {
80
+ updateImageDimensions (width) {
81
+ if (width > 0) {
82
+ this.$nextTick(() => {
83
+ this.updateAttrs({
84
+ height: width / this.ratioFactor,
85
+ width: width
86
+ })
87
+ })
88
+ }
89
+ },
90
+
91
+ setRatio (updateSize = false) {
92
+ const img = new Image()
93
+ img.onload = () => {
94
+ this.ratioFactor = img.width / img.height
95
+
96
+ // If the width is not set jet, set it to the image original dimensions
97
+ if (updateSize) {
98
+ // The max width should not be wider than the editor.
99
+ const innerEditorWidth = this.$parent.$el.clientWidth - 40
100
+ const width = (img.width < innerEditorWidth) ? img.width : innerEditorWidth
101
+ this.updateImageDimensions(width)
102
+ }
103
+ }
104
+ img.src = this.node.attrs.src // This must be done AFTER setting onload
105
+ }
106
+ },
107
+
108
+ mounted () {
109
+ // Observe changes to image wrapper to update image size
110
+ this.observer = new ResizeObserver(e => this.updateImageDimensions(e[0].target.offsetWidth))
111
+ this.observer.observe(this.$refs.imagewrapper)
112
+
113
+ this.$root.$on('update-image:' + this.id, data => {
114
+ this.updateAttrs(data)
115
+ })
116
+
117
+ const updateSize = (this.node.attrs.height > 0) === false || this.node.attrs.height === Infinity
118
+ this.setRatio(updateSize)
119
+ }
120
+ }
121
+ </script>