proje-react-panel 1.0.17 → 1.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.
Files changed (113) hide show
  1. package/dist/components/Counter.d.ts +9 -0
  2. package/dist/components/DetailsPage.d.ts +7 -0
  3. package/dist/components/ErrorBoundary.d.ts +16 -0
  4. package/dist/components/ErrorComponent.d.ts +4 -0
  5. package/dist/components/LoadingScreen.d.ts +2 -0
  6. package/dist/components/Login.d.ts +13 -0
  7. package/dist/components/Panel.d.ts +1 -3
  8. package/dist/components/components/Checkbox.d.ts +3 -2
  9. package/dist/components/components/FormField.d.ts +5 -1
  10. package/dist/components/components/InnerForm.d.ts +8 -3
  11. package/dist/components/components/Label.d.ts +3 -2
  12. package/dist/components/components/Uploader.d.ts +8 -0
  13. package/dist/components/components/index.d.ts +1 -1
  14. package/dist/components/components/list/ListPage.d.ts +1 -1
  15. package/dist/components/form/Checkbox.d.ts +7 -0
  16. package/dist/components/form/FormField.d.ts +17 -0
  17. package/dist/components/form/FormPage.d.ts +6 -0
  18. package/dist/components/form/InnerForm.d.ts +10 -0
  19. package/dist/components/form/Label.d.ts +9 -0
  20. package/dist/components/form/Uploader.d.ts +8 -0
  21. package/dist/components/layout/Layout.d.ts +3 -4
  22. package/dist/components/layout/SideBar.d.ts +2 -3
  23. package/dist/components/list/CellField.d.ts +9 -0
  24. package/dist/components/list/Datagrid.d.ts +6 -8
  25. package/dist/components/list/FilterPopup.d.ts +7 -5
  26. package/dist/components/list/ListHeader.d.ts +11 -0
  27. package/dist/components/list/ListPage.d.ts +6 -0
  28. package/dist/components/pages/FormPage.d.ts +8 -2
  29. package/dist/decorators/details/Details.d.ts +11 -0
  30. package/dist/decorators/details/DetailsItem.d.ts +11 -0
  31. package/dist/decorators/details/getDetailsPageMeta.d.ts +8 -0
  32. package/dist/decorators/form/Form.d.ts +21 -5
  33. package/dist/decorators/form/Input.d.ts +7 -3
  34. package/dist/decorators/form/getFormPageMeta.d.ts +10 -0
  35. package/dist/decorators/list/Cell.d.ts +13 -1
  36. package/dist/decorators/list/List.d.ts +18 -1
  37. package/dist/decorators/list/cells/ImageCell.d.ts +9 -0
  38. package/dist/decorators/list/getListPageMeta.d.ts +8 -0
  39. package/dist/index.cjs.js +1 -1
  40. package/dist/index.d.ts +12 -17
  41. package/dist/index.esm.js +1 -1
  42. package/dist/initPanel.d.ts +1 -1
  43. package/dist/store/store.d.ts +0 -3
  44. package/dist/types/AnyClass.d.ts +2 -1
  45. package/dist/types/getDetailsData.d.ts +1 -0
  46. package/dist/types/initPanelOptions.d.ts +0 -1
  47. package/package.json +1 -1
  48. package/src/assets/icons/svg/check.svg +4 -0
  49. package/src/assets/icons/svg/cross.svg +4 -0
  50. package/src/components/DetailsPage.tsx +55 -0
  51. package/src/components/{components/ErrorComponent.tsx → ErrorComponent.tsx} +1 -1
  52. package/src/components/{pages/Login.tsx → Login.tsx} +2 -2
  53. package/src/components/Panel.tsx +4 -5
  54. package/src/components/form/Checkbox.tsx +21 -0
  55. package/src/components/{components → form}/FormField.tsx +30 -22
  56. package/src/components/form/FormPage.tsx +32 -0
  57. package/src/components/form/InnerForm.tsx +85 -0
  58. package/src/components/form/Label.tsx +21 -0
  59. package/src/components/form/Uploader.tsx +66 -0
  60. package/src/components/layout/Layout.tsx +29 -32
  61. package/src/components/layout/SideBar.tsx +4 -13
  62. package/src/components/list/CellField.tsx +63 -0
  63. package/src/components/list/Datagrid.tsx +106 -0
  64. package/src/components/{components/list → list}/FilterPopup.tsx +13 -9
  65. package/src/components/list/ListHeader.tsx +47 -0
  66. package/src/components/{components/list → list}/ListPage.tsx +20 -82
  67. package/src/decorators/details/Details.ts +31 -0
  68. package/src/decorators/details/DetailsItem.ts +40 -0
  69. package/src/decorators/details/getDetailsPageMeta.ts +15 -0
  70. package/src/decorators/form/Form.ts +37 -12
  71. package/src/decorators/form/Input.ts +11 -4
  72. package/src/decorators/form/getFormPageMeta.ts +21 -0
  73. package/src/decorators/list/Cell.ts +41 -1
  74. package/src/decorators/list/List.ts +30 -6
  75. package/src/decorators/list/cells/ImageCell.ts +17 -0
  76. package/src/decorators/list/getListPageMeta.ts +16 -0
  77. package/src/index.ts +32 -24
  78. package/src/initPanel.ts +1 -4
  79. package/src/store/store.ts +0 -5
  80. package/src/styles/components/checkbox.scss +42 -0
  81. package/src/styles/components/uploader.scss +86 -0
  82. package/src/styles/details.scss +62 -0
  83. package/src/styles/form.scss +9 -11
  84. package/src/styles/index.scss +26 -12
  85. package/src/styles/list.scss +3 -1
  86. package/src/types/AnyClass.ts +2 -1
  87. package/src/types/initPanelOptions.ts +1 -3
  88. package/src/components/components/Checkbox.tsx +0 -9
  89. package/src/components/components/ImageUploader.tsx +0 -301
  90. package/src/components/components/InnerForm.tsx +0 -74
  91. package/src/components/components/Label.tsx +0 -15
  92. package/src/components/components/index.ts +0 -8
  93. package/src/components/components/list/Datagrid.tsx +0 -127
  94. package/src/components/pages/ControllerDetails.tsx +0 -37
  95. package/src/components/pages/FormPage.tsx +0 -34
  96. package/src/decorators/Crud.ts +0 -20
  97. package/src/decorators/form/FormOptions.ts +0 -8
  98. package/src/decorators/form/getFormFields.ts +0 -13
  99. package/src/decorators/list/GetCellFields.ts +0 -13
  100. package/src/decorators/list/ImageCell.ts +0 -13
  101. package/src/decorators/list/ListData.ts +0 -7
  102. package/src/decorators/list/getListFields.ts +0 -10
  103. package/src/styles/image-uploader.scss +0 -94
  104. package/src/types/Screen.ts +0 -4
  105. package/src/types/ScreenCreatorData.ts +0 -14
  106. package/src/utils/createScreens.ts +0 -5
  107. package/src/utils/getFields.ts +0 -22
  108. /package/src/components/{components/Counter.tsx → Counter.tsx} +0 -0
  109. /package/src/components/{components/ErrorBoundary.tsx → ErrorBoundary.tsx} +0 -0
  110. /package/src/components/{components/LoadingScreen.tsx → LoadingScreen.tsx} +0 -0
  111. /package/src/components/{components/list → list}/EmptyList.tsx +0 -0
  112. /package/src/components/{components/list → list}/Pagination.tsx +0 -0
  113. /package/src/components/{components/list → list}/index.ts +0 -0
@@ -0,0 +1,42 @@
1
+ .checkbox-label {
2
+ display: flex;
3
+ align-items: center;
4
+ gap: 10px;
5
+ * {
6
+ cursor: pointer;
7
+ }
8
+ }
9
+
10
+ input.apple-switch {
11
+ position: relative;
12
+ -webkit-appearance: none;
13
+ outline: none;
14
+ width: 50px;
15
+ height: 30px;
16
+ background-color: #fff;
17
+ border: 1px solid #d9dadc;
18
+ border-radius: 50px;
19
+ box-shadow: inset -20px 0 0 0 #fff;
20
+ }
21
+
22
+ input.apple-switch:after {
23
+ content: '';
24
+ position: absolute;
25
+ top: 1px;
26
+ left: 1px;
27
+ background: transparent;
28
+ width: 26px;
29
+ height: 26px;
30
+ border-radius: 50%;
31
+ box-shadow: 2px 4px 6px rgba(0, 0, 0, 0.2);
32
+ }
33
+
34
+ input.apple-switch:checked {
35
+ box-shadow: inset 20px 0 0 0 #4ed164;
36
+ border-color: #4ed164;
37
+ }
38
+
39
+ input.apple-switch:checked:after {
40
+ left: 20px;
41
+ box-shadow: -2px 4px 3px rgba(0, 0, 0, 0.05);
42
+ }
@@ -0,0 +1,86 @@
1
+ .uploader {
2
+ &-container {
3
+ padding: 1.5rem;
4
+ border: 2px dashed var(--border-color, #e0e0e0);
5
+ border-radius: 8px;
6
+ background-color: var(--bg-color, #fafafa);
7
+ transition: all 0.3s ease;
8
+
9
+ &:hover {
10
+ border-color: var(--primary-color, #2196f3);
11
+ background-color: var(--bg-hover-color, #f5f5f5);
12
+ }
13
+ }
14
+
15
+ &-button {
16
+ background-color: var(--primary-color, #2196f3);
17
+ color: var(--button-text-color, white);
18
+ padding: 0.75rem 1.5rem;
19
+ border: none;
20
+ border-radius: 4px;
21
+ font-weight: 500;
22
+ cursor: pointer;
23
+ transition: background-color 0.3s ease;
24
+
25
+ &:hover {
26
+ background-color: var(--primary-hover-color, #1976d2);
27
+ }
28
+
29
+ &:active {
30
+ background-color: var(--primary-active-color, #1565c0);
31
+ }
32
+ }
33
+
34
+ &-files {
35
+ margin-top: 1.5rem;
36
+ display: grid;
37
+ gap: 1rem;
38
+ }
39
+
40
+ &-file {
41
+ background-color: var(--file-bg-color, white);
42
+ padding: 1rem;
43
+ border-radius: 6px;
44
+ box-shadow: 0 2px 4px var(--shadow-color, rgba(0, 0, 0, 0.05));
45
+ display: grid;
46
+ gap: 0.5rem;
47
+
48
+ p {
49
+ margin: 0;
50
+ color: var(--text-color, #424242);
51
+ font-size: 0.875rem;
52
+
53
+ &:first-child {
54
+ color: var(--primary-color, #1976d2);
55
+ font-weight: 500;
56
+ }
57
+ }
58
+ }
59
+ }
60
+
61
+ :root {
62
+ --border-color: #424242;
63
+ --bg-color: #1e1e1e;
64
+ --bg-hover-color: #2d2d2d;
65
+ --primary-color: #64b5f6;
66
+ --primary-hover-color: #42a5f5;
67
+ --primary-active-color: #2196f3;
68
+ --button-text-color: #ffffff;
69
+ --file-bg-color: #2d2d2d;
70
+ --shadow-color: rgba(0, 0, 0, 0.2);
71
+ --text-color: #e0e0e0;
72
+ }
73
+
74
+ // Dark theme
75
+ [data-theme='dark'] {
76
+ --border-color: #424242;
77
+ --bg-color: #1e1e1e;
78
+ --bg-hover-color: #2d2d2d;
79
+ --primary-color: #64b5f6;
80
+ --primary-hover-color: #42a5f5;
81
+ --primary-active-color: #2196f3;
82
+ --button-text-color: #ffffff;
83
+ --file-bg-color: #2d2d2d;
84
+ --shadow-color: rgba(0, 0, 0, 0.2);
85
+ --text-color: #e0e0e0;
86
+ }
@@ -0,0 +1,62 @@
1
+ :root {
2
+ --background-color: #1a1a1a;
3
+ --text-color: #e0e0e0;
4
+ --label-color: #a0a0a0;
5
+ --empty-value-color: #666;
6
+ --error-color: #ff6b6b;
7
+ --shadow-color: rgba(0, 0, 0, 0.3);
8
+ }
9
+
10
+ .details-page {
11
+ padding: 2rem;
12
+ max-width: 1200px;
13
+ margin: 0 auto;
14
+
15
+ .details-item {
16
+ background: var(--background-color);
17
+ border-radius: 8px;
18
+ padding: 1.5rem;
19
+ margin-bottom: 1rem;
20
+ box-shadow: 0 2px 4px var(--shadow-color);
21
+ transition: transform 0.2s ease;
22
+
23
+ &:hover {
24
+ transform: translateY(-2px);
25
+ }
26
+
27
+ .item-label {
28
+ font-size: 0.875rem;
29
+ color: var(--label-color);
30
+ text-transform: uppercase;
31
+ letter-spacing: 0.5px;
32
+ margin-bottom: 0.5rem;
33
+ }
34
+
35
+ .item-value {
36
+ font-size: 1rem;
37
+ color: var(--text-color);
38
+ line-height: 1.5;
39
+ word-break: break-word;
40
+
41
+ &:empty {
42
+ &::after {
43
+ content: '—';
44
+ color: var(--empty-value-color);
45
+ }
46
+ }
47
+ }
48
+ }
49
+ }
50
+
51
+ .loading-container {
52
+ display: flex;
53
+ justify-content: center;
54
+ align-items: center;
55
+ min-height: 400px;
56
+ }
57
+
58
+ .error-container {
59
+ padding: 2rem;
60
+ text-align: center;
61
+ color: var(--error-color);
62
+ }
@@ -1,10 +1,8 @@
1
1
  .form-wrapper {
2
- max-width: 400px;
3
2
  padding: 20px;
4
- border-radius: 10px;
5
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
6
3
  background-color: #1a1a1a;
7
4
  color: #f2f2f2;
5
+ width: 100%;
8
6
 
9
7
  form {
10
8
  display: flex;
@@ -14,13 +12,6 @@
14
12
  .form-field {
15
13
  margin-bottom: 16px;
16
14
 
17
- label {
18
- font-weight: bold;
19
- margin-bottom: 6px;
20
- display: block;
21
- color: #f2f2f2;
22
- }
23
-
24
15
  input[type='text'],
25
16
  input[type='password'],
26
17
  input[type='email'],
@@ -59,7 +50,7 @@
59
50
 
60
51
  select {
61
52
  appearance: none;
62
- background-image: url("data:image/svg+xml;charset=US-ASCII,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22292.4%22%20height%3D%22292.4%22%3E%3Cpath%20fill%3D%22%23FFFFFF%22%20d%3D%22M287%2069.4a17.6%2017.6%200%200%200-13-5.4H18.4c-5%200-9.3%201.8-12.9%205.4A17.6%2017.6%200%200%200%200%2082.2c0%205%201.8%209.3%205.4%2012.9l128%20127.9c3.6%203.6%207.8%205.4%2012.8%205.4s9.2-1.8%2012.8-5.4L287%2095c3.5-3.5%205.4-7.8%205.4-12.8%200-5-1.9-9.2-5.5-12.8z%22%2F%3E%3C%2Fsvg%3E");
53
+ background-image: url('data:image/svg+xml;charset=US-ASCII,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22292.4%22%20height%3D%22292.4%22%3E%3Cpath%20fill%3D%22%23FFFFFF%22%20d%3D%22M287%2069.4a17.6%2017.6%200%200%200-13-5.4H18.4c-5%200-9.3%201.8-12.9%205.4A17.6%2017.6%200%200%200%200%2082.2c0%205%201.8%209.3%205.4%2012.9l128%20127.9c3.6%203.6%207.8%205.4%2012.8%205.4s9.2-1.8%2012.8-5.4L287%2095c3.5-3.5%205.4-7.8%205.4-12.8%200-5-1.9-9.2-5.5-12.8z%22%2F%3E%3C%2Fsvg%3E');
63
54
  background-repeat: no-repeat;
64
55
  background-position: right 0.7rem top 50%;
65
56
  background-size: 0.65rem auto;
@@ -88,3 +79,10 @@
88
79
  }
89
80
  }
90
81
  }
82
+
83
+ .label {
84
+ font-weight: bold;
85
+ margin-bottom: 6px;
86
+ display: block;
87
+ color: #f2f2f2;
88
+ }
@@ -1,16 +1,18 @@
1
- @use './layout';
2
- // Imported styles will be scoped under .layout
3
- @use './sidebar';
4
- @use './form';
5
- @use './list';
6
- @use './login';
7
- @use './error-boundary';
8
- @use './image-uploader';
9
- @use './counter';
10
- @use './loading-screen';
11
- @use './pagination';
12
- @use './filter-popup';
1
+ @import './layout';
2
+ @import './sidebar';
3
+ @import './form';
4
+ @import './list';
5
+ @import './login';
6
+ @import './error-boundary';
7
+ @import './counter';
8
+ @import './loading-screen';
9
+ @import './pagination';
10
+ @import './filter-popup';
11
+ @import './components/uploader.scss';
12
+ @import './components/checkbox';
13
+ @import './details';
13
14
 
15
+ //TODO: import deprecated
14
16
  .layout {
15
17
  display: flex;
16
18
  max-height: 100vh;
@@ -39,4 +41,16 @@
39
41
  height: 14px;
40
42
  stroke: currentColor;
41
43
  fill: currentColor;
44
+ &.icon-true {
45
+ width: 20px;
46
+ height: 20px;
47
+ stroke: #4caf50;
48
+ fill: none;
49
+ }
50
+ &.icon-false {
51
+ width: 20px;
52
+ height: 20px;
53
+ stroke: #ff0000;
54
+ fill: none;
55
+ }
42
56
  }
@@ -143,7 +143,9 @@ $datagrid-height: calc(100vh - ($header-height + $footer-height));
143
143
  color: #666;
144
144
  text-decoration: none;
145
145
  transition: color 0.2s ease;
146
-
146
+ &.util-cell-link-remove {
147
+ cursor: pointer;
148
+ }
147
149
  &:hover {
148
150
  color: #333;
149
151
  }
@@ -1 +1,2 @@
1
- export type AnyClass = abstract new (...args: any[]) => any;
1
+ export type AnyClass = Record<string, any>;
2
+ export type AnyClassConstructor<T extends AnyClass> = new (...args: any[]) => T;
@@ -1,3 +1 @@
1
- export interface InitPanelOptions {
2
- screenPaths: Record<string, string>;
3
- }
1
+ export interface InitPanelOptions {}
@@ -1,9 +0,0 @@
1
- import React from 'react';
2
-
3
- interface CheckboxProps extends React.InputHTMLAttributes<HTMLInputElement> {
4
- id: string;
5
- }
6
-
7
- export function Checkbox({ id, ...props }: CheckboxProps) {
8
- return <input type="checkbox" id={id} className="checkbox" {...props} />;
9
- }
@@ -1,301 +0,0 @@
1
- import React, { useEffect } from "react";
2
- import { useFormContext } from "react-hook-form";
3
- import { bytesToSize } from "../../utils/format";
4
-
5
- interface ThumbnailImageProps {
6
- name: string;
7
- src: string;
8
- size: number;
9
- style?: React.CSSProperties;
10
- }
11
-
12
- interface MultipleImageUploaderProps {
13
- value?: Array<{ file: File; image: string; remove?: boolean }>;
14
- onError?: (error: string | null) => void;
15
- onClear?: () => void;
16
- reset?: any;
17
- onFilesChange?: (files: File[]) => void;
18
- }
19
-
20
- interface FileWithPreview {
21
- file: File;
22
- image: string;
23
- }
24
-
25
- const uploadState = Object.freeze({
26
- BEFORE: "before",
27
- HOVER: "hover",
28
- AFTER: "after",
29
- } as const);
30
-
31
- type UploadStateType = (typeof uploadState)[keyof typeof uploadState];
32
-
33
- function ThumbnailImage(props: ThumbnailImageProps) {
34
- return (
35
- <div>
36
- <img {...props} style={{ width: 100 }} />
37
- <p>
38
- {props.name} <span style={{ whiteSpace: "none" }}>({bytesToSize(props.size)})</span>
39
- </p>
40
- </div>
41
- );
42
- }
43
-
44
- export function ImageUploader() {
45
- const {
46
- register,
47
- formState: { errors },
48
- watch,
49
- setValue,
50
- clearErrors,
51
- setError,
52
- } = useFormContext();
53
- const up = watch("uploader");
54
-
55
- useEffect(() => {
56
- register("uploader", { required: true });
57
- }, [register]);
58
-
59
- return (
60
- <div>
61
- <span className="form-error" style={{ bottom: 2, top: "unset" }}>
62
- {errors.uploader?.type === "required" && "At least 1 image is required!"}
63
- {errors.uploader?.type === "custom" && errors.uploader.message?.toString()}
64
- </span>
65
- <MultipleImageUploader
66
- reset={up}
67
- onError={(data: string | null) => {
68
- if (!data) {
69
- setValue("uploader", { files: [] });
70
- clearErrors("uploader");
71
- } else {
72
- setError("uploader", {
73
- type: "custom",
74
- message: data,
75
- });
76
- }
77
- }}
78
- onClear={() => {
79
- setValue("uploader", { files: [] });
80
- clearErrors("uploader");
81
- }}
82
- onFilesChange={(files) => {
83
- setValue("uploader", { files });
84
- }}
85
- />
86
- </div>
87
- );
88
- }
89
-
90
- export function MultipleImageUploader(props: MultipleImageUploaderProps) {
91
- const [currentUploadState, setUploadState] = React.useState<UploadStateType>(uploadState.BEFORE);
92
- const [images, setImages] = React.useState<Array<{ file: File; image: string; remove?: boolean }>>(
93
- props.value || []
94
- );
95
- const [files, setFiles] = React.useState<FileWithPreview[]>([]);
96
- const [counter, setCounter] = React.useState(0);
97
- console.log("files", files);
98
- const dropzoneElement = React.useRef<HTMLDivElement>(null);
99
- const imageInputRef = React.useRef<HTMLInputElement>(null);
100
-
101
- React.useEffect(() => {
102
- const element = dropzoneElement.current;
103
- if (!element) return;
104
-
105
- const handleDragEnter = (e: DragEvent) => {
106
- e.preventDefault();
107
- e.stopPropagation();
108
- dragEnter();
109
- };
110
-
111
- const handleDragLeave = (e: DragEvent) => {
112
- e.preventDefault();
113
- e.stopPropagation();
114
- dragLeave();
115
- };
116
-
117
- const handleDragOver = (e: DragEvent) => {
118
- e.preventDefault();
119
- e.stopPropagation();
120
- };
121
-
122
- const handleDrop = (e: DragEvent) => {
123
- e.preventDefault();
124
- e.stopPropagation();
125
- setCounter(0);
126
- const droppedFiles = e.dataTransfer?.files;
127
- if (!droppedFiles) return;
128
-
129
- setFiles([]);
130
- setUploadState(uploadState.AFTER);
131
-
132
- const newFiles: File[] = [];
133
- for (let i = 0; i < droppedFiles.length; i++) {
134
- const reader = new FileReader();
135
- reader.onload = (event) => {
136
- if (!event.target) return;
137
- const check = onFileChange([
138
- ...files,
139
- { file: droppedFiles[i], image: event.target.result as string },
140
- ]);
141
- if (check) {
142
- newFiles.push(droppedFiles[i]);
143
- if (imageInputRef.current) {
144
- imageInputRef.current.files = droppedFiles;
145
- }
146
- setFiles([]);
147
- }
148
- // Notify parent of file changes
149
- if (props.onFilesChange) {
150
- props.onFilesChange(newFiles);
151
- }
152
- };
153
- reader.readAsDataURL(droppedFiles[i]);
154
- }
155
- if (imageInputRef.current) {
156
- imageInputRef.current.files = droppedFiles;
157
- }
158
- };
159
-
160
- element.addEventListener("dragenter", handleDragEnter, false);
161
- element.addEventListener("dragleave", handleDragLeave, false);
162
- element.addEventListener("dragover", handleDragOver, false);
163
- element.addEventListener("drop", handleDrop, false);
164
-
165
- return () => {
166
- element.removeEventListener("dragenter", handleDragEnter);
167
- element.removeEventListener("dragleave", handleDragLeave);
168
- element.removeEventListener("dragover", handleDragOver);
169
- element.removeEventListener("drop", handleDrop);
170
- };
171
- }, [files]);
172
-
173
- const dragEnter = () => {
174
- setCounter((prev) => prev + 1);
175
- setUploadState(uploadState.HOVER);
176
- };
177
-
178
- const dragLeave = () => {
179
- setCounter((prev) => {
180
- if (prev - 1 === 0) {
181
- setUploadState(uploadState.BEFORE);
182
- return 0;
183
- }
184
- return prev - 1;
185
- });
186
- };
187
-
188
- const clickRemoveImage = (i: number) => {
189
- const sources = [...images];
190
- sources[i].remove = !sources[i].remove;
191
- setImages(sources);
192
- };
193
-
194
- const clickRemoveFile = () => {
195
- setFiles([]);
196
- if (imageInputRef.current) {
197
- imageInputRef.current.value = "";
198
- }
199
- props.onClear?.();
200
- };
201
-
202
- const checkValid = (filesInner: FileWithPreview[]): string | null => {
203
- if (!filesInner) return null;
204
- if (filesInner.length >= 10) return "you can't send more than 10 images";
205
- for (let i = 0; i < filesInner.length; i++) {
206
- const file = filesInner[i].file;
207
- const split = file.name.split(".");
208
- if (!["png", "jpg", "jpeg"].includes(split[split.length - 1])) {
209
- return `Extension of the file can only be "png", "jpg" or "jpeg" `;
210
- }
211
- if (file) {
212
- if (file.size > 1048576) {
213
- return `Size of "${file.name}" can't be bigger than 1mb`;
214
- }
215
- }
216
- }
217
- return null;
218
- };
219
-
220
- const onFileChange = (filesInner: FileWithPreview[]): string | null => {
221
- const check = checkValid(filesInner);
222
- if (!check) {
223
- setFiles(filesInner);
224
- }
225
- props.onError?.(check);
226
- return check;
227
- };
228
-
229
- const renderImages = () => {
230
- const imageElements = [];
231
- if (files) {
232
- console.log("---->", files);
233
- for (let i = 0; i < files.length; i++) {
234
- let imageClassName = "image";
235
- imageElements.push(
236
- <div key={i} className="image-container">
237
- <div className={imageClassName}>
238
- <ThumbnailImage name={files[i].file.name} src={files[i].image} size={files[i].file.size} />
239
- </div>
240
- </div>
241
- );
242
- }
243
- }
244
- return imageElements;
245
- };
246
-
247
- return (
248
- <div ref={dropzoneElement} className={"multi-image form-element dropzone " + currentUploadState}>
249
- <input ref={imageInputRef} type="file" style={{ display: "none" }} className="target" name={"file"} />
250
- <div className="container">
251
- <button className="trash" onClick={clickRemoveFile} type="button">
252
- Delete All
253
- </button>
254
- {renderImages()}
255
- <div>
256
- <button
257
- type={"button"}
258
- onClick={() => {
259
- const fileInput = document.getElementById("file__") as HTMLInputElement;
260
- if (fileInput) {
261
- fileInput.click();
262
- }
263
- }}
264
- className="plus">
265
- <span>
266
- + Add Image
267
- <p>Drag image here or Select Image</p>
268
- </span>
269
- </button>
270
- </div>
271
- <input
272
- hidden
273
- id={"file__"}
274
- multiple
275
- type={"file"}
276
- onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
277
- const selectedFiles = event.target.files;
278
- if (!selectedFiles) return;
279
-
280
- setFiles([]);
281
- setUploadState(uploadState.AFTER);
282
- for (let i = 0; i < selectedFiles.length; i++) {
283
- const reader = new FileReader();
284
- reader.onload = (eventInner) => {
285
- if (!eventInner.target) return;
286
- onFileChange([
287
- ...files,
288
- { file: selectedFiles[i], image: eventInner.target.result as string },
289
- ]);
290
- };
291
- reader.readAsDataURL(selectedFiles[i]);
292
- }
293
- if (imageInputRef.current) {
294
- imageInputRef.current.files = selectedFiles;
295
- }
296
- }}
297
- />
298
- </div>
299
- </div>
300
- );
301
- }
@@ -1,74 +0,0 @@
1
- import React, { useEffect, useState } from 'react';
2
- import { FormProvider, useForm } from 'react-hook-form';
3
- import { InputOptions } from '../../decorators/form/Input';
4
- import { FormField } from './FormField';
5
- import { FormOptions } from '../../decorators/form/FormOptions';
6
- import { AnyClass } from '../../types/AnyClass';
7
- import { OnSubmitFN, GetDetailsDataFN } from '../pages/FormPage';
8
- import { useParams, useNavigate } from 'react-router';
9
-
10
- interface InnerFormProps<T extends AnyClass> {
11
- formOptions: FormOptions;
12
- onSubmit: OnSubmitFN<T>;
13
- getDetailsData?: GetDetailsDataFN<T>;
14
- redirectBackOnSuccess?: boolean;
15
- }
16
-
17
- export function InnerForm<T extends AnyClass>({
18
- formOptions,
19
- onSubmit,
20
- getDetailsData,
21
- redirectBackOnSuccess,
22
- }: InnerFormProps<T>) {
23
- const params = useParams();
24
- const form = useForm<T>({
25
- resolver: formOptions.resolver,
26
- });
27
- const navigate = useNavigate();
28
- const inputs = formOptions.inputs;
29
- useEffect(() => {
30
- if (getDetailsData) {
31
- getDetailsData(params as Record<string, string>).then(data => {
32
- form.reset({ ...data });
33
- });
34
- }
35
- }, [params, form.reset]);
36
-
37
- return (
38
- <div className="form-wrapper">
39
- <FormProvider {...form}>
40
- <form
41
- onSubmit={form.handleSubmit(
42
- async dataForm => {
43
- await onSubmit(dataForm);
44
- if (redirectBackOnSuccess) {
45
- navigate(-1);
46
- }
47
- },
48
- (errors, event) => {
49
- console.log('error creating creation', errors, event);
50
- }
51
- )}
52
- >
53
- <div>
54
- {inputs?.map((input: InputOptions) => (
55
- <FormField
56
- key={input.name || ''}
57
- input={input}
58
- register={form.register}
59
- error={
60
- input.name
61
- ? { message: (form.formState.errors[input.name as keyof T] as any)?.message }
62
- : undefined
63
- }
64
- />
65
- ))}
66
- <button type="submit" className="submit-button">
67
- Submit
68
- </button>
69
- </div>
70
- </form>
71
- </FormProvider>
72
- </div>
73
- );
74
- }