@rebasepro/ui 0.24.0 → 0.24.1-canary.gcb918fc
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/Alert.d.ts +6 -0
- package/dist/components/Alert.es.js +82 -0
- package/dist/components/Alert.es.js.map +1 -0
- package/dist/components/Autocomplete.es.js +53 -0
- package/dist/components/Autocomplete.es.js.map +1 -0
- package/dist/components/Avatar.d.ts +3 -1
- package/dist/components/Avatar.es.js +37 -0
- package/dist/components/Avatar.es.js.map +1 -0
- package/dist/components/Badge.es.js +27 -0
- package/dist/components/Badge.es.js.map +1 -0
- package/dist/components/BooleanSwitch.es.js +75 -0
- package/dist/components/BooleanSwitch.es.js.map +1 -0
- package/dist/components/BooleanSwitchWithLabel.es.js +55 -0
- package/dist/components/BooleanSwitchWithLabel.es.js.map +1 -0
- package/dist/components/Button.es.js +58 -0
- package/dist/components/Button.es.js.map +1 -0
- package/dist/components/Card.es.js +26 -0
- package/dist/components/Card.es.js.map +1 -0
- package/dist/components/CenteredView.es.js +23 -0
- package/dist/components/CenteredView.es.js.map +1 -0
- package/dist/components/Checkbox.es.js +62 -0
- package/dist/components/Checkbox.es.js.map +1 -0
- package/dist/components/Chip.es.js +72 -0
- package/dist/components/Chip.es.js.map +1 -0
- package/dist/components/CircularProgress.es.js +28 -0
- package/dist/components/CircularProgress.es.js.map +1 -0
- package/dist/components/CircularProgressCenter.es.js +30 -0
- package/dist/components/CircularProgressCenter.es.js.map +1 -0
- package/dist/components/Collapse.es.js +57 -0
- package/dist/components/Collapse.es.js.map +1 -0
- package/dist/components/ColorPicker.es.js +89 -0
- package/dist/components/ColorPicker.es.js.map +1 -0
- package/dist/components/Container.es.js +31 -0
- package/dist/components/Container.es.js.map +1 -0
- package/dist/components/DateTimeField.d.ts +11 -1
- package/dist/components/DateTimeField.es.js +253 -0
- package/dist/components/DateTimeField.es.js.map +1 -0
- package/dist/components/DebouncedTextField.es.js +93 -0
- package/dist/components/DebouncedTextField.es.js.map +1 -0
- package/dist/components/Dialog.es.js +98 -0
- package/dist/components/Dialog.es.js.map +1 -0
- package/dist/components/DialogActions.es.js +15 -0
- package/dist/components/DialogActions.es.js.map +1 -0
- package/dist/components/DialogContent.es.js +18 -0
- package/dist/components/DialogContent.es.js.map +1 -0
- package/dist/components/DialogTitle.es.js +26 -0
- package/dist/components/DialogTitle.es.js.map +1 -0
- package/dist/components/ErrorBoundary.es.js +158 -0
- package/dist/components/ErrorBoundary.es.js.map +1 -0
- package/dist/components/ExpandablePanel.es.js +91 -0
- package/dist/components/ExpandablePanel.es.js.map +1 -0
- package/dist/components/FileUpload.es.js +58 -0
- package/dist/components/FileUpload.es.js.map +1 -0
- package/dist/components/FilterChip.es.js +31 -0
- package/dist/components/FilterChip.es.js.map +1 -0
- package/dist/components/IconButton.es.js +52 -0
- package/dist/components/IconButton.es.js.map +1 -0
- package/dist/components/InfoLabel.es.js +18 -0
- package/dist/components/InfoLabel.es.js.map +1 -0
- package/dist/components/InputLabel.es.js +27 -0
- package/dist/components/InputLabel.es.js.map +1 -0
- package/dist/components/Label.es.js +18 -0
- package/dist/components/Label.es.js.map +1 -0
- package/dist/components/LoadingButton.es.js +21 -0
- package/dist/components/LoadingButton.es.js.map +1 -0
- package/dist/components/Markdown.es.js +88 -0
- package/dist/components/Markdown.es.js.map +1 -0
- package/dist/components/Menu.es.js +46 -0
- package/dist/components/Menu.es.js.map +1 -0
- package/dist/components/Menubar.es.js +142 -0
- package/dist/components/Menubar.es.js.map +1 -0
- package/dist/components/MultiSelect.d.ts +1 -7
- package/dist/components/MultiSelect.es.js +271 -0
- package/dist/components/MultiSelect.es.js.map +1 -0
- package/dist/components/Paper.es.js +16 -0
- package/dist/components/Paper.es.js.map +1 -0
- package/dist/components/Popover.es.js +113 -0
- package/dist/components/Popover.es.js.map +1 -0
- package/dist/components/RadioGroup.es.js +30 -0
- package/dist/components/RadioGroup.es.js.map +1 -0
- package/dist/components/ResizablePanels.d.ts +3 -1
- package/dist/components/ResizablePanels.es.js +137 -0
- package/dist/components/ResizablePanels.es.js.map +1 -0
- package/dist/components/SearchBar.es.js +82 -0
- package/dist/components/SearchBar.es.js.map +1 -0
- package/dist/components/Select.d.ts +8 -1
- package/dist/components/Select.es.js +300 -0
- package/dist/components/Select.es.js.map +1 -0
- package/dist/components/Separator.es.js +20 -0
- package/dist/components/Separator.es.js.map +1 -0
- package/dist/components/Sheet.es.js +73 -0
- package/dist/components/Sheet.es.js.map +1 -0
- package/dist/components/Skeleton.es.js +33 -0
- package/dist/components/Skeleton.es.js.map +1 -0
- package/dist/components/Slider.es.js +56 -0
- package/dist/components/Slider.es.js.map +1 -0
- package/dist/components/Table.d.ts +18 -3
- package/dist/components/Table.es.js +69 -0
- package/dist/components/Table.es.js.map +1 -0
- package/dist/components/Tabs.es.js +101 -0
- package/dist/components/Tabs.es.js.map +1 -0
- package/dist/components/TextField.d.ts +0 -8
- package/dist/components/TextField.es.js +111 -0
- package/dist/components/TextField.es.js.map +1 -0
- package/dist/components/TextareaAutosize.es.js +187 -0
- package/dist/components/TextareaAutosize.es.js.map +1 -0
- package/dist/components/ToggleButtonGroup.d.ts +6 -1
- package/dist/components/ToggleButtonGroup.es.js +35 -0
- package/dist/components/ToggleButtonGroup.es.js.map +1 -0
- package/dist/components/Tooltip.d.ts +24 -0
- package/dist/components/Tooltip.es.js +165 -0
- package/dist/components/Tooltip.es.js.map +1 -0
- package/dist/components/Typography.es.js +85 -0
- package/dist/components/Typography.es.js.map +1 -0
- package/dist/components/VirtualTable/VirtualTable.es.js +516 -0
- package/dist/components/VirtualTable/VirtualTable.es.js.map +1 -0
- package/dist/components/VirtualTable/VirtualTableCell.d.ts +1 -1
- package/dist/components/VirtualTable/VirtualTableCell.es.js +40 -0
- package/dist/components/VirtualTable/VirtualTableCell.es.js.map +1 -0
- package/dist/components/VirtualTable/VirtualTableHeader.es.js +209 -0
- package/dist/components/VirtualTable/VirtualTableHeader.es.js.map +1 -0
- package/dist/components/VirtualTable/VirtualTableHeaderRow.es.js +166 -0
- package/dist/components/VirtualTable/VirtualTableHeaderRow.es.js.map +1 -0
- package/dist/components/VirtualTable/VirtualTableProps.d.ts +13 -2
- package/dist/components/VirtualTable/VirtualTableRow.d.ts +6 -7
- package/dist/components/VirtualTable/VirtualTableRow.es.js +41 -0
- package/dist/components/VirtualTable/VirtualTableRow.es.js.map +1 -0
- package/dist/components/VirtualTable/fields/VirtualTableDateField.d.ts +2 -1
- package/dist/components/VirtualTable/fields/VirtualTableDateField.es.js +71 -0
- package/dist/components/VirtualTable/fields/VirtualTableDateField.es.js.map +1 -0
- package/dist/components/VirtualTable/fields/VirtualTableInput.es.js +73 -0
- package/dist/components/VirtualTable/fields/VirtualTableInput.es.js.map +1 -0
- package/dist/components/VirtualTable/fields/VirtualTableNumberInput.es.js +47 -0
- package/dist/components/VirtualTable/fields/VirtualTableNumberInput.es.js.map +1 -0
- package/dist/components/VirtualTable/fields/VirtualTableSelect.es.js +83 -0
- package/dist/components/VirtualTable/fields/VirtualTableSelect.es.js.map +1 -0
- package/dist/components/VirtualTable/fields/VirtualTableSwitch.es.js +22 -0
- package/dist/components/VirtualTable/fields/VirtualTableSwitch.es.js.map +1 -0
- package/dist/components/VirtualTable/selection/SelectionContext.es.js +23 -0
- package/dist/components/VirtualTable/selection/SelectionContext.es.js.map +1 -0
- package/dist/components/VirtualTable/selection/SelectionStore.es.js +43 -0
- package/dist/components/VirtualTable/selection/SelectionStore.es.js.map +1 -0
- package/dist/components/VirtualTable/types.d.ts +1 -1
- package/dist/components/common/SelectInputLabel.es.js +28 -0
- package/dist/components/common/SelectInputLabel.es.js.map +1 -0
- package/dist/components/index.es.js +64 -0
- package/dist/hooks/PortalContainerContext.es.js +40 -0
- package/dist/hooks/PortalContainerContext.es.js.map +1 -0
- package/dist/hooks/UiLabelsContext.d.ts +63 -0
- package/dist/hooks/UiLabelsContext.es.js +59 -0
- package/dist/hooks/UiLabelsContext.es.js.map +1 -0
- package/dist/hooks/useDebounceCallback.es.js +15 -0
- package/dist/hooks/useDebounceCallback.es.js.map +1 -0
- package/dist/hooks/useDebounceValue.es.js +19 -0
- package/dist/hooks/useDebounceValue.es.js.map +1 -0
- package/dist/hooks/useDebouncedCallback.es.js +36 -0
- package/dist/hooks/useDebouncedCallback.es.js.map +1 -0
- package/dist/hooks/useInjectStyles.es.js +27 -0
- package/dist/hooks/useInjectStyles.es.js.map +1 -0
- package/dist/hooks/useIsDarkMode.es.js +61 -0
- package/dist/hooks/useIsDarkMode.es.js.map +1 -0
- package/dist/hooks/useModalFocus.es.js +81 -0
- package/dist/hooks/useModalFocus.es.js.map +1 -0
- package/dist/hooks/useOutsideAlerter.es.js +41 -0
- package/dist/hooks/useOutsideAlerter.es.js.map +1 -0
- package/dist/hooks/useRestoreInterruptedFocus.es.js +61 -0
- package/dist/hooks/useRestoreInterruptedFocus.es.js.map +1 -0
- package/dist/icons/GitHubIcon.es.js +23 -0
- package/dist/icons/GitHubIcon.es.js.map +1 -0
- package/dist/icons/HandleIcon.es.js +60 -0
- package/dist/icons/HandleIcon.es.js.map +1 -0
- package/dist/icons/Icon.es.js +25 -0
- package/dist/icons/Icon.es.js.map +1 -0
- package/dist/icons/LucideIconByName.d.ts +12 -9
- package/dist/icons/LucideIconByName.es.js +83 -0
- package/dist/icons/LucideIconByName.es.js.map +1 -0
- package/dist/icons/cool_icon_keys.es.js +55 -0
- package/dist/icons/cool_icon_keys.es.js.map +1 -0
- package/dist/icons/icon_keys.es.js +1941 -0
- package/dist/icons/icon_keys.es.js.map +1 -0
- package/dist/icons/index.d.ts +4 -3
- package/dist/icons/index.es.js +8 -0
- package/dist/icons/lucide_icon_map.d.ts +12 -0
- package/dist/icons/lucide_icon_map.es.js +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.es.js +87 -9563
- package/dist/styles.es.js +158 -0
- package/dist/styles.es.js.map +1 -0
- package/dist/util/chip_colors.es.js +290 -0
- package/dist/util/chip_colors.es.js.map +1 -0
- package/dist/util/cls.es.js +10 -0
- package/dist/util/cls.es.js.map +1 -0
- package/dist/util/debounce.es.js +22 -0
- package/dist/util/debounce.es.js.map +1 -0
- package/dist/util/hash.es.js +16 -0
- package/dist/util/hash.es.js.map +1 -0
- package/dist/util/keys.es.js +37 -0
- package/dist/util/keys.es.js.map +1 -0
- package/dist/util/lazy_chunk.es.js +96 -0
- package/dist/util/lazy_chunk.es.js.map +1 -0
- package/dist/views/CardView.d.ts +26 -2
- package/dist/views/CardView.es.js +435 -0
- package/dist/views/CardView.es.js.map +1 -0
- package/dist/views/CollectionView/CollectionCardView.es.js +179 -0
- package/dist/views/CollectionView/CollectionCardView.es.js.map +1 -0
- package/dist/views/CollectionView/CollectionKanbanView.es.js +172 -0
- package/dist/views/CollectionView/CollectionKanbanView.es.js.map +1 -0
- package/dist/views/CollectionView/CollectionListView.es.js +156 -0
- package/dist/views/CollectionView/CollectionListView.es.js.map +1 -0
- package/dist/views/CollectionView/CollectionTableView.es.js +149 -0
- package/dist/views/CollectionView/CollectionTableView.es.js.map +1 -0
- package/dist/views/CollectionView/CollectionView.es.js +197 -0
- package/dist/views/CollectionView/CollectionView.es.js.map +1 -0
- package/dist/views/CollectionView/CollectionViewToolbar.es.js +155 -0
- package/dist/views/CollectionView/CollectionViewToolbar.es.js.map +1 -0
- package/dist/views/CollectionView/DefaultCellRenderer.es.js +302 -0
- package/dist/views/CollectionView/DefaultCellRenderer.es.js.map +1 -0
- package/dist/views/CollectionView/resolveTitleProperty.es.js +56 -0
- package/dist/views/CollectionView/resolveTitleProperty.es.js.map +1 -0
- package/dist/views/CollectionView/utils.es.js +19 -0
- package/dist/views/CollectionView/utils.es.js.map +1 -0
- package/dist/views/Kanban/Board.es.js +237 -0
- package/dist/views/Kanban/Board.es.js.map +1 -0
- package/dist/views/Kanban/BoardColumn.es.js +81 -0
- package/dist/views/Kanban/BoardColumn.es.js.map +1 -0
- package/dist/views/Kanban/BoardColumnTitle.es.js +27 -0
- package/dist/views/Kanban/BoardColumnTitle.es.js.map +1 -0
- package/dist/views/Kanban/BoardSortableList.es.js +98 -0
- package/dist/views/Kanban/BoardSortableList.es.js.map +1 -0
- package/dist/views/Kanban/placement.es.js +27 -0
- package/dist/views/Kanban/placement.es.js.map +1 -0
- package/dist/views/KanbanView.es.js +11 -0
- package/dist/views/KanbanView.es.js.map +1 -0
- package/dist/views/ListView.d.ts +15 -2
- package/dist/views/ListView.es.js +245 -0
- package/dist/views/ListView.es.js.map +1 -0
- package/dist/views/useScrollParentRestoration.d.ts +8 -1
- package/dist/views/useScrollParentRestoration.es.js +75 -0
- package/dist/views/useScrollParentRestoration.es.js.map +1 -0
- package/package.json +2 -2
- package/dist/index.es.js.map +0 -1
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { fieldBackgroundDisabledMixin, fieldBackgroundHoverMixin } from "../styles.es.js";
|
|
3
|
+
import { cls } from "../util/cls.es.js";
|
|
4
|
+
import { useInjectStyles } from "../hooks/useInjectStyles.es.js";
|
|
5
|
+
import { iconSize } from "../icons/Icon.es.js";
|
|
6
|
+
import { Typography } from "./Typography.es.js";
|
|
7
|
+
import { IconButton } from "./IconButton.es.js";
|
|
8
|
+
import { InputLabel } from "./InputLabel.es.js";
|
|
9
|
+
import { useUiLabels } from "../hooks/UiLabelsContext.es.js";
|
|
10
|
+
import React, { useCallback, useId, useRef, useState } from "react";
|
|
11
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
12
|
+
import { AlertCircleIcon, CalendarIcon, ClockIcon, XIcon } from "lucide-react";
|
|
13
|
+
//#region src/components/DateTimeField.tsx
|
|
14
|
+
/** The parts `<input type="date">` / `"datetime-local"` values are built from. */
|
|
15
|
+
var INPUT_PARTS = {
|
|
16
|
+
year: "numeric",
|
|
17
|
+
month: "2-digit",
|
|
18
|
+
day: "2-digit",
|
|
19
|
+
hour: "2-digit",
|
|
20
|
+
minute: "2-digit",
|
|
21
|
+
hour12: false
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Formatters for named time zones, keyed by the IANA name ("UTC" included).
|
|
25
|
+
*
|
|
26
|
+
* Building an `Intl.DateTimeFormat` costs about ten times what formatting
|
|
27
|
+
* with one does, and this field needs one per render and three per keystroke
|
|
28
|
+
* when it has a `timezone`. A named zone's rules cannot change during a
|
|
29
|
+
* session, so its formatter is built once; the map holds one entry per zone
|
|
30
|
+
* a field has used. The local zone (no `timezone`) is NOT cached: a formatter
|
|
31
|
+
* fixes the zone it resolved when it was built, while the parse side reads the
|
|
32
|
+
* machine's zone live through `Date`, and a laptop can change zones with the
|
|
33
|
+
* tab open.
|
|
34
|
+
*/
|
|
35
|
+
var zoneFormatters = /* @__PURE__ */ new Map();
|
|
36
|
+
function inputPartsFormatter(timeZone) {
|
|
37
|
+
if (timeZone === void 0) return new Intl.DateTimeFormat("en-CA", INPUT_PARTS);
|
|
38
|
+
let formatter = zoneFormatters.get(timeZone);
|
|
39
|
+
if (!formatter) {
|
|
40
|
+
formatter = new Intl.DateTimeFormat("en-CA", {
|
|
41
|
+
...INPUT_PARTS,
|
|
42
|
+
timeZone
|
|
43
|
+
});
|
|
44
|
+
zoneFormatters.set(timeZone, formatter);
|
|
45
|
+
}
|
|
46
|
+
return formatter;
|
|
47
|
+
}
|
|
48
|
+
/** A UTC `Date` as the input's value string, in `timezone` (undefined = local). */
|
|
49
|
+
function valueAsInputValue(dateValue, mode, timezone) {
|
|
50
|
+
if (!dateValue || !(dateValue instanceof Date) || isNaN(dateValue.getTime())) return "";
|
|
51
|
+
const parts = inputPartsFormatter(timezone).formatToParts(dateValue);
|
|
52
|
+
const getPart = (type) => parts.find((p) => p.type === type)?.value ?? "";
|
|
53
|
+
const year = getPart("year");
|
|
54
|
+
const month = getPart("month");
|
|
55
|
+
const day = getPart("day");
|
|
56
|
+
if (mode === "time") return `${getPart("hour")}:${getPart("minute")}`;
|
|
57
|
+
if (mode === "date") return `${year}-${month}-${day}`;
|
|
58
|
+
else return `${year}-${month}-${day}T${getPart("hour")}:${getPart("minute")}`;
|
|
59
|
+
}
|
|
60
|
+
var DateTimeField = ({ value, label, onChange, disabled, clearable, mode = "date", error, size = "large", className, style, inputClassName, invisible, pickerButton = true, inputRef: inputRefProp, timezone, locale, "aria-label": ariaLabel }) => {
|
|
61
|
+
const inputRef = useRef(null);
|
|
62
|
+
const labels = useUiLabels();
|
|
63
|
+
const setInputRef = useCallback((node) => {
|
|
64
|
+
inputRef.current = node;
|
|
65
|
+
if (typeof inputRefProp === "function") inputRefProp(node);
|
|
66
|
+
else if (inputRefProp) inputRefProp.current = node;
|
|
67
|
+
}, [inputRefProp]);
|
|
68
|
+
const [focused, setFocused] = useState(false);
|
|
69
|
+
const inputId = useId();
|
|
70
|
+
const labelId = `${inputId}-label`;
|
|
71
|
+
const [internalValue, setInternalValue] = useState("");
|
|
72
|
+
const [isTyping, setIsTyping] = useState(false);
|
|
73
|
+
const invalidValue = value !== void 0 && value !== null && (!(value instanceof Date) || isNaN(value.getTime()));
|
|
74
|
+
const adornmentSize = size === "large" ? "medium" : size === "medium" ? "small" : "smallest";
|
|
75
|
+
useInjectStyles("DateTimeField", inputStyles);
|
|
76
|
+
const handleClear = (e) => {
|
|
77
|
+
e.preventDefault();
|
|
78
|
+
if (disabled) return;
|
|
79
|
+
setInternalValue("");
|
|
80
|
+
setIsTyping(false);
|
|
81
|
+
onChange?.(null);
|
|
82
|
+
};
|
|
83
|
+
const getTimezoneOffsetMinutes = (date, tz) => {
|
|
84
|
+
if (!tz) return -date.getTimezoneOffset();
|
|
85
|
+
const utcFormatter = inputPartsFormatter("UTC");
|
|
86
|
+
const tzFormatter = inputPartsFormatter(tz);
|
|
87
|
+
const utcParts = utcFormatter.formatToParts(date);
|
|
88
|
+
const tzParts = tzFormatter.formatToParts(date);
|
|
89
|
+
const getPart = (parts, type) => parseInt(parts.find((p) => p.type === type)?.value ?? "0", 10);
|
|
90
|
+
const utcDate = new Date(Date.UTC(getPart(utcParts, "year"), getPart(utcParts, "month") - 1, getPart(utcParts, "day"), getPart(utcParts, "hour"), getPart(utcParts, "minute")));
|
|
91
|
+
return (new Date(Date.UTC(getPart(tzParts, "year"), getPart(tzParts, "month") - 1, getPart(tzParts, "day"), getPart(tzParts, "hour"), getPart(tzParts, "minute"))).getTime() - utcDate.getTime()) / 6e4;
|
|
92
|
+
};
|
|
93
|
+
const handleInputChange = (e) => {
|
|
94
|
+
const inputValue = e.target.value;
|
|
95
|
+
setInternalValue(inputValue);
|
|
96
|
+
setIsTyping(true);
|
|
97
|
+
if (!inputValue) {
|
|
98
|
+
onChange?.(null);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
try {
|
|
102
|
+
let year, month, day, hours = 0, minutes = 0;
|
|
103
|
+
if (mode === "time") {
|
|
104
|
+
[year, month, day] = [
|
|
105
|
+
1970,
|
|
106
|
+
1,
|
|
107
|
+
1
|
|
108
|
+
];
|
|
109
|
+
[hours, minutes] = inputValue.split(":").map(Number);
|
|
110
|
+
} else if (mode === "date") [year, month, day] = inputValue.split("-").map(Number);
|
|
111
|
+
else {
|
|
112
|
+
const [datePart, timePart] = inputValue.split("T");
|
|
113
|
+
[year, month, day] = datePart.split("-").map(Number);
|
|
114
|
+
[hours, minutes] = timePart.split(":").map(Number);
|
|
115
|
+
}
|
|
116
|
+
let resultDate;
|
|
117
|
+
if (!timezone) {
|
|
118
|
+
resultDate = /* @__PURE__ */ new Date(0);
|
|
119
|
+
resultDate.setFullYear(year, month - 1, day);
|
|
120
|
+
resultDate.setHours(hours, minutes, 0, 0);
|
|
121
|
+
} else {
|
|
122
|
+
const wallClock = /* @__PURE__ */ new Date(0);
|
|
123
|
+
wallClock.setUTCFullYear(year, month - 1, day);
|
|
124
|
+
wallClock.setUTCHours(hours, minutes, 0, 0);
|
|
125
|
+
const wallClockAsUtc = wallClock.getTime();
|
|
126
|
+
const firstGuess = getTimezoneOffsetMinutes(new Date(wallClockAsUtc), timezone);
|
|
127
|
+
const offsetMinutes = getTimezoneOffsetMinutes(/* @__PURE__ */ new Date(wallClockAsUtc - firstGuess * 6e4), timezone);
|
|
128
|
+
resultDate = /* @__PURE__ */ new Date(wallClockAsUtc - offsetMinutes * 6e4);
|
|
129
|
+
if (getTimezoneOffsetMinutes(resultDate, timezone) !== offsetMinutes) resultDate = /* @__PURE__ */ new Date(wallClockAsUtc - firstGuess * 6e4);
|
|
130
|
+
}
|
|
131
|
+
if (isNaN(resultDate.getTime())) throw new Error("Invalid date");
|
|
132
|
+
onChange?.(resultDate);
|
|
133
|
+
} catch (e) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
const valueTime = value instanceof Date ? value.getTime() : void 0;
|
|
138
|
+
const displayValue = React.useMemo(() => valueAsInputValue(valueTime === void 0 ? null : new Date(valueTime), mode, timezone), [
|
|
139
|
+
valueTime,
|
|
140
|
+
mode,
|
|
141
|
+
timezone
|
|
142
|
+
]);
|
|
143
|
+
const handleFocus = () => {
|
|
144
|
+
setFocused(true);
|
|
145
|
+
setIsTyping(true);
|
|
146
|
+
setInternalValue(valueAsInputValue(value ?? null, mode, timezone));
|
|
147
|
+
};
|
|
148
|
+
const handleBlur = () => {
|
|
149
|
+
setFocused(false);
|
|
150
|
+
setIsTyping(false);
|
|
151
|
+
};
|
|
152
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("div", {
|
|
153
|
+
className: cls("rounded-lg relative max-w-full", !invisible && "bg-surface-field border border-hairline", disabled ? fieldBackgroundDisabledMixin : invisible ? "" : fieldBackgroundHoverMixin, {
|
|
154
|
+
"min-h-[28px]": size === "smallest",
|
|
155
|
+
"min-h-[32px]": size === "small",
|
|
156
|
+
"min-h-[40px]": size === "medium",
|
|
157
|
+
"min-h-[48px]": size === "large"
|
|
158
|
+
}, className),
|
|
159
|
+
style,
|
|
160
|
+
onClick: () => {
|
|
161
|
+
if (!disabled) inputRef.current?.focus();
|
|
162
|
+
},
|
|
163
|
+
children: [
|
|
164
|
+
label && /* @__PURE__ */ jsx(InputLabel, {
|
|
165
|
+
id: labelId,
|
|
166
|
+
htmlFor: inputId,
|
|
167
|
+
className: cls("absolute top-1 pointer-events-none", !error ? focused ? "text-primary" : "text-text-secondary dark:text-text-secondary-dark" : "text-red-600 dark:text-red-500", disabled ? "opacity-50" : ""),
|
|
168
|
+
shrink: true,
|
|
169
|
+
children: label
|
|
170
|
+
}),
|
|
171
|
+
/* @__PURE__ */ jsx("input", {
|
|
172
|
+
ref: setInputRef,
|
|
173
|
+
id: inputId,
|
|
174
|
+
"aria-labelledby": label ? labelId : void 0,
|
|
175
|
+
"aria-label": label ? void 0 : ariaLabel,
|
|
176
|
+
type: mode === "date_time" ? "datetime-local" : mode === "time" ? "time" : "date",
|
|
177
|
+
lang: locale,
|
|
178
|
+
"aria-invalid": error || void 0,
|
|
179
|
+
value: isTyping ? internalValue : displayValue,
|
|
180
|
+
onChange: handleInputChange,
|
|
181
|
+
onFocus: handleFocus,
|
|
182
|
+
onBlur: handleBlur,
|
|
183
|
+
disabled,
|
|
184
|
+
className: cls("w-full outline-hidden bg-transparent leading-normal text-sm px-3", pickerButton ? clearable ? "pr-14" : "pr-12" : clearable ? "pr-12" : "", "rounded-lg", {
|
|
185
|
+
"min-h-[28px]": size === "smallest",
|
|
186
|
+
"min-h-[32px]": size === "small",
|
|
187
|
+
"min-h-[40px]": size === "medium",
|
|
188
|
+
"min-h-[48px]": size === "large"
|
|
189
|
+
}, label ? "pt-8 pb-2" : {
|
|
190
|
+
smallest: "py-0.5",
|
|
191
|
+
small: "py-1",
|
|
192
|
+
medium: "py-2",
|
|
193
|
+
large: "py-3"
|
|
194
|
+
}[size], inputClassName, disabled && "border border-transparent outline-hidden opacity-50 dark:opacity-50 text-surface-accent-600 dark:text-surface-accent-500")
|
|
195
|
+
}),
|
|
196
|
+
pickerButton && /* @__PURE__ */ jsx(IconButton, {
|
|
197
|
+
size: adornmentSize,
|
|
198
|
+
"aria-label": mode === "time" ? labels.openTimePicker : "Open calendar",
|
|
199
|
+
disabled,
|
|
200
|
+
onClick: (e) => {
|
|
201
|
+
e.stopPropagation();
|
|
202
|
+
try {
|
|
203
|
+
inputRef.current?.showPicker();
|
|
204
|
+
} catch {}
|
|
205
|
+
},
|
|
206
|
+
className: "absolute right-3 top-1/2 transform -translate-y-1/2 text-surface-accent-500!",
|
|
207
|
+
children: mode === "time" ? /* @__PURE__ */ jsx(ClockIcon, {}) : /* @__PURE__ */ jsx(CalendarIcon, {})
|
|
208
|
+
}),
|
|
209
|
+
clearable && value && /* @__PURE__ */ jsx(IconButton, {
|
|
210
|
+
size: adornmentSize,
|
|
211
|
+
"aria-label": "Clear date",
|
|
212
|
+
disabled,
|
|
213
|
+
onClick: handleClear,
|
|
214
|
+
className: "absolute right-14 top-1/2 transform -translate-y-1/2 text-surface-accent-400 ",
|
|
215
|
+
children: /* @__PURE__ */ jsx(XIcon, {})
|
|
216
|
+
})
|
|
217
|
+
]
|
|
218
|
+
}), invalidValue && /* @__PURE__ */ jsxs("div", {
|
|
219
|
+
className: "flex items-center m-2",
|
|
220
|
+
children: [/* @__PURE__ */ jsx(AlertCircleIcon, {
|
|
221
|
+
size: iconSize.small,
|
|
222
|
+
color: "error"
|
|
223
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
224
|
+
className: "pl-2",
|
|
225
|
+
children: [/* @__PURE__ */ jsx(Typography, {
|
|
226
|
+
variant: "body2",
|
|
227
|
+
children: "Invalid date value for this field"
|
|
228
|
+
}), /* @__PURE__ */ jsx(Typography, {
|
|
229
|
+
variant: "body2",
|
|
230
|
+
children: `The provided value is: ${JSON.stringify(value)}`
|
|
231
|
+
})]
|
|
232
|
+
})]
|
|
233
|
+
})] });
|
|
234
|
+
};
|
|
235
|
+
var inputStyles = `
|
|
236
|
+
/* Hide the default calendar icon in Chrome, Safari, Edge, Opera */
|
|
237
|
+
input[type="date"]::-webkit-calendar-picker-indicator,
|
|
238
|
+
input[type="datetime-local"]::-webkit-calendar-picker-indicator,
|
|
239
|
+
input[type="time"]::-webkit-calendar-picker-indicator {
|
|
240
|
+
display: none;
|
|
241
|
+
-webkit-appearance: none;
|
|
242
|
+
}
|
|
243
|
+
/* Hide default calendar icon in Firefox */
|
|
244
|
+
input[type="date"],
|
|
245
|
+
input[type="datetime-local"],
|
|
246
|
+
input[type="time"] {
|
|
247
|
+
-moz-appearance:textfield;
|
|
248
|
+
}
|
|
249
|
+
`;
|
|
250
|
+
//#endregion
|
|
251
|
+
export { DateTimeField };
|
|
252
|
+
|
|
253
|
+
//# sourceMappingURL=DateTimeField.es.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DateTimeField.es.js","names":[],"sources":["../../src/components/DateTimeField.tsx"],"sourcesContent":["\n\"use client\";\n\nimport React, { useCallback, useId, useRef, useState } from \"react\";\nimport { CalendarIcon, ClockIcon, XIcon, AlertCircleIcon } from \"lucide-react\";\nimport { iconSize } from \"../icons/Icon\";\nimport { IconButton } from \"./IconButton\";\nimport { fieldBackgroundDisabledMixin, fieldBackgroundHoverMixin, fieldBackgroundMixin } from \"../styles\";\nimport { InputLabel } from \"./InputLabel\";\nimport { Typography } from \"./Typography\";\nimport { cls } from \"../util\";\nimport { useInjectStyles } from \"../hooks\";\nimport { useUiLabels } from \"../hooks/UiLabelsContext\";\n\n/**\n * What the field picks: a day, a day and a time, or a time of day alone. A\n * time of day is a `Date` on 1970-01-01 — how a `time` column travels — read\n * and written in `timezone`.\n */\nexport type DateTimeFieldMode = \"date\" | \"date_time\" | \"time\";\n\nexport type DateTimeFieldProps = {\n value?: Date | null;\n onChange?: (date: Date | null) => void;\n mode?: DateTimeFieldMode;\n disabled?: boolean;\n clearable?: boolean;\n error?: boolean;\n size?: \"smallest\" | \"small\" | \"medium\" | \"large\";\n label?: React.ReactNode;\n className?: string;\n style?: React.CSSProperties;\n inputClassName?: string;\n invisible?: boolean;\n /**\n * Whether the field draws its own calendar button. A table cell turns it\n * off: the cell draws one opener for every kind of editor, in the same\n * place, and opens the picker through `inputRef`.\n */\n pickerButton?: boolean;\n /** The native input, for a caller that opens the picker itself (`showPicker()`). */\n inputRef?: React.Ref<HTMLInputElement>;\n /**\n * BCP 47 locale, set as the input's `lang`. The browser still draws a\n * native date input in its own UI language; this is the hint it may use.\n */\n locale?: string;\n /**\n * IANA timezone string (e.g., \"America/New_York\", \"Europe/London\").\n * Used to display and input dates in the specified timezone.\n * The value passed to onChange will always be in UTC.\n * If not provided, uses the user's local timezone.\n */\n timezone?: string;\n /**\n * Names the control when there is no `label` — e.g. when the surrounding\n * form draws the label itself and passes `label={undefined}`.\n */\n \"aria-label\"?: string;\n};\n\n/** The parts `<input type=\"date\">` / `\"datetime-local\"` values are built from. */\nconst INPUT_PARTS: Intl.DateTimeFormatOptions = {\n year: \"numeric\",\n month: \"2-digit\",\n day: \"2-digit\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n hour12: false\n};\n\n/**\n * Formatters for named time zones, keyed by the IANA name (\"UTC\" included).\n *\n * Building an `Intl.DateTimeFormat` costs about ten times what formatting\n * with one does, and this field needs one per render and three per keystroke\n * when it has a `timezone`. A named zone's rules cannot change during a\n * session, so its formatter is built once; the map holds one entry per zone\n * a field has used. The local zone (no `timezone`) is NOT cached: a formatter\n * fixes the zone it resolved when it was built, while the parse side reads the\n * machine's zone live through `Date`, and a laptop can change zones with the\n * tab open.\n */\nconst zoneFormatters = new Map<string, Intl.DateTimeFormat>();\n\nfunction inputPartsFormatter(timeZone: string | undefined): Intl.DateTimeFormat {\n if (timeZone === undefined) return new Intl.DateTimeFormat(\"en-CA\", INPUT_PARTS);\n let formatter = zoneFormatters.get(timeZone);\n if (!formatter) {\n formatter = new Intl.DateTimeFormat(\"en-CA\", {\n ...INPUT_PARTS,\n timeZone\n });\n zoneFormatters.set(timeZone, formatter);\n }\n return formatter;\n}\n\n/** A UTC `Date` as the input's value string, in `timezone` (undefined = local). */\nfunction valueAsInputValue(\n dateValue: Date | null,\n mode: DateTimeFieldMode,\n timezone: string | undefined\n): string {\n if (!dateValue || !(dateValue instanceof Date) || isNaN(dateValue.getTime())) {\n return \"\";\n }\n const pad = (n: number) => n.toString().padStart(2, \"0\");\n\n // The date parts in the target timezone (undefined = local timezone)\n const parts = inputPartsFormatter(timezone).formatToParts(dateValue);\n\n const getPart = (type: string) => parts.find(p => p.type === type)?.value ?? \"\";\n\n const year = getPart(\"year\");\n const month = getPart(\"month\");\n const day = getPart(\"day\");\n\n if (mode === \"time\") {\n return `${getPart(\"hour\")}:${getPart(\"minute\")}`;\n }\n if (mode === \"date\") {\n return `${year}-${month}-${day}`;\n } else {\n const hours = getPart(\"hour\");\n const minutes = getPart(\"minute\");\n return `${year}-${month}-${day}T${hours}:${minutes}`;\n }\n}\n\nexport const DateTimeField: React.FC<DateTimeFieldProps> = ({\n value,\n label,\n onChange,\n disabled,\n clearable,\n mode = \"date\",\n error,\n size = \"large\",\n className,\n style,\n inputClassName,\n invisible,\n pickerButton = true,\n inputRef: inputRefProp,\n timezone,\n locale,\n \"aria-label\": ariaLabel\n}) => {\n const inputRef = useRef<HTMLInputElement>(null);\n const labels = useUiLabels();\n const setInputRef = useCallback((node: HTMLInputElement | null) => {\n inputRef.current = node;\n if (typeof inputRefProp === \"function\") inputRefProp(node);\n else if (inputRefProp) inputRefProp.current = node;\n }, [inputRefProp]);\n const [focused, setFocused] = useState(false);\n // The label was decorative: an InputLabel with no `htmlFor` beside an input\n // with no `id`, so a date field announced itself by its value even when it\n // visibly had a label. Wired the way TextField already does it.\n const inputId = useId();\n const labelId = `${inputId}-label`;\n const [internalValue, setInternalValue] = useState<string>(\"\");\n const [isTyping, setIsTyping] = useState(false);\n const invalidValue = value !== undefined && value !== null && (!(value instanceof Date) || isNaN(value.getTime()));\n\n // The calendar/clear adornments are IconButtons. Left at their default\n // (`medium`, 40px) they floored the whole field at 40px, so smallest/small\n // rendered identically to medium. Keep them a step below the field.\n const adornmentSize = size === \"large\" ? \"medium\" : size === \"medium\" ? \"small\" : \"smallest\";\n\n useInjectStyles(\"DateTimeField\", inputStyles);\n\n const handleClear = (e: React.MouseEvent) => {\n e.preventDefault();\n // A disabled field is read-only, its adornments included.\n if (disabled) return;\n setInternalValue(\"\");\n setIsTyping(false);\n onChange?.(null);\n };\n\n // Get the UTC offset for a specific date in the target timezone (in minutes)\n const getTimezoneOffsetMinutes = (date: Date, tz?: string): number => {\n if (!tz) {\n // Local timezone: use built-in getTimezoneOffset (returns offset in minutes, inverted sign)\n return -date.getTimezoneOffset();\n }\n // For named timezones, calculate the offset by comparing formatted times\n const utcFormatter = inputPartsFormatter(\"UTC\");\n const tzFormatter = inputPartsFormatter(tz);\n\n const utcParts = utcFormatter.formatToParts(date);\n const tzParts = tzFormatter.formatToParts(date);\n\n const getPart = (parts: Intl.DateTimeFormatPart[], type: string) =>\n parseInt(parts.find(p => p.type === type)?.value ?? \"0\", 10);\n\n const utcDate = new Date(Date.UTC(\n getPart(utcParts, \"year\"),\n getPart(utcParts, \"month\") - 1,\n getPart(utcParts, \"day\"),\n getPart(utcParts, \"hour\"),\n getPart(utcParts, \"minute\")\n ));\n\n const tzDate = new Date(Date.UTC(\n getPart(tzParts, \"year\"),\n getPart(tzParts, \"month\") - 1,\n getPart(tzParts, \"day\"),\n getPart(tzParts, \"hour\"),\n getPart(tzParts, \"minute\")\n ));\n\n return (tzDate.getTime() - utcDate.getTime()) / 60000;\n };\n\n // Handle input value change - convert from display timezone to UTC\n const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const inputValue = e.target.value;\n setInternalValue(inputValue);\n setIsTyping(true);\n\n if (!inputValue) {\n onChange?.(null);\n return;\n }\n\n try {\n let year: number, month: number, day: number, hours = 0, minutes = 0;\n\n if (mode === \"time\") {\n // Parse time-only input: \"HH:MM\" (or \"HH:MM:SS\"), on the\n // day a time of day travels on.\n [year, month, day] = [1970, 1, 1];\n [hours, minutes] = inputValue.split(\":\").map(Number);\n } else if (mode === \"date\") {\n // Parse date-only input: \"YYYY-MM-DD\"\n [year, month, day] = inputValue.split(\"-\").map(Number);\n } else {\n // Parse datetime-local input: \"YYYY-MM-DDTHH:MM\"\n const [datePart, timePart] = inputValue.split(\"T\");\n [year, month, day] = datePart.split(\"-\").map(Number);\n [hours, minutes] = timePart.split(\":\").map(Number);\n }\n\n // Built field by field: `new Date(year, …)` and `Date.UTC(year, …)`\n // read a year of 0–99 as 1900–1999, so a year typed digit by digit\n // (0002, 0020) came out as 1902 and 1920.\n let resultDate: Date;\n\n if (!timezone) {\n // No timezone specified: interpret input as local time (backward compatible)\n resultDate = new Date(0);\n resultDate.setFullYear(year, month - 1, day);\n resultDate.setHours(hours, minutes, 0, 0);\n } else {\n // Timezone specified: interpret input as that timezone and convert to UTC.\n // If the user entered 00:00 in Mexico (UTC-6, offset=-360), the\n // wall-clock time read as UTC is 00:00Z, and subtracting -360\n // minutes gives 06:00 UTC.\n const wallClock = new Date(0);\n wallClock.setUTCFullYear(year, month - 1, day);\n wallClock.setUTCHours(hours, minutes, 0, 0);\n const wallClockAsUtc = wallClock.getTime();\n\n // The offset belongs to the instant being entered, not to the\n // wall-clock time read as UTC: within hours of a DST switch those\n // two sit on opposite sides of it. So the offset at the wall\n // clock is only a first guess, and the offset at the instant it\n // points to is the one that applies.\n const firstGuess = getTimezoneOffsetMinutes(new Date(wallClockAsUtc), timezone);\n const offsetMinutes = getTimezoneOffsetMinutes(new Date(wallClockAsUtc - firstGuess * 60000), timezone);\n resultDate = new Date(wallClockAsUtc - offsetMinutes * 60000);\n\n // A time the spring switch skips (02:30 in New York) has no\n // instant of its own; it moves forward across the gap.\n if (getTimezoneOffsetMinutes(resultDate, timezone) !== offsetMinutes) {\n resultDate = new Date(wallClockAsUtc - firstGuess * 60000);\n }\n }\n\n if (isNaN(resultDate.getTime())) {\n throw new Error(\"Invalid date\");\n }\n\n onChange?.(resultDate);\n } catch (e) {\n // Don't call onChange with null while typing\n return;\n }\n };\n\n // Formatted when the value, mode or zone changes, not on every render.\n // Focusing formats afresh (below), so typing always starts from the\n // current wall-clock text.\n const valueTime = value instanceof Date ? value.getTime() : undefined;\n const displayValue = React.useMemo(\n () => valueAsInputValue(valueTime === undefined ? null : new Date(valueTime), mode, timezone),\n [valueTime, mode, timezone]\n );\n\n const handleFocus = () => {\n setFocused(true);\n setIsTyping(true);\n setInternalValue(valueAsInputValue(value ?? null, mode, timezone));\n };\n\n const handleBlur = () => {\n setFocused(false);\n setIsTyping(false);\n };\n\n return (\n <>\n <div\n className={cls(\n \"rounded-lg relative max-w-full\",\n !invisible && fieldBackgroundMixin,\n // An invisible field has no hover fill of its own: whatever frames it (a table cell) carries the hover.\n disabled ? fieldBackgroundDisabledMixin : (invisible ? \"\" : fieldBackgroundHoverMixin),\n {\n \"min-h-[28px]\": size === \"smallest\",\n \"min-h-[32px]\": size === \"small\",\n \"min-h-[40px]\": size === \"medium\",\n \"min-h-[48px]\": size === \"large\"\n },\n className\n )}\n style={style}\n onClick={() => {\n if (!disabled) {\n inputRef.current?.focus();\n }\n }}\n >\n {label && (\n <InputLabel\n id={labelId}\n htmlFor={inputId}\n className={cls(\n \"absolute top-1 pointer-events-none\",\n !error\n ? focused\n ? \"text-primary\"\n : \"text-text-secondary dark:text-text-secondary-dark\"\n : \"text-red-600 dark:text-red-500\",\n disabled ? \"opacity-50\" : \"\"\n )}\n shrink={true}\n // shrink={hasValue || focused}\n >\n {label}\n </InputLabel>\n )}\n\n <input\n ref={setInputRef}\n id={inputId}\n aria-labelledby={label ? labelId : undefined}\n aria-label={label ? undefined : ariaLabel}\n type={mode === \"date_time\" ? \"datetime-local\" : mode === \"time\" ? \"time\" : \"date\"}\n lang={locale}\n aria-invalid={error || undefined}\n value={isTyping ? internalValue : displayValue}\n onChange={handleInputChange}\n onFocus={handleFocus}\n onBlur={handleBlur}\n disabled={disabled}\n className={cls(\n \"w-full outline-hidden bg-transparent leading-normal text-sm px-3\",\n // Room for the adornments drawn over the input's right end.\n pickerButton ? (clearable ? \"pr-14\" : \"pr-12\") : (clearable ? \"pr-12\" : \"\"),\n \"rounded-lg\",\n {\n \"min-h-[28px]\": size === \"smallest\",\n \"min-h-[32px]\": size === \"small\",\n \"min-h-[40px]\": size === \"medium\",\n \"min-h-[48px]\": size === \"large\"\n },\n // A flat py-2 (16px) plus the line box floored\n // the field at 40px, so smallest/small/medium all\n // rendered the same height. Scale it with `size`.\n label\n ? \"pt-8 pb-2\"\n : { smallest: \"py-0.5\", small: \"py-1\", medium: \"py-2\", large: \"py-3\" }[size],\n inputClassName,\n disabled &&\n \"border border-transparent outline-hidden opacity-50 dark:opacity-50 text-surface-accent-600 dark:text-surface-accent-500\"\n )}\n />\n {pickerButton && <IconButton\n size={adornmentSize}\n aria-label={mode === \"time\" ? labels.openTimePicker : \"Open calendar\"}\n disabled={disabled}\n onClick={(e) => {\n e.stopPropagation();\n try {\n inputRef.current?.showPicker();\n } catch {\n // The browser refuses (a disabled or read-only input, or\n // no user activation): there is no picker to open, and\n // the field still takes typing.\n }\n }}\n className=\"absolute right-3 top-1/2 transform -translate-y-1/2 text-surface-accent-500!\"\n >\n {mode === \"time\" ? <ClockIcon/> : <CalendarIcon/>}\n </IconButton>}\n {clearable && value && (\n <IconButton\n size={adornmentSize}\n aria-label=\"Clear date\"\n disabled={disabled}\n onClick={handleClear}\n className=\"absolute right-14 top-1/2 transform -translate-y-1/2 text-surface-accent-400 \"\n >\n <XIcon/>\n </IconButton>\n )}\n </div>\n {invalidValue && (\n <div className=\"flex items-center m-2\">\n <AlertCircleIcon size={iconSize.small} color={\"error\"}/>\n <div className=\"pl-2\">\n <Typography variant={\"body2\"}>\n Invalid date value for this field\n </Typography>\n <Typography variant={\"body2\"}>\n {`The provided value is: ${JSON.stringify(value)}`}\n </Typography>\n </div>\n </div>\n )}\n </>\n );\n};\n\nconst inputStyles = `\n /* Hide the default calendar icon in Chrome, Safari, Edge, Opera */\n input[type=\"date\"]::-webkit-calendar-picker-indicator,\n input[type=\"datetime-local\"]::-webkit-calendar-picker-indicator,\n input[type=\"time\"]::-webkit-calendar-picker-indicator {\n display: none;\n -webkit-appearance: none;\n }\n /* Hide default calendar icon in Firefox */\n input[type=\"date\"],\n input[type=\"datetime-local\"],\n input[type=\"time\"] {\n -moz-appearance:textfield;\n }\n `;\n"],"mappings":";;;;;;;;;;;;;;AA8DA,IAAM,cAA0C;CAC5C,MAAM;CACN,OAAO;CACP,KAAK;CACL,MAAM;CACN,QAAQ;CACR,QAAQ;AACZ;;;;;;;;;;;;;AAcA,IAAM,iCAAiB,IAAI,IAAiC;AAE5D,SAAS,oBAAoB,UAAmD;CAC5E,IAAI,aAAa,KAAA,GAAW,OAAO,IAAI,KAAK,eAAe,SAAS,WAAW;CAC/E,IAAI,YAAY,eAAe,IAAI,QAAQ;CAC3C,IAAI,CAAC,WAAW;EACZ,YAAY,IAAI,KAAK,eAAe,SAAS;GACzC,GAAG;GACH;EACJ,CAAC;EACD,eAAe,IAAI,UAAU,SAAS;CAC1C;CACA,OAAO;AACX;;AAGA,SAAS,kBACL,WACA,MACA,UACM;CACN,IAAI,CAAC,aAAa,EAAE,qBAAqB,SAAS,MAAM,UAAU,QAAQ,CAAC,GACvE,OAAO;CAKX,MAAM,QAAQ,oBAAoB,QAAQ,CAAC,CAAC,cAAc,SAAS;CAEnE,MAAM,WAAW,SAAiB,MAAM,MAAK,MAAK,EAAE,SAAS,IAAI,CAAC,EAAE,SAAS;CAE7E,MAAM,OAAO,QAAQ,MAAM;CAC3B,MAAM,QAAQ,QAAQ,OAAO;CAC7B,MAAM,MAAM,QAAQ,KAAK;CAEzB,IAAI,SAAS,QACT,OAAO,GAAG,QAAQ,MAAM,EAAE,GAAG,QAAQ,QAAQ;CAEjD,IAAI,SAAS,QACT,OAAO,GAAG,KAAK,GAAG,MAAM,GAAG;MAI3B,OAAO,GAAG,KAAK,GAAG,MAAM,GAAG,IAAI,GAFjB,QAAQ,MAEY,EAAM,GADxB,QAAQ,QACmB;AAEnD;AAEA,IAAa,iBAA+C,EACxD,OACA,OACA,UACA,UACA,WACA,OAAO,QACP,OACA,OAAO,SACP,WACA,OACA,gBACA,WACA,eAAe,MACf,UAAU,cACV,UACA,QACA,cAAc,gBACZ;CACF,MAAM,WAAW,OAAyB,IAAI;CAC9C,MAAM,SAAS,YAAY;CAC3B,MAAM,cAAc,aAAa,SAAkC;EAC/D,SAAS,UAAU;EACnB,IAAI,OAAO,iBAAiB,YAAY,aAAa,IAAI;OACpD,IAAI,cAAc,aAAa,UAAU;CAClD,GAAG,CAAC,YAAY,CAAC;CACjB,MAAM,CAAC,SAAS,cAAc,SAAS,KAAK;CAI5C,MAAM,UAAU,MAAM;CACtB,MAAM,UAAU,GAAG,QAAQ;CAC3B,MAAM,CAAC,eAAe,oBAAoB,SAAiB,EAAE;CAC7D,MAAM,CAAC,UAAU,eAAe,SAAS,KAAK;CAC9C,MAAM,eAAe,UAAU,KAAA,KAAa,UAAU,SAAS,EAAE,iBAAiB,SAAS,MAAM,MAAM,QAAQ,CAAC;CAKhH,MAAM,gBAAgB,SAAS,UAAU,WAAW,SAAS,WAAW,UAAU;CAElF,gBAAgB,iBAAiB,WAAW;CAE5C,MAAM,eAAe,MAAwB;EACzC,EAAE,eAAe;EAEjB,IAAI,UAAU;EACd,iBAAiB,EAAE;EACnB,YAAY,KAAK;EACjB,WAAW,IAAI;CACnB;CAGA,MAAM,4BAA4B,MAAY,OAAwB;EAClE,IAAI,CAAC,IAED,OAAO,CAAC,KAAK,kBAAkB;EAGnC,MAAM,eAAe,oBAAoB,KAAK;EAC9C,MAAM,cAAc,oBAAoB,EAAE;EAE1C,MAAM,WAAW,aAAa,cAAc,IAAI;EAChD,MAAM,UAAU,YAAY,cAAc,IAAI;EAE9C,MAAM,WAAW,OAAkC,SAC/C,SAAS,MAAM,MAAK,MAAK,EAAE,SAAS,IAAI,CAAC,EAAE,SAAS,KAAK,EAAE;EAE/D,MAAM,UAAU,IAAI,KAAK,KAAK,IAC1B,QAAQ,UAAU,MAAM,GACxB,QAAQ,UAAU,OAAO,IAAI,GAC7B,QAAQ,UAAU,KAAK,GACvB,QAAQ,UAAU,MAAM,GACxB,QAAQ,UAAU,QAAQ,CAC9B,CAAC;EAUD,QAAQ,IARW,KAAK,KAAK,IACzB,QAAQ,SAAS,MAAM,GACvB,QAAQ,SAAS,OAAO,IAAI,GAC5B,QAAQ,SAAS,KAAK,GACtB,QAAQ,SAAS,MAAM,GACvB,QAAQ,SAAS,QAAQ,CAC7B,CAEQ,CAAA,CAAO,QAAQ,IAAI,QAAQ,QAAQ,KAAK;CACpD;CAGA,MAAM,qBAAqB,MAA2C;EAClE,MAAM,aAAa,EAAE,OAAO;EAC5B,iBAAiB,UAAU;EAC3B,YAAY,IAAI;EAEhB,IAAI,CAAC,YAAY;GACb,WAAW,IAAI;GACf;EACJ;EAEA,IAAI;GACA,IAAI,MAAc,OAAe,KAAa,QAAQ,GAAG,UAAU;GAEnE,IAAI,SAAS,QAAQ;IAGjB,CAAC,MAAM,OAAO,OAAO;KAAC;KAAM;KAAG;IAAC;IAChC,CAAC,OAAO,WAAW,WAAW,MAAM,GAAG,CAAC,CAAC,IAAI,MAAM;GACvD,OAAO,IAAI,SAAS,QAEhB,CAAC,MAAM,OAAO,OAAO,WAAW,MAAM,GAAG,CAAC,CAAC,IAAI,MAAM;QAClD;IAEH,MAAM,CAAC,UAAU,YAAY,WAAW,MAAM,GAAG;IACjD,CAAC,MAAM,OAAO,OAAO,SAAS,MAAM,GAAG,CAAC,CAAC,IAAI,MAAM;IACnD,CAAC,OAAO,WAAW,SAAS,MAAM,GAAG,CAAC,CAAC,IAAI,MAAM;GACrD;GAKA,IAAI;GAEJ,IAAI,CAAC,UAAU;IAEX,6BAAa,IAAI,KAAK,CAAC;IACvB,WAAW,YAAY,MAAM,QAAQ,GAAG,GAAG;IAC3C,WAAW,SAAS,OAAO,SAAS,GAAG,CAAC;GAC5C,OAAO;IAKH,MAAM,4BAAY,IAAI,KAAK,CAAC;IAC5B,UAAU,eAAe,MAAM,QAAQ,GAAG,GAAG;IAC7C,UAAU,YAAY,OAAO,SAAS,GAAG,CAAC;IAC1C,MAAM,iBAAiB,UAAU,QAAQ;IAOzC,MAAM,aAAa,yBAAyB,IAAI,KAAK,cAAc,GAAG,QAAQ;IAC9E,MAAM,gBAAgB,yCAAyB,IAAI,KAAK,iBAAiB,aAAa,GAAK,GAAG,QAAQ;IACtG,6BAAa,IAAI,KAAK,iBAAiB,gBAAgB,GAAK;IAI5D,IAAI,yBAAyB,YAAY,QAAQ,MAAM,eACnD,6BAAa,IAAI,KAAK,iBAAiB,aAAa,GAAK;GAEjE;GAEA,IAAI,MAAM,WAAW,QAAQ,CAAC,GAC1B,MAAM,IAAI,MAAM,cAAc;GAGlC,WAAW,UAAU;EACzB,SAAS,GAAG;GAER;EACJ;CACJ;CAKA,MAAM,YAAY,iBAAiB,OAAO,MAAM,QAAQ,IAAI,KAAA;CAC5D,MAAM,eAAe,MAAM,cACjB,kBAAkB,cAAc,KAAA,IAAY,OAAO,IAAI,KAAK,SAAS,GAAG,MAAM,QAAQ,GAC5F;EAAC;EAAW;EAAM;CAAQ,CAC9B;CAEA,MAAM,oBAAoB;EACtB,WAAW,IAAI;EACf,YAAY,IAAI;EAChB,iBAAiB,kBAAkB,SAAS,MAAM,MAAM,QAAQ,CAAC;CACrE;CAEA,MAAM,mBAAmB;EACrB,WAAW,KAAK;EAChB,YAAY,KAAK;CACrB;CAEA,OACI,qBAAA,UAAA,EAAA,UAAA,CACI,qBAAC,OAAD;EACI,WAAW,IACP,kCACA,CAAC,aAAA,2CAEL,WAAW,+BAAgC,YAAY,KAAK,2BACxD;GACI,gBAAgB,SAAS;GACzB,gBAAgB,SAAS;GACzB,gBAAgB,SAAS;GACzB,gBAAgB,SAAS;EAC7B,GACA,SACJ;EACO;EACP,eAAe;GACX,IAAI,CAAC,UACD,SAAS,SAAS,MAAM;EAEhC;YAnBJ;GAqBK,SACG,oBAAC,YAAD;IACI,IAAI;IACJ,SAAS;IACT,WAAW,IACP,sCACA,CAAC,QACK,UACI,iBACA,sDACJ,kCACN,WAAW,eAAe,EAC9B;IACA,QAAQ;cAGP;GACO,CAAA;GAGhB,oBAAC,SAAD;IACI,KAAK;IACL,IAAI;IACJ,mBAAiB,QAAQ,UAAU,KAAA;IACnC,cAAY,QAAQ,KAAA,IAAY;IAChC,MAAM,SAAS,cAAc,mBAAmB,SAAS,SAAS,SAAS;IAC3E,MAAM;IACN,gBAAc,SAAS,KAAA;IACvB,OAAO,WAAW,gBAAgB;IAClC,UAAU;IACV,SAAS;IACT,QAAQ;IACE;IACV,WAAW,IACP,oEAEA,eAAgB,YAAY,UAAU,UAAY,YAAY,UAAU,IACxE,cACA;KACI,gBAAgB,SAAS;KACzB,gBAAgB,SAAS;KACzB,gBAAgB,SAAS;KACzB,gBAAgB,SAAS;IAC7B,GAIA,QACM,cACA;KAAE,UAAU;KAAU,OAAO;KAAQ,QAAQ;KAAQ,OAAO;IAAO,EAAE,OAC3E,gBACA,YACA,0HACJ;GACH,CAAA;GACA,gBAAgB,oBAAC,YAAD;IACb,MAAM;IACN,cAAY,SAAS,SAAS,OAAO,iBAAiB;IAC5C;IACV,UAAU,MAAM;KACZ,EAAE,gBAAgB;KAClB,IAAI;MACA,SAAS,SAAS,WAAW;KACjC,QAAQ,CAIR;IACJ;IACA,WAAU;cAET,SAAS,SAAS,oBAAC,WAAD,CAAW,CAAA,IAAI,oBAAC,cAAD,CAAc,CAAA;GACxC,CAAA;GACX,aAAa,SACV,oBAAC,YAAD;IACI,MAAM;IACN,cAAW;IACD;IACV,SAAS;IACT,WAAU;cAEV,oBAAC,OAAD,CAAO,CAAA;GACC,CAAA;EAEf;KACJ,gBACG,qBAAC,OAAD;EAAK,WAAU;YAAf,CACI,oBAAC,iBAAD;GAAiB,MAAM,SAAS;GAAO,OAAO;EAAS,CAAA,GACvD,qBAAC,OAAD;GAAK,WAAU;aAAf,CACI,oBAAC,YAAD;IAAY,SAAS;cAAS;GAElB,CAAA,GACZ,oBAAC,YAAD;IAAY,SAAS;cAChB,0BAA0B,KAAK,UAAU,KAAK;GACvC,CAAA,CACX;IACJ;GAEX,EAAA,CAAA;AAEV;AAEA,IAAM,cAAc"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { TextField } from "./TextField.es.js";
|
|
3
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
4
|
+
import { jsx } from "react/jsx-runtime";
|
|
5
|
+
//#region src/components/DebouncedTextField.tsx
|
|
6
|
+
function DebouncedTextField(props) {
|
|
7
|
+
const { value, onChange, name, onKeyDown, onBlur } = props;
|
|
8
|
+
const [internalValue, setInternalValue] = useState(value ?? "");
|
|
9
|
+
const lastSentValueRef = useRef(value ?? "");
|
|
10
|
+
const timerRef = useRef(void 0);
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
const externalValue = value ?? "";
|
|
13
|
+
if (externalValue !== lastSentValueRef.current) {
|
|
14
|
+
setInternalValue(externalValue);
|
|
15
|
+
lastSentValueRef.current = externalValue;
|
|
16
|
+
}
|
|
17
|
+
}, [value]);
|
|
18
|
+
const pendingFlushRef = useRef(void 0);
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
return () => {
|
|
21
|
+
if (timerRef.current) {
|
|
22
|
+
clearTimeout(timerRef.current);
|
|
23
|
+
timerRef.current = void 0;
|
|
24
|
+
pendingFlushRef.current?.();
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
}, []);
|
|
28
|
+
const flushChange = useCallback((next, event) => {
|
|
29
|
+
if (timerRef.current) {
|
|
30
|
+
clearTimeout(timerRef.current);
|
|
31
|
+
timerRef.current = void 0;
|
|
32
|
+
}
|
|
33
|
+
if (next !== value && onChange) {
|
|
34
|
+
lastSentValueRef.current = next;
|
|
35
|
+
const e = {
|
|
36
|
+
...event,
|
|
37
|
+
target: {
|
|
38
|
+
...event?.target,
|
|
39
|
+
value: next,
|
|
40
|
+
name
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
onChange(e);
|
|
44
|
+
}
|
|
45
|
+
}, [
|
|
46
|
+
value,
|
|
47
|
+
onChange,
|
|
48
|
+
name
|
|
49
|
+
]);
|
|
50
|
+
const internalOnChange = useCallback((event) => {
|
|
51
|
+
const newValue = event.target.value;
|
|
52
|
+
setInternalValue(newValue);
|
|
53
|
+
if (timerRef.current) clearTimeout(timerRef.current);
|
|
54
|
+
const eventCopy = {
|
|
55
|
+
...event,
|
|
56
|
+
target: {
|
|
57
|
+
...event?.target,
|
|
58
|
+
name: event?.target?.name
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
pendingFlushRef.current = () => flushChange(newValue, eventCopy);
|
|
62
|
+
timerRef.current = setTimeout(() => {
|
|
63
|
+
flushChange(newValue, eventCopy);
|
|
64
|
+
}, 150);
|
|
65
|
+
}, [flushChange]);
|
|
66
|
+
const internalOnKeyDown = useCallback((event) => {
|
|
67
|
+
if (event.key === "Enter" && timerRef.current) flushChange(internalValue);
|
|
68
|
+
onKeyDown?.(event);
|
|
69
|
+
}, [
|
|
70
|
+
internalValue,
|
|
71
|
+
flushChange,
|
|
72
|
+
onKeyDown
|
|
73
|
+
]);
|
|
74
|
+
const internalOnBlur = useCallback((event) => {
|
|
75
|
+
flushChange(internalValue, event);
|
|
76
|
+
onBlur?.(event);
|
|
77
|
+
}, [
|
|
78
|
+
internalValue,
|
|
79
|
+
flushChange,
|
|
80
|
+
onBlur
|
|
81
|
+
]);
|
|
82
|
+
return /* @__PURE__ */ jsx(TextField, {
|
|
83
|
+
...props,
|
|
84
|
+
onChange: internalOnChange,
|
|
85
|
+
onKeyDown: internalOnKeyDown,
|
|
86
|
+
onBlur: internalOnBlur,
|
|
87
|
+
value: internalValue
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
//#endregion
|
|
91
|
+
export { DebouncedTextField };
|
|
92
|
+
|
|
93
|
+
//# sourceMappingURL=DebouncedTextField.es.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DebouncedTextField.es.js","names":[],"sources":["../../src/components/DebouncedTextField.tsx"],"sourcesContent":["\"use client\";\nimport React, { useCallback, useEffect, useRef, useState } from \"react\";\nimport { TextField, TextFieldProps } from \"./TextField\";\n\ntype TextFieldChangeEvent = React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>;\n\nexport function DebouncedTextField<T extends string | number>(props: TextFieldProps<T>) {\n\n const { value, onChange, name, onKeyDown, onBlur } = props;\n const [internalValue, setInternalValue] = useState<T | string>(value ?? \"\");\n const lastSentValueRef = useRef<T | string>(value ?? \"\");\n const timerRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\n\n // Sync state with props.value when it changes externally\n useEffect(() => {\n const externalValue = value ?? \"\";\n if (externalValue !== lastSentValueRef.current) {\n setInternalValue(externalValue);\n lastSentValueRef.current = externalValue;\n }\n }, [value]);\n\n // A value still waiting on the timer when the field unmounts is reported,\n // not dropped: a dialog that closes on Enter unmounts the field it was\n // typed into. Read through a ref — the cleanup runs once, at unmount.\n const pendingFlushRef = useRef<(() => void) | undefined>(undefined);\n useEffect(() => {\n return () => {\n if (timerRef.current) {\n clearTimeout(timerRef.current);\n timerRef.current = undefined;\n pendingFlushRef.current?.();\n }\n };\n }, []);\n\n const flushChange = useCallback((next: T | string, event?: TextFieldChangeEvent) => {\n if (timerRef.current) {\n clearTimeout(timerRef.current);\n timerRef.current = undefined;\n }\n if (next !== value && onChange) {\n lastSentValueRef.current = next;\n const e = {\n ...event,\n target: {\n ...event?.target,\n value: next,\n name\n }\n } as TextFieldChangeEvent;\n onChange(e);\n }\n }, [value, onChange, name]);\n\n const internalOnChange = useCallback((event: TextFieldChangeEvent) => {\n const newValue = event.target.value;\n setInternalValue(newValue);\n\n if (timerRef.current) clearTimeout(timerRef.current);\n\n const eventCopy = {\n ...event,\n target: {\n ...event?.target,\n name: event?.target?.name\n }\n };\n\n pendingFlushRef.current = () => flushChange(newValue, eventCopy as TextFieldChangeEvent);\n timerRef.current = setTimeout(() => {\n flushChange(newValue, eventCopy as TextFieldChangeEvent);\n }, 150);\n }, [flushChange]);\n\n // Enter submits the form around the field without blurring it, so the\n // timer had not fired yet and the form submitted the value from before\n // typing. Report it first: the keydown is handled before the browser's\n // implicit submission, and React renders the change in between.\n const internalOnKeyDown = useCallback((event: React.KeyboardEvent<HTMLInputElement | HTMLTextAreaElement>) => {\n if (event.key === \"Enter\" && timerRef.current) flushChange(internalValue);\n onKeyDown?.(event as React.KeyboardEvent<HTMLInputElement>);\n }, [internalValue, flushChange, onKeyDown]);\n\n const internalOnBlur = useCallback((event: React.FocusEvent<HTMLInputElement>) => {\n flushChange(internalValue, event as TextFieldChangeEvent);\n onBlur?.(event);\n }, [internalValue, flushChange, onBlur]);\n\n return <TextField {...props}\n onChange={internalOnChange}\n onKeyDown={internalOnKeyDown}\n onBlur={internalOnBlur}\n value={internalValue as T}/>;\n}\n"],"mappings":";;;;;AAMA,SAAgB,mBAA8C,OAA0B;CAEpF,MAAM,EAAE,OAAO,UAAU,MAAM,WAAW,WAAW;CACrD,MAAM,CAAC,eAAe,oBAAoB,SAAqB,SAAS,EAAE;CAC1E,MAAM,mBAAmB,OAAmB,SAAS,EAAE;CACvD,MAAM,WAAW,OAAkD,KAAA,CAAS;CAG5E,gBAAgB;EACZ,MAAM,gBAAgB,SAAS;EAC/B,IAAI,kBAAkB,iBAAiB,SAAS;GAC5C,iBAAiB,aAAa;GAC9B,iBAAiB,UAAU;EAC/B;CACJ,GAAG,CAAC,KAAK,CAAC;CAKV,MAAM,kBAAkB,OAAiC,KAAA,CAAS;CAClE,gBAAgB;EACZ,aAAa;GACT,IAAI,SAAS,SAAS;IAClB,aAAa,SAAS,OAAO;IAC7B,SAAS,UAAU,KAAA;IACnB,gBAAgB,UAAU;GAC9B;EACJ;CACJ,GAAG,CAAC,CAAC;CAEL,MAAM,cAAc,aAAa,MAAkB,UAAiC;EAChF,IAAI,SAAS,SAAS;GAClB,aAAa,SAAS,OAAO;GAC7B,SAAS,UAAU,KAAA;EACvB;EACA,IAAI,SAAS,SAAS,UAAU;GAC5B,iBAAiB,UAAU;GAC3B,MAAM,IAAI;IACN,GAAG;IACH,QAAQ;KACJ,GAAG,OAAO;KACV,OAAO;KACP;IACJ;GACJ;GACA,SAAS,CAAC;EACd;CACJ,GAAG;EAAC;EAAO;EAAU;CAAI,CAAC;CAE1B,MAAM,mBAAmB,aAAa,UAAgC;EAClE,MAAM,WAAW,MAAM,OAAO;EAC9B,iBAAiB,QAAQ;EAEzB,IAAI,SAAS,SAAS,aAAa,SAAS,OAAO;EAEnD,MAAM,YAAY;GACd,GAAG;GACH,QAAQ;IACJ,GAAG,OAAO;IACV,MAAM,OAAO,QAAQ;GACzB;EACJ;EAEA,gBAAgB,gBAAgB,YAAY,UAAU,SAAiC;EACvF,SAAS,UAAU,iBAAiB;GAChC,YAAY,UAAU,SAAiC;EAC3D,GAAG,GAAG;CACV,GAAG,CAAC,WAAW,CAAC;CAMhB,MAAM,oBAAoB,aAAa,UAAuE;EAC1G,IAAI,MAAM,QAAQ,WAAW,SAAS,SAAS,YAAY,aAAa;EACxE,YAAY,KAA8C;CAC9D,GAAG;EAAC;EAAe;EAAa;CAAS,CAAC;CAE1C,MAAM,iBAAiB,aAAa,UAA8C;EAC9E,YAAY,eAAe,KAA6B;EACxD,SAAS,KAAK;CAClB,GAAG;EAAC;EAAe;EAAa;CAAM,CAAC;CAEvC,OAAO,oBAAC,WAAD;EAAW,GAAI;EAClB,UAAU;EACV,WAAW;EACX,QAAQ;EACR,OAAO;CAAoB,CAAA;AACnC"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { focusedDisabled, paperMixin } from "../styles.es.js";
|
|
3
|
+
import { cls } from "../util/cls.es.js";
|
|
4
|
+
import { PortalContainerProvider, usePortalContainer } from "../hooks/PortalContainerContext.es.js";
|
|
5
|
+
import { useRestoreInterruptedFocus } from "../hooks/useRestoreInterruptedFocus.es.js";
|
|
6
|
+
import { useModalFocus } from "../hooks/useModalFocus.es.js";
|
|
7
|
+
import { useEffect, useState } from "react";
|
|
8
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
9
|
+
import * as DialogPrimitive from "@radix-ui/react-dialog";
|
|
10
|
+
//#region src/components/Dialog.tsx
|
|
11
|
+
var widthClasses = {
|
|
12
|
+
xs: "max-w-xs w-xs",
|
|
13
|
+
sm: "max-w-sm w-sm",
|
|
14
|
+
md: "max-w-md w-md",
|
|
15
|
+
lg: "max-w-lg w-lg",
|
|
16
|
+
xl: "max-w-xl w-xl",
|
|
17
|
+
"2xl": "max-w-2xl w-2xl",
|
|
18
|
+
"3xl": "max-w-3xl w-3xl",
|
|
19
|
+
"4xl": "max-w-4xl w-4xl",
|
|
20
|
+
"5xl": "max-w-5xl w-5xl",
|
|
21
|
+
"6xl": "max-w-6xl w-6xl",
|
|
22
|
+
"7xl": "max-w-7xl w-7xl",
|
|
23
|
+
full: "max-w-full w-full"
|
|
24
|
+
};
|
|
25
|
+
var Dialog = ({ open, onOpenChange, children, className, containerClassName, fullWidth = true, fullHeight, fullScreen, scrollable = true, maxWidth = "lg", modal = true, onOpenAutoFocus, onCloseAutoFocus, onEscapeKeyDown, onPointerDownOutside, onInteractOutside, dismissOnBackdrop = false, initialFocus, disableInitialFocus = true, portalContainer, "aria-describedby": ariaDescribedby }) => {
|
|
26
|
+
const [displayed, setDisplayed] = useState(false);
|
|
27
|
+
const [popupHost, setPopupHost] = useState(null);
|
|
28
|
+
const [contentEl, setContentEl] = useState(null);
|
|
29
|
+
useRestoreInterruptedFocus(contentEl);
|
|
30
|
+
const [overlayEl, setOverlayEl] = useState(null);
|
|
31
|
+
const [layerEl, setLayerEl] = useState(null);
|
|
32
|
+
const focus = useModalFocus({
|
|
33
|
+
open: Boolean(open),
|
|
34
|
+
initialFocus,
|
|
35
|
+
focusContainer: disableInitialFocus,
|
|
36
|
+
onOpenAutoFocus,
|
|
37
|
+
onCloseAutoFocus
|
|
38
|
+
});
|
|
39
|
+
const contextContainer = usePortalContainer();
|
|
40
|
+
const finalContainer = portalContainer ?? contextContainer ?? void 0;
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
if (!open) {
|
|
43
|
+
const timeout = setTimeout(() => {
|
|
44
|
+
setDisplayed(false);
|
|
45
|
+
}, 100);
|
|
46
|
+
return () => clearTimeout(timeout);
|
|
47
|
+
} else {
|
|
48
|
+
setDisplayed(true);
|
|
49
|
+
return () => {};
|
|
50
|
+
}
|
|
51
|
+
}, [open]);
|
|
52
|
+
return /* @__PURE__ */ jsx(DialogPrimitive.Root, {
|
|
53
|
+
open: displayed || open,
|
|
54
|
+
modal,
|
|
55
|
+
onOpenChange,
|
|
56
|
+
children: /* @__PURE__ */ jsx(DialogPrimitive.Portal, {
|
|
57
|
+
container: finalContainer,
|
|
58
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
59
|
+
ref: setLayerEl,
|
|
60
|
+
className: cls("fixed inset-0 z-50 flex justify-center items-center", containerClassName),
|
|
61
|
+
children: [/* @__PURE__ */ jsx(DialogPrimitive.Overlay, {
|
|
62
|
+
ref: setOverlayEl,
|
|
63
|
+
className: cls("fixed inset-0 transition-opacity ease-in-out duration-200 motion-reduce:transition-none bg-surface-scrim backdrop-blur-sm", displayed && open ? "opacity-100" : "opacity-0", "z-50 fixed top-0 left-0 w-full h-full"),
|
|
64
|
+
style: { pointerEvents: displayed ? "auto" : "none" }
|
|
65
|
+
}), /* @__PURE__ */ jsxs(DialogPrimitive.Content, {
|
|
66
|
+
ref: setContentEl,
|
|
67
|
+
onEscapeKeyDown,
|
|
68
|
+
onOpenAutoFocus: focus.onOpenAutoFocus,
|
|
69
|
+
onCloseAutoFocus: focus.onCloseAutoFocus,
|
|
70
|
+
onPointerDownOutside: (e) => {
|
|
71
|
+
onPointerDownOutside?.(e);
|
|
72
|
+
if (e.defaultPrevented) return;
|
|
73
|
+
const target = e.target;
|
|
74
|
+
if (!dismissOnBackdrop || !(target === overlayEl || target === layerEl)) e.preventDefault();
|
|
75
|
+
},
|
|
76
|
+
onFocusOutside: (e) => e.preventDefault(),
|
|
77
|
+
onInteractOutside,
|
|
78
|
+
"aria-describedby": ariaDescribedby,
|
|
79
|
+
className: cls("relative z-60 outline-none", focusedDisabled, fullWidth && !fullScreen ? "w-11/12" : void 0, fullHeight && !fullScreen ? "h-[90vh]" : void 0, fullScreen ? "h-screen w-screen" : void 0, maxWidth && !fullScreen ? widthClasses[maxWidth] : void 0),
|
|
80
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
81
|
+
className: cls(paperMixin, "rounded-2xl", "z-60", "relative", "w-full", "overflow-hidden", "outline-none focus:outline-none", fullHeight || fullScreen ? "h-full" : void 0, "text-text-primary dark:text-text-primary-dark", "justify-center items-center", fullScreen ? void 0 : "max-h-[90vh] shadow-lg", "ease-in-out duration-200 motion-reduce:transition-none", scrollable && "overflow-y-auto", displayed && open ? "opacity-100 scale-100" : "opacity-0 scale-[0.97]", className),
|
|
82
|
+
children: /* @__PURE__ */ jsx(PortalContainerProvider, {
|
|
83
|
+
container: popupHost,
|
|
84
|
+
children
|
|
85
|
+
})
|
|
86
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
87
|
+
ref: setPopupHost,
|
|
88
|
+
className: "relative z-70 w-0 h-0"
|
|
89
|
+
})]
|
|
90
|
+
})]
|
|
91
|
+
})
|
|
92
|
+
})
|
|
93
|
+
});
|
|
94
|
+
};
|
|
95
|
+
//#endregion
|
|
96
|
+
export { Dialog };
|
|
97
|
+
|
|
98
|
+
//# sourceMappingURL=Dialog.es.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Dialog.es.js","names":[],"sources":["../../src/components/Dialog.tsx"],"sourcesContent":["\"use client\";\nimport React, { useEffect, useState } from \"react\";\nimport * as DialogPrimitive from \"@radix-ui/react-dialog\";\nimport { focusedDisabled, paperMixin } from \"../styles\";\nimport { cls } from \"../util\";\nimport { PortalContainerProvider, usePortalContainer } from \"../hooks/PortalContainerContext\";\nimport { useRestoreInterruptedFocus } from \"../hooks/useRestoreInterruptedFocus\";\nimport { useModalFocus } from \"../hooks/useModalFocus\";\n\nexport type DialogProps = {\n open?: boolean;\n onOpenChange?: (open: boolean) => void;\n children: React.ReactNode;\n className?: string;\n containerClassName?: string;\n fullWidth?: boolean;\n fullHeight?: boolean;\n fullScreen?: boolean;\n scrollable?: boolean;\n maxWidth?: keyof typeof widthClasses;\n modal?: boolean;\n onOpenAutoFocus?: (e: Event) => void;\n /**\n * Called when focus is about to return after the dialog closes. By default\n * it goes back to the element that had it when the dialog opened; call\n * `preventDefault()` to put it somewhere else.\n */\n onCloseAutoFocus?: (e: Event) => void;\n onEscapeKeyDown?: (e: KeyboardEvent) => void;\n onPointerDownOutside?: (e: Event) => void;\n onInteractOutside?: (e: Event) => void;\n /**\n * Whether a click on the dimmed backdrop closes the dialog. Off by default:\n * a record or a form dialog holds work that one stray click must not throw\n * away. Turn it on for a picker, where closing loses nothing. Escape closes\n * either kind.\n */\n dismissOnBackdrop?: boolean;\n /**\n * The element to focus when the dialog opens, for a dialog whose purpose is\n * one field. Without it the dialog focuses itself, so a screen reader\n * announces it by its title and a phone does not raise its keyboard.\n */\n initialFocus?: React.RefObject<HTMLElement | null>;\n /**\n * If `true` (the default), the dialog will not focus the first focusable\n * element when opened: it focuses its own container instead. `false` lets\n * the first focusable element take focus.\n */\n disableInitialFocus?: boolean;\n portalContainer?: HTMLElement | null;\n \"aria-describedby\"?: string;\n};\n\nconst widthClasses = {\n xs: \"max-w-xs w-xs\",\n sm: \"max-w-sm w-sm\",\n md: \"max-w-md w-md\",\n lg: \"max-w-lg w-lg\",\n xl: \"max-w-xl w-xl\",\n \"2xl\": \"max-w-2xl w-2xl\",\n \"3xl\": \"max-w-3xl w-3xl\",\n \"4xl\": \"max-w-4xl w-4xl\",\n \"5xl\": \"max-w-5xl w-5xl\",\n \"6xl\": \"max-w-6xl w-6xl\",\n \"7xl\": \"max-w-7xl w-7xl\",\n full: \"max-w-full w-full\"\n};\n\nexport const Dialog = ({\n open,\n onOpenChange,\n children,\n className,\n containerClassName,\n fullWidth = true,\n fullHeight,\n fullScreen,\n scrollable = true,\n maxWidth = \"lg\",\n modal = true,\n onOpenAutoFocus,\n onCloseAutoFocus,\n onEscapeKeyDown,\n onPointerDownOutside,\n onInteractOutside,\n dismissOnBackdrop = false,\n initialFocus,\n disableInitialFocus = true,\n portalContainer,\n \"aria-describedby\": ariaDescribedby\n }: DialogProps) => {\n const [displayed, setDisplayed] = useState(false);\n // Where popups rendered *inside* this dialog get portaled to. A modal\n // dialog locks scrolling everywhere but its own content, so a popup\n // portaled to `document.body` renders fine and then refuses to scroll:\n // `react-remove-scroll` cancels every wheel event outside the lock.\n // Handing descendants this host keeps them inside it.\n const [popupHost, setPopupHost] = useState<HTMLDivElement | null>(null);\n\n // The focus trap's container. Tab between two fields used to land here\n // whenever the first field's tooltip closed on the way out.\n const [contentEl, setContentEl] = useState<HTMLDivElement | null>(null);\n useRestoreInterruptedFocus(contentEl);\n\n // The two surfaces outside the paper: a pointer-down on either is a\n // click on the backdrop. Anything else outside the paper — a toast, a\n // popup some other layer portaled to the body — is not.\n const [overlayEl, setOverlayEl] = useState<HTMLDivElement | null>(null);\n const [layerEl, setLayerEl] = useState<HTMLDivElement | null>(null);\n\n const focus = useModalFocus({\n open: Boolean(open),\n initialFocus,\n focusContainer: disableInitialFocus,\n onOpenAutoFocus,\n onCloseAutoFocus\n });\n\n // Get the portal container from context\n const contextContainer = usePortalContainer();\n\n // Prioritize manual prop, fallback to context container\n const finalContainer = (portalContainer ?? contextContainer ?? undefined) as HTMLElement | undefined;\n\n useEffect(() => {\n if (!open) {\n const timeout = setTimeout(() => {\n setDisplayed(false);\n }, 100);\n return () => clearTimeout(timeout);\n } else {\n setDisplayed(true);\n return () => {\n };\n }\n }, [open]);\n\n return (\n <DialogPrimitive.Root open={displayed || open}\n modal={modal}\n onOpenChange={onOpenChange}>\n <DialogPrimitive.Portal container={finalContainer}>\n\n <div ref={setLayerEl}\n className={cls(\"fixed inset-0 z-50 flex justify-center items-center\", containerClassName)}>\n\n <DialogPrimitive.Overlay\n ref={setOverlayEl}\n className={cls(\"fixed inset-0 transition-opacity ease-in-out duration-200 motion-reduce:transition-none bg-surface-scrim backdrop-blur-sm\",\n displayed && open ? \"opacity-100\" : \"opacity-0\",\n \"z-50 fixed top-0 left-0 w-full h-full\"\n )}\n style={{\n pointerEvents: displayed ? \"auto\" : \"none\"\n }}\n />\n\n {/* The content IS the paper's box: what is outside it is the\n backdrop, and what assistive technology and a test runner\n measure as the dialog is what the user sees. It used to fill\n the viewport, so the dimmed area was part of the dialog and\n the outside-click handlers could never fire. The paper itself\n is a child because its open animation scales it, and a\n transform would make it the containing block of every popup\n portaled into the host beside it. */}\n <DialogPrimitive.Content\n ref={setContentEl}\n onEscapeKeyDown={onEscapeKeyDown}\n onOpenAutoFocus={focus.onOpenAutoFocus}\n onCloseAutoFocus={focus.onCloseAutoFocus}\n onPointerDownOutside={(e) => {\n onPointerDownOutside?.(e);\n if (e.defaultPrevented) return;\n const target = e.target;\n const onBackdrop = target === overlayEl || target === layerEl;\n if (!dismissOnBackdrop || !onBackdrop) e.preventDefault();\n }}\n // Focus leaving the paper is not a request to close it. A modal\n // dialog already ignores it; a non-modal one would dismiss.\n onFocusOutside={(e) => e.preventDefault()}\n onInteractOutside={onInteractOutside}\n aria-describedby={ariaDescribedby}\n className={cls(\n \"relative z-60 outline-none\",\n // Focused on open so a screen reader announces it; it is\n // a container, not a control, so it draws no ring.\n focusedDisabled,\n fullWidth && !fullScreen ? \"w-11/12\" : undefined,\n fullHeight && !fullScreen ? \"h-[90vh]\" : undefined,\n fullScreen ? \"h-screen w-screen\" : undefined,\n maxWidth && !fullScreen ? widthClasses[maxWidth] : undefined\n )}\n >\n <div\n className={cls(paperMixin,\n \"rounded-2xl\",\n \"z-60\",\n \"relative\",\n \"w-full\",\n \"overflow-hidden\",\n \"outline-none focus:outline-none\",\n fullHeight || fullScreen ? \"h-full\" : undefined,\n \"text-text-primary dark:text-text-primary-dark\",\n \"justify-center items-center\",\n fullScreen ? undefined : \"max-h-[90vh] shadow-lg\",\n \"ease-in-out duration-200 motion-reduce:transition-none\",\n scrollable && \"overflow-y-auto\",\n displayed && open ? \"opacity-100 scale-100\" : \"opacity-0 scale-[0.97]\",\n className\n )}>\n <PortalContainerProvider container={popupHost}>\n {children}\n </PortalContainerProvider>\n </div>\n\n {/* Sized to nothing so it changes no layout, but positioned and\n stacked above the paper so popups portaled here are not\n painted behind it. */}\n <div ref={setPopupHost} className={\"relative z-70 w-0 h-0\"}/>\n\n </DialogPrimitive.Content>\n </div>\n\n </DialogPrimitive.Portal>\n </DialogPrimitive.Root>\n );\n};\n"],"mappings":";;;;;;;;;;AAsDA,IAAM,eAAe;CACjB,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,OAAO;CACP,OAAO;CACP,OAAO;CACP,OAAO;CACP,OAAO;CACP,OAAO;CACP,MAAM;AACV;AAEA,IAAa,UAAU,EACI,MACA,cACA,UACA,WACA,oBACA,YAAY,MACZ,YACA,YACA,aAAa,MACb,WAAW,MACX,QAAQ,MACR,iBACA,kBACA,iBACA,sBACA,mBACA,oBAAoB,OACpB,cACA,sBAAsB,MACtB,iBACA,oBAAoB,sBACL;CACtC,MAAM,CAAC,WAAW,gBAAgB,SAAS,KAAK;CAMhD,MAAM,CAAC,WAAW,gBAAgB,SAAgC,IAAI;CAItE,MAAM,CAAC,WAAW,gBAAgB,SAAgC,IAAI;CACtE,2BAA2B,SAAS;CAKpC,MAAM,CAAC,WAAW,gBAAgB,SAAgC,IAAI;CACtE,MAAM,CAAC,SAAS,cAAc,SAAgC,IAAI;CAElE,MAAM,QAAQ,cAAc;EACxB,MAAM,QAAQ,IAAI;EAClB;EACA,gBAAgB;EAChB;EACA;CACJ,CAAC;CAGD,MAAM,mBAAmB,mBAAmB;CAG5C,MAAM,iBAAkB,mBAAmB,oBAAoB,KAAA;CAE/D,gBAAgB;EACZ,IAAI,CAAC,MAAM;GACP,MAAM,UAAU,iBAAiB;IAC7B,aAAa,KAAK;GACtB,GAAG,GAAG;GACN,aAAa,aAAa,OAAO;EACrC,OAAO;GACH,aAAa,IAAI;GACjB,aAAa,CACb;EACJ;CACJ,GAAG,CAAC,IAAI,CAAC;CAET,OACI,oBAAC,gBAAgB,MAAjB;EAAsB,MAAM,aAAa;EACZ;EACO;YAChC,oBAAC,gBAAgB,QAAjB;GAAwB,WAAW;aAE/B,qBAAC,OAAD;IAAK,KAAK;IACL,WAAW,IAAI,uDAAuD,kBAAkB;cAD7F,CAGI,oBAAC,gBAAgB,SAAjB;KACI,KAAK;KACL,WAAW,IAAI,6HACX,aAAa,OAAO,gBAAgB,aACpC,uCACJ;KACA,OAAO,EACH,eAAe,YAAY,SAAS,OACxC;IACH,CAAA,GAUD,qBAAC,gBAAgB,SAAjB;KACI,KAAK;KACY;KACjB,iBAAiB,MAAM;KACvB,kBAAkB,MAAM;KACxB,uBAAuB,MAAM;MACzB,uBAAuB,CAAC;MACxB,IAAI,EAAE,kBAAkB;MACxB,MAAM,SAAS,EAAE;MAEjB,IAAI,CAAC,qBAAqB,EADP,WAAW,aAAa,WAAW,UACf,EAAE,eAAe;KAC5D;KAGA,iBAAiB,MAAM,EAAE,eAAe;KACrB;KACnB,oBAAkB;KAClB,WAAW,IACP,8BAGA,iBACA,aAAa,CAAC,aAAa,YAAY,KAAA,GACvC,cAAc,CAAC,aAAa,aAAa,KAAA,GACzC,aAAa,sBAAsB,KAAA,GACnC,YAAY,CAAC,aAAa,aAAa,YAAY,KAAA,CACvD;eA1BJ,CA4BI,oBAAC,OAAD;MACI,WAAW,IAAI,YACX,eACA,QACA,YACA,UACA,mBACA,mCACA,cAAc,aAAa,WAAW,KAAA,GACtC,iDACA,+BACA,aAAa,KAAA,IAAY,0BACzB,0DACA,cAAc,mBACd,aAAa,OAAO,0BAA0B,0BAC9C,SACJ;gBACA,oBAAC,yBAAD;OAAyB,WAAW;OAC/B;MACoB,CAAA;KACxB,CAAA,GAKL,oBAAC,OAAD;MAAK,KAAK;MAAc,WAAW;KAAyB,CAAA,CAEvC;MACxB;;EAEe,CAAA;CACN,CAAA;AAE9B"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { defaultBorderMixin } from "../styles.es.js";
|
|
2
|
+
import { cls } from "../util/cls.es.js";
|
|
3
|
+
import "react";
|
|
4
|
+
import { jsx } from "react/jsx-runtime";
|
|
5
|
+
//#region src/components/DialogActions.tsx
|
|
6
|
+
function DialogActions({ children, position = "sticky", translucent = true, className }) {
|
|
7
|
+
return /* @__PURE__ */ jsx("div", {
|
|
8
|
+
className: cls(defaultBorderMixin, "pt-2 pb-4 px-4 border-t flex flex-row items-center justify-end shrink-0 bottom-0 right-0 left-0 text-right z-2 gap-2", position, "bg-surface-card/60", translucent ? "backdrop-blur-sm" : "", className),
|
|
9
|
+
children
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
//#endregion
|
|
13
|
+
export { DialogActions };
|
|
14
|
+
|
|
15
|
+
//# sourceMappingURL=DialogActions.es.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DialogActions.es.js","names":[],"sources":["../../src/components/DialogActions.tsx"],"sourcesContent":["import React from \"react\";\nimport { defaultBorderMixin } from \"../styles\";\nimport { cls } from \"../util\";\n\nexport function DialogActions({\n children,\n position = \"sticky\",\n translucent = true,\n className\n }: {\n children: React.ReactNode,\n position?: \"sticky\" | \"absolute\",\n translucent?: boolean,\n className?: string\n}) {\n\n return <div\n className={cls(\n defaultBorderMixin,\n // `shrink-0`: as a flex child at the foot of a column this bar must\n // keep its height instead of being squeezed by the content above it.\n \"pt-2 pb-4 px-4 border-t flex flex-row items-center justify-end shrink-0 bottom-0 right-0 left-0 text-right z-2 gap-2\",\n position,\n \"bg-surface-card/60\",\n translucent ? \"backdrop-blur-sm\" : \"\",\n className)}>\n {children}\n </div>;\n}\n"],"mappings":";;;;;AAIA,SAAgB,cAAc,EACI,UACA,WAAW,UACX,cAAc,MACd,aAM/B;CAEC,OAAO,oBAAC,OAAD;EACH,WAAW,IACP,oBAGA,wHACA,UACA,sBACA,cAAc,qBAAqB,IACnC,SAAS;EACZ;CACA,CAAA;AACT"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { cls } from "../util/cls.es.js";
|
|
2
|
+
import "react";
|
|
3
|
+
import { jsx } from "react/jsx-runtime";
|
|
4
|
+
//#region src/components/DialogContent.tsx
|
|
5
|
+
function DialogContent({ children, className, fullHeight, includeMargin = true }) {
|
|
6
|
+
if (fullHeight) return /* @__PURE__ */ jsx("div", {
|
|
7
|
+
className: "flex-grow flex flex-col h-full relative",
|
|
8
|
+
children
|
|
9
|
+
});
|
|
10
|
+
return /* @__PURE__ */ jsx("div", {
|
|
11
|
+
className: cls("flex-grow", { "my-8 mx-8": includeMargin }, className),
|
|
12
|
+
children
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
//#endregion
|
|
16
|
+
export { DialogContent };
|
|
17
|
+
|
|
18
|
+
//# sourceMappingURL=DialogContent.es.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DialogContent.es.js","names":[],"sources":["../../src/components/DialogContent.tsx"],"sourcesContent":["import React from \"react\";\nimport { cls } from \"../util\";\n\nexport function DialogContent({\n children,\n className,\n fullHeight,\n includeMargin = true\n }: {\n children: React.ReactNode,\n includeMargin?: boolean,\n fullHeight?: boolean,\n className?: string\n}) {\n\n if (fullHeight)\n return <div className={\"flex-grow flex flex-col h-full relative\"}>\n {children}\n </div>;\n\n return <div\n className={cls(\"flex-grow\",\n { \"my-8 mx-8\": includeMargin },\n className)}>\n {children}\n </div>;\n}\n"],"mappings":";;;;AAGA,SAAgB,cAAc,EACI,UACA,WACA,YACA,gBAAgB,QAM/C;CAEC,IAAI,YACA,OAAO,oBAAC,OAAD;EAAK,WAAW;EAClB;CACA,CAAA;CAET,OAAO,oBAAC,OAAD;EACH,WAAW,IAAI,aACX,EAAE,aAAa,cAAc,GAC7B,SAAS;EACZ;CACA,CAAA;AACT"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cls } from "../util/cls.es.js";
|
|
3
|
+
import { Typography } from "./Typography.es.js";
|
|
4
|
+
import "react";
|
|
5
|
+
import { jsx } from "react/jsx-runtime";
|
|
6
|
+
import * as DialogPrimitive from "@radix-ui/react-dialog";
|
|
7
|
+
import * as VisuallyHidden from "@radix-ui/react-visually-hidden";
|
|
8
|
+
//#region src/components/DialogTitle.tsx
|
|
9
|
+
function DialogTitle({ children, hidden, className, variant = "subtitle2", gutterBottom = true, includeMargin = true, ...props }) {
|
|
10
|
+
const title = /* @__PURE__ */ jsx(DialogPrimitive.Title, {
|
|
11
|
+
asChild: true,
|
|
12
|
+
children: /* @__PURE__ */ jsx(Typography, {
|
|
13
|
+
variant,
|
|
14
|
+
className: cls({ "mt-8 mx-8": includeMargin }, className),
|
|
15
|
+
gutterBottom,
|
|
16
|
+
...props,
|
|
17
|
+
children
|
|
18
|
+
})
|
|
19
|
+
});
|
|
20
|
+
if (hidden) return /* @__PURE__ */ jsx(VisuallyHidden.Root, { children: title });
|
|
21
|
+
return title;
|
|
22
|
+
}
|
|
23
|
+
//#endregion
|
|
24
|
+
export { DialogTitle };
|
|
25
|
+
|
|
26
|
+
//# sourceMappingURL=DialogTitle.es.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DialogTitle.es.js","names":[],"sources":["../../src/components/DialogTitle.tsx"],"sourcesContent":["\"use client\";\n\nimport React from \"react\";\nimport * as DialogPrimitive from \"@radix-ui/react-dialog\";\nimport * as VisuallyHidden from \"@radix-ui/react-visually-hidden\";\nimport { Typography, TypographyProps, TypographyVariant } from \"./Typography\";\nimport { cls } from \"../util\";\n\nexport type DialogContentProps = TypographyProps & {\n children: React.ReactNode,\n hidden?: boolean,\n className?: string,\n includeMargin?: boolean,\n variant?: TypographyVariant\n};\n\nexport function DialogTitle({\n children,\n hidden,\n className,\n variant = \"subtitle2\",\n gutterBottom = true,\n includeMargin = true,\n ...props\n }: DialogContentProps) {\n\n const title = <DialogPrimitive.Title asChild>\n <Typography variant={variant}\n className={cls({ \"mt-8 mx-8\": includeMargin }, className)}\n gutterBottom={gutterBottom}\n {...props}>\n {children}\n </Typography>\n </DialogPrimitive.Title>;\n\n if (hidden) {\n return <VisuallyHidden.Root>{title}</VisuallyHidden.Root>\n }\n\n return title;\n}\n"],"mappings":";;;;;;;;AAgBA,SAAgB,YAAY,EACI,UACA,QACA,WACA,UAAU,aACV,eAAe,MACf,gBAAgB,MAChB,GAAG,SACgB;CAE/C,MAAM,QAAQ,oBAAC,gBAAgB,OAAjB;EAAuB,SAAA;YACjC,oBAAC,YAAD;GAAqB;GACT,WAAW,IAAI,EAAE,aAAa,cAAc,GAAG,SAAS;GAC1C;GACd,GAAI;GACX;EACO,CAAA;CACO,CAAA;CAEvB,IAAI,QACA,OAAO,oBAAC,eAAe,MAAhB,EAAA,UAAsB,MAA2B,CAAA;CAG5D,OAAO;AACX"}
|