@backstage/plugin-techdocs 1.17.1-next.2 → 1.17.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/dist/alpha/components/TechDocsIndexPageContent.esm.js +36 -0
  3. package/dist/alpha/components/TechDocsIndexPageContent.esm.js.map +1 -0
  4. package/dist/alpha/components/TechDocsReaderContent.esm.js +35 -0
  5. package/dist/alpha/components/TechDocsReaderContent.esm.js.map +1 -0
  6. package/dist/alpha/components/TechDocsReaderEntityCard.esm.js +59 -0
  7. package/dist/alpha/components/TechDocsReaderEntityCard.esm.js.map +1 -0
  8. package/dist/alpha/components/TechDocsReaderEntityCard.module.css.esm.js +8 -0
  9. package/dist/alpha/components/TechDocsReaderEntityCard.module.css.esm.js.map +1 -0
  10. package/dist/alpha/components/TechDocsReaderHeader.esm.js +49 -0
  11. package/dist/alpha/components/TechDocsReaderHeader.esm.js.map +1 -0
  12. package/dist/alpha/components/TechDocsReaderLayout.esm.js +21 -0
  13. package/dist/alpha/components/TechDocsReaderLayout.esm.js.map +1 -0
  14. package/dist/alpha/components/TechDocsReaderSearch.esm.js +82 -0
  15. package/dist/alpha/components/TechDocsReaderSearch.esm.js.map +1 -0
  16. package/dist/alpha.d.ts +13 -0
  17. package/dist/alpha.esm.js +15 -20
  18. package/dist/alpha.esm.js.map +1 -1
  19. package/dist/hooks/useTechDocsReaderContentData.esm.js +63 -0
  20. package/dist/hooks/useTechDocsReaderContentData.esm.js.map +1 -0
  21. package/dist/hooks/useTechDocsReaderHeaderData.esm.js +70 -0
  22. package/dist/hooks/useTechDocsReaderHeaderData.esm.js.map +1 -0
  23. package/dist/hooks/useTechDocsSearch.esm.js +46 -0
  24. package/dist/hooks/useTechDocsSearch.esm.js.map +1 -0
  25. package/dist/node_modules_dist/style-inject/dist/style-inject.es.esm.js +29 -0
  26. package/dist/node_modules_dist/style-inject/dist/style-inject.es.esm.js.map +1 -0
  27. package/dist/{package.json.esm.js → plugins/techdocs/package.json.esm.js} +3 -1
  28. package/dist/{package.json.esm.js.map → plugins/techdocs/package.json.esm.js.map} +1 -1
  29. package/dist/reader/components/TechDocsReaderPageContent/TechDocsReaderPageContent.esm.js +18 -40
  30. package/dist/reader/components/TechDocsReaderPageContent/TechDocsReaderPageContent.esm.js.map +1 -1
  31. package/dist/reader/components/TechDocsReaderPageHeader/TechDocsReaderPageHeader.esm.js +17 -43
  32. package/dist/reader/components/TechDocsReaderPageHeader/TechDocsReaderPageHeader.esm.js.map +1 -1
  33. package/dist/search/components/TechDocsSearch.esm.js +5 -30
  34. package/dist/search/components/TechDocsSearch.esm.js.map +1 -1
  35. package/package.json +25 -23
@@ -0,0 +1,63 @@
1
+ import { useEffect, useCallback } from 'react';
2
+ import { useLocation } from 'react-router-dom';
3
+ import { useTechDocsReaderPage, useShadowDomStylesLoading, useShadowRootElements } from '@backstage/plugin-techdocs-react';
4
+ import { useApp } from '@backstage/core-plugin-api';
5
+ import { useTechDocsReaderDom } from '../reader/components/TechDocsReaderPageContent/dom.esm.js';
6
+ import { useTechDocsReader } from '../reader/components/TechDocsReaderProvider.esm.js';
7
+
8
+ function useTechDocsReaderContentData(options) {
9
+ const { defaultPath, onReady } = options;
10
+ const {
11
+ entityMetadata: { value: entityMetadata, loading: entityMetadataLoading },
12
+ entityRef,
13
+ setShadowRoot
14
+ } = useTechDocsReaderPage();
15
+ const { state } = useTechDocsReader();
16
+ const dom = useTechDocsReaderDom(entityRef, defaultPath);
17
+ const location = useLocation();
18
+ const path = location.pathname;
19
+ const hash = location.hash;
20
+ const isStyleLoading = useShadowDomStylesLoading(dom);
21
+ const [hashElement] = useShadowRootElements([`[id="${hash.slice(1)}"]`]);
22
+ const app = useApp();
23
+ const { NotFoundErrorPage } = app.getComponents();
24
+ useEffect(() => {
25
+ if (isStyleLoading) return;
26
+ if (hash) {
27
+ if (hashElement) {
28
+ hashElement.scrollIntoView();
29
+ const link = hashElement.querySelector("a.headerlink");
30
+ if (link) {
31
+ link.focus();
32
+ }
33
+ }
34
+ } else {
35
+ document?.querySelector("header")?.scrollIntoView();
36
+ }
37
+ }, [path, hash, hashElement, isStyleLoading]);
38
+ const handleAppend = useCallback(
39
+ (newShadowRoot) => {
40
+ setShadowRoot(newShadowRoot);
41
+ if (onReady instanceof Function) {
42
+ onReady();
43
+ }
44
+ },
45
+ [setShadowRoot, onReady]
46
+ );
47
+ const isNotFound = entityMetadataLoading === false && !entityMetadata;
48
+ const isDomReady = !!dom;
49
+ const showProgress = state === "CHECKING" || isStyleLoading;
50
+ return {
51
+ entityRef,
52
+ entityMetadata,
53
+ dom,
54
+ handleAppend,
55
+ isNotFound,
56
+ isDomReady,
57
+ showProgress,
58
+ NotFoundErrorPage
59
+ };
60
+ }
61
+
62
+ export { useTechDocsReaderContentData };
63
+ //# sourceMappingURL=useTechDocsReaderContentData.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useTechDocsReaderContentData.esm.js","sources":["../../src/hooks/useTechDocsReaderContentData.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 { useCallback, useEffect } from 'react';\nimport { useLocation } from 'react-router-dom';\nimport {\n useShadowDomStylesLoading,\n useShadowRootElements,\n useTechDocsReaderPage,\n} from '@backstage/plugin-techdocs-react';\nimport { useApp } from '@backstage/core-plugin-api';\nimport { useTechDocsReaderDom } from '../reader/components/TechDocsReaderPageContent/dom';\nimport { useTechDocsReader } from '../reader/components/TechDocsReaderProvider';\n\n/**\n * Shared hook for TechDocs reader content data.\n * Encapsulates DOM setup, hash scrolling, shadow root handling,\n * 404 detection, and loading state.\n */\nexport function useTechDocsReaderContentData(options: {\n defaultPath?: string;\n onReady?: () => void;\n}) {\n const { defaultPath, onReady } = options;\n\n const {\n entityMetadata: { value: entityMetadata, loading: entityMetadataLoading },\n entityRef,\n setShadowRoot,\n } = useTechDocsReaderPage();\n const { state } = useTechDocsReader();\n const dom = useTechDocsReaderDom(entityRef, defaultPath);\n const location = useLocation();\n const path = location.pathname;\n const hash = location.hash;\n const isStyleLoading = useShadowDomStylesLoading(dom);\n const [hashElement] = useShadowRootElements([`[id=\"${hash.slice(1)}\"]`]);\n const app = useApp();\n const { NotFoundErrorPage } = app.getComponents();\n\n useEffect(() => {\n if (isStyleLoading) return;\n\n if (hash) {\n if (hashElement) {\n hashElement.scrollIntoView();\n const link = hashElement.querySelector<HTMLElement>('a.headerlink');\n if (link) {\n link.focus();\n }\n }\n } else {\n document?.querySelector('header')?.scrollIntoView();\n }\n }, [path, hash, hashElement, isStyleLoading]);\n\n const handleAppend = useCallback(\n (newShadowRoot: ShadowRoot) => {\n setShadowRoot(newShadowRoot);\n if (onReady instanceof Function) {\n onReady();\n }\n },\n [setShadowRoot, onReady],\n );\n\n const isNotFound = entityMetadataLoading === false && !entityMetadata;\n const isDomReady = !!dom;\n const showProgress = state === 'CHECKING' || isStyleLoading;\n\n return {\n entityRef,\n entityMetadata,\n dom,\n handleAppend,\n isNotFound,\n isDomReady,\n showProgress,\n NotFoundErrorPage,\n };\n}\n"],"names":[],"mappings":";;;;;;;AAgCO,SAAS,6BAA6B,OAAA,EAG1C;AACD,EAAA,MAAM,EAAE,WAAA,EAAa,OAAA,EAAQ,GAAI,OAAA;AAEjC,EAAA,MAAM;AAAA,IACJ,cAAA,EAAgB,EAAE,KAAA,EAAO,cAAA,EAAgB,SAAS,qBAAA,EAAsB;AAAA,IACxE,SAAA;AAAA,IACA;AAAA,MACE,qBAAA,EAAsB;AAC1B,EAAA,MAAM,EAAE,KAAA,EAAM,GAAI,iBAAA,EAAkB;AACpC,EAAA,MAAM,GAAA,GAAM,oBAAA,CAAqB,SAAA,EAAW,WAAW,CAAA;AACvD,EAAA,MAAM,WAAW,WAAA,EAAY;AAC7B,EAAA,MAAM,OAAO,QAAA,CAAS,QAAA;AACtB,EAAA,MAAM,OAAO,QAAA,CAAS,IAAA;AACtB,EAAA,MAAM,cAAA,GAAiB,0BAA0B,GAAG,CAAA;AACpD,EAAA,MAAM,CAAC,WAAW,CAAA,GAAI,qBAAA,CAAsB,CAAC,CAAA,KAAA,EAAQ,IAAA,CAAK,KAAA,CAAM,CAAC,CAAC,CAAA,EAAA,CAAI,CAAC,CAAA;AACvE,EAAA,MAAM,MAAM,MAAA,EAAO;AACnB,EAAA,MAAM,EAAE,iBAAA,EAAkB,GAAI,GAAA,CAAI,aAAA,EAAc;AAEhD,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,cAAA,EAAgB;AAEpB,IAAA,IAAI,IAAA,EAAM;AACR,MAAA,IAAI,WAAA,EAAa;AACf,QAAA,WAAA,CAAY,cAAA,EAAe;AAC3B,QAAA,MAAM,IAAA,GAAO,WAAA,CAAY,aAAA,CAA2B,cAAc,CAAA;AAClE,QAAA,IAAI,IAAA,EAAM;AACR,UAAA,IAAA,CAAK,KAAA,EAAM;AAAA,QACb;AAAA,MACF;AAAA,IACF,CAAA,MAAO;AACL,MAAA,QAAA,EAAU,aAAA,CAAc,QAAQ,CAAA,EAAG,cAAA,EAAe;AAAA,IACpD;AAAA,EACF,GAAG,CAAC,IAAA,EAAM,IAAA,EAAM,WAAA,EAAa,cAAc,CAAC,CAAA;AAE5C,EAAA,MAAM,YAAA,GAAe,WAAA;AAAA,IACnB,CAAC,aAAA,KAA8B;AAC7B,MAAA,aAAA,CAAc,aAAa,CAAA;AAC3B,MAAA,IAAI,mBAAmB,QAAA,EAAU;AAC/B,QAAA,OAAA,EAAQ;AAAA,MACV;AAAA,IACF,CAAA;AAAA,IACA,CAAC,eAAe,OAAO;AAAA,GACzB;AAEA,EAAA,MAAM,UAAA,GAAa,qBAAA,KAA0B,KAAA,IAAS,CAAC,cAAA;AACvD,EAAA,MAAM,UAAA,GAAa,CAAC,CAAC,GAAA;AACrB,EAAA,MAAM,YAAA,GAAe,UAAU,UAAA,IAAc,cAAA;AAE7C,EAAA,OAAO;AAAA,IACL,SAAA;AAAA,IACA,cAAA;AAAA,IACA,GAAA;AAAA,IACA,YAAA;AAAA,IACA,UAAA;AAAA,IACA,UAAA;AAAA,IACA,YAAA;AAAA,IACA;AAAA,GACF;AACF;;;;"}
@@ -0,0 +1,70 @@
1
+ import { useEffect } from 'react';
2
+ import { useParams } from 'react-router-dom';
3
+ import { stringifyEntityRef } from '@backstage/catalog-model';
4
+ import { useApi, configApiRef } from '@backstage/core-plugin-api';
5
+ import { useTechDocsAddons, useTechDocsReaderPage } from '@backstage/plugin-techdocs-react';
6
+ import { entityPresentationApiRef } from '@backstage/plugin-catalog-react';
7
+ import capitalize from 'lodash/capitalize';
8
+
9
+ function useTechDocsReaderHeaderData() {
10
+ const addons = useTechDocsAddons();
11
+ const configApi = useApi(configApiRef);
12
+ const entityPresentationApi = useApi(entityPresentationApiRef);
13
+ const { "*": path = "" } = useParams();
14
+ const {
15
+ title,
16
+ setTitle,
17
+ subtitle,
18
+ setSubtitle,
19
+ entityRef,
20
+ metadata: { value: metadata, loading: metadataLoading },
21
+ entityMetadata: { value: entityMetadata, loading: entityMetadataLoading }
22
+ } = useTechDocsReaderPage();
23
+ useEffect(() => {
24
+ if (!metadata) return;
25
+ setTitle(metadata.site_name);
26
+ setSubtitle(() => {
27
+ let { site_description } = metadata;
28
+ if (!site_description || site_description === "None") {
29
+ site_description = "";
30
+ }
31
+ return site_description;
32
+ });
33
+ }, [metadata, setTitle, setSubtitle]);
34
+ const appTitle = configApi.getOptionalString("app.title") || "Backstage";
35
+ const locationMetadata = entityMetadata?.locationMetadata;
36
+ const showSourceLink = !!locationMetadata && locationMetadata.type !== "dir" && locationMetadata.type !== "file";
37
+ const noEntMetadata = !entityMetadataLoading && entityMetadata === void 0;
38
+ const noTdMetadata = !metadataLoading && metadata === void 0;
39
+ const hidden = noEntMetadata || noTdMetadata;
40
+ const removeTrailingSlash = (str) => str.replace(/\/$/, "");
41
+ const normalizeAndSpace = (str) => str.replace(/[-_]/g, " ").split(" ").map(capitalize).join(" ");
42
+ let tabTitle = appTitle;
43
+ if (!hidden) {
44
+ const stringEntityRef = stringifyEntityRef(entityRef);
45
+ const entityDisplayName = entityPresentationApi.forEntity(stringEntityRef).snapshot.primaryTitle;
46
+ let techdocsTabTitleItems = [];
47
+ if (path !== "")
48
+ techdocsTabTitleItems = removeTrailingSlash(path).split("/").map(normalizeAndSpace);
49
+ const tabTitleItems = [
50
+ entityDisplayName,
51
+ ...techdocsTabTitleItems,
52
+ appTitle
53
+ ];
54
+ tabTitle = tabTitleItems.join(" | ");
55
+ }
56
+ return {
57
+ title,
58
+ subtitle,
59
+ entityRef,
60
+ entityMetadata,
61
+ tabTitle,
62
+ hidden,
63
+ showSourceLink,
64
+ sourceLink: locationMetadata?.target,
65
+ addons
66
+ };
67
+ }
68
+
69
+ export { useTechDocsReaderHeaderData };
70
+ //# sourceMappingURL=useTechDocsReaderHeaderData.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useTechDocsReaderHeaderData.esm.js","sources":["../../src/hooks/useTechDocsReaderHeaderData.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 { useEffect } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { stringifyEntityRef } from '@backstage/catalog-model';\nimport { configApiRef, useApi } from '@backstage/core-plugin-api';\nimport {\n useTechDocsAddons,\n useTechDocsReaderPage,\n} from '@backstage/plugin-techdocs-react';\nimport { entityPresentationApiRef } from '@backstage/plugin-catalog-react';\nimport capitalize from 'lodash/capitalize';\n\n/**\n * Shared hook for TechDocs reader header data.\n * Encapsulates title/subtitle sync, tab title computation,\n * 404 detection, and source link visibility.\n */\nexport function useTechDocsReaderHeaderData() {\n const addons = useTechDocsAddons();\n const configApi = useApi(configApiRef);\n const entityPresentationApi = useApi(entityPresentationApiRef);\n const { '*': path = '' } = useParams();\n\n const {\n title,\n setTitle,\n subtitle,\n setSubtitle,\n entityRef,\n metadata: { value: metadata, loading: metadataLoading },\n entityMetadata: { value: entityMetadata, loading: entityMetadataLoading },\n } = useTechDocsReaderPage();\n\n useEffect(() => {\n if (!metadata) return;\n setTitle(metadata.site_name);\n setSubtitle(() => {\n let { site_description } = metadata;\n if (!site_description || site_description === 'None') {\n site_description = '';\n }\n return site_description;\n });\n }, [metadata, setTitle, setSubtitle]);\n\n const appTitle = configApi.getOptionalString('app.title') || 'Backstage';\n const locationMetadata = entityMetadata?.locationMetadata;\n const showSourceLink =\n !!locationMetadata &&\n locationMetadata.type !== 'dir' &&\n locationMetadata.type !== 'file';\n\n const noEntMetadata = !entityMetadataLoading && entityMetadata === undefined;\n const noTdMetadata = !metadataLoading && metadata === undefined;\n const hidden = noEntMetadata || noTdMetadata;\n\n const removeTrailingSlash = (str: string) => str.replace(/\\/$/, '');\n const normalizeAndSpace = (str: string) =>\n str.replace(/[-_]/g, ' ').split(' ').map(capitalize).join(' ');\n\n let tabTitle = appTitle;\n if (!hidden) {\n const stringEntityRef = stringifyEntityRef(entityRef);\n const entityDisplayName =\n entityPresentationApi.forEntity(stringEntityRef).snapshot.primaryTitle;\n\n let techdocsTabTitleItems: string[] = [];\n if (path !== '')\n techdocsTabTitleItems = removeTrailingSlash(path)\n .split('/')\n .map(normalizeAndSpace);\n\n const tabTitleItems = [\n entityDisplayName,\n ...techdocsTabTitleItems,\n appTitle,\n ];\n tabTitle = tabTitleItems.join(' | ');\n }\n\n return {\n title,\n subtitle,\n entityRef,\n entityMetadata,\n tabTitle,\n hidden,\n showSourceLink,\n sourceLink: locationMetadata?.target,\n addons,\n };\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ import { useState, useRef, useEffect } from 'react';
2
+ import { useSearch } from '@backstage/plugin-search-react';
3
+
4
+ function useTechDocsSearch(entityId) {
5
+ const {
6
+ setFilters,
7
+ setTerm,
8
+ term,
9
+ result: { loading, value: searchVal }
10
+ } = useSearch();
11
+ const [results, setResults] = useState([]);
12
+ const [deferredLoading, setDeferredLoading] = useState(false);
13
+ const loadingTimer = useRef();
14
+ useEffect(() => {
15
+ if (searchVal) {
16
+ setResults(searchVal.results.slice(0, 10));
17
+ }
18
+ }, [loading, searchVal]);
19
+ useEffect(() => {
20
+ clearTimeout(loadingTimer.current);
21
+ setDeferredLoading(false);
22
+ if (loading) {
23
+ loadingTimer.current = setTimeout(() => setDeferredLoading(true), 200);
24
+ }
25
+ return () => clearTimeout(loadingTimer.current);
26
+ }, [term, loading]);
27
+ const { kind, name, namespace } = entityId;
28
+ useEffect(() => {
29
+ setFilters((prevFilters) => ({
30
+ ...prevFilters,
31
+ kind,
32
+ namespace,
33
+ name
34
+ }));
35
+ }, [kind, namespace, name, setFilters]);
36
+ return {
37
+ results,
38
+ term,
39
+ setTerm,
40
+ loading,
41
+ deferredLoading
42
+ };
43
+ }
44
+
45
+ export { useTechDocsSearch };
46
+ //# sourceMappingURL=useTechDocsSearch.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useTechDocsSearch.esm.js","sources":["../../src/hooks/useTechDocsSearch.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 { useEffect, useRef, useState } from 'react';\nimport { useSearch } from '@backstage/plugin-search-react';\nimport { CompoundEntityRef } from '@backstage/catalog-model';\nimport { ResultHighlight } from '@backstage/plugin-search-common';\n\nexport type TechDocsDoc = {\n namespace: string;\n kind: string;\n name: string;\n path: string;\n location: string;\n title: string;\n text: string;\n};\n\nexport type TechDocsSearchResult = {\n type: string;\n document: TechDocsDoc;\n highlight?: ResultHighlight;\n};\n\n/**\n * Shared hook for TechDocs search logic.\n * Encapsulates entity filter sync, results slicing,\n * and deferred loading state.\n */\nexport function useTechDocsSearch(entityId: CompoundEntityRef) {\n const {\n setFilters,\n setTerm,\n term,\n result: { loading, value: searchVal },\n } = useSearch();\n const [results, setResults] = useState<TechDocsSearchResult[]>([]);\n const [deferredLoading, setDeferredLoading] = useState(false);\n const loadingTimer = useRef<ReturnType<typeof setTimeout>>();\n\n useEffect(() => {\n if (searchVal) {\n setResults(searchVal.results.slice(0, 10) as TechDocsSearchResult[]);\n }\n }, [loading, searchVal]);\n\n useEffect(() => {\n clearTimeout(loadingTimer.current);\n setDeferredLoading(false);\n if (loading) {\n loadingTimer.current = setTimeout(() => setDeferredLoading(true), 200);\n }\n return () => clearTimeout(loadingTimer.current);\n }, [term, loading]);\n\n const { kind, name, namespace } = entityId;\n useEffect(() => {\n setFilters(prevFilters => ({\n ...prevFilters,\n kind,\n namespace,\n name,\n }));\n }, [kind, namespace, name, setFilters]);\n\n return {\n results,\n term,\n setTerm,\n loading,\n deferredLoading,\n };\n}\n"],"names":[],"mappings":";;;AA0CO,SAAS,kBAAkB,QAAA,EAA6B;AAC7D,EAAA,MAAM;AAAA,IACJ,UAAA;AAAA,IACA,OAAA;AAAA,IACA,IAAA;AAAA,IACA,MAAA,EAAQ,EAAE,OAAA,EAAS,KAAA,EAAO,SAAA;AAAU,MAClC,SAAA,EAAU;AACd,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAI,QAAA,CAAiC,EAAE,CAAA;AACjE,EAAA,MAAM,CAAC,eAAA,EAAiB,kBAAkB,CAAA,GAAI,SAAS,KAAK,CAAA;AAC5D,EAAA,MAAM,eAAe,MAAA,EAAsC;AAE3D,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,SAAA,EAAW;AACb,MAAA,UAAA,CAAW,SAAA,CAAU,OAAA,CAAQ,KAAA,CAAM,CAAA,EAAG,EAAE,CAA2B,CAAA;AAAA,IACrE;AAAA,EACF,CAAA,EAAG,CAAC,OAAA,EAAS,SAAS,CAAC,CAAA;AAEvB,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,YAAA,CAAa,aAAa,OAAO,CAAA;AACjC,IAAA,kBAAA,CAAmB,KAAK,CAAA;AACxB,IAAA,IAAI,OAAA,EAAS;AACX,MAAA,YAAA,CAAa,UAAU,UAAA,CAAW,MAAM,kBAAA,CAAmB,IAAI,GAAG,GAAG,CAAA;AAAA,IACvE;AACA,IAAA,OAAO,MAAM,YAAA,CAAa,YAAA,CAAa,OAAO,CAAA;AAAA,EAChD,CAAA,EAAG,CAAC,IAAA,EAAM,OAAO,CAAC,CAAA;AAElB,EAAA,MAAM,EAAE,IAAA,EAAM,IAAA,EAAM,SAAA,EAAU,GAAI,QAAA;AAClC,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,UAAA,CAAW,CAAA,WAAA,MAAgB;AAAA,MACzB,GAAG,WAAA;AAAA,MACH,IAAA;AAAA,MACA,SAAA;AAAA,MACA;AAAA,KACF,CAAE,CAAA;AAAA,EACJ,GAAG,CAAC,IAAA,EAAM,SAAA,EAAW,IAAA,EAAM,UAAU,CAAC,CAAA;AAEtC,EAAA,OAAO;AAAA,IACL,OAAA;AAAA,IACA,IAAA;AAAA,IACA,OAAA;AAAA,IACA,OAAA;AAAA,IACA;AAAA,GACF;AACF;;;;"}
@@ -0,0 +1,29 @@
1
+ function styleInject(css, ref) {
2
+ if ( ref === void 0 ) ref = {};
3
+ var insertAt = ref.insertAt;
4
+
5
+ if (typeof document === 'undefined') { return; }
6
+
7
+ var head = document.head || document.getElementsByTagName('head')[0];
8
+ var style = document.createElement('style');
9
+ style.type = 'text/css';
10
+
11
+ if (insertAt === 'top') {
12
+ if (head.firstChild) {
13
+ head.insertBefore(style, head.firstChild);
14
+ } else {
15
+ head.appendChild(style);
16
+ }
17
+ } else {
18
+ head.appendChild(style);
19
+ }
20
+
21
+ if (style.styleSheet) {
22
+ style.styleSheet.cssText = css;
23
+ } else {
24
+ style.appendChild(document.createTextNode(css));
25
+ }
26
+ }
27
+
28
+ export { styleInject as default };
29
+ //# sourceMappingURL=style-inject.es.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"style-inject.es.esm.js","sources":["../../../../../../node_modules/style-inject/dist/style-inject.es.js"],"sourcesContent":["function styleInject(css, ref) {\n if ( ref === void 0 ) ref = {};\n var insertAt = ref.insertAt;\n\n if (!css || typeof document === 'undefined') { return; }\n\n var head = document.head || document.getElementsByTagName('head')[0];\n var style = document.createElement('style');\n style.type = 'text/css';\n\n if (insertAt === 'top') {\n if (head.firstChild) {\n head.insertBefore(style, head.firstChild);\n } else {\n head.appendChild(style);\n }\n } else {\n head.appendChild(style);\n }\n\n if (style.styleSheet) {\n style.styleSheet.cssText = css;\n } else {\n style.appendChild(document.createTextNode(css));\n }\n}\n\nexport default styleInject;\n"],"names":[],"mappings":"AAAA,SAAS,WAAW,CAAC,GAAG,EAAE,GAAG,EAAE;AAC/B,EAAE,KAAK,GAAG,KAAK,MAAM,GAAG,GAAG,GAAG,EAAE;AAChC,EAAE,IAAI,QAAQ,GAAG,GAAG,CAAC,QAAQ;;AAE7B,EAAE,IAAY,OAAO,QAAQ,KAAK,WAAW,EAAE,EAAE,OAAO,CAAC;;AAEzD,EAAE,IAAI,IAAI,GAAG,QAAQ,CAAC,IAAI,IAAI,QAAQ,CAAC,oBAAoB,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;AACtE,EAAE,IAAI,KAAK,GAAG,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC;AAC7C,EAAE,KAAK,CAAC,IAAI,GAAG,UAAU;;AAEzB,EAAE,IAAI,QAAQ,KAAK,KAAK,EAAE;AAC1B,IAAI,IAAI,IAAI,CAAC,UAAU,EAAE;AACzB,MAAM,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,IAAI,CAAC,UAAU,CAAC;AAC/C,IAAI,CAAC,MAAM;AACX,MAAM,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC;AAC7B,IAAI;AACJ,EAAE,CAAC,MAAM;AACT,IAAI,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC;AAC3B,EAAE;;AAEF,EAAE,IAAI,KAAK,CAAC,UAAU,EAAE;AACxB,IAAI,KAAK,CAAC,UAAU,CAAC,OAAO,GAAG,GAAG;AAClC,EAAE,CAAC,MAAM;AACT,IAAI,KAAK,CAAC,WAAW,CAAC,QAAQ,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC;AACnD,EAAE;AACF;;;;","x_google_ignoreList":[0]}
@@ -1,5 +1,5 @@
1
1
  var name = "@backstage/plugin-techdocs";
2
- var version = "1.17.1-next.2";
2
+ var version = "1.17.2";
3
3
  var description = "The Backstage plugin that renders technical documentation for your components";
4
4
  var backstage = {
5
5
  role: "frontend-plugin",
@@ -74,11 +74,13 @@ var dependencies = {
74
74
  "@backstage/plugin-techdocs-common": "workspace:^",
75
75
  "@backstage/plugin-techdocs-react": "workspace:^",
76
76
  "@backstage/theme": "workspace:^",
77
+ "@backstage/ui": "workspace:^",
77
78
  "@material-ui/core": "^4.12.2",
78
79
  "@material-ui/icons": "^4.9.1",
79
80
  "@material-ui/lab": "4.0.0-alpha.61",
80
81
  "@material-ui/styles": "^4.10.0",
81
82
  "@microsoft/fetch-event-source": "^2.0.1",
83
+ "@remixicon/react": "^4.6.0",
82
84
  dompurify: "^3.3.2",
83
85
  "git-url-parse": "^15.0.0",
84
86
  lodash: "^4.17.21",
@@ -1 +1 @@
1
- {"version":3,"file":"package.json.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"package.json.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -1,15 +1,13 @@
1
1
  import { jsx, jsxs } from 'react/jsx-runtime';
2
- import { useEffect, useCallback } from 'react';
3
2
  import Grid from '@material-ui/core/Grid';
4
3
  import { makeStyles } from '@material-ui/core/styles';
5
- import { useTechDocsReaderPage, useShadowDomStylesLoading, useShadowRootElements, TechDocsShadowDom } from '@backstage/plugin-techdocs-react';
4
+ import { TechDocsShadowDom } from '@backstage/plugin-techdocs-react';
6
5
  import { Content, Progress } from '@backstage/core-components';
7
6
  import { TechDocsSearch } from '../../../search/components/TechDocsSearch.esm.js';
8
7
  import { TechDocsStateIndicator } from '../TechDocsStateIndicator.esm.js';
9
- import { useTechDocsReaderDom } from './dom.esm.js';
10
- import { withTechDocsReaderProvider, useTechDocsReader } from '../TechDocsReaderProvider.esm.js';
8
+ import { withTechDocsReaderProvider } from '../TechDocsReaderProvider.esm.js';
11
9
  import { TechDocsReaderPageContentAddons } from './TechDocsReaderPageContentAddons.esm.js';
12
- import { useApp } from '@backstage/core-plugin-api';
10
+ import { useTechDocsReaderContentData } from '../../../hooks/useTechDocsReaderContentData.esm.js';
13
11
 
14
12
  const useStyles = makeStyles({
15
13
  search: {
@@ -25,43 +23,23 @@ const useStyles = makeStyles({
25
23
  });
26
24
  const TechDocsReaderPageContent = withTechDocsReaderProvider(
27
25
  (props) => {
28
- const { withSearch = true, searchResultUrlMapper, onReady } = props;
26
+ const { withSearch = true, searchResultUrlMapper } = props;
29
27
  const classes = useStyles();
30
28
  const {
31
- entityMetadata: { value: entityMetadata, loading: entityMetadataLoading },
32
29
  entityRef,
33
- setShadowRoot
34
- } = useTechDocsReaderPage();
35
- const { state } = useTechDocsReader();
36
- const dom = useTechDocsReaderDom(entityRef, props.defaultPath);
37
- const path = window.location.pathname;
38
- const hash = window.location.hash;
39
- const isStyleLoading = useShadowDomStylesLoading(dom);
40
- const [hashElement] = useShadowRootElements([`[id="${hash.slice(1)}"]`]);
41
- const app = useApp();
42
- const { NotFoundErrorPage } = app.getComponents();
43
- useEffect(() => {
44
- if (isStyleLoading) return;
45
- if (hash) {
46
- if (hashElement) {
47
- hashElement.scrollIntoView();
48
- }
49
- } else {
50
- document?.querySelector("header")?.scrollIntoView();
51
- }
52
- }, [path, hash, hashElement, isStyleLoading]);
53
- const handleAppend = useCallback(
54
- (newShadowRoot) => {
55
- setShadowRoot(newShadowRoot);
56
- if (onReady instanceof Function) {
57
- onReady();
58
- }
59
- },
60
- [setShadowRoot, onReady]
61
- );
62
- if (entityMetadataLoading === false && !entityMetadata)
63
- return /* @__PURE__ */ jsx(NotFoundErrorPage, {});
64
- if (!dom) {
30
+ entityMetadata,
31
+ dom,
32
+ handleAppend,
33
+ isNotFound,
34
+ isDomReady,
35
+ showProgress,
36
+ NotFoundErrorPage
37
+ } = useTechDocsReaderContentData({
38
+ defaultPath: props.defaultPath,
39
+ onReady: props.onReady
40
+ });
41
+ if (isNotFound) return /* @__PURE__ */ jsx(NotFoundErrorPage, {});
42
+ if (!isDomReady) {
65
43
  return /* @__PURE__ */ jsx(Content, { children: /* @__PURE__ */ jsx(Grid, { container: true, children: /* @__PURE__ */ jsx(Grid, { xs: 12, item: true, children: /* @__PURE__ */ jsx(TechDocsStateIndicator, {}) }) }) });
66
44
  }
67
45
  return /* @__PURE__ */ jsx(Content, { children: /* @__PURE__ */ jsxs(Grid, { container: true, children: [
@@ -75,7 +53,7 @@ const TechDocsReaderPageContent = withTechDocsReaderProvider(
75
53
  }
76
54
  ) }),
77
55
  /* @__PURE__ */ jsxs(Grid, { xs: 12, item: true, children: [
78
- (state === "CHECKING" || isStyleLoading) && /* @__PURE__ */ jsx(Progress, {}),
56
+ showProgress && /* @__PURE__ */ jsx(Progress, {}),
79
57
  /* @__PURE__ */ jsx(TechDocsShadowDom, { element: dom, onAppend: handleAppend, children: /* @__PURE__ */ jsx(TechDocsReaderPageContentAddons, {}) })
80
58
  ] })
81
59
  ] }) });
@@ -1 +1 @@
1
- {"version":3,"file":"TechDocsReaderPageContent.esm.js","sources":["../../../../src/reader/components/TechDocsReaderPageContent/TechDocsReaderPageContent.tsx"],"sourcesContent":["/*\n * Copyright 2022 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, useEffect } from 'react';\n\nimport Grid from '@material-ui/core/Grid';\nimport { makeStyles } from '@material-ui/core/styles';\n\nimport {\n TechDocsShadowDom,\n useShadowDomStylesLoading,\n useShadowRootElements,\n useTechDocsReaderPage,\n} from '@backstage/plugin-techdocs-react';\nimport { CompoundEntityRef } from '@backstage/catalog-model';\nimport { Content, Progress } from '@backstage/core-components';\n\nimport { TechDocsSearch } from '../../../search';\nimport { TechDocsStateIndicator } from '../TechDocsStateIndicator';\n\nimport { useTechDocsReaderDom } from './dom';\nimport {\n useTechDocsReader,\n withTechDocsReaderProvider,\n} from '../TechDocsReaderProvider';\nimport { TechDocsReaderPageContentAddons } from './TechDocsReaderPageContentAddons';\nimport { useApp } from '@backstage/core-plugin-api';\n\nconst useStyles = makeStyles({\n search: {\n width: '100%',\n '@media (min-width: 76.1875em)': {\n width: 'calc(100% - 34.4rem)',\n margin: '0 auto',\n },\n '@media print': {\n display: 'none',\n },\n },\n});\n\n/**\n * Props for {@link TechDocsReaderPageContent}\n * @public\n */\nexport type TechDocsReaderPageContentProps = {\n /**\n * @deprecated No need to pass down entityRef as property anymore. Consumes the entityName from `TechDocsReaderPageContext`. Use the {@link @backstage/plugin-techdocs-react#useTechDocsReaderPage} hook for custom reader page content.\n */\n entityRef?: CompoundEntityRef;\n /**\n * Path in the docs to render by default. This should be used when rendering docs for an entity that specifies the\n * \"backstage.io/techdocs-entity-path\" annotation for deep linking into another entities docs.\n */\n defaultPath?: string;\n /**\n * Show or hide the search bar, defaults to true.\n */\n withSearch?: boolean;\n /**\n * If {@link TechDocsReaderPageContentProps.withSearch | withSearch} is true,\n * this will redirect the search result urls, e.g. turn search results into\n * links within the \"Docs\" tab of the entity page, instead of the global docs\n * page.\n */\n searchResultUrlMapper?: (url: string) => string;\n /**\n * Callback called when the content is rendered.\n */\n onReady?: () => void;\n};\n\n/**\n * Renders the reader page content\n * @public\n */\nexport const TechDocsReaderPageContent = withTechDocsReaderProvider(\n (props: TechDocsReaderPageContentProps) => {\n const { withSearch = true, searchResultUrlMapper, onReady } = props;\n const classes = useStyles();\n\n const {\n entityMetadata: { value: entityMetadata, loading: entityMetadataLoading },\n entityRef,\n setShadowRoot,\n } = useTechDocsReaderPage();\n const { state } = useTechDocsReader();\n const dom = useTechDocsReaderDom(entityRef, props.defaultPath);\n const path = window.location.pathname;\n const hash = window.location.hash;\n const isStyleLoading = useShadowDomStylesLoading(dom);\n const [hashElement] = useShadowRootElements([`[id=\"${hash.slice(1)}\"]`]);\n const app = useApp();\n const { NotFoundErrorPage } = app.getComponents();\n\n useEffect(() => {\n if (isStyleLoading) return;\n\n if (hash) {\n if (hashElement) {\n hashElement.scrollIntoView();\n }\n } else {\n document?.querySelector('header')?.scrollIntoView();\n }\n }, [path, hash, hashElement, isStyleLoading]);\n\n const handleAppend = useCallback(\n (newShadowRoot: ShadowRoot) => {\n setShadowRoot(newShadowRoot);\n if (onReady instanceof Function) {\n onReady();\n }\n },\n [setShadowRoot, onReady],\n );\n\n // No entity metadata = 404. Don't render content at all.\n if (entityMetadataLoading === false && !entityMetadata)\n return <NotFoundErrorPage />;\n\n // Do not return content until dom is ready; instead, render a state\n // indicator, which handles progress and content errors on our behalf.\n if (!dom) {\n return (\n <Content>\n <Grid container>\n <Grid xs={12} item>\n <TechDocsStateIndicator />\n </Grid>\n </Grid>\n </Content>\n );\n }\n\n return (\n <Content>\n <Grid container>\n <Grid xs={12} item>\n <TechDocsStateIndicator />\n </Grid>\n {withSearch && (\n <Grid className={classes.search} xs=\"auto\" item>\n <TechDocsSearch\n entityId={entityRef}\n entityTitle={entityMetadata?.metadata?.title}\n searchResultUrlMapper={searchResultUrlMapper}\n />\n </Grid>\n )}\n <Grid xs={12} item>\n {/* Centers the styles loaded event to avoid having multiple locations setting the opacity style in Shadow Dom causing the screen to flash multiple times */}\n {(state === 'CHECKING' || isStyleLoading) && <Progress />}\n\n <TechDocsShadowDom element={dom} onAppend={handleAppend}>\n <TechDocsReaderPageContentAddons />\n </TechDocsShadowDom>\n </Grid>\n </Grid>\n </Content>\n );\n },\n);\n\n/**\n * Props for {@link Reader}\n *\n * @public\n * @deprecated use `TechDocsReaderPageContentProps` instead.\n */\nexport type ReaderProps = TechDocsReaderPageContentProps;\n\n/**\n * Component responsible for rendering TechDocs documentation\n * @public\n * @deprecated use `TechDocsReaderPageContent` component instead.\n */\nexport const Reader = TechDocsReaderPageContent;\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"TechDocsReaderPageContent.esm.js","sources":["../../../../src/reader/components/TechDocsReaderPageContent/TechDocsReaderPageContent.tsx"],"sourcesContent":["/*\n * Copyright 2022 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 Grid from '@material-ui/core/Grid';\nimport { makeStyles } from '@material-ui/core/styles';\n\nimport { TechDocsShadowDom } from '@backstage/plugin-techdocs-react';\nimport { CompoundEntityRef } from '@backstage/catalog-model';\nimport { Content, Progress } from '@backstage/core-components';\n\nimport { TechDocsSearch } from '../../../search';\nimport { TechDocsStateIndicator } from '../TechDocsStateIndicator';\n\nimport { withTechDocsReaderProvider } from '../TechDocsReaderProvider';\nimport { TechDocsReaderPageContentAddons } from './TechDocsReaderPageContentAddons';\nimport { useTechDocsReaderContentData } from '../../../hooks/useTechDocsReaderContentData';\n\nconst useStyles = makeStyles({\n search: {\n width: '100%',\n '@media (min-width: 76.1875em)': {\n width: 'calc(100% - 34.4rem)',\n margin: '0 auto',\n },\n '@media print': {\n display: 'none',\n },\n },\n});\n\n/**\n * Props for {@link TechDocsReaderPageContent}\n * @public\n */\nexport type TechDocsReaderPageContentProps = {\n /**\n * @deprecated No need to pass down entityRef as property anymore. Consumes the entityName from `TechDocsReaderPageContext`. Use the {@link @backstage/plugin-techdocs-react#useTechDocsReaderPage} hook for custom reader page content.\n */\n entityRef?: CompoundEntityRef;\n /**\n * Path in the docs to render by default. This should be used when rendering docs for an entity that specifies the\n * \"backstage.io/techdocs-entity-path\" annotation for deep linking into another entities docs.\n */\n defaultPath?: string;\n /**\n * Show or hide the search bar, defaults to true.\n */\n withSearch?: boolean;\n /**\n * If {@link TechDocsReaderPageContentProps.withSearch | withSearch} is true,\n * this will redirect the search result urls, e.g. turn search results into\n * links within the \"Docs\" tab of the entity page, instead of the global docs\n * page.\n */\n searchResultUrlMapper?: (url: string) => string;\n /**\n * Callback called when the content is rendered.\n */\n onReady?: () => void;\n};\n\n/**\n * Renders the reader page content\n * @public\n */\nexport const TechDocsReaderPageContent = withTechDocsReaderProvider(\n (props: TechDocsReaderPageContentProps) => {\n const { withSearch = true, searchResultUrlMapper } = props;\n const classes = useStyles();\n\n const {\n entityRef,\n entityMetadata,\n dom,\n handleAppend,\n isNotFound,\n isDomReady,\n showProgress,\n NotFoundErrorPage,\n } = useTechDocsReaderContentData({\n defaultPath: props.defaultPath,\n onReady: props.onReady,\n });\n\n if (isNotFound) return <NotFoundErrorPage />;\n\n if (!isDomReady) {\n return (\n <Content>\n <Grid container>\n <Grid xs={12} item>\n <TechDocsStateIndicator />\n </Grid>\n </Grid>\n </Content>\n );\n }\n\n return (\n <Content>\n <Grid container>\n <Grid xs={12} item>\n <TechDocsStateIndicator />\n </Grid>\n {withSearch && (\n <Grid className={classes.search} xs=\"auto\" item>\n <TechDocsSearch\n entityId={entityRef}\n entityTitle={entityMetadata?.metadata?.title}\n searchResultUrlMapper={searchResultUrlMapper}\n />\n </Grid>\n )}\n <Grid xs={12} item>\n {showProgress && <Progress />}\n\n <TechDocsShadowDom element={dom!} onAppend={handleAppend}>\n <TechDocsReaderPageContentAddons />\n </TechDocsShadowDom>\n </Grid>\n </Grid>\n </Content>\n );\n },\n);\n\n/**\n * Props for {@link Reader}\n *\n * @public\n * @deprecated use `TechDocsReaderPageContentProps` instead.\n */\nexport type ReaderProps = TechDocsReaderPageContentProps;\n\n/**\n * Component responsible for rendering TechDocs documentation\n * @public\n * @deprecated use `TechDocsReaderPageContent` component instead.\n */\nexport const Reader = TechDocsReaderPageContent;\n"],"names":[],"mappings":";;;;;;;;;;;AA8BA,MAAM,YAAY,UAAA,CAAW;AAAA,EAC3B,MAAA,EAAQ;AAAA,IACN,KAAA,EAAO,MAAA;AAAA,IACP,+BAAA,EAAiC;AAAA,MAC/B,KAAA,EAAO,sBAAA;AAAA,MACP,MAAA,EAAQ;AAAA,KACV;AAAA,IACA,cAAA,EAAgB;AAAA,MACd,OAAA,EAAS;AAAA;AACX;AAEJ,CAAC,CAAA;AAqCM,MAAM,yBAAA,GAA4B,0BAAA;AAAA,EACvC,CAAC,KAAA,KAA0C;AACzC,IAAA,MAAM,EAAE,UAAA,GAAa,IAAA,EAAM,qBAAA,EAAsB,GAAI,KAAA;AACrD,IAAA,MAAM,UAAU,SAAA,EAAU;AAE1B,IAAA,MAAM;AAAA,MACJ,SAAA;AAAA,MACA,cAAA;AAAA,MACA,GAAA;AAAA,MACA,YAAA;AAAA,MACA,UAAA;AAAA,MACA,UAAA;AAAA,MACA,YAAA;AAAA,MACA;AAAA,QACE,4BAAA,CAA6B;AAAA,MAC/B,aAAa,KAAA,CAAM,WAAA;AAAA,MACnB,SAAS,KAAA,CAAM;AAAA,KAChB,CAAA;AAED,IAAA,IAAI,UAAA,EAAY,uBAAO,GAAA,CAAC,iBAAA,EAAA,EAAkB,CAAA;AAE1C,IAAA,IAAI,CAAC,UAAA,EAAY;AACf,MAAA,2BACG,OAAA,EAAA,EACC,QAAA,kBAAA,GAAA,CAAC,IAAA,EAAA,EAAK,SAAA,EAAS,MACb,QAAA,kBAAA,GAAA,CAAC,IAAA,EAAA,EAAK,EAAA,EAAI,EAAA,EAAI,MAAI,IAAA,EAChB,QAAA,kBAAA,GAAA,CAAC,sBAAA,EAAA,EAAuB,CAAA,EAC1B,GACF,CAAA,EACF,CAAA;AAAA,IAEJ;AAEA,IAAA,uBACE,GAAA,CAAC,OAAA,EAAA,EACC,QAAA,kBAAA,IAAA,CAAC,IAAA,EAAA,EAAK,WAAS,IAAA,EACb,QAAA,EAAA;AAAA,sBAAA,GAAA,CAAC,QAAK,EAAA,EAAI,EAAA,EAAI,MAAI,IAAA,EAChB,QAAA,kBAAA,GAAA,CAAC,0BAAuB,CAAA,EAC1B,CAAA;AAAA,MACC,UAAA,wBACE,IAAA,EAAA,EAAK,SAAA,EAAW,QAAQ,MAAA,EAAQ,EAAA,EAAG,MAAA,EAAO,IAAA,EAAI,IAAA,EAC7C,QAAA,kBAAA,GAAA;AAAA,QAAC,cAAA;AAAA,QAAA;AAAA,UACC,QAAA,EAAU,SAAA;AAAA,UACV,WAAA,EAAa,gBAAgB,QAAA,EAAU,KAAA;AAAA,UACvC;AAAA;AAAA,OACF,EACF,CAAA;AAAA,sBAEF,IAAA,CAAC,IAAA,EAAA,EAAK,EAAA,EAAI,EAAA,EAAI,MAAI,IAAA,EACf,QAAA,EAAA;AAAA,QAAA,YAAA,wBAAiB,QAAA,EAAA,EAAS,CAAA;AAAA,wBAE3B,GAAA,CAAC,qBAAkB,OAAA,EAAS,GAAA,EAAM,UAAU,YAAA,EAC1C,QAAA,kBAAA,GAAA,CAAC,mCAAgC,CAAA,EACnC;AAAA,OAAA,EACF;AAAA,KAAA,EACF,CAAA,EACF,CAAA;AAAA,EAEJ;AACF;AAeO,MAAM,MAAA,GAAS;;;;"}
@@ -1,50 +1,36 @@
1
1
  import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
2
- import { useEffect } from 'react';
3
2
  import Helmet from 'react-helmet';
4
3
  import Grid from '@material-ui/core/Grid';
5
4
  import Skeleton from '@material-ui/lab/Skeleton';
6
5
  import CodeIcon from '@material-ui/icons/Code';
7
- import { useTechDocsAddons, useTechDocsReaderPage, TechDocsAddonLocations } from '@backstage/plugin-techdocs-react';
8
- import { entityPresentationApiRef, getEntityRelations, EntityRefLink, EntityRefLinks } from '@backstage/plugin-catalog-react';
9
- import { RELATION_OWNED_BY, stringifyEntityRef } from '@backstage/catalog-model';
6
+ import { TechDocsAddonLocations } from '@backstage/plugin-techdocs-react';
7
+ import { getEntityRelations, EntityRefLink, EntityRefLinks } from '@backstage/plugin-catalog-react';
8
+ import { RELATION_OWNED_BY } from '@backstage/catalog-model';
10
9
  import { HeaderLabel, Header } from '@backstage/core-components';
11
- import { useApi, configApiRef, useRouteRef } from '@backstage/core-plugin-api';
10
+ import { useRouteRef } from '@backstage/core-plugin-api';
12
11
  import capitalize from 'lodash/capitalize';
13
12
  import { rootRouteRef } from '../../../routes.esm.js';
14
- import { useParams } from 'react-router-dom';
13
+ import { useTechDocsReaderHeaderData } from '../../../hooks/useTechDocsReaderHeaderData.esm.js';
15
14
 
16
15
  const skeleton = /* @__PURE__ */ jsx(Skeleton, { animation: "wave", variant: "text", height: 40 });
17
16
  const TechDocsReaderPageHeader = (props) => {
18
17
  const { children } = props;
19
- const addons = useTechDocsAddons();
20
- const configApi = useApi(configApiRef);
21
- const entityPresentationApi = useApi(entityPresentationApiRef);
22
- const { "*": path = "" } = useParams();
23
18
  const {
24
19
  title,
25
- setTitle,
26
20
  subtitle,
27
- setSubtitle,
28
21
  entityRef,
29
- metadata: { value: metadata, loading: metadataLoading },
30
- entityMetadata: { value: entityMetadata, loading: entityMetadataLoading }
31
- } = useTechDocsReaderPage();
32
- useEffect(() => {
33
- if (!metadata) return;
34
- setTitle(metadata.site_name);
35
- setSubtitle(() => {
36
- let { site_description } = metadata;
37
- if (!site_description || site_description === "None") {
38
- site_description = "";
39
- }
40
- return site_description;
41
- });
42
- }, [metadata, setTitle, setSubtitle]);
43
- const appTitle = configApi.getOptional("app.title") || "Backstage";
44
- const { locationMetadata, spec } = entityMetadata || {};
22
+ entityMetadata,
23
+ tabTitle,
24
+ hidden,
25
+ showSourceLink,
26
+ sourceLink,
27
+ addons
28
+ } = useTechDocsReaderHeaderData();
29
+ const docsRootLink = useRouteRef(rootRouteRef)();
30
+ if (hidden) return null;
31
+ const { spec } = entityMetadata || {};
45
32
  const lifecycle = spec?.lifecycle;
46
33
  const ownedByRelations = entityMetadata ? getEntityRelations(entityMetadata, RELATION_OWNED_BY) : [];
47
- const docsRootLink = useRouteRef(rootRouteRef)();
48
34
  const labels = /* @__PURE__ */ jsxs(Fragment, { children: [
49
35
  /* @__PURE__ */ jsx(
50
36
  HeaderLabel,
@@ -76,7 +62,7 @@ const TechDocsReaderPageHeader = (props) => {
76
62
  }
77
63
  ),
78
64
  lifecycle ? /* @__PURE__ */ jsx(HeaderLabel, { label: "Lifecycle", value: String(lifecycle) }) : null,
79
- locationMetadata && locationMetadata.type !== "dir" && locationMetadata.type !== "file" ? /* @__PURE__ */ jsx(
65
+ showSourceLink ? /* @__PURE__ */ jsx(
80
66
  HeaderLabel,
81
67
  {
82
68
  label: "",
@@ -84,22 +70,10 @@ const TechDocsReaderPageHeader = (props) => {
84
70
  /* @__PURE__ */ jsx(Grid, { style: { padding: 0 }, item: true, children: /* @__PURE__ */ jsx(CodeIcon, { style: { marginTop: "-25px" } }) }),
85
71
  /* @__PURE__ */ jsx(Grid, { style: { padding: 0 }, item: true, children: "Source" })
86
72
  ] }),
87
- url: locationMetadata.target
73
+ url: sourceLink
88
74
  }
89
75
  ) : null
90
76
  ] });
91
- const noEntMetadata = !entityMetadataLoading && entityMetadata === void 0;
92
- const noTdMetadata = !metadataLoading && metadata === void 0;
93
- if (noEntMetadata || noTdMetadata) return null;
94
- const stringEntityRef = stringifyEntityRef(entityRef);
95
- const entityDisplayName = entityPresentationApi.forEntity(stringEntityRef).snapshot.primaryTitle;
96
- const removeTrailingSlash = (str) => str.replace(/\/$/, "");
97
- const normalizeAndSpace = (str) => str.replace(/[-_]/g, " ").split(" ").map(capitalize).join(" ");
98
- let techdocsTabTitleItems = [];
99
- if (path !== "")
100
- techdocsTabTitleItems = removeTrailingSlash(path).split("/").map(normalizeAndSpace);
101
- const tabTitleItems = [entityDisplayName, ...techdocsTabTitleItems, appTitle];
102
- const tabTitle = tabTitleItems.join(" | ");
103
77
  return /* @__PURE__ */ jsxs(
104
78
  Header,
105
79
  {
@@ -1 +1 @@
1
- {"version":3,"file":"TechDocsReaderPageHeader.esm.js","sources":["../../../../src/reader/components/TechDocsReaderPageHeader/TechDocsReaderPageHeader.tsx"],"sourcesContent":["/*\n * Copyright 2022 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 { PropsWithChildren, useEffect } from 'react';\nimport Helmet from 'react-helmet';\n\nimport Grid from '@material-ui/core/Grid';\nimport Skeleton from '@material-ui/lab/Skeleton';\nimport CodeIcon from '@material-ui/icons/Code';\n\nimport {\n TechDocsAddonLocations as locations,\n useTechDocsAddons,\n useTechDocsReaderPage,\n TechDocsEntityMetadata,\n TechDocsMetadata,\n} from '@backstage/plugin-techdocs-react';\nimport {\n entityPresentationApiRef,\n EntityRefLink,\n EntityRefLinks,\n getEntityRelations,\n} from '@backstage/plugin-catalog-react';\nimport {\n RELATION_OWNED_BY,\n CompoundEntityRef,\n stringifyEntityRef,\n} from '@backstage/catalog-model';\nimport { Header, HeaderLabel } from '@backstage/core-components';\nimport { useRouteRef, configApiRef, useApi } from '@backstage/core-plugin-api';\n\nimport capitalize from 'lodash/capitalize';\n\nimport { rootRouteRef } from '../../../routes';\nimport { useParams } from 'react-router-dom';\n\nconst skeleton = <Skeleton animation=\"wave\" variant=\"text\" height={40} />;\n\n/**\n * Props for {@link TechDocsReaderPageHeader}\n *\n * @public\n * @deprecated No need to pass down properties anymore. The component consumes data from `TechDocsReaderPageContext` instead. Use the {@link @backstage/plugin-techdocs-react#useTechDocsReaderPage} hook for custom header.\n */\nexport type TechDocsReaderPageHeaderProps = PropsWithChildren<{\n entityRef?: CompoundEntityRef;\n entityMetadata?: TechDocsEntityMetadata;\n techDocsMetadata?: TechDocsMetadata;\n}>;\n\n/**\n * Renders the reader page header.\n * This component does not accept props, please use\n * the Tech Docs add-ons to customize it\n * @public\n */\nexport const TechDocsReaderPageHeader = (\n props: TechDocsReaderPageHeaderProps,\n) => {\n const { children } = props;\n const addons = useTechDocsAddons();\n const configApi = useApi(configApiRef);\n\n const entityPresentationApi = useApi(entityPresentationApiRef);\n const { '*': path = '' } = useParams();\n\n const {\n title,\n setTitle,\n subtitle,\n setSubtitle,\n entityRef,\n metadata: { value: metadata, loading: metadataLoading },\n entityMetadata: { value: entityMetadata, loading: entityMetadataLoading },\n } = useTechDocsReaderPage();\n\n useEffect(() => {\n if (!metadata) return;\n setTitle(metadata.site_name);\n setSubtitle(() => {\n let { site_description } = metadata;\n if (!site_description || site_description === 'None') {\n site_description = '';\n }\n return site_description;\n });\n }, [metadata, setTitle, setSubtitle]);\n\n const appTitle = configApi.getOptional('app.title') || 'Backstage';\n\n const { locationMetadata, spec } = entityMetadata || {};\n const lifecycle = spec?.lifecycle;\n\n const ownedByRelations = entityMetadata\n ? getEntityRelations(entityMetadata, RELATION_OWNED_BY)\n : [];\n\n const docsRootLink = useRouteRef(rootRouteRef)();\n\n const labels = (\n <>\n <HeaderLabel\n label={capitalize(entityMetadata?.kind || 'entity')}\n value={\n <EntityRefLink\n color=\"inherit\"\n entityRef={entityRef}\n title={entityMetadata?.metadata.title}\n defaultKind=\"Component\"\n />\n }\n />\n {ownedByRelations.length > 0 && (\n <HeaderLabel\n label=\"Owner\"\n value={\n <EntityRefLinks\n color=\"inherit\"\n entityRefs={ownedByRelations}\n defaultKind=\"group\"\n />\n }\n />\n )}\n {lifecycle ? (\n <HeaderLabel label=\"Lifecycle\" value={String(lifecycle)} />\n ) : null}\n {locationMetadata &&\n locationMetadata.type !== 'dir' &&\n locationMetadata.type !== 'file' ? (\n <HeaderLabel\n label=\"\"\n value={\n <Grid container direction=\"column\" alignItems=\"center\">\n <Grid style={{ padding: 0 }} item>\n <CodeIcon style={{ marginTop: '-25px' }} />\n </Grid>\n <Grid style={{ padding: 0 }} item>\n Source\n </Grid>\n </Grid>\n }\n url={locationMetadata.target}\n />\n ) : null}\n </>\n );\n\n // If there is no entity or techdocs metadata, there's no reason to show the\n // header (hides the header on 404 error pages).\n const noEntMetadata = !entityMetadataLoading && entityMetadata === undefined;\n const noTdMetadata = !metadataLoading && metadata === undefined;\n if (noEntMetadata || noTdMetadata) return null;\n\n const stringEntityRef = stringifyEntityRef(entityRef);\n\n const entityDisplayName =\n entityPresentationApi.forEntity(stringEntityRef).snapshot.primaryTitle;\n\n const removeTrailingSlash = (str: string) => str.replace(/\\/$/, '');\n const normalizeAndSpace = (str: string) =>\n str.replace(/[-_]/g, ' ').split(' ').map(capitalize).join(' ');\n\n let techdocsTabTitleItems: string[] = [];\n\n if (path !== '')\n techdocsTabTitleItems = removeTrailingSlash(path)\n .split('/')\n .map(normalizeAndSpace);\n\n const tabTitleItems = [entityDisplayName, ...techdocsTabTitleItems, appTitle];\n const tabTitle = tabTitleItems.join(' | ');\n\n return (\n <Header\n type=\"Documentation\"\n typeLink={docsRootLink}\n title={title || skeleton}\n subtitle={subtitle === '' ? undefined : subtitle || skeleton}\n >\n <Helmet titleTemplate=\"%s\">\n <title>{tabTitle}</title>\n </Helmet>\n {labels}\n {children}\n {addons.renderComponentsByLocation(locations.Header)}\n </Header>\n );\n};\n"],"names":["locations"],"mappings":"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"}
1
+ {"version":3,"file":"TechDocsReaderPageHeader.esm.js","sources":["../../../../src/reader/components/TechDocsReaderPageHeader/TechDocsReaderPageHeader.tsx"],"sourcesContent":["/*\n * Copyright 2022 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 { PropsWithChildren } from 'react';\nimport Helmet from 'react-helmet';\n\nimport Grid from '@material-ui/core/Grid';\nimport Skeleton from '@material-ui/lab/Skeleton';\nimport CodeIcon from '@material-ui/icons/Code';\n\nimport {\n TechDocsAddonLocations as locations,\n TechDocsEntityMetadata,\n TechDocsMetadata,\n} from '@backstage/plugin-techdocs-react';\nimport {\n EntityRefLink,\n EntityRefLinks,\n getEntityRelations,\n} from '@backstage/plugin-catalog-react';\nimport { RELATION_OWNED_BY, CompoundEntityRef } from '@backstage/catalog-model';\nimport { Header, HeaderLabel } from '@backstage/core-components';\nimport { useRouteRef } from '@backstage/core-plugin-api';\n\nimport capitalize from 'lodash/capitalize';\n\nimport { rootRouteRef } from '../../../routes';\nimport { useTechDocsReaderHeaderData } from '../../../hooks/useTechDocsReaderHeaderData';\n\nconst skeleton = <Skeleton animation=\"wave\" variant=\"text\" height={40} />;\n\n/**\n * Props for {@link TechDocsReaderPageHeader}\n *\n * @public\n * @deprecated No need to pass down properties anymore. The component consumes data from `TechDocsReaderPageContext` instead. Use the {@link @backstage/plugin-techdocs-react#useTechDocsReaderPage} hook for custom header.\n */\nexport type TechDocsReaderPageHeaderProps = PropsWithChildren<{\n entityRef?: CompoundEntityRef;\n entityMetadata?: TechDocsEntityMetadata;\n techDocsMetadata?: TechDocsMetadata;\n}>;\n\n/**\n * Renders the reader page header.\n * This component does not accept props, please use\n * the Tech Docs add-ons to customize it\n * @public\n */\nexport const TechDocsReaderPageHeader = (\n props: TechDocsReaderPageHeaderProps,\n) => {\n const { children } = props;\n const {\n title,\n subtitle,\n entityRef,\n entityMetadata,\n tabTitle,\n hidden,\n showSourceLink,\n sourceLink,\n addons,\n } = useTechDocsReaderHeaderData();\n\n const docsRootLink = useRouteRef(rootRouteRef)();\n\n if (hidden) return null;\n\n const { spec } = entityMetadata || {};\n const lifecycle = spec?.lifecycle;\n\n const ownedByRelations = entityMetadata\n ? getEntityRelations(entityMetadata, RELATION_OWNED_BY)\n : [];\n\n const labels = (\n <>\n <HeaderLabel\n label={capitalize(entityMetadata?.kind || 'entity')}\n value={\n <EntityRefLink\n color=\"inherit\"\n entityRef={entityRef}\n title={entityMetadata?.metadata.title}\n defaultKind=\"Component\"\n />\n }\n />\n {ownedByRelations.length > 0 && (\n <HeaderLabel\n label=\"Owner\"\n value={\n <EntityRefLinks\n color=\"inherit\"\n entityRefs={ownedByRelations}\n defaultKind=\"group\"\n />\n }\n />\n )}\n {lifecycle ? (\n <HeaderLabel label=\"Lifecycle\" value={String(lifecycle)} />\n ) : null}\n {showSourceLink ? (\n <HeaderLabel\n label=\"\"\n value={\n <Grid container direction=\"column\" alignItems=\"center\">\n <Grid style={{ padding: 0 }} item>\n <CodeIcon style={{ marginTop: '-25px' }} />\n </Grid>\n <Grid style={{ padding: 0 }} item>\n Source\n </Grid>\n </Grid>\n }\n url={sourceLink}\n />\n ) : null}\n </>\n );\n\n return (\n <Header\n type=\"Documentation\"\n typeLink={docsRootLink}\n title={title || skeleton}\n subtitle={subtitle === '' ? undefined : subtitle || skeleton}\n >\n <Helmet titleTemplate=\"%s\">\n <title>{tabTitle}</title>\n </Helmet>\n {labels}\n {children}\n {addons.renderComponentsByLocation(locations.Header)}\n </Header>\n );\n};\n"],"names":["locations"],"mappings":"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"}
@@ -1,8 +1,9 @@
1
1
  import { jsx } from 'react/jsx-runtime';
2
- import { SearchContextProvider, useSearch, SearchAutocomplete } from '@backstage/plugin-search-react';
3
- import { useState, useEffect } from 'react';
2
+ import { SearchContextProvider, SearchAutocomplete } from '@backstage/plugin-search-react';
3
+ import { useState } from 'react';
4
4
  import { useNavigate } from 'react-router-dom';
5
5
  import { TechDocsSearchResultListItem } from './TechDocsSearchResultListItem.esm.js';
6
+ import { useTechDocsSearch } from '../../hooks/useTechDocsSearch.esm.js';
6
7
 
7
8
  const isTechDocsSearchResult = (option) => {
8
9
  return option?.document;
@@ -16,33 +17,7 @@ const TechDocsSearchBar = (props) => {
16
17
  } = props;
17
18
  const [open, setOpen] = useState(false);
18
19
  const navigate = useNavigate();
19
- const {
20
- setFilters,
21
- term,
22
- result: { loading, value: searchVal }
23
- } = useSearch();
24
- const [options, setOptions] = useState([]);
25
- useEffect(() => {
26
- let mounted = true;
27
- if (mounted && searchVal) {
28
- const searchResults = searchVal.results.slice(0, 10);
29
- setOptions(searchResults);
30
- }
31
- return () => {
32
- mounted = false;
33
- };
34
- }, [loading, searchVal]);
35
- const { kind, name, namespace } = entityId;
36
- useEffect(() => {
37
- setFilters((prevFilters) => {
38
- return {
39
- ...prevFilters,
40
- kind,
41
- namespace,
42
- name
43
- };
44
- });
45
- }, [kind, namespace, name, setFilters]);
20
+ const { results, term, loading } = useTechDocsSearch(entityId);
46
21
  const handleSelection = (_, selection) => {
47
22
  if (isTechDocsSearchResult(selection)) {
48
23
  const { location } = selection.document;
@@ -71,7 +46,7 @@ const TechDocsSearchBar = (props) => {
71
46
  blurOnSelect: true,
72
47
  noOptionsText: "No results found",
73
48
  value: null,
74
- options,
49
+ options: results,
75
50
  renderOption: ({ document, highlight }) => /* @__PURE__ */ jsx(
76
51
  TechDocsSearchResultListItem,
77
52
  {