@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,261 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/InlineEdit/InlineEdit.tsx
|
|
4
|
+
import { forwardRef, useEffect, useRef, useState } from "react";
|
|
5
|
+
import { cva } from "class-variance-authority";
|
|
6
|
+
import clsx from "clsx";
|
|
7
|
+
import { Button } from "../Button/index.js";
|
|
8
|
+
import { Input } from "../Input/index.js";
|
|
9
|
+
import { Textarea } from "../Textarea/index.js";
|
|
10
|
+
import { joinIds, usePartIds } from "../_internal/fieldIds.js";
|
|
11
|
+
import { useIsMounted } from "../_internal/useIsMounted.js";
|
|
12
|
+
import { useMergedRefs } from "../_internal/mergeRefs.js";
|
|
13
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
14
|
+
var rootStyles = cva(
|
|
15
|
+
"flex min-w-0 flex-col gap-1u font-sans text-oprt-base normal-case tracking-normal text-oprt-text"
|
|
16
|
+
);
|
|
17
|
+
function matchesAttempt(attempt, value, label) {
|
|
18
|
+
const acceptedReplacement = value === attempt.submitted && value !== attempt.session.baseline;
|
|
19
|
+
return (label === attempt.session.label || acceptedReplacement) && (value === attempt.session.baseline || value === attempt.submitted);
|
|
20
|
+
}
|
|
21
|
+
var InlineEdit = forwardRef(
|
|
22
|
+
({ label, value, onSave, disabled = false, readOnly = false, validate, className, ...props }, ref) => {
|
|
23
|
+
const [editing, setEditing] = useState(false);
|
|
24
|
+
const [draft, setDraft] = useState(value);
|
|
25
|
+
const [error, setError] = useState(null);
|
|
26
|
+
const [pending, setPending] = useState(false);
|
|
27
|
+
const [multiline, setMultiline] = useState(false);
|
|
28
|
+
const [returnFocus, setReturnFocus] = useState(null);
|
|
29
|
+
const [changedTo, setChangedTo] = useState(null);
|
|
30
|
+
const rootRef = useRef(null);
|
|
31
|
+
const mergedRef = useMergedRefs(rootRef, ref);
|
|
32
|
+
const inputRef = useRef(null);
|
|
33
|
+
const editRef = useRef(null);
|
|
34
|
+
const refocusInput = useRef(false);
|
|
35
|
+
const session = useRef(null);
|
|
36
|
+
const request = useRef(null);
|
|
37
|
+
const composing = useRef(false);
|
|
38
|
+
const isMounted = useIsMounted();
|
|
39
|
+
const latest = useRef({ label, value, disabled, readOnly });
|
|
40
|
+
latest.current = { label, value, disabled, readOnly };
|
|
41
|
+
const ids = usePartIds(["error", "changed"]);
|
|
42
|
+
useEffect(
|
|
43
|
+
() => () => {
|
|
44
|
+
session.current = null;
|
|
45
|
+
request.current = null;
|
|
46
|
+
},
|
|
47
|
+
[]
|
|
48
|
+
);
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
const current = session.current;
|
|
51
|
+
if (!current) return;
|
|
52
|
+
const attempt = request.current;
|
|
53
|
+
if (disabled || readOnly || attempt && !matchesAttempt(attempt, value, label)) {
|
|
54
|
+
session.current = null;
|
|
55
|
+
request.current = null;
|
|
56
|
+
setEditing(false);
|
|
57
|
+
setPending(false);
|
|
58
|
+
setError(null);
|
|
59
|
+
setReturnFocus(null);
|
|
60
|
+
setChangedTo(null);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
if (attempt || current.label === label && current.baseline === value) return;
|
|
64
|
+
session.current = { baseline: value, label };
|
|
65
|
+
if (current.baseline !== value) setChangedTo(value);
|
|
66
|
+
}, [value, label, disabled, readOnly]);
|
|
67
|
+
useEffect(() => {
|
|
68
|
+
if (editing) inputRef.current?.focus();
|
|
69
|
+
}, [editing]);
|
|
70
|
+
useEffect(() => {
|
|
71
|
+
if (pending || !refocusInput.current) return;
|
|
72
|
+
refocusInput.current = false;
|
|
73
|
+
const active = document.activeElement;
|
|
74
|
+
if (active === null || active === document.body || rootRef.current?.contains(active))
|
|
75
|
+
inputRef.current?.focus();
|
|
76
|
+
}, [pending]);
|
|
77
|
+
useEffect(() => {
|
|
78
|
+
if (!returnFocus || editing) return;
|
|
79
|
+
if (!disabled && !readOnly && matchesAttempt(returnFocus, value, label))
|
|
80
|
+
editRef.current?.focus();
|
|
81
|
+
setReturnFocus(null);
|
|
82
|
+
}, [returnFocus, editing, disabled, readOnly, label, value]);
|
|
83
|
+
function start() {
|
|
84
|
+
if (disabled || readOnly) return;
|
|
85
|
+
session.current = { baseline: value, label };
|
|
86
|
+
composing.current = false;
|
|
87
|
+
setMultiline(/[\r\n]/.test(value));
|
|
88
|
+
setDraft(value);
|
|
89
|
+
setError(null);
|
|
90
|
+
setChangedTo(null);
|
|
91
|
+
setEditing(true);
|
|
92
|
+
}
|
|
93
|
+
function cancel() {
|
|
94
|
+
if (request.current || !session.current) return;
|
|
95
|
+
setReturnFocus({ session: session.current, submitted: value });
|
|
96
|
+
session.current = null;
|
|
97
|
+
setEditing(false);
|
|
98
|
+
setError(null);
|
|
99
|
+
setChangedTo(null);
|
|
100
|
+
}
|
|
101
|
+
function isCurrent(attempt) {
|
|
102
|
+
const current = latest.current;
|
|
103
|
+
return isMounted() && request.current === attempt && session.current === attempt.session && !current.disabled && !current.readOnly && matchesAttempt(attempt, current.value, current.label);
|
|
104
|
+
}
|
|
105
|
+
async function save() {
|
|
106
|
+
if (request.current || !session.current || disabled || readOnly || composing.current) return;
|
|
107
|
+
const validationError = validate?.(draft);
|
|
108
|
+
if (validationError) {
|
|
109
|
+
setError(validationError);
|
|
110
|
+
inputRef.current?.focus();
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
const attempt = { session: session.current, submitted: draft };
|
|
114
|
+
request.current = attempt;
|
|
115
|
+
setPending(true);
|
|
116
|
+
setError(null);
|
|
117
|
+
try {
|
|
118
|
+
await onSave(draft);
|
|
119
|
+
if (!isCurrent(attempt)) return;
|
|
120
|
+
request.current = null;
|
|
121
|
+
session.current = null;
|
|
122
|
+
setPending(false);
|
|
123
|
+
setEditing(false);
|
|
124
|
+
setChangedTo(null);
|
|
125
|
+
setReturnFocus(attempt);
|
|
126
|
+
} catch (reason) {
|
|
127
|
+
if (!isCurrent(attempt)) return;
|
|
128
|
+
request.current = null;
|
|
129
|
+
refocusInput.current = true;
|
|
130
|
+
setPending(false);
|
|
131
|
+
setError(
|
|
132
|
+
reason instanceof Error && reason.message ? reason.message : "Could not save. Try again."
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
const Control = multiline ? Textarea : Input;
|
|
137
|
+
return /* @__PURE__ */ jsx(
|
|
138
|
+
"div",
|
|
139
|
+
{
|
|
140
|
+
...props,
|
|
141
|
+
ref: mergedRef,
|
|
142
|
+
className: clsx(rootStyles(), className),
|
|
143
|
+
"aria-busy": pending || void 0,
|
|
144
|
+
children: editing ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
145
|
+
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-wrap items-center gap-1u", children: [
|
|
146
|
+
/* @__PURE__ */ jsx(
|
|
147
|
+
Control,
|
|
148
|
+
{
|
|
149
|
+
ref: (node) => {
|
|
150
|
+
inputRef.current = node;
|
|
151
|
+
},
|
|
152
|
+
"aria-label": label,
|
|
153
|
+
value: draft,
|
|
154
|
+
disabled: pending || disabled || readOnly,
|
|
155
|
+
"aria-invalid": Boolean(error),
|
|
156
|
+
"aria-describedby": joinIds(changedTo !== null && ids.changed, error && ids.error),
|
|
157
|
+
className: "min-w-0 flex-1",
|
|
158
|
+
onChange: (event) => {
|
|
159
|
+
setDraft(event.target.value);
|
|
160
|
+
setError(null);
|
|
161
|
+
},
|
|
162
|
+
onCompositionStart: () => {
|
|
163
|
+
composing.current = true;
|
|
164
|
+
},
|
|
165
|
+
onCompositionEnd: () => {
|
|
166
|
+
composing.current = false;
|
|
167
|
+
},
|
|
168
|
+
onKeyDown: (event) => {
|
|
169
|
+
if (composing.current || event.nativeEvent.isComposing || event.keyCode === 229)
|
|
170
|
+
return;
|
|
171
|
+
if (event.key === "Enter" && !multiline) {
|
|
172
|
+
event.preventDefault();
|
|
173
|
+
void save();
|
|
174
|
+
} else if (event.key === "Escape" && !pending) {
|
|
175
|
+
event.preventDefault();
|
|
176
|
+
event.stopPropagation();
|
|
177
|
+
cancel();
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
),
|
|
182
|
+
/* @__PURE__ */ jsx(
|
|
183
|
+
Button,
|
|
184
|
+
{
|
|
185
|
+
size: "sm",
|
|
186
|
+
"aria-label": `Save ${label}`,
|
|
187
|
+
disabled: pending || disabled || readOnly,
|
|
188
|
+
onClick: () => {
|
|
189
|
+
void save();
|
|
190
|
+
},
|
|
191
|
+
children: "Save"
|
|
192
|
+
}
|
|
193
|
+
),
|
|
194
|
+
/* @__PURE__ */ jsx(
|
|
195
|
+
Button,
|
|
196
|
+
{
|
|
197
|
+
size: "sm",
|
|
198
|
+
variant: "secondary",
|
|
199
|
+
"aria-label": `Cancel ${label}`,
|
|
200
|
+
disabled: pending,
|
|
201
|
+
onClick: cancel,
|
|
202
|
+
children: "Cancel"
|
|
203
|
+
}
|
|
204
|
+
)
|
|
205
|
+
] }),
|
|
206
|
+
/* @__PURE__ */ jsx(
|
|
207
|
+
"p",
|
|
208
|
+
{
|
|
209
|
+
id: ids.changed,
|
|
210
|
+
className: clsx(
|
|
211
|
+
"m-0 oprt-wrap-anywhere text-oprt-base text-oprt-text-dim",
|
|
212
|
+
changedTo === null && "sr-only"
|
|
213
|
+
),
|
|
214
|
+
role: "status",
|
|
215
|
+
children: changedTo !== null && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
216
|
+
"Changed to \u201C",
|
|
217
|
+
changedTo || "\u2014",
|
|
218
|
+
"\u201D while you were editing."
|
|
219
|
+
] })
|
|
220
|
+
}
|
|
221
|
+
),
|
|
222
|
+
/* @__PURE__ */ jsx(
|
|
223
|
+
"p",
|
|
224
|
+
{
|
|
225
|
+
className: clsx("m-0 text-oprt-base text-oprt-text-dim", !pending && "sr-only"),
|
|
226
|
+
role: "status",
|
|
227
|
+
children: pending && "Saving\u2026"
|
|
228
|
+
}
|
|
229
|
+
),
|
|
230
|
+
error && /* @__PURE__ */ jsx(
|
|
231
|
+
"p",
|
|
232
|
+
{
|
|
233
|
+
id: ids.error,
|
|
234
|
+
className: "m-0 oprt-wrap-anywhere text-oprt-base text-oprt-error",
|
|
235
|
+
role: "alert",
|
|
236
|
+
children: error
|
|
237
|
+
}
|
|
238
|
+
)
|
|
239
|
+
] }) : /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-wrap items-center gap-2u", children: [
|
|
240
|
+
/* @__PURE__ */ jsx("span", { className: "min-w-0 whitespace-break-spaces oprt-wrap-anywhere", children: value || "\u2014" }),
|
|
241
|
+
!readOnly && /* @__PURE__ */ jsx(
|
|
242
|
+
Button,
|
|
243
|
+
{
|
|
244
|
+
ref: editRef,
|
|
245
|
+
size: "sm",
|
|
246
|
+
variant: "ghost",
|
|
247
|
+
"aria-label": `Edit ${label}`,
|
|
248
|
+
disabled,
|
|
249
|
+
onClick: start,
|
|
250
|
+
children: "Edit"
|
|
251
|
+
}
|
|
252
|
+
)
|
|
253
|
+
] })
|
|
254
|
+
}
|
|
255
|
+
);
|
|
256
|
+
}
|
|
257
|
+
);
|
|
258
|
+
InlineEdit.displayName = "InlineEdit";
|
|
259
|
+
export {
|
|
260
|
+
InlineEdit
|
|
261
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// src/InlineEdit/InlineEdit.manifest.ts
|
|
2
|
+
var InlineEditManifest = {
|
|
3
|
+
InlineEdit: {
|
|
4
|
+
description: "Controlled inline text editing with explicit save, cancel and recoverable validation or save errors.",
|
|
5
|
+
props: {
|
|
6
|
+
label: {
|
|
7
|
+
type: "string",
|
|
8
|
+
required: true,
|
|
9
|
+
description: "Names the editor and its Edit, Save and Cancel actions."
|
|
10
|
+
},
|
|
11
|
+
value: {
|
|
12
|
+
type: "string",
|
|
13
|
+
required: true,
|
|
14
|
+
description: "Authoritative displayed value. Case is preserved. A change while the operator is typing keeps the draft and shows a status line naming the new value."
|
|
15
|
+
},
|
|
16
|
+
onSave: {
|
|
17
|
+
type: "function",
|
|
18
|
+
required: true,
|
|
19
|
+
description: "Receives the exact draft; return a promise for asynchronous acceptance and reject to retain the draft with an error."
|
|
20
|
+
},
|
|
21
|
+
validate: {
|
|
22
|
+
type: "function",
|
|
23
|
+
description: "Returns an error message or undefined before saving."
|
|
24
|
+
},
|
|
25
|
+
disabled: { type: "boolean", default: false },
|
|
26
|
+
readOnly: {
|
|
27
|
+
type: "boolean",
|
|
28
|
+
default: false,
|
|
29
|
+
description: "Displays the value without an Edit action."
|
|
30
|
+
},
|
|
31
|
+
className: { type: "string" }
|
|
32
|
+
},
|
|
33
|
+
composable_with: ["ContextMenu", "DataGrid", "FormSection"],
|
|
34
|
+
never_inside: ["Button"],
|
|
35
|
+
example: '<InlineEdit label="Equipment name" value={name} onSave={saveName} validate={(value) => value.trim() ? undefined : "Enter a name."} />'
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
export {
|
|
39
|
+
InlineEditManifest
|
|
40
|
+
};
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/Input/Input.tsx
|
|
4
|
+
import { cva } from "class-variance-authority";
|
|
5
|
+
import { forwardRef } from "react";
|
|
6
|
+
import clsx from "clsx";
|
|
7
|
+
import { InputManifest } from "./Input.manifest.js";
|
|
8
|
+
import { devValidate } from "../_internal/devValidate.js";
|
|
9
|
+
import { useFieldWiring } from "../_internal/fieldIds.js";
|
|
10
|
+
import { FORCED_FOCUS_WITHIN, FORCED_NO_RESTING_OUTLINE } from "../_internal/controlStyles.js";
|
|
11
|
+
import { Calendar, Clock } from "@oprt/icons";
|
|
12
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
13
|
+
var wrapperStyles = cva(
|
|
14
|
+
// Disabled looks like DC-9's disabled buttons (coordinator ruling on DC-B04-2): dim text, the
|
|
15
|
+
// control outline and a muted fill, with no fade of the whole field, which took an affix to
|
|
16
|
+
// 2.14:1 (Input-B2M-5); the outline also replaces an invalid field's red, as for danger buttons.
|
|
17
|
+
"inline-flex min-w-0 max-w-full items-center gap-1u border bg-oprt-surface-2 normal-case transition-colors duration-oprt-fast focus-within:shadow-oprt-focus has-[input:disabled]:cursor-not-allowed has-[input:disabled]:bg-oprt-surface-3 has-[input:disabled]:border-oprt-border-control",
|
|
18
|
+
{
|
|
19
|
+
variants: {
|
|
20
|
+
invalid: {
|
|
21
|
+
true: "border-oprt-error focus-within:border-oprt-error",
|
|
22
|
+
// The control outline (design call DC-3); focus in the focus token, as the siblings do.
|
|
23
|
+
false: "border-oprt-border-control focus-within:border-oprt-border-focus"
|
|
24
|
+
},
|
|
25
|
+
// The height is a minimum: a long prefix or suffix wraps in its own column and the field
|
|
26
|
+
// grows taller (coordinator ruling on DC-B04-3, Input-B2M-1..3), while a one-line field keeps
|
|
27
|
+
// exactly its token height, as DC-8 rules for buttons. The value's 24px line fits md and lg;
|
|
28
|
+
// sm caps it inside its 2px of border, as the fixed height did. A capped input cannot stretch
|
|
29
|
+
// to a field that grew taller, and a stretched item that hits its cap sits at the top, so sm
|
|
30
|
+
// centers it with auto margins instead (Stage 1 final review, I-2).
|
|
31
|
+
size: {
|
|
32
|
+
sm: "min-h-oprt-sm px-2u [&_input]:my-auto [&_input]:max-h-[calc(var(--oprt-height-sm)-2px)]",
|
|
33
|
+
md: "min-h-oprt-md px-2u",
|
|
34
|
+
lg: "min-h-oprt-lg px-3u"
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
defaultVariants: { invalid: false, size: "md" }
|
|
38
|
+
}
|
|
39
|
+
);
|
|
40
|
+
var inputStyles = cva(
|
|
41
|
+
// The value keeps about ten characters beside a long affix, and less only in a container
|
|
42
|
+
// narrower than that (Input-B2M-1..3); the field around it can shrink in a narrow host (Input-W0-1).
|
|
43
|
+
// It stretches to the field's height, so the whole field is the click and measured target and
|
|
44
|
+
// not the one line of text inside it (operations-map-R4); a text input centers its text itself.
|
|
45
|
+
"peer min-w-[min(10ch,100%)] flex-1 self-stretch bg-transparent border-0 outline-hidden font-sans font-medium text-oprt-md text-oprt-text placeholder:text-oprt-text-placeholder disabled:placeholder:text-oprt-text-dim disabled:cursor-not-allowed disabled:text-oprt-text-dim"
|
|
46
|
+
);
|
|
47
|
+
var Input = forwardRef((allProps, ref) => {
|
|
48
|
+
const {
|
|
49
|
+
className,
|
|
50
|
+
size,
|
|
51
|
+
prefix,
|
|
52
|
+
suffix,
|
|
53
|
+
promptSymbol,
|
|
54
|
+
type = "text",
|
|
55
|
+
id,
|
|
56
|
+
invalid: invalidProp,
|
|
57
|
+
required: requiredProp,
|
|
58
|
+
"aria-describedby": ariaDescribedBy,
|
|
59
|
+
"aria-invalid": ariaInvalid,
|
|
60
|
+
...props
|
|
61
|
+
} = allProps;
|
|
62
|
+
const {
|
|
63
|
+
id: finalId,
|
|
64
|
+
describedBy,
|
|
65
|
+
invalid,
|
|
66
|
+
ariaInvalid: finalAriaInvalid,
|
|
67
|
+
required
|
|
68
|
+
} = useFieldWiring(
|
|
69
|
+
{
|
|
70
|
+
id,
|
|
71
|
+
"aria-describedby": ariaDescribedBy,
|
|
72
|
+
"aria-invalid": ariaInvalid,
|
|
73
|
+
invalid: invalidProp,
|
|
74
|
+
required: requiredProp
|
|
75
|
+
},
|
|
76
|
+
{ label: "for" }
|
|
77
|
+
);
|
|
78
|
+
if (process.env.NODE_ENV !== "production") {
|
|
79
|
+
devValidate("Input", InputManifest.Input.props, { ...allProps });
|
|
80
|
+
}
|
|
81
|
+
const temporal = type === "date" || type === "time" || type === "datetime-local";
|
|
82
|
+
const PickerIcon = type === "time" ? Clock : Calendar;
|
|
83
|
+
const control = /* @__PURE__ */ jsx(
|
|
84
|
+
"input",
|
|
85
|
+
{
|
|
86
|
+
ref,
|
|
87
|
+
id: finalId,
|
|
88
|
+
type,
|
|
89
|
+
"aria-invalid": finalAriaInvalid,
|
|
90
|
+
"aria-describedby": describedBy,
|
|
91
|
+
required,
|
|
92
|
+
className: clsx(
|
|
93
|
+
inputStyles(),
|
|
94
|
+
// Forced colors paint outline-hidden's transparent outline; the field draws focus instead.
|
|
95
|
+
FORCED_NO_RESTING_OUTLINE,
|
|
96
|
+
temporal && "h-full w-full pr-3u [color-scheme:dark] [&::-webkit-calendar-picker-indicator]:absolute [&::-webkit-calendar-picker-indicator]:right-0 [&::-webkit-calendar-picker-indicator]:top-1/2 [&::-webkit-calendar-picker-indicator]:-translate-y-1/2 [&::-webkit-calendar-picker-indicator]:m-0 [&::-webkit-calendar-picker-indicator]:h-oprt-sm [&::-webkit-calendar-picker-indicator]:w-oprt-sm [&::-webkit-calendar-picker-indicator]:p-0 [&::-webkit-calendar-picker-indicator]:opacity-0 [&::-webkit-calendar-picker-indicator]:cursor-pointer"
|
|
97
|
+
),
|
|
98
|
+
...props
|
|
99
|
+
}
|
|
100
|
+
);
|
|
101
|
+
return (
|
|
102
|
+
// In forced colors, which drop the focus shadow and flatten the border, focus anywhere in the
|
|
103
|
+
// field is a 2px CanvasText outline around it, as for ComboBox (Input-B5-1).
|
|
104
|
+
/* @__PURE__ */ jsxs("div", { className: clsx(wrapperStyles({ invalid, size }), FORCED_FOCUS_WITHIN, className), children: [
|
|
105
|
+
promptSymbol && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "text-oprt-accent font-mono text-oprt-md", children: ">" }),
|
|
106
|
+
prefix && /* @__PURE__ */ jsx("span", { className: "min-w-0 oprt-wrap-anywhere text-oprt-text-dim", children: prefix }),
|
|
107
|
+
temporal ? /* @__PURE__ */ jsxs("div", { className: "relative flex min-w-0 flex-1 items-center self-stretch", children: [
|
|
108
|
+
control,
|
|
109
|
+
/* @__PURE__ */ jsx(
|
|
110
|
+
"span",
|
|
111
|
+
{
|
|
112
|
+
"data-oprt-picker-icon": "",
|
|
113
|
+
"aria-hidden": "true",
|
|
114
|
+
className: "pointer-events-none absolute inset-y-0 right-0 flex w-3u items-center justify-center bg-oprt-surface-2 text-oprt-text-dim peer-disabled:bg-oprt-surface-3",
|
|
115
|
+
children: /* @__PURE__ */ jsx(PickerIcon, { size: 16 })
|
|
116
|
+
}
|
|
117
|
+
)
|
|
118
|
+
] }) : control,
|
|
119
|
+
suffix && /* @__PURE__ */ jsx("span", { className: "min-w-0 oprt-wrap-anywhere text-oprt-text-dim", children: suffix })
|
|
120
|
+
] })
|
|
121
|
+
);
|
|
122
|
+
});
|
|
123
|
+
Input.displayName = "Input";
|
|
124
|
+
export {
|
|
125
|
+
Input
|
|
126
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// src/Input/Input.manifest.ts
|
|
2
|
+
var InputManifest = {
|
|
3
|
+
Input: {
|
|
4
|
+
description: "Single-line input with optional prefix/suffix slots and prompt-symbol variant. Date and time fields preserve native editing and picker interaction with decorative OPERATOR Calendar/Clock icons and a dark native color scheme.",
|
|
5
|
+
props: {
|
|
6
|
+
type: {
|
|
7
|
+
type: "enum",
|
|
8
|
+
values: [
|
|
9
|
+
"text",
|
|
10
|
+
"number",
|
|
11
|
+
"password",
|
|
12
|
+
"search",
|
|
13
|
+
"email",
|
|
14
|
+
"tel",
|
|
15
|
+
"url",
|
|
16
|
+
"date",
|
|
17
|
+
"time",
|
|
18
|
+
"datetime-local",
|
|
19
|
+
"month",
|
|
20
|
+
"week"
|
|
21
|
+
],
|
|
22
|
+
default: "text",
|
|
23
|
+
description: "The text-like and date-like input types. TypeScript accepts any HTML input type, but one that is not a text field (checkbox, radio, file, range, color, hidden, button, submit, reset, image) breaks the field's look and is reported in development; use Checkbox, RadioGroup, Slider or FileUpload instead."
|
|
24
|
+
},
|
|
25
|
+
size: { type: "enum", values: ["sm", "md", "lg"], default: "md" },
|
|
26
|
+
placeholder: { type: "string" },
|
|
27
|
+
disabled: { type: "boolean", default: false },
|
|
28
|
+
invalid: {
|
|
29
|
+
type: "boolean",
|
|
30
|
+
description: "Marks the field invalid: an error border and aria-invalid. Wins over aria-invalid, which wins over a surrounding Field's error, as on Textarea."
|
|
31
|
+
},
|
|
32
|
+
promptSymbol: { type: "boolean", default: false },
|
|
33
|
+
prefix: {
|
|
34
|
+
type: "node",
|
|
35
|
+
description: "ReactNode rendered before the input (icon, currency symbol, etc.)."
|
|
36
|
+
},
|
|
37
|
+
suffix: {
|
|
38
|
+
type: "node",
|
|
39
|
+
description: "ReactNode rendered after the input (icon, clear button, unit, etc.). For interactive elements use an accessible Button or icon-button."
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
composable_with: ["Field"],
|
|
43
|
+
never_inside: ["Button", "Badge"],
|
|
44
|
+
example: '<Field label="ENDPOINT">\n <Input placeholder="api.system.io" />\n</Field>'
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
export {
|
|
48
|
+
InputManifest
|
|
49
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/Inspector/Inspector.tsx
|
|
4
|
+
import { forwardRef, useRef } from "react";
|
|
5
|
+
import { cva } from "class-variance-authority";
|
|
6
|
+
import clsx from "clsx";
|
|
7
|
+
import { Button } from "../Button/Button.js";
|
|
8
|
+
import { joinIds, usePartIds } from "../_internal/fieldIds.js";
|
|
9
|
+
import { useMergedRefs } from "../_internal/mergeRefs.js";
|
|
10
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
11
|
+
var inspectorStyles = cva(
|
|
12
|
+
"flex min-w-0 flex-col gap-2u break-words border border-oprt-border bg-oprt-surface-1 p-2u font-sans text-oprt-base normal-case text-oprt-text"
|
|
13
|
+
);
|
|
14
|
+
var Inspector = forwardRef(
|
|
15
|
+
({
|
|
16
|
+
open = true,
|
|
17
|
+
title,
|
|
18
|
+
description,
|
|
19
|
+
onClose,
|
|
20
|
+
children,
|
|
21
|
+
className,
|
|
22
|
+
"aria-labelledby": labelledBy,
|
|
23
|
+
"aria-describedby": describedBy,
|
|
24
|
+
...props
|
|
25
|
+
}, ref) => {
|
|
26
|
+
const root = useRef(null);
|
|
27
|
+
const setRef = useMergedRefs(root, ref);
|
|
28
|
+
const ids = usePartIds(["title", "description"], {
|
|
29
|
+
readRendered: () => root.current?.getAttribute("data-oprt-ids")
|
|
30
|
+
});
|
|
31
|
+
if (!open) return null;
|
|
32
|
+
return /* @__PURE__ */ jsxs(
|
|
33
|
+
"aside",
|
|
34
|
+
{
|
|
35
|
+
...props,
|
|
36
|
+
ref: setRef,
|
|
37
|
+
"data-oprt-ids": ids.base,
|
|
38
|
+
"aria-labelledby": joinIds(ids.title, labelledBy),
|
|
39
|
+
"aria-describedby": joinIds(description ? ids.description : void 0, describedBy),
|
|
40
|
+
className: clsx(inspectorStyles(), className),
|
|
41
|
+
children: [
|
|
42
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-start justify-between gap-1u", children: [
|
|
43
|
+
/* @__PURE__ */ jsx(
|
|
44
|
+
"h3",
|
|
45
|
+
{
|
|
46
|
+
id: ids.title,
|
|
47
|
+
className: "m-0 min-w-0 text-oprt-md font-medium normal-case oprt-wrap-anywhere",
|
|
48
|
+
children: title
|
|
49
|
+
}
|
|
50
|
+
),
|
|
51
|
+
onClose && /* @__PURE__ */ jsx(Button, { variant: "tertiary", size: "sm", onClick: onClose, "aria-label": `Close ${title}`, children: "Close" })
|
|
52
|
+
] }),
|
|
53
|
+
description && /* @__PURE__ */ jsx("p", { id: ids.description, className: "m-0 min-w-0 text-oprt-text-dim oprt-wrap-anywhere", children: description }),
|
|
54
|
+
children
|
|
55
|
+
]
|
|
56
|
+
}
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
);
|
|
60
|
+
Inspector.displayName = "Inspector";
|
|
61
|
+
export {
|
|
62
|
+
Inspector
|
|
63
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// src/Inspector/Inspector.manifest.ts
|
|
2
|
+
var InspectorManifest = {
|
|
3
|
+
Inspector: {
|
|
4
|
+
description: "Modeless named details aside that leaves background content and focus navigation available.",
|
|
5
|
+
props: {
|
|
6
|
+
open: {
|
|
7
|
+
type: "boolean",
|
|
8
|
+
default: true
|
|
9
|
+
},
|
|
10
|
+
title: {
|
|
11
|
+
type: "string",
|
|
12
|
+
required: true,
|
|
13
|
+
description: "Accessible title; entity names preserve supplied casing."
|
|
14
|
+
},
|
|
15
|
+
description: {
|
|
16
|
+
type: "string"
|
|
17
|
+
},
|
|
18
|
+
onClose: {
|
|
19
|
+
type: "function",
|
|
20
|
+
description: "Shows Close when supplied. The caller manages closing state and return focus."
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
slots: {
|
|
24
|
+
children: {
|
|
25
|
+
type: "ReactNode"
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
composable_with: ["AppShell", "SplitPane", "OperationalStatus", "ActionStatus"],
|
|
29
|
+
never_inside: ["Button"],
|
|
30
|
+
example: "<Inspector open={open} title={asset.name} onClose={closeAndRestoreFocus}><OperationalStatus state={asset.status} source={source} updatedAt={asset.updatedAt} /></Inspector>"
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
export {
|
|
34
|
+
InspectorManifest
|
|
35
|
+
};
|