@saasmakers/ui 2.0.80 → 2.0.82

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.
@@ -18,8 +18,6 @@ const visible = defineModel<boolean>({ default: false })
18
18
  const contentRef = ref<HTMLElement>()
19
19
  const panelRef = ref<HTMLElement>()
20
20
  const { release: onAfterLeave } = useDialog(visible, panelRef)
21
- const { isKeyboardOpen, keyboardInset } = useKeyboardInset()
22
- const skipLeaveSlide = ref(false)
23
21
 
24
22
  const drag = reactive({
25
23
  closing: false,
@@ -34,30 +32,6 @@ const closeThreshold = computed(() => {
34
32
  return (panelRef.value?.offsetHeight ?? 320) * closeThresholdRatio
35
33
  })
36
34
 
37
- // Use `bottom` (not transform) so keyboard lift does not fight drag-to-dismiss.
38
- const panelStyle = computed(() => {
39
- return {
40
- bottom: `${keyboardInset.value}px`,
41
- maxHeight: isKeyboardOpen.value
42
- ? `calc(100dvh - ${keyboardInset.value}px - env(safe-area-inset-top))`
43
- : undefined,
44
- transform: drag.closing ? 'translateY(100%)' : drag.offset > 0 ? `translateY(${drag.offset}px)` : undefined,
45
- }
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
-
61
35
  function onBackdropClick() {
62
36
  if (props.closeOnBackdrop) {
63
37
  onClose()
@@ -98,12 +72,6 @@ function onHeaderPointerDown(event: PointerEvent) {
98
72
  handle.setPointerCapture(event.pointerId)
99
73
  }
100
74
 
101
- function onLeaveComplete() {
102
- skipLeaveSlide.value = false
103
-
104
- onAfterLeave()
105
- }
106
-
107
75
  function onPanelTransitionEnd(event: TransitionEvent) {
108
76
  if (event.propertyName !== 'transform' || !drag.closing) {
109
77
  return
@@ -188,13 +156,6 @@ function onPointerUp(event: PointerEvent) {
188
156
  handle.releasePointerCapture(event.pointerId)
189
157
 
190
158
  if (drag.offset >= closeThreshold.value) {
191
- if (isKeyboardOpen.value) {
192
- onClose()
193
- resetDragState()
194
-
195
- return
196
- }
197
-
198
159
  drag.closing = true
199
160
 
200
161
  return
@@ -219,10 +180,6 @@ function resetPendingDrag() {
219
180
 
220
181
  watch(visible, (isVisible) => {
221
182
  if (!isVisible) {
222
- // iOS hides the keyboard before a leave slide would finish.
223
- skipLeaveSlide.value = isKeyboardOpen.value
224
-
225
- dismissKeyboard()
226
183
  resetDragState()
227
184
  }
228
185
  })
@@ -240,28 +197,28 @@ watch(visible, (isVisible) => {
240
197
  <ClientOnly>
241
198
  <Teleport to="body">
242
199
  <Transition
243
- :css="!skipLeaveSlide"
244
200
  enter-active-class="transition-transform duration-300 ease motion-reduce:transition-none motion-reduce:duration-0"
245
201
  enter-from-class="translate-y-full motion-reduce:translate-y-0"
246
202
  enter-to-class="translate-y-0"
247
203
  leave-active-class="transition-transform duration-300 ease motion-reduce:transition-none motion-reduce:duration-0"
248
204
  leave-from-class="translate-y-0"
249
205
  leave-to-class="translate-y-full motion-reduce:translate-y-0"
250
- @after-leave="onLeaveComplete"
206
+ @after-leave="onAfterLeave"
251
207
  >
252
208
  <div
253
209
  v-if="visible"
254
210
  ref="panelRef"
255
211
  :aria-label="props['aria-label']"
256
212
  aria-modal="true"
257
- class="layout-bottom-sheet fixed bottom-0 left-0 right-0 z-[60] mx-auto max-h-[85dvh] max-w-screen-md flex flex-col rounded-t-2xl bg-white text-gray-900 app-text shadow-lg outline-none dark:bg-gray-900 dark:text-gray-100"
213
+ class="layout-bottom-sheet fixed bottom-0 left-0 right-0 z-[60] mx-auto max-h-[85dvh] max-w-screen-md flex flex-col rounded-t-2xl bg-white text-gray-900 app-text shadow-lg outline-none safe-bottom dark:bg-gray-900 dark:text-gray-100"
258
214
  :class="{
259
- 'safe-bottom': !isKeyboardOpen,
260
215
  'transition-none motion-reduce:transition-none': drag.isDragging,
261
- 'transition-[bottom,max-height,transform] duration-250 ease-out motion-reduce:transition-none motion-reduce:duration-0': !drag.isDragging,
216
+ 'transition-transform duration-200 ease motion-reduce:transition-none motion-reduce:duration-0': !drag.isDragging && (drag.closing || drag.offset > 0),
262
217
  }"
263
- :style="panelStyle"
264
218
  role="dialog"
219
+ :style="{
220
+ transform: drag.closing ? 'translateY(100%)' : drag.offset > 0 ? `translateY(${drag.offset}px)` : undefined,
221
+ }"
265
222
  tabindex="-1"
266
223
  @transitionend="onPanelTransitionEnd"
267
224
  >
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saasmakers/ui",
3
- "version": "2.0.80",
3
+ "version": "2.0.82",
4
4
  "private": false,
5
5
  "description": "Reusable Nuxt UI components for SaaS Makers projects",
6
6
  "license": "MIT",
@@ -26,7 +26,6 @@
26
26
  ],
27
27
  "dependencies": {
28
28
  "@capacitor/core": "8.4.0",
29
- "@capacitor/keyboard": "8.0.6",
30
29
  "@capacitor/preferences": "8.0.1",
31
30
  "@capgo/capacitor-social-login": "8.3.22",
32
31
  "@nuxt/icon": "2.2.3",
@@ -1,46 +0,0 @@
1
- import type { PluginListenerHandle } from '@capacitor/core'
2
- import { Keyboard } from '@capacitor/keyboard'
3
-
4
- // Tracks the on-screen keyboard height on iOS Capacitor only.
5
- // With Keyboard.resize = 'none', the WebView does not shrink; consumers shift
6
- // fixed UI (e.g. bottom sheets) with this inset so it moves with the keyboard
7
- // animation instead of jumping after it finishes. Android keeps soft-input
8
- // resize, so applying the inset there would double-offset.
9
- export default function useKeyboardInset() {
10
- const { isIOS, isPluginAvailable } = useCapacitor()
11
- const keyboardInset = ref(0)
12
-
13
- const isKeyboardOpen = computed(() => {
14
- return keyboardInset.value > 0
15
- })
16
-
17
- const listenerHandles: PluginListenerHandle[] = []
18
-
19
- onMounted(async () => {
20
- if (!import.meta.client || !isIOS.value || !isPluginAvailable('Keyboard')) {
21
- return
22
- }
23
-
24
- listenerHandles.push(
25
- await Keyboard.addListener('keyboardWillShow', (info) => {
26
- keyboardInset.value = info.keyboardHeight
27
- }),
28
- await Keyboard.addListener('keyboardWillHide', () => {
29
- keyboardInset.value = 0
30
- }),
31
- )
32
- })
33
-
34
- onBeforeUnmount(() => {
35
- for (const handle of listenerHandles) {
36
- void handle.remove()
37
- }
38
-
39
- listenerHandles.length = 0
40
- })
41
-
42
- return {
43
- isKeyboardOpen,
44
- keyboardInset,
45
- }
46
- }
@@ -1,44 +0,0 @@
1
- // Always-mounted hidden input used to open the mobile keyboard within the user
2
- // gesture, before any async work (network create) breaks user activation.
3
- // Kept at module scope so every consumer shares the same singleton input.
4
- let warmupInput: HTMLInputElement | undefined
5
-
6
- export default function useKeyboardWarmup() {
7
- const { isDesktopBrowser } = useDevice()
8
-
9
- function ensureWarmupInput() {
10
- if (!import.meta.client || warmupInput) {
11
- return
12
- }
13
-
14
- const input = document.createElement('input')
15
-
16
- input.type = 'text'
17
- input.tabIndex = -1
18
-
19
- input.setAttribute('aria-hidden', 'true')
20
-
21
- input.style.cssText = 'position:fixed;top:0;left:0;height:1px;width:1px;opacity:0;pointer-events:none;'
22
-
23
- document.body.appendChild(input)
24
-
25
- warmupInput = input
26
- }
27
-
28
- // Must run synchronously inside a user gesture. Focusing a real text input
29
- // raises the on-screen keyboard; a later text-to-text focus move (to the
30
- // profile name field) keeps it open without needing a fresh gesture.
31
- function focusWarmupInput() {
32
- if (isDesktopBrowser.value) {
33
- return
34
- }
35
-
36
- ensureWarmupInput()
37
- warmupInput?.focus()
38
- }
39
-
40
- return {
41
- ensureWarmupInput,
42
- focusWarmupInput,
43
- }
44
- }