@galaxy-io/dls 2.8.1 → 2.10.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/README.md +2 -2
- package/dist/beacons/Beacon.d.ts +1 -1
- package/dist/beacons/Beacon.js +12 -28
- package/dist/brand/GalaxyAlchemyWordmark.d.ts +4 -1
- package/dist/brand/GalaxyAlchemyWordmark.js +7 -1
- package/dist/brand/GalaxyFilamentWordmark.d.ts +5 -2
- package/dist/brand/GalaxyFilamentWordmark.js +9 -2
- package/dist/brand/GalaxyLogomark.js +3 -0
- package/dist/brand/PoweredBy.d.ts +27 -0
- package/dist/brand/PoweredBy.js +96 -0
- package/dist/brand/internal/svg/GalaxyAlchemyWordmarkSvg.d.ts +1 -1
- package/dist/brand/internal/svg/GalaxyAlchemyWordmarkSvg.js +34 -8
- package/dist/brand/internal/svg/GalaxyFilamentWordmarkSvg.d.ts +1 -1
- package/dist/brand/internal/svg/GalaxyFilamentWordmarkSvg.js +2 -2
- package/dist/charts/Heatmap.js +25 -9
- package/dist/charts/Sparkline.d.ts +1 -1
- package/dist/charts/Sparkline.js +10 -26
- package/dist/charts/chartFormat.d.ts +1 -1
- package/dist/charts/chartFormat.js +2 -2
- package/dist/charts/heatmapGeometry.d.ts +14 -0
- package/dist/charts/heatmapGeometry.js +17 -1
- package/dist/chips/Chip.d.ts +2 -2
- package/dist/chips/Chip.js +15 -31
- package/dist/drawer/Drawer.d.ts +2 -2
- package/dist/drawer/Drawer.js +1 -1
- package/dist/editor/CodeEditor.d.ts +1 -1
- package/dist/editor/CodeEditor.js +2 -1
- package/dist/feedback/Alert.d.ts +36 -9
- package/dist/feedback/Alert.js +26 -8
- package/dist/feedback/ProgressBar.d.ts +4 -1
- package/dist/feedback/ProgressBar.js +17 -30
- package/dist/feedback/ProgressCircle.d.ts +1 -1
- package/dist/feedback/ProgressCircle.js +14 -30
- package/dist/hooks/useEndReached.d.ts +40 -0
- package/dist/hooks/useEndReached.js +69 -0
- package/dist/icons/Icon.d.ts +36 -8
- package/dist/icons/Icon.js +14 -7
- package/dist/inputs/CronInput.js +114 -78
- package/dist/inputs/DateInput.d.ts +1 -1
- package/dist/inputs/DateInput.js +10 -10
- package/dist/inputs/MultiSelectInput.d.ts +3 -1
- package/dist/inputs/MultiSelectInput.js +8 -3
- package/dist/inputs/SelectInput.d.ts +8 -1
- package/dist/inputs/SelectInput.js +52 -25
- package/dist/inputs/internal/selectAll.d.ts +11 -0
- package/dist/inputs/internal/selectAll.js +28 -0
- package/dist/json/JsonViewer.js +7 -6
- package/dist/layout/AppFrame.d.ts +20 -0
- package/dist/layout/AppFrame.js +71 -0
- package/dist/layout/EmptyLayout.d.ts +53 -0
- package/dist/layout/EmptyLayout.js +59 -0
- package/dist/layout/ErrorLayout.d.ts +42 -0
- package/dist/layout/ErrorLayout.js +60 -0
- package/dist/layout/PageLayout.d.ts +26 -0
- package/dist/layout/PageLayout.js +116 -0
- package/dist/layout/PendingLayout.d.ts +30 -0
- package/dist/layout/PendingLayout.js +57 -0
- package/dist/layout/internal/errorState.d.ts +14 -0
- package/dist/layout/internal/errorState.js +49 -0
- package/dist/layout/internal/region.d.ts +10 -0
- package/dist/layout/internal/region.js +24 -0
- package/dist/lists/DescriptionList.js +1 -0
- package/dist/menu/Menu.d.ts +2 -2
- package/dist/menu/Menu.js +6 -13
- package/dist/modal/Modal.d.ts +2 -2
- package/dist/modal/Modal.js +1 -1
- package/dist/navigation/CommandPalette.js +4 -11
- package/dist/navigation/SidebarNav.d.ts +5 -0
- package/dist/navigation/SidebarNav.js +41 -22
- package/dist/navigation/Topbar.js +4 -0
- package/dist/shapes/Circle.d.ts +1 -1
- package/dist/shapes/Circle.js +10 -26
- package/dist/shapes/Square.d.ts +1 -1
- package/dist/shapes/Square.js +10 -26
- package/dist/styles/internal/roleColor.d.ts +3 -0
- package/dist/styles/internal/roleColor.js +42 -0
- package/dist/styles/mixins.js +1 -1
- package/dist/styles.css +1 -1
- package/dist/table/InfiniteSpreadsheet.js +6 -6
- package/dist/table/InfiniteTable.d.ts +57 -18
- package/dist/table/InfiniteTable.js +5 -3
- package/dist/table/TableCore.d.ts +11 -2
- package/dist/table/TableCore.js +108 -87
- package/dist/table/VirtualizedInfiniteTable.d.ts +58 -19
- package/dist/table/VirtualizedInfiniteTable.js +9 -6
- package/dist/table/useInfiniteScrollSentinel.d.ts +13 -13
- package/dist/table/useInfiniteScrollSentinel.js +7 -51
- package/dist/table/useTableColumnLayout.d.ts +8 -2
- package/dist/table/useTableColumnLayout.js +36 -13
- package/dist/theme/tokens/dark.js +1 -1
- package/dist/theme/tokens/light.js +4 -3
- package/dist/theme/tokens/types.d.ts +7 -7
- package/dist/timeline/Timeline.d.ts +1 -1
- package/dist/timeline/Timeline.js +6 -24
- package/dist/tokens.css +1 -1
- package/dist/utils/format.d.ts +35 -10
- package/dist/utils/format.js +60 -12
- package/dist/utils/search.d.ts +15 -0
- package/dist/utils/search.js +24 -0
- package/dist/vite/index.d.ts +16 -2
- package/dist/vite/index.js +28 -19
- package/dist/vite/internal/themeBoot.d.ts +25 -0
- package/dist/vite/internal/themeBoot.js +44 -0
- package/dist/vite/internal/wywOptions.d.ts +46 -0
- package/dist/vite/internal/wywOptions.js +44 -0
- package/package.json +34 -1
|
@@ -22,13 +22,14 @@ import { rootStyle, useOwnField } from "./internal/ownField.js";
|
|
|
22
22
|
import { BareFieldContext, useIsBareField } from "./internal/bareField.js";
|
|
23
23
|
import "./internal/innerButton.js";
|
|
24
24
|
import { useDedupedOpen, useRestoreFocusAfterOutsidePress } from "./internal/useComboboxPopover.js";
|
|
25
|
+
import { isSearchMatch, normalizeSearchTerm } from "../utils/search.js";
|
|
25
26
|
/* empty css */
|
|
26
27
|
import { styled } from "@linaria/react";
|
|
27
28
|
import { forwardRef, useCallback, useEffect, useRef, useState } from "react";
|
|
28
29
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
29
30
|
import { cx } from "@linaria/core";
|
|
30
31
|
import { useMergeRefs } from "@floating-ui/react";
|
|
31
|
-
import { CaretDownIcon, CheckIcon, PlusIcon, XIcon } from "@phosphor-icons/react";
|
|
32
|
+
import { CaretDownIcon, CheckIcon, MinusIcon, PlusIcon, XIcon } from "@phosphor-icons/react";
|
|
32
33
|
import { defaultRangeExtractor, useVirtualizer } from "@tanstack/react-virtual";
|
|
33
34
|
//#region src/inputs/SelectInput.tsx
|
|
34
35
|
/**
|
|
@@ -63,7 +64,7 @@ var SelectInputSize = /* @__PURE__ */ function(SelectInputSize) {
|
|
|
63
64
|
SelectInputSize["X_LARGE"] = "X_LARGE";
|
|
64
65
|
return SelectInputSize;
|
|
65
66
|
}({});
|
|
66
|
-
/** Resting fill of the field (
|
|
67
|
+
/** Resting fill of the field (AGENTS.md "Variant": surface set; 1.x `InputVariant` unchanged). */
|
|
67
68
|
var SelectInputVariant = /* @__PURE__ */ function(SelectInputVariant) {
|
|
68
69
|
/** `background.base` fill. */
|
|
69
70
|
SelectInputVariant["BASE"] = "BASE";
|
|
@@ -227,7 +228,7 @@ var SELECT_INPUT_VARIANT_TO_CSS_MAP = {
|
|
|
227
228
|
var GHOST_CSS = "gx-SelectInput-GHOST_CSS-g1yocujd";
|
|
228
229
|
/**
|
|
229
230
|
* Hover on an editable field: `border.hovered` (stronger than `border.primary` in both themes,
|
|
230
|
-
*
|
|
231
|
+
* `tokens.test.ts` holds the order).
|
|
231
232
|
*/
|
|
232
233
|
var SELECT_INPUT_VARIANT_TO_HOVER_CSS_MAP = {
|
|
233
234
|
["BASE"]: "gx-SelectInput-BASE-b1uexxly",
|
|
@@ -451,22 +452,14 @@ var SELECT_INPUT_SIZE_TO_GROUP_LABEL_CSS_MAP = {
|
|
|
451
452
|
};
|
|
452
453
|
/** Space above and below the pinned section's separator (Menu's). */
|
|
453
454
|
var SEPARATOR_CSS = "gx-SelectInput-SEPARATOR_CSS-s9yly5q";
|
|
454
|
-
/** Lower-cased, trimmed term (the filter's comparison form; Menu's rule). */
|
|
455
|
-
function normalizeTerm(term) {
|
|
456
|
-
return term.trim().toLocaleLowerCase();
|
|
457
|
-
}
|
|
458
455
|
/** An option matches when the term is empty or its label (or description) contains it. */
|
|
459
|
-
|
|
460
|
-
if (term === "") return true;
|
|
461
|
-
if (option.label.toLocaleLowerCase().includes(term)) return true;
|
|
462
|
-
return option.description?.toLocaleLowerCase().includes(term) ?? false;
|
|
463
|
-
}
|
|
456
|
+
var matches = (term, option) => isSearchMatch(term, option.label, option.description);
|
|
464
457
|
/** A printable character that should drive typeahead or the search term. */
|
|
465
458
|
function isPrintableKey(event) {
|
|
466
459
|
return event.key.length === 1 && !event.ctrlKey && !event.metaKey && !event.altKey;
|
|
467
460
|
}
|
|
468
|
-
/** Builds the rows in order: pinned, separator, sections, Create, then the status row. */
|
|
469
|
-
function buildModel({ options, pinnedIds, term, isFiltering, selected, limitReason, createLabel, isLoading, listboxId }) {
|
|
461
|
+
/** Builds the rows in order: select-all, pinned, separator, sections, Create, then the status row. */
|
|
462
|
+
function buildModel({ options, pinnedIds, term, isFiltering, selected, limitReason, createLabel, selectAllLabel, isLoading, listboxId }) {
|
|
470
463
|
const indexById = /* @__PURE__ */ new Map();
|
|
471
464
|
options.forEach((option, index) => {
|
|
472
465
|
if (!indexById.has(option.id)) indexById.set(option.id, index);
|
|
@@ -525,7 +518,19 @@ function buildModel({ options, pinnedIds, term, isFiltering, selected, limitReas
|
|
|
525
518
|
section.rows.push(makeItem(option, index));
|
|
526
519
|
});
|
|
527
520
|
const restCount = sections.reduce((count, section) => count + section.rows.length, 0);
|
|
528
|
-
const
|
|
521
|
+
const targets = [...pinned, ...sections.flatMap((section) => section.rows)].filter((item) => !item.isDisabled);
|
|
522
|
+
const selectedTargetCount = targets.filter((item) => item.isSelected).length;
|
|
523
|
+
const selectAll = selectAllLabel === null || targets.length === 0 ? null : {
|
|
524
|
+
kind: "selectAll",
|
|
525
|
+
key: "select-all",
|
|
526
|
+
domId: `${listboxId}-select-all`,
|
|
527
|
+
navIndex: -1,
|
|
528
|
+
flatIndex: -1,
|
|
529
|
+
label: selectAllLabel,
|
|
530
|
+
targetIds: targets.map((item) => item.option.id),
|
|
531
|
+
state: selectedTargetCount === targets.length ? "checked" : selectedTargetCount > 0 ? "mixed" : "unchecked"
|
|
532
|
+
};
|
|
533
|
+
const hasSeparator = (pinned.length > 0 || selectAll !== null) && restCount > 0;
|
|
529
534
|
const create = createLabel === null ? null : {
|
|
530
535
|
kind: "create",
|
|
531
536
|
key: "create",
|
|
@@ -542,6 +547,7 @@ function buildModel({ options, pinnedIds, term, isFiltering, selected, limitReas
|
|
|
542
547
|
nav.push(item);
|
|
543
548
|
flat.push(item);
|
|
544
549
|
};
|
|
550
|
+
if (selectAll !== null) pushNav(selectAll);
|
|
545
551
|
for (const item of pinned) pushNav(item);
|
|
546
552
|
if (hasSeparator) flat.push({
|
|
547
553
|
kind: "separator",
|
|
@@ -558,6 +564,7 @@ function buildModel({ options, pinnedIds, term, isFiltering, selected, limitReas
|
|
|
558
564
|
key: status
|
|
559
565
|
});
|
|
560
566
|
return {
|
|
567
|
+
selectAll,
|
|
561
568
|
pinned,
|
|
562
569
|
hasSeparator,
|
|
563
570
|
sections,
|
|
@@ -568,9 +575,12 @@ function buildModel({ options, pinnedIds, term, isFiltering, selected, limitReas
|
|
|
568
575
|
optionCount: position
|
|
569
576
|
};
|
|
570
577
|
}
|
|
571
|
-
/**
|
|
578
|
+
/**
|
|
579
|
+
* The first enabled option or Create row, else the first row (or -1). Select-all is skipped, so
|
|
580
|
+
* Enter after typing a term picks the first match rather than every match.
|
|
581
|
+
*/
|
|
572
582
|
function firstEnabledIndex(nav) {
|
|
573
|
-
const index = nav.findIndex((item) => item.kind === "create" || !item.isDisabled);
|
|
583
|
+
const index = nav.findIndex((item) => item.kind === "create" || item.kind === "option" && !item.isDisabled);
|
|
574
584
|
return index === -1 ? nav.length > 0 ? 0 : -1 : index;
|
|
575
585
|
}
|
|
576
586
|
/** The first selected option row, or -1. */
|
|
@@ -588,7 +598,7 @@ function openIndex(nav, target) {
|
|
|
588
598
|
}
|
|
589
599
|
/** The field, the trailing parts, the panel and the engine, wired to the nearest Field. */
|
|
590
600
|
function SelectInputFrame({ rootProps, forwardedRef, isRoot }) {
|
|
591
|
-
const { options, selectedIds, isMultiple, onOptionSelect, onClear, renderSelection, getSelectionText, limitReason, onCreate, size = "MEDIUM", variant = "PRIMARY", isGhost = false, label: _label, labelTooltip: _labelTooltip, description: _description, placeholder = "Select an option…", error, isError = false, isDisabled = false, isReadOnly = false, isRequired = false, fillWidth = false, ariaLabel, isOpen, defaultIsOpen = false, onOpenChange, isSearchable = false, onSearch, isClearable = false, isLoading = false, emptyMessage = "No results", onEndReached, renderOption, pinnedIds, placement = Placement.BOTTOM_START, id, "aria-describedby": ariaDescribedBy, className, style, ...rest } = rootProps;
|
|
601
|
+
const { options, selectedIds, isMultiple, onOptionSelect, onClear, renderSelection, getSelectionText, limitReason, onCreate, selectAllLabel, onSelectAll, size = "MEDIUM", variant = "PRIMARY", isGhost = false, label: _label, labelTooltip: _labelTooltip, description: _description, placeholder = "Select an option…", error, isError = false, isDisabled = false, isReadOnly = false, isRequired = false, fillWidth = false, ariaLabel, isOpen, defaultIsOpen = false, onOpenChange, isSearchable = false, onSearch, isClearable = false, isLoading = false, emptyMessage = "No results", onEndReached, renderOption, pinnedIds, placement = Placement.BOTTOM_START, id, "aria-describedby": ariaDescribedBy, className, style, ...rest } = rootProps;
|
|
592
602
|
const isBare = useIsBareField();
|
|
593
603
|
const { field, controlId, describedBy, isInvalid, isRequired: isRequiredState, hasName } = useFieldWiring({
|
|
594
604
|
id,
|
|
@@ -641,7 +651,7 @@ function SelectInputFrame({ rootProps, forwardedRef, isRoot }) {
|
|
|
641
651
|
}
|
|
642
652
|
}, [unknownKey]);
|
|
643
653
|
const selectionText = selectedOptions.length > 0 ? getSelectionText(selectedOptions) : "";
|
|
644
|
-
const term = isSearchable ?
|
|
654
|
+
const term = isSearchable ? normalizeSearchTerm(rawTerm) : "";
|
|
645
655
|
const trimmedTerm = rawTerm.trim();
|
|
646
656
|
const isFiltering = isSearchable && onSearch === void 0;
|
|
647
657
|
const hasExactMatch = trimmedTerm !== "" && options.some((option) => option.label.toLocaleLowerCase() === trimmedTerm.toLocaleLowerCase());
|
|
@@ -653,6 +663,7 @@ function SelectInputFrame({ rootProps, forwardedRef, isRoot }) {
|
|
|
653
663
|
selected: selectedSet,
|
|
654
664
|
limitReason,
|
|
655
665
|
createLabel: onCreate !== void 0 && isSearchable && trimmedTerm !== "" && !hasExactMatch && limitReason === void 0 ? trimmedTerm : null,
|
|
666
|
+
selectAllLabel: isMultiple && isFilled(selectAllLabel) ? selectAllLabel : null,
|
|
656
667
|
isLoading,
|
|
657
668
|
listboxId
|
|
658
669
|
});
|
|
@@ -767,6 +778,10 @@ function SelectInputFrame({ rootProps, forwardedRef, isRoot }) {
|
|
|
767
778
|
setRawTerm("");
|
|
768
779
|
return;
|
|
769
780
|
}
|
|
781
|
+
if (item.kind === "selectAll") {
|
|
782
|
+
onSelectAll?.(item.targetIds, item.state !== "checked");
|
|
783
|
+
return;
|
|
784
|
+
}
|
|
770
785
|
if (item.isDisabled) return;
|
|
771
786
|
onOptionSelect(item.option.id);
|
|
772
787
|
if (!isMultiple) close();
|
|
@@ -1016,11 +1031,12 @@ function SelectInputFrame({ rootProps, forwardedRef, isRoot }) {
|
|
|
1016
1031
|
children: placeholderText
|
|
1017
1032
|
})
|
|
1018
1033
|
});
|
|
1019
|
-
/** One option or
|
|
1034
|
+
/** One option, Create or select-all row. */
|
|
1020
1035
|
const renderNavRow = (item, virtualStart) => {
|
|
1021
1036
|
const isActive = item.navIndex === activeIndex;
|
|
1022
1037
|
const isRowDisabled = item.kind === "option" && item.isDisabled;
|
|
1023
|
-
const isSelected = item.kind === "option" && item.isSelected;
|
|
1038
|
+
const isSelected = item.kind === "option" && item.isSelected || item.kind === "selectAll" && item.state === "checked";
|
|
1039
|
+
const isMixed = item.kind === "selectAll" && item.state === "mixed";
|
|
1024
1040
|
const virtualProps = virtualStart === void 0 ? {} : {
|
|
1025
1041
|
"data-index": item.flatIndex,
|
|
1026
1042
|
ref: virtualizer.measureElement,
|
|
@@ -1031,7 +1047,13 @@ function SelectInputFrame({ rootProps, forwardedRef, isRoot }) {
|
|
|
1031
1047
|
} : {}
|
|
1032
1048
|
};
|
|
1033
1049
|
let content;
|
|
1034
|
-
if (item.kind === "
|
|
1050
|
+
if (item.kind === "selectAll") content = /* @__PURE__ */ jsx(Content, { children: /* @__PURE__ */ jsx(Text, {
|
|
1051
|
+
as: "span",
|
|
1052
|
+
size: rowTextSize,
|
|
1053
|
+
lineClamp: 1,
|
|
1054
|
+
children: item.label
|
|
1055
|
+
}) });
|
|
1056
|
+
else if (item.kind === "create") content = /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(Icon, {
|
|
1035
1057
|
component: PlusIcon,
|
|
1036
1058
|
size: rowIconPx,
|
|
1037
1059
|
variant: IconVariant.SECONDARY,
|
|
@@ -1083,6 +1105,7 @@ function SelectInputFrame({ rootProps, forwardedRef, isRoot }) {
|
|
|
1083
1105
|
id: item.domId,
|
|
1084
1106
|
role: "option",
|
|
1085
1107
|
"aria-selected": isSelected,
|
|
1108
|
+
...item.kind === "selectAll" ? { "aria-checked": isMixed ? "mixed" : isSelected } : {},
|
|
1086
1109
|
...isRowDisabled ? { "aria-disabled": true } : {},
|
|
1087
1110
|
onPointerMove: () => {
|
|
1088
1111
|
if (item.navIndex !== activeIndex) setActive(item.navIndex, false);
|
|
@@ -1091,8 +1114,8 @@ function SelectInputFrame({ rootProps, forwardedRef, isRoot }) {
|
|
|
1091
1114
|
className: cx(SELECT_INPUT_SIZE_TO_ROW_CSS_MAP[size], isActive && ACTIVE_ROW_CSS, isActive && !isRowDisabled && ACTIVE_ROW_PRESS_CSS, isRowDisabled && ROW_DISABLED_CSS, virtualStart !== void 0 && VIRTUAL_ROW_CSS),
|
|
1092
1115
|
children: [content, /* @__PURE__ */ jsx(CheckSlot, {
|
|
1093
1116
|
className: SELECT_INPUT_SIZE_TO_CHECK_SLOT_CSS_MAP[size],
|
|
1094
|
-
children: isSelected && /* @__PURE__ */ jsx(Icon, {
|
|
1095
|
-
component: CheckIcon,
|
|
1117
|
+
children: (isSelected || isMixed) && /* @__PURE__ */ jsx(Icon, {
|
|
1118
|
+
component: isMixed ? MinusIcon : CheckIcon,
|
|
1096
1119
|
size: rowIconPx,
|
|
1097
1120
|
variant: isRowDisabled ? IconVariant.DISABLED : IconVariant.PRIMARY,
|
|
1098
1121
|
"aria-hidden": true
|
|
@@ -1168,7 +1191,8 @@ function SelectInputFrame({ rootProps, forwardedRef, isRoot }) {
|
|
|
1168
1191
|
};
|
|
1169
1192
|
switch (item.kind) {
|
|
1170
1193
|
case "option":
|
|
1171
|
-
case "create":
|
|
1194
|
+
case "create":
|
|
1195
|
+
case "selectAll": return renderNavRow(item, virtualItem.start);
|
|
1172
1196
|
case "heading": return renderHeading(item, virtual);
|
|
1173
1197
|
case "separator": return renderSeparator(item.key, virtual);
|
|
1174
1198
|
default: return renderStatus(item.kind, virtual);
|
|
@@ -1176,6 +1200,7 @@ function SelectInputFrame({ rootProps, forwardedRef, isRoot }) {
|
|
|
1176
1200
|
})
|
|
1177
1201
|
});
|
|
1178
1202
|
else listContent = /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
1203
|
+
model.selectAll !== null && renderNavRow(model.selectAll),
|
|
1179
1204
|
model.pinned.map((item) => renderNavRow(item)),
|
|
1180
1205
|
model.hasSeparator && renderSeparator("separator"),
|
|
1181
1206
|
model.sections.map((section) => section.heading === null ? section.rows.map((item) => renderNavRow(item)) : /* @__PURE__ */ jsxs(GroupRoot, {
|
|
@@ -1351,6 +1376,8 @@ var SelectInputInner = forwardRef(function SelectInput({ value, defaultValue = n
|
|
|
1351
1376
|
getSelectionText: (selected) => selected[0]?.label ?? "",
|
|
1352
1377
|
limitReason: void 0,
|
|
1353
1378
|
onCreate: void 0,
|
|
1379
|
+
selectAllLabel: void 0,
|
|
1380
|
+
onSelectAll: void 0,
|
|
1354
1381
|
...className === void 0 ? {} : { className },
|
|
1355
1382
|
...style === void 0 ? {} : { style }
|
|
1356
1383
|
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @internal The selection MultiSelectInput's select-all row produces. Private on purpose (an
|
|
3
|
+
* `internal/` module: built, never a package export).
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* The next selection after the select-all row is chosen. `shouldSelect` appends the covered ids
|
|
7
|
+
* that are not yet selected, in display order, after the current selection (selection order is
|
|
8
|
+
* kept) and stops at `limit` (`maxSelected`); otherwise it removes every covered id. Ids outside
|
|
9
|
+
* `ids` (options hidden by the search term) are never touched.
|
|
10
|
+
*/
|
|
11
|
+
export declare function applySelectAll<TId extends string>(current: readonly TId[], ids: readonly TId[], shouldSelect: boolean, limit?: number): TId[];
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
//#region src/inputs/internal/selectAll.ts
|
|
2
|
+
/**
|
|
3
|
+
* @internal The selection MultiSelectInput's select-all row produces. Private on purpose (an
|
|
4
|
+
* `internal/` module: built, never a package export).
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* The next selection after the select-all row is chosen. `shouldSelect` appends the covered ids
|
|
8
|
+
* that are not yet selected, in display order, after the current selection (selection order is
|
|
9
|
+
* kept) and stops at `limit` (`maxSelected`); otherwise it removes every covered id. Ids outside
|
|
10
|
+
* `ids` (options hidden by the search term) are never touched.
|
|
11
|
+
*/
|
|
12
|
+
function applySelectAll(current, ids, shouldSelect, limit) {
|
|
13
|
+
if (!shouldSelect) {
|
|
14
|
+
const covered = new Set(ids);
|
|
15
|
+
return current.filter((id) => !covered.has(id));
|
|
16
|
+
}
|
|
17
|
+
const next = [...current];
|
|
18
|
+
const selected = new Set(current);
|
|
19
|
+
for (const id of ids) {
|
|
20
|
+
if (limit !== void 0 && next.length >= limit) break;
|
|
21
|
+
if (selected.has(id)) continue;
|
|
22
|
+
selected.add(id);
|
|
23
|
+
next.push(id);
|
|
24
|
+
}
|
|
25
|
+
return next;
|
|
26
|
+
}
|
|
27
|
+
//#endregion
|
|
28
|
+
export { applySelectAll };
|
package/dist/json/JsonViewer.js
CHANGED
|
@@ -7,8 +7,9 @@ import { ButtonSize, ButtonVariant } from "../buttons/Button.js";
|
|
|
7
7
|
import CopyButton from "../buttons/CopyButton.js";
|
|
8
8
|
import Span, { SpanVariant } from "../text/Span.js";
|
|
9
9
|
import { InputSize, InputVariant } from "../inputs/Input.js";
|
|
10
|
-
import SearchInput from "../inputs/SearchInput.js";
|
|
11
10
|
import CollapseMotion from "../transform/internal/CollapseMotion.js";
|
|
11
|
+
import { isSearchMatch, normalizeSearchTerm } from "../utils/search.js";
|
|
12
|
+
import SearchInput from "../inputs/SearchInput.js";
|
|
12
13
|
import { TREE_ROW_CSS, TREE_ROW_LABEL_CSS, TREE_ROW_LEVEL_VARIABLE, TREE_ROW_SIZE_TO_CARET_PX_MAP, TREE_ROW_SIZE_TO_CSS_MAP, TREE_ROW_SIZE_TO_TEXT_SIZE_MAP, TreeRowCaretSlot, TreeRowSize } from "../tree/treeRow.js";
|
|
13
14
|
/* empty css */
|
|
14
15
|
import { styled } from "@linaria/react";
|
|
@@ -161,12 +162,12 @@ var searchTextOf = (node) => {
|
|
|
161
162
|
const display = displayOf(node.value, node.kind);
|
|
162
163
|
return node.kind === "STRING" ? display.slice(1, -1) : display;
|
|
163
164
|
};
|
|
164
|
-
/** Whether a node's key (not an index) or primitive value contains the
|
|
165
|
-
var isMatch = (node, term) => typeof node.key === "string"
|
|
165
|
+
/** Whether a node's key (not an index) or primitive value contains the term (the shared search rule). */
|
|
166
|
+
var isMatch = (node, term) => isSearchMatch(term, typeof node.key === "string" ? node.key : void 0, searchTextOf(node));
|
|
166
167
|
/** Splits `text` around case-insensitive occurrences of `term`, marking each. */
|
|
167
168
|
function highlight(text, term) {
|
|
168
169
|
if (term === "") return text;
|
|
169
|
-
const lower = text.
|
|
170
|
+
const lower = text.toLocaleLowerCase();
|
|
170
171
|
const parts = [];
|
|
171
172
|
let from = 0;
|
|
172
173
|
let at = lower.indexOf(term, from);
|
|
@@ -198,11 +199,11 @@ var JsonViewer = forwardRef(function JsonViewer(props, ref) {
|
|
|
198
199
|
const [expanded, setExpanded] = useState(() => new Set(allNodes.filter((node) => node.isContainer && node.level <= defaultExpandedDepth).map((node) => node.path)));
|
|
199
200
|
const [term, setTerm] = useState("");
|
|
200
201
|
const preSearchExpanded = useRef(null);
|
|
201
|
-
const lowerTerm = term
|
|
202
|
+
const lowerTerm = normalizeSearchTerm(term);
|
|
202
203
|
const matchCount = useMemo(() => lowerTerm === "" ? 0 : allNodes.filter((node) => isMatch(node, lowerTerm)).length, [allNodes, lowerTerm]);
|
|
203
204
|
/** Search drives expansion: the ancestors of every match open; clearing restores the set. */
|
|
204
205
|
const handleSearch = (next) => {
|
|
205
|
-
const lower = next
|
|
206
|
+
const lower = normalizeSearchTerm(next);
|
|
206
207
|
if (lower === "") {
|
|
207
208
|
if (preSearchExpanded.current !== null) setExpanded(preSearchExpanded.current);
|
|
208
209
|
preSearchExpanded.current = null;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type AriaAttributes, type CSSProperties, type ReactNode } from "react";
|
|
2
|
+
/** The sidebar column's width in px, the same in every Galaxy module (Filament, Alchemy). */
|
|
3
|
+
export declare const APP_FRAME_SIDEBAR_WIDTH = 240;
|
|
4
|
+
export interface AppFrameProps extends AriaAttributes {
|
|
5
|
+
/** The sidebar column's content: a `SidebarNav` with no `header`. It fills the column. */
|
|
6
|
+
sidebar: ReactNode;
|
|
7
|
+
/** The page, usually a `PageLayout`. The column is a flex column that never overflows the frame. */
|
|
8
|
+
children: ReactNode;
|
|
9
|
+
/** DOM id on the root `<div>`. */
|
|
10
|
+
id?: string;
|
|
11
|
+
/** Any `data-*` attribute, forwarded to the root `<div>`. */
|
|
12
|
+
[dataAttribute: `data-${string}`]: string | number | boolean | undefined;
|
|
13
|
+
/** Extra class on the root `<div>`. */
|
|
14
|
+
className?: string;
|
|
15
|
+
/** Inline style on the root `<div>`; only for setting CSS custom properties. */
|
|
16
|
+
style?: CSSProperties;
|
|
17
|
+
}
|
|
18
|
+
/** A Galaxy module's frame: the sidebar column, a hairline, and the page. */
|
|
19
|
+
declare const AppFrame: import("react").ForwardRefExoticComponent<AppFrameProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
20
|
+
export default AppFrame;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { Orientation } from "../theme/enums.js";
|
|
2
|
+
import Divider from "./Divider.js";
|
|
3
|
+
/* empty css */
|
|
4
|
+
import { styled } from "@linaria/react";
|
|
5
|
+
import { forwardRef } from "react";
|
|
6
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
+
//#region src/layout/AppFrame.tsx
|
|
8
|
+
/**
|
|
9
|
+
* The product frame of a Galaxy module: a fixed sidebar column, a vertical hairline, and a content
|
|
10
|
+
* column that holds the page. It fills the box the host gives it (`height: 100%`) on
|
|
11
|
+
* `background.primary`, the sidebar's own fill, and clips: the page owns the one scrolling region.
|
|
12
|
+
*
|
|
13
|
+
* It is one of two frame systems, and they never mix:
|
|
14
|
+
*
|
|
15
|
+
* - **AppFrame + PageLayout + a header-less SidebarNav** (Filament, Alchemy): a module inside the
|
|
16
|
+
* Galaxy host. The sidebar column is inset `8 8 0` so, with SidebarNav's own 8px, the first item
|
|
17
|
+
* sits 16px from the top and both sides and lines up with the host's Home button. PageLayout's
|
|
18
|
+
* 64px header carries the page title.
|
|
19
|
+
* - **Topbar + a SidebarNav `header`** (the app template, standalone apps): both rows are 48px, so
|
|
20
|
+
* their bottom hairlines meet. That frame is composed from `Flex`, `SidebarNav` and `Topbar`.
|
|
21
|
+
*
|
|
22
|
+
* A SidebarNav `header` inside AppFrame would sit 8px below a Topbar line it never meets, so pass
|
|
23
|
+
* it a `footer` and its items only, and `hasDividers={false}` when the footer should run on
|
|
24
|
+
* without a line.
|
|
25
|
+
*
|
|
26
|
+
* There is no width prop (layout props live on Box, Flex, FlexItem and Grid): the sidebar is
|
|
27
|
+
* `APP_FRAME_SIDEBAR_WIDTH`, 240px, the width every Galaxy module uses. Not a page (`PageLayout`),
|
|
28
|
+
* not resizable panes (`ResizablePanels`).
|
|
29
|
+
*/
|
|
30
|
+
/** The sidebar column's width in px, the same in every Galaxy module (Filament, Alchemy). */
|
|
31
|
+
var APP_FRAME_SIDEBAR_WIDTH = 240;
|
|
32
|
+
/** The frame: a row that fills the host's box on the sidebar's fill, clipped. */
|
|
33
|
+
var Root = /*#__PURE__*/ styled("div")({
|
|
34
|
+
name: "Root",
|
|
35
|
+
class: "gx-AppFrame-r1x0tg2p",
|
|
36
|
+
propsAsIs: false
|
|
37
|
+
});
|
|
38
|
+
/**
|
|
39
|
+
* The sidebar column: fixed width, inset `8 8 0`. With SidebarNav's own 8px the first item sits
|
|
40
|
+
* 16px from the top and both sides, on the Galaxy host's Home button (the host contract), and the
|
|
41
|
+
* footer runs to the bottom edge.
|
|
42
|
+
*/
|
|
43
|
+
var Sidebar = /*#__PURE__*/ styled("div")({
|
|
44
|
+
name: "Sidebar",
|
|
45
|
+
class: "gx-AppFrame-Sidebar-s1mhncm4",
|
|
46
|
+
propsAsIs: false
|
|
47
|
+
});
|
|
48
|
+
/** The page column: takes the rest of the row and may shrink to nothing, so the page clips, not the frame. */
|
|
49
|
+
var Content = /*#__PURE__*/ styled("div")({
|
|
50
|
+
name: "Content",
|
|
51
|
+
class: "gx-AppFrame-Content-c1eeh9po",
|
|
52
|
+
propsAsIs: false
|
|
53
|
+
});
|
|
54
|
+
/** A Galaxy module's frame: the sidebar column, a hairline, and the page. */
|
|
55
|
+
var AppFrame = forwardRef(function AppFrame({ sidebar, children, id, className, style, ...rest }, ref) {
|
|
56
|
+
return /* @__PURE__ */ jsxs(Root, {
|
|
57
|
+
...rest,
|
|
58
|
+
ref,
|
|
59
|
+
id,
|
|
60
|
+
className,
|
|
61
|
+
style,
|
|
62
|
+
children: [
|
|
63
|
+
/* @__PURE__ */ jsx(Sidebar, { children: sidebar }),
|
|
64
|
+
/* @__PURE__ */ jsx(Divider, { orientation: Orientation.VERTICAL }),
|
|
65
|
+
/* @__PURE__ */ jsx(Content, { children })
|
|
66
|
+
]
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
AppFrame.displayName = "AppFrame";
|
|
70
|
+
//#endregion
|
|
71
|
+
export { APP_FRAME_SIDEBAR_WIDTH, AppFrame as default };
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { Icon as PhosphorIcon } from "@phosphor-icons/react";
|
|
2
|
+
import { type AriaAttributes, type CSSProperties, type ReactNode } from "react";
|
|
3
|
+
/** Where the region sits (canonical rungs, subset); one-to-one with `EmptyStateSize`. */
|
|
4
|
+
export declare enum EmptyLayoutSize {
|
|
5
|
+
/** A small card or a side panel: EmptyState `SMALL`. */
|
|
6
|
+
SMALL = "SMALL",
|
|
7
|
+
/** A panel or a page section. The default: EmptyState `MEDIUM`. */
|
|
8
|
+
MEDIUM = "MEDIUM",
|
|
9
|
+
/** A whole page body: EmptyState `LARGE`. */
|
|
10
|
+
LARGE = "LARGE"
|
|
11
|
+
}
|
|
12
|
+
/** The mark above the header: an icon in a tile. */
|
|
13
|
+
interface EmptyLayoutIconProps {
|
|
14
|
+
/** Phosphor icon component (`TrayIcon`, not `<TrayIcon />`) shown in a small tile above the header. Not with `graphic`. */
|
|
15
|
+
icon?: PhosphorIcon;
|
|
16
|
+
/** A node of the app's own above the header, with no tile around it. Not with `icon`. */
|
|
17
|
+
graphic?: undefined;
|
|
18
|
+
}
|
|
19
|
+
/** An app illustration instead of the icon tile. */
|
|
20
|
+
interface EmptyLayoutGraphicProps {
|
|
21
|
+
/** Phosphor icon component (`TrayIcon`, not `<TrayIcon />`) shown in a small tile above the header. Not with `graphic`. */
|
|
22
|
+
icon?: undefined;
|
|
23
|
+
/** A node of the app's own above the header, with no tile around it. Not with `icon`. */
|
|
24
|
+
graphic: ReactNode;
|
|
25
|
+
}
|
|
26
|
+
interface EmptyLayoutBaseProps extends AriaAttributes {
|
|
27
|
+
/** One line that says what would be here ("No pipelines yet"). */
|
|
28
|
+
header: string;
|
|
29
|
+
/** Why it is empty or what to do next. Up to three short lines. */
|
|
30
|
+
description?: string;
|
|
31
|
+
/** The way out: usually one `PRIMARY` and one `SECONDARY` `Button`. */
|
|
32
|
+
actions?: ReactNode;
|
|
33
|
+
/**
|
|
34
|
+
* Where the region sits; picks the EmptyState size one-to-one.
|
|
35
|
+
* @default EmptyLayoutSize.MEDIUM
|
|
36
|
+
*/
|
|
37
|
+
size?: EmptyLayoutSize;
|
|
38
|
+
/** Pass `"status"` when the region empties because of something the user did (a search), so it is announced. */
|
|
39
|
+
role?: "status";
|
|
40
|
+
/** DOM id on the root `<div>`. */
|
|
41
|
+
id?: string;
|
|
42
|
+
/** Any `data-*` attribute, forwarded to the root `<div>`. */
|
|
43
|
+
[dataAttribute: `data-${string}`]: string | number | boolean | undefined;
|
|
44
|
+
/** Extra class on the root `<div>`. */
|
|
45
|
+
className?: string;
|
|
46
|
+
/** Inline style on the root `<div>`; only for setting CSS custom properties. */
|
|
47
|
+
style?: CSSProperties;
|
|
48
|
+
}
|
|
49
|
+
/** EmptyLayout props. Passing both `icon` and `graphic` is a type error, as on EmptyState. */
|
|
50
|
+
export type EmptyLayoutProps = EmptyLayoutBaseProps & (EmptyLayoutIconProps | EmptyLayoutGraphicProps);
|
|
51
|
+
/** A region with nothing to show: an EmptyState, filled and centred. */
|
|
52
|
+
declare const EmptyLayout: import("react").ForwardRefExoticComponent<EmptyLayoutProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
53
|
+
export default EmptyLayout;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import EmptyState, { EmptyStateSize } from "../feedback/EmptyState.js";
|
|
2
|
+
/* empty css */
|
|
3
|
+
import { styled } from "@linaria/react";
|
|
4
|
+
import { forwardRef } from "react";
|
|
5
|
+
import { jsx } from "react/jsx-runtime";
|
|
6
|
+
//#region src/layout/EmptyLayout.tsx
|
|
7
|
+
/**
|
|
8
|
+
* A region with nothing to show, filled and centred: `EmptyState` sized for where it sits, in a
|
|
9
|
+
* box that fills its parent (a page body, a panel, a table area) and centres it both ways. A
|
|
10
|
+
* preset over EmptyState, the `Modal → ConfirmDialog` pattern: EmptyState centres only across, and
|
|
11
|
+
* a non-layout component may not take a height, so this is the one place that fills.
|
|
12
|
+
*
|
|
13
|
+
* Siblings: `ErrorLayout` (the region failed), `PendingLayout` (the region is loading). Inside a
|
|
14
|
+
* table body, a chart or a card, use `EmptyState` itself: those already size the region.
|
|
15
|
+
*/
|
|
16
|
+
/** Where the region sits (canonical rungs, subset); one-to-one with `EmptyStateSize`. */
|
|
17
|
+
var EmptyLayoutSize = /* @__PURE__ */ function(EmptyLayoutSize) {
|
|
18
|
+
/** A small card or a side panel: EmptyState `SMALL`. */
|
|
19
|
+
EmptyLayoutSize["SMALL"] = "SMALL";
|
|
20
|
+
/** A panel or a page section. The default: EmptyState `MEDIUM`. */
|
|
21
|
+
EmptyLayoutSize["MEDIUM"] = "MEDIUM";
|
|
22
|
+
/** A whole page body: EmptyState `LARGE`. */
|
|
23
|
+
EmptyLayoutSize["LARGE"] = "LARGE";
|
|
24
|
+
return EmptyLayoutSize;
|
|
25
|
+
}({});
|
|
26
|
+
/** Fills the region and centres the empty state in it. */
|
|
27
|
+
var Root = /*#__PURE__*/ styled("div")({
|
|
28
|
+
name: "Root",
|
|
29
|
+
class: "gx-EmptyLayout-rxz77xy",
|
|
30
|
+
propsAsIs: false
|
|
31
|
+
});
|
|
32
|
+
/** One-to-one: the layout's size is the empty state's. */
|
|
33
|
+
var EMPTY_LAYOUT_SIZE_TO_EMPTY_STATE_SIZE_MAP = {
|
|
34
|
+
["SMALL"]: EmptyStateSize.SMALL,
|
|
35
|
+
["MEDIUM"]: EmptyStateSize.MEDIUM,
|
|
36
|
+
["LARGE"]: EmptyStateSize.LARGE
|
|
37
|
+
};
|
|
38
|
+
/** A region with nothing to show: an EmptyState, filled and centred. */
|
|
39
|
+
var EmptyLayout = forwardRef(function EmptyLayout({ header, description, actions, icon, graphic, size = "MEDIUM", role, id, className, style, ...rest }, ref) {
|
|
40
|
+
const mark = graphic === void 0 ? icon === void 0 ? {} : { icon } : { graphic };
|
|
41
|
+
return /* @__PURE__ */ jsx(Root, {
|
|
42
|
+
...rest,
|
|
43
|
+
ref,
|
|
44
|
+
id,
|
|
45
|
+
className,
|
|
46
|
+
style,
|
|
47
|
+
children: /* @__PURE__ */ jsx(EmptyState, {
|
|
48
|
+
...mark,
|
|
49
|
+
size: EMPTY_LAYOUT_SIZE_TO_EMPTY_STATE_SIZE_MAP[size],
|
|
50
|
+
header,
|
|
51
|
+
...description === void 0 ? {} : { description },
|
|
52
|
+
...actions === void 0 ? {} : { actions },
|
|
53
|
+
...role === void 0 ? {} : { role }
|
|
54
|
+
})
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
EmptyLayout.displayName = "EmptyLayout";
|
|
58
|
+
//#endregion
|
|
59
|
+
export { EmptyLayoutSize, EmptyLayout as default };
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { Icon as PhosphorIcon } from "@phosphor-icons/react";
|
|
2
|
+
import { type AriaAttributes, type CSSProperties, type ReactNode } from "react";
|
|
3
|
+
/** Where the region sits (canonical rungs, subset); one-to-one with `EmptyStateSize`. */
|
|
4
|
+
export declare enum ErrorLayoutSize {
|
|
5
|
+
/** A small card or a side panel: EmptyState `SMALL`, caption detail. */
|
|
6
|
+
SMALL = "SMALL",
|
|
7
|
+
/** A panel or a page section. The default: EmptyState `MEDIUM`, caption detail. */
|
|
8
|
+
MEDIUM = "MEDIUM",
|
|
9
|
+
/** A whole page body: EmptyState `LARGE`, `body_sm` detail. */
|
|
10
|
+
LARGE = "LARGE"
|
|
11
|
+
}
|
|
12
|
+
export interface ErrorLayoutProps extends AriaAttributes {
|
|
13
|
+
/** One line that says what failed ("Couldn't load pipelines"). */
|
|
14
|
+
header: string;
|
|
15
|
+
/** What the reader can do about it, in plain words. */
|
|
16
|
+
description?: string;
|
|
17
|
+
/** The raw error message, under the state in mono, selectable. For a message worth copying, not for prose. */
|
|
18
|
+
detail?: string;
|
|
19
|
+
/** The way out: a "Try again" `Button`, and a link to help when there is one. */
|
|
20
|
+
actions?: ReactNode;
|
|
21
|
+
/**
|
|
22
|
+
* Replaces the icon in the tile. Pass the component (`PlugsIcon`), not an element.
|
|
23
|
+
* @default WarningCircleIcon
|
|
24
|
+
*/
|
|
25
|
+
icon?: PhosphorIcon;
|
|
26
|
+
/**
|
|
27
|
+
* Where the region sits; picks the EmptyState size one-to-one.
|
|
28
|
+
* @default ErrorLayoutSize.MEDIUM
|
|
29
|
+
*/
|
|
30
|
+
size?: ErrorLayoutSize;
|
|
31
|
+
/** DOM id on the root `<div>`. */
|
|
32
|
+
id?: string;
|
|
33
|
+
/** Any `data-*` attribute, forwarded to the root `<div>`. */
|
|
34
|
+
[dataAttribute: `data-${string}`]: string | number | boolean | undefined;
|
|
35
|
+
/** Extra class on the root `<div>`. */
|
|
36
|
+
className?: string;
|
|
37
|
+
/** Inline style on the root `<div>`; only for setting CSS custom properties. */
|
|
38
|
+
style?: CSSProperties;
|
|
39
|
+
}
|
|
40
|
+
/** A region that failed: an error EmptyState with an optional detail, filled, centred and announced. */
|
|
41
|
+
declare const ErrorLayout: import("react").ForwardRefExoticComponent<ErrorLayoutProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
42
|
+
export default ErrorLayout;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { EmptyStateSize } from "../feedback/EmptyState.js";
|
|
2
|
+
import { ErrorStateContent } from "./internal/errorState.js";
|
|
3
|
+
/* empty css */
|
|
4
|
+
import { styled } from "@linaria/react";
|
|
5
|
+
import { forwardRef } from "react";
|
|
6
|
+
import { jsx } from "react/jsx-runtime";
|
|
7
|
+
//#region src/layout/ErrorLayout.tsx
|
|
8
|
+
/**
|
|
9
|
+
* A region that failed to load, filled and centred: an `ERROR` EmptyState (a `WarningCircleIcon`
|
|
10
|
+
* tile by default) with the way out in `actions`, and an optional `detail` (the raw message, mono
|
|
11
|
+
* and selectable, so it can be copied into a bug report). The stack is `role="alert"`, so a region
|
|
12
|
+
* that fails after it was on screen is announced. A preset over EmptyState, the
|
|
13
|
+
* `Modal → ConfirmDialog` pattern; an empty table body shows the same error state.
|
|
14
|
+
*
|
|
15
|
+
* Siblings: `EmptyLayout` (nothing to show), `PendingLayout` (still loading). A problem with the
|
|
16
|
+
* content on screen, which stays readable, is an `Alert`.
|
|
17
|
+
*/
|
|
18
|
+
/** Where the region sits (canonical rungs, subset); one-to-one with `EmptyStateSize`. */
|
|
19
|
+
var ErrorLayoutSize = /* @__PURE__ */ function(ErrorLayoutSize) {
|
|
20
|
+
/** A small card or a side panel: EmptyState `SMALL`, caption detail. */
|
|
21
|
+
ErrorLayoutSize["SMALL"] = "SMALL";
|
|
22
|
+
/** A panel or a page section. The default: EmptyState `MEDIUM`, caption detail. */
|
|
23
|
+
ErrorLayoutSize["MEDIUM"] = "MEDIUM";
|
|
24
|
+
/** A whole page body: EmptyState `LARGE`, `body_sm` detail. */
|
|
25
|
+
ErrorLayoutSize["LARGE"] = "LARGE";
|
|
26
|
+
return ErrorLayoutSize;
|
|
27
|
+
}({});
|
|
28
|
+
/** Fills the region and centres the error state in it. */
|
|
29
|
+
var Root = /*#__PURE__*/ styled("div")({
|
|
30
|
+
name: "Root",
|
|
31
|
+
class: "gx-ErrorLayout-r17y69m",
|
|
32
|
+
propsAsIs: false
|
|
33
|
+
});
|
|
34
|
+
/** One-to-one: the layout's size is the empty state's. */
|
|
35
|
+
var ERROR_LAYOUT_SIZE_TO_EMPTY_STATE_SIZE_MAP = {
|
|
36
|
+
["SMALL"]: EmptyStateSize.SMALL,
|
|
37
|
+
["MEDIUM"]: EmptyStateSize.MEDIUM,
|
|
38
|
+
["LARGE"]: EmptyStateSize.LARGE
|
|
39
|
+
};
|
|
40
|
+
/** A region that failed: an error EmptyState with an optional detail, filled, centred and announced. */
|
|
41
|
+
var ErrorLayout = forwardRef(function ErrorLayout({ header, description, detail, actions, icon, size = "MEDIUM", id, className, style, ...rest }, ref) {
|
|
42
|
+
return /* @__PURE__ */ jsx(Root, {
|
|
43
|
+
...rest,
|
|
44
|
+
ref,
|
|
45
|
+
id,
|
|
46
|
+
className,
|
|
47
|
+
style,
|
|
48
|
+
children: /* @__PURE__ */ jsx(ErrorStateContent, {
|
|
49
|
+
size: ERROR_LAYOUT_SIZE_TO_EMPTY_STATE_SIZE_MAP[size],
|
|
50
|
+
icon,
|
|
51
|
+
header,
|
|
52
|
+
description,
|
|
53
|
+
detail,
|
|
54
|
+
actions
|
|
55
|
+
})
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
ErrorLayout.displayName = "ErrorLayout";
|
|
59
|
+
//#endregion
|
|
60
|
+
export { ErrorLayoutSize, ErrorLayout as default };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type AriaAttributes, type CSSProperties, type ReactNode } from "react";
|
|
2
|
+
export interface PageLayoutProps extends AriaAttributes {
|
|
3
|
+
/** The page title. A string renders as an `h1` (`HEADING_MD`, medium, one line); a node renders as is. It names the `<main>` landmark. */
|
|
4
|
+
header: ReactNode;
|
|
5
|
+
/** The page's actions, at the end of the header row: one `PRIMARY` button at most, the rest `SECONDARY` or `TERTIARY`. */
|
|
6
|
+
actions?: ReactNode;
|
|
7
|
+
/** A strip under the header's hairline, above the tabs: an `Alert isBanner`. It stays put while the body scrolls. */
|
|
8
|
+
banner?: ReactNode;
|
|
9
|
+
/** The page's sibling views: a `Tabs` row, under the banner. */
|
|
10
|
+
tabs?: ReactNode;
|
|
11
|
+
/** A padded row of filters, a search or view switches, under the tabs, with a hairline under it. */
|
|
12
|
+
toolbar?: ReactNode;
|
|
13
|
+
/** The page body, in a `<main>` on `background.base` that never scrolls: wrap scrolling content in a `ScrollArea`. */
|
|
14
|
+
children: ReactNode;
|
|
15
|
+
/** DOM id on the root `<div>`. */
|
|
16
|
+
id?: string;
|
|
17
|
+
/** Any `data-*` attribute, forwarded to the root `<div>`. */
|
|
18
|
+
[dataAttribute: `data-${string}`]: string | number | boolean | undefined;
|
|
19
|
+
/** Extra class on the root `<div>`. */
|
|
20
|
+
className?: string;
|
|
21
|
+
/** Inline style on the root `<div>`; only for setting CSS custom properties. */
|
|
22
|
+
style?: CSSProperties;
|
|
23
|
+
}
|
|
24
|
+
/** One page of a Galaxy module: header, hairline, banner, tabs, toolbar, then the body. */
|
|
25
|
+
declare const PageLayout: import("react").ForwardRefExoticComponent<PageLayoutProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
26
|
+
export default PageLayout;
|