pixedi 1.0.0 → 1.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 (113) hide show
  1. package/README.md +65 -60
  2. package/README.npm.md +80 -0
  3. package/dist/lib/index.js +1602 -0
  4. package/dist/lib/index.umd.js +28 -0
  5. package/package.json +9 -4
  6. package/.github/workflows/deploy.yml +0 -54
  7. package/.prettierr +0 -9
  8. package/.storybook/main.ts +0 -16
  9. package/.storybook/preview.ts +0 -53
  10. package/.vscode/extensions.json +0 -6
  11. package/eslint.config.js +0 -22
  12. package/index.html +0 -13
  13. package/pnpm-workspace.yaml +0 -2
  14. package/public/favicon.svg +0 -1
  15. package/src/app/App.tsx +0 -10
  16. package/src/app/entries/index.ts +0 -145
  17. package/src/app/providers/RouterProvider.tsx +0 -6
  18. package/src/app/providers/index.ts +0 -1
  19. package/src/app/router/AppRouter.tsx +0 -12
  20. package/src/app/router/index.ts +0 -0
  21. package/src/app/styles/index.css +0 -4
  22. package/src/features/gallery/GalleryPage.tsx +0 -33
  23. package/src/features/gallery/ImageEditPage.tsx +0 -37
  24. package/src/features/gallery/index.ts +0 -2
  25. package/src/main.tsx +0 -10
  26. package/src/shared/components/ImageCard/ImageCard.tsx +0 -54
  27. package/src/shared/components/ImageCard/index.ts +0 -1
  28. package/src/shared/components/ImageEditor/ImageEditor.tsx +0 -48
  29. package/src/shared/components/ImageEditor/assets/icons.tsx +0 -114
  30. package/src/shared/components/ImageEditor/constants.ts +0 -118
  31. package/src/shared/components/ImageEditor/crop/Crop.tsx +0 -60
  32. package/src/shared/components/ImageEditor/crop/CropBorders.tsx +0 -28
  33. package/src/shared/components/ImageEditor/crop/CropLines.tsx +0 -8
  34. package/src/shared/components/ImageEditor/crop/CropPointer.tsx +0 -21
  35. package/src/shared/components/ImageEditor/crop/CropPointers.tsx +0 -44
  36. package/src/shared/components/ImageEditor/crop/CropTools.tsx +0 -138
  37. package/src/shared/components/ImageEditor/crop/crop.css +0 -180
  38. package/src/shared/components/ImageEditor/crop/index.ts +0 -8
  39. package/src/shared/components/ImageEditor/crop/useCropInteraction.ts +0 -230
  40. package/src/shared/components/ImageEditor/eventBus.ts +0 -8
  41. package/src/shared/components/ImageEditor/frame/Frame.tsx +0 -48
  42. package/src/shared/components/ImageEditor/frame/frame.css +0 -40
  43. package/src/shared/components/ImageEditor/frame/index.ts +0 -1
  44. package/src/shared/components/ImageEditor/header/Header.tsx +0 -81
  45. package/src/shared/components/ImageEditor/header/header.css +0 -27
  46. package/src/shared/components/ImageEditor/header/index.ts +0 -1
  47. package/src/shared/components/ImageEditor/hooks/index.ts +0 -5
  48. package/src/shared/components/ImageEditor/hooks/useAbove.tsx +0 -18
  49. package/src/shared/components/ImageEditor/hooks/useBellow.tsx +0 -18
  50. package/src/shared/components/ImageEditor/hooks/useImageLoader.tsx +0 -110
  51. package/src/shared/components/ImageEditor/hooks/useImageProcessor.test.tsx +0 -147
  52. package/src/shared/components/ImageEditor/hooks/useImageProcessor.ts +0 -225
  53. package/src/shared/components/ImageEditor/hooks/useMobile.tsx +0 -33
  54. package/src/shared/components/ImageEditor/index.css +0 -145
  55. package/src/shared/components/ImageEditor/index.ts +0 -1
  56. package/src/shared/components/ImageEditor/provider/ImageEditorProvider.test.tsx +0 -126
  57. package/src/shared/components/ImageEditor/provider/ImageEditorProvider.tsx +0 -107
  58. package/src/shared/components/ImageEditor/provider/StoreContext.tsx +0 -20
  59. package/src/shared/components/ImageEditor/provider/initialState.ts +0 -30
  60. package/src/shared/components/ImageEditor/provider/useImageEditorContext.tsx +0 -12
  61. package/src/shared/components/ImageEditor/resize/ResizeTools.tsx +0 -183
  62. package/src/shared/components/ImageEditor/resize/index.ts +0 -1
  63. package/src/shared/components/ImageEditor/resize/resize.css +0 -56
  64. package/src/shared/components/ImageEditor/sidebar/Sidebar.tsx +0 -54
  65. package/src/shared/components/ImageEditor/sidebar/SidebarCrop.tsx +0 -57
  66. package/src/shared/components/ImageEditor/sidebar/SidebarPresets.tsx +0 -54
  67. package/src/shared/components/ImageEditor/sidebar/SidebarResize.tsx +0 -26
  68. package/src/shared/components/ImageEditor/sidebar/index.ts +0 -1
  69. package/src/shared/components/ImageEditor/sidebar/sidebar.css +0 -101
  70. package/src/shared/components/ImageEditor/types.ts +0 -59
  71. package/src/shared/components/ImageEditor/ui/button/Button.stories.tsx +0 -42
  72. package/src/shared/components/ImageEditor/ui/button/Button.tsx +0 -21
  73. package/src/shared/components/ImageEditor/ui/button/button.css +0 -69
  74. package/src/shared/components/ImageEditor/ui/button-crop/ButtonCrop.stories.tsx +0 -26
  75. package/src/shared/components/ImageEditor/ui/button-crop/ButtonCrop.tsx +0 -29
  76. package/src/shared/components/ImageEditor/ui/button-crop/button-crop.css +0 -38
  77. package/src/shared/components/ImageEditor/ui/index.ts +0 -6
  78. package/src/shared/components/ImageEditor/ui/input/Input.stories.tsx +0 -45
  79. package/src/shared/components/ImageEditor/ui/input/Input.tsx +0 -20
  80. package/src/shared/components/ImageEditor/ui/input/input.css +0 -48
  81. package/src/shared/components/ImageEditor/ui/input-pixel/InputPixel.stories.tsx +0 -33
  82. package/src/shared/components/ImageEditor/ui/input-pixel/InputPixel.tsx +0 -23
  83. package/src/shared/components/ImageEditor/ui/input-pixel/input-pixel.css +0 -24
  84. package/src/shared/components/ImageEditor/ui/select/Select.stories.tsx +0 -67
  85. package/src/shared/components/ImageEditor/ui/select/Select.tsx +0 -161
  86. package/src/shared/components/ImageEditor/ui/select/select.css +0 -147
  87. package/src/shared/components/ImageEditor/ui/separator/Separator.stories.tsx +0 -37
  88. package/src/shared/components/ImageEditor/ui/separator/Separator.tsx +0 -27
  89. package/src/shared/components/ImageEditor/ui/separator/separator.css +0 -19
  90. package/src/shared/components/ImageEditor/utils.test.ts +0 -136
  91. package/src/shared/components/ImageEditor/utils.ts +0 -497
  92. package/src/shared/components/ui/index.ts +0 -1
  93. package/src/shared/components/ui/preloader.tsx +0 -78
  94. package/src/shared/config/index.ts +0 -3
  95. package/src/shared/config/routes.ts +0 -4
  96. package/src/shared/features/gallery/GalleryPage.tsx +0 -33
  97. package/src/shared/features/gallery/ImageEditPage.tsx +0 -37
  98. package/src/shared/features/gallery/index.ts +0 -2
  99. package/src/shared/hooks/index.ts +0 -1
  100. package/src/shared/hooks/useImagePreload.tsx +0 -49
  101. package/src/shared/types/index.ts +0 -11
  102. package/src/test/setup.ts +0 -14
  103. package/src/virtual-css-injected-by-js.d.ts +0 -8
  104. package/src/widget.tsx +0 -119
  105. package/tsconfig.app.json +0 -29
  106. package/tsconfig.json +0 -12
  107. package/tsconfig.lib.json +0 -18
  108. package/tsconfig.node.json +0 -23
  109. package/vite-env.d.ts +0 -1
  110. package/vite.config.ts +0 -24
  111. package/vite.lib.config.ts +0 -33
  112. package/vite.widget.config.ts +0 -39
  113. package/wrangler.json +0 -5
@@ -1,145 +0,0 @@
1
- .picedi {
2
- font-size: 16px;
3
- font-family: var(--font-sans);
4
-
5
- --font-sans:
6
- system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Tahoma,
7
- Helvetica, Arial, sans-serif;
8
-
9
- --color-white: #fff;
10
- --color-black: #000;
11
- --color-transparent: transparent;
12
- --color-red: oklch(0.637 0.237 25.331);
13
- --color-green: oklch(0.768 0.188 151.716);
14
-
15
- --background: oklch(1 0 0);
16
- --foreground: oklch(0.145 0 0);
17
- --card: oklch(1 0 0);
18
- --card-foreground: oklch(0.145 0 0);
19
- --popover: oklch(1 0 0);
20
- --popover-foreground: oklch(0.145 0 0);
21
- --primary: oklch(0.205 0 0);
22
- --primary-foreground: oklch(0.985 0 0);
23
- --secondary: oklch(0.97 0 0);
24
- --secondary-foreground: oklch(0.205 0 0);
25
- --muted: oklch(0.97 0 0);
26
- --muted-foreground: oklch(0.556 0 0);
27
- --accent: oklch(0.97 0 0);
28
- --accent-foreground: oklch(0.205 0 0);
29
- --accent-blue: oklch(0.609 0.221 251.687);
30
- --accent-blue-light: oklch(0.96 0.02 250);
31
- --destructive: oklch(0.577 0.245 27.325);
32
- --success: oklch(0.768 0.188 151.716);
33
- --border: oklch(0.922 0 0);
34
- --input: oklch(0.922 0 0);
35
- --ring: oklch(0.708 0 0);
36
- --radius: 4px;
37
- --sidebar: oklch(0.985 0 0);
38
- --sidebar-foreground: oklch(0.145 0 0);
39
- --sidebar-primary: oklch(0.205 0 0);
40
- --sidebar-primary-foreground: oklch(0.985 0 0);
41
- --sidebar-accent: oklch(0.97 0 0);
42
- --sidebar-accent-foreground: oklch(0.205 0 0);
43
- --sidebar-border: oklch(0.922 0 0);
44
- --sidebar-ring: oklch(0.708 0 0);
45
- --overlay-bg: color-mix(in srgb, var(--color-black) 50%, transparent);
46
- --overlay-bg-white: color-mix(in srgb, var(--color-white) 80%, transparent);
47
- }
48
-
49
- :host([data-theme="dark"]) .picedi,
50
- [data-theme="dark"] .picedi,
51
- [data-theme="dark"].picedi {
52
- --background: oklch(0.145 0 0);
53
- --foreground: oklch(0.985 0 0);
54
- --card: oklch(0.205 0 0);
55
- --card-foreground: oklch(0.985 0 0);
56
- --popover: oklch(0.205 0 0);
57
- --popover-foreground: oklch(0.985 0 0);
58
- --primary: oklch(0.922 0 0);
59
- --primary-foreground: oklch(0.205 0 0);
60
- --secondary: oklch(0.269 0 0);
61
- --secondary-foreground: oklch(0.985 0 0);
62
- --muted: oklch(0.269 0 0);
63
- --muted-foreground: oklch(0.708 0 0);
64
- --accent: oklch(0.269 0 0);
65
- --accent-foreground: oklch(0.985 0 0);
66
- --accent-blue-light: oklch(0.25 0.06 250);
67
- --destructive: oklch(0.704 0.191 22.216);
68
- --border: oklch(1 0 0 / 10%);
69
- --input: oklch(1 0 0 / 15%);
70
- --ring: oklch(0.556 0 0);
71
- --sidebar-foreground: oklch(0.985 0 0);
72
- --sidebar-primary: oklch(0.488 0.243 264.376);
73
- --sidebar-primary-foreground: oklch(0.985 0 0);
74
- --sidebar-accent: oklch(0.269 0 0);
75
- --sidebar-accent-foreground: oklch(0.985 0 0);
76
- --sidebar-border: oklch(1 0 0 / 10%);
77
- --sidebar-ring: oklch(0.556 0 0);
78
- --overlay-bg: color-mix(in srgb, var(--color-black) 75%, transparent);
79
- }
80
-
81
- .picedi h1,
82
- .picedi h2,
83
- .picedi h3,
84
- .picedi h4,
85
- .picedi h5,
86
- .picedi h6 {
87
- margin: 0;
88
- font-weight: 600;
89
- }
90
-
91
- .text-red {
92
- color: var(--color-red);
93
- }
94
-
95
- .bg-red {
96
- background-color: var(--color-red);
97
- }
98
-
99
- .bg-green {
100
- background-color: var(--color-green);
101
- }
102
-
103
- .text-green {
104
- color: var(--color-green);
105
- }
106
-
107
- .mask {
108
- background-color: var(--overlay-bg);
109
- }
110
-
111
- .semibold {
112
- font-weight: 600;
113
- letter-spacing: 0.4px;
114
- }
115
-
116
- .bold {
117
- font-weight: bold;
118
- letter-spacing: 0.4px;
119
- }
120
-
121
- .sys {
122
- position: relative;
123
- width: 100%;
124
- height: 100%;
125
- display: flex;
126
- justify-content: center;
127
- align-items: center;
128
- color: var(--muted-foreground);
129
- }
130
-
131
- .main {
132
- display: grid;
133
- grid-template-rows: auto 1fr;
134
- height: 100%;
135
- overflow: hidden;
136
- background-color: var(--background);
137
- }
138
-
139
- .grid {
140
- position: relative;
141
- display: grid;
142
- grid-template-columns: 1fr auto;
143
- min-height: 0;
144
- overflow: hidden;
145
- }
@@ -1 +0,0 @@
1
- export { ImageEditor } from "./ImageEditor";
@@ -1,126 +0,0 @@
1
- import { act, renderHook } from "@testing-library/react";
2
- import type { PropsWithChildren } from "react";
3
- import { describe, expect, it } from "vitest";
4
- import { ImageEditorProvider } from "./ImageEditorProvider";
5
- import { useImageEditorContext } from "./useImageEditorContext";
6
-
7
- const initialItem = {
8
- name: "Initial",
9
- base64: "data:image/png;base64,initial",
10
- width: 800,
11
- height: 600,
12
- };
13
-
14
- const editedItem = {
15
- name: "Resize",
16
- base64: "data:image/png;base64,edited",
17
- width: 400,
18
- height: 300,
19
- };
20
-
21
- const secondEditedItem = {
22
- name: "Crop",
23
- base64: "data:image/png;base64,cropped",
24
- width: 200,
25
- height: 200,
26
- };
27
-
28
- const wrapper = ({ children }: PropsWithChildren) => (
29
- <ImageEditorProvider
30
- base64={initialItem.base64}
31
- width={initialItem.width}
32
- height={initialItem.height}
33
- ext="png"
34
- >
35
- {children}
36
- </ImageEditorProvider>
37
- );
38
-
39
- describe("ImageEditorProvider", () => {
40
- it("initializes the image history and editor state", () => {
41
- const { result } = renderHook(() => useImageEditorContext(), { wrapper });
42
-
43
- expect(result.current.history).toEqual({ items: [initialItem], pointer: 0 });
44
- expect(result.current.extension).toBe("png");
45
- expect(result.current.currentAction).toBeNull();
46
- expect(result.current.getSidebar()).toBe(false);
47
- expect(result.current.getLastHistoryItem()).toEqual(initialItem);
48
- });
49
-
50
- it("updates the selected action and sidebar visibility", () => {
51
- const { result } = renderHook(() => useImageEditorContext(), { wrapper });
52
- const action = {
53
- name: "resize" as const,
54
- args: { width: 400, height: 300 },
55
- };
56
-
57
- act(() => {
58
- result.current.setCurrentAction(action);
59
- result.current.setSidebar(true);
60
- });
61
-
62
- expect(result.current.getCurrentAction()).toEqual(action);
63
- expect(result.current.getSidebar()).toBe(true);
64
- });
65
-
66
- it("adds edits, clears the action, and discards redo history", () => {
67
- const { result } = renderHook(() => useImageEditorContext(), { wrapper });
68
-
69
- act(() => {
70
- result.current.setCurrentAction({
71
- name: "resize",
72
- args: { width: 400, height: 300 },
73
- });
74
- result.current.addToHistory(editedItem);
75
- result.current.addToHistory(secondEditedItem);
76
- result.current.undo();
77
- result.current.addToHistory({ ...editedItem, name: "Replacement" });
78
- });
79
-
80
- expect(result.current.currentAction).toBeNull();
81
- expect(result.current.history.pointer).toBe(2);
82
- expect(result.current.history.items).toEqual([
83
- initialItem,
84
- editedItem,
85
- { ...editedItem, name: "Replacement" },
86
- ]);
87
- });
88
-
89
- it("moves through history without exceeding its boundaries", () => {
90
- const { result } = renderHook(() => useImageEditorContext(), { wrapper });
91
-
92
- act(() => {
93
- result.current.undo();
94
- });
95
- expect(result.current.history.pointer).toBe(0);
96
-
97
- act(() => {
98
- result.current.addToHistory(editedItem);
99
- result.current.undo();
100
- });
101
- expect(result.current.history.pointer).toBe(0);
102
- expect(result.current.getLastHistoryItem()).toEqual(initialItem);
103
-
104
- act(() => {
105
- result.current.redo();
106
- result.current.redo();
107
- });
108
- expect(result.current.history.pointer).toBe(1);
109
- expect(result.current.getLastHistoryItem()).toEqual(editedItem);
110
- });
111
-
112
- it("resets to the source image and resets history after saving", () => {
113
- const { result } = renderHook(() => useImageEditorContext(), { wrapper });
114
-
115
- act(() => {
116
- result.current.addToHistory(editedItem);
117
- result.current.resetHistoryAfterSave();
118
- });
119
- expect(result.current.history).toEqual({ items: [editedItem], pointer: 0 });
120
-
121
- act(() => {
122
- result.current.resetHistory();
123
- });
124
- expect(result.current.history).toEqual({ items: [initialItem], pointer: 0 });
125
- });
126
- });
@@ -1,107 +0,0 @@
1
- import { useState } from "react";
2
- import { getInitialState } from "./initialState";
3
- import { StoreContext } from "./StoreContext";
4
- import type { ImageEditorContextType } from "./initialState";
5
- import type { Action, HistoryItem } from "../types";
6
-
7
- type ImageEditorProviderProps = {
8
- children: React.ReactNode;
9
- base64: string;
10
- width: number;
11
- height: number;
12
- ext: string;
13
- };
14
-
15
- export const ImageEditorProvider = ({
16
- children,
17
- base64,
18
- width,
19
- height,
20
- ext,
21
- }: ImageEditorProviderProps) => {
22
- const [state, setState] = useState<ImageEditorContextType>(
23
- getInitialState(ext, width, height, base64),
24
- );
25
-
26
- const getCurrentAction = () => state.currentAction;
27
-
28
- const setCurrentAction = (payload: Action | null) =>
29
- setState((store) => ({ ...store, currentAction: payload }));
30
-
31
- const addToHistory = (payload: HistoryItem) =>
32
- setState((store) => {
33
- const incrementedPointer = store.history.pointer + 1;
34
- const updatedItems = [
35
- ...store.history.items.slice(0, incrementedPointer),
36
- payload,
37
- ];
38
- return {
39
- ...store,
40
- currentAction: null,
41
- history: {
42
- items: updatedItems,
43
- pointer: incrementedPointer,
44
- },
45
- };
46
- });
47
-
48
- const getLastHistoryItem = () =>
49
- state.history.items.at(state.history.pointer)!;
50
-
51
- const resetHistory = () =>
52
- setState((store) => ({
53
- ...store,
54
- history: getInitialState(ext, width, height, base64).history,
55
- }));
56
-
57
- const resetHistoryAfterSave = () =>
58
- setState((store) => {
59
- const savedHistoryItem = store.history.items.at(store.history.pointer)!;
60
- return {
61
- ...store,
62
- history: {
63
- items: [savedHistoryItem],
64
- pointer: 0,
65
- },
66
- };
67
- });
68
-
69
- const undo = () =>
70
- setState((store) => {
71
- if (store.history.pointer <= 0) return store;
72
- return {
73
- ...store,
74
- history: { ...store.history, pointer: store.history.pointer - 1 },
75
- };
76
- });
77
-
78
- const redo = () =>
79
- setState((store) => {
80
- if (store.history.pointer >= store.history.items.length - 1) return store;
81
- return {
82
- ...store,
83
- history: { ...store.history, pointer: store.history.pointer + 1 },
84
- };
85
- });
86
-
87
- const getSidebar = () => state.sidebar;
88
-
89
- const setSidebar = (payload: boolean) =>
90
- setState((store) => ({ ...store, sidebar: payload }));
91
-
92
- const value = {
93
- ...state,
94
- getCurrentAction,
95
- setCurrentAction,
96
- addToHistory,
97
- resetHistory,
98
- resetHistoryAfterSave,
99
- undo,
100
- redo,
101
- getLastHistoryItem,
102
- getSidebar,
103
- setSidebar,
104
- };
105
-
106
- return <StoreContext value={value}>{children}</StoreContext>;
107
- };
@@ -1,20 +0,0 @@
1
- import { createContext } from "react";
2
- import { type ImageEditorContextType } from "./initialState";
3
- import type { Action, HistoryItem } from "../types";
4
-
5
- type StoreContextType = ImageEditorContextType & {
6
- getCurrentAction: () => Action | null;
7
- setCurrentAction: (payload: Action | null) => void;
8
- addToHistory: (payload: HistoryItem) => void;
9
- getLastHistoryItem: () => HistoryItem;
10
- resetHistory: () => void;
11
- resetHistoryAfterSave: () => void;
12
- undo: () => void;
13
- redo: () => void;
14
- getSidebar: () => boolean;
15
- setSidebar: (payload: boolean) => void;
16
- };
17
-
18
- export const StoreContext = createContext<StoreContextType | undefined>(
19
- undefined,
20
- );
@@ -1,30 +0,0 @@
1
- import type { History, Action } from "../types";
2
-
3
- export type ImageEditorContextType = {
4
- history: History;
5
- currentAction: Action | null;
6
- extension: string | null;
7
- sidebar: boolean;
8
- };
9
-
10
- export const getInitialState = (
11
- extension: string,
12
- width: number,
13
- height: number,
14
- base64: string,
15
- ): ImageEditorContextType => ({
16
- history: {
17
- pointer: 0,
18
- items: [
19
- {
20
- name: "Initial",
21
- base64,
22
- width,
23
- height,
24
- },
25
- ],
26
- },
27
- currentAction: null,
28
- extension,
29
- sidebar: false,
30
- });
@@ -1,12 +0,0 @@
1
- import { useContext } from "react";
2
- import { StoreContext } from "./StoreContext";
3
-
4
- export const useImageEditorContext = () => {
5
- const context = useContext(StoreContext);
6
- if (!context) {
7
- throw new Error(
8
- "useImageEditorContext must be used within an ImageEditorProvider"
9
- );
10
- }
11
- return context;
12
- };
@@ -1,183 +0,0 @@
1
- import { useEffect, useRef, useState } from "react";
2
- import { useImageEditorContext } from "../provider/useImageEditorContext";
3
- import { Loader, Check, X, Lock } from "../assets/icons";
4
- import { useImageProcessor, useMobile } from "../hooks";
5
- import { Button } from "../ui";
6
- import { InputPixel } from "../ui";
7
- import "./resize.css";
8
-
9
- const minScale = 10;
10
- const maxScale = 200;
11
-
12
- const sizeCalculator = (scale: number, width: number, height: number) => {
13
- if (scale < minScale) scale = minScale;
14
- if (scale > maxScale) scale = maxScale;
15
- return {
16
- updatedScale: scale,
17
- updatedWidth: Math.round(width * (scale / 100)),
18
- updatedHeight: Math.round(height * (scale / 100)),
19
- };
20
- };
21
-
22
- type ResizeToolsProps = {
23
- onResizing: (scale: number) => void;
24
- };
25
-
26
- export const ResizeTools = ({ onResizing }: ResizeToolsProps) => {
27
- const { getLastHistoryItem, setCurrentAction, addToHistory, setSidebar } =
28
- useImageEditorContext();
29
- const {
30
- width: currentWidth,
31
- height: currentHeight,
32
- base64,
33
- } = getLastHistoryItem();
34
- const [loading, setLoading] = useState(false);
35
- const [width, setWidth] = useState(currentWidth);
36
- const [height, setHeight] = useState(currentHeight);
37
- const startVerticalSlideRef = useRef<number>(0);
38
- const { resize } = useImageProcessor();
39
- const mobile = useMobile();
40
-
41
- const handleChangeWidth = (e: React.ChangeEvent<HTMLInputElement>) => {
42
- const { updatedScale, updatedWidth, updatedHeight } = sizeCalculator(
43
- (parseInt(e.target.value) / currentWidth) * 100,
44
- currentWidth,
45
- currentHeight,
46
- );
47
- setWidth(updatedWidth);
48
- setHeight(updatedHeight);
49
- onResizing(updatedScale);
50
- };
51
-
52
- const handleChangeHeight = (e: React.ChangeEvent<HTMLInputElement>) => {
53
- const { updatedScale, updatedWidth, updatedHeight } = sizeCalculator(
54
- (parseInt(e.target.value) / currentHeight) * 100,
55
- currentWidth,
56
- currentHeight,
57
- );
58
- setHeight(updatedHeight);
59
- setWidth(updatedWidth);
60
- onResizing(updatedScale);
61
- };
62
-
63
- const handleClose = () => {
64
- onResizing(100);
65
- setCurrentAction(null);
66
- setSidebar(true);
67
- };
68
-
69
- const handleSave = async () => {
70
- try {
71
- setLoading(true);
72
- const processedBase64 = await resize(base64, width, height);
73
- addToHistory({
74
- name: "Resize",
75
- base64: processedBase64,
76
- width,
77
- height,
78
- });
79
- onResizing(100);
80
- setSidebar(true);
81
- } catch (error) {
82
- console.error("Failed to process image:", error);
83
- } finally {
84
- setLoading(false);
85
- }
86
- };
87
-
88
- useEffect(() => {
89
- let updatedScale = Math.round((width / currentWidth) * 100);
90
-
91
- const updateScale = (scale: number) => {
92
- const { updatedHeight, updatedWidth } = sizeCalculator(
93
- scale,
94
- currentWidth,
95
- currentHeight,
96
- );
97
- setHeight(updatedHeight);
98
- setWidth(updatedWidth);
99
- onResizing(scale);
100
- };
101
-
102
- const handleWheel = (e: WheelEvent) => {
103
- if (loading) return;
104
- const direction = Math.sign(e.deltaY);
105
- updatedScale -= direction * 2;
106
- if (updatedScale <= minScale) updatedScale = minScale;
107
- if (updatedScale >= maxScale) updatedScale = maxScale;
108
- updateScale(updatedScale);
109
- };
110
-
111
- const handleTouchStart = (e: TouchEvent) => {
112
- startVerticalSlideRef.current = e.touches[0].clientY;
113
- };
114
-
115
- const handleTouchMove = (e: TouchEvent) => {
116
- if (loading) return;
117
- const y = e.touches[0].clientY;
118
- const deltaY = startVerticalSlideRef.current - y;
119
- if (Math.abs(deltaY) < 10) return;
120
- const direction = Math.sign(deltaY);
121
- updatedScale += direction * 2;
122
- if (updatedScale <= minScale) updatedScale = minScale;
123
- if (updatedScale >= maxScale) updatedScale = maxScale;
124
- updateScale(updatedScale);
125
- startVerticalSlideRef.current = y;
126
- };
127
-
128
- const controller = new AbortController();
129
- const { signal } = controller;
130
-
131
- window.addEventListener("wheel", handleWheel, { signal });
132
- window.addEventListener("touchstart", handleTouchStart, { signal });
133
- window.addEventListener("touchmove", handleTouchMove, { signal });
134
-
135
- return () => controller.abort();
136
- }, [currentHeight, currentWidth, loading, mobile, onResizing, width]);
137
-
138
- return (
139
- <div className="resize">
140
- <div className="resize-text">
141
- {mobile ? "Slide UP/Down to Resize" : "Scroll UP/Down to Resize"}
142
- </div>
143
- <div className="resize-border" />
144
- <div className="resize-tools">
145
- <InputPixel
146
- value={width}
147
- name="width"
148
- label="Width"
149
- disabled={loading}
150
- style={{ width: "88px" }}
151
- onChange={handleChangeWidth}
152
- />
153
- <Lock className="resize-tools-lock" />
154
- <InputPixel
155
- value={height}
156
- name="height"
157
- label="Height"
158
- disabled={loading}
159
- style={{ width: "88px" }}
160
- onChange={handleChangeHeight}
161
- />
162
- <div className="resize-tools-buttons">
163
- <Button
164
- variant="outline"
165
- className="resize-tools-save text-green"
166
- disabled={width === currentWidth || loading}
167
- onClick={() => handleSave()}
168
- >
169
- {loading ? <Loader /> : <Check />}
170
- </Button>
171
- <Button
172
- variant="outline"
173
- className="resize-tools-close text-red"
174
- disabled={loading}
175
- onClick={handleClose}
176
- >
177
- <X />
178
- </Button>
179
- </div>
180
- </div>
181
- </div>
182
- );
183
- };
@@ -1 +0,0 @@
1
- export { ResizeTools } from "./ResizeTools";
@@ -1,56 +0,0 @@
1
- .resize {
2
- position: absolute;
3
- display: flex;
4
- flex-direction: column;
5
- bottom: 24px;
6
- left: 50%;
7
- transform: translateX(-50%);
8
- background-color: var(--background);
9
- padding: 16px;
10
- border-radius: 4px;
11
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
12
- }
13
-
14
- .resize-text {
15
- font-size: 14px;
16
- text-align: center;
17
- color: var(--muted-foreground);
18
- }
19
-
20
- .resize-border {
21
- margin: 12px auto 4px auto;
22
- display: inline-block;
23
- width: 60px;
24
- border-bottom: 1px solid var(--border);
25
- }
26
-
27
- .resize-tools {
28
- display: flex;
29
- gap: 6px;
30
- align-items: center;
31
- margin-top: 16px;
32
- }
33
-
34
- .resize-tools-lock {
35
- width: 12px;
36
- height: 12px;
37
- color: var(--muted-foreground);
38
- }
39
-
40
- .resize-tools-buttons {
41
- display: flex;
42
- margin-left: 4px;
43
- }
44
-
45
- .resize-tools-save {
46
- width: 36px;
47
- border-bottom-right-radius: 0;
48
- border-top-right-radius: 0;
49
- }
50
-
51
- .resize-tools-close {
52
- width: 36px;
53
- border-bottom-left-radius: 0;
54
- border-top-left-radius: 0;
55
- margin-left: -1px;
56
- }