@passui/react 0.0.0-stage → 0.1.14
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/LICENSE +21 -0
- package/README.md +83 -2
- package/package.json +96 -5
- package/src/appearance/index.ts +2 -0
- package/src/appearance/platform.tsx +123 -0
- package/src/appearance/theme.tsx +535 -0
- package/src/components/ambient/ambient.docs.tsx +68 -0
- package/src/components/ambient/ambient.tsx +43 -0
- package/src/components/ambient/examples/ambient-images.tsx +30 -0
- package/src/components/ambient/examples/ambient-now-playing.tsx +55 -0
- package/src/components/ambient/examples/ambient-showcase.tsx +33 -0
- package/src/components/ambient/examples/ambient-usage.tsx +19 -0
- package/src/components/ambient/index.ts +1 -0
- package/src/components/button/button-styles.ts +60 -0
- package/src/components/button/button.docs.tsx +116 -0
- package/src/components/button/button.tsx +91 -0
- package/src/components/button/examples/button-disabled.tsx +19 -0
- package/src/components/button/examples/button-icon.tsx +39 -0
- package/src/components/button/examples/button-link.tsx +12 -0
- package/src/components/button/examples/button-pending.tsx +27 -0
- package/src/components/button/examples/button-showcase.tsx +115 -0
- package/src/components/button/examples/button-sizes.tsx +12 -0
- package/src/components/button/examples/button-usage.tsx +12 -0
- package/src/components/button/examples/button-variants.tsx +15 -0
- package/src/components/button/examples/button-with-icon.tsx +32 -0
- package/src/components/button/examples/icon-button-player.tsx +38 -0
- package/src/components/button/examples/icon-button-showcase.tsx +83 -0
- package/src/components/button/examples/icon-button-usage.tsx +11 -0
- package/src/components/button/examples/icon-button-variants.tsx +39 -0
- package/src/components/button/icon-button.docs.tsx +68 -0
- package/src/components/button/icon-button.tsx +73 -0
- package/src/components/button/pending-spinner.tsx +14 -0
- package/src/components/button/use-pending-indicator.ts +27 -0
- package/src/components/chip/chip.docs.tsx +37 -0
- package/src/components/chip/chip.tsx +35 -0
- package/src/components/chip/examples/chip-in-rows.tsx +19 -0
- package/src/components/chip/examples/chip-showcase.tsx +51 -0
- package/src/components/chip/examples/chip-tones.tsx +13 -0
- package/src/components/chip/examples/chip-usage.tsx +5 -0
- package/src/components/compact-date-picker/calendar-month.ts +131 -0
- package/src/components/compact-date-picker/compact-date-picker.docs.tsx +81 -0
- package/src/components/compact-date-picker/compact-date-picker.tsx +438 -0
- package/src/components/compact-date-picker/examples/compact-date-picker-showcase.tsx +35 -0
- package/src/components/compact-date-picker/examples/compact-date-picker-usage.tsx +22 -0
- package/src/components/compact-time-picker/compact-time-picker.docs.tsx +80 -0
- package/src/components/compact-time-picker/compact-time-picker.tsx +432 -0
- package/src/components/compact-time-picker/examples/compact-time-picker-showcase.tsx +31 -0
- package/src/components/compact-time-picker/examples/compact-time-picker-usage.tsx +21 -0
- package/src/components/compact-time-picker/time-value.ts +67 -0
- package/src/components/content-view/content-view.docs.tsx +61 -0
- package/src/components/content-view/content-view.tsx +197 -0
- package/src/components/content-view/examples/content-view-header-action.tsx +33 -0
- package/src/components/content-view/examples/content-view-showcase.tsx +65 -0
- package/src/components/content-view/examples/content-view-stats.tsx +35 -0
- package/src/components/content-view/examples/content-view-usage.tsx +14 -0
- package/src/components/content-view/index.ts +1 -0
- package/src/components/context-menu/conceal-menu-source.ts +33 -0
- package/src/components/context-menu/context-menu-host.tsx +445 -0
- package/src/components/context-menu/context-menu-store.ts +165 -0
- package/src/components/context-menu/context-menu.docs.tsx +92 -0
- package/src/components/context-menu/examples/context-menu-checked.tsx +37 -0
- package/src/components/context-menu/examples/context-menu-list.tsx +85 -0
- package/src/components/context-menu/examples/context-menu-showcase.tsx +56 -0
- package/src/components/context-menu/examples/context-menu-toolbar.tsx +63 -0
- package/src/components/context-menu/examples/context-menu-usage.tsx +24 -0
- package/src/components/context-menu/menu-position.ts +55 -0
- package/src/components/context-menu/menu-reveal.ts +75 -0
- package/src/components/context-menu/use-context-menu.ts +90 -0
- package/src/components/conversation/conversation.docs.tsx +88 -0
- package/src/components/conversation/conversation.tsx +279 -0
- package/src/components/conversation/examples/conversation-composer.tsx +36 -0
- package/src/components/conversation/examples/conversation-days.tsx +48 -0
- package/src/components/conversation/examples/conversation-showcase.tsx +71 -0
- package/src/components/conversation/examples/conversation-usage.tsx +28 -0
- package/src/components/conversation/index.ts +1 -0
- package/src/components/date-picker/date-picker.docs.tsx +88 -0
- package/src/components/date-picker/date-picker.tsx +103 -0
- package/src/components/date-picker/date-value.ts +37 -0
- package/src/components/date-picker/examples/date-picker-locales.tsx +41 -0
- package/src/components/date-picker/examples/date-picker-past-only.tsx +29 -0
- package/src/components/date-picker/examples/date-picker-showcase.tsx +49 -0
- package/src/components/date-picker/examples/date-picker-usage.tsx +22 -0
- package/src/components/dialog/dialog-host.tsx +156 -0
- package/src/components/dialog/dialog-store.ts +56 -0
- package/src/components/dialog/dialog.docs.tsx +114 -0
- package/src/components/dialog/dialog.tsx +200 -0
- package/src/components/dialog/examples/dialog-confirm.tsx +24 -0
- package/src/components/dialog/examples/dialog-form.tsx +46 -0
- package/src/components/dialog/examples/dialog-inline.tsx +48 -0
- package/src/components/dialog/examples/dialog-showcase.tsx +53 -0
- package/src/components/dialog/examples/dialog-usage.tsx +31 -0
- package/src/components/dialog/index.ts +1 -0
- package/src/components/labeled-content/examples/labeled-content-descriptions.tsx +37 -0
- package/src/components/labeled-content/examples/labeled-content-showcase.tsx +54 -0
- package/src/components/labeled-content/examples/labeled-content-usage.tsx +22 -0
- package/src/components/labeled-content/examples/labeled-content-values.tsx +26 -0
- package/src/components/labeled-content/examples/labeled-content-wide.tsx +46 -0
- package/src/components/labeled-content/labeled-content.docs.tsx +69 -0
- package/src/components/labeled-content/labeled-content.tsx +66 -0
- package/src/components/locale-switcher/examples/locale-switcher-native-names.tsx +30 -0
- package/src/components/locale-switcher/examples/locale-switcher-showcase.tsx +46 -0
- package/src/components/locale-switcher/examples/locale-switcher-translated-page.tsx +35 -0
- package/src/components/locale-switcher/examples/locale-switcher-usage.tsx +20 -0
- package/src/components/locale-switcher/index.ts +1 -0
- package/src/components/locale-switcher/locale-switcher.docs.tsx +91 -0
- package/src/components/locale-switcher/locale-switcher.tsx +252 -0
- package/src/components/picker/examples/picker-details.tsx +44 -0
- package/src/components/picker/examples/picker-searchable.tsx +48 -0
- package/src/components/picker/examples/picker-showcase.tsx +66 -0
- package/src/components/picker/examples/picker-usage.tsx +20 -0
- package/src/components/picker/picker.docs.tsx +68 -0
- package/src/components/picker/picker.tsx +493 -0
- package/src/components/popover/examples/popover-help.tsx +57 -0
- package/src/components/popover/examples/popover-menu.tsx +51 -0
- package/src/components/popover/examples/popover-showcase.tsx +61 -0
- package/src/components/popover/examples/popover-usage.tsx +31 -0
- package/src/components/popover/popover.docs.tsx +84 -0
- package/src/components/popover/popover.tsx +138 -0
- package/src/components/profile-image/examples/profile-image-fallbacks.tsx +29 -0
- package/src/components/profile-image/examples/profile-image-group.tsx +34 -0
- package/src/components/profile-image/examples/profile-image-showcase.tsx +30 -0
- package/src/components/profile-image/examples/profile-image-usage.tsx +14 -0
- package/src/components/profile-image/profile-image.docs.tsx +81 -0
- package/src/components/profile-image/profile-image.tsx +80 -0
- package/src/components/search-field/examples/search-field-filter.tsx +53 -0
- package/src/components/search-field/examples/search-field-showcase.tsx +56 -0
- package/src/components/search-field/examples/search-field-toolbar.tsx +27 -0
- package/src/components/search-field/examples/search-field-usage.tsx +18 -0
- package/src/components/search-field/search-field.docs.tsx +58 -0
- package/src/components/search-field/search-field.tsx +29 -0
- package/src/components/search-list/examples/search-list-dropdown.tsx +53 -0
- package/src/components/search-list/examples/search-list-groups.tsx +64 -0
- package/src/components/search-list/examples/search-list-showcase.tsx +67 -0
- package/src/components/search-list/examples/search-list-usage.tsx +27 -0
- package/src/components/search-list/search-list.docs.tsx +87 -0
- package/src/components/search-list/search-list.tsx +265 -0
- package/src/components/section/examples/section-actions.tsx +35 -0
- package/src/components/section/examples/section-groups.tsx +41 -0
- package/src/components/section/examples/section-showcase.tsx +63 -0
- package/src/components/section/examples/section-usage.tsx +19 -0
- package/src/components/section/section.docs.tsx +51 -0
- package/src/components/section/section.tsx +41 -0
- package/src/components/secure-field/examples/secure-field-new-password.tsx +56 -0
- package/src/components/secure-field/examples/secure-field-reveal.tsx +20 -0
- package/src/components/secure-field/examples/secure-field-showcase.tsx +62 -0
- package/src/components/secure-field/examples/secure-field-usage.tsx +19 -0
- package/src/components/secure-field/secure-field.docs.tsx +72 -0
- package/src/components/secure-field/secure-field.tsx +10 -0
- package/src/components/segmented-control/examples/segmented-control-overflow.tsx +28 -0
- package/src/components/segmented-control/examples/segmented-control-showcase.tsx +59 -0
- package/src/components/segmented-control/examples/segmented-control-usage.tsx +20 -0
- package/src/components/segmented-control/examples/segmented-control-views.tsx +32 -0
- package/src/components/segmented-control/segmented-control.docs.tsx +61 -0
- package/src/components/segmented-control/segmented-control.tsx +122 -0
- package/src/components/sheet/examples/sheet-choices.tsx +58 -0
- package/src/components/sheet/examples/sheet-share.tsx +51 -0
- package/src/components/sheet/examples/sheet-showcase.tsx +74 -0
- package/src/components/sheet/examples/sheet-usage.tsx +32 -0
- package/src/components/sheet/modal-environment.ts +113 -0
- package/src/components/sheet/sheet-portal.tsx +89 -0
- package/src/components/sheet/sheet.docs.tsx +75 -0
- package/src/components/sheet/sheet.tsx +112 -0
- package/src/components/sheet/use-sheet-focus.ts +130 -0
- package/src/components/sidebar/examples/sidebar-groups.tsx +133 -0
- package/src/components/sidebar/examples/sidebar-showcase.tsx +123 -0
- package/src/components/sidebar/examples/sidebar-unread.tsx +66 -0
- package/src/components/sidebar/examples/sidebar-usage.tsx +52 -0
- package/src/components/sidebar/sidebar.docs.tsx +135 -0
- package/src/components/sidebar/sidebar.tsx +69 -0
- package/src/components/stat-tile/examples/stat-tile-filters.tsx +32 -0
- package/src/components/stat-tile/examples/stat-tile-grid.tsx +21 -0
- package/src/components/stat-tile/examples/stat-tile-showcase.tsx +52 -0
- package/src/components/stat-tile/examples/stat-tile-usage.tsx +13 -0
- package/src/components/stat-tile/index.ts +1 -0
- package/src/components/stat-tile/stat-tile.docs.tsx +60 -0
- package/src/components/stat-tile/stat-tile.tsx +68 -0
- package/src/components/status-indicator/examples/status-indicator-live.tsx +34 -0
- package/src/components/status-indicator/examples/status-indicator-showcase.tsx +39 -0
- package/src/components/status-indicator/examples/status-indicator-states.tsx +17 -0
- package/src/components/status-indicator/examples/status-indicator-usage.tsx +9 -0
- package/src/components/status-indicator/status-indicator.docs.tsx +43 -0
- package/src/components/status-indicator/status-indicator.tsx +35 -0
- package/src/components/swatch-picker/examples/swatch-picker-accents.tsx +42 -0
- package/src/components/swatch-picker/examples/swatch-picker-labels.tsx +45 -0
- package/src/components/swatch-picker/examples/swatch-picker-showcase.tsx +70 -0
- package/src/components/swatch-picker/examples/swatch-picker-usage.tsx +39 -0
- package/src/components/swatch-picker/swatch-picker.docs.tsx +60 -0
- package/src/components/swatch-picker/swatch-picker.tsx +96 -0
- package/src/components/tab-bar/examples/tab-bar-five-tabs.tsx +66 -0
- package/src/components/tab-bar/examples/tab-bar-minimize.tsx +71 -0
- package/src/components/tab-bar/examples/tab-bar-showcase.tsx +82 -0
- package/src/components/tab-bar/examples/tab-bar-usage.tsx +54 -0
- package/src/components/tab-bar/tab-bar.docs.tsx +136 -0
- package/src/components/tab-bar/tab-bar.tsx +148 -0
- package/src/components/tab-view/examples/tab-view-keyboard.tsx +39 -0
- package/src/components/tab-view/examples/tab-view-settings.tsx +56 -0
- package/src/components/tab-view/examples/tab-view-showcase.tsx +53 -0
- package/src/components/tab-view/examples/tab-view-usage.tsx +23 -0
- package/src/components/tab-view/tab-view.docs.tsx +77 -0
- package/src/components/tab-view/tab-view.tsx +117 -0
- package/src/components/table/examples/table-empty.tsx +53 -0
- package/src/components/table/examples/table-invoices.tsx +102 -0
- package/src/components/table/examples/table-selection.tsx +150 -0
- package/src/components/table/examples/table-showcase.tsx +115 -0
- package/src/components/table/examples/table-usage.tsx +67 -0
- package/src/components/table/examples/table-wide.tsx +75 -0
- package/src/components/table/table.docs.tsx +131 -0
- package/src/components/table/table.tsx +149 -0
- package/src/components/text-editor/examples/text-editor-comment.tsx +48 -0
- package/src/components/text-editor/examples/text-editor-count.tsx +38 -0
- package/src/components/text-editor/examples/text-editor-showcase.tsx +58 -0
- package/src/components/text-editor/examples/text-editor-usage.tsx +25 -0
- package/src/components/text-editor/text-editor.docs.tsx +61 -0
- package/src/components/text-editor/text-editor.tsx +19 -0
- package/src/components/text-field/examples/text-field-icons.tsx +72 -0
- package/src/components/text-field/examples/text-field-showcase.tsx +50 -0
- package/src/components/text-field/examples/text-field-usage.tsx +18 -0
- package/src/components/text-field/examples/text-field-validation.tsx +27 -0
- package/src/components/text-field/text-field.docs.tsx +118 -0
- package/src/components/text-field/text-field.tsx +135 -0
- package/src/components/toast/examples/toast-action.tsx +32 -0
- package/src/components/toast/examples/toast-promise.tsx +45 -0
- package/src/components/toast/examples/toast-showcase.tsx +49 -0
- package/src/components/toast/examples/toast-usage.tsx +13 -0
- package/src/components/toast/toast-store.ts +166 -0
- package/src/components/toast/toast.docs.tsx +79 -0
- package/src/components/toast/toaster.tsx +374 -0
- package/src/components/toggle/examples/toggle-checkboxes.tsx +43 -0
- package/src/components/toggle/examples/toggle-settings.tsx +31 -0
- package/src/components/toggle/examples/toggle-showcase.tsx +59 -0
- package/src/components/toggle/examples/toggle-usage.tsx +13 -0
- package/src/components/toggle/toggle.docs.tsx +69 -0
- package/src/components/toggle/toggle.tsx +85 -0
- package/src/components/tooltip/examples/tooltip-icon-buttons.tsx +27 -0
- package/src/components/tooltip/examples/tooltip-shortcuts.tsx +38 -0
- package/src/components/tooltip/examples/tooltip-showcase.tsx +44 -0
- package/src/components/tooltip/examples/tooltip-usage.tsx +13 -0
- package/src/components/tooltip/tooltip-host.tsx +156 -0
- package/src/components/tooltip/tooltip.docs.tsx +56 -0
- package/src/components/view-controller/examples/view-controller-action.tsx +27 -0
- package/src/components/view-controller/examples/view-controller-settings.tsx +31 -0
- package/src/components/view-controller/examples/view-controller-showcase.tsx +45 -0
- package/src/components/view-controller/examples/view-controller-usage.tsx +19 -0
- package/src/components/view-controller/index.ts +1 -0
- package/src/components/view-controller/view-controller.docs.tsx +71 -0
- package/src/components/view-controller/view-controller.tsx +81 -0
- package/src/docs/index.ts +3 -0
- package/src/docs/registry.ts +113 -0
- package/src/docs/snippet.ts +17 -0
- package/src/docs/types.ts +180 -0
- package/src/hooks/scroll/minimize-decision.ts +13 -0
- package/src/hooks/use-anchored-placement.ts +43 -0
- package/src/hooks/use-dismiss.ts +58 -0
- package/src/hooks/use-media-query.ts +106 -0
- package/src/hooks/use-minimize-on-scroll.ts +92 -0
- package/src/hooks/use-visual-viewport.ts +76 -0
- package/src/hooks/viewport/types.ts +19 -0
- package/src/hooks/viewport/viewport-metrics.ts +25 -0
- package/src/icons/icons.tsx +298 -0
- package/src/index.ts +78 -0
- package/src/lib/anchored-placement.ts +92 -0
- package/src/lib/class-names.ts +21 -0
- package/src/lib/date.ts +96 -0
- package/src/lib/floating-layers.ts +28 -0
- package/src/lib/haptics.ts +25 -0
- package/src/lib/language.ts +21 -0
- package/src/lib/screen-edges.ts +45 -0
- package/src/lib/shapes.ts +70 -0
- package/src/motion/transitions.ts +30 -0
- package/src/styles/base.css +1 -0
- package/src/styles/styles.css +3 -0
- package/src/styles/theme.css +1 -0
- package/src/styles/typeset.css +1 -0
- package/src/styles/utilities.css +1 -0
- package/src/tokens.ts +1 -0
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { useEffect, useState } from "react"
|
|
2
|
+
import { minimizeDecision } from "./scroll/minimize-decision"
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Minimized while you scroll down through content, restored when you scroll
|
|
6
|
+
* back up or reach the top: iOS 26's `tabBarMinimizeBehavior(.onScrollDown)`.
|
|
7
|
+
* Listens to every scroll container, since each screen scrolls its own pane.
|
|
8
|
+
*/
|
|
9
|
+
export function useMinimizeOnScroll() {
|
|
10
|
+
const [minimized, setMinimized] = useState(false)
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
const anchors = new WeakMap<Element, number>()
|
|
13
|
+
let intent: number | null = null
|
|
14
|
+
const onIntent = () => {
|
|
15
|
+
intent = performance.now()
|
|
16
|
+
}
|
|
17
|
+
const onKey = (event: KeyboardEvent) => {
|
|
18
|
+
if (
|
|
19
|
+
[
|
|
20
|
+
"ArrowDown",
|
|
21
|
+
"ArrowUp",
|
|
22
|
+
"PageDown",
|
|
23
|
+
"PageUp",
|
|
24
|
+
"Home",
|
|
25
|
+
"End",
|
|
26
|
+
" ",
|
|
27
|
+
].includes(event.key)
|
|
28
|
+
)
|
|
29
|
+
onIntent()
|
|
30
|
+
}
|
|
31
|
+
const onPointer = (event: PointerEvent) => {
|
|
32
|
+
if (event.buttons === 1) onIntent()
|
|
33
|
+
}
|
|
34
|
+
const onScroll = (event: Event) => {
|
|
35
|
+
const pane =
|
|
36
|
+
event.target instanceof Element
|
|
37
|
+
? event.target
|
|
38
|
+
: document.scrollingElement
|
|
39
|
+
if (
|
|
40
|
+
!pane ||
|
|
41
|
+
pane.closest(
|
|
42
|
+
"[role=dialog], [data-floating-layer]"
|
|
43
|
+
) ||
|
|
44
|
+
pane.scrollHeight <= pane.clientHeight + 1
|
|
45
|
+
)
|
|
46
|
+
return
|
|
47
|
+
const top = pane.scrollTop
|
|
48
|
+
const userDriven =
|
|
49
|
+
intent !== null &&
|
|
50
|
+
performance.now() - intent < 2000
|
|
51
|
+
const anchor = anchors.get(pane) ?? 0
|
|
52
|
+
const next = minimizeDecision(
|
|
53
|
+
top,
|
|
54
|
+
anchor,
|
|
55
|
+
userDriven
|
|
56
|
+
)
|
|
57
|
+
if (next !== null) setMinimized(next)
|
|
58
|
+
if (next !== null || !userDriven)
|
|
59
|
+
anchors.set(pane, top)
|
|
60
|
+
}
|
|
61
|
+
document.addEventListener("touchmove", onIntent, {
|
|
62
|
+
passive: true,
|
|
63
|
+
})
|
|
64
|
+
document.addEventListener("wheel", onIntent, {
|
|
65
|
+
passive: true,
|
|
66
|
+
})
|
|
67
|
+
document.addEventListener("pointermove", onPointer, {
|
|
68
|
+
passive: true,
|
|
69
|
+
})
|
|
70
|
+
document.addEventListener("keydown", onKey)
|
|
71
|
+
document.addEventListener("scroll", onScroll, {
|
|
72
|
+
capture: true,
|
|
73
|
+
passive: true,
|
|
74
|
+
})
|
|
75
|
+
return () => {
|
|
76
|
+
document.removeEventListener("scroll", onScroll, {
|
|
77
|
+
capture: true,
|
|
78
|
+
})
|
|
79
|
+
document.removeEventListener(
|
|
80
|
+
"touchmove",
|
|
81
|
+
onIntent
|
|
82
|
+
)
|
|
83
|
+
document.removeEventListener("wheel", onIntent)
|
|
84
|
+
document.removeEventListener(
|
|
85
|
+
"pointermove",
|
|
86
|
+
onPointer
|
|
87
|
+
)
|
|
88
|
+
document.removeEventListener("keydown", onKey)
|
|
89
|
+
}
|
|
90
|
+
}, [])
|
|
91
|
+
return [minimized, setMinimized] as const
|
|
92
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { useLayoutEffect, useState } from "react"
|
|
2
|
+
import { viewportMetrics } from "./viewport/viewport-metrics"
|
|
3
|
+
import type { ViewportMetrics } from "./viewport/types"
|
|
4
|
+
export type { ViewportMetrics } from "./viewport/types"
|
|
5
|
+
|
|
6
|
+
/** Measures once per frame, including Android resize and Safari viewport panning. */
|
|
7
|
+
export function useVisualViewport(): ViewportMetrics | null {
|
|
8
|
+
const [metrics, setMetrics] =
|
|
9
|
+
useState<ViewportMetrics | null>(null)
|
|
10
|
+
useLayoutEffect(() => {
|
|
11
|
+
const viewport = window.visualViewport
|
|
12
|
+
let baseline = {
|
|
13
|
+
width: window.innerWidth,
|
|
14
|
+
height: window.innerHeight,
|
|
15
|
+
}
|
|
16
|
+
let frame = 0
|
|
17
|
+
const measure = () => {
|
|
18
|
+
cancelAnimationFrame(frame)
|
|
19
|
+
frame = requestAnimationFrame(() => {
|
|
20
|
+
const active = document.activeElement
|
|
21
|
+
const editing = !!active?.matches(
|
|
22
|
+
"input:not([type=button]):not([type=checkbox]):not([type=radio]), textarea, [contenteditable=true]"
|
|
23
|
+
)
|
|
24
|
+
const width =
|
|
25
|
+
viewport?.width ?? window.innerWidth
|
|
26
|
+
const height =
|
|
27
|
+
viewport?.height ?? window.innerHeight
|
|
28
|
+
if (
|
|
29
|
+
Math.abs(baseline.width - width) > 80 ||
|
|
30
|
+
!editing
|
|
31
|
+
)
|
|
32
|
+
baseline = {
|
|
33
|
+
width,
|
|
34
|
+
height: window.innerHeight,
|
|
35
|
+
}
|
|
36
|
+
const next = viewportMetrics(
|
|
37
|
+
{
|
|
38
|
+
width,
|
|
39
|
+
height,
|
|
40
|
+
layoutHeight: window.innerHeight,
|
|
41
|
+
top: viewport?.offsetTop ?? 0,
|
|
42
|
+
left: viewport?.offsetLeft ?? 0,
|
|
43
|
+
editing,
|
|
44
|
+
},
|
|
45
|
+
baseline
|
|
46
|
+
)
|
|
47
|
+
setMetrics((previous) =>
|
|
48
|
+
previous &&
|
|
49
|
+
Object.entries(next).every(
|
|
50
|
+
([key, value]) =>
|
|
51
|
+
previous[
|
|
52
|
+
key as keyof ViewportMetrics
|
|
53
|
+
] === value
|
|
54
|
+
)
|
|
55
|
+
? previous
|
|
56
|
+
: next
|
|
57
|
+
)
|
|
58
|
+
})
|
|
59
|
+
}
|
|
60
|
+
measure()
|
|
61
|
+
viewport?.addEventListener("resize", measure)
|
|
62
|
+
viewport?.addEventListener("scroll", measure)
|
|
63
|
+
window.addEventListener("resize", measure)
|
|
64
|
+
document.addEventListener("focusin", measure)
|
|
65
|
+
document.addEventListener("focusout", measure)
|
|
66
|
+
return () => {
|
|
67
|
+
cancelAnimationFrame(frame)
|
|
68
|
+
viewport?.removeEventListener("resize", measure)
|
|
69
|
+
viewport?.removeEventListener("scroll", measure)
|
|
70
|
+
window.removeEventListener("resize", measure)
|
|
71
|
+
document.removeEventListener("focusin", measure)
|
|
72
|
+
document.removeEventListener("focusout", measure)
|
|
73
|
+
}
|
|
74
|
+
}, [])
|
|
75
|
+
return metrics
|
|
76
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export type ViewportInput = {
|
|
2
|
+
width: number
|
|
3
|
+
height: number
|
|
4
|
+
top: number
|
|
5
|
+
left: number
|
|
6
|
+
layoutHeight: number
|
|
7
|
+
editing: boolean
|
|
8
|
+
}
|
|
9
|
+
export type ViewportMetrics = {
|
|
10
|
+
width: number
|
|
11
|
+
height: number
|
|
12
|
+
top: number
|
|
13
|
+
left: number
|
|
14
|
+
keyboardOpen: boolean
|
|
15
|
+
}
|
|
16
|
+
export type ViewportBaseline = {
|
|
17
|
+
width: number
|
|
18
|
+
height: number
|
|
19
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ViewportBaseline,
|
|
3
|
+
ViewportInput,
|
|
4
|
+
ViewportMetrics,
|
|
5
|
+
} from "./types"
|
|
6
|
+
|
|
7
|
+
/** A resized WebView and Safari's overlaid keyboard share one visible rectangle. */
|
|
8
|
+
export function viewportMetrics(
|
|
9
|
+
input: ViewportInput,
|
|
10
|
+
baseline: ViewportBaseline
|
|
11
|
+
): ViewportMetrics {
|
|
12
|
+
const visibleHeight = Math.min(
|
|
13
|
+
input.height,
|
|
14
|
+
Math.max(0, input.layoutHeight - input.top)
|
|
15
|
+
)
|
|
16
|
+
return {
|
|
17
|
+
width: input.width,
|
|
18
|
+
height: visibleHeight,
|
|
19
|
+
top: input.top,
|
|
20
|
+
left: input.left,
|
|
21
|
+
keyboardOpen:
|
|
22
|
+
input.editing &&
|
|
23
|
+
baseline.height - visibleHeight > 120,
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Add01Icon,
|
|
3
|
+
AiMagicIcon,
|
|
4
|
+
AlarmClockIcon,
|
|
5
|
+
Alert02Icon,
|
|
6
|
+
ArrowDown01Icon,
|
|
7
|
+
ArrowLeft01Icon,
|
|
8
|
+
ArrowRight01Icon,
|
|
9
|
+
ArrowUp01Icon,
|
|
10
|
+
BirthdayCakeIcon,
|
|
11
|
+
Book02Icon,
|
|
12
|
+
BookOpen01Icon,
|
|
13
|
+
BubbleChatIcon,
|
|
14
|
+
Calendar03Icon,
|
|
15
|
+
Camera01Icon,
|
|
16
|
+
Cancel01Icon,
|
|
17
|
+
CheckListIcon,
|
|
18
|
+
CheckmarkBadge01Icon,
|
|
19
|
+
CheckmarkCircle02Icon,
|
|
20
|
+
Clock01Icon,
|
|
21
|
+
Comment01Icon,
|
|
22
|
+
Copy01Icon,
|
|
23
|
+
Cursor01Icon,
|
|
24
|
+
Delete02Icon,
|
|
25
|
+
Download01Icon,
|
|
26
|
+
DragDropVerticalIcon,
|
|
27
|
+
Eraser01Icon,
|
|
28
|
+
FavouriteIcon,
|
|
29
|
+
Folder01Icon,
|
|
30
|
+
FileImportIcon,
|
|
31
|
+
FullScreenIcon,
|
|
32
|
+
Globe02Icon,
|
|
33
|
+
GoBackward15SecIcon,
|
|
34
|
+
GoForward30SecIcon,
|
|
35
|
+
CanvasIcon as HugeCanvasIcon,
|
|
36
|
+
HeadphonesIcon as HugeHeadphonesIcon,
|
|
37
|
+
HighlighterIcon as HugeHighlighterIcon,
|
|
38
|
+
PauseIcon as HugePauseIcon,
|
|
39
|
+
PlayIcon as HugePlayIcon,
|
|
40
|
+
RedoIcon as HugeRedoIcon,
|
|
41
|
+
RepeatIcon as HugeRepeatIcon,
|
|
42
|
+
StarIcon as HugeStarIcon,
|
|
43
|
+
ThumbsUpIcon as HugeThumbsUpIcon,
|
|
44
|
+
UndoIcon as HugeUndoIcon,
|
|
45
|
+
Image01Icon,
|
|
46
|
+
Invoice03Icon,
|
|
47
|
+
LayoutGridIcon,
|
|
48
|
+
LeftToRightListBulletIcon,
|
|
49
|
+
Link01Icon,
|
|
50
|
+
Location01Icon,
|
|
51
|
+
LockIcon as LockIconSvg,
|
|
52
|
+
Mic01Icon,
|
|
53
|
+
Moon02Icon,
|
|
54
|
+
MoreHorizontalIcon,
|
|
55
|
+
Mortarboard01Icon,
|
|
56
|
+
NextIcon,
|
|
57
|
+
NfcIcon,
|
|
58
|
+
Note01Icon,
|
|
59
|
+
NotebookIcon,
|
|
60
|
+
Notification03Icon,
|
|
61
|
+
PaintBrush02Icon,
|
|
62
|
+
PanelLeftIcon,
|
|
63
|
+
PencilEdit02Icon,
|
|
64
|
+
PencilIcon,
|
|
65
|
+
Pin02Icon,
|
|
66
|
+
PreviousIcon,
|
|
67
|
+
PrinterIcon,
|
|
68
|
+
QrCodeIcon,
|
|
69
|
+
QuillWrite02Icon,
|
|
70
|
+
ScanIcon,
|
|
71
|
+
Search01Icon,
|
|
72
|
+
SearchAddIcon,
|
|
73
|
+
Settings02Icon,
|
|
74
|
+
Share08Icon,
|
|
75
|
+
Store01Icon,
|
|
76
|
+
Task01Icon,
|
|
77
|
+
Tick02Icon,
|
|
78
|
+
Time04Icon,
|
|
79
|
+
Touch01Icon,
|
|
80
|
+
UnfoldMoreIcon,
|
|
81
|
+
UserAdd01Icon,
|
|
82
|
+
UserMultipleIcon,
|
|
83
|
+
ViewIcon,
|
|
84
|
+
ViewOffSlashIcon,
|
|
85
|
+
Wallet01Icon,
|
|
86
|
+
} from "@hugeicons/core-free-icons"
|
|
87
|
+
import {
|
|
88
|
+
HugeiconsIcon,
|
|
89
|
+
type IconSvgElement,
|
|
90
|
+
} from "@hugeicons/react"
|
|
91
|
+
import type { SVGProps } from "react"
|
|
92
|
+
import { cn } from "../lib/class-names"
|
|
93
|
+
|
|
94
|
+
export type IconProps = Omit<
|
|
95
|
+
SVGProps<SVGSVGElement>,
|
|
96
|
+
"ref"
|
|
97
|
+
> & {
|
|
98
|
+
size?: number
|
|
99
|
+
strokeWidth?: number
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** Hugeicons (stroke rounded), sized and weighted for the Notables UI. */
|
|
103
|
+
function icon(svg: IconSvgElement, displayName: string) {
|
|
104
|
+
const Icon = ({
|
|
105
|
+
size = 20,
|
|
106
|
+
strokeWidth = 1.6,
|
|
107
|
+
...props
|
|
108
|
+
}: IconProps) => (
|
|
109
|
+
<HugeiconsIcon
|
|
110
|
+
icon={svg}
|
|
111
|
+
size={size}
|
|
112
|
+
strokeWidth={strokeWidth}
|
|
113
|
+
aria-hidden="true"
|
|
114
|
+
{...props}
|
|
115
|
+
/>
|
|
116
|
+
)
|
|
117
|
+
Icon.displayName = displayName
|
|
118
|
+
return Icon
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** A filled variant for toggled states (hearted, starred). */
|
|
122
|
+
function toggleIcon(
|
|
123
|
+
svg: IconSvgElement,
|
|
124
|
+
displayName: string
|
|
125
|
+
) {
|
|
126
|
+
const Base = icon(svg, displayName)
|
|
127
|
+
const Icon = ({
|
|
128
|
+
filled = false,
|
|
129
|
+
className,
|
|
130
|
+
...props
|
|
131
|
+
}: IconProps & { filled?: boolean }) => (
|
|
132
|
+
<Base
|
|
133
|
+
className={cn(
|
|
134
|
+
filled && "[&_path]:fill-current",
|
|
135
|
+
className
|
|
136
|
+
)}
|
|
137
|
+
{...props}
|
|
138
|
+
/>
|
|
139
|
+
)
|
|
140
|
+
Icon.displayName = displayName
|
|
141
|
+
return Icon
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export const NoteIcon = icon(Note01Icon, "NoteIcon")
|
|
145
|
+
export const JournalIcon = icon(NotebookIcon, "JournalIcon")
|
|
146
|
+
export const PenIcon = icon(PencilEdit02Icon, "PenIcon")
|
|
147
|
+
export const StoryIcon = icon(QuillWrite02Icon, "StoryIcon")
|
|
148
|
+
export const BookIcon = icon(BookOpen01Icon, "BookIcon")
|
|
149
|
+
export const ArticleIcon = icon(Book02Icon, "ArticleIcon")
|
|
150
|
+
export const CanvasIcon = icon(HugeCanvasIcon, "CanvasIcon")
|
|
151
|
+
export const LessonIcon = icon(
|
|
152
|
+
Mortarboard01Icon,
|
|
153
|
+
"LessonIcon"
|
|
154
|
+
)
|
|
155
|
+
export const PlanIcon = icon(CheckListIcon, "PlanIcon")
|
|
156
|
+
export const MicIcon = icon(Mic01Icon, "MicIcon")
|
|
157
|
+
export const GlobeIcon = icon(Globe02Icon, "GlobeIcon")
|
|
158
|
+
export const ChecklistIcon = icon(
|
|
159
|
+
CheckmarkCircle02Icon,
|
|
160
|
+
"ChecklistIcon"
|
|
161
|
+
)
|
|
162
|
+
export const DrawIcon = icon(PencilIcon, "DrawIcon")
|
|
163
|
+
export const ShareIcon = icon(Share08Icon, "ShareIcon")
|
|
164
|
+
export const SearchIcon = icon(Search01Icon, "SearchIcon")
|
|
165
|
+
export const ChevronLeftIcon = icon(
|
|
166
|
+
ArrowLeft01Icon,
|
|
167
|
+
"ChevronLeftIcon"
|
|
168
|
+
)
|
|
169
|
+
export const ChevronRightIcon = icon(
|
|
170
|
+
ArrowRight01Icon,
|
|
171
|
+
"ChevronRightIcon"
|
|
172
|
+
)
|
|
173
|
+
export const ChevronUpIcon = icon(
|
|
174
|
+
ArrowUp01Icon,
|
|
175
|
+
"ChevronUpIcon"
|
|
176
|
+
)
|
|
177
|
+
export const ChevronDownIcon = icon(
|
|
178
|
+
ArrowDown01Icon,
|
|
179
|
+
"ChevronDownIcon"
|
|
180
|
+
)
|
|
181
|
+
export const CloseIcon = icon(Cancel01Icon, "CloseIcon")
|
|
182
|
+
export const PlusIcon = icon(Add01Icon, "PlusIcon")
|
|
183
|
+
export const LinkIcon = icon(Link01Icon, "LinkIcon")
|
|
184
|
+
export const CommentIcon = icon(Comment01Icon, "CommentIcon")
|
|
185
|
+
export const TrashIcon = icon(Delete02Icon, "TrashIcon")
|
|
186
|
+
export const PinIcon = icon(Pin02Icon, "PinIcon")
|
|
187
|
+
export const SidebarIcon = icon(PanelLeftIcon, "SidebarIcon")
|
|
188
|
+
export const SettingsIcon = icon(
|
|
189
|
+
Settings02Icon,
|
|
190
|
+
"SettingsIcon"
|
|
191
|
+
)
|
|
192
|
+
export const DragHandleIcon = icon(
|
|
193
|
+
DragDropVerticalIcon,
|
|
194
|
+
"DragHandleIcon"
|
|
195
|
+
)
|
|
196
|
+
export const VisibleIcon = icon(ViewIcon, "VisibleIcon")
|
|
197
|
+
export const HiddenIcon = icon(ViewOffSlashIcon, "HiddenIcon")
|
|
198
|
+
export const CheckIcon = icon(Tick02Icon, "CheckIcon")
|
|
199
|
+
export const InvoiceIcon = icon(Invoice03Icon, "InvoiceIcon")
|
|
200
|
+
export const QrCodeGlyph = icon(QrCodeIcon, "QrCodeGlyph")
|
|
201
|
+
export const WalletIcon = icon(Wallet01Icon, "WalletIcon")
|
|
202
|
+
export const ScanCodeIcon = icon(ScanIcon, "ScanCodeIcon")
|
|
203
|
+
export const VerifiedIcon = icon(
|
|
204
|
+
CheckmarkBadge01Icon,
|
|
205
|
+
"VerifiedIcon"
|
|
206
|
+
)
|
|
207
|
+
export const WarningIcon = icon(Alert02Icon, "WarningIcon")
|
|
208
|
+
export const PrintIcon = icon(PrinterIcon, "PrintIcon")
|
|
209
|
+
export const ImportIcon = icon(FileImportIcon, "ImportIcon")
|
|
210
|
+
export const DownloadIcon = icon(
|
|
211
|
+
Download01Icon,
|
|
212
|
+
"DownloadIcon"
|
|
213
|
+
)
|
|
214
|
+
export const PlayIcon = icon(HugePlayIcon, "PlayIcon")
|
|
215
|
+
export const PauseIcon = icon(HugePauseIcon, "PauseIcon")
|
|
216
|
+
export const SkipBackIcon = icon(
|
|
217
|
+
GoBackward15SecIcon,
|
|
218
|
+
"SkipBackIcon"
|
|
219
|
+
)
|
|
220
|
+
export const SkipForwardIcon = icon(
|
|
221
|
+
GoForward30SecIcon,
|
|
222
|
+
"SkipForwardIcon"
|
|
223
|
+
)
|
|
224
|
+
export const PreviousTrackIcon = icon(
|
|
225
|
+
PreviousIcon,
|
|
226
|
+
"PreviousTrackIcon"
|
|
227
|
+
)
|
|
228
|
+
export const NextTrackIcon = icon(NextIcon, "NextTrackIcon")
|
|
229
|
+
export const HeadphonesIcon = icon(
|
|
230
|
+
HugeHeadphonesIcon,
|
|
231
|
+
"HeadphonesIcon"
|
|
232
|
+
)
|
|
233
|
+
export const HighlighterIcon = icon(
|
|
234
|
+
HugeHighlighterIcon,
|
|
235
|
+
"HighlighterIcon"
|
|
236
|
+
)
|
|
237
|
+
export const ChaptersIcon = icon(
|
|
238
|
+
LeftToRightListBulletIcon,
|
|
239
|
+
"ChaptersIcon"
|
|
240
|
+
)
|
|
241
|
+
export const SleepIcon = icon(Moon02Icon, "SleepIcon")
|
|
242
|
+
export const ExpandIcon = icon(FullScreenIcon, "ExpandIcon")
|
|
243
|
+
export const SparkleIcon = icon(AiMagicIcon, "SparkleIcon")
|
|
244
|
+
export const BellIcon = icon(Notification03Icon, "BellIcon")
|
|
245
|
+
export const BusinessIcon = icon(Store01Icon, "BusinessIcon")
|
|
246
|
+
export const CalendarIcon = icon(
|
|
247
|
+
Calendar03Icon,
|
|
248
|
+
"CalendarIcon"
|
|
249
|
+
)
|
|
250
|
+
export const BirthdayIcon = icon(
|
|
251
|
+
BirthdayCakeIcon,
|
|
252
|
+
"BirthdayIcon"
|
|
253
|
+
)
|
|
254
|
+
export const AlarmIcon = icon(AlarmClockIcon, "AlarmIcon")
|
|
255
|
+
export const RepeatIcon = icon(HugeRepeatIcon, "RepeatIcon")
|
|
256
|
+
export const UndoIcon = icon(HugeUndoIcon, "UndoIcon")
|
|
257
|
+
export const RedoIcon = icon(HugeRedoIcon, "RedoIcon")
|
|
258
|
+
export const PointerIcon = icon(Cursor01Icon, "PointerIcon")
|
|
259
|
+
export const EraserIcon = icon(Eraser01Icon, "EraserIcon")
|
|
260
|
+
export const TouchIcon = icon(Touch01Icon, "TouchIcon")
|
|
261
|
+
export const BubbleIcon = icon(BubbleChatIcon, "BubbleIcon")
|
|
262
|
+
export const BrushIcon = icon(PaintBrush02Icon, "BrushIcon")
|
|
263
|
+
export const LayoutIcon = icon(LayoutGridIcon, "LayoutIcon")
|
|
264
|
+
export const ZoomIcon = icon(SearchAddIcon, "ZoomIcon")
|
|
265
|
+
export const SelectorIcon = icon(
|
|
266
|
+
UnfoldMoreIcon,
|
|
267
|
+
"SelectorIcon"
|
|
268
|
+
)
|
|
269
|
+
export const MoreIcon = icon(MoreHorizontalIcon, "MoreIcon")
|
|
270
|
+
export const PeopleIcon = icon(UserMultipleIcon, "PeopleIcon")
|
|
271
|
+
export const AddPersonIcon = icon(
|
|
272
|
+
UserAdd01Icon,
|
|
273
|
+
"AddPersonIcon"
|
|
274
|
+
)
|
|
275
|
+
export const ClockIcon = icon(Clock01Icon, "ClockIcon")
|
|
276
|
+
export const LocationIcon = icon(
|
|
277
|
+
Location01Icon,
|
|
278
|
+
"LocationIcon"
|
|
279
|
+
)
|
|
280
|
+
export const TaskIcon = icon(Task01Icon, "TaskIcon")
|
|
281
|
+
export const FolderIcon = icon(Folder01Icon, "FolderIcon")
|
|
282
|
+
export const RecentIcon = icon(Time04Icon, "RecentIcon")
|
|
283
|
+
export const ThumbsUpIcon = toggleIcon(
|
|
284
|
+
HugeThumbsUpIcon,
|
|
285
|
+
"ThumbsUpIcon"
|
|
286
|
+
)
|
|
287
|
+
export const HeartIcon = toggleIcon(
|
|
288
|
+
FavouriteIcon,
|
|
289
|
+
"HeartIcon"
|
|
290
|
+
)
|
|
291
|
+
export const StarIcon = toggleIcon(HugeStarIcon, "StarIcon")
|
|
292
|
+
|
|
293
|
+
// Wallet: adding, copying and protecting cards.
|
|
294
|
+
export const CameraIcon = icon(Camera01Icon, "CameraIcon")
|
|
295
|
+
export const CopyIcon = icon(Copy01Icon, "CopyIcon")
|
|
296
|
+
export const LockIcon = icon(LockIconSvg, "LockIcon")
|
|
297
|
+
export const NfcReadIcon = icon(NfcIcon, "NfcReadIcon")
|
|
298
|
+
export const PhotoIcon = icon(Image01Icon, "PhotoIcon")
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
export * from "./components/button/button"
|
|
2
|
+
export * from "./components/text-field/text-field"
|
|
3
|
+
export * from "./components/secure-field/secure-field"
|
|
4
|
+
export * from "./components/text-editor/text-editor"
|
|
5
|
+
export * from "./components/tab-view/tab-view"
|
|
6
|
+
export * from "./components/profile-image/profile-image"
|
|
7
|
+
export * from "./components/search-list/search-list"
|
|
8
|
+
export * from "./components/table/table"
|
|
9
|
+
export * from "./components/button/icon-button"
|
|
10
|
+
export * from "./components/chip/chip"
|
|
11
|
+
export * from "./components/context-menu/context-menu-host"
|
|
12
|
+
export {
|
|
13
|
+
type ContextMenuItem,
|
|
14
|
+
type ContextMenuAlignX,
|
|
15
|
+
type ContextMenuAlignY,
|
|
16
|
+
type ContextMenuEdge,
|
|
17
|
+
closeContextMenu,
|
|
18
|
+
openContextMenu,
|
|
19
|
+
openFollowUpMenu,
|
|
20
|
+
openMenu,
|
|
21
|
+
} from "./components/context-menu/context-menu-store"
|
|
22
|
+
export * from "./components/context-menu/use-context-menu"
|
|
23
|
+
export * from "./components/dialog/dialog-host"
|
|
24
|
+
export {
|
|
25
|
+
type ConfirmOptions,
|
|
26
|
+
confirmDialog,
|
|
27
|
+
} from "./components/dialog/dialog-store"
|
|
28
|
+
export * from "./components/labeled-content/labeled-content"
|
|
29
|
+
export * from "./components/picker/picker"
|
|
30
|
+
export * from "./components/popover/popover"
|
|
31
|
+
export * from "./components/search-field/search-field"
|
|
32
|
+
export * from "./components/section/section"
|
|
33
|
+
export * from "./components/segmented-control/segmented-control"
|
|
34
|
+
export * from "./components/sheet/sheet"
|
|
35
|
+
export * from "./components/sidebar/sidebar"
|
|
36
|
+
export * from "./components/status-indicator/status-indicator"
|
|
37
|
+
export * from "./components/swatch-picker/swatch-picker"
|
|
38
|
+
export * from "./components/tab-bar/tab-bar"
|
|
39
|
+
export {
|
|
40
|
+
type ToastOptions,
|
|
41
|
+
type ToastTone,
|
|
42
|
+
toast,
|
|
43
|
+
} from "./components/toast/toast-store"
|
|
44
|
+
export * from "./components/toast/toaster"
|
|
45
|
+
export * from "./components/toggle/toggle"
|
|
46
|
+
export * from "./components/tooltip/tooltip-host"
|
|
47
|
+
export * from "./hooks/use-anchored-placement"
|
|
48
|
+
export * from "./hooks/use-dismiss"
|
|
49
|
+
export * from "./hooks/use-media-query"
|
|
50
|
+
export * from "./hooks/use-minimize-on-scroll"
|
|
51
|
+
export * from "./icons/icons"
|
|
52
|
+
export * from "./lib/anchored-placement"
|
|
53
|
+
export * from "./lib/class-names"
|
|
54
|
+
export * from "./lib/haptics"
|
|
55
|
+
export * from "./lib/screen-edges"
|
|
56
|
+
export * from "./motion/transitions"
|
|
57
|
+
export * from "./appearance"
|
|
58
|
+
|
|
59
|
+
export * from "./components/date-picker/date-picker"
|
|
60
|
+
export * from "./components/compact-date-picker/compact-date-picker"
|
|
61
|
+
export * from "./components/compact-time-picker/compact-time-picker"
|
|
62
|
+
|
|
63
|
+
export { Dialog } from "./components/dialog"
|
|
64
|
+
export type {
|
|
65
|
+
DialogPresentation,
|
|
66
|
+
DialogTone,
|
|
67
|
+
} from "./components/dialog/dialog"
|
|
68
|
+
export type {
|
|
69
|
+
ContentViewHeadingLevel,
|
|
70
|
+
ContentViewVariant,
|
|
71
|
+
} from "./components/content-view/content-view"
|
|
72
|
+
export type { StatTileTone } from "./components/stat-tile/stat-tile"
|
|
73
|
+
|
|
74
|
+
export * from "./hooks/use-visual-viewport"
|
|
75
|
+
|
|
76
|
+
export { registerFloatingLayer } from "./lib/floating-layers"
|
|
77
|
+
|
|
78
|
+
export * from "./hooks/use-minimize-on-scroll"
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { screenEdges } from "./screen-edges"
|
|
2
|
+
|
|
3
|
+
export interface AnchoredPlacement {
|
|
4
|
+
left: number
|
|
5
|
+
width: number
|
|
6
|
+
top?: number
|
|
7
|
+
bottom?: number
|
|
8
|
+
/** Opens upward, so motion should grow from the bottom edge. */
|
|
9
|
+
above: boolean
|
|
10
|
+
/**
|
|
11
|
+
* Set when the chosen side has less room than the panel needs: the
|
|
12
|
+
* panel scrolls inside this height instead of crossing the edge.
|
|
13
|
+
*/
|
|
14
|
+
maxHeight?: number
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** The measurements placement decides on, so the math stays testable. */
|
|
18
|
+
export interface PlacementFrame {
|
|
19
|
+
anchor: { top: number; bottom: number; left: number }
|
|
20
|
+
viewport: { width: number; height: number }
|
|
21
|
+
edges: {
|
|
22
|
+
top: number
|
|
23
|
+
right: number
|
|
24
|
+
bottom: number
|
|
25
|
+
left: number
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Places a panel of `width` under its anchor, or above it when there's
|
|
31
|
+
* less than `needed` height below and more room above, kept inside the
|
|
32
|
+
* screen's safe edges.
|
|
33
|
+
*/
|
|
34
|
+
export function placeInViewport(
|
|
35
|
+
{ anchor, viewport, edges }: PlacementFrame,
|
|
36
|
+
width: number,
|
|
37
|
+
needed: number,
|
|
38
|
+
gap = 6
|
|
39
|
+
): AnchoredPlacement {
|
|
40
|
+
const fitted = Math.min(
|
|
41
|
+
width,
|
|
42
|
+
viewport.width - edges.left - edges.right
|
|
43
|
+
)
|
|
44
|
+
const left = Math.min(
|
|
45
|
+
Math.max(edges.left, anchor.left),
|
|
46
|
+
viewport.width - fitted - edges.right
|
|
47
|
+
)
|
|
48
|
+
const roomBelow =
|
|
49
|
+
viewport.height - anchor.bottom - gap - edges.bottom
|
|
50
|
+
const roomAbove = anchor.top - gap - edges.top
|
|
51
|
+
const above = roomBelow < needed && roomAbove > roomBelow
|
|
52
|
+
const room = above ? roomAbove : roomBelow
|
|
53
|
+
const placement: AnchoredPlacement = {
|
|
54
|
+
left,
|
|
55
|
+
width: fitted,
|
|
56
|
+
above,
|
|
57
|
+
...(above
|
|
58
|
+
? { bottom: viewport.height - anchor.top + gap }
|
|
59
|
+
: { top: anchor.bottom + gap }),
|
|
60
|
+
}
|
|
61
|
+
if (room < needed) placement.maxHeight = room
|
|
62
|
+
return placement
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** `placeInViewport` from live DOM measurements. */
|
|
66
|
+
export function placeAnchored(
|
|
67
|
+
anchor: HTMLElement,
|
|
68
|
+
width: number,
|
|
69
|
+
needed: number,
|
|
70
|
+
gap = 6
|
|
71
|
+
): AnchoredPlacement {
|
|
72
|
+
const rect = anchor.getBoundingClientRect()
|
|
73
|
+
const viewport = window.visualViewport
|
|
74
|
+
return placeInViewport(
|
|
75
|
+
{
|
|
76
|
+
anchor: {
|
|
77
|
+
top: rect.top,
|
|
78
|
+
bottom: rect.bottom,
|
|
79
|
+
left: rect.left,
|
|
80
|
+
},
|
|
81
|
+
viewport: {
|
|
82
|
+
width: viewport?.width ?? window.innerWidth,
|
|
83
|
+
height:
|
|
84
|
+
viewport?.height ?? window.innerHeight,
|
|
85
|
+
},
|
|
86
|
+
edges: screenEdges(),
|
|
87
|
+
},
|
|
88
|
+
width,
|
|
89
|
+
needed,
|
|
90
|
+
gap
|
|
91
|
+
)
|
|
92
|
+
}
|