From e27abef2de79d4c7eeaf38fac7467744ca6d9ad3 Mon Sep 17 00:00:00 2001 From: Katrin Yordanova Date: Thu, 6 Apr 2023 14:49:08 +0300 Subject: [PATCH] Add calculatePosition prop to vue-select in all webapps --- .../src/components/Apigw/CFilterParams.vue | 1 + .../src/components/Authclient/CSelectUser.vue | 1 + .../Permissions/CPermissionClone.vue | 1 + .../Permissions/CPermissionList.vue | 2 + client/web/admin/src/mixins/index.js | 3 ++ .../admin/src/mixins/vue-select-position.js | 48 +++++++++++++++++++ .../Public/Record/Exporter/Configurator.vue | 1 + .../components/Requests/Editor/Correct.vue | 1 + .../src/components/Requests/Editor/Delete.vue | 1 + client/web/privacy/src/mixins/index.js | 2 + .../privacy/src/mixins/vue-select-position.js | 48 +++++++++++++++++++ .../Privacy/DataOverview/Application.vue | 1 + .../src/components/Common/ColumnSelector.vue | 1 + .../DisplayElements/Configurators/Chart.vue | 1 + client/web/reporter/src/mixins/index.js | 2 + .../src/mixins/vue-select-position.js | 48 +++++++++++++++++++ .../web/reporter/src/views/Report/Builder.vue | 1 + client/web/reporter/src/views/Report/View.vue | 1 + .../src/components/Configurator/Function.vue | 4 ++ .../src/components/Configurator/Trigger.vue | 4 ++ .../src/components/Configurator/Workflow.vue | 1 + .../src/components/ExpressionTable.vue | 1 + client/web/workflow/src/mixins/index.js | 2 + .../src/mixins/vue-select-position.js | 48 +++++++++++++++++++ .../permissions/CPermissionsModal.vue | 8 ++++ .../privacy/CSensitivityLevelPicker.vue | 6 +++ .../kinds/CPromptComposeRecordPicker.vue | 4 +- lib/vue/src/mixins/index.ts | 1 + lib/vue/src/mixins/vue-select-position.js | 48 +++++++++++++++++++ 29 files changed, 289 insertions(+), 2 deletions(-) create mode 100644 client/web/admin/src/mixins/vue-select-position.js create mode 100644 client/web/privacy/src/mixins/vue-select-position.js create mode 100644 client/web/reporter/src/mixins/vue-select-position.js create mode 100644 client/web/workflow/src/mixins/vue-select-position.js create mode 100644 lib/vue/src/mixins/vue-select-position.js diff --git a/client/web/admin/src/components/Apigw/CFilterParams.vue b/client/web/admin/src/components/Apigw/CFilterParams.vue index e254f3c3d..c36f5b880 100644 --- a/client/web/admin/src/components/Apigw/CFilterParams.vue +++ b/client/web/admin/src/components/Apigw/CFilterParams.vue @@ -39,6 +39,7 @@ :get-option-key="getOptionKey" :reduce="wf => wf.workflowID" :placeholder="$t('filters.placeholders.workflow')" + :calculate-position="calculateDropdownPosition" class="bg-white" /> diff --git a/client/web/admin/src/components/Authclient/CSelectUser.vue b/client/web/admin/src/components/Authclient/CSelectUser.vue index 66a49f5e3..922a3c098 100644 --- a/client/web/admin/src/components/Authclient/CSelectUser.vue +++ b/client/web/admin/src/components/Authclient/CSelectUser.vue @@ -5,6 +5,7 @@ :get-option-label="getOptionLabel" :get-option-key="getOptionKey" :value="user.value" + :calculate-position="calculateDropdownPosition" class="bg-white" @search="search" @input="updateRunAs" diff --git a/client/web/admin/src/components/Permissions/CPermissionClone.vue b/client/web/admin/src/components/Permissions/CPermissionClone.vue index 6638b313b..8cd38b720 100644 --- a/client/web/admin/src/components/Permissions/CPermissionClone.vue +++ b/client/web/admin/src/components/Permissions/CPermissionClone.vue @@ -36,6 +36,7 @@ :loading="processingRoles" multiple :placeholder="$t('ui.clone.pick-role')" + :calculate-position="calculateDropdownPosition" class="bg-white" /> diff --git a/client/web/admin/src/components/Permissions/CPermissionList.vue b/client/web/admin/src/components/Permissions/CPermissionList.vue index 810a1d76c..377864241 100644 --- a/client/web/admin/src/components/Permissions/CPermissionList.vue +++ b/client/web/admin/src/components/Permissions/CPermissionList.vue @@ -232,6 +232,7 @@ clearable :disabled="add.mode === 'eval' && !!add.userID" :placeholder="$t('ui.add.role.placeholder')" + :calculate-position="calculateDropdownPosition" class="bg-white" /> @@ -253,6 +254,7 @@ label="name" clearable :placeholder="$t('ui.add.user.placeholder')" + :calculate-position="calculateDropdownPosition" class="bg-white" @search="searchUsers" /> diff --git a/client/web/admin/src/mixins/index.js b/client/web/admin/src/mixins/index.js index 3e2cf82d2..66d3ade33 100644 --- a/client/web/admin/src/mixins/index.js +++ b/client/web/admin/src/mixins/index.js @@ -3,8 +3,11 @@ import Vue from 'vue' import resourceTranslations from './resource-translations' import toast from './toast' +import vueSelectPosition from './vue-select-position' + import './eventbus' Vue.mixin(resourceTranslations) Vue.mixin(toast) +Vue.mixin(vueSelectPosition) diff --git a/client/web/admin/src/mixins/vue-select-position.js b/client/web/admin/src/mixins/vue-select-position.js new file mode 100644 index 000000000..4973cffd7 --- /dev/null +++ b/client/web/admin/src/mixins/vue-select-position.js @@ -0,0 +1,48 @@ +import { createPopper } from '@popperjs/core' + +export default { + methods: { + calculateDropdownPosition (dropdownList, component, { width }) { + /** + * We need to explicitly define the dropdown width since + * it is usually inherited from the parent with CSS. + */ + dropdownList.style.width = width + + /** + * Here we position the dropdownList relative to the $refs.toggle Element. + * + * The 'offset' modifier aligns the dropdown so that the $refs.toggle and + * the dropdownList overlap by 1 pixel. + * + * The 'toggleClass' modifier adds a 'drop-up' class to the Vue Select + * wrapper so that we can set some styles for when the dropdown is placed + * above. + */ + const popper = createPopper(component.$refs.toggle, dropdownList, { + placement: 'bottom', + modifiers: [ + { + name: 'offset', + options: { + offset: [0, -1], + }, + }, + { + name: 'toggleClass', + enabled: true, + phase: 'write', + fn ({ state }) { + component.$el.classList.toggle('drop-up', state.placement === 'top') + }, + }], + }) + + /** + * To prevent memory leaks Popper needs to be destroyed. + * If you return function, it will be called just before dropdown is removed from DOM. + */ + return () => popper.destroy() + }, + }, +} diff --git a/client/web/compose/src/components/Public/Record/Exporter/Configurator.vue b/client/web/compose/src/components/Public/Record/Exporter/Configurator.vue index f3512e9ff..72126118a 100644 --- a/client/web/compose/src/components/Public/Record/Exporter/Configurator.vue +++ b/client/web/compose/src/components/Public/Record/Exporter/Configurator.vue @@ -31,6 +31,7 @@ v-model="exportTimezone" :options="timezones" :get-option-key="getOptionKey" + :calculate-position="calculateDropdownPosition" :placeholder="$t('recordList.export.timezonePlaceholder')" class="bg-white" /> diff --git a/client/web/privacy/src/components/Requests/Editor/Correct.vue b/client/web/privacy/src/components/Requests/Editor/Correct.vue index 755b49df2..4b10303f6 100644 --- a/client/web/privacy/src/components/Requests/Editor/Correct.vue +++ b/client/web/privacy/src/components/Requests/Editor/Correct.vue @@ -18,6 +18,7 @@ :get-option-label="({ handle, meta }) => meta.name || handle" :get-option-key="getOptionKey" :placeholder="$t('connection.placeholder')" + :calculate-position="calculateDropdownPosition" class="h-100 bg-white" /> diff --git a/client/web/privacy/src/components/Requests/Editor/Delete.vue b/client/web/privacy/src/components/Requests/Editor/Delete.vue index d33931c41..43dd97d13 100644 --- a/client/web/privacy/src/components/Requests/Editor/Delete.vue +++ b/client/web/privacy/src/components/Requests/Editor/Delete.vue @@ -18,6 +18,7 @@ :get-option-label="({ handle, meta }) => meta.name || handle" :get-option-key="getOptionKey" :placeholder="$t('connection.placeholder')" + :calculate-position="calculateDropdownPosition" class="h-100 bg-white" /> diff --git a/client/web/privacy/src/mixins/index.js b/client/web/privacy/src/mixins/index.js index f80c84cf9..75be1ded0 100644 --- a/client/web/privacy/src/mixins/index.js +++ b/client/web/privacy/src/mixins/index.js @@ -1,5 +1,7 @@ import Vue from 'vue' import toast from './toast' +import vueSelectPosition from './vue-select-position' Vue.mixin(toast) +Vue.mixin(vueSelectPosition) diff --git a/client/web/privacy/src/mixins/vue-select-position.js b/client/web/privacy/src/mixins/vue-select-position.js new file mode 100644 index 000000000..4973cffd7 --- /dev/null +++ b/client/web/privacy/src/mixins/vue-select-position.js @@ -0,0 +1,48 @@ +import { createPopper } from '@popperjs/core' + +export default { + methods: { + calculateDropdownPosition (dropdownList, component, { width }) { + /** + * We need to explicitly define the dropdown width since + * it is usually inherited from the parent with CSS. + */ + dropdownList.style.width = width + + /** + * Here we position the dropdownList relative to the $refs.toggle Element. + * + * The 'offset' modifier aligns the dropdown so that the $refs.toggle and + * the dropdownList overlap by 1 pixel. + * + * The 'toggleClass' modifier adds a 'drop-up' class to the Vue Select + * wrapper so that we can set some styles for when the dropdown is placed + * above. + */ + const popper = createPopper(component.$refs.toggle, dropdownList, { + placement: 'bottom', + modifiers: [ + { + name: 'offset', + options: { + offset: [0, -1], + }, + }, + { + name: 'toggleClass', + enabled: true, + phase: 'write', + fn ({ state }) { + component.$el.classList.toggle('drop-up', state.placement === 'top') + }, + }], + }) + + /** + * To prevent memory leaks Popper needs to be destroyed. + * If you return function, it will be called just before dropdown is removed from DOM. + */ + return () => popper.destroy() + }, + }, +} diff --git a/client/web/privacy/src/views/Privacy/DataOverview/Application.vue b/client/web/privacy/src/views/Privacy/DataOverview/Application.vue index 50697f603..f84075619 100644 --- a/client/web/privacy/src/views/Privacy/DataOverview/Application.vue +++ b/client/web/privacy/src/views/Privacy/DataOverview/Application.vue @@ -20,6 +20,7 @@ :options="connections" :clearable="false" :placeholder="$t('connection.placeholder')" + :calculate-position="calculateDropdownPosition" :get-option-label="({ handle, meta }) => meta.name || handle" :get-option-key="getOptionKey" class="h-100 bg-white" diff --git a/client/web/reporter/src/components/Common/ColumnSelector.vue b/client/web/reporter/src/components/Common/ColumnSelector.vue index 80008cbbe..d9bf97f0a 100644 --- a/client/web/reporter/src/components/Common/ColumnSelector.vue +++ b/client/web/reporter/src/components/Common/ColumnSelector.vue @@ -8,6 +8,7 @@ :placeholder="$t('general:label.none')" :reduce="r => r.name" append-to-body + :calculate-position="calculateDropdownPosition" class="column-selector bg-white" v-on="$listeners" /> diff --git a/client/web/reporter/src/components/Report/Blocks/DisplayElements/Configurators/Chart.vue b/client/web/reporter/src/components/Report/Blocks/DisplayElements/Configurators/Chart.vue index 80bb70dfd..2bf3f7bba 100644 --- a/client/web/reporter/src/components/Report/Blocks/DisplayElements/Configurators/Chart.vue +++ b/client/web/reporter/src/components/Report/Blocks/DisplayElements/Configurators/Chart.vue @@ -59,6 +59,7 @@ option-text="label" option-value="value" clearable + :calculate-position="calculateDropdownPosition" class="mw-100" style="min-width: 100%;" > diff --git a/client/web/reporter/src/mixins/index.js b/client/web/reporter/src/mixins/index.js index f80c84cf9..75be1ded0 100644 --- a/client/web/reporter/src/mixins/index.js +++ b/client/web/reporter/src/mixins/index.js @@ -1,5 +1,7 @@ import Vue from 'vue' import toast from './toast' +import vueSelectPosition from './vue-select-position' Vue.mixin(toast) +Vue.mixin(vueSelectPosition) diff --git a/client/web/reporter/src/mixins/vue-select-position.js b/client/web/reporter/src/mixins/vue-select-position.js new file mode 100644 index 000000000..4973cffd7 --- /dev/null +++ b/client/web/reporter/src/mixins/vue-select-position.js @@ -0,0 +1,48 @@ +import { createPopper } from '@popperjs/core' + +export default { + methods: { + calculateDropdownPosition (dropdownList, component, { width }) { + /** + * We need to explicitly define the dropdown width since + * it is usually inherited from the parent with CSS. + */ + dropdownList.style.width = width + + /** + * Here we position the dropdownList relative to the $refs.toggle Element. + * + * The 'offset' modifier aligns the dropdown so that the $refs.toggle and + * the dropdownList overlap by 1 pixel. + * + * The 'toggleClass' modifier adds a 'drop-up' class to the Vue Select + * wrapper so that we can set some styles for when the dropdown is placed + * above. + */ + const popper = createPopper(component.$refs.toggle, dropdownList, { + placement: 'bottom', + modifiers: [ + { + name: 'offset', + options: { + offset: [0, -1], + }, + }, + { + name: 'toggleClass', + enabled: true, + phase: 'write', + fn ({ state }) { + component.$el.classList.toggle('drop-up', state.placement === 'top') + }, + }], + }) + + /** + * To prevent memory leaks Popper needs to be destroyed. + * If you return function, it will be called just before dropdown is removed from DOM. + */ + return () => popper.destroy() + }, + }, +} diff --git a/client/web/reporter/src/views/Report/Builder.vue b/client/web/reporter/src/views/Report/Builder.vue index 704cee21a..97404fbf1 100644 --- a/client/web/reporter/src/views/Report/Builder.vue +++ b/client/web/reporter/src/views/Report/Builder.vue @@ -19,6 +19,7 @@ :options="scenarioOptions" :get-option-key="getOptionKey" :placeholder="$t('builder:pick-scenario')" + :calculate-position="calculateDropdownPosition" class="bg-white rounded" @input="refreshReport()" /> diff --git a/client/web/reporter/src/views/Report/View.vue b/client/web/reporter/src/views/Report/View.vue index 692aa1ba0..ea7f7f3ef 100644 --- a/client/web/reporter/src/views/Report/View.vue +++ b/client/web/reporter/src/views/Report/View.vue @@ -16,6 +16,7 @@ :options="scenarioOptions" :get-option-key="getOptionKey" :placeholder="$t('pick-scenario')" + :calculate-position="calculateDropdownPosition" class="bg-white rounded" @input="refreshReport()" /> diff --git a/client/web/workflow/src/components/Configurator/Function.vue b/client/web/workflow/src/components/Configurator/Function.vue index 94b83299f..8938fc5bd 100644 --- a/client/web/workflow/src/components/Configurator/Function.vue +++ b/client/web/workflow/src/components/Configurator/Function.vue @@ -34,6 +34,7 @@ :reduce="f => f.value" :filter="functionFilter" :placeholder="$t('steps:function.configurator.select-function')" + :calculate-position="calculateDropdownPosition" @input="functionChanged" /> @@ -109,6 +110,7 @@ :get-option-key="getOptionParamKey" :filter="argTypeFilter" :clearable="false" + :calculate-position="calculateDropdownPosition" @input="$root.$emit('change-detected')" /> @@ -130,6 +132,7 @@ :reduce="wf => a.type === 'ID' ? wf.workflowID : wf.handle" clearable :placeholder="$t('steps:function.configurator.search-workflow')" + :calculate-position="calculateDropdownPosition" class="bg-white rounded" @input="$root.$emit('change-detected')" @search="searchWorkflows" @@ -144,6 +147,7 @@ :filter="varFilter" :reduce="a => a.value" :placeholder="$t('steps:function.configurator.option-select')" + :calculate-position="calculateDropdownPosition" @input="$root.$emit('change-detected')" /> diff --git a/client/web/workflow/src/components/Configurator/Trigger.vue b/client/web/workflow/src/components/Configurator/Trigger.vue index faad11e7a..4b63b0bbe 100644 --- a/client/web/workflow/src/components/Configurator/Trigger.vue +++ b/client/web/workflow/src/components/Configurator/Trigger.vue @@ -28,6 +28,7 @@ :reduce="r => r.value" :filter="resTypeFilter" :placeholder="$t('steps:trigger.configurator.select-resource-type')" + :calculate-position="calculateDropdownPosition" @input="resourceChanged" /> @@ -45,6 +46,7 @@ :reduce="e => e.eventType" :filter="evtTypeFilter" :placeholder="$t('steps:trigger.configurator.select-event-type')" + :calculate-position="calculateDropdownPosition" @input="eventChanged" /> @@ -149,6 +151,7 @@ :reduce="c => c.value" :filter="constrFilter" :placeholder="$t('steps:trigger.configurator.select-constraint-type')" + :calculate-position="calculateDropdownPosition" @input="$root.$emit('change-detected')" /> @@ -164,6 +167,7 @@ label="text" :reduce="c => c.value" :placeholder="$t('steps:trigger.configurator.select-operator')" + :calculate-position="calculateDropdownPosition" @input="$root.$emit('change-detected')" /> diff --git a/client/web/workflow/src/components/Configurator/Workflow.vue b/client/web/workflow/src/components/Configurator/Workflow.vue index aaa802998..b38dec95f 100644 --- a/client/web/workflow/src/components/Configurator/Workflow.vue +++ b/client/web/workflow/src/components/Configurator/Workflow.vue @@ -49,6 +49,7 @@ :get-option-label="getOptionLabel" :get-option-key="getOptionKey" :value="user.value" + :calculate-position="calculateDropdownPosition" @search="search" @input="updateRunAs" /> diff --git a/client/web/workflow/src/components/ExpressionTable.vue b/client/web/workflow/src/components/ExpressionTable.vue index 3447de994..e02850c38 100644 --- a/client/web/workflow/src/components/ExpressionTable.vue +++ b/client/web/workflow/src/components/ExpressionTable.vue @@ -72,6 +72,7 @@ :get-option-key="getOptionKey" :clearable="false" :filter="varFilter" + :calculate-position="calculateDropdownPosition" @input="$root.$emit('change-detected')" /> diff --git a/client/web/workflow/src/mixins/index.js b/client/web/workflow/src/mixins/index.js index f80c84cf9..75be1ded0 100644 --- a/client/web/workflow/src/mixins/index.js +++ b/client/web/workflow/src/mixins/index.js @@ -1,5 +1,7 @@ import Vue from 'vue' import toast from './toast' +import vueSelectPosition from './vue-select-position' Vue.mixin(toast) +Vue.mixin(vueSelectPosition) diff --git a/client/web/workflow/src/mixins/vue-select-position.js b/client/web/workflow/src/mixins/vue-select-position.js new file mode 100644 index 000000000..4973cffd7 --- /dev/null +++ b/client/web/workflow/src/mixins/vue-select-position.js @@ -0,0 +1,48 @@ +import { createPopper } from '@popperjs/core' + +export default { + methods: { + calculateDropdownPosition (dropdownList, component, { width }) { + /** + * We need to explicitly define the dropdown width since + * it is usually inherited from the parent with CSS. + */ + dropdownList.style.width = width + + /** + * Here we position the dropdownList relative to the $refs.toggle Element. + * + * The 'offset' modifier aligns the dropdown so that the $refs.toggle and + * the dropdownList overlap by 1 pixel. + * + * The 'toggleClass' modifier adds a 'drop-up' class to the Vue Select + * wrapper so that we can set some styles for when the dropdown is placed + * above. + */ + const popper = createPopper(component.$refs.toggle, dropdownList, { + placement: 'bottom', + modifiers: [ + { + name: 'offset', + options: { + offset: [0, -1], + }, + }, + { + name: 'toggleClass', + enabled: true, + phase: 'write', + fn ({ state }) { + component.$el.classList.toggle('drop-up', state.placement === 'top') + }, + }], + }) + + /** + * To prevent memory leaks Popper needs to be destroyed. + * If you return function, it will be called just before dropdown is removed from DOM. + */ + return () => popper.destroy() + }, + }, +} diff --git a/lib/vue/src/components/permissions/CPermissionsModal.vue b/lib/vue/src/components/permissions/CPermissionsModal.vue index 9c92b7c24..1fbec300e 100644 --- a/lib/vue/src/components/permissions/CPermissionsModal.vue +++ b/lib/vue/src/components/permissions/CPermissionsModal.vue @@ -56,6 +56,7 @@ :options="roles" :get-option-key="getOptionRoleKey" append-to-body + :calculate-position="calculateDropdownPosition" class="h-100 bg-white" @input="onRoleChange" /> @@ -190,6 +191,7 @@ clearable :disabled="!!add.userID" :placeholder="labels.add.role.placeholder" + :calculate-position="calculateDropdownPosition" class="bg-white" /> @@ -210,6 +212,7 @@ label="name" clearable :placeholder="labels.add.user.placeholder" + :calculate-position="calculateDropdownPosition" class="bg-white" @search="searchUsers" /> @@ -221,6 +224,7 @@ import { modalOpenEventName, split } from './def.ts' import { VueSelect } from 'vue-select' import Rules from './form/Rules.vue' +import calculateDropdownPosition from '../../mixins/vue-select-position' export default { i18nOptions: { @@ -232,6 +236,10 @@ export default { VueSelect, }, + mixins: [ + calculateDropdownPosition + ], + props: { labels: { required: false, diff --git a/lib/vue/src/components/privacy/CSensitivityLevelPicker.vue b/lib/vue/src/components/privacy/CSensitivityLevelPicker.vue index 58f62f046..ff722d50d 100644 --- a/lib/vue/src/components/privacy/CSensitivityLevelPicker.vue +++ b/lib/vue/src/components/privacy/CSensitivityLevelPicker.vue @@ -9,6 +9,7 @@ :placeholder="placeholder" :reduce="l => l.sensitivityLevelID" append-to-body + :calculate-position="calculateDropdownPosition" class="bg-white" @input="onInput" /> @@ -17,12 +18,17 @@