js-kompo-utils 1.0.21 → 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() {
@@ -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,7 +59,7 @@ export default {
58
59
  data() {
59
60
  return {
60
61
  flipUp: false,
61
- overlayStyle: null,
62
+ panelStyle: null,
62
63
  }
63
64
  },
64
65
  computed: {
@@ -69,51 +70,98 @@ export default {
69
70
  return !!this.$_config('openUpward')
70
71
  },
71
72
  optionsStyle() {
72
- if (this.overlayStyle) return this.overlayStyle
73
- if (this.flipUp) {
74
- return { top: 'auto', bottom: '100%', borderRadius: '5px 5px 0 0' }
75
- }
76
- return {}
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
80
  this.$nextTick(() => this.refresh())
81
+ this.attachListeners()
83
82
  } else {
84
83
  this.flipUp = false
85
- this.overlayStyle = null
84
+ this.panelStyle = null
85
+ this.detachListeners()
86
86
  }
87
87
  },
88
88
  filteredOptions() {
89
89
  if (this.isFocused) this.$nextTick(() => this.refresh())
90
90
  },
91
91
  },
92
+ mounted() {
93
+ this.teleportOptions()
94
+ },
95
+ beforeDestroy() {
96
+ this.cleanupOptions()
97
+ this.detachListeners()
98
+ },
92
99
  methods: {
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)
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
+ },
93
133
  refresh() {
94
134
  const opts = this.$refs.selectOptions
95
- const anchor = opts && opts.parentElement
135
+ const anchor = this.anchorEl()
96
136
  if (!opts || !anchor) return
97
137
 
98
138
  const rect = anchor.getBoundingClientRect()
99
- const optsHeight = opts.offsetHeight
100
- const spaceBelow = window.innerHeight - rect.bottom
101
- const spaceAbove = rect.top
102
- this.flipUp = this.forceUp || (spaceBelow < optsHeight && spaceAbove > spaceBelow)
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
103
149
 
104
- if (opts.closest('.kompoModal, .kompoDrawer, .vl-sidebar-l')) {
105
- this.overlayStyle = {
106
- position: 'fixed',
107
- left: rect.left + 'px',
108
- width: rect.width + 'px',
109
- zIndex: 5001,
110
- ...(this.flipUp
111
- ? { top: 'auto', bottom: (window.innerHeight - rect.top) + 'px', borderRadius: '5px 5px 0 0' }
112
- : { top: rect.bottom + 'px', bottom: 'auto' }),
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
+ }
113
163
  }
114
- } else {
115
- this.overlayStyle = null
116
- }
164
+ })
117
165
  },
118
166
  },
119
167
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "js-kompo-utils",
3
- "version": "1.0.21",
3
+ "version": "1.0.22",
4
4
  "description": "",
5
5
  "main": "app.js",
6
6
  "scripts": {