@weni/unnnic-system 1.24.21 → 1.25.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.
@@ -1,63 +1,21 @@
1
1
  <template>
2
2
  <div>
3
+ <UnnnicDropArea
4
+ :currentFiles.sync="currentFiles"
5
+ :acceptMultiple="acceptMultiple"
6
+ :supportedFormats="supportedFormats"
7
+ :maxFileSize="maxFileSize"
8
+ :shouldReplace="shouldReplace"
9
+ :maximumUploads="maximumUploads"
10
+ :subtitle="subtitle"
11
+ @file-change="$emit('fileChange', $event)"
12
+ />
13
+
3
14
  <div
4
- ref="dropzone"
5
- :class="{
6
- 'unnnic-upload-area__dropzone': true,
7
- 'unnnic-upload-area__dropzone__is-dragover': isDragging,
8
- 'unnnic-upload-area__dropzone__has-error': hasError,
9
- }"
10
- v-on:dragenter.stop.prevent="dragenter"
11
- v-on:dragover.stop.prevent="dragover"
12
- v-on:dragleave.stop.prevent="dragleave"
13
- v-on:dragend.stop.prevent="dragend"
14
- v-on:drop.stop.prevent="drop"
15
- @click="() => this.$refs.file.click()"
15
+ v-if="currentFiles.length > 0"
16
+ class="unnnic-upload-area__cards"
16
17
  >
17
- <unnnic-icon-svg
18
- class="unnnic-upload-area__dropzone__icon"
19
- icon="upload-bottom-1"
20
- :scheme="hasError ? 'feedback-red' : 'brand-weni'"
21
- size="xl"
22
- />
23
-
24
- <div class="unnnic-upload-area__dropzone__content">
25
- <span class="unnnic-upload-area__dropzone__content__title">
26
- {{ $t('upload_area.title.text') }}
27
- <span
28
- :class="`unnnic-upload-area__dropzone__content__title__${
29
- hasError ? 'error' : 'search'
30
- }`"
31
- >
32
- {{ $t('upload_area.title.highlight') }}
33
- </span>
34
- </span>
35
- <span
36
- :class="[
37
- 'unnnic-upload-area__dropzone__content__subtitle',
38
- { 'unnnic-upload-area__dropzone__content__subtitle__error': hasError },
39
- ]"
40
- :title='formattedSupportedFormats'
41
- >
42
- {{
43
- subtitle ||
44
- `${$t(
45
- `upload_area${hasError ? '.invalid' : ''}.subtitle`
46
- )} ${formattedSupportedFormats}`
47
- }}
48
- </span>
49
- </div>
50
- <input
51
- type="file"
52
- ref="file"
53
- :accept="supportedFormats"
54
- :multiple="acceptMultiple"
55
- @input="handleFileChange"
56
- style="display: none;"
57
- />
58
- </div>
59
- <div v-if="currentFiles.length > 0" class="unnnic-upload-area__cards">
60
- <unnnic-import-card
18
+ <UnnnicImportCard
61
19
  v-for="(file, index) in currentFiles"
62
20
  :key="index"
63
21
  :title="file.name"
@@ -69,24 +27,26 @@
69
27
  :acceptedFormats="supportedFormats"
70
28
  uploadIcon="button-refresh-arrows-1"
71
29
  @delete="removeFile(index)"
72
- @modifiedFile="modifyFile(index, $event)"
30
+ @modified-file="modifyFile(index, $event)"
73
31
  />
74
32
  </div>
75
33
  </div>
76
34
  </template>
77
35
 
78
36
  <script>
79
- import mime from 'mime-types';
80
-
81
- import UnnnicIconSvg from '../Icon.vue';
37
+ import UnnnicDropArea from '../DropArea/DropArea.vue';
82
38
  import UnnnicImportCard from '../ImportCard/ImportCard.vue';
83
39
 
84
40
  export default {
85
- name: 'unnnic-upload-area',
41
+ name: 'UnnnicUploadArea',
86
42
  components: {
87
- UnnnicIconSvg,
43
+ UnnnicDropArea,
88
44
  UnnnicImportCard,
89
45
  },
46
+ model: {
47
+ prop: 'files',
48
+ event: 'fileChange',
49
+ },
90
50
  props: {
91
51
  files: {
92
52
  type: Array,
@@ -134,152 +94,26 @@ export default {
134
94
  default: '',
135
95
  },
136
96
  },
137
- model: {
138
- prop: 'files',
139
- event: 'fileChange',
140
- },
97
+
98
+ emits: ['fileChange'],
99
+
141
100
  data() {
142
101
  return {
143
- hasError: false,
144
- isDragging: false,
145
- dragEnterCounter: 0, // to handle dragenter/dragleave on child elements
146
102
  currentFiles: this.files,
147
103
  };
148
104
  },
105
+
149
106
  watch: {
150
107
  files(newValue) {
151
108
  this.currentFiles = newValue;
152
109
  },
153
110
  },
154
- computed: {
155
- formattedSupportedFormats() {
156
- const formats = this.supportedFormats.split(',').map((format) => format.toUpperCase());
157
111
 
158
- return formats.join(', ');
159
- },
160
- },
161
112
  methods: {
162
- dragenter() {
163
- this.dragEnterCounter += 1;
164
- this.isDragging = true;
165
- },
166
- dragover() {
167
- this.isDragging = true;
168
- },
169
- dragleave() {
170
- this.dragEnterCounter -= 1;
171
- if (this.dragEnterCounter === 0) {
172
- this.isDragging = false;
173
- }
174
- },
175
- dragend() {
176
- this.isDragging = false;
177
- },
178
- drop(event) {
179
- this.isDragging = false;
180
-
181
- const { files } = event.dataTransfer;
182
-
183
- if (this.validateFiles(files)) {
184
- this.addFiles(files);
185
- }
186
- },
187
- handleFileChange(event) {
188
- const { files } = event.target;
189
-
190
- if (this.validateFiles(files)) {
191
- this.addFiles(files);
192
- }
193
- this.$refs.file.value = '';
194
- },
195
- validateFiles(files) {
196
- if (!this.acceptMultiple && files.length > 1) {
197
- this.setErrorState();
198
- return false;
199
- }
200
-
201
- if (!this.validFormat(files)) {
202
- this.setErrorState();
203
- return false;
204
- }
205
-
206
- if (!this.validSize(files)) {
207
- this.setErrorState();
208
- return false;
209
- }
210
-
211
- return true;
212
- },
213
- validFormat(files) {
214
- const formats = this.supportedFormats.split(',').map((format) => format.trim());
215
-
216
- const isValid = Array.from(files).find((file) => {
217
- const fileName = file.name.toLowerCase();
218
- const fileType = file.type.toLowerCase();
219
- const fileExtension = `.${fileName.split('.').pop()}`;
220
-
221
- const isValidFileExtension = formats.includes(fileExtension);
222
- const isValidFileType = fileType === mime.lookup(fileName);
223
-
224
- return isValidFileExtension && isValidFileType;
225
- });
226
-
227
- return isValid;
228
- },
229
-
230
- validSize(files) {
231
- if (!this.maxFileSize) {
232
- return true;
233
- }
234
-
235
- const isValid = Array.from(files).find((file) => {
236
- const sizeInMB = (file.size / (1024 * 1024)).toFixed(2);
237
-
238
- return sizeInMB <= this.maxFileSize;
239
- });
240
-
241
- return isValid;
242
- },
243
-
244
- setErrorState() {
245
- this.hasError = true;
246
-
247
- setTimeout(() => {
248
- this.hasError = false;
249
- }, 5000);
250
- },
251
-
252
113
  emitFileChange() {
253
114
  this.$emit('fileChange', this.currentFiles);
254
115
  },
255
116
 
256
- addFiles(files) {
257
- let totalLength = files.length;
258
-
259
- if (!this.shouldReplace) {
260
- totalLength += this.currentFiles.length;
261
- }
262
-
263
- if (totalLength > this.maximumUploads) {
264
- this.setErrorState();
265
- return;
266
- }
267
-
268
- const validFiles = Array.from(files).filter((file) => {
269
- if (this.validFormat([file]) && this.validSize([file])) {
270
- return true;
271
- }
272
- return false;
273
- });
274
-
275
- if (this.shouldReplace) {
276
- this.currentFiles = validFiles;
277
- } else {
278
- this.currentFiles = this.currentFiles.concat(validFiles);
279
- }
280
- this.emitFileChange();
281
- },
282
-
283
117
  modifyFile(index, file) {
284
118
  this.currentFiles.splice(index, 1, file);
285
119
  this.emitFileChange();
@@ -289,14 +123,6 @@ export default {
289
123
  this.currentFiles.splice(index, 1);
290
124
  this.emitFileChange();
291
125
  },
292
-
293
- checkDragAndDropSupport() {
294
- const { dropzone } = this.$refs;
295
- return (
296
- 'FileReader' in window
297
- && ('draggable' in dropzone || ('ondragstart' in dropzone && 'ondrop' in dropzone))
298
- );
299
- },
300
126
  },
301
127
  };
302
128
  </script>
@@ -304,71 +130,7 @@ export default {
304
130
  <style lang="scss" scoped>
305
131
  @import '../../assets/scss/unnnic.scss';
306
132
 
307
- @function borderDashed($color) {
308
- @return url("data:image/svg+xml,%3csvg width='100%25' height='100%25' xmlns='http://www.w3.org/2000/svg'%3e%3crect width='100%25' height='100%25' fill='none' rx='16' ry='16' stroke='%23#{str-slice(quote($color), 2)}' stroke-width='4' stroke-dasharray='4%2c 12' stroke-dashoffset='9' stroke-linecap='square'/%3e%3c/svg%3e");
309
- }
310
-
311
133
  .unnnic-upload-area {
312
- &__dropzone {
313
- border-radius: $unnnic-border-radius-lg;
314
- background-color: $unnnic-color-background-carpet;
315
- padding: $unnnic-spacing-inset-lg;
316
-
317
- // Dashed border with increased dashes spacing and color neutral clean
318
- background-image: borderDashed($unnnic-color-neutral-cleanest);
319
-
320
- &__has-error {
321
- background-image: borderDashed($unnnic-color-feedback-red);
322
- }
323
-
324
- &__is-dragover {
325
- background-color: $unnnic-color-background-sky;
326
- background-image: borderDashed($unnnic-color-brand-weni);
327
- }
328
-
329
- &__icon {
330
- display: block;
331
- margin: 0 auto;
332
-
333
- margin-bottom: $unnnic-spacing-stack-sm;
334
- }
335
-
336
- &__content {
337
- display: flex;
338
- flex-direction: column;
339
- gap: $unnnic-spacing-stack-nano;
340
-
341
- text-align: center;
342
- font-family: $unnnic-font-family-secondary;
343
-
344
- &__title {
345
- color: $unnnic-color-neutral-darkest;
346
- font-weight: $unnnic-font-weight-bold;
347
- font-size: $unnnic-font-size-body-gt;
348
- line-height: $unnnic-font-size-body-gt + $unnnic-line-height-md;
349
-
350
- &__search {
351
- color: $unnnic-color-brand-weni;
352
- }
353
-
354
- &__error {
355
- color: $unnnic-color-feedback-red;
356
- }
357
- }
358
-
359
- &__subtitle {
360
- color: $unnnic-color-neutral-cloudy;
361
- font-weight: $unnnic-font-weight-regular;
362
- font-size: $unnnic-font-size-body-md;
363
- line-height: $unnnic-font-size-body-md + $unnnic-line-height-md;
364
-
365
- &__error {
366
- color: $unnnic-color-feedback-red;
367
- }
368
- }
369
- }
370
- }
371
-
372
134
  &__cards {
373
135
  margin-top: $unnnic-spacing-stack-md;
374
136
  display: flex;
@@ -376,11 +138,4 @@ export default {
376
138
  gap: $unnnic-spacing-stack-xs;
377
139
  }
378
140
  }
379
-
380
- .unnnic-upload-area__dropzone__dragndrop,
381
- .unnnic-upload-area__dropzone__uploading,
382
- .unnnic-upload-area__dropzone__success,
383
- .unnnic-upload-area__dropzone__error {
384
- display: none;
385
- }
386
141
  </style>
@@ -57,6 +57,7 @@ import Switch from './Switch/Switch.vue';
57
57
  import Slider from './Slider/Slider.vue';
58
58
  import DataArea from './DataArea/DataArea.vue';
59
59
  import Pagination from './Pagination/Pagination.vue';
60
+ import DropArea from './DropArea/DropArea.vue';
60
61
  import UploadArea from './UploadArea/UploadArea.vue';
61
62
  import ImportCard from './ImportCard/ImportCard.vue';
62
63
  import DateFilter from './DateFilter/DateFilter.vue';
@@ -142,6 +143,7 @@ const components = {
142
143
  unnnicSlider: Slider,
143
144
  unnnicDataArea: DataArea,
144
145
  unnnicPagination: Pagination,
146
+ unnnicDropArea: DropArea,
145
147
  unnnicUploadArea: UploadArea,
146
148
  unnnicImportCard: ImportCard,
147
149
  unnnicDateFilter: DateFilter,
@@ -234,6 +236,7 @@ export const unnnicSwitch = Switch;
234
236
  export const unnnicSlider = Slider;
235
237
  export const unnnicDataArea = DataArea;
236
238
  export const unnnicPagination = Pagination;
239
+ export const unnnicDropArea = DropArea;
237
240
  export const unnnicUploadArea = UploadArea;
238
241
  export const unnnicImportCard = ImportCard;
239
242
  export const unnnicDateFilter = DateFilter;
@@ -0,0 +1,80 @@
1
+ import UnnnicDropArea from '../components/DropArea/DropArea.vue';
2
+
3
+ const events = {
4
+ 'update:currentFiles': { action: 'update:currentFiles' },
5
+ fileChange: { action: 'fileChange' },
6
+ unsupportedFormat: { action: 'unsupportedFormat' },
7
+ exceededTheMaximumFileSizeLimit: { action: 'exceededTheMaximumFileSizeLimit' },
8
+ };
9
+
10
+ function getterEvents() {
11
+ return Object.keys(events)
12
+ .reduce(
13
+ (previous, current) => (
14
+ { ...previous, [current]: this.$props[current] }
15
+ ), {},
16
+ );
17
+ }
18
+
19
+ export default {
20
+ title: 'Example/DropArea',
21
+ component: UnnnicDropArea,
22
+ argTypes: {
23
+ acceptMultiple: { control: 'boolean' },
24
+ supportedFormats: { control: 'text' },
25
+ maxFileSize: { control: 'number' },
26
+ shouldReplace: { control: 'boolean' },
27
+ currentFiles: { control: 'object' },
28
+ maximumUploads: { control: 'number' },
29
+ subtitle: { control: 'text' },
30
+ ...events,
31
+ },
32
+ args: {
33
+ acceptMultiple: true,
34
+ supportedFormats: '*',
35
+ maxFileSize: undefined,
36
+ shouldReplace: false,
37
+ currentFiles: [],
38
+ maximumUploads: 1,
39
+ subtitle: '',
40
+ },
41
+ };
42
+
43
+ const Template = (_args, { argTypes }) => ({
44
+ components: { UnnnicDropArea },
45
+
46
+ props: Object.keys(argTypes),
47
+
48
+ computed: {
49
+ events: getterEvents,
50
+ },
51
+
52
+ template: `
53
+ <UnnnicDropArea v-bind="$props" v-on="events" />
54
+ `,
55
+ });
56
+
57
+ const TemplateWithTitleAndSubtitleSlots = (_args, { argTypes }) => ({
58
+ components: { UnnnicDropArea },
59
+
60
+ props: Object.keys(argTypes),
61
+
62
+ computed: {
63
+ events: getterEvents,
64
+ },
65
+
66
+ template: `
67
+ <UnnnicDropArea v-bind="$props" v-on="events">
68
+ <template #title>Title Slot</template>
69
+ <template #subtitle>Subtitle Slot</template>
70
+ </UnnnicDropArea>
71
+ `,
72
+ });
73
+
74
+ export const Default = Template.bind({});
75
+ Default.args = {};
76
+
77
+ export const WithTitleAndSubtitleSlots = TemplateWithTitleAndSubtitleSlots.bind(
78
+ {},
79
+ );
80
+ WithTitleAndSubtitleSlots.args = {};