@backstage/plugin-techdocs-module-addons-contrib 1.1.9 → 1.1.10-next.1
Sign up to get free protection for your applications and to get access to all the features.
- package/CHANGELOG.md +20 -0
- package/dist/ExpandableNavigation/ExpandableNavigation.esm.js +81 -0
- package/dist/ExpandableNavigation/ExpandableNavigation.esm.js.map +1 -0
- package/dist/LigthBox/LightBox.esm.js +60 -0
- package/dist/LigthBox/LightBox.esm.js.map +1 -0
- package/dist/ReportIssue/IssueLink.esm.js +49 -0
- package/dist/ReportIssue/IssueLink.esm.js.map +1 -0
- package/dist/ReportIssue/ReportIssue.esm.js +84 -0
- package/dist/ReportIssue/ReportIssue.esm.js.map +1 -0
- package/dist/ReportIssue/constants.esm.js +8 -0
- package/dist/ReportIssue/constants.esm.js.map +1 -0
- package/dist/ReportIssue/hooks.esm.js +79 -0
- package/dist/ReportIssue/hooks.esm.js.map +1 -0
- package/dist/TextSize/TextSize.esm.js +188 -0
- package/dist/TextSize/TextSize.esm.js.map +1 -0
- package/dist/index.esm.js +1 -548
- package/dist/index.esm.js.map +1 -1
- package/dist/plugin.esm.js +41 -0
- package/dist/plugin.esm.js.map +1 -0
- package/package.json +6 -6
package/CHANGELOG.md
CHANGED
@@ -1,5 +1,25 @@
|
|
1
1
|
# @backstage/plugin-techdocs-module-addons-contrib
|
2
2
|
|
3
|
+
## 1.1.10-next.1
|
4
|
+
|
5
|
+
### Patch Changes
|
6
|
+
|
7
|
+
- Updated dependencies
|
8
|
+
- @backstage/core-components@0.14.6-next.1
|
9
|
+
- @backstage/integration-react@1.1.26
|
10
|
+
- @backstage/plugin-techdocs-react@1.2.4-next.1
|
11
|
+
|
12
|
+
## 1.1.10-next.0
|
13
|
+
|
14
|
+
### Patch Changes
|
15
|
+
|
16
|
+
- Updated dependencies
|
17
|
+
- @backstage/core-components@0.14.5-next.0
|
18
|
+
- @backstage/plugin-techdocs-react@1.2.4-next.0
|
19
|
+
- @backstage/core-plugin-api@1.9.2
|
20
|
+
- @backstage/integration@1.10.0
|
21
|
+
- @backstage/integration-react@1.1.26
|
22
|
+
|
3
23
|
## 1.1.9
|
4
24
|
|
5
25
|
### Patch Changes
|
@@ -0,0 +1,81 @@
|
|
1
|
+
import React, { useState, useCallback, useEffect } from 'react';
|
2
|
+
import { useLocalStorageValue } from '@react-hookz/web';
|
3
|
+
import { withStyles, Button } from '@material-ui/core';
|
4
|
+
import ChevronRightIcon from '@material-ui/icons/ChevronRight';
|
5
|
+
import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
|
6
|
+
import { useShadowRootElements } from '@backstage/plugin-techdocs-react';
|
7
|
+
|
8
|
+
const NESTED_LIST_TOGGLE = ".md-nav__item--nested .md-toggle";
|
9
|
+
const EXPANDABLE_NAVIGATION_LOCAL_STORAGE = "@backstage/techdocs-addons/nav-expanded";
|
10
|
+
const StyledButton = withStyles({
|
11
|
+
root: {
|
12
|
+
position: "absolute",
|
13
|
+
left: "220px",
|
14
|
+
top: "19px",
|
15
|
+
padding: 0,
|
16
|
+
minWidth: 0
|
17
|
+
}
|
18
|
+
})(Button);
|
19
|
+
const CollapsedIcon = withStyles({
|
20
|
+
root: {
|
21
|
+
height: "20px",
|
22
|
+
width: "20px"
|
23
|
+
}
|
24
|
+
})(ChevronRightIcon);
|
25
|
+
const ExpandedIcon = withStyles({
|
26
|
+
root: {
|
27
|
+
height: "20px",
|
28
|
+
width: "20px"
|
29
|
+
}
|
30
|
+
})(ExpandMoreIcon);
|
31
|
+
const ExpandableNavigationAddon = () => {
|
32
|
+
const defaultValue = { expandAllNestedNavs: false };
|
33
|
+
const { value: expanded, set: setExpanded } = useLocalStorageValue(
|
34
|
+
EXPANDABLE_NAVIGATION_LOCAL_STORAGE,
|
35
|
+
{ defaultValue }
|
36
|
+
);
|
37
|
+
const [hasNavSubLevels, setHasNavSubLevels] = useState(false);
|
38
|
+
const [...checkboxToggles] = useShadowRootElements([
|
39
|
+
NESTED_LIST_TOGGLE
|
40
|
+
]);
|
41
|
+
const shouldToggle = useCallback(
|
42
|
+
(item) => {
|
43
|
+
const isExpanded = item.checked;
|
44
|
+
const shouldExpand = expanded == null ? void 0 : expanded.expandAllNestedNavs;
|
45
|
+
if (shouldExpand && !isExpanded) {
|
46
|
+
return true;
|
47
|
+
}
|
48
|
+
if (!shouldExpand && isExpanded) {
|
49
|
+
return true;
|
50
|
+
}
|
51
|
+
return false;
|
52
|
+
},
|
53
|
+
[expanded]
|
54
|
+
);
|
55
|
+
useEffect(() => {
|
56
|
+
if (!(checkboxToggles == null ? void 0 : checkboxToggles.length))
|
57
|
+
return;
|
58
|
+
setHasNavSubLevels(true);
|
59
|
+
checkboxToggles.forEach((item) => {
|
60
|
+
if (shouldToggle(item))
|
61
|
+
item.click();
|
62
|
+
});
|
63
|
+
}, [expanded, shouldToggle, checkboxToggles]);
|
64
|
+
const handleState = () => {
|
65
|
+
setExpanded((prevState) => ({
|
66
|
+
expandAllNestedNavs: !(prevState == null ? void 0 : prevState.expandAllNestedNavs)
|
67
|
+
}));
|
68
|
+
};
|
69
|
+
return /* @__PURE__ */ React.createElement(React.Fragment, null, hasNavSubLevels ? /* @__PURE__ */ React.createElement(
|
70
|
+
StyledButton,
|
71
|
+
{
|
72
|
+
size: "small",
|
73
|
+
onClick: handleState,
|
74
|
+
"aria-label": (expanded == null ? void 0 : expanded.expandAllNestedNavs) ? "collapse-nav" : "expand-nav"
|
75
|
+
},
|
76
|
+
(expanded == null ? void 0 : expanded.expandAllNestedNavs) ? /* @__PURE__ */ React.createElement(ExpandedIcon, null) : /* @__PURE__ */ React.createElement(CollapsedIcon, null)
|
77
|
+
) : null);
|
78
|
+
};
|
79
|
+
|
80
|
+
export { ExpandableNavigationAddon };
|
81
|
+
//# sourceMappingURL=ExpandableNavigation.esm.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"ExpandableNavigation.esm.js","sources":["../../src/ExpandableNavigation/ExpandableNavigation.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 React, { useEffect, useCallback, useState } from 'react';\nimport { useLocalStorageValue } from '@react-hookz/web';\nimport { Button, withStyles } from '@material-ui/core';\nimport ChevronRightIcon from '@material-ui/icons/ChevronRight';\nimport ExpandMoreIcon from '@material-ui/icons/ExpandMore';\n\nimport { useShadowRootElements } from '@backstage/plugin-techdocs-react';\n\nconst NESTED_LIST_TOGGLE = '.md-nav__item--nested .md-toggle';\n\nconst EXPANDABLE_NAVIGATION_LOCAL_STORAGE =\n '@backstage/techdocs-addons/nav-expanded';\n\nconst StyledButton = withStyles({\n root: {\n position: 'absolute',\n left: '220px',\n top: '19px',\n padding: 0,\n minWidth: 0,\n },\n})(Button);\n\nconst CollapsedIcon = withStyles({\n root: {\n height: '20px',\n width: '20px',\n },\n})(ChevronRightIcon);\n\nconst ExpandedIcon = withStyles({\n root: {\n height: '20px',\n width: '20px',\n },\n})(ExpandMoreIcon);\n\ntype expandableNavigationLocalStorage = {\n expandAllNestedNavs: boolean;\n};\n\n/**\n * Show expand/collapse navigation button next to site name in main\n * navigation menu if documentation site has nested navigation.\n */\nexport const ExpandableNavigationAddon = () => {\n const defaultValue = { expandAllNestedNavs: false };\n const { value: expanded, set: setExpanded } =\n useLocalStorageValue<expandableNavigationLocalStorage>(\n EXPANDABLE_NAVIGATION_LOCAL_STORAGE,\n { defaultValue },\n );\n const [hasNavSubLevels, setHasNavSubLevels] = useState<boolean>(false);\n\n const [...checkboxToggles] = useShadowRootElements<HTMLInputElement>([\n NESTED_LIST_TOGGLE,\n ]);\n\n const shouldToggle = useCallback(\n (item: HTMLInputElement) => {\n const isExpanded = item.checked;\n const shouldExpand = expanded?.expandAllNestedNavs;\n\n // Is collapsed but should expand\n if (shouldExpand && !isExpanded) {\n return true;\n }\n\n // Is expanded but should collapse\n if (!shouldExpand && isExpanded) {\n return true;\n }\n\n return false;\n },\n [expanded],\n );\n\n useEffect(() => {\n // There is no nested navs\n if (!checkboxToggles?.length) return;\n\n setHasNavSubLevels(true);\n checkboxToggles.forEach(item => {\n if (shouldToggle(item)) item.click();\n });\n }, [expanded, shouldToggle, checkboxToggles]);\n\n const handleState = () => {\n setExpanded(prevState => ({\n expandAllNestedNavs: !prevState?.expandAllNestedNavs,\n }));\n };\n\n return (\n <>\n {hasNavSubLevels ? (\n <StyledButton\n size=\"small\"\n onClick={handleState}\n aria-label={\n expanded?.expandAllNestedNavs ? 'collapse-nav' : 'expand-nav'\n }\n >\n {expanded?.expandAllNestedNavs ? <ExpandedIcon /> : <CollapsedIcon />}\n </StyledButton>\n ) : null}\n </>\n );\n};\n"],"names":[],"mappings":";;;;;;;AAwBA,MAAM,kBAAqB,GAAA,kCAAA,CAAA;AAE3B,MAAM,mCACJ,GAAA,yCAAA,CAAA;AAEF,MAAM,eAAe,UAAW,CAAA;AAAA,EAC9B,IAAM,EAAA;AAAA,IACJ,QAAU,EAAA,UAAA;AAAA,IACV,IAAM,EAAA,OAAA;AAAA,IACN,GAAK,EAAA,MAAA;AAAA,IACL,OAAS,EAAA,CAAA;AAAA,IACT,QAAU,EAAA,CAAA;AAAA,GACZ;AACF,CAAC,EAAE,MAAM,CAAA,CAAA;AAET,MAAM,gBAAgB,UAAW,CAAA;AAAA,EAC/B,IAAM,EAAA;AAAA,IACJ,MAAQ,EAAA,MAAA;AAAA,IACR,KAAO,EAAA,MAAA;AAAA,GACT;AACF,CAAC,EAAE,gBAAgB,CAAA,CAAA;AAEnB,MAAM,eAAe,UAAW,CAAA;AAAA,EAC9B,IAAM,EAAA;AAAA,IACJ,MAAQ,EAAA,MAAA;AAAA,IACR,KAAO,EAAA,MAAA;AAAA,GACT;AACF,CAAC,EAAE,cAAc,CAAA,CAAA;AAUV,MAAM,4BAA4B,MAAM;AAC7C,EAAM,MAAA,YAAA,GAAe,EAAE,mBAAA,EAAqB,KAAM,EAAA,CAAA;AAClD,EAAA,MAAM,EAAE,KAAA,EAAO,QAAU,EAAA,GAAA,EAAK,aAC5B,GAAA,oBAAA;AAAA,IACE,mCAAA;AAAA,IACA,EAAE,YAAa,EAAA;AAAA,GACjB,CAAA;AACF,EAAA,MAAM,CAAC,eAAA,EAAiB,kBAAkB,CAAA,GAAI,SAAkB,KAAK,CAAA,CAAA;AAErE,EAAA,MAAM,CAAC,GAAG,eAAe,CAAA,GAAI,qBAAwC,CAAA;AAAA,IACnE,kBAAA;AAAA,GACD,CAAA,CAAA;AAED,EAAA,MAAM,YAAe,GAAA,WAAA;AAAA,IACnB,CAAC,IAA2B,KAAA;AAC1B,MAAA,MAAM,aAAa,IAAK,CAAA,OAAA,CAAA;AACxB,MAAA,MAAM,eAAe,QAAU,IAAA,IAAA,GAAA,KAAA,CAAA,GAAA,QAAA,CAAA,mBAAA,CAAA;AAG/B,MAAI,IAAA,YAAA,IAAgB,CAAC,UAAY,EAAA;AAC/B,QAAO,OAAA,IAAA,CAAA;AAAA,OACT;AAGA,MAAI,IAAA,CAAC,gBAAgB,UAAY,EAAA;AAC/B,QAAO,OAAA,IAAA,CAAA;AAAA,OACT;AAEA,MAAO,OAAA,KAAA,CAAA;AAAA,KACT;AAAA,IACA,CAAC,QAAQ,CAAA;AAAA,GACX,CAAA;AAEA,EAAA,SAAA,CAAU,MAAM;AAEd,IAAA,IAAI,EAAC,eAAiB,IAAA,IAAA,GAAA,KAAA,CAAA,GAAA,eAAA,CAAA,MAAA,CAAA;AAAQ,MAAA,OAAA;AAE9B,IAAA,kBAAA,CAAmB,IAAI,CAAA,CAAA;AACvB,IAAA,eAAA,CAAgB,QAAQ,CAAQ,IAAA,KAAA;AAC9B,MAAA,IAAI,aAAa,IAAI,CAAA;AAAG,QAAA,IAAA,CAAK,KAAM,EAAA,CAAA;AAAA,KACpC,CAAA,CAAA;AAAA,GACA,EAAA,CAAC,QAAU,EAAA,YAAA,EAAc,eAAe,CAAC,CAAA,CAAA;AAE5C,EAAA,MAAM,cAAc,MAAM;AACxB,IAAA,WAAA,CAAY,CAAc,SAAA,MAAA;AAAA,MACxB,mBAAA,EAAqB,EAAC,SAAW,IAAA,IAAA,GAAA,KAAA,CAAA,GAAA,SAAA,CAAA,mBAAA,CAAA;AAAA,KACjC,CAAA,CAAA,CAAA;AAAA,GACJ,CAAA;AAEA,EAAA,iEAEK,eACC,mBAAA,KAAA,CAAA,aAAA;AAAA,IAAC,YAAA;AAAA,IAAA;AAAA,MACC,IAAK,EAAA,OAAA;AAAA,MACL,OAAS,EAAA,WAAA;AAAA,MACT,YAAA,EAAA,CACE,QAAU,IAAA,IAAA,GAAA,KAAA,CAAA,GAAA,QAAA,CAAA,mBAAA,IAAsB,cAAiB,GAAA,YAAA;AAAA,KAAA;AAAA,IAAA,CAGlD,qCAAU,mBAAsB,oBAAA,KAAA,CAAA,aAAA,CAAC,YAAa,EAAA,IAAA,CAAA,uCAAM,aAAc,EAAA,IAAA,CAAA;AAAA,MAEnE,IACN,CAAA,CAAA;AAEJ;;;;"}
|
@@ -0,0 +1,60 @@
|
|
1
|
+
import { useEffect } from 'react';
|
2
|
+
import { useShadowRootElements } from '@backstage/plugin-techdocs-react';
|
3
|
+
import PhotoSwipeLightbox from 'photoswipe/lightbox';
|
4
|
+
import PhotoSwipe from 'photoswipe';
|
5
|
+
import 'photoswipe/style.css';
|
6
|
+
|
7
|
+
const LightBoxAddon = () => {
|
8
|
+
const images = useShadowRootElements(["img"]);
|
9
|
+
useEffect(() => {
|
10
|
+
let dataSourceImages = null;
|
11
|
+
let lightbox = new PhotoSwipeLightbox({
|
12
|
+
pswpModule: PhotoSwipe,
|
13
|
+
initialZoomLevel: 1,
|
14
|
+
secondaryZoomLevel: (zoomLevelObject) => {
|
15
|
+
const imageWidth = zoomLevelObject.elementSize.x;
|
16
|
+
const imageHeight = zoomLevelObject.elementSize.y;
|
17
|
+
const viewportWidth = zoomLevelObject.panAreaSize.x;
|
18
|
+
const viewportHeight = zoomLevelObject.panAreaSize.y;
|
19
|
+
const widthScale = viewportWidth / imageWidth;
|
20
|
+
const heightScale = viewportHeight / imageHeight;
|
21
|
+
const scaleFactor = Math.min(widthScale, heightScale);
|
22
|
+
return scaleFactor;
|
23
|
+
},
|
24
|
+
wheelToZoom: true,
|
25
|
+
arrowPrevSVG: '<svg class="MuiSvgIcon-root MuiSvgIcon-fontSizeLarge css-c1sh5i" focusable="false" aria-hidden="true" viewBox="0 0 24 24" data-testid="ArrowBackIosIcon" aria-label="fontSize large"><path d="M11.67 3.87 9.9 2.1 0 12l9.9 9.9 1.77-1.77L3.54 12z"></path></svg>',
|
26
|
+
arrowNextSVG: '<svg class="MuiSvgIcon-root MuiSvgIcon-fontSizeLarge css-c1sh5i" focusable="false" aria-hidden="true" viewBox="0 0 24 24" data-testid="ArrowForwardIosIcon" aria-label="fontSize large"><path d="M6.23 20.23 8 22l10-10L8 2 6.23 3.77 14.46 12z"></path></svg>',
|
27
|
+
closeSVG: '<svg class="MuiSvgIcon-root MuiSvgIcon-fontSizeLarge css-c1sh5i" focusable="false" aria-hidden="true" viewBox="0 0 24 24" data-testid="CloseIcon" aria-label="fontSize large"><path d="M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"></path></svg>',
|
28
|
+
zoomSVG: `<svg class="MuiSvgIcon-root MuiSvgIcon-fontSizeLarge css-c1sh5i" focusable="false" aria-hidden="true" viewBox="0 0 24 24" data-testid="ZoomIcon" aria-label="fontSize large">
|
29
|
+
<path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"></path><path d="M12 10h-2v2H9v-2H7V9h2V7h1v2h2v1z" id="photoswipe-zoom-icon-zoomin-path"></path><path d="M12 10H 7 V 9 H 12 Z" id="photoswipe-zoom-icon-zoomout-path">
|
30
|
+
</svg>`
|
31
|
+
});
|
32
|
+
images.forEach((image, index) => {
|
33
|
+
image.onclick = () => {
|
34
|
+
if (dataSourceImages === null) {
|
35
|
+
dataSourceImages = images.map((dataSourceImage) => {
|
36
|
+
return {
|
37
|
+
element: dataSourceImage,
|
38
|
+
src: dataSourceImage.src,
|
39
|
+
msrc: dataSourceImage.src,
|
40
|
+
alt: dataSourceImage.alt,
|
41
|
+
width: dataSourceImage.clientWidth,
|
42
|
+
height: dataSourceImage.clientHeight
|
43
|
+
};
|
44
|
+
});
|
45
|
+
}
|
46
|
+
lightbox.loadAndOpen(index, dataSourceImages);
|
47
|
+
return false;
|
48
|
+
};
|
49
|
+
});
|
50
|
+
lightbox.init();
|
51
|
+
return () => {
|
52
|
+
lightbox.destroy();
|
53
|
+
lightbox = null;
|
54
|
+
};
|
55
|
+
}, [images]);
|
56
|
+
return null;
|
57
|
+
};
|
58
|
+
|
59
|
+
export { LightBoxAddon };
|
60
|
+
//# sourceMappingURL=LightBox.esm.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"LightBox.esm.js","sources":["../../src/LigthBox/LightBox.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 { useEffect } from 'react';\nimport { useShadowRootElements } from '@backstage/plugin-techdocs-react';\n// @ts-ignore\nimport PhotoSwipeLightbox, { DataSource, ZoomLevel } from 'photoswipe/lightbox';\nimport PhotoSwipe from 'photoswipe';\nimport 'photoswipe/style.css';\nimport './lightbox.css';\n\nexport const LightBoxAddon = () => {\n const images = useShadowRootElements<HTMLImageElement>(['img']);\n\n useEffect(() => {\n let dataSourceImages: DataSource | null = null;\n\n let lightbox = new PhotoSwipeLightbox({\n pswpModule: PhotoSwipe,\n initialZoomLevel: 1,\n secondaryZoomLevel: (zoomLevelObject: ZoomLevel) => {\n // photoswipe/lightbox won't zoom the image further then the given width and height.\n // therefore we need to calculate the zoom factor needed to fit the complete image in the viewport manually.\n const imageWidth = zoomLevelObject.elementSize.x;\n const imageHeight = zoomLevelObject.elementSize.y;\n const viewportWidth = zoomLevelObject.panAreaSize.x;\n const viewportHeight = zoomLevelObject.panAreaSize.y;\n\n const widthScale = viewportWidth / imageWidth;\n const heightScale = viewportHeight / imageHeight;\n\n const scaleFactor = Math.min(widthScale, heightScale);\n return scaleFactor;\n },\n wheelToZoom: true,\n arrowPrevSVG:\n '<svg class=\"MuiSvgIcon-root MuiSvgIcon-fontSizeLarge css-c1sh5i\" focusable=\"false\" aria-hidden=\"true\" viewBox=\"0 0 24 24\" data-testid=\"ArrowBackIosIcon\" aria-label=\"fontSize large\"><path d=\"M11.67 3.87 9.9 2.1 0 12l9.9 9.9 1.77-1.77L3.54 12z\"></path></svg>',\n arrowNextSVG:\n '<svg class=\"MuiSvgIcon-root MuiSvgIcon-fontSizeLarge css-c1sh5i\" focusable=\"false\" aria-hidden=\"true\" viewBox=\"0 0 24 24\" data-testid=\"ArrowForwardIosIcon\" aria-label=\"fontSize large\"><path d=\"M6.23 20.23 8 22l10-10L8 2 6.23 3.77 14.46 12z\"></path></svg>',\n closeSVG:\n '<svg class=\"MuiSvgIcon-root MuiSvgIcon-fontSizeLarge css-c1sh5i\" focusable=\"false\" aria-hidden=\"true\" viewBox=\"0 0 24 24\" data-testid=\"CloseIcon\" aria-label=\"fontSize large\"><path d=\"M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z\"></path></svg>',\n zoomSVG: `<svg class=\"MuiSvgIcon-root MuiSvgIcon-fontSizeLarge css-c1sh5i\" focusable=\"false\" aria-hidden=\"true\" viewBox=\"0 0 24 24\" data-testid=\"ZoomIcon\" aria-label=\"fontSize large\">\n <path d=\"M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z\"></path><path d=\"M12 10h-2v2H9v-2H7V9h2V7h1v2h2v1z\" id=\"photoswipe-zoom-icon-zoomin-path\"></path><path d=\"M12 10H 7 V 9 H 12 Z\" id=\"photoswipe-zoom-icon-zoomout-path\">\n </svg>`,\n });\n\n images.forEach((image, index) => {\n image.onclick = () => {\n if (dataSourceImages === null) {\n dataSourceImages = images.map(dataSourceImage => {\n return {\n element: dataSourceImage,\n src: dataSourceImage.src,\n msrc: dataSourceImage.src,\n alt: dataSourceImage.alt,\n width: dataSourceImage.clientWidth,\n height: dataSourceImage.clientHeight,\n };\n });\n }\n lightbox.loadAndOpen(index, dataSourceImages);\n return false;\n };\n });\n lightbox.init();\n\n return () => {\n lightbox.destroy();\n lightbox = null;\n };\n }, [images]);\n\n return null;\n};\n"],"names":[],"mappings":";;;;;;AAwBO,MAAM,gBAAgB,MAAM;AACjC,EAAA,MAAM,MAAS,GAAA,qBAAA,CAAwC,CAAC,KAAK,CAAC,CAAA,CAAA;AAE9D,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,gBAAsC,GAAA,IAAA,CAAA;AAE1C,IAAI,IAAA,QAAA,GAAW,IAAI,kBAAmB,CAAA;AAAA,MACpC,UAAY,EAAA,UAAA;AAAA,MACZ,gBAAkB,EAAA,CAAA;AAAA,MAClB,kBAAA,EAAoB,CAAC,eAA+B,KAAA;AAGlD,QAAM,MAAA,UAAA,GAAa,gBAAgB,WAAY,CAAA,CAAA,CAAA;AAC/C,QAAM,MAAA,WAAA,GAAc,gBAAgB,WAAY,CAAA,CAAA,CAAA;AAChD,QAAM,MAAA,aAAA,GAAgB,gBAAgB,WAAY,CAAA,CAAA,CAAA;AAClD,QAAM,MAAA,cAAA,GAAiB,gBAAgB,WAAY,CAAA,CAAA,CAAA;AAEnD,QAAA,MAAM,aAAa,aAAgB,GAAA,UAAA,CAAA;AACnC,QAAA,MAAM,cAAc,cAAiB,GAAA,WAAA,CAAA;AAErC,QAAA,MAAM,WAAc,GAAA,IAAA,CAAK,GAAI,CAAA,UAAA,EAAY,WAAW,CAAA,CAAA;AACpD,QAAO,OAAA,WAAA,CAAA;AAAA,OACT;AAAA,MACA,WAAa,EAAA,IAAA;AAAA,MACb,YACE,EAAA,mQAAA;AAAA,MACF,YACE,EAAA,iQAAA;AAAA,MACF,QACE,EAAA,8SAAA;AAAA,MACF,OAAS,EAAA,CAAA;AAAA;AAAA,cAAA,CAAA;AAAA,KAGV,CAAA,CAAA;AAED,IAAO,MAAA,CAAA,OAAA,CAAQ,CAAC,KAAA,EAAO,KAAU,KAAA;AAC/B,MAAA,KAAA,CAAM,UAAU,MAAM;AACpB,QAAA,IAAI,qBAAqB,IAAM,EAAA;AAC7B,UAAmB,gBAAA,GAAA,MAAA,CAAO,IAAI,CAAmB,eAAA,KAAA;AAC/C,YAAO,OAAA;AAAA,cACL,OAAS,EAAA,eAAA;AAAA,cACT,KAAK,eAAgB,CAAA,GAAA;AAAA,cACrB,MAAM,eAAgB,CAAA,GAAA;AAAA,cACtB,KAAK,eAAgB,CAAA,GAAA;AAAA,cACrB,OAAO,eAAgB,CAAA,WAAA;AAAA,cACvB,QAAQ,eAAgB,CAAA,YAAA;AAAA,aAC1B,CAAA;AAAA,WACD,CAAA,CAAA;AAAA,SACH;AACA,QAAS,QAAA,CAAA,WAAA,CAAY,OAAO,gBAAgB,CAAA,CAAA;AAC5C,QAAO,OAAA,KAAA,CAAA;AAAA,OACT,CAAA;AAAA,KACD,CAAA,CAAA;AACD,IAAA,QAAA,CAAS,IAAK,EAAA,CAAA;AAEd,IAAA,OAAO,MAAM;AACX,MAAA,QAAA,CAAS,OAAQ,EAAA,CAAA;AACjB,MAAW,QAAA,GAAA,IAAA,CAAA;AAAA,KACb,CAAA;AAAA,GACF,EAAG,CAAC,MAAM,CAAC,CAAA,CAAA;AAEX,EAAO,OAAA,IAAA,CAAA;AACT;;;;"}
|
@@ -0,0 +1,49 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import { makeStyles } from '@material-ui/core';
|
3
|
+
import BugReportIcon from '@material-ui/icons/BugReport';
|
4
|
+
import { Link, GitHubIcon } from '@backstage/core-components';
|
5
|
+
|
6
|
+
const useStyles = makeStyles((theme) => ({
|
7
|
+
root: {
|
8
|
+
display: "grid",
|
9
|
+
gridGap: theme.spacing(1),
|
10
|
+
gridAutoFlow: "column",
|
11
|
+
justifyContent: "center",
|
12
|
+
alignItems: "center",
|
13
|
+
color: theme.palette.common.black,
|
14
|
+
fontSize: theme.typography.button.fontSize
|
15
|
+
}
|
16
|
+
}));
|
17
|
+
const getIcon = ({ type }) => {
|
18
|
+
if (type === "github") {
|
19
|
+
return GitHubIcon;
|
20
|
+
}
|
21
|
+
return BugReportIcon;
|
22
|
+
};
|
23
|
+
const getName = ({ type }) => {
|
24
|
+
if (type === "github") {
|
25
|
+
return "Github";
|
26
|
+
}
|
27
|
+
return "Gitlab";
|
28
|
+
};
|
29
|
+
const getUrl = (repository, template) => {
|
30
|
+
const { title, body } = template;
|
31
|
+
const encodedTitle = encodeURIComponent(title);
|
32
|
+
const encodedBody = encodeURIComponent(body);
|
33
|
+
const { protocol, resource, owner, name, type } = repository;
|
34
|
+
const url = `${protocol}://${resource}/${owner}/${name}`;
|
35
|
+
const encodedUrl = encodeURI(url);
|
36
|
+
if (type === "github") {
|
37
|
+
return `${encodedUrl}/issues/new?title=${encodedTitle}&body=${encodedBody}`;
|
38
|
+
}
|
39
|
+
return `${encodedUrl}/issues/new?issue[title]=${encodedTitle}&issue[description]=${encodedBody}`;
|
40
|
+
};
|
41
|
+
const IssueLink = ({ template, repository }) => {
|
42
|
+
const classes = useStyles();
|
43
|
+
const Icon = getIcon(repository);
|
44
|
+
const url = getUrl(repository, template);
|
45
|
+
return /* @__PURE__ */ React.createElement(Link, { className: classes.root, to: url, target: "_blank" }, /* @__PURE__ */ React.createElement(Icon, null), " Open new ", getName(repository), " issue");
|
46
|
+
};
|
47
|
+
|
48
|
+
export { IssueLink };
|
49
|
+
//# sourceMappingURL=IssueLink.esm.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"IssueLink.esm.js","sources":["../../src/ReportIssue/IssueLink.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 React from 'react';\n\nimport { makeStyles } from '@material-ui/core';\nimport BugReportIcon from '@material-ui/icons/BugReport';\n\nimport { Link, GitHubIcon } from '@backstage/core-components';\n\nimport { ReportIssueTemplate, Repository } from './types';\n\nconst useStyles = makeStyles(theme => ({\n root: {\n display: 'grid',\n gridGap: theme.spacing(1),\n gridAutoFlow: 'column',\n justifyContent: 'center',\n alignItems: 'center',\n color: theme.palette.common.black,\n fontSize: theme.typography.button.fontSize,\n },\n}));\n\ntype IssueLinkProps = {\n template: ReportIssueTemplate;\n repository: Repository;\n};\n\nconst getIcon = ({ type }: Repository) => {\n if (type === 'github') {\n return GitHubIcon;\n }\n return BugReportIcon;\n};\n\nconst getName = ({ type }: Repository) => {\n if (type === 'github') {\n return 'Github';\n }\n return 'Gitlab';\n};\n\nconst getUrl = (repository: Repository, template: ReportIssueTemplate) => {\n const { title, body } = template;\n const encodedTitle = encodeURIComponent(title);\n const encodedBody = encodeURIComponent(body);\n const { protocol, resource, owner, name, type } = repository;\n\n const url = `${protocol}://${resource}/${owner}/${name}`;\n const encodedUrl = encodeURI(url);\n if (type === 'github') {\n return `${encodedUrl}/issues/new?title=${encodedTitle}&body=${encodedBody}`;\n }\n return `${encodedUrl}/issues/new?issue[title]=${encodedTitle}&issue[description]=${encodedBody}`;\n};\n\nexport const IssueLink = ({ template, repository }: IssueLinkProps) => {\n const classes = useStyles();\n\n const Icon = getIcon(repository);\n const url = getUrl(repository, template);\n\n return (\n <Link className={classes.root} to={url} target=\"_blank\">\n <Icon /> Open new {getName(repository)} issue\n </Link>\n );\n};\n"],"names":[],"mappings":";;;;;AAyBA,MAAM,SAAA,GAAY,WAAW,CAAU,KAAA,MAAA;AAAA,EACrC,IAAM,EAAA;AAAA,IACJ,OAAS,EAAA,MAAA;AAAA,IACT,OAAA,EAAS,KAAM,CAAA,OAAA,CAAQ,CAAC,CAAA;AAAA,IACxB,YAAc,EAAA,QAAA;AAAA,IACd,cAAgB,EAAA,QAAA;AAAA,IAChB,UAAY,EAAA,QAAA;AAAA,IACZ,KAAA,EAAO,KAAM,CAAA,OAAA,CAAQ,MAAO,CAAA,KAAA;AAAA,IAC5B,QAAA,EAAU,KAAM,CAAA,UAAA,CAAW,MAAO,CAAA,QAAA;AAAA,GACpC;AACF,CAAE,CAAA,CAAA,CAAA;AAOF,MAAM,OAAU,GAAA,CAAC,EAAE,IAAA,EAAuB,KAAA;AACxC,EAAA,IAAI,SAAS,QAAU,EAAA;AACrB,IAAO,OAAA,UAAA,CAAA;AAAA,GACT;AACA,EAAO,OAAA,aAAA,CAAA;AACT,CAAA,CAAA;AAEA,MAAM,OAAU,GAAA,CAAC,EAAE,IAAA,EAAuB,KAAA;AACxC,EAAA,IAAI,SAAS,QAAU,EAAA;AACrB,IAAO,OAAA,QAAA,CAAA;AAAA,GACT;AACA,EAAO,OAAA,QAAA,CAAA;AACT,CAAA,CAAA;AAEA,MAAM,MAAA,GAAS,CAAC,UAAA,EAAwB,QAAkC,KAAA;AACxE,EAAM,MAAA,EAAE,KAAO,EAAA,IAAA,EAAS,GAAA,QAAA,CAAA;AACxB,EAAM,MAAA,YAAA,GAAe,mBAAmB,KAAK,CAAA,CAAA;AAC7C,EAAM,MAAA,WAAA,GAAc,mBAAmB,IAAI,CAAA,CAAA;AAC3C,EAAA,MAAM,EAAE,QAAU,EAAA,QAAA,EAAU,KAAO,EAAA,IAAA,EAAM,MAAS,GAAA,UAAA,CAAA;AAElD,EAAM,MAAA,GAAA,GAAM,GAAG,QAAQ,CAAA,GAAA,EAAM,QAAQ,CAAI,CAAA,EAAA,KAAK,IAAI,IAAI,CAAA,CAAA,CAAA;AACtD,EAAM,MAAA,UAAA,GAAa,UAAU,GAAG,CAAA,CAAA;AAChC,EAAA,IAAI,SAAS,QAAU,EAAA;AACrB,IAAA,OAAO,CAAG,EAAA,UAAU,CAAqB,kBAAA,EAAA,YAAY,SAAS,WAAW,CAAA,CAAA,CAAA;AAAA,GAC3E;AACA,EAAA,OAAO,CAAG,EAAA,UAAU,CAA4B,yBAAA,EAAA,YAAY,uBAAuB,WAAW,CAAA,CAAA,CAAA;AAChG,CAAA,CAAA;AAEO,MAAM,SAAY,GAAA,CAAC,EAAE,QAAA,EAAU,YAAiC,KAAA;AACrE,EAAA,MAAM,UAAU,SAAU,EAAA,CAAA;AAE1B,EAAM,MAAA,IAAA,GAAO,QAAQ,UAAU,CAAA,CAAA;AAC/B,EAAM,MAAA,GAAA,GAAM,MAAO,CAAA,UAAA,EAAY,QAAQ,CAAA,CAAA;AAEvC,EAAA,2CACG,IAAK,EAAA,EAAA,SAAA,EAAW,OAAQ,CAAA,IAAA,EAAM,IAAI,GAAK,EAAA,MAAA,EAAO,QAC7C,EAAA,kBAAA,KAAA,CAAA,aAAA,CAAC,UAAK,CAAE,EAAA,YAAA,EAAW,OAAQ,CAAA,UAAU,GAAE,QACzC,CAAA,CAAA;AAEJ;;;;"}
|
@@ -0,0 +1,84 @@
|
|
1
|
+
import React, { useState, useEffect } from 'react';
|
2
|
+
import { makeStyles, Portal, Paper } from '@material-ui/core';
|
3
|
+
import { useGitRepository, useGitTemplate } from './hooks.esm.js';
|
4
|
+
import { PAGE_MAIN_CONTENT_SELECTOR, PAGE_FEEDBACK_LINK_SELECTOR, ADDON_FEEDBACK_CONTAINER_SELECTOR, ADDON_FEEDBACK_CONTAINER_ID } from './constants.esm.js';
|
5
|
+
import { IssueLink } from './IssueLink.esm.js';
|
6
|
+
import { useShadowRootSelection, useShadowRootElements } from '@backstage/plugin-techdocs-react';
|
7
|
+
|
8
|
+
const useStyles = makeStyles((theme) => ({
|
9
|
+
root: {
|
10
|
+
transform: "translate(-100%, -100%)",
|
11
|
+
position: "absolute",
|
12
|
+
padding: theme.spacing(1),
|
13
|
+
zIndex: theme.zIndex.tooltip,
|
14
|
+
background: theme.palette.common.white
|
15
|
+
}
|
16
|
+
}));
|
17
|
+
const ReportIssueAddon = ({
|
18
|
+
debounceTime = 500,
|
19
|
+
templateBuilder: buildTemplate
|
20
|
+
}) => {
|
21
|
+
const classes = useStyles();
|
22
|
+
const [style, setStyle] = useState();
|
23
|
+
const repository = useGitRepository();
|
24
|
+
const defaultTemplate = useGitTemplate(debounceTime);
|
25
|
+
const selection = useShadowRootSelection(debounceTime);
|
26
|
+
const [mainContent, feedbackLink] = useShadowRootElements([
|
27
|
+
PAGE_MAIN_CONTENT_SELECTOR,
|
28
|
+
PAGE_FEEDBACK_LINK_SELECTOR
|
29
|
+
]);
|
30
|
+
let [feedbackContainer] = useShadowRootElements([
|
31
|
+
ADDON_FEEDBACK_CONTAINER_SELECTOR
|
32
|
+
]);
|
33
|
+
if (feedbackLink) {
|
34
|
+
feedbackLink.style.display = "none";
|
35
|
+
}
|
36
|
+
useEffect(() => {
|
37
|
+
if (
|
38
|
+
// todo(backstage/techdocs-core) handle non-repo rendering
|
39
|
+
!repository || !selection || !selection.containsNode(mainContent, true) || (selection == null ? void 0 : selection.containsNode(feedbackContainer, true))
|
40
|
+
) {
|
41
|
+
return;
|
42
|
+
}
|
43
|
+
const mainContentPosition = mainContent.getBoundingClientRect();
|
44
|
+
const selectionPosition = selection.getRangeAt(0).getBoundingClientRect();
|
45
|
+
const distanceFromTop = selectionPosition.top - mainContentPosition.top;
|
46
|
+
const minDistanceFromTop = 50;
|
47
|
+
let top = distanceFromTop < minDistanceFromTop ? 101 : distanceFromTop - 16;
|
48
|
+
if (mainContentPosition.top < 0) {
|
49
|
+
const absMainContentTop = Math.abs(mainContentPosition.top);
|
50
|
+
if (distanceFromTop - absMainContentTop < minDistanceFromTop) {
|
51
|
+
top += 89;
|
52
|
+
}
|
53
|
+
}
|
54
|
+
setStyle({
|
55
|
+
top: `${top}px`,
|
56
|
+
left: `${selectionPosition.left + selectionPosition.width / 2}px`
|
57
|
+
});
|
58
|
+
}, [selection, mainContent, feedbackContainer]);
|
59
|
+
if (!selection || !repository || !["github", "gitlab"].includes(repository.type))
|
60
|
+
return null;
|
61
|
+
if (!feedbackContainer) {
|
62
|
+
feedbackContainer = document.createElement("div");
|
63
|
+
feedbackContainer.setAttribute("id", ADDON_FEEDBACK_CONTAINER_ID);
|
64
|
+
mainContent.prepend(feedbackContainer);
|
65
|
+
}
|
66
|
+
return /* @__PURE__ */ React.createElement(Portal, { container: feedbackContainer }, /* @__PURE__ */ React.createElement(
|
67
|
+
Paper,
|
68
|
+
{
|
69
|
+
"data-testid": "report-issue-addon",
|
70
|
+
className: classes.root,
|
71
|
+
style
|
72
|
+
},
|
73
|
+
/* @__PURE__ */ React.createElement(
|
74
|
+
IssueLink,
|
75
|
+
{
|
76
|
+
repository,
|
77
|
+
template: buildTemplate ? buildTemplate({ selection }) : defaultTemplate
|
78
|
+
}
|
79
|
+
)
|
80
|
+
));
|
81
|
+
};
|
82
|
+
|
83
|
+
export { ReportIssueAddon };
|
84
|
+
//# sourceMappingURL=ReportIssue.esm.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"ReportIssue.esm.js","sources":["../../src/ReportIssue/ReportIssue.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 React, { useState, useEffect } from 'react';\n\nimport { makeStyles, Portal, Paper } from '@material-ui/core';\n\nimport { useGitTemplate, useGitRepository } from './hooks';\nimport { ReportIssueTemplateBuilder } from './types';\nimport {\n PAGE_MAIN_CONTENT_SELECTOR,\n PAGE_FEEDBACK_LINK_SELECTOR,\n ADDON_FEEDBACK_CONTAINER_ID,\n ADDON_FEEDBACK_CONTAINER_SELECTOR,\n} from './constants';\nimport { IssueLink } from './IssueLink';\n\nimport {\n useShadowRootElements,\n useShadowRootSelection,\n} from '@backstage/plugin-techdocs-react';\n\nconst useStyles = makeStyles(theme => ({\n root: {\n transform: 'translate(-100%, -100%)',\n position: 'absolute',\n padding: theme.spacing(1),\n zIndex: theme.zIndex.tooltip,\n background: theme.palette.common.white,\n },\n}));\n\ntype Style = {\n top: string;\n left: string;\n};\n\n/**\n * Props customizing the <ReportIssue /> Addon.\n *\n * @public\n */\nexport type ReportIssueProps = {\n /**\n * Number of milliseconds after a user highlights some text before the report\n * issue link appears above the highlighted text. Defaults to 500ms.\n */\n debounceTime?: number;\n\n /**\n * An optional function defining how a custom issue title and body should be\n * constructed, given some selected text.\n */\n templateBuilder?: ReportIssueTemplateBuilder;\n};\n\n/**\n * Show report issue button when text is highlighted\n */\nexport const ReportIssueAddon = ({\n debounceTime = 500,\n templateBuilder: buildTemplate,\n}: ReportIssueProps) => {\n const classes = useStyles();\n const [style, setStyle] = useState<Style>();\n\n const repository = useGitRepository();\n\n const defaultTemplate = useGitTemplate(debounceTime);\n\n const selection = useShadowRootSelection(debounceTime);\n\n const [mainContent, feedbackLink] = useShadowRootElements([\n PAGE_MAIN_CONTENT_SELECTOR,\n PAGE_FEEDBACK_LINK_SELECTOR,\n ]);\n\n let [feedbackContainer] = useShadowRootElements([\n ADDON_FEEDBACK_CONTAINER_SELECTOR,\n ]);\n\n if (feedbackLink) {\n feedbackLink.style.display = 'none';\n }\n\n // calculates the position of the selected text to be able to set the position of the addon\n useEffect(() => {\n if (\n // todo(backstage/techdocs-core) handle non-repo rendering\n !repository ||\n !selection ||\n !selection.containsNode(mainContent!, true) ||\n selection?.containsNode(feedbackContainer!, true)\n ) {\n return;\n }\n\n const mainContentPosition = mainContent!.getBoundingClientRect();\n const selectionPosition = selection.getRangeAt(0).getBoundingClientRect();\n\n // Calculating the distance between the selection's top and the main content's top\n const distanceFromTop = selectionPosition.top - mainContentPosition.top;\n const minDistanceFromTop = 50;\n\n // Defining a base value for 'top'\n let top = distanceFromTop < minDistanceFromTop ? 101 : distanceFromTop - 16;\n\n // Checking if the main content is off-screen towards the top\n if (mainContentPosition.top < 0) {\n const absMainContentTop = Math.abs(mainContentPosition.top);\n\n // Adjusting 'top' if the selection is close to the top edge and the main content is off-screen\n if (distanceFromTop - absMainContentTop < minDistanceFromTop) {\n top += 89;\n }\n }\n\n setStyle({\n top: `${top}px`,\n left: `${selectionPosition.left + selectionPosition.width / 2}px`,\n });\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [selection, mainContent, feedbackContainer]);\n\n if (\n !selection ||\n !repository ||\n !['github', 'gitlab'].includes(repository.type)\n )\n return null;\n\n if (!feedbackContainer) {\n feedbackContainer = document.createElement('div');\n feedbackContainer.setAttribute('id', ADDON_FEEDBACK_CONTAINER_ID);\n mainContent!.prepend(feedbackContainer);\n }\n\n return (\n <Portal container={feedbackContainer}>\n <Paper\n data-testid=\"report-issue-addon\"\n className={classes.root}\n style={style}\n >\n <IssueLink\n repository={repository}\n template={\n buildTemplate ? buildTemplate({ selection }) : defaultTemplate\n }\n />\n </Paper>\n </Portal>\n );\n};\n"],"names":[],"mappings":"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"}
|
@@ -0,0 +1,8 @@
|
|
1
|
+
const ADDON_FEEDBACK_CONTAINER_ID = "techdocs-report-issue";
|
2
|
+
const ADDON_FEEDBACK_CONTAINER_SELECTOR = `#${ADDON_FEEDBACK_CONTAINER_ID}`;
|
3
|
+
const PAGE_EDIT_LINK_SELECTOR = '[title^="Edit this page"]';
|
4
|
+
const PAGE_FEEDBACK_LINK_SELECTOR = '[title^="Leave feedback for"]';
|
5
|
+
const PAGE_MAIN_CONTENT_SELECTOR = '[data-md-component="main"] .md-content';
|
6
|
+
|
7
|
+
export { ADDON_FEEDBACK_CONTAINER_ID, ADDON_FEEDBACK_CONTAINER_SELECTOR, PAGE_EDIT_LINK_SELECTOR, PAGE_FEEDBACK_LINK_SELECTOR, PAGE_MAIN_CONTENT_SELECTOR };
|
8
|
+
//# sourceMappingURL=constants.esm.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"constants.esm.js","sources":["../../src/ReportIssue/constants.ts"],"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\nexport const ADDON_FEEDBACK_CONTAINER_ID = 'techdocs-report-issue';\nexport const ADDON_FEEDBACK_CONTAINER_SELECTOR = `#${ADDON_FEEDBACK_CONTAINER_ID}`;\nexport const PAGE_EDIT_LINK_SELECTOR = '[title^=\"Edit this page\"]';\nexport const PAGE_FEEDBACK_LINK_SELECTOR = '[title^=\"Leave feedback for\"]';\nexport const PAGE_MAIN_CONTENT_SELECTOR =\n '[data-md-component=\"main\"] .md-content';\n"],"names":[],"mappings":"AAgBO,MAAM,2BAA8B,GAAA,wBAAA;AAC9B,MAAA,iCAAA,GAAoC,IAAI,2BAA2B,CAAA,EAAA;AACzE,MAAM,uBAA0B,GAAA,4BAAA;AAChC,MAAM,2BAA8B,GAAA,gCAAA;AACpC,MAAM,0BACX,GAAA;;;;"}
|
@@ -0,0 +1,79 @@
|
|
1
|
+
import parseGitUrl from 'git-url-parse';
|
2
|
+
import { useApi, configApiRef } from '@backstage/core-plugin-api';
|
3
|
+
import { replaceGithubUrlType, replaceGitLabUrlType } from '@backstage/integration';
|
4
|
+
import { scmIntegrationsApiRef } from '@backstage/integration-react';
|
5
|
+
import { useShadowRootSelection, useShadowRootElements } from '@backstage/plugin-techdocs-react';
|
6
|
+
import { PAGE_EDIT_LINK_SELECTOR } from './constants.esm.js';
|
7
|
+
|
8
|
+
const resolveBlobUrl = (url, type) => {
|
9
|
+
if (type === "github") {
|
10
|
+
return replaceGithubUrlType(url, "blob");
|
11
|
+
} else if (type === "gitlab") {
|
12
|
+
return replaceGitLabUrlType(url, "blob");
|
13
|
+
}
|
14
|
+
console.error(
|
15
|
+
`Invalid SCM type ${type} found in ReportIssue addon for URL ${url}!`
|
16
|
+
);
|
17
|
+
return url;
|
18
|
+
};
|
19
|
+
const getTitle = (selection) => {
|
20
|
+
const text = selection.toString().substring(0, 70);
|
21
|
+
const ellipsis = text.length === 70 ? "..." : "";
|
22
|
+
return `Documentation feedback: ${text}${ellipsis}`;
|
23
|
+
};
|
24
|
+
const getBody = (selection, markdownUrl, appTitle) => {
|
25
|
+
const title = "## Documentation Feedback \u{1F4DD}";
|
26
|
+
const subheading = "#### The highlighted text:";
|
27
|
+
const commentHeading = "#### The comment on the text:";
|
28
|
+
const commentPlaceholder = "_>replace this line with your comment<_";
|
29
|
+
const highlightedTextAsQuote = selection.toString().trim().split("\n").map((line) => `> ${line.trim()}`).join("\n");
|
30
|
+
const facts = [
|
31
|
+
`${appTitle} URL: <${window.location.href}>
|
32
|
+
Markdown URL: <${markdownUrl}>`
|
33
|
+
];
|
34
|
+
return `${title}
|
35
|
+
|
36
|
+
${subheading}
|
37
|
+
|
38
|
+
${highlightedTextAsQuote}
|
39
|
+
|
40
|
+
${commentHeading}
|
41
|
+
${commentPlaceholder}
|
42
|
+
|
43
|
+
___
|
44
|
+
${facts}`;
|
45
|
+
};
|
46
|
+
const useGitTemplate = (debounceTime) => {
|
47
|
+
var _a, _b;
|
48
|
+
const initialTemplate = { title: "", body: "" };
|
49
|
+
const selection = useShadowRootSelection(debounceTime);
|
50
|
+
const [editLink] = useShadowRootElements([PAGE_EDIT_LINK_SELECTOR]);
|
51
|
+
const url = (_a = editLink == null ? void 0 : editLink.href) != null ? _a : "";
|
52
|
+
const scmIntegrationsApi = useApi(scmIntegrationsApiRef);
|
53
|
+
const configApi = useApi(configApiRef);
|
54
|
+
const appTitle = configApi.getOptionalString("app.title") || "Backstage";
|
55
|
+
if (!selection || !url)
|
56
|
+
return initialTemplate;
|
57
|
+
const type = (_b = scmIntegrationsApi.byUrl(url)) == null ? void 0 : _b.type;
|
58
|
+
if (!type)
|
59
|
+
return initialTemplate;
|
60
|
+
return {
|
61
|
+
title: getTitle(selection),
|
62
|
+
body: getBody(selection, resolveBlobUrl(url, type), appTitle)
|
63
|
+
};
|
64
|
+
};
|
65
|
+
const useGitRepository = () => {
|
66
|
+
var _a, _b;
|
67
|
+
const scmIntegrationsApi = useApi(scmIntegrationsApiRef);
|
68
|
+
const [editLink] = useShadowRootElements([PAGE_EDIT_LINK_SELECTOR]);
|
69
|
+
const url = (_a = editLink == null ? void 0 : editLink.href) != null ? _a : "";
|
70
|
+
if (!url)
|
71
|
+
return null;
|
72
|
+
const type = (_b = scmIntegrationsApi.byUrl(url)) == null ? void 0 : _b.type;
|
73
|
+
if (!type)
|
74
|
+
return null;
|
75
|
+
return { ...parseGitUrl(resolveBlobUrl(url, type)), type };
|
76
|
+
};
|
77
|
+
|
78
|
+
export { getBody, getTitle, useGitRepository, useGitTemplate };
|
79
|
+
//# sourceMappingURL=hooks.esm.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"hooks.esm.js","sources":["../../src/ReportIssue/hooks.ts"],"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 parseGitUrl from 'git-url-parse';\n\nimport { configApiRef, useApi } from '@backstage/core-plugin-api';\nimport {\n replaceGithubUrlType,\n replaceGitLabUrlType,\n} from '@backstage/integration';\nimport { scmIntegrationsApiRef } from '@backstage/integration-react';\nimport {\n useShadowRootElements,\n useShadowRootSelection,\n} from '@backstage/plugin-techdocs-react';\n\nimport { PAGE_EDIT_LINK_SELECTOR } from './constants';\n\nconst resolveBlobUrl = (url: string, type: string) => {\n if (type === 'github') {\n return replaceGithubUrlType(url, 'blob');\n } else if (type === 'gitlab') {\n return replaceGitLabUrlType(url, 'blob');\n }\n // eslint-disable-next-line no-console\n console.error(\n `Invalid SCM type ${type} found in ReportIssue addon for URL ${url}!`,\n );\n return url;\n};\n\nexport const getTitle = (selection: Selection) => {\n const text = selection.toString().substring(0, 70);\n const ellipsis = text.length === 70 ? '...' : '';\n return `Documentation feedback: ${text}${ellipsis}`;\n};\n\nexport const getBody = (\n selection: Selection,\n markdownUrl: string,\n appTitle: string,\n) => {\n const title = '## Documentation Feedback 📝';\n const subheading = '#### The highlighted text:';\n const commentHeading = '#### The comment on the text:';\n const commentPlaceholder = '_>replace this line with your comment<_';\n const highlightedTextAsQuote = selection\n .toString()\n .trim()\n .split('\\n')\n .map(line => `> ${line.trim()}`)\n .join('\\n');\n\n const facts = [\n `${appTitle} URL: <${window.location.href}> \\nMarkdown URL: <${markdownUrl}>`,\n ];\n\n return `${title}\\n\\n ${subheading} \\n\\n ${highlightedTextAsQuote}\\n\\n ${commentHeading} \\n ${commentPlaceholder}\\n\\n ___\\n${facts}`;\n};\n\nexport const useGitTemplate = (debounceTime?: number) => {\n const initialTemplate = { title: '', body: '' };\n const selection = useShadowRootSelection(debounceTime);\n const [editLink] = useShadowRootElements([PAGE_EDIT_LINK_SELECTOR]);\n const url = (editLink as HTMLAnchorElement)?.href ?? '';\n const scmIntegrationsApi = useApi(scmIntegrationsApiRef);\n const configApi = useApi(configApiRef);\n const appTitle = configApi.getOptionalString('app.title') || 'Backstage';\n if (!selection || !url) return initialTemplate;\n\n const type = scmIntegrationsApi.byUrl(url)?.type;\n\n if (!type) return initialTemplate;\n\n return {\n title: getTitle(selection),\n body: getBody(selection, resolveBlobUrl(url, type), appTitle),\n };\n};\n\nexport const useGitRepository = () => {\n const scmIntegrationsApi = useApi(scmIntegrationsApiRef);\n\n const [editLink] = useShadowRootElements([PAGE_EDIT_LINK_SELECTOR]);\n const url = (editLink as HTMLAnchorElement)?.href ?? '';\n\n if (!url) return null;\n\n const type = scmIntegrationsApi.byUrl(url)?.type;\n\n if (!type) return null;\n\n return { ...parseGitUrl(resolveBlobUrl(url, type)), type };\n};\n"],"names":[],"mappings":"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"}
|