@treelocator/runtime 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc +3 -0
- package/.turbo/turbo-build.log +30 -0
- package/.turbo/turbo-test.log +18 -0
- package/.turbo/turbo-ts.log +4 -0
- package/LICENSE +22 -0
- package/babel.config.js +14 -0
- package/dist/_generated_styles.d.ts +2 -0
- package/dist/_generated_styles.js +1734 -0
- package/dist/_generated_tree_icon.d.ts +2 -0
- package/dist/_generated_tree_icon.js +2 -0
- package/dist/adapters/HtmlElementTreeNode.d.ts +16 -0
- package/dist/adapters/HtmlElementTreeNode.js +43 -0
- package/dist/adapters/adapterApi.d.ts +30 -0
- package/dist/adapters/adapterApi.js +1 -0
- package/dist/adapters/createTreeNode.d.ts +2 -0
- package/dist/adapters/createTreeNode.js +17 -0
- package/dist/adapters/getElementInfo.d.ts +2 -0
- package/dist/adapters/getElementInfo.js +19 -0
- package/dist/adapters/getParentsPath.d.ts +2 -0
- package/dist/adapters/getParentsPath.js +35 -0
- package/dist/adapters/getTree.d.ts +1 -0
- package/dist/adapters/getTree.js +35 -0
- package/dist/adapters/goUpByTheTree.d.ts +7 -0
- package/dist/adapters/goUpByTheTree.js +22 -0
- package/dist/adapters/jsx/getExpressionData.d.ts +2 -0
- package/dist/adapters/jsx/getExpressionData.js +44 -0
- package/dist/adapters/jsx/getJSXComponentBoundingBox.d.ts +5 -0
- package/dist/adapters/jsx/getJSXComponentBoundingBox.js +46 -0
- package/dist/adapters/jsx/jsxAdapter.d.ts +11 -0
- package/dist/adapters/jsx/jsxAdapter.js +208 -0
- package/dist/adapters/jsx/runtimeStore.d.ts +10 -0
- package/dist/adapters/jsx/runtimeStore.js +87 -0
- package/dist/adapters/react/fiberToSimple.d.ts +3 -0
- package/dist/adapters/react/fiberToSimple.js +55 -0
- package/dist/adapters/react/findDebugSource.d.ts +5 -0
- package/dist/adapters/react/findDebugSource.js +13 -0
- package/dist/adapters/react/findFiberByHtmlElement.d.ts +2 -0
- package/dist/adapters/react/findFiberByHtmlElement.js +22 -0
- package/dist/adapters/react/gatherFiberRoots.d.ts +2 -0
- package/dist/adapters/react/gatherFiberRoots.js +29 -0
- package/dist/adapters/react/getAllFiberChildren.d.ts +2 -0
- package/dist/adapters/react/getAllFiberChildren.js +9 -0
- package/dist/adapters/react/getAllParentsElementsAndRootComponent.d.ts +8 -0
- package/dist/adapters/react/getAllParentsElementsAndRootComponent.js +34 -0
- package/dist/adapters/react/getAllWrappingParents.d.ts +2 -0
- package/dist/adapters/react/getAllWrappingParents.js +19 -0
- package/dist/adapters/react/getFiberComponentBoundingBox.d.ts +3 -0
- package/dist/adapters/react/getFiberComponentBoundingBox.js +27 -0
- package/dist/adapters/react/getFiberLabel.d.ts +3 -0
- package/dist/adapters/react/getFiberLabel.js +14 -0
- package/dist/adapters/react/getFiberOwnBoundingBox.d.ts +3 -0
- package/dist/adapters/react/getFiberOwnBoundingBox.js +6 -0
- package/dist/adapters/react/isStyled.d.ts +2 -0
- package/dist/adapters/react/isStyled.js +3 -0
- package/dist/adapters/react/makeFiberId.d.ts +2 -0
- package/dist/adapters/react/makeFiberId.js +16 -0
- package/dist/adapters/react/reactAdapter.d.ts +11 -0
- package/dist/adapters/react/reactAdapter.js +114 -0
- package/dist/adapters/react/searchDevtoolsRenderersForClosestTarget.d.ts +1 -0
- package/dist/adapters/react/searchDevtoolsRenderersForClosestTarget.js +11 -0
- package/dist/adapters/svelte/svelteAdapter.d.ts +22 -0
- package/dist/adapters/svelte/svelteAdapter.js +88 -0
- package/dist/adapters/vue/getVNodeBoundingBox.d.ts +4 -0
- package/dist/adapters/vue/getVNodeBoundingBox.js +31 -0
- package/dist/adapters/vue/vueAdapter.d.ts +15 -0
- package/dist/adapters/vue/vueAdapter.js +113 -0
- package/dist/browserApi.d.ts +103 -0
- package/dist/browserApi.js +160 -0
- package/dist/components/Button.d.ts +4 -0
- package/dist/components/Button.js +17 -0
- package/dist/components/ComponentOutline.d.ts +7 -0
- package/dist/components/ComponentOutline.js +93 -0
- package/dist/components/MaybeOutline.d.ts +7 -0
- package/dist/components/MaybeOutline.js +43 -0
- package/dist/components/Outline.d.ts +25 -0
- package/dist/components/Outline.js +135 -0
- package/dist/components/RenderBoxes.d.ts +4 -0
- package/dist/components/RenderBoxes.js +73 -0
- package/dist/components/Runtime.d.ts +3 -0
- package/dist/components/Runtime.js +188 -0
- package/dist/components/SimpleNodeOutline.d.ts +4 -0
- package/dist/components/SimpleNodeOutline.js +47 -0
- package/dist/components/Toast.d.ts +4 -0
- package/dist/components/Toast.js +68 -0
- package/dist/components/Tooltip.d.ts +5 -0
- package/dist/components/Tooltip.js +21 -0
- package/dist/consts.d.ts +6 -0
- package/dist/consts.js +5 -0
- package/dist/functions/cropPath.d.ts +1 -0
- package/dist/functions/cropPath.js +9 -0
- package/dist/functions/cropPath.test.d.ts +1 -0
- package/dist/functions/cropPath.test.js +16 -0
- package/dist/functions/deduplicateLabels.d.ts +2 -0
- package/dist/functions/deduplicateLabels.js +12 -0
- package/dist/functions/evalTemplate.d.ts +3 -0
- package/dist/functions/evalTemplate.js +7 -0
- package/dist/functions/evalTemplate.test.d.ts +1 -0
- package/dist/functions/evalTemplate.test.js +11 -0
- package/dist/functions/findNames.d.ts +5 -0
- package/dist/functions/findNames.js +15 -0
- package/dist/functions/formatAncestryChain.d.ts +9 -0
- package/dist/functions/formatAncestryChain.js +56 -0
- package/dist/functions/getBoundingRect.d.ts +1 -0
- package/dist/functions/getBoundingRect.js +11 -0
- package/dist/functions/getComposedBoundingBox.d.ts +2 -0
- package/dist/functions/getComposedBoundingBox.js +20 -0
- package/dist/functions/getIdsOnPathToRoot.d.ts +3 -0
- package/dist/functions/getIdsOnPathToRoot.js +15 -0
- package/dist/functions/getMultipleElementsBoundingBox.d.ts +2 -0
- package/dist/functions/getMultipleElementsBoundingBox.js +20 -0
- package/dist/functions/getPathToParent.d.ts +1 -0
- package/dist/functions/getPathToParent.js +15 -0
- package/dist/functions/getReferenceId.d.ts +1 -0
- package/dist/functions/getReferenceId.js +9 -0
- package/dist/functions/getUsableFileName.d.ts +1 -0
- package/dist/functions/getUsableFileName.js +17 -0
- package/dist/functions/getUsableFileName.test.d.ts +1 -0
- package/dist/functions/getUsableFileName.test.js +16 -0
- package/dist/functions/getUsableName.d.ts +2 -0
- package/dist/functions/getUsableName.js +47 -0
- package/dist/functions/isCombinationModifiersPressed.d.ts +4 -0
- package/dist/functions/isCombinationModifiersPressed.js +16 -0
- package/dist/functions/isLocatorsOwnElement.d.ts +1 -0
- package/dist/functions/isLocatorsOwnElement.js +3 -0
- package/dist/functions/mergeRects.d.ts +2 -0
- package/dist/functions/mergeRects.js +10 -0
- package/dist/functions/mergeRects.test.d.ts +1 -0
- package/dist/functions/mergeRects.test.js +23 -0
- package/dist/functions/nonNullable.d.ts +1 -0
- package/dist/functions/nonNullable.js +3 -0
- package/dist/functions/parseDataId.d.ts +3 -0
- package/dist/functions/parseDataId.js +44 -0
- package/dist/functions/transformPath.d.ts +1 -0
- package/dist/functions/transformPath.js +7 -0
- package/dist/functions/transformPath.test.d.ts +1 -0
- package/dist/functions/transformPath.test.js +16 -0
- package/dist/global.d.js +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +13 -0
- package/dist/initRuntime.d.ts +8 -0
- package/dist/initRuntime.js +80 -0
- package/dist/output.css +1733 -0
- package/dist/types/LabelData.d.ts +5 -0
- package/dist/types/LabelData.js +1 -0
- package/dist/types/TreeNode.d.ts +19 -0
- package/dist/types/TreeNode.js +1 -0
- package/dist/types/types.d.ts +53 -0
- package/dist/types/types.js +1 -0
- package/jest.config.ts +195 -0
- package/package.json +75 -0
- package/scripts/wrapCSS.js +26 -0
- package/scripts/wrapImage.js +24 -0
- package/src/_generated_styles.ts +1734 -0
- package/src/_generated_tree_icon.ts +2 -0
- package/src/adapters/HtmlElementTreeNode.ts +51 -0
- package/src/adapters/adapterApi.ts +35 -0
- package/src/adapters/createTreeNode.ts +25 -0
- package/src/adapters/getElementInfo.tsx +27 -0
- package/src/adapters/getParentsPath.tsx +49 -0
- package/src/adapters/getTree.tsx +45 -0
- package/src/adapters/goUpByTheTree.ts +20 -0
- package/src/adapters/jsx/getExpressionData.ts +47 -0
- package/src/adapters/jsx/getJSXComponentBoundingBox.ts +63 -0
- package/src/adapters/jsx/jsxAdapter.ts +276 -0
- package/src/adapters/jsx/runtimeStore.ts +94 -0
- package/src/adapters/react/fiberToSimple.tsx +72 -0
- package/src/adapters/react/findDebugSource.ts +15 -0
- package/src/adapters/react/findFiberByHtmlElement.ts +27 -0
- package/src/adapters/react/gatherFiberRoots.tsx +36 -0
- package/src/adapters/react/getAllFiberChildren.tsx +11 -0
- package/src/adapters/react/getAllParentsElementsAndRootComponent.ts +52 -0
- package/src/adapters/react/getAllWrappingParents.ts +25 -0
- package/src/adapters/react/getFiberComponentBoundingBox.ts +30 -0
- package/src/adapters/react/getFiberLabel.ts +20 -0
- package/src/adapters/react/getFiberOwnBoundingBox.ts +9 -0
- package/src/adapters/react/isStyled.ts +5 -0
- package/src/adapters/react/makeFiberId.tsx +19 -0
- package/src/adapters/react/reactAdapter.ts +148 -0
- package/src/adapters/react/searchDevtoolsRenderersForClosestTarget.ts +15 -0
- package/src/adapters/svelte/svelteAdapter.ts +111 -0
- package/src/adapters/vue/getVNodeBoundingBox.tsx +42 -0
- package/src/adapters/vue/vueAdapter.ts +139 -0
- package/src/assets/tree-icon.png +0 -0
- package/src/browserApi.ts +288 -0
- package/src/components/Button.tsx +14 -0
- package/src/components/ComponentOutline.tsx +98 -0
- package/src/components/MaybeOutline.tsx +49 -0
- package/src/components/Outline.tsx +153 -0
- package/src/components/RenderBoxes.tsx +57 -0
- package/src/components/Runtime.tsx +246 -0
- package/src/components/SimpleNodeOutline.tsx +27 -0
- package/src/components/Toast.tsx +83 -0
- package/src/components/Tooltip.tsx +28 -0
- package/src/consts.ts +7 -0
- package/src/functions/cropPath.test.ts +18 -0
- package/src/functions/cropPath.ts +12 -0
- package/src/functions/deduplicateLabels.ts +16 -0
- package/src/functions/evalTemplate.test.ts +12 -0
- package/src/functions/evalTemplate.ts +8 -0
- package/src/functions/findNames.ts +20 -0
- package/src/functions/formatAncestryChain.ts +80 -0
- package/src/functions/getBoundingRect.tsx +11 -0
- package/src/functions/getComposedBoundingBox.tsx +25 -0
- package/src/functions/getIdsOnPathToRoot.tsx +21 -0
- package/src/functions/getMultipleElementsBoundingBox.tsx +25 -0
- package/src/functions/getPathToParent.tsx +17 -0
- package/src/functions/getReferenceId.tsx +10 -0
- package/src/functions/getUsableFileName.test.tsx +24 -0
- package/src/functions/getUsableFileName.tsx +19 -0
- package/src/functions/getUsableName.ts +52 -0
- package/src/functions/isCombinationModifiersPressed.ts +32 -0
- package/src/functions/isLocatorsOwnElement.tsx +9 -0
- package/src/functions/mergeRects.test.ts +15 -0
- package/src/functions/mergeRects.tsx +12 -0
- package/src/functions/nonNullable.ts +3 -0
- package/src/functions/parseDataId.ts +62 -0
- package/src/functions/transformPath.test.ts +28 -0
- package/src/functions/transformPath.ts +7 -0
- package/src/global.d.ts +31 -0
- package/src/index.ts +18 -0
- package/src/initRuntime.ts +83 -0
- package/src/main.css +3 -0
- package/src/types/LabelData.ts +6 -0
- package/src/types/TreeNode.ts +22 -0
- package/src/types/types.ts +55 -0
- package/tailwind.config.js +9 -0
- package/tsconfig.json +20 -0
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { findDebugSource } from "./findDebugSource";
|
|
2
|
+
import { findFiberByHtmlElement } from "./findFiberByHtmlElement";
|
|
3
|
+
import { getFiberLabel } from "./getFiberLabel";
|
|
4
|
+
import { getAllWrappingParents } from "./getAllWrappingParents";
|
|
5
|
+
import { deduplicateLabels } from "../../functions/deduplicateLabels";
|
|
6
|
+
import { getFiberOwnBoundingBox } from "./getFiberOwnBoundingBox";
|
|
7
|
+
import { getAllParentsElementsAndRootComponent } from "./getAllParentsElementsAndRootComponent";
|
|
8
|
+
import { isStyledElement } from "./isStyled";
|
|
9
|
+
import { goUpByTheTree } from "../goUpByTheTree";
|
|
10
|
+
import { HtmlElementTreeNode } from "../HtmlElementTreeNode";
|
|
11
|
+
export function getElementInfo(found) {
|
|
12
|
+
// Instead of labels, return this element, parent elements leading to closest component, its component labels, all wrapping components labels.
|
|
13
|
+
const labels = [];
|
|
14
|
+
const fiber = findFiberByHtmlElement(found, false);
|
|
15
|
+
if (fiber) {
|
|
16
|
+
const {
|
|
17
|
+
component,
|
|
18
|
+
componentBox,
|
|
19
|
+
parentElements
|
|
20
|
+
} = getAllParentsElementsAndRootComponent(fiber);
|
|
21
|
+
const allPotentialComponentFibers = getAllWrappingParents(component);
|
|
22
|
+
|
|
23
|
+
// This handles a common case when the component root is basically the comopnent itself, so I want to go to usage of the component
|
|
24
|
+
// TODO: whaat? why? currently I see that it adds the original styled components which is not necessary.
|
|
25
|
+
|
|
26
|
+
// if (fiber.return && fiber.return === fiber._debugOwner) {
|
|
27
|
+
// allPotentialComponentFibers.unshift(fiber.return);
|
|
28
|
+
// }
|
|
29
|
+
|
|
30
|
+
allPotentialComponentFibers.forEach(fiber => {
|
|
31
|
+
const fiberWithSource = findDebugSource(fiber);
|
|
32
|
+
if (fiberWithSource) {
|
|
33
|
+
const label = getFiberLabel(fiberWithSource.fiber, fiberWithSource.source);
|
|
34
|
+
labels.push(label);
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
const thisLabel = getFiberLabel(fiber, findDebugSource(fiber)?.source);
|
|
38
|
+
if (isStyledElement(fiber)) {
|
|
39
|
+
thisLabel.label = `${thisLabel.label} (styled)`;
|
|
40
|
+
}
|
|
41
|
+
return {
|
|
42
|
+
thisElement: {
|
|
43
|
+
box: getFiberOwnBoundingBox(fiber) || found.getBoundingClientRect(),
|
|
44
|
+
...thisLabel
|
|
45
|
+
},
|
|
46
|
+
htmlElement: found,
|
|
47
|
+
parentElements: parentElements,
|
|
48
|
+
componentBox,
|
|
49
|
+
componentsLabels: deduplicateLabels(labels)
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
export class ReactTreeNodeElement extends HtmlElementTreeNode {
|
|
55
|
+
getSource() {
|
|
56
|
+
const fiber = findFiberByHtmlElement(this.element, false);
|
|
57
|
+
if (fiber && fiber._debugSource) {
|
|
58
|
+
return {
|
|
59
|
+
fileName: fiber._debugSource.fileName,
|
|
60
|
+
lineNumber: fiber._debugSource.lineNumber,
|
|
61
|
+
columnNumber: fiber._debugSource.columnNumber
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
getComponent() {
|
|
67
|
+
const fiber = findFiberByHtmlElement(this.element, false);
|
|
68
|
+
const componentFiber = fiber?._debugOwner;
|
|
69
|
+
if (componentFiber) {
|
|
70
|
+
const fiberLabel = getFiberLabel(componentFiber, findDebugSource(componentFiber)?.source);
|
|
71
|
+
return {
|
|
72
|
+
label: fiberLabel.label,
|
|
73
|
+
callLink: fiberLabel.link && {
|
|
74
|
+
fileName: fiberLabel.link.filePath,
|
|
75
|
+
lineNumber: fiberLabel.link.line,
|
|
76
|
+
columnNumber: fiberLabel.link.column,
|
|
77
|
+
projectPath: fiberLabel.link.projectPath
|
|
78
|
+
} || undefined
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
function getTree(element) {
|
|
85
|
+
const originalRoot = new ReactTreeNodeElement(element);
|
|
86
|
+
return goUpByTheTree(originalRoot);
|
|
87
|
+
}
|
|
88
|
+
function fiberToPathItem(fiber) {
|
|
89
|
+
const label = getFiberLabel(fiber, findDebugSource(fiber)?.source);
|
|
90
|
+
return {
|
|
91
|
+
title: label.label,
|
|
92
|
+
link: label.link
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
function getParentsPaths(element) {
|
|
96
|
+
const fiber = findFiberByHtmlElement(element, false);
|
|
97
|
+
if (fiber) {
|
|
98
|
+
const pathItems = [];
|
|
99
|
+
let currentFiber = fiber;
|
|
100
|
+
pathItems.push(fiberToPathItem(currentFiber));
|
|
101
|
+
while (currentFiber._debugOwner) {
|
|
102
|
+
currentFiber = currentFiber._debugOwner;
|
|
103
|
+
pathItems.push(fiberToPathItem(currentFiber));
|
|
104
|
+
}
|
|
105
|
+
return pathItems;
|
|
106
|
+
}
|
|
107
|
+
return [];
|
|
108
|
+
}
|
|
109
|
+
const reactAdapter = {
|
|
110
|
+
getElementInfo,
|
|
111
|
+
getTree,
|
|
112
|
+
getParentsPaths
|
|
113
|
+
};
|
|
114
|
+
export default reactAdapter;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function searchDevtoolsRenderersForClosestTarget(target: HTMLElement): HTMLElement | null;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { findFiberByHtmlElement } from "./findFiberByHtmlElement";
|
|
2
|
+
export function searchDevtoolsRenderersForClosestTarget(target) {
|
|
3
|
+
let closest = target;
|
|
4
|
+
while (closest) {
|
|
5
|
+
if (findFiberByHtmlElement(closest, false)) {
|
|
6
|
+
return closest;
|
|
7
|
+
}
|
|
8
|
+
closest = closest.parentElement;
|
|
9
|
+
}
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Source } from "@locator/shared";
|
|
2
|
+
import { TreeNodeComponent } from "../../types/TreeNode";
|
|
3
|
+
import { AdapterObject, FullElementInfo } from "../adapterApi";
|
|
4
|
+
import { HtmlElementTreeNode } from "../HtmlElementTreeNode";
|
|
5
|
+
type SvelteLoc = {
|
|
6
|
+
char: number;
|
|
7
|
+
column: number;
|
|
8
|
+
file: string;
|
|
9
|
+
line: number;
|
|
10
|
+
};
|
|
11
|
+
type SvelteElement = HTMLElement & {
|
|
12
|
+
__svelte_meta?: {
|
|
13
|
+
loc: SvelteLoc;
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
export declare function getElementInfo(found: SvelteElement): FullElementInfo | null;
|
|
17
|
+
export declare class SvelteTreeNodeElement extends HtmlElementTreeNode {
|
|
18
|
+
getSource(): Source | null;
|
|
19
|
+
getComponent(): TreeNodeComponent | null;
|
|
20
|
+
}
|
|
21
|
+
declare const svelteAdapter: AdapterObject;
|
|
22
|
+
export default svelteAdapter;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { goUpByTheTree } from "../goUpByTheTree";
|
|
2
|
+
import { HtmlElementTreeNode } from "../HtmlElementTreeNode";
|
|
3
|
+
export function getElementInfo(found) {
|
|
4
|
+
if (found.__svelte_meta) {
|
|
5
|
+
const {
|
|
6
|
+
loc
|
|
7
|
+
} = found.__svelte_meta;
|
|
8
|
+
return {
|
|
9
|
+
thisElement: {
|
|
10
|
+
box: found.getBoundingClientRect(),
|
|
11
|
+
label: found.nodeName.toLowerCase(),
|
|
12
|
+
link: {
|
|
13
|
+
column: loc.column + 1,
|
|
14
|
+
line: loc.line + 1,
|
|
15
|
+
filePath: loc.file,
|
|
16
|
+
projectPath: ""
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
htmlElement: found,
|
|
20
|
+
parentElements: [],
|
|
21
|
+
componentBox: found.getBoundingClientRect(),
|
|
22
|
+
componentsLabels: []
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
export class SvelteTreeNodeElement extends HtmlElementTreeNode {
|
|
28
|
+
getSource() {
|
|
29
|
+
const element = this.element;
|
|
30
|
+
if (element.__svelte_meta) {
|
|
31
|
+
const {
|
|
32
|
+
loc
|
|
33
|
+
} = element.__svelte_meta;
|
|
34
|
+
return {
|
|
35
|
+
fileName: loc.file,
|
|
36
|
+
lineNumber: loc.line + 1,
|
|
37
|
+
columnNumber: loc.column + 1
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
getComponent() {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function getTree(element) {
|
|
47
|
+
const originalRoot = new SvelteTreeNodeElement(element);
|
|
48
|
+
return goUpByTheTree(originalRoot);
|
|
49
|
+
}
|
|
50
|
+
function getParentsPaths(element) {
|
|
51
|
+
const path = [];
|
|
52
|
+
let currentElement = element;
|
|
53
|
+
let maxDepth = 10000; // just in case, it should not be needed
|
|
54
|
+
|
|
55
|
+
do {
|
|
56
|
+
if (currentElement?.__svelte_meta) {
|
|
57
|
+
const {
|
|
58
|
+
loc
|
|
59
|
+
} = currentElement.__svelte_meta;
|
|
60
|
+
|
|
61
|
+
// we assume that there is one component per file
|
|
62
|
+
const isSameFileAsTheLastOne = loc.file === path[path.length - 1]?.link?.filePath;
|
|
63
|
+
if (!isSameFileAsTheLastOne) {
|
|
64
|
+
path.push({
|
|
65
|
+
title: currentElement.nodeName.toLowerCase(),
|
|
66
|
+
link: {
|
|
67
|
+
column: loc.column + 1,
|
|
68
|
+
line: loc.line + 1,
|
|
69
|
+
filePath: loc.file,
|
|
70
|
+
projectPath: ""
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
currentElement = currentElement.parentElement;
|
|
76
|
+
maxDepth--;
|
|
77
|
+
if (maxDepth < 0) {
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
} while (currentElement);
|
|
81
|
+
return path;
|
|
82
|
+
}
|
|
83
|
+
const svelteAdapter = {
|
|
84
|
+
getElementInfo,
|
|
85
|
+
getTree,
|
|
86
|
+
getParentsPaths
|
|
87
|
+
};
|
|
88
|
+
export default svelteAdapter;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { SimpleDOMRect } from "../../types/types";
|
|
2
|
+
import type { ComponentInternalInstance, VNode } from "vue";
|
|
3
|
+
export declare function getVueComponentBoundingBox(vcomponent: ComponentInternalInstance): null;
|
|
4
|
+
export declare function getVNodeBoundingBox(vnode: VNode): SimpleDOMRect | null;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { mergeRects } from "../../functions/mergeRects";
|
|
2
|
+
export function getVueComponentBoundingBox(vcomponent) {
|
|
3
|
+
let composedRect = null;
|
|
4
|
+
if (vcomponent?.subTree?.children && vcomponent?.subTree?.children instanceof Array) {
|
|
5
|
+
vcomponent?.subTree?.children.forEach(child => {
|
|
6
|
+
const box = getVNodeBoundingBox(child);
|
|
7
|
+
if (!box) {
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
if (box.width <= 0 || box.height <= 0) {
|
|
11
|
+
// ignore zero-sized rects
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
if (composedRect) {
|
|
15
|
+
composedRect = mergeRects(composedRect, box);
|
|
16
|
+
} else {
|
|
17
|
+
composedRect = box;
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
return composedRect;
|
|
22
|
+
}
|
|
23
|
+
export function getVNodeBoundingBox(vnode) {
|
|
24
|
+
if (vnode.el instanceof HTMLElement) {
|
|
25
|
+
return vnode.el.getBoundingClientRect();
|
|
26
|
+
}
|
|
27
|
+
if (vnode.component) {
|
|
28
|
+
return getVueComponentBoundingBox(vnode.component);
|
|
29
|
+
}
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Source } from "@locator/shared";
|
|
2
|
+
import type { ComponentInternalInstance } from "vue";
|
|
3
|
+
import { TreeNodeComponent } from "../../types/TreeNode";
|
|
4
|
+
import { AdapterObject, FullElementInfo } from "../adapterApi";
|
|
5
|
+
import { HtmlElementTreeNode } from "../HtmlElementTreeNode";
|
|
6
|
+
type VueElement = HTMLElement & {
|
|
7
|
+
__vueParentComponent?: ComponentInternalInstance;
|
|
8
|
+
};
|
|
9
|
+
export declare function getElementInfo(found: VueElement): FullElementInfo | null;
|
|
10
|
+
export declare class VueTreeNodeElement extends HtmlElementTreeNode {
|
|
11
|
+
getSource(): Source | null;
|
|
12
|
+
getComponent(): TreeNodeComponent | null;
|
|
13
|
+
}
|
|
14
|
+
declare const vueAdapter: AdapterObject;
|
|
15
|
+
export default vueAdapter;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { goUpByTheTree } from "../goUpByTheTree";
|
|
2
|
+
import { HtmlElementTreeNode } from "../HtmlElementTreeNode";
|
|
3
|
+
import { getVueComponentBoundingBox } from "./getVNodeBoundingBox";
|
|
4
|
+
export function getElementInfo(found) {
|
|
5
|
+
const parentComponent = found.__vueParentComponent;
|
|
6
|
+
if (parentComponent) {
|
|
7
|
+
if (!parentComponent.type) {
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
const componentBBox = getVueComponentBoundingBox(parentComponent);
|
|
11
|
+
const {
|
|
12
|
+
__file,
|
|
13
|
+
__name
|
|
14
|
+
} = parentComponent.type;
|
|
15
|
+
if (__file && __name) {
|
|
16
|
+
return {
|
|
17
|
+
thisElement: {
|
|
18
|
+
box: found.getBoundingClientRect(),
|
|
19
|
+
label: found.nodeName.toLowerCase(),
|
|
20
|
+
link: {
|
|
21
|
+
column: 1,
|
|
22
|
+
line: 1,
|
|
23
|
+
filePath: __file,
|
|
24
|
+
projectPath: ""
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
htmlElement: found,
|
|
28
|
+
parentElements: [],
|
|
29
|
+
componentBox: componentBBox || found.getBoundingClientRect(),
|
|
30
|
+
componentsLabels: [{
|
|
31
|
+
label: __name,
|
|
32
|
+
link: {
|
|
33
|
+
column: 1,
|
|
34
|
+
line: 1,
|
|
35
|
+
filePath: __file,
|
|
36
|
+
projectPath: ""
|
|
37
|
+
}
|
|
38
|
+
}]
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
export class VueTreeNodeElement extends HtmlElementTreeNode {
|
|
45
|
+
getSource() {
|
|
46
|
+
const element = this.element;
|
|
47
|
+
const parentComponent = element.__vueParentComponent;
|
|
48
|
+
if (parentComponent && parentComponent.type) {
|
|
49
|
+
const {
|
|
50
|
+
__file
|
|
51
|
+
} = parentComponent.type;
|
|
52
|
+
if (__file) {
|
|
53
|
+
return {
|
|
54
|
+
fileName: __file,
|
|
55
|
+
lineNumber: 1,
|
|
56
|
+
columnNumber: 1
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
getComponent() {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
// getComponent(): TreeNodeComponent | null {
|
|
66
|
+
// const element = this.element as VueElement;
|
|
67
|
+
// const parentComponent = element.__vueParentComponent;
|
|
68
|
+
// if (parentComponent && parentComponent.type) {
|
|
69
|
+
// const { __name } = parentComponent.type;
|
|
70
|
+
// if (__name) {
|
|
71
|
+
// return {
|
|
72
|
+
// label: __name,
|
|
73
|
+
// definitionLink: this.getSource() || undefined,
|
|
74
|
+
// };
|
|
75
|
+
// }
|
|
76
|
+
// }
|
|
77
|
+
// return null;
|
|
78
|
+
// }
|
|
79
|
+
}
|
|
80
|
+
function getTree(element) {
|
|
81
|
+
const originalRoot = new VueTreeNodeElement(element);
|
|
82
|
+
return goUpByTheTree(originalRoot);
|
|
83
|
+
}
|
|
84
|
+
function getParentsPaths(element) {
|
|
85
|
+
const path = [];
|
|
86
|
+
let currentElement = element;
|
|
87
|
+
let previousComponentKey = null;
|
|
88
|
+
do {
|
|
89
|
+
if (currentElement) {
|
|
90
|
+
const info = getElementInfo(currentElement);
|
|
91
|
+
const currentComponentKey = JSON.stringify(info?.componentsLabels);
|
|
92
|
+
if (info && currentComponentKey !== previousComponentKey) {
|
|
93
|
+
previousComponentKey = currentComponentKey;
|
|
94
|
+
const link = info.thisElement.link;
|
|
95
|
+
const label = info.thisElement.label;
|
|
96
|
+
if (link) {
|
|
97
|
+
path.push({
|
|
98
|
+
title: label,
|
|
99
|
+
link: link
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
currentElement = currentElement.parentElement;
|
|
105
|
+
} while (currentElement);
|
|
106
|
+
return path;
|
|
107
|
+
}
|
|
108
|
+
const vueAdapter = {
|
|
109
|
+
getElementInfo,
|
|
110
|
+
getTree,
|
|
111
|
+
getParentsPaths
|
|
112
|
+
};
|
|
113
|
+
export default vueAdapter;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { AdapterId } from "./consts";
|
|
2
|
+
import { AncestryItem } from "./functions/formatAncestryChain";
|
|
3
|
+
export interface LocatorJSAPI {
|
|
4
|
+
/**
|
|
5
|
+
* Get formatted ancestry path for an element.
|
|
6
|
+
* Returns a human-readable string showing the component hierarchy from root to target element.
|
|
7
|
+
*
|
|
8
|
+
* @param elementOrSelector - HTMLElement or CSS selector string (e.g., 'button.submit', '#login-form')
|
|
9
|
+
* @returns Formatted ancestry chain as string, or null if element not found/unsupported
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* // Basic usage with CSS selector
|
|
13
|
+
* window.__locatorjs__.getPath('button.submit');
|
|
14
|
+
* // Returns:
|
|
15
|
+
* // "div in App at src/App.tsx:15
|
|
16
|
+
* // └─ button in SubmitButton at src/components/SubmitButton.tsx:12"
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* // Usage with HTMLElement
|
|
20
|
+
* const button = document.querySelector('button.submit');
|
|
21
|
+
* window.__locatorjs__.getPath(button);
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* // In Playwright
|
|
25
|
+
* const path = await page.evaluate(() => {
|
|
26
|
+
* return window.__locatorjs__.getPath('button.submit');
|
|
27
|
+
* });
|
|
28
|
+
* console.log(path);
|
|
29
|
+
*/
|
|
30
|
+
getPath(elementOrSelector: HTMLElement | string): string | null;
|
|
31
|
+
/**
|
|
32
|
+
* Get raw ancestry data for an element.
|
|
33
|
+
* Returns an array of objects containing component names, file paths, and line numbers.
|
|
34
|
+
*
|
|
35
|
+
* @param elementOrSelector - HTMLElement or CSS selector string
|
|
36
|
+
* @returns Array of ancestry items with structure:
|
|
37
|
+
* - elementName: HTML element tag (e.g., 'div', 'button')
|
|
38
|
+
* - componentName: Component name (e.g., 'LoginButton')
|
|
39
|
+
* - filePath: Source file path (e.g., 'src/components/LoginButton.tsx')
|
|
40
|
+
* - line: Line number in source file
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* // Get structured ancestry data
|
|
44
|
+
* const ancestry = window.__locatorjs__.getAncestry('button.submit');
|
|
45
|
+
* // Returns: [
|
|
46
|
+
* // { elementName: 'div', componentName: 'App', filePath: 'src/App.tsx', line: 15 },
|
|
47
|
+
* // { elementName: 'button', componentName: 'SubmitButton', filePath: 'src/components/SubmitButton.tsx', line: 12 }
|
|
48
|
+
* // ]
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* // In Playwright - extract just component names
|
|
52
|
+
* const components = await page.evaluate(() => {
|
|
53
|
+
* const ancestry = window.__locatorjs__.getAncestry('.my-element');
|
|
54
|
+
* return ancestry?.map(item => item.componentName).filter(Boolean);
|
|
55
|
+
* });
|
|
56
|
+
*/
|
|
57
|
+
getAncestry(elementOrSelector: HTMLElement | string): AncestryItem[] | null;
|
|
58
|
+
/**
|
|
59
|
+
* Get both formatted path and raw ancestry data in a single call.
|
|
60
|
+
* Convenience method that combines getPath() and getAncestry().
|
|
61
|
+
*
|
|
62
|
+
* @param elementOrSelector - HTMLElement or CSS selector string
|
|
63
|
+
* @returns Object with { path: string, ancestry: AncestryItem[] }, or null
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* // Get both formats at once
|
|
67
|
+
* const data = window.__locatorjs__.getPathData('button.submit');
|
|
68
|
+
* console.log(data.path); // Human-readable string
|
|
69
|
+
* console.log(data.ancestry); // Structured array
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* // In Playwright - useful for comprehensive debugging
|
|
73
|
+
* const data = await page.evaluate(() => {
|
|
74
|
+
* return window.__locatorjs__.getPathData('.error-message');
|
|
75
|
+
* });
|
|
76
|
+
* if (data) {
|
|
77
|
+
* console.log('Component tree:', data.path);
|
|
78
|
+
* console.log('Source files:', data.ancestry.map(a => a.filePath));
|
|
79
|
+
* }
|
|
80
|
+
*/
|
|
81
|
+
getPathData(elementOrSelector: HTMLElement | string): {
|
|
82
|
+
path: string;
|
|
83
|
+
ancestry: AncestryItem[];
|
|
84
|
+
} | null;
|
|
85
|
+
/**
|
|
86
|
+
* Display help information about the LocatorJS API.
|
|
87
|
+
* Shows usage examples and method descriptions for browser automation tools.
|
|
88
|
+
*
|
|
89
|
+
* @returns Help text as a string
|
|
90
|
+
*
|
|
91
|
+
* @example
|
|
92
|
+
* // View help in browser console
|
|
93
|
+
* console.log(window.__locatorjs__.help());
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
* // In Playwright - view help
|
|
97
|
+
* const help = await page.evaluate(() => window.__locatorjs__.help());
|
|
98
|
+
* console.log(help);
|
|
99
|
+
*/
|
|
100
|
+
help(): string;
|
|
101
|
+
}
|
|
102
|
+
export declare function createBrowserAPI(adapterIdParam?: AdapterId): LocatorJSAPI;
|
|
103
|
+
export declare function installBrowserAPI(adapterIdParam?: AdapterId): void;
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { createTreeNode } from "./adapters/createTreeNode";
|
|
2
|
+
import { collectAncestry, formatAncestryChain } from "./functions/formatAncestryChain";
|
|
3
|
+
let adapterId;
|
|
4
|
+
function resolveElement(elementOrSelector) {
|
|
5
|
+
if (typeof elementOrSelector === "string") {
|
|
6
|
+
const element = document.querySelector(elementOrSelector);
|
|
7
|
+
return element instanceof HTMLElement ? element : null;
|
|
8
|
+
}
|
|
9
|
+
return elementOrSelector;
|
|
10
|
+
}
|
|
11
|
+
function getAncestryForElement(element) {
|
|
12
|
+
const treeNode = createTreeNode(element, adapterId);
|
|
13
|
+
if (!treeNode) {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
return collectAncestry(treeNode);
|
|
17
|
+
}
|
|
18
|
+
const HELP_TEXT = `
|
|
19
|
+
╔═══════════════════════════════════════════════════════════════════════════╗
|
|
20
|
+
║ LocatorJS Browser API ║
|
|
21
|
+
║ Programmatic Component Ancestry Access ║
|
|
22
|
+
╚═══════════════════════════════════════════════════════════════════════════╝
|
|
23
|
+
|
|
24
|
+
METHODS:
|
|
25
|
+
--------
|
|
26
|
+
|
|
27
|
+
1. getPath(elementOrSelector)
|
|
28
|
+
Returns a formatted string showing the component hierarchy.
|
|
29
|
+
|
|
30
|
+
Usage:
|
|
31
|
+
window.__locatorjs__.getPath('button.submit')
|
|
32
|
+
window.__locatorjs__.getPath(document.querySelector('.my-button'))
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
"div in App at src/App.tsx:15
|
|
36
|
+
└─ button in SubmitButton at src/components/SubmitButton.tsx:12"
|
|
37
|
+
|
|
38
|
+
2. getAncestry(elementOrSelector)
|
|
39
|
+
Returns raw ancestry data as an array of objects.
|
|
40
|
+
|
|
41
|
+
Usage:
|
|
42
|
+
window.__locatorjs__.getAncestry('button.submit')
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
[
|
|
46
|
+
{ elementName: 'div', componentName: 'App',
|
|
47
|
+
filePath: 'src/App.tsx', line: 15 },
|
|
48
|
+
{ elementName: 'button', componentName: 'SubmitButton',
|
|
49
|
+
filePath: 'src/components/SubmitButton.tsx', line: 12 }
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
3. getPathData(elementOrSelector)
|
|
53
|
+
Returns both formatted path and raw ancestry in one call.
|
|
54
|
+
|
|
55
|
+
Usage:
|
|
56
|
+
const data = window.__locatorjs__.getPathData('button.submit')
|
|
57
|
+
console.log(data.path) // formatted string
|
|
58
|
+
console.log(data.ancestry) // structured array
|
|
59
|
+
|
|
60
|
+
4. help()
|
|
61
|
+
Displays this help message.
|
|
62
|
+
|
|
63
|
+
PLAYWRIGHT EXAMPLES:
|
|
64
|
+
-------------------
|
|
65
|
+
|
|
66
|
+
// Get component path for debugging
|
|
67
|
+
const path = await page.evaluate(() => {
|
|
68
|
+
return window.__locatorjs__.getPath('button.submit');
|
|
69
|
+
});
|
|
70
|
+
console.log(path);
|
|
71
|
+
|
|
72
|
+
// Extract component names
|
|
73
|
+
const components = await page.evaluate(() => {
|
|
74
|
+
const ancestry = window.__locatorjs__.getAncestry('.error-message');
|
|
75
|
+
return ancestry?.map(item => item.componentName).filter(Boolean);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
// Create a test helper
|
|
79
|
+
async function getComponentPath(page, selector) {
|
|
80
|
+
return await page.evaluate((sel) => {
|
|
81
|
+
return window.__locatorjs__.getPath(sel);
|
|
82
|
+
}, selector);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
PUPPETEER EXAMPLES:
|
|
86
|
+
------------------
|
|
87
|
+
|
|
88
|
+
const path = await page.evaluate(() => {
|
|
89
|
+
return window.__locatorjs__.getPath('.my-button');
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
SELENIUM EXAMPLES:
|
|
93
|
+
-----------------
|
|
94
|
+
|
|
95
|
+
const path = await driver.executeScript(() => {
|
|
96
|
+
return window.__locatorjs__.getPath('button.submit');
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
CYPRESS EXAMPLES:
|
|
100
|
+
----------------
|
|
101
|
+
|
|
102
|
+
cy.window().then((win) => {
|
|
103
|
+
const path = win.__locatorjs__.getPath('button.submit');
|
|
104
|
+
cy.log(path);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
NOTES:
|
|
108
|
+
------
|
|
109
|
+
• Accepts CSS selectors or HTMLElement objects
|
|
110
|
+
• Returns null if element not found or framework not supported
|
|
111
|
+
• Works with React, Vue, Svelte, Preact, and any JSX framework
|
|
112
|
+
• Automatically installed when LocatorJS runtime initializes
|
|
113
|
+
|
|
114
|
+
Documentation: https://github.com/infi-pc/locatorjs
|
|
115
|
+
`;
|
|
116
|
+
export function createBrowserAPI(adapterIdParam) {
|
|
117
|
+
adapterId = adapterIdParam;
|
|
118
|
+
return {
|
|
119
|
+
getPath(elementOrSelector) {
|
|
120
|
+
const element = resolveElement(elementOrSelector);
|
|
121
|
+
if (!element) {
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
const ancestry = getAncestryForElement(element);
|
|
125
|
+
if (!ancestry) {
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
return formatAncestryChain(ancestry);
|
|
129
|
+
},
|
|
130
|
+
getAncestry(elementOrSelector) {
|
|
131
|
+
const element = resolveElement(elementOrSelector);
|
|
132
|
+
if (!element) {
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
return getAncestryForElement(element);
|
|
136
|
+
},
|
|
137
|
+
getPathData(elementOrSelector) {
|
|
138
|
+
const element = resolveElement(elementOrSelector);
|
|
139
|
+
if (!element) {
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
const ancestry = getAncestryForElement(element);
|
|
143
|
+
if (!ancestry) {
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
return {
|
|
147
|
+
path: formatAncestryChain(ancestry),
|
|
148
|
+
ancestry
|
|
149
|
+
};
|
|
150
|
+
},
|
|
151
|
+
help() {
|
|
152
|
+
return HELP_TEXT;
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
export function installBrowserAPI(adapterIdParam) {
|
|
157
|
+
if (typeof window !== "undefined") {
|
|
158
|
+
window.__locatorjs__ = createBrowserAPI(adapterIdParam);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { template as _$template } from "solid-js/web";
|
|
2
|
+
import { delegateEvents as _$delegateEvents } from "solid-js/web";
|
|
3
|
+
import { insert as _$insert } from "solid-js/web";
|
|
4
|
+
var _tmpl$ = /*#__PURE__*/_$template(`<button class="py-1 px-1 hover:bg-white/30 pointer hover:text-gray-100 rounded">`);
|
|
5
|
+
export function Button(props) {
|
|
6
|
+
return (() => {
|
|
7
|
+
var _el$ = _tmpl$();
|
|
8
|
+
_el$.$$click = e => {
|
|
9
|
+
e.preventDefault();
|
|
10
|
+
e.stopPropagation();
|
|
11
|
+
props.onClick();
|
|
12
|
+
};
|
|
13
|
+
_$insert(_el$, () => props.children);
|
|
14
|
+
return _el$;
|
|
15
|
+
})();
|
|
16
|
+
}
|
|
17
|
+
_$delegateEvents(["click"]);
|