js-kompo-utils 1.0.20 → 1.0.22

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.
@@ -63,6 +63,9 @@ export default {
63
63
  shouldShowMenu(visible) {
64
64
  if (visible) {
65
65
  this.updatePosition()
66
+ this.attachListeners()
67
+ } else {
68
+ this.detachListeners()
66
69
  }
67
70
  if (!visible && this.openOnClick) {
68
71
  this.open = false
@@ -147,11 +150,10 @@ export default {
147
150
  },
148
151
  mounted() {
149
152
  this.teleportMenu()
150
- this.setupListeners()
151
153
  },
152
154
  beforeDestroy() {
153
155
  this.cleanupMenu()
154
- this.removeListeners()
156
+ this.detachListeners()
155
157
  },
156
158
  methods:{
157
159
  teleportMenu() {
@@ -159,7 +161,7 @@ export default {
159
161
  const root = this.$refs.dropdownRoot
160
162
  if (!menu) return
161
163
 
162
- this.inModal = !!(root && root.closest('.kompoModal'))
164
+ this.inModal = !!(root && root.closest('.kompoModal, .kompoDrawer, .vl-sidebar-l'))
163
165
 
164
166
  // Transfer scroll styles from root before teleporting
165
167
  if (root && root.classList.contains('dropdown-with-scroll')) {
@@ -182,27 +184,19 @@ export default {
182
184
  clearTimeout(this._closeTimeout)
183
185
  }
184
186
  },
185
- setupListeners() {
186
- this._scrollHandler = () => {
187
- if (this.shouldShowMenu) {
188
- requestAnimationFrame(() => this.updatePosition())
189
- }
190
- }
191
- this._resizeHandler = () => {
192
- if (this.shouldShowMenu) {
193
- this.updatePosition()
194
- }
195
- }
187
+ attachListeners() {
188
+ if (this._listenersAttached) return
189
+ this._scrollHandler = () => requestAnimationFrame(() => this.updatePosition())
190
+ this._resizeHandler = () => this.updatePosition()
196
191
  window.addEventListener('scroll', this._scrollHandler, true)
197
192
  window.addEventListener('resize', this._resizeHandler, { passive: true })
193
+ this._listenersAttached = true
198
194
  },
199
- removeListeners() {
200
- if (this._scrollHandler) {
201
- window.removeEventListener('scroll', this._scrollHandler, true)
202
- }
203
- if (this._resizeHandler) {
204
- window.removeEventListener('resize', this._resizeHandler)
205
- }
195
+ detachListeners() {
196
+ if (!this._listenersAttached) return
197
+ window.removeEventListener('scroll', this._scrollHandler, true)
198
+ window.removeEventListener('resize', this._resizeHandler)
199
+ this._listenersAttached = false
206
200
  },
207
201
  updatePosition() {
208
202
  const root = this.$refs.dropdownRoot
@@ -3,7 +3,8 @@
3
3
  <vlTaggableInput
4
4
  v-click-out="blur"
5
5
  v-bind="$_taggableInputAttributes"
6
- v-on="$_taggableInputEvents">
6
+ v-on="$_taggableInputEvents"
7
+ ref="taggableInput">
7
8
  <template v-slot:prepend v-if="$_icon">
8
9
  <div v-bind="prependIcon" />
9
10
  </template>
@@ -21,11 +22,11 @@
21
22
  <div v-html="appendIcon"/>
22
23
  </template>
23
24
  </vlTaggableInput>
24
- <div class="vlOptions"
25
+ <div class="vlOptions vlOptionsTeleported"
25
26
  :key="optionsKey"
26
27
  ref="selectOptions"
27
28
  :style="optionsStyle"
28
- :class="{ 'vlOptionsFlipUp': flipUp }">
29
+ :class="{ 'vlOptionsFlipUp': flipUp, 'vlOptionsVisible': isFocused }">
29
30
  <template v-if="filteredOptions.length">
30
31
  <div v-for="(option,key) in filteredOptions" :key="option.value" :ref="key"
31
32
  class="vlOption"
@@ -58,6 +59,7 @@ export default {
58
59
  data() {
59
60
  return {
60
61
  flipUp: false,
62
+ panelStyle: null,
61
63
  }
62
64
  },
63
65
  computed: {
@@ -68,43 +70,98 @@ export default {
68
70
  return !!this.$_config('openUpward')
69
71
  },
70
72
  optionsStyle() {
71
- if (!this.flipUp) return {}
72
- return {
73
- top: 'auto',
74
- bottom: '100%',
75
- borderRadius: '5px 5px 0 0',
76
- }
73
+ if (!this.isFocused || !this.panelStyle) return { display: 'none' }
74
+ return this.panelStyle
77
75
  },
78
76
  },
79
77
  watch: {
80
78
  isFocused(focused) {
81
79
  if (focused) {
82
- this.$nextTick(() => this.computeFlip())
80
+ this.$nextTick(() => this.refresh())
81
+ this.attachListeners()
83
82
  } else {
84
83
  this.flipUp = false
84
+ this.panelStyle = null
85
+ this.detachListeners()
85
86
  }
86
87
  },
87
88
  filteredOptions() {
88
- if (this.isFocused) {
89
- this.$nextTick(() => this.computeFlip())
90
- }
89
+ if (this.isFocused) this.$nextTick(() => this.refresh())
91
90
  },
92
91
  },
92
+ mounted() {
93
+ this.teleportOptions()
94
+ },
95
+ beforeDestroy() {
96
+ this.cleanupOptions()
97
+ this.detachListeners()
98
+ },
93
99
  methods: {
94
- computeFlip() {
95
- if (this.forceUp) {
96
- this.flipUp = true
97
- return
100
+ teleportOptions() {
101
+ const opts = this.$refs.selectOptions
102
+ if (opts && opts.parentNode !== document.body) {
103
+ document.body.appendChild(opts)
104
+ }
105
+ },
106
+ cleanupOptions() {
107
+ const opts = this.$refs.selectOptions
108
+ if (opts && opts.parentNode === document.body) {
109
+ document.body.removeChild(opts)
98
110
  }
111
+ },
112
+ anchorEl() {
113
+ return this.$refs.taggableInput?.$el || this.$refs.input?.parentElement
114
+ },
115
+ attachListeners() {
116
+ if (this._listenersAttached) return
117
+ this._onScroll = (e) => {
118
+ const opts = this.$refs.selectOptions
119
+ if (opts && e.target instanceof Node && opts.contains(e.target)) return
120
+ this.blur()
121
+ }
122
+ this._onResize = () => this.refresh()
123
+ window.addEventListener('scroll', this._onScroll, true)
124
+ window.addEventListener('resize', this._onResize, { passive: true })
125
+ this._listenersAttached = true
126
+ },
127
+ detachListeners() {
128
+ if (!this._listenersAttached) return
129
+ window.removeEventListener('scroll', this._onScroll, true)
130
+ window.removeEventListener('resize', this._onResize)
131
+ this._listenersAttached = false
132
+ },
133
+ refresh() {
99
134
  const opts = this.$refs.selectOptions
100
- if (!opts) return
101
- const anchor = opts.parentElement
102
- if (!anchor) return
103
- const anchorRect = anchor.getBoundingClientRect()
104
- const optsHeight = opts.offsetHeight
105
- const spaceBelow = window.innerHeight - anchorRect.bottom
106
- const spaceAbove = anchorRect.top
107
- this.flipUp = spaceBelow < optsHeight && spaceAbove > spaceBelow
135
+ const anchor = this.anchorEl()
136
+ if (!opts || !anchor) return
137
+
138
+ const rect = anchor.getBoundingClientRect()
139
+ const inOverlay = !!anchor.closest('.kompoModal, .kompoDrawer, .vl-sidebar-l')
140
+ const baseStyle = {
141
+ position: 'fixed',
142
+ left: rect.left + 'px',
143
+ width: rect.width + 'px',
144
+ zIndex: inOverlay ? 5001 : 1000,
145
+ top: rect.bottom + 'px',
146
+ bottom: 'auto',
147
+ }
148
+ this.panelStyle = baseStyle
149
+
150
+ this.$nextTick(() => {
151
+ const optsHeight = opts.offsetHeight
152
+ const spaceBelow = window.innerHeight - rect.bottom
153
+ const spaceAbove = rect.top
154
+ const shouldFlip = this.forceUp || (spaceBelow < optsHeight && spaceAbove > spaceBelow)
155
+ this.flipUp = shouldFlip
156
+ if (shouldFlip) {
157
+ this.panelStyle = {
158
+ ...baseStyle,
159
+ top: 'auto',
160
+ bottom: (window.innerHeight - rect.top) + 'px',
161
+ borderRadius: '5px 5px 0 0',
162
+ }
163
+ }
164
+ })
108
165
  },
109
166
  },
110
167
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "js-kompo-utils",
3
- "version": "1.0.20",
3
+ "version": "1.0.22",
4
4
  "description": "",
5
5
  "main": "app.js",
6
6
  "scripts": {