@saasmakers/ui 2.0.79 → 2.0.81

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.
@@ -46,10 +46,12 @@ const id = useId()
46
46
  const modelValue = defineModel<FieldInput['modelValue']>({ default: '' })
47
47
  const input = ref<HTMLInputElement>()
48
48
  const showPassword = ref(false)
49
- const { isDesktopBrowser } = useDevice()
50
49
 
50
+ // On mobile, a prior focusWarmupInput() already opened the keyboard in the
51
+ // user gesture; focusing here moves focus input→input so the keyboard stays
52
+ // open and typing lands in the visible field.
51
53
  onMounted(() => {
52
- if (props.autofocus && input.value && isDesktopBrowser.value) {
54
+ if (props.autofocus && input.value) {
53
55
  input.value.focus()
54
56
  }
55
57
  })
@@ -30,10 +30,11 @@ const emit = defineEmits<{
30
30
  const id = useId()
31
31
  const modelValue = defineModel<FieldTextarea['modelValue']>({ default: '' })
32
32
  const textarea = ref<HTMLTextAreaElement>()
33
- const { isDesktopBrowser } = useDevice()
34
33
 
34
+ // Same as FieldInput: on mobile, transfer focus from the keyboard warmup
35
+ // input so typing lands in the visible field while the keyboard stays open.
35
36
  onMounted(() => {
36
- if (props.autofocus && textarea.value && isDesktopBrowser.value) {
37
+ if (props.autofocus && textarea.value) {
37
38
  textarea.value.focus()
38
39
  }
39
40
  })
@@ -19,6 +19,7 @@ const contentRef = ref<HTMLElement>()
19
19
  const panelRef = ref<HTMLElement>()
20
20
  const { release: onAfterLeave } = useDialog(visible, panelRef)
21
21
  const { isKeyboardOpen, keyboardInset } = useKeyboardInset()
22
+ const skipLeaveSlide = ref(false)
22
23
 
23
24
  const drag = reactive({
24
25
  closing: false,
@@ -44,6 +45,19 @@ const panelStyle = computed(() => {
44
45
  }
45
46
  })
46
47
 
48
+ function dismissKeyboard() {
49
+ if (!import.meta.client) {
50
+ return
51
+ }
52
+
53
+ const activeElement = document.activeElement
54
+
55
+ // Blur instead of Keyboard.hide() so focus is cleared too (incl. the warmup input).
56
+ if (activeElement instanceof HTMLElement && activeElement !== document.body) {
57
+ activeElement.blur()
58
+ }
59
+ }
60
+
47
61
  function onBackdropClick() {
48
62
  if (props.closeOnBackdrop) {
49
63
  onClose()
@@ -84,6 +98,12 @@ function onHeaderPointerDown(event: PointerEvent) {
84
98
  handle.setPointerCapture(event.pointerId)
85
99
  }
86
100
 
101
+ function onLeaveComplete() {
102
+ skipLeaveSlide.value = false
103
+
104
+ onAfterLeave()
105
+ }
106
+
87
107
  function onPanelTransitionEnd(event: TransitionEvent) {
88
108
  if (event.propertyName !== 'transform' || !drag.closing) {
89
109
  return
@@ -168,6 +188,13 @@ function onPointerUp(event: PointerEvent) {
168
188
  handle.releasePointerCapture(event.pointerId)
169
189
 
170
190
  if (drag.offset >= closeThreshold.value) {
191
+ if (isKeyboardOpen.value) {
192
+ onClose()
193
+ resetDragState()
194
+
195
+ return
196
+ }
197
+
171
198
  drag.closing = true
172
199
 
173
200
  return
@@ -192,6 +219,10 @@ function resetPendingDrag() {
192
219
 
193
220
  watch(visible, (isVisible) => {
194
221
  if (!isVisible) {
222
+ // iOS hides the keyboard before a leave slide would finish.
223
+ skipLeaveSlide.value = isKeyboardOpen.value
224
+
225
+ dismissKeyboard()
195
226
  resetDragState()
196
227
  }
197
228
  })
@@ -209,13 +240,14 @@ watch(visible, (isVisible) => {
209
240
  <ClientOnly>
210
241
  <Teleport to="body">
211
242
  <Transition
243
+ :css="!skipLeaveSlide"
212
244
  enter-active-class="transition-transform duration-300 ease motion-reduce:transition-none motion-reduce:duration-0"
213
245
  enter-from-class="translate-y-full motion-reduce:translate-y-0"
214
246
  enter-to-class="translate-y-0"
215
247
  leave-active-class="transition-transform duration-300 ease motion-reduce:transition-none motion-reduce:duration-0"
216
248
  leave-from-class="translate-y-0"
217
249
  leave-to-class="translate-y-full motion-reduce:translate-y-0"
218
- @after-leave="onAfterLeave"
250
+ @after-leave="onLeaveComplete"
219
251
  >
220
252
  <div
221
253
  v-if="visible"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saasmakers/ui",
3
- "version": "2.0.79",
3
+ "version": "2.0.81",
4
4
  "private": false,
5
5
  "description": "Reusable Nuxt UI components for SaaS Makers projects",
6
6
  "license": "MIT",