ar-design 0.2.77 → 0.2.79

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.
@@ -0,0 +1,136 @@
1
+ .ar-upload-button > .button.multiple > .preview {
2
+ display: flex;
3
+ flex-direction: row;
4
+ flex-wrap: nowrap;
5
+ width: 100%;
6
+ height: 100%;
7
+ border-right: solid 1px rgba(var(--black-rgb), 0.75);
8
+ }
9
+ .ar-upload-button > .button.multiple > .preview > .selected-image {
10
+ height: 100%;
11
+ object-fit: fill;
12
+ object-position: top;
13
+ }
14
+
15
+ .ar-upload-button > .button.multiple > .items {
16
+ display: flex;
17
+ flex-direction: column;
18
+ flex-wrap: nowrap;
19
+ background-color: var(--primary);
20
+ backdrop-filter: blur(5px);
21
+ width: 10rem;
22
+ height: 12.5rem;
23
+ overflow-x: hidden;
24
+ overflow-y: auto;
25
+ }
26
+ .ar-upload-button > .button.multiple > .items > .item {
27
+ --size: 5rem;
28
+
29
+ position: relative;
30
+ min-width: var(--size);
31
+ min-height: var(--size);
32
+ height: var(--size);
33
+ border-bottom: solid 1px rgba(var(--black-rgb), 0.75);
34
+ overflow: hidden;
35
+ }
36
+ .ar-upload-button > .button.multiple > .items > .item > .buttons {
37
+ position: absolute;
38
+ inset: 0;
39
+ display: flex;
40
+ justify-content: center;
41
+ align-items: center;
42
+ }
43
+ .ar-upload-button > .button.multiple > .items > .item > .buttons::before {
44
+ position: absolute;
45
+ inset: 0;
46
+ content: "";
47
+ background-color: transparent;
48
+ z-index: 1;
49
+ transition: background-color 250ms ease-in-out;
50
+ }
51
+ .ar-upload-button > .button.multiple > .items > .item > .buttons:hover::before {
52
+ background-color: rgba(var(--black-rgb), 0.75);
53
+ }
54
+ .ar-upload-button > .button.multiple > .items > .item > .buttons > div {
55
+ display: flex;
56
+ flex-direction: row;
57
+ visibility: hidden;
58
+ opacity: 0;
59
+ z-index: 2;
60
+ transition: visibility 250ms, opacity 250ms ease-in-out;
61
+ }
62
+ .ar-upload-button > .button.multiple > .items > .item > .buttons:hover div {
63
+ visibility: visible;
64
+ opacity: 1;
65
+ }
66
+ .ar-upload-button > .button.multiple > .items > .item > img {
67
+ width: 100%;
68
+ height: 100%;
69
+ object-fit: cover;
70
+ object-position: top;
71
+ }
72
+ .ar-upload-button > .button.multiple > .items > .item .error {
73
+ position: absolute;
74
+ inset: 0;
75
+ display: flex;
76
+ flex-direction: column;
77
+ justify-content: center;
78
+ align-items: center;
79
+ gap: 0.5rem;
80
+ background-color: rgba(var(--danger-rgb), 0.5);
81
+ color: var(--white);
82
+ font-size: 0.75rem;
83
+ font-weight: 700;
84
+ text-align: center;
85
+ z-index: 5;
86
+ }
87
+
88
+ .ar-upload-button > .button.multiple > .preview > .informations {
89
+ visibility: visible;
90
+ opacity: 1;
91
+ display: flex;
92
+ flex-direction: row;
93
+ flex-wrap: wrap;
94
+ justify-content: space-between;
95
+ align-items: center;
96
+ gap: 0.5rem;
97
+ position: absolute;
98
+ inset: auto 7.5rem 0 0;
99
+ background-color: rgba(var(--black-rgb), 0.75);
100
+ padding: 0.5rem;
101
+ transition: visibility 250ms, opacity 250ms ease-in-out;
102
+ }
103
+ .ar-upload-button > .button.multiple > .preview > .informations > .file-name {
104
+ color: var(--white);
105
+ font-size: 0.75rem;
106
+ font-weight: 700;
107
+ }
108
+ .ar-upload-button > .button.multiple > .preview > .informations > div {
109
+ display: flex;
110
+ gap: 0.25rem;
111
+ }
112
+ .ar-upload-button > .button.multiple > .preview > .informations > div > .file-size {
113
+ background-color: var(--primary);
114
+ padding: 0.25rem 0 0.25rem 0.25rem;
115
+ border-radius: var(--border-radius-sm);
116
+ color: var(--white);
117
+ font-size: 0.75rem;
118
+ font-weight: 700;
119
+ }
120
+ .ar-upload-button > .button.multiple > .preview > .informations > div > .file-size > .size-type {
121
+ background-color: rgba(var(--black-rgb), 0.5);
122
+ margin-left: 0.25rem;
123
+ border-top-right-radius: var(--border-radius-sm);
124
+ border-bottom-right-radius: var(--border-radius-sm);
125
+ padding: 0.25rem;
126
+ color: var(--primary);
127
+ font-weight: 700;
128
+ }
129
+ .ar-upload-button > .button.multiple > .preview > .informations > div > .file-type {
130
+ background-color: var(--warning);
131
+ padding: 0.25rem;
132
+ border-radius: var(--border-radius-sm);
133
+ color: var(--black);
134
+ font-size: 0.75rem;
135
+ font-weight: 700;
136
+ }
@@ -18,89 +18,150 @@
18
18
  }
19
19
 
20
20
  .ar-upload-button > .button {
21
+ position: relative;
21
22
  display: flex;
22
23
  flex-direction: column;
23
24
  justify-content: center;
24
25
  align-items: center;
25
26
  gap: 1rem;
26
- padding: 1rem 0;
27
+ height: 8.5rem;
27
28
  border: dashed 1px var(--gray-300);
28
29
  border-radius: var(--border-radius-sm);
29
30
  cursor: pointer;
30
31
  user-select: none;
32
+ transition: border 250ms, box-shadow 250ms ease-in-out;
33
+ }
34
+ .ar-upload-button > .button.multiple {
35
+ height: 12.5rem !important;
36
+ }
37
+ .ar-upload-button > .button.has-file {
38
+ flex-direction: row !important;
39
+ justify-content: flex-end !important;
40
+ gap: 0 !important;
41
+ align-items: flex-start !important;
42
+ }
43
+ .ar-upload-button > .button:not(.multiple):hover .preview > .informations {
44
+ visibility: hidden;
45
+ opacity: 0;
31
46
  }
32
- .ar-upload-button > .button:has(> img) {
47
+ .ar-upload-button > .button:not(.multiple):has(.preview > img):hover {
48
+ border: solid 1px transparent !important;
49
+ }
50
+ .ar-upload-button > .button:has(.preview > img) {
33
51
  padding: 0;
34
- border: none;
52
+ border: solid 1px rgba(var(--black-rgb), 0.75);
35
53
  overflow: hidden;
36
54
  }
37
- .ar-upload-button > .button > img {
38
- width: 100%;
39
- height: 200px;
40
- object-fit: fill;
41
- object-position: top;
55
+ .ar-upload-button > .button.dragging {
56
+ box-shadow: 0 0 0 2.5px rgba(var(--primary-rgb), 0.75);
42
57
  }
43
- .ar-upload-button > .button > .information {
58
+
59
+ .ar-upload-button > .button > .preview > .buttons {
60
+ position: absolute;
61
+ inset: 0;
44
62
  display: flex;
45
- flex-direction: column;
46
63
  justify-content: center;
47
64
  align-items: center;
48
- gap: 0.5rem;
49
65
  }
50
- .ar-upload-button > .button > .information > .properies {
66
+ .ar-upload-button > .button > .preview > .buttons::before {
67
+ position: absolute;
68
+ inset: 0;
69
+ content: "";
70
+ background-color: transparent;
71
+ z-index: 1;
72
+ transition: background-color 250ms ease-in-out;
73
+ }
74
+ .ar-upload-button > .button > .preview > .buttons:hover::before {
75
+ background-color: rgba(var(--black-rgb), 0.75);
76
+ }
77
+ .ar-upload-button > .button > .preview > .buttons > div {
51
78
  display: flex;
52
79
  flex-direction: row;
53
- gap: 0.5rem;
54
- font-size: 0.85rem;
55
- }
56
- .ar-upload-button > .button > .information > .properies > .max-size {
57
- color: var(--gray-500);
80
+ visibility: hidden;
81
+ opacity: 0;
82
+ z-index: 2;
83
+ transition: visibility 250ms, opacity 250ms ease-in-out;
58
84
  }
59
- .ar-upload-button > .button > .information > .properies > .allow-types {
60
- color: var(--gray-500);
85
+ .ar-upload-button > .button > .preview > .buttons:hover div {
86
+ visibility: visible;
87
+ opacity: 1;
61
88
  }
62
-
63
- .ar-upload-button > .ar-upload-files {
89
+ .ar-upload-button > .button > .preview > img {
64
90
  width: 100%;
65
- transition: top 250ms ease-in-out;
66
- }
67
- .ar-upload-button > .ar-upload-files > ul {
68
- list-style: none;
91
+ height: 100%;
92
+ object-fit: fill;
93
+ object-position: top;
69
94
  }
70
95
 
71
- .ar-upload-button > .ar-upload-files > ul > li > .file,
72
- .ar-upload-button > .ar-upload-files > .file {
96
+ .ar-upload-button > .button > .preview > .informations {
97
+ visibility: visible;
98
+ opacity: 1;
73
99
  display: flex;
74
100
  flex-direction: row;
101
+ flex-wrap: wrap;
75
102
  justify-content: space-between;
76
103
  align-items: center;
77
- background-color: var(--gray-200);
78
- margin-top: 0.5rem;
104
+ gap: 0.5rem;
105
+ position: absolute;
106
+ inset: auto 0 0 0;
107
+ background-color: rgba(var(--black-rgb), 0.75);
79
108
  padding: 0.5rem;
80
- border-radius: var(--border-radius-sm);
109
+ transition: visibility 250ms, opacity 250ms ease-in-out;
81
110
  }
82
- .ar-upload-button > .ar-upload-files > ul > li > .file > .information,
83
- .ar-upload-button > .ar-upload-files > .file > .information {
84
- display: flex;
85
- flex-direction: row;
86
- gap: 0.5rem;
111
+ .ar-upload-button > .button > .preview > .informations > .file-name {
112
+ color: var(--white);
113
+ font-size: 0.75rem;
114
+ font-weight: 700;
87
115
  }
88
- .ar-upload-button > .ar-upload-files > ul > li > .file > .information > span,
89
- .ar-upload-button > .ar-upload-files > .file > .information > span {
90
- display: inline-block;
116
+ .ar-upload-button > .button > .preview > .informations > div {
117
+ display: flex;
118
+ gap: 0.25rem;
91
119
  }
92
- .ar-upload-button > .ar-upload-files > ul > li > .file > .information > span:last-child,
93
- .ar-upload-button > .ar-upload-files > .file > .information > span:last-child {
120
+ .ar-upload-button > .button > .preview > .informations > div > .file-size {
94
121
  background-color: var(--primary);
95
- padding: 0 0.25rem;
122
+ padding: 0.25rem 0 0.25rem 0.25rem;
96
123
  border-radius: var(--border-radius-sm);
97
124
  color: var(--white);
98
125
  font-size: 0.75rem;
99
- line-height: 2;
126
+ font-weight: 700;
127
+ }
128
+ .ar-upload-button > .button > .preview > .informations > div > .file-size > .size-type {
129
+ background-color: rgba(var(--black-rgb), 0.5);
130
+ margin-left: 0.25rem;
131
+ border-top-right-radius: var(--border-radius-sm);
132
+ border-bottom-right-radius: var(--border-radius-sm);
133
+ padding: 0.25rem;
134
+ color: var(--primary);
135
+ font-weight: 700;
136
+ }
137
+ .ar-upload-button > .button > .preview > .informations > div > .file-type {
138
+ background-color: var(--warning);
139
+ padding: 0.25rem;
140
+ border-radius: var(--border-radius-sm);
141
+ color: var(--black);
142
+ font-size: 0.75rem;
143
+ font-weight: 700;
100
144
  }
101
- .ar-upload-button > .ar-upload-files > ul > li > .file > .delete,
102
- .ar-upload-button > .ar-upload-files > .file > .delete {
103
- cursor: pointer;
145
+
146
+ .ar-upload-button > .button > .upload {
147
+ display: flex;
148
+ flex-direction: column;
149
+ justify-content: center;
150
+ align-items: center;
151
+ gap: 0.5rem;
152
+ }
153
+ .ar-upload-button > .button > .upload > .properies {
154
+ display: flex;
155
+ flex-direction: row;
156
+ gap: 0.5rem;
157
+ font-size: 0.85rem;
158
+ }
159
+ .ar-upload-button > .button > .upload > .properies > .max-size {
160
+ color: var(--gray-500);
161
+ }
162
+ .ar-upload-button > .button > .upload > .properies > .allow-types {
163
+ color: var(--gray-500);
104
164
  }
105
165
 
166
+ @import url("./preview-selected-files.css");
106
167
  @import url("./animations.css");
@@ -0,0 +1,8 @@
1
+ import React from "react";
2
+ interface IProps {
3
+ selectedFile: File | undefined;
4
+ handleFileToBase64: (file: File) => Promise<string>;
5
+ handleFileRemove: (fileToRemove: File) => void;
6
+ }
7
+ declare const _default: React.MemoExoticComponent<({ selectedFile, handleFileToBase64, handleFileRemove }: IProps) => React.JSX.Element | undefined>;
8
+ export default _default;
@@ -0,0 +1,36 @@
1
+ import React, { memo, useEffect, useState } from "react";
2
+ import Button from "../button";
3
+ import { ARIcon } from "../../icons";
4
+ const PreviewSelectedFile = ({ selectedFile, handleFileToBase64, handleFileRemove }) => {
5
+ // states
6
+ const [selectedFileBase64, setSelectedFileBase64] = useState(undefined);
7
+ // useEffects
8
+ useEffect(() => {
9
+ (async () => {
10
+ if (!selectedFile)
11
+ return;
12
+ setSelectedFileBase64(await handleFileToBase64(selectedFile));
13
+ })();
14
+ }, [selectedFile]);
15
+ return (selectedFile && (React.createElement("div", { className: "preview" },
16
+ React.createElement("div", { className: "buttons" },
17
+ React.createElement("div", null,
18
+ React.createElement(Button, { variant: "borderless", icon: { element: React.createElement(ARIcon, { variant: "fill", icon: "Eye", fill: "currentColor" }) }, onClick: (event) => {
19
+ event.stopPropagation();
20
+ const newTab = window.open();
21
+ newTab?.document.write(`<img src="${selectedFileBase64}" title="${selectedFile.name}" alt="${selectedFile.name}" />`);
22
+ } }),
23
+ React.createElement(Button, { variant: "borderless", status: "danger", icon: { element: React.createElement(ARIcon, { variant: "fill", icon: "Trash", fill: "currentColor" }) }, onClick: (event) => {
24
+ event.stopPropagation();
25
+ handleFileRemove(selectedFile);
26
+ } }))),
27
+ React.createElement("img", { src: selectedFileBase64 }),
28
+ React.createElement("div", { className: "informations" },
29
+ React.createElement("span", { className: "file-name" }, selectedFile.name),
30
+ React.createElement("div", null,
31
+ React.createElement("span", { className: "file-size" },
32
+ (selectedFile.size / 1024).toFixed(3),
33
+ React.createElement("span", { className: "size-type" }, "KB")),
34
+ React.createElement("span", { className: "file-type" }, selectedFile.type.split("/")[1].toLocaleUpperCase()))))));
35
+ };
36
+ export default memo(PreviewSelectedFile);
@@ -0,0 +1,10 @@
1
+ import React from "react";
2
+ import { ValidationError } from ".";
3
+ interface IProps {
4
+ selectedFiles: File[];
5
+ validationErrors: ValidationError[];
6
+ handleFileToBase64: (file: File) => Promise<string>;
7
+ handleFileRemove: (fileToRemove: File) => void;
8
+ }
9
+ declare const _default: React.MemoExoticComponent<({ selectedFiles, validationErrors, handleFileToBase64, handleFileRemove }: IProps) => false | React.JSX.Element>;
10
+ export default _default;
@@ -0,0 +1,53 @@
1
+ import React, { memo, useEffect, useState } from "react";
2
+ import Button from "../button";
3
+ import { ARIcon } from "../../icons";
4
+ const PreviewSelectedFiles = ({ selectedFiles, validationErrors, handleFileToBase64, handleFileRemove }) => {
5
+ // states
6
+ const [items, setItems] = useState([]);
7
+ const [selectedFile, setSelectedFile] = useState(undefined);
8
+ const [selectedFileBase64, setSelectedFileBase64] = useState(undefined);
9
+ // useEffects
10
+ useEffect(() => {
11
+ (async () => {
12
+ const items = await Promise.all(selectedFiles.map(async (selectedFile) => {
13
+ const _selectedFileBase64 = await handleFileToBase64(selectedFile);
14
+ const message = validationErrors.find((validationError) => validationError.fileName === selectedFile.name)?.message;
15
+ return (React.createElement("div", { className: "item", onClick: (event) => event.stopPropagation(), onMouseOver: async (event) => {
16
+ event.stopPropagation();
17
+ setSelectedFileBase64(await handleFileToBase64(selectedFile));
18
+ setSelectedFile(selectedFile);
19
+ } },
20
+ message && (React.createElement("div", { className: "error" },
21
+ React.createElement(ARIcon, { variant: "fill", icon: "ExclamationDiamond", fill: "var(--white)" }),
22
+ React.createElement("span", null, message))),
23
+ React.createElement("div", { className: "buttons" },
24
+ React.createElement("div", null,
25
+ React.createElement(Button, { variant: "borderless", icon: { element: React.createElement(ARIcon, { variant: "fill", icon: "Eye", fill: "currentColor" }) }, onClick: (event) => {
26
+ event.stopPropagation();
27
+ const newTab = window.open();
28
+ newTab?.document.write(`<img src="${_selectedFileBase64}" title="${selectedFile.name}" alt="${selectedFile.name}" />`);
29
+ } }),
30
+ React.createElement(Button, { variant: "borderless", status: "danger", icon: { element: React.createElement(ARIcon, { variant: "fill", icon: "Trash", fill: "currentColor" }) }, onClick: (event) => {
31
+ event.stopPropagation();
32
+ handleFileRemove(selectedFile);
33
+ } }))),
34
+ React.createElement("img", { src: _selectedFileBase64 })));
35
+ }));
36
+ setItems(items);
37
+ setSelectedFileBase64(await handleFileToBase64(selectedFiles[0]));
38
+ setSelectedFile(selectedFiles[0]);
39
+ })();
40
+ }, [selectedFiles, validationErrors]);
41
+ return (selectedFiles.length > 0 && (React.createElement(React.Fragment, null,
42
+ React.createElement("div", { className: "preview" },
43
+ React.createElement("img", { src: selectedFileBase64, className: "selected-image" }),
44
+ selectedFile && (React.createElement("div", { className: "informations" },
45
+ React.createElement("span", { className: "file-name" }, selectedFile.name),
46
+ React.createElement("div", null,
47
+ React.createElement("span", { className: "file-size" },
48
+ (selectedFile.size / 1024).toFixed(3),
49
+ React.createElement("span", { className: "size-type" }, "KB")),
50
+ React.createElement("span", { className: "file-type" }, selectedFile.type.split("/")[1].toLocaleUpperCase()))))),
51
+ React.createElement("div", { className: "items" }, items))));
52
+ };
53
+ export default memo(PreviewSelectedFiles);
@@ -1,5 +1,9 @@
1
1
  import React from "react";
2
2
  import Props from "./Props";
3
3
  import "../../../assets/css/components/form/upload/styles.css";
4
+ export type ValidationError = {
5
+ fileName: string;
6
+ message: string;
7
+ };
4
8
  declare const Upload: React.FC<Props>;
5
9
  export default Upload;
@@ -1,7 +1,8 @@
1
- import React, { useEffect, useRef, useState } from "react";
1
+ import React, { useCallback, useEffect, useRef, useState } from "react";
2
2
  import "../../../assets/css/components/form/upload/styles.css";
3
- import Tooltip from "../../feedback/tooltip";
4
3
  import { ARIcon } from "../../icons";
4
+ import PreviewSelectedFile from "./PreviewSelectedFile";
5
+ import PreviewSelectedFiles from "./PreviewSelectedFiles";
5
6
  const Upload = ({ text, file, onChange, allowedTypes, uploadType = "application-file", maxSize, multiple, }) => {
6
7
  // refs
7
8
  const _firstLoad = useRef(false);
@@ -9,26 +10,32 @@ const Upload = ({ text, file, onChange, allowedTypes, uploadType = "application-
9
10
  const _arUplaod = useRef(null);
10
11
  // refs -> File Data
11
12
  const _validationErrors = useRef([]);
13
+ // variables
14
+ const [className, setClassName] = useState(["button"]);
12
15
  // states
13
16
  const [selectedFile, setSelectedFile] = useState(undefined);
14
- const [selectedFileBase64, setSelectedFileBase64] = useState(undefined);
15
17
  const [selectedFiles, setSelectedFiles] = useState([]);
16
18
  const [validationErrors, setValidationErrors] = useState([]);
17
19
  // methods
18
- const handleFileChange = (files) => {
20
+ const handleFileChange = useCallback((files) => {
19
21
  const _files = Array.from(files ?? []);
20
- if (multiple) {
21
- setSelectedFiles((prev) => {
22
- const previousFileNames = prev.map((f) => f.name);
23
- const newFiles = _files.filter((f) => !previousFileNames.includes(f.name)) ?? [];
24
- return [...prev, ...newFiles];
25
- });
22
+ if (_files.length > 0) {
23
+ if (multiple) {
24
+ setSelectedFiles((prev) => {
25
+ const previousFileNames = prev.map((f) => f.name);
26
+ const newFiles = _files.filter((f) => !previousFileNames.includes(f.name)) ?? [];
27
+ return [...prev, ...newFiles];
28
+ });
29
+ }
30
+ else {
31
+ setSelectedFile(_files[0]);
32
+ }
26
33
  }
27
34
  else {
28
- setSelectedFile(_files[0]);
35
+ multiple ? setSelectedFiles(file) : setSelectedFile(file);
29
36
  }
30
- };
31
- const handleFileRemove = (fileToRemove) => {
37
+ }, []);
38
+ const handleFileRemove = useCallback((fileToRemove) => {
32
39
  if (multiple) {
33
40
  const dataTransfer = new DataTransfer();
34
41
  setSelectedFiles((prev) => {
@@ -36,32 +43,34 @@ const Upload = ({ text, file, onChange, allowedTypes, uploadType = "application-
36
43
  newList.forEach((file) => dataTransfer.items.add(file));
37
44
  if (_input.current)
38
45
  _input.current.files = dataTransfer.files;
46
+ if (newList.length === 0)
47
+ setClassName((prev) => prev.filter((c) => c !== "has-file"));
39
48
  return newList;
40
49
  });
41
50
  }
42
51
  else {
43
52
  setSelectedFile(undefined);
44
- setSelectedFileBase64(undefined);
53
+ setClassName((prev) => prev.filter((c) => c !== "has-file"));
45
54
  }
46
- };
47
- const handleValidationFile = (file) => {
55
+ }, []);
56
+ const handleValidationFile = useCallback((file) => {
48
57
  const newErrors = [];
49
58
  if (allowedTypes) {
50
59
  if (!allowedTypes.includes(file.type)) {
51
- newErrors.push({ [file.name]: "Geçersiz dosya türü." });
60
+ newErrors.push({ fileName: file.name, message: "Geçersiz dosya türü." });
52
61
  _validationErrors.current.push(file.name);
53
62
  }
54
63
  }
55
64
  if (maxSize) {
56
65
  const _maxSize = maxSize * 1024 * 1024; // MB
57
66
  if (file.size > _maxSize) {
58
- newErrors.push({ [file.name]: "Dosya boyutu çok büyük." });
67
+ newErrors.push({ fileName: file.name, message: "Dosya boyutu çok büyük." });
59
68
  _validationErrors.current.push(file.name);
60
69
  }
61
70
  }
62
71
  setValidationErrors((prev) => [...prev, ...newErrors]);
63
- };
64
- function handleFileToBase64(file) {
72
+ }, []);
73
+ const handleFileToBase64 = useCallback((file) => {
65
74
  return new Promise((resolve, reject) => {
66
75
  const reader = new FileReader();
67
76
  reader.onload = () => {
@@ -75,7 +84,37 @@ const Upload = ({ text, file, onChange, allowedTypes, uploadType = "application-
75
84
  reader.onerror = reject;
76
85
  reader.readAsDataURL(file);
77
86
  });
78
- }
87
+ }, []);
88
+ const handleDrag = useCallback((e) => {
89
+ e.preventDefault();
90
+ e.stopPropagation();
91
+ if (e.type === "dragenter" || e.type === "dragover") {
92
+ setClassName((prev) => {
93
+ const index = prev.findIndex((c) => c === "dragging");
94
+ if (index === -1)
95
+ return [...prev, "dragging"];
96
+ return prev;
97
+ });
98
+ }
99
+ else {
100
+ setClassName((prev) => prev.filter((c) => c !== "dragging"));
101
+ }
102
+ }, []);
103
+ const handleDrop = useCallback((e) => {
104
+ e.preventDefault();
105
+ e.stopPropagation();
106
+ const files = e.dataTransfer.files;
107
+ if (files && files.length > 0) {
108
+ if (multiple) {
109
+ setSelectedFiles(Array.from(files));
110
+ }
111
+ else {
112
+ setSelectedFile(files[files.length - 1]);
113
+ _firstLoad.current = true;
114
+ }
115
+ }
116
+ setClassName((prev) => prev.filter((c) => c !== "dragging"));
117
+ }, []);
79
118
  // useEffects
80
119
  useEffect(() => {
81
120
  (async () => {
@@ -85,6 +124,8 @@ const Upload = ({ text, file, onChange, allowedTypes, uploadType = "application-
85
124
  _validationErrors.current = [];
86
125
  if (_input.current) {
87
126
  if (multiple) {
127
+ if (selectedFiles.length === 0)
128
+ return;
88
129
  // Seçilmiş olan dosyalar validasyona gönderiliyor.
89
130
  selectedFiles.forEach((f) => handleValidationFile(f));
90
131
  const inValidFiles = Array.from(new Set(_validationErrors.current));
@@ -97,13 +138,19 @@ const Upload = ({ text, file, onChange, allowedTypes, uploadType = "application-
97
138
  // Geçerli olan dosyalar base64'e dönüştürülüyor...
98
139
  const base64Array = await Promise.all(validFiles.map((validFile) => handleFileToBase64(validFile)));
99
140
  onChange(fileFormData, validFiles, base64Array, _validationErrors.current.length === 0);
141
+ // Eğer dosya varsa.
142
+ setClassName((prev) => {
143
+ const index = prev.findIndex((c) => c === "has-file");
144
+ if (index === -1)
145
+ return [...prev, "has-file"];
146
+ return prev;
147
+ });
100
148
  }
101
149
  else {
102
150
  if (selectedFile) {
103
151
  handleValidationFile(selectedFile);
104
152
  fileFormData.append("file", selectedFile);
105
153
  onChange(fileFormData, selectedFile, await handleFileToBase64(selectedFile));
106
- setSelectedFileBase64(await handleFileToBase64(selectedFile));
107
154
  // Input içerisine dosyalar aktarılıyor.
108
155
  dataTransfer.items.add(selectedFile);
109
156
  _input.current.files = dataTransfer.files;
@@ -115,50 +162,41 @@ const Upload = ({ text, file, onChange, allowedTypes, uploadType = "application-
115
162
  useEffect(() => {
116
163
  if (_firstLoad.current)
117
164
  return;
118
- multiple ? setSelectedFiles(file) : setSelectedFile(file);
119
- _firstLoad.current = true;
165
+ if (multiple) {
166
+ if (file.length === 0)
167
+ return;
168
+ setSelectedFiles(file);
169
+ _firstLoad.current = true;
170
+ }
171
+ else {
172
+ if (!file)
173
+ return;
174
+ setSelectedFile(file);
175
+ _firstLoad.current = true;
176
+ }
120
177
  }, [file]);
178
+ useEffect(() => {
179
+ if (multiple)
180
+ setClassName((prev) => [...prev, "multiple"]);
181
+ }, []);
121
182
  return (React.createElement("div", { ref: _arUplaod, className: "ar-upload" },
122
183
  React.createElement("input", { ref: _input, type: "file", onChange: (event) => handleFileChange(event.target.files), multiple: multiple }),
123
184
  React.createElement("div", { className: "ar-upload-button" },
124
- React.createElement("div", { className: "button", onClick: () => {
185
+ React.createElement("div", { className: className.map((c) => c).join(" "), onDragEnter: handleDrag, onDragLeave: handleDrag, onDragOver: handleDrag, onDrop: handleDrop, onClick: () => {
125
186
  if (_input.current)
126
187
  _input.current.click();
127
188
  } },
128
- uploadType === "image" && selectedFileBase64 && React.createElement("img", { src: selectedFileBase64 }),
129
- (uploadType === "application-file" || !selectedFileBase64) && (React.createElement(React.Fragment, null,
130
- React.createElement("div", { className: "information" },
131
- React.createElement(ARIcon, { variant: "linear", icon: "Upload", stroke: "var(--gray-600)", fill: "transparent" }),
189
+ uploadType === "image" &&
190
+ (multiple ? (React.createElement(PreviewSelectedFiles, { selectedFiles: selectedFiles, validationErrors: validationErrors, handleFileToBase64: handleFileToBase64, handleFileRemove: handleFileRemove })) : (React.createElement(PreviewSelectedFile, { selectedFile: selectedFile, handleFileToBase64: handleFileToBase64, handleFileRemove: handleFileRemove }))),
191
+ !selectedFile && selectedFiles.length === 0 && (React.createElement(React.Fragment, null,
192
+ React.createElement("div", { className: "upload" },
193
+ React.createElement(ARIcon, { variant: "fill", icon: "CloudUpload", size: 32 }),
132
194
  React.createElement("div", { className: "properies" },
133
195
  allowedTypes && (React.createElement("div", { className: "allow-types" }, allowedTypes?.map((allowedType) => allowedType.split("/")[1].toLocaleUpperCase()).join(", "))),
134
196
  maxSize && React.createElement("div", { className: "max-size" },
135
197
  "up to ",
136
198
  maxSize,
137
199
  "MB"))),
138
- text && React.createElement("span", null, text)))),
139
- React.createElement("div", { className: "ar-upload-files" }, multiple ? (React.createElement("ul", null, selectedFiles.map((selectedFile, index) => {
140
- let _className = [];
141
- const errorMessages = validationErrors
142
- .filter((error) => Object.keys(error).includes(selectedFile.name))
143
- .map((error) => error[selectedFile.name]);
144
- if (errorMessages.length > 0)
145
- _className.push("error");
146
- const content = (React.createElement("div", { className: "file" },
147
- React.createElement("div", { className: "information" },
148
- React.createElement("span", null, selectedFile.name),
149
- React.createElement("span", null,
150
- (selectedFile.size / 1024).toFixed(3),
151
- "KB")),
152
- React.createElement("div", { className: "delete", onClick: () => handleFileRemove(selectedFile) },
153
- React.createElement(ARIcon, { icon: "CloseCircle", fill: "transparent", size: 20 }))));
154
- return (React.createElement("li", { key: index, className: _className.map((c) => c).join(" ") }, errorMessages.length === 0 ? (content) : (React.createElement(Tooltip, { text: errorMessages.map((message) => message ?? "") }, content))));
155
- }))) : (selectedFile && (React.createElement("div", { className: "file" },
156
- React.createElement("div", { className: "information" },
157
- React.createElement("span", null, selectedFile.name),
158
- React.createElement("span", null,
159
- (selectedFile.size / 1024).toFixed(3),
160
- "KB")),
161
- React.createElement("div", { className: "delete", onClick: () => handleFileRemove(selectedFile) },
162
- React.createElement(ARIcon, { icon: "CloseCircle", fill: "transparent", size: 20 })))))))));
200
+ text && React.createElement("span", null, text)))))));
163
201
  };
164
202
  export default Upload;
@@ -105,33 +105,25 @@ class Icon {
105
105
  return (React.createElement(React.Fragment, null,
106
106
  React.createElement("path", { d: "M12 7.75V13M2.92 8.58c0-1.12.6-2.16 1.57-2.73l5.94-3.43c.97-.56 2.17-.56 3.15 0l5.94 3.43c.97.56 1.57 1.6 1.57 2.73v6.84c0 1.12-.6 2.16-1.57 2.73l-5.94 3.43c-.97.56-2.17.56-3.15 0l-5.94-3.43a3.15 3.15 0 0 1-1.57-2.73v-2.76", stroke: this._stroke, strokeWidth: this._strokeWidth, strokeLinecap: "round", strokeLinejoin: "round" }),
107
107
  React.createElement("path", { d: "M12 16.2v.1", stroke: this._stroke, strokeWidth: this._strokeWidth, strokeLinecap: "round", strokeLinejoin: "round" })));
108
+ case "ExclamationCircle":
109
+ return (React.createElement(React.Fragment, null,
110
+ React.createElement("path", { d: "M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14m0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16" }),
111
+ React.createElement("path", { d: "M7.002 11a1 1 0 1 1 2 0 1 1 0 0 1-2 0M7.1 4.995a.905.905 0 1 1 1.8 0l-.35 3.507a.552.552 0 0 1-1.1 0z" })));
108
112
  default:
109
113
  return null;
110
114
  }
111
- case "bulk":
115
+ case "fill":
112
116
  switch (icon) {
113
- case "Drive":
114
- return (React.createElement(React.Fragment, null,
115
- React.createElement("path", { opacity: ".4", d: "M16.19 2H7.82C4.18 2 2.01 4.17 2.01 7.81v8.37c0 3.64 2.17 5.81 5.81 5.81h8.37c3.64 0 5.81-2.17 5.81-5.81V7.81C22 4.17 19.83 2 16.19 2Z", fill: this._fill }),
116
- React.createElement("path", { d: "M6 9c-.41 0-.75-.34-.75-.75v-2c0-.41.34-.75.75-.75s.75.34.75.75v2c0 .41-.34.75-.75.75ZM10 9c-.41 0-.75-.34-.75-.75v-2c0-.41.34-.75.75-.75s.75.34.75.75v2c0 .41-.34.75-.75.75ZM6 18.75c-.41 0-.75-.34-.75-.75v-2c0-.41.34-.75.75-.75s.75.34.75.75v2c0 .41-.34.75-.75.75ZM10 18.75c-.41 0-.75-.34-.75-.75v-2c0-.41.34-.75.75-.75s.75.34.75.75v2c0 .41-.34.75-.75.75ZM18 8h-4c-.41 0-.75-.34-.75-.75s.34-.75.75-.75h4c.41 0 .75.34.75.75S18.41 8 18 8ZM18 17.75h-4c-.41 0-.75-.34-.75-.75s.34-.75.75-.75h4c.41 0 .75.34.75.75s-.34.75-.75.75ZM22 11.25H2v1.5h20v-1.5Z", fill: this._fill })));
117
- case "Folder":
118
- return (React.createElement(React.Fragment, null,
119
- React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "32", height: "32", viewBox: "0 0 24 24", fill: "none" },
120
- React.createElement("path", { d: "M22 11.071v5.58c0 2.95-2.4 5.35-5.35 5.35h-9.3c-2.95 0-5.35-2.4-5.35-5.35v-7.21h19.74c.15.45.23.91.25 1.4.01.07.01.16.01.23Z", fill: this._fill }),
121
- React.createElement("path", { opacity: ".4", d: "M21.74 9.44H2V6.42C2 3.98 3.98 2 6.42 2h2.33c1.63 0 2.14.53 2.79 1.4l1.4 1.86c.31.41.35.47.93.47h2.79c2.37-.01 4.39 1.55 5.08 3.71Z", fill: this._fill }))));
122
- case "Upload":
123
- return (React.createElement(React.Fragment, null,
124
- React.createElement("path", { d: "M20.5 10.19h-2.89c-2.37 0-4.3-1.93-4.3-4.3V3c0-.55-.45-1-1-1H8.07C4.99 2 2.5 4 2.5 7.57v8.86C2.5 20 4.99 22 8.07 22h7.86c3.08 0 5.57-2 5.57-5.57v-5.24c0-.55-.45-1-1-1Z", opacity: ".4", fill: this._fill }),
125
- React.createElement("path", { d: "M15.8 2.21c-.41-.41-1.12-.13-1.12.44v3.49c0 1.46 1.24 2.67 2.75 2.67.95.01 2.27.01 3.4.01.57 0 .87-.67.47-1.07-1.44-1.45-4.02-4.06-5.5-5.54ZM11.53 12.47l-2-2c-.01-.01-.02-.01-.02-.02a.855.855 0 0 0-.22-.15h-.02c-.08-.03-.16-.04-.24-.05h-.08c-.06 0-.13.02-.19.04-.03.01-.05.02-.07.03-.08.04-.16.08-.22.15l-2 2c-.29.29-.29.77 0 1.06.29.29.77.29 1.06 0l.72-.72V17c0 .41.34.75.75.75s.75-.34.75-.75v-4.19l.72.72c.15.15.34.22.53.22s.38-.07.53-.22c.29-.29.29-.77 0-1.06Z", fill: this._fill })));
126
- case "Image":
127
- return (React.createElement(React.Fragment, null,
128
- React.createElement("path", { opacity: ".4", d: "m22.019 16.82-3.13-7.32c-.57-1.34-1.42-2.1-2.39-2.15-.96-.05-1.89.62-2.6 1.9l-1.9 3.41c-.4.72-.97 1.15-1.59 1.2-.63.06-1.26-.27-1.77-.92l-.22-.28c-.71-.89-1.59-1.32-2.49-1.23-.9.09-1.67.71-2.18 1.72l-1.73 3.45c-.62 1.25-.56 2.7.17 3.88.73 1.18 2 1.89 3.39 1.89h12.76c1.34 0 2.59-.67 3.33-1.79.76-1.12.88-2.53.35-3.76Z", fill: this._fill }),
129
- React.createElement("path", { d: "M6.97 8.381a3.38 3.38 0 1 0 0-6.76 3.38 3.38 0 0 0 0 6.76Z", fill: this._fill })));
117
+ case "CloudUpload":
118
+ return (React.createElement("path", { fillRule: "evenodd", d: "M8 0a5.53 5.53 0 0 0-3.594 1.342c-.766.66-1.321 1.52-1.464 2.383C1.266 4.095 0 5.555 0 7.318 0 9.366 1.708 11 3.781 11H7.5V5.707L5.354 7.854a.5.5 0 1 1-.708-.708l3-3a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1-.708.708L8.5 5.707V11h4.188C14.502 11 16 9.57 16 7.773c0-1.636-1.242-2.969-2.834-3.194C12.923 1.999 10.69 0 8 0m-.5 14.5V11h1v3.5a.5.5 0 0 1-1 0" }));
130
119
  case "Trash":
120
+ return (React.createElement("path", { d: "M2.5 1a1 1 0 0 0-1 1v1a1 1 0 0 0 1 1H3v9a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V4h.5a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1H10a1 1 0 0 0-1-1H7a1 1 0 0 0-1 1zm3 4a.5.5 0 0 1 .5.5v7a.5.5 0 0 1-1 0v-7a.5.5 0 0 1 .5-.5M8 5a.5.5 0 0 1 .5.5v7a.5.5 0 0 1-1 0v-7A.5.5 0 0 1 8 5m3 .5v7a.5.5 0 0 1-1 0v-7a.5.5 0 0 1 1 0" }));
121
+ case "Eye":
131
122
  return (React.createElement(React.Fragment, null,
132
- React.createElement("path", { d: "M21.07 5.23c-1.61-.16-3.22-.28-4.84-.37v-.01l-.22-1.3c-.15-.92-.37-2.3-2.71-2.3h-2.62c-2.33 0-2.55 1.32-2.71 2.29l-.21 1.28c-.93.06-1.86.12-2.79.21l-2.04.2c-.42.04-.72.41-.68.82.04.41.4.71.82.67l2.04-.2c5.24-.52 10.52-.32 15.82.21h.08c.38 0 .71-.29.75-.68a.766.766 0 0 0-.69-.82Z", fill: this._fill }),
133
- React.createElement("path", { opacity: ".399", d: "M19.23 8.14c-.24-.25-.57-.39-.91-.39H5.68c-.34 0-.68.14-.91.39-.23.25-.36.59-.34.94l.62 10.26c.11 1.52.25 3.42 3.74 3.42h6.42c3.49 0 3.63-1.89 3.74-3.42l.62-10.25c.02-.36-.11-.7-.34-.95Z", fill: this._fill }),
134
- React.createElement("path", { "fill-rule": "evenodd", "clip-rule": "evenodd", d: "M9.58 17a.75.75 0 0 1 .75-.75h3.33a.75.75 0 0 1 0 1.5h-3.33a.75.75 0 0 1-.75-.75ZM8.75 13a.75.75 0 0 1 .75-.75h5a.75.75 0 0 1 0 1.5h-5a.75.75 0 0 1-.75-.75Z", fill: this._fill })));
123
+ React.createElement("path", { d: "M10.5 8a2.5 2.5 0 1 1-5 0 2.5 2.5 0 0 1 5 0" }),
124
+ React.createElement("path", { d: "M0 8s3-5.5 8-5.5S16 8 16 8s-3 5.5-8 5.5S0 8 0 8m8 3.5a3.5 3.5 0 1 0 0-7 3.5 3.5 0 0 0 0 7" })));
125
+ case "ExclamationDiamond":
126
+ return (React.createElement("path", { d: "M9.05.435c-.58-.58-1.52-.58-2.1 0L.436 6.95c-.58.58-.58 1.519 0 2.098l6.516 6.516c.58.58 1.519.58 2.098 0l6.516-6.516c.58-.58.58-1.519 0-2.098zM8 4c.535 0 .954.462.9.995l-.35 3.507a.552.552 0 0 1-1.1 0L7.1 4.995A.905.905 0 0 1 8 4m.002 6a1 1 0 1 1 0 2 1 1 0 0 1 0-2" }));
135
127
  default:
136
128
  return null;
137
129
  }
@@ -1,7 +1,6 @@
1
1
  import React from "react";
2
- import Svg from "./Svg";
3
2
  import Icon from "./Compiler";
4
- export const ARIcon = ({ viewBox, size, variant = "linear", icon, fill = "var(--dark)", stroke = "var(--dark)", strokeWidth = 1, style, }) => {
3
+ export const ARIcon = ({ viewBox, size = 16, variant = "linear", icon, fill = "var(--dark)", stroke = "var(--dark)", strokeWidth = 1, style, }) => {
5
4
  const { Compiler } = new Icon(fill, stroke, strokeWidth);
6
- return (React.createElement(Svg, { viewBox: viewBox, fill: fill, width: size, height: size, style: style }, Compiler(variant, icon)));
5
+ return (React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: viewBox ?? `0 0 16 16`, fill: fill, width: size, height: size, style: style }, Compiler(variant, icon)));
7
6
  };
@@ -68,8 +68,8 @@ export type Errors<TData> = Partial<{
68
68
  [key in keyof TData]: string;
69
69
  }>;
70
70
  export type AllowedTypes = "image/jpeg" | "image/png" | "image/gif" | "image/webp" | "image/svg+xml" | "image/bmp" | "image/tiff" | "application/pdf" | "application/msword" | "application/vnd.openxmlformats-officedocument.wordprocessingml.document" | "application/vnd.ms-excel" | "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" | "application/vnd.openxmlformats-officedocument.presentationml.presentation" | "application/zip" | "application/x-rar-compressed" | "application/x-7z-compressed" | "application/gzip" | "application/json" | "application/xml" | "text/plain" | "text/csv" | "text/html" | "video/mp4" | "video/quicktime" | "video/x-msvideo" | "video/x-matroska" | "video/webm" | "video/x-flv" | "audio/mpeg" | "audio/wav" | "audio/ogg" | "audio/aac" | "audio/flac" | "application/octet-stream";
71
- export type IconVariants = "linear" | "bulk";
72
- export type Icons = "Add" | "ArrowLeft" | "ArrowRight" | "CloseCircle" | "CloseSquare" | "Drive" | "Document" | "Folder" | "Trash" | "Upload" | "Image" | "Import" | "Bold" | "Italic" | "Underline" | "Success" | "Strikethrough" | "BulletList" | "NumberList" | "TextAlingLeft" | "TextAlingCenter" | "TextAlingRight" | "Filter" | "TickCircle" | "File" | "Warning";
71
+ export type IconVariants = "linear" | "fill";
72
+ export type Icons = "Add" | "ArrowLeft" | "ArrowRight" | "Bold" | "BulletList" | "CloseCircle" | "CloseSquare" | "CloudUpload" | "Document" | "Drive" | "ExclamationCircle" | "ExclamationDiamond" | "Eye" | "File" | "Filter" | "Folder" | "Image" | "Import" | "Italic" | "NumberList" | "Strikethrough" | "Success" | "TextAlingCenter" | "TextAlingLeft" | "TextAlingRight" | "TickCircle" | "Trash" | "Underline" | "Upload" | "Warning";
73
73
  export type PieChartDataType = {
74
74
  value: number;
75
75
  text: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ar-design",
3
- "version": "0.2.77",
3
+ "version": "0.2.79",
4
4
  "main": "./dist/index.js",
5
5
  "module": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -47,5 +47,8 @@
47
47
  "ts-node": "^10.9.2",
48
48
  "tslib": "^2.6.3",
49
49
  "typescript": "^5.5.4"
50
+ },
51
+ "dependencies": {
52
+ "bootstrap-icons": "^1.13.1"
50
53
  }
51
54
  }
@@ -1,4 +0,0 @@
1
- import React from "react";
2
- import { IChildren } from "../../libs/types/IGlobalProps";
3
- declare const Svg: React.FC<React.SVGProps<SVGElement> & IChildren>;
4
- export default Svg;
@@ -1,5 +0,0 @@
1
- import React from "react";
2
- const Svg = ({ children, viewBox, fill = "none", width = 32, height = 32, style, }) => {
3
- return (React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: viewBox ?? "0 0 24 24", fill: fill, width: width, height: height, style: style }, children));
4
- };
5
- export default Svg;