sprintify-ui 0.2.29 → 0.4.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.
@@ -14,7 +14,6 @@ declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<{
14
14
  };
15
15
  multiple: {
16
16
  type: import("vue").PropType<boolean>;
17
- required: true;
18
17
  default: boolean;
19
18
  };
20
19
  cropper: {
@@ -45,7 +44,7 @@ declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<{
45
44
  type: import("vue").PropType<() => boolean>;
46
45
  default: () => boolean;
47
46
  };
48
- }, {}, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, ("upload:start" | "upload:success" | "upload:fail" | "upload:end")[], "upload:start" | "upload:success" | "upload:fail" | "upload:end", import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly<import("vue").ExtractPropTypes<{
47
+ }, {}, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, ("success" | "end" | "start" | "fail")[], "success" | "end" | "start" | "fail", import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly<import("vue").ExtractPropTypes<{
49
48
  component: {
50
49
  type: import("vue").PropType<"BaseFilePicker" | "BaseFilePickerCrop">;
51
50
  default: string;
@@ -60,7 +59,6 @@ declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<{
60
59
  };
61
60
  multiple: {
62
61
  type: import("vue").PropType<boolean>;
63
- required: true;
64
62
  default: boolean;
65
63
  };
66
64
  cropper: {
@@ -92,10 +90,10 @@ declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<{
92
90
  default: () => boolean;
93
91
  };
94
92
  }>> & {
95
- "onUpload:start"?: ((...args: any[]) => any) | undefined;
96
- "onUpload:success"?: ((...args: any[]) => any) | undefined;
97
- "onUpload:fail"?: ((...args: any[]) => any) | undefined;
98
- "onUpload:end"?: ((...args: any[]) => any) | undefined;
93
+ onSuccess?: ((...args: any[]) => any) | undefined;
94
+ onEnd?: ((...args: any[]) => any) | undefined;
95
+ onStart?: ((...args: any[]) => any) | undefined;
96
+ onFail?: ((...args: any[]) => any) | undefined;
99
97
  }, {
100
98
  component: "BaseFilePicker" | "BaseFilePickerCrop";
101
99
  disabled: boolean;
@@ -72,7 +72,7 @@ declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<{
72
72
  default: string;
73
73
  type: PropType<"list" | "gallery" | "images">;
74
74
  };
75
- }, {}, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, ("update:modelValue" | "upload:start" | "upload:success" | "upload:fail" | "upload:end")[], "update:modelValue" | "upload:start" | "upload:success" | "upload:fail" | "upload:end", import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly<import("vue").ExtractPropTypes<{
75
+ }, {}, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, ("success" | "update:modelValue" | "end" | "start" | "fail")[], "success" | "update:modelValue" | "end" | "start" | "fail", import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly<import("vue").ExtractPropTypes<{
76
76
  modelValue: {
77
77
  default: undefined;
78
78
  type: PropType<MediaLibraryPayload>;
@@ -143,10 +143,10 @@ declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<{
143
143
  };
144
144
  }>> & {
145
145
  "onUpdate:modelValue"?: ((...args: any[]) => any) | undefined;
146
- "onUpload:start"?: ((...args: any[]) => any) | undefined;
147
- "onUpload:success"?: ((...args: any[]) => any) | undefined;
148
- "onUpload:fail"?: ((...args: any[]) => any) | undefined;
149
- "onUpload:end"?: ((...args: any[]) => any) | undefined;
146
+ onSuccess?: ((...args: any[]) => any) | undefined;
147
+ onEnd?: ((...args: any[]) => any) | undefined;
148
+ onStart?: ((...args: any[]) => any) | undefined;
149
+ onFail?: ((...args: any[]) => any) | undefined;
150
150
  }, {
151
151
  draggable: boolean;
152
152
  name: string;
@@ -3,7 +3,7 @@ export declare const useSystemAlertStore: import("pinia").StoreDefinition<"syste
3
3
  count: number;
4
4
  systemAlerts: SystemAlert[];
5
5
  }, {}, {
6
- push(systemAlert: SystemAlertOptions): void;
7
- remove(systemAlert: SystemAlert): void;
6
+ push(systemAlert: SystemAlertOptions): string | number;
7
+ remove(alertId: number | string): void;
8
8
  clear(): void;
9
9
  }>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sprintify-ui",
3
- "version": "0.2.29",
3
+ "version": "0.4.0",
4
4
  "scripts": {
5
5
  "build": "rimraf dist && vue-tsc && vite build",
6
6
  "build-fast": "rimraf dist && vite build",
@@ -13,7 +13,8 @@
13
13
  "prepack": "npm run build",
14
14
  "prepare": "husky install && relative-deps",
15
15
  "release": "standard-version",
16
- "commit": "cz"
16
+ "commit": "cz",
17
+ "vue-tsc": "vue-tsc"
17
18
  },
18
19
  "peerDependencies": {
19
20
  "@tailwindcss/aspect-ratio": "^0.4.2",
@@ -1,6 +1,7 @@
1
1
  import BaseFileUploader from '@/components/BaseFileUploader.vue';
2
2
  import BaseLoadingCover from '@/components/BaseLoadingCover.vue';
3
3
  import BaseAppNotifications from '@/components/BaseAppNotifications.vue';
4
+ import ShowValue from '../../.storybook/components/ShowValue.vue';
4
5
  import { Icon as BaseIcon } from '@iconify/vue';
5
6
  import { t } from '@/i18n';
6
7
 
@@ -21,12 +22,20 @@ const Template = (args) => ({
21
22
  BaseIcon,
22
23
  BaseLoadingCover,
23
24
  BaseAppNotifications,
25
+ ShowValue,
24
26
  },
25
27
  setup() {
26
- return { args, t };
28
+
29
+ const files = ref([]);
30
+
31
+ function onSuccess(payload) {
32
+ files.value = payload;
33
+ }
34
+
35
+ return { args, t, files, onSuccess };
27
36
  },
28
37
  template: `
29
- <BaseFileUploader v-bind="args">
38
+ <BaseFileUploader v-bind="args" @success="onSuccess">
30
39
  <template #default="{ dragging, disabled, uploading, selecting }">
31
40
  <div
32
41
  class="flex w-full items-center space-x-4 rounded-lg border-2 border-dashed border-slate-200 p-5 duration-100"
@@ -59,6 +68,9 @@ const Template = (args) => ({
59
68
  />
60
69
  </template>
61
70
  </BaseFileUploader>
71
+
72
+ <ShowValue :value="files" />
73
+
62
74
  <BaseAppNotifications></BaseAppNotifications>
63
75
  `,
64
76
  });
@@ -54,7 +54,7 @@ const props = withDefaults(
54
54
  accept?: string;
55
55
  acceptedExtensions?: string[];
56
56
  cropper?: BaseCropperConfig | Record<string, any> | boolean | null;
57
- multiple: boolean;
57
+ multiple?: boolean;
58
58
  }>(),
59
59
  {
60
60
  component: 'BaseFilePicker',
@@ -74,10 +74,10 @@ const props = withDefaults(
74
74
  );
75
75
 
76
76
  const emit = defineEmits([
77
- 'upload:start',
78
- 'upload:success',
79
- 'upload:fail',
80
- 'upload:end',
77
+ 'start',
78
+ 'success',
79
+ 'fail',
80
+ 'end',
81
81
  ]);
82
82
 
83
83
  const componentInternal = computed(() => {
@@ -118,57 +118,65 @@ async function onFileSelect(files: File | File[]) {
118
118
  return;
119
119
  }
120
120
 
121
+ emit('start');
122
+
123
+ let payloads = [] as UploadedFile[];
124
+
121
125
  try {
122
126
  const formData = new FormData();
123
127
 
124
128
  if (Array.isArray(files)) {
125
- await Promise.all(files.map(f => processFileUpload(formData, f)))
129
+ payloads = await Promise.all(files.map(f => processFileUpload(formData, f)));
126
130
  } else {
127
- await processFileUpload(formData, files)
131
+ const payload = await processFileUpload(formData, files);
132
+ payloads.push(payload);
128
133
  }
134
+
135
+ emit('success', payloads)
136
+
129
137
  } catch (e: unknown) {
130
138
  console.error(e);
131
- emit('upload:fail');
139
+ emit('fail');
132
140
  notifications.push({
133
141
  color: 'danger',
134
142
  title: t('sui.error'),
135
143
  text: t('sui.upload_failed'),
136
144
  });
137
145
  } finally {
138
- emit('upload:end');
146
+ emit('end');
139
147
  uploading.value = false;
140
148
  }
141
149
  }
142
150
 
143
- async function processFileUpload(formData: FormData, file: File) {
151
+ async function processFileUpload(formData: FormData, file: File): Promise<UploadedFile> {
144
152
  formData.append('file', file);
145
153
 
146
- emit('upload:start');
147
-
148
154
  const response = await http.post(props.url ?? config.upload_url, formData);
149
155
 
150
156
  const payload = response.data as UploadedFile;
151
157
  payload.original_file = file;
152
158
 
153
- const reader = new FileReader();
159
+ // Read file if image, add add data_url to payload
154
160
 
155
- reader.onload = (e: any) => {
156
- payload.data_url = e.target.result;
157
- onSuccess(payload);
158
- };
161
+ return new Promise(resolve => {
159
162
 
160
- reader.onerror = (e: any) => {
161
- onSuccess(payload);
162
- };
163
+ const reader = new FileReader();
163
164
 
164
- if (payload.mime_type.includes('image')) {
165
- reader.readAsDataURL(file);
166
- } else {
167
- onSuccess(payload);
168
- }
169
- }
165
+ reader.onload = (e: any) => {
166
+ payload.data_url = e.target.result;
167
+ resolve(payload);
168
+ };
169
+
170
+ reader.onerror = () => {
171
+ resolve(payload);
172
+ };
170
173
 
171
- function onSuccess(payload: any) {
172
- emit('upload:success', payload);
174
+ if (payload.mime_type.includes('image')) {
175
+ reader.readAsDataURL(file);
176
+ } else {
177
+ resolve(payload);
178
+ }
179
+ });
173
180
  }
181
+
174
182
  </script>
@@ -18,7 +18,7 @@
18
18
  leave-from="opacity-100"
19
19
  leave-to="opacity-0"
20
20
  >
21
- <div class="fixed inset-0 bg-slate-600 bg-opacity-75" />
21
+ <div class="fixed inset-0 bg-opacity-75 bg-slate-600" />
22
22
  </TransitionChild>
23
23
 
24
24
  <div class="fixed inset-0 z-40 flex">
@@ -32,7 +32,7 @@
32
32
  leave-to="-translate-x-full"
33
33
  >
34
34
  <DialogPanel
35
- class="relative flex w-full max-w-xs flex-1 flex-col pt-5 pb-4"
35
+ class="relative flex flex-col flex-1 w-full max-w-xs pt-5 pb-4"
36
36
  :class="[dark ? 'bg-slate-800' : 'bg-white']"
37
37
  >
38
38
  <TransitionChild
@@ -44,31 +44,31 @@
44
44
  leave-from="opacity-100"
45
45
  leave-to="opacity-0"
46
46
  >
47
- <div class="absolute top-0 right-0 -mr-12 pt-2">
47
+ <div class="absolute top-0 right-0 pt-2 -mr-12">
48
48
  <button
49
49
  type="button"
50
- class="ml-1 flex h-10 w-10 items-center justify-center rounded-full"
50
+ class="flex items-center justify-center w-10 h-10 ml-1 rounded-full"
51
51
  @click="showMobileMenu = false"
52
52
  >
53
53
  <span class="sr-only">Close sidebar</span>
54
54
  <BaseIcon
55
55
  icon="heroicons:x-mark"
56
- class="h-6 w-6 text-white"
56
+ class="w-6 h-6 text-white"
57
57
  aria-hidden="true"
58
58
  />
59
59
  </button>
60
60
  </div>
61
61
  </TransitionChild>
62
- <div class="flex flex-shrink-0 items-center px-4">
62
+ <div class="flex items-center flex-shrink-0 px-4">
63
63
  <img
64
- class="block h-8 w-auto"
64
+ class="block w-auto h-8"
65
65
  :src="logoUrl"
66
66
  :alt="appName"
67
67
  >
68
68
  </div>
69
69
  <div
70
70
  data-scroll-lock-scrollable
71
- class="mt-5 h-0 flex-1 overflow-y-auto"
71
+ class="flex-1 h-0 mt-5 overflow-y-auto"
72
72
  >
73
73
  <nav>
74
74
  <slot name="menu" />
@@ -77,7 +77,7 @@
77
77
  </DialogPanel>
78
78
  </TransitionChild>
79
79
  <div
80
- class="w-14 flex-shrink-0"
80
+ class="flex-shrink-0 w-14"
81
81
  aria-hidden="true"
82
82
  >
83
83
  <!-- Dummy element to force sidebar to shrink to fit close icon -->
@@ -86,8 +86,8 @@
86
86
  </Dialog>
87
87
  </TransitionRoot>
88
88
 
89
- <div class="flex min-h-full flex-col xl:pl-64">
90
- <div class="sticky top-0 left-0 z-10 shrink-0 shadow">
89
+ <div class="flex flex-col min-h-full xl:pl-64">
90
+ <div class="sticky top-0 left-0 z-10 shadow shrink-0">
91
91
  <BaseSystemAlert
92
92
  v-for="systemAlert in systemAlerts"
93
93
  :key="systemAlert.id"
@@ -95,7 +95,7 @@
95
95
  :to="systemAlert.to"
96
96
  :action="systemAlert.action"
97
97
  :closable="systemAlert.closable"
98
- @close="systemAlertStore.remove(systemAlert)"
98
+ @close="systemAlertStore.remove(systemAlert.id)"
99
99
  >
100
100
  {{ systemAlert.message }}
101
101
  </BaseSystemAlert>
@@ -106,13 +106,13 @@
106
106
  >
107
107
  <button
108
108
  type="button"
109
- class="border-r border-slate-200 px-4 text-slate-500 xl:hidden"
109
+ class="px-4 border-r border-slate-200 text-slate-500 xl:hidden"
110
110
  @click="showMobileMenu = true"
111
111
  >
112
112
  <span class="sr-only">Open sidebar</span>
113
113
  <BaseIcon
114
114
  icon="heroicons:bars-3-bottom-left"
115
- class="h-6 w-6"
115
+ class="w-6 h-6"
116
116
  aria-hidden="true"
117
117
  />
118
118
  </button>
@@ -125,7 +125,7 @@
125
125
  </div>
126
126
 
127
127
  <!-- Position: relative to contain Loading Covers -->
128
- <main class="relative min-h-full flex-1">
128
+ <main class="relative flex-1 min-h-full">
129
129
  <slot />
130
130
  </main>
131
131
  </div>
@@ -135,16 +135,16 @@
135
135
  <!-- Sidebar component, swap this element with another sidebar if you like -->
136
136
  <div
137
137
  data-scroll-lock-scrollable
138
- class="flex min-h-0 flex-1 flex-col overflow-y-auto"
138
+ class="flex flex-col flex-1 min-h-0 overflow-y-auto"
139
139
  :class="[dark ? 'bg-slate-800' : 'bg-white shadow']"
140
140
  >
141
141
  <div
142
- class="flex flex-shrink-0 items-center px-4"
142
+ class="flex items-center flex-shrink-0 px-4"
143
143
  :style="{ height: navbarHeight + 'px' }"
144
144
  :class="[dark ? 'bg-slate-900' : 'bg-white']"
145
145
  >
146
146
  <img
147
- class="block h-8 w-auto"
147
+ class="block w-auto h-8"
148
148
  :src="logoUrl"
149
149
  :alt="appName"
150
150
  >
@@ -7,7 +7,7 @@
7
7
  :to="systemAlert.to"
8
8
  :action="systemAlert.action"
9
9
  :closable="systemAlert.closable"
10
- @close="systemAlertStore.remove(systemAlert)"
10
+ @close="systemAlertStore.remove(systemAlert.id)"
11
11
  >
12
12
  {{ systemAlert.message }}
13
13
  </BaseSystemAlert>
@@ -3,7 +3,7 @@
3
3
  <BaseFileUploader
4
4
  :component="pickerComponent"
5
5
  :max-size="maxSize"
6
- :disabled="disabled"
6
+ :disabled="disabledInternal"
7
7
  class="w-full"
8
8
  tw-button="w-full"
9
9
  :accept="accept"
@@ -11,10 +11,10 @@
11
11
  :url="uploadUrl"
12
12
  :multiple="multiple"
13
13
  :cropper="pickerComponent == 'BaseFilePickerCrop' ? cropper : undefined"
14
- @upload:start="onUploadStart"
15
- @upload:end="onUploadEnd"
16
- @upload:fail="$emit('upload:fail', $event)"
17
- @upload:success="onUploadSuccess"
14
+ @start="onStart"
15
+ @success="onSuccess"
16
+ @fail="onFail"
17
+ @end="onEnd"
18
18
  >
19
19
  <template #default="baseFileUploaderProps">
20
20
  <slot
@@ -62,7 +62,7 @@
62
62
  <slot
63
63
  :model-value="normalizedModelValue"
64
64
  name="list"
65
- :disabled="disabled"
65
+ :disabled="disabledInternal"
66
66
  :draggable="draggable"
67
67
  :remove="promptRemove"
68
68
  @update:model-value="sync"
@@ -71,7 +71,7 @@
71
71
  v-if="layout == 'images'"
72
72
  v-bind="listProps"
73
73
  :model-value="normalizedModelValue"
74
- :disabled="disabled"
74
+ :disabled="disabledInternal"
75
75
  :draggable="draggable"
76
76
  @update:model-value="sync"
77
77
  @remove="promptRemove($event)"
@@ -81,7 +81,7 @@
81
81
  v-else-if="layout == 'list'"
82
82
  v-bind="listProps"
83
83
  :model-value="normalizedModelValue"
84
- :disabled="disabled"
84
+ :disabled="disabledInternal"
85
85
  :draggable="draggable"
86
86
  @update:model-value="sync"
87
87
  @remove="promptRemove($event)"
@@ -91,7 +91,7 @@
91
91
  v-else-if="layout == 'gallery'"
92
92
  v-bind="listProps"
93
93
  :model-value="normalizedModelValue"
94
- :disabled="disabled"
94
+ :disabled="disabledInternal"
95
95
  :draggable="draggable"
96
96
  @update:model-value="sync"
97
97
  @remove="promptRemove($event)"
@@ -196,10 +196,10 @@ const props = defineProps({
196
196
 
197
197
  const emit = defineEmits([
198
198
  'update:modelValue',
199
- 'upload:start',
200
- 'upload:success',
201
- 'upload:fail',
202
- 'upload:end',
199
+ 'start',
200
+ 'success',
201
+ 'fail',
202
+ 'end',
203
203
  ]);
204
204
 
205
205
  const { emitUpdate, enableForm, disableForm } = useField({
@@ -222,10 +222,6 @@ const normalizedModelValue = computed(() => {
222
222
 
223
223
  sync(normalizedModelValue.value);
224
224
 
225
- const numberOfFiles = computed((): number => {
226
- return normalizedModelValue.value.length;
227
- });
228
-
229
225
  // Validations
230
226
 
231
227
  const normalizedMax = computed(() => {
@@ -248,40 +244,59 @@ const maxFileSize = computed(() => {
248
244
 
249
245
  // Upload
250
246
 
251
- function onUploadSuccess(file: UploadedFile) {
252
- if (file == null) {
253
- return;
254
- }
247
+ const syncingFiles = ref(false);
248
+
249
+ function onSuccess(files: UploadedFile[]) {
255
250
 
256
- if (
257
- normalizedMax.value &&
258
- numberOfFiles.value >= normalizedMax.value &&
259
- normalizedMax.value > 1
260
- ) {
261
- notifications.push({
262
- title: t('sui.whoops'),
263
- text: t('sui.you_can_upload_up_to_n_files', {
264
- count: normalizedMax.value,
265
- }),
266
- color: 'danger',
267
- });
251
+ if (files.length == 0) {
268
252
  return;
269
253
  }
270
254
 
255
+ syncingFiles.value = true;
256
+
271
257
  let modelValue = cloneDeep(normalizedModelValue.value);
272
258
 
273
- if (normalizedMax.value == 1) {
274
- // Remove everything...
275
- modelValue = [];
276
- }
259
+ for (const file of files) {
260
+
261
+
262
+ if (
263
+ normalizedMax.value &&
264
+ modelValue.length >= normalizedMax.value &&
265
+ normalizedMax.value > 1
266
+ ) {
267
+ notifications.push({
268
+ title: t('sui.whoops'),
269
+ text: t('sui.you_can_upload_up_to_n_files', {
270
+ count: normalizedMax.value,
271
+ }),
272
+ color: 'danger',
273
+ });
274
+
275
+ break;
276
+ }
277
277
 
278
- modelValue.push(file);
278
+ if (normalizedMax.value == 1) {
279
+ // Remove everything...
280
+ modelValue = [];
281
+ }
282
+
283
+ modelValue.push(file);
284
+ }
279
285
 
280
286
  sync(modelValue);
281
287
 
282
- emit('upload:success', file);
288
+ syncingFiles.value = false;
289
+
290
+ emit('success', files);
283
291
  }
284
292
 
293
+ // Disabled
294
+
295
+ const disabledInternal = computed(() => {
296
+ return props.disabled || syncingFiles.value;
297
+ });
298
+
299
+
285
300
  // Remove
286
301
 
287
302
  function promptRemove(index: number, length = 1) {
@@ -311,13 +326,17 @@ function sync(modelValue: MediaLibraryPayload) {
311
326
 
312
327
  // Events
313
328
 
314
- function onUploadStart(event: any) {
315
- emit('upload:start', event);
329
+ function onStart(event: unknown) {
330
+ emit('start', event);
316
331
  disableForm();
317
332
  }
318
333
 
319
- function onUploadEnd(event: any) {
320
- emit('upload:end', event);
334
+ function onFail(event: unknown) {
335
+ emit('fail', event);
336
+ }
337
+
338
+ function onEnd(event: unknown) {
339
+ emit('end', event);
321
340
  enableForm();
322
341
  }
323
342
  </script>