@shoplflow/extension 0.0.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 (76) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/LICENSE +201 -0
  3. package/README.md +172 -0
  4. package/dist/assets/css/contentStyle17107484687.chunk.css +1 -0
  5. package/dist/assets/css/optionsIndex.chunk.css +1 -0
  6. package/dist/assets/css/popupIndex.chunk.css +1 -0
  7. package/dist/assets/js/index.a235624f.js +1 -0
  8. package/dist/assets/js/jsx-runtime.ea866ad2.js +40 -0
  9. package/dist/assets/js/twind.60e4b726.js +1 -0
  10. package/dist/icon-128.png +0 -0
  11. package/dist/icon-34.png +0 -0
  12. package/dist/manifest.json +45 -0
  13. package/dist/src/pages/background/index.js +1 -0
  14. package/dist/src/pages/content/index.js +1 -0
  15. package/dist/src/pages/options/index.html +15 -0
  16. package/dist/src/pages/options/index.js +1 -0
  17. package/dist/src/pages/popup/index.html +16 -0
  18. package/dist/src/pages/popup/index.js +918 -0
  19. package/manifest.ts +37 -0
  20. package/package.json +57 -0
  21. package/public/icon-128.png +0 -0
  22. package/public/icon-34.png +0 -0
  23. package/public/manifest.json +45 -0
  24. package/src/Components/ElementListCard.tsx +35 -0
  25. package/src/assets/img/logo.svg +7 -0
  26. package/src/environment.d.ts +10 -0
  27. package/src/global.d.ts +37 -0
  28. package/src/pages/background/index.ts +11 -0
  29. package/src/pages/content/components/Demo/app.tsx +93 -0
  30. package/src/pages/content/components/Demo/index.tsx +27 -0
  31. package/src/pages/content/index.ts +7 -0
  32. package/src/pages/content/style.scss +7 -0
  33. package/src/pages/options/Options.css +8 -0
  34. package/src/pages/options/Options.tsx +8 -0
  35. package/src/pages/options/index.css +0 -0
  36. package/src/pages/options/index.html +12 -0
  37. package/src/pages/options/index.tsx +18 -0
  38. package/src/pages/panel/Panel.css +7 -0
  39. package/src/pages/panel/Panel.tsx +65 -0
  40. package/src/pages/panel/index.css +0 -0
  41. package/src/pages/panel/index.html +12 -0
  42. package/src/pages/panel/index.tsx +20 -0
  43. package/src/pages/popup/Popup.css +44 -0
  44. package/src/pages/popup/Popup.tsx +123 -0
  45. package/src/pages/popup/index.css +13 -0
  46. package/src/pages/popup/index.html +12 -0
  47. package/src/pages/popup/index.tsx +20 -0
  48. package/src/shared/hoc/withErrorBoundary.tsx +42 -0
  49. package/src/shared/hoc/withSuspense.tsx +14 -0
  50. package/src/shared/hooks/useStorage.tsx +47 -0
  51. package/src/shared/storages/base.ts +75 -0
  52. package/src/shared/storages/componentsInfoStorage.ts +46 -0
  53. package/src/shared/style/twind.ts +13 -0
  54. package/src/vite-env.d.ts +1 -0
  55. package/tsconfig.json +29 -0
  56. package/twind.config.ts +7 -0
  57. package/utils/checkShopl.ts +6 -0
  58. package/utils/log.ts +52 -0
  59. package/utils/manifest-parser/index.ts +35 -0
  60. package/utils/plugins/add-hmr.ts +46 -0
  61. package/utils/plugins/custom-dynamic-import.ts +34 -0
  62. package/utils/plugins/make-manifest.ts +49 -0
  63. package/utils/plugins/watch-rebuild.ts +16 -0
  64. package/utils/reload/constant.ts +5 -0
  65. package/utils/reload/initReloadClient.ts +52 -0
  66. package/utils/reload/initReloadServer.js +72 -0
  67. package/utils/reload/initReloadServer.ts +68 -0
  68. package/utils/reload/injections/script.js +60 -0
  69. package/utils/reload/injections/script.ts +12 -0
  70. package/utils/reload/injections/view.js +74 -0
  71. package/utils/reload/injections/view.ts +29 -0
  72. package/utils/reload/interpreter/index.ts +13 -0
  73. package/utils/reload/interpreter/types.ts +15 -0
  74. package/utils/reload/rollup.config.ts +28 -0
  75. package/utils/reload/utils.ts +9 -0
  76. package/vite.config.ts +89 -0
package/manifest.ts ADDED
@@ -0,0 +1,37 @@
1
+ import packageJson from './package.json';
2
+
3
+ /**
4
+ * After changing, please reload the extension at `chrome://extensions`
5
+ */
6
+ const manifest: chrome.runtime.ManifestV3 = {
7
+ manifest_version: 3,
8
+ name: packageJson.name,
9
+ version: packageJson.version,
10
+ description: packageJson.description,
11
+ permissions: ['storage'],
12
+ action: {
13
+ default_popup: 'src/pages/popup/index.html',
14
+ default_icon: 'icon-34.png',
15
+ },
16
+
17
+ icons: {
18
+ '128': 'icon-128.png',
19
+ },
20
+ content_scripts: [
21
+ {
22
+ matches: ['http://*/*', 'https://*/*', '<all_urls>'],
23
+ js: ['src/pages/content/index.js'],
24
+ // KEY for cache invalidation
25
+ css: ['assets/css/contentStyle<KEY>.chunk.css'],
26
+ },
27
+ ],
28
+ devtools_page: 'src/pages/devtools/index.html',
29
+ web_accessible_resources: [
30
+ {
31
+ resources: ['assets/js/*.js', 'assets/css/*.css', 'icon-128.png', 'icon-34.png'],
32
+ matches: ['*://*/*'],
33
+ },
34
+ ],
35
+ };
36
+
37
+ export default manifest;
package/package.json ADDED
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "@shoplflow/extension",
3
+ "version": "0.0.2",
4
+ "license": "MIT",
5
+ "description": "Shoplflow 점유율 확인 extension",
6
+ "type": "module",
7
+ "dependencies": {
8
+ "@shoplflow/base": "^0.26.0",
9
+ "chart.js": "^4.4.0",
10
+ "construct-style-sheets-polyfill": "^3.1.0",
11
+ "react": "18.2.0",
12
+ "react-chartjs-2": "^5.2.0",
13
+ "react-dom": "18.2.0"
14
+ },
15
+ "devDependencies": {
16
+ "@commitlint/cli": "^17.7.2",
17
+ "@commitlint/config-conventional": "^17.7.0",
18
+ "@emotion/styled": "^11.11.0",
19
+ "@rollup/plugin-typescript": "^8.5.0",
20
+ "@testing-library/react": "13.4.0",
21
+ "@twind/core": "^1.1.3",
22
+ "@twind/preset-autoprefix": "^1.0.7",
23
+ "@twind/preset-tailwind": "^1.1.4",
24
+ "@types/chrome": "0.0.224",
25
+ "@types/node": "18.15.11",
26
+ "@types/react": "18.2.29",
27
+ "@types/react-dom": "18.2.13",
28
+ "@types/ws": "^8.5.4",
29
+ "@typescript-eslint/eslint-plugin": "^6.7.5",
30
+ "@typescript-eslint/parser": "^6.7.5",
31
+ "@vitejs/plugin-react": "2.2.0",
32
+ "chokidar": "^3.5.3",
33
+ "cross-env": "^7.0.3",
34
+ "fs-extra": "11.1.0",
35
+ "lint-staged": "^14.0.1",
36
+ "npm-run-all": "^4.1.5",
37
+ "prettier": "^2.8.8",
38
+ "rollup": "2.79.1",
39
+ "sass": "1.62.1",
40
+ "ts-jest": "29.0.2",
41
+ "ts-loader": "9.4.2",
42
+ "tslib": "^2.6.2",
43
+ "typescript": "4.9.5",
44
+ "vite": "3.2.7",
45
+ "ws": "8.13.0"
46
+ },
47
+ "scripts": {
48
+ "build": "vite build",
49
+ "build:firefox": "tsc --noEmit && cross-env __FIREFOX__=true vite build",
50
+ "build:watch": "cross-env __DEV__=true vite build -w --mode development",
51
+ "build:firefox:watch": "cross-env __DEV__=true __FIREFOX__=true vite build -w --mode development",
52
+ "build:hmr": "rollup --config utils/reload/rollup.config.ts",
53
+ "wss": "node utils/reload/initReloadServer.js",
54
+ "dev": "pnpm build:hmr && (run-p wss build:watch)",
55
+ "dev:firefox": "pnpm build:hmr && (run-p wss build:firefox:watch)"
56
+ }
57
+ }
Binary file
Binary file
@@ -0,0 +1,45 @@
1
+ {
2
+ "manifest_version": 3,
3
+ "name": "@shoplflow/extension",
4
+ "version": "0.0.2",
5
+ "description": "Shoplflow 점유율 확인 extension",
6
+ "permissions": [
7
+ "storage"
8
+ ],
9
+ "action": {
10
+ "default_popup": "src/pages/popup/index.html",
11
+ "default_icon": "icon-34.png"
12
+ },
13
+ "icons": {
14
+ "128": "icon-128.png"
15
+ },
16
+ "content_scripts": [
17
+ {
18
+ "matches": [
19
+ "http://*/*",
20
+ "https://*/*",
21
+ "<all_urls>"
22
+ ],
23
+ "js": [
24
+ "src/pages/content/index.js"
25
+ ],
26
+ "css": [
27
+ "assets/css/contentStyle17107484687.chunk.css"
28
+ ]
29
+ }
30
+ ],
31
+ "devtools_page": "src/pages/devtools/index.html",
32
+ "web_accessible_resources": [
33
+ {
34
+ "resources": [
35
+ "assets/js/*.js",
36
+ "assets/css/*.css",
37
+ "icon-128.png",
38
+ "icon-34.png"
39
+ ],
40
+ "matches": [
41
+ "*://*/*"
42
+ ]
43
+ }
44
+ ]
45
+ }
@@ -0,0 +1,35 @@
1
+ import React from 'react';
2
+ import { ColorTokens, Stack, Text } from '@shoplflow/base';
3
+ interface ElementListCardProps {
4
+ title: string;
5
+ value: string;
6
+ valueColor?: ColorTokens;
7
+ }
8
+
9
+ const ElementListCard = ({ title, value, valueColor }: ElementListCardProps) => {
10
+ return (
11
+ <Stack.Horizontal
12
+ width={'100%'}
13
+ height={'20px'}
14
+ justify={'space-between'}
15
+ align={'center'}
16
+ spacing={'spacing04'}
17
+ style={{
18
+ padding: '16px 20px',
19
+ boxSizing: 'border-box',
20
+ }}>
21
+ <Text
22
+ typography={'body1_400'}
23
+ lineClamp={1}
24
+ color={valueColor}
25
+ style={{
26
+ width: '100px',
27
+ }}>
28
+ {title}
29
+ </Text>
30
+ <Text typography={'body1_700'}>{value}</Text>
31
+ </Stack.Horizontal>
32
+ );
33
+ };
34
+
35
+ export default ElementListCard;
@@ -0,0 +1,7 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3">
2
+ <g fill="#61DAFB">
3
+ <path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/>
4
+ <circle cx="420.9" cy="296.5" r="45.7"/>
5
+ <path d="M520.5 78.1z"/>
6
+ </g>
7
+ </svg>
@@ -0,0 +1,10 @@
1
+ declare global {
2
+ namespace NodeJS {
3
+ interface ProcessEnv {
4
+ __DEV__: string;
5
+ __FIREFOX__: string;
6
+ }
7
+ }
8
+ }
9
+
10
+ export {};
@@ -0,0 +1,37 @@
1
+ import Chrome from 'chrome';
2
+
3
+ declare namespace chrome {
4
+ export default Chrome;
5
+ }
6
+
7
+ declare module 'virtual:reload-on-update-in-background-script' {
8
+ export const reloadOnUpdate: (watchPath: string) => void;
9
+ export default reloadOnUpdate;
10
+ }
11
+
12
+ declare module 'virtual:reload-on-update-in-view' {
13
+ const refreshOnUpdate: (watchPath: string) => void;
14
+ export default refreshOnUpdate;
15
+ }
16
+
17
+ declare module '*.svg' {
18
+ import React = require('react');
19
+ export const ReactComponent: React.SFC<React.SVGProps<SVGSVGElement>>;
20
+ const src: string;
21
+ export default src;
22
+ }
23
+
24
+ declare module '*.jpg' {
25
+ const content: string;
26
+ export default content;
27
+ }
28
+
29
+ declare module '*.png' {
30
+ const content: string;
31
+ export default content;
32
+ }
33
+
34
+ declare module '*.json' {
35
+ const content: string;
36
+ export default content;
37
+ }
@@ -0,0 +1,11 @@
1
+ import reloadOnUpdate from 'virtual:reload-on-update-in-background-script';
2
+
3
+ reloadOnUpdate('pages/background');
4
+
5
+ /**
6
+ * Extension reloading is necessary because the browser automatically caches the css.
7
+ * If you do not use the css of the content script, please delete it.
8
+ */
9
+ reloadOnUpdate('pages/content/style.scss');
10
+
11
+ console.log('background loaded');
@@ -0,0 +1,93 @@
1
+ import { useEffect, useRef } from 'react';
2
+ import componentsInfoStorage from '@src/shared/storages/componentsInfoStorage';
3
+ import useStorage from '@src/shared/hooks/useStorage';
4
+ import { checkShopl } from '@root/utils/checkShopl';
5
+
6
+ export default function App() {
7
+ const { refresh } = useStorage(componentsInfoStorage);
8
+ const observer = useRef(null);
9
+ function accessIframeContent(rootElement: HTMLElement) {
10
+ const components: HTMLElement[] = [];
11
+
12
+ function traverseNode(node: HTMLElement) {
13
+ if (node.id === 'root') {
14
+ Array.from(node.children).forEach(traverseNode);
15
+ return;
16
+ }
17
+
18
+ components.push(node);
19
+
20
+ if (node.tagName === 'IFRAME') {
21
+ const iframe = (node as HTMLIFrameElement).contentDocument;
22
+ traverseNode(iframe.body);
23
+ return;
24
+ }
25
+
26
+ Array.from(node.children).forEach(traverseNode);
27
+ }
28
+
29
+ traverseNode(rootElement);
30
+
31
+ return components;
32
+ }
33
+
34
+ const handleRenderComplete = async () => {
35
+ let rootElement = document.querySelector('#root');
36
+
37
+ let result: HTMLElement[] = [];
38
+
39
+ // If #root exists, search elements after #root
40
+ if (rootElement) {
41
+ result = accessIframeContent(rootElement);
42
+ } else {
43
+ // If #root doesn't exist, search all elements in body
44
+ rootElement = document.body;
45
+ result = accessIframeContent(rootElement);
46
+ }
47
+
48
+ // Update component info storage
49
+ const system = result.filter((element) => Boolean(element.dataset?.shoplflow));
50
+
51
+ if (system.length < 1) {
52
+ return;
53
+ }
54
+
55
+ const filterElements = system?.reduce((acc, element) => {
56
+ const elementName = element.dataset.shoplflow;
57
+ return {
58
+ ...acc,
59
+ [elementName]: elementName in acc ? acc[elementName] + 1 : 1,
60
+ };
61
+ });
62
+ if (system) {
63
+ await chrome.storage.local.set({
64
+ systems: system.length,
65
+ components: result.length,
66
+ systemElement: filterElements,
67
+ });
68
+ }
69
+ };
70
+
71
+ useEffect(() => {
72
+ const origin = window.location.origin;
73
+ if (!checkShopl(origin)) {
74
+ return;
75
+ }
76
+ handleRenderComplete();
77
+ observer.current = new MutationObserver(handleRenderComplete);
78
+
79
+ observer.current.observe(document.body || document.querySelector('#root'), {
80
+ childList: true,
81
+ attributes: true,
82
+ subtree: true,
83
+ });
84
+
85
+ return () => {
86
+ if (observer.current) {
87
+ observer.current.disconnect();
88
+ }
89
+ };
90
+ }, [handleRenderComplete, refresh]);
91
+
92
+ return null;
93
+ }
@@ -0,0 +1,27 @@
1
+ import { createRoot } from 'react-dom/client';
2
+ import App from '@src/pages/content/components/Demo/app';
3
+ import refreshOnUpdate from 'virtual:reload-on-update-in-view';
4
+ import { attachTwindStyle } from '@src/shared/style/twind';
5
+
6
+ refreshOnUpdate('pages/content');
7
+
8
+ const root = document.createElement('div');
9
+ root.id = 'chrome-extension-boilerplate-react-vite-content-view-root';
10
+
11
+ document.body.append(root);
12
+
13
+ const rootIntoShadow = document.createElement('div');
14
+ rootIntoShadow.id = 'shadow-root';
15
+
16
+ const shadowRoot = root.attachShadow({ mode: 'open' });
17
+ shadowRoot.appendChild(rootIntoShadow);
18
+
19
+ /**
20
+ * https://github.com/Jonghakseo/chrome-extension-boilerplate-react-vite/pull/174
21
+ *
22
+ * In the firefox environment, the adoptedStyleSheets bug may prevent contentStyle from being applied properly.
23
+ * Please refer to the PR link above and go back to the contentStyle.css implementation, or raise a PR if you have a better way to improve it.
24
+ */
25
+ attachTwindStyle(rootIntoShadow, shadowRoot);
26
+
27
+ createRoot(rootIntoShadow).render(<App />);
@@ -0,0 +1,7 @@
1
+ console.log('content loaded');
2
+
3
+ /**
4
+ * @description
5
+ * Chrome extensions don't support modules in content scripts.
6
+ */
7
+ import('./components/Demo');
@@ -0,0 +1,7 @@
1
+
2
+
3
+ // CSS that can pollute global styles. Use it with caution.
4
+ // If you use ShodowRoot for style pollution, this style will not be applied inside the content script.
5
+ .content-view-style-may-be-not-working {
6
+ font-size: 30px;
7
+ }
@@ -0,0 +1,8 @@
1
+ .container {
2
+ width: 100%;
3
+ height: 50vh;
4
+ font-size: 2rem;
5
+ display: flex;
6
+ align-items: center;
7
+ justify-content: center;
8
+ }
@@ -0,0 +1,8 @@
1
+ import React from 'react';
2
+ import '@pages/options/Options.css';
3
+
4
+ const Options: React.FC = () => {
5
+ return <div className="container text-lime-400">Options</div>;
6
+ };
7
+
8
+ export default Options;
File without changes
@@ -0,0 +1,12 @@
1
+ <!DOCTYPE html>
2
+ <html lang="ko">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <title>Options</title>
6
+ </head>
7
+
8
+ <body>
9
+ <div id="app-container"></div>
10
+ <script type="module" src="./index.tsx"></script>
11
+ </body>
12
+ </html>
@@ -0,0 +1,18 @@
1
+ import React from 'react';
2
+ import { createRoot } from 'react-dom/client';
3
+ import Options from '@pages/options/Options';
4
+ import '@pages/options/index.css';
5
+ import refreshOnUpdate from 'virtual:reload-on-update-in-view';
6
+
7
+ refreshOnUpdate('pages/options');
8
+
9
+ function init() {
10
+ const appContainer = document.querySelector('#app-container');
11
+ if (!appContainer) {
12
+ throw new Error('Can not find #app-container');
13
+ }
14
+ const root = createRoot(appContainer);
15
+ root.render(<Options />);
16
+ }
17
+
18
+ init();
@@ -0,0 +1,7 @@
1
+ body {
2
+ background-color: #242424;
3
+ }
4
+
5
+ .container {
6
+ color: #ffffff;
7
+ }
@@ -0,0 +1,65 @@
1
+ import React, { useEffect, useState } from 'react';
2
+ import '@pages/panel/Panel.css';
3
+ import { ArcElement, Chart as ChartJS, Legend, Tooltip } from 'chart.js';
4
+ import { ShoplflowProvider, Stack, Text } from '@shoplflow/base';
5
+ import { Doughnut } from 'react-chartjs-2';
6
+ ChartJS.register(Legend, ArcElement, Tooltip);
7
+ const Panel: React.FC = () => {
8
+ const [{ systems, components, systemElement }, setData] = useState({
9
+ systems: 0,
10
+ components: 0,
11
+ });
12
+
13
+ useEffect(() => {
14
+ (async () => {
15
+ await chrome.storage.local.get(['systems', 'components', 'systemElement'], result => setData(result));
16
+ })();
17
+ }, []);
18
+
19
+ const percentage = (systems / components) * 100;
20
+
21
+ const data = {
22
+ labels: ['HTML 요소', 'shoplflow'],
23
+ datasets: [
24
+ {
25
+ label: '# 컴포넌트 개수',
26
+ data: [components - systems, systems],
27
+ backgroundColor: ['#3eb0b8', '#84c2fc'],
28
+ borderColor: ['#2f839f', '#3299fe'],
29
+ borderWidth: 1,
30
+ },
31
+ ],
32
+ };
33
+
34
+ console.log(systemElement);
35
+ return (
36
+ <ShoplflowProvider>
37
+ <Stack.Horizontal width={'100%'} align={'center'} spacing={'spacing20'}>
38
+ <Stack.Vertical height={'fit-content'} width={'200px'}>
39
+ <Doughnut data={data} />
40
+ </Stack.Vertical>
41
+ <Stack.Vertical width={'100%'} justify={'center'} align={'center'} height={'20px'}>
42
+ <Text typography={'title1_700'}>shoplflow 점유율 {percentage.toFixed(2)}%</Text>
43
+ <Stack.Horizontal width={'100%'} justify={'center'} align={'center'} height={'20px'}>
44
+ <Text typography={'caption_400'}>전체 컴포넌트 {components}</Text>
45
+ <Text typography={'caption_400'}>시스템 컴포넌트 {systems}</Text>
46
+ </Stack.Horizontal>
47
+ <Stack.Vertical>
48
+ {Object.keys(systemElement)?.map(key => (
49
+ <Stack.Horizontal key={key} width={'100%'} justify={'center'} align={'center'} height={'20px'}>
50
+ <Text typography={'caption_400'}>{key}</Text>
51
+ <Text typography={'caption_400'}>{systemElement[key]}개</Text>
52
+ </Stack.Horizontal>
53
+ ))}
54
+ </Stack.Vertical>
55
+ <Stack>{JSON.stringify(systemElement)}</Stack>
56
+ </Stack.Vertical>
57
+ <Stack.Vertical width={'100%'} justify={'center'} align={'end'} height={'20px'}>
58
+ <Text typography={'caption_400'}>@shoplflow</Text>
59
+ </Stack.Vertical>
60
+ </Stack.Horizontal>
61
+ </ShoplflowProvider>
62
+ );
63
+ };
64
+
65
+ export default Panel;
File without changes
@@ -0,0 +1,12 @@
1
+ <!DOCTYPE html>
2
+ <html lang="ko">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <title>Devtools Panel</title>
6
+ </head>
7
+
8
+ <body>
9
+ <div id="app-container"></div>
10
+ <script type="module" src="./index.tsx"></script>
11
+ </body>
12
+ </html>
@@ -0,0 +1,20 @@
1
+ import React from 'react';
2
+ import { createRoot } from 'react-dom/client';
3
+ import Panel from '@pages/panel/Panel';
4
+ import '@pages/panel/index.css';
5
+ import refreshOnUpdate from 'virtual:reload-on-update-in-view';
6
+ import { attachTwindStyle } from '@src/shared/style/twind';
7
+
8
+ refreshOnUpdate('pages/panel');
9
+
10
+ function init() {
11
+ const appContainer = document.querySelector('#app-container');
12
+ if (!appContainer) {
13
+ throw new Error('Can not find #app-container');
14
+ }
15
+ attachTwindStyle(appContainer, document);
16
+ const root = createRoot(appContainer);
17
+ root.render(<Panel />);
18
+ }
19
+
20
+ init();
@@ -0,0 +1,44 @@
1
+ .App {
2
+ position: absolute;
3
+ top: 0;
4
+ bottom: 0;
5
+ left: 0;
6
+ right: 0;
7
+ text-align: center;
8
+ padding: 20px;
9
+ background-color: var(--neutral100);
10
+ }
11
+
12
+ .App-logo {
13
+ height: 30vmin;
14
+ pointer-events: none;
15
+ }
16
+
17
+ @media (prefers-reduced-motion: no-preference) {
18
+ .App-logo {
19
+ animation: App-logo-spin infinite 20s linear;
20
+ }
21
+ }
22
+
23
+ .App-header {
24
+ height: 100%;
25
+ display: flex;
26
+ flex-direction: column;
27
+ align-items: center;
28
+ justify-content: center;
29
+ font-size: calc(10px + 2vmin);
30
+ color: white;
31
+ }
32
+
33
+ .App-link {
34
+ color: #61dafb;
35
+ }
36
+
37
+ @keyframes App-logo-spin {
38
+ from {
39
+ transform: rotate(0deg);
40
+ }
41
+ to {
42
+ transform: rotate(360deg);
43
+ }
44
+ }