@policystudio/policy-studio-ui-vue 1.1.90-beta.75 → 1.1.90-beta.76

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.
@@ -4013,21 +4013,9 @@ video {
4013
4013
  min-height: 27px;
4014
4014
  font-size: 0.875rem;
4015
4015
  }
4016
- .psui-el-dropdown-menu-dialog-wrapper {
4017
- position: fixed;
4018
- }
4019
4016
  .psui-el-dropdown-menu-dialog-wrapper {
4020
4017
  z-index: 50;
4021
4018
  }
4022
- .psui-el-dropdown-menu-dialog-wrapper {
4023
- display: none;
4024
- }
4025
- .psui-el-dropdown-menu-dialog-wrapper {
4026
- width: auto;
4027
- }
4028
- .psui-el-dropdown-menu-dialog-wrapper {
4029
- transform-origin: top right;
4030
- }
4031
4019
  .psui-el-dropdown-menu-dialog-wrapper {
4032
4020
  overflow: auto;
4033
4021
  }
@@ -4038,28 +4026,11 @@ video {
4038
4026
  --tw-bg-opacity: 1;
4039
4027
  background-color: rgb(255, 255, 255, var(--tw-bg-opacity, 1));
4040
4028
  }
4041
- .psui-el-dropdown-menu-dialog-wrapper {
4042
- opacity: 0;
4043
- }
4044
4029
  .psui-el-dropdown-menu-dialog-wrapper {
4045
4030
  --tw-shadow: 0px 0px 8px rgba(0, 0, 0, 0.04), 0px 5px 6px rgba(0, 0, 0, 0.1);
4046
4031
  --tw-shadow-colored: 0px 0px 8px var(--tw-shadow-color), 0px 5px 6px var(--tw-shadow-color);
4047
4032
  box-shadow: var(--tw-ring-offset-shadow, 0 0 rgba(0, 0, 0, 0)), var(--tw-ring-shadow, 0 0 rgba(0, 0, 0, 0)), var(--tw-shadow);
4048
4033
  }
4049
- .psui-el-dropdown-menu-dialog-wrapper {
4050
- transition-property: opacity;
4051
- transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
4052
- transition-duration: 150ms;
4053
- }
4054
- .psui-el-dropdown-menu-dialog-wrapper {
4055
- transition-duration: 100ms;
4056
- }
4057
- .psui-el-dropdown-menu-dialog-wrapper {
4058
- transition-timing-function: cubic-bezier(0.4, 0, 1, 1);
4059
- }
4060
- .psui-el-dropdown-menu-dialog-wrapper-dialog {
4061
- width: 100%;
4062
- }
4063
4034
 
4064
4035
  .psui-el-dropdown-menu-list {
4065
4036
  margin-top: 1rem;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@policystudio/policy-studio-ui-vue",
3
- "version": "1.1.90-beta.75",
3
+ "version": "1.1.90-beta.76",
4
4
  "description": "Policy Studio UI",
5
5
  "author": "Policy Studio Team",
6
6
  "scripts": {
@@ -15,10 +15,12 @@
15
15
  "main": "dist/index.js",
16
16
  "types": "src/types/index.d.ts",
17
17
  "dependencies": {
18
+ "@floating-ui/vue": "^1.1.9",
18
19
  "@vue/compat": "^3.4.5",
19
20
  "core-js": "^3.6.5",
20
21
  "v-tooltip": "^2.1.3",
21
- "vue": "^3.4.5"
22
+ "vue": "^3.4.5",
23
+ "vue2-teleport": "^1.1.4"
22
24
  },
23
25
  "devDependencies": {
24
26
  "@typescript-eslint/eslint-plugin": "^5.4.0",
@@ -13,11 +13,7 @@
13
13
  }
14
14
 
15
15
  &-dialog-wrapper {
16
- @apply psui-hidden psui-origin-top-right psui-bg-white psui-fixed psui-w-auto psui-rounded-md psui-z-50 psui-opacity-0 psui-shadow-elevation-20 psui-overflow-auto psui-transition-opacity psui-duration-100 psui-ease-in;
17
-
18
- &-dialog {
19
- @apply psui-w-full;
20
- }
16
+ @apply psui-bg-white psui-rounded-md psui-z-50 psui-shadow-elevation-20 psui-overflow-auto;
21
17
  }
22
18
  }
23
19
  }
@@ -1,212 +1,120 @@
1
1
  <template>
2
2
  <div
3
- ref="PSDropdown"
4
3
  class="psui-el-dropdown-menu"
5
- :class="{ 'is-open': show.value }"
6
- v-click-outside="close"
7
4
  >
8
5
  <div
9
6
  ref="PSDropdownTrigger"
10
- v-if="$slots.dropdownTrigger"
11
- @click="show && !toggleWhenActive ? '' : toggle($event)"
12
- >
13
- <slot name="dropdownTrigger" />
14
- </div>
15
-
16
- <button
17
- v-else
18
- @click="show && !toggleWhenActive ? '' : toggle($event)"
19
- type="button"
20
- :id="id.value"
21
- aria-haspopup="true"
22
- aria-expanded="true"
23
- ref="PSDropdownTrigger"
7
+ @click="toggle"
24
8
  >
25
9
  <slot
26
- v-if="show.value && $slots.buttonLabelOnShow"
27
- name="buttonLabelOnShow"
10
+ v-if="$slots.dropdownTrigger"
11
+ name="dropdownTrigger"
28
12
  />
29
- <slot
13
+ <button
30
14
  v-else
31
- name="buttonLabel"
32
- />
33
- </button>
34
- <div
35
- ref="PSDropdownDialog"
36
- role="menu"
37
- class="psui-el-dropdown-menu-dialog-wrapper"
38
- aria-orientation="vertical"
39
- :aria-labelledby="id.value"
40
- :style="{ minWidth: minWidthDropDown }"
41
- >
42
- <div class="psui-el-dropdown-menu-dialog">
43
- <slot name="items" />
44
- </div>
15
+ type="button"
16
+ :aria-expanded="show"
17
+ >
18
+ <slot
19
+ v-if="show && $slots.buttonLabelOnShow"
20
+ name="buttonLabelOnShow"
21
+ />
22
+ <slot
23
+ v-else
24
+ name="buttonLabel"
25
+ />
26
+ </button>
45
27
  </div>
28
+
29
+
30
+ <Teleport to="body">
31
+ <Transition name="fade">
32
+ <div
33
+ v-if="show"
34
+ ref="PSDropdownFloating"
35
+ role="menu"
36
+ class="psui-el-dropdown-menu-dialog-wrapper"
37
+ aria-orientation="vertical"
38
+ :style="floatingStyles"
39
+ v-click-outside="close"
40
+ >
41
+ <div class="psui-el-dropdown-menu-dialog">
42
+ <slot name="items" />
43
+ </div>
44
+ </div>
45
+ </Transition>
46
+ </Teleport>
46
47
  </div>
47
48
  </template>
48
49
 
49
50
  <script setup>
50
51
  // Figma - 2.3 Dropdown with category divider https://www.figma.com/file/Tto8hrNlSfuPcwd1pfqogF/%E2%9A%A1%EF%B8%8F-Design-System?node-id=1768%3A64886
51
-
52
- import { randomString, getParentScrollableEl } from '../../util/GeneralFunctions.js'
53
-
54
- import { ref, onBeforeUnmount } from 'vue'
55
-
56
- defineExpose({
57
- close:() => close(),
58
- open:() => open(),
59
- })
52
+ import {
53
+ useFloating,
54
+ autoUpdate,
55
+ offset,
56
+ flip,
57
+ shift,
58
+ size
59
+ } from '@floating-ui/vue'
60
+
61
+ import { ref, defineProps } from 'vue'
62
+ import Teleport from 'vue2-teleport'
60
63
 
61
64
  const props = defineProps({
62
- /**
63
- * It sets a minimun width for the dropdown menu.
64
- */
65
- minWidthDropDown: {
66
- type: String,
67
- default: '240px',
68
- },
69
- /**
70
- * It's a boolean responsible for showing a slot within the html tag button.
71
- */
72
- buttonLabelOnShow: {
73
- type: Boolean,
74
- default: false,
75
- },
76
- /**
77
- * It's a property responsible for toggling the dropdown menu. default: true.
78
- */
79
- toggleWhenActive: {
80
- type: Boolean,
81
- default: true,
82
- },
83
- /**
84
- * Disable the toogle on click. default: false.
85
- */
86
- disabled: {
87
- type: Boolean,
88
- default: false,
89
- },
90
- /**
91
- * It sets the vertical and horizontal position.
92
- */
93
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment, vue/require-default-prop
94
- position: {
95
- type: String,
96
- validator: (value) => ['custom'].includes(value),
97
- },
65
+ disabled: {type: Boolean, default: false},
66
+ placement: { type: String, default: 'bottom-start' },
67
+ offsetVal: { type: Number, default: 4 },
68
+ dropdownWidth: {
69
+ type: Number,
70
+ default: null
71
+ }
98
72
  })
99
73
 
100
74
  const emit = defineEmits(['open', 'close'])
101
75
 
102
76
  const show = ref(false)
103
- const id = ref(randomString(8))
104
- // const marginLeft = ref('-0px')
105
- const scrollableParentEl = ref(null)
106
- const PSDropdown = ref(null)
107
- const PSDropdownDialog = ref(null)
108
- const PSDropdownTrigger = ref(null)
109
-
110
- // const getMaxWidth = computed(() => {
111
- // let bounds = PSDropdown.getBoundingClientRect()
112
- // return document.documentElement.clientWidth - bounds['left'] - 30
113
- // })
114
77
 
115
- onBeforeUnmount(() => {
116
- unwatchParentScrolling()
78
+ const PSDropdownTrigger = ref(null)
79
+ const PSDropdownFloating = ref(null)
80
+
81
+ const { floatingStyles } = useFloating(PSDropdownTrigger, PSDropdownFloating, {
82
+ placement: props.placement,
83
+ whileElementsMounted: autoUpdate,
84
+ middleware: [
85
+ offset(props.offsetVal),
86
+ flip(),
87
+ shift(),
88
+ size({
89
+ apply({rects, elements}) {
90
+ const customWidth = props.dropdownWidth ?? rects.reference.width
91
+ Object.assign(elements.floating.style, {
92
+ width: `${customWidth}px`,
93
+ minWidth: 'fit-content'
94
+ })
95
+ },
96
+ })
97
+ ],
117
98
  })
118
99
 
119
- const toggle = (event) => {
120
- if (props.disabled) return
121
- if (!show.value) {
122
- open()
123
- } else {
124
- close()
125
- }
126
- event.stopPropagation()
127
- }
128
-
129
- const handleEsc = (evt) => {
130
- if (show.value && evt.keyCode === 27) close()
131
- }
132
-
133
100
  const open = () => {
134
- emit('open')
135
101
  show.value = true
136
- if (PSDropdownDialog.value) {
137
- PSDropdownDialog.value.style.opacity = 0
138
- PSDropdownDialog.value.style.display = 'block'
139
- }
140
- setTimeout(() => {
141
- updatePosition()
142
- watchParentScrolling()
143
- document.addEventListener('keyup', handleEsc)
144
- window.addEventListener('resize', updatePosition)
145
- // window.addEventListener('click', clickOutside)
146
- }, 10)
102
+ emit('open')
147
103
  }
148
104
 
149
105
  const close = () => {
150
- if (show.value == true) {
151
- emit('close')
152
- if (PSDropdownDialog.value !== null) {
153
- PSDropdownDialog.value.style.display = 'none'
154
- PSDropdownDialog.value.style.opacity = 0
155
- }
156
- show.value = false
157
- unwatchParentScrolling()
158
- }
159
- document.removeEventListener('keyup', handleEsc)
160
- document.removeEventListener('resize', updatePosition)
161
- // document.removeEventListener('click', clickOutside)
106
+ show.value = false
107
+ emit('close')
162
108
  }
163
109
 
164
- const watchParentScrolling = () => {
165
- scrollableParentEl.value = getParentScrollableEl(PSDropdown.value)
166
- if (scrollableParentEl.value) {
167
- scrollableParentEl.value.addEventListener('scroll', updatePosition)
168
- }
169
- }
170
-
171
- const unwatchParentScrolling = () => {
172
- if (scrollableParentEl.value) {
173
- scrollableParentEl.value.removeEventListener('scroll', updatePosition())
174
- }
175
- }
176
-
177
- const updatePosition = () => {
178
- if (PSDropdownDialog.value === null || PSDropdownTrigger.value === null) return
179
-
180
- const rectTrigger = PSDropdownTrigger.value.getBoundingClientRect()
181
- const rectDialog = PSDropdownDialog.value.getBoundingClientRect()
182
- const windowWidth = document.documentElement.clientWidth
183
-
184
- PSDropdownDialog.value.style.position = 'fixed'
185
- PSDropdownDialog.value.style.top = `${rectTrigger.y + rectTrigger.height}px`
186
- PSDropdownDialog.value.style.minWidth = `${rectTrigger.width}px`
187
-
188
- if (rectTrigger.x + rectDialog.width + 20 > windowWidth) {
189
- PSDropdownDialog.value.style.left = `${windowWidth - rectDialog.width - 30}px`
190
- } else {
191
- PSDropdownDialog.value.style.left = `${rectTrigger.x}px`
192
- }
193
-
194
- if (props.position == 'custom') {
195
- PSDropdownDialog.value.style.top = `${rectTrigger.y}px`
196
- PSDropdownDialog.value.style.left = `${rectTrigger.x + 100}px`
197
- }
110
+ const toggle = (event) => {
111
+ if (props.disabled) return
198
112
 
199
- if (rectTrigger.top < 10) {
200
- close()
201
- console.warn('The dropdown are too close from the top of the page')
202
- return
203
- }
113
+ if (event) event.stopPropagation()
204
114
 
205
- setTimeout(() => {
206
- if(PSDropdownDialog.value){
207
- PSDropdownDialog.value.style.opacity = 1
208
- }
209
- }, 10)
115
+ show.value ? close() : open()
116
+
210
117
  }
211
118
 
119
+ defineExpose({ open, close })
212
120
  </script>