@oprt/react 0.0.0-stage → 0.1.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 +110 -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 +122 -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 +17811 -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,118 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/JobStatus/JobStatus.tsx
|
|
4
|
+
import { forwardRef, useEffect, useState } from "react";
|
|
5
|
+
import { useActionFocus } from "../_internal/useActionFocus.js";
|
|
6
|
+
import { cva } from "class-variance-authority";
|
|
7
|
+
import clsx from "clsx";
|
|
8
|
+
import { ActionStatusLine } from "../ActionStatus/ActionStatus.js";
|
|
9
|
+
import { Progress } from "../Progress/Progress.js";
|
|
10
|
+
import { Button } from "../Button/Button.js";
|
|
11
|
+
import { devValidate } from "../_internal/devValidate.js";
|
|
12
|
+
import { JobStatusManifest } from "./JobStatus.manifest.js";
|
|
13
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
14
|
+
var jobStyles = cva(
|
|
15
|
+
"flex min-w-0 flex-col gap-2u border border-oprt-border p-2u font-sans text-oprt-base normal-case text-oprt-text outline-hidden focus-visible:shadow-oprt-focus"
|
|
16
|
+
);
|
|
17
|
+
var JobStatus = forwardRef((allProps, ref) => {
|
|
18
|
+
const {
|
|
19
|
+
jobId,
|
|
20
|
+
label,
|
|
21
|
+
state,
|
|
22
|
+
message,
|
|
23
|
+
progress,
|
|
24
|
+
requestPending = null,
|
|
25
|
+
requestError,
|
|
26
|
+
onCancelRequest,
|
|
27
|
+
onRetryRequest,
|
|
28
|
+
disabled = false,
|
|
29
|
+
className,
|
|
30
|
+
onFocusCapture,
|
|
31
|
+
onBlurCapture,
|
|
32
|
+
...props
|
|
33
|
+
} = allProps;
|
|
34
|
+
const focus = useActionFocus(ref, { onFocusCapture, onBlurCapture });
|
|
35
|
+
if (process.env.NODE_ENV !== "production") {
|
|
36
|
+
devValidate("JobStatus", JobStatusManifest.JobStatus.props, allProps);
|
|
37
|
+
}
|
|
38
|
+
const validProgress = !progress || Number.isSafeInteger(progress.completed) && progress.completed >= 0 && (progress.total === null || Number.isSafeInteger(progress.total) && progress.total >= 0 && progress.completed <= progress.total);
|
|
39
|
+
const canCancel = !!onCancelRequest && (state === "queued" || state === "running");
|
|
40
|
+
const canRetry = !!onRetryRequest && ["failed", "partial", "canceled"].includes(state);
|
|
41
|
+
const blocked = disabled || requestPending !== null;
|
|
42
|
+
const pendingText = requestPending ? `${requestPending === "cancel" ? "Cancellation" : "Retry"} request pending. Awaiting an application update.` : "";
|
|
43
|
+
const [spokenText, setSpokenText] = useState(pendingText);
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
let second = 0;
|
|
46
|
+
const first = requestAnimationFrame(() => {
|
|
47
|
+
second = requestAnimationFrame(() => setSpokenText(pendingText));
|
|
48
|
+
});
|
|
49
|
+
return () => {
|
|
50
|
+
cancelAnimationFrame(first);
|
|
51
|
+
cancelAnimationFrame(second);
|
|
52
|
+
};
|
|
53
|
+
}, [pendingText]);
|
|
54
|
+
return /* @__PURE__ */ jsxs(
|
|
55
|
+
"div",
|
|
56
|
+
{
|
|
57
|
+
...props,
|
|
58
|
+
...focus,
|
|
59
|
+
role: "region",
|
|
60
|
+
"aria-label": label,
|
|
61
|
+
tabIndex: -1,
|
|
62
|
+
className: clsx(jobStyles(), className),
|
|
63
|
+
children: [
|
|
64
|
+
/* @__PURE__ */ jsx(ActionStatusLine, { state, label, message, children: state === "cancel-requested" && /* @__PURE__ */ jsx("p", { className: "m-0 min-w-0 oprt-wrap-anywhere", children: "Cancellation was requested. Work may still finish." }) }),
|
|
65
|
+
/* @__PURE__ */ jsxs("p", { className: "m-0 min-w-0 oprt-wrap-anywhere font-mono", children: [
|
|
66
|
+
"Job: ",
|
|
67
|
+
jobId
|
|
68
|
+
] }),
|
|
69
|
+
!validProgress && /* @__PURE__ */ jsx("p", { className: "m-0 min-w-0 oprt-wrap-anywhere text-oprt-error", children: "Invalid progress. Supply nonnegative integer counts with completed no greater than total." }),
|
|
70
|
+
progress && validProgress && /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-col gap-1u", children: [
|
|
71
|
+
/* @__PURE__ */ jsx("p", { className: "m-0 min-w-0 oprt-wrap-anywhere font-mono tabular-nums", children: progress.total === 0 ? "0 items" : progress.total === null ? `${progress.completed} items processed; total unknown` : `${progress.completed} of ${progress.total} items processed` }),
|
|
72
|
+
progress.total !== 0 && /* @__PURE__ */ jsx(
|
|
73
|
+
Progress,
|
|
74
|
+
{
|
|
75
|
+
label: `${label} progress`,
|
|
76
|
+
...progress.total === null ? {} : { value: progress.completed, max: progress.total }
|
|
77
|
+
}
|
|
78
|
+
)
|
|
79
|
+
] }),
|
|
80
|
+
/* @__PURE__ */ jsx("p", { "aria-live": "polite", "aria-atomic": "true", className: "sr-only m-0", children: spokenText }),
|
|
81
|
+
requestPending && /* @__PURE__ */ jsx("p", { "aria-hidden": "true", className: "m-0 min-w-0 oprt-wrap-anywhere", children: pendingText }),
|
|
82
|
+
requestError && /* @__PURE__ */ jsx("p", { className: "m-0 min-w-0 oprt-wrap-anywhere text-oprt-error", role: "alert", children: requestError }),
|
|
83
|
+
(canCancel || canRetry) && /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-1u", children: [
|
|
84
|
+
canCancel && /* @__PURE__ */ jsx(
|
|
85
|
+
Button,
|
|
86
|
+
{
|
|
87
|
+
className: "max-w-full",
|
|
88
|
+
size: "sm",
|
|
89
|
+
variant: "tertiary",
|
|
90
|
+
disabled: blocked,
|
|
91
|
+
onClick: () => {
|
|
92
|
+
if (!blocked && canCancel) onCancelRequest?.(jobId);
|
|
93
|
+
},
|
|
94
|
+
children: "Request cancellation"
|
|
95
|
+
}
|
|
96
|
+
),
|
|
97
|
+
canRetry && /* @__PURE__ */ jsx(
|
|
98
|
+
Button,
|
|
99
|
+
{
|
|
100
|
+
className: "max-w-full",
|
|
101
|
+
size: "sm",
|
|
102
|
+
variant: "secondary",
|
|
103
|
+
disabled: blocked,
|
|
104
|
+
onClick: () => {
|
|
105
|
+
if (!blocked && canRetry) onRetryRequest?.(jobId);
|
|
106
|
+
},
|
|
107
|
+
children: "Request retry"
|
|
108
|
+
}
|
|
109
|
+
)
|
|
110
|
+
] })
|
|
111
|
+
]
|
|
112
|
+
}
|
|
113
|
+
);
|
|
114
|
+
});
|
|
115
|
+
JobStatus.displayName = "JobStatus";
|
|
116
|
+
export {
|
|
117
|
+
JobStatus
|
|
118
|
+
};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// src/JobStatus/JobStatus.manifest.ts
|
|
2
|
+
var JobStatusManifest = {
|
|
3
|
+
JobStatus: {
|
|
4
|
+
description: "Application-supplied job state and validated item progress with explicit cancellation/retry requests. Requests never establish a job outcome. A pending request is announced through a live region that is always present; native attributes reach the job region; long text wraps.",
|
|
5
|
+
props: {
|
|
6
|
+
jobId: { type: "string", required: true },
|
|
7
|
+
label: { type: "string", required: true },
|
|
8
|
+
state: {
|
|
9
|
+
type: "enum",
|
|
10
|
+
values: [
|
|
11
|
+
"queued",
|
|
12
|
+
"running",
|
|
13
|
+
"cancel-requested",
|
|
14
|
+
"canceled",
|
|
15
|
+
"complete",
|
|
16
|
+
"partial",
|
|
17
|
+
"failed"
|
|
18
|
+
],
|
|
19
|
+
required: true
|
|
20
|
+
},
|
|
21
|
+
message: { type: "string" },
|
|
22
|
+
progress: {
|
|
23
|
+
type: "node",
|
|
24
|
+
description: "{ completed: number; total: number | null }. Nonnegative safe integers; completed cannot exceed a known total. Null total is indeterminate; zero shows zero items without a percentage."
|
|
25
|
+
},
|
|
26
|
+
requestPending: {
|
|
27
|
+
type: "enum",
|
|
28
|
+
values: ["cancel", "retry"],
|
|
29
|
+
description: "Pending request kind, or null. Prevents duplicate requests without changing the supplied state."
|
|
30
|
+
},
|
|
31
|
+
requestError: {
|
|
32
|
+
type: "string",
|
|
33
|
+
description: "Request-delivery feedback supplied independently of the job outcome."
|
|
34
|
+
},
|
|
35
|
+
onCancelRequest: {
|
|
36
|
+
type: "function",
|
|
37
|
+
description: "(jobId) => void. Offered only while queued or running. Does not establish cancellation."
|
|
38
|
+
},
|
|
39
|
+
onRetryRequest: {
|
|
40
|
+
type: "function",
|
|
41
|
+
description: "(jobId) => void. Offered only for failed, partial, or canceled jobs. The application chooses eligible work and request identity."
|
|
42
|
+
},
|
|
43
|
+
disabled: { type: "boolean", default: false },
|
|
44
|
+
className: { type: "string" }
|
|
45
|
+
},
|
|
46
|
+
composable_with: ["ActionStatus", "Progress", "Button", "BulkResults"],
|
|
47
|
+
never_inside: ["Button"],
|
|
48
|
+
example: '<JobStatus jobId="import-1" label="Import equipment" state="cancel-requested" progress={{completed:2,total:5}} />'
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
export {
|
|
52
|
+
JobStatusManifest
|
|
53
|
+
};
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/LayerControl/LayerControl.tsx
|
|
4
|
+
import { forwardRef, useImperativeHandle, useRef } from "react";
|
|
5
|
+
import { cva } from "class-variance-authority";
|
|
6
|
+
import clsx from "clsx";
|
|
7
|
+
import { Button } from "../Button/Button.js";
|
|
8
|
+
import { Checkbox } from "../Checkbox/Checkbox.js";
|
|
9
|
+
import { Slider } from "../Slider/Slider.js";
|
|
10
|
+
import { Field } from "../Field/Field.js";
|
|
11
|
+
import { joinIds, usePartIds } from "../_internal/fieldIds.js";
|
|
12
|
+
import { useRestoreFocusOnRemoval } from "../_internal/useRestoreFocusOnRemoval.js";
|
|
13
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
14
|
+
var controlStyles = cva(
|
|
15
|
+
"flex min-w-0 flex-col gap-2u font-sans text-oprt-base text-oprt-text outline-hidden focus-visible:shadow-oprt-focus"
|
|
16
|
+
);
|
|
17
|
+
var rowStyles = cva(
|
|
18
|
+
"flex min-w-0 flex-col gap-2u border border-oprt-border p-2u outline-hidden focus-visible:shadow-oprt-focus"
|
|
19
|
+
);
|
|
20
|
+
var LayerControl = forwardRef(
|
|
21
|
+
({
|
|
22
|
+
label = "Map layers",
|
|
23
|
+
layers,
|
|
24
|
+
onLayersChange,
|
|
25
|
+
disabled = false,
|
|
26
|
+
className,
|
|
27
|
+
id,
|
|
28
|
+
onFocusCapture,
|
|
29
|
+
onBlurCapture,
|
|
30
|
+
"aria-labelledby": labelledBy,
|
|
31
|
+
...props
|
|
32
|
+
}, ref) => {
|
|
33
|
+
const root = useRef(null);
|
|
34
|
+
const labelRef = useRef(null);
|
|
35
|
+
const ids = usePartIds(["label"], {
|
|
36
|
+
id,
|
|
37
|
+
readRendered: () => labelRef.current?.id.replace(/-label$/, "")
|
|
38
|
+
});
|
|
39
|
+
const rows = useRef(/* @__PURE__ */ new Map());
|
|
40
|
+
useImperativeHandle(ref, () => root.current, []);
|
|
41
|
+
const invalidIds = layers.some(
|
|
42
|
+
(item, index) => !item.id.trim() || layers.findIndex((other) => other.id === item.id) !== index
|
|
43
|
+
);
|
|
44
|
+
const blocked = disabled || !onLayersChange || invalidIds;
|
|
45
|
+
const unavailable = (index) => blocked || layers[index]?.disabled === true;
|
|
46
|
+
const canMove = (index, direction) => !unavailable(index) && index + direction >= 0 && index + direction < layers.length && !unavailable(index + direction);
|
|
47
|
+
const emit = (next) => {
|
|
48
|
+
if (!blocked) onLayersChange?.(Object.freeze(next.map((item) => Object.freeze({ ...item }))));
|
|
49
|
+
};
|
|
50
|
+
const change = (index, patch) => {
|
|
51
|
+
if (!unavailable(index))
|
|
52
|
+
emit(layers.map((item, position) => position === index ? { ...item, ...patch } : item));
|
|
53
|
+
};
|
|
54
|
+
const move = (index, direction) => {
|
|
55
|
+
if (!canMove(index, direction)) return;
|
|
56
|
+
const next = [...layers];
|
|
57
|
+
[next[index], next[index + direction]] = [next[index + direction], next[index]];
|
|
58
|
+
emit(next);
|
|
59
|
+
};
|
|
60
|
+
const restoreFocus = useRestoreFocusOnRemoval(
|
|
61
|
+
root,
|
|
62
|
+
{
|
|
63
|
+
track: (element) => {
|
|
64
|
+
const index = layers.findIndex((item) => rows.current.get(item.id)?.contains(element));
|
|
65
|
+
return index < 0 ? null : { id: layers[index].id, index };
|
|
66
|
+
},
|
|
67
|
+
findTarget: ({ id: layerId, index }) => rows.current.get(layerId) ?? rows.current.get(layers[Math.min(index, layers.length - 1)]?.id ?? "") ?? root.current
|
|
68
|
+
}
|
|
69
|
+
);
|
|
70
|
+
return /* @__PURE__ */ jsxs(
|
|
71
|
+
"div",
|
|
72
|
+
{
|
|
73
|
+
...props,
|
|
74
|
+
id,
|
|
75
|
+
ref: root,
|
|
76
|
+
role: "group",
|
|
77
|
+
tabIndex: -1,
|
|
78
|
+
"aria-labelledby": joinIds(ids.label, labelledBy),
|
|
79
|
+
className: clsx(controlStyles(), className),
|
|
80
|
+
onFocusCapture: (event) => {
|
|
81
|
+
onFocusCapture?.(event);
|
|
82
|
+
restoreFocus.onFocusCapture(event);
|
|
83
|
+
},
|
|
84
|
+
onBlurCapture: (event) => {
|
|
85
|
+
onBlurCapture?.(event);
|
|
86
|
+
restoreFocus.onBlurCapture(event);
|
|
87
|
+
},
|
|
88
|
+
children: [
|
|
89
|
+
/* @__PURE__ */ jsx(
|
|
90
|
+
"h3",
|
|
91
|
+
{
|
|
92
|
+
ref: labelRef,
|
|
93
|
+
id: ids.label,
|
|
94
|
+
className: "m-0 min-w-0 font-medium uppercase tracking-oprt-sm oprt-wrap-anywhere",
|
|
95
|
+
children: label
|
|
96
|
+
}
|
|
97
|
+
),
|
|
98
|
+
/* @__PURE__ */ jsx("p", { className: "m-0 text-oprt-text-dim", children: "Top of this list is drawn on top of the map." }),
|
|
99
|
+
invalidIds && /* @__PURE__ */ jsx("p", { className: "m-0 text-oprt-error", children: "Layer IDs must be unique and nonempty. Editing is unavailable until corrected." }),
|
|
100
|
+
layers.length === 0 ? /* @__PURE__ */ jsx("p", { className: "m-0", children: "No layers supplied." }) : /* @__PURE__ */ jsx("ol", { "aria-label": label, className: "m-0 flex min-w-0 list-none flex-col gap-2u p-0", children: layers.map((item, index) => {
|
|
101
|
+
const validOpacity = Number.isFinite(item.opacity) && item.opacity >= 0 && item.opacity <= 1;
|
|
102
|
+
return /* @__PURE__ */ jsxs(
|
|
103
|
+
"li",
|
|
104
|
+
{
|
|
105
|
+
ref: (node) => {
|
|
106
|
+
if (node) rows.current.set(item.id, node);
|
|
107
|
+
else rows.current.delete(item.id);
|
|
108
|
+
},
|
|
109
|
+
tabIndex: -1,
|
|
110
|
+
className: rowStyles(),
|
|
111
|
+
children: [
|
|
112
|
+
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-start gap-1u", children: [
|
|
113
|
+
/* @__PURE__ */ jsx(
|
|
114
|
+
Checkbox,
|
|
115
|
+
{
|
|
116
|
+
id: `${ids.base}-visibility-${index}`,
|
|
117
|
+
"aria-label": `Show ${item.label}`,
|
|
118
|
+
checked: item.visible,
|
|
119
|
+
disabled: unavailable(index),
|
|
120
|
+
onCheckedChange: (checked) => {
|
|
121
|
+
if (typeof checked === "boolean") change(index, { visible: checked });
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
),
|
|
125
|
+
/* @__PURE__ */ jsx(
|
|
126
|
+
"label",
|
|
127
|
+
{
|
|
128
|
+
htmlFor: `${ids.base}-visibility-${index}`,
|
|
129
|
+
className: "m-0 min-w-0 font-medium uppercase tracking-oprt-sm oprt-wrap-anywhere",
|
|
130
|
+
children: item.label
|
|
131
|
+
}
|
|
132
|
+
)
|
|
133
|
+
] }),
|
|
134
|
+
item.description && /* @__PURE__ */ jsx("p", { className: "m-0 text-oprt-text-dim oprt-wrap-anywhere", children: item.description }),
|
|
135
|
+
validOpacity ? /* @__PURE__ */ jsxs(
|
|
136
|
+
Field,
|
|
137
|
+
{
|
|
138
|
+
label: `${item.label} opacity percent`,
|
|
139
|
+
className: "min-w-0 oprt-wrap-anywhere [&>label]:uppercase [&>label]:tracking-oprt-sm",
|
|
140
|
+
children: [
|
|
141
|
+
/* @__PURE__ */ jsx(
|
|
142
|
+
Slider,
|
|
143
|
+
{
|
|
144
|
+
min: 0,
|
|
145
|
+
max: 100,
|
|
146
|
+
step: 1,
|
|
147
|
+
value: item.opacity * 100,
|
|
148
|
+
disabled: unavailable(index),
|
|
149
|
+
onValueChange: (value) => {
|
|
150
|
+
if (Number.isFinite(value) && value >= 0 && value <= 100)
|
|
151
|
+
change(index, { opacity: value / 100 });
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
),
|
|
155
|
+
/* @__PURE__ */ jsxs("p", { className: "m-0 font-mono tabular-nums", children: [
|
|
156
|
+
Math.round(item.opacity * 100),
|
|
157
|
+
"%"
|
|
158
|
+
] })
|
|
159
|
+
]
|
|
160
|
+
}
|
|
161
|
+
) : /* @__PURE__ */ jsx("p", { className: "m-0 text-oprt-text-dim", children: "Opacity unavailable" }),
|
|
162
|
+
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-wrap gap-1u", children: [
|
|
163
|
+
/* @__PURE__ */ jsx(
|
|
164
|
+
Button,
|
|
165
|
+
{
|
|
166
|
+
size: "sm",
|
|
167
|
+
variant: "tertiary",
|
|
168
|
+
"aria-label": `Move ${item.label} up`,
|
|
169
|
+
disabled: !canMove(index, -1),
|
|
170
|
+
onClick: () => move(index, -1),
|
|
171
|
+
children: "Move up"
|
|
172
|
+
}
|
|
173
|
+
),
|
|
174
|
+
/* @__PURE__ */ jsx(
|
|
175
|
+
Button,
|
|
176
|
+
{
|
|
177
|
+
size: "sm",
|
|
178
|
+
variant: "tertiary",
|
|
179
|
+
"aria-label": `Move ${item.label} down`,
|
|
180
|
+
disabled: !canMove(index, 1),
|
|
181
|
+
onClick: () => move(index, 1),
|
|
182
|
+
children: "Move down"
|
|
183
|
+
}
|
|
184
|
+
)
|
|
185
|
+
] })
|
|
186
|
+
]
|
|
187
|
+
},
|
|
188
|
+
invalidIds ? index : item.id
|
|
189
|
+
);
|
|
190
|
+
}) })
|
|
191
|
+
]
|
|
192
|
+
}
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
);
|
|
196
|
+
LayerControl.displayName = "LayerControl";
|
|
197
|
+
export {
|
|
198
|
+
LayerControl
|
|
199
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// src/LayerControl/LayerControl.manifest.ts
|
|
2
|
+
var LayerControlManifest = {
|
|
3
|
+
LayerControl: {
|
|
4
|
+
description: "Controlled top-first map layers with visibility, opacity and keyboard-operable ordering.",
|
|
5
|
+
props: {
|
|
6
|
+
label: { type: "string", default: "Map layers" },
|
|
7
|
+
layers: {
|
|
8
|
+
type: "array",
|
|
9
|
+
required: true,
|
|
10
|
+
description: "Readonly LayerControlItem[]. IDs must be unique and nonempty. First item is drawn on top. Nonfinite/out-of-range opacity stays unavailable; zero is transparent.",
|
|
11
|
+
items: {
|
|
12
|
+
id: "string",
|
|
13
|
+
label: "string",
|
|
14
|
+
visible: "boolean",
|
|
15
|
+
opacity: "number (0..1)",
|
|
16
|
+
description: "string (optional)",
|
|
17
|
+
disabled: "boolean (optional)"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
onLayersChange: {
|
|
21
|
+
type: "function",
|
|
22
|
+
description: "(layers: readonly LayerControlItem[]) => void. Receives a new frozen array of copied frozen items. Requests do not change supplied values. Omit for read-only controls. Disabled rows cannot mutate or be crossed."
|
|
23
|
+
},
|
|
24
|
+
disabled: { type: "boolean", default: false },
|
|
25
|
+
className: { type: "string" }
|
|
26
|
+
},
|
|
27
|
+
composable_with: ["MapControls", "MapLegend", "Slider", "Checkbox", "Field"],
|
|
28
|
+
never_inside: ["Button"],
|
|
29
|
+
example: "<LayerControl layers={layers} onLayersChange={setLayers} />"
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
export {
|
|
33
|
+
LayerControlManifest
|
|
34
|
+
};
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/Link/Link.tsx
|
|
4
|
+
import { cva } from "class-variance-authority";
|
|
5
|
+
import clsx from "clsx";
|
|
6
|
+
import {
|
|
7
|
+
Children,
|
|
8
|
+
forwardRef,
|
|
9
|
+
isValidElement
|
|
10
|
+
} from "react";
|
|
11
|
+
import { jsx } from "react/jsx-runtime";
|
|
12
|
+
var linkStyles = cva(
|
|
13
|
+
"min-h-oprt-sm min-w-oprt-sm border border-transparent py-[calc((var(--oprt-height-sm)-2px-var(--oprt-line-base))/2)] box-decoration-clone font-sans text-oprt-base font-medium text-oprt-accent underline underline-offset-2 oprt-wrap-anywhere [&_svg]:inline-block hover:text-oprt-accent-hover aria-disabled:text-[color:inherit] aria-disabled:hover:text-[color:inherit] aria-disabled:no-underline aria-disabled:cursor-default focus-visible:outline-hidden focus-visible:border-oprt-border-focus focus-visible:shadow-oprt-focus forced-colors:focus-visible:outline-solid forced-colors:focus-visible:outline-2 forced-colors:focus-visible:outline-[CanvasText]",
|
|
14
|
+
{
|
|
15
|
+
variants: {
|
|
16
|
+
// An icon-only link is a 24px box of its own, with the icon centered in it.
|
|
17
|
+
iconOnly: { true: "inline-flex items-center justify-center", false: "" }
|
|
18
|
+
},
|
|
19
|
+
defaultVariants: { iconOnly: false }
|
|
20
|
+
}
|
|
21
|
+
);
|
|
22
|
+
function isIconOnly(children) {
|
|
23
|
+
const parts = Children.toArray(children).filter(
|
|
24
|
+
(part) => !(typeof part === "string" && part.trim() === "")
|
|
25
|
+
);
|
|
26
|
+
const [only] = parts;
|
|
27
|
+
if (parts.length !== 1 || !isValidElement(only)) return false;
|
|
28
|
+
const hidden = only.props["aria-hidden"];
|
|
29
|
+
return only.type === "svg" || hidden === true || hidden === "true" || typeof only.type !== "string" && only.props.children == null;
|
|
30
|
+
}
|
|
31
|
+
var SAFE_SCHEMES = /* @__PURE__ */ new Set(["http", "https", "mailto", "tel"]);
|
|
32
|
+
var DOWNLOAD_SCHEMES = /* @__PURE__ */ new Set(["blob", "data"]);
|
|
33
|
+
function schemeOf(destination) {
|
|
34
|
+
let start = 0;
|
|
35
|
+
let end = destination.length;
|
|
36
|
+
while (start < end && destination.charCodeAt(start) <= 32) start += 1;
|
|
37
|
+
while (end > start && destination.charCodeAt(end - 1) <= 32) end -= 1;
|
|
38
|
+
const url = destination.slice(start, end).replace(/[\t\n\r]/g, "");
|
|
39
|
+
const scheme = /^([a-z][a-z0-9+.-]*):/i.exec(url);
|
|
40
|
+
return scheme ? scheme[1].toLowerCase() : null;
|
|
41
|
+
}
|
|
42
|
+
function isSafeHref(destination, download) {
|
|
43
|
+
const scheme = schemeOf(destination);
|
|
44
|
+
return scheme === null || SAFE_SCHEMES.has(scheme) || download && DOWNLOAD_SCHEMES.has(scheme);
|
|
45
|
+
}
|
|
46
|
+
var reported = /* @__PURE__ */ new Set();
|
|
47
|
+
function reportUnsafeHref(destination) {
|
|
48
|
+
if (process.env.NODE_ENV === "production" || reported.has(destination)) return;
|
|
49
|
+
reported.add(destination);
|
|
50
|
+
console.error(
|
|
51
|
+
JSON.stringify(
|
|
52
|
+
{
|
|
53
|
+
component: "Link",
|
|
54
|
+
error: "UNSAFE_HREF",
|
|
55
|
+
prop: "href",
|
|
56
|
+
received: destination.slice(0, 80),
|
|
57
|
+
suggestion: "Link renders only http, https, mailto, tel, relative and hash destinations (and blob: or data: with download). Any other scheme renders as plain text, not a link and not a Tab stop. Use a Button for an action."
|
|
58
|
+
},
|
|
59
|
+
null,
|
|
60
|
+
2
|
|
61
|
+
)
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
var Link = forwardRef(
|
|
65
|
+
({ className, href, children, ...props }, ref) => {
|
|
66
|
+
const safe = typeof href !== "string" || isSafeHref(href, props.download !== void 0 && props.download !== false);
|
|
67
|
+
if (!safe) reportUnsafeHref(href);
|
|
68
|
+
return /* @__PURE__ */ jsx(
|
|
69
|
+
"a",
|
|
70
|
+
{
|
|
71
|
+
ref,
|
|
72
|
+
className: clsx(linkStyles({ iconOnly: isIconOnly(children) }), className),
|
|
73
|
+
...safe ? { href } : { "aria-disabled": true },
|
|
74
|
+
...props,
|
|
75
|
+
children
|
|
76
|
+
}
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
);
|
|
80
|
+
Link.displayName = "Link";
|
|
81
|
+
export {
|
|
82
|
+
Link
|
|
83
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// src/Link/Link.manifest.ts
|
|
2
|
+
var LinkManifest = {
|
|
3
|
+
Link: {
|
|
4
|
+
description: "Native anchor navigation with an evident link treatment and token-based keyboard focus. It is inline, so a link in a sentence wraps with the sentence; as a flex or grid item it is a 24px target. Only http, https, mailto, tel, relative and hash destinations are rendered.",
|
|
5
|
+
props: {
|
|
6
|
+
href: {
|
|
7
|
+
type: "string",
|
|
8
|
+
required: true,
|
|
9
|
+
description: "Native anchor destination; no click interception or router dependency. Only http, https, mailto, tel, relative and hash destinations are rendered; any other scheme (javascript:, vbscript:, data:, blob:, file:) renders as plain text, not a link and not a Tab stop. A blob: or data: destination is rendered when download is set; offer only files your page generates from content you trust, and give a file built from untrusted content to a Button that starts the download instead of a persistent link."
|
|
10
|
+
},
|
|
11
|
+
children: {
|
|
12
|
+
type: "node",
|
|
13
|
+
description: "Accessible link name. Authored names and prose preserve their case; provide aria-label for icon-only links."
|
|
14
|
+
},
|
|
15
|
+
target: {
|
|
16
|
+
type: "string",
|
|
17
|
+
description: "Native browsing context, such as _blank. Explain new-window behavior in the accessible name."
|
|
18
|
+
},
|
|
19
|
+
rel: {
|
|
20
|
+
type: "string",
|
|
21
|
+
description: "Native relationship attributes; use noopener noreferrer for external new-window links."
|
|
22
|
+
},
|
|
23
|
+
download: {
|
|
24
|
+
type: "union",
|
|
25
|
+
types: ["string", "boolean"],
|
|
26
|
+
description: "Native download attribute, subject to browser behavior."
|
|
27
|
+
},
|
|
28
|
+
className: { type: "string" }
|
|
29
|
+
},
|
|
30
|
+
composable_with: ["DescriptionList", "EntityPreview", "Popover", "Inspector", "Card"],
|
|
31
|
+
never_inside: ["Button", "Link"],
|
|
32
|
+
example: '<Link href="/equipment/pump-07">Pump 07</Link>'
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
export {
|
|
36
|
+
LinkManifest
|
|
37
|
+
};
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/MapControls/MapControls.tsx
|
|
4
|
+
import { forwardRef, useImperativeHandle, useRef } from "react";
|
|
5
|
+
import { useNumberFormatter } from "react-aria";
|
|
6
|
+
import { cva } from "class-variance-authority";
|
|
7
|
+
import clsx from "clsx";
|
|
8
|
+
import { Button } from "../Button/Button.js";
|
|
9
|
+
import { joinIds, usePartIds } from "../_internal/fieldIds.js";
|
|
10
|
+
import { useRestoreFocusOnRemoval } from "../_internal/useRestoreFocusOnRemoval.js";
|
|
11
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
12
|
+
var controlStyles = cva(
|
|
13
|
+
"flex min-w-0 flex-col gap-2u font-sans text-oprt-base text-oprt-text outline-hidden focus-visible:shadow-oprt-focus"
|
|
14
|
+
);
|
|
15
|
+
var READOUT = { maximumFractionDigits: 2 };
|
|
16
|
+
var shownBearing = (bearing) => Math.round(bearing * 100) / 100 % 360 + 0;
|
|
17
|
+
var actions = [
|
|
18
|
+
{ id: "zoom-in", label: "Zoom in" },
|
|
19
|
+
{ id: "zoom-out", label: "Zoom out" },
|
|
20
|
+
{ id: "pan-north", label: "Pan north" },
|
|
21
|
+
{ id: "pan-east", label: "Pan east" },
|
|
22
|
+
{ id: "pan-south", label: "Pan south" },
|
|
23
|
+
{ id: "pan-west", label: "Pan west" },
|
|
24
|
+
{ id: "reset-north", label: "Reset north" },
|
|
25
|
+
{ id: "fit", label: "Fit map" }
|
|
26
|
+
];
|
|
27
|
+
var MapControls = forwardRef(
|
|
28
|
+
({
|
|
29
|
+
label = "Map controls",
|
|
30
|
+
zoom = null,
|
|
31
|
+
bearing = null,
|
|
32
|
+
minZoom = 2,
|
|
33
|
+
maxZoom = 20,
|
|
34
|
+
onAction,
|
|
35
|
+
disabled = false,
|
|
36
|
+
className,
|
|
37
|
+
id,
|
|
38
|
+
"aria-labelledby": labelledBy,
|
|
39
|
+
onFocusCapture,
|
|
40
|
+
onBlurCapture,
|
|
41
|
+
...props
|
|
42
|
+
}, ref) => {
|
|
43
|
+
const root = useRef(null);
|
|
44
|
+
useImperativeHandle(ref, () => root.current, []);
|
|
45
|
+
const labelRef = useRef(null);
|
|
46
|
+
const ids = usePartIds(["label"], {
|
|
47
|
+
id,
|
|
48
|
+
readRendered: () => labelRef.current?.id.replace(/-label$/, "")
|
|
49
|
+
});
|
|
50
|
+
const readout = useNumberFormatter(READOUT);
|
|
51
|
+
const buttons = useRef(/* @__PURE__ */ new Map());
|
|
52
|
+
const limitsValid = Number.isFinite(minZoom) && Number.isFinite(maxZoom) && minZoom <= maxZoom;
|
|
53
|
+
const validZoom = typeof zoom === "number" && Number.isFinite(zoom) && limitsValid && zoom >= minZoom && zoom <= maxZoom;
|
|
54
|
+
const validBearing = typeof bearing === "number" && Number.isFinite(bearing) && bearing >= 0 && bearing < 360;
|
|
55
|
+
const unavailable = (action) => disabled || !onAction || action === "zoom-in" && (!validZoom || zoom >= maxZoom) || action === "zoom-out" && (!validZoom || zoom <= minZoom) || action === "reset-north" && !validBearing;
|
|
56
|
+
const restoreFocus = useRestoreFocusOnRemoval(root, {
|
|
57
|
+
track: (element) => [...buttons.current].find(([, button]) => button === element)?.[0] ?? null,
|
|
58
|
+
findTarget: (action) => {
|
|
59
|
+
const opposite = action === "zoom-in" ? "zoom-out" : action === "zoom-out" ? "zoom-in" : null;
|
|
60
|
+
const other = opposite ? buttons.current.get(opposite) : void 0;
|
|
61
|
+
return other?.isConnected && !other.disabled ? other : root.current;
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
return /* @__PURE__ */ jsxs(
|
|
65
|
+
"div",
|
|
66
|
+
{
|
|
67
|
+
...props,
|
|
68
|
+
id,
|
|
69
|
+
ref: root,
|
|
70
|
+
role: "group",
|
|
71
|
+
tabIndex: -1,
|
|
72
|
+
"aria-labelledby": joinIds(ids.label, labelledBy),
|
|
73
|
+
className: clsx(controlStyles(), className),
|
|
74
|
+
onFocusCapture: (event) => {
|
|
75
|
+
onFocusCapture?.(event);
|
|
76
|
+
restoreFocus.onFocusCapture(event);
|
|
77
|
+
},
|
|
78
|
+
onBlurCapture: (event) => {
|
|
79
|
+
onBlurCapture?.(event);
|
|
80
|
+
restoreFocus.onBlurCapture(event);
|
|
81
|
+
},
|
|
82
|
+
children: [
|
|
83
|
+
/* @__PURE__ */ jsx(
|
|
84
|
+
"h3",
|
|
85
|
+
{
|
|
86
|
+
ref: labelRef,
|
|
87
|
+
id: ids.label,
|
|
88
|
+
className: "m-0 min-w-0 font-medium uppercase tracking-oprt-sm oprt-wrap-anywhere",
|
|
89
|
+
children: label
|
|
90
|
+
}
|
|
91
|
+
),
|
|
92
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-2u font-mono tabular-nums", children: [
|
|
93
|
+
/* @__PURE__ */ jsx("p", { className: "m-0", children: validZoom ? `Zoom ${readout.format(zoom)}` : "Zoom unknown" }),
|
|
94
|
+
/* @__PURE__ */ jsx("p", { className: "m-0", children: validBearing ? `Bearing ${readout.format(shownBearing(bearing))}\xB0` : "Bearing unknown" })
|
|
95
|
+
] }),
|
|
96
|
+
!limitsValid && /* @__PURE__ */ jsx("p", { className: "m-0 text-oprt-error", children: "Zoom limits are invalid. Supply finite minimum and maximum values in order." }),
|
|
97
|
+
/* @__PURE__ */ jsx("div", { className: "flex min-w-0 flex-wrap gap-1u", children: actions.map((action) => /* @__PURE__ */ jsx(
|
|
98
|
+
Button,
|
|
99
|
+
{
|
|
100
|
+
ref: (button) => {
|
|
101
|
+
if (button) buttons.current.set(action.id, button);
|
|
102
|
+
else buttons.current.delete(action.id);
|
|
103
|
+
},
|
|
104
|
+
size: "sm",
|
|
105
|
+
variant: "secondary",
|
|
106
|
+
disabled: unavailable(action.id),
|
|
107
|
+
onClick: () => {
|
|
108
|
+
if (!unavailable(action.id)) onAction?.(action.id);
|
|
109
|
+
},
|
|
110
|
+
children: action.label
|
|
111
|
+
},
|
|
112
|
+
action.id
|
|
113
|
+
)) })
|
|
114
|
+
]
|
|
115
|
+
}
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
);
|
|
119
|
+
MapControls.displayName = "MapControls";
|
|
120
|
+
export {
|
|
121
|
+
MapControls
|
|
122
|
+
};
|