@backstage/ui 0.17.1 → 0.18.0-next.1
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 +22 -0
- package/dist/analytics/useAnalytics.esm.js +5 -5
- package/dist/analytics/useAnalytics.esm.js.map +1 -1
- package/dist/components/Accordion/definition.esm.js +3 -1
- package/dist/components/Accordion/definition.esm.js.map +1 -1
- package/dist/components/Alert/definition.esm.js +3 -1
- package/dist/components/Alert/definition.esm.js.map +1 -1
- package/dist/components/Avatar/definition.esm.js +3 -1
- package/dist/components/Avatar/definition.esm.js.map +1 -1
- package/dist/components/Badge/Badge.module.css.esm.js +2 -2
- package/dist/components/Badge/definition.esm.js +3 -1
- package/dist/components/Badge/definition.esm.js.map +1 -1
- package/dist/components/Box/definition.esm.js +3 -1
- package/dist/components/Box/definition.esm.js.map +1 -1
- package/dist/components/Button/Button.module.css.esm.js +2 -2
- package/dist/components/Button/definition.esm.js +3 -1
- package/dist/components/Button/definition.esm.js.map +1 -1
- package/dist/components/ButtonIcon/definition.esm.js +3 -1
- package/dist/components/ButtonIcon/definition.esm.js.map +1 -1
- package/dist/components/ButtonLink/ButtonLink.esm.js +39 -23
- package/dist/components/ButtonLink/ButtonLink.esm.js.map +1 -1
- package/dist/components/ButtonLink/ButtonLink.module.css.esm.js +2 -2
- package/dist/components/ButtonLink/definition.esm.js +4 -1
- package/dist/components/ButtonLink/definition.esm.js.map +1 -1
- package/dist/components/Card/Card.esm.js +2 -1
- package/dist/components/Card/Card.esm.js.map +1 -1
- package/dist/components/Card/Card.module.css.esm.js +2 -2
- package/dist/components/Card/definition.esm.js +3 -1
- package/dist/components/Card/definition.esm.js.map +1 -1
- package/dist/components/Checkbox/Checkbox.module.css.esm.js +2 -2
- package/dist/components/Checkbox/definition.esm.js +3 -1
- package/dist/components/Checkbox/definition.esm.js.map +1 -1
- package/dist/components/CheckboxGroup/definition.esm.js +3 -1
- package/dist/components/CheckboxGroup/definition.esm.js.map +1 -1
- package/dist/components/Combobox/Combobox.module.css.esm.js +2 -2
- package/dist/components/Combobox/ComboboxItem.esm.js +37 -11
- package/dist/components/Combobox/ComboboxItem.esm.js.map +1 -1
- package/dist/components/Combobox/definition.esm.js +4 -1
- package/dist/components/Combobox/definition.esm.js.map +1 -1
- package/dist/components/Container/definition.esm.js +3 -1
- package/dist/components/Container/definition.esm.js.map +1 -1
- package/dist/components/DatePicker/DatePicker.module.css.esm.js +2 -2
- package/dist/components/DatePicker/definition.esm.js +3 -1
- package/dist/components/DatePicker/definition.esm.js.map +1 -1
- package/dist/components/DateRangePicker/DateRangePicker.module.css.esm.js +2 -2
- package/dist/components/DateRangePicker/definition.esm.js +3 -1
- package/dist/components/DateRangePicker/definition.esm.js.map +1 -1
- package/dist/components/Dialog/Dialog.module.css.esm.js +2 -2
- package/dist/components/Dialog/definition.esm.js +3 -1
- package/dist/components/Dialog/definition.esm.js.map +1 -1
- package/dist/components/FieldError/FieldError.module.css.esm.js +2 -2
- package/dist/components/FieldError/definition.esm.js +3 -1
- package/dist/components/FieldError/definition.esm.js.map +1 -1
- package/dist/components/FieldLabel/FieldLabel.module.css.esm.js +2 -2
- package/dist/components/FieldLabel/definition.esm.js +3 -1
- package/dist/components/FieldLabel/definition.esm.js.map +1 -1
- package/dist/components/Flex/definition.esm.js +3 -1
- package/dist/components/Flex/definition.esm.js.map +1 -1
- package/dist/components/FullPage/definition.esm.js +3 -1
- package/dist/components/FullPage/definition.esm.js.map +1 -1
- package/dist/components/Grid/definition.esm.js +3 -1
- package/dist/components/Grid/definition.esm.js.map +1 -1
- package/dist/components/Header/HeaderMetadataStatus.esm.js +3 -1
- package/dist/components/Header/HeaderMetadataStatus.esm.js.map +1 -1
- package/dist/components/Header/HeaderMetadataUsers.esm.js +3 -1
- package/dist/components/Header/HeaderMetadataUsers.esm.js.map +1 -1
- package/dist/components/Header/HeaderNav.esm.js +67 -24
- package/dist/components/Header/HeaderNav.esm.js.map +1 -1
- package/dist/components/Header/HeaderNav.module.css.esm.js +2 -2
- package/dist/components/Header/HeaderNavDefinition.esm.js +4 -1
- package/dist/components/Header/HeaderNavDefinition.esm.js.map +1 -1
- package/dist/components/Header/definition.esm.js +3 -1
- package/dist/components/Header/definition.esm.js.map +1 -1
- package/dist/components/Link/Link.esm.js +64 -23
- package/dist/components/Link/Link.esm.js.map +1 -1
- package/dist/components/Link/definition.esm.js +4 -1
- package/dist/components/Link/definition.esm.js.map +1 -1
- package/dist/components/List/List.esm.js +3 -2
- package/dist/components/List/List.esm.js.map +1 -1
- package/dist/components/List/List.module.css.esm.js +2 -2
- package/dist/components/List/definition.esm.js +3 -1
- package/dist/components/List/definition.esm.js.map +1 -1
- package/dist/components/Menu/Menu.esm.js +48 -14
- package/dist/components/Menu/Menu.esm.js.map +1 -1
- package/dist/components/Menu/Menu.module.css.esm.js +2 -2
- package/dist/components/Menu/definition.esm.js +5 -1
- package/dist/components/Menu/definition.esm.js.map +1 -1
- package/dist/components/NumberField/NumberField.module.css.esm.js +2 -2
- package/dist/components/NumberField/definition.esm.js +3 -1
- package/dist/components/NumberField/definition.esm.js.map +1 -1
- package/dist/components/PasswordField/PasswordField.module.css.esm.js +2 -2
- package/dist/components/PasswordField/definition.esm.js +3 -1
- package/dist/components/PasswordField/definition.esm.js.map +1 -1
- package/dist/components/PluginHeader/PluginHeaderBreadcrumbs.esm.js +3 -1
- package/dist/components/PluginHeader/PluginHeaderBreadcrumbs.esm.js.map +1 -1
- package/dist/components/PluginHeader/definition.esm.js +3 -1
- package/dist/components/PluginHeader/definition.esm.js.map +1 -1
- package/dist/components/Popover/definition.esm.js +3 -1
- package/dist/components/Popover/definition.esm.js.map +1 -1
- package/dist/components/RadioGroup/RadioGroup.module.css.esm.js +2 -2
- package/dist/components/RadioGroup/definition.esm.js +3 -1
- package/dist/components/RadioGroup/definition.esm.js.map +1 -1
- package/dist/components/SearchAutocomplete/SearchAutocomplete.esm.js +23 -5
- package/dist/components/SearchAutocomplete/SearchAutocomplete.esm.js.map +1 -1
- package/dist/components/SearchAutocomplete/SearchAutocomplete.module.css.esm.js +2 -2
- package/dist/components/SearchAutocomplete/definition.esm.js +4 -1
- package/dist/components/SearchAutocomplete/definition.esm.js.map +1 -1
- package/dist/components/SearchField/SearchField.module.css.esm.js +2 -2
- package/dist/components/SearchField/definition.esm.js +3 -1
- package/dist/components/SearchField/definition.esm.js.map +1 -1
- package/dist/components/Select/Select.module.css.esm.js +2 -2
- package/dist/components/Select/SelectItem.esm.js +17 -3
- package/dist/components/Select/SelectItem.esm.js.map +1 -1
- package/dist/components/Select/definition.esm.js +4 -1
- package/dist/components/Select/definition.esm.js.map +1 -1
- package/dist/components/Skeleton/definition.esm.js +3 -1
- package/dist/components/Skeleton/definition.esm.js.map +1 -1
- package/dist/components/Slider/Slider.module.css.esm.js +2 -2
- package/dist/components/Slider/definition.esm.js +3 -1
- package/dist/components/Slider/definition.esm.js.map +1 -1
- package/dist/components/Switch/Switch.module.css.esm.js +2 -2
- package/dist/components/Switch/definition.esm.js +3 -1
- package/dist/components/Switch/definition.esm.js.map +1 -1
- package/dist/components/Table/Table.module.css.esm.js +2 -2
- package/dist/components/Table/components/Row.esm.js +9 -9
- package/dist/components/Table/components/Row.esm.js.map +1 -1
- package/dist/components/Table/components/Table.esm.js +5 -3
- package/dist/components/Table/components/Table.esm.js.map +1 -1
- package/dist/components/Table/components/TableBodySkeleton.esm.js +3 -1
- package/dist/components/Table/components/TableBodySkeleton.esm.js.map +1 -1
- package/dist/components/Table/components/TableRoot.esm.js +3 -2
- package/dist/components/Table/components/TableRoot.esm.js.map +1 -1
- package/dist/components/Table/definition.esm.js +3 -2
- package/dist/components/Table/definition.esm.js.map +1 -1
- package/dist/components/TablePagination/TablePagination.esm.js +3 -1
- package/dist/components/TablePagination/TablePagination.esm.js.map +1 -1
- package/dist/components/TablePagination/definition.esm.js +3 -1
- package/dist/components/TablePagination/definition.esm.js.map +1 -1
- package/dist/components/Tabs/Tabs.esm.js +38 -15
- package/dist/components/Tabs/Tabs.esm.js.map +1 -1
- package/dist/components/Tabs/Tabs.module.css.esm.js +2 -2
- package/dist/components/Tabs/definition.esm.js +4 -1
- package/dist/components/Tabs/definition.esm.js.map +1 -1
- package/dist/components/TagGroup/TagGroup.esm.js +12 -12
- package/dist/components/TagGroup/TagGroup.esm.js.map +1 -1
- package/dist/components/TagGroup/TagGroup.module.css.esm.js +2 -2
- package/dist/components/TagGroup/definition.esm.js +3 -2
- package/dist/components/TagGroup/definition.esm.js.map +1 -1
- package/dist/components/Text/definition.esm.js +3 -1
- package/dist/components/Text/definition.esm.js.map +1 -1
- package/dist/components/TextAreaField/definition.esm.js +3 -1
- package/dist/components/TextAreaField/definition.esm.js.map +1 -1
- package/dist/components/TextField/TextField.module.css.esm.js +2 -2
- package/dist/components/TextField/definition.esm.js +3 -1
- package/dist/components/TextField/definition.esm.js.map +1 -1
- package/dist/components/ToggleButton/ToggleButton.module.css.esm.js +2 -2
- package/dist/components/ToggleButton/definition.esm.js +3 -1
- package/dist/components/ToggleButton/definition.esm.js.map +1 -1
- package/dist/components/ToggleButtonGroup/definition.esm.js +3 -1
- package/dist/components/ToggleButtonGroup/definition.esm.js.map +1 -1
- package/dist/components/Tooltip/Tooltip.module.css.esm.js +2 -2
- package/dist/components/Tooltip/definition.esm.js +3 -1
- package/dist/components/Tooltip/definition.esm.js.map +1 -1
- package/dist/components/VisuallyHidden/definition.esm.js +3 -1
- package/dist/components/VisuallyHidden/definition.esm.js.map +1 -1
- package/dist/css/styles.css +4 -7
- package/dist/hooks/useDefinition/useDefinition.esm.js +8 -13
- package/dist/hooks/useDefinition/useDefinition.esm.js.map +1 -1
- package/dist/hooks/useDefinition/useDefinitionNavigation.esm.js +50 -0
- package/dist/hooks/useDefinition/useDefinitionNavigation.esm.js.map +1 -0
- package/dist/index.d.ts +32 -8
- package/dist/navigation/BUIRoutingProvider.esm.js +44 -0
- package/dist/navigation/BUIRoutingProvider.esm.js.map +1 -0
- package/dist/navigation/useNavigation.esm.js +130 -0
- package/dist/navigation/useNavigation.esm.js.map +1 -0
- package/dist/navigation/useRouting.esm.js +27 -0
- package/dist/navigation/useRouting.esm.js.map +1 -0
- package/dist/provider/BUIContext.esm.js +6 -0
- package/dist/provider/BUIContext.esm.js.map +1 -0
- package/dist/provider/BUIProvider.esm.js +5 -13
- package/dist/provider/BUIProvider.esm.js.map +1 -1
- package/dist/utils/linkUtils.esm.js +6 -12
- package/dist/utils/linkUtils.esm.js.map +1 -1
- package/package.json +5 -4
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { useCallback } from 'react';
|
|
3
|
+
import { RouterProvider } from 'react-aria-components';
|
|
4
|
+
import { useResolvedPath, useNavigate, useLocation, useInRouterContext, useHref, Link } from 'react-router-dom';
|
|
5
|
+
import { useResolvedHref } from '../hooks/useResolvedHref.esm.js';
|
|
6
|
+
|
|
7
|
+
const delegatedNavigations = /* @__PURE__ */ new WeakMap();
|
|
8
|
+
const buiRoutingIntegration = {
|
|
9
|
+
Link,
|
|
10
|
+
useHref,
|
|
11
|
+
useInRouterContext,
|
|
12
|
+
useLocation,
|
|
13
|
+
useNavigate,
|
|
14
|
+
useResolvedPath,
|
|
15
|
+
createRouterOptions(action, options) {
|
|
16
|
+
const routerOptions = { ...options };
|
|
17
|
+
delegatedNavigations.set(routerOptions, action);
|
|
18
|
+
return routerOptions;
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
function BUIRoutingProvider({ children }) {
|
|
22
|
+
if (!useInRouterContext()) {
|
|
23
|
+
return children;
|
|
24
|
+
}
|
|
25
|
+
return /* @__PURE__ */ jsx(ReactAriaRoutingProvider, { children });
|
|
26
|
+
}
|
|
27
|
+
function ReactAriaRoutingProvider({ children }) {
|
|
28
|
+
const providerNavigate = useNavigate();
|
|
29
|
+
const navigate = useCallback(
|
|
30
|
+
(href, options) => {
|
|
31
|
+
const delegatedNavigation = options ? delegatedNavigations.get(options) : void 0;
|
|
32
|
+
if (delegatedNavigation) {
|
|
33
|
+
delegatedNavigation();
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
providerNavigate(href, options);
|
|
37
|
+
},
|
|
38
|
+
[providerNavigate]
|
|
39
|
+
);
|
|
40
|
+
return /* @__PURE__ */ jsx(RouterProvider, { navigate, useHref: useResolvedHref, children });
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export { BUIRoutingProvider, buiRoutingIntegration };
|
|
44
|
+
//# sourceMappingURL=BUIRoutingProvider.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BUIRoutingProvider.esm.js","sources":["../../src/navigation/BUIRoutingProvider.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, type ReactNode } from 'react';\nimport { RouterProvider } from 'react-aria-components';\nimport {\n Link,\n useHref,\n useInRouterContext,\n useLocation,\n useNavigate,\n useResolvedPath,\n} from 'react-router-dom';\nimport { useResolvedHref } from '../hooks/useResolvedHref';\nimport type { BUIRoutingIntegration } from './types';\n\n// BUIProvider mounts this provider centrally so older BUI components from the\n// same React Aria module instance keep delegated client-side navigation.\n// Collection roots also mount it locally so their synthetic link items and the\n// provider always use the same React Aria module instance. Preserving each\n// item's href and original activation event lets React Aria choose between\n// client navigation and temporary-anchor activation for native link behavior.\n//\n// The exact options object identifies a component-scoped action. Unregistered\n// objects, including those from older BUI components or direct React Aria\n// usage, use the provider's router context instead. A separate React Aria\n// module instance cannot reach this provider and is handled by the rendered\n// host Link in useNavigation.\nconst delegatedNavigations = new WeakMap<object, () => void>();\n\n/** @internal */\nexport const buiRoutingIntegration: BUIRoutingIntegration = {\n Link,\n useHref,\n useInRouterContext,\n useLocation,\n useNavigate,\n useResolvedPath,\n createRouterOptions(action, options) {\n const routerOptions = { ...options };\n delegatedNavigations.set(routerOptions, action);\n return routerOptions;\n },\n};\n\n/** @internal */\nexport function BUIRoutingProvider({ children }: { children: ReactNode }) {\n if (!useInRouterContext()) {\n return children;\n }\n return <ReactAriaRoutingProvider>{children}</ReactAriaRoutingProvider>;\n}\n\nfunction ReactAriaRoutingProvider({ children }: { children: ReactNode }) {\n const providerNavigate = useNavigate();\n const navigate = useCallback(\n (href: string, options: object | undefined) => {\n const delegatedNavigation = options\n ? delegatedNavigations.get(options)\n : undefined;\n if (delegatedNavigation) {\n delegatedNavigation();\n return;\n }\n providerNavigate(href, options);\n },\n [providerNavigate],\n );\n\n return (\n <RouterProvider navigate={navigate} useHref={useResolvedHref}>\n {children}\n </RouterProvider>\n );\n}\n"],"names":[],"mappings":";;;;;;AAyCA,MAAM,oBAAA,uBAA2B,OAAA,EAA4B;AAGtD,MAAM,qBAAA,GAA+C;AAAA,EAC1D,IAAA;AAAA,EACA,OAAA;AAAA,EACA,kBAAA;AAAA,EACA,WAAA;AAAA,EACA,WAAA;AAAA,EACA,eAAA;AAAA,EACA,mBAAA,CAAoB,QAAQ,OAAA,EAAS;AACnC,IAAA,MAAM,aAAA,GAAgB,EAAE,GAAG,OAAA,EAAQ;AACnC,IAAA,oBAAA,CAAqB,GAAA,CAAI,eAAe,MAAM,CAAA;AAC9C,IAAA,OAAO,aAAA;AAAA,EACT;AACF;AAGO,SAAS,kBAAA,CAAmB,EAAE,QAAA,EAAS,EAA4B;AACxE,EAAA,IAAI,CAAC,oBAAmB,EAAG;AACzB,IAAA,OAAO,QAAA;AAAA,EACT;AACA,EAAA,uBAAO,GAAA,CAAC,4BAA0B,QAAA,EAAS,CAAA;AAC7C;AAEA,SAAS,wBAAA,CAAyB,EAAE,QAAA,EAAS,EAA4B;AACvE,EAAA,MAAM,mBAAmB,WAAA,EAAY;AACrC,EAAA,MAAM,QAAA,GAAW,WAAA;AAAA,IACf,CAAC,MAAc,OAAA,KAAgC;AAC7C,MAAA,MAAM,mBAAA,GAAsB,OAAA,GACxB,oBAAA,CAAqB,GAAA,CAAI,OAAO,CAAA,GAChC,MAAA;AACJ,MAAA,IAAI,mBAAA,EAAqB;AACvB,QAAA,mBAAA,EAAoB;AACpB,QAAA;AAAA,MACF;AACA,MAAA,gBAAA,CAAiB,MAAM,OAAO,CAAA;AAAA,IAChC,CAAA;AAAA,IACA,CAAC,gBAAgB;AAAA,GACnB;AAEA,EAAA,uBACE,GAAA,CAAC,cAAA,EAAA,EAAe,QAAA,EAAoB,OAAA,EAAS,iBAC1C,QAAA,EACH,CAAA;AAEJ;;;;"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { useCallback, useMemo } from 'react';
|
|
3
|
+
import { createPath } from 'react-router-dom';
|
|
4
|
+
import { isBrowserOwnedHref } from '../utils/linkUtils.esm.js';
|
|
5
|
+
import { useRoutingIntegration, fallbackRoutingIntegration } from './useRouting.esm.js';
|
|
6
|
+
|
|
7
|
+
function isNativeNavigation(props) {
|
|
8
|
+
const { href, target, download } = props;
|
|
9
|
+
return Boolean(
|
|
10
|
+
href !== void 0 && isBrowserOwnedHref(href) || target && target !== "_self" || download !== void 0 && download !== false
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
function useAnchorNavigation(props) {
|
|
14
|
+
const { href, routerOptions: navigateOptions } = props;
|
|
15
|
+
const routingIntegration = useRoutingIntegration();
|
|
16
|
+
const routing = routingIntegration ?? fallbackRoutingIntegration;
|
|
17
|
+
const navigate = routing.useNavigate();
|
|
18
|
+
const routerHref = href !== void 0 && !isBrowserOwnedHref(href) ? href : ".";
|
|
19
|
+
const relativeOptions = { relative: navigateOptions?.relative };
|
|
20
|
+
const resolvedPath = routing.useResolvedPath(routerHref, relativeOptions);
|
|
21
|
+
const resolvedHref = routing.useHref(routerHref, relativeOptions);
|
|
22
|
+
const navigateWithOptions = useCallback(() => {
|
|
23
|
+
if (href !== void 0) {
|
|
24
|
+
navigate(href, navigateOptions);
|
|
25
|
+
}
|
|
26
|
+
}, [navigate, href, navigateOptions]);
|
|
27
|
+
const delegatedRouterOptions = useMemo(() => {
|
|
28
|
+
if (!routingIntegration || href === void 0) {
|
|
29
|
+
return void 0;
|
|
30
|
+
}
|
|
31
|
+
return routingIntegration.createRouterOptions(
|
|
32
|
+
navigateWithOptions,
|
|
33
|
+
navigateOptions
|
|
34
|
+
);
|
|
35
|
+
}, [routingIntegration, navigateWithOptions, href, navigateOptions]);
|
|
36
|
+
let routerLinkOptions;
|
|
37
|
+
if (navigateOptions) {
|
|
38
|
+
const { flushSync: _flushSync, ...supportedOptions } = navigateOptions;
|
|
39
|
+
routerLinkOptions = supportedOptions;
|
|
40
|
+
}
|
|
41
|
+
const navigateWithFullOptions = navigateOptions?.flushSync === true ? navigateWithOptions : void 0;
|
|
42
|
+
if (href === void 0) {
|
|
43
|
+
return { type: "none", canMatchRoute: false };
|
|
44
|
+
}
|
|
45
|
+
const canMatchRoute = !isBrowserOwnedHref(href);
|
|
46
|
+
if (isNativeNavigation(props)) {
|
|
47
|
+
return {
|
|
48
|
+
type: "native",
|
|
49
|
+
canMatchRoute,
|
|
50
|
+
ariaHref: href,
|
|
51
|
+
browserHref: isBrowserOwnedHref(href) ? href : resolvedHref
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
if (routingIntegration) {
|
|
55
|
+
return {
|
|
56
|
+
type: "router",
|
|
57
|
+
canMatchRoute,
|
|
58
|
+
ariaHref: href,
|
|
59
|
+
to: href,
|
|
60
|
+
Link: routingIntegration.Link,
|
|
61
|
+
routerOptions: delegatedRouterOptions,
|
|
62
|
+
routerLinkOptions,
|
|
63
|
+
navigateWithFullOptions
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
return {
|
|
67
|
+
type: "router",
|
|
68
|
+
canMatchRoute,
|
|
69
|
+
ariaHref: createPath(resolvedPath),
|
|
70
|
+
to: href,
|
|
71
|
+
Link: fallbackRoutingIntegration.Link,
|
|
72
|
+
routerOptions: navigateOptions,
|
|
73
|
+
routerLinkOptions,
|
|
74
|
+
navigateWithFullOptions
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
function handleRouterLinkClick(event, navigation) {
|
|
78
|
+
const navigateWithFullOptions = navigation.type === "router" ? navigation.navigateWithFullOptions : void 0;
|
|
79
|
+
if (!navigateWithFullOptions || event.defaultPrevented || event.button !== 0 || event.currentTarget.target && event.currentTarget.target !== "_self" || event.metaKey || event.altKey || event.ctrlKey || event.shiftKey) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
event.preventDefault();
|
|
83
|
+
navigateWithFullOptions();
|
|
84
|
+
}
|
|
85
|
+
function getReactAriaAnchorProps(navigation, props) {
|
|
86
|
+
switch (navigation.type) {
|
|
87
|
+
case "none":
|
|
88
|
+
return {
|
|
89
|
+
href: props.href,
|
|
90
|
+
routerOptions: props.routerOptions,
|
|
91
|
+
render: void 0
|
|
92
|
+
};
|
|
93
|
+
case "router":
|
|
94
|
+
return {
|
|
95
|
+
href: navigation.ariaHref,
|
|
96
|
+
routerOptions: navigation.routerOptions,
|
|
97
|
+
render: (domProps) => {
|
|
98
|
+
const {
|
|
99
|
+
href: _href,
|
|
100
|
+
onClick: reactAriaOnClick,
|
|
101
|
+
...routerLinkProps
|
|
102
|
+
} = domProps;
|
|
103
|
+
const onClick = navigation.navigateWithFullOptions ? (event) => {
|
|
104
|
+
reactAriaOnClick?.(event);
|
|
105
|
+
handleRouterLinkClick(event, navigation);
|
|
106
|
+
} : reactAriaOnClick;
|
|
107
|
+
return /* @__PURE__ */ jsx(
|
|
108
|
+
navigation.Link,
|
|
109
|
+
{
|
|
110
|
+
...routerLinkProps,
|
|
111
|
+
...navigation.routerLinkOptions,
|
|
112
|
+
...onClick ? { onClick } : {},
|
|
113
|
+
to: navigation.to
|
|
114
|
+
}
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
case "native":
|
|
119
|
+
return {
|
|
120
|
+
href: navigation.ariaHref,
|
|
121
|
+
routerOptions: props.routerOptions,
|
|
122
|
+
// React Aria classifies the raw href, while the final DOM anchor needs
|
|
123
|
+
// the router-resolved href so a basename is applied exactly once.
|
|
124
|
+
render: (domProps) => /* @__PURE__ */ jsx("a", { ...domProps, href: navigation.browserHref })
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export { getReactAriaAnchorProps, handleRouterLinkClick, isBrowserOwnedHref, isNativeNavigation, useAnchorNavigation };
|
|
130
|
+
//# sourceMappingURL=useNavigation.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useNavigation.esm.js","sources":["../../src/navigation/useNavigation.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 {\n useCallback,\n useMemo,\n type HTMLAttributeAnchorTarget,\n type JSX,\n type MouseEvent,\n type ReactElement,\n} from 'react';\nimport { createPath, type NavigateOptions } from 'react-router-dom';\nimport { isBrowserOwnedHref } from '../utils/linkUtils';\nimport type { BUIRoutingIntegration } from './types';\nimport {\n fallbackRoutingIntegration,\n useRoutingIntegration,\n} from './useRouting';\n\n/** @internal */\nexport type NavigationProps = {\n href?: string;\n target?: HTMLAttributeAnchorTarget;\n download?: boolean | string;\n routerOptions?: NavigateOptions;\n};\n\n/** @internal */\nexport type AnchorNavigation = { canMatchRoute: boolean } & (\n | { type: 'none' }\n | { type: 'native'; ariaHref: string; browserHref: string }\n | {\n type: 'router';\n ariaHref: string;\n to: string;\n Link: BUIRoutingIntegration['Link'];\n routerOptions?: NavigateOptions;\n routerLinkOptions: Omit<NavigateOptions, 'flushSync'> | undefined;\n navigateWithFullOptions?: () => void;\n }\n);\n\ntype PolymorphicRenderFunction = (...args: any[]) => ReactElement;\n\nexport { isBrowserOwnedHref } from '../utils/linkUtils';\n\n/** @internal */\nexport function isNativeNavigation(props: NavigationProps): boolean {\n const { href, target, download } = props;\n return Boolean(\n (href !== undefined && isBrowserOwnedHref(href)) ||\n (target && target !== '_self') ||\n (download !== undefined && download !== false),\n );\n}\n\n/**\n * A BUI component may come from a plugin that was built and released\n * independently from the application. We therefore cannot assume that the\n * component and the application's BUIProvider use the same BUI version or the\n * same installed copy of React Aria.\n *\n * These are two separate compatibility concerns:\n *\n * 1. BUIProvider version. A provider with routing support supplies the\n * application's React Router hooks and Link component. An older provider\n * does not, so the component falls back to directly imported React Router\n * APIs. This fallback can be removed when providers without routing support\n * are no longer supported.\n *\n * 2. React Aria package instance. React context is only visible to components\n * using the same React Aria copy. A plugin using another copy cannot see the\n * application's React Aria RouterProvider.\n *\n * Internal links render a React Router Link, either directly or through React\n * Aria's render prop. In both cases, React Aria's generated props, including its\n * click handler, are passed to that Link. React Router runs this supplied click\n * handler before its own navigation handler.\n *\n * - If the component can see the application's React Aria RouterProvider,\n * React Aria prevents the click's default behavior and delegates navigation\n * through that provider. React Router sees the prevented event and does not\n * navigate a second time.\n * - If the component uses a separate React Aria copy, it cannot see that\n * provider. React Aria leaves the event unhandled, so the rendered React\n * Router Link performs the navigation.\n *\n * Navigation options are provided to both paths so they behave consistently.\n * React Router Link does not support `flushSync`, so when that option is\n * enabled, BUI calls `navigate` directly if React Aria did not already handle\n * an ordinary click. Modified clicks and other browser-owned activation still\n * reach the rendered anchor unchanged.\n *\n * This dual click handling exists for compatibility with older BUI components\n * and older BUIProvider implementations. Once supported applications and\n * plugins are required to use both a routing-capable BUIProvider and BUI\n * components that render React Router links, the React Aria RouterProvider and\n * its delegated-navigation path can be removed. The rendered React Router Link\n * can then become the only client-side navigation path for anchors.\n *\n * After selecting the routing integration, this hook determines who should\n * handle the destination. React Router handles ordinary internal links. The\n * browser handles external URLs, custom URL schemes, downloads, and links with\n * a non-self target. Route matching is tracked separately: an internal tab can\n * still match the current route even when its activation is browser-owned. A\n * missing href does not navigate.\n *\n * @internal\n */\nexport function useAnchorNavigation(props: NavigationProps): AnchorNavigation {\n const { href, routerOptions: navigateOptions } = props;\n const routingIntegration = useRoutingIntegration();\n const routing = routingIntegration ?? fallbackRoutingIntegration;\n const navigate = routing.useNavigate();\n // React hooks must be called in the same order on every render. Missing hrefs,\n // external URLs, and custom URL schemes do not need route resolution, so \".\"\n // is used as a harmless placeholder when calling the router hooks.\n const routerHref =\n href !== undefined && !isBrowserOwnedHref(href) ? href : '.';\n const relativeOptions = { relative: navigateOptions?.relative };\n const resolvedPath = routing.useResolvedPath(routerHref, relativeOptions);\n const resolvedHref = routing.useHref(routerHref, relativeOptions);\n const navigateWithOptions = useCallback(() => {\n if (href !== undefined) {\n navigate(href, navigateOptions);\n }\n }, [navigate, href, navigateOptions]);\n const delegatedRouterOptions = useMemo(() => {\n if (!routingIntegration || href === undefined) {\n return undefined;\n }\n return routingIntegration.createRouterOptions(\n navigateWithOptions,\n navigateOptions,\n );\n }, [routingIntegration, navigateWithOptions, href, navigateOptions]);\n let routerLinkOptions: Omit<NavigateOptions, 'flushSync'> | undefined;\n if (navigateOptions) {\n // React Aria accepts all NavigateOptions when it delegates through its\n // RouterProvider. React Router Link accepts the same options except\n // flushSync, so omit that option from the props passed to the rendered Link.\n const { flushSync: _flushSync, ...supportedOptions } = navigateOptions;\n routerLinkOptions = supportedOptions;\n }\n const navigateWithFullOptions =\n navigateOptions?.flushSync === true ? navigateWithOptions : undefined;\n\n if (href === undefined) {\n return { type: 'none', canMatchRoute: false };\n }\n const canMatchRoute = !isBrowserOwnedHref(href);\n if (isNativeNavigation(props)) {\n return {\n type: 'native',\n canMatchRoute,\n ariaHref: href,\n browserHref: isBrowserOwnedHref(href) ? href : resolvedHref,\n };\n }\n if (routingIntegration) {\n return {\n type: 'router',\n canMatchRoute,\n ariaHref: href,\n to: href,\n Link: routingIntegration.Link,\n routerOptions: delegatedRouterOptions,\n routerLinkOptions,\n navigateWithFullOptions,\n };\n }\n return {\n type: 'router',\n canMatchRoute,\n ariaHref: createPath(resolvedPath),\n to: href,\n Link: fallbackRoutingIntegration.Link,\n routerOptions: navigateOptions,\n routerLinkOptions,\n navigateWithFullOptions,\n };\n}\n\n/**\n * Completes an eligible click with the one navigation option that React Router\n * Link cannot represent. Callers invoke this after React Aria's click handler.\n *\n * @internal\n */\nexport function handleRouterLinkClick(\n event: MouseEvent<HTMLAnchorElement>,\n navigation: AnchorNavigation,\n) {\n const navigateWithFullOptions =\n navigation.type === 'router'\n ? navigation.navigateWithFullOptions\n : undefined;\n if (\n !navigateWithFullOptions ||\n event.defaultPrevented ||\n event.button !== 0 ||\n (event.currentTarget.target && event.currentTarget.target !== '_self') ||\n event.metaKey ||\n event.altKey ||\n event.ctrlKey ||\n event.shiftKey\n ) {\n return;\n }\n\n event.preventDefault();\n navigateWithFullOptions();\n}\n\n/**\n * Converts the selected navigation behavior into props understood by React\n * Aria.\n *\n * Internal destinations are rendered as the selected React Router Link.\n * `routerOptions` are used when React Aria delegates the click through its\n * RouterProvider. The equivalent Link props are also applied to the rendered\n * React Router Link for components that cannot see that provider.\n *\n * Destinations handled by the browser are rendered as plain anchors. Their\n * final href includes any React Router basename exactly once.\n *\n * @internal\n */\nexport function getReactAriaAnchorProps(\n navigation: AnchorNavigation,\n props: {\n href?: string;\n routerOptions?: NavigateOptions;\n },\n): {\n href?: string;\n routerOptions?: NavigateOptions;\n render?: PolymorphicRenderFunction;\n} {\n switch (navigation.type) {\n case 'none':\n return {\n href: props.href,\n routerOptions: props.routerOptions,\n render: undefined,\n };\n case 'router':\n return {\n href: navigation.ariaHref,\n routerOptions: navigation.routerOptions,\n render: (domProps: JSX.IntrinsicElements['a']) => {\n const {\n href: _href,\n onClick: reactAriaOnClick,\n ...routerLinkProps\n } = domProps;\n const onClick = navigation.navigateWithFullOptions\n ? (event: MouseEvent<HTMLAnchorElement>) => {\n reactAriaOnClick?.(event);\n handleRouterLinkClick(event, navigation);\n }\n : reactAriaOnClick;\n return (\n <navigation.Link\n {...routerLinkProps}\n {...navigation.routerLinkOptions}\n {...(onClick ? { onClick } : {})}\n to={navigation.to}\n />\n );\n },\n };\n case 'native':\n return {\n href: navigation.ariaHref,\n routerOptions: props.routerOptions,\n // React Aria classifies the raw href, while the final DOM anchor needs\n // the router-resolved href so a basename is applied exactly once.\n render: (domProps: JSX.IntrinsicElements['a']) => (\n <a {...domProps} href={navigation.browserHref} />\n ),\n };\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { useVersionedContext } from '@backstage/version-bridge';
|
|
2
|
+
import { useResolvedPath, useNavigate, useLocation, useInRouterContext, useHref, Link } from 'react-router-dom';
|
|
3
|
+
|
|
4
|
+
function useRoutingIntegration(options) {
|
|
5
|
+
const routing = useVersionedContext("bui")?.atVersion(2)?.routing;
|
|
6
|
+
if (routing) {
|
|
7
|
+
return routing;
|
|
8
|
+
}
|
|
9
|
+
if (options?.fallback) {
|
|
10
|
+
return fallbackRoutingIntegration;
|
|
11
|
+
}
|
|
12
|
+
return void 0;
|
|
13
|
+
}
|
|
14
|
+
const fallbackRoutingIntegration = {
|
|
15
|
+
Link,
|
|
16
|
+
useHref,
|
|
17
|
+
useInRouterContext,
|
|
18
|
+
useLocation,
|
|
19
|
+
useNavigate,
|
|
20
|
+
useResolvedPath,
|
|
21
|
+
createRouterOptions(_action, options) {
|
|
22
|
+
return { ...options };
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export { fallbackRoutingIntegration, useRoutingIntegration };
|
|
27
|
+
//# sourceMappingURL=useRouting.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useRouting.esm.js","sources":["../../src/navigation/useRouting.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 { useVersionedContext } from '@backstage/version-bridge';\nimport {\n Link,\n useHref,\n useInRouterContext,\n useLocation,\n useNavigate,\n useResolvedPath,\n} from 'react-router-dom';\nimport type { BUIContextVersions } from '../provider/BUIContext';\nimport type { BUIRoutingIntegration } from './types';\n\n/** @internal */\nexport function useRoutingIntegration(): BUIRoutingIntegration | undefined;\n/** @internal */\nexport function useRoutingIntegration(options: {\n fallback: true;\n}): BUIRoutingIntegration;\n/** @internal */\nexport function useRoutingIntegration(options?: {\n fallback?: true;\n}): BUIRoutingIntegration | undefined {\n const routing =\n useVersionedContext<BUIContextVersions>('bui')?.atVersion(2)?.routing;\n if (routing) {\n return routing;\n }\n if (options?.fallback) {\n return fallbackRoutingIntegration;\n }\n return undefined;\n}\n\n/** @internal */\nexport const fallbackRoutingIntegration: BUIRoutingIntegration = {\n Link,\n useHref,\n useInRouterContext,\n useLocation,\n useNavigate,\n useResolvedPath,\n createRouterOptions(_action, options) {\n return { ...options };\n },\n};\n"],"names":[],"mappings":";;;AAmCO,SAAS,sBAAsB,OAAA,EAEA;AACpC,EAAA,MAAM,UACJ,mBAAA,CAAwC,KAAK,CAAA,EAAG,SAAA,CAAU,CAAC,CAAA,EAAG,OAAA;AAChE,EAAA,IAAI,OAAA,EAAS;AACX,IAAA,OAAO,OAAA;AAAA,EACT;AACA,EAAA,IAAI,SAAS,QAAA,EAAU;AACrB,IAAA,OAAO,0BAAA;AAAA,EACT;AACA,EAAA,OAAO,MAAA;AACT;AAGO,MAAM,0BAAA,GAAoD;AAAA,EAC/D,IAAA;AAAA,EACA,OAAA;AAAA,EACA,kBAAA;AAAA,EACA,WAAA;AAAA,EACA,WAAA;AAAA,EACA,eAAA;AAAA,EACA,mBAAA,CAAoB,SAAS,OAAA,EAAS;AACpC,IAAA,OAAO,EAAE,GAAG,OAAA,EAAQ;AAAA,EACtB;AACF;;;;"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BUIContext.esm.js","sources":["../../src/provider/BUIContext.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 { createVersionedContext } from '@backstage/version-bridge';\nimport type { UseAnalyticsFn } from '../analytics/types';\nimport type { BUIRoutingIntegration } from '../navigation/types';\n\n/** @internal */\nexport type BUIContextValueV1 = {\n useAnalytics?: UseAnalyticsFn;\n};\n\n/** @internal */\nexport type BUIContextValueV2 = {\n useAnalytics?: UseAnalyticsFn;\n routing: BUIRoutingIntegration;\n};\n\n/** @internal */\nexport type BUIContextVersions = {\n 1: BUIContextValueV1;\n 2: BUIContextValueV2;\n};\n\n/** @internal */\nexport const BUIContext = createVersionedContext<BUIContextVersions>('bui');\n"],"names":[],"mappings":";;AAsCO,MAAM,UAAA,GAAa,uBAA2C,KAAK;;;;"}
|
|
@@ -1,28 +1,20 @@
|
|
|
1
1
|
import { jsx } from 'react/jsx-runtime';
|
|
2
2
|
import { useMemo } from 'react';
|
|
3
|
-
import { RouterProvider } from 'react-aria-components';
|
|
4
|
-
import { useInRouterContext, useNavigate } from 'react-router-dom';
|
|
5
3
|
import { createVersionedValueMap } from '@backstage/version-bridge';
|
|
6
|
-
import { BUIContext } from '
|
|
7
|
-
import {
|
|
4
|
+
import { BUIContext } from './BUIContext.esm.js';
|
|
5
|
+
import { buiRoutingIntegration, BUIRoutingProvider } from '../navigation/BUIRoutingProvider.esm.js';
|
|
8
6
|
|
|
9
7
|
function BUIProvider(props) {
|
|
10
8
|
const { useAnalytics, children } = props;
|
|
11
9
|
const value = useMemo(
|
|
12
10
|
() => createVersionedValueMap({
|
|
13
|
-
1: { useAnalytics }
|
|
11
|
+
1: { useAnalytics },
|
|
12
|
+
2: { useAnalytics, routing: buiRoutingIntegration }
|
|
14
13
|
}),
|
|
15
14
|
[useAnalytics]
|
|
16
15
|
);
|
|
17
16
|
const content = /* @__PURE__ */ jsx(BUIContext.Provider, { value, children });
|
|
18
|
-
|
|
19
|
-
return /* @__PURE__ */ jsx(RoutedContent, { children: content });
|
|
20
|
-
}
|
|
21
|
-
return content;
|
|
22
|
-
}
|
|
23
|
-
function RoutedContent({ children }) {
|
|
24
|
-
const navigate = useNavigate();
|
|
25
|
-
return /* @__PURE__ */ jsx(RouterProvider, { navigate, useHref: useResolvedHref, children });
|
|
17
|
+
return /* @__PURE__ */ jsx(BUIRoutingProvider, { children: content });
|
|
26
18
|
}
|
|
27
19
|
|
|
28
20
|
export { BUIProvider };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BUIProvider.esm.js","sources":["../../src/provider/BUIProvider.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 { useMemo, type ReactNode } from 'react';\nimport {
|
|
1
|
+
{"version":3,"file":"BUIProvider.esm.js","sources":["../../src/provider/BUIProvider.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 { useMemo, type ReactNode } from 'react';\nimport { createVersionedValueMap } from '@backstage/version-bridge';\nimport { BUIContext } from './BUIContext';\nimport type { UseAnalyticsFn } from '../analytics/types';\nimport {\n BUIRoutingProvider,\n buiRoutingIntegration,\n} from '../navigation/BUIRoutingProvider';\n\n/** @public */\nexport type BUIProviderProps = {\n useAnalytics?: UseAnalyticsFn;\n children: ReactNode;\n};\n\n/**\n * Provides integration capabilities to all descendant BUI components.\n *\n * When rendered inside the Backstage app router, BUI components use\n * client-side navigation for internal links. Relative destinations resolve\n * from the route of the component that renders the link, and the router\n * basename applies once.\n *\n * External and scheme links, downloads, and links with non-self targets use the\n * browser's native navigation. BUI components rendered outside React Router use\n * native links without throwing.\n *\n * @example\n * ```tsx\n * import { BUIProvider } from '@backstage/ui';\n * import { useAnalytics as useBackstageAnalytics } from '@backstage/core-plugin-api';\n *\n * function App() {\n * return (\n * <BUIProvider useAnalytics={useBackstageAnalytics}>\n * <AppContent />\n * </BUIProvider>\n * );\n * }\n * ```\n *\n * @public\n */\nexport function BUIProvider(props: BUIProviderProps) {\n const { useAnalytics, children } = props;\n const value = useMemo(\n () =>\n createVersionedValueMap({\n 1: { useAnalytics },\n 2: { useAnalytics, routing: buiRoutingIntegration },\n }),\n [useAnalytics],\n );\n\n const content = (\n <BUIContext.Provider value={value}>{children}</BUIContext.Provider>\n );\n return <BUIRoutingProvider>{content}</BUIRoutingProvider>;\n}\n"],"names":[],"mappings":";;;;;;AA2DO,SAAS,YAAY,KAAA,EAAyB;AACnD,EAAA,MAAM,EAAE,YAAA,EAAc,QAAA,EAAS,GAAI,KAAA;AACnC,EAAA,MAAM,KAAA,GAAQ,OAAA;AAAA,IACZ,MACE,uBAAA,CAAwB;AAAA,MACtB,CAAA,EAAG,EAAE,YAAA,EAAa;AAAA,MAClB,CAAA,EAAG,EAAE,YAAA,EAAc,OAAA,EAAS,qBAAA;AAAsB,KACnD,CAAA;AAAA,IACH,CAAC,YAAY;AAAA,GACf;AAEA,EAAA,MAAM,0BACJ,GAAA,CAAC,UAAA,CAAW,QAAA,EAAX,EAAoB,OAAe,QAAA,EAAS,CAAA;AAE/C,EAAA,uBAAO,GAAA,CAAC,sBAAoB,QAAA,EAAA,OAAA,EAAQ,CAAA;AACtC;;;;"}
|
|
@@ -1,19 +1,13 @@
|
|
|
1
|
+
const ABSOLUTE_DESTINATION = /^(?:[a-z][a-z0-9+.-]*:|\/\/)/i;
|
|
2
|
+
function isBrowserOwnedHref(href) {
|
|
3
|
+
return ABSOLUTE_DESTINATION.test(href);
|
|
4
|
+
}
|
|
1
5
|
function isExternalLink(href) {
|
|
2
|
-
|
|
3
|
-
if (href.startsWith("http://") || href.startsWith("https://")) {
|
|
4
|
-
return true;
|
|
5
|
-
}
|
|
6
|
-
if (href.startsWith("//")) {
|
|
7
|
-
return true;
|
|
8
|
-
}
|
|
9
|
-
if (href.startsWith("mailto:") || href.startsWith("tel:")) {
|
|
10
|
-
return true;
|
|
11
|
-
}
|
|
12
|
-
return false;
|
|
6
|
+
return href ? isBrowserOwnedHref(href) : false;
|
|
13
7
|
}
|
|
14
8
|
function isInternalLink(href) {
|
|
15
9
|
return !!href && !isExternalLink(href);
|
|
16
10
|
}
|
|
17
11
|
|
|
18
|
-
export { isExternalLink, isInternalLink };
|
|
12
|
+
export { isBrowserOwnedHref, isExternalLink, isInternalLink };
|
|
19
13
|
//# sourceMappingURL=linkUtils.esm.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"linkUtils.esm.js","sources":["../../src/utils/linkUtils.ts"],"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\n/**\n * Determines if a link is external.\n * @param href - The href of the link.\n * @returns True if the link is external, false otherwise.\n * @internal\n */\nexport function isExternalLink(href?: string): boolean {\n
|
|
1
|
+
{"version":3,"file":"linkUtils.esm.js","sources":["../../src/utils/linkUtils.ts"],"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\nconst ABSOLUTE_DESTINATION = /^(?:[a-z][a-z0-9+.-]*:|\\/\\/)/i;\n\n/** @internal */\nexport function isBrowserOwnedHref(href: string): boolean {\n return ABSOLUTE_DESTINATION.test(href);\n}\n\n/**\n * Determines if a link is external.\n * @param href - The href of the link.\n * @returns True if the link is external, false otherwise.\n * @internal\n */\nexport function isExternalLink(href?: string): boolean {\n return href ? isBrowserOwnedHref(href) : false;\n}\n\n/**\n * Checks if an href is an internal link (not external and not empty).\n *\n * @internal\n */\nexport function isInternalLink(href: string | undefined): href is string {\n return !!href && !isExternalLink(href);\n}\n"],"names":[],"mappings":"AAgBA,MAAM,oBAAA,GAAuB,+BAAA;AAGtB,SAAS,mBAAmB,IAAA,EAAuB;AACxD,EAAA,OAAO,oBAAA,CAAqB,KAAK,IAAI,CAAA;AACvC;AAQO,SAAS,eAAe,IAAA,EAAwB;AACrD,EAAA,OAAO,IAAA,GAAO,kBAAA,CAAmB,IAAI,CAAA,GAAI,KAAA;AAC3C;AAOO,SAAS,eAAe,IAAA,EAA0C;AACvE,EAAA,OAAO,CAAC,CAAC,IAAA,IAAQ,CAAC,eAAe,IAAI,CAAA;AACvC;;;;"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@backstage/ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0-next.1",
|
|
4
4
|
"backstage": {
|
|
5
5
|
"role": "web-library"
|
|
6
6
|
},
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"test": "backstage-cli package test"
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
|
-
"@backstage/version-bridge": "
|
|
53
|
+
"@backstage/version-bridge": "1.0.12",
|
|
54
54
|
"@braintree/sanitize-url": "^7.1.2",
|
|
55
55
|
"@internationalized/date": "^3.12.0",
|
|
56
56
|
"@remixicon/react": ">=4.6.0 <4.9.0",
|
|
@@ -63,9 +63,10 @@
|
|
|
63
63
|
"use-sync-external-store": "^1.4.0"
|
|
64
64
|
},
|
|
65
65
|
"devDependencies": {
|
|
66
|
-
"@backstage/cli": "
|
|
67
|
-
"@backstage/core-components": "
|
|
66
|
+
"@backstage/cli": "0.36.6-next.0",
|
|
67
|
+
"@backstage/core-components": "0.18.14-next.1",
|
|
68
68
|
"@storybook/react-vite": "^10.4.0",
|
|
69
|
+
"@testing-library/dom": "^10.0.0",
|
|
69
70
|
"@testing-library/jest-dom": "^6.0.0",
|
|
70
71
|
"@testing-library/react": "^16.0.0",
|
|
71
72
|
"@types/react": "^18.0.0",
|