@lobehub/ui 5.57.3 → 5.59.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 (88) hide show
  1. package/es/Grid/style.mjs +8 -8
  2. package/es/Grid/style.mjs.map +1 -1
  3. package/es/Image/type.d.mts +1 -0
  4. package/es/Image/viewer/ImageViewer.mjs +1 -0
  5. package/es/Image/viewer/ImageViewer.mjs.map +1 -1
  6. package/es/Image/viewer/Toolbar.mjs +19 -6
  7. package/es/Image/viewer/Toolbar.mjs.map +1 -1
  8. package/es/Image/viewer/ViewerChrome.mjs.map +1 -1
  9. package/es/base-ui/Input/style.d.mts +1 -1
  10. package/es/base-ui/Input/style.mjs +8 -2
  11. package/es/base-ui/Input/style.mjs.map +1 -1
  12. package/es/dashboard/ChartGrid/ChartGrid.d.mts +10 -0
  13. package/es/dashboard/ChartGrid/ChartGrid.mjs +18 -0
  14. package/es/dashboard/ChartGrid/ChartGrid.mjs.map +1 -0
  15. package/es/dashboard/ChartGrid/index.d.mts +3 -0
  16. package/es/dashboard/ChartGrid/style.mjs +32 -0
  17. package/es/dashboard/ChartGrid/style.mjs.map +1 -0
  18. package/es/dashboard/ChartGrid/type.d.mts +9 -0
  19. package/es/dashboard/ConsoleShell/ConsoleShell.d.mts +1 -1
  20. package/es/dashboard/ConsoleShell/ConsoleShell.mjs +12 -4
  21. package/es/dashboard/ConsoleShell/ConsoleShell.mjs.map +1 -1
  22. package/es/dashboard/ConsoleShell/style.mjs +15 -0
  23. package/es/dashboard/ConsoleShell/style.mjs.map +1 -1
  24. package/es/dashboard/ConsoleShell/type.d.mts +2 -0
  25. package/es/dashboard/FilterStrip/FilterStrip.d.mts +11 -0
  26. package/es/dashboard/FilterStrip/FilterStrip.mjs +51 -0
  27. package/es/dashboard/FilterStrip/FilterStrip.mjs.map +1 -0
  28. package/es/dashboard/FilterStrip/index.d.mts +3 -0
  29. package/es/dashboard/FilterStrip/style.mjs +93 -0
  30. package/es/dashboard/FilterStrip/style.mjs.map +1 -0
  31. package/es/dashboard/FilterStrip/type.d.mts +26 -0
  32. package/es/dashboard/LabelWithKey/LabelWithKey.d.mts +10 -0
  33. package/es/dashboard/LabelWithKey/LabelWithKey.mjs +18 -0
  34. package/es/dashboard/LabelWithKey/LabelWithKey.mjs.map +1 -0
  35. package/es/dashboard/LabelWithKey/index.d.mts +3 -0
  36. package/es/dashboard/LabelWithKey/type.d.mts +12 -0
  37. package/es/dashboard/MetricStrip/MetricStrip.d.mts +10 -0
  38. package/es/dashboard/MetricStrip/MetricStrip.mjs +76 -0
  39. package/es/dashboard/MetricStrip/MetricStrip.mjs.map +1 -0
  40. package/es/dashboard/MetricStrip/index.d.mts +3 -0
  41. package/es/dashboard/MetricStrip/style.mjs +116 -0
  42. package/es/dashboard/MetricStrip/style.mjs.map +1 -0
  43. package/es/dashboard/MetricStrip/type.d.mts +25 -0
  44. package/es/dashboard/PageToolbar/PageToolbar.d.mts +10 -0
  45. package/es/dashboard/PageToolbar/PageToolbar.mjs +21 -0
  46. package/es/dashboard/PageToolbar/PageToolbar.mjs.map +1 -0
  47. package/es/dashboard/PageToolbar/index.d.mts +3 -0
  48. package/es/dashboard/PageToolbar/style.mjs +34 -0
  49. package/es/dashboard/PageToolbar/style.mjs.map +1 -0
  50. package/es/dashboard/PageToolbar/type.d.mts +12 -0
  51. package/es/dashboard/PanelSurface/PanelSurface.d.mts +10 -0
  52. package/es/dashboard/PanelSurface/PanelSurface.mjs +36 -0
  53. package/es/dashboard/PanelSurface/PanelSurface.mjs.map +1 -0
  54. package/es/dashboard/PanelSurface/index.d.mts +3 -0
  55. package/es/dashboard/PanelSurface/style.mjs +24 -0
  56. package/es/dashboard/PanelSurface/style.mjs.map +1 -0
  57. package/es/dashboard/PanelSurface/type.d.mts +18 -0
  58. package/es/dashboard/Section/Section.d.mts +10 -0
  59. package/es/dashboard/Section/Section.mjs +81 -0
  60. package/es/dashboard/Section/Section.mjs.map +1 -0
  61. package/es/dashboard/Section/index.d.mts +3 -0
  62. package/es/dashboard/Section/style.mjs +35 -0
  63. package/es/dashboard/Section/style.mjs.map +1 -0
  64. package/es/dashboard/Section/type.d.mts +24 -0
  65. package/es/dashboard/Surface/Surface.d.mts +4 -3
  66. package/es/dashboard/Surface/Surface.mjs +67 -7
  67. package/es/dashboard/Surface/Surface.mjs.map +1 -1
  68. package/es/dashboard/Surface/index.d.mts +3 -2
  69. package/es/dashboard/Surface/link.d.mts +14 -0
  70. package/es/dashboard/Surface/link.mjs +22 -0
  71. package/es/dashboard/Surface/link.mjs.map +1 -0
  72. package/es/dashboard/Surface/style.d.mts +23 -0
  73. package/es/dashboard/Surface/style.mjs +283 -2
  74. package/es/dashboard/Surface/style.mjs.map +1 -1
  75. package/es/dashboard/Surface/type.d.mts +30 -2
  76. package/es/dashboard/TableIconActions/TableIconActions.d.mts +17 -0
  77. package/es/dashboard/TableIconActions/TableIconActions.mjs +39 -0
  78. package/es/dashboard/TableIconActions/TableIconActions.mjs.map +1 -0
  79. package/es/dashboard/TableIconActions/index.d.mts +3 -0
  80. package/es/dashboard/TableIconActions/type.d.mts +18 -0
  81. package/es/dashboard/TitleWithTips/TitleWithTips.d.mts +10 -0
  82. package/es/dashboard/TitleWithTips/TitleWithTips.mjs +28 -0
  83. package/es/dashboard/TitleWithTips/TitleWithTips.mjs.map +1 -0
  84. package/es/dashboard/TitleWithTips/index.d.mts +3 -0
  85. package/es/dashboard/TitleWithTips/type.d.mts +11 -0
  86. package/es/dashboard/index.d.mts +30 -2
  87. package/es/dashboard/index.mjs +12 -2
  88. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"style.mjs","names":[],"sources":["../../../src/dashboard/ConsoleShell/style.ts"],"sourcesContent":["import { createStaticStyles } from 'antd-style';\n\nconst SIDEBAR_INLINE_SIZE = 248;\n// Twice the nav icon inset (8px nav padding + 10px item padding) plus the 18px icon, so the icons\n// land centered on the rail without moving sideways.\nconst SIDEBAR_RAIL_INLINE_SIZE = 54;\nconst WORKSPACE_INSET = 8;\n\n/**\n * `responsive.laptop`, not `responsive.tablet`. The aliases are named for\n * devices, and only `laptop` (max-width 991px) lines up with `useIsCompact`.\n */\nexport const styles = createStaticStyles(({ css, cssVar, responsive }) => ({\n brand: css`\n display: flex;\n flex: none;\n align-items: center;\n\n block-size: 56px;\n margin-block-start: ${WORKSPACE_INSET}px;\n padding-inline: 15px;\n\n color: ${cssVar.colorText};\n white-space: nowrap;\n\n ${responsive.laptop} {\n margin-block-start: 0;\n }\n `,\n brandAnchor: css`\n display: flex;\n align-items: center;\n\n inline-size: 100%;\n min-inline-size: 0;\n\n color: inherit;\n text-decoration: none;\n `,\n brandLockup: css`\n display: flex;\n gap: 10px;\n align-items: center;\n min-inline-size: 0;\n `,\n brandName: css`\n overflow: hidden;\n\n font-size: 14px;\n font-weight: 600;\n line-height: 1.3;\n color: ${cssVar.colorText};\n text-overflow: ellipsis;\n white-space: nowrap;\n\n transition: opacity 120ms ease 80ms;\n\n [data-collapsed='true'] & {\n text-overflow: clip;\n opacity: 0;\n transition-delay: 0s;\n }\n\n @media (prefers-reduced-motion: reduce) {\n transition: none;\n }\n `,\n main: css`\n overflow: auto;\n display: flex;\n flex: 1;\n flex-direction: column;\n gap: 20px;\n\n min-inline-size: 0;\n min-block-size: 0;\n padding: 24px;\n\n /*\n * A page that owns a full-height split sets data-page-layout=\"flush\" and\n * takes over padding and scrolling, so its sidebar can sit on the card edge.\n */\n &:has([data-page-layout='flush']) {\n overflow: hidden;\n gap: 0;\n padding: 0;\n }\n\n ${responsive.mobile} {\n gap: 16px;\n padding: 16px;\n }\n `,\n navSlot: css`\n display: flex;\n flex: 1;\n flex-direction: column;\n min-block-size: 0;\n `,\n shell: css`\n overflow: hidden;\n display: flex;\n flex: 1;\n\n block-size: 100%;\n min-block-size: 0;\n\n background: ${cssVar.colorBgLayout};\n `,\n sidebar: css`\n overflow: hidden;\n display: flex;\n flex: none;\n flex-direction: column;\n\n inline-size: ${SIDEBAR_INLINE_SIZE}px;\n block-size: 100%;\n min-block-size: 0;\n\n transition: inline-size 200ms ease;\n\n &[data-collapsed='true'] {\n inline-size: ${SIDEBAR_RAIL_INLINE_SIZE}px;\n }\n\n &[data-instant='true'] {\n transition: none;\n }\n\n ${responsive.laptop} {\n display: none;\n }\n `,\n sidebarBottom: css`\n flex: none;\n padding: 8px;\n `,\n skipLink: css`\n position: fixed;\n z-index: 2000;\n inset-block-start: -100px;\n inset-inline-start: 12px;\n\n padding-block: 8px;\n padding-inline: 12px;\n border-radius: ${cssVar.borderRadius};\n\n color: ${cssVar.colorText};\n\n background: ${cssVar.colorBgElevated};\n\n &:focus {\n inset-block-start: 12px;\n }\n `,\n topbar: css`\n display: flex;\n flex: none;\n gap: 8px;\n align-items: center;\n\n block-size: 56px;\n padding-inline: 12px;\n border-block-end: 1px solid ${cssVar.colorBorderSecondary};\n `,\n topbarDivider: css`\n flex: none;\n\n inline-size: 1px;\n block-size: 18px;\n margin-inline: 2px;\n\n background: ${cssVar.colorBorderSecondary};\n `,\n topbarMain: css`\n display: flex;\n flex: 1;\n align-items: center;\n min-inline-size: 0;\n `,\n tools: css`\n display: flex;\n flex: none;\n gap: 6px;\n align-items: center;\n\n margin-inline-start: auto;\n `,\n workspace: css`\n overflow: hidden;\n display: flex;\n flex: 1;\n flex-direction: column;\n\n min-inline-size: 0;\n min-block-size: 0;\n margin-block: ${WORKSPACE_INSET}px;\n margin-inline: 0 ${WORKSPACE_INSET}px;\n border: 1px solid ${cssVar.colorBorderSecondary};\n border-radius: ${cssVar.borderRadiusLG};\n\n background: ${cssVar.colorBgContainer};\n box-shadow: ${cssVar.boxShadowTertiary};\n\n ${responsive.laptop} {\n margin: 0;\n border: none;\n border-radius: 0;\n box-shadow: none;\n }\n `,\n}));\n"],"mappings":";;AAEA,MAAM,sBAAsB;AAG5B,MAAM,2BAA2B;AACjC,MAAM,kBAAkB;;;;;AAMxB,MAAa,SAAS,oBAAoB,EAAE,KAAK,QAAQ,kBAAkB;CACzE,OAAO,GAAG;;;;;;0BAMc,gBAAgB;;;aAG7B,OAAO,UAAU;;;MAGxB,WAAW,OAAO;;;;CAItB,aAAa,GAAG;;;;;;;;;;CAUhB,aAAa,GAAG;;;;;;CAMhB,WAAW,GAAG;;;;;;aAMH,OAAO,UAAU;;;;;;;;;;;;;;;;CAgB5B,MAAM,GAAG;;;;;;;;;;;;;;;;;;;;;MAqBL,WAAW,OAAO;;;;;CAKtB,SAAS,GAAG;;;;;;CAMZ,OAAO,GAAG;;;;;;;;kBAQM,OAAO,cAAc;;CAErC,SAAS,GAAG;;;;;;mBAMK,oBAAoB;;;;;;;qBAOlB,yBAAyB;;;;;;;MAOxC,WAAW,OAAO;;;;CAItB,eAAe,GAAG;;;;CAIlB,UAAU,GAAG;;;;;;;;qBAQM,OAAO,aAAa;;aAE5B,OAAO,UAAU;;kBAEZ,OAAO,gBAAgB;;;;;;CAMvC,QAAQ,GAAG;;;;;;;;kCAQqB,OAAO,qBAAqB;;CAE5D,eAAe,GAAG;;;;;;;kBAOF,OAAO,qBAAqB;;CAE5C,YAAY,GAAG;;;;;;CAMf,OAAO,GAAG;;;;;;;;CAQV,WAAW,GAAG;;;;;;;;oBAQI,gBAAgB;uBACb,gBAAgB;wBACf,OAAO,qBAAqB;qBAC/B,OAAO,eAAe;;kBAEzB,OAAO,iBAAiB;kBACxB,OAAO,kBAAkB;;MAErC,WAAW,OAAO;;;;;;;AAOxB,EAAE"}
1
+ {"version":3,"file":"style.mjs","names":[],"sources":["../../../src/dashboard/ConsoleShell/style.ts"],"sourcesContent":["import { createStaticStyles } from 'antd-style';\n\nconst SIDEBAR_INLINE_SIZE = 248;\n// Twice the nav icon inset (8px nav padding + 10px item padding) plus the 18px icon, so the icons\n// land centered on the rail without moving sideways.\nconst SIDEBAR_RAIL_INLINE_SIZE = 54;\nconst WORKSPACE_INSET = 8;\n\n/**\n * `responsive.laptop`, not `responsive.tablet`. The aliases are named for\n * devices, and only `laptop` (max-width 991px) lines up with `useIsCompact`.\n */\nexport const styles = createStaticStyles(({ css, cssVar, responsive }) => ({\n brand: css`\n display: flex;\n flex: none;\n align-items: center;\n\n block-size: 56px;\n margin-block-start: ${WORKSPACE_INSET}px;\n padding-inline: 15px;\n\n color: ${cssVar.colorText};\n white-space: nowrap;\n\n ${responsive.laptop} {\n margin-block-start: 0;\n }\n `,\n /** Close button in the drawer's brand row: right edge, vertically centred with the brand. */\n brandClose: css`\n flex: none;\n margin-inline: auto -7px;\n `,\n brandAnchor: css`\n display: flex;\n align-items: center;\n\n inline-size: 100%;\n min-inline-size: 0;\n\n color: inherit;\n text-decoration: none;\n `,\n brandLockup: css`\n display: flex;\n gap: 10px;\n align-items: center;\n min-inline-size: 0;\n `,\n brandName: css`\n overflow: hidden;\n\n font-size: 14px;\n font-weight: 600;\n line-height: 1.3;\n color: ${cssVar.colorText};\n text-overflow: ellipsis;\n white-space: nowrap;\n\n transition: opacity 120ms ease 80ms;\n\n [data-collapsed='true'] & {\n text-overflow: clip;\n opacity: 0;\n transition-delay: 0s;\n }\n\n @media (prefers-reduced-motion: reduce) {\n transition: none;\n }\n `,\n main: css`\n overflow: auto;\n display: flex;\n flex: 1;\n flex-direction: column;\n gap: 20px;\n\n min-inline-size: 0;\n min-block-size: 0;\n padding: 24px;\n\n /*\n * A page that owns a full-height split sets data-page-layout=\"flush\" and\n * takes over padding and scrolling, so its sidebar can sit on the card edge.\n */\n &:has([data-page-layout='flush']) {\n overflow: hidden;\n gap: 0;\n padding: 0;\n }\n\n ${responsive.mobile} {\n gap: 16px;\n padding: 16px;\n }\n `,\n /**\n * The drawer body is the scroll container. Without its default padding the scrollbar sits on\n * the panel edge; the brand row, nav and footer keep their own insets.\n */\n drawerBody: css`\n && {\n min-block-size: 100%;\n padding: 0;\n }\n `,\n navSlot: css`\n display: flex;\n flex: 1;\n flex-direction: column;\n min-block-size: 0;\n `,\n shell: css`\n overflow: hidden;\n display: flex;\n flex: 1;\n\n block-size: 100%;\n min-block-size: 0;\n\n background: ${cssVar.colorBgLayout};\n `,\n sidebar: css`\n overflow: hidden;\n display: flex;\n flex: none;\n flex-direction: column;\n\n inline-size: ${SIDEBAR_INLINE_SIZE}px;\n block-size: 100%;\n min-block-size: 0;\n\n transition: inline-size 200ms ease;\n\n &[data-collapsed='true'] {\n inline-size: ${SIDEBAR_RAIL_INLINE_SIZE}px;\n }\n\n &[data-instant='true'] {\n transition: none;\n }\n\n ${responsive.laptop} {\n display: none;\n }\n `,\n sidebarBottom: css`\n flex: none;\n padding: 8px;\n `,\n skipLink: css`\n position: fixed;\n z-index: 2000;\n inset-block-start: -100px;\n inset-inline-start: 12px;\n\n padding-block: 8px;\n padding-inline: 12px;\n border-radius: ${cssVar.borderRadius};\n\n color: ${cssVar.colorText};\n\n background: ${cssVar.colorBgElevated};\n\n &:focus {\n inset-block-start: 12px;\n }\n `,\n topbar: css`\n display: flex;\n flex: none;\n gap: 8px;\n align-items: center;\n\n block-size: 56px;\n padding-inline: 12px;\n border-block-end: 1px solid ${cssVar.colorBorderSecondary};\n `,\n topbarDivider: css`\n flex: none;\n\n inline-size: 1px;\n block-size: 18px;\n margin-inline: 2px;\n\n background: ${cssVar.colorBorderSecondary};\n `,\n topbarMain: css`\n display: flex;\n flex: 1;\n align-items: center;\n min-inline-size: 0;\n `,\n tools: css`\n display: flex;\n flex: none;\n gap: 6px;\n align-items: center;\n\n margin-inline-start: auto;\n `,\n workspace: css`\n overflow: hidden;\n display: flex;\n flex: 1;\n flex-direction: column;\n\n min-inline-size: 0;\n min-block-size: 0;\n margin-block: ${WORKSPACE_INSET}px;\n margin-inline: 0 ${WORKSPACE_INSET}px;\n border: 1px solid ${cssVar.colorBorderSecondary};\n border-radius: ${cssVar.borderRadiusLG};\n\n background: ${cssVar.colorBgContainer};\n box-shadow: ${cssVar.boxShadowTertiary};\n\n ${responsive.laptop} {\n margin: 0;\n border: none;\n border-radius: 0;\n box-shadow: none;\n }\n `,\n}));\n"],"mappings":";;AAEA,MAAM,sBAAsB;AAG5B,MAAM,2BAA2B;AACjC,MAAM,kBAAkB;;;;;AAMxB,MAAa,SAAS,oBAAoB,EAAE,KAAK,QAAQ,kBAAkB;CACzE,OAAO,GAAG;;;;;;0BAMc,gBAAgB;;;aAG7B,OAAO,UAAU;;;MAGxB,WAAW,OAAO;;;;;CAKtB,YAAY,GAAG;;;;CAIf,aAAa,GAAG;;;;;;;;;;CAUhB,aAAa,GAAG;;;;;;CAMhB,WAAW,GAAG;;;;;;aAMH,OAAO,UAAU;;;;;;;;;;;;;;;;CAgB5B,MAAM,GAAG;;;;;;;;;;;;;;;;;;;;;MAqBL,WAAW,OAAO;;;;;;;;;CAStB,YAAY,GAAG;;;;;;CAMf,SAAS,GAAG;;;;;;CAMZ,OAAO,GAAG;;;;;;;;kBAQM,OAAO,cAAc;;CAErC,SAAS,GAAG;;;;;;mBAMK,oBAAoB;;;;;;;qBAOlB,yBAAyB;;;;;;;MAOxC,WAAW,OAAO;;;;CAItB,eAAe,GAAG;;;;CAIlB,UAAU,GAAG;;;;;;;;qBAQM,OAAO,aAAa;;aAE5B,OAAO,UAAU;;kBAEZ,OAAO,gBAAgB;;;;;;CAMvC,QAAQ,GAAG;;;;;;;;kCAQqB,OAAO,qBAAqB;;CAE5D,eAAe,GAAG;;;;;;;kBAOF,OAAO,qBAAqB;;CAE5C,YAAY,GAAG;;;;;;CAMf,OAAO,GAAG;;;;;;;;CAQV,WAAW,GAAG;;;;;;;;oBAQI,gBAAgB;uBACb,gBAAgB;wBACf,OAAO,qBAAqB;qBAC/B,OAAO,eAAe;;kBAEzB,OAAO,iBAAiB;kBACxB,OAAO,kBAAkB;;MAErC,WAAW,OAAO;;;;;;;AAOxB,EAAE"}
@@ -38,6 +38,8 @@ interface ConsoleShellProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title'
38
38
  /** Location shown in the top bar, usually a `Breadcrumb`. */
39
39
  breadcrumb?: ReactNode;
40
40
  children?: ReactNode;
41
+ /** Accessible name of the close button in the compact navigation drawer. */
42
+ closeNavigationLabel?: string;
41
43
  /** Collapse the sidebar to an icon rail. Ignored while compact. */
42
44
  collapsed?: boolean;
43
45
  collapseLabel?: string;
@@ -0,0 +1,11 @@
1
+ import { FilterStripProps } from "./type.mjs";
2
+ //#region src/dashboard/FilterStrip/FilterStrip.d.ts
3
+ declare const formatInteger: (count: number) => string;
4
+ /** One surface of selectable counts. Peer filters share a seam instead of a row of equal cards. */
5
+ declare const FilterStrip: {
6
+ ({ emptyText, formatCount, items, label, onChange, value }: FilterStripProps): import("react").JSX.Element;
7
+ displayName: string;
8
+ };
9
+ //#endregion
10
+ export { FilterStrip as default, formatInteger };
11
+ //# sourceMappingURL=FilterStrip.d.mts.map
@@ -0,0 +1,51 @@
1
+ "use client";
2
+ import Skeleton from "../../base-ui/Skeleton/index.mjs";
3
+ import Surface from "../Surface/Surface.mjs";
4
+ import { styles } from "./style.mjs";
5
+ import { jsx, jsxs } from "react/jsx-runtime";
6
+ import { cx } from "antd-style";
7
+ //#region src/dashboard/FilterStrip/FilterStrip.tsx
8
+ const countClass = {
9
+ danger: styles.countDanger,
10
+ warning: styles.countWarning
11
+ };
12
+ const integerFormat = new Intl.NumberFormat("en-US", { maximumFractionDigits: 0 });
13
+ const formatInteger = (count) => integerFormat.format(count);
14
+ /** One surface of selectable counts. Peer filters share a seam instead of a row of equal cards. */
15
+ const FilterStrip = ({ emptyText = "--", formatCount = formatInteger, items, label, onChange, value }) => /* @__PURE__ */ jsx(Surface, { children: /* @__PURE__ */ jsx("div", {
16
+ "aria-label": label,
17
+ className: styles.frame,
18
+ role: "radiogroup",
19
+ children: /* @__PURE__ */ jsx("div", {
20
+ className: styles.grid,
21
+ children: items.map((item) => {
22
+ const active = item.key === value;
23
+ const missing = item.count === void 0 || item.count === null;
24
+ const emphasize = item.tone && typeof item.count === "number" && item.count > 0;
25
+ return /* @__PURE__ */ jsxs("button", {
26
+ "aria-checked": active,
27
+ className: cx(styles.cell, active && styles.cellActive),
28
+ role: "radio",
29
+ title: item.hint,
30
+ type: "button",
31
+ onClick: () => onChange(item.key),
32
+ children: [/* @__PURE__ */ jsx("span", {
33
+ className: cx(styles.label, active && styles.labelActive),
34
+ children: item.label
35
+ }), /* @__PURE__ */ jsx("span", {
36
+ className: cx(styles.count, emphasize && countClass[item.tone]),
37
+ children: item.loading ? /* @__PURE__ */ jsx(Skeleton, {
38
+ height: 22,
39
+ radius: 6,
40
+ width: 48
41
+ }) : missing ? emptyText : formatCount(item.count)
42
+ })]
43
+ }, item.key);
44
+ })
45
+ })
46
+ }) });
47
+ FilterStrip.displayName = "FilterStrip";
48
+ //#endregion
49
+ export { FilterStrip as default, formatInteger };
50
+
51
+ //# sourceMappingURL=FilterStrip.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FilterStrip.mjs","names":[],"sources":["../../../src/dashboard/FilterStrip/FilterStrip.tsx"],"sourcesContent":["'use client';\n\nimport { cx } from 'antd-style';\n\nimport Skeleton from '@/base-ui/Skeleton';\n\nimport Surface from '../Surface/Surface';\nimport { styles } from './style';\nimport type { FilterStripProps } from './type';\n\nconst countClass = {\n danger: styles.countDanger,\n warning: styles.countWarning,\n} as const;\n\nconst integerFormat = new Intl.NumberFormat('en-US', { maximumFractionDigits: 0 });\n\nexport const formatInteger = (count: number) => integerFormat.format(count);\n\n/** One surface of selectable counts. Peer filters share a seam instead of a row of equal cards. */\nconst FilterStrip = ({\n emptyText = '--',\n formatCount = formatInteger,\n items,\n label,\n onChange,\n value,\n}: FilterStripProps) => (\n <Surface>\n <div aria-label={label} className={styles.frame} role=\"radiogroup\">\n <div className={styles.grid}>\n {items.map((item) => {\n const active = item.key === value;\n const missing = item.count === undefined || item.count === null;\n const emphasize = item.tone && typeof item.count === 'number' && item.count > 0;\n\n return (\n <button\n aria-checked={active}\n className={cx(styles.cell, active && styles.cellActive)}\n key={item.key}\n role=\"radio\"\n title={item.hint}\n type=\"button\"\n onClick={() => onChange(item.key)}\n >\n <span className={cx(styles.label, active && styles.labelActive)}>{item.label}</span>\n <span className={cx(styles.count, emphasize && countClass[item.tone!])}>\n {item.loading ? (\n <Skeleton height={22} radius={6} width={48} />\n ) : missing ? (\n emptyText\n ) : (\n formatCount(item.count!)\n )}\n </span>\n </button>\n );\n })}\n </div>\n </div>\n </Surface>\n);\n\nFilterStrip.displayName = 'FilterStrip';\n\nexport default FilterStrip;\n"],"mappings":";;;;;;;AAUA,MAAM,aAAa;CACjB,QAAQ,OAAO;CACf,SAAS,OAAO;AAClB;AAEA,MAAM,gBAAgB,IAAI,KAAK,aAAa,SAAS,EAAE,uBAAuB,EAAE,CAAC;AAEjF,MAAa,iBAAiB,UAAkB,cAAc,OAAO,KAAK;;AAG1E,MAAM,eAAe,EACnB,YAAY,MACZ,cAAc,eACd,OACA,OACA,UACA,YAEA,oBAAC,SAAD,EAAA,UACE,oBAAC,OAAD;CAAK,cAAY;CAAO,WAAW,OAAO;CAAO,MAAK;CACpD,UAAA,oBAAC,OAAD;EAAK,WAAW,OAAO;EACpB,UAAA,MAAM,KAAK,SAAS;GACnB,MAAM,SAAS,KAAK,QAAQ;GAC5B,MAAM,UAAU,KAAK,UAAU,KAAA,KAAa,KAAK,UAAU;GAC3D,MAAM,YAAY,KAAK,QAAQ,OAAO,KAAK,UAAU,YAAY,KAAK,QAAQ;GAE9E,OACE,qBAAC,UAAD;IACE,gBAAc;IACd,WAAW,GAAG,OAAO,MAAM,UAAU,OAAO,UAAU;IAEtD,MAAK;IACL,OAAO,KAAK;IACZ,MAAK;IACL,eAAe,SAAS,KAAK,GAAG;IAPlC,UAAA,CASE,oBAAC,QAAD;KAAM,WAAW,GAAG,OAAO,OAAO,UAAU,OAAO,WAAW;KAAI,UAAA,KAAK;IAAY,CAAA,GACnF,oBAAC,QAAD;KAAM,WAAW,GAAG,OAAO,OAAO,aAAa,WAAW,KAAK,KAAM;KAClE,UAAA,KAAK,UACJ,oBAAC,UAAD;MAAU,QAAQ;MAAI,QAAQ;MAAG,OAAO;KAAK,CAAA,IAC3C,UACF,YAEA,YAAY,KAAK,KAAM;IAErB,CAAA,CACA;GAhBD,GAAA,KAAK,GAgBJ;EAEZ,CAAC;CACE,CAAA;AACF,CAAA,EACE,CAAA;AAGX,YAAY,cAAc"}
@@ -0,0 +1,3 @@
1
+ import { FilterStripItem, FilterStripProps } from "./type.mjs";
2
+ import FilterStrip, { formatInteger } from "./FilterStrip.mjs";
3
+ export { type FilterStripItem, type FilterStripProps, FilterStrip as default, formatInteger };
@@ -0,0 +1,93 @@
1
+ import { createStaticStyles } from "antd-style";
2
+ //#region src/dashboard/FilterStrip/style.ts
3
+ const styles = createStaticStyles(({ css, cssVar }) => ({
4
+ cell: css`
5
+ cursor: pointer;
6
+
7
+ position: relative;
8
+
9
+ display: flex;
10
+ flex-direction: column;
11
+ gap: 6px;
12
+ align-items: flex-start;
13
+
14
+ min-inline-size: 0;
15
+ margin: 0;
16
+ padding-block: 14px 16px;
17
+ padding-inline: 16px;
18
+ border: 0;
19
+
20
+ font: inherit;
21
+ color: inherit;
22
+ text-align: start;
23
+
24
+ background: transparent;
25
+ box-shadow:
26
+ inset -1px 0 0 var(--filter-seam),
27
+ inset 0 -1px 0 var(--filter-seam);
28
+
29
+ &::after {
30
+ content: '';
31
+
32
+ position: absolute;
33
+ inset-block-end: 1px;
34
+ inset-inline: 0;
35
+
36
+ block-size: 3px;
37
+
38
+ background: transparent;
39
+ }
40
+
41
+ &:hover {
42
+ background: ${cssVar.colorFillQuaternary};
43
+ }
44
+ `,
45
+ cellActive: css`
46
+ background: ${cssVar.colorFillTertiary};
47
+
48
+ &::after {
49
+ background: ${cssVar.colorText};
50
+ }
51
+
52
+ &:hover {
53
+ background: ${cssVar.colorFillTertiary};
54
+ }
55
+ `,
56
+ count: css`
57
+ font-size: ${cssVar.fontSizeHeading4};
58
+ font-weight: 600;
59
+ font-variant-numeric: tabular-nums;
60
+ line-height: 1.15;
61
+ color: ${cssVar.colorText};
62
+ `,
63
+ countDanger: css`
64
+ color: ${cssVar.colorError};
65
+ `,
66
+ countWarning: css`
67
+ color: ${cssVar.colorWarning};
68
+ `,
69
+ frame: css`
70
+ --filter-seam: color-mix(in srgb, ${cssVar.colorText} 10%, transparent);
71
+
72
+ overflow: hidden;
73
+ `,
74
+ grid: css`
75
+ display: grid;
76
+ grid-template-columns: repeat(auto-fit, minmax(148px, 1fr));
77
+ inline-size: calc(100% + 1px);
78
+ margin-block-end: -1px;
79
+ `,
80
+ label: css`
81
+ font-size: 13px;
82
+ font-weight: 500;
83
+ line-height: 1.4;
84
+ color: ${cssVar.colorTextSecondary};
85
+ `,
86
+ labelActive: css`
87
+ color: ${cssVar.colorText};
88
+ `
89
+ }));
90
+ //#endregion
91
+ export { styles };
92
+
93
+ //# sourceMappingURL=style.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"style.mjs","names":[],"sources":["../../../src/dashboard/FilterStrip/style.ts"],"sourcesContent":["import { createStaticStyles } from 'antd-style';\n\nexport const styles = createStaticStyles(({ css, cssVar }) => ({\n cell: css`\n cursor: pointer;\n\n position: relative;\n\n display: flex;\n flex-direction: column;\n gap: 6px;\n align-items: flex-start;\n\n min-inline-size: 0;\n margin: 0;\n padding-block: 14px 16px;\n padding-inline: 16px;\n border: 0;\n\n font: inherit;\n color: inherit;\n text-align: start;\n\n background: transparent;\n box-shadow:\n inset -1px 0 0 var(--filter-seam),\n inset 0 -1px 0 var(--filter-seam);\n\n &::after {\n content: '';\n\n position: absolute;\n inset-block-end: 1px;\n inset-inline: 0;\n\n block-size: 3px;\n\n background: transparent;\n }\n\n &:hover {\n background: ${cssVar.colorFillQuaternary};\n }\n `,\n cellActive: css`\n background: ${cssVar.colorFillTertiary};\n\n &::after {\n background: ${cssVar.colorText};\n }\n\n &:hover {\n background: ${cssVar.colorFillTertiary};\n }\n `,\n count: css`\n font-size: ${cssVar.fontSizeHeading4};\n font-weight: 600;\n font-variant-numeric: tabular-nums;\n line-height: 1.15;\n color: ${cssVar.colorText};\n `,\n countDanger: css`\n color: ${cssVar.colorError};\n `,\n countWarning: css`\n color: ${cssVar.colorWarning};\n `,\n frame: css`\n --filter-seam: color-mix(in srgb, ${cssVar.colorText} 10%, transparent);\n\n overflow: hidden;\n `,\n grid: css`\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(148px, 1fr));\n inline-size: calc(100% + 1px);\n margin-block-end: -1px;\n `,\n label: css`\n font-size: 13px;\n font-weight: 500;\n line-height: 1.4;\n color: ${cssVar.colorTextSecondary};\n `,\n labelActive: css`\n color: ${cssVar.colorText};\n `,\n}));\n"],"mappings":";;AAEA,MAAa,SAAS,oBAAoB,EAAE,KAAK,cAAc;CAC7D,MAAM,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;oBAsCS,OAAO,oBAAoB;;;CAG7C,YAAY,GAAG;kBACC,OAAO,kBAAkB;;;oBAGvB,OAAO,UAAU;;;;oBAIjB,OAAO,kBAAkB;;;CAG3C,OAAO,GAAG;iBACK,OAAO,iBAAiB;;;;aAI5B,OAAO,UAAU;;CAE5B,aAAa,GAAG;aACL,OAAO,WAAW;;CAE7B,cAAc,GAAG;aACN,OAAO,aAAa;;CAE/B,OAAO,GAAG;wCAC4B,OAAO,UAAU;;;;CAIvD,MAAM,GAAG;;;;;;CAMT,OAAO,GAAG;;;;aAIC,OAAO,mBAAmB;;CAErC,aAAa,GAAG;aACL,OAAO,UAAU;;AAE9B,EAAE"}
@@ -0,0 +1,26 @@
1
+ import { ReactNode } from "react";
2
+ //#region src/dashboard/FilterStrip/type.d.ts
3
+ interface FilterStripItem {
4
+ count?: number;
5
+ /** Shown as the control's title. Definitions stay off the number so the row can be scanned. */
6
+ hint?: string;
7
+ key: string;
8
+ label: ReactNode;
9
+ loading?: boolean;
10
+ /** Colors the count only when it is greater than zero; a zero is not an incident. */
11
+ tone?: 'danger' | 'warning';
12
+ }
13
+ interface FilterStripProps {
14
+ /** Shown when an item has no count. Defaults to `--`. */
15
+ emptyText?: ReactNode;
16
+ /** Formats a count. Defaults to a grouped integer, e.g. `12,345`. */
17
+ formatCount?: (count: number) => ReactNode;
18
+ items: FilterStripItem[];
19
+ /** Accessible name for the radio group. */
20
+ label: string;
21
+ onChange: (key: string) => void;
22
+ value: string;
23
+ }
24
+ //#endregion
25
+ export { FilterStripItem, FilterStripProps };
26
+ //# sourceMappingURL=type.d.mts.map
@@ -0,0 +1,10 @@
1
+ import { LabelWithKeyProps } from "./type.mjs";
2
+ //#region src/dashboard/LabelWithKey/LabelWithKey.d.ts
3
+ /** A human label followed by its machine key as a tag. */
4
+ declare const LabelWithKey: {
5
+ ({ tag, title, ...rest }: LabelWithKeyProps): import("react").JSX.Element;
6
+ displayName: string;
7
+ };
8
+ //#endregion
9
+ export { LabelWithKey as default };
10
+ //# sourceMappingURL=LabelWithKey.d.mts.map
@@ -0,0 +1,18 @@
1
+ "use client";
2
+ import FlexBasic_default from "../../Flex/FlexBasic.mjs";
3
+ import Tag from "../../base-ui/Tag/Tag.mjs";
4
+ import { jsx, jsxs } from "react/jsx-runtime";
5
+ //#region src/dashboard/LabelWithKey/LabelWithKey.tsx
6
+ /** A human label followed by its machine key as a tag. */
7
+ const LabelWithKey = ({ tag, title, ...rest }) => /* @__PURE__ */ jsxs(FlexBasic_default, {
8
+ horizontal: true,
9
+ align: "center",
10
+ gap: 8,
11
+ ...rest,
12
+ children: [title, /* @__PURE__ */ jsx(Tag, { children: tag })]
13
+ });
14
+ LabelWithKey.displayName = "LabelWithKey";
15
+ //#endregion
16
+ export { LabelWithKey as default };
17
+
18
+ //# sourceMappingURL=LabelWithKey.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LabelWithKey.mjs","names":["Flexbox"],"sources":["../../../src/dashboard/LabelWithKey/LabelWithKey.tsx"],"sourcesContent":["'use client';\n\nimport Tag from '@/base-ui/Tag';\nimport { Flexbox } from '@/Flex';\n\nimport type { LabelWithKeyProps } from './type';\n\n/** A human label followed by its machine key as a tag. */\nconst LabelWithKey = ({ tag, title, ...rest }: LabelWithKeyProps) => (\n <Flexbox horizontal align={'center'} gap={8} {...rest}>\n {title}\n <Tag>{tag}</Tag>\n </Flexbox>\n);\n\nLabelWithKey.displayName = 'LabelWithKey';\n\nexport default LabelWithKey;\n"],"mappings":";;;;;;AAQA,MAAM,gBAAgB,EAAE,KAAK,OAAO,GAAG,WACrC,qBAACA,mBAAD;CAAS,YAAA;CAAW,OAAO;CAAU,KAAK;CAAG,GAAI;CAAjD,UAAA,CACG,OACD,oBAAC,KAAD,EAAA,UAAM,IAAS,CAAA,CACR;;AAGX,aAAa,cAAc"}
@@ -0,0 +1,3 @@
1
+ import { LabelWithKeyProps } from "./type.mjs";
2
+ import LabelWithKey from "./LabelWithKey.mjs";
3
+ export { type LabelWithKeyProps, LabelWithKey as default };
@@ -0,0 +1,12 @@
1
+ import { FlexboxProps } from "../../Flex/type.mjs";
2
+ import "../../Flex/index.mjs";
3
+ import { ReactNode } from "react";
4
+ //#region src/dashboard/LabelWithKey/type.d.ts
5
+ interface LabelWithKeyProps extends Omit<FlexboxProps, 'title'> {
6
+ /** The machine key shown as a tag after the label, e.g. a config or permission id. */
7
+ tag: ReactNode;
8
+ title: ReactNode;
9
+ }
10
+ //#endregion
11
+ export { LabelWithKeyProps };
12
+ //# sourceMappingURL=type.d.mts.map
@@ -0,0 +1,10 @@
1
+ import { MetricStripProps } from "./type.mjs";
2
+ //#region src/dashboard/MetricStrip/MetricStrip.d.ts
3
+ /** One card, divided into metrics. Peer numbers share a surface instead of a row of equal cards. */
4
+ declare const MetricStrip: {
5
+ ({ items, loading, renderLink }: MetricStripProps): import("react").JSX.Element;
6
+ displayName: string;
7
+ };
8
+ //#endregion
9
+ export { MetricStrip as default };
10
+ //# sourceMappingURL=MetricStrip.d.mts.map
@@ -0,0 +1,76 @@
1
+ "use client";
2
+ import Skeleton from "../../base-ui/Skeleton/index.mjs";
3
+ import { useSurfaceLink } from "../Surface/link.mjs";
4
+ import Surface from "../Surface/Surface.mjs";
5
+ import { styles } from "./style.mjs";
6
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
7
+ import { cx } from "antd-style";
8
+ //#region src/dashboard/MetricStrip/MetricStrip.tsx
9
+ const deltaClass = {
10
+ down: styles.deltaDown,
11
+ flat: styles.deltaFlat,
12
+ up: styles.deltaUp
13
+ };
14
+ const MetricBody = ({ item, loading }) => /* @__PURE__ */ jsxs(Fragment, { children: [
15
+ /* @__PURE__ */ jsx("div", {
16
+ className: styles.label,
17
+ children: item.label
18
+ }),
19
+ loading ? /* @__PURE__ */ jsx(Skeleton, {
20
+ height: 28,
21
+ radius: 6,
22
+ width: 88
23
+ }) : /* @__PURE__ */ jsx("div", {
24
+ className: cx(styles.value, item.valueTone === "danger" && styles.valueDanger, item.valueTone === "warning" && styles.valueWarning),
25
+ children: item.value
26
+ }),
27
+ !loading && (item.delta || item.hint) ? /* @__PURE__ */ jsxs("div", {
28
+ className: styles.foot,
29
+ children: [item.delta ? /* @__PURE__ */ jsx("span", {
30
+ className: cx(styles.delta, deltaClass[item.direction ?? "flat"]),
31
+ children: item.delta
32
+ }) : null, item.hint ? /* @__PURE__ */ jsx("span", {
33
+ className: styles.hint,
34
+ children: item.hint
35
+ }) : null]
36
+ }) : null
37
+ ] });
38
+ /** One card, divided into metrics. Peer numbers share a surface instead of a row of equal cards. */
39
+ const MetricStrip = ({ items, loading, renderLink }) => {
40
+ const link = useSurfaceLink(renderLink);
41
+ return /* @__PURE__ */ jsx(Surface, { children: /* @__PURE__ */ jsx("div", {
42
+ className: styles.frame,
43
+ children: /* @__PURE__ */ jsx("div", {
44
+ className: styles.grid,
45
+ children: items.map((item, index) => {
46
+ const body = /* @__PURE__ */ jsx(MetricBody, {
47
+ item,
48
+ loading: item.loading ?? loading
49
+ });
50
+ if (item.href) return /* @__PURE__ */ jsx("div", {
51
+ className: styles.slot,
52
+ children: link({
53
+ children: body,
54
+ className: cx(styles.cell, styles.link),
55
+ href: item.href
56
+ })
57
+ }, `${index}:${item.href}`);
58
+ if (item.onClick) return /* @__PURE__ */ jsx("button", {
59
+ className: cx(styles.cell, styles.press),
60
+ type: "button",
61
+ onClick: item.onClick,
62
+ children: body
63
+ }, index);
64
+ return /* @__PURE__ */ jsx("div", {
65
+ className: styles.cell,
66
+ children: body
67
+ }, index);
68
+ })
69
+ })
70
+ }) });
71
+ };
72
+ MetricStrip.displayName = "MetricStrip";
73
+ //#endregion
74
+ export { MetricStrip as default };
75
+
76
+ //# sourceMappingURL=MetricStrip.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MetricStrip.mjs","names":[],"sources":["../../../src/dashboard/MetricStrip/MetricStrip.tsx"],"sourcesContent":["'use client';\n\nimport { cx } from 'antd-style';\n\nimport Skeleton from '@/base-ui/Skeleton';\n\nimport { useSurfaceLink } from '../Surface/link';\nimport Surface from '../Surface/Surface';\nimport { styles } from './style';\nimport type { MetricItem, MetricStripProps } from './type';\n\nconst deltaClass = {\n down: styles.deltaDown,\n flat: styles.deltaFlat,\n up: styles.deltaUp,\n} as const;\n\nconst MetricBody = ({ item, loading }: { item: MetricItem; loading?: boolean }) => (\n <>\n <div className={styles.label}>{item.label}</div>\n {loading ? (\n <Skeleton height={28} radius={6} width={88} />\n ) : (\n <div\n className={cx(\n styles.value,\n item.valueTone === 'danger' && styles.valueDanger,\n item.valueTone === 'warning' && styles.valueWarning,\n )}\n >\n {item.value}\n </div>\n )}\n {!loading && (item.delta || item.hint) ? (\n <div className={styles.foot}>\n {item.delta ? (\n <span className={cx(styles.delta, deltaClass[item.direction ?? 'flat'])}>\n {item.delta}\n </span>\n ) : null}\n {item.hint ? <span className={styles.hint}>{item.hint}</span> : null}\n </div>\n ) : null}\n </>\n);\n\n/** One card, divided into metrics. Peer numbers share a surface instead of a row of equal cards. */\nconst MetricStrip = ({ items, loading, renderLink }: MetricStripProps) => {\n const link = useSurfaceLink(renderLink);\n\n return (\n <Surface>\n <div className={styles.frame}>\n <div className={styles.grid}>\n {items.map((item, index) => {\n const body = <MetricBody item={item} loading={item.loading ?? loading} />;\n if (item.href) {\n return (\n <div className={styles.slot} key={`${index}:${item.href}`}>\n {link({\n children: body,\n className: cx(styles.cell, styles.link),\n href: item.href,\n })}\n </div>\n );\n }\n if (item.onClick) {\n return (\n <button\n className={cx(styles.cell, styles.press)}\n key={index}\n type=\"button\"\n onClick={item.onClick}\n >\n {body}\n </button>\n );\n }\n return (\n <div className={styles.cell} key={index}>\n {body}\n </div>\n );\n })}\n </div>\n </div>\n </Surface>\n );\n};\n\nMetricStrip.displayName = 'MetricStrip';\n\nexport default MetricStrip;\n"],"mappings":";;;;;;;;AAWA,MAAM,aAAa;CACjB,MAAM,OAAO;CACb,MAAM,OAAO;CACb,IAAI,OAAO;AACb;AAEA,MAAM,cAAc,EAAE,MAAM,cAC1B,qBAAA,UAAA,EAAA,UAAA;CACE,oBAAC,OAAD;EAAK,WAAW,OAAO;EAAQ,UAAA,KAAK;CAAW,CAAA;CAC9C,UACC,oBAAC,UAAD;EAAU,QAAQ;EAAI,QAAQ;EAAG,OAAO;CAAK,CAAA,IAE7C,oBAAC,OAAD;EACE,WAAW,GACT,OAAO,OACP,KAAK,cAAc,YAAY,OAAO,aACtC,KAAK,cAAc,aAAa,OAAO,YACzC;EAEC,UAAA,KAAK;CACH,CAAA;CAEN,CAAC,YAAY,KAAK,SAAS,KAAK,QAC/B,qBAAC,OAAD;EAAK,WAAW,OAAO;EAAvB,UAAA,CACG,KAAK,QACJ,oBAAC,QAAD;GAAM,WAAW,GAAG,OAAO,OAAO,WAAW,KAAK,aAAa,OAAO;GACnE,UAAA,KAAK;EACF,CAAA,IACJ,MACH,KAAK,OAAO,oBAAC,QAAD;GAAM,WAAW,OAAO;GAAO,UAAA,KAAK;EAAW,CAAA,IAAI,IAC7D;CACH,CAAA,IAAA;AACJ,EAAA,CAAA;;AAIJ,MAAM,eAAe,EAAE,OAAO,SAAS,iBAAmC;CACxE,MAAM,OAAO,eAAe,UAAU;CAEtC,OACE,oBAAC,SAAD,EAAA,UACE,oBAAC,OAAD;EAAK,WAAW,OAAO;EACrB,UAAA,oBAAC,OAAD;GAAK,WAAW,OAAO;GACpB,UAAA,MAAM,KAAK,MAAM,UAAU;IAC1B,MAAM,OAAO,oBAAC,YAAD;KAAkB;KAAM,SAAS,KAAK,WAAW;IAAU,CAAA;IACxE,IAAI,KAAK,MACP,OACE,oBAAC,OAAD;KAAK,WAAW,OAAO;KACpB,UAAA,KAAK;MACJ,UAAU;MACV,WAAW,GAAG,OAAO,MAAM,OAAO,IAAI;MACtC,MAAM,KAAK;KACb,CAAC;IACE,GAN6B,GAAG,MAAM,GAAG,KAAK,MAM9C;IAGT,IAAI,KAAK,SACP,OACE,oBAAC,UAAD;KACE,WAAW,GAAG,OAAO,MAAM,OAAO,KAAK;KAEvC,MAAK;KACL,SAAS,KAAK;KAEb,UAAA;IACK,GALD,KAKC;IAGZ,OACE,oBAAC,OAAD;KAAK,WAAW,OAAO;KACpB,UAAA;IACE,GAF6B,KAE7B;GAET,CAAC;EACE,CAAA;CACF,CAAA,EACE,CAAA;AAEb;AAEA,YAAY,cAAc"}
@@ -0,0 +1,3 @@
1
+ import { MetricItem, MetricStripProps } from "./type.mjs";
2
+ import MetricStrip from "./MetricStrip.mjs";
3
+ export { type MetricItem, type MetricStripProps, MetricStrip as default };
@@ -0,0 +1,116 @@
1
+ import { createStaticStyles } from "antd-style";
2
+ //#region src/dashboard/MetricStrip/style.ts
3
+ const styles = createStaticStyles(({ css, cssVar }) => ({
4
+ cell: css`
5
+ display: flex;
6
+ flex-direction: column;
7
+ gap: 6px;
8
+
9
+ min-inline-size: 0;
10
+ padding-block: 16px;
11
+ padding-inline: 20px;
12
+
13
+ background: ${cssVar.colorBgElevated};
14
+ box-shadow:
15
+ inset -1px 0 0 var(--metric-seam),
16
+ inset 0 -1px 0 var(--metric-seam);
17
+ `,
18
+ delta: css`
19
+ font-size: 13px;
20
+ font-variant-numeric: tabular-nums;
21
+ line-height: 1.4;
22
+ `,
23
+ deltaDown: css`
24
+ color: ${cssVar.colorError};
25
+ `,
26
+ deltaFlat: css`
27
+ color: ${cssVar.colorTextSecondary};
28
+ `,
29
+ deltaUp: css`
30
+ color: ${cssVar.colorSuccess};
31
+ `,
32
+ foot: css`
33
+ display: flex;
34
+ flex-wrap: wrap;
35
+ gap: 8px;
36
+ align-items: baseline;
37
+ `,
38
+ /**
39
+ * Clips the seam that would otherwise sit on the card's trailing and bottom
40
+ * edges. A short last row used to show the seam color across the empty
41
+ * track, which reads louder than the cells.
42
+ */
43
+ frame: css`
44
+ --metric-seam: color-mix(in srgb, ${cssVar.colorText} 10%, transparent);
45
+
46
+ overflow: hidden;
47
+ background: ${cssVar.colorBgElevated};
48
+ `,
49
+ grid: css`
50
+ display: grid;
51
+ grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
52
+
53
+ inline-size: calc(100% + 1px);
54
+ margin-block-end: -1px;
55
+
56
+ background: ${cssVar.colorBgElevated};
57
+ `,
58
+ hint: css`
59
+ font-size: 13px;
60
+ line-height: 1.4;
61
+ color: ${cssVar.colorTextSecondary};
62
+ `,
63
+ label: css`
64
+ font-size: 13px;
65
+ font-weight: 500;
66
+ line-height: 1.4;
67
+ color: ${cssVar.colorTextSecondary};
68
+ `,
69
+ link: css`
70
+ color: inherit;
71
+ text-decoration: none;
72
+
73
+ &:hover {
74
+ background: ${cssVar.colorFillQuaternary};
75
+ }
76
+ `,
77
+ press: css`
78
+ cursor: pointer;
79
+
80
+ border: none;
81
+
82
+ font: inherit;
83
+ color: inherit;
84
+ text-align: start;
85
+
86
+ &:hover {
87
+ background: ${cssVar.colorFillQuaternary};
88
+ }
89
+ `,
90
+ /** Grid cell around a rendered link, so the link fills it. */
91
+ slot: css`
92
+ display: grid;
93
+ min-inline-size: 0;
94
+ `,
95
+ value: css`
96
+ overflow: hidden;
97
+
98
+ font-size: ${cssVar.fontSizeHeading3};
99
+ font-weight: 600;
100
+ font-variant-numeric: tabular-nums;
101
+ line-height: 1.15;
102
+ color: ${cssVar.colorText};
103
+ text-overflow: ellipsis;
104
+ white-space: nowrap;
105
+ `,
106
+ valueDanger: css`
107
+ color: ${cssVar.colorError};
108
+ `,
109
+ valueWarning: css`
110
+ color: ${cssVar.colorWarning};
111
+ `
112
+ }));
113
+ //#endregion
114
+ export { styles };
115
+
116
+ //# sourceMappingURL=style.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"style.mjs","names":[],"sources":["../../../src/dashboard/MetricStrip/style.ts"],"sourcesContent":["import { createStaticStyles } from 'antd-style';\n\nexport const styles = createStaticStyles(({ css, cssVar }) => ({\n cell: css`\n display: flex;\n flex-direction: column;\n gap: 6px;\n\n min-inline-size: 0;\n padding-block: 16px;\n padding-inline: 20px;\n\n background: ${cssVar.colorBgElevated};\n box-shadow:\n inset -1px 0 0 var(--metric-seam),\n inset 0 -1px 0 var(--metric-seam);\n `,\n delta: css`\n font-size: 13px;\n font-variant-numeric: tabular-nums;\n line-height: 1.4;\n `,\n deltaDown: css`\n color: ${cssVar.colorError};\n `,\n deltaFlat: css`\n color: ${cssVar.colorTextSecondary};\n `,\n deltaUp: css`\n color: ${cssVar.colorSuccess};\n `,\n foot: css`\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n align-items: baseline;\n `,\n /**\n * Clips the seam that would otherwise sit on the card's trailing and bottom\n * edges. A short last row used to show the seam color across the empty\n * track, which reads louder than the cells.\n */\n frame: css`\n --metric-seam: color-mix(in srgb, ${cssVar.colorText} 10%, transparent);\n\n overflow: hidden;\n background: ${cssVar.colorBgElevated};\n `,\n grid: css`\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));\n\n inline-size: calc(100% + 1px);\n margin-block-end: -1px;\n\n background: ${cssVar.colorBgElevated};\n `,\n hint: css`\n font-size: 13px;\n line-height: 1.4;\n color: ${cssVar.colorTextSecondary};\n `,\n label: css`\n font-size: 13px;\n font-weight: 500;\n line-height: 1.4;\n color: ${cssVar.colorTextSecondary};\n `,\n link: css`\n color: inherit;\n text-decoration: none;\n\n &:hover {\n background: ${cssVar.colorFillQuaternary};\n }\n `,\n press: css`\n cursor: pointer;\n\n border: none;\n\n font: inherit;\n color: inherit;\n text-align: start;\n\n &:hover {\n background: ${cssVar.colorFillQuaternary};\n }\n `,\n /** Grid cell around a rendered link, so the link fills it. */\n slot: css`\n display: grid;\n min-inline-size: 0;\n `,\n value: css`\n overflow: hidden;\n\n font-size: ${cssVar.fontSizeHeading3};\n font-weight: 600;\n font-variant-numeric: tabular-nums;\n line-height: 1.15;\n color: ${cssVar.colorText};\n text-overflow: ellipsis;\n white-space: nowrap;\n `,\n valueDanger: css`\n color: ${cssVar.colorError};\n `,\n valueWarning: css`\n color: ${cssVar.colorWarning};\n `,\n}));\n"],"mappings":";;AAEA,MAAa,SAAS,oBAAoB,EAAE,KAAK,cAAc;CAC7D,MAAM,GAAG;;;;;;;;;kBASO,OAAO,gBAAgB;;;;;CAKvC,OAAO,GAAG;;;;;CAKV,WAAW,GAAG;aACH,OAAO,WAAW;;CAE7B,WAAW,GAAG;aACH,OAAO,mBAAmB;;CAErC,SAAS,GAAG;aACD,OAAO,aAAa;;CAE/B,MAAM,GAAG;;;;;;;;;;;CAWT,OAAO,GAAG;wCAC4B,OAAO,UAAU;;;kBAGvC,OAAO,gBAAgB;;CAEvC,MAAM,GAAG;;;;;;;kBAOO,OAAO,gBAAgB;;CAEvC,MAAM,GAAG;;;aAGE,OAAO,mBAAmB;;CAErC,OAAO,GAAG;;;;aAIC,OAAO,mBAAmB;;CAErC,MAAM,GAAG;;;;;oBAKS,OAAO,oBAAoB;;;CAG7C,OAAO,GAAG;;;;;;;;;;oBAUQ,OAAO,oBAAoB;;;;CAI7C,MAAM,GAAG;;;;CAIT,OAAO,GAAG;;;iBAGK,OAAO,iBAAiB;;;;aAI5B,OAAO,UAAU;;;;CAI5B,aAAa,GAAG;aACL,OAAO,WAAW;;CAE7B,cAAc,GAAG;aACN,OAAO,aAAa;;AAEjC,EAAE"}
@@ -0,0 +1,25 @@
1
+ import { SurfaceRenderLink } from "../Surface/type.mjs";
2
+ import { ReactNode } from "react";
3
+ //#region src/dashboard/MetricStrip/type.d.ts
4
+ interface MetricItem {
5
+ delta?: ReactNode;
6
+ direction?: 'up' | 'down' | 'flat';
7
+ hint?: ReactNode;
8
+ href?: string;
9
+ label: ReactNode;
10
+ /** This cell only. Cells fed by different queries arrive at different times. */
11
+ loading?: boolean;
12
+ onClick?: () => void;
13
+ value: ReactNode;
14
+ /** Color the figure when the number itself is the thing to act on. */
15
+ valueTone?: 'danger' | 'warning';
16
+ }
17
+ interface MetricStripProps {
18
+ items: MetricItem[];
19
+ /** First load only: values become placeholders, labels stay. */
20
+ loading?: boolean;
21
+ renderLink?: SurfaceRenderLink;
22
+ }
23
+ //#endregion
24
+ export { MetricItem, MetricStripProps };
25
+ //# sourceMappingURL=type.d.mts.map
@@ -0,0 +1,10 @@
1
+ import { PageToolbarProps } from "./type.mjs";
2
+ //#region src/dashboard/PageToolbar/PageToolbar.d.ts
3
+ /** Filters start at the left edge; actions hold the right edge, also after wrapping. */
4
+ declare const PageToolbar: {
5
+ ({ actions, children, className }: PageToolbarProps): import("react").JSX.Element;
6
+ displayName: string;
7
+ };
8
+ //#endregion
9
+ export { PageToolbar as default };
10
+ //# sourceMappingURL=PageToolbar.d.mts.map
@@ -0,0 +1,21 @@
1
+ "use client";
2
+ import { styles } from "./style.mjs";
3
+ import { jsx, jsxs } from "react/jsx-runtime";
4
+ import { cx } from "antd-style";
5
+ //#region src/dashboard/PageToolbar/PageToolbar.tsx
6
+ /** Filters start at the left edge; actions hold the right edge, also after wrapping. */
7
+ const PageToolbar = ({ actions, children, className }) => /* @__PURE__ */ jsxs("div", {
8
+ className: cx(styles.root, className),
9
+ children: [children ? /* @__PURE__ */ jsx("div", {
10
+ className: styles.filters,
11
+ children
12
+ }) : null, actions ? /* @__PURE__ */ jsx("div", {
13
+ className: styles.actions,
14
+ children: actions
15
+ }) : null]
16
+ });
17
+ PageToolbar.displayName = "PageToolbar";
18
+ //#endregion
19
+ export { PageToolbar as default };
20
+
21
+ //# sourceMappingURL=PageToolbar.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PageToolbar.mjs","names":[],"sources":["../../../src/dashboard/PageToolbar/PageToolbar.tsx"],"sourcesContent":["'use client';\n\nimport { cx } from 'antd-style';\n\nimport { styles } from './style';\nimport type { PageToolbarProps } from './type';\n\n/** Filters start at the left edge; actions hold the right edge, also after wrapping. */\nconst PageToolbar = ({ actions, children, className }: PageToolbarProps) => (\n <div className={cx(styles.root, className)}>\n {children ? <div className={styles.filters}>{children}</div> : null}\n {actions ? <div className={styles.actions}>{actions}</div> : null}\n </div>\n);\n\nPageToolbar.displayName = 'PageToolbar';\n\nexport default PageToolbar;\n"],"mappings":";;;;;;AAQA,MAAM,eAAe,EAAE,SAAS,UAAU,gBACxC,qBAAC,OAAD;CAAK,WAAW,GAAG,OAAO,MAAM,SAAS;CAAzC,UAAA,CACG,WAAW,oBAAC,OAAD;EAAK,WAAW,OAAO;EAAU;CAAc,CAAA,IAAI,MAC9D,UAAU,oBAAC,OAAD;EAAK,WAAW,OAAO;EAAU,UAAA;CAAa,CAAA,IAAI,IAC1D;;AAGP,YAAY,cAAc"}
@@ -0,0 +1,3 @@
1
+ import { PageToolbarProps } from "./type.mjs";
2
+ import PageToolbar from "./PageToolbar.mjs";
3
+ export { type PageToolbarProps, PageToolbar as default };
@@ -0,0 +1,34 @@
1
+ import { createStaticStyles } from "antd-style";
2
+ //#region src/dashboard/PageToolbar/style.ts
3
+ const styles = createStaticStyles(({ css }) => ({
4
+ actions: css`
5
+ display: flex;
6
+ flex: none;
7
+ flex-wrap: wrap;
8
+ gap: 8px;
9
+ align-items: center;
10
+
11
+ margin-inline-start: auto;
12
+ `,
13
+ filters: css`
14
+ display: flex;
15
+ flex: 0 1 auto;
16
+ flex-wrap: wrap;
17
+ gap: 8px;
18
+ align-items: center;
19
+
20
+ min-inline-size: 0;
21
+ `,
22
+ root: css`
23
+ display: flex;
24
+ flex-wrap: wrap;
25
+ gap: 8px 12px;
26
+ align-items: center;
27
+
28
+ min-inline-size: 0;
29
+ `
30
+ }));
31
+ //#endregion
32
+ export { styles };
33
+
34
+ //# sourceMappingURL=style.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"style.mjs","names":[],"sources":["../../../src/dashboard/PageToolbar/style.ts"],"sourcesContent":["import { createStaticStyles } from 'antd-style';\n\nexport const styles = createStaticStyles(({ css }) => ({\n actions: css`\n display: flex;\n flex: none;\n flex-wrap: wrap;\n gap: 8px;\n align-items: center;\n\n margin-inline-start: auto;\n `,\n filters: css`\n display: flex;\n flex: 0 1 auto;\n flex-wrap: wrap;\n gap: 8px;\n align-items: center;\n\n min-inline-size: 0;\n `,\n root: css`\n display: flex;\n flex-wrap: wrap;\n gap: 8px 12px;\n align-items: center;\n\n min-inline-size: 0;\n `,\n}));\n"],"mappings":";;AAEA,MAAa,SAAS,oBAAoB,EAAE,WAAW;CACrD,SAAS,GAAG;;;;;;;;;CASZ,SAAS,GAAG;;;;;;;;;CASZ,MAAM,GAAG;;;;;;;;AAQX,EAAE"}
@@ -0,0 +1,12 @@
1
+ import { ReactNode } from "react";
2
+ //#region src/dashboard/PageToolbar/type.d.ts
3
+ interface PageToolbarProps {
4
+ /** Right side: refresh, export, the page's own buttons. */
5
+ actions?: ReactNode;
6
+ /** Left side: range, scope, search, whatever narrows the page. */
7
+ children?: ReactNode;
8
+ className?: string;
9
+ }
10
+ //#endregion
11
+ export { PageToolbarProps };
12
+ //# sourceMappingURL=type.d.mts.map