pixedi 1.0.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/.github/workflows/deploy.yml +54 -0
- package/.prettierr +9 -0
- package/.storybook/main.ts +16 -0
- package/.storybook/preview.ts +53 -0
- package/.vscode/extensions.json +6 -0
- package/README.md +75 -0
- package/eslint.config.js +22 -0
- package/index.html +13 -0
- package/package.json +57 -0
- package/pnpm-workspace.yaml +2 -0
- package/public/favicon.svg +1 -0
- package/src/app/App.tsx +10 -0
- package/src/app/entries/index.ts +145 -0
- package/src/app/providers/RouterProvider.tsx +6 -0
- package/src/app/providers/index.ts +1 -0
- package/src/app/router/AppRouter.tsx +12 -0
- package/src/app/router/index.ts +0 -0
- package/src/app/styles/index.css +4 -0
- package/src/features/gallery/GalleryPage.tsx +33 -0
- package/src/features/gallery/ImageEditPage.tsx +37 -0
- package/src/features/gallery/index.ts +2 -0
- package/src/main.tsx +10 -0
- package/src/shared/components/ImageCard/ImageCard.tsx +54 -0
- package/src/shared/components/ImageCard/index.ts +1 -0
- package/src/shared/components/ImageEditor/ImageEditor.tsx +48 -0
- package/src/shared/components/ImageEditor/assets/icons.tsx +114 -0
- package/src/shared/components/ImageEditor/constants.ts +118 -0
- package/src/shared/components/ImageEditor/crop/Crop.tsx +60 -0
- package/src/shared/components/ImageEditor/crop/CropBorders.tsx +28 -0
- package/src/shared/components/ImageEditor/crop/CropLines.tsx +8 -0
- package/src/shared/components/ImageEditor/crop/CropPointer.tsx +21 -0
- package/src/shared/components/ImageEditor/crop/CropPointers.tsx +44 -0
- package/src/shared/components/ImageEditor/crop/CropTools.tsx +138 -0
- package/src/shared/components/ImageEditor/crop/crop.css +180 -0
- package/src/shared/components/ImageEditor/crop/index.ts +8 -0
- package/src/shared/components/ImageEditor/crop/useCropInteraction.ts +230 -0
- package/src/shared/components/ImageEditor/eventBus.ts +8 -0
- package/src/shared/components/ImageEditor/frame/Frame.tsx +48 -0
- package/src/shared/components/ImageEditor/frame/frame.css +40 -0
- package/src/shared/components/ImageEditor/frame/index.ts +1 -0
- package/src/shared/components/ImageEditor/header/Header.tsx +81 -0
- package/src/shared/components/ImageEditor/header/header.css +27 -0
- package/src/shared/components/ImageEditor/header/index.ts +1 -0
- package/src/shared/components/ImageEditor/hooks/index.ts +5 -0
- package/src/shared/components/ImageEditor/hooks/useAbove.tsx +18 -0
- package/src/shared/components/ImageEditor/hooks/useBellow.tsx +18 -0
- package/src/shared/components/ImageEditor/hooks/useImageLoader.tsx +110 -0
- package/src/shared/components/ImageEditor/hooks/useImageProcessor.test.tsx +147 -0
- package/src/shared/components/ImageEditor/hooks/useImageProcessor.ts +225 -0
- package/src/shared/components/ImageEditor/hooks/useMobile.tsx +33 -0
- package/src/shared/components/ImageEditor/index.css +145 -0
- package/src/shared/components/ImageEditor/index.ts +1 -0
- package/src/shared/components/ImageEditor/provider/ImageEditorProvider.test.tsx +126 -0
- package/src/shared/components/ImageEditor/provider/ImageEditorProvider.tsx +107 -0
- package/src/shared/components/ImageEditor/provider/StoreContext.tsx +20 -0
- package/src/shared/components/ImageEditor/provider/initialState.ts +30 -0
- package/src/shared/components/ImageEditor/provider/useImageEditorContext.tsx +12 -0
- package/src/shared/components/ImageEditor/resize/ResizeTools.tsx +183 -0
- package/src/shared/components/ImageEditor/resize/index.ts +1 -0
- package/src/shared/components/ImageEditor/resize/resize.css +56 -0
- package/src/shared/components/ImageEditor/sidebar/Sidebar.tsx +54 -0
- package/src/shared/components/ImageEditor/sidebar/SidebarCrop.tsx +57 -0
- package/src/shared/components/ImageEditor/sidebar/SidebarPresets.tsx +54 -0
- package/src/shared/components/ImageEditor/sidebar/SidebarResize.tsx +26 -0
- package/src/shared/components/ImageEditor/sidebar/index.ts +1 -0
- package/src/shared/components/ImageEditor/sidebar/sidebar.css +101 -0
- package/src/shared/components/ImageEditor/types.ts +59 -0
- package/src/shared/components/ImageEditor/ui/button/Button.stories.tsx +42 -0
- package/src/shared/components/ImageEditor/ui/button/Button.tsx +21 -0
- package/src/shared/components/ImageEditor/ui/button/button.css +69 -0
- package/src/shared/components/ImageEditor/ui/button-crop/ButtonCrop.stories.tsx +26 -0
- package/src/shared/components/ImageEditor/ui/button-crop/ButtonCrop.tsx +29 -0
- package/src/shared/components/ImageEditor/ui/button-crop/button-crop.css +38 -0
- package/src/shared/components/ImageEditor/ui/index.ts +6 -0
- package/src/shared/components/ImageEditor/ui/input/Input.stories.tsx +45 -0
- package/src/shared/components/ImageEditor/ui/input/Input.tsx +20 -0
- package/src/shared/components/ImageEditor/ui/input/input.css +48 -0
- package/src/shared/components/ImageEditor/ui/input-pixel/InputPixel.stories.tsx +33 -0
- package/src/shared/components/ImageEditor/ui/input-pixel/InputPixel.tsx +23 -0
- package/src/shared/components/ImageEditor/ui/input-pixel/input-pixel.css +24 -0
- package/src/shared/components/ImageEditor/ui/select/Select.stories.tsx +67 -0
- package/src/shared/components/ImageEditor/ui/select/Select.tsx +161 -0
- package/src/shared/components/ImageEditor/ui/select/select.css +147 -0
- package/src/shared/components/ImageEditor/ui/separator/Separator.stories.tsx +37 -0
- package/src/shared/components/ImageEditor/ui/separator/Separator.tsx +27 -0
- package/src/shared/components/ImageEditor/ui/separator/separator.css +19 -0
- package/src/shared/components/ImageEditor/utils.test.ts +136 -0
- package/src/shared/components/ImageEditor/utils.ts +497 -0
- package/src/shared/components/ui/index.ts +1 -0
- package/src/shared/components/ui/preloader.tsx +78 -0
- package/src/shared/config/index.ts +3 -0
- package/src/shared/config/routes.ts +4 -0
- package/src/shared/features/gallery/GalleryPage.tsx +33 -0
- package/src/shared/features/gallery/ImageEditPage.tsx +37 -0
- package/src/shared/features/gallery/index.ts +2 -0
- package/src/shared/hooks/index.ts +1 -0
- package/src/shared/hooks/useImagePreload.tsx +49 -0
- package/src/shared/types/index.ts +11 -0
- package/src/test/setup.ts +14 -0
- package/src/virtual-css-injected-by-js.d.ts +8 -0
- package/src/widget.tsx +119 -0
- package/tsconfig.app.json +29 -0
- package/tsconfig.json +12 -0
- package/tsconfig.lib.json +18 -0
- package/tsconfig.node.json +23 -0
- package/vite-env.d.ts +1 -0
- package/vite.config.ts +24 -0
- package/vite.lib.config.ts +33 -0
- package/vite.widget.config.ts +39 -0
- package/wrangler.json +5 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import "@testing-library/jest-dom/vitest";
|
|
2
|
+
import { vi } from "vitest";
|
|
3
|
+
|
|
4
|
+
vi.stubGlobal("createImageBitmap", vi.fn());
|
|
5
|
+
vi.spyOn(HTMLCanvasElement.prototype, "getContext").mockReturnValue(null);
|
|
6
|
+
vi.spyOn(HTMLCanvasElement.prototype, "toDataURL").mockReturnValue("");
|
|
7
|
+
|
|
8
|
+
if (!globalThis.ImageBitmap) {
|
|
9
|
+
class ImageBitmapMock {
|
|
10
|
+
close() {}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
vi.stubGlobal("ImageBitmap", ImageBitmapMock);
|
|
14
|
+
}
|
package/src/widget.tsx
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import ReactDOM from "react-dom/client";
|
|
3
|
+
import { injectCSS, removeCSS } from "virtual:css-injected-by-js";
|
|
4
|
+
import type { FuncSaveArgs } from "@/shared/components/ImageEditor/types";
|
|
5
|
+
import { ImageEditor } from "@/shared/components/ImageEditor";
|
|
6
|
+
|
|
7
|
+
type WidgetTheme = "light" | "dark";
|
|
8
|
+
|
|
9
|
+
interface WidgetOptions {
|
|
10
|
+
containerId: string;
|
|
11
|
+
image: string;
|
|
12
|
+
onSave: FuncSaveArgs;
|
|
13
|
+
onBack: () => void;
|
|
14
|
+
theme?: WidgetTheme;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface ImageEditorWidgetInstance {
|
|
18
|
+
destroy: () => void;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface ImageEditorWidget {
|
|
22
|
+
init: (options: WidgetOptions) => ImageEditorWidgetInstance | undefined;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface ActiveWidget {
|
|
26
|
+
root: ReactDOM.Root;
|
|
27
|
+
shadowRoot: ShadowRoot;
|
|
28
|
+
mountElement: HTMLDivElement;
|
|
29
|
+
container: HTMLElement;
|
|
30
|
+
previousTheme: string | null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
declare global {
|
|
34
|
+
interface Window {
|
|
35
|
+
ImageEditorWidget?: ImageEditorWidget;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
let activeWidget: ActiveWidget | null = null;
|
|
40
|
+
|
|
41
|
+
const destroyActiveWidget = () => {
|
|
42
|
+
if (!activeWidget) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
activeWidget.root.unmount();
|
|
47
|
+
removeCSS({ target: activeWidget.shadowRoot });
|
|
48
|
+
activeWidget.mountElement.remove();
|
|
49
|
+
|
|
50
|
+
if (activeWidget.previousTheme === null) {
|
|
51
|
+
activeWidget.container.removeAttribute("data-theme");
|
|
52
|
+
} else {
|
|
53
|
+
activeWidget.container.setAttribute(
|
|
54
|
+
"data-theme",
|
|
55
|
+
activeWidget.previousTheme,
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
activeWidget = null;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const ImageEditorWidget: ImageEditorWidget = {
|
|
63
|
+
init: (options) => {
|
|
64
|
+
const container = document.getElementById(options.containerId);
|
|
65
|
+
if (!container) return;
|
|
66
|
+
|
|
67
|
+
destroyActiveWidget();
|
|
68
|
+
|
|
69
|
+
let shadowRoot = container.shadowRoot;
|
|
70
|
+
if (!shadowRoot) {
|
|
71
|
+
try {
|
|
72
|
+
shadowRoot = container.attachShadow({ mode: "open" });
|
|
73
|
+
} catch {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const previousTheme = container.getAttribute("data-theme");
|
|
79
|
+
container.setAttribute("data-theme", options.theme ?? "light");
|
|
80
|
+
|
|
81
|
+
injectCSS({ target: shadowRoot });
|
|
82
|
+
|
|
83
|
+
const mountElement = document.createElement("div");
|
|
84
|
+
shadowRoot.append(mountElement);
|
|
85
|
+
|
|
86
|
+
const root = ReactDOM.createRoot(mountElement);
|
|
87
|
+
activeWidget = {
|
|
88
|
+
root,
|
|
89
|
+
shadowRoot,
|
|
90
|
+
mountElement,
|
|
91
|
+
container,
|
|
92
|
+
previousTheme,
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
root.render(
|
|
96
|
+
<React.StrictMode>
|
|
97
|
+
<ImageEditor
|
|
98
|
+
image={options.image}
|
|
99
|
+
onSave={options.onSave}
|
|
100
|
+
onBack={options.onBack}
|
|
101
|
+
/>
|
|
102
|
+
</React.StrictMode>,
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
destroy: () => {
|
|
107
|
+
if (activeWidget?.root === root) {
|
|
108
|
+
destroyActiveWidget();
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
if (typeof window !== "undefined") {
|
|
116
|
+
window.ImageEditorWidget = ImageEditorWidget;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export default ImageEditorWidget;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
|
4
|
+
"target": "es2023",
|
|
5
|
+
"lib": ["ES2023", "DOM"],
|
|
6
|
+
"module": "esnext",
|
|
7
|
+
"types": ["vite/client"],
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
|
|
10
|
+
/* Bundler mode */
|
|
11
|
+
"moduleResolution": "bundler",
|
|
12
|
+
"allowImportingTsExtensions": true,
|
|
13
|
+
"verbatimModuleSyntax": true,
|
|
14
|
+
"moduleDetection": "force",
|
|
15
|
+
"noEmit": true,
|
|
16
|
+
"jsx": "react-jsx",
|
|
17
|
+
|
|
18
|
+
/* Linting */
|
|
19
|
+
"noUnusedLocals": true,
|
|
20
|
+
"noUnusedParameters": true,
|
|
21
|
+
"erasableSyntaxOnly": true,
|
|
22
|
+
"noFallthroughCasesInSwitch": true,
|
|
23
|
+
|
|
24
|
+
"paths": {
|
|
25
|
+
"@/*": ["./src/*"]
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"include": ["src"]
|
|
29
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "./tsconfig.app.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"declaration": true,
|
|
5
|
+
"emitDeclarationOnly": true,
|
|
6
|
+
"outDir": "./dist/lib",
|
|
7
|
+
"rootDir": "."
|
|
8
|
+
},
|
|
9
|
+
"include": ["src/shared/components/ImageEditor/**/*"],
|
|
10
|
+
"exclude": [
|
|
11
|
+
"src/shared/components/ImageEditor/**/*.stories.tsx",
|
|
12
|
+
"src/shared/components/ImageEditor/**/*.stories.ts",
|
|
13
|
+
"src/shared/components/ImageEditor/**/*.test.tsx",
|
|
14
|
+
"src/shared/components/ImageEditor/**/*.test.ts",
|
|
15
|
+
"src/shared/components/ImageEditor/**/*.spec.tsx",
|
|
16
|
+
"src/shared/components/ImageEditor/**/*.spec.ts"
|
|
17
|
+
]
|
|
18
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
|
4
|
+
"target": "es2023",
|
|
5
|
+
"lib": ["ES2023"],
|
|
6
|
+
"types": ["node"],
|
|
7
|
+
"skipLibCheck": true,
|
|
8
|
+
|
|
9
|
+
/* Bundler mode */
|
|
10
|
+
"module": "nodenext",
|
|
11
|
+
"allowImportingTsExtensions": true,
|
|
12
|
+
"verbatimModuleSyntax": true,
|
|
13
|
+
"moduleDetection": "force",
|
|
14
|
+
"noEmit": true,
|
|
15
|
+
|
|
16
|
+
/* Linting */
|
|
17
|
+
"noUnusedLocals": true,
|
|
18
|
+
"noUnusedParameters": true,
|
|
19
|
+
"erasableSyntaxOnly": true,
|
|
20
|
+
"noFallthroughCasesInSwitch": true
|
|
21
|
+
},
|
|
22
|
+
"include": ["vite.config.ts"]
|
|
23
|
+
}
|
package/vite-env.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/// <reference types="vite/client" />
|
package/vite.config.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
import { defineConfig } from "vitest/config";
|
|
3
|
+
import react from "@vitejs/plugin-react";
|
|
4
|
+
|
|
5
|
+
// https://vite.dev/config/
|
|
6
|
+
export default defineConfig({
|
|
7
|
+
plugins: [react()],
|
|
8
|
+
server: {
|
|
9
|
+
port: 3000,
|
|
10
|
+
},
|
|
11
|
+
resolve: {
|
|
12
|
+
alias: {
|
|
13
|
+
"@": path.resolve(__dirname, "./src"),
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
build: {
|
|
17
|
+
outDir: "dist/app",
|
|
18
|
+
minify: true,
|
|
19
|
+
},
|
|
20
|
+
test: {
|
|
21
|
+
environment: "jsdom",
|
|
22
|
+
setupFiles: ["./src/test/setup.ts"],
|
|
23
|
+
},
|
|
24
|
+
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
import { defineConfig } from "vite";
|
|
3
|
+
import react from "@vitejs/plugin-react";
|
|
4
|
+
import cssInjectedByJsPlugin from "vite-plugin-css-injected-by-js";
|
|
5
|
+
|
|
6
|
+
export default defineConfig({
|
|
7
|
+
plugins: [react(), cssInjectedByJsPlugin()],
|
|
8
|
+
publicDir: false,
|
|
9
|
+
resolve: {
|
|
10
|
+
alias: {
|
|
11
|
+
"@": path.resolve(__dirname, "./src"),
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
build: {
|
|
15
|
+
outDir: "dist/lib",
|
|
16
|
+
emptyOutDir: false,
|
|
17
|
+
lib: {
|
|
18
|
+
entry: "src/shared/components/ImageEditor/ImageEditor.tsx",
|
|
19
|
+
name: "Pixedi",
|
|
20
|
+
fileName: (format) => `index.${format === "es" ? "js" : "umd.js"}`,
|
|
21
|
+
formats: ["es", "umd"],
|
|
22
|
+
},
|
|
23
|
+
rollupOptions: {
|
|
24
|
+
external: ["react", "react-dom"],
|
|
25
|
+
output: {
|
|
26
|
+
globals: {
|
|
27
|
+
react: "React",
|
|
28
|
+
"react-dom": "ReactDOM",
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
});
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
import { defineConfig } from "vite";
|
|
3
|
+
import react from "@vitejs/plugin-react";
|
|
4
|
+
import cssInjectedByJsPlugin from "vite-plugin-css-injected-by-js";
|
|
5
|
+
|
|
6
|
+
export default defineConfig({
|
|
7
|
+
plugins: [
|
|
8
|
+
react(),
|
|
9
|
+
cssInjectedByJsPlugin({
|
|
10
|
+
attributes: { "data-image-editor-widget": "" },
|
|
11
|
+
}),
|
|
12
|
+
],
|
|
13
|
+
publicDir: false,
|
|
14
|
+
define: {
|
|
15
|
+
"process.env.NODE_ENV": '"production"',
|
|
16
|
+
},
|
|
17
|
+
resolve: {
|
|
18
|
+
alias: {
|
|
19
|
+
"@": path.resolve(__dirname, "./src"),
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
build: {
|
|
23
|
+
outDir: "dist/widget",
|
|
24
|
+
emptyOutDir: false,
|
|
25
|
+
minify: "oxc",
|
|
26
|
+
lib: {
|
|
27
|
+
entry: "src/widget.tsx",
|
|
28
|
+
name: "ImageEditorWidget",
|
|
29
|
+
formats: ["umd"],
|
|
30
|
+
},
|
|
31
|
+
rolldownOptions: {
|
|
32
|
+
output: {
|
|
33
|
+
comments: { legal: false },
|
|
34
|
+
entryFileNames: "pixedi-widget.js",
|
|
35
|
+
assetFileNames: "[name].[ext]",
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
});
|