@nospt/backstage-plugin-apigee 1.1.0-rc3 → 1.1.0-rc4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/ApiCard.esm.js +58 -53
- package/dist/components/ApiCard.esm.js.map +1 -1
- package/dist/components/ApiCard.module.css.esm.js +2 -2
- package/dist/components/ApiCardGrid.esm.js +12 -32
- package/dist/components/ApiCardGrid.esm.js.map +1 -1
- package/dist/components/ApiTable.esm.js +162 -0
- package/dist/components/ApiTable.esm.js.map +1 -0
- package/dist/components/ApiTable.module.css.esm.js +8 -0
- package/dist/components/ApiTable.module.css.esm.js.map +1 -0
- package/dist/components/ApigeeApiCatalogPage.esm.js +42 -36
- package/dist/components/ApigeeApiCatalogPage.esm.js.map +1 -1
- package/dist/components/CountCards.esm.js +23 -0
- package/dist/components/CountCards.esm.js.map +1 -1
- package/dist/components/FilterToolbar.esm.js +16 -13
- package/dist/components/FilterToolbar.esm.js.map +1 -1
- package/dist/components/FilterToolbar.module.css.esm.js +2 -2
- package/dist/components/apiCardViewModel.esm.js +16 -2
- package/dist/components/apiCardViewModel.esm.js.map +1 -1
- package/dist/components/grouping.esm.js +1 -26
- package/dist/components/grouping.esm.js.map +1 -1
- package/dist/constants.esm.js +3 -2
- package/dist/constants.esm.js.map +1 -1
- package/package.json +2 -2
- package/dist/components/ApiCardGrid.module.css.esm.js +0 -8
- package/dist/components/ApiCardGrid.module.css.esm.js.map +0 -1
|
@@ -1,22 +1,25 @@
|
|
|
1
1
|
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
2
|
-
import { Link
|
|
2
|
+
import { Link } from '@backstage/core-components';
|
|
3
3
|
import { useRouteRef } from '@backstage/frontend-plugin-api';
|
|
4
4
|
import { entityRouteRef } from '@backstage/plugin-catalog-react';
|
|
5
5
|
import { Text } from '@backstage/ui';
|
|
6
|
-
import { RiTeamLine, RiFileTextLine } from '@remixicon/react';
|
|
6
|
+
import { RiGlobalLine, RiLockLine, RiTeamLine, RiBuildingLine, RiFileTextLine } from '@remixicon/react';
|
|
7
7
|
import styles from './ApiCard.module.css.esm.js';
|
|
8
8
|
|
|
9
|
+
const BADGE_KIND_CLASS = {
|
|
10
|
+
ok: styles.badgeOk,
|
|
11
|
+
warning: styles.badgeWarning,
|
|
12
|
+
error: styles.badgeError
|
|
13
|
+
};
|
|
14
|
+
const VISIBILITY_ICON = {
|
|
15
|
+
private: RiLockLine,
|
|
16
|
+
public: RiGlobalLine
|
|
17
|
+
};
|
|
9
18
|
function LifecycleBadge({
|
|
10
19
|
kind,
|
|
11
20
|
label
|
|
12
21
|
}) {
|
|
13
|
-
|
|
14
|
-
return /* @__PURE__ */ jsx(StatusOK, { children: label });
|
|
15
|
-
}
|
|
16
|
-
if (kind === "error") {
|
|
17
|
-
return /* @__PURE__ */ jsx(StatusError, { children: label });
|
|
18
|
-
}
|
|
19
|
-
return /* @__PURE__ */ jsx(StatusWarning, { children: label });
|
|
22
|
+
return /* @__PURE__ */ jsx("span", { className: `${styles.badge} ${BADGE_KIND_CLASS[kind]}`, children: label });
|
|
20
23
|
}
|
|
21
24
|
function ApiCard({ vm, icon: Icon }) {
|
|
22
25
|
const entityRoute = useRouteRef(entityRouteRef);
|
|
@@ -25,59 +28,61 @@ function ApiCard({ vm, icon: Icon }) {
|
|
|
25
28
|
const ariaLabel = `Open ${vm.name} API details`;
|
|
26
29
|
const content = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
27
30
|
/* @__PURE__ */ jsxs("div", { className: styles.header, children: [
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
31
|
+
/* @__PURE__ */ jsxs("div", { className: styles.titleRow, children: [
|
|
32
|
+
Icon ? /* @__PURE__ */ jsx(Icon, { "aria-hidden": true, className: styles.categoryIcon, size: 20 }) : null,
|
|
33
|
+
/* @__PURE__ */ jsx(
|
|
34
|
+
Text,
|
|
35
|
+
{
|
|
36
|
+
variant: "body-medium",
|
|
37
|
+
weight: "bold",
|
|
38
|
+
as: "h3",
|
|
39
|
+
className: styles.name,
|
|
40
|
+
children: vm.name
|
|
41
|
+
}
|
|
42
|
+
)
|
|
43
|
+
] }),
|
|
44
|
+
/* @__PURE__ */ jsxs("div", { className: styles.badges, children: [
|
|
45
|
+
/* @__PURE__ */ jsx(LifecycleBadge, { kind: vm.badge.kind, label: vm.badge.label }),
|
|
46
|
+
vm.version ? /* @__PURE__ */ jsx("span", { className: `${styles.badge} ${styles.badgeVersion}`, children: vm.version }) : null
|
|
47
|
+
] }),
|
|
48
|
+
vm.description ? /* @__PURE__ */ jsx(
|
|
45
49
|
Text,
|
|
46
50
|
{
|
|
47
51
|
variant: "body-small",
|
|
48
52
|
color: "secondary",
|
|
49
|
-
as: "
|
|
50
|
-
className: styles.
|
|
51
|
-
children:
|
|
52
|
-
"v",
|
|
53
|
-
vm.version
|
|
54
|
-
]
|
|
53
|
+
as: "p",
|
|
54
|
+
className: styles.description,
|
|
55
|
+
children: vm.description
|
|
55
56
|
}
|
|
56
57
|
) : null
|
|
57
58
|
] }),
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
className: styles.
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
59
|
+
/* @__PURE__ */ jsxs("div", { className: styles.body, children: [
|
|
60
|
+
/* @__PURE__ */ jsxs("div", { className: styles.leftCol, children: [
|
|
61
|
+
/* @__PURE__ */ jsxs("div", { className: styles.metaItem, children: [
|
|
62
|
+
/* @__PURE__ */ jsx(RiTeamLine, { "aria-hidden": true, size: 16 }),
|
|
63
|
+
/* @__PURE__ */ jsx(Text, { variant: "body-small", color: "secondary", as: "span", children: vm.ownerLabel })
|
|
64
|
+
] }),
|
|
65
|
+
vm.department ? /* @__PURE__ */ jsxs("div", { className: styles.metaItem, children: [
|
|
66
|
+
/* @__PURE__ */ jsx(RiBuildingLine, { "aria-hidden": true, size: 16 }),
|
|
67
|
+
/* @__PURE__ */ jsx(Text, { variant: "body-small", color: "secondary", as: "span", children: vm.department })
|
|
68
|
+
] }) : null,
|
|
69
|
+
vm.visibilities.map((visibility) => {
|
|
70
|
+
const VisibilityIcon = VISIBILITY_ICON[visibility];
|
|
71
|
+
return /* @__PURE__ */ jsxs("div", { className: styles.metaItem, children: [
|
|
72
|
+
/* @__PURE__ */ jsx(VisibilityIcon, { "aria-hidden": true, size: 16 }),
|
|
73
|
+
/* @__PURE__ */ jsx(Text, { variant: "body-small", color: "secondary", as: "span", children: visibility })
|
|
74
|
+
] }, visibility);
|
|
75
|
+
}),
|
|
76
|
+
vm.hasSpec ? /* @__PURE__ */ jsxs("div", { className: styles.metaItem, children: [
|
|
77
|
+
/* @__PURE__ */ jsx(RiFileTextLine, { "aria-hidden": true, size: 16 }),
|
|
78
|
+
/* @__PURE__ */ jsx(Text, { variant: "body-small", color: "secondary", as: "span", children: "OpenAPI spec" })
|
|
79
|
+
] }) : null
|
|
80
|
+
] }),
|
|
81
|
+
/* @__PURE__ */ jsx("div", { className: styles.rightCol })
|
|
82
|
+
] })
|
|
78
83
|
] });
|
|
79
84
|
return /* @__PURE__ */ jsx("div", { className: styles.card, children: href ? /* @__PURE__ */ jsx(Link, { to: href, className: styles.action, "aria-label": ariaLabel, children: content }) : /* @__PURE__ */ jsx("div", { className: styles.action, children: content }) });
|
|
80
85
|
}
|
|
81
86
|
|
|
82
|
-
export { ApiCard };
|
|
87
|
+
export { ApiCard, LifecycleBadge, VISIBILITY_ICON };
|
|
83
88
|
//# sourceMappingURL=ApiCard.esm.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ApiCard.esm.js","sources":["../../src/components/ApiCard.tsx"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\nimport {
|
|
1
|
+
{"version":3,"file":"ApiCard.esm.js","sources":["../../src/components/ApiCard.tsx"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\nimport { Link } from '@backstage/core-components';\nimport { useRouteRef } from '@backstage/frontend-plugin-api';\nimport { entityRouteRef } from '@backstage/plugin-catalog-react';\nimport { Text } from '@backstage/ui';\nimport {\n RiBuildingLine,\n RiFileTextLine,\n RiGlobalLine,\n RiLockLine,\n RiTeamLine,\n type RemixiconComponentType,\n} from '@remixicon/react';\nimport type {\n ApiCardViewModel,\n ApiVisibility,\n LifecycleBadgeKind,\n} from './apiCardViewModel';\nimport styles from './ApiCard.module.css';\n\nexport interface ApiCardProps {\n vm: ApiCardViewModel;\n /** Category icon resolved by the parent grid (05-05); decorative. */\n icon?: RemixiconComponentType;\n}\n\nconst BADGE_KIND_CLASS: Record<LifecycleBadgeKind, string> = {\n ok: styles.badgeOk,\n warning: styles.badgeWarning,\n error: styles.badgeError,\n};\n\nexport const VISIBILITY_ICON: Record<ApiVisibility, RemixiconComponentType> = {\n private: RiLockLine,\n public: RiGlobalLine,\n};\n\n/** Solid colored status pill (D-04). Color conveys state; the text label is the\n * accessible source of truth so it never relies on color alone. */\nexport function LifecycleBadge({\n kind,\n label,\n}: {\n kind: LifecycleBadgeKind;\n label: string;\n}) {\n return (\n <span className={`${styles.badge} ${BADGE_KIND_CLASS[kind]}`}>{label}</span>\n );\n}\n\n/**\n * A single Apigee API tile. The whole card is one anchor-based control whose href is\n * resolved via `entityRouteRef` (never hardcoded — TR04); it degrades to a non-link\n * button when the route ref is unbound (dev harness). Entity text renders as React\n * children only — never via raw HTML injection (T-05-03). (UI-02, UI-03)\n */\nexport function ApiCard({ vm, icon: Icon }: ApiCardProps) {\n const entityRoute = useRouteRef(entityRouteRef);\n const { kind, namespace, name } = vm.targetRef;\n const href = entityRoute\n ? entityRoute({ kind: kind.toLowerCase(), namespace, name })\n : undefined;\n const ariaLabel = `Open ${vm.name} API details`;\n\n const content = (\n <>\n <div className={styles.header}>\n <div className={styles.titleRow}>\n {Icon ? (\n <Icon aria-hidden className={styles.categoryIcon} size={20} />\n ) : null}\n <Text\n variant=\"body-medium\"\n weight=\"bold\"\n as=\"h3\"\n className={styles.name}\n >\n {vm.name}\n </Text>\n </div>\n <div className={styles.badges}>\n <LifecycleBadge kind={vm.badge.kind} label={vm.badge.label} />\n {vm.version ? (\n <span className={`${styles.badge} ${styles.badgeVersion}`}>\n {vm.version}\n </span>\n ) : null}\n </div>\n {vm.description ? (\n <Text\n variant=\"body-small\"\n color=\"secondary\"\n as=\"p\"\n className={styles.description}\n >\n {vm.description}\n </Text>\n ) : null}\n </div>\n\n <div className={styles.body}>\n <div className={styles.leftCol}>\n <div className={styles.metaItem}>\n <RiTeamLine aria-hidden size={16} />\n <Text variant=\"body-small\" color=\"secondary\" as=\"span\">\n {vm.ownerLabel}\n </Text>\n </div>\n {vm.department ? (\n <div className={styles.metaItem}>\n <RiBuildingLine aria-hidden size={16} />\n <Text variant=\"body-small\" color=\"secondary\" as=\"span\">\n {vm.department}\n </Text>\n </div>\n ) : null}\n {vm.visibilities.map(visibility => {\n const VisibilityIcon = VISIBILITY_ICON[visibility];\n return (\n <div key={visibility} className={styles.metaItem}>\n <VisibilityIcon aria-hidden size={16} />\n <Text variant=\"body-small\" color=\"secondary\" as=\"span\">\n {visibility}\n </Text>\n </div>\n );\n })}\n {vm.hasSpec ? (\n <div className={styles.metaItem}>\n <RiFileTextLine aria-hidden size={16} />\n <Text variant=\"body-small\" color=\"secondary\" as=\"span\">\n OpenAPI spec\n </Text>\n </div>\n ) : null}\n </div>\n\n <div className={styles.rightCol}>\n {/* Quality score + information coverage list — to be implemented later */}\n </div>\n </div>\n </>\n );\n\n return (\n <div className={styles.card}>\n {href ? (\n <Link to={href} className={styles.action} aria-label={ariaLabel}>\n {content}\n </Link>\n ) : (\n <div className={styles.action}>{content}</div>\n )}\n </div>\n );\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import styleInject from '../node_modules_dist/style-inject/dist/style-inject.es.esm.js';
|
|
2
2
|
|
|
3
|
-
var css_248z = ".
|
|
4
|
-
var styles = {"card":"
|
|
3
|
+
var css_248z = ".ApiCard_card__3afe92d8c5 {\n height: 100%;\n border: 1px solid var(--bui-border-1);\n border-radius: var(--bui-radius-3);\n background-color: var(--bui-bg-neutral-1);\n transition: box-shadow 200ms ease;\n}\n\n.ApiCard_card__3afe92d8c5:hover {\n box-shadow: var(--bui-shadow);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .ApiCard_card__3afe92d8c5 {\n transition: none;\n }\n}\n\n.ApiCard_action__3afe92d8c5 {\n display: flex;\n flex-direction: column;\n gap: var(--bui-space-2);\n height: 100%;\n min-height: 172px;\n padding: var(--bui-space-4);\n color: inherit;\n text-decoration: none;\n border-radius: var(--bui-radius-3);\n}\n\n.ApiCard_action__3afe92d8c5:focus-visible {\n outline: 2px solid var(--bui-ring);\n outline-offset: 2px;\n}\n\n.ApiCard_header__3afe92d8c5 {\n display: flex;\n flex-direction: column;\n gap: var(--bui-space-2);\n}\n\n.ApiCard_titleRow__3afe92d8c5 {\n display: flex;\n align-items: flex-start;\n gap: var(--bui-space-2);\n}\n\n.ApiCard_categoryIcon__3afe92d8c5 {\n margin-top: 2px;\n color: var(--bui-fg-secondary);\n}\n\n.ApiCard_name__3afe92d8c5 {\n flex: 1;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n word-break: break-word;\n}\n\n.ApiCard_badges__3afe92d8c5 {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--bui-space-1);\n}\n\n.ApiCard_badge__3afe92d8c5 {\n flex-shrink: 0;\n display: inline-flex;\n align-items: center;\n padding: 2px var(--bui-space-2);\n border-radius: var(--bui-radius-full);\n font-size: var(--bui-font-size-1);\n font-weight: var(--bui-font-weight-bold);\n line-height: 1.5;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n white-space: nowrap;\n}\n\n.ApiCard_badgeOk__3afe92d8c5 {\n background-color: var(--bui-bg-success);\n color: var(--bui-fg-success-on-bg);\n}\n\n.ApiCard_badgeWarning__3afe92d8c5 {\n background-color: var(--bui-bg-warning);\n color: var(--bui-fg-warning-on-bg);\n}\n\n.ApiCard_badgeError__3afe92d8c5 {\n background-color: var(--bui-bg-danger);\n color: var(--bui-fg-danger-on-bg);\n}\n\n.ApiCard_badgeVersion__3afe92d8c5 {\n background-color: var(--bui-bg-info);\n color: var(--bui-fg-info-on-bg);\n text-transform: none;\n}\n\n.ApiCard_body__3afe92d8c5 {\n display: flex;\n align-items: flex-start;\n gap: var(--bui-space-4);\n}\n\n.ApiCard_leftCol__3afe92d8c5,\n.ApiCard_rightCol__3afe92d8c5 {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: var(--bui-space-1);\n}\n\n.ApiCard_leftCol__3afe92d8c5 {\n color: var(--bui-fg-secondary);\n}\n\n.ApiCard_metaItem__3afe92d8c5 {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--bui-space-1);\n}\n\n.ApiCard_description__3afe92d8c5 {\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n";
|
|
4
|
+
var styles = {"card":"ApiCard_card__3afe92d8c5","action":"ApiCard_action__3afe92d8c5","header":"ApiCard_header__3afe92d8c5","titleRow":"ApiCard_titleRow__3afe92d8c5","categoryIcon":"ApiCard_categoryIcon__3afe92d8c5","name":"ApiCard_name__3afe92d8c5","badges":"ApiCard_badges__3afe92d8c5","badge":"ApiCard_badge__3afe92d8c5","badgeOk":"ApiCard_badgeOk__3afe92d8c5","badgeWarning":"ApiCard_badgeWarning__3afe92d8c5","badgeError":"ApiCard_badgeError__3afe92d8c5","badgeVersion":"ApiCard_badgeVersion__3afe92d8c5","body":"ApiCard_body__3afe92d8c5","leftCol":"ApiCard_leftCol__3afe92d8c5","rightCol":"ApiCard_rightCol__3afe92d8c5","metaItem":"ApiCard_metaItem__3afe92d8c5","description":"ApiCard_description__3afe92d8c5"};
|
|
5
5
|
styleInject(css_248z);
|
|
6
6
|
|
|
7
7
|
export { styles as default };
|
|
@@ -1,41 +1,21 @@
|
|
|
1
|
-
import { jsx
|
|
1
|
+
import { jsx } from 'react/jsx-runtime';
|
|
2
2
|
import { ItemCardGrid } from '@backstage/core-components';
|
|
3
|
-
import { Text } from '@backstage/ui';
|
|
4
3
|
import { ApiCard } from './ApiCard.esm.js';
|
|
4
|
+
import { ApiTable } from './ApiTable.esm.js';
|
|
5
5
|
import { resolveCategoryIcon } from './categoryIcons.esm.js';
|
|
6
|
-
import { groupByCategory } from './grouping.esm.js';
|
|
7
|
-
import styles from './ApiCardGrid.module.css.esm.js';
|
|
8
6
|
|
|
9
7
|
function ApiCardGrid({ vms, viewMode, overrides }) {
|
|
10
|
-
if (viewMode === "
|
|
11
|
-
return /* @__PURE__ */ jsx(
|
|
12
|
-
ApiCard,
|
|
13
|
-
{
|
|
14
|
-
vm,
|
|
15
|
-
icon: resolveCategoryIcon(vm.category, overrides)
|
|
16
|
-
},
|
|
17
|
-
vm.targetRef.name
|
|
18
|
-
)) });
|
|
8
|
+
if (viewMode === "list") {
|
|
9
|
+
return /* @__PURE__ */ jsx(ApiTable, { vms });
|
|
19
10
|
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
] }),
|
|
29
|
-
/* @__PURE__ */ jsx(ItemCardGrid, { children: group.items.map((vm) => /* @__PURE__ */ jsx(
|
|
30
|
-
ApiCard,
|
|
31
|
-
{
|
|
32
|
-
vm,
|
|
33
|
-
icon: resolveCategoryIcon(vm.category, overrides)
|
|
34
|
-
},
|
|
35
|
-
vm.targetRef.name
|
|
36
|
-
)) })
|
|
37
|
-
] }, group.key);
|
|
38
|
-
}) });
|
|
11
|
+
return /* @__PURE__ */ jsx(ItemCardGrid, { children: vms.map((vm) => /* @__PURE__ */ jsx(
|
|
12
|
+
ApiCard,
|
|
13
|
+
{
|
|
14
|
+
vm,
|
|
15
|
+
icon: resolveCategoryIcon(vm.category, overrides)
|
|
16
|
+
},
|
|
17
|
+
vm.targetRef.name
|
|
18
|
+
)) });
|
|
39
19
|
}
|
|
40
20
|
|
|
41
21
|
export { ApiCardGrid };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ApiCardGrid.esm.js","sources":["../../src/components/ApiCardGrid.tsx"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\nimport { ItemCardGrid } from '@backstage/core-components';\nimport {
|
|
1
|
+
{"version":3,"file":"ApiCardGrid.esm.js","sources":["../../src/components/ApiCardGrid.tsx"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\nimport { ItemCardGrid } from '@backstage/core-components';\nimport { ApiCard } from './ApiCard';\nimport { ApiTable } from './ApiTable';\nimport type { ApiCardViewModel } from './apiCardViewModel';\nimport { resolveCategoryIcon } from './categoryIcons';\n\nexport type ViewMode = 'flat' | 'list';\n\nexport interface ApiCardGridProps {\n vms: ApiCardViewModel[];\n viewMode: ViewMode;\n /** Raw `apigee.catalog.categoryIcons` override map (slug -> IconKey string). */\n overrides?: Record<string, string>;\n}\n\n/**\n * Presentational grid of `ApiCard` tiles. In `flat` mode a single responsive grid; in\n * `list` mode a table. No data fetching. (UI-01, UI-11)\n */\nexport function ApiCardGrid({ vms, viewMode, overrides }: ApiCardGridProps) {\n if (viewMode === 'list') {\n return <ApiTable vms={vms} />;\n }\n\n return (\n <ItemCardGrid>\n {vms.map(vm => (\n <ApiCard\n key={vm.targetRef.name}\n vm={vm}\n icon={resolveCategoryIcon(vm.category, overrides)}\n />\n ))}\n </ItemCardGrid>\n );\n}\n"],"names":[],"mappings":";;;;;;AAoBO,SAAS,WAAA,CAAY,EAAE,GAAA,EAAK,QAAA,EAAU,WAAU,EAAqB;AAC1E,EAAA,IAAI,aAAa,MAAA,EAAQ;AACvB,IAAA,uBAAO,GAAA,CAAC,YAAS,GAAA,EAAU,CAAA;AAAA,EAC7B;AAEA,EAAA,uBACE,GAAA,CAAC,YAAA,EAAA,EACE,QAAA,EAAA,GAAA,CAAI,GAAA,CAAI,CAAA,EAAA,qBACP,GAAA;AAAA,IAAC,OAAA;AAAA,IAAA;AAAA,MAEC,EAAA;AAAA,MACA,IAAA,EAAM,mBAAA,CAAoB,EAAA,CAAG,QAAA,EAAU,SAAS;AAAA,KAAA;AAAA,IAF3C,GAAG,SAAA,CAAU;AAAA,GAIrB,CAAA,EACH,CAAA;AAEJ;;;;"}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
2
|
+
import { Link } from '@backstage/core-components';
|
|
3
|
+
import { useRouteRef } from '@backstage/frontend-plugin-api';
|
|
4
|
+
import { entityRouteRef } from '@backstage/plugin-catalog-react';
|
|
5
|
+
import { RiFileTextLine, RiArrowUpSLine, RiArrowDownSLine, RiArrowUpDownLine } from '@remixicon/react';
|
|
6
|
+
import { useState, useMemo } from 'react';
|
|
7
|
+
import { LifecycleBadge, VISIBILITY_ICON } from './ApiCard.esm.js';
|
|
8
|
+
import styles from './ApiTable.module.css.esm.js';
|
|
9
|
+
|
|
10
|
+
function sortValue(vm, key) {
|
|
11
|
+
switch (key) {
|
|
12
|
+
case "name":
|
|
13
|
+
return vm.name;
|
|
14
|
+
case "lifecycle":
|
|
15
|
+
return vm.badge.label;
|
|
16
|
+
case "version":
|
|
17
|
+
return vm.version ?? "";
|
|
18
|
+
case "owner":
|
|
19
|
+
return vm.ownerLabel;
|
|
20
|
+
case "department":
|
|
21
|
+
return vm.department ?? "";
|
|
22
|
+
case "orgs":
|
|
23
|
+
return vm.visibilities.join(", ");
|
|
24
|
+
default:
|
|
25
|
+
return "";
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function SortableHeader({
|
|
29
|
+
label,
|
|
30
|
+
sortKey,
|
|
31
|
+
sort,
|
|
32
|
+
onSort
|
|
33
|
+
}) {
|
|
34
|
+
const activeSort = sort && sort.key === sortKey ? sort : null;
|
|
35
|
+
const ariaSort = activeSort ? activeSort.dir === "asc" ? "ascending" : "descending" : "none";
|
|
36
|
+
const Icon = activeSort ? activeSort.dir === "asc" ? RiArrowUpSLine : RiArrowDownSLine : RiArrowUpDownLine;
|
|
37
|
+
return /* @__PURE__ */ jsx("th", { scope: "col", "aria-sort": ariaSort, children: /* @__PURE__ */ jsxs(
|
|
38
|
+
"button",
|
|
39
|
+
{
|
|
40
|
+
type: "button",
|
|
41
|
+
className: styles.sortButton,
|
|
42
|
+
onClick: () => onSort(sortKey),
|
|
43
|
+
children: [
|
|
44
|
+
label,
|
|
45
|
+
/* @__PURE__ */ jsx(
|
|
46
|
+
Icon,
|
|
47
|
+
{
|
|
48
|
+
"aria-hidden": true,
|
|
49
|
+
size: 14,
|
|
50
|
+
className: activeSort ? styles.sortIcon : styles.sortIconIdle
|
|
51
|
+
}
|
|
52
|
+
)
|
|
53
|
+
]
|
|
54
|
+
}
|
|
55
|
+
) });
|
|
56
|
+
}
|
|
57
|
+
function ApiTable({ vms }) {
|
|
58
|
+
const entityRoute = useRouteRef(entityRouteRef);
|
|
59
|
+
const [sort, setSort] = useState(null);
|
|
60
|
+
const handleSort = (key) => setSort(
|
|
61
|
+
(prev) => prev?.key === key ? { key, dir: prev.dir === "asc" ? "desc" : "asc" } : { key, dir: "asc" }
|
|
62
|
+
);
|
|
63
|
+
const sortedVms = useMemo(() => {
|
|
64
|
+
if (!sort) {
|
|
65
|
+
return vms;
|
|
66
|
+
}
|
|
67
|
+
const dir = sort.dir === "asc" ? 1 : -1;
|
|
68
|
+
return [...vms].sort(
|
|
69
|
+
(a, b) => dir * sortValue(a, sort.key).localeCompare(sortValue(b, sort.key))
|
|
70
|
+
);
|
|
71
|
+
}, [vms, sort]);
|
|
72
|
+
return /* @__PURE__ */ jsx("div", { className: styles.tableWrap, children: /* @__PURE__ */ jsxs("table", { className: styles.table, children: [
|
|
73
|
+
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [
|
|
74
|
+
/* @__PURE__ */ jsx(
|
|
75
|
+
SortableHeader,
|
|
76
|
+
{
|
|
77
|
+
label: "Name",
|
|
78
|
+
sortKey: "name",
|
|
79
|
+
sort,
|
|
80
|
+
onSort: handleSort
|
|
81
|
+
}
|
|
82
|
+
),
|
|
83
|
+
/* @__PURE__ */ jsx(
|
|
84
|
+
SortableHeader,
|
|
85
|
+
{
|
|
86
|
+
label: "Lifecycle",
|
|
87
|
+
sortKey: "lifecycle",
|
|
88
|
+
sort,
|
|
89
|
+
onSort: handleSort
|
|
90
|
+
}
|
|
91
|
+
),
|
|
92
|
+
/* @__PURE__ */ jsx(
|
|
93
|
+
SortableHeader,
|
|
94
|
+
{
|
|
95
|
+
label: "Version",
|
|
96
|
+
sortKey: "version",
|
|
97
|
+
sort,
|
|
98
|
+
onSort: handleSort
|
|
99
|
+
}
|
|
100
|
+
),
|
|
101
|
+
/* @__PURE__ */ jsx(
|
|
102
|
+
SortableHeader,
|
|
103
|
+
{
|
|
104
|
+
label: "Owner",
|
|
105
|
+
sortKey: "owner",
|
|
106
|
+
sort,
|
|
107
|
+
onSort: handleSort
|
|
108
|
+
}
|
|
109
|
+
),
|
|
110
|
+
/* @__PURE__ */ jsx(
|
|
111
|
+
SortableHeader,
|
|
112
|
+
{
|
|
113
|
+
label: "Department",
|
|
114
|
+
sortKey: "department",
|
|
115
|
+
sort,
|
|
116
|
+
onSort: handleSort
|
|
117
|
+
}
|
|
118
|
+
),
|
|
119
|
+
/* @__PURE__ */ jsx(
|
|
120
|
+
SortableHeader,
|
|
121
|
+
{
|
|
122
|
+
label: "Orgs",
|
|
123
|
+
sortKey: "orgs",
|
|
124
|
+
sort,
|
|
125
|
+
onSort: handleSort
|
|
126
|
+
}
|
|
127
|
+
),
|
|
128
|
+
/* @__PURE__ */ jsx("th", { scope: "col", children: "Spec" })
|
|
129
|
+
] }) }),
|
|
130
|
+
/* @__PURE__ */ jsx("tbody", { children: sortedVms.map((vm) => {
|
|
131
|
+
const { kind, namespace, name } = vm.targetRef;
|
|
132
|
+
const href = entityRoute ? entityRoute({ kind: kind.toLowerCase(), namespace, name }) : void 0;
|
|
133
|
+
return /* @__PURE__ */ jsxs("tr", { children: [
|
|
134
|
+
/* @__PURE__ */ jsx("td", { className: styles.nameCell, children: href ? /* @__PURE__ */ jsx(Link, { to: href, className: styles.nameLink, children: vm.name }) : vm.name }),
|
|
135
|
+
/* @__PURE__ */ jsx("td", { children: /* @__PURE__ */ jsx(LifecycleBadge, { kind: vm.badge.kind, label: vm.badge.label }) }),
|
|
136
|
+
/* @__PURE__ */ jsx("td", { children: vm.version ?? "\u2014" }),
|
|
137
|
+
/* @__PURE__ */ jsx("td", { children: vm.ownerLabel }),
|
|
138
|
+
/* @__PURE__ */ jsx("td", { children: vm.department ?? "\u2014" }),
|
|
139
|
+
/* @__PURE__ */ jsx("td", { children: vm.visibilities.length > 0 ? /* @__PURE__ */ jsx("span", { className: styles.orgs, children: vm.visibilities.map((visibility) => {
|
|
140
|
+
const VisibilityIcon = VISIBILITY_ICON[visibility];
|
|
141
|
+
return /* @__PURE__ */ jsxs("span", { className: styles.org, children: [
|
|
142
|
+
/* @__PURE__ */ jsx(VisibilityIcon, { "aria-hidden": true, size: 14 }),
|
|
143
|
+
visibility
|
|
144
|
+
] }, visibility);
|
|
145
|
+
}) }) : "\u2014" }),
|
|
146
|
+
/* @__PURE__ */ jsx("td", { children: vm.hasSpec ? /* @__PURE__ */ jsx(
|
|
147
|
+
"span",
|
|
148
|
+
{
|
|
149
|
+
role: "img",
|
|
150
|
+
"aria-label": "OpenAPI spec available",
|
|
151
|
+
title: "OpenAPI spec available",
|
|
152
|
+
className: styles.specIcon,
|
|
153
|
+
children: /* @__PURE__ */ jsx(RiFileTextLine, { "aria-hidden": true, size: 16 })
|
|
154
|
+
}
|
|
155
|
+
) : "\u2014" })
|
|
156
|
+
] }, vm.targetRef.name);
|
|
157
|
+
}) })
|
|
158
|
+
] }) });
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export { ApiTable };
|
|
162
|
+
//# sourceMappingURL=ApiTable.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ApiTable.esm.js","sources":["../../src/components/ApiTable.tsx"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\nimport { Link } from '@backstage/core-components';\nimport { useRouteRef } from '@backstage/frontend-plugin-api';\nimport { entityRouteRef } from '@backstage/plugin-catalog-react';\nimport {\n RiArrowDownSLine,\n RiArrowUpDownLine,\n RiArrowUpSLine,\n RiFileTextLine,\n} from '@remixicon/react';\nimport { useMemo, useState } from 'react';\nimport { LifecycleBadge, VISIBILITY_ICON } from './ApiCard';\nimport type { ApiCardViewModel } from './apiCardViewModel';\nimport styles from './ApiTable.module.css';\n\nexport interface ApiTableProps {\n vms: ApiCardViewModel[];\n}\n\ntype SortKey = 'name' | 'lifecycle' | 'version' | 'owner' | 'department' | 'orgs';\ntype SortState = { key: SortKey; dir: 'asc' | 'desc' };\n\n/** Alphabetical sort key per column; missing values collapse to '' (sort first asc). */\nfunction sortValue(vm: ApiCardViewModel, key: SortKey): string {\n switch (key) {\n case 'name':\n return vm.name;\n case 'lifecycle':\n return vm.badge.label;\n case 'version':\n return vm.version ?? '';\n case 'owner':\n return vm.ownerLabel;\n case 'department':\n return vm.department ?? '';\n case 'orgs':\n return vm.visibilities.join(', ');\n default:\n return '';\n }\n}\n\n/** Clickable column header that toggles asc/desc alphabetical sort for its column. */\nfunction SortableHeader({\n label,\n sortKey,\n sort,\n onSort,\n}: {\n label: string;\n sortKey: SortKey;\n sort: SortState | null;\n onSort: (key: SortKey) => void;\n}) {\n const activeSort = sort && sort.key === sortKey ? sort : null;\n const ariaSort = activeSort\n ? activeSort.dir === 'asc'\n ? 'ascending'\n : 'descending'\n : 'none';\n const Icon = activeSort\n ? activeSort.dir === 'asc'\n ? RiArrowUpSLine\n : RiArrowDownSLine\n : RiArrowUpDownLine;\n return (\n <th scope=\"col\" aria-sort={ariaSort}>\n <button\n type=\"button\"\n className={styles.sortButton}\n onClick={() => onSort(sortKey)}\n >\n {label}\n <Icon\n aria-hidden\n size={14}\n className={activeSort ? styles.sortIcon : styles.sortIconIdle}\n />\n </button>\n </th>\n );\n}\n\n/**\n * Tabular (\"list\") presentation of the same view-models rendered as tiles by `ApiCard`.\n * Each row's name is the entity link (resolved via `entityRouteRef`, degrading to plain\n * text when the route ref is unbound); one anchor per row keeps the table keyboard\n * accessible. Reuses `ApiCard`'s lifecycle badge and org icons. (UI-01)\n */\nexport function ApiTable({ vms }: ApiTableProps) {\n const entityRoute = useRouteRef(entityRouteRef);\n const [sort, setSort] = useState<SortState | null>(null);\n\n const handleSort = (key: SortKey) =>\n setSort(prev =>\n prev?.key === key\n ? { key, dir: prev.dir === 'asc' ? 'desc' : 'asc' }\n : { key, dir: 'asc' },\n );\n\n const sortedVms = useMemo(() => {\n if (!sort) {\n return vms;\n }\n const dir = sort.dir === 'asc' ? 1 : -1;\n return [...vms].sort(\n (a, b) => dir * sortValue(a, sort.key).localeCompare(sortValue(b, sort.key)),\n );\n }, [vms, sort]);\n\n return (\n <div className={styles.tableWrap}>\n <table className={styles.table}>\n <thead>\n <tr>\n <SortableHeader\n label=\"Name\"\n sortKey=\"name\"\n sort={sort}\n onSort={handleSort}\n />\n <SortableHeader\n label=\"Lifecycle\"\n sortKey=\"lifecycle\"\n sort={sort}\n onSort={handleSort}\n />\n <SortableHeader\n label=\"Version\"\n sortKey=\"version\"\n sort={sort}\n onSort={handleSort}\n />\n <SortableHeader\n label=\"Owner\"\n sortKey=\"owner\"\n sort={sort}\n onSort={handleSort}\n />\n <SortableHeader\n label=\"Department\"\n sortKey=\"department\"\n sort={sort}\n onSort={handleSort}\n />\n <SortableHeader\n label=\"Orgs\"\n sortKey=\"orgs\"\n sort={sort}\n onSort={handleSort}\n />\n <th scope=\"col\">Spec</th>\n </tr>\n </thead>\n <tbody>\n {sortedVms.map(vm => {\n const { kind, namespace, name } = vm.targetRef;\n const href = entityRoute\n ? entityRoute({ kind: kind.toLowerCase(), namespace, name })\n : undefined;\n return (\n <tr key={vm.targetRef.name}>\n <td className={styles.nameCell}>\n {href ? (\n <Link to={href} className={styles.nameLink}>\n {vm.name}\n </Link>\n ) : (\n vm.name\n )}\n </td>\n <td>\n <LifecycleBadge kind={vm.badge.kind} label={vm.badge.label} />\n </td>\n <td>{vm.version ?? '—'}</td>\n <td>{vm.ownerLabel}</td>\n <td>{vm.department ?? '—'}</td>\n <td>\n {vm.visibilities.length > 0 ? (\n <span className={styles.orgs}>\n {vm.visibilities.map(visibility => {\n const VisibilityIcon = VISIBILITY_ICON[visibility];\n return (\n <span key={visibility} className={styles.org}>\n <VisibilityIcon aria-hidden size={14} />\n {visibility}\n </span>\n );\n })}\n </span>\n ) : (\n '—'\n )}\n </td>\n <td>\n {vm.hasSpec ? (\n <span\n role=\"img\"\n aria-label=\"OpenAPI spec available\"\n title=\"OpenAPI spec available\"\n className={styles.specIcon}\n >\n <RiFileTextLine aria-hidden size={16} />\n </span>\n ) : (\n '—'\n )}\n </td>\n </tr>\n );\n })}\n </tbody>\n </table>\n </div>\n );\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import styleInject from '../node_modules_dist/style-inject/dist/style-inject.es.esm.js';
|
|
2
|
+
|
|
3
|
+
var css_248z = ".ApiTable_tableWrap__13d7c249ed {\n width: 100%;\n overflow-x: auto;\n}\n\n.ApiTable_table__13d7c249ed {\n width: 100%;\n border-collapse: collapse;\n font-size: var(--bui-font-size-2);\n}\n\n.ApiTable_table__13d7c249ed th,\n.ApiTable_table__13d7c249ed td {\n padding: var(--bui-space-2) var(--bui-space-3);\n text-align: left;\n vertical-align: middle;\n white-space: nowrap;\n border-bottom: 1px solid var(--bui-border-1);\n}\n\n.ApiTable_table__13d7c249ed th {\n color: var(--bui-fg-secondary);\n font-weight: var(--bui-font-weight-bold);\n}\n\n.ApiTable_table__13d7c249ed td {\n color: var(--bui-fg-secondary);\n}\n\n.ApiTable_table__13d7c249ed tbody tr:hover {\n background-color: var(--bui-bg-neutral-1);\n}\n\n.ApiTable_sortButton__13d7c249ed {\n display: inline-flex;\n align-items: center;\n gap: var(--bui-space-1);\n margin: 0;\n padding: 0;\n border: none;\n background: none;\n color: inherit;\n font: inherit;\n font-weight: var(--bui-font-weight-bold);\n cursor: pointer;\n}\n\n.ApiTable_sortButton__13d7c249ed:hover {\n color: var(--bui-fg-primary);\n}\n\n.ApiTable_sortButton__13d7c249ed:focus-visible {\n outline: 2px solid var(--bui-ring);\n outline-offset: 2px;\n border-radius: 2px;\n}\n\n.ApiTable_sortIcon__13d7c249ed {\n color: var(--bui-fg-primary);\n}\n\n.ApiTable_sortIconIdle__13d7c249ed {\n opacity: 0.4;\n}\n\n.ApiTable_nameCell__13d7c249ed {\n color: var(--bui-fg-primary);\n font-weight: var(--bui-font-weight-bold);\n}\n\n.ApiTable_nameLink__13d7c249ed {\n color: inherit;\n text-decoration: none;\n}\n\n.ApiTable_nameLink__13d7c249ed:hover {\n text-decoration: underline;\n}\n\n.ApiTable_orgs__13d7c249ed {\n display: inline-flex;\n align-items: center;\n gap: var(--bui-space-2);\n}\n\n.ApiTable_org__13d7c249ed {\n display: inline-flex;\n align-items: center;\n gap: var(--bui-space-1);\n}\n\n.ApiTable_specIcon__13d7c249ed {\n display: inline-flex;\n color: var(--bui-fg-secondary);\n}\n";
|
|
4
|
+
var styles = {"tableWrap":"ApiTable_tableWrap__13d7c249ed","table":"ApiTable_table__13d7c249ed","sortButton":"ApiTable_sortButton__13d7c249ed","sortIcon":"ApiTable_sortIcon__13d7c249ed","sortIconIdle":"ApiTable_sortIconIdle__13d7c249ed","nameCell":"ApiTable_nameCell__13d7c249ed","nameLink":"ApiTable_nameLink__13d7c249ed","orgs":"ApiTable_orgs__13d7c249ed","org":"ApiTable_org__13d7c249ed","specIcon":"ApiTable_specIcon__13d7c249ed"};
|
|
5
|
+
styleInject(css_248z);
|
|
6
|
+
|
|
7
|
+
export { styles as default };
|
|
8
|
+
//# sourceMappingURL=ApiTable.module.css.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ApiTable.module.css.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;"}
|
|
@@ -3,8 +3,9 @@ import { Page, Content, ContentHeader } from '@backstage/core-components';
|
|
|
3
3
|
import { useApi, configApiRef } from '@backstage/core-plugin-api';
|
|
4
4
|
import { EntityListProvider, useEntityList } from '@backstage/plugin-catalog-react';
|
|
5
5
|
import { ToggleButton } from '@backstage/ui';
|
|
6
|
-
import { RiLayoutGridLine,
|
|
6
|
+
import { RiLayoutGridLine, RiListUnordered } from '@remixicon/react';
|
|
7
7
|
import { useEffect, useMemo, useState } from 'react';
|
|
8
|
+
import { ANNOTATION_HAS_SPEC } from '../constants.esm.js';
|
|
8
9
|
import { toApiCardViewModel } from './apiCardViewModel.esm.js';
|
|
9
10
|
import { ApiCardGrid } from './ApiCardGrid.esm.js';
|
|
10
11
|
import { ApiKindFilter, isApigeeEntity } from './apigeeFacetFilters.esm.js';
|
|
@@ -80,6 +81,12 @@ function ApigeeCatalogContent() {
|
|
|
80
81
|
() => deriveCounts(apigeeEntities, ["org", "lifecycle", "department"], orgLabels),
|
|
81
82
|
[apigeeEntities, orgLabels]
|
|
82
83
|
);
|
|
84
|
+
const specCount = useMemo(
|
|
85
|
+
() => apigeeEntities.filter(
|
|
86
|
+
(entity) => entity.metadata.annotations?.[ANNOTATION_HAS_SPEC] === "true"
|
|
87
|
+
).length,
|
|
88
|
+
[apigeeEntities]
|
|
89
|
+
);
|
|
83
90
|
const active = isActive(state);
|
|
84
91
|
const [viewMode, setViewMode] = useState("flat");
|
|
85
92
|
const renderBody = () => {
|
|
@@ -94,46 +101,44 @@ function ApigeeCatalogContent() {
|
|
|
94
101
|
}
|
|
95
102
|
return /* @__PURE__ */ jsx(ApiCardGrid, { vms, viewMode, overrides });
|
|
96
103
|
};
|
|
104
|
+
const viewToggle = /* @__PURE__ */ jsxs(
|
|
105
|
+
"div",
|
|
106
|
+
{
|
|
107
|
+
role: "group",
|
|
108
|
+
"aria-label": "API view mode",
|
|
109
|
+
style: { display: "inline-flex", gap: "var(--bui-space-1)" },
|
|
110
|
+
children: [
|
|
111
|
+
/* @__PURE__ */ jsx(
|
|
112
|
+
ToggleButton,
|
|
113
|
+
{
|
|
114
|
+
"aria-label": "Flat grid view",
|
|
115
|
+
isSelected: viewMode === "flat",
|
|
116
|
+
onChange: () => setViewMode("flat"),
|
|
117
|
+
iconStart: /* @__PURE__ */ jsx(RiLayoutGridLine, {}),
|
|
118
|
+
children: "Flat"
|
|
119
|
+
}
|
|
120
|
+
),
|
|
121
|
+
/* @__PURE__ */ jsx(
|
|
122
|
+
ToggleButton,
|
|
123
|
+
{
|
|
124
|
+
"aria-label": "List view",
|
|
125
|
+
isSelected: viewMode === "list",
|
|
126
|
+
onChange: () => setViewMode("list"),
|
|
127
|
+
iconStart: /* @__PURE__ */ jsx(RiListUnordered, {}),
|
|
128
|
+
children: "List"
|
|
129
|
+
}
|
|
130
|
+
)
|
|
131
|
+
]
|
|
132
|
+
}
|
|
133
|
+
);
|
|
97
134
|
return /* @__PURE__ */ jsx(Page, { themeId: "apis", children: /* @__PURE__ */ jsxs(Content, { children: [
|
|
98
|
-
/* @__PURE__ */ jsx(ContentHeader, { title: "APIs",
|
|
99
|
-
"div",
|
|
100
|
-
{
|
|
101
|
-
role: "group",
|
|
102
|
-
"aria-label": "API view mode",
|
|
103
|
-
style: {
|
|
104
|
-
marginLeft: "auto",
|
|
105
|
-
display: "inline-flex",
|
|
106
|
-
gap: "var(--bui-space-1)"
|
|
107
|
-
},
|
|
108
|
-
children: [
|
|
109
|
-
/* @__PURE__ */ jsx(
|
|
110
|
-
ToggleButton,
|
|
111
|
-
{
|
|
112
|
-
"aria-label": "Flat grid view",
|
|
113
|
-
isSelected: viewMode === "flat",
|
|
114
|
-
onChange: () => setViewMode("flat"),
|
|
115
|
-
iconStart: /* @__PURE__ */ jsx(RiLayoutGridLine, {}),
|
|
116
|
-
children: "Flat"
|
|
117
|
-
}
|
|
118
|
-
),
|
|
119
|
-
/* @__PURE__ */ jsx(
|
|
120
|
-
ToggleButton,
|
|
121
|
-
{
|
|
122
|
-
"aria-label": "Group by category",
|
|
123
|
-
isSelected: viewMode === "grouped",
|
|
124
|
-
onChange: () => setViewMode("grouped"),
|
|
125
|
-
iconStart: /* @__PURE__ */ jsx(RiLayoutRowLine, {}),
|
|
126
|
-
children: "Grouped"
|
|
127
|
-
}
|
|
128
|
-
)
|
|
129
|
-
]
|
|
130
|
-
}
|
|
131
|
-
) }),
|
|
135
|
+
/* @__PURE__ */ jsx(ContentHeader, { title: "APIs" }),
|
|
132
136
|
!loading && !error && apigeeEntities.length > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
133
137
|
/* @__PURE__ */ jsx(
|
|
134
138
|
CountCards,
|
|
135
139
|
{
|
|
136
140
|
total: apigeeEntities.length,
|
|
141
|
+
specCount,
|
|
137
142
|
counts,
|
|
138
143
|
selected: state.selected,
|
|
139
144
|
onSelectFacet: selectOnlyFacet
|
|
@@ -149,7 +154,8 @@ function ApigeeCatalogContent() {
|
|
|
149
154
|
onClearAll: clearAll,
|
|
150
155
|
active,
|
|
151
156
|
resultCount: vms.length,
|
|
152
|
-
searchBar: /* @__PURE__ */ jsx(SearchBar, { value: inputValue, onChange: setInputValue })
|
|
157
|
+
searchBar: /* @__PURE__ */ jsx(SearchBar, { value: inputValue, onChange: setInputValue }),
|
|
158
|
+
viewToggle
|
|
153
159
|
}
|
|
154
160
|
)
|
|
155
161
|
] }) : null,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ApigeeApiCatalogPage.esm.js","sources":["../../src/components/ApigeeApiCatalogPage.tsx"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\nimport { Content, ContentHeader, Page } from '@backstage/core-components';\nimport { configApiRef, useApi } from '@backstage/core-plugin-api';\nimport {\n EntityListProvider,\n useEntityList,\n type DefaultEntityFilters,\n} from '@backstage/plugin-catalog-react';\nimport { ToggleButton } from '@backstage/ui';\nimport { RiLayoutGridLine, RiLayoutRowLine } from '@remixicon/react';\nimport { useEffect, useMemo, useState } from 'react';\nimport { toApiCardViewModel } from './apiCardViewModel';\nimport { ApiCardGrid, type ViewMode } from './ApiCardGrid';\nimport {\n ApiKindFilter,\n isApigeeEntity,\n type ApiSearchFilter,\n type FieldFacetFilter,\n} from './apigeeFacetFilters';\nimport { ApigeeOrgFilter } from './apigeeOrgFilter';\nimport { isActive } from './apigeeSearchParams';\nimport { CountCards } from './CountCards';\nimport { deriveCounts, deriveFacets } from './facetModel';\nimport { FilterToolbar } from './FilterToolbar';\nimport { SearchBar } from './SearchBar';\nimport {\n ApiCatalogEmptyState,\n ApiCatalogError,\n NoMatchState,\n SkeletonGrid,\n} from './states';\nimport { useApigeeFilters } from './useApigeeFilters';\n\n/**\n * Entity-list filters registered on the Apigee catalog page. `apigeeKind` (the seam-free\n * `ApiKindFilter`) and `apigeeOrg` (the dual-key is-Apigee gate) are the backend filters applied\n * once by {@link ApplyApigeeFilters}; the facet + search filters\n * (`apigeeLifecycle`/`apigeeOrgFacet`/`apigeeOwner`/`apigeeCategory`/`apigeeDepartment`/`apigeeSearch`)\n * are driven from the URL by `useApigeeFilters`. Exported so the hook can import the filter contract.\n */\nexport type ApigeeFilters = DefaultEntityFilters & {\n apigeeKind?: ApiKindFilter;\n apigeeOrg?: ApigeeOrgFilter;\n apigeeLifecycle?: FieldFacetFilter;\n apigeeOrgFacet?: FieldFacetFilter;\n apigeeOwner?: FieldFacetFilter;\n apigeeCategory?: FieldFacetFilter;\n apigeeDepartment?: FieldFacetFilter;\n apigeeSearch?: ApiSearchFilter;\n};\n\n/**\n * Registers the Apigee backend filters on the shared EntityListProvider ONCE: the seam-free\n * `ApiKindFilter` (kind=api with NO `toQueryValue`, so the provider's URL writer stays a no-op and\n * `useSearchParams` owns a clean URL) plus the dual-key `ApigeeOrgFilter` is-Apigee gate. The facet\n * + search filters are applied separately, URL-driven, by `useApigeeFilters`. Rendered inside the\n * provider; renders nothing itself.\n */\nfunction ApplyApigeeFilters() {\n const { updateFilters } = useEntityList<ApigeeFilters>();\n useEffect(() => {\n updateFilters({\n apigeeKind: new ApiKindFilter(),\n apigeeOrg: new ApigeeOrgFilter(),\n });\n }, [updateFilters]);\n return null;\n}\n\n/**\n * The page body: reads the filtered entity list, maps to view-models, and branches on\n * loading/empty/error. Exposes a Flat/Grouped view toggle (component state only, no view\n * URL param — that is a Phase 6 seam). (UI-01, UI-05, UI-11)\n */\nexport function ApigeeCatalogContent() {\n const { entities, backendEntities, loading, error } =\n useEntityList<ApigeeFilters>();\n const {\n state,\n inputValue,\n setInputValue,\n toggleFacet,\n selectOnlyFacet,\n clearFacet,\n clearAll,\n } = useApigeeFilters();\n const configApi = useApi(configApiRef);\n\n const overrides = useMemo<Record<string, string> | undefined>(() => {\n const config = configApi.getOptionalConfig('apigee.catalog.categoryIcons');\n if (!config) {\n return undefined;\n }\n const out: Record<string, string> = {};\n for (const key of config.keys()) {\n const value = config.getOptionalString(key);\n if (value) {\n out[key] = value;\n }\n }\n return Object.keys(out).length > 0 ? out : undefined;\n }, [configApi]);\n\n const orgLabels = useMemo<Record<string, string> | undefined>(() => {\n const config = configApi.getOptionalConfig('apigee.catalog.orgLabels');\n if (!config) {\n return undefined;\n }\n const out: Record<string, string> = {};\n for (const key of config.keys()) {\n const value = config.getOptionalString(key);\n if (value) {\n out[key] = value;\n }\n }\n return Object.keys(out).length > 0 ? out : undefined;\n }, [configApi]);\n\n const apigeeEntities = useMemo(\n () => backendEntities.filter(isApigeeEntity),\n [backendEntities],\n );\n const vms = useMemo(() => entities.map(toApiCardViewModel), [entities]);\n const facets = useMemo(\n () => deriveFacets(apigeeEntities, overrides, orgLabels),\n [apigeeEntities, overrides, orgLabels],\n );\n // Static full-catalog counts: the cards stay fixed as filters apply; only the grid narrows.\n const counts = useMemo(\n () => deriveCounts(apigeeEntities, ['org', 'lifecycle', 'department'], orgLabels),\n [apigeeEntities, orgLabels],\n );\n const active = isActive(state);\n const [viewMode, setViewMode] = useState<ViewMode>('flat');\n\n const renderBody = () => {\n if (error) {\n return <ApiCatalogError error={error} />;\n }\n if (loading) {\n return <SkeletonGrid />;\n }\n if (vms.length === 0) {\n return active ? (\n <NoMatchState onClearAll={clearAll} />\n ) : (\n <ApiCatalogEmptyState />\n );\n }\n return <ApiCardGrid vms={vms} viewMode={viewMode} overrides={overrides} />;\n };\n\n return (\n <Page themeId=\"apis\">\n <Content>\n <ContentHeader title=\"APIs\">\n <div\n role=\"group\"\n aria-label=\"API view mode\"\n style={{\n marginLeft: 'auto',\n display: 'inline-flex',\n gap: 'var(--bui-space-1)',\n }}\n >\n <ToggleButton\n aria-label=\"Flat grid view\"\n isSelected={viewMode === 'flat'}\n onChange={() => setViewMode('flat')}\n iconStart={<RiLayoutGridLine />}\n >\n Flat\n </ToggleButton>\n <ToggleButton\n aria-label=\"Group by category\"\n isSelected={viewMode === 'grouped'}\n onChange={() => setViewMode('grouped')}\n iconStart={<RiLayoutRowLine />}\n >\n Grouped\n </ToggleButton>\n </div>\n </ContentHeader>\n {!loading && !error && apigeeEntities.length > 0 ? (\n <>\n <CountCards\n total={apigeeEntities.length}\n counts={counts}\n selected={state.selected}\n onSelectFacet={selectOnlyFacet}\n />\n <FilterToolbar\n facets={facets}\n selected={state.selected}\n onToggleFacet={toggleFacet}\n onClearFacet={clearFacet}\n onClearAll={clearAll}\n active={active}\n resultCount={vms.length}\n searchBar={\n <SearchBar value={inputValue} onChange={setInputValue} />\n }\n />\n </>\n ) : null}\n {renderBody()}\n </Content>\n </Page>\n );\n}\n\n/**\n * Route target for `/apigee` (05-06). Wraps the content in an `EntityListProvider` and\n * registers the Apigee filters — the shared filter context Phase 6's facets + search reuse.\n */\nexport function ApigeeApiCatalogPage() {\n return (\n <EntityListProvider>\n <ApplyApigeeFilters />\n <ApigeeCatalogContent />\n </EntityListProvider>\n );\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ApigeeApiCatalogPage.esm.js","sources":["../../src/components/ApigeeApiCatalogPage.tsx"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\nimport { Content, ContentHeader, Page } from '@backstage/core-components';\nimport { configApiRef, useApi } from '@backstage/core-plugin-api';\nimport {\n EntityListProvider,\n useEntityList,\n type DefaultEntityFilters,\n} from '@backstage/plugin-catalog-react';\nimport { ToggleButton } from '@backstage/ui';\nimport { RiLayoutGridLine, RiListUnordered } from '@remixicon/react';\nimport { useEffect, useMemo, useState } from 'react';\nimport { ANNOTATION_HAS_SPEC } from '../constants';\nimport { toApiCardViewModel } from './apiCardViewModel';\nimport { ApiCardGrid, type ViewMode } from './ApiCardGrid';\nimport {\n ApiKindFilter,\n isApigeeEntity,\n type ApiSearchFilter,\n type FieldFacetFilter,\n} from './apigeeFacetFilters';\nimport { ApigeeOrgFilter } from './apigeeOrgFilter';\nimport { isActive } from './apigeeSearchParams';\nimport { CountCards } from './CountCards';\nimport { deriveCounts, deriveFacets } from './facetModel';\nimport { FilterToolbar } from './FilterToolbar';\nimport { SearchBar } from './SearchBar';\nimport {\n ApiCatalogEmptyState,\n ApiCatalogError,\n NoMatchState,\n SkeletonGrid,\n} from './states';\nimport { useApigeeFilters } from './useApigeeFilters';\n\n/**\n * Entity-list filters registered on the Apigee catalog page. `apigeeKind` (the seam-free\n * `ApiKindFilter`) and `apigeeOrg` (the dual-key is-Apigee gate) are the backend filters applied\n * once by {@link ApplyApigeeFilters}; the facet + search filters\n * (`apigeeLifecycle`/`apigeeOrgFacet`/`apigeeOwner`/`apigeeCategory`/`apigeeDepartment`/`apigeeSearch`)\n * are driven from the URL by `useApigeeFilters`. Exported so the hook can import the filter contract.\n */\nexport type ApigeeFilters = DefaultEntityFilters & {\n apigeeKind?: ApiKindFilter;\n apigeeOrg?: ApigeeOrgFilter;\n apigeeLifecycle?: FieldFacetFilter;\n apigeeOrgFacet?: FieldFacetFilter;\n apigeeOwner?: FieldFacetFilter;\n apigeeCategory?: FieldFacetFilter;\n apigeeDepartment?: FieldFacetFilter;\n apigeeSearch?: ApiSearchFilter;\n};\n\n/**\n * Registers the Apigee backend filters on the shared EntityListProvider ONCE: the seam-free\n * `ApiKindFilter` (kind=api with NO `toQueryValue`, so the provider's URL writer stays a no-op and\n * `useSearchParams` owns a clean URL) plus the dual-key `ApigeeOrgFilter` is-Apigee gate. The facet\n * + search filters are applied separately, URL-driven, by `useApigeeFilters`. Rendered inside the\n * provider; renders nothing itself.\n */\nfunction ApplyApigeeFilters() {\n const { updateFilters } = useEntityList<ApigeeFilters>();\n useEffect(() => {\n updateFilters({\n apigeeKind: new ApiKindFilter(),\n apigeeOrg: new ApigeeOrgFilter(),\n });\n }, [updateFilters]);\n return null;\n}\n\n/**\n * The page body: reads the filtered entity list, maps to view-models, and branches on\n * loading/empty/error. Exposes a Flat/List view toggle (component state only, no view\n * URL param — that is a Phase 6 seam). (UI-01, UI-05, UI-11)\n */\nexport function ApigeeCatalogContent() {\n const { entities, backendEntities, loading, error } =\n useEntityList<ApigeeFilters>();\n const {\n state,\n inputValue,\n setInputValue,\n toggleFacet,\n selectOnlyFacet,\n clearFacet,\n clearAll,\n } = useApigeeFilters();\n const configApi = useApi(configApiRef);\n\n const overrides = useMemo<Record<string, string> | undefined>(() => {\n const config = configApi.getOptionalConfig('apigee.catalog.categoryIcons');\n if (!config) {\n return undefined;\n }\n const out: Record<string, string> = {};\n for (const key of config.keys()) {\n const value = config.getOptionalString(key);\n if (value) {\n out[key] = value;\n }\n }\n return Object.keys(out).length > 0 ? out : undefined;\n }, [configApi]);\n\n const orgLabels = useMemo<Record<string, string> | undefined>(() => {\n const config = configApi.getOptionalConfig('apigee.catalog.orgLabels');\n if (!config) {\n return undefined;\n }\n const out: Record<string, string> = {};\n for (const key of config.keys()) {\n const value = config.getOptionalString(key);\n if (value) {\n out[key] = value;\n }\n }\n return Object.keys(out).length > 0 ? out : undefined;\n }, [configApi]);\n\n const apigeeEntities = useMemo(\n () => backendEntities.filter(isApigeeEntity),\n [backendEntities],\n );\n const vms = useMemo(() => entities.map(toApiCardViewModel), [entities]);\n const facets = useMemo(\n () => deriveFacets(apigeeEntities, overrides, orgLabels),\n [apigeeEntities, overrides, orgLabels],\n );\n // Static full-catalog counts: the cards stay fixed as filters apply; only the grid narrows.\n const counts = useMemo(\n () => deriveCounts(apigeeEntities, ['org', 'lifecycle', 'department'], orgLabels),\n [apigeeEntities, orgLabels],\n );\n const specCount = useMemo(\n () =>\n apigeeEntities.filter(\n entity => entity.metadata.annotations?.[ANNOTATION_HAS_SPEC] === 'true',\n ).length,\n [apigeeEntities],\n );\n const active = isActive(state);\n const [viewMode, setViewMode] = useState<ViewMode>('flat');\n\n const renderBody = () => {\n if (error) {\n return <ApiCatalogError error={error} />;\n }\n if (loading) {\n return <SkeletonGrid />;\n }\n if (vms.length === 0) {\n return active ? (\n <NoMatchState onClearAll={clearAll} />\n ) : (\n <ApiCatalogEmptyState />\n );\n }\n return <ApiCardGrid vms={vms} viewMode={viewMode} overrides={overrides} />;\n };\n\n const viewToggle = (\n <div\n role=\"group\"\n aria-label=\"API view mode\"\n style={{ display: 'inline-flex', gap: 'var(--bui-space-1)' }}\n >\n <ToggleButton\n aria-label=\"Flat grid view\"\n isSelected={viewMode === 'flat'}\n onChange={() => setViewMode('flat')}\n iconStart={<RiLayoutGridLine />}\n >\n Flat\n </ToggleButton>\n <ToggleButton\n aria-label=\"List view\"\n isSelected={viewMode === 'list'}\n onChange={() => setViewMode('list')}\n iconStart={<RiListUnordered />}\n >\n List\n </ToggleButton>\n </div>\n );\n\n return (\n <Page themeId=\"apis\">\n <Content>\n <ContentHeader title=\"APIs\" />\n {!loading && !error && apigeeEntities.length > 0 ? (\n <>\n <CountCards\n total={apigeeEntities.length}\n specCount={specCount}\n counts={counts}\n selected={state.selected}\n onSelectFacet={selectOnlyFacet}\n />\n <FilterToolbar\n facets={facets}\n selected={state.selected}\n onToggleFacet={toggleFacet}\n onClearFacet={clearFacet}\n onClearAll={clearAll}\n active={active}\n resultCount={vms.length}\n searchBar={\n <SearchBar value={inputValue} onChange={setInputValue} />\n }\n viewToggle={viewToggle}\n />\n </>\n ) : null}\n {renderBody()}\n </Content>\n </Page>\n );\n}\n\n/**\n * Route target for `/apigee` (05-06). Wraps the content in an `EntityListProvider` and\n * registers the Apigee filters — the shared filter context Phase 6's facets + search reuse.\n */\nexport function ApigeeApiCatalogPage() {\n return (\n <EntityListProvider>\n <ApplyApigeeFilters />\n <ApigeeCatalogContent />\n </EntityListProvider>\n );\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -4,6 +4,7 @@ import styles from './CountCards.module.css.esm.js';
|
|
|
4
4
|
|
|
5
5
|
function CountCards({
|
|
6
6
|
total,
|
|
7
|
+
specCount,
|
|
7
8
|
counts,
|
|
8
9
|
selected,
|
|
9
10
|
onSelectFacet
|
|
@@ -31,6 +32,28 @@ function CountCards({
|
|
|
31
32
|
}
|
|
32
33
|
)
|
|
33
34
|
] }),
|
|
35
|
+
/* @__PURE__ */ jsxs("div", { role: "group", "aria-label": "With specs", className: styles.card, children: [
|
|
36
|
+
/* @__PURE__ */ jsx(
|
|
37
|
+
Text,
|
|
38
|
+
{
|
|
39
|
+
as: "p",
|
|
40
|
+
variant: "title-large",
|
|
41
|
+
weight: "bold",
|
|
42
|
+
className: styles.total,
|
|
43
|
+
children: specCount
|
|
44
|
+
}
|
|
45
|
+
),
|
|
46
|
+
/* @__PURE__ */ jsx(
|
|
47
|
+
Text,
|
|
48
|
+
{
|
|
49
|
+
as: "h3",
|
|
50
|
+
variant: "body-x-small",
|
|
51
|
+
color: "secondary",
|
|
52
|
+
className: styles.cardTitle,
|
|
53
|
+
children: "With specs"
|
|
54
|
+
}
|
|
55
|
+
)
|
|
56
|
+
] }),
|
|
34
57
|
counts.map((count) => {
|
|
35
58
|
const heading = `By ${count.label.toLowerCase()}`;
|
|
36
59
|
return /* @__PURE__ */ jsxs(
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CountCards.esm.js","sources":["../../src/components/CountCards.tsx"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\nimport { Text } from '@backstage/ui';\nimport type { DerivedCount, FacetKey } from './facetModel';\nimport styles from './CountCards.module.css';\n\nexport interface CountCardsProps {\n /** Total APIs in the Apigee catalog (static snapshot — not the filtered count). */\n total: number;\n /** Per-facet breakdowns (org / lifecycle / department) from `deriveCounts`. */\n counts: DerivedCount[];\n /** Currently selected values per facet (from the URL filter state). */\n selected: Record<FacetKey, string[]>;\n /** Select a single facet value — clicking filters the catalog to exactly that value, replacing any other card selection. */\n onSelectFacet: (key: FacetKey, value: string) => void;\n}\n\n/**\n * Summary stat row shown above the filter toolbar:
|
|
1
|
+
{"version":3,"file":"CountCards.esm.js","sources":["../../src/components/CountCards.tsx"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\nimport { Text } from '@backstage/ui';\nimport type { DerivedCount, FacetKey } from './facetModel';\nimport styles from './CountCards.module.css';\n\nexport interface CountCardsProps {\n /** Total APIs in the Apigee catalog (static snapshot — not the filtered count). */\n total: number;\n /** APIs in the catalog that have an OpenAPI spec (static snapshot). */\n specCount: number;\n /** Per-facet breakdowns (org / lifecycle / department) from `deriveCounts`. */\n counts: DerivedCount[];\n /** Currently selected values per facet (from the URL filter state). */\n selected: Record<FacetKey, string[]>;\n /** Select a single facet value — clicking filters the catalog to exactly that value, replacing any other card selection. */\n onSelectFacet: (key: FacetKey, value: string) => void;\n}\n\n/**\n * Summary stat row shown above the filter toolbar: the prominent total-APIs card, a with-specs\n * stat card, and one breakdown card per facet (org / lifecycle / department). Counts are a\n * fixed snapshot of the full Apigee catalog — they do NOT change as filters apply. Each bucket is\n * a single-select link: clicking filters the grid to exactly that value (replacing any other card\n * selection), and the active value is highlighted. Purely presentational.\n */\nexport function CountCards({\n total,\n specCount,\n counts,\n selected,\n onSelectFacet,\n}: CountCardsProps) {\n return (\n <div className={styles.row}>\n <div role=\"group\" aria-label=\"APIs\" className={styles.card}>\n <Text\n as=\"p\"\n variant=\"title-large\"\n weight=\"bold\"\n className={styles.total}\n >\n {total}\n </Text>\n <Text\n as=\"h3\"\n variant=\"body-x-small\"\n color=\"secondary\"\n className={styles.cardTitle}\n >\n APIs\n </Text>\n </div>\n <div role=\"group\" aria-label=\"With specs\" className={styles.card}>\n <Text\n as=\"p\"\n variant=\"title-large\"\n weight=\"bold\"\n className={styles.total}\n >\n {specCount}\n </Text>\n <Text\n as=\"h3\"\n variant=\"body-x-small\"\n color=\"secondary\"\n className={styles.cardTitle}\n >\n With specs\n </Text>\n </div>\n {counts.map(count => {\n const heading = `By ${count.label.toLowerCase()}`;\n return (\n <div\n key={count.key}\n role=\"group\"\n aria-label={heading}\n className={styles.card}\n >\n <Text\n as=\"h3\"\n variant=\"body-x-small\"\n color=\"secondary\"\n className={styles.cardTitle}\n >\n {heading}\n </Text>\n {count.buckets.length === 0 ? (\n <Text variant=\"body-small\" color=\"secondary\">\n No data\n </Text>\n ) : (\n <div className={styles.list}>\n {count.buckets.map(bucket => {\n const isSelected = selected[count.key].includes(bucket.value);\n return (\n <button\n key={bucket.value}\n type=\"button\"\n className={`${styles.bucket} ${\n isSelected ? styles.bucketSelected : ''\n }`}\n aria-pressed={isSelected}\n aria-label={`${bucket.label}, ${bucket.count} ${\n bucket.count === 1 ? 'API' : 'APIs'\n }`}\n onClick={() => onSelectFacet(count.key, bucket.value)}\n >\n <Text\n as=\"span\"\n variant=\"body-small\"\n truncate\n title={bucket.label}\n className={styles.bucketLabel}\n >\n {bucket.label}\n </Text>\n <Text\n as=\"span\"\n variant=\"body-small\"\n weight=\"bold\"\n className={styles.bucketCount}\n >\n {bucket.count}\n </Text>\n </button>\n );\n })}\n </div>\n )}\n </div>\n );\n })}\n </div>\n );\n}\n"],"names":[],"mappings":";;;;AAyBO,SAAS,UAAA,CAAW;AAAA,EACzB,KAAA;AAAA,EACA,SAAA;AAAA,EACA,MAAA;AAAA,EACA,QAAA;AAAA,EACA;AACF,CAAA,EAAoB;AAClB,EAAA,uBACE,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAW,MAAA,CAAO,GAAA,EACrB,QAAA,EAAA;AAAA,oBAAA,IAAA,CAAC,SAAI,IAAA,EAAK,OAAA,EAAQ,cAAW,MAAA,EAAO,SAAA,EAAW,OAAO,IAAA,EACpD,QAAA,EAAA;AAAA,sBAAA,GAAA;AAAA,QAAC,IAAA;AAAA,QAAA;AAAA,UACC,EAAA,EAAG,GAAA;AAAA,UACH,OAAA,EAAQ,aAAA;AAAA,UACR,MAAA,EAAO,MAAA;AAAA,UACP,WAAW,MAAA,CAAO,KAAA;AAAA,UAEjB,QAAA,EAAA;AAAA;AAAA,OACH;AAAA,sBACA,GAAA;AAAA,QAAC,IAAA;AAAA,QAAA;AAAA,UACC,EAAA,EAAG,IAAA;AAAA,UACH,OAAA,EAAQ,cAAA;AAAA,UACR,KAAA,EAAM,WAAA;AAAA,UACN,WAAW,MAAA,CAAO,SAAA;AAAA,UACnB,QAAA,EAAA;AAAA;AAAA;AAED,KAAA,EACF,CAAA;AAAA,oBACA,IAAA,CAAC,SAAI,IAAA,EAAK,OAAA,EAAQ,cAAW,YAAA,EAAa,SAAA,EAAW,OAAO,IAAA,EAC1D,QAAA,EAAA;AAAA,sBAAA,GAAA;AAAA,QAAC,IAAA;AAAA,QAAA;AAAA,UACC,EAAA,EAAG,GAAA;AAAA,UACH,OAAA,EAAQ,aAAA;AAAA,UACR,MAAA,EAAO,MAAA;AAAA,UACP,WAAW,MAAA,CAAO,KAAA;AAAA,UAEjB,QAAA,EAAA;AAAA;AAAA,OACH;AAAA,sBACA,GAAA;AAAA,QAAC,IAAA;AAAA,QAAA;AAAA,UACC,EAAA,EAAG,IAAA;AAAA,UACH,OAAA,EAAQ,cAAA;AAAA,UACR,KAAA,EAAM,WAAA;AAAA,UACN,WAAW,MAAA,CAAO,SAAA;AAAA,UACnB,QAAA,EAAA;AAAA;AAAA;AAED,KAAA,EACF,CAAA;AAAA,IACC,MAAA,CAAO,IAAI,CAAA,KAAA,KAAS;AACnB,MAAA,MAAM,OAAA,GAAU,CAAA,GAAA,EAAM,KAAA,CAAM,KAAA,CAAM,aAAa,CAAA,CAAA;AAC/C,MAAA,uBACE,IAAA;AAAA,QAAC,KAAA;AAAA,QAAA;AAAA,UAEC,IAAA,EAAK,OAAA;AAAA,UACL,YAAA,EAAY,OAAA;AAAA,UACZ,WAAW,MAAA,CAAO,IAAA;AAAA,UAElB,QAAA,EAAA;AAAA,4BAAA,GAAA;AAAA,cAAC,IAAA;AAAA,cAAA;AAAA,gBACC,EAAA,EAAG,IAAA;AAAA,gBACH,OAAA,EAAQ,cAAA;AAAA,gBACR,KAAA,EAAM,WAAA;AAAA,gBACN,WAAW,MAAA,CAAO,SAAA;AAAA,gBAEjB,QAAA,EAAA;AAAA;AAAA,aACH;AAAA,YACC,KAAA,CAAM,QAAQ,MAAA,KAAW,CAAA,uBACvB,IAAA,EAAA,EAAK,OAAA,EAAQ,cAAa,KAAA,EAAM,WAAA,EAAY,qBAE7C,CAAA,mBAEA,GAAA,CAAC,SAAI,SAAA,EAAW,MAAA,CAAO,MACpB,QAAA,EAAA,KAAA,CAAM,OAAA,CAAQ,IAAI,CAAA,MAAA,KAAU;AAC3B,cAAA,MAAM,aAAa,QAAA,CAAS,KAAA,CAAM,GAAG,CAAA,CAAE,QAAA,CAAS,OAAO,KAAK,CAAA;AAC5D,cAAA,uBACE,IAAA;AAAA,gBAAC,QAAA;AAAA,gBAAA;AAAA,kBAEC,IAAA,EAAK,QAAA;AAAA,kBACL,SAAA,EAAW,GAAG,MAAA,CAAO,MAAM,IACzB,UAAA,GAAa,MAAA,CAAO,iBAAiB,EACvC,CAAA,CAAA;AAAA,kBACA,cAAA,EAAc,UAAA;AAAA,kBACd,YAAA,EAAY,CAAA,EAAG,MAAA,CAAO,KAAK,CAAA,EAAA,EAAK,MAAA,CAAO,KAAK,CAAA,CAAA,EAC1C,MAAA,CAAO,KAAA,KAAU,CAAA,GAAI,KAAA,GAAQ,MAC/B,CAAA,CAAA;AAAA,kBACA,SAAS,MAAM,aAAA,CAAc,KAAA,CAAM,GAAA,EAAK,OAAO,KAAK,CAAA;AAAA,kBAEpD,QAAA,EAAA;AAAA,oCAAA,GAAA;AAAA,sBAAC,IAAA;AAAA,sBAAA;AAAA,wBACC,EAAA,EAAG,MAAA;AAAA,wBACH,OAAA,EAAQ,YAAA;AAAA,wBACR,QAAA,EAAQ,IAAA;AAAA,wBACR,OAAO,MAAA,CAAO,KAAA;AAAA,wBACd,WAAW,MAAA,CAAO,WAAA;AAAA,wBAEjB,QAAA,EAAA,MAAA,CAAO;AAAA;AAAA,qBACV;AAAA,oCACA,GAAA;AAAA,sBAAC,IAAA;AAAA,sBAAA;AAAA,wBACC,EAAA,EAAG,MAAA;AAAA,wBACH,OAAA,EAAQ,YAAA;AAAA,wBACR,MAAA,EAAO,MAAA;AAAA,wBACP,WAAW,MAAA,CAAO,WAAA;AAAA,wBAEjB,QAAA,EAAA,MAAA,CAAO;AAAA;AAAA;AACV;AAAA,iBAAA;AAAA,gBA3BK,MAAA,CAAO;AAAA,eA4Bd;AAAA,YAEJ,CAAC,CAAA,EACH;AAAA;AAAA,SAAA;AAAA,QAtDG,KAAA,CAAM;AAAA,OAwDb;AAAA,IAEJ,CAAC;AAAA,GAAA,EACH,CAAA;AAEJ;;;;"}
|
|
@@ -92,7 +92,8 @@ function FilterToolbar({
|
|
|
92
92
|
onClearAll,
|
|
93
93
|
active,
|
|
94
94
|
resultCount,
|
|
95
|
-
searchBar
|
|
95
|
+
searchBar,
|
|
96
|
+
viewToggle
|
|
96
97
|
}) {
|
|
97
98
|
return /* @__PURE__ */ jsxs("div", { role: "search", "aria-label": "Filter and search APIs", children: [
|
|
98
99
|
/* @__PURE__ */ jsxs("div", { className: styles.toolbar, children: [
|
|
@@ -106,18 +107,20 @@ function FilterToolbar({
|
|
|
106
107
|
},
|
|
107
108
|
facet.key
|
|
108
109
|
)),
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
110
|
+
/* @__PURE__ */ jsxs("div", { className: styles.searchRow, children: [
|
|
111
|
+
searchBar,
|
|
112
|
+
active ? /* @__PURE__ */ jsx(
|
|
113
|
+
Button,
|
|
114
|
+
{
|
|
115
|
+
variant: "tertiary",
|
|
116
|
+
size: "small",
|
|
117
|
+
iconStart: /* @__PURE__ */ jsx(RiCloseLine, {}),
|
|
118
|
+
onPress: onClearAll,
|
|
119
|
+
children: "Clear all"
|
|
120
|
+
}
|
|
121
|
+
) : null,
|
|
122
|
+
viewToggle
|
|
123
|
+
] })
|
|
121
124
|
] }),
|
|
122
125
|
/* @__PURE__ */ jsx("span", { className: styles.srOnly, "aria-live": "polite", children: `${resultCount} APIs match your filters` })
|
|
123
126
|
] });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FilterToolbar.esm.js","sources":["../../src/components/FilterToolbar.tsx"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\nimport { useEffect, useRef, useState, type ReactNode } from 'react';\nimport { Button, Checkbox } from '@backstage/ui';\nimport { RiArrowDropDownLine, RiCloseLine } from '@remixicon/react';\nimport type { DerivedFacet, FacetKey } from './facetModel';\nimport styles from './FilterToolbar.module.css';\n\nexport interface FilterToolbarProps {\n /** Derived facets from `deriveFacets()`; ALL render (revised D-05) — one with no values is disabled. */\n facets: DerivedFacet[];\n /** Current selections per facet (hydrated from the URL state in 06-04). */\n selected: Record<FacetKey, string[]>;\n onToggleFacet: (key: FacetKey, value: string) => void;\n /** Per-facet menu-footer \"Clear\" (clears just that facet). */\n onClearFacet: (key: FacetKey) => void;\n /** Toolbar \"Clear all\" (resets every facet + search). */\n onClearAll: () => void;\n /** `isActive(state)` — gates the \"Clear all\" affordance. */\n active: boolean;\n /** Filtered result count for the polite live region (UI-07). */\n resultCount: number;\n /** `SearchBar` mounted inline in the toolbar row. */\n searchBar: ReactNode;\n}\n\n/**\n * A single multi-select facet as an outlined disclosure button. Options are @backstage/ui\n * `Checkbox`es in a popover that stays OPEN while toggling (multi-select); the popover closes on\n * outside-click or Escape. All dynamic text (`facet.label`, `option.label`) renders as React\n * children only — never raw HTML (T-06-03-01).\n */\nfunction FacetDropdown({\n facet,\n selected,\n onToggle,\n onClear,\n}: {\n facet: DerivedFacet;\n selected: string[];\n onToggle: (value: string) => void;\n onClear: () => void;\n}) {\n const [open, setOpen] = useState(false);\n const ref = useRef<HTMLDivElement>(null);\n const menuId = `facet-menu-${facet.key}`;\n const count = selected.length;\n\n useEffect(() => {\n if (!open) {\n return undefined;\n }\n const onDocMouseDown = (event: MouseEvent) => {\n if (ref.current && !ref.current.contains(event.target as Node)) {\n setOpen(false);\n }\n };\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key === 'Escape') {\n setOpen(false);\n }\n };\n document.addEventListener('mousedown', onDocMouseDown);\n document.addEventListener('keydown', onKeyDown);\n return () => {\n document.removeEventListener('mousedown', onDocMouseDown);\n document.removeEventListener('keydown', onKeyDown);\n };\n }, [open]);\n\n return (\n <div ref={ref} className={styles.facet}>\n <Button\n variant={count > 0 ? 'primary' : 'secondary'}\n size=\"small\"\n isDisabled={!facet.enabled}\n iconEnd={<RiArrowDropDownLine />}\n aria-haspopup=\"true\"\n aria-expanded={open}\n aria-controls={open ? menuId : undefined}\n aria-label={\n count > 0\n ? `${facet.label} filter, ${count} selected`\n : `${facet.label} filter`\n }\n onPress={() => setOpen(prev => !prev)}\n >\n {facet.label}\n {count > 0 ? <span className={styles.countChip}>{count}</span> : null}\n </Button>\n {open ? (\n <div\n id={menuId}\n className={styles.menu}\n role=\"group\"\n aria-label={`${facet.label} options`}\n >\n {facet.options.map(option => {\n const OptionIcon = facet.icons?.[option.value];\n return (\n <Checkbox\n key={option.value}\n className={styles.option}\n isSelected={selected.includes(option.value)}\n onChange={() => onToggle(option.value)}\n >\n {OptionIcon ? (\n <span className={styles.optionLabel}>\n <OptionIcon aria-hidden size={16} />\n {option.label}\n </span>\n ) : (\n option.label\n )}\n </Checkbox>\n );\n })}\n {count > 0 ? (\n <div className={styles.menuFooter}>\n <Button variant=\"tertiary\" size=\"small\" onPress={onClear}>\n Clear\n </Button>\n </div>\n ) : null}\n </div>\n ) : null}\n </div>\n );\n}\n\n/**\n * Presentational Phase 6 toolbar (D-01 top toolbar, D-02 multi-select dropdowns). Renders EVERY\n * facet in `FACET_ORDER` (revised D-05 — a facet with no values renders disabled), the inline\n * `searchBar` slot, a conditional right-aligned \"Clear all\", and a polite `aria-live` result-count\n * region. Purely props-driven — no data fetching and no URL/provider access (that wiring is 06-04).\n * (UI-06, UI-07)\n */\nexport function FilterToolbar({\n facets,\n selected,\n onToggleFacet,\n onClearFacet,\n onClearAll,\n active,\n resultCount,\n searchBar,\n}: FilterToolbarProps) {\n return (\n <div role=\"search\" aria-label=\"Filter and search APIs\">\n <div className={styles.toolbar}>\n {facets.map(facet => (\n <FacetDropdown\n key={facet.key}\n facet={facet}\n selected={selected[facet.key]}\n onToggle={value => onToggleFacet(facet.key, value)}\n onClear={() => onClearFacet(facet.key)}\n />\n ))}\n {searchBar}\n {active ? (\n <Button\n className={styles.clearAll}\n variant=\"tertiary\"\n size=\"small\"\n iconStart={<RiCloseLine />}\n onPress={onClearAll}\n >\n Clear all\n </Button>\n ) : null}\n </div>\n <span className={styles.srOnly} aria-live=\"polite\">\n {`${resultCount} APIs match your filters`}\n </span>\n </div>\n );\n}\n"],"names":[],"mappings":";;;;;;AA+BA,SAAS,aAAA,CAAc;AAAA,EACrB,KAAA;AAAA,EACA,QAAA;AAAA,EACA,QAAA;AAAA,EACA;AACF,CAAA,EAKG;AACD,EAAA,MAAM,CAAC,IAAA,EAAM,OAAO,CAAA,GAAI,SAAS,KAAK,CAAA;AACtC,EAAA,MAAM,GAAA,GAAM,OAAuB,IAAI,CAAA;AACvC,EAAA,MAAM,MAAA,GAAS,CAAA,WAAA,EAAc,KAAA,CAAM,GAAG,CAAA,CAAA;AACtC,EAAA,MAAM,QAAQ,QAAA,CAAS,MAAA;AAEvB,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,IAAA,EAAM;AACT,MAAA,OAAO,MAAA;AAAA,IACT;AACA,IAAA,MAAM,cAAA,GAAiB,CAAC,KAAA,KAAsB;AAC5C,MAAA,IAAI,GAAA,CAAI,WAAW,CAAC,GAAA,CAAI,QAAQ,QAAA,CAAS,KAAA,CAAM,MAAc,CAAA,EAAG;AAC9D,QAAA,OAAA,CAAQ,KAAK,CAAA;AAAA,MACf;AAAA,IACF,CAAA;AACA,IAAA,MAAM,SAAA,GAAY,CAAC,KAAA,KAAyB;AAC1C,MAAA,IAAI,KAAA,CAAM,QAAQ,QAAA,EAAU;AAC1B,QAAA,OAAA,CAAQ,KAAK,CAAA;AAAA,MACf;AAAA,IACF,CAAA;AACA,IAAA,QAAA,CAAS,gBAAA,CAAiB,aAAa,cAAc,CAAA;AACrD,IAAA,QAAA,CAAS,gBAAA,CAAiB,WAAW,SAAS,CAAA;AAC9C,IAAA,OAAO,MAAM;AACX,MAAA,QAAA,CAAS,mBAAA,CAAoB,aAAa,cAAc,CAAA;AACxD,MAAA,QAAA,CAAS,mBAAA,CAAoB,WAAW,SAAS,CAAA;AAAA,IACnD,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,IAAI,CAAC,CAAA;AAET,EAAA,uBACE,IAAA,CAAC,KAAA,EAAA,EAAI,GAAA,EAAU,SAAA,EAAW,OAAO,KAAA,EAC/B,QAAA,EAAA;AAAA,oBAAA,IAAA;AAAA,MAAC,MAAA;AAAA,MAAA;AAAA,QACC,OAAA,EAAS,KAAA,GAAQ,CAAA,GAAI,SAAA,GAAY,WAAA;AAAA,QACjC,IAAA,EAAK,OAAA;AAAA,QACL,UAAA,EAAY,CAAC,KAAA,CAAM,OAAA;AAAA,QACnB,OAAA,sBAAU,mBAAA,EAAA,EAAoB,CAAA;AAAA,QAC9B,eAAA,EAAc,MAAA;AAAA,QACd,eAAA,EAAe,IAAA;AAAA,QACf,eAAA,EAAe,OAAO,MAAA,GAAS,MAAA;AAAA,QAC/B,YAAA,EACE,KAAA,GAAQ,CAAA,GACJ,CAAA,EAAG,KAAA,CAAM,KAAK,CAAA,SAAA,EAAY,KAAK,CAAA,SAAA,CAAA,GAC/B,CAAA,EAAG,KAAA,CAAM,KAAK,CAAA,OAAA,CAAA;AAAA,QAEpB,OAAA,EAAS,MAAM,OAAA,CAAQ,CAAA,IAAA,KAAQ,CAAC,IAAI,CAAA;AAAA,QAEnC,QAAA,EAAA;AAAA,UAAA,KAAA,CAAM,KAAA;AAAA,UACN,KAAA,GAAQ,oBAAI,GAAA,CAAC,MAAA,EAAA,EAAK,WAAW,MAAA,CAAO,SAAA,EAAY,iBAAM,CAAA,GAAU;AAAA;AAAA;AAAA,KACnE;AAAA,IACC,IAAA,mBACC,IAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,EAAA,EAAI,MAAA;AAAA,QACJ,WAAW,MAAA,CAAO,IAAA;AAAA,QAClB,IAAA,EAAK,OAAA;AAAA,QACL,YAAA,EAAY,CAAA,EAAG,KAAA,CAAM,KAAK,CAAA,QAAA,CAAA;AAAA,QAEzB,QAAA,EAAA;AAAA,UAAA,KAAA,CAAM,OAAA,CAAQ,IAAI,CAAA,MAAA,KAAU;AAC3B,YAAA,MAAM,UAAA,GAAa,KAAA,CAAM,KAAA,GAAQ,MAAA,CAAO,KAAK,CAAA;AAC7C,YAAA,uBACE,GAAA;AAAA,cAAC,QAAA;AAAA,cAAA;AAAA,gBAEC,WAAW,MAAA,CAAO,MAAA;AAAA,gBAClB,UAAA,EAAY,QAAA,CAAS,QAAA,CAAS,MAAA,CAAO,KAAK,CAAA;AAAA,gBAC1C,QAAA,EAAU,MAAM,QAAA,CAAS,MAAA,CAAO,KAAK,CAAA;AAAA,gBAEpC,QAAA,EAAA,UAAA,mBACC,IAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAW,OAAO,WAAA,EACtB,QAAA,EAAA;AAAA,kCAAA,GAAA,CAAC,UAAA,EAAA,EAAW,aAAA,EAAW,IAAA,EAAC,IAAA,EAAM,EAAA,EAAI,CAAA;AAAA,kBACjC,MAAA,CAAO;AAAA,iBAAA,EACV,IAEA,MAAA,CAAO;AAAA,eAAA;AAAA,cAXJ,MAAA,CAAO;AAAA,aAad;AAAA,UAEJ,CAAC,CAAA;AAAA,UACA,QAAQ,CAAA,mBACP,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAW,OAAO,UAAA,EACrB,QAAA,kBAAA,GAAA,CAAC,MAAA,EAAA,EAAO,OAAA,EAAQ,YAAW,IAAA,EAAK,OAAA,EAAQ,SAAS,OAAA,EAAS,QAAA,EAAA,OAAA,EAE1D,GACF,CAAA,GACE;AAAA;AAAA;AAAA,KACN,GACE;AAAA,GAAA,EACN,CAAA;AAEJ;AASO,SAAS,aAAA,CAAc;AAAA,EAC5B,MAAA;AAAA,EACA,QAAA;AAAA,EACA,aAAA;AAAA,EACA,YAAA;AAAA,EACA,UAAA;AAAA,EACA,MAAA;AAAA,EACA,WAAA;AAAA,EACA;AACF,CAAA,EAAuB;AACrB,EAAA,uBACE,IAAA,CAAC,KAAA,EAAA,EAAI,IAAA,EAAK,QAAA,EAAS,cAAW,wBAAA,EAC5B,QAAA,EAAA;AAAA,oBAAA,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAW,MAAA,CAAO,OAAA,EACpB,QAAA,EAAA;AAAA,MAAA,MAAA,CAAO,IAAI,CAAA,KAAA,qBACV,GAAA;AAAA,QAAC,aAAA;AAAA,QAAA;AAAA,UAEC,KAAA;AAAA,UACA,QAAA,EAAU,QAAA,CAAS,KAAA,CAAM,GAAG,CAAA;AAAA,UAC5B,QAAA,EAAU,CAAA,KAAA,KAAS,aAAA,CAAc,KAAA,CAAM,KAAK,KAAK,CAAA;AAAA,UACjD,OAAA,EAAS,MAAM,YAAA,CAAa,KAAA,CAAM,GAAG;AAAA,SAAA;AAAA,QAJhC,KAAA,CAAM;AAAA,OAMd,CAAA;AAAA,MACA,SAAA;AAAA,MACA,MAAA,mBACC,GAAA;AAAA,QAAC,MAAA;AAAA,QAAA;AAAA,UACC,WAAW,MAAA,CAAO,QAAA;AAAA,UAClB,OAAA,EAAQ,UAAA;AAAA,UACR,IAAA,EAAK,OAAA;AAAA,UACL,SAAA,sBAAY,WAAA,EAAA,EAAY,CAAA;AAAA,UACxB,OAAA,EAAS,UAAA;AAAA,UACV,QAAA,EAAA;AAAA;AAAA,OAED,GACE;AAAA,KAAA,EACN,CAAA;AAAA,oBACA,GAAA,CAAC,UAAK,SAAA,EAAW,MAAA,CAAO,QAAQ,WAAA,EAAU,QAAA,EACvC,QAAA,EAAA,CAAA,EAAG,WAAW,CAAA,wBAAA,CAAA,EACjB;AAAA,GAAA,EACF,CAAA;AAEJ;;;;"}
|
|
1
|
+
{"version":3,"file":"FilterToolbar.esm.js","sources":["../../src/components/FilterToolbar.tsx"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\nimport { useEffect, useRef, useState, type ReactNode } from 'react';\nimport { Button, Checkbox } from '@backstage/ui';\nimport { RiArrowDropDownLine, RiCloseLine } from '@remixicon/react';\nimport type { DerivedFacet, FacetKey } from './facetModel';\nimport styles from './FilterToolbar.module.css';\n\nexport interface FilterToolbarProps {\n /** Derived facets from `deriveFacets()`; ALL render (revised D-05) — one with no values is disabled. */\n facets: DerivedFacet[];\n /** Current selections per facet (hydrated from the URL state in 06-04). */\n selected: Record<FacetKey, string[]>;\n onToggleFacet: (key: FacetKey, value: string) => void;\n /** Per-facet menu-footer \"Clear\" (clears just that facet). */\n onClearFacet: (key: FacetKey) => void;\n /** Toolbar \"Clear all\" (resets every facet + search). */\n onClearAll: () => void;\n /** `isActive(state)` — gates the \"Clear all\" affordance. */\n active: boolean;\n /** Filtered result count for the polite live region (UI-07). */\n resultCount: number;\n /** `SearchBar` mounted inline in the toolbar row. */\n searchBar: ReactNode;\n /** View-mode toggle (Flat/List) mounted at the right of the toolbar row. */\n viewToggle?: ReactNode;\n}\n\n/**\n * A single multi-select facet as an outlined disclosure button. Options are @backstage/ui\n * `Checkbox`es in a popover that stays OPEN while toggling (multi-select); the popover closes on\n * outside-click or Escape. All dynamic text (`facet.label`, `option.label`) renders as React\n * children only — never raw HTML (T-06-03-01).\n */\nfunction FacetDropdown({\n facet,\n selected,\n onToggle,\n onClear,\n}: {\n facet: DerivedFacet;\n selected: string[];\n onToggle: (value: string) => void;\n onClear: () => void;\n}) {\n const [open, setOpen] = useState(false);\n const ref = useRef<HTMLDivElement>(null);\n const menuId = `facet-menu-${facet.key}`;\n const count = selected.length;\n\n useEffect(() => {\n if (!open) {\n return undefined;\n }\n const onDocMouseDown = (event: MouseEvent) => {\n if (ref.current && !ref.current.contains(event.target as Node)) {\n setOpen(false);\n }\n };\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key === 'Escape') {\n setOpen(false);\n }\n };\n document.addEventListener('mousedown', onDocMouseDown);\n document.addEventListener('keydown', onKeyDown);\n return () => {\n document.removeEventListener('mousedown', onDocMouseDown);\n document.removeEventListener('keydown', onKeyDown);\n };\n }, [open]);\n\n return (\n <div ref={ref} className={styles.facet}>\n <Button\n variant={count > 0 ? 'primary' : 'secondary'}\n size=\"small\"\n isDisabled={!facet.enabled}\n iconEnd={<RiArrowDropDownLine />}\n aria-haspopup=\"true\"\n aria-expanded={open}\n aria-controls={open ? menuId : undefined}\n aria-label={\n count > 0\n ? `${facet.label} filter, ${count} selected`\n : `${facet.label} filter`\n }\n onPress={() => setOpen(prev => !prev)}\n >\n {facet.label}\n {count > 0 ? <span className={styles.countChip}>{count}</span> : null}\n </Button>\n {open ? (\n <div\n id={menuId}\n className={styles.menu}\n role=\"group\"\n aria-label={`${facet.label} options`}\n >\n {facet.options.map(option => {\n const OptionIcon = facet.icons?.[option.value];\n return (\n <Checkbox\n key={option.value}\n className={styles.option}\n isSelected={selected.includes(option.value)}\n onChange={() => onToggle(option.value)}\n >\n {OptionIcon ? (\n <span className={styles.optionLabel}>\n <OptionIcon aria-hidden size={16} />\n {option.label}\n </span>\n ) : (\n option.label\n )}\n </Checkbox>\n );\n })}\n {count > 0 ? (\n <div className={styles.menuFooter}>\n <Button variant=\"tertiary\" size=\"small\" onPress={onClear}>\n Clear\n </Button>\n </div>\n ) : null}\n </div>\n ) : null}\n </div>\n );\n}\n\n/**\n * Presentational Phase 6 toolbar (D-01 top toolbar, D-02 multi-select dropdowns). Renders EVERY\n * facet in `FACET_ORDER` (revised D-05 — a facet with no values renders disabled) followed on the\n * same row by the inline `searchBar` paired with the `viewToggle` (and a conditional \"Clear all\"),\n * plus a polite `aria-live` result-count region. Purely props-driven — no data fetching and no\n * URL/provider access (that wiring is 06-04). (UI-06, UI-07)\n */\nexport function FilterToolbar({\n facets,\n selected,\n onToggleFacet,\n onClearFacet,\n onClearAll,\n active,\n resultCount,\n searchBar,\n viewToggle,\n}: FilterToolbarProps) {\n return (\n <div role=\"search\" aria-label=\"Filter and search APIs\">\n <div className={styles.toolbar}>\n {facets.map(facet => (\n <FacetDropdown\n key={facet.key}\n facet={facet}\n selected={selected[facet.key]}\n onToggle={value => onToggleFacet(facet.key, value)}\n onClear={() => onClearFacet(facet.key)}\n />\n ))}\n <div className={styles.searchRow}>\n {searchBar}\n {active ? (\n <Button\n variant=\"tertiary\"\n size=\"small\"\n iconStart={<RiCloseLine />}\n onPress={onClearAll}\n >\n Clear all\n </Button>\n ) : null}\n {viewToggle}\n </div>\n </div>\n <span className={styles.srOnly} aria-live=\"polite\">\n {`${resultCount} APIs match your filters`}\n </span>\n </div>\n );\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import styleInject from '../node_modules_dist/style-inject/dist/style-inject.es.esm.js';
|
|
2
2
|
|
|
3
|
-
var css_248z = ".
|
|
4
|
-
var styles = {"toolbar":"
|
|
3
|
+
var css_248z = ".FilterToolbar_toolbar__0a37ccc2c0 {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--bui-space-2);\n margin-bottom: var(--bui-space-4);\n}\n\n.FilterToolbar_facet__0a37ccc2c0 {\n position: relative;\n}\n\n.FilterToolbar_countChip__0a37ccc2c0 {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 18px;\n height: 18px;\n margin-left: var(--bui-space-1);\n padding: 0 var(--bui-space-1);\n border-radius: var(--bui-radius-full);\n background-color: var(--bui-bg-solid);\n color: var(--bui-fg-solid);\n font-size: var(--bui-font-size-1);\n font-variant-numeric: tabular-nums;\n}\n\n.FilterToolbar_menu__0a37ccc2c0 {\n position: absolute;\n top: calc(100% + var(--bui-space-1));\n left: 0;\n z-index: 10;\n min-width: 200px;\n padding: var(--bui-space-2) 0;\n border: 1px solid var(--bui-border-1);\n border-radius: var(--bui-radius-3);\n background-color: var(--bui-bg-app);\n box-shadow: var(--bui-shadow);\n}\n\n.FilterToolbar_option__0a37ccc2c0 {\n padding: var(--bui-space-1) var(--bui-space-3);\n}\n\n.FilterToolbar_optionLabel__0a37ccc2c0 {\n display: inline-flex;\n align-items: center;\n gap: var(--bui-space-1);\n}\n\n.FilterToolbar_menuFooter__0a37ccc2c0 {\n display: flex;\n justify-content: flex-end;\n padding: var(--bui-space-1) var(--bui-space-2) 0;\n}\n\n.FilterToolbar_searchRow__0a37ccc2c0 {\n flex: 1 1 auto;\n min-width: 0;\n display: flex;\n align-items: center;\n gap: var(--bui-space-2);\n}\n\n.FilterToolbar_srOnly__0a37ccc2c0 {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0 0 0 0);\n white-space: nowrap;\n border: 0;\n}\n";
|
|
4
|
+
var styles = {"toolbar":"FilterToolbar_toolbar__0a37ccc2c0","facet":"FilterToolbar_facet__0a37ccc2c0","countChip":"FilterToolbar_countChip__0a37ccc2c0","menu":"FilterToolbar_menu__0a37ccc2c0","option":"FilterToolbar_option__0a37ccc2c0","optionLabel":"FilterToolbar_optionLabel__0a37ccc2c0","menuFooter":"FilterToolbar_menuFooter__0a37ccc2c0","searchRow":"FilterToolbar_searchRow__0a37ccc2c0","srOnly":"FilterToolbar_srOnly__0a37ccc2c0"};
|
|
5
5
|
styleInject(css_248z);
|
|
6
6
|
|
|
7
7
|
export { styles as default };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { parseEntityRef } from '@backstage/catalog-model';
|
|
2
2
|
import { humanizeEntityRef } from '@backstage/plugin-catalog-react';
|
|
3
|
-
import { ANNOTATION_CATEGORY,
|
|
3
|
+
import { ANNOTATION_CATEGORY, ANNOTATION_VERSION, ANNOTATION_PROXY_NAME, ANNOTATION_HAS_SPEC, LABEL_DEPARTMENT, ANNOTATION_APIGEE_ORGS, ANNOTATION_APIGEE_ORG } from '../constants.esm.js';
|
|
4
4
|
|
|
5
5
|
function toBadge(lifecycle) {
|
|
6
6
|
switch (lifecycle) {
|
|
@@ -14,6 +14,18 @@ function toBadge(lifecycle) {
|
|
|
14
14
|
return { label: "Unknown", kind: "warning" };
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
|
+
function toVisibilities(annotations) {
|
|
18
|
+
const raw = annotations[ANNOTATION_APIGEE_ORGS] ?? annotations[ANNOTATION_APIGEE_ORG] ?? "";
|
|
19
|
+
const orgs = raw.split(",").map((org) => org.trim().toLowerCase()).filter(Boolean);
|
|
20
|
+
const visibilities = [];
|
|
21
|
+
if (orgs.some((org) => org.includes("private"))) visibilities.push("private");
|
|
22
|
+
if (orgs.some((org) => org.includes("public"))) visibilities.push("public");
|
|
23
|
+
return visibilities;
|
|
24
|
+
}
|
|
25
|
+
function versionFromProxyName(proxyName) {
|
|
26
|
+
const match = proxyName?.match(/-v(\d+)$/i);
|
|
27
|
+
return match ? `${Number(match[1])}.0.0` : void 0;
|
|
28
|
+
}
|
|
17
29
|
function toApiCardViewModel(entity) {
|
|
18
30
|
const annotations = entity.metadata.annotations ?? {};
|
|
19
31
|
const owner = entity.spec?.owner;
|
|
@@ -24,9 +36,11 @@ function toApiCardViewModel(entity) {
|
|
|
24
36
|
parseEntityRef(owner, { defaultKind: "group", defaultNamespace: "default" }),
|
|
25
37
|
{ defaultKind: "group" }
|
|
26
38
|
) : "unknown",
|
|
39
|
+
department: entity.metadata.labels?.[LABEL_DEPARTMENT] || void 0,
|
|
40
|
+
visibilities: toVisibilities(annotations),
|
|
27
41
|
badge: toBadge(entity.spec?.lifecycle),
|
|
28
42
|
hasSpec: annotations[ANNOTATION_HAS_SPEC] === "true",
|
|
29
|
-
version: annotations[
|
|
43
|
+
version: annotations[ANNOTATION_VERSION] || versionFromProxyName(annotations[ANNOTATION_PROXY_NAME]) || void 0,
|
|
30
44
|
category: annotations[ANNOTATION_CATEGORY]?.split(",")[0]?.trim() || void 0,
|
|
31
45
|
description,
|
|
32
46
|
targetRef: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apiCardViewModel.esm.js","sources":["../../src/components/apiCardViewModel.ts"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\nimport { type Entity, parseEntityRef } from '@backstage/catalog-model';\nimport { humanizeEntityRef } from '@backstage/plugin-catalog-react';\nimport {\n ANNOTATION_HAS_SPEC,\n ANNOTATION_CATEGORY,\n
|
|
1
|
+
{"version":3,"file":"apiCardViewModel.esm.js","sources":["../../src/components/apiCardViewModel.ts"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\nimport { type Entity, parseEntityRef } from '@backstage/catalog-model';\nimport { humanizeEntityRef } from '@backstage/plugin-catalog-react';\nimport {\n ANNOTATION_HAS_SPEC,\n ANNOTATION_CATEGORY,\n ANNOTATION_VERSION,\n ANNOTATION_PROXY_NAME,\n ANNOTATION_APIGEE_ORGS,\n ANNOTATION_APIGEE_ORG,\n LABEL_DEPARTMENT,\n} from '../constants';\n\nexport type LifecycleBadgeKind = 'ok' | 'warning' | 'error';\n\n/** Apigee org exposure shown on the card, derived from the org name. */\nexport type ApiVisibility = 'private' | 'public';\n\nexport interface ApiCardViewModel {\n /** metadata.title ?? metadata.name */\n name: string;\n /** humanizeEntityRef(spec.owner, { defaultKind: 'group' }) — 'unknown' when absent */\n ownerLabel: string;\n /** metadata.labels.department — omitted when absent */\n department?: string;\n /** Exposure derived from apigee.com/orgs — 'private', 'public', or both; [] when none. */\n visibilities: ApiVisibility[];\n badge: { label: string; kind: LifecycleBadgeKind };\n /** annotations['apigee.com/has-spec'] === 'true' */\n hasSpec: boolean;\n /** annotations['apigee.com/version'], else the proxy name's -v<major> as x.0.0 — omitted when absent (D-05) */\n version?: string;\n /** first comma-separated slug of apigee.com/category — undefined => 'Other' bucket */\n category?: string;\n /** metadata.description — omitted when empty */\n description?: string;\n /** for entityRouteRef navigation (05-04) */\n targetRef: { kind: 'API'; namespace: string; name: string };\n}\n\n/** D-04 lifecycle -> badge mapping. Unknown/undeployed/missing default to warning. */\nfunction toBadge(lifecycle: string | undefined): {\n label: string;\n kind: LifecycleBadgeKind;\n} {\n switch (lifecycle) {\n case 'production':\n return { label: 'Production', kind: 'ok' };\n case 'deprecated':\n return { label: 'Deprecated', kind: 'error' };\n case 'experimental':\n return { label: 'Experimental', kind: 'warning' };\n default:\n return { label: 'Unknown', kind: 'warning' };\n }\n}\n\n/** Derive exposure from the Apigee org annotation(s). Org names follow\n * `apigee-x-{private|public}-p-<id>`; an API can be published to one or both. */\nfunction toVisibilities(annotations: Record<string, string>): ApiVisibility[] {\n const raw =\n annotations[ANNOTATION_APIGEE_ORGS] ?? annotations[ANNOTATION_APIGEE_ORG] ?? '';\n const orgs = raw\n .split(',')\n .map(org => org.trim().toLowerCase())\n .filter(Boolean);\n const visibilities: ApiVisibility[] = [];\n if (orgs.some(org => org.includes('private'))) visibilities.push('private');\n if (orgs.some(org => org.includes('public'))) visibilities.push('public');\n return visibilities;\n}\n\n/** Extract a semantic version from a proxy name's trailing `-v<major>` segment\n * (e.g. `...-v4` -> `4.0.0`). Undefined when the name has no version suffix. */\nfunction versionFromProxyName(proxyName: string | undefined): string | undefined {\n const match = proxyName?.match(/-v(\\d+)$/i);\n return match ? `${Number(match[1])}.0.0` : undefined;\n}\n\n/** PURE mapper: an Apigee `kind: API` entity -> the view-model each tile renders. */\nexport function toApiCardViewModel(entity: Entity): ApiCardViewModel {\n const annotations = entity.metadata.annotations ?? {};\n const owner = entity.spec?.owner as string | undefined;\n const description = entity.metadata.description?.trim()\n ? entity.metadata.description\n : undefined;\n\n return {\n name: entity.metadata.title ?? entity.metadata.name,\n ownerLabel: owner\n ? humanizeEntityRef(\n parseEntityRef(owner, { defaultKind: 'group', defaultNamespace: 'default' }),\n { defaultKind: 'group' },\n )\n : 'unknown',\n department: entity.metadata.labels?.[LABEL_DEPARTMENT] || undefined,\n visibilities: toVisibilities(annotations),\n badge: toBadge(entity.spec?.lifecycle as string | undefined),\n hasSpec: annotations[ANNOTATION_HAS_SPEC] === 'true',\n version:\n annotations[ANNOTATION_VERSION] ||\n versionFromProxyName(annotations[ANNOTATION_PROXY_NAME]) ||\n undefined,\n category: annotations[ANNOTATION_CATEGORY]?.split(',')[0]?.trim() || undefined,\n description,\n targetRef: {\n kind: 'API',\n namespace: entity.metadata.namespace ?? 'default',\n name: entity.metadata.name,\n },\n };\n}\n"],"names":[],"mappings":";;;;AAyCA,SAAS,QAAQ,SAAA,EAGf;AACA,EAAA,QAAQ,SAAA;AAAW,IACjB,KAAK,YAAA;AACH,MAAA,OAAO,EAAE,KAAA,EAAO,YAAA,EAAc,IAAA,EAAM,IAAA,EAAK;AAAA,IAC3C,KAAK,YAAA;AACH,MAAA,OAAO,EAAE,KAAA,EAAO,YAAA,EAAc,IAAA,EAAM,OAAA,EAAQ;AAAA,IAC9C,KAAK,cAAA;AACH,MAAA,OAAO,EAAE,KAAA,EAAO,cAAA,EAAgB,IAAA,EAAM,SAAA,EAAU;AAAA,IAClD;AACE,MAAA,OAAO,EAAE,KAAA,EAAO,SAAA,EAAW,IAAA,EAAM,SAAA,EAAU;AAAA;AAEjD;AAIA,SAAS,eAAe,WAAA,EAAsD;AAC5E,EAAA,MAAM,MACJ,WAAA,CAAY,sBAAsB,CAAA,IAAK,WAAA,CAAY,qBAAqB,CAAA,IAAK,EAAA;AAC/E,EAAA,MAAM,IAAA,GAAO,GAAA,CACV,KAAA,CAAM,GAAG,EACT,GAAA,CAAI,CAAA,GAAA,KAAO,GAAA,CAAI,IAAA,EAAK,CAAE,WAAA,EAAa,CAAA,CACnC,OAAO,OAAO,CAAA;AACjB,EAAA,MAAM,eAAgC,EAAC;AACvC,EAAA,IAAI,IAAA,CAAK,IAAA,CAAK,CAAA,GAAA,KAAO,GAAA,CAAI,QAAA,CAAS,SAAS,CAAC,CAAA,EAAG,YAAA,CAAa,IAAA,CAAK,SAAS,CAAA;AAC1E,EAAA,IAAI,IAAA,CAAK,IAAA,CAAK,CAAA,GAAA,KAAO,GAAA,CAAI,QAAA,CAAS,QAAQ,CAAC,CAAA,EAAG,YAAA,CAAa,IAAA,CAAK,QAAQ,CAAA;AACxE,EAAA,OAAO,YAAA;AACT;AAIA,SAAS,qBAAqB,SAAA,EAAmD;AAC/E,EAAA,MAAM,KAAA,GAAQ,SAAA,EAAW,KAAA,CAAM,WAAW,CAAA;AAC1C,EAAA,OAAO,QAAQ,CAAA,EAAG,MAAA,CAAO,MAAM,CAAC,CAAC,CAAC,CAAA,IAAA,CAAA,GAAS,MAAA;AAC7C;AAGO,SAAS,mBAAmB,MAAA,EAAkC;AACnE,EAAA,MAAM,WAAA,GAAc,MAAA,CAAO,QAAA,CAAS,WAAA,IAAe,EAAC;AACpD,EAAA,MAAM,KAAA,GAAQ,OAAO,IAAA,EAAM,KAAA;AAC3B,EAAA,MAAM,WAAA,GAAc,OAAO,QAAA,CAAS,WAAA,EAAa,MAAK,GAClD,MAAA,CAAO,SAAS,WAAA,GAChB,MAAA;AAEJ,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,MAAA,CAAO,QAAA,CAAS,KAAA,IAAS,OAAO,QAAA,CAAS,IAAA;AAAA,IAC/C,YAAY,KAAA,GACR,iBAAA;AAAA,MACE,eAAe,KAAA,EAAO,EAAE,aAAa,OAAA,EAAS,gBAAA,EAAkB,WAAW,CAAA;AAAA,MAC3E,EAAE,aAAa,OAAA;AAAQ,KACzB,GACA,SAAA;AAAA,IACJ,UAAA,EAAY,MAAA,CAAO,QAAA,CAAS,MAAA,GAAS,gBAAgB,CAAA,IAAK,MAAA;AAAA,IAC1D,YAAA,EAAc,eAAe,WAAW,CAAA;AAAA,IACxC,KAAA,EAAO,OAAA,CAAQ,MAAA,CAAO,IAAA,EAAM,SAA+B,CAAA;AAAA,IAC3D,OAAA,EAAS,WAAA,CAAY,mBAAmB,CAAA,KAAM,MAAA;AAAA,IAC9C,OAAA,EACE,YAAY,kBAAkB,CAAA,IAC9B,qBAAqB,WAAA,CAAY,qBAAqB,CAAC,CAAA,IACvD,MAAA;AAAA,IACF,QAAA,EAAU,WAAA,CAAY,mBAAmB,CAAA,EAAG,KAAA,CAAM,GAAG,CAAA,CAAE,CAAC,CAAA,EAAG,IAAA,EAAK,IAAK,MAAA;AAAA,IACrE,WAAA;AAAA,IACA,SAAA,EAAW;AAAA,MACT,IAAA,EAAM,KAAA;AAAA,MACN,SAAA,EAAW,MAAA,CAAO,QAAA,CAAS,SAAA,IAAa,SAAA;AAAA,MACxC,IAAA,EAAM,OAAO,QAAA,CAAS;AAAA;AACxB,GACF;AACF;;;;"}
|
|
@@ -1,31 +1,6 @@
|
|
|
1
1
|
function humanise(slug) {
|
|
2
2
|
return slug.split(/[-_]/).filter(Boolean).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
3
3
|
}
|
|
4
|
-
function groupByCategory(items) {
|
|
5
|
-
const buckets = /* @__PURE__ */ new Map();
|
|
6
|
-
for (const item of items) {
|
|
7
|
-
const key = item.category?.trim() || "other";
|
|
8
|
-
const existing = buckets.get(key);
|
|
9
|
-
if (existing) {
|
|
10
|
-
existing.push(item);
|
|
11
|
-
} else {
|
|
12
|
-
buckets.set(key, [item]);
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
const groups = Array.from(buckets.entries()).map(
|
|
16
|
-
([key, groupItems]) => ({
|
|
17
|
-
key,
|
|
18
|
-
label: key === "other" ? "Other" : humanise(key),
|
|
19
|
-
items: groupItems
|
|
20
|
-
})
|
|
21
|
-
);
|
|
22
|
-
groups.sort((a, b) => {
|
|
23
|
-
if (a.key === "other") return 1;
|
|
24
|
-
if (b.key === "other") return -1;
|
|
25
|
-
return a.label.localeCompare(b.label);
|
|
26
|
-
});
|
|
27
|
-
return groups;
|
|
28
|
-
}
|
|
29
4
|
|
|
30
|
-
export {
|
|
5
|
+
export { humanise };
|
|
31
6
|
//# sourceMappingURL=grouping.esm.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"grouping.esm.js","sources":["../../src/components/grouping.ts"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\
|
|
1
|
+
{"version":3,"file":"grouping.esm.js","sources":["../../src/components/grouping.ts"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/** Humanise a slug: split on `-`/`_`, drop empties, capitalise each word. */\nexport function humanise(slug: string): string {\n return slug\n .split(/[-_]/)\n .filter(Boolean)\n .map(word => word.charAt(0).toUpperCase() + word.slice(1))\n .join(' ');\n}\n"],"names":[],"mappings":"AAGO,SAAS,SAAS,IAAA,EAAsB;AAC7C,EAAA,OAAO,IAAA,CACJ,MAAM,MAAM,CAAA,CACZ,OAAO,OAAO,CAAA,CACd,IAAI,CAAA,IAAA,KAAQ,IAAA,CAAK,OAAO,CAAC,CAAA,CAAE,aAAY,GAAI,IAAA,CAAK,MAAM,CAAC,CAAC,CAAA,CACxD,IAAA,CAAK,GAAG,CAAA;AACb;;;;"}
|
package/dist/constants.esm.js
CHANGED
|
@@ -2,8 +2,9 @@ const ANNOTATION_APIGEE_ORGS = "apigee.com/orgs";
|
|
|
2
2
|
const ANNOTATION_APIGEE_ORG = "apigee.com/org";
|
|
3
3
|
const ANNOTATION_HAS_SPEC = "apigee.com/has-spec";
|
|
4
4
|
const ANNOTATION_CATEGORY = "apigee.com/category";
|
|
5
|
-
const
|
|
5
|
+
const ANNOTATION_VERSION = "apigee.com/version";
|
|
6
|
+
const ANNOTATION_PROXY_NAME = "apigee.com/proxy-name";
|
|
6
7
|
const LABEL_DEPARTMENT = "department";
|
|
7
8
|
|
|
8
|
-
export { ANNOTATION_APIGEE_ORG, ANNOTATION_APIGEE_ORGS,
|
|
9
|
+
export { ANNOTATION_APIGEE_ORG, ANNOTATION_APIGEE_ORGS, ANNOTATION_CATEGORY, ANNOTATION_HAS_SPEC, ANNOTATION_PROXY_NAME, ANNOTATION_VERSION, LABEL_DEPARTMENT };
|
|
9
10
|
//# sourceMappingURL=constants.esm.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.esm.js","sources":["../src/constants.ts"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n// Apigee catalog annotation keys. Authoritative source:\n// plugins/apigee-backend/src/lib/entity-builder.ts (verified 2026-07-09).\n\n/** Org annotation written by proxyToEntities (PLURAL, proxy-derived API entities). */\nexport const ANNOTATION_APIGEE_ORGS = 'apigee.com/orgs';\n/** Org annotation written by hubOnlyToEntity (SINGULAR, hub-only API entities). */\nexport const ANNOTATION_APIGEE_ORG = 'apigee.com/org';\n/** 'true' | 'false' — set by applyHubMetadata when a spec is embedded or linked. */\nexport const ANNOTATION_HAS_SPEC = 'apigee.com/has-spec';\n/** Comma-joined slugified categories (absent in the current seed dump). */\nexport const ANNOTATION_CATEGORY = 'apigee.com/category';\n/** Full API Hub resource name — present only on hub-only entities. */\nexport const ANNOTATION_API_HUB_ID = 'apigee.com/api-hub-id';\n/**\n *
|
|
1
|
+
{"version":3,"file":"constants.esm.js","sources":["../src/constants.ts"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n// Apigee catalog annotation keys. Authoritative source:\n// plugins/apigee-backend/src/lib/entity-builder.ts (verified 2026-07-09).\n\n/** Org annotation written by proxyToEntities (PLURAL, proxy-derived API entities). */\nexport const ANNOTATION_APIGEE_ORGS = 'apigee.com/orgs';\n/** Org annotation written by hubOnlyToEntity (SINGULAR, hub-only API entities). */\nexport const ANNOTATION_APIGEE_ORG = 'apigee.com/org';\n/** 'true' | 'false' — set by applyHubMetadata when a spec is embedded or linked. */\nexport const ANNOTATION_HAS_SPEC = 'apigee.com/has-spec';\n/** Comma-joined slugified categories (absent in the current seed dump). */\nexport const ANNOTATION_CATEGORY = 'apigee.com/category';\n/** Full API Hub resource name — present only on hub-only entities. */\nexport const ANNOTATION_API_HUB_ID = 'apigee.com/api-hub-id';\n/**\n * Spec `info.version` — emitted by the backend on both Component and API entities\n * (entity-provider.ts / applyHubMetadata). Primary source for the card version line.\n */\nexport const ANNOTATION_VERSION = 'apigee.com/version';\n/**\n * Proxy name — when `apigee.com/version` is absent, the card derives the version\n * from this name's trailing `-v<major>` segment (e.g. `...-v4` -> `4.0.0`).\n */\nexport const ANNOTATION_PROXY_NAME = 'apigee.com/proxy-name';\n\n/**\n * Department label key (`metadata.labels.department`). Written by the backend's\n * applyHubMetadata from the API Hub \"Business unit\" attribute.\n */\nexport const LABEL_DEPARTMENT = 'department';\n"],"names":[],"mappings":"AAKO,MAAM,sBAAA,GAAyB;AAE/B,MAAM,qBAAA,GAAwB;AAE9B,MAAM,mBAAA,GAAsB;AAE5B,MAAM,mBAAA,GAAsB;AAO5B,MAAM,kBAAA,GAAqB;AAK3B,MAAM,qBAAA,GAAwB;AAM9B,MAAM,gBAAA,GAAmB;;;;"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nospt/backstage-plugin-apigee",
|
|
3
|
-
"version": "1.1.0-
|
|
3
|
+
"version": "1.1.0-rc4",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"description": "Backstage frontend plugin bootstrap for Apigee integration",
|
|
6
6
|
"author": "NOS SGPS, S.A.",
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"@backstage/frontend-plugin-api": "^0.17.0",
|
|
54
54
|
"@backstage/plugin-catalog-react": "^3.0.0",
|
|
55
55
|
"@backstage/ui": "^0.15.0",
|
|
56
|
-
"@nospt/backstage-plugin-apigee-common": "^1.1.0-
|
|
56
|
+
"@nospt/backstage-plugin-apigee-common": "^1.1.0-rc4",
|
|
57
57
|
"@remixicon/react": "^4.6.0"
|
|
58
58
|
},
|
|
59
59
|
"peerDependencies": {
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import styleInject from '../node_modules_dist/style-inject/dist/style-inject.es.esm.js';
|
|
2
|
-
|
|
3
|
-
var css_248z = ".ApiCardGrid_section__f119e0fd3b {\n margin-bottom: var(--bui-space-8);\n}\n\n.ApiCardGrid_sectionHeader__f119e0fd3b {\n display: flex;\n align-items: center;\n gap: var(--bui-space-2);\n margin-bottom: var(--bui-space-4);\n}\n\n.ApiCardGrid_sectionIcon__f119e0fd3b {\n color: var(--bui-fg-secondary);\n}\n\n.ApiCardGrid_count__f119e0fd3b {\n display: inline-flex;\n align-items: center;\n min-width: 20px;\n height: 20px;\n padding: 0 var(--bui-space-2);\n border-radius: var(--bui-radius-full);\n background-color: var(--bui-bg-neutral-2);\n color: var(--bui-fg-secondary);\n font-size: var(--bui-font-size-2);\n font-variant-numeric: tabular-nums;\n}\n";
|
|
4
|
-
var styles = {"section":"ApiCardGrid_section__f119e0fd3b","sectionHeader":"ApiCardGrid_sectionHeader__f119e0fd3b","sectionIcon":"ApiCardGrid_sectionIcon__f119e0fd3b","count":"ApiCardGrid_count__f119e0fd3b"};
|
|
5
|
-
styleInject(css_248z);
|
|
6
|
-
|
|
7
|
-
export { styles as default };
|
|
8
|
-
//# sourceMappingURL=ApiCardGrid.module.css.esm.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ApiCardGrid.module.css.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;"}
|