@galaxy-io/dls 2.9.0 → 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.
Files changed (101) hide show
  1. package/dist/beacons/Beacon.d.ts +1 -1
  2. package/dist/beacons/Beacon.js +12 -28
  3. package/dist/brand/GalaxyAlchemyWordmark.d.ts +4 -1
  4. package/dist/brand/GalaxyAlchemyWordmark.js +7 -1
  5. package/dist/brand/GalaxyFilamentWordmark.d.ts +5 -2
  6. package/dist/brand/GalaxyFilamentWordmark.js +9 -2
  7. package/dist/brand/GalaxyLogomark.js +3 -0
  8. package/dist/brand/PoweredBy.d.ts +27 -0
  9. package/dist/brand/PoweredBy.js +96 -0
  10. package/dist/brand/internal/svg/GalaxyFilamentWordmarkSvg.d.ts +1 -1
  11. package/dist/brand/internal/svg/GalaxyFilamentWordmarkSvg.js +2 -2
  12. package/dist/charts/Heatmap.js +25 -9
  13. package/dist/charts/Sparkline.d.ts +1 -1
  14. package/dist/charts/Sparkline.js +10 -26
  15. package/dist/charts/chartFormat.d.ts +1 -1
  16. package/dist/charts/chartFormat.js +2 -2
  17. package/dist/charts/heatmapGeometry.d.ts +14 -0
  18. package/dist/charts/heatmapGeometry.js +17 -1
  19. package/dist/chips/Chip.d.ts +2 -2
  20. package/dist/chips/Chip.js +15 -31
  21. package/dist/drawer/Drawer.d.ts +2 -2
  22. package/dist/drawer/Drawer.js +1 -1
  23. package/dist/editor/CodeEditor.d.ts +1 -1
  24. package/dist/editor/CodeEditor.js +2 -1
  25. package/dist/feedback/Alert.d.ts +36 -9
  26. package/dist/feedback/Alert.js +26 -8
  27. package/dist/feedback/ProgressBar.d.ts +4 -1
  28. package/dist/feedback/ProgressBar.js +17 -30
  29. package/dist/feedback/ProgressCircle.d.ts +1 -1
  30. package/dist/feedback/ProgressCircle.js +14 -30
  31. package/dist/hooks/useEndReached.d.ts +40 -0
  32. package/dist/hooks/useEndReached.js +69 -0
  33. package/dist/icons/Icon.d.ts +36 -8
  34. package/dist/icons/Icon.js +14 -7
  35. package/dist/inputs/CronInput.js +114 -78
  36. package/dist/inputs/DateInput.d.ts +1 -1
  37. package/dist/inputs/DateInput.js +10 -10
  38. package/dist/inputs/MultiSelectInput.d.ts +3 -1
  39. package/dist/inputs/MultiSelectInput.js +8 -3
  40. package/dist/inputs/SelectInput.d.ts +8 -1
  41. package/dist/inputs/SelectInput.js +52 -25
  42. package/dist/inputs/internal/selectAll.d.ts +11 -0
  43. package/dist/inputs/internal/selectAll.js +28 -0
  44. package/dist/json/JsonViewer.js +7 -6
  45. package/dist/layout/AppFrame.d.ts +20 -0
  46. package/dist/layout/AppFrame.js +71 -0
  47. package/dist/layout/EmptyLayout.d.ts +53 -0
  48. package/dist/layout/EmptyLayout.js +59 -0
  49. package/dist/layout/ErrorLayout.d.ts +42 -0
  50. package/dist/layout/ErrorLayout.js +60 -0
  51. package/dist/layout/PageLayout.d.ts +26 -0
  52. package/dist/layout/PageLayout.js +116 -0
  53. package/dist/layout/PendingLayout.d.ts +30 -0
  54. package/dist/layout/PendingLayout.js +57 -0
  55. package/dist/layout/internal/errorState.d.ts +14 -0
  56. package/dist/layout/internal/errorState.js +49 -0
  57. package/dist/layout/internal/region.d.ts +10 -0
  58. package/dist/layout/internal/region.js +24 -0
  59. package/dist/lists/DescriptionList.js +1 -0
  60. package/dist/menu/Menu.d.ts +2 -2
  61. package/dist/menu/Menu.js +6 -13
  62. package/dist/modal/Modal.d.ts +2 -2
  63. package/dist/modal/Modal.js +1 -1
  64. package/dist/navigation/CommandPalette.js +4 -11
  65. package/dist/navigation/SidebarNav.d.ts +5 -0
  66. package/dist/navigation/SidebarNav.js +41 -22
  67. package/dist/navigation/Topbar.js +4 -0
  68. package/dist/shapes/Circle.d.ts +1 -1
  69. package/dist/shapes/Circle.js +10 -26
  70. package/dist/shapes/Square.d.ts +1 -1
  71. package/dist/shapes/Square.js +10 -26
  72. package/dist/styles/internal/roleColor.d.ts +3 -0
  73. package/dist/styles/internal/roleColor.js +42 -0
  74. package/dist/styles/mixins.js +1 -1
  75. package/dist/styles.css +1 -1
  76. package/dist/table/InfiniteSpreadsheet.js +6 -6
  77. package/dist/table/InfiniteTable.d.ts +52 -20
  78. package/dist/table/InfiniteTable.js +3 -2
  79. package/dist/table/TableCore.d.ts +7 -1
  80. package/dist/table/TableCore.js +93 -83
  81. package/dist/table/VirtualizedInfiniteTable.d.ts +53 -21
  82. package/dist/table/VirtualizedInfiniteTable.js +7 -5
  83. package/dist/table/useInfiniteScrollSentinel.d.ts +13 -13
  84. package/dist/table/useInfiniteScrollSentinel.js +7 -51
  85. package/dist/theme/tokens/dark.js +1 -1
  86. package/dist/theme/tokens/light.js +4 -3
  87. package/dist/theme/tokens/types.d.ts +7 -7
  88. package/dist/timeline/Timeline.d.ts +1 -1
  89. package/dist/timeline/Timeline.js +6 -24
  90. package/dist/tokens.css +1 -1
  91. package/dist/utils/format.d.ts +35 -10
  92. package/dist/utils/format.js +60 -12
  93. package/dist/utils/search.d.ts +15 -0
  94. package/dist/utils/search.js +24 -0
  95. package/dist/vite/index.d.ts +16 -2
  96. package/dist/vite/index.js +28 -19
  97. package/dist/vite/internal/themeBoot.d.ts +25 -0
  98. package/dist/vite/internal/themeBoot.js +44 -0
  99. package/dist/vite/internal/wywOptions.d.ts +46 -0
  100. package/dist/vite/internal/wywOptions.js +44 -0
  101. package/package.json +34 -1
@@ -0,0 +1,116 @@
1
+ import Text, { TextSize, TextWeight } from "../text/Text.js";
2
+ import Divider from "./Divider.js";
3
+ import { hasContent } from "../inputs/internal/predicates.js";
4
+ /* empty css */
5
+ import { styled } from "@linaria/react";
6
+ import { forwardRef, useId } from "react";
7
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
8
+ //#region src/layout/PageLayout.tsx
9
+ /**
10
+ * The chrome of one page in a Galaxy module: a 64px header with the page title and its actions, a
11
+ * hairline, then the optional `banner`, `tabs` and `toolbar` rows, then the page body in a `<main>`.
12
+ * It fills the column it is given (AppFrame's page column) and never scrolls; the body is
13
+ * `background.base` with `overflow: hidden`, and the page opts into scrolling with a `ScrollArea`
14
+ * (the reset hides native scrollbars, so a bare `overflow: auto` would scroll with no bar).
15
+ *
16
+ * `header` follows Modal, Drawer and Widget: a string renders as an `h1` (`HEADING_MD`, medium, one
17
+ * line) and a node renders as is (a back button and a breadcrumb, a title with a status `Chip`).
18
+ * Either way it names the `<main>` landmark. The header row is 32px tall, the `MEDIUM` control
19
+ * rung, sitting on a 16px inset at the bottom of the 64px band, so a title and a `SMALL` or
20
+ * `MEDIUM` button share a center.
21
+ *
22
+ * `banner` sits under the header's hairline and above the tabs, as in Modal and Drawer, for an
23
+ * `Alert isBanner` that stays put while the body scrolls. `toolbar` is a padded row (filters, a
24
+ * search, view switches) with its own hairline under it.
25
+ *
26
+ * It belongs to the module frame: `AppFrame` + PageLayout + a header-less `SidebarNav`. A standalone
27
+ * app's `Topbar` is app chrome (breadcrumbs, search), not the page title, and the two frames are
28
+ * never mixed. Not a card (`Widget`), not an overlay (`Modal`, `Drawer`).
29
+ */
30
+ /** The page: a column that fills the frame's page column and never overflows it. */
31
+ var Root = /*#__PURE__*/ styled("div")({
32
+ name: "Root",
33
+ class: "gx-PageLayout-rqlvk19",
34
+ propsAsIs: false
35
+ });
36
+ /** Everything above the body: never shrinks, so the body gives up the height. */
37
+ var Head = /*#__PURE__*/ styled("div")({
38
+ name: "Head",
39
+ class: "gx-PageLayout-Head-h1n8da9l",
40
+ propsAsIs: false
41
+ });
42
+ /**
43
+ * The 64px header band (48 + 16): the title row sits on a 16px inset at its bottom, 16px in from
44
+ * both sides, so the title lines up with the body's 16px padding below.
45
+ */
46
+ var HeaderBand = /*#__PURE__*/ styled("div")({
47
+ name: "HeaderBand",
48
+ class: "gx-PageLayout-HeaderBand-homwm0",
49
+ propsAsIs: false
50
+ });
51
+ /** The title row: the `MEDIUM` control rung tall, title at the start, actions at the end. */
52
+ var HeaderRow = /*#__PURE__*/ styled("div")({
53
+ name: "HeaderRow",
54
+ class: "gx-PageLayout-HeaderRow-h1g2txxb",
55
+ propsAsIs: false
56
+ });
57
+ /** The title (string or node): takes the free width and truncates before the actions move. */
58
+ var Title = /*#__PURE__*/ styled("div")({
59
+ name: "Title",
60
+ class: "gx-PageLayout-Title-tieplva",
61
+ propsAsIs: false
62
+ });
63
+ /** The actions: never shrink, so the title gives up the width first. */
64
+ var Actions = /*#__PURE__*/ styled("div")({
65
+ name: "Actions",
66
+ class: "gx-PageLayout-Actions-a1g2osxc",
67
+ propsAsIs: false
68
+ });
69
+ /** The toolbar row: 8px over and under, 16px in from the sides, controls 8px apart. */
70
+ var Toolbar = /*#__PURE__*/ styled("div")({
71
+ name: "Toolbar",
72
+ class: "gx-PageLayout-Toolbar-t1jq83mv",
73
+ propsAsIs: false
74
+ });
75
+ /**
76
+ * The body: the canvas fill, and clipped. It takes the rest of the height and may shrink to
77
+ * nothing, so a `ScrollArea` inside it gets a bounded height to scroll in.
78
+ */
79
+ var Main = /*#__PURE__*/ styled("main")({
80
+ name: "Main",
81
+ class: "gx-PageLayout-Main-m15sg9c6",
82
+ propsAsIs: false
83
+ });
84
+ /** One page of a Galaxy module: header, hairline, banner, tabs, toolbar, then the body. */
85
+ var PageLayout = forwardRef(function PageLayout({ header, actions, banner, tabs, toolbar, children, id, className, style, ...rest }, ref) {
86
+ const titleId = useId();
87
+ return /* @__PURE__ */ jsxs(Root, {
88
+ ...rest,
89
+ ref,
90
+ id,
91
+ className,
92
+ style,
93
+ children: [/* @__PURE__ */ jsxs(Head, { children: [
94
+ /* @__PURE__ */ jsx(HeaderBand, { children: /* @__PURE__ */ jsxs(HeaderRow, { children: [/* @__PURE__ */ jsx(Title, {
95
+ id: titleId,
96
+ children: typeof header === "string" ? /* @__PURE__ */ jsx(Text, {
97
+ as: "h1",
98
+ size: TextSize.HEADING_MD,
99
+ weight: TextWeight.MEDIUM,
100
+ lineClamp: 1,
101
+ children: header
102
+ }) : header
103
+ }), hasContent(actions) && /* @__PURE__ */ jsx(Actions, { children: actions })] }) }),
104
+ /* @__PURE__ */ jsx(Divider, {}),
105
+ hasContent(banner) && banner,
106
+ hasContent(tabs) && tabs,
107
+ hasContent(toolbar) && /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(Toolbar, { children: toolbar }), /* @__PURE__ */ jsx(Divider, {})] })
108
+ ] }), /* @__PURE__ */ jsx(Main, {
109
+ "aria-labelledby": titleId,
110
+ children
111
+ })]
112
+ });
113
+ });
114
+ PageLayout.displayName = "PageLayout";
115
+ //#endregion
116
+ export { PageLayout as default };
@@ -0,0 +1,30 @@
1
+ import { type AriaAttributes, type CSSProperties } from "react";
2
+ /** Where the region sits (canonical rungs, subset); picks the Spinner rung. */
3
+ export declare enum PendingLayoutSize {
4
+ /** A small card or a side panel: Spinner `SMALL`, a 12px ring and a `body_sm` message. */
5
+ SMALL = "SMALL",
6
+ /** A panel or a page section. The default: Spinner `MEDIUM`, 14px and `body_md`. */
7
+ MEDIUM = "MEDIUM",
8
+ /** A whole page body: Spinner `X_LARGE`, 16px and `body_lg`. */
9
+ LARGE = "LARGE"
10
+ }
11
+ export interface PendingLayoutProps extends AriaAttributes {
12
+ /** Visible text beside the ring ("Loading pipelines…"); also what is announced. Without it, "Loading" is announced. */
13
+ message?: string;
14
+ /**
15
+ * Where the region sits; picks the Spinner rung.
16
+ * @default PendingLayoutSize.MEDIUM
17
+ */
18
+ size?: PendingLayoutSize;
19
+ /** DOM id on the root `<div>`. */
20
+ id?: string;
21
+ /** Any `data-*` attribute, forwarded to the root `<div>`. */
22
+ [dataAttribute: `data-${string}`]: string | number | boolean | undefined;
23
+ /** Extra class on the root `<div>`. */
24
+ className?: string;
25
+ /** Inline style on the root `<div>`; only for setting CSS custom properties. */
26
+ style?: CSSProperties;
27
+ }
28
+ /** A region that is still loading: a Spinner and its message, filled and centred. */
29
+ declare const PendingLayout: import("react").ForwardRefExoticComponent<PendingLayoutProps & import("react").RefAttributes<HTMLDivElement>>;
30
+ export default PendingLayout;
@@ -0,0 +1,57 @@
1
+ import Spinner, { SpinnerSize } from "../feedback/Spinner.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/PendingLayout.tsx
7
+ /**
8
+ * A region that is still loading, filled and centred: a `Spinner` with an optional `message`
9
+ * beside it ("Loading pipelines…"), in a box that fills its parent and centres it both ways. The
10
+ * Spinner is `role="status"`: the message, or "Loading", is announced.
11
+ *
12
+ * A Spinner and not the animated Galaxy logomark: it needs no `lottie-react` peer or animation
13
+ * data, it is already a status region, and a Galaxy logomark inside a product module would sit
14
+ * near the product's wordmark, which the brand rules never allow (decision, DLS 2.10).
15
+ *
16
+ * Siblings: `EmptyLayout` (loaded, nothing to show), `ErrorLayout` (failed). For rows or a card
17
+ * whose shape is known, prefer `Skeleton`, which holds the layout while it loads.
18
+ */
19
+ /** Where the region sits (canonical rungs, subset); picks the Spinner rung. */
20
+ var PendingLayoutSize = /* @__PURE__ */ function(PendingLayoutSize) {
21
+ /** A small card or a side panel: Spinner `SMALL`, a 12px ring and a `body_sm` message. */
22
+ PendingLayoutSize["SMALL"] = "SMALL";
23
+ /** A panel or a page section. The default: Spinner `MEDIUM`, 14px and `body_md`. */
24
+ PendingLayoutSize["MEDIUM"] = "MEDIUM";
25
+ /** A whole page body: Spinner `X_LARGE`, 16px and `body_lg`. */
26
+ PendingLayoutSize["LARGE"] = "LARGE";
27
+ return PendingLayoutSize;
28
+ }({});
29
+ /** Fills the region and centres the spinner in it. */
30
+ var Root = /*#__PURE__*/ styled("div")({
31
+ name: "Root",
32
+ class: "gx-PendingLayout-rx7ogqs",
33
+ propsAsIs: false
34
+ });
35
+ /** The Spinner rung per region: small, medium, and the largest ring for a whole page. */
36
+ var PENDING_LAYOUT_SIZE_TO_SPINNER_SIZE_MAP = {
37
+ ["SMALL"]: SpinnerSize.SMALL,
38
+ ["MEDIUM"]: SpinnerSize.MEDIUM,
39
+ ["LARGE"]: SpinnerSize.X_LARGE
40
+ };
41
+ /** A region that is still loading: a Spinner and its message, filled and centred. */
42
+ var PendingLayout = forwardRef(function PendingLayout({ message, size = "MEDIUM", id, className, style, ...rest }, ref) {
43
+ return /* @__PURE__ */ jsx(Root, {
44
+ ...rest,
45
+ ref,
46
+ id,
47
+ className,
48
+ style,
49
+ children: /* @__PURE__ */ jsx(Spinner, {
50
+ size: PENDING_LAYOUT_SIZE_TO_SPINNER_SIZE_MAP[size],
51
+ ...message === void 0 || message === "" ? {} : { label: message }
52
+ })
53
+ });
54
+ });
55
+ PendingLayout.displayName = "PendingLayout";
56
+ //#endregion
57
+ export { PendingLayoutSize, PendingLayout as default };
@@ -0,0 +1,14 @@
1
+ import { type Icon as PhosphorIcon } from "@phosphor-icons/react";
2
+ import type { ReactNode } from "react";
3
+ import { EmptyStateSize } from "../../feedback/EmptyState.js";
4
+ /** @internal Props of `ErrorStateContent`. */
5
+ export interface ErrorStateContentProps {
6
+ size: EmptyStateSize;
7
+ icon?: PhosphorIcon | undefined;
8
+ header: string;
9
+ description?: string | undefined;
10
+ detail?: string | undefined;
11
+ actions?: ReactNode;
12
+ }
13
+ /** @internal The error stack, announced assertively (`role="alert"`). */
14
+ export declare function ErrorStateContent({ size, icon, header, description, detail, actions, }: ErrorStateContentProps): import("react").JSX.Element;
@@ -0,0 +1,49 @@
1
+ import { FontFamily } from "../../theme/enums.js";
2
+ import Text, { TextSize, TextVariant } from "../../text/Text.js";
3
+ import EmptyState, { EmptyStateSize, EmptyStateVariant } from "../../feedback/EmptyState.js";
4
+ /* empty css */
5
+ import { styled } from "@linaria/react";
6
+ import { jsx, jsxs } from "react/jsx-runtime";
7
+ import { WarningCircleIcon } from "@phosphor-icons/react";
8
+ //#region src/layout/internal/errorState.tsx
9
+ /**
10
+ * @internal The one error state: an `ERROR` EmptyState (a `WarningCircleIcon` tile by default)
11
+ * and an optional `detail` line (the raw message, mono and selectable), in a `role="alert"` stack.
12
+ * ErrorLayout centres it in a region and TableCore renders it in an empty table body, so a page
13
+ * that failed and a table that failed read the same. Private on purpose (an `internal/` module:
14
+ * built, never a package export).
15
+ */
16
+ /** The stack: the empty state, then the detail 8px under it, both centred. */
17
+ var Root = /*#__PURE__*/ styled("div")({
18
+ name: "Root",
19
+ class: "gx-errorState-r12y0h8s",
20
+ propsAsIs: false
21
+ });
22
+ /** The detail's type step: a caption under a cell or panel state, `body_sm` under a page's. */
23
+ var EMPTY_STATE_SIZE_TO_DETAIL_SIZE_MAP = {
24
+ [EmptyStateSize.SMALL]: TextSize.CAPTION,
25
+ [EmptyStateSize.MEDIUM]: TextSize.CAPTION,
26
+ [EmptyStateSize.LARGE]: TextSize.BODY_SM
27
+ };
28
+ /** @internal The error stack, announced assertively (`role="alert"`). */
29
+ function ErrorStateContent({ size, icon = WarningCircleIcon, header, description, detail, actions }) {
30
+ return /* @__PURE__ */ jsxs(Root, {
31
+ role: "alert",
32
+ children: [/* @__PURE__ */ jsx(EmptyState, {
33
+ size,
34
+ variant: EmptyStateVariant.ERROR,
35
+ icon,
36
+ header,
37
+ ...description === void 0 || description === "" ? {} : { description },
38
+ ...actions === void 0 ? {} : { actions }
39
+ }), detail !== void 0 && detail !== "" && /* @__PURE__ */ jsx(Text, {
40
+ size: EMPTY_STATE_SIZE_TO_DETAIL_SIZE_MAP[size],
41
+ variant: TextVariant.ERROR,
42
+ family: FontFamily.MONO,
43
+ isSelectable: true,
44
+ children: detail
45
+ })]
46
+ });
47
+ }
48
+ //#endregion
49
+ export { ErrorStateContent };
@@ -0,0 +1,10 @@
1
+ /**
2
+ * @internal The rule EmptyLayout, ErrorLayout and PendingLayout share: fill the region they are
3
+ * given and centre their content in it. A CSS-text string like the mixins, interpolated into each
4
+ * Root (never passed to `cx`). Private on purpose (an `internal/` module: built, never a package
5
+ * export).
6
+ *
7
+ * `height: 100%` fills a block parent with a height; `flex: 1 1 auto` with `min-height: 0` fills
8
+ * a flex column (PageLayout's body, a Widget body) without overflowing it.
9
+ */
10
+ export declare const REGION_FILL = "\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n flex: 1 1 auto;\n box-sizing: border-box;\n width: 100%;\n height: 100%;\n min-width: 0;\n min-height: 0;\n";
@@ -0,0 +1,24 @@
1
+ //#region src/layout/internal/region.ts
2
+ /**
3
+ * @internal The rule EmptyLayout, ErrorLayout and PendingLayout share: fill the region they are
4
+ * given and centre their content in it. A CSS-text string like the mixins, interpolated into each
5
+ * Root (never passed to `cx`). Private on purpose (an `internal/` module: built, never a package
6
+ * export).
7
+ *
8
+ * `height: 100%` fills a block parent with a height; `flex: 1 1 auto` with `min-height: 0` fills
9
+ * a flex column (PageLayout's body, a Widget body) without overflowing it.
10
+ */
11
+ var REGION_FILL = `
12
+ display: flex;
13
+ flex-direction: column;
14
+ align-items: center;
15
+ justify-content: center;
16
+ flex: 1 1 auto;
17
+ box-sizing: border-box;
18
+ width: 100%;
19
+ height: 100%;
20
+ min-width: 0;
21
+ min-height: 0;
22
+ `;
23
+ //#endregion
24
+ export { REGION_FILL };
@@ -2,6 +2,7 @@ import { FontFamily, Orientation } from "../theme/enums.js";
2
2
  import Text, { TextSize, TextVariant } from "../text/Text.js";
3
3
  import { ButtonSize, ButtonVariant } from "../buttons/Button.js";
4
4
  import CopyButton from "../buttons/CopyButton.js";
5
+ import "../utils/format.js";
5
6
  /* empty css */
6
7
  import { styled } from "@linaria/react";
7
8
  import { forwardRef } from "react";
@@ -9,7 +9,7 @@ export declare enum MenuSize {
9
9
  /** 32px rows, `body_md` labels, 14px icons, 12px submenu caret. The default. */
10
10
  MEDIUM = "MEDIUM"
11
11
  }
12
- /** Panel fill: a surface step (Constitution V-A surface set). */
12
+ /** Panel fill: a surface step (AGENTS.md "Variant": surface set). */
13
13
  export declare enum MenuVariant {
14
14
  /** The canvas fill, `background.base`. The default. */
15
15
  BASE = "BASE",
@@ -20,7 +20,7 @@ export declare enum MenuVariant {
20
20
  /** The most elevated fill, `background.tertiary`. */
21
21
  TERTIARY = "TERTIARY"
22
22
  }
23
- /** Row mode (Constitution V-A: MenuItem `DEFAULT` / `ERROR`; declared in `scripts/lint-dls.ts` `MODE_SETS`). */
23
+ /** Row mode (AGENTS.md "Variant": the `MenuItemVariant` mode set `DEFAULT` / `ERROR`, declared in `scripts/lint-dls.ts` `MODE_SETS`). */
24
24
  export declare enum MenuItemVariant {
25
25
  /** A monochrome row. The default. */
26
26
  DEFAULT = "DEFAULT",
package/dist/menu/Menu.js CHANGED
@@ -11,6 +11,7 @@ import Divider from "../layout/Divider.js";
11
11
  import Popover, { PopoverRole, PopoverTrigger, PopoverVariant } from "../overlays/Popover.js";
12
12
  import Icon, { IconVariant } from "../icons/Icon.js";
13
13
  import { InputSize } from "../inputs/Input.js";
14
+ import { isSearchMatch, normalizeSearchTerm } from "../utils/search.js";
14
15
  import SearchInput from "../inputs/SearchInput.js";
15
16
  /* empty css */
16
17
  import { styled } from "@linaria/react";
@@ -45,7 +46,7 @@ var MenuSize = /* @__PURE__ */ function(MenuSize) {
45
46
  MenuSize["MEDIUM"] = "MEDIUM";
46
47
  return MenuSize;
47
48
  }({});
48
- /** Panel fill: a surface step (Constitution V-A surface set). */
49
+ /** Panel fill: a surface step (AGENTS.md "Variant": surface set). */
49
50
  var MenuVariant = /* @__PURE__ */ function(MenuVariant) {
50
51
  /** The canvas fill, `background.base`. The default. */
51
52
  MenuVariant["BASE"] = "BASE";
@@ -57,7 +58,7 @@ var MenuVariant = /* @__PURE__ */ function(MenuVariant) {
57
58
  MenuVariant["TERTIARY"] = "TERTIARY";
58
59
  return MenuVariant;
59
60
  }({});
60
- /** Row mode (Constitution V-A: MenuItem `DEFAULT` / `ERROR`; declared in `scripts/lint-dls.ts` `MODE_SETS`). */
61
+ /** Row mode (AGENTS.md "Variant": the `MenuItemVariant` mode set `DEFAULT` / `ERROR`, declared in `scripts/lint-dls.ts` `MODE_SETS`). */
61
62
  var MenuItemVariant = /* @__PURE__ */ function(MenuItemVariant) {
62
63
  /** A monochrome row. The default. */
63
64
  MenuItemVariant["DEFAULT"] = "DEFAULT";
@@ -87,7 +88,7 @@ var ItemRoot = /*#__PURE__*/ styled("button")({
87
88
  class: "gx-Menu-ItemRoot-i1kxcddz",
88
89
  propsAsIs: false
89
90
  });
90
- /** Row height, inline and block padding, and gap per rung (Constitution III-A.4). */
91
+ /** Row height, inline and block padding, and gap per rung (AGENTS.md "Control rungs"). */
91
92
  var MENU_SIZE_TO_ITEM_CSS_MAP = {
92
93
  ["SMALL"]: "gx-Menu-SMALL-s14ltqej",
93
94
  ["MEDIUM"]: "gx-Menu-MEDIUM-m1sw24v2"
@@ -237,16 +238,8 @@ function useMenuContext(part) {
237
238
  if (context === null) throw new Error(`${part} must be rendered inside a Menu or ContextMenu`);
238
239
  return context;
239
240
  }
240
- /** Lower-cased, trimmed term (the filter's comparison form). */
241
- function normalizeTerm(term) {
242
- return term.trim().toLocaleLowerCase();
243
- }
244
241
  /** A row matches when the term is empty or its label (or description) contains it. */
245
- function matches(term, label, description) {
246
- if (term === "") return true;
247
- if (label.toLocaleLowerCase().includes(term)) return true;
248
- return description?.toLocaleLowerCase().includes(term) ?? false;
249
- }
242
+ var matches = (term, label, description) => isSearchMatch(term, label, description);
250
243
  /** The rows of a list element, in DOM order (portaled submenus are not descendants). */
251
244
  function getRows(list) {
252
245
  if (list === null) return [];
@@ -919,7 +912,7 @@ var MenuRoot = forwardRef(function MenuRoot({ trigger, children, size = "MEDIUM"
919
912
  const triggerProps = trigger.props;
920
913
  const triggerId = typeof triggerProps.id === "string" ? triggerProps.id : generatedTriggerId;
921
914
  const closeTree = useCallback(() => setOpen(false), [setOpen]);
922
- const term = isSearchable ? normalizeTerm(rawTerm) : "";
915
+ const term = isSearchable ? normalizeSearchTerm(rawTerm) : "";
923
916
  const rootValue = useMemo(() => ({
924
917
  size,
925
918
  variant,
@@ -1,6 +1,6 @@
1
1
  import { type Icon as PhosphorIcon } from "@phosphor-icons/react";
2
2
  import { type AriaAttributes, type CSSProperties, type ReactNode } from "react";
3
- /** Panel width. Canonical rung names (Constitution V); the values are widths, not control heights. */
3
+ /** Panel width. Canonical rung names (AGENTS.md "Size"); the values are widths, not control heights. */
4
4
  export declare enum ModalSize {
5
5
  /** 400px: a question or a notice (`ConfirmDialog`). */
6
6
  SMALL = "SMALL",
@@ -39,7 +39,7 @@ interface ModalBaseProps extends AriaAttributes {
39
39
  * @default true
40
40
  */
41
41
  isDismissable?: boolean;
42
- /** Controlled open state. When set, Modal calls `onOpenChange` and lets the caller decide. */
42
+ /** Controlled open state. When set, Modal calls `onOpenChange` and lets the caller decide. While it closes, the panel keeps the `children` and slots it had when `isOpen` turned false, so pass the open record as a prop with no hook to retain it. */
43
43
  isOpen?: boolean;
44
44
  /**
45
45
  * Initial open state while uncontrolled.
@@ -36,7 +36,7 @@ import { XIcon } from "@phosphor-icons/react";
36
36
  * SSR: nothing renders on the server (closed by default, and `Portal` renders nothing there). The
37
37
  * scroll lock is CSS keyed on the backdrop; nothing touches `window` or `document` at module scope.
38
38
  */
39
- /** Panel width. Canonical rung names (Constitution V); the values are widths, not control heights. */
39
+ /** Panel width. Canonical rung names (AGENTS.md "Size"); the values are widths, not control heights. */
40
40
  var ModalSize = /* @__PURE__ */ function(ModalSize) {
41
41
  /** 400px: a question or a notice (`ConfirmDialog`). */
42
42
  ModalSize["SMALL"] = "SMALL";
@@ -15,6 +15,7 @@ import Fade from "../transform/Fade.js";
15
15
  import { useDebouncedValue } from "../hooks/useDebouncedValue.js";
16
16
  import { useLocalStorage } from "../hooks/useLocalStorage.js";
17
17
  import { InputSize } from "../inputs/Input.js";
18
+ import { isSearchMatch } from "../utils/search.js";
18
19
  import SearchInput from "../inputs/SearchInput.js";
19
20
  /* empty css */
20
21
  import { styled } from "@linaria/react";
@@ -81,15 +82,8 @@ function focusPastExitingTrap(target) {
81
82
  window.removeEventListener("focusin", stop, true);
82
83
  }
83
84
  }
84
- /** Whether an item matches the normalized term (label, description or keywords; `includes`). */
85
- function matches(item, normalized) {
86
- if (normalized === "") return true;
87
- return [
88
- item.label,
89
- item.description ?? "",
90
- ...item.keywords ?? []
91
- ].some((field) => field.toLocaleLowerCase().includes(normalized));
92
- }
85
+ /** Whether an item matches the term: its label, description or keywords (the shared search rule). */
86
+ var matches = (item, term) => isSearchMatch(term, item.label, item.description, ...item.keywords ?? []);
93
87
  /**
94
88
  * Scroll lock: while the backdrop is mounted (open or exiting) the page does not scroll. Keyed on
95
89
  * this module's backdrop, so it holds until the last modal backdrop is gone.
@@ -250,8 +244,7 @@ var CommandPalette = forwardRef(function CommandPalette({ items, isOpen: isOpenP
250
244
  setActiveId(null);
251
245
  setHasTyped(true);
252
246
  };
253
- const normalized = term.trim().toLocaleLowerCase();
254
- const visibleItems = onSearch === void 0 ? items.filter((item) => matches(item, normalized)) : items;
247
+ const visibleItems = onSearch === void 0 ? items.filter((item) => matches(item, term)) : items;
255
248
  const recentItems = storageKey !== void 0 && term === "" ? recentIds.flatMap((recentId) => {
256
249
  const item = items.find((candidate) => candidate.id === recentId);
257
250
  return item !== void 0 && !item.isDisabled ? [item] : [];
@@ -7,6 +7,11 @@ export interface SidebarNavProps extends AriaAttributes {
7
7
  header?: ReactNode;
8
8
  /** Content pinned under the list: the account row, `NavItem`s such as Settings, the collapse toggle. */
9
9
  footer?: ReactNode;
10
+ /**
11
+ * The hairlines under the header and over the footer, together (the Modal and Drawer meaning). On by default, unlike the overlays, because the header's line meets the Topbar's in an app frame.
12
+ * @default true
13
+ */
14
+ hasDividers?: boolean;
10
15
  /**
11
16
  * Icon rail: items show only their icon (labels and counts move into tooltips), group labels become dividers. Give the parent a 48px width.
12
17
  * @default false
@@ -24,6 +24,15 @@ import { ArrowUpRightIcon, CaretRightIcon } from "@phosphor-icons/react";
24
24
  * optional count `Chip` and an active state for the current page. `isCollapsed` turns the column
25
25
  * into an icon rail whose labels move into tooltips.
26
26
  *
27
+ * `hasDividers` (on by default) draws the hairline under the header and over the footer. A frame
28
+ * whose own chrome already separates the column (a header-less SidebarNav in an inset frame) turns
29
+ * them off.
30
+ *
31
+ * Two frame systems use it. In a standalone app it sits beside a `Topbar` with a `header`: both
32
+ * rows are 48px, so their hairlines meet. In a Galaxy module it goes in `AppFrame` with no `header`
33
+ * (the host draws the brand), `hasDividers={false}` for a footer with no line. Never a `header`
34
+ * inside AppFrame: its line would meet nothing.
35
+ *
27
36
  * SidebarNav fills the box its parent gives it (the app's frame sizes, resizes and collapses that box)
28
37
  * and never routes: the app passes `href` and derives `isActive` from the current route. Sibling
29
38
  * views inside a page are `Tabs`; a hierarchy of records is a `Tree`.
@@ -63,76 +72,80 @@ var Footer = /*#__PURE__*/ styled("div")({
63
72
  class: "gx-SidebarNav-Footer-f1bmww1o",
64
73
  propsAsIs: false
65
74
  });
75
+ /** `hasDividers`: the header's bottom hairline. Inside the 48px box (border-box), so the row height holds. */
76
+ var HEADER_DIVIDER_CSS = "gx-SidebarNav-HEADER_DIVIDER_CSS-hwiy54t";
77
+ /** `hasDividers`: the footer's top hairline. */
78
+ var FOOTER_DIVIDER_CSS = "gx-SidebarNav-FOOTER_DIVIDER_CSS-f1otz5la";
66
79
  /** A list item holding one `NavItem`. */
67
80
  var ItemLi = /*#__PURE__*/ styled("li")({
68
81
  name: "ItemLi",
69
- class: "gx-SidebarNav-ItemLi-iwiy54t",
82
+ class: "gx-SidebarNav-ItemLi-ijewkq1",
70
83
  propsAsIs: false
71
84
  });
72
85
  /** The shared item box: a link or a button on the `MEDIUM` rung (a plain string, like the mixins). */
73
86
  var ItemLink = /*#__PURE__*/ styled("a")({
74
87
  name: "ItemLink",
75
- class: "gx-SidebarNav-ItemLink-i1otz5la",
88
+ class: "gx-SidebarNav-ItemLink-i12h7i8o",
76
89
  propsAsIs: false
77
90
  });
78
91
  var ItemButton = /*#__PURE__*/ styled("button")({
79
92
  name: "ItemButton",
80
- class: "gx-SidebarNav-ItemButton-ijewkq1",
93
+ class: "gx-SidebarNav-ItemButton-i156g88p",
81
94
  propsAsIs: false
82
95
  });
83
96
  var LetterBox = /*#__PURE__*/ styled("span")({
84
97
  name: "LetterBox",
85
- class: "gx-SidebarNav-LetterBox-l12h7i8o",
98
+ class: "gx-SidebarNav-LetterBox-l1ka7is",
86
99
  propsAsIs: false
87
100
  });
88
101
  var Trailing = /*#__PURE__*/ styled("span")({
89
102
  name: "Trailing",
90
- class: "gx-SidebarNav-Trailing-t156g88p",
103
+ class: "gx-SidebarNav-Trailing-tls5awu",
91
104
  propsAsIs: false
92
105
  });
93
106
  /** A section: its heading (or the rail divider) and its own list. */
94
107
  var GroupRoot = /*#__PURE__*/ styled("li")({
95
108
  name: "GroupRoot",
96
- class: "gx-SidebarNav-GroupRoot-g1ka7is",
109
+ class: "gx-SidebarNav-GroupRoot-g4ix9y9",
97
110
  propsAsIs: false
98
111
  });
99
112
  var GroupList = /*#__PURE__*/ styled("ul")({
100
113
  name: "GroupList",
101
- class: "gx-SidebarNav-GroupList-gls5awu",
114
+ class: "gx-SidebarNav-GroupList-gqfe40h",
102
115
  propsAsIs: false
103
116
  });
104
117
  var GroupLabel = /*#__PURE__*/ styled("div")({
105
118
  name: "GroupLabel",
106
- class: "gx-SidebarNav-GroupLabel-g4ix9y9",
119
+ class: "gx-SidebarNav-GroupLabel-g9ff0oz",
107
120
  propsAsIs: false
108
121
  });
109
122
  var GroupToggle = /*#__PURE__*/ styled("button")({
110
123
  name: "GroupToggle",
111
- class: "gx-SidebarNav-GroupToggle-gqfe40h",
124
+ class: "gx-SidebarNav-GroupToggle-g6f6h0i",
112
125
  propsAsIs: false
113
126
  });
114
127
  /** The rail's stand-in for a section heading; the first section has none. */
115
128
  var GroupDivider = /*#__PURE__*/ styled("div")({
116
129
  name: "GroupDivider",
117
- class: "gx-SidebarNav-GroupDivider-g9ff0oz",
130
+ class: "gx-SidebarNav-GroupDivider-gyqt4ix",
118
131
  propsAsIs: false
119
132
  });
120
133
  /** Rail mode: the header content and the footer content center in the 48px column. */
121
- var ROOT_COLLAPSED_CSS = "gx-SidebarNav-ROOT_COLLAPSED_CSS-r6f6h0i";
134
+ var ROOT_COLLAPSED_CSS = "gx-SidebarNav-ROOT_COLLAPSED_CSS-rieaizt";
122
135
  /** The scrolling list takes the height between header and footer. */
123
- var LIST_SCROLL_CSS = "gx-SidebarNav-LIST_SCROLL_CSS-lyqt4ix";
124
- var ITEM_LI_COLLAPSED_CSS = "gx-SidebarNav-ITEM_LI_COLLAPSED_CSS-iieaizt";
136
+ var LIST_SCROLL_CSS = "gx-SidebarNav-LIST_SCROLL_CSS-lo6ca5i";
137
+ var ITEM_LI_COLLAPSED_CSS = "gx-SidebarNav-ITEM_LI_COLLAPSED_CSS-iuopbgu";
125
138
  /** Hover and press on an enabled item that is not the current page. */
126
- var ITEM_INTERACTIVE_CSS = "gx-SidebarNav-ITEM_INTERACTIVE_CSS-io6ca5i";
139
+ var ITEM_INTERACTIVE_CSS = "gx-SidebarNav-ITEM_INTERACTIVE_CSS-i6m4lrh";
127
140
  /** The current page: the selection fill (`background.selected`) and primary text (hover keeps it). */
128
- var ITEM_ACTIVE_CSS = "gx-SidebarNav-ITEM_ACTIVE_CSS-iuopbgu";
129
- var ITEM_DISABLED_CSS = "gx-SidebarNav-ITEM_DISABLED_CSS-i6m4lrh";
141
+ var ITEM_ACTIVE_CSS = "gx-SidebarNav-ITEM_ACTIVE_CSS-i1qgyulv";
142
+ var ITEM_DISABLED_CSS = "gx-SidebarNav-ITEM_DISABLED_CSS-i189383r";
130
143
  /** Rail mode: a 32px square (the 8px list padding makes the rail 48px). */
131
- var ITEM_COLLAPSED_CSS = "gx-SidebarNav-ITEM_COLLAPSED_CSS-i1qgyulv";
132
- var LABEL_CSS = "gx-SidebarNav-LABEL_CSS-l189383r";
133
- var VISUALLY_HIDDEN_CSS = "gx-SidebarNav-VISUALLY_HIDDEN_CSS-v1ietnnw";
144
+ var ITEM_COLLAPSED_CSS = "gx-SidebarNav-ITEM_COLLAPSED_CSS-i1ietnnw";
145
+ var LABEL_CSS = "gx-SidebarNav-LABEL_CSS-l1d4bor0";
146
+ var VISUALLY_HIDDEN_CSS = "gx-SidebarNav-VISUALLY_HIDDEN_CSS-v1rmtlmv";
134
147
  /** The primary navigation column: header, scrolling list of items and sections, footer. */
135
- var SidebarNav = forwardRef(function SidebarNav({ children, header, footer, isCollapsed = false, ariaLabel, id, className, style, ...rest }, ref) {
148
+ var SidebarNav = forwardRef(function SidebarNav({ children, header, footer, hasDividers = true, isCollapsed = false, ariaLabel, id, className, style, ...rest }, ref) {
136
149
  const name = ariaLabel || rest["aria-label"] || (rest["aria-labelledby"] ? void 0 : DEFAULT_ARIA_LABEL);
137
150
  const listContext = useMemo(() => ({
138
151
  isCollapsed,
@@ -150,7 +163,10 @@ var SidebarNav = forwardRef(function SidebarNav({ children, header, footer, isCo
150
163
  className: cx(isCollapsed && ROOT_COLLAPSED_CSS, className),
151
164
  style,
152
165
  children: [
153
- header !== void 0 && header !== null && header !== false && /* @__PURE__ */ jsx(Header, { children: header }),
166
+ header !== void 0 && header !== null && header !== false && /* @__PURE__ */ jsx(Header, {
167
+ className: cx(hasDividers && HEADER_DIVIDER_CSS),
168
+ children: header
169
+ }),
154
170
  /* @__PURE__ */ jsx(ScrollArea, {
155
171
  className: LIST_SCROLL_CSS,
156
172
  children: /* @__PURE__ */ jsx(SidebarNavContext.Provider, {
@@ -163,7 +179,10 @@ var SidebarNav = forwardRef(function SidebarNav({ children, header, footer, isCo
163
179
  }),
164
180
  footer !== void 0 && footer !== null && footer !== false && /* @__PURE__ */ jsx(SidebarNavContext.Provider, {
165
181
  value: footerContext,
166
- children: /* @__PURE__ */ jsx(Footer, { children: footer })
182
+ children: /* @__PURE__ */ jsx(Footer, {
183
+ className: cx(hasDividers && FOOTER_DIVIDER_CSS),
184
+ children: footer
185
+ })
167
186
  })
168
187
  ]
169
188
  });
@@ -12,6 +12,10 @@ import { jsx, jsxs } from "react/jsx-runtime";
12
12
  * spacing and the left / right split; everything inside is composed from other components. It
13
13
  * does not stick, route or collapse. In an app frame it sits above the page's scrolling region.
14
14
  *
15
+ * It belongs to the standalone-app frame (Topbar + a `SidebarNav` with a `header`, whose 48px rows
16
+ * meet on one hairline). A Galaxy module uses `AppFrame` + `PageLayout` instead, whose 64px title
17
+ * row carries the page; the two frames are never mixed.
18
+ *
15
19
  * Outside `main`, `article`, `aside`, `nav` and `section` the `<header>` is the page's `banner`
16
20
  * landmark, so use one Topbar per page. New in 2.0 (from the rafa prototype).
17
21
  */
@@ -13,7 +13,7 @@ export declare enum CircleSize {
13
13
  /** 12px, beside `body_lg` (20px line). */
14
14
  X_LARGE = "X_LARGE"
15
15
  }
16
- /** Fill color role (Constitution V-A color set). Neutral steps resolve to `color.text.*`, statuses to `color.mark.*`. */
16
+ /** Fill color role (AGENTS.md "Variant": color set). Neutral steps resolve to `color.text.*`, statuses to `color.mark.*`. */
17
17
  export declare enum CircleVariant {
18
18
  /** `text.primary`. */
19
19
  PRIMARY = "PRIMARY",