@mieweb/ui 0.10.0-dev.17 → 0.10.0-dev.19
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/dist/{chunk-LENW5YOC.js → chunk-GBUVIGD5.js} +46 -17
- package/dist/chunk-GBUVIGD5.js.map +1 -0
- package/dist/{chunk-X2PPZJSL.cjs → chunk-LHBWRMNC.cjs} +46 -16
- package/dist/chunk-LHBWRMNC.cjs.map +1 -0
- package/dist/hooks/index.cjs +22 -18
- package/dist/hooks/index.d.cts +53 -2
- package/dist/hooks/index.d.ts +53 -2
- package/dist/hooks/index.js +1 -1
- package/dist/index.cjs +27 -23
- package/dist/index.d.cts +53 -2
- package/dist/index.d.ts +53 -2
- package/dist/index.js +2 -2
- package/package.json +3 -3
- package/dist/chunk-LENW5YOC.js.map +0 -1
- package/dist/chunk-X2PPZJSL.cjs.map +0 -1
|
@@ -36,15 +36,52 @@ var KEYBOARD_INSET_VAR = "--mieweb-keyboard-inset";
|
|
|
36
36
|
var VISUAL_VIEWPORT_HEIGHT_VAR = "--mieweb-visual-viewport-height";
|
|
37
37
|
var VISUAL_VIEWPORT_OFFSET_TOP_VAR = "--mieweb-visual-viewport-offset-top";
|
|
38
38
|
var KEYBOARD_OPEN_ATTRIBUTE = "data-keyboard-open";
|
|
39
|
+
var KEYBOARD_SOURCE_ATTRIBUTE = "data-keyboard-source";
|
|
39
40
|
var CLOSED = { keyboardInset: 0, isKeyboardOpen: false };
|
|
40
41
|
function useKeyboardInset(options = {}) {
|
|
41
|
-
const { enabled = true } = options;
|
|
42
|
+
const { enabled = true, source = "visual-viewport" } = options;
|
|
42
43
|
const [state, setState] = useState(CLOSED);
|
|
43
44
|
useEffect(() => {
|
|
44
45
|
if (!enabled || typeof window === "undefined") return;
|
|
46
|
+
const root = document.documentElement;
|
|
47
|
+
const publish = (keyboardInset) => {
|
|
48
|
+
const isKeyboardOpen = keyboardInset > 0;
|
|
49
|
+
root.style.setProperty(KEYBOARD_INSET_VAR, `${keyboardInset}px`);
|
|
50
|
+
root.toggleAttribute(KEYBOARD_OPEN_ATTRIBUTE, isKeyboardOpen);
|
|
51
|
+
setState(
|
|
52
|
+
(previous) => previous.keyboardInset === keyboardInset ? previous : { keyboardInset, isKeyboardOpen }
|
|
53
|
+
);
|
|
54
|
+
};
|
|
55
|
+
const clear = () => {
|
|
56
|
+
root.style.removeProperty(KEYBOARD_INSET_VAR);
|
|
57
|
+
root.style.removeProperty(VISUAL_VIEWPORT_HEIGHT_VAR);
|
|
58
|
+
root.style.removeProperty(VISUAL_VIEWPORT_OFFSET_TOP_VAR);
|
|
59
|
+
root.removeAttribute(KEYBOARD_OPEN_ATTRIBUTE);
|
|
60
|
+
root.removeAttribute(KEYBOARD_SOURCE_ATTRIBUTE);
|
|
61
|
+
setState(CLOSED);
|
|
62
|
+
};
|
|
63
|
+
if (source === "native") {
|
|
64
|
+
const handleWillShow = (event) => {
|
|
65
|
+
publish(
|
|
66
|
+
Math.max(
|
|
67
|
+
0,
|
|
68
|
+
Math.round(event.keyboardHeight ?? 0)
|
|
69
|
+
)
|
|
70
|
+
);
|
|
71
|
+
};
|
|
72
|
+
const handleWillHide = () => publish(0);
|
|
73
|
+
root.setAttribute(KEYBOARD_SOURCE_ATTRIBUTE, source);
|
|
74
|
+
publish(0);
|
|
75
|
+
window.addEventListener("keyboardWillShow", handleWillShow);
|
|
76
|
+
window.addEventListener("keyboardWillHide", handleWillHide);
|
|
77
|
+
return () => {
|
|
78
|
+
window.removeEventListener("keyboardWillShow", handleWillShow);
|
|
79
|
+
window.removeEventListener("keyboardWillHide", handleWillHide);
|
|
80
|
+
clear();
|
|
81
|
+
};
|
|
82
|
+
}
|
|
45
83
|
const viewport = window.visualViewport;
|
|
46
84
|
if (!viewport) return;
|
|
47
|
-
const root = document.documentElement;
|
|
48
85
|
let frame = 0;
|
|
49
86
|
const measure = () => {
|
|
50
87
|
frame = 0;
|
|
@@ -55,8 +92,6 @@ function useKeyboardInset(options = {}) {
|
|
|
55
92
|
window.innerHeight - viewport.height - viewport.offsetTop
|
|
56
93
|
)
|
|
57
94
|
);
|
|
58
|
-
const isKeyboardOpen = keyboardInset > 0;
|
|
59
|
-
root.style.setProperty(KEYBOARD_INSET_VAR, `${keyboardInset}px`);
|
|
60
95
|
root.style.setProperty(
|
|
61
96
|
VISUAL_VIEWPORT_HEIGHT_VAR,
|
|
62
97
|
`${zoomed ? window.innerHeight : Math.round(viewport.height)}px`
|
|
@@ -65,14 +100,12 @@ function useKeyboardInset(options = {}) {
|
|
|
65
100
|
VISUAL_VIEWPORT_OFFSET_TOP_VAR,
|
|
66
101
|
`${zoomed ? 0 : Math.round(viewport.offsetTop)}px`
|
|
67
102
|
);
|
|
68
|
-
|
|
69
|
-
setState(
|
|
70
|
-
(previous) => previous.keyboardInset === keyboardInset ? previous : { keyboardInset, isKeyboardOpen }
|
|
71
|
-
);
|
|
103
|
+
publish(keyboardInset);
|
|
72
104
|
};
|
|
73
105
|
const schedule = () => {
|
|
74
106
|
if (frame === 0) frame = requestAnimationFrame(measure);
|
|
75
107
|
};
|
|
108
|
+
root.setAttribute(KEYBOARD_SOURCE_ATTRIBUTE, source);
|
|
76
109
|
measure();
|
|
77
110
|
viewport.addEventListener("resize", schedule);
|
|
78
111
|
viewport.addEventListener("scroll", schedule);
|
|
@@ -80,13 +113,9 @@ function useKeyboardInset(options = {}) {
|
|
|
80
113
|
cancelAnimationFrame(frame);
|
|
81
114
|
viewport.removeEventListener("resize", schedule);
|
|
82
115
|
viewport.removeEventListener("scroll", schedule);
|
|
83
|
-
|
|
84
|
-
root.style.removeProperty(VISUAL_VIEWPORT_HEIGHT_VAR);
|
|
85
|
-
root.style.removeProperty(VISUAL_VIEWPORT_OFFSET_TOP_VAR);
|
|
86
|
-
root.removeAttribute(KEYBOARD_OPEN_ATTRIBUTE);
|
|
87
|
-
setState(CLOSED);
|
|
116
|
+
clear();
|
|
88
117
|
};
|
|
89
|
-
}, [enabled]);
|
|
118
|
+
}, [enabled, source]);
|
|
90
119
|
return state;
|
|
91
120
|
}
|
|
92
121
|
function useKeyboardShortcut(key, callback, options = {}) {
|
|
@@ -182,6 +211,6 @@ function useIsMobileOrTablet() {
|
|
|
182
211
|
return useMediaQuery("(max-width: 1023px)");
|
|
183
212
|
}
|
|
184
213
|
|
|
185
|
-
export { KEYBOARD_INSET_VAR, KEYBOARD_OPEN_ATTRIBUTE, RTL_LOCALES, VISUAL_VIEWPORT_HEIGHT_VAR, VISUAL_VIEWPORT_OFFSET_TOP_VAR, isRtlLocale, useCommandK, useDirection, useIsDesktop, useIsLargeDesktop, useIsMobile, useIsMobileOrTablet, useIsSmallTablet, useIsTablet, useKeyboardInset, useKeyboardShortcut, useMediaQuery };
|
|
186
|
-
//# sourceMappingURL=chunk-
|
|
187
|
-
//# sourceMappingURL=chunk-
|
|
214
|
+
export { KEYBOARD_INSET_VAR, KEYBOARD_OPEN_ATTRIBUTE, KEYBOARD_SOURCE_ATTRIBUTE, RTL_LOCALES, VISUAL_VIEWPORT_HEIGHT_VAR, VISUAL_VIEWPORT_OFFSET_TOP_VAR, isRtlLocale, useCommandK, useDirection, useIsDesktop, useIsLargeDesktop, useIsMobile, useIsMobileOrTablet, useIsSmallTablet, useIsTablet, useKeyboardInset, useKeyboardShortcut, useMediaQuery };
|
|
215
|
+
//# sourceMappingURL=chunk-GBUVIGD5.js.map
|
|
216
|
+
//# sourceMappingURL=chunk-GBUVIGD5.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/hooks/useDirection.ts","../src/hooks/useKeyboardInset.ts","../src/hooks/useKeyboardShortcut.ts","../src/hooks/useMediaQuery.ts"],"names":["useState","useEffect"],"mappings":"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","file":"chunk-GBUVIGD5.js","sourcesContent":["import { useState, useEffect } from 'react';\nimport type { RefObject } from 'react';\n\nexport type Direction = 'ltr' | 'rtl';\n\n/** Locales whose primary script reads right-to-left. */\nexport const RTL_LOCALES = ['ar', 'he', 'fa', 'ur'] as const;\n\n/**\n * Returns true when the given locale (e.g. `ar`, `ar-SA`, `he_IL`) uses a\n * right-to-left script.\n */\nexport function isRtlLocale(locale: string): boolean {\n const lang = locale.toLowerCase().split(/[-_]/)[0];\n return (RTL_LOCALES as readonly string[]).includes(lang);\n}\n\nfunction resolveDirection(element?: HTMLElement | null): Direction {\n if (typeof document === 'undefined') return 'ltr';\n const target = element ?? document.documentElement;\n return getComputedStyle(target).direction === 'rtl' ? 'rtl' : 'ltr';\n}\n\n/**\n * Hook returning the effective text direction (`ltr` | `rtl`) inherited from\n * the nearest `dir` attribute — zero setup for consumers who set\n * `<html dir=\"rtl\">`.\n *\n * Use for JS-level direction logic that CSS logical properties can't cover:\n * inverting ArrowLeft/ArrowRight keyboard handling, popover/toast placement,\n * drag math, etc.\n *\n * @param elementRef - Optional ref; when provided, direction is resolved from\n * that element (respecting any local `dir` override) instead of `<html>`.\n *\n * @example\n * ```tsx\n * const direction = useDirection();\n * const nextKey = direction === 'rtl' ? 'ArrowLeft' : 'ArrowRight';\n * ```\n */\nexport function useDirection(\n elementRef?: RefObject<HTMLElement | null>\n): Direction {\n const [direction, setDirection] = useState<Direction>(() =>\n resolveDirection(elementRef?.current)\n );\n\n useEffect(() => {\n const update = () => setDirection(resolveDirection(elementRef?.current));\n update();\n // dir changes are rare; observing the whole tree for just this attribute is cheap.\n const observer = new MutationObserver(update);\n observer.observe(document.documentElement, {\n attributes: true,\n attributeFilter: ['dir'],\n subtree: true,\n });\n return () => observer.disconnect();\n }, [elementRef]);\n\n // Re-sync when a re-render points elementRef.current at a different node:\n // the MutationObserver only fires on `dir` attribute mutations, so a swapped\n // ref target with a different inherited direction would otherwise go stale.\n const currentElement = elementRef?.current ?? null;\n useEffect(() => {\n setDirection(resolveDirection(currentElement));\n }, [currentElement]);\n\n return direction;\n}\n","import { useEffect, useState } from 'react';\n\n/** CSS custom properties written to `<html>` while the hook is mounted. */\nexport const KEYBOARD_INSET_VAR = '--mieweb-keyboard-inset';\nexport const VISUAL_VIEWPORT_HEIGHT_VAR = '--mieweb-visual-viewport-height';\nexport const VISUAL_VIEWPORT_OFFSET_TOP_VAR =\n '--mieweb-visual-viewport-offset-top';\n/** Boolean attribute set on `<html>` while an on-screen keyboard is open. */\nexport const KEYBOARD_OPEN_ATTRIBUTE = 'data-keyboard-open';\n/** Attribute on `<html>` naming the active {@link KeyboardInsetSource}. */\nexport const KEYBOARD_SOURCE_ATTRIBUTE = 'data-keyboard-source';\n\n/**\n * Where keyboard metrics come from.\n *\n * - `'visual-viewport'` — the Visual Viewport API. Works in any browser, but\n * iOS only reports the keyboard once its slide-in animation has finished.\n * - `'native'` — `keyboardWillShow` / `keyboardWillHide` window events with a\n * `keyboardHeight` property, as fired by `cordova-plugin-ionic-keyboard` and\n * Capacitor's Keyboard plugin. They arrive *before* the keyboard animates,\n * so layout can move in step with it.\n */\nexport type KeyboardInsetSource = 'visual-viewport' | 'native';\n\nexport interface KeyboardInsetState {\n /** Pixels of the layout viewport covered by the on-screen keyboard. */\n keyboardInset: number;\n /** Whether an on-screen keyboard currently covers part of the page. */\n isKeyboardOpen: boolean;\n}\n\nexport interface UseKeyboardInsetOptions {\n /** Track the keyboard; `false` stops tracking and clears the CSS variables. @default true */\n enabled?: boolean;\n /** Where keyboard metrics come from. @default 'visual-viewport' */\n source?: KeyboardInsetSource;\n}\n\ninterface NativeKeyboardEvent extends Event {\n keyboardHeight?: number;\n}\n\nconst CLOSED: KeyboardInsetState = { keyboardInset: 0, isKeyboardOpen: false };\n\n/**\n * Tracks the on-screen keyboard and publishes it on `<html>`.\n *\n * ### `source: 'native'` (Cordova / Capacitor apps)\n *\n * Listens for the native keyboard plugin's `keyboardWillShow` /\n * `keyboardWillHide` window events. Configure the plugin so the WebView is\n * neither resized nor panned (for `cordova-plugin-ionic-keyboard`:\n * `KeyboardResize=false`), keep the app shell at full height, and move only\n * the composer dock by `--mieweb-keyboard-inset` with a short transition\n * (~250ms) so it rides up with the keyboard. Only `--mieweb-keyboard-inset`,\n * `data-keyboard-open` and `data-keyboard-source` are published; the\n * visual-viewport variables stay unset.\n *\n * ### `source: 'visual-viewport'` (browsers, default)\n *\n * Uses the Visual Viewport API.\n *\n * iOS Safari and WKWebView (Cordova) ignore\n * `interactive-widget=resizes-content`, and `dvh` does not shrink for the\n * keyboard: the page keeps its full height and iOS pans the visual viewport\n * to reveal the focused input, pushing headers off-screen. This hook\n * measures what is actually visible and publishes it on `<html>` so an app\n * shell can follow the visible area instead:\n *\n * - `--mieweb-keyboard-inset` — keyboard height in px (0 when closed)\n * - `--mieweb-visual-viewport-height` — visible height in px\n * - `--mieweb-visual-viewport-offset-top` — how far iOS panned the page\n * - `data-keyboard-open` — present while the keyboard is open\n *\n * On browsers that already resize the layout for the keyboard (Chrome on\n * Android with `interactive-widget=resizes-content`) the inset stays 0 and\n * the variables simply mirror the layout viewport.\n *\n * iOS reports the visual viewport only after the keyboard has finished\n * animating, so a shell sized this way snaps into place once the keyboard\n * lands. Prefer `source: 'native'` wherever a native keyboard plugin exists.\n *\n * Mount it once, near the app root.\n *\n * @example\n * ```tsx\n * function AppShell({ children }) {\n * useKeyboardInset();\n * return (\n * <div\n * style={{\n * height: 'var(--mieweb-visual-viewport-height, 100dvh)',\n * transform: 'translateY(var(--mieweb-visual-viewport-offset-top, 0px))',\n * }}\n * >\n * {children}\n * </div>\n * );\n * }\n * ```\n *\n * @example Native app (keyboard plugin with WebView resizing disabled)\n * ```tsx\n * function ComposerDock({ children }) {\n * useKeyboardInset({ source: 'native' });\n * return (\n * <div\n * style={{\n * paddingBottom: 'calc(var(--mieweb-keyboard-inset, 0px) + 0.5rem)',\n * transition: 'padding-bottom 250ms ease',\n * }}\n * >\n * {children}\n * </div>\n * );\n * }\n * ```\n */\nexport function useKeyboardInset(\n options: UseKeyboardInsetOptions = {}\n): KeyboardInsetState {\n const { enabled = true, source = 'visual-viewport' } = options;\n const [state, setState] = useState<KeyboardInsetState>(CLOSED);\n\n useEffect(() => {\n if (!enabled || typeof window === 'undefined') return;\n const root = document.documentElement;\n\n const publish = (keyboardInset: number) => {\n const isKeyboardOpen = keyboardInset > 0;\n root.style.setProperty(KEYBOARD_INSET_VAR, `${keyboardInset}px`);\n root.toggleAttribute(KEYBOARD_OPEN_ATTRIBUTE, isKeyboardOpen);\n setState((previous) =>\n previous.keyboardInset === keyboardInset\n ? previous\n : { keyboardInset, isKeyboardOpen }\n );\n };\n\n const clear = () => {\n root.style.removeProperty(KEYBOARD_INSET_VAR);\n root.style.removeProperty(VISUAL_VIEWPORT_HEIGHT_VAR);\n root.style.removeProperty(VISUAL_VIEWPORT_OFFSET_TOP_VAR);\n root.removeAttribute(KEYBOARD_OPEN_ATTRIBUTE);\n root.removeAttribute(KEYBOARD_SOURCE_ATTRIBUTE);\n setState(CLOSED);\n };\n\n if (source === 'native') {\n const handleWillShow = (event: Event) => {\n publish(\n Math.max(\n 0,\n Math.round((event as NativeKeyboardEvent).keyboardHeight ?? 0)\n )\n );\n };\n const handleWillHide = () => publish(0);\n\n root.setAttribute(KEYBOARD_SOURCE_ATTRIBUTE, source);\n publish(0);\n window.addEventListener('keyboardWillShow', handleWillShow);\n window.addEventListener('keyboardWillHide', handleWillHide);\n\n return () => {\n window.removeEventListener('keyboardWillShow', handleWillShow);\n window.removeEventListener('keyboardWillHide', handleWillHide);\n clear();\n };\n }\n\n const viewport = window.visualViewport;\n if (!viewport) return;\n\n let frame = 0;\n\n const measure = () => {\n frame = 0;\n // Pinch-zoom shrinks the visual viewport too; that is not a keyboard.\n const zoomed = Math.abs(viewport.scale - 1) > 0.01;\n const keyboardInset = zoomed\n ? 0\n : Math.max(\n 0,\n Math.round(\n window.innerHeight - viewport.height - viewport.offsetTop\n )\n );\n\n root.style.setProperty(\n VISUAL_VIEWPORT_HEIGHT_VAR,\n `${zoomed ? window.innerHeight : Math.round(viewport.height)}px`\n );\n root.style.setProperty(\n VISUAL_VIEWPORT_OFFSET_TOP_VAR,\n `${zoomed ? 0 : Math.round(viewport.offsetTop)}px`\n );\n publish(keyboardInset);\n };\n\n // Coalesce the resize + scroll bursts iOS fires while the keyboard\n // animates into one write per frame.\n const schedule = () => {\n if (frame === 0) frame = requestAnimationFrame(measure);\n };\n\n root.setAttribute(KEYBOARD_SOURCE_ATTRIBUTE, source);\n measure();\n viewport.addEventListener('resize', schedule);\n viewport.addEventListener('scroll', schedule);\n\n return () => {\n cancelAnimationFrame(frame);\n viewport.removeEventListener('resize', schedule);\n viewport.removeEventListener('scroll', schedule);\n clear();\n };\n }, [enabled, source]);\n\n return state;\n}\n","import { useEffect, useCallback } from 'react';\n\n/**\n * Options for keyboard shortcut hook\n */\nexport interface KeyboardShortcutOptions {\n /** Whether the shortcut is currently enabled (default: true) */\n enabled?: boolean;\n /** Modifier keys required (default: none) */\n modifiers?: {\n ctrl?: boolean;\n shift?: boolean;\n alt?: boolean;\n meta?: boolean;\n };\n /** Whether to prevent default browser behavior (default: true) */\n preventDefault?: boolean;\n /** Whether to stop event propagation (default: false) */\n stopPropagation?: boolean;\n /** Element types to ignore when the shortcut is pressed (default: ['INPUT', 'TEXTAREA', 'SELECT']) */\n ignoreInputs?: boolean;\n}\n\n/**\n * Hook that triggers a callback when a specific keyboard shortcut is pressed.\n * Supports modifier keys (Ctrl, Shift, Alt, Meta) and ignores input fields by default.\n *\n * @param key - The key to listen for (e.g., 'k', 'Enter', 'Escape', '/')\n * @param callback - Function to call when the shortcut is pressed\n * @param options - Configuration options\n *\n * @example\n * ```tsx\n * // Cmd/Ctrl+K to open search\n * useKeyboardShortcut('k', openSearch, { modifiers: { meta: true, ctrl: true } });\n *\n * // Forward slash to focus search (ignoring when typing)\n * useKeyboardShortcut('/', focusSearch);\n *\n * // Escape to close modal\n * useKeyboardShortcut('Escape', closeModal);\n * ```\n */\nexport function useKeyboardShortcut(\n key: string,\n callback: (event: KeyboardEvent) => void,\n options: KeyboardShortcutOptions = {}\n): void {\n const {\n enabled = true,\n modifiers = {},\n preventDefault = true,\n stopPropagation = false,\n ignoreInputs = true,\n } = options;\n\n const handleKeyDown = useCallback(\n (event: KeyboardEvent) => {\n // Check if we should ignore input fields\n if (ignoreInputs) {\n const target = event.target as HTMLElement;\n const tagName = target.tagName.toUpperCase();\n if (\n tagName === 'INPUT' ||\n tagName === 'TEXTAREA' ||\n tagName === 'SELECT' ||\n target.isContentEditable\n ) {\n return;\n }\n }\n\n // Check modifiers\n const { ctrl, shift, alt, meta } = modifiers;\n\n // For Cmd+K style shortcuts, allow either meta (Mac) or ctrl (Windows/Linux)\n const metaOrCtrl = meta || ctrl;\n if (metaOrCtrl) {\n if (!(event.metaKey || event.ctrlKey)) return;\n } else {\n // If no meta/ctrl modifier specified, ensure neither is pressed\n if (event.metaKey || event.ctrlKey) return;\n }\n\n if (shift && !event.shiftKey) return;\n if (alt && !event.altKey) return;\n\n // Check key match (case-insensitive for letters)\n const eventKey =\n event.key.length === 1 ? event.key.toLowerCase() : event.key;\n const targetKey = key.length === 1 ? key.toLowerCase() : key;\n if (eventKey !== targetKey) return;\n\n if (preventDefault) {\n event.preventDefault();\n }\n if (stopPropagation) {\n event.stopPropagation();\n }\n\n callback(event);\n },\n [key, callback, modifiers, preventDefault, stopPropagation, ignoreInputs]\n );\n\n useEffect(() => {\n if (!enabled) return;\n\n document.addEventListener('keydown', handleKeyDown);\n return () => document.removeEventListener('keydown', handleKeyDown);\n }, [handleKeyDown, enabled]);\n}\n\n/**\n * Hook for the common Cmd/Ctrl+K shortcut pattern (command palette, search, etc.)\n *\n * @param callback - Function to call when Cmd/Ctrl+K is pressed\n * @param enabled - Whether the shortcut is active (default: true)\n *\n * @example\n * ```tsx\n * useCommandK(() => setIsOpen(true));\n * ```\n */\nexport function useCommandK(callback: () => void, enabled = true): void {\n useKeyboardShortcut('k', callback, {\n enabled,\n modifiers: { meta: true, ctrl: true },\n ignoreInputs: false, // Cmd+K should work even in inputs\n });\n}\n","import { useState, useEffect } from 'react';\n\n/**\n * Hook that tracks whether a media query matches.\n * Uses the native `matchMedia` API for efficient media query tracking.\n *\n * @param query - CSS media query string (e.g., '(min-width: 768px)')\n * @returns Boolean indicating whether the media query matches\n *\n * @example\n * ```tsx\n * function ResponsiveComponent() {\n * const isMobile = useMediaQuery('(max-width: 767px)');\n * const isTablet = useMediaQuery('(min-width: 768px) and (max-width: 1023px)');\n * const isDesktop = useMediaQuery('(min-width: 1024px)');\n *\n * return (\n * <div>\n * {isMobile && <MobileLayout />}\n * {isTablet && <TabletLayout />}\n * {isDesktop && <DesktopLayout />}\n * </div>\n * );\n * }\n * ```\n */\nexport function useMediaQuery(query: string): boolean {\n // Initialize with null to indicate SSR/initial state\n const [matches, setMatches] = useState<boolean>(() => {\n // Check if we're in a browser environment\n if (typeof window === 'undefined') return false;\n return window.matchMedia(query).matches;\n });\n\n useEffect(() => {\n if (typeof window === 'undefined') return;\n\n const mediaQuery = window.matchMedia(query);\n\n // Set initial value\n setMatches(mediaQuery.matches);\n\n // Handler for media query changes\n const handler = (event: MediaQueryListEvent) => {\n setMatches(event.matches);\n };\n\n // Modern browsers use addEventListener\n if (mediaQuery.addEventListener) {\n mediaQuery.addEventListener('change', handler);\n return () => mediaQuery.removeEventListener('change', handler);\n }\n\n // Fallback for older browsers\n mediaQuery.addListener(handler);\n return () => mediaQuery.removeListener(handler);\n }, [query]);\n\n return matches;\n}\n\n/**\n * Preset breakpoint hooks following common responsive design patterns\n */\n\n/** Returns true when viewport is smaller than 640px (mobile) */\nexport function useIsMobile(): boolean {\n return useMediaQuery('(max-width: 639px)');\n}\n\n/** Returns true when viewport is 640px-767px (large mobile / small tablet) */\nexport function useIsSmallTablet(): boolean {\n return useMediaQuery('(min-width: 640px) and (max-width: 767px)');\n}\n\n/** Returns true when viewport is 768px-1023px (tablet) */\nexport function useIsTablet(): boolean {\n return useMediaQuery('(min-width: 768px) and (max-width: 1023px)');\n}\n\n/** Returns true when viewport is 1024px or larger (desktop) */\nexport function useIsDesktop(): boolean {\n return useMediaQuery('(min-width: 1024px)');\n}\n\n/** Returns true when viewport is 1280px or larger (large desktop) */\nexport function useIsLargeDesktop(): boolean {\n return useMediaQuery('(min-width: 1280px)');\n}\n\n/** Returns true when viewport is smaller than 1024px (mobile/tablet) */\nexport function useIsMobileOrTablet(): boolean {\n return useMediaQuery('(max-width: 1023px)');\n}\n"]}
|
|
@@ -38,15 +38,52 @@ var KEYBOARD_INSET_VAR = "--mieweb-keyboard-inset";
|
|
|
38
38
|
var VISUAL_VIEWPORT_HEIGHT_VAR = "--mieweb-visual-viewport-height";
|
|
39
39
|
var VISUAL_VIEWPORT_OFFSET_TOP_VAR = "--mieweb-visual-viewport-offset-top";
|
|
40
40
|
var KEYBOARD_OPEN_ATTRIBUTE = "data-keyboard-open";
|
|
41
|
+
var KEYBOARD_SOURCE_ATTRIBUTE = "data-keyboard-source";
|
|
41
42
|
var CLOSED = { keyboardInset: 0, isKeyboardOpen: false };
|
|
42
43
|
function useKeyboardInset(options = {}) {
|
|
43
|
-
const { enabled = true } = options;
|
|
44
|
+
const { enabled = true, source = "visual-viewport" } = options;
|
|
44
45
|
const [state, setState] = react.useState(CLOSED);
|
|
45
46
|
react.useEffect(() => {
|
|
46
47
|
if (!enabled || typeof window === "undefined") return;
|
|
48
|
+
const root = document.documentElement;
|
|
49
|
+
const publish = (keyboardInset) => {
|
|
50
|
+
const isKeyboardOpen = keyboardInset > 0;
|
|
51
|
+
root.style.setProperty(KEYBOARD_INSET_VAR, `${keyboardInset}px`);
|
|
52
|
+
root.toggleAttribute(KEYBOARD_OPEN_ATTRIBUTE, isKeyboardOpen);
|
|
53
|
+
setState(
|
|
54
|
+
(previous) => previous.keyboardInset === keyboardInset ? previous : { keyboardInset, isKeyboardOpen }
|
|
55
|
+
);
|
|
56
|
+
};
|
|
57
|
+
const clear = () => {
|
|
58
|
+
root.style.removeProperty(KEYBOARD_INSET_VAR);
|
|
59
|
+
root.style.removeProperty(VISUAL_VIEWPORT_HEIGHT_VAR);
|
|
60
|
+
root.style.removeProperty(VISUAL_VIEWPORT_OFFSET_TOP_VAR);
|
|
61
|
+
root.removeAttribute(KEYBOARD_OPEN_ATTRIBUTE);
|
|
62
|
+
root.removeAttribute(KEYBOARD_SOURCE_ATTRIBUTE);
|
|
63
|
+
setState(CLOSED);
|
|
64
|
+
};
|
|
65
|
+
if (source === "native") {
|
|
66
|
+
const handleWillShow = (event) => {
|
|
67
|
+
publish(
|
|
68
|
+
Math.max(
|
|
69
|
+
0,
|
|
70
|
+
Math.round(event.keyboardHeight ?? 0)
|
|
71
|
+
)
|
|
72
|
+
);
|
|
73
|
+
};
|
|
74
|
+
const handleWillHide = () => publish(0);
|
|
75
|
+
root.setAttribute(KEYBOARD_SOURCE_ATTRIBUTE, source);
|
|
76
|
+
publish(0);
|
|
77
|
+
window.addEventListener("keyboardWillShow", handleWillShow);
|
|
78
|
+
window.addEventListener("keyboardWillHide", handleWillHide);
|
|
79
|
+
return () => {
|
|
80
|
+
window.removeEventListener("keyboardWillShow", handleWillShow);
|
|
81
|
+
window.removeEventListener("keyboardWillHide", handleWillHide);
|
|
82
|
+
clear();
|
|
83
|
+
};
|
|
84
|
+
}
|
|
47
85
|
const viewport = window.visualViewport;
|
|
48
86
|
if (!viewport) return;
|
|
49
|
-
const root = document.documentElement;
|
|
50
87
|
let frame = 0;
|
|
51
88
|
const measure = () => {
|
|
52
89
|
frame = 0;
|
|
@@ -57,8 +94,6 @@ function useKeyboardInset(options = {}) {
|
|
|
57
94
|
window.innerHeight - viewport.height - viewport.offsetTop
|
|
58
95
|
)
|
|
59
96
|
);
|
|
60
|
-
const isKeyboardOpen = keyboardInset > 0;
|
|
61
|
-
root.style.setProperty(KEYBOARD_INSET_VAR, `${keyboardInset}px`);
|
|
62
97
|
root.style.setProperty(
|
|
63
98
|
VISUAL_VIEWPORT_HEIGHT_VAR,
|
|
64
99
|
`${zoomed ? window.innerHeight : Math.round(viewport.height)}px`
|
|
@@ -67,14 +102,12 @@ function useKeyboardInset(options = {}) {
|
|
|
67
102
|
VISUAL_VIEWPORT_OFFSET_TOP_VAR,
|
|
68
103
|
`${zoomed ? 0 : Math.round(viewport.offsetTop)}px`
|
|
69
104
|
);
|
|
70
|
-
|
|
71
|
-
setState(
|
|
72
|
-
(previous) => previous.keyboardInset === keyboardInset ? previous : { keyboardInset, isKeyboardOpen }
|
|
73
|
-
);
|
|
105
|
+
publish(keyboardInset);
|
|
74
106
|
};
|
|
75
107
|
const schedule = () => {
|
|
76
108
|
if (frame === 0) frame = requestAnimationFrame(measure);
|
|
77
109
|
};
|
|
110
|
+
root.setAttribute(KEYBOARD_SOURCE_ATTRIBUTE, source);
|
|
78
111
|
measure();
|
|
79
112
|
viewport.addEventListener("resize", schedule);
|
|
80
113
|
viewport.addEventListener("scroll", schedule);
|
|
@@ -82,13 +115,9 @@ function useKeyboardInset(options = {}) {
|
|
|
82
115
|
cancelAnimationFrame(frame);
|
|
83
116
|
viewport.removeEventListener("resize", schedule);
|
|
84
117
|
viewport.removeEventListener("scroll", schedule);
|
|
85
|
-
|
|
86
|
-
root.style.removeProperty(VISUAL_VIEWPORT_HEIGHT_VAR);
|
|
87
|
-
root.style.removeProperty(VISUAL_VIEWPORT_OFFSET_TOP_VAR);
|
|
88
|
-
root.removeAttribute(KEYBOARD_OPEN_ATTRIBUTE);
|
|
89
|
-
setState(CLOSED);
|
|
118
|
+
clear();
|
|
90
119
|
};
|
|
91
|
-
}, [enabled]);
|
|
120
|
+
}, [enabled, source]);
|
|
92
121
|
return state;
|
|
93
122
|
}
|
|
94
123
|
function useKeyboardShortcut(key, callback, options = {}) {
|
|
@@ -186,6 +215,7 @@ function useIsMobileOrTablet() {
|
|
|
186
215
|
|
|
187
216
|
exports.KEYBOARD_INSET_VAR = KEYBOARD_INSET_VAR;
|
|
188
217
|
exports.KEYBOARD_OPEN_ATTRIBUTE = KEYBOARD_OPEN_ATTRIBUTE;
|
|
218
|
+
exports.KEYBOARD_SOURCE_ATTRIBUTE = KEYBOARD_SOURCE_ATTRIBUTE;
|
|
189
219
|
exports.RTL_LOCALES = RTL_LOCALES;
|
|
190
220
|
exports.VISUAL_VIEWPORT_HEIGHT_VAR = VISUAL_VIEWPORT_HEIGHT_VAR;
|
|
191
221
|
exports.VISUAL_VIEWPORT_OFFSET_TOP_VAR = VISUAL_VIEWPORT_OFFSET_TOP_VAR;
|
|
@@ -201,5 +231,5 @@ exports.useIsTablet = useIsTablet;
|
|
|
201
231
|
exports.useKeyboardInset = useKeyboardInset;
|
|
202
232
|
exports.useKeyboardShortcut = useKeyboardShortcut;
|
|
203
233
|
exports.useMediaQuery = useMediaQuery;
|
|
204
|
-
//# sourceMappingURL=chunk-
|
|
205
|
-
//# sourceMappingURL=chunk-
|
|
234
|
+
//# sourceMappingURL=chunk-LHBWRMNC.cjs.map
|
|
235
|
+
//# sourceMappingURL=chunk-LHBWRMNC.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/hooks/useDirection.ts","../src/hooks/useKeyboardInset.ts","../src/hooks/useKeyboardShortcut.ts","../src/hooks/useMediaQuery.ts"],"names":["useState","useEffect","useCallback"],"mappings":"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","file":"chunk-LHBWRMNC.cjs","sourcesContent":["import { useState, useEffect } from 'react';\nimport type { RefObject } from 'react';\n\nexport type Direction = 'ltr' | 'rtl';\n\n/** Locales whose primary script reads right-to-left. */\nexport const RTL_LOCALES = ['ar', 'he', 'fa', 'ur'] as const;\n\n/**\n * Returns true when the given locale (e.g. `ar`, `ar-SA`, `he_IL`) uses a\n * right-to-left script.\n */\nexport function isRtlLocale(locale: string): boolean {\n const lang = locale.toLowerCase().split(/[-_]/)[0];\n return (RTL_LOCALES as readonly string[]).includes(lang);\n}\n\nfunction resolveDirection(element?: HTMLElement | null): Direction {\n if (typeof document === 'undefined') return 'ltr';\n const target = element ?? document.documentElement;\n return getComputedStyle(target).direction === 'rtl' ? 'rtl' : 'ltr';\n}\n\n/**\n * Hook returning the effective text direction (`ltr` | `rtl`) inherited from\n * the nearest `dir` attribute — zero setup for consumers who set\n * `<html dir=\"rtl\">`.\n *\n * Use for JS-level direction logic that CSS logical properties can't cover:\n * inverting ArrowLeft/ArrowRight keyboard handling, popover/toast placement,\n * drag math, etc.\n *\n * @param elementRef - Optional ref; when provided, direction is resolved from\n * that element (respecting any local `dir` override) instead of `<html>`.\n *\n * @example\n * ```tsx\n * const direction = useDirection();\n * const nextKey = direction === 'rtl' ? 'ArrowLeft' : 'ArrowRight';\n * ```\n */\nexport function useDirection(\n elementRef?: RefObject<HTMLElement | null>\n): Direction {\n const [direction, setDirection] = useState<Direction>(() =>\n resolveDirection(elementRef?.current)\n );\n\n useEffect(() => {\n const update = () => setDirection(resolveDirection(elementRef?.current));\n update();\n // dir changes are rare; observing the whole tree for just this attribute is cheap.\n const observer = new MutationObserver(update);\n observer.observe(document.documentElement, {\n attributes: true,\n attributeFilter: ['dir'],\n subtree: true,\n });\n return () => observer.disconnect();\n }, [elementRef]);\n\n // Re-sync when a re-render points elementRef.current at a different node:\n // the MutationObserver only fires on `dir` attribute mutations, so a swapped\n // ref target with a different inherited direction would otherwise go stale.\n const currentElement = elementRef?.current ?? null;\n useEffect(() => {\n setDirection(resolveDirection(currentElement));\n }, [currentElement]);\n\n return direction;\n}\n","import { useEffect, useState } from 'react';\n\n/** CSS custom properties written to `<html>` while the hook is mounted. */\nexport const KEYBOARD_INSET_VAR = '--mieweb-keyboard-inset';\nexport const VISUAL_VIEWPORT_HEIGHT_VAR = '--mieweb-visual-viewport-height';\nexport const VISUAL_VIEWPORT_OFFSET_TOP_VAR =\n '--mieweb-visual-viewport-offset-top';\n/** Boolean attribute set on `<html>` while an on-screen keyboard is open. */\nexport const KEYBOARD_OPEN_ATTRIBUTE = 'data-keyboard-open';\n/** Attribute on `<html>` naming the active {@link KeyboardInsetSource}. */\nexport const KEYBOARD_SOURCE_ATTRIBUTE = 'data-keyboard-source';\n\n/**\n * Where keyboard metrics come from.\n *\n * - `'visual-viewport'` — the Visual Viewport API. Works in any browser, but\n * iOS only reports the keyboard once its slide-in animation has finished.\n * - `'native'` — `keyboardWillShow` / `keyboardWillHide` window events with a\n * `keyboardHeight` property, as fired by `cordova-plugin-ionic-keyboard` and\n * Capacitor's Keyboard plugin. They arrive *before* the keyboard animates,\n * so layout can move in step with it.\n */\nexport type KeyboardInsetSource = 'visual-viewport' | 'native';\n\nexport interface KeyboardInsetState {\n /** Pixels of the layout viewport covered by the on-screen keyboard. */\n keyboardInset: number;\n /** Whether an on-screen keyboard currently covers part of the page. */\n isKeyboardOpen: boolean;\n}\n\nexport interface UseKeyboardInsetOptions {\n /** Track the keyboard; `false` stops tracking and clears the CSS variables. @default true */\n enabled?: boolean;\n /** Where keyboard metrics come from. @default 'visual-viewport' */\n source?: KeyboardInsetSource;\n}\n\ninterface NativeKeyboardEvent extends Event {\n keyboardHeight?: number;\n}\n\nconst CLOSED: KeyboardInsetState = { keyboardInset: 0, isKeyboardOpen: false };\n\n/**\n * Tracks the on-screen keyboard and publishes it on `<html>`.\n *\n * ### `source: 'native'` (Cordova / Capacitor apps)\n *\n * Listens for the native keyboard plugin's `keyboardWillShow` /\n * `keyboardWillHide` window events. Configure the plugin so the WebView is\n * neither resized nor panned (for `cordova-plugin-ionic-keyboard`:\n * `KeyboardResize=false`), keep the app shell at full height, and move only\n * the composer dock by `--mieweb-keyboard-inset` with a short transition\n * (~250ms) so it rides up with the keyboard. Only `--mieweb-keyboard-inset`,\n * `data-keyboard-open` and `data-keyboard-source` are published; the\n * visual-viewport variables stay unset.\n *\n * ### `source: 'visual-viewport'` (browsers, default)\n *\n * Uses the Visual Viewport API.\n *\n * iOS Safari and WKWebView (Cordova) ignore\n * `interactive-widget=resizes-content`, and `dvh` does not shrink for the\n * keyboard: the page keeps its full height and iOS pans the visual viewport\n * to reveal the focused input, pushing headers off-screen. This hook\n * measures what is actually visible and publishes it on `<html>` so an app\n * shell can follow the visible area instead:\n *\n * - `--mieweb-keyboard-inset` — keyboard height in px (0 when closed)\n * - `--mieweb-visual-viewport-height` — visible height in px\n * - `--mieweb-visual-viewport-offset-top` — how far iOS panned the page\n * - `data-keyboard-open` — present while the keyboard is open\n *\n * On browsers that already resize the layout for the keyboard (Chrome on\n * Android with `interactive-widget=resizes-content`) the inset stays 0 and\n * the variables simply mirror the layout viewport.\n *\n * iOS reports the visual viewport only after the keyboard has finished\n * animating, so a shell sized this way snaps into place once the keyboard\n * lands. Prefer `source: 'native'` wherever a native keyboard plugin exists.\n *\n * Mount it once, near the app root.\n *\n * @example\n * ```tsx\n * function AppShell({ children }) {\n * useKeyboardInset();\n * return (\n * <div\n * style={{\n * height: 'var(--mieweb-visual-viewport-height, 100dvh)',\n * transform: 'translateY(var(--mieweb-visual-viewport-offset-top, 0px))',\n * }}\n * >\n * {children}\n * </div>\n * );\n * }\n * ```\n *\n * @example Native app (keyboard plugin with WebView resizing disabled)\n * ```tsx\n * function ComposerDock({ children }) {\n * useKeyboardInset({ source: 'native' });\n * return (\n * <div\n * style={{\n * paddingBottom: 'calc(var(--mieweb-keyboard-inset, 0px) + 0.5rem)',\n * transition: 'padding-bottom 250ms ease',\n * }}\n * >\n * {children}\n * </div>\n * );\n * }\n * ```\n */\nexport function useKeyboardInset(\n options: UseKeyboardInsetOptions = {}\n): KeyboardInsetState {\n const { enabled = true, source = 'visual-viewport' } = options;\n const [state, setState] = useState<KeyboardInsetState>(CLOSED);\n\n useEffect(() => {\n if (!enabled || typeof window === 'undefined') return;\n const root = document.documentElement;\n\n const publish = (keyboardInset: number) => {\n const isKeyboardOpen = keyboardInset > 0;\n root.style.setProperty(KEYBOARD_INSET_VAR, `${keyboardInset}px`);\n root.toggleAttribute(KEYBOARD_OPEN_ATTRIBUTE, isKeyboardOpen);\n setState((previous) =>\n previous.keyboardInset === keyboardInset\n ? previous\n : { keyboardInset, isKeyboardOpen }\n );\n };\n\n const clear = () => {\n root.style.removeProperty(KEYBOARD_INSET_VAR);\n root.style.removeProperty(VISUAL_VIEWPORT_HEIGHT_VAR);\n root.style.removeProperty(VISUAL_VIEWPORT_OFFSET_TOP_VAR);\n root.removeAttribute(KEYBOARD_OPEN_ATTRIBUTE);\n root.removeAttribute(KEYBOARD_SOURCE_ATTRIBUTE);\n setState(CLOSED);\n };\n\n if (source === 'native') {\n const handleWillShow = (event: Event) => {\n publish(\n Math.max(\n 0,\n Math.round((event as NativeKeyboardEvent).keyboardHeight ?? 0)\n )\n );\n };\n const handleWillHide = () => publish(0);\n\n root.setAttribute(KEYBOARD_SOURCE_ATTRIBUTE, source);\n publish(0);\n window.addEventListener('keyboardWillShow', handleWillShow);\n window.addEventListener('keyboardWillHide', handleWillHide);\n\n return () => {\n window.removeEventListener('keyboardWillShow', handleWillShow);\n window.removeEventListener('keyboardWillHide', handleWillHide);\n clear();\n };\n }\n\n const viewport = window.visualViewport;\n if (!viewport) return;\n\n let frame = 0;\n\n const measure = () => {\n frame = 0;\n // Pinch-zoom shrinks the visual viewport too; that is not a keyboard.\n const zoomed = Math.abs(viewport.scale - 1) > 0.01;\n const keyboardInset = zoomed\n ? 0\n : Math.max(\n 0,\n Math.round(\n window.innerHeight - viewport.height - viewport.offsetTop\n )\n );\n\n root.style.setProperty(\n VISUAL_VIEWPORT_HEIGHT_VAR,\n `${zoomed ? window.innerHeight : Math.round(viewport.height)}px`\n );\n root.style.setProperty(\n VISUAL_VIEWPORT_OFFSET_TOP_VAR,\n `${zoomed ? 0 : Math.round(viewport.offsetTop)}px`\n );\n publish(keyboardInset);\n };\n\n // Coalesce the resize + scroll bursts iOS fires while the keyboard\n // animates into one write per frame.\n const schedule = () => {\n if (frame === 0) frame = requestAnimationFrame(measure);\n };\n\n root.setAttribute(KEYBOARD_SOURCE_ATTRIBUTE, source);\n measure();\n viewport.addEventListener('resize', schedule);\n viewport.addEventListener('scroll', schedule);\n\n return () => {\n cancelAnimationFrame(frame);\n viewport.removeEventListener('resize', schedule);\n viewport.removeEventListener('scroll', schedule);\n clear();\n };\n }, [enabled, source]);\n\n return state;\n}\n","import { useEffect, useCallback } from 'react';\n\n/**\n * Options for keyboard shortcut hook\n */\nexport interface KeyboardShortcutOptions {\n /** Whether the shortcut is currently enabled (default: true) */\n enabled?: boolean;\n /** Modifier keys required (default: none) */\n modifiers?: {\n ctrl?: boolean;\n shift?: boolean;\n alt?: boolean;\n meta?: boolean;\n };\n /** Whether to prevent default browser behavior (default: true) */\n preventDefault?: boolean;\n /** Whether to stop event propagation (default: false) */\n stopPropagation?: boolean;\n /** Element types to ignore when the shortcut is pressed (default: ['INPUT', 'TEXTAREA', 'SELECT']) */\n ignoreInputs?: boolean;\n}\n\n/**\n * Hook that triggers a callback when a specific keyboard shortcut is pressed.\n * Supports modifier keys (Ctrl, Shift, Alt, Meta) and ignores input fields by default.\n *\n * @param key - The key to listen for (e.g., 'k', 'Enter', 'Escape', '/')\n * @param callback - Function to call when the shortcut is pressed\n * @param options - Configuration options\n *\n * @example\n * ```tsx\n * // Cmd/Ctrl+K to open search\n * useKeyboardShortcut('k', openSearch, { modifiers: { meta: true, ctrl: true } });\n *\n * // Forward slash to focus search (ignoring when typing)\n * useKeyboardShortcut('/', focusSearch);\n *\n * // Escape to close modal\n * useKeyboardShortcut('Escape', closeModal);\n * ```\n */\nexport function useKeyboardShortcut(\n key: string,\n callback: (event: KeyboardEvent) => void,\n options: KeyboardShortcutOptions = {}\n): void {\n const {\n enabled = true,\n modifiers = {},\n preventDefault = true,\n stopPropagation = false,\n ignoreInputs = true,\n } = options;\n\n const handleKeyDown = useCallback(\n (event: KeyboardEvent) => {\n // Check if we should ignore input fields\n if (ignoreInputs) {\n const target = event.target as HTMLElement;\n const tagName = target.tagName.toUpperCase();\n if (\n tagName === 'INPUT' ||\n tagName === 'TEXTAREA' ||\n tagName === 'SELECT' ||\n target.isContentEditable\n ) {\n return;\n }\n }\n\n // Check modifiers\n const { ctrl, shift, alt, meta } = modifiers;\n\n // For Cmd+K style shortcuts, allow either meta (Mac) or ctrl (Windows/Linux)\n const metaOrCtrl = meta || ctrl;\n if (metaOrCtrl) {\n if (!(event.metaKey || event.ctrlKey)) return;\n } else {\n // If no meta/ctrl modifier specified, ensure neither is pressed\n if (event.metaKey || event.ctrlKey) return;\n }\n\n if (shift && !event.shiftKey) return;\n if (alt && !event.altKey) return;\n\n // Check key match (case-insensitive for letters)\n const eventKey =\n event.key.length === 1 ? event.key.toLowerCase() : event.key;\n const targetKey = key.length === 1 ? key.toLowerCase() : key;\n if (eventKey !== targetKey) return;\n\n if (preventDefault) {\n event.preventDefault();\n }\n if (stopPropagation) {\n event.stopPropagation();\n }\n\n callback(event);\n },\n [key, callback, modifiers, preventDefault, stopPropagation, ignoreInputs]\n );\n\n useEffect(() => {\n if (!enabled) return;\n\n document.addEventListener('keydown', handleKeyDown);\n return () => document.removeEventListener('keydown', handleKeyDown);\n }, [handleKeyDown, enabled]);\n}\n\n/**\n * Hook for the common Cmd/Ctrl+K shortcut pattern (command palette, search, etc.)\n *\n * @param callback - Function to call when Cmd/Ctrl+K is pressed\n * @param enabled - Whether the shortcut is active (default: true)\n *\n * @example\n * ```tsx\n * useCommandK(() => setIsOpen(true));\n * ```\n */\nexport function useCommandK(callback: () => void, enabled = true): void {\n useKeyboardShortcut('k', callback, {\n enabled,\n modifiers: { meta: true, ctrl: true },\n ignoreInputs: false, // Cmd+K should work even in inputs\n });\n}\n","import { useState, useEffect } from 'react';\n\n/**\n * Hook that tracks whether a media query matches.\n * Uses the native `matchMedia` API for efficient media query tracking.\n *\n * @param query - CSS media query string (e.g., '(min-width: 768px)')\n * @returns Boolean indicating whether the media query matches\n *\n * @example\n * ```tsx\n * function ResponsiveComponent() {\n * const isMobile = useMediaQuery('(max-width: 767px)');\n * const isTablet = useMediaQuery('(min-width: 768px) and (max-width: 1023px)');\n * const isDesktop = useMediaQuery('(min-width: 1024px)');\n *\n * return (\n * <div>\n * {isMobile && <MobileLayout />}\n * {isTablet && <TabletLayout />}\n * {isDesktop && <DesktopLayout />}\n * </div>\n * );\n * }\n * ```\n */\nexport function useMediaQuery(query: string): boolean {\n // Initialize with null to indicate SSR/initial state\n const [matches, setMatches] = useState<boolean>(() => {\n // Check if we're in a browser environment\n if (typeof window === 'undefined') return false;\n return window.matchMedia(query).matches;\n });\n\n useEffect(() => {\n if (typeof window === 'undefined') return;\n\n const mediaQuery = window.matchMedia(query);\n\n // Set initial value\n setMatches(mediaQuery.matches);\n\n // Handler for media query changes\n const handler = (event: MediaQueryListEvent) => {\n setMatches(event.matches);\n };\n\n // Modern browsers use addEventListener\n if (mediaQuery.addEventListener) {\n mediaQuery.addEventListener('change', handler);\n return () => mediaQuery.removeEventListener('change', handler);\n }\n\n // Fallback for older browsers\n mediaQuery.addListener(handler);\n return () => mediaQuery.removeListener(handler);\n }, [query]);\n\n return matches;\n}\n\n/**\n * Preset breakpoint hooks following common responsive design patterns\n */\n\n/** Returns true when viewport is smaller than 640px (mobile) */\nexport function useIsMobile(): boolean {\n return useMediaQuery('(max-width: 639px)');\n}\n\n/** Returns true when viewport is 640px-767px (large mobile / small tablet) */\nexport function useIsSmallTablet(): boolean {\n return useMediaQuery('(min-width: 640px) and (max-width: 767px)');\n}\n\n/** Returns true when viewport is 768px-1023px (tablet) */\nexport function useIsTablet(): boolean {\n return useMediaQuery('(min-width: 768px) and (max-width: 1023px)');\n}\n\n/** Returns true when viewport is 1024px or larger (desktop) */\nexport function useIsDesktop(): boolean {\n return useMediaQuery('(min-width: 1024px)');\n}\n\n/** Returns true when viewport is 1280px or larger (large desktop) */\nexport function useIsLargeDesktop(): boolean {\n return useMediaQuery('(min-width: 1280px)');\n}\n\n/** Returns true when viewport is smaller than 1024px (mobile/tablet) */\nexport function useIsMobileOrTablet(): boolean {\n return useMediaQuery('(max-width: 1023px)');\n}\n"]}
|
package/dist/hooks/index.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkLHBWRMNC_cjs = require('../chunk-LHBWRMNC.cjs');
|
|
4
4
|
var chunkIXFC7LK2_cjs = require('../chunk-IXFC7LK2.cjs');
|
|
5
5
|
var chunk2O7D6F67_cjs = require('../chunk-2O7D6F67.cjs');
|
|
6
6
|
var chunkVPTASZ2O_cjs = require('../chunk-VPTASZ2O.cjs');
|
|
@@ -22,71 +22,75 @@ require('../chunk-EUAMEVVI.cjs');
|
|
|
22
22
|
|
|
23
23
|
Object.defineProperty(exports, "KEYBOARD_INSET_VAR", {
|
|
24
24
|
enumerable: true,
|
|
25
|
-
get: function () { return
|
|
25
|
+
get: function () { return chunkLHBWRMNC_cjs.KEYBOARD_INSET_VAR; }
|
|
26
26
|
});
|
|
27
27
|
Object.defineProperty(exports, "KEYBOARD_OPEN_ATTRIBUTE", {
|
|
28
28
|
enumerable: true,
|
|
29
|
-
get: function () { return
|
|
29
|
+
get: function () { return chunkLHBWRMNC_cjs.KEYBOARD_OPEN_ATTRIBUTE; }
|
|
30
|
+
});
|
|
31
|
+
Object.defineProperty(exports, "KEYBOARD_SOURCE_ATTRIBUTE", {
|
|
32
|
+
enumerable: true,
|
|
33
|
+
get: function () { return chunkLHBWRMNC_cjs.KEYBOARD_SOURCE_ATTRIBUTE; }
|
|
30
34
|
});
|
|
31
35
|
Object.defineProperty(exports, "RTL_LOCALES", {
|
|
32
36
|
enumerable: true,
|
|
33
|
-
get: function () { return
|
|
37
|
+
get: function () { return chunkLHBWRMNC_cjs.RTL_LOCALES; }
|
|
34
38
|
});
|
|
35
39
|
Object.defineProperty(exports, "VISUAL_VIEWPORT_HEIGHT_VAR", {
|
|
36
40
|
enumerable: true,
|
|
37
|
-
get: function () { return
|
|
41
|
+
get: function () { return chunkLHBWRMNC_cjs.VISUAL_VIEWPORT_HEIGHT_VAR; }
|
|
38
42
|
});
|
|
39
43
|
Object.defineProperty(exports, "VISUAL_VIEWPORT_OFFSET_TOP_VAR", {
|
|
40
44
|
enumerable: true,
|
|
41
|
-
get: function () { return
|
|
45
|
+
get: function () { return chunkLHBWRMNC_cjs.VISUAL_VIEWPORT_OFFSET_TOP_VAR; }
|
|
42
46
|
});
|
|
43
47
|
Object.defineProperty(exports, "isRtlLocale", {
|
|
44
48
|
enumerable: true,
|
|
45
|
-
get: function () { return
|
|
49
|
+
get: function () { return chunkLHBWRMNC_cjs.isRtlLocale; }
|
|
46
50
|
});
|
|
47
51
|
Object.defineProperty(exports, "useCommandK", {
|
|
48
52
|
enumerable: true,
|
|
49
|
-
get: function () { return
|
|
53
|
+
get: function () { return chunkLHBWRMNC_cjs.useCommandK; }
|
|
50
54
|
});
|
|
51
55
|
Object.defineProperty(exports, "useDirection", {
|
|
52
56
|
enumerable: true,
|
|
53
|
-
get: function () { return
|
|
57
|
+
get: function () { return chunkLHBWRMNC_cjs.useDirection; }
|
|
54
58
|
});
|
|
55
59
|
Object.defineProperty(exports, "useIsDesktop", {
|
|
56
60
|
enumerable: true,
|
|
57
|
-
get: function () { return
|
|
61
|
+
get: function () { return chunkLHBWRMNC_cjs.useIsDesktop; }
|
|
58
62
|
});
|
|
59
63
|
Object.defineProperty(exports, "useIsLargeDesktop", {
|
|
60
64
|
enumerable: true,
|
|
61
|
-
get: function () { return
|
|
65
|
+
get: function () { return chunkLHBWRMNC_cjs.useIsLargeDesktop; }
|
|
62
66
|
});
|
|
63
67
|
Object.defineProperty(exports, "useIsMobile", {
|
|
64
68
|
enumerable: true,
|
|
65
|
-
get: function () { return
|
|
69
|
+
get: function () { return chunkLHBWRMNC_cjs.useIsMobile; }
|
|
66
70
|
});
|
|
67
71
|
Object.defineProperty(exports, "useIsMobileOrTablet", {
|
|
68
72
|
enumerable: true,
|
|
69
|
-
get: function () { return
|
|
73
|
+
get: function () { return chunkLHBWRMNC_cjs.useIsMobileOrTablet; }
|
|
70
74
|
});
|
|
71
75
|
Object.defineProperty(exports, "useIsSmallTablet", {
|
|
72
76
|
enumerable: true,
|
|
73
|
-
get: function () { return
|
|
77
|
+
get: function () { return chunkLHBWRMNC_cjs.useIsSmallTablet; }
|
|
74
78
|
});
|
|
75
79
|
Object.defineProperty(exports, "useIsTablet", {
|
|
76
80
|
enumerable: true,
|
|
77
|
-
get: function () { return
|
|
81
|
+
get: function () { return chunkLHBWRMNC_cjs.useIsTablet; }
|
|
78
82
|
});
|
|
79
83
|
Object.defineProperty(exports, "useKeyboardInset", {
|
|
80
84
|
enumerable: true,
|
|
81
|
-
get: function () { return
|
|
85
|
+
get: function () { return chunkLHBWRMNC_cjs.useKeyboardInset; }
|
|
82
86
|
});
|
|
83
87
|
Object.defineProperty(exports, "useKeyboardShortcut", {
|
|
84
88
|
enumerable: true,
|
|
85
|
-
get: function () { return
|
|
89
|
+
get: function () { return chunkLHBWRMNC_cjs.useKeyboardShortcut; }
|
|
86
90
|
});
|
|
87
91
|
Object.defineProperty(exports, "useMediaQuery", {
|
|
88
92
|
enumerable: true,
|
|
89
|
-
get: function () { return
|
|
93
|
+
get: function () { return chunkLHBWRMNC_cjs.useMediaQuery; }
|
|
90
94
|
});
|
|
91
95
|
Object.defineProperty(exports, "useYouTubeHoverPreview", {
|
|
92
96
|
enumerable: true,
|
package/dist/hooks/index.d.cts
CHANGED
|
@@ -226,6 +226,19 @@ declare const VISUAL_VIEWPORT_HEIGHT_VAR = "--mieweb-visual-viewport-height";
|
|
|
226
226
|
declare const VISUAL_VIEWPORT_OFFSET_TOP_VAR = "--mieweb-visual-viewport-offset-top";
|
|
227
227
|
/** Boolean attribute set on `<html>` while an on-screen keyboard is open. */
|
|
228
228
|
declare const KEYBOARD_OPEN_ATTRIBUTE = "data-keyboard-open";
|
|
229
|
+
/** Attribute on `<html>` naming the active {@link KeyboardInsetSource}. */
|
|
230
|
+
declare const KEYBOARD_SOURCE_ATTRIBUTE = "data-keyboard-source";
|
|
231
|
+
/**
|
|
232
|
+
* Where keyboard metrics come from.
|
|
233
|
+
*
|
|
234
|
+
* - `'visual-viewport'` — the Visual Viewport API. Works in any browser, but
|
|
235
|
+
* iOS only reports the keyboard once its slide-in animation has finished.
|
|
236
|
+
* - `'native'` — `keyboardWillShow` / `keyboardWillHide` window events with a
|
|
237
|
+
* `keyboardHeight` property, as fired by `cordova-plugin-ionic-keyboard` and
|
|
238
|
+
* Capacitor's Keyboard plugin. They arrive *before* the keyboard animates,
|
|
239
|
+
* so layout can move in step with it.
|
|
240
|
+
*/
|
|
241
|
+
type KeyboardInsetSource = 'visual-viewport' | 'native';
|
|
229
242
|
interface KeyboardInsetState {
|
|
230
243
|
/** Pixels of the layout viewport covered by the on-screen keyboard. */
|
|
231
244
|
keyboardInset: number;
|
|
@@ -235,9 +248,26 @@ interface KeyboardInsetState {
|
|
|
235
248
|
interface UseKeyboardInsetOptions {
|
|
236
249
|
/** Track the keyboard; `false` stops tracking and clears the CSS variables. @default true */
|
|
237
250
|
enabled?: boolean;
|
|
251
|
+
/** Where keyboard metrics come from. @default 'visual-viewport' */
|
|
252
|
+
source?: KeyboardInsetSource;
|
|
238
253
|
}
|
|
239
254
|
/**
|
|
240
|
-
* Tracks the on-screen keyboard
|
|
255
|
+
* Tracks the on-screen keyboard and publishes it on `<html>`.
|
|
256
|
+
*
|
|
257
|
+
* ### `source: 'native'` (Cordova / Capacitor apps)
|
|
258
|
+
*
|
|
259
|
+
* Listens for the native keyboard plugin's `keyboardWillShow` /
|
|
260
|
+
* `keyboardWillHide` window events. Configure the plugin so the WebView is
|
|
261
|
+
* neither resized nor panned (for `cordova-plugin-ionic-keyboard`:
|
|
262
|
+
* `KeyboardResize=false`), keep the app shell at full height, and move only
|
|
263
|
+
* the composer dock by `--mieweb-keyboard-inset` with a short transition
|
|
264
|
+
* (~250ms) so it rides up with the keyboard. Only `--mieweb-keyboard-inset`,
|
|
265
|
+
* `data-keyboard-open` and `data-keyboard-source` are published; the
|
|
266
|
+
* visual-viewport variables stay unset.
|
|
267
|
+
*
|
|
268
|
+
* ### `source: 'visual-viewport'` (browsers, default)
|
|
269
|
+
*
|
|
270
|
+
* Uses the Visual Viewport API.
|
|
241
271
|
*
|
|
242
272
|
* iOS Safari and WKWebView (Cordova) ignore
|
|
243
273
|
* `interactive-widget=resizes-content`, and `dvh` does not shrink for the
|
|
@@ -255,6 +285,10 @@ interface UseKeyboardInsetOptions {
|
|
|
255
285
|
* Android with `interactive-widget=resizes-content`) the inset stays 0 and
|
|
256
286
|
* the variables simply mirror the layout viewport.
|
|
257
287
|
*
|
|
288
|
+
* iOS reports the visual viewport only after the keyboard has finished
|
|
289
|
+
* animating, so a shell sized this way snaps into place once the keyboard
|
|
290
|
+
* lands. Prefer `source: 'native'` wherever a native keyboard plugin exists.
|
|
291
|
+
*
|
|
258
292
|
* Mount it once, near the app root.
|
|
259
293
|
*
|
|
260
294
|
* @example
|
|
@@ -273,6 +307,23 @@ interface UseKeyboardInsetOptions {
|
|
|
273
307
|
* );
|
|
274
308
|
* }
|
|
275
309
|
* ```
|
|
310
|
+
*
|
|
311
|
+
* @example Native app (keyboard plugin with WebView resizing disabled)
|
|
312
|
+
* ```tsx
|
|
313
|
+
* function ComposerDock({ children }) {
|
|
314
|
+
* useKeyboardInset({ source: 'native' });
|
|
315
|
+
* return (
|
|
316
|
+
* <div
|
|
317
|
+
* style={{
|
|
318
|
+
* paddingBottom: 'calc(var(--mieweb-keyboard-inset, 0px) + 0.5rem)',
|
|
319
|
+
* transition: 'padding-bottom 250ms ease',
|
|
320
|
+
* }}
|
|
321
|
+
* >
|
|
322
|
+
* {children}
|
|
323
|
+
* </div>
|
|
324
|
+
* );
|
|
325
|
+
* }
|
|
326
|
+
* ```
|
|
276
327
|
*/
|
|
277
328
|
declare function useKeyboardInset(options?: UseKeyboardInsetOptions): KeyboardInsetState;
|
|
278
329
|
|
|
@@ -952,4 +1003,4 @@ interface UseTranscriptEditsResult {
|
|
|
952
1003
|
*/
|
|
953
1004
|
declare function useTranscriptEdits(options: UseTranscriptEditsOptions): UseTranscriptEditsResult;
|
|
954
1005
|
|
|
955
|
-
export { type AnchoredPlacement, DEFAULT_FILLER_WORDS, DEFAULT_MIN_SILENCE_MS, DEFAULT_NL_SILENCE_MS, type Direction, type DragOverState, type FillerAnalysis, type HoverPreviewState, KEYBOARD_INSET_VAR, KEYBOARD_OPEN_ATTRIBUTE, type KeyboardInsetState, type KeyboardShortcutOptions, type MediaTransportState, RTL_LOCALES, type ResolvedTheme, type SilenceThresholdCount, type Theme, type TranscriptClipboard, type TranscriptEditStats, type UseAnchoredPositionOptions, type UseAnchoredPositionReturn, type UseDragReorderOptions, type UseDragReorderReturn, type UseKeyboardInsetOptions, type UseMediaTransportOptions, type UseMediaTransportReturn, type UseScrollSpyOptions, type UseScrollSpyReturn, type UseStickToBottomOptions, type UseStickToBottomReturn, type UseTranscriptEditsOptions, type UseTranscriptEditsResult, type UseYouTubeHoverPreviewOptions, type UseYouTubeHoverPreviewReturn, VISUAL_VIEWPORT_HEIGHT_VAR, VISUAL_VIEWPORT_OFFSET_TOP_VAR, buildPlaybackSegments, dragIndicatorClasses, getSpeedAtIndex, initEditableWords, insertSilences, isDarkMode, isRtlLocale, reorderIds, useAnchoredPosition, useClickOutside, useCommandK, useDirection, useDragReorder, useEscapeKey, useFocusTrap, useIsDarkMode, useIsDesktop, useIsLargeDesktop, useIsMobile, useIsMobileOrTablet, useIsSmallTablet, useIsTablet, useKeyboardInset, useKeyboardShortcut, useLiveAnnouncement, useMediaQuery, useMediaTransport, usePrefersReducedMotion, useScrollSpy, useStickToBottom, useStreamEndedBelowFold, useTheme, useTranscriptEdits, useYouTubeHoverPreview };
|
|
1006
|
+
export { type AnchoredPlacement, DEFAULT_FILLER_WORDS, DEFAULT_MIN_SILENCE_MS, DEFAULT_NL_SILENCE_MS, type Direction, type DragOverState, type FillerAnalysis, type HoverPreviewState, KEYBOARD_INSET_VAR, KEYBOARD_OPEN_ATTRIBUTE, KEYBOARD_SOURCE_ATTRIBUTE, type KeyboardInsetSource, type KeyboardInsetState, type KeyboardShortcutOptions, type MediaTransportState, RTL_LOCALES, type ResolvedTheme, type SilenceThresholdCount, type Theme, type TranscriptClipboard, type TranscriptEditStats, type UseAnchoredPositionOptions, type UseAnchoredPositionReturn, type UseDragReorderOptions, type UseDragReorderReturn, type UseKeyboardInsetOptions, type UseMediaTransportOptions, type UseMediaTransportReturn, type UseScrollSpyOptions, type UseScrollSpyReturn, type UseStickToBottomOptions, type UseStickToBottomReturn, type UseTranscriptEditsOptions, type UseTranscriptEditsResult, type UseYouTubeHoverPreviewOptions, type UseYouTubeHoverPreviewReturn, VISUAL_VIEWPORT_HEIGHT_VAR, VISUAL_VIEWPORT_OFFSET_TOP_VAR, buildPlaybackSegments, dragIndicatorClasses, getSpeedAtIndex, initEditableWords, insertSilences, isDarkMode, isRtlLocale, reorderIds, useAnchoredPosition, useClickOutside, useCommandK, useDirection, useDragReorder, useEscapeKey, useFocusTrap, useIsDarkMode, useIsDesktop, useIsLargeDesktop, useIsMobile, useIsMobileOrTablet, useIsSmallTablet, useIsTablet, useKeyboardInset, useKeyboardShortcut, useLiveAnnouncement, useMediaQuery, useMediaTransport, usePrefersReducedMotion, useScrollSpy, useStickToBottom, useStreamEndedBelowFold, useTheme, useTranscriptEdits, useYouTubeHoverPreview };
|
package/dist/hooks/index.d.ts
CHANGED
|
@@ -226,6 +226,19 @@ declare const VISUAL_VIEWPORT_HEIGHT_VAR = "--mieweb-visual-viewport-height";
|
|
|
226
226
|
declare const VISUAL_VIEWPORT_OFFSET_TOP_VAR = "--mieweb-visual-viewport-offset-top";
|
|
227
227
|
/** Boolean attribute set on `<html>` while an on-screen keyboard is open. */
|
|
228
228
|
declare const KEYBOARD_OPEN_ATTRIBUTE = "data-keyboard-open";
|
|
229
|
+
/** Attribute on `<html>` naming the active {@link KeyboardInsetSource}. */
|
|
230
|
+
declare const KEYBOARD_SOURCE_ATTRIBUTE = "data-keyboard-source";
|
|
231
|
+
/**
|
|
232
|
+
* Where keyboard metrics come from.
|
|
233
|
+
*
|
|
234
|
+
* - `'visual-viewport'` — the Visual Viewport API. Works in any browser, but
|
|
235
|
+
* iOS only reports the keyboard once its slide-in animation has finished.
|
|
236
|
+
* - `'native'` — `keyboardWillShow` / `keyboardWillHide` window events with a
|
|
237
|
+
* `keyboardHeight` property, as fired by `cordova-plugin-ionic-keyboard` and
|
|
238
|
+
* Capacitor's Keyboard plugin. They arrive *before* the keyboard animates,
|
|
239
|
+
* so layout can move in step with it.
|
|
240
|
+
*/
|
|
241
|
+
type KeyboardInsetSource = 'visual-viewport' | 'native';
|
|
229
242
|
interface KeyboardInsetState {
|
|
230
243
|
/** Pixels of the layout viewport covered by the on-screen keyboard. */
|
|
231
244
|
keyboardInset: number;
|
|
@@ -235,9 +248,26 @@ interface KeyboardInsetState {
|
|
|
235
248
|
interface UseKeyboardInsetOptions {
|
|
236
249
|
/** Track the keyboard; `false` stops tracking and clears the CSS variables. @default true */
|
|
237
250
|
enabled?: boolean;
|
|
251
|
+
/** Where keyboard metrics come from. @default 'visual-viewport' */
|
|
252
|
+
source?: KeyboardInsetSource;
|
|
238
253
|
}
|
|
239
254
|
/**
|
|
240
|
-
* Tracks the on-screen keyboard
|
|
255
|
+
* Tracks the on-screen keyboard and publishes it on `<html>`.
|
|
256
|
+
*
|
|
257
|
+
* ### `source: 'native'` (Cordova / Capacitor apps)
|
|
258
|
+
*
|
|
259
|
+
* Listens for the native keyboard plugin's `keyboardWillShow` /
|
|
260
|
+
* `keyboardWillHide` window events. Configure the plugin so the WebView is
|
|
261
|
+
* neither resized nor panned (for `cordova-plugin-ionic-keyboard`:
|
|
262
|
+
* `KeyboardResize=false`), keep the app shell at full height, and move only
|
|
263
|
+
* the composer dock by `--mieweb-keyboard-inset` with a short transition
|
|
264
|
+
* (~250ms) so it rides up with the keyboard. Only `--mieweb-keyboard-inset`,
|
|
265
|
+
* `data-keyboard-open` and `data-keyboard-source` are published; the
|
|
266
|
+
* visual-viewport variables stay unset.
|
|
267
|
+
*
|
|
268
|
+
* ### `source: 'visual-viewport'` (browsers, default)
|
|
269
|
+
*
|
|
270
|
+
* Uses the Visual Viewport API.
|
|
241
271
|
*
|
|
242
272
|
* iOS Safari and WKWebView (Cordova) ignore
|
|
243
273
|
* `interactive-widget=resizes-content`, and `dvh` does not shrink for the
|
|
@@ -255,6 +285,10 @@ interface UseKeyboardInsetOptions {
|
|
|
255
285
|
* Android with `interactive-widget=resizes-content`) the inset stays 0 and
|
|
256
286
|
* the variables simply mirror the layout viewport.
|
|
257
287
|
*
|
|
288
|
+
* iOS reports the visual viewport only after the keyboard has finished
|
|
289
|
+
* animating, so a shell sized this way snaps into place once the keyboard
|
|
290
|
+
* lands. Prefer `source: 'native'` wherever a native keyboard plugin exists.
|
|
291
|
+
*
|
|
258
292
|
* Mount it once, near the app root.
|
|
259
293
|
*
|
|
260
294
|
* @example
|
|
@@ -273,6 +307,23 @@ interface UseKeyboardInsetOptions {
|
|
|
273
307
|
* );
|
|
274
308
|
* }
|
|
275
309
|
* ```
|
|
310
|
+
*
|
|
311
|
+
* @example Native app (keyboard plugin with WebView resizing disabled)
|
|
312
|
+
* ```tsx
|
|
313
|
+
* function ComposerDock({ children }) {
|
|
314
|
+
* useKeyboardInset({ source: 'native' });
|
|
315
|
+
* return (
|
|
316
|
+
* <div
|
|
317
|
+
* style={{
|
|
318
|
+
* paddingBottom: 'calc(var(--mieweb-keyboard-inset, 0px) + 0.5rem)',
|
|
319
|
+
* transition: 'padding-bottom 250ms ease',
|
|
320
|
+
* }}
|
|
321
|
+
* >
|
|
322
|
+
* {children}
|
|
323
|
+
* </div>
|
|
324
|
+
* );
|
|
325
|
+
* }
|
|
326
|
+
* ```
|
|
276
327
|
*/
|
|
277
328
|
declare function useKeyboardInset(options?: UseKeyboardInsetOptions): KeyboardInsetState;
|
|
278
329
|
|
|
@@ -952,4 +1003,4 @@ interface UseTranscriptEditsResult {
|
|
|
952
1003
|
*/
|
|
953
1004
|
declare function useTranscriptEdits(options: UseTranscriptEditsOptions): UseTranscriptEditsResult;
|
|
954
1005
|
|
|
955
|
-
export { type AnchoredPlacement, DEFAULT_FILLER_WORDS, DEFAULT_MIN_SILENCE_MS, DEFAULT_NL_SILENCE_MS, type Direction, type DragOverState, type FillerAnalysis, type HoverPreviewState, KEYBOARD_INSET_VAR, KEYBOARD_OPEN_ATTRIBUTE, type KeyboardInsetState, type KeyboardShortcutOptions, type MediaTransportState, RTL_LOCALES, type ResolvedTheme, type SilenceThresholdCount, type Theme, type TranscriptClipboard, type TranscriptEditStats, type UseAnchoredPositionOptions, type UseAnchoredPositionReturn, type UseDragReorderOptions, type UseDragReorderReturn, type UseKeyboardInsetOptions, type UseMediaTransportOptions, type UseMediaTransportReturn, type UseScrollSpyOptions, type UseScrollSpyReturn, type UseStickToBottomOptions, type UseStickToBottomReturn, type UseTranscriptEditsOptions, type UseTranscriptEditsResult, type UseYouTubeHoverPreviewOptions, type UseYouTubeHoverPreviewReturn, VISUAL_VIEWPORT_HEIGHT_VAR, VISUAL_VIEWPORT_OFFSET_TOP_VAR, buildPlaybackSegments, dragIndicatorClasses, getSpeedAtIndex, initEditableWords, insertSilences, isDarkMode, isRtlLocale, reorderIds, useAnchoredPosition, useClickOutside, useCommandK, useDirection, useDragReorder, useEscapeKey, useFocusTrap, useIsDarkMode, useIsDesktop, useIsLargeDesktop, useIsMobile, useIsMobileOrTablet, useIsSmallTablet, useIsTablet, useKeyboardInset, useKeyboardShortcut, useLiveAnnouncement, useMediaQuery, useMediaTransport, usePrefersReducedMotion, useScrollSpy, useStickToBottom, useStreamEndedBelowFold, useTheme, useTranscriptEdits, useYouTubeHoverPreview };
|
|
1006
|
+
export { type AnchoredPlacement, DEFAULT_FILLER_WORDS, DEFAULT_MIN_SILENCE_MS, DEFAULT_NL_SILENCE_MS, type Direction, type DragOverState, type FillerAnalysis, type HoverPreviewState, KEYBOARD_INSET_VAR, KEYBOARD_OPEN_ATTRIBUTE, KEYBOARD_SOURCE_ATTRIBUTE, type KeyboardInsetSource, type KeyboardInsetState, type KeyboardShortcutOptions, type MediaTransportState, RTL_LOCALES, type ResolvedTheme, type SilenceThresholdCount, type Theme, type TranscriptClipboard, type TranscriptEditStats, type UseAnchoredPositionOptions, type UseAnchoredPositionReturn, type UseDragReorderOptions, type UseDragReorderReturn, type UseKeyboardInsetOptions, type UseMediaTransportOptions, type UseMediaTransportReturn, type UseScrollSpyOptions, type UseScrollSpyReturn, type UseStickToBottomOptions, type UseStickToBottomReturn, type UseTranscriptEditsOptions, type UseTranscriptEditsResult, type UseYouTubeHoverPreviewOptions, type UseYouTubeHoverPreviewReturn, VISUAL_VIEWPORT_HEIGHT_VAR, VISUAL_VIEWPORT_OFFSET_TOP_VAR, buildPlaybackSegments, dragIndicatorClasses, getSpeedAtIndex, initEditableWords, insertSilences, isDarkMode, isRtlLocale, reorderIds, useAnchoredPosition, useClickOutside, useCommandK, useDirection, useDragReorder, useEscapeKey, useFocusTrap, useIsDarkMode, useIsDesktop, useIsLargeDesktop, useIsMobile, useIsMobileOrTablet, useIsSmallTablet, useIsTablet, useKeyboardInset, useKeyboardShortcut, useLiveAnnouncement, useMediaQuery, useMediaTransport, usePrefersReducedMotion, useScrollSpy, useStickToBottom, useStreamEndedBelowFold, useTheme, useTranscriptEdits, useYouTubeHoverPreview };
|
package/dist/hooks/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { KEYBOARD_INSET_VAR, KEYBOARD_OPEN_ATTRIBUTE, RTL_LOCALES, VISUAL_VIEWPORT_HEIGHT_VAR, VISUAL_VIEWPORT_OFFSET_TOP_VAR, isRtlLocale, useCommandK, useDirection, useIsDesktop, useIsLargeDesktop, useIsMobile, useIsMobileOrTablet, useIsSmallTablet, useIsTablet, useKeyboardInset, useKeyboardShortcut, useMediaQuery } from '../chunk-
|
|
1
|
+
export { KEYBOARD_INSET_VAR, KEYBOARD_OPEN_ATTRIBUTE, KEYBOARD_SOURCE_ATTRIBUTE, RTL_LOCALES, VISUAL_VIEWPORT_HEIGHT_VAR, VISUAL_VIEWPORT_OFFSET_TOP_VAR, isRtlLocale, useCommandK, useDirection, useIsDesktop, useIsLargeDesktop, useIsMobile, useIsMobileOrTablet, useIsSmallTablet, useIsTablet, useKeyboardInset, useKeyboardShortcut, useMediaQuery } from '../chunk-GBUVIGD5.js';
|
|
2
2
|
export { useYouTubeHoverPreview } from '../chunk-POWS35OT.js';
|
|
3
3
|
export { useTheme } from '../chunk-KJZNEVYM.js';
|
|
4
4
|
export { useStickToBottom, useStreamEndedBelowFold } from '../chunk-BM35CACB.js';
|