@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.
- package/es/Grid/style.mjs +8 -8
- package/es/Grid/style.mjs.map +1 -1
- package/es/Image/type.d.mts +1 -0
- package/es/Image/viewer/ImageViewer.mjs +1 -0
- package/es/Image/viewer/ImageViewer.mjs.map +1 -1
- package/es/Image/viewer/Toolbar.mjs +19 -6
- package/es/Image/viewer/Toolbar.mjs.map +1 -1
- package/es/Image/viewer/ViewerChrome.mjs.map +1 -1
- package/es/base-ui/Input/style.d.mts +1 -1
- package/es/base-ui/Input/style.mjs +8 -2
- package/es/base-ui/Input/style.mjs.map +1 -1
- package/es/dashboard/ChartGrid/ChartGrid.d.mts +10 -0
- package/es/dashboard/ChartGrid/ChartGrid.mjs +18 -0
- package/es/dashboard/ChartGrid/ChartGrid.mjs.map +1 -0
- package/es/dashboard/ChartGrid/index.d.mts +3 -0
- package/es/dashboard/ChartGrid/style.mjs +32 -0
- package/es/dashboard/ChartGrid/style.mjs.map +1 -0
- package/es/dashboard/ChartGrid/type.d.mts +9 -0
- package/es/dashboard/ConsoleShell/ConsoleShell.d.mts +1 -1
- package/es/dashboard/ConsoleShell/ConsoleShell.mjs +12 -4
- package/es/dashboard/ConsoleShell/ConsoleShell.mjs.map +1 -1
- package/es/dashboard/ConsoleShell/style.mjs +15 -0
- package/es/dashboard/ConsoleShell/style.mjs.map +1 -1
- package/es/dashboard/ConsoleShell/type.d.mts +2 -0
- package/es/dashboard/FilterStrip/FilterStrip.d.mts +11 -0
- package/es/dashboard/FilterStrip/FilterStrip.mjs +51 -0
- package/es/dashboard/FilterStrip/FilterStrip.mjs.map +1 -0
- package/es/dashboard/FilterStrip/index.d.mts +3 -0
- package/es/dashboard/FilterStrip/style.mjs +93 -0
- package/es/dashboard/FilterStrip/style.mjs.map +1 -0
- package/es/dashboard/FilterStrip/type.d.mts +26 -0
- package/es/dashboard/LabelWithKey/LabelWithKey.d.mts +10 -0
- package/es/dashboard/LabelWithKey/LabelWithKey.mjs +18 -0
- package/es/dashboard/LabelWithKey/LabelWithKey.mjs.map +1 -0
- package/es/dashboard/LabelWithKey/index.d.mts +3 -0
- package/es/dashboard/LabelWithKey/type.d.mts +12 -0
- package/es/dashboard/MetricStrip/MetricStrip.d.mts +10 -0
- package/es/dashboard/MetricStrip/MetricStrip.mjs +76 -0
- package/es/dashboard/MetricStrip/MetricStrip.mjs.map +1 -0
- package/es/dashboard/MetricStrip/index.d.mts +3 -0
- package/es/dashboard/MetricStrip/style.mjs +116 -0
- package/es/dashboard/MetricStrip/style.mjs.map +1 -0
- package/es/dashboard/MetricStrip/type.d.mts +25 -0
- package/es/dashboard/PageToolbar/PageToolbar.d.mts +10 -0
- package/es/dashboard/PageToolbar/PageToolbar.mjs +21 -0
- package/es/dashboard/PageToolbar/PageToolbar.mjs.map +1 -0
- package/es/dashboard/PageToolbar/index.d.mts +3 -0
- package/es/dashboard/PageToolbar/style.mjs +34 -0
- package/es/dashboard/PageToolbar/style.mjs.map +1 -0
- package/es/dashboard/PageToolbar/type.d.mts +12 -0
- package/es/dashboard/PanelSurface/PanelSurface.d.mts +10 -0
- package/es/dashboard/PanelSurface/PanelSurface.mjs +36 -0
- package/es/dashboard/PanelSurface/PanelSurface.mjs.map +1 -0
- package/es/dashboard/PanelSurface/index.d.mts +3 -0
- package/es/dashboard/PanelSurface/style.mjs +24 -0
- package/es/dashboard/PanelSurface/style.mjs.map +1 -0
- package/es/dashboard/PanelSurface/type.d.mts +18 -0
- package/es/dashboard/Section/Section.d.mts +10 -0
- package/es/dashboard/Section/Section.mjs +81 -0
- package/es/dashboard/Section/Section.mjs.map +1 -0
- package/es/dashboard/Section/index.d.mts +3 -0
- package/es/dashboard/Section/style.mjs +35 -0
- package/es/dashboard/Section/style.mjs.map +1 -0
- package/es/dashboard/Section/type.d.mts +24 -0
- package/es/dashboard/Surface/Surface.d.mts +4 -3
- package/es/dashboard/Surface/Surface.mjs +67 -7
- package/es/dashboard/Surface/Surface.mjs.map +1 -1
- package/es/dashboard/Surface/index.d.mts +3 -2
- package/es/dashboard/Surface/link.d.mts +14 -0
- package/es/dashboard/Surface/link.mjs +22 -0
- package/es/dashboard/Surface/link.mjs.map +1 -0
- package/es/dashboard/Surface/style.d.mts +23 -0
- package/es/dashboard/Surface/style.mjs +283 -2
- package/es/dashboard/Surface/style.mjs.map +1 -1
- package/es/dashboard/Surface/type.d.mts +30 -2
- package/es/dashboard/TableIconActions/TableIconActions.d.mts +17 -0
- package/es/dashboard/TableIconActions/TableIconActions.mjs +39 -0
- package/es/dashboard/TableIconActions/TableIconActions.mjs.map +1 -0
- package/es/dashboard/TableIconActions/index.d.mts +3 -0
- package/es/dashboard/TableIconActions/type.d.mts +18 -0
- package/es/dashboard/TitleWithTips/TitleWithTips.d.mts +10 -0
- package/es/dashboard/TitleWithTips/TitleWithTips.mjs +28 -0
- package/es/dashboard/TitleWithTips/TitleWithTips.mjs.map +1 -0
- package/es/dashboard/TitleWithTips/index.d.mts +3 -0
- package/es/dashboard/TitleWithTips/type.d.mts +11 -0
- package/es/dashboard/index.d.mts +30 -2
- package/es/dashboard/index.mjs +12 -2
- 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;;;;
|
|
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,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,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,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,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
|