@skyservice-developers/vue-dev-kit 1.5.9 → 1.5.11

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skyservice-developers/vue-dev-kit",
3
- "version": "1.5.9",
3
+ "version": "1.5.11",
4
4
  "description": "Vue 2 and Vue 3 developer toolkit - components and helpers",
5
5
  "type": "module",
6
6
  "main": "./dist/vue3/vue-dev-kit.cjs",
@@ -78,3 +78,7 @@ export function getParentStoreValue(key) {
78
78
  export function getParentWindowValue(key) {
79
79
  return requestFromParent('window', key)
80
80
  }
81
+
82
+ export function setParentLocalStorage(key, value) {
83
+ sendToParent({ type: 'setLocalStorage', key, value })
84
+ }
@@ -71,7 +71,7 @@
71
71
 
72
72
  <script>
73
73
  import { isInIframe } from '../../shared/utils/webviewCheck'
74
- import { getParentLocalStorage, sendToParent, setSenderId, getSenderId } from '../../shared/utils/parentBridge'
74
+ import { getParentLocalStorage, setParentLocalStorage, sendToParent, setSenderId, getSenderId } from '../../shared/utils/parentBridge'
75
75
 
76
76
  export default {
77
77
  name: 'Header',
@@ -150,8 +150,12 @@ export default {
150
150
  }
151
151
 
152
152
  // Set rocketMode
153
- getParentLocalStorage('rocketMode').then((value) => {
153
+ getParentLocalStorage('rocketMode').then(async (value) => {
154
154
  this.previousRocketMode = value
155
+ const existingFallback = await getParentLocalStorage('fallbackRocketMode')
156
+ if (existingFallback === null) {
157
+ setParentLocalStorage('fallbackRocketMode', value === true || value === 'true' ? 'true' : 'false')
158
+ }
155
159
  sendToParent({ type: 'setRocketMode', value: true })
156
160
  })
157
161
 
@@ -26,24 +26,49 @@
26
26
  </svg>
27
27
  </button>
28
28
 
29
- <div v-if="open" class="sky-select-dropdown">
30
- <div
31
- v-for="(option, idx) in normalizedOptions"
32
- :key="option.value"
33
- class="sky-select-option"
34
- :class="{
35
- 'sky-select-option-selected': option.value === value,
36
- 'sky-select-option-focused': idx === focusedIdx,
37
- }"
38
- @click="select(option)"
39
- @mouseenter="focusedIdx = idx"
40
- >
41
- <span>{{ option.label }}</span>
42
- <svg v-if="option.value === value" class="sky-select-check" viewBox="0 0 16 16" fill="none" aria-hidden="true">
43
- <path d="M3 8l4 4 6-6" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
44
- </svg>
29
+ <template v-if="open">
30
+ <portal v-if="teleport" to="sky-select-portal">
31
+ <div
32
+ class="sky-select-dropdown sky-select-dropdown-teleported"
33
+ :style="dropdownStyle"
34
+ >
35
+ <div
36
+ v-for="(option, idx) in normalizedOptions"
37
+ :key="option.value"
38
+ class="sky-select-option"
39
+ :class="{
40
+ 'sky-select-option-selected': option.value === value,
41
+ 'sky-select-option-focused': idx === focusedIdx,
42
+ }"
43
+ @click="select(option)"
44
+ @mouseenter="focusedIdx = idx"
45
+ >
46
+ <span>{{ option.label }}</span>
47
+ <svg v-if="option.value === value" class="sky-select-check" viewBox="0 0 16 16" fill="none" aria-hidden="true">
48
+ <path d="M3 8l4 4 6-6" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
49
+ </svg>
50
+ </div>
51
+ </div>
52
+ </portal>
53
+ <div v-else class="sky-select-dropdown">
54
+ <div
55
+ v-for="(option, idx) in normalizedOptions"
56
+ :key="option.value"
57
+ class="sky-select-option"
58
+ :class="{
59
+ 'sky-select-option-selected': option.value === value,
60
+ 'sky-select-option-focused': idx === focusedIdx,
61
+ }"
62
+ @click="select(option)"
63
+ @mouseenter="focusedIdx = idx"
64
+ >
65
+ <span>{{ option.label }}</span>
66
+ <svg v-if="option.value === value" class="sky-select-check" viewBox="0 0 16 16" fill="none" aria-hidden="true">
67
+ <path d="M3 8l4 4 6-6" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
68
+ </svg>
69
+ </div>
45
70
  </div>
46
- </div>
71
+ </template>
47
72
  </div>
48
73
  </template>
49
74
 
@@ -71,11 +96,16 @@ export default {
71
96
  type: Boolean,
72
97
  default: false,
73
98
  },
99
+ teleport: {
100
+ type: Boolean,
101
+ default: false,
102
+ },
74
103
  },
75
104
  data() {
76
105
  return {
77
106
  open: false,
78
107
  focusedIdx: -1,
108
+ dropdownStyle: {},
79
109
  };
80
110
  },
81
111
  computed: {
@@ -89,6 +119,17 @@ export default {
89
119
  },
90
120
  },
91
121
  methods: {
122
+ calcDropdownStyle() {
123
+ if (!this.teleport || !this.$refs.root) return;
124
+ const rect = this.$refs.root.getBoundingClientRect();
125
+ this.dropdownStyle = {
126
+ position: 'fixed',
127
+ top: `${rect.bottom + 4}px`,
128
+ left: `${rect.left}px`,
129
+ width: `${rect.width}px`,
130
+ zIndex: 'var(--sky-select-dropdown-z-index, 9999)',
131
+ };
132
+ },
92
133
  toggle() {
93
134
  if (this.disabled) return;
94
135
  this.open ? this.close() : this.openDropdown();
@@ -96,11 +137,18 @@ export default {
96
137
  openDropdown() {
97
138
  this.open = true;
98
139
  this.focusedIdx = this.normalizedOptions.findIndex((o) => o.value === this.value);
140
+ this.calcDropdownStyle();
99
141
  this.$nextTick(() => document.addEventListener('mousedown', this.onOutsideClick));
142
+ if (this.teleport) {
143
+ window.addEventListener('scroll', this.onScrollOrResize, true);
144
+ window.addEventListener('resize', this.onScrollOrResize);
145
+ }
100
146
  },
101
147
  close() {
102
148
  this.open = false;
103
149
  document.removeEventListener('mousedown', this.onOutsideClick);
150
+ window.removeEventListener('scroll', this.onScrollOrResize, true);
151
+ window.removeEventListener('resize', this.onScrollOrResize);
104
152
  },
105
153
  select(option) {
106
154
  this.$emit('input', option.value);
@@ -109,6 +157,9 @@ export default {
109
157
  onOutsideClick(e) {
110
158
  if (!this.$refs.root.contains(e.target)) this.close();
111
159
  },
160
+ onScrollOrResize() {
161
+ this.calcDropdownStyle();
162
+ },
112
163
  onKeydown(e) {
113
164
  if (!this.open) {
114
165
  if (e.key === 'Enter' || e.key === ' ') {
@@ -133,6 +184,8 @@ export default {
133
184
  },
134
185
  beforeDestroy() {
135
186
  document.removeEventListener('mousedown', this.onOutsideClick);
187
+ window.removeEventListener('scroll', this.onScrollOrResize, true);
188
+ window.removeEventListener('resize', this.onScrollOrResize);
136
189
  },
137
190
  };
138
191
  </script>
@@ -233,6 +286,17 @@ export default {
233
286
  padding: 4px;
234
287
  }
235
288
 
289
+ .sky-select-dropdown-teleported {
290
+ position: fixed;
291
+ background: var(--sky-select-dropdown-bg, #fff);
292
+ border: var(--sky-select-dropdown-border, 1px solid #d1d5db);
293
+ border-radius: var(--sky-select-dropdown-radius, 6px);
294
+ box-shadow: var(--sky-select-dropdown-shadow, 0 4px 12px rgba(0, 0, 0, 0.1));
295
+ max-height: var(--sky-select-dropdown-max-height, 220px);
296
+ overflow-y: auto;
297
+ padding: 4px;
298
+ }
299
+
236
300
  /* Option */
237
301
  .sky-select-option {
238
302
  display: flex;
@@ -80,7 +80,7 @@
80
80
  <script setup>
81
81
  import { ref, computed, onMounted, onUnmounted } from 'vue'
82
82
  import { isInIframe } from '../../shared/utils/webviewCheck'
83
- import { getParentLocalStorage, getParentWindowValue, sendToParent, setSenderId, getSenderId } from '../../shared/utils/parentBridge'
83
+ import { getParentLocalStorage, getParentWindowValue, setParentLocalStorage, sendToParent, setSenderId, getSenderId } from '../../shared/utils/parentBridge'
84
84
 
85
85
  const props = defineProps({
86
86
  title: {
@@ -153,8 +153,12 @@ if (isInIframe() && props.trackPageName) {
153
153
  const previousRocketMode = ref(null)
154
154
 
155
155
  if (isInIframe()) {
156
- getParentLocalStorage('rocketMode').then((value) => {
156
+ getParentLocalStorage('rocketMode').then(async (value) => {
157
157
  previousRocketMode.value = value
158
+ const existingFallback = await getParentLocalStorage('fallbackRocketMode')
159
+ if (existingFallback === null) {
160
+ setParentLocalStorage('fallbackRocketMode', value === true || value === 'true' ? 'true' : 'false')
161
+ }
158
162
  sendToParent({ type: 'setRocketMode', value: true })
159
163
  })
160
164
  }
@@ -21,34 +21,95 @@
21
21
  >
22
22
  {{ selectedOption ? selectedOption.label : placeholder }}
23
23
  </span>
24
- <svg class="sky-select-chevron" viewBox="0 0 16 16" fill="none" aria-hidden="true">
25
- <path d="M4 6l4 4 4-4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
24
+ <svg
25
+ class="sky-select-chevron"
26
+ viewBox="0 0 16 16"
27
+ fill="none"
28
+ aria-hidden="true"
29
+ >
30
+ <path
31
+ d="M4 6l4 4 4-4"
32
+ stroke="currentColor"
33
+ stroke-width="1.5"
34
+ stroke-linecap="round"
35
+ stroke-linejoin="round"
36
+ />
26
37
  </svg>
27
38
  </button>
28
39
 
29
- <div v-if="open" class="sky-select-dropdown">
30
- <div
31
- v-for="(option, idx) in normalizedOptions"
32
- :key="option.value"
33
- class="sky-select-option"
34
- :class="{
35
- 'sky-select-option-selected': option.value === modelValue,
36
- 'sky-select-option-focused': idx === focusedIdx,
37
- }"
38
- @click="select(option)"
39
- @mouseenter="focusedIdx = idx"
40
- >
41
- <span>{{ option.label }}</span>
42
- <svg v-if="option.value === modelValue" class="sky-select-check" viewBox="0 0 16 16" fill="none" aria-hidden="true">
43
- <path d="M3 8l4 4 6-6" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
44
- </svg>
40
+ <template v-if="open">
41
+ <Teleport v-if="teleport" to="body">
42
+ <div
43
+ ref="dropdownEl"
44
+ class="sky-select-dropdown-teleported"
45
+ :style="dropdownStyle"
46
+ >
47
+ <div
48
+ v-for="(option, idx) in normalizedOptions"
49
+ :key="option.value"
50
+ class="sky-select-option"
51
+ :class="{
52
+ 'sky-select-option-selected': option.value === modelValue,
53
+ 'sky-select-option-focused': idx === focusedIdx,
54
+ }"
55
+ @click="select(option)"
56
+ @mouseenter="focusedIdx = idx"
57
+ >
58
+ <span>{{ option.label }}</span>
59
+ <svg
60
+ v-if="option.value === modelValue"
61
+ class="sky-select-check"
62
+ viewBox="0 0 16 16"
63
+ fill="none"
64
+ aria-hidden="true"
65
+ >
66
+ <path
67
+ d="M3 8l4 4 6-6"
68
+ stroke="currentColor"
69
+ stroke-width="1.5"
70
+ stroke-linecap="round"
71
+ stroke-linejoin="round"
72
+ />
73
+ </svg>
74
+ </div>
75
+ </div>
76
+ </Teleport>
77
+ <div v-else class="sky-select-dropdown">
78
+ <div
79
+ v-for="(option, idx) in normalizedOptions"
80
+ :key="option.value"
81
+ class="sky-select-option"
82
+ :class="{
83
+ 'sky-select-option-selected': option.value === modelValue,
84
+ 'sky-select-option-focused': idx === focusedIdx,
85
+ }"
86
+ @click="select(option)"
87
+ @mouseenter="focusedIdx = idx"
88
+ >
89
+ <span>{{ option.label }}</span>
90
+ <svg
91
+ v-if="option.value === modelValue"
92
+ class="sky-select-check"
93
+ viewBox="0 0 16 16"
94
+ fill="none"
95
+ aria-hidden="true"
96
+ >
97
+ <path
98
+ d="M3 8l4 4 6-6"
99
+ stroke="currentColor"
100
+ stroke-width="1.5"
101
+ stroke-linecap="round"
102
+ stroke-linejoin="round"
103
+ />
104
+ </svg>
105
+ </div>
45
106
  </div>
46
- </div>
107
+ </template>
47
108
  </div>
48
109
  </template>
49
110
 
50
111
  <script setup>
51
- import { ref, computed, onBeforeUnmount } from 'vue';
112
+ import { ref, computed, onBeforeUnmount } from "vue";
52
113
 
53
114
  const props = defineProps({
54
115
  modelValue: {
@@ -60,7 +121,7 @@ const props = defineProps({
60
121
  },
61
122
  placeholder: {
62
123
  type: String,
63
- default: '',
124
+ default: "",
64
125
  },
65
126
  disabled: {
66
127
  type: Boolean,
@@ -70,24 +131,41 @@ const props = defineProps({
70
131
  type: Boolean,
71
132
  default: false,
72
133
  },
134
+ teleport: {
135
+ type: Boolean,
136
+ default: false,
137
+ },
73
138
  });
74
139
 
75
- const emit = defineEmits(['update:modelValue']);
140
+ const emit = defineEmits(["update:modelValue"]);
76
141
 
77
142
  const root = ref(null);
143
+ const dropdownEl = ref(null);
78
144
  const open = ref(false);
79
145
  const focusedIdx = ref(-1);
146
+ const dropdownStyle = ref({});
80
147
 
81
148
  const normalizedOptions = computed(() =>
82
149
  props.options.map((opt) =>
83
- typeof opt === 'string' ? { label: opt, value: opt } : opt
84
- )
150
+ typeof opt === "string" ? { label: opt, value: opt } : opt,
151
+ ),
85
152
  );
86
153
 
87
- const selectedOption = computed(() =>
88
- normalizedOptions.value.find((o) => o.value === props.modelValue) ?? null
154
+ const selectedOption = computed(
155
+ () =>
156
+ normalizedOptions.value.find((o) => o.value === props.modelValue) ?? null,
89
157
  );
90
158
 
159
+ function calcDropdownStyle() {
160
+ if (!props.teleport || !root.value) return;
161
+ const rect = root.value.getBoundingClientRect();
162
+ dropdownStyle.value = {
163
+ top: `${rect.bottom + 4}px`,
164
+ left: `${rect.left}px`,
165
+ width: `${rect.width}px`,
166
+ };
167
+ }
168
+
91
169
  function toggle() {
92
170
  if (props.disabled) return;
93
171
  open.value ? close() : openDropdown();
@@ -95,51 +173,86 @@ function toggle() {
95
173
 
96
174
  function openDropdown() {
97
175
  open.value = true;
98
- focusedIdx.value = normalizedOptions.value.findIndex((o) => o.value === props.modelValue);
99
- document.addEventListener('mousedown', onOutsideClick);
176
+ focusedIdx.value = normalizedOptions.value.findIndex(
177
+ (o) => o.value === props.modelValue,
178
+ );
179
+ calcDropdownStyle();
180
+ document.addEventListener("mousedown", onOutsideClick);
181
+ if (props.teleport) {
182
+ window.addEventListener("scroll", onScrollOrResize, true);
183
+ window.addEventListener("resize", onScrollOrResize);
184
+ }
100
185
  }
101
186
 
102
187
  function close() {
103
188
  open.value = false;
104
- document.removeEventListener('mousedown', onOutsideClick);
189
+ document.removeEventListener("mousedown", onOutsideClick);
190
+ window.removeEventListener("scroll", onScrollOrResize, true);
191
+ window.removeEventListener("resize", onScrollOrResize);
105
192
  }
106
193
 
107
194
  function select(option) {
108
- emit('update:modelValue', option.value);
195
+ emit("update:modelValue", option.value);
109
196
  close();
110
197
  }
111
198
 
112
199
  function onOutsideClick(e) {
113
- if (!root.value?.contains(e.target)) close();
200
+ if (root.value?.contains(e.target)) return;
201
+ if (dropdownEl.value?.contains(e.target)) return;
202
+ close();
203
+ }
204
+
205
+ function onScrollOrResize() {
206
+ calcDropdownStyle();
114
207
  }
115
208
 
116
209
  function onKeydown(e) {
117
210
  if (!open.value) {
118
- if (e.key === 'Enter' || e.key === ' ') {
211
+ if (e.key === "Enter" || e.key === " ") {
119
212
  e.preventDefault();
120
213
  openDropdown();
121
214
  }
122
215
  return;
123
216
  }
124
- if (e.key === 'Escape') {
217
+ if (e.key === "Escape") {
125
218
  close();
126
- } else if (e.key === 'ArrowDown') {
219
+ } else if (e.key === "ArrowDown") {
127
220
  e.preventDefault();
128
- focusedIdx.value = Math.min(focusedIdx.value + 1, normalizedOptions.value.length - 1);
129
- } else if (e.key === 'ArrowUp') {
221
+ focusedIdx.value = Math.min(
222
+ focusedIdx.value + 1,
223
+ normalizedOptions.value.length - 1,
224
+ );
225
+ } else if (e.key === "ArrowUp") {
130
226
  e.preventDefault();
131
227
  focusedIdx.value = Math.max(focusedIdx.value - 1, 0);
132
- } else if (e.key === 'Enter') {
228
+ } else if (e.key === "Enter") {
133
229
  e.preventDefault();
134
- if (focusedIdx.value >= 0) select(normalizedOptions.value[focusedIdx.value]);
230
+ if (focusedIdx.value >= 0)
231
+ select(normalizedOptions.value[focusedIdx.value]);
135
232
  }
136
233
  }
137
234
 
138
235
  onBeforeUnmount(() => {
139
- document.removeEventListener('mousedown', onOutsideClick);
236
+ document.removeEventListener("mousedown", onOutsideClick);
237
+ window.removeEventListener("scroll", onScrollOrResize, true);
238
+ window.removeEventListener("resize", onScrollOrResize);
140
239
  });
141
240
  </script>
142
241
 
242
+ <style>
243
+ .sky-select-dropdown-teleported {
244
+ position: fixed;
245
+ z-index: var(--sky-select-dropdown-z-index, 9999);
246
+ background: var(--sky-select-dropdown-bg, #fff);
247
+ border: var(--sky-select-dropdown-border, 1px solid #d1d5db);
248
+ border-radius: var(--sky-select-dropdown-radius, 6px);
249
+ box-shadow: var(--sky-select-dropdown-shadow, 0 4px 12px rgba(0, 0, 0, 0.1));
250
+ max-height: var(--sky-select-dropdown-max-height, 220px);
251
+ overflow-y: auto;
252
+ padding: 4px;
253
+ }
254
+ </style>
255
+
143
256
  <style scoped>
144
257
  .sky-select {
145
258
  position: relative;