@backstage/ui 0.15.1-next.0 → 0.17.0-next.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +186 -0
- package/dist/components/Combobox/Combobox.esm.js +150 -52
- package/dist/components/Combobox/Combobox.esm.js.map +1 -1
- package/dist/components/Combobox/Combobox.module.css.esm.js +2 -2
- package/dist/components/Combobox/ComboboxItem.esm.js +76 -0
- package/dist/components/Combobox/ComboboxItem.esm.js.map +1 -0
- package/dist/components/Combobox/ComboboxListBox.esm.js +215 -17
- package/dist/components/Combobox/ComboboxListBox.esm.js.map +1 -1
- package/dist/components/Combobox/definition.esm.js +62 -3
- package/dist/components/Combobox/definition.esm.js.map +1 -1
- package/dist/components/Combobox/useAsyncComboboxState.esm.js +133 -0
- package/dist/components/Combobox/useAsyncComboboxState.esm.js.map +1 -0
- package/dist/components/Header/HeaderNav.esm.js +0 -1
- package/dist/components/Header/HeaderNav.esm.js.map +1 -1
- package/dist/components/PluginHeader/PluginHeader.esm.js +16 -1
- package/dist/components/PluginHeader/PluginHeader.esm.js.map +1 -1
- package/dist/components/PluginHeader/PluginHeader.module.css.esm.js +2 -2
- package/dist/components/PluginHeader/PluginHeaderBreadcrumbs.esm.js +106 -0
- package/dist/components/PluginHeader/PluginHeaderBreadcrumbs.esm.js.map +1 -0
- package/dist/components/PluginHeader/definition.esm.js +3 -0
- package/dist/components/PluginHeader/definition.esm.js.map +1 -1
- package/dist/components/PluginHeader/useIsTruncated.esm.js +36 -0
- package/dist/components/PluginHeader/useIsTruncated.esm.js.map +1 -0
- package/dist/components/Select/Select.esm.js +87 -19
- package/dist/components/Select/Select.esm.js.map +1 -1
- package/dist/components/Select/Select.module.css.esm.js +2 -2
- package/dist/components/Select/SelectContent.esm.js +70 -18
- package/dist/components/Select/SelectContent.esm.js.map +1 -1
- package/dist/components/Select/SelectItem.esm.js +76 -0
- package/dist/components/Select/SelectItem.esm.js.map +1 -0
- package/dist/components/Select/SelectListBox.esm.js +175 -19
- package/dist/components/Select/SelectListBox.esm.js.map +1 -1
- package/dist/components/Select/SelectTrigger.esm.js +1 -1
- package/dist/components/Select/SelectTrigger.esm.js.map +1 -1
- package/dist/components/Select/definition.esm.js +72 -9
- package/dist/components/Select/definition.esm.js.map +1 -1
- package/dist/components/Skeleton/Skeleton.module.css.esm.js +2 -2
- package/dist/components/Skeleton/definition.esm.js +1 -0
- package/dist/components/Skeleton/definition.esm.js.map +1 -1
- package/dist/components/Table/Table.module.css.esm.js +2 -2
- package/dist/components/Table/components/Table.esm.js +60 -57
- package/dist/components/Table/components/Table.esm.js.map +1 -1
- package/dist/components/Table/definition.esm.js +2 -1
- package/dist/components/Table/definition.esm.js.map +1 -1
- package/dist/components/TablePagination/TablePagination.esm.js +4 -1
- package/dist/components/TablePagination/TablePagination.esm.js.map +1 -1
- package/dist/components/Tabs/TabsIndicators.esm.js +155 -108
- package/dist/components/Tabs/TabsIndicators.esm.js.map +1 -1
- package/dist/components/TextAreaField/TextAreaField.esm.js +61 -0
- package/dist/components/TextAreaField/TextAreaField.esm.js.map +1 -0
- package/dist/components/TextAreaField/TextAreaField.module.css.esm.js +8 -0
- package/dist/components/TextAreaField/TextAreaField.module.css.esm.js.map +1 -0
- package/dist/components/TextAreaField/definition.esm.js +32 -0
- package/dist/components/TextAreaField/definition.esm.js.map +1 -0
- package/dist/css/styles.css +4 -4
- package/dist/hooks/useCollectionAdapter.esm.js +67 -0
- package/dist/hooks/useCollectionAdapter.esm.js.map +1 -0
- package/dist/hooks/useDelayedVisibility.esm.js +17 -0
- package/dist/hooks/useDelayedVisibility.esm.js.map +1 -0
- package/dist/hooks/useTrackedSelectionKeys.esm.js +23 -0
- package/dist/hooks/useTrackedSelectionKeys.esm.js.map +1 -0
- package/dist/index.d.ts +817 -77
- package/dist/index.esm.js +8 -2
- package/dist/index.esm.js.map +1 -1
- package/dist/utils/selectableCollection.esm.js +75 -0
- package/dist/utils/selectableCollection.esm.js.map +1 -0
- package/package.json +3 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HeaderNav.esm.js","sources":["../../../src/components/Header/HeaderNav.tsx"],"sourcesContent":["/*\n * Copyright 2026 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { useCallback, useMemo, useRef, useState } from 'react';\nimport { useFocusVisible, useHover, useLink } from 'react-aria';\nimport {\n matchRoutes,\n resolvePath,\n useInRouterContext,\n useLocation,\n useResolvedPath,\n} from 'react-router-dom';\nimport { Button as RAButton } from 'react-aria-components';\nimport { RiArrowDownSLine } from '@remixicon/react';\nimport { useDefinition } from '../../hooks/useDefinition';\nimport {\n HeaderNavDefinition,\n HeaderNavItemDefinition,\n HeaderNavGroupDefinition,\n} from './HeaderNavDefinition';\nimport { HeaderNavIndicators } from './HeaderNavIndicators';\nimport { MenuTrigger, Menu, MenuItem } from '../Menu';\nimport type {\n HeaderNavLinkProps,\n HeaderNavTabGroup,\n HeaderNavTabItem,\n} from './types';\n\nfunction isTabGroup(tab: HeaderNavTabItem): tab is HeaderNavTabGroup {\n return 'items' in tab;\n}\n\nfunction HeaderNavLink(props: HeaderNavLinkProps) {\n const { ownProps, analytics } = useDefinition(HeaderNavItemDefinition, props);\n const { id, label, href, active, registerRef, onHighlight } = ownProps;\n\n const linkRef = useRef<HTMLAnchorElement>(null);\n const { linkProps } = useLink({ href }, linkRef);\n const { hoverProps } = useHover({\n onHoverStart: () => onHighlight(id),\n onHoverEnd: () => onHighlight(null),\n });\n\n const handleClick = (e: React.MouseEvent<HTMLAnchorElement>) => {\n linkProps.onClick?.(e);\n analytics.captureEvent('click', label, {\n attributes: { to: href },\n });\n };\n\n return (\n <li>\n <a\n {...linkProps}\n {...hoverProps}\n ref={el => {\n (\n linkRef as React.MutableRefObject<HTMLAnchorElement | null>\n ).current = el;\n registerRef(id, el);\n }}\n href={href}\n className={ownProps.classes.root}\n aria-current={active ? 'page' : undefined}\n onClick={handleClick}\n onFocus={() => onHighlight(id)}\n onBlur={() => onHighlight(null)}\n >\n {label}\n </a>\n </li>\n );\n}\n\ninterface HeaderNavGroupItemProps {\n group: HeaderNavTabGroup;\n active: boolean;\n activeChildId?: string;\n registerRef: (key: string, el: HTMLElement | null) => void;\n onHighlight: (key: string | null) => void;\n}\n\nfunction HeaderNavGroupItem(props: HeaderNavGroupItemProps) {\n const { group, active, activeChildId, registerRef, onHighlight } = props;\n const { ownProps } = useDefinition(HeaderNavGroupDefinition, {});\n const { hoverProps } = useHover({\n onHoverStart: () => onHighlight(group.id),\n onHoverEnd: () => onHighlight(null),\n });\n\n return (\n <li>\n <MenuTrigger>\n <RAButton\n ref={el => {\n registerRef(group.id, el);\n }}\n className={ownProps.classes.root}\n aria-current={active ? 'page' : undefined}\n {...hoverProps}\n onFocus={() => onHighlight(group.id)}\n onBlur={() => onHighlight(null)}\n >\n {group.label}\n <RiArrowDownSLine size={16} />\n </RAButton>\n <Menu\n selectionMode=\"single\"\n selectedKeys={new Set(activeChildId ? [activeChildId] : [])}\n >\n {group.items.map(item => (\n <MenuItem key={item.id} id={item.id} href={item.href}>\n {item.label}\n </MenuItem>\n ))}\n </Menu>\n </MenuTrigger>\n </li>\n );\n}\n\ninterface HeaderNavProps {\n tabs: HeaderNavTabItem[];\n activeTabId?: string | null;\n}\n\nfunction useAutoActiveTabId(tabs: HeaderNavTabItem[]): string | undefined {\n const basePath = useResolvedPath('.').pathname;\n const { pathname } = useLocation();\n\n return useMemo(() => {\n const allTabs = tabs.flatMap(tab => (isTabGroup(tab) ? tab.items : [tab]));\n const routeObjects = allTabs.map(tab => ({\n path: `${resolvePath(tab.href, basePath).pathname}/*`,\n id: tab.id,\n }));\n const matches = matchRoutes(routeObjects, pathname);\n return matches?.[0]?.route.id;\n }, [tabs, basePath, pathname]);\n}\n\nfunction HeaderNavAutoDetect(props: { tabs: HeaderNavTabItem[] }) {\n const activeTabId = useAutoActiveTabId(props.tabs);\n return <HeaderNavInner tabs={props.tabs} activeTabId={activeTabId} />;\n}\n\nfunction HeaderNavInner(props: HeaderNavProps) {\n const { tabs, activeTabId } = props;\n const { ownProps } = useDefinition(HeaderNavDefinition, {\n tabs,\n activeTabId,\n });\n const { classes } = ownProps;\n\n const { isFocusVisible } = useFocusVisible();\n const navRef = useRef<HTMLElement>(null);\n const itemRefs = useRef<Map<string, HTMLElement>>(new Map());\n\n const [highlightedKey, setHighlightedKey] = useState<string | null>(null);\n\n // Resolve activeTabId to a top-level key (groups own their children's active state)\n const { activeKey, activeChildId } = useMemo(() => {\n if (!activeTabId) return { activeKey: undefined, activeChildId: undefined };\n for (const item of tabs) {\n if (isTabGroup(item)) {\n const child = item.items.find(c => c.id === activeTabId);\n if (child) {\n return { activeKey: item.id, activeChildId: child.id };\n }\n } else if (item.id === activeTabId) {\n return { activeKey: item.id, activeChildId: undefined };\n }\n }\n return { activeKey: undefined, activeChildId: undefined };\n }, [activeTabId, tabs]);\n\n const registerRef = useCallback((key: string, el: HTMLElement | null) => {\n if (el) {\n itemRefs.current.set(key, el);\n } else {\n itemRefs.current.delete(key);\n }\n }, []);\n\n return (\n <nav\n ref={navRef}\n aria-label=\"Content navigation\"\n className={classes.root}\n data-focus-visible={isFocusVisible || undefined}\n >\n <ul role=\"list\" className={classes.list}>\n {tabs.map(item =>\n isTabGroup(item) ? (\n <HeaderNavGroupItem\n key={item.id}\n group={item}\n active={activeKey === item.id}\n activeChildId={activeChildId}\n registerRef={registerRef}\n onHighlight={setHighlightedKey}\n />\n ) : (\n <HeaderNavLink\n key={item.id}\n id={item.id}\n label={item.label}\n href={item.href}\n active={activeKey === item.id}\n registerRef={registerRef}\n onHighlight={setHighlightedKey}\n />\n ),\n )}\n </ul>\n <HeaderNavIndicators\n navRef={navRef}\n itemRefs={itemRefs}\n activeKey={activeKey}\n highlightedKey={highlightedKey}\n classes={{ active: classes.active, hovered: classes.hovered }}\n />\n </nav>\n );\n}\n\n/** @internal */\nexport function HeaderNav(props: HeaderNavProps) {\n const inRouter = useInRouterContext();\n\n if (props.activeTabId === undefined && inRouter) {\n return <HeaderNavAutoDetect tabs={props.tabs} />;\n }\n\n return <HeaderNavInner tabs={props.tabs} activeTabId={props.activeTabId} />;\n}\n"],"names":["RAButton"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"HeaderNav.esm.js","sources":["../../../src/components/Header/HeaderNav.tsx"],"sourcesContent":["/*\n * Copyright 2026 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { useCallback, useMemo, useRef, useState } from 'react';\nimport { useFocusVisible, useHover, useLink } from 'react-aria';\nimport {\n matchRoutes,\n resolvePath,\n useInRouterContext,\n useLocation,\n useResolvedPath,\n} from 'react-router-dom';\nimport { Button as RAButton } from 'react-aria-components';\nimport { RiArrowDownSLine } from '@remixicon/react';\nimport { useDefinition } from '../../hooks/useDefinition';\nimport {\n HeaderNavDefinition,\n HeaderNavItemDefinition,\n HeaderNavGroupDefinition,\n} from './HeaderNavDefinition';\nimport { HeaderNavIndicators } from './HeaderNavIndicators';\nimport { MenuTrigger, Menu, MenuItem } from '../Menu';\nimport type {\n HeaderNavLinkProps,\n HeaderNavTabGroup,\n HeaderNavTabItem,\n} from './types';\n\nfunction isTabGroup(tab: HeaderNavTabItem): tab is HeaderNavTabGroup {\n return 'items' in tab;\n}\n\nfunction HeaderNavLink(props: HeaderNavLinkProps) {\n const { ownProps, analytics } = useDefinition(HeaderNavItemDefinition, props);\n const { id, label, href, active, registerRef, onHighlight } = ownProps;\n\n const linkRef = useRef<HTMLAnchorElement>(null);\n const { linkProps } = useLink({ href }, linkRef);\n const { hoverProps } = useHover({\n onHoverStart: () => onHighlight(id),\n onHoverEnd: () => onHighlight(null),\n });\n\n const handleClick = (e: React.MouseEvent<HTMLAnchorElement>) => {\n linkProps.onClick?.(e);\n analytics.captureEvent('click', label, {\n attributes: { to: href },\n });\n };\n\n return (\n <li>\n <a\n {...linkProps}\n {...hoverProps}\n ref={el => {\n (\n linkRef as React.MutableRefObject<HTMLAnchorElement | null>\n ).current = el;\n registerRef(id, el);\n }}\n className={ownProps.classes.root}\n aria-current={active ? 'page' : undefined}\n onClick={handleClick}\n onFocus={() => onHighlight(id)}\n onBlur={() => onHighlight(null)}\n >\n {label}\n </a>\n </li>\n );\n}\n\ninterface HeaderNavGroupItemProps {\n group: HeaderNavTabGroup;\n active: boolean;\n activeChildId?: string;\n registerRef: (key: string, el: HTMLElement | null) => void;\n onHighlight: (key: string | null) => void;\n}\n\nfunction HeaderNavGroupItem(props: HeaderNavGroupItemProps) {\n const { group, active, activeChildId, registerRef, onHighlight } = props;\n const { ownProps } = useDefinition(HeaderNavGroupDefinition, {});\n const { hoverProps } = useHover({\n onHoverStart: () => onHighlight(group.id),\n onHoverEnd: () => onHighlight(null),\n });\n\n return (\n <li>\n <MenuTrigger>\n <RAButton\n ref={el => {\n registerRef(group.id, el);\n }}\n className={ownProps.classes.root}\n aria-current={active ? 'page' : undefined}\n {...hoverProps}\n onFocus={() => onHighlight(group.id)}\n onBlur={() => onHighlight(null)}\n >\n {group.label}\n <RiArrowDownSLine size={16} />\n </RAButton>\n <Menu\n selectionMode=\"single\"\n selectedKeys={new Set(activeChildId ? [activeChildId] : [])}\n >\n {group.items.map(item => (\n <MenuItem key={item.id} id={item.id} href={item.href}>\n {item.label}\n </MenuItem>\n ))}\n </Menu>\n </MenuTrigger>\n </li>\n );\n}\n\ninterface HeaderNavProps {\n tabs: HeaderNavTabItem[];\n activeTabId?: string | null;\n}\n\nfunction useAutoActiveTabId(tabs: HeaderNavTabItem[]): string | undefined {\n const basePath = useResolvedPath('.').pathname;\n const { pathname } = useLocation();\n\n return useMemo(() => {\n const allTabs = tabs.flatMap(tab => (isTabGroup(tab) ? tab.items : [tab]));\n const routeObjects = allTabs.map(tab => ({\n path: `${resolvePath(tab.href, basePath).pathname}/*`,\n id: tab.id,\n }));\n const matches = matchRoutes(routeObjects, pathname);\n return matches?.[0]?.route.id;\n }, [tabs, basePath, pathname]);\n}\n\nfunction HeaderNavAutoDetect(props: { tabs: HeaderNavTabItem[] }) {\n const activeTabId = useAutoActiveTabId(props.tabs);\n return <HeaderNavInner tabs={props.tabs} activeTabId={activeTabId} />;\n}\n\nfunction HeaderNavInner(props: HeaderNavProps) {\n const { tabs, activeTabId } = props;\n const { ownProps } = useDefinition(HeaderNavDefinition, {\n tabs,\n activeTabId,\n });\n const { classes } = ownProps;\n\n const { isFocusVisible } = useFocusVisible();\n const navRef = useRef<HTMLElement>(null);\n const itemRefs = useRef<Map<string, HTMLElement>>(new Map());\n\n const [highlightedKey, setHighlightedKey] = useState<string | null>(null);\n\n // Resolve activeTabId to a top-level key (groups own their children's active state)\n const { activeKey, activeChildId } = useMemo(() => {\n if (!activeTabId) return { activeKey: undefined, activeChildId: undefined };\n for (const item of tabs) {\n if (isTabGroup(item)) {\n const child = item.items.find(c => c.id === activeTabId);\n if (child) {\n return { activeKey: item.id, activeChildId: child.id };\n }\n } else if (item.id === activeTabId) {\n return { activeKey: item.id, activeChildId: undefined };\n }\n }\n return { activeKey: undefined, activeChildId: undefined };\n }, [activeTabId, tabs]);\n\n const registerRef = useCallback((key: string, el: HTMLElement | null) => {\n if (el) {\n itemRefs.current.set(key, el);\n } else {\n itemRefs.current.delete(key);\n }\n }, []);\n\n return (\n <nav\n ref={navRef}\n aria-label=\"Content navigation\"\n className={classes.root}\n data-focus-visible={isFocusVisible || undefined}\n >\n <ul role=\"list\" className={classes.list}>\n {tabs.map(item =>\n isTabGroup(item) ? (\n <HeaderNavGroupItem\n key={item.id}\n group={item}\n active={activeKey === item.id}\n activeChildId={activeChildId}\n registerRef={registerRef}\n onHighlight={setHighlightedKey}\n />\n ) : (\n <HeaderNavLink\n key={item.id}\n id={item.id}\n label={item.label}\n href={item.href}\n active={activeKey === item.id}\n registerRef={registerRef}\n onHighlight={setHighlightedKey}\n />\n ),\n )}\n </ul>\n <HeaderNavIndicators\n navRef={navRef}\n itemRefs={itemRefs}\n activeKey={activeKey}\n highlightedKey={highlightedKey}\n classes={{ active: classes.active, hovered: classes.hovered }}\n />\n </nav>\n );\n}\n\n/** @internal */\nexport function HeaderNav(props: HeaderNavProps) {\n const inRouter = useInRouterContext();\n\n if (props.activeTabId === undefined && inRouter) {\n return <HeaderNavAutoDetect tabs={props.tabs} />;\n }\n\n return <HeaderNavInner tabs={props.tabs} activeTabId={props.activeTabId} />;\n}\n"],"names":["RAButton"],"mappings":"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"}
|
|
@@ -12,6 +12,9 @@ import '../Link/Link.module.css.esm.js';
|
|
|
12
12
|
import { RiShapesLine } from '@remixicon/react';
|
|
13
13
|
import { Text } from '../Text/Text.esm.js';
|
|
14
14
|
import '../Text/Text.module.css.esm.js';
|
|
15
|
+
import { VisuallyHidden } from '../VisuallyHidden/VisuallyHidden.esm.js';
|
|
16
|
+
import '../VisuallyHidden/VisuallyHidden.module.css.esm.js';
|
|
17
|
+
import { PluginHeaderBreadcrumbs } from './PluginHeaderBreadcrumbs.esm.js';
|
|
15
18
|
|
|
16
19
|
const PluginHeader = (props) => {
|
|
17
20
|
const { ownProps } = useDefinition(PluginHeaderDefinition, props);
|
|
@@ -21,10 +24,12 @@ const PluginHeader = (props) => {
|
|
|
21
24
|
icon,
|
|
22
25
|
title,
|
|
23
26
|
titleLink,
|
|
27
|
+
breadcrumbs,
|
|
24
28
|
customActions,
|
|
25
29
|
onTabSelectionChange
|
|
26
30
|
} = ownProps;
|
|
27
31
|
const hasTabs = tabs && tabs.length > 0;
|
|
32
|
+
const hasBreadcrumbs = breadcrumbs && breadcrumbs.length > 0;
|
|
28
33
|
const rootRef = useRef(null);
|
|
29
34
|
const animationFrameRef = useRef(void 0);
|
|
30
35
|
const lastAppliedHeightRef = useRef(void 0);
|
|
@@ -84,7 +89,17 @@ const PluginHeader = (props) => {
|
|
|
84
89
|
/* @__PURE__ */ jsxs("div", { className: classes.toolbar, "data-has-tabs": hasTabs ? "" : void 0, children: [
|
|
85
90
|
/* @__PURE__ */ jsxs("div", { className: classes.toolbarContent, children: [
|
|
86
91
|
/* @__PURE__ */ jsx(Box, { bg: "neutral", className: classes.toolbarIcon, "aria-hidden": "true", children: icon || /* @__PURE__ */ jsx(RiShapesLine, {}) }),
|
|
87
|
-
/* @__PURE__ */
|
|
92
|
+
hasBreadcrumbs ? /* @__PURE__ */ jsxs("div", { className: classes.toolbarName, children: [
|
|
93
|
+
/* @__PURE__ */ jsx(VisuallyHidden, { children: /* @__PURE__ */ jsx("h1", { children: titleText }) }),
|
|
94
|
+
/* @__PURE__ */ jsx(
|
|
95
|
+
PluginHeaderBreadcrumbs,
|
|
96
|
+
{
|
|
97
|
+
entries: breadcrumbs,
|
|
98
|
+
className: classes.breadcrumbs,
|
|
99
|
+
ellipsisClassName: classes.breadcrumbsEllipsis
|
|
100
|
+
}
|
|
101
|
+
)
|
|
102
|
+
] }) : /* @__PURE__ */ jsx("h1", { className: classes.toolbarName, children: titleLink ? /* @__PURE__ */ jsx(Link, { href: titleLink, standalone: true, variant: "body-medium", children: titleText }) : /* @__PURE__ */ jsx(Text, { as: "span", variant: "body-medium", children: titleText }) })
|
|
88
103
|
] }),
|
|
89
104
|
/* @__PURE__ */ jsx("div", { className: classes.toolbarControls, children: actionChildren })
|
|
90
105
|
] }),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PluginHeader.esm.js","sources":["../../../src/components/PluginHeader/PluginHeader.tsx"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { PluginHeaderProps } from './types';\nimport { Tabs, TabList, Tab } from '../Tabs';\nimport { useDefinition } from '../../hooks/useDefinition';\nimport { PluginHeaderDefinition } from './definition';\nimport { type NavigateOptions } from 'react-router-dom';\nimport { Children, useMemo, useRef } from 'react';\nimport { useIsomorphicLayoutEffect } from '../../hooks/useIsomorphicLayoutEffect';\nimport { Box } from '../Box';\nimport { Link } from '../Link';\nimport { RiShapesLine } from '@remixicon/react';\nimport { Text } from '../Text';\n\ndeclare module 'react-aria-components' {\n interface RouterConfig {\n routerOptions: NavigateOptions;\n }\n}\n\n/**\n * Renders a plugin header with icon, title, custom actions, and optional tabs.\n * Always participates in the background context system so descendants (e.g. buttons)\n * get the correct `data-on-bg` styling inside the toolbar and tabs.\n *\n * @public\n */\nexport const PluginHeader = (props: PluginHeaderProps) => {\n const { ownProps } = useDefinition(PluginHeaderDefinition, props);\n const {\n classes,\n tabs,\n icon,\n title,\n titleLink,\n customActions,\n onTabSelectionChange,\n } = ownProps;\n\n const hasTabs = tabs && tabs.length > 0;\n const rootRef = useRef<HTMLDivElement>(null);\n const animationFrameRef = useRef<number | undefined>(undefined);\n const lastAppliedHeightRef = useRef<number | undefined>(undefined);\n\n const actionChildren = useMemo(() => {\n return Children.toArray(customActions);\n }, [customActions]);\n\n useIsomorphicLayoutEffect(() => {\n const el = rootRef.current;\n if (!el) {\n return undefined;\n }\n\n const cancelScheduledUpdate = () => {\n if (animationFrameRef.current === undefined) {\n return;\n }\n\n cancelAnimationFrame(animationFrameRef.current);\n animationFrameRef.current = undefined;\n };\n\n const applyHeight = (height: number) => {\n if (lastAppliedHeightRef.current === height) {\n return;\n }\n\n lastAppliedHeightRef.current = height;\n document.documentElement.style.setProperty(\n '--bui-header-height',\n `${height}px`,\n );\n };\n\n const scheduleHeightUpdate = () => {\n cancelScheduledUpdate();\n animationFrameRef.current = requestAnimationFrame(() => {\n animationFrameRef.current = undefined;\n applyHeight(el.offsetHeight);\n });\n };\n\n // Set height once immediately so the initial layout is correct.\n applyHeight(el.offsetHeight);\n\n // Observe for resize changes if ResizeObserver is available\n // (not present in Jest/jsdom by default)\n if (typeof ResizeObserver === 'undefined') {\n return () => {\n cancelScheduledUpdate();\n lastAppliedHeightRef.current = undefined;\n document.documentElement.style.removeProperty('--bui-header-height');\n };\n }\n\n const observer = new ResizeObserver(() => {\n scheduleHeightUpdate();\n });\n observer.observe(el);\n\n return () => {\n observer.disconnect();\n cancelScheduledUpdate();\n lastAppliedHeightRef.current = undefined;\n document.documentElement.style.removeProperty('--bui-header-height');\n };\n }, []);\n\n const titleText = title || 'Your plugin';\n\n return (\n <div ref={rootRef} className={classes.root}>\n <div className={classes.toolbar} data-has-tabs={hasTabs ? '' : undefined}>\n <div className={classes.toolbarContent}>\n <Box bg=\"neutral\" className={classes.toolbarIcon} aria-hidden=\"true\">\n {icon || <RiShapesLine />}\n </Box>\n <
|
|
1
|
+
{"version":3,"file":"PluginHeader.esm.js","sources":["../../../src/components/PluginHeader/PluginHeader.tsx"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { PluginHeaderProps } from './types';\nimport { Tabs, TabList, Tab } from '../Tabs';\nimport { useDefinition } from '../../hooks/useDefinition';\nimport { PluginHeaderDefinition } from './definition';\nimport { type NavigateOptions } from 'react-router-dom';\nimport { Children, useMemo, useRef } from 'react';\nimport { useIsomorphicLayoutEffect } from '../../hooks/useIsomorphicLayoutEffect';\nimport { Box } from '../Box';\nimport { Link } from '../Link';\nimport { RiShapesLine } from '@remixicon/react';\nimport { Text } from '../Text';\nimport { VisuallyHidden } from '../VisuallyHidden';\nimport { PluginHeaderBreadcrumbs } from './PluginHeaderBreadcrumbs';\n\ndeclare module 'react-aria-components' {\n interface RouterConfig {\n routerOptions: NavigateOptions;\n }\n}\n\n/**\n * Renders a plugin header with icon, title, custom actions, and optional tabs.\n * Always participates in the background context system so descendants (e.g. buttons)\n * get the correct `data-on-bg` styling inside the toolbar and tabs.\n *\n * @public\n */\nexport const PluginHeader = (props: PluginHeaderProps) => {\n const { ownProps } = useDefinition(PluginHeaderDefinition, props);\n const {\n classes,\n tabs,\n icon,\n title,\n titleLink,\n breadcrumbs,\n customActions,\n onTabSelectionChange,\n } = ownProps;\n\n const hasTabs = tabs && tabs.length > 0;\n const hasBreadcrumbs = breadcrumbs && breadcrumbs.length > 0;\n const rootRef = useRef<HTMLDivElement>(null);\n const animationFrameRef = useRef<number | undefined>(undefined);\n const lastAppliedHeightRef = useRef<number | undefined>(undefined);\n\n const actionChildren = useMemo(() => {\n return Children.toArray(customActions);\n }, [customActions]);\n\n useIsomorphicLayoutEffect(() => {\n const el = rootRef.current;\n if (!el) {\n return undefined;\n }\n\n const cancelScheduledUpdate = () => {\n if (animationFrameRef.current === undefined) {\n return;\n }\n\n cancelAnimationFrame(animationFrameRef.current);\n animationFrameRef.current = undefined;\n };\n\n const applyHeight = (height: number) => {\n if (lastAppliedHeightRef.current === height) {\n return;\n }\n\n lastAppliedHeightRef.current = height;\n document.documentElement.style.setProperty(\n '--bui-header-height',\n `${height}px`,\n );\n };\n\n const scheduleHeightUpdate = () => {\n cancelScheduledUpdate();\n animationFrameRef.current = requestAnimationFrame(() => {\n animationFrameRef.current = undefined;\n applyHeight(el.offsetHeight);\n });\n };\n\n // Set height once immediately so the initial layout is correct.\n applyHeight(el.offsetHeight);\n\n // Observe for resize changes if ResizeObserver is available\n // (not present in Jest/jsdom by default)\n if (typeof ResizeObserver === 'undefined') {\n return () => {\n cancelScheduledUpdate();\n lastAppliedHeightRef.current = undefined;\n document.documentElement.style.removeProperty('--bui-header-height');\n };\n }\n\n const observer = new ResizeObserver(() => {\n scheduleHeightUpdate();\n });\n observer.observe(el);\n\n return () => {\n observer.disconnect();\n cancelScheduledUpdate();\n lastAppliedHeightRef.current = undefined;\n document.documentElement.style.removeProperty('--bui-header-height');\n };\n }, []);\n\n const titleText = title || 'Your plugin';\n\n return (\n <div ref={rootRef} className={classes.root}>\n <div className={classes.toolbar} data-has-tabs={hasTabs ? '' : undefined}>\n <div className={classes.toolbarContent}>\n <Box bg=\"neutral\" className={classes.toolbarIcon} aria-hidden=\"true\">\n {icon || <RiShapesLine />}\n </Box>\n {hasBreadcrumbs ? (\n <div className={classes.toolbarName}>\n <VisuallyHidden>\n <h1>{titleText}</h1>\n </VisuallyHidden>\n <PluginHeaderBreadcrumbs\n entries={breadcrumbs}\n className={classes.breadcrumbs}\n ellipsisClassName={classes.breadcrumbsEllipsis}\n />\n </div>\n ) : (\n <h1 className={classes.toolbarName}>\n {titleLink ? (\n <Link href={titleLink} standalone variant=\"body-medium\">\n {titleText}\n </Link>\n ) : (\n <Text as=\"span\" variant=\"body-medium\">\n {titleText}\n </Text>\n )}\n </h1>\n )}\n </div>\n <div className={classes.toolbarControls}>{actionChildren}</div>\n </div>\n {hasTabs && (\n <div className={classes.tabs}>\n <Tabs onSelectionChange={onTabSelectionChange}>\n <TabList>\n {tabs?.map(tab => (\n <Tab\n key={tab.id}\n id={tab.id}\n href={tab.href}\n matchStrategy={tab.matchStrategy}\n >\n {tab.label}\n </Tab>\n ))}\n </TabList>\n </Tabs>\n </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 = "/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n@layer tokens, base, components, utilities;\n\n@layer components {\n .PluginHeader_bui-
|
|
4
|
-
var styles = {"bui-PluginHeader":"PluginHeader_bui-
|
|
3
|
+
var css_248z = "/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n@layer tokens, base, components, utilities;\n\n@layer components {\n .PluginHeader_bui-PluginHeader__cf3f6a1fa8 {\n --bui-plugin-header-margin-bottom: var(--bui-space-6);\n --bui-plugin-header-toolbar-border-bottom: solid 1px var(--bui-border-1);\n --bui-plugin-header-tabs-border-bottom: 1px solid var(--bui-border-1);\n --bui-plugin-header-toolbar-padding-bottom: var(--bui-space-4);\n --bui-plugin-header-toolbar-tabs-padding-bottom: var(--bui-space-1);\n --bui-plugin-header-background-color: transparent;\n --bui-plugin-header-padding-top: var(--bui-space-4);\n --bui-plugin-header-breadcrumbs-gap: var(--bui-space-3);\n --bui-plugin-header-breadcrumbs-max-width: 165px;\n --bui-plugin-header-breadcrumbs-current-max-width: none;\n\n margin-bottom: var(--bui-plugin-header-margin-bottom);\n padding-inline: var(--bui-space-5);\n padding-top: var(--bui-plugin-header-padding-top);\n background-color: var(--bui-plugin-header-background-color);\n }\n\n .PluginHeader_bui-PluginHeader__cf3f6a1fa8:has(+ .PluginHeader_bui-HeaderTop__cf3f6a1fa8),\n .PluginHeader_bui-PluginHeader__cf3f6a1fa8:has(+ [data-backstage-core-header]),\n .PluginHeader_bui-PluginHeader__cf3f6a1fa8:has(\n + [data-backstage-core-page] [data-backstage-core-header]\n ) {\n --bui-plugin-header-margin-bottom: 0;\n --bui-plugin-header-toolbar-border-bottom: none;\n --bui-plugin-header-tabs-border-bottom: none;\n --bui-plugin-header-toolbar-padding-bottom: var(--bui-space-2);\n --bui-plugin-header-toolbar-tabs-padding-bottom: var(--bui-space-2);\n --bui-plugin-header-background-color: var(--bui-bg-neutral-1);\n --bui-plugin-header-padding-top: var(--bui-space-2);\n }\n\n .PluginHeader_bui-PluginHeader__cf3f6a1fa8:has(+ [data-backstage-core-header])\n .PluginHeader_bui-PluginHeaderToolbarIcon__cf3f6a1fa8,\n .PluginHeader_bui-PluginHeader__cf3f6a1fa8:has(\n + [data-backstage-core-page] [data-backstage-core-header]\n )\n .PluginHeader_bui-PluginHeaderToolbarIcon__cf3f6a1fa8 {\n background-color: var(--bui-bg-neutral-3);\n }\n\n .PluginHeader_bui-PluginHeaderToolbar__cf3f6a1fa8 {\n display: flex;\n flex-direction: row;\n align-items: center;\n justify-content: space-between;\n color: var(--bui-fg-primary);\n border-bottom: var(--bui-plugin-header-toolbar-border-bottom);\n padding-bottom: var(--bui-plugin-header-toolbar-padding-bottom);\n\n &[data-has-tabs] {\n border-bottom: none;\n padding-bottom: var(--bui-plugin-header-toolbar-tabs-padding-bottom);\n }\n }\n\n .PluginHeader_bui-PluginHeaderToolbarContent__cf3f6a1fa8 {\n display: flex;\n flex-direction: row;\n align-items: center;\n gap: var(--bui-space-2);\n min-width: 0;\n padding: var(--bui-space-1) 0;\n }\n\n .PluginHeader_bui-PluginHeaderToolbarName__cf3f6a1fa8 {\n display: flex;\n flex-direction: row;\n align-items: center;\n gap: var(--bui-space-2);\n margin: 0;\n min-width: 0;\n }\n\n .PluginHeader_bui-PluginHeaderToolbarName__cf3f6a1fa8:not(:has(.PluginHeader_bui-PluginHeaderBreadcrumbs__cf3f6a1fa8)) {\n flex-shrink: 0;\n }\n\n .PluginHeader_bui-PluginHeaderBreadcrumbs__cf3f6a1fa8 {\n min-width: 0;\n }\n\n .PluginHeader_bui-PluginHeaderBreadcrumbs__cf3f6a1fa8 ol {\n display: flex;\n flex-direction: row;\n list-style: none;\n margin: 0;\n padding: 0;\n gap: var(--bui-plugin-header-breadcrumbs-gap);\n min-width: 0;\n }\n\n .PluginHeader_bui-PluginHeaderBreadcrumbs__cf3f6a1fa8 ol li {\n display: flex;\n flex-direction: row;\n align-items: center;\n gap: var(--bui-plugin-header-breadcrumbs-gap);\n max-width: var(--bui-plugin-header-breadcrumbs-max-width);\n\n & svg {\n flex-shrink: 0;\n }\n\n &[data-current] {\n flex: 1;\n min-width: 0;\n max-width: var(--bui-plugin-header-breadcrumbs-current-max-width);\n }\n\n & a[data-focus-visible] {\n border-radius: var(--bui-radius-1);\n outline: 2px solid var(--bui-ring);\n outline-offset: 2px;\n }\n }\n\n .PluginHeader_bui-PluginHeaderBreadcrumbsEllipsis__cf3f6a1fa8 {\n all: unset;\n cursor: pointer;\n font: inherit;\n color: inherit;\n line-height: 1;\n letter-spacing: 0.1em;\n\n &[data-hovered] {\n text-decoration-line: underline;\n text-decoration-style: solid;\n text-decoration-thickness: 1px;\n text-underline-offset: 2px;\n text-decoration-color: color-mix(in srgb, currentColor 30%, transparent);\n }\n\n &[data-focus-visible] {\n border-radius: var(--bui-radius-1);\n outline: 2px solid var(--bui-ring);\n outline-offset: 2px;\n }\n }\n\n .PluginHeader_bui-PluginHeader__cf3f6a1fa8 .PluginHeader_bui-PluginHeaderToolbarIcon__cf3f6a1fa8 {\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n width: 2rem;\n height: 2rem;\n border-radius: var(--bui-radius-2);\n color: var(--bui-fg-primary);\n\n & svg {\n width: 1rem;\n height: 1rem;\n }\n }\n\n .PluginHeader_bui-PluginHeaderToolbarControls__cf3f6a1fa8 {\n display: flex;\n flex-direction: row;\n align-items: center;\n gap: var(--bui-space-2);\n }\n\n .PluginHeader_bui-PluginHeaderTabsWrapper__cf3f6a1fa8 {\n border-bottom: var(--bui-plugin-header-tabs-border-bottom);\n }\n}\n";
|
|
4
|
+
var styles = {"bui-PluginHeader":"PluginHeader_bui-PluginHeader__cf3f6a1fa8","bui-HeaderTop":"PluginHeader_bui-HeaderTop__cf3f6a1fa8","bui-PluginHeaderToolbarIcon":"PluginHeader_bui-PluginHeaderToolbarIcon__cf3f6a1fa8","bui-PluginHeaderToolbar":"PluginHeader_bui-PluginHeaderToolbar__cf3f6a1fa8","bui-PluginHeaderToolbarContent":"PluginHeader_bui-PluginHeaderToolbarContent__cf3f6a1fa8","bui-PluginHeaderToolbarName":"PluginHeader_bui-PluginHeaderToolbarName__cf3f6a1fa8","bui-PluginHeaderBreadcrumbs":"PluginHeader_bui-PluginHeaderBreadcrumbs__cf3f6a1fa8","bui-PluginHeaderBreadcrumbsEllipsis":"PluginHeader_bui-PluginHeaderBreadcrumbsEllipsis__cf3f6a1fa8","bui-PluginHeaderToolbarControls":"PluginHeader_bui-PluginHeaderToolbarControls__cf3f6a1fa8","bui-PluginHeaderTabsWrapper":"PluginHeader_bui-PluginHeaderTabsWrapper__cf3f6a1fa8"};
|
|
5
5
|
styleInject(css_248z);
|
|
6
6
|
|
|
7
7
|
export { styles as default };
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { Link } from '../Link/Link.esm.js';
|
|
3
|
+
import 'clsx';
|
|
4
|
+
import '../../hooks/useBreakpoint.esm.js';
|
|
5
|
+
import '../../hooks/useBg.esm.js';
|
|
6
|
+
import '../../hooks/useDefinition/helpers.esm.js';
|
|
7
|
+
import '../../analytics/useAnalytics.esm.js';
|
|
8
|
+
import 'react-router-dom';
|
|
9
|
+
import '../Link/Link.module.css.esm.js';
|
|
10
|
+
import { Text } from '../Text/Text.esm.js';
|
|
11
|
+
import '../Text/Text.module.css.esm.js';
|
|
12
|
+
import { MenuTrigger, Menu, MenuItem } from '../Menu/Menu.esm.js';
|
|
13
|
+
import '../Menu/definition.esm.js';
|
|
14
|
+
import { RiArrowRightSLine } from '@remixicon/react';
|
|
15
|
+
import { Breadcrumbs, Breadcrumb, Button, Focusable } from 'react-aria-components';
|
|
16
|
+
import { useIsTruncated } from './useIsTruncated.esm.js';
|
|
17
|
+
import { TooltipTrigger, Tooltip } from '../Tooltip/Tooltip.esm.js';
|
|
18
|
+
import '../Tooltip/Tooltip.module.css.esm.js';
|
|
19
|
+
|
|
20
|
+
const COLLAPSE_THRESHOLD = 5;
|
|
21
|
+
const ROOT_ITEMS = 1;
|
|
22
|
+
function BreadcrumbSeparator() {
|
|
23
|
+
return /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: /* @__PURE__ */ jsx(RiArrowRightSLine, { size: 16 }) });
|
|
24
|
+
}
|
|
25
|
+
function BreadcrumbTooltipWrapper(props) {
|
|
26
|
+
const { showTooltip, label, children } = props;
|
|
27
|
+
return /* @__PURE__ */ jsxs(TooltipTrigger, { delay: 300, isDisabled: !showTooltip, children: [
|
|
28
|
+
children,
|
|
29
|
+
/* @__PURE__ */ jsx(Tooltip, { children: label })
|
|
30
|
+
] });
|
|
31
|
+
}
|
|
32
|
+
function BreadcrumbLink(props) {
|
|
33
|
+
const { entry } = props;
|
|
34
|
+
const { ref, truncated } = useIsTruncated();
|
|
35
|
+
return /* @__PURE__ */ jsx(BreadcrumbTooltipWrapper, { label: entry.label, showTooltip: truncated, children: /* @__PURE__ */ jsx(
|
|
36
|
+
Link,
|
|
37
|
+
{
|
|
38
|
+
href: entry.href,
|
|
39
|
+
standalone: true,
|
|
40
|
+
variant: "body-medium",
|
|
41
|
+
truncate: true,
|
|
42
|
+
ref,
|
|
43
|
+
children: entry.label
|
|
44
|
+
}
|
|
45
|
+
) });
|
|
46
|
+
}
|
|
47
|
+
function BreadcrumbText(props) {
|
|
48
|
+
const { entry } = props;
|
|
49
|
+
const { ref, truncated } = useIsTruncated();
|
|
50
|
+
return /* @__PURE__ */ jsx(BreadcrumbTooltipWrapper, { label: entry.label, showTooltip: truncated, children: /* @__PURE__ */ jsx(Focusable, { excludeFromTabOrder: !truncated, children: /* @__PURE__ */ jsx(Text, { variant: "body-medium", truncate: true, ref, children: entry.label }) }) });
|
|
51
|
+
}
|
|
52
|
+
function CollapsedSegment(props) {
|
|
53
|
+
const { items, ellipsisClassName } = props;
|
|
54
|
+
const ariaLabel = "Show more breadcrumbs";
|
|
55
|
+
return /* @__PURE__ */ jsxs(Breadcrumb, { children: [
|
|
56
|
+
/* @__PURE__ */ jsxs(MenuTrigger, { children: [
|
|
57
|
+
/* @__PURE__ */ jsx(Button, { "aria-label": ariaLabel, className: ellipsisClassName, children: /* @__PURE__ */ jsx(Text, { as: "span", variant: "body-medium", children: "\u2026" }) }),
|
|
58
|
+
/* @__PURE__ */ jsx(Menu, { children: items.map((item) => /* @__PURE__ */ jsx(MenuItem, { href: item.href, children: item.label }, item.href)) })
|
|
59
|
+
] }),
|
|
60
|
+
/* @__PURE__ */ jsx(BreadcrumbSeparator, {})
|
|
61
|
+
] }, "collapsed");
|
|
62
|
+
}
|
|
63
|
+
function AncestorSegment(props) {
|
|
64
|
+
const { entry } = props;
|
|
65
|
+
return /* @__PURE__ */ jsxs(Breadcrumb, { children: [
|
|
66
|
+
/* @__PURE__ */ jsx(BreadcrumbLink, { entry }),
|
|
67
|
+
/* @__PURE__ */ jsx(BreadcrumbSeparator, {})
|
|
68
|
+
] }, entry.href);
|
|
69
|
+
}
|
|
70
|
+
function CurrentSegment(props) {
|
|
71
|
+
const { entry, isSingleEntry } = props;
|
|
72
|
+
return /* @__PURE__ */ jsx(Breadcrumb, { children: isSingleEntry ? /* @__PURE__ */ jsx(BreadcrumbLink, { entry }) : /* @__PURE__ */ jsx(BreadcrumbText, { entry }) }, entry.href);
|
|
73
|
+
}
|
|
74
|
+
function PluginHeaderBreadcrumbs(props) {
|
|
75
|
+
const { entries, className, ellipsisClassName } = props;
|
|
76
|
+
if (entries.length === 0) return null;
|
|
77
|
+
const isSingleEntry = entries.length === 1;
|
|
78
|
+
const current = entries[entries.length - 1];
|
|
79
|
+
const rest = entries.slice(0, -1);
|
|
80
|
+
let ancestorItems = null;
|
|
81
|
+
if (entries.length >= COLLAPSE_THRESHOLD) {
|
|
82
|
+
const root = rest.slice(0, ROOT_ITEMS);
|
|
83
|
+
const leading = rest.slice(-1);
|
|
84
|
+
const collapsed = rest.slice(ROOT_ITEMS, -1);
|
|
85
|
+
ancestorItems = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
86
|
+
root.map((entry) => /* @__PURE__ */ jsx(AncestorSegment, { entry }, entry.href)),
|
|
87
|
+
/* @__PURE__ */ jsx(
|
|
88
|
+
CollapsedSegment,
|
|
89
|
+
{
|
|
90
|
+
items: collapsed,
|
|
91
|
+
ellipsisClassName
|
|
92
|
+
}
|
|
93
|
+
),
|
|
94
|
+
leading.map((entry) => /* @__PURE__ */ jsx(AncestorSegment, { entry }, entry.href))
|
|
95
|
+
] });
|
|
96
|
+
} else {
|
|
97
|
+
ancestorItems = rest.map((entry) => /* @__PURE__ */ jsx(AncestorSegment, { entry }, entry.href));
|
|
98
|
+
}
|
|
99
|
+
return /* @__PURE__ */ jsx("nav", { id: "Breadcrumbs", "aria-label": "Breadcrumbs", className, children: /* @__PURE__ */ jsxs(Breadcrumbs, { children: [
|
|
100
|
+
ancestorItems,
|
|
101
|
+
/* @__PURE__ */ jsx(CurrentSegment, { entry: current, isSingleEntry })
|
|
102
|
+
] }) });
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export { PluginHeaderBreadcrumbs };
|
|
106
|
+
//# sourceMappingURL=PluginHeaderBreadcrumbs.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PluginHeaderBreadcrumbs.esm.js","sources":["../../../src/components/PluginHeader/PluginHeaderBreadcrumbs.tsx"],"sourcesContent":["/*\n * Copyright 2026 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { PluginHeaderBreadcrumbEntry } from './types';\nimport { Link } from '../Link';\nimport { Text } from '../Text';\nimport { MenuTrigger, Menu, MenuItem } from '../Menu';\nimport { RiArrowRightSLine } from '@remixicon/react';\nimport {\n Focusable,\n Breadcrumb as RACBreadcrumb,\n Breadcrumbs as RACBreadcrumbs,\n Button as RACButton,\n} from 'react-aria-components';\nimport { useIsTruncated } from './useIsTruncated';\nimport { Tooltip, TooltipTrigger } from '../Tooltip';\n\nconst COLLAPSE_THRESHOLD = 5;\nconst ROOT_ITEMS = 1;\nconst LEADING_ITEMS = 1;\n\n/** Separator icon that appears after non-current segments */\nfunction BreadcrumbSeparator() {\n return (\n <span aria-hidden=\"true\">\n <RiArrowRightSLine size={16} />\n </span>\n );\n}\n\n/** Wraps children in a tooltip that shows on hover when the label is truncated. */\nfunction BreadcrumbTooltipWrapper(props: {\n showTooltip: boolean;\n label: string;\n children: React.ReactNode;\n}) {\n const { showTooltip, label, children } = props;\n\n return (\n <TooltipTrigger delay={300} isDisabled={!showTooltip}>\n {children}\n <Tooltip>{label}</Tooltip>\n </TooltipTrigger>\n );\n}\n\n/** Renders a link in the breadcrumbs trail */\nfunction BreadcrumbLink(props: { entry: PluginHeaderBreadcrumbEntry }) {\n const { entry } = props;\n const { ref, truncated } = useIsTruncated<HTMLAnchorElement>();\n return (\n <BreadcrumbTooltipWrapper label={entry.label} showTooltip={truncated}>\n <Link\n href={entry.href}\n standalone\n variant=\"body-medium\"\n truncate\n ref={ref}\n >\n {entry.label}\n </Link>\n </BreadcrumbTooltipWrapper>\n );\n}\n\n/**\n * Renders a text in the breadcrumbs trail.\n * If it is truncated, it becomes focusable (requirement for the tooltip to work)\n */\nfunction BreadcrumbText(props: { entry: PluginHeaderBreadcrumbEntry }) {\n const { entry } = props;\n const { ref, truncated } = useIsTruncated<HTMLParagraphElement>();\n\n return (\n <BreadcrumbTooltipWrapper label={entry.label} showTooltip={truncated}>\n <Focusable excludeFromTabOrder={!truncated}>\n <Text variant=\"body-medium\" truncate ref={ref}>\n {entry.label}\n </Text>\n </Focusable>\n </BreadcrumbTooltipWrapper>\n );\n}\n\n/** Renders an ellipsis button that opens a menu with the collapsed breadcrumb items. */\nfunction CollapsedSegment(props: {\n items: PluginHeaderBreadcrumbEntry[];\n ellipsisClassName?: string;\n}) {\n const { items, ellipsisClassName } = props;\n const ariaLabel = 'Show more breadcrumbs';\n return (\n <RACBreadcrumb key=\"collapsed\">\n <MenuTrigger>\n <RACButton aria-label={ariaLabel} className={ellipsisClassName}>\n <Text as=\"span\" variant=\"body-medium\">\n …\n </Text>\n </RACButton>\n <Menu>\n {items.map(item => (\n <MenuItem key={item.href} href={item.href}>\n {item.label}\n </MenuItem>\n ))}\n </Menu>\n </MenuTrigger>\n <BreadcrumbSeparator />\n </RACBreadcrumb>\n );\n}\n\n/** Renders a non-current breadcrumb segment as a link with a trailing separator. */\nfunction AncestorSegment(props: { entry: PluginHeaderBreadcrumbEntry }) {\n const { entry } = props;\n return (\n <RACBreadcrumb key={entry.href}>\n <BreadcrumbLink entry={entry} />\n <BreadcrumbSeparator />\n </RACBreadcrumb>\n );\n}\n\n/**\n * Renders the current page in the breadcrumbs trail\n * - RAC Breadcrumbs will attach `data-current` to this segment\n * - If it's the only entry (eg. we are on the root page of the Plugin), it will render as a link\n * - Otherwise renders as text\n * - If truncated, will be focusable and show a breadcrumb\n */\nfunction CurrentSegment(props: {\n entry: PluginHeaderBreadcrumbEntry;\n isSingleEntry: boolean;\n}) {\n const { entry, isSingleEntry } = props;\n return (\n <RACBreadcrumb key={entry.href}>\n {isSingleEntry ? (\n <BreadcrumbLink entry={entry} />\n ) : (\n <BreadcrumbText entry={entry} />\n )}\n </RACBreadcrumb>\n );\n}\n\n/**\n * Renders a breadcrumb navigation trail from an ordered list of entries.\n *\n * - Uses RAC Breadcrumb and Breadcrumbs internally for a11y utils\n * - A single entry renders as a clickable link (plugin root page).\n * - Multiple entries render ancestors as links and the last entry as plain text.\n * - When there are {@link COLLAPSE_THRESHOLD} or more entries, middle items\n * collapse behind an ellipsis menu, keeping the root and leading items visible.\n *\n * @internal\n */\nexport function PluginHeaderBreadcrumbs(props: {\n entries: PluginHeaderBreadcrumbEntry[];\n className?: string;\n ellipsisClassName?: string;\n}) {\n const { entries, className, ellipsisClassName } = props;\n\n if (entries.length === 0) return null;\n\n const isSingleEntry = entries.length === 1;\n const current = entries[entries.length - 1];\n const rest = entries.slice(0, -1);\n\n let ancestorItems: React.ReactNode = null;\n\n if (entries.length >= COLLAPSE_THRESHOLD) {\n const root = rest.slice(0, ROOT_ITEMS);\n const leading = rest.slice(-LEADING_ITEMS);\n const collapsed = rest.slice(ROOT_ITEMS, -LEADING_ITEMS);\n\n ancestorItems = (\n <>\n {root.map(entry => (\n <AncestorSegment key={entry.href} entry={entry} />\n ))}\n <CollapsedSegment\n items={collapsed}\n ellipsisClassName={ellipsisClassName}\n />\n {leading.map(entry => (\n <AncestorSegment key={entry.href} entry={entry} />\n ))}\n </>\n );\n } else {\n ancestorItems = rest.map(entry => (\n <AncestorSegment key={entry.href} entry={entry} />\n ));\n }\n\n return (\n <nav id=\"Breadcrumbs\" aria-label=\"Breadcrumbs\" className={className}>\n <RACBreadcrumbs>\n {ancestorItems}\n <CurrentSegment entry={current} isSingleEntry={isSingleEntry} />\n </RACBreadcrumbs>\n </nav>\n );\n}\n"],"names":["RACBreadcrumb","RACButton","RACBreadcrumbs"],"mappings":"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"}
|
|
@@ -17,12 +17,15 @@ const PluginHeaderDefinition = defineComponent()({
|
|
|
17
17
|
toolbarControls: "bui-PluginHeaderToolbarControls",
|
|
18
18
|
toolbarIcon: "bui-PluginHeaderToolbarIcon",
|
|
19
19
|
toolbarName: "bui-PluginHeaderToolbarName",
|
|
20
|
+
breadcrumbs: "bui-PluginHeaderBreadcrumbs",
|
|
21
|
+
breadcrumbsEllipsis: "bui-PluginHeaderBreadcrumbsEllipsis",
|
|
20
22
|
tabs: "bui-PluginHeaderTabsWrapper"
|
|
21
23
|
},
|
|
22
24
|
propDefs: {
|
|
23
25
|
icon: {},
|
|
24
26
|
title: {},
|
|
25
27
|
titleLink: {},
|
|
28
|
+
breadcrumbs: {},
|
|
26
29
|
customActions: {},
|
|
27
30
|
tabs: {},
|
|
28
31
|
onTabSelectionChange: {},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"definition.esm.js","sources":["../../../src/components/PluginHeader/definition.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { defineComponent } from '../../hooks/useDefinition';\nimport type { PluginHeaderOwnProps } from './types';\nimport styles from './PluginHeader.module.css';\n\n/**\n * Component definition for PluginHeader\n * @public\n */\nexport const PluginHeaderDefinition = defineComponent<PluginHeaderOwnProps>()({\n styles,\n classNames: {\n root: 'bui-PluginHeader',\n toolbar: 'bui-PluginHeaderToolbar',\n toolbarContent: 'bui-PluginHeaderToolbarContent',\n toolbarControls: 'bui-PluginHeaderToolbarControls',\n toolbarIcon: 'bui-PluginHeaderToolbarIcon',\n toolbarName: 'bui-PluginHeaderToolbarName',\n tabs: 'bui-PluginHeaderTabsWrapper',\n },\n propDefs: {\n icon: {},\n title: {},\n titleLink: {},\n customActions: {},\n tabs: {},\n onTabSelectionChange: {},\n className: {},\n },\n});\n"],"names":[],"mappings":";;;;;;;;;;AAwBO,MAAM,sBAAA,GAAyB,iBAAsC,CAAE;AAAA,EAC5E,MAAA;AAAA,EACA,UAAA,EAAY;AAAA,IACV,IAAA,EAAM,kBAAA;AAAA,IACN,OAAA,EAAS,yBAAA;AAAA,IACT,cAAA,EAAgB,gCAAA;AAAA,IAChB,eAAA,EAAiB,iCAAA;AAAA,IACjB,WAAA,EAAa,6BAAA;AAAA,IACb,WAAA,EAAa,6BAAA;AAAA,IACb,IAAA,EAAM;AAAA,GACR;AAAA,EACA,QAAA,EAAU;AAAA,IACR,MAAM,EAAC;AAAA,IACP,OAAO,EAAC;AAAA,IACR,WAAW,EAAC;AAAA,IACZ,eAAe,EAAC;AAAA,IAChB,MAAM,EAAC;AAAA,IACP,sBAAsB,EAAC;AAAA,IACvB,WAAW;AAAC;AAEhB,CAAC;;;;"}
|
|
1
|
+
{"version":3,"file":"definition.esm.js","sources":["../../../src/components/PluginHeader/definition.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { defineComponent } from '../../hooks/useDefinition';\nimport type { PluginHeaderOwnProps } from './types';\nimport styles from './PluginHeader.module.css';\n\n/**\n * Component definition for PluginHeader\n * @public\n */\nexport const PluginHeaderDefinition = defineComponent<PluginHeaderOwnProps>()({\n styles,\n classNames: {\n root: 'bui-PluginHeader',\n toolbar: 'bui-PluginHeaderToolbar',\n toolbarContent: 'bui-PluginHeaderToolbarContent',\n toolbarControls: 'bui-PluginHeaderToolbarControls',\n toolbarIcon: 'bui-PluginHeaderToolbarIcon',\n toolbarName: 'bui-PluginHeaderToolbarName',\n breadcrumbs: 'bui-PluginHeaderBreadcrumbs',\n breadcrumbsEllipsis: 'bui-PluginHeaderBreadcrumbsEllipsis',\n tabs: 'bui-PluginHeaderTabsWrapper',\n },\n propDefs: {\n icon: {},\n title: {},\n titleLink: {},\n breadcrumbs: {},\n customActions: {},\n tabs: {},\n onTabSelectionChange: {},\n className: {},\n },\n});\n"],"names":[],"mappings":";;;;;;;;;;AAwBO,MAAM,sBAAA,GAAyB,iBAAsC,CAAE;AAAA,EAC5E,MAAA;AAAA,EACA,UAAA,EAAY;AAAA,IACV,IAAA,EAAM,kBAAA;AAAA,IACN,OAAA,EAAS,yBAAA;AAAA,IACT,cAAA,EAAgB,gCAAA;AAAA,IAChB,eAAA,EAAiB,iCAAA;AAAA,IACjB,WAAA,EAAa,6BAAA;AAAA,IACb,WAAA,EAAa,6BAAA;AAAA,IACb,WAAA,EAAa,6BAAA;AAAA,IACb,mBAAA,EAAqB,qCAAA;AAAA,IACrB,IAAA,EAAM;AAAA,GACR;AAAA,EACA,QAAA,EAAU;AAAA,IACR,MAAM,EAAC;AAAA,IACP,OAAO,EAAC;AAAA,IACR,WAAW,EAAC;AAAA,IACZ,aAAa,EAAC;AAAA,IACd,eAAe,EAAC;AAAA,IAChB,MAAM,EAAC;AAAA,IACP,sBAAsB,EAAC;AAAA,IACvB,WAAW;AAAC;AAEhB,CAAC;;;;"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { useRef, useState } from 'react';
|
|
2
|
+
import { useIsomorphicLayoutEffect } from '../../hooks/useIsomorphicLayoutEffect.esm.js';
|
|
3
|
+
|
|
4
|
+
const DEBOUNCE_MS = 150;
|
|
5
|
+
function observeResize(el, callback) {
|
|
6
|
+
if (typeof ResizeObserver === "undefined") return void 0;
|
|
7
|
+
let timerId;
|
|
8
|
+
const observer = new ResizeObserver(() => {
|
|
9
|
+
clearTimeout(timerId);
|
|
10
|
+
timerId = setTimeout(callback, DEBOUNCE_MS);
|
|
11
|
+
});
|
|
12
|
+
observer.observe(el);
|
|
13
|
+
return () => {
|
|
14
|
+
clearTimeout(timerId);
|
|
15
|
+
observer.disconnect();
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
function useIsTruncated() {
|
|
19
|
+
const ref = useRef(null);
|
|
20
|
+
const [truncated, setTruncated] = useState(false);
|
|
21
|
+
useIsomorphicLayoutEffect(() => {
|
|
22
|
+
const el = ref.current;
|
|
23
|
+
if (!el) return;
|
|
24
|
+
setTruncated(el.scrollWidth > el.clientWidth);
|
|
25
|
+
});
|
|
26
|
+
useIsomorphicLayoutEffect(() => {
|
|
27
|
+
const el = ref.current;
|
|
28
|
+
if (!el) return void 0;
|
|
29
|
+
const check = () => setTruncated(el.scrollWidth > el.clientWidth);
|
|
30
|
+
return observeResize(el, check);
|
|
31
|
+
}, []);
|
|
32
|
+
return { ref, truncated };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export { useIsTruncated };
|
|
36
|
+
//# sourceMappingURL=useIsTruncated.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useIsTruncated.esm.js","sources":["../../../src/components/PluginHeader/useIsTruncated.ts"],"sourcesContent":["/*\n * Copyright 2026 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { useRef, useState } from 'react';\nimport { useIsomorphicLayoutEffect } from '../../hooks/useIsomorphicLayoutEffect';\n\nconst DEBOUNCE_MS = 150;\n\nfunction observeResize(\n el: HTMLElement,\n callback: () => void,\n): (() => void) | undefined {\n if (typeof ResizeObserver === 'undefined') return undefined; // ResizeObserver isn't available in all runtimes (e.g. older browsers, SSR, Jest/jsdom)\n\n let timerId: ReturnType<typeof setTimeout> | undefined;\n const observer = new ResizeObserver(() => {\n clearTimeout(timerId);\n timerId = setTimeout(callback, DEBOUNCE_MS);\n });\n observer.observe(el);\n return () => {\n clearTimeout(timerId);\n observer.disconnect();\n };\n}\n\n/**\n * Tracks whether a text element is overflowing its container via CSS truncation.\n * Useful for conditionally showing a tooltip only when text is truncated.\n *\n * Checks on every render and whenever the element resizes (via ResizeObserver, debounced).\n *\n * @example\n * ```tsx\n * const { ref, truncated } = useIsTruncated();\n *\n * <TooltipTrigger isDisabled={!truncated}>\n * <span ref={ref}>\n * {label}\n * </span>\n * <Tooltip>{label}</Tooltip>\n * </TooltipTrigger>\n * ```\n *\n * @internal\n */\nexport function useIsTruncated<T extends HTMLElement = HTMLElement>(): {\n ref: React.RefObject<T>;\n truncated: boolean;\n} {\n const ref = useRef<T>(null);\n const [truncated, setTruncated] = useState(false);\n\n // Re-check after each render in case the content changes without a resize event.\n useIsomorphicLayoutEffect(() => {\n const el = ref.current;\n if (!el) return;\n setTruncated(el.scrollWidth > el.clientWidth);\n });\n\n // Also keep it up-to-date when the element resizes.\n useIsomorphicLayoutEffect(() => {\n const el = ref.current;\n if (!el) return undefined;\n\n const check = () => setTruncated(el.scrollWidth > el.clientWidth);\n\n return observeResize(el, check);\n }, []);\n\n return { ref, truncated };\n}\n"],"names":[],"mappings":";;;AAmBA,MAAM,WAAA,GAAc,GAAA;AAEpB,SAAS,aAAA,CACP,IACA,QAAA,EAC0B;AAC1B,EAAA,IAAI,OAAO,cAAA,KAAmB,WAAA,EAAa,OAAO,MAAA;AAElD,EAAA,IAAI,OAAA;AACJ,EAAA,MAAM,QAAA,GAAW,IAAI,cAAA,CAAe,MAAM;AACxC,IAAA,YAAA,CAAa,OAAO,CAAA;AACpB,IAAA,OAAA,GAAU,UAAA,CAAW,UAAU,WAAW,CAAA;AAAA,EAC5C,CAAC,CAAA;AACD,EAAA,QAAA,CAAS,QAAQ,EAAE,CAAA;AACnB,EAAA,OAAO,MAAM;AACX,IAAA,YAAA,CAAa,OAAO,CAAA;AACpB,IAAA,QAAA,CAAS,UAAA,EAAW;AAAA,EACtB,CAAA;AACF;AAsBO,SAAS,cAAA,GAGd;AACA,EAAA,MAAM,GAAA,GAAM,OAAU,IAAI,CAAA;AAC1B,EAAA,MAAM,CAAC,SAAA,EAAW,YAAY,CAAA,GAAI,SAAS,KAAK,CAAA;AAGhD,EAAA,yBAAA,CAA0B,MAAM;AAC9B,IAAA,MAAM,KAAK,GAAA,CAAI,OAAA;AACf,IAAA,IAAI,CAAC,EAAA,EAAI;AACT,IAAA,YAAA,CAAa,EAAA,CAAG,WAAA,GAAc,EAAA,CAAG,WAAW,CAAA;AAAA,EAC9C,CAAC,CAAA;AAGD,EAAA,yBAAA,CAA0B,MAAM;AAC9B,IAAA,MAAM,KAAK,GAAA,CAAI,OAAA;AACf,IAAA,IAAI,CAAC,IAAI,OAAO,MAAA;AAEhB,IAAA,MAAM,QAAQ,MAAM,YAAA,CAAa,EAAA,CAAG,WAAA,GAAc,GAAG,WAAW,CAAA;AAEhE,IAAA,OAAO,aAAA,CAAc,IAAI,KAAK,CAAA;AAAA,EAChC,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,OAAO,EAAE,KAAK,SAAA,EAAU;AAC1B;;;;"}
|
|
@@ -1,18 +1,52 @@
|
|
|
1
1
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
2
|
import { forwardRef, useEffect } from 'react';
|
|
3
|
-
import { Select as Select$1
|
|
3
|
+
import { Select as Select$1 } from 'react-aria-components';
|
|
4
4
|
import { useDefinition } from '../../hooks/useDefinition/useDefinition.esm.js';
|
|
5
5
|
import { SelectDefinition } from './definition.esm.js';
|
|
6
|
-
import { PopoverDefinition } from '../Popover/definition.esm.js';
|
|
7
|
-
import clsx from 'clsx';
|
|
8
6
|
import { FieldLabel } from '../FieldLabel/FieldLabel.esm.js';
|
|
9
7
|
import '../FieldLabel/FieldLabel.module.css.esm.js';
|
|
10
8
|
import { FieldError } from '../FieldError/FieldError.esm.js';
|
|
11
9
|
import '../FieldError/FieldError.module.css.esm.js';
|
|
10
|
+
import { Popover } from '../Popover/Popover.esm.js';
|
|
11
|
+
import '../Popover/Popover.module.css.esm.js';
|
|
12
12
|
import { SelectTrigger } from './SelectTrigger.esm.js';
|
|
13
13
|
import { SelectContent } from './SelectContent.esm.js';
|
|
14
|
+
import { useCollectionAdapter } from '../../hooks/useCollectionAdapter.esm.js';
|
|
15
|
+
import { useTrackedSelectionKeys } from '../../hooks/useTrackedSelectionKeys.esm.js';
|
|
16
|
+
import { resolveCollectionSource, toSelection, getItemKeys } from '../../utils/selectableCollection.esm.js';
|
|
14
17
|
|
|
15
|
-
|
|
18
|
+
function resolveSelectSearch({
|
|
19
|
+
search,
|
|
20
|
+
searchable,
|
|
21
|
+
searchPlaceholder
|
|
22
|
+
}) {
|
|
23
|
+
if (search !== void 0) {
|
|
24
|
+
return search;
|
|
25
|
+
}
|
|
26
|
+
if (searchable) {
|
|
27
|
+
return { placeholder: searchPlaceholder };
|
|
28
|
+
}
|
|
29
|
+
return void 0;
|
|
30
|
+
}
|
|
31
|
+
function resolveContentSearch(search, collection) {
|
|
32
|
+
if (typeof search !== "object") {
|
|
33
|
+
return search;
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
...search,
|
|
37
|
+
inputValue: collection.inputValue,
|
|
38
|
+
defaultInputValue: collection.defaultInputValue,
|
|
39
|
+
onInputChange: collection.onInputChange
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function getRetainedOptions(flatOptions, canonicalItems) {
|
|
43
|
+
if (!flatOptions) {
|
|
44
|
+
return void 0;
|
|
45
|
+
}
|
|
46
|
+
const sourceIds = getItemKeys(flatOptions);
|
|
47
|
+
return canonicalItems.filter((item) => !sourceIds.has(item.id));
|
|
48
|
+
}
|
|
49
|
+
function SelectImpl(props, ref) {
|
|
16
50
|
const { ownProps, restProps, dataAttributes } = useDefinition(
|
|
17
51
|
SelectDefinition,
|
|
18
52
|
{
|
|
@@ -20,7 +54,6 @@ const Select = forwardRef((props, ref) => {
|
|
|
20
54
|
...props
|
|
21
55
|
}
|
|
22
56
|
);
|
|
23
|
-
const { ownProps: popoverOwnProps } = useDefinition(PopoverDefinition, {});
|
|
24
57
|
const {
|
|
25
58
|
classes,
|
|
26
59
|
label,
|
|
@@ -29,6 +62,11 @@ const Select = forwardRef((props, ref) => {
|
|
|
29
62
|
icon,
|
|
30
63
|
searchable,
|
|
31
64
|
searchPlaceholder,
|
|
65
|
+
search,
|
|
66
|
+
loading,
|
|
67
|
+
items,
|
|
68
|
+
children,
|
|
69
|
+
dependencies,
|
|
32
70
|
isRequired,
|
|
33
71
|
secondaryLabel
|
|
34
72
|
} = ownProps;
|
|
@@ -42,6 +80,34 @@ const Select = forwardRef((props, ref) => {
|
|
|
42
80
|
}
|
|
43
81
|
}, [label, ariaLabel, ariaLabelledBy]);
|
|
44
82
|
const secondaryLabelText = secondaryLabel || (isRequired ? "Required" : null);
|
|
83
|
+
const resolvedSearch = resolveSelectSearch({
|
|
84
|
+
search,
|
|
85
|
+
searchable,
|
|
86
|
+
searchPlaceholder
|
|
87
|
+
});
|
|
88
|
+
const collectionSource = resolveCollectionSource({ options, items });
|
|
89
|
+
const controlledValue = restProps.value !== void 0 ? restProps.value : restProps.selectedKey;
|
|
90
|
+
const defaultValue = restProps.defaultValue !== void 0 ? restProps.defaultValue : restProps.defaultSelectedKey;
|
|
91
|
+
const trackedSelection = useTrackedSelectionKeys({
|
|
92
|
+
selectedKeys: controlledValue === void 0 ? void 0 : toSelection(controlledValue),
|
|
93
|
+
defaultSelectedKeys: toSelection(defaultValue)
|
|
94
|
+
});
|
|
95
|
+
const collection = useCollectionAdapter({
|
|
96
|
+
items: collectionSource.source,
|
|
97
|
+
selectedKeys: trackedSelection.selectedKeys,
|
|
98
|
+
search: resolvedSearch,
|
|
99
|
+
loading
|
|
100
|
+
});
|
|
101
|
+
const retainedOptions = getRetainedOptions(
|
|
102
|
+
collectionSource.flatOptions,
|
|
103
|
+
collection.canonicalItems
|
|
104
|
+
);
|
|
105
|
+
const renderedItems = collectionSource.rendersItems ? collection.canonicalItems : void 0;
|
|
106
|
+
const contentSearch = resolveContentSearch(resolvedSearch, collection);
|
|
107
|
+
const handleChange = (value) => {
|
|
108
|
+
trackedSelection.onSelectionChange(toSelection(value));
|
|
109
|
+
restProps.onChange?.(value);
|
|
110
|
+
};
|
|
45
111
|
return /* @__PURE__ */ jsxs(
|
|
46
112
|
Select$1,
|
|
47
113
|
{
|
|
@@ -49,6 +115,8 @@ const Select = forwardRef((props, ref) => {
|
|
|
49
115
|
...dataAttributes,
|
|
50
116
|
ref,
|
|
51
117
|
...restProps,
|
|
118
|
+
onChange: handleChange,
|
|
119
|
+
allowsEmptyCollection: true,
|
|
52
120
|
children: [
|
|
53
121
|
/* @__PURE__ */ jsx(
|
|
54
122
|
FieldLabel,
|
|
@@ -61,25 +129,25 @@ const Select = forwardRef((props, ref) => {
|
|
|
61
129
|
),
|
|
62
130
|
/* @__PURE__ */ jsx(SelectTrigger, { icon }),
|
|
63
131
|
/* @__PURE__ */ jsx(FieldError, {}),
|
|
64
|
-
/* @__PURE__ */ jsx(
|
|
65
|
-
|
|
132
|
+
/* @__PURE__ */ jsx(Popover, { className: classes.popover, hideArrow: true, ...dataAttributes, children: /* @__PURE__ */ jsx(
|
|
133
|
+
SelectContent,
|
|
66
134
|
{
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
)
|
|
135
|
+
search: contentSearch,
|
|
136
|
+
options: collectionSource.options,
|
|
137
|
+
items: renderedItems,
|
|
138
|
+
dependencies,
|
|
139
|
+
loading: collection.loading,
|
|
140
|
+
isStale: collection.isStale,
|
|
141
|
+
visibleIds: collection.visibleIds,
|
|
142
|
+
retainedOptions,
|
|
143
|
+
children
|
|
77
144
|
}
|
|
78
|
-
)
|
|
145
|
+
) })
|
|
79
146
|
]
|
|
80
147
|
}
|
|
81
148
|
);
|
|
82
|
-
}
|
|
149
|
+
}
|
|
150
|
+
const Select = forwardRef(SelectImpl);
|
|
83
151
|
Select.displayName = "Select";
|
|
84
152
|
|
|
85
153
|
export { Select };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Select.esm.js","sources":["../../../src/components/Select/Select.tsx"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { forwardRef, useEffect } from 'react';\nimport { Select as AriaSelect, Popover } from 'react-aria-components';\nimport { SelectProps } from './types';\nimport { useDefinition } from '../../hooks/useDefinition';\nimport { SelectDefinition } from './definition';\nimport { PopoverDefinition } from '../Popover/definition';\nimport clsx from 'clsx';\nimport { FieldLabel } from '../FieldLabel';\nimport { FieldError } from '../FieldError';\nimport { SelectTrigger } from './SelectTrigger';\nimport { SelectContent } from './SelectContent';\n\n/**\n * A dropdown picker for selecting one or multiple options from a list, with optional search filtering and inline error display.\n *\n * @public\n */\nexport const Select = forwardRef<\n HTMLDivElement,\n SelectProps<'single' | 'multiple'>\n>((props, ref) => {\n const { ownProps, restProps, dataAttributes } = useDefinition(\n SelectDefinition,\n {\n placeholder: 'Select an option',\n ...props,\n },\n );\n const { ownProps: popoverOwnProps } = useDefinition(PopoverDefinition, {});\n\n const {\n classes,\n label,\n description,\n options,\n icon,\n searchable,\n searchPlaceholder,\n isRequired,\n secondaryLabel,\n } = ownProps;\n\n const ariaLabel = restProps['aria-label'];\n const ariaLabelledBy = restProps['aria-labelledby'];\n\n useEffect(() => {\n if (!label && !ariaLabel && !ariaLabelledBy) {\n console.warn(\n 'Select requires either a visible label, aria-label, or aria-labelledby for accessibility',\n );\n }\n }, [label, ariaLabel, ariaLabelledBy]);\n\n const secondaryLabelText = secondaryLabel || (isRequired ? 'Required' : null);\n\n return (\n <AriaSelect\n className={classes.root}\n {...dataAttributes}\n ref={ref}\n {...restProps}\n >\n <FieldLabel\n label={label}\n secondaryLabel={secondaryLabelText}\n description={description}\n descriptionSlot=\"description\"\n />\n <SelectTrigger icon={icon} />\n <FieldError />\n <Popover\n className={clsx(popoverOwnProps.classes.root, classes.popover)}\n {...dataAttributes}\n >\n <SelectContent\n searchable={searchable}\n searchPlaceholder={searchPlaceholder}\n options={options}\n />\n </Popover>\n </AriaSelect>\n );\n});\n\nSelect.displayName = 'Select';\n"],"names":["AriaSelect"],"mappings":";;;;;;;;;;;;;;AAiCO,MAAM,MAAA,GAAS,UAAA,CAGpB,CAAC,KAAA,EAAO,GAAA,KAAQ;AAChB,EAAA,MAAM,EAAE,QAAA,EAAU,SAAA,EAAW,cAAA,EAAe,GAAI,aAAA;AAAA,IAC9C,gBAAA;AAAA,IACA;AAAA,MACE,WAAA,EAAa,kBAAA;AAAA,MACb,GAAG;AAAA;AACL,GACF;AACA,EAAA,MAAM,EAAE,QAAA,EAAU,eAAA,KAAoB,aAAA,CAAc,iBAAA,EAAmB,EAAE,CAAA;AAEzE,EAAA,MAAM;AAAA,IACJ,OAAA;AAAA,IACA,KAAA;AAAA,IACA,WAAA;AAAA,IACA,OAAA;AAAA,IACA,IAAA;AAAA,IACA,UAAA;AAAA,IACA,iBAAA;AAAA,IACA,UAAA;AAAA,IACA;AAAA,GACF,GAAI,QAAA;AAEJ,EAAA,MAAM,SAAA,GAAY,UAAU,YAAY,CAAA;AACxC,EAAA,MAAM,cAAA,GAAiB,UAAU,iBAAiB,CAAA;AAElD,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,KAAA,IAAS,CAAC,SAAA,IAAa,CAAC,cAAA,EAAgB;AAC3C,MAAA,OAAA,CAAQ,IAAA;AAAA,QACN;AAAA,OACF;AAAA,IACF;AAAA,EACF,CAAA,EAAG,CAAC,KAAA,EAAO,SAAA,EAAW,cAAc,CAAC,CAAA;AAErC,EAAA,MAAM,kBAAA,GAAqB,cAAA,KAAmB,UAAA,GAAa,UAAA,GAAa,IAAA,CAAA;AAExE,EAAA,uBACE,IAAA;AAAA,IAACA,QAAA;AAAA,IAAA;AAAA,MACC,WAAW,OAAA,CAAQ,IAAA;AAAA,MAClB,GAAG,cAAA;AAAA,MACJ,GAAA;AAAA,MACC,GAAG,SAAA;AAAA,MAEJ,QAAA,EAAA;AAAA,wBAAA,GAAA;AAAA,UAAC,UAAA;AAAA,UAAA;AAAA,YACC,KAAA;AAAA,YACA,cAAA,EAAgB,kBAAA;AAAA,YAChB,WAAA;AAAA,YACA,eAAA,EAAgB;AAAA;AAAA,SAClB;AAAA,wBACA,GAAA,CAAC,iBAAc,IAAA,EAAY,CAAA;AAAA,4BAC1B,UAAA,EAAA,EAAW,CAAA;AAAA,wBACZ,GAAA;AAAA,UAAC,OAAA;AAAA,UAAA;AAAA,YACC,WAAW,IAAA,CAAK,eAAA,CAAgB,OAAA,CAAQ,IAAA,EAAM,QAAQ,OAAO,CAAA;AAAA,YAC5D,GAAG,cAAA;AAAA,YAEJ,QAAA,kBAAA,GAAA;AAAA,cAAC,aAAA;AAAA,cAAA;AAAA,gBACC,UAAA;AAAA,gBACA,iBAAA;AAAA,gBACA;AAAA;AAAA;AACF;AAAA;AACF;AAAA;AAAA,GACF;AAEJ,CAAC;AAED,MAAA,CAAO,WAAA,GAAc,QAAA;;;;"}
|
|
1
|
+
{"version":3,"file":"Select.esm.js","sources":["../../../src/components/Select/Select.tsx"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { forwardRef, useEffect } from 'react';\nimport { Select as AriaSelect } from 'react-aria-components';\nimport type {\n CollectionItem,\n NormalizedOption,\n} from '../../types/selectableCollection';\nimport type { Key } from 'react-aria-components';\nimport type { ChangeValueType } from 'react-stately/useSelectState';\nimport type { SelectContentOwnProps, SelectProps } from './types';\nimport { useDefinition } from '../../hooks/useDefinition';\nimport { SelectDefinition } from './definition';\nimport { FieldLabel } from '../FieldLabel';\nimport { FieldError } from '../FieldError';\nimport { Popover } from '../Popover';\nimport { SelectTrigger } from './SelectTrigger';\nimport { SelectContent } from './SelectContent';\nimport {\n useCollectionAdapter,\n type CollectionAdapterResult,\n} from '../../hooks/useCollectionAdapter';\nimport { useTrackedSelectionKeys } from '../../hooks/useTrackedSelectionKeys';\nimport {\n getItemKeys,\n resolveCollectionSource,\n toSelection,\n} from '../../utils/selectableCollection';\n\nfunction resolveSelectSearch<T extends CollectionItem>({\n search,\n searchable,\n searchPlaceholder,\n}: {\n search?: SelectContentOwnProps<T>['search'];\n searchable?: boolean;\n searchPlaceholder?: string;\n}): SelectContentOwnProps<T>['search'] {\n if (search !== undefined) {\n return search;\n }\n\n if (searchable) {\n return { placeholder: searchPlaceholder };\n }\n\n return undefined;\n}\n\nfunction resolveContentSearch<T extends CollectionItem>(\n search: SelectContentOwnProps<T>['search'],\n collection: CollectionAdapterResult<T>,\n): SelectContentOwnProps<T>['search'] {\n if (typeof search !== 'object') {\n return search;\n }\n\n return {\n ...search,\n inputValue: collection.inputValue,\n defaultInputValue: collection.defaultInputValue,\n onInputChange: collection.onInputChange,\n } as SelectContentOwnProps<T>['search'];\n}\n\nfunction getRetainedOptions<T extends CollectionItem>(\n flatOptions: NormalizedOption[] | undefined,\n canonicalItems: T[],\n) {\n if (!flatOptions) {\n return undefined;\n }\n\n const sourceIds = getItemKeys(flatOptions);\n return canonicalItems.filter(item => !sourceIds.has(item.id));\n}\n\n/**\n * A dropdown picker for selecting one or multiple options from a list, with optional search filtering and inline error display.\n *\n * @public\n */\nfunction SelectImpl<\n M extends 'single' | 'multiple' = 'single' | 'multiple',\n T extends CollectionItem = NormalizedOption,\n>(props: SelectProps<M, T>, ref: React.ForwardedRef<HTMLDivElement>) {\n const { ownProps, restProps, dataAttributes } = useDefinition(\n SelectDefinition,\n {\n placeholder: 'Select an option',\n ...props,\n },\n );\n\n const {\n classes,\n label,\n description,\n options,\n icon,\n searchable,\n searchPlaceholder,\n search,\n loading,\n items,\n children,\n dependencies,\n isRequired,\n secondaryLabel,\n } = ownProps;\n\n const ariaLabel = restProps['aria-label'];\n const ariaLabelledBy = restProps['aria-labelledby'];\n\n useEffect(() => {\n if (!label && !ariaLabel && !ariaLabelledBy) {\n console.warn(\n 'Select requires either a visible label, aria-label, or aria-labelledby for accessibility',\n );\n }\n }, [label, ariaLabel, ariaLabelledBy]);\n\n const secondaryLabelText = secondaryLabel || (isRequired ? 'Required' : null);\n const resolvedSearch = resolveSelectSearch<T>({\n search: search as SelectContentOwnProps<T>['search'],\n searchable,\n searchPlaceholder,\n });\n const collectionSource = resolveCollectionSource<T>({ options, items });\n const controlledValue =\n restProps.value !== undefined ? restProps.value : restProps.selectedKey;\n const defaultValue =\n restProps.defaultValue !== undefined\n ? restProps.defaultValue\n : restProps.defaultSelectedKey;\n const trackedSelection = useTrackedSelectionKeys({\n selectedKeys:\n controlledValue === undefined ? undefined : toSelection(controlledValue),\n defaultSelectedKeys: toSelection(defaultValue),\n });\n const collection = useCollectionAdapter({\n items: collectionSource.source,\n selectedKeys: trackedSelection.selectedKeys,\n search: resolvedSearch,\n loading,\n });\n const retainedOptions = getRetainedOptions(\n collectionSource.flatOptions,\n collection.canonicalItems,\n );\n const renderedItems = collectionSource.rendersItems\n ? collection.canonicalItems\n : undefined;\n const contentSearch = resolveContentSearch(resolvedSearch, collection);\n const handleChange = (value: ChangeValueType<M>) => {\n trackedSelection.onSelectionChange(toSelection(value));\n restProps.onChange?.(value);\n };\n\n return (\n <AriaSelect\n className={classes.root}\n {...dataAttributes}\n ref={ref}\n {...restProps}\n onChange={handleChange}\n allowsEmptyCollection\n >\n <FieldLabel\n label={label}\n secondaryLabel={secondaryLabelText}\n description={description}\n descriptionSlot=\"description\"\n />\n <SelectTrigger icon={icon} />\n <FieldError />\n <Popover className={classes.popover} hideArrow {...dataAttributes}>\n <SelectContent\n search={contentSearch}\n options={collectionSource.options}\n items={renderedItems}\n dependencies={dependencies}\n loading={collection.loading}\n isStale={collection.isStale}\n visibleIds={collection.visibleIds}\n retainedOptions={\n retainedOptions as unknown as ReadonlyArray<NormalizedOption>\n }\n >\n {children}\n </SelectContent>\n </Popover>\n </AriaSelect>\n );\n}\n\n/** @public */\nexport const Select = forwardRef(SelectImpl) as {\n <\n M extends 'single' | 'multiple' = 'single' | 'multiple',\n T extends { id: Key } = NormalizedOption,\n >(\n props: SelectProps<M, T> & React.RefAttributes<HTMLDivElement>,\n ): React.ReactElement | null;\n displayName?: string;\n};\n\nSelect.displayName = 'Select';\n"],"names":["AriaSelect"],"mappings":"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"}
|