@oprt/react 0.0.0-stage → 0.2.0
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 +105 -2
- package/dist/ActionStatus/ActionStatus.js +104 -0
- package/dist/ActionStatus/ActionStatus.manifest.js +64 -0
- package/dist/ActionStatus/index.js +7 -0
- package/dist/Alert/Alert.js +86 -0
- package/dist/Alert/Alert.manifest.js +36 -0
- package/dist/Alert/index.js +7 -0
- package/dist/AppShell/AppShell.js +103 -0
- package/dist/AppShell/AppShell.manifest.js +66 -0
- package/dist/AppShell/index.js +9 -0
- package/dist/AttachmentPreview/AttachmentPreview.js +145 -0
- package/dist/AttachmentPreview/AttachmentPreview.manifest.js +42 -0
- package/dist/AttachmentPreview/index.js +9 -0
- package/dist/Avatar/Avatar.js +87 -0
- package/dist/Avatar/Avatar.manifest.js +25 -0
- package/dist/Avatar/index.js +7 -0
- package/dist/Badge/Badge.js +124 -0
- package/dist/Badge/Badge.manifest.js +41 -0
- package/dist/Badge/index.js +7 -0
- package/dist/Breadcrumb/Breadcrumb.js +64 -0
- package/dist/Breadcrumb/Breadcrumb.manifest.js +28 -0
- package/dist/Breadcrumb/index.js +7 -0
- package/dist/BulkActionBar/BulkActionBar.js +145 -0
- package/dist/BulkActionBar/BulkActionBar.manifest.js +46 -0
- package/dist/BulkActionBar/index.js +9 -0
- package/dist/BulkActionBar/selection.js +44 -0
- package/dist/BulkResults/BulkResults.js +269 -0
- package/dist/BulkResults/BulkResults.manifest.js +45 -0
- package/dist/BulkResults/index.js +7 -0
- package/dist/Button/Button.js +170 -0
- package/dist/Button/Button.manifest.js +36 -0
- package/dist/Button/index.js +7 -0
- package/dist/ButtonGroup/ButtonGroup.js +60 -0
- package/dist/ButtonGroup/ButtonGroup.manifest.js +22 -0
- package/dist/ButtonGroup/index.js +7 -0
- package/dist/Calendar/Calendar.js +106 -0
- package/dist/Calendar/Calendar.manifest.js +32 -0
- package/dist/Calendar/index.js +7 -0
- package/dist/Card/Card.js +70 -0
- package/dist/Card/Card.manifest.js +57 -0
- package/dist/Card/index.js +16 -0
- package/dist/Chart/Chart.js +1200 -0
- package/dist/Chart/Chart.manifest.js +110 -0
- package/dist/Chart/ChartControls.js +190 -0
- package/dist/Chart/geometry.js +217 -0
- package/dist/Chart/index.js +9 -0
- package/dist/Checkbox/Checkbox.js +166 -0
- package/dist/Checkbox/Checkbox.manifest.js +38 -0
- package/dist/Checkbox/index.js +7 -0
- package/dist/Collapsible/Collapsible.js +177 -0
- package/dist/Collapsible/Collapsible.manifest.js +54 -0
- package/dist/Collapsible/index.js +12 -0
- package/dist/CollectionState/CollectionState.js +68 -0
- package/dist/CollectionState/CollectionState.manifest.js +44 -0
- package/dist/CollectionState/index.js +7 -0
- package/dist/ComboBox/ComboBox.js +221 -0
- package/dist/ComboBox/ComboBox.manifest.js +87 -0
- package/dist/ComboBox/index.js +7 -0
- package/dist/CommandPalette/CommandPalette.js +276 -0
- package/dist/CommandPalette/CommandPalette.manifest.js +24 -0
- package/dist/CommandPalette/index.js +7 -0
- package/dist/ConfirmDialog/ConfirmDialog.js +84 -0
- package/dist/ConfirmDialog/ConfirmDialog.manifest.js +32 -0
- package/dist/ConfirmDialog/index.js +7 -0
- package/dist/ContextMenu/ContextMenu.js +192 -0
- package/dist/ContextMenu/ContextMenu.manifest.js +41 -0
- package/dist/ContextMenu/index.js +7 -0
- package/dist/CoordinateInput/CoordinateInput.js +141 -0
- package/dist/CoordinateInput/CoordinateInput.manifest.js +36 -0
- package/dist/CoordinateInput/index.js +9 -0
- package/dist/CopyButton/CopyButton.js +129 -0
- package/dist/CopyButton/CopyButton.manifest.js +35 -0
- package/dist/CopyButton/index.js +7 -0
- package/dist/DataGrid/DataGrid.js +459 -0
- package/dist/DataGrid/DataGrid.manifest.js +135 -0
- package/dist/DataGrid/PageControls.js +41 -0
- package/dist/DataGrid/index.js +9 -0
- package/dist/DataTable/DataTable.js +375 -0
- package/dist/DataTable/DataTable.manifest.js +86 -0
- package/dist/DataTable/index.js +9 -0
- package/dist/DataTable/sort.js +45 -0
- package/dist/DataTable/useScrollsSideways.js +61 -0
- package/dist/DataTable/useTableWindow.js +185 -0
- package/dist/DatePicker/DatePicker.js +187 -0
- package/dist/DatePicker/DatePicker.manifest.js +40 -0
- package/dist/DatePicker/index.js +7 -0
- package/dist/DateRangePicker/DateRangePicker.js +156 -0
- package/dist/DateRangePicker/DateRangePicker.manifest.js +41 -0
- package/dist/DateRangePicker/index.js +7 -0
- package/dist/DateTimePicker/DateTimePicker.js +213 -0
- package/dist/DateTimePicker/DateTimePicker.manifest.js +44 -0
- package/dist/DateTimePicker/index.js +7 -0
- package/dist/DescriptionList/DescriptionList.js +93 -0
- package/dist/DescriptionList/DescriptionList.manifest.js +40 -0
- package/dist/DescriptionList/index.js +9 -0
- package/dist/Dialog/Dialog.js +214 -0
- package/dist/Dialog/Dialog.manifest.js +31 -0
- package/dist/Dialog/index.js +7 -0
- package/dist/Divider/Divider.js +64 -0
- package/dist/Divider/Divider.manifest.js +21 -0
- package/dist/Divider/index.js +7 -0
- package/dist/DropdownMenu/DropdownMenu.js +170 -0
- package/dist/DropdownMenu/DropdownMenu.manifest.js +82 -0
- package/dist/DropdownMenu/index.js +16 -0
- package/dist/EntityPreview/EntityPreview.js +82 -0
- package/dist/EntityPreview/EntityPreview.manifest.js +43 -0
- package/dist/EntityPreview/index.js +7 -0
- package/dist/EventTimeline/EventTimeline.js +249 -0
- package/dist/EventTimeline/EventTimeline.manifest.js +57 -0
- package/dist/EventTimeline/index.js +7 -0
- package/dist/Field/Field.js +81 -0
- package/dist/Field/Field.manifest.js +32 -0
- package/dist/Field/index.js +9 -0
- package/dist/FileUpload/FileUpload.js +215 -0
- package/dist/FileUpload/FileUpload.manifest.js +48 -0
- package/dist/FileUpload/index.js +7 -0
- package/dist/FilterBar/FilterBar.js +173 -0
- package/dist/FilterBar/FilterBar.manifest.js +49 -0
- package/dist/FilterBar/index.js +7 -0
- package/dist/FormErrorSummary/FormErrorSummary.js +70 -0
- package/dist/FormErrorSummary/FormErrorSummary.manifest.js +29 -0
- package/dist/FormErrorSummary/index.js +9 -0
- package/dist/FormSection/FormSection.js +51 -0
- package/dist/FormSection/FormSection.manifest.js +30 -0
- package/dist/FormSection/index.js +7 -0
- package/dist/InlineEdit/InlineEdit.js +261 -0
- package/dist/InlineEdit/InlineEdit.manifest.js +40 -0
- package/dist/InlineEdit/index.js +7 -0
- package/dist/Input/Input.js +126 -0
- package/dist/Input/Input.manifest.js +49 -0
- package/dist/Input/index.js +7 -0
- package/dist/Inspector/Inspector.js +63 -0
- package/dist/Inspector/Inspector.manifest.js +35 -0
- package/dist/Inspector/index.js +7 -0
- package/dist/JobStatus/JobStatus.js +118 -0
- package/dist/JobStatus/JobStatus.manifest.js +53 -0
- package/dist/JobStatus/index.js +7 -0
- package/dist/LayerControl/LayerControl.js +199 -0
- package/dist/LayerControl/LayerControl.manifest.js +34 -0
- package/dist/LayerControl/index.js +7 -0
- package/dist/Link/Link.js +83 -0
- package/dist/Link/Link.manifest.js +37 -0
- package/dist/Link/index.js +7 -0
- package/dist/MapControls/MapControls.js +122 -0
- package/dist/MapControls/MapControls.manifest.js +31 -0
- package/dist/MapControls/index.js +7 -0
- package/dist/MapLegend/MapLegend.js +89 -0
- package/dist/MapLegend/MapLegend.manifest.js +29 -0
- package/dist/MapLegend/index.js +7 -0
- package/dist/MapScale/MapScale.js +96 -0
- package/dist/MapScale/MapScale.manifest.js +31 -0
- package/dist/MapScale/index.js +7 -0
- package/dist/MediaFrame/MediaFrame.js +113 -0
- package/dist/MediaFrame/MediaFrame.manifest.js +57 -0
- package/dist/MediaFrame/index.js +7 -0
- package/dist/MultiSelect/MultiSelect.js +113 -0
- package/dist/MultiSelect/MultiSelect.manifest.js +51 -0
- package/dist/MultiSelect/index.js +7 -0
- package/dist/NumberInput/NumberInput.js +210 -0
- package/dist/NumberInput/NumberInput.manifest.js +56 -0
- package/dist/NumberInput/index.js +7 -0
- package/dist/OperationalStatus/OperationalStatus.js +89 -0
- package/dist/OperationalStatus/OperationalStatus.manifest.js +39 -0
- package/dist/OperationalStatus/index.js +9 -0
- package/dist/PlaybackControls/PlaybackControls.js +289 -0
- package/dist/PlaybackControls/PlaybackControls.manifest.js +85 -0
- package/dist/PlaybackControls/index.js +7 -0
- package/dist/Popover/Popover.js +302 -0
- package/dist/Popover/Popover.manifest.js +50 -0
- package/dist/Popover/index.js +7 -0
- package/dist/Progress/Progress.js +132 -0
- package/dist/Progress/Progress.manifest.js +30 -0
- package/dist/Progress/index.js +7 -0
- package/dist/PropertyFilter/PropertyFilter.js +306 -0
- package/dist/PropertyFilter/PropertyFilter.manifest.js +32 -0
- package/dist/PropertyFilter/index.js +11 -0
- package/dist/PropertyFilter/tokenError.js +17 -0
- package/dist/RadioGroup/RadioGroup.js +143 -0
- package/dist/RadioGroup/RadioGroup.manifest.js +44 -0
- package/dist/RadioGroup/index.js +9 -0
- package/dist/Select/Select.js +219 -0
- package/dist/Select/Select.manifest.js +77 -0
- package/dist/Select/index.js +9 -0
- package/dist/Sidebar/Sidebar.js +146 -0
- package/dist/Sidebar/Sidebar.manifest.js +72 -0
- package/dist/Sidebar/index.js +18 -0
- package/dist/Skeleton/Skeleton.js +76 -0
- package/dist/Skeleton/Skeleton.manifest.js +30 -0
- package/dist/Skeleton/index.js +7 -0
- package/dist/Slider/Slider.js +246 -0
- package/dist/Slider/Slider.manifest.js +56 -0
- package/dist/Slider/index.js +7 -0
- package/dist/Sparkline/Sparkline.js +135 -0
- package/dist/Sparkline/Sparkline.manifest.js +37 -0
- package/dist/Sparkline/index.js +7 -0
- package/dist/SplitPane/SplitPane.js +316 -0
- package/dist/SplitPane/SplitPane.manifest.js +79 -0
- package/dist/SplitPane/index.js +7 -0
- package/dist/StatCard/StatCard.js +163 -0
- package/dist/StatCard/StatCard.manifest.js +45 -0
- package/dist/StatCard/index.js +7 -0
- package/dist/Tabs/Tabs.js +181 -0
- package/dist/Tabs/Tabs.manifest.js +72 -0
- package/dist/Tabs/index.js +16 -0
- package/dist/Tabs/useTabsIndicator.js +71 -0
- package/dist/Tabs/useTabsScroll.js +123 -0
- package/dist/TagGroup/TagGroup.js +142 -0
- package/dist/TagGroup/TagGroup.manifest.js +35 -0
- package/dist/TagGroup/index.js +7 -0
- package/dist/Textarea/Textarea.js +68 -0
- package/dist/Textarea/Textarea.manifest.js +25 -0
- package/dist/Textarea/index.js +7 -0
- package/dist/TimeField/TimeField.js +118 -0
- package/dist/TimeField/TimeField.manifest.js +42 -0
- package/dist/TimeField/index.js +7 -0
- package/dist/TimeRange/TimeRange.js +184 -0
- package/dist/TimeRange/TimeRange.manifest.js +48 -0
- package/dist/TimeRange/index.js +9 -0
- package/dist/Toast/Toast.js +188 -0
- package/dist/Toast/Toast.manifest.js +77 -0
- package/dist/Toast/index.js +9 -0
- package/dist/Toggle/Toggle.js +154 -0
- package/dist/Toggle/Toggle.manifest.js +54 -0
- package/dist/Toggle/index.js +7 -0
- package/dist/Toolbar/Toolbar.js +86 -0
- package/dist/Toolbar/Toolbar.manifest.js +70 -0
- package/dist/Toolbar/index.js +12 -0
- package/dist/Tooltip/Tooltip.js +123 -0
- package/dist/Tooltip/Tooltip.manifest.js +27 -0
- package/dist/Tooltip/index.js +7 -0
- package/dist/TreeView/TreeView.js +129 -0
- package/dist/TreeView/TreeView.manifest.js +56 -0
- package/dist/TreeView/index.js +7 -0
- package/dist/UploadQueue/UploadQueue.js +296 -0
- package/dist/UploadQueue/UploadQueue.manifest.js +34 -0
- package/dist/UploadQueue/index.js +7 -0
- package/dist/_internal/MotionPopover.js +113 -0
- package/dist/_internal/aria.js +40 -0
- package/dist/_internal/bytes.js +18 -0
- package/dist/_internal/controlStyles.js +26 -0
- package/dist/_internal/date.js +179 -0
- package/dist/_internal/devValidate.js +51 -0
- package/dist/_internal/fieldIds.js +50 -0
- package/dist/_internal/hydration.js +74 -0
- package/dist/_internal/manifest-entry.js +0 -0
- package/dist/_internal/mergeRefs.js +68 -0
- package/dist/_internal/motion.js +138 -0
- package/dist/_internal/navigationLandmark.js +13 -0
- package/dist/_internal/numberText.js +176 -0
- package/dist/_internal/overlay.js +53 -0
- package/dist/_internal/segmentField.js +125 -0
- package/dist/_internal/suggestions.js +20 -0
- package/dist/_internal/timestamp.js +25 -0
- package/dist/_internal/unavailable.js +13 -0
- package/dist/_internal/useActionFocus.js +27 -0
- package/dist/_internal/useElementWidth.js +37 -0
- package/dist/_internal/useIsMounted.js +18 -0
- package/dist/_internal/useIsomorphicLayoutEffect.js +8 -0
- package/dist/_internal/useRestoreFocusOnRemoval.js +57 -0
- package/dist/index.cjs +17831 -0
- package/dist/index.d.cts +2330 -0
- package/dist/index.d.ts +2330 -0
- package/dist/index.js +407 -0
- package/dist/version.js +8 -0
- package/package.json +80 -3
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/PlaybackControls/PlaybackControls.tsx
|
|
4
|
+
import { cva } from "class-variance-authority";
|
|
5
|
+
import clsx from "clsx";
|
|
6
|
+
import {
|
|
7
|
+
forwardRef,
|
|
8
|
+
useId,
|
|
9
|
+
useImperativeHandle,
|
|
10
|
+
useMemo,
|
|
11
|
+
useRef
|
|
12
|
+
} from "react";
|
|
13
|
+
import { useLocale } from "react-aria-components";
|
|
14
|
+
import { Button } from "../Button/Button.js";
|
|
15
|
+
import { Select, SelectOption } from "../Select/Select.js";
|
|
16
|
+
import { Slider } from "../Slider/Slider.js";
|
|
17
|
+
import { joinIds } from "../_internal/fieldIds.js";
|
|
18
|
+
import { formatUtcTimestamp, isValidTimestamp } from "../_internal/timestamp.js";
|
|
19
|
+
import { useRestoreFocusOnRemoval } from "../_internal/useRestoreFocusOnRemoval.js";
|
|
20
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
21
|
+
var rootStyles = cva(
|
|
22
|
+
"flex min-w-0 flex-col gap-2u font-sans text-oprt-text outline-hidden focus-visible:shadow-oprt-focus"
|
|
23
|
+
);
|
|
24
|
+
var timestampStyles = "font-mono text-oprt-base tabular-nums break-words";
|
|
25
|
+
var defaultRates = [0.5, 1, 2, 4];
|
|
26
|
+
var validTime = (value) => isValidTimestamp(value);
|
|
27
|
+
var formatTime = (value) => formatUtcTimestamp(value, { precision: "milliseconds" }) ?? "Unknown";
|
|
28
|
+
var validRate = (rate) => Number.isFinite(rate) && rate > 0;
|
|
29
|
+
var readableSteps = [
|
|
30
|
+
1,
|
|
31
|
+
10,
|
|
32
|
+
50,
|
|
33
|
+
100,
|
|
34
|
+
500,
|
|
35
|
+
1e3,
|
|
36
|
+
5e3,
|
|
37
|
+
1e4,
|
|
38
|
+
3e4,
|
|
39
|
+
6e4,
|
|
40
|
+
3e5,
|
|
41
|
+
6e5,
|
|
42
|
+
18e5,
|
|
43
|
+
36e5,
|
|
44
|
+
216e5,
|
|
45
|
+
432e5,
|
|
46
|
+
864e5
|
|
47
|
+
];
|
|
48
|
+
function keyboardStep(span) {
|
|
49
|
+
const target = span / 200;
|
|
50
|
+
let best = readableSteps[0];
|
|
51
|
+
for (const step of readableSteps) {
|
|
52
|
+
if (Math.abs(Math.log(step / target)) < Math.abs(Math.log(best / target))) best = step;
|
|
53
|
+
}
|
|
54
|
+
return best;
|
|
55
|
+
}
|
|
56
|
+
function firstAfter(times, value) {
|
|
57
|
+
let low = 0;
|
|
58
|
+
let high = times.length;
|
|
59
|
+
while (low < high) {
|
|
60
|
+
const middle = low + high >>> 1;
|
|
61
|
+
if (times[middle] <= value) low = middle + 1;
|
|
62
|
+
else high = middle;
|
|
63
|
+
}
|
|
64
|
+
return low;
|
|
65
|
+
}
|
|
66
|
+
function arrowDirection(key, rightToLeft) {
|
|
67
|
+
if (key === "ArrowUp" || key === "Up") return 1;
|
|
68
|
+
if (key === "ArrowDown" || key === "Down") return -1;
|
|
69
|
+
if (key === "ArrowRight" || key === "Right") return rightToLeft ? -1 : 1;
|
|
70
|
+
if (key === "ArrowLeft" || key === "Left") return rightToLeft ? 1 : -1;
|
|
71
|
+
return 0;
|
|
72
|
+
}
|
|
73
|
+
var PlaybackControls = forwardRef(
|
|
74
|
+
({
|
|
75
|
+
start,
|
|
76
|
+
end,
|
|
77
|
+
value,
|
|
78
|
+
playing,
|
|
79
|
+
onValueChange,
|
|
80
|
+
onPlayingChange,
|
|
81
|
+
playbackRate = 1,
|
|
82
|
+
playbackRates = defaultRates,
|
|
83
|
+
onPlaybackRateChange,
|
|
84
|
+
eventTimes,
|
|
85
|
+
liveTime,
|
|
86
|
+
followingLive = false,
|
|
87
|
+
onReturnToLive,
|
|
88
|
+
disabled = false,
|
|
89
|
+
label = "Playback",
|
|
90
|
+
className,
|
|
91
|
+
"aria-describedby": describedBy,
|
|
92
|
+
onKeyDownCapture,
|
|
93
|
+
onFocusCapture,
|
|
94
|
+
onBlurCapture,
|
|
95
|
+
...props
|
|
96
|
+
}, ref) => {
|
|
97
|
+
const descriptionId = useId();
|
|
98
|
+
const root = useRef(null);
|
|
99
|
+
useImperativeHandle(ref, () => root.current, []);
|
|
100
|
+
const previousRef = useRef(null);
|
|
101
|
+
const playRef = useRef(null);
|
|
102
|
+
const nextRef = useRef(null);
|
|
103
|
+
const { direction } = useLocale();
|
|
104
|
+
const validBounds = validTime(start) && validTime(end);
|
|
105
|
+
const validRange = validBounds && start < end;
|
|
106
|
+
const validCursor = validTime(value);
|
|
107
|
+
const inRange = validCursor && value >= start && value <= end;
|
|
108
|
+
const unavailable = !validBounds ? "Supply a valid start and end date in UTC." : start === end ? "A single timestamp is available. Playback requires a time range." : start > end ? "Start must be before end." : !validCursor ? "Invalid playback position. Supply a valid timestamp in UTC." : !inRange ? "The playback position is outside the playback range." : "";
|
|
109
|
+
const transportDisabled = disabled || !!unavailable;
|
|
110
|
+
const sliderStart = validRange ? start : 0;
|
|
111
|
+
const sliderEnd = validRange ? end : 1;
|
|
112
|
+
const displayValue = validRange && validCursor ? Math.min(end, Math.max(start, value)) : sliderStart;
|
|
113
|
+
const positionText = validRange && validCursor ? formatTime(displayValue) : validBounds && start === end ? formatTime(start) : "Unknown playback position";
|
|
114
|
+
const events = useMemo(
|
|
115
|
+
() => [
|
|
116
|
+
...new Set(
|
|
117
|
+
(eventTimes ?? []).filter((time) => validTime(time) && time >= start && time <= end)
|
|
118
|
+
)
|
|
119
|
+
].sort((a, b) => a - b),
|
|
120
|
+
[eventTimes, start, end]
|
|
121
|
+
);
|
|
122
|
+
const after = firstAfter(events, displayValue);
|
|
123
|
+
const next = events[after];
|
|
124
|
+
const previous = events[events[after - 1] === displayValue ? after - 2 : after - 1];
|
|
125
|
+
const rates = [...new Set(playbackRates.filter(validRate))];
|
|
126
|
+
if (validRate(playbackRate) && !rates.includes(playbackRate)) rates.push(playbackRate);
|
|
127
|
+
rates.sort((a, b) => a - b);
|
|
128
|
+
const canReturn = !!onReturnToLive && liveTime !== void 0 && validTime(liveTime);
|
|
129
|
+
const requestTime = (time) => {
|
|
130
|
+
if (transportDisabled || !validTime(time) || time < start || time > end) return;
|
|
131
|
+
onPlayingChange(false);
|
|
132
|
+
onValueChange(time);
|
|
133
|
+
};
|
|
134
|
+
const stepByKeyboard = (event) => {
|
|
135
|
+
const target = event.target;
|
|
136
|
+
if (event.shiftKey || !(target instanceof HTMLInputElement) || target.type !== "range")
|
|
137
|
+
return;
|
|
138
|
+
const sign = arrowDirection(event.key, direction === "rtl");
|
|
139
|
+
if (sign === 0 || transportDisabled || !validRange || !validCursor) return;
|
|
140
|
+
event.preventDefault();
|
|
141
|
+
event.stopPropagation();
|
|
142
|
+
const next2 = Math.min(end, Math.max(start, displayValue + sign * keyboardStep(end - start)));
|
|
143
|
+
if (next2 !== displayValue) requestTime(next2);
|
|
144
|
+
};
|
|
145
|
+
const restoreFocus = useRestoreFocusOnRemoval(
|
|
146
|
+
root,
|
|
147
|
+
{
|
|
148
|
+
track: (element) => element === previousRef.current ? "previous" : element === nextRef.current ? "next" : "other",
|
|
149
|
+
findTarget: (which) => {
|
|
150
|
+
const usable = (button) => button?.isConnected && !button.disabled ? button : null;
|
|
151
|
+
const sibling = which === "previous" ? nextRef.current : which === "next" ? previousRef.current : null;
|
|
152
|
+
return usable(sibling) ?? usable(playRef.current) ?? root.current;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
);
|
|
156
|
+
return /* @__PURE__ */ jsxs(
|
|
157
|
+
"div",
|
|
158
|
+
{
|
|
159
|
+
...props,
|
|
160
|
+
ref: root,
|
|
161
|
+
role: "group",
|
|
162
|
+
tabIndex: -1,
|
|
163
|
+
"aria-label": label,
|
|
164
|
+
"aria-describedby": joinIds(describedBy, unavailable ? descriptionId : void 0),
|
|
165
|
+
className: clsx(rootStyles(), className),
|
|
166
|
+
onKeyDownCapture: (event) => {
|
|
167
|
+
onKeyDownCapture?.(event);
|
|
168
|
+
stepByKeyboard(event);
|
|
169
|
+
},
|
|
170
|
+
onFocusCapture: (event) => {
|
|
171
|
+
onFocusCapture?.(event);
|
|
172
|
+
restoreFocus.onFocusCapture(event);
|
|
173
|
+
},
|
|
174
|
+
onBlurCapture: (event) => {
|
|
175
|
+
onBlurCapture?.(event);
|
|
176
|
+
restoreFocus.onBlurCapture(event);
|
|
177
|
+
},
|
|
178
|
+
children: [
|
|
179
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-1u", children: [
|
|
180
|
+
eventTimes !== void 0 && /* @__PURE__ */ jsx(
|
|
181
|
+
Button,
|
|
182
|
+
{
|
|
183
|
+
ref: previousRef,
|
|
184
|
+
variant: "tertiary",
|
|
185
|
+
size: "sm",
|
|
186
|
+
disabled: transportDisabled || previous === void 0,
|
|
187
|
+
onClick: () => {
|
|
188
|
+
if (previous !== void 0) requestTime(previous);
|
|
189
|
+
},
|
|
190
|
+
children: "Previous event"
|
|
191
|
+
}
|
|
192
|
+
),
|
|
193
|
+
/* @__PURE__ */ jsx(
|
|
194
|
+
Button,
|
|
195
|
+
{
|
|
196
|
+
ref: playRef,
|
|
197
|
+
variant: "secondary",
|
|
198
|
+
size: "sm",
|
|
199
|
+
disabled: transportDisabled,
|
|
200
|
+
onClick: () => {
|
|
201
|
+
if (!transportDisabled) onPlayingChange(!playing);
|
|
202
|
+
},
|
|
203
|
+
children: playing ? "Pause" : "Play"
|
|
204
|
+
}
|
|
205
|
+
),
|
|
206
|
+
eventTimes !== void 0 && /* @__PURE__ */ jsx(
|
|
207
|
+
Button,
|
|
208
|
+
{
|
|
209
|
+
ref: nextRef,
|
|
210
|
+
variant: "tertiary",
|
|
211
|
+
size: "sm",
|
|
212
|
+
disabled: transportDisabled || next === void 0,
|
|
213
|
+
onClick: () => {
|
|
214
|
+
if (next !== void 0) requestTime(next);
|
|
215
|
+
},
|
|
216
|
+
children: "Next event"
|
|
217
|
+
}
|
|
218
|
+
),
|
|
219
|
+
onPlaybackRateChange && /* @__PURE__ */ jsx(
|
|
220
|
+
Select,
|
|
221
|
+
{
|
|
222
|
+
"aria-label": `${label} speed`,
|
|
223
|
+
size: "sm",
|
|
224
|
+
value: validRate(playbackRate) ? String(playbackRate) : "",
|
|
225
|
+
placeholder: "Unknown rate",
|
|
226
|
+
disabled: transportDisabled || rates.length === 0,
|
|
227
|
+
onValueChange: (selected) => {
|
|
228
|
+
const rate = Number(selected);
|
|
229
|
+
if (!transportDisabled && validRate(rate) && rates.includes(rate))
|
|
230
|
+
onPlaybackRateChange(rate);
|
|
231
|
+
},
|
|
232
|
+
children: rates.map((rate) => /* @__PURE__ */ jsx(SelectOption, { value: String(rate), children: `${rate}\xD7` }, rate))
|
|
233
|
+
}
|
|
234
|
+
),
|
|
235
|
+
canReturn && /* @__PURE__ */ jsx(
|
|
236
|
+
Button,
|
|
237
|
+
{
|
|
238
|
+
variant: "tertiary",
|
|
239
|
+
size: "sm",
|
|
240
|
+
disabled: transportDisabled || followingLive,
|
|
241
|
+
onClick: () => {
|
|
242
|
+
if (!transportDisabled && !followingLive) onReturnToLive?.();
|
|
243
|
+
},
|
|
244
|
+
children: "Return to live"
|
|
245
|
+
}
|
|
246
|
+
),
|
|
247
|
+
canReturn && followingLive && /* @__PURE__ */ jsx("span", { className: "text-oprt-base font-medium uppercase tracking-oprt-caps", children: "Following live" })
|
|
248
|
+
] }),
|
|
249
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-baseline justify-between gap-1u", children: [
|
|
250
|
+
/* @__PURE__ */ jsxs("span", { className: "text-oprt-base font-medium uppercase tracking-oprt-caps", children: [
|
|
251
|
+
label,
|
|
252
|
+
" position"
|
|
253
|
+
] }),
|
|
254
|
+
/* @__PURE__ */ jsx("span", { className: timestampStyles, children: positionText })
|
|
255
|
+
] }),
|
|
256
|
+
/* @__PURE__ */ jsx(
|
|
257
|
+
Slider,
|
|
258
|
+
{
|
|
259
|
+
"aria-label": `${label} position`,
|
|
260
|
+
"aria-describedby": unavailable ? descriptionId : void 0,
|
|
261
|
+
min: sliderStart,
|
|
262
|
+
max: sliderEnd,
|
|
263
|
+
value: displayValue,
|
|
264
|
+
step: 1,
|
|
265
|
+
disabled: transportDisabled,
|
|
266
|
+
formatValue: validRange && validCursor ? formatTime : () => positionText,
|
|
267
|
+
onValueChange: requestTime
|
|
268
|
+
}
|
|
269
|
+
),
|
|
270
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap justify-between gap-1u text-oprt-text-dim", children: [
|
|
271
|
+
/* @__PURE__ */ jsxs("span", { className: timestampStyles, children: [
|
|
272
|
+
"Start: ",
|
|
273
|
+
validTime(start) ? formatTime(start) : "Unknown"
|
|
274
|
+
] }),
|
|
275
|
+
/* @__PURE__ */ jsxs("span", { className: timestampStyles, children: [
|
|
276
|
+
"End: ",
|
|
277
|
+
validTime(end) ? formatTime(end) : "Unknown"
|
|
278
|
+
] })
|
|
279
|
+
] }),
|
|
280
|
+
unavailable && /* @__PURE__ */ jsx("p", { id: descriptionId, className: "m-0 text-oprt-base text-oprt-text-dim", children: unavailable })
|
|
281
|
+
]
|
|
282
|
+
}
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
);
|
|
286
|
+
PlaybackControls.displayName = "PlaybackControls";
|
|
287
|
+
export {
|
|
288
|
+
PlaybackControls
|
|
289
|
+
};
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
// src/PlaybackControls/PlaybackControls.manifest.ts
|
|
2
|
+
var PlaybackControlsManifest = {
|
|
3
|
+
PlaybackControls: {
|
|
4
|
+
description: "Controlled UTC timestamp transport with a bounded scrubber, play/pause, optional event stepping, playback speed and return to a supplied live reference.",
|
|
5
|
+
props: {
|
|
6
|
+
start: {
|
|
7
|
+
type: "number",
|
|
8
|
+
required: true,
|
|
9
|
+
description: "Inclusive range start in Unix epoch milliseconds. Must be a valid date before end; zero is valid."
|
|
10
|
+
},
|
|
11
|
+
end: {
|
|
12
|
+
type: "number",
|
|
13
|
+
required: true,
|
|
14
|
+
description: "Inclusive range end in Unix epoch milliseconds. Invalid or single-point ranges disable transport with explanatory text."
|
|
15
|
+
},
|
|
16
|
+
value: {
|
|
17
|
+
type: "number",
|
|
18
|
+
required: true,
|
|
19
|
+
description: "Controlled cursor in Unix epoch milliseconds. Out-of-range values clamp for display and disable transport; invalid dates display an unknown position."
|
|
20
|
+
},
|
|
21
|
+
playing: {
|
|
22
|
+
type: "boolean",
|
|
23
|
+
required: true,
|
|
24
|
+
description: "Caller-owned playback state. The component owns no clock and makes no optimistic state changes."
|
|
25
|
+
},
|
|
26
|
+
onValueChange: {
|
|
27
|
+
type: "function",
|
|
28
|
+
required: true,
|
|
29
|
+
description: "(value: number) => void. Requests a valid in-range timestamp after requesting pause."
|
|
30
|
+
},
|
|
31
|
+
onPlayingChange: {
|
|
32
|
+
type: "function",
|
|
33
|
+
required: true,
|
|
34
|
+
description: "(playing: boolean) => void. Requests play/pause. Scrubbing and event stepping call this with false before onValueChange."
|
|
35
|
+
},
|
|
36
|
+
playbackRate: {
|
|
37
|
+
type: "number",
|
|
38
|
+
default: 1,
|
|
39
|
+
description: "Controlled positive finite speed multiplier. A valid supplied rate remains visible, in order, even when absent from playbackRates; invalid rates display Unknown rate."
|
|
40
|
+
},
|
|
41
|
+
playbackRates: {
|
|
42
|
+
type: "array",
|
|
43
|
+
description: "Readonly number array of offered speed multipliers. Defaults to [0.5, 1, 2, 4]. Non-positive/non-finite rates and duplicates are omitted. Listed in ascending order."
|
|
44
|
+
},
|
|
45
|
+
onPlaybackRateChange: {
|
|
46
|
+
type: "function",
|
|
47
|
+
description: "(rate: number) => void. Supplying this callback shows the controlled speed selector."
|
|
48
|
+
},
|
|
49
|
+
eventTimes: {
|
|
50
|
+
type: "array",
|
|
51
|
+
description: "Readonly epoch-millisecond array. Supplying it shows previous/next event buttons. Times are sorted, deduplicated and filtered to valid dates within the range; stepping skips the current time."
|
|
52
|
+
},
|
|
53
|
+
liveTime: {
|
|
54
|
+
type: "number",
|
|
55
|
+
description: "Application-supplied live reference in epoch milliseconds. May lie outside the review range; the application owns any range change."
|
|
56
|
+
},
|
|
57
|
+
followingLive: {
|
|
58
|
+
type: "boolean",
|
|
59
|
+
default: false,
|
|
60
|
+
description: "Shows Following live and disables Return to live while the caller follows its reference."
|
|
61
|
+
},
|
|
62
|
+
onReturnToLive: {
|
|
63
|
+
type: "function",
|
|
64
|
+
description: "() => void. Shows Return to live only when liveTime is a valid date. Requests no cursor, range or playing change itself."
|
|
65
|
+
},
|
|
66
|
+
disabled: {
|
|
67
|
+
type: "boolean",
|
|
68
|
+
default: false,
|
|
69
|
+
description: "Disables all transport actions and removes them from sequential keyboard navigation."
|
|
70
|
+
},
|
|
71
|
+
label: {
|
|
72
|
+
type: "string",
|
|
73
|
+
default: "Playback",
|
|
74
|
+
description: "Accessible root-group name. Also names the position slider and playback-speed selector."
|
|
75
|
+
},
|
|
76
|
+
className: { type: "string" }
|
|
77
|
+
},
|
|
78
|
+
composable_with: ["MediaFrame", "TimeRange", "EventTimeline", "Chart"],
|
|
79
|
+
never_inside: ["Button"],
|
|
80
|
+
example: "<PlaybackControls start={start} end={end} value={cursor} playing={playing} onValueChange={setCursor} onPlayingChange={setPlaying} eventTimes={eventTimes} playbackRate={rate} onPlaybackRateChange={setRate} liveTime={liveTime} followingLive={followingLive} onReturnToLive={returnToLive} />"
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
export {
|
|
84
|
+
PlaybackControlsManifest
|
|
85
|
+
};
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/Popover/Popover.tsx
|
|
4
|
+
import { DialogTrigger, Pressable } from "react-aria-components";
|
|
5
|
+
import {
|
|
6
|
+
cloneElement,
|
|
7
|
+
forwardRef,
|
|
8
|
+
useCallback,
|
|
9
|
+
useEffect,
|
|
10
|
+
useId,
|
|
11
|
+
useRef,
|
|
12
|
+
useState,
|
|
13
|
+
version
|
|
14
|
+
} from "react";
|
|
15
|
+
import { cva } from "class-variance-authority";
|
|
16
|
+
import clsx from "clsx";
|
|
17
|
+
import { Button } from "../Button/index.js";
|
|
18
|
+
import { Tooltip } from "../Tooltip/index.js";
|
|
19
|
+
import { PopoverManifest } from "./Popover.manifest.js";
|
|
20
|
+
import { devValidate } from "../_internal/devValidate.js";
|
|
21
|
+
import { useMergedRefs } from "../_internal/mergeRefs.js";
|
|
22
|
+
import { MotionContext, useMotion } from "../_internal/motion.js";
|
|
23
|
+
import { MotionPopover } from "../_internal/MotionPopover.js";
|
|
24
|
+
import { POPOVER_WIDTH, SCREEN_MARGIN, toPlacement } from "../_internal/overlay.js";
|
|
25
|
+
import { useIsomorphicLayoutEffect } from "../_internal/useIsomorphicLayoutEffect.js";
|
|
26
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
27
|
+
var surfaceStyles = cva(
|
|
28
|
+
`z-50 border border-oprt-border bg-oprt-surface-1 font-sans text-oprt-base text-oprt-text ${POPOVER_WIDTH} max-h-[calc(100dvh-var(--oprt-space-4))] overflow-y-auto`
|
|
29
|
+
);
|
|
30
|
+
var dialogStyles = "outline-hidden p-3u focus-visible:border-oprt-border-focus focus-visible:shadow-oprt-focus focus-visible:outline-solid focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-oprt-border-focus forced-colors:focus-visible:outline-[CanvasText]";
|
|
31
|
+
function topSurface() {
|
|
32
|
+
const surfaces = document.querySelectorAll(
|
|
33
|
+
".oprt-motion-popover:not([data-exiting]):not([inert])"
|
|
34
|
+
);
|
|
35
|
+
return surfaces[surfaces.length - 1];
|
|
36
|
+
}
|
|
37
|
+
var Popover = forwardRef(
|
|
38
|
+
({
|
|
39
|
+
children,
|
|
40
|
+
content,
|
|
41
|
+
title,
|
|
42
|
+
"aria-label": ariaLabel,
|
|
43
|
+
open,
|
|
44
|
+
defaultOpen = false,
|
|
45
|
+
onOpenChange,
|
|
46
|
+
side = "bottom",
|
|
47
|
+
align = "start",
|
|
48
|
+
disabled = false,
|
|
49
|
+
className,
|
|
50
|
+
motion = "auto",
|
|
51
|
+
description,
|
|
52
|
+
returnFocusRef,
|
|
53
|
+
onCloseAutoFocus,
|
|
54
|
+
id: suppliedId,
|
|
55
|
+
...dialogProps
|
|
56
|
+
}, ref) => {
|
|
57
|
+
const [localOpen, setLocalOpen] = useState(defaultOpen);
|
|
58
|
+
const isOpen = open ?? localOpen;
|
|
59
|
+
const [positioning, setPositioning] = useState(true);
|
|
60
|
+
const [tooltipOpen, setTooltipOpen] = useState(false);
|
|
61
|
+
const motionState = useMotion(motion);
|
|
62
|
+
if (process.env.NODE_ENV !== "production") {
|
|
63
|
+
devValidate("Popover", PopoverManifest.Popover.props, { ...dialogProps, side, align });
|
|
64
|
+
}
|
|
65
|
+
const generatedId = useId();
|
|
66
|
+
const id = suppliedId ?? `oprt-popover-${generatedId}`;
|
|
67
|
+
const trigger = useRef(null);
|
|
68
|
+
const lastTrigger = useRef(null);
|
|
69
|
+
const dialog = useRef(null);
|
|
70
|
+
const lastDialog = useRef(null);
|
|
71
|
+
const wasOpen = useRef(false);
|
|
72
|
+
const focusFrame = useRef();
|
|
73
|
+
const focusScrollClose = useRef(false);
|
|
74
|
+
const current = useRef({ isOpen, onCloseAutoFocus, returnFocusRef });
|
|
75
|
+
current.current = { isOpen, onCloseAutoFocus, returnFocusRef };
|
|
76
|
+
const childRef = parseInt(version, 10) < 19 ? children.ref : children.props.ref;
|
|
77
|
+
const rememberTrigger = useCallback((node) => {
|
|
78
|
+
trigger.current = node;
|
|
79
|
+
if (node) lastTrigger.current = node;
|
|
80
|
+
}, []);
|
|
81
|
+
const setTrigger = useMergedRefs(rememberTrigger, childRef);
|
|
82
|
+
const rememberDialog = useCallback((node) => {
|
|
83
|
+
dialog.current = node;
|
|
84
|
+
if (node) lastDialog.current = node;
|
|
85
|
+
}, []);
|
|
86
|
+
const setDialog = useMergedRefs(rememberDialog, ref);
|
|
87
|
+
const restoreFocus = useCallback(() => {
|
|
88
|
+
const event = new Event("popovercloseautofocus", { cancelable: true });
|
|
89
|
+
current.current.onCloseAutoFocus?.(event);
|
|
90
|
+
if (event.defaultPrevented) return;
|
|
91
|
+
if (focusFrame.current !== void 0) cancelAnimationFrame(focusFrame.current);
|
|
92
|
+
focusFrame.current = requestAnimationFrame(() => {
|
|
93
|
+
focusFrame.current = void 0;
|
|
94
|
+
if (current.current.isOpen && dialog.current?.isConnected) return;
|
|
95
|
+
const active = document.activeElement;
|
|
96
|
+
if (active && active !== document.body && active.isConnected && !lastDialog.current?.contains(active))
|
|
97
|
+
return;
|
|
98
|
+
const target = lastTrigger.current?.isConnected ? lastTrigger.current : current.current.returnFocusRef?.current;
|
|
99
|
+
if (target?.isConnected) target.focus({ preventScroll: true });
|
|
100
|
+
});
|
|
101
|
+
}, []);
|
|
102
|
+
useEffect(() => {
|
|
103
|
+
if (isOpen && !wasOpen.current) {
|
|
104
|
+
if (focusFrame.current !== void 0) cancelAnimationFrame(focusFrame.current);
|
|
105
|
+
setTooltipOpen(false);
|
|
106
|
+
dialog.current?.focus({ preventScroll: true });
|
|
107
|
+
}
|
|
108
|
+
if (!isOpen && wasOpen.current) restoreFocus();
|
|
109
|
+
wasOpen.current = isOpen;
|
|
110
|
+
}, [isOpen, restoreFocus]);
|
|
111
|
+
useEffect(
|
|
112
|
+
() => () => {
|
|
113
|
+
if (wasOpen.current && !dialog.current?.isConnected) restoreFocus();
|
|
114
|
+
},
|
|
115
|
+
[restoreFocus]
|
|
116
|
+
);
|
|
117
|
+
const requestOpen = useCallback(
|
|
118
|
+
(next) => {
|
|
119
|
+
if (next && disabled) return;
|
|
120
|
+
if (!next && focusScrollClose.current) return;
|
|
121
|
+
if (open === void 0) setLocalOpen(next);
|
|
122
|
+
onOpenChange?.(next);
|
|
123
|
+
},
|
|
124
|
+
[disabled, open, onOpenChange]
|
|
125
|
+
);
|
|
126
|
+
useIsomorphicLayoutEffect(() => {
|
|
127
|
+
if (!positioning) setPositioning(true);
|
|
128
|
+
}, [positioning]);
|
|
129
|
+
useIsomorphicLayoutEffect(() => {
|
|
130
|
+
if (!isOpen) return;
|
|
131
|
+
let settlingFocus = true;
|
|
132
|
+
let explicitScroll = false;
|
|
133
|
+
let anchorRect = trigger.current?.getBoundingClientRect();
|
|
134
|
+
let scrollTask;
|
|
135
|
+
let frame;
|
|
136
|
+
const settleFocus = () => {
|
|
137
|
+
cancelAnimationFrame(frame);
|
|
138
|
+
settlingFocus = true;
|
|
139
|
+
explicitScroll = false;
|
|
140
|
+
frame = requestAnimationFrame(() => {
|
|
141
|
+
frame = requestAnimationFrame(() => {
|
|
142
|
+
settlingFocus = false;
|
|
143
|
+
anchorRect = trigger.current?.getBoundingClientRect();
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
};
|
|
147
|
+
settleFocus();
|
|
148
|
+
const focus = (event) => {
|
|
149
|
+
if (dialog.current?.contains(event.target)) settleFocus();
|
|
150
|
+
};
|
|
151
|
+
const scroll = () => {
|
|
152
|
+
const nextRect = trigger.current?.getBoundingClientRect();
|
|
153
|
+
const anchored = !explicitScroll && anchorRect && nextRect && anchorRect.width > 0 && anchorRect.height > 0 && Math.abs(anchorRect.left - nextRect.left) < 0.5 && Math.abs(anchorRect.top - nextRect.top) < 0.5 && Math.abs(anchorRect.width - nextRect.width) < 0.5 && Math.abs(anchorRect.height - nextRect.height) < 0.5;
|
|
154
|
+
if (!settlingFocus && !anchored) {
|
|
155
|
+
focusScrollClose.current = false;
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
focusScrollClose.current = true;
|
|
159
|
+
setPositioning(false);
|
|
160
|
+
if (scrollTask !== void 0) clearTimeout(scrollTask);
|
|
161
|
+
scrollTask = setTimeout(() => {
|
|
162
|
+
focusScrollClose.current = false;
|
|
163
|
+
scrollTask = void 0;
|
|
164
|
+
}, 0);
|
|
165
|
+
};
|
|
166
|
+
const interact = () => {
|
|
167
|
+
settlingFocus = false;
|
|
168
|
+
explicitScroll = true;
|
|
169
|
+
focusScrollClose.current = false;
|
|
170
|
+
};
|
|
171
|
+
window.addEventListener("scroll", scroll, true);
|
|
172
|
+
window.addEventListener("focusin", focus, true);
|
|
173
|
+
for (const event of ["wheel", "pointerdown", "touchmove", "keydown"])
|
|
174
|
+
window.addEventListener(event, interact, true);
|
|
175
|
+
return () => {
|
|
176
|
+
cancelAnimationFrame(frame);
|
|
177
|
+
if (scrollTask !== void 0) clearTimeout(scrollTask);
|
|
178
|
+
focusScrollClose.current = false;
|
|
179
|
+
window.removeEventListener("scroll", scroll, true);
|
|
180
|
+
window.removeEventListener("focusin", focus, true);
|
|
181
|
+
for (const event of ["wheel", "pointerdown", "touchmove", "keydown"])
|
|
182
|
+
window.removeEventListener(event, interact, true);
|
|
183
|
+
};
|
|
184
|
+
}, [isOpen]);
|
|
185
|
+
useEffect(() => {
|
|
186
|
+
if (!isOpen) return;
|
|
187
|
+
let startedOutside = false;
|
|
188
|
+
const start = (event) => {
|
|
189
|
+
const surface = dialog.current?.parentElement;
|
|
190
|
+
const target = event.target;
|
|
191
|
+
startedOutside = event.button === 0 && !!surface && topSurface() === surface && !surface.contains(target) && !trigger.current?.contains(target);
|
|
192
|
+
};
|
|
193
|
+
const end = (event) => {
|
|
194
|
+
const surface = dialog.current?.parentElement;
|
|
195
|
+
const target = event.target;
|
|
196
|
+
if (startedOutside && surface && topSurface() === surface && !surface.contains(target) && !trigger.current?.contains(target))
|
|
197
|
+
requestOpen(false);
|
|
198
|
+
startedOutside = false;
|
|
199
|
+
};
|
|
200
|
+
const cancel = () => {
|
|
201
|
+
startedOutside = false;
|
|
202
|
+
};
|
|
203
|
+
document.addEventListener("pointerdown", start, true);
|
|
204
|
+
document.addEventListener("pointerup", end, true);
|
|
205
|
+
document.addEventListener("pointercancel", cancel, true);
|
|
206
|
+
return () => {
|
|
207
|
+
document.removeEventListener("pointerdown", start, true);
|
|
208
|
+
document.removeEventListener("pointerup", end, true);
|
|
209
|
+
document.removeEventListener("pointercancel", cancel, true);
|
|
210
|
+
};
|
|
211
|
+
}, [isOpen, requestOpen]);
|
|
212
|
+
const triggerElement = cloneElement(children, {
|
|
213
|
+
ref: setTrigger,
|
|
214
|
+
disabled: disabled || children.props.disabled,
|
|
215
|
+
"aria-disabled": disabled || children.props["aria-disabled"],
|
|
216
|
+
"aria-controls": isOpen ? id : void 0
|
|
217
|
+
});
|
|
218
|
+
const pressable = /* @__PURE__ */ jsx(Pressable, { isDisabled: disabled, children: triggerElement });
|
|
219
|
+
return /* @__PURE__ */ jsx(MotionContext.Provider, { value: motionState.mode, children: /* @__PURE__ */ jsxs(DialogTrigger, { isOpen, onOpenChange: requestOpen, children: [
|
|
220
|
+
/* @__PURE__ */ jsx("span", { className: "contents", ...motionState.props, children: description ? /* @__PURE__ */ jsx(
|
|
221
|
+
Tooltip,
|
|
222
|
+
{
|
|
223
|
+
content: description,
|
|
224
|
+
open: tooltipOpen && !isOpen,
|
|
225
|
+
onOpenChange: setTooltipOpen,
|
|
226
|
+
children: pressable
|
|
227
|
+
}
|
|
228
|
+
) : pressable }),
|
|
229
|
+
/* @__PURE__ */ jsx(
|
|
230
|
+
MotionPopover,
|
|
231
|
+
{
|
|
232
|
+
motionState,
|
|
233
|
+
focusManagement: "manual",
|
|
234
|
+
isNonModal: true,
|
|
235
|
+
shouldUpdatePosition: positioning,
|
|
236
|
+
placement: toPlacement(side, align),
|
|
237
|
+
offset: 4,
|
|
238
|
+
containerPadding: SCREEN_MARGIN,
|
|
239
|
+
className: clsx(surfaceStyles(), className),
|
|
240
|
+
shouldCloseOnInteractOutside: (element) => {
|
|
241
|
+
if (trigger.current?.contains(element)) return false;
|
|
242
|
+
const surface = dialog.current?.parentElement;
|
|
243
|
+
return !!surface && topSurface() === surface;
|
|
244
|
+
},
|
|
245
|
+
children: /* @__PURE__ */ jsxs(
|
|
246
|
+
"div",
|
|
247
|
+
{
|
|
248
|
+
...dialogProps,
|
|
249
|
+
ref: setDialog,
|
|
250
|
+
id,
|
|
251
|
+
role: "dialog",
|
|
252
|
+
tabIndex: -1,
|
|
253
|
+
"aria-label": ariaLabel,
|
|
254
|
+
"aria-labelledby": ariaLabel ? void 0 : `${id}-title`,
|
|
255
|
+
"aria-describedby": [dialogProps["aria-describedby"], description ? `${id}-description` : void 0].filter(Boolean).join(" ") || void 0,
|
|
256
|
+
"data-oprt-popover-dialog": "",
|
|
257
|
+
className: dialogStyles,
|
|
258
|
+
children: [
|
|
259
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3u mb-2u", children: [
|
|
260
|
+
title && /* @__PURE__ */ jsx(
|
|
261
|
+
"h2",
|
|
262
|
+
{
|
|
263
|
+
id: `${id}-title`,
|
|
264
|
+
className: "m-0 min-w-0 font-sans text-oprt-base font-semibold uppercase tracking-oprt-caps oprt-wrap-anywhere",
|
|
265
|
+
children: title
|
|
266
|
+
}
|
|
267
|
+
),
|
|
268
|
+
/* @__PURE__ */ jsx(
|
|
269
|
+
Button,
|
|
270
|
+
{
|
|
271
|
+
variant: "ghost",
|
|
272
|
+
size: "sm",
|
|
273
|
+
className: "ml-auto",
|
|
274
|
+
onClick: () => {
|
|
275
|
+
focusScrollClose.current = false;
|
|
276
|
+
requestOpen(false);
|
|
277
|
+
},
|
|
278
|
+
children: "Close"
|
|
279
|
+
}
|
|
280
|
+
)
|
|
281
|
+
] }),
|
|
282
|
+
description && /* @__PURE__ */ jsx(
|
|
283
|
+
"p",
|
|
284
|
+
{
|
|
285
|
+
id: `${id}-description`,
|
|
286
|
+
className: "m-0 mb-2u text-oprt-base normal-case text-oprt-text-dim oprt-wrap-anywhere",
|
|
287
|
+
children: description
|
|
288
|
+
}
|
|
289
|
+
),
|
|
290
|
+
content
|
|
291
|
+
]
|
|
292
|
+
}
|
|
293
|
+
)
|
|
294
|
+
}
|
|
295
|
+
)
|
|
296
|
+
] }) });
|
|
297
|
+
}
|
|
298
|
+
);
|
|
299
|
+
Popover.displayName = "Popover";
|
|
300
|
+
export {
|
|
301
|
+
Popover
|
|
302
|
+
};
|