@visns-studio/visns-components 5.8.1 → 5.8.3

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/package.json CHANGED
@@ -4,10 +4,10 @@
4
4
  "@fontsource/barlow": "^5.2.5",
5
5
  "@inovua/reactdatagrid-community": "^5.10.2",
6
6
  "@inovua/reactdatagrid-enterprise": "^5.10.2",
7
- "@nivo/bar": "^0.88.0",
8
- "@nivo/core": "^0.88.0",
9
- "@nivo/line": "^0.88.0",
10
- "@nivo/pie": "^0.88.0",
7
+ "@nivo/bar": "^0.95.0",
8
+ "@nivo/core": "^0.95.0",
9
+ "@nivo/line": "^0.95.0",
10
+ "@nivo/pie": "^0.95.0",
11
11
  "@tinymce/tinymce-react": "^6.1.0",
12
12
  "@visns-studio/visns-datagrid-community": "^1.0.14",
13
13
  "@visns-studio/visns-datagrid-enterprise": "^1.0.14",
@@ -18,14 +18,14 @@
18
18
  "awesome-debounce-promise": "^2.1.0",
19
19
  "browser-image-compression": "^2.0.2",
20
20
  "dayjs": "^1.11.13",
21
- "fabric": "^6.6.4",
21
+ "fabric": "^6.6.5",
22
22
  "file-saver": "^2.0.5",
23
- "framer-motion": "^12.9.7",
24
- "html-react-parser": "^5.2.3",
23
+ "framer-motion": "^12.11.0",
24
+ "html-react-parser": "^5.2.5",
25
25
  "lodash": "^4.17.21",
26
26
  "lodash.debounce": "^4.0.8",
27
27
  "moment": "^2.30.1",
28
- "motion": "^12.9.7",
28
+ "motion": "^12.11.0",
29
29
  "numeral": "^2.0.6",
30
30
  "pluralize": "^8.0.0",
31
31
  "qrcode.react": "^4.2.0",
@@ -60,12 +60,12 @@
60
60
  "validator": "^13.15.0",
61
61
  "vite": "^6.3.5",
62
62
  "yarn": "^1.22.22",
63
- "yet-another-react-lightbox": "^3.23.0"
63
+ "yet-another-react-lightbox": "^3.23.1"
64
64
  },
65
65
  "devDependencies": {
66
66
  "@babel/core": "^7.27.1",
67
67
  "@babel/plugin-transform-runtime": "^7.27.1",
68
- "@babel/preset-env": "^7.27.1",
68
+ "@babel/preset-env": "^7.27.2",
69
69
  "@babel/preset-react": "^7.27.1",
70
70
  "babel-loader": "^10.0.0",
71
71
  "copy-webpack-plugin": "^13.0.0",
@@ -74,7 +74,7 @@
74
74
  "mini-css-extract-plugin": "^2.9.2",
75
75
  "react": "^18.3.1",
76
76
  "react-dom": "^18.3.1",
77
- "sass": "^1.87.0",
77
+ "sass": "^1.88.0",
78
78
  "sass-loader": "^16.0.5"
79
79
  },
80
80
  "peerDependencies": {
@@ -82,7 +82,7 @@
82
82
  "react-dom": "^17.0.0 || ^18.0.0"
83
83
  },
84
84
  "name": "@visns-studio/visns-components",
85
- "version": "5.8.1",
85
+ "version": "5.8.3",
86
86
  "description": "Various packages to assist in the development of our Custom Applications.",
87
87
  "main": "src/index.js",
88
88
  "files": [
@@ -9,7 +9,8 @@ import imageCompression from 'browser-image-compression';
9
9
 
10
10
  import { confirmDialog } from '../utils/ConfirmDialog';
11
11
  import CustomFetch from '../crm/Fetch';
12
- import SortableList from './sorting/List';
12
+ import Gallery from './Gallery';
13
+ import styles from './styles/DropZone.module.css';
13
14
 
14
15
  function VisnsDropZone({ fetchData, files, settings }) {
15
16
  const [data, setData] = useState(files);
@@ -210,10 +211,92 @@ function VisnsDropZone({ fetchData, files, settings }) {
210
211
 
211
212
  return (
212
213
  <>
213
- {settings.type === 'gallery' ? (
214
- <div className="image-container">
215
- {data && data.length > 0 ? (
216
- <SortableList
214
+ <div className={styles.dropzoneWrapper}>
215
+ <div className={styles.progress}>
216
+ <motion.div
217
+ className={styles.progressBar}
218
+ initial={{ width: '0%' }}
219
+ animate={{
220
+ width: loadingProgress + '%',
221
+ }}
222
+ transition={{
223
+ duration: 1,
224
+ ease: [0.165, 0.84, 0.44, 1.0],
225
+ }}
226
+ />
227
+ </div>
228
+ <section className={styles.dropzoneContainer}>
229
+ <div {...getRootProps()}>
230
+ <input {...getInputProps()} />
231
+ {isDragActive ? (
232
+ <p>Drop the files here ...</p>
233
+ ) : (
234
+ <p>
235
+ Drag 'n' drop some files here, or click to
236
+ select files
237
+ </p>
238
+ )}
239
+ </div>
240
+ {settings.type !== 'gallery' ? (
241
+ <ul className={styles.dropzoneFiles}>
242
+ {data && data.length > 0
243
+ ? data.map((a, b) => (
244
+ <li
245
+ onClick={() => {
246
+ handleDownload(a.id);
247
+ }}
248
+ key={'tf-' + b}
249
+ className={styles.dropzoneFileItem}
250
+ >
251
+ <div
252
+ className={
253
+ styles.dropzoneFileContent
254
+ }
255
+ >
256
+ <File
257
+ strokeWidth={2}
258
+ size={20}
259
+ className={
260
+ styles.dropzoneFileIcon
261
+ }
262
+ />
263
+ <span
264
+ className={
265
+ styles.dropzoneFileName
266
+ }
267
+ >
268
+ {a.file_name}
269
+ </span>
270
+ </div>
271
+ <button
272
+ onClick={(e) => {
273
+ e.preventDefault();
274
+ e.stopPropagation();
275
+ handleDelete(
276
+ a.id,
277
+ a.file_name
278
+ );
279
+ }}
280
+ className={
281
+ styles.dropzoneDeleteBtn
282
+ }
283
+ aria-label="Delete file"
284
+ title="Delete file"
285
+ >
286
+ <TrashCan
287
+ strokeWidth={2}
288
+ size={18}
289
+ />
290
+ </button>
291
+ </li>
292
+ ))
293
+ : null}
294
+ </ul>
295
+ ) : null}
296
+ </section>
297
+ {settings.type === 'gallery' ? (
298
+ <div className={styles.imageContainer}>
299
+ <Gallery
217
300
  items={data}
218
301
  onSortEnd={onSortEnd}
219
302
  axis="xy"
@@ -222,115 +305,68 @@ function VisnsDropZone({ fetchData, files, settings }) {
222
305
  handleDelete={handleDelete}
223
306
  handleEdit={handleEdit}
224
307
  useDragHandle
225
- showImage={true}
308
+ distance={1}
226
309
  />
227
- ) : null}
228
- </div>
229
- ) : null}
230
- <div className="progress">
231
- <motion.div
232
- className="progress__bar"
233
- initial={{ width: '0%' }}
234
- animate={{
235
- width: loadingProgress + '%',
236
- }}
237
- transition={{
238
- duration: 1,
239
- ease: [0.165, 0.84, 0.44, 1.0],
240
- }}
241
- />
242
- </div>
243
- <section className="dropzone__container">
244
- <div {...getRootProps()}>
245
- <input {...getInputProps()} />
246
- {isDragActive ? (
247
- <p>Drop the files here ...</p>
248
- ) : (
249
- <p>
250
- Drag 'n' drop some files here, or click to select
251
- files
252
- </p>
253
- )}
254
- </div>
255
- {settings.type !== 'gallery' ? (
256
- <ul className="dropzone__files">
257
- {data && data.length > 0
258
- ? data.map((a, b) => (
259
- <li
260
- onClick={() => {
261
- handleDownload(a.id);
262
- }}
263
- key={'tf-' + b}
264
- >
265
- <File strokeWidth={2} size={18} />
266
- {a.file_name}
267
- <button
268
- onClick={(e) => {
269
- e.preventDefault();
270
- e.stopPropagation();
271
-
272
- handleDelete(a.id, a.file_name);
273
- }}
274
- >
275
- <TrashCan strokeWidth={2} size={18} />
276
- </button>
277
- </li>
278
- ))
279
- : null}
280
- </ul>
310
+ </div>
281
311
  ) : null}
282
- </section>
312
+ </div>
283
313
  <Popup
284
314
  open={modalShow}
285
315
  onClose={modalClose}
286
316
  closeOnDocumentClick={false}
287
317
  >
288
- <div className="modalwrap top--modal">
289
- <div className="modal">
290
- <div className="modal__header">
318
+ <div className={`${styles.modalwrap} top--modal`}>
319
+ <div className={styles.modal}>
320
+ <div className={styles.modalHeader}>
291
321
  <h1>Update File Metadata</h1>
292
322
  <button
293
- className="modal__close"
323
+ className={styles.modalClose}
294
324
  onClick={modalClose}
295
325
  >
296
326
  <CircleX strokeWidth={1} size={24} />
297
327
  </button>
298
328
  </div>
299
- <div className="modal__content">
329
+ <div className={styles.modalContent}>
300
330
  <div className="formcontainer">
301
331
  <form
302
332
  className="modalForm"
303
333
  onSubmit={handleSubmit}
304
334
  >
305
- <div className="formItem fwItem">
306
- <label className="fi__label">
335
+ <div
336
+ className={`${styles.formItem} fwItem`}
337
+ >
338
+ <label className={styles.fiLabel}>
307
339
  <input
308
340
  type="text"
309
341
  name="title"
310
342
  onChange={handleChange}
311
343
  value={modalData.title}
312
344
  />
313
- <span className="fi__span">
345
+ <span className={styles.fiSpan}>
314
346
  Title
315
347
  </span>
316
348
  </label>
317
349
  </div>
318
- <div className="formItem fwItem">
319
- <label className="fi__label">
350
+ <div
351
+ className={`${styles.formItem} fwItem`}
352
+ >
353
+ <label className={styles.fiLabel}>
320
354
  <textarea
321
355
  name="description"
322
356
  rows="5"
323
357
  onChange={handleChange}
324
358
  value={modalData.description}
325
359
  ></textarea>
326
- <span className="fi__span">
360
+ <span className={styles.fiSpan}>
327
361
  Description
328
362
  </span>
329
363
  </label>
330
364
  </div>
331
- <div className="formItem fwItem lastItem">
365
+ <div
366
+ className={`${styles.formItem} fwItem lastItem`}
367
+ >
332
368
  <button
333
- className="btn modalsave"
369
+ className={`btn ${styles.btnModalsave}`}
334
370
  type="submit"
335
371
  >
336
372
  Save
@@ -0,0 +1,147 @@
1
+ import React from 'react';
2
+ import {
3
+ SortableContainer,
4
+ SortableElement,
5
+ sortableHandle,
6
+ } from 'react-sortable-hoc';
7
+ import { ChevronVertical, Pencil, TrashCan } from 'akar-icons';
8
+ import styles from './styles/Gallery.module.css';
9
+
10
+ // Drag handle component
11
+ const DragHandle = sortableHandle(() => (
12
+ <div className={styles.galleryDragHandle}>
13
+ <ChevronVertical strokeWidth={1.5} size={16} />
14
+ </div>
15
+ ));
16
+
17
+ // Utility function to truncate text
18
+ const truncateText = (text, maxLength) => {
19
+ if (text && text.length > maxLength) {
20
+ return `${text.substring(0, maxLength)}...`;
21
+ }
22
+ return text;
23
+ };
24
+
25
+ // Sortable gallery item
26
+ const SortableGalleryItem = SortableElement(
27
+ ({ value, handleEdit, handleDelete }) => {
28
+ const imageUrl = value.image_url || value.file_url;
29
+ const title =
30
+ value.file_title || value.label || value.file_name || 'Untitled';
31
+ const description = value.file_description || '';
32
+
33
+ return (
34
+ <div className={styles.galleryItem}>
35
+ <DragHandle />
36
+
37
+ <div className={styles.galleryImage}>
38
+ {imageUrl ? (
39
+ <img src={imageUrl} alt={title} />
40
+ ) : (
41
+ <img
42
+ src="data:image/png;base64,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"
43
+ alt="Placeholder"
44
+ />
45
+ )}
46
+ </div>
47
+
48
+ <div className={styles.galleryInfo}>
49
+ <h3 className={styles.galleryTitle}>
50
+ {truncateText(title, 30)}
51
+ </h3>
52
+ {description && (
53
+ <p className={styles.galleryDescription}>
54
+ {truncateText(description, 100)}
55
+ </p>
56
+ )}
57
+ </div>
58
+
59
+ <div className={styles.galleryControls}>
60
+ {handleEdit && (
61
+ <button
62
+ className={`${styles.galleryControl} ${styles.edit}`}
63
+ onClick={(e) => {
64
+ e.preventDefault();
65
+ e.stopPropagation();
66
+ handleEdit(value);
67
+ }}
68
+ title="Edit"
69
+ >
70
+ <Pencil strokeWidth={1.5} size={16} />
71
+ </button>
72
+ )}
73
+
74
+ {handleDelete && (
75
+ <button
76
+ className={`${styles.galleryControl} ${styles.delete}`}
77
+ onClick={(e) => {
78
+ e.preventDefault();
79
+ e.stopPropagation();
80
+ handleDelete(
81
+ value.id,
82
+ value.file_name || value.label
83
+ );
84
+ }}
85
+ title="Delete"
86
+ >
87
+ <TrashCan strokeWidth={1.5} size={16} />
88
+ </button>
89
+ )}
90
+ </div>
91
+ </div>
92
+ );
93
+ }
94
+ );
95
+
96
+ // Sortable gallery container
97
+ const Gallery = SortableContainer(({ items, handleEdit, handleDelete }) => {
98
+ if (!items || items.length === 0) {
99
+ return (
100
+ <div className={styles.galleryEmpty}>
101
+ <div className={styles.galleryEmptyIcon}>
102
+ <svg
103
+ xmlns="http://www.w3.org/2000/svg"
104
+ width="48"
105
+ height="48"
106
+ viewBox="0 0 24 24"
107
+ fill="none"
108
+ stroke="currentColor"
109
+ strokeWidth="1"
110
+ strokeLinecap="round"
111
+ strokeLinejoin="round"
112
+ >
113
+ <rect
114
+ x="3"
115
+ y="3"
116
+ width="18"
117
+ height="18"
118
+ rx="2"
119
+ ry="2"
120
+ ></rect>
121
+ <circle cx="8.5" cy="8.5" r="1.5"></circle>
122
+ <polyline points="21 15 16 10 5 21"></polyline>
123
+ </svg>
124
+ </div>
125
+ <p className={styles.galleryEmptyText}>
126
+ No images to display. Drag and drop files to upload.
127
+ </p>
128
+ </div>
129
+ );
130
+ }
131
+
132
+ return (
133
+ <div className={styles.galleryGrid}>
134
+ {items.map((value, index) => (
135
+ <SortableGalleryItem
136
+ key={`item-${index}`}
137
+ index={index}
138
+ value={value}
139
+ handleEdit={handleEdit}
140
+ handleDelete={handleDelete}
141
+ />
142
+ ))}
143
+ </div>
144
+ );
145
+ });
146
+
147
+ export default Gallery;
@@ -11,9 +11,9 @@
11
11
 
12
12
  :global {
13
13
  .delete {
14
- width: 22px;
15
- height: 22px;
16
- color: var(--primary-color);
14
+ width: 32px;
15
+ height: 32px;
16
+ color: #fff;
17
17
  transition: color 0.2s ease;
18
18
  cursor: pointer;
19
19
  display: block;
@@ -24,8 +24,8 @@
24
24
  }
25
25
 
26
26
  .pencil-edit {
27
- width: 22px;
28
- height: 22px;
27
+ width: 32px;
28
+ height: 32px;
29
29
  color: var(--primary-color);
30
30
  transition: color 0.2s ease;
31
31
  cursor: pointer;