sprintify-ui 0.0.10 → 0.0.12

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 (103) hide show
  1. package/dist/sprintify-ui.es.js +5174 -6624
  2. package/dist/style.css +1 -1
  3. package/dist/tailwindcss/index.js +23 -0
  4. package/dist/types/src/components/BaseAutocomplete.vue.d.ts +0 -1
  5. package/dist/types/src/components/BaseAutocompleteFetch.vue.d.ts +0 -1
  6. package/dist/types/src/components/BaseBelongsTo.vue.d.ts +13 -3
  7. package/dist/types/src/components/BaseCharacterCounter.vue.d.ts +143 -0
  8. package/dist/types/src/components/BaseDataTable.vue.d.ts +95 -64
  9. package/dist/types/src/components/BaseDialog.vue.d.ts +8 -8
  10. package/dist/types/src/components/BaseFilePicker.vue.d.ts +3 -3
  11. package/dist/types/src/components/BaseInput.vue.d.ts +39 -5
  12. package/dist/types/src/components/BaseLoadingCover.vue.d.ts +84 -12
  13. package/dist/types/src/components/BaseMenuItem.vue.d.ts +4 -4
  14. package/dist/types/src/components/BaseModalCenter.vue.d.ts +8 -8
  15. package/dist/types/src/components/BaseModalSide.vue.d.ts +8 -8
  16. package/dist/types/src/components/BaseNavbarItemContent.vue.d.ts +4 -4
  17. package/dist/types/src/components/BasePagination.vue.d.ts +105 -13
  18. package/dist/types/src/components/BasePaginationSimple.vue.d.ts +2 -2
  19. package/dist/types/src/components/BaseSelect.vue.d.ts +130 -26
  20. package/dist/types/src/components/BaseSwitch.vue.d.ts +15 -8
  21. package/dist/types/src/components/BaseTabItem.vue.d.ts +26 -4
  22. package/dist/types/src/components/BaseTableColumn.vue.d.ts +4 -4
  23. package/dist/types/src/components/BaseTextareaAutoresize.vue.d.ts +175 -21
  24. package/dist/types/src/components/index.d.ts +24 -1
  25. package/dist/types/src/index.d.ts +4 -0
  26. package/dist/types/src/svg/BaseEmptyState.vue.d.ts +2 -0
  27. package/dist/types/src/{components/BaseSpinner.vue.d.ts → svg/BaseSpinnerLarge.vue.d.ts} +0 -0
  28. package/dist/types/src/svg/BaseSpinnerSmall.vue.d.ts +44 -0
  29. package/dist/types/src/types/types.d.ts +1 -1
  30. package/package.json +4 -2
  31. package/src/components/BaseAutocomplete.stories.js +7 -4
  32. package/src/components/BaseAutocomplete.vue +44 -15
  33. package/src/components/BaseAutocompleteFetch.stories.js +6 -3
  34. package/src/components/BaseAutocompleteFetch.vue +8 -3
  35. package/src/components/BaseBelongsTo.stories.js +9 -4
  36. package/src/components/BaseBelongsTo.vue +1 -0
  37. package/src/components/BaseCard.vue +1 -1
  38. package/src/components/BaseCharacterCounter.stories.js +30 -0
  39. package/src/components/BaseCharacterCounter.vue +60 -0
  40. package/src/components/BaseDataIterator.stories.js +102 -3
  41. package/src/components/BaseDataIterator.vue +75 -49
  42. package/src/components/BaseDataTable.stories.js +149 -2
  43. package/src/components/BaseDataTable.vue +34 -28
  44. package/src/components/BaseDataTableToggleColumns.vue +1 -1
  45. package/src/components/BaseDateSelect.vue +6 -2
  46. package/src/components/BaseDescriptionListItem.vue +40 -4
  47. package/src/components/BaseDialog.stories.js +51 -0
  48. package/src/components/BaseDialog.vue +13 -7
  49. package/src/components/BaseFilePicker.stories.js +51 -0
  50. package/src/components/BaseFilePicker.vue +6 -6
  51. package/src/components/BaseFileUploader.stories.js +80 -0
  52. package/src/components/BaseFileUploader.vue +16 -3
  53. package/src/components/BaseInput.stories.js +46 -0
  54. package/src/components/BaseInput.vue +10 -2
  55. package/src/components/BaseInputLabel.stories.js +31 -0
  56. package/src/components/BaseInputLabel.vue +1 -1
  57. package/src/components/BaseLoadingCover.stories.js +55 -0
  58. package/src/components/BaseLoadingCover.vue +27 -17
  59. package/src/components/BaseMenu.stories.js +125 -0
  60. package/src/components/BaseModalCenter.stories.js +61 -0
  61. package/src/components/BaseModalCenter.vue +2 -2
  62. package/src/components/BaseModalSide.stories.js +55 -0
  63. package/src/components/BaseModalSide.vue +2 -2
  64. package/src/components/BaseNavbar.stories.js +150 -0
  65. package/src/components/BaseNavbar.vue +3 -0
  66. package/src/components/BaseNavbarItem.vue +1 -0
  67. package/src/components/BaseNavbarItemContent.vue +3 -0
  68. package/src/components/BasePagination.stories.js +32 -0
  69. package/src/components/BasePagination.vue +126 -40
  70. package/src/components/BasePaginationSimple.vue +3 -3
  71. package/src/components/BasePanel.stories.js +56 -0
  72. package/src/components/BasePassword.stories.js +36 -0
  73. package/src/components/BasePassword.vue +11 -5
  74. package/src/components/BaseProcessRing.stories.js +27 -0
  75. package/src/components/BaseReadMore.stories.js +30 -0
  76. package/src/components/BaseReadMore.vue +1 -1
  77. package/src/components/BaseSelect.stories.js +67 -0
  78. package/src/components/BaseSelect.vue +144 -44
  79. package/src/components/BaseSideNavigation.stories.js +55 -0
  80. package/src/components/BaseSideNavigation.vue +7 -2
  81. package/src/components/BaseSideNavigationItem.vue +11 -3
  82. package/src/components/BaseSkeleton.stories.js +36 -0
  83. package/src/components/BaseSwitch.stories.js +101 -0
  84. package/src/components/BaseSwitch.vue +90 -12
  85. package/src/components/BaseSystemAlert.stories.js +63 -0
  86. package/src/components/BaseTabItem.vue +19 -6
  87. package/src/components/BaseTable.vue +42 -29
  88. package/src/components/BaseTableColumn.vue +2 -2
  89. package/src/components/BaseTabs.stories.js +54 -0
  90. package/src/components/BaseTabs.vue +3 -3
  91. package/src/components/BaseTextarea.stories.js +35 -0
  92. package/src/components/BaseTextarea.vue +1 -1
  93. package/src/components/BaseTextareaAutoresize.stories.js +49 -0
  94. package/src/components/BaseTextareaAutoresize.vue +83 -87
  95. package/src/components/index.ts +46 -0
  96. package/src/lang/en.json +1 -0
  97. package/src/lang/fr.json +1 -0
  98. package/src/svg/BaseEmptyState.vue +34 -0
  99. package/src/{components/BaseSpinner.vue → svg/BaseSpinnerLarge.vue} +0 -0
  100. package/src/svg/BaseSpinnerSmall.vue +9 -0
  101. package/src/types/types.ts +1 -1
  102. package/dist/types/src/components/BaseWordCount.vue.d.ts +0 -31
  103. package/src/components/BaseWordCount.vue +0 -36
@@ -20,7 +20,7 @@
20
20
  firstLoad,
21
21
  }"
22
22
  >
23
- <BaseCard clipped class="isolate w-full overflow-hidden">
23
+ <BaseCard clipped class="w-full overflow-hidden">
24
24
  <BaseTable
25
25
  ref="table"
26
26
  :data="items"
@@ -48,7 +48,7 @@
48
48
  <BaseTableColumn
49
49
  v-slot="{ row }"
50
50
  :visible="editButton || deleteButton || $slots.rowActions != null"
51
- :always-visible="true"
51
+ :toggle="false"
52
52
  >
53
53
  <div class="flex justify-end text-right">
54
54
  <slot name="rowActions" :row="row" />
@@ -58,7 +58,7 @@
58
58
  :to="editUrl(row)"
59
59
  :disabled="!canUpdate(row)"
60
60
  >
61
- <button class="btn btn-white p-2">
61
+ <button class="btn btn-white bg-transparent p-2">
62
62
  <BaseIcon
63
63
  icon="heroicons:cog-6-tooth-solid"
64
64
  class="text-slate-500"
@@ -69,7 +69,7 @@
69
69
  <button
70
70
  v-if="deleteButton && deleteUrl"
71
71
  type="button"
72
- class="btn btn-white p-2"
72
+ class="btn btn-white bg-transparent p-2"
73
73
  :disabled="!canDelete(row)"
74
74
  @click="onDeleteClick(row)"
75
75
  >
@@ -107,11 +107,8 @@
107
107
  class="flex items-center justify-center py-16"
108
108
  >
109
109
  <div class="flex flex-col items-center">
110
- <img
111
- :src="'/img/empty-states/data.svg'"
112
- alt="No data"
113
- width="100"
114
- />
110
+ <BaseEmptyState class="w-32"></BaseEmptyState>
111
+
115
112
  <p class="mt-3 text-center text-sm text-slate-600">
116
113
  {{ $t('sui.nothing_found') }}
117
114
  </p>
@@ -138,7 +135,7 @@
138
135
  <slot name="sidebarTop" v-bind="sidebarProps"></slot>
139
136
  </template>
140
137
 
141
- <template #sidebarBottom>
138
+ <template v-if="toggleable" #sidebarBottom>
142
139
  <div class="mb-3">
143
140
  <h3
144
141
  class="mb-1 text-xs font-semibold uppercase tracking-wider text-slate-500"
@@ -177,6 +174,7 @@ import BaseTable from './BaseTable.vue';
177
174
  import BaseTableColumn from './BaseTableColumn.vue';
178
175
  import BaseDataTableToggleColumns from './BaseDataTableToggleColumns.vue';
179
176
  import { config } from '@/index';
177
+ import BaseEmptyState from '../svg/BaseEmptyState.vue';
180
178
 
181
179
  const i18n = useI18n();
182
180
 
@@ -196,22 +194,6 @@ const props = defineProps({
196
194
  type: String,
197
195
  },
198
196
 
199
- /**
200
- * Show/Hide edit button
201
- */
202
- editButton: {
203
- default: true,
204
- type: Boolean,
205
- },
206
-
207
- /**
208
- * Show/Hide delete button
209
- */
210
- deleteButton: {
211
- default: true,
212
- type: Boolean,
213
- },
214
-
215
197
  /**
216
198
  * Route key name for Laravel route model binding
217
199
  */
@@ -237,6 +219,14 @@ const props = defineProps({
237
219
  type: Object as PropType<DataTableQuery>,
238
220
  },
239
221
 
222
+ /**
223
+ * Show/Hide edit button
224
+ */
225
+ editButton: {
226
+ default: true,
227
+ type: Boolean,
228
+ },
229
+
240
230
  /**
241
231
  * Edit url for router link
242
232
  */
@@ -245,6 +235,14 @@ const props = defineProps({
245
235
  type: Function as PropType<(row: CollectionItem) => string>,
246
236
  },
247
237
 
238
+ /**
239
+ * Show/Hide delete button
240
+ */
241
+ deleteButton: {
242
+ default: true,
243
+ type: Boolean,
244
+ },
245
+
248
246
  /**
249
247
  * Delete endpoint to delete an item
250
248
  */
@@ -262,7 +260,7 @@ const props = defineProps({
262
260
  },
263
261
 
264
262
  /**
265
- * Has detailed visible
263
+ * Check is a given row has details
266
264
  */
267
265
  hasDetailedVisible: {
268
266
  default() {
@@ -315,6 +313,14 @@ const props = defineProps({
315
313
  type: Boolean,
316
314
  },
317
315
 
316
+ /**
317
+ * Shows the column toggle utility
318
+ */
319
+ toggleable: {
320
+ default: true,
321
+ type: Boolean,
322
+ },
323
+
318
324
  /**
319
325
  * Actions
320
326
  */
@@ -388,7 +394,7 @@ function onDeleteClick(row: CollectionItem) {
388
394
  title: i18n.t('sui.delete_record') + '?',
389
395
  message: i18n.t('sui.delete_record_description'),
390
396
  color: 'danger',
391
- closeOnOutsideClick: false,
397
+ closeOnOutsideClick: true,
392
398
  confirmText: i18n.t('sui.yes_delete'),
393
399
  onConfirm: () => onDelete(row),
394
400
  });
@@ -46,7 +46,7 @@ const toggleableColumns = computed(() => {
46
46
  return [];
47
47
  }
48
48
 
49
- return tableVue.newColumns.filter((c) => !c.alwaysVisible);
49
+ return tableVue.newColumns.filter((c) => c.toggle);
50
50
  });
51
51
 
52
52
  function onVisibleColumnChange(event: any, newKey: number) {
@@ -54,13 +54,13 @@
54
54
  <div class="w-auto p-0.5">
55
55
  <select
56
56
  v-model="date.day"
57
- :disabled="disabled"
57
+ :disabled="dayDisabled"
58
58
  :required="required"
59
59
  data-cy="day"
60
60
  class="w-full rounded capitalize"
61
61
  :class="[
62
62
  {
63
- 'cursor-not-allowed bg-slate-100 text-slate-500': disabled,
63
+ 'cursor-not-allowed bg-slate-100 text-slate-500': dayDisabled,
64
64
  },
65
65
  inputClass,
66
66
  ]"
@@ -189,4 +189,8 @@ function getDateTime(): DateTime | null {
189
189
  padStart(date.value.day + '', 2, '0')
190
190
  );
191
191
  }
192
+
193
+ const dayDisabled = computed(() => {
194
+ return days.value.length == 0 || props.disabled;
195
+ });
192
196
  </script>
@@ -1,12 +1,48 @@
1
1
  <template>
2
- <div class="px-4 py-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:py-5 sm:px-6">
3
- <dt class="text-sm font-medium text-slate-500">
2
+ <div
3
+ ref="item"
4
+ :class="{
5
+ 'px-4 py-4': mobile,
6
+ 'grid grid-cols-3 gap-4 py-5 px-6': !mobile,
7
+ }"
8
+ >
9
+ <dt class="text-sm font-medium leading-tight text-slate-500">
4
10
  <slot name="left" />
5
11
  </dt>
6
- <dd class="mt-1 text-sm text-slate-900 sm:col-span-2 sm:mt-0">
12
+ <dd
13
+ class="text-sm leading-tight text-slate-900"
14
+ :class="{
15
+ 'mt-1.5': mobile,
16
+ 'col-span-2': !mobile,
17
+ }"
18
+ >
7
19
  <slot name="right" />
8
20
  </dd>
9
21
  </div>
10
22
  </template>
11
23
 
12
- <script lang="ts" setup></script>
24
+ <script lang="ts" setup>
25
+ import { useResizeObserver } from '@vueuse/core';
26
+ import { debounce } from 'lodash';
27
+ import { Ref } from 'vue';
28
+ import breakpoints from '../../config/breakpoints.json';
29
+
30
+ const DEFAULT_WIDTH = 800;
31
+
32
+ const item = ref(null) as Ref<HTMLElement | null>;
33
+ const width = ref(DEFAULT_WIDTH);
34
+
35
+ const mobile = computed(() => {
36
+ return width.value < breakpoints.sm;
37
+ });
38
+
39
+ function setWidth() {
40
+ width.value = item.value?.clientWidth ?? DEFAULT_WIDTH;
41
+ }
42
+
43
+ onMounted(() => {
44
+ setWidth();
45
+ });
46
+
47
+ useResizeObserver(item, debounce(setWidth, 50));
48
+ </script>
@@ -0,0 +1,51 @@
1
+ import BaseDialog from './BaseDialog.vue';
2
+
3
+ export default {
4
+ title: 'Components/BaseDialog',
5
+ component: BaseDialog,
6
+ argTypes: {
7
+ color: {
8
+ control: { type: 'select' },
9
+ options: ['success', 'info', 'warning', 'danger'],
10
+ },
11
+ },
12
+ args: {
13
+ message:
14
+ 'Nisi Lorem sunt amet aliqua dolor ullamco deserunt enim irure non ad. Excepteur culpa consectetur dolore culpa sunt aliquip proident quis.',
15
+ },
16
+ };
17
+
18
+ const Template = (args) => ({
19
+ components: { BaseDialog },
20
+ setup() {
21
+ return { args };
22
+ },
23
+ template: `
24
+ <BaseDialog v-bind="args">
25
+ </BaseDialog>
26
+ `,
27
+ });
28
+
29
+ export const Demo = Template.bind({});
30
+ Demo.args = {
31
+ title: 'Be careful',
32
+ color: 'warning',
33
+ };
34
+
35
+ export const Success = Template.bind({});
36
+ Success.args = {
37
+ title: 'Success',
38
+ color: 'success',
39
+ };
40
+
41
+ export const Danger = Template.bind({});
42
+ Danger.args = {
43
+ title: 'Error',
44
+ color: 'danger',
45
+ };
46
+
47
+ export const Info = Template.bind({});
48
+ Info.args = {
49
+ title: 'Information',
50
+ color: 'info',
51
+ };
@@ -15,22 +15,22 @@
15
15
  <BaseIcon
16
16
  v-if="color == 'danger'"
17
17
  class="h-6 w-6 text-red-600"
18
- icon="heroicons:exclaimation-triangle"
18
+ icon="heroicons:exclamation-triangle-20-solid"
19
19
  />
20
20
  <BaseIcon
21
21
  v-else-if="color == 'warning'"
22
22
  class="h-6 w-6 text-yellow-600"
23
- icon="heroicons:exclaimation-circle"
23
+ icon="heroicons:exclamation-triangle-20-solid"
24
24
  />
25
25
  <BaseIcon
26
26
  v-else-if="color == 'success'"
27
27
  class="h-6 w-6 text-green-600"
28
- icon="heroicons:check-circle"
28
+ icon="heroicons:check-circle-20-solid"
29
29
  />
30
30
  <BaseIcon
31
31
  v-else
32
32
  class="h-6 w-6 text-primary-600"
33
- icon="heroicons:information-circle"
33
+ icon="heroicons:information-circle-20-solid"
34
34
  />
35
35
  </div>
36
36
  <div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
@@ -42,7 +42,7 @@
42
42
  {{ title }}
43
43
  </h3>
44
44
  <div class="mt-2">
45
- <p class="text-sm text-slate-500">
45
+ <p class="text-base font-light text-slate-600">
46
46
  {{ message }}
47
47
  </p>
48
48
  </div>
@@ -92,11 +92,17 @@ defineProps({
92
92
  type: String,
93
93
  },
94
94
  confirmText: {
95
- default: '',
95
+ default() {
96
+ const i18n = useI18n();
97
+ return i18n.t('sui.confirm');
98
+ },
96
99
  type: String,
97
100
  },
98
101
  cancelText: {
99
- default: '',
102
+ default() {
103
+ const i18n = useI18n();
104
+ return i18n.t('sui.cancel');
105
+ },
100
106
  type: String,
101
107
  },
102
108
  });
@@ -0,0 +1,51 @@
1
+ import BaseFilePicker from './BaseFilePicker.vue';
2
+ import { Icon as BaseIcon } from '@iconify/vue';
3
+
4
+ export default {
5
+ title: 'Form/BaseFilePicker',
6
+ component: BaseFilePicker,
7
+ };
8
+
9
+ const Template = (args) => ({
10
+ components: { BaseFilePicker, BaseIcon },
11
+ setup() {
12
+ return { args };
13
+ },
14
+ template: `
15
+ <BaseFilePicker v-bind="args">
16
+ <template #default="{ dragging, disabled }">
17
+ <div
18
+ class="flex w-full items-center space-x-4 rounded-lg border-2 border-dashed border-slate-200 p-5 duration-100"
19
+ :class="[
20
+ dragging ? 'bg-slate-100' : 'bg-white',
21
+ disabled ? 'bg-slate-100 cursor-not-allowed' : 'hover:bg-slate-50',
22
+ ]"
23
+ >
24
+ <div class="rounded-full bg-slate-200 p-2">
25
+ <BaseIcon
26
+ icon="heroicons:arrow-up-on-square"
27
+ class="h-6 w-6"
28
+ :class="[disabled ? 'text-slate-400' : 'text-slate-500']"
29
+ />
30
+ </div>
31
+ <div class="text-left">
32
+ <p
33
+ class="mb-0 text-sm font-medium leading-tight"
34
+ :class="[disabled ? 'text-slate-400' : 'text-slate-900']"
35
+ >
36
+ {{ $t("sui.drop_or_click_to_upload") }}
37
+ </p>
38
+ </div>
39
+ </div>
40
+ </template>
41
+ </BaseFilePicker>
42
+ `,
43
+ });
44
+
45
+ export const Demo = Template.bind({});
46
+ Demo.args = {};
47
+
48
+ export const Disabled = Template.bind({});
49
+ Disabled.args = {
50
+ disabled: true,
51
+ };
@@ -12,7 +12,7 @@
12
12
  @dragenter.prevent="dragging = true"
13
13
  @click="pickFile"
14
14
  >
15
- <slot :selecting="selecting" :dragging="dragging" />
15
+ <slot :selecting="selecting" :dragging="dragging" :disabled="disabled" />
16
16
  </button>
17
17
  <input
18
18
  ref="input"
@@ -41,7 +41,7 @@ export default defineComponent({
41
41
  type: String,
42
42
  },
43
43
  },
44
- emits: ['upload'],
44
+ emits: ['select'],
45
45
  data() {
46
46
  return {
47
47
  selecting: false,
@@ -63,7 +63,7 @@ export default defineComponent({
63
63
  },
64
64
  onInputChange() {
65
65
  const files = (this.inputElement?.files ?? []) as File[];
66
- this.upload(files);
66
+ this.select(files);
67
67
  },
68
68
  handleDrop(e: any) {
69
69
  if (this.disabled) {
@@ -72,9 +72,9 @@ export default defineComponent({
72
72
 
73
73
  const files = e?.dataTransfer?.files ?? [];
74
74
 
75
- this.upload(files);
75
+ this.select(files);
76
76
  },
77
- async upload(files: File[]) {
77
+ async select(files: File[]) {
78
78
  if (this.disabled) {
79
79
  return;
80
80
  }
@@ -88,7 +88,7 @@ export default defineComponent({
88
88
  try {
89
89
  const file = files[0];
90
90
 
91
- this.$emit('upload', file);
91
+ this.$emit('select', file);
92
92
  } finally {
93
93
  if (this.inputElement) {
94
94
  this.inputElement.value = '';
@@ -0,0 +1,80 @@
1
+ import BaseFileUploader from './BaseFileUploader.vue';
2
+ import BaseLoadingCover from './BaseLoadingCover.vue';
3
+ import BaseAppNotifications from './BaseAppNotifications.vue';
4
+ import { Icon as BaseIcon } from '@iconify/vue';
5
+
6
+ export default {
7
+ title: 'Form/BaseFileUploader',
8
+ component: BaseFileUploader,
9
+ args: {
10
+ buttonClass: 'w-full',
11
+ acceptedExtensions: ['jpg', 'png'],
12
+ maxSize: 1024 * 200, // 200kb
13
+ url: 'https://dummyjson.com/posts/add',
14
+ },
15
+ };
16
+
17
+ const Template = (args) => ({
18
+ components: {
19
+ BaseFileUploader,
20
+ BaseIcon,
21
+ BaseLoadingCover,
22
+ BaseAppNotifications,
23
+ },
24
+ setup() {
25
+ return { args };
26
+ },
27
+ template: `
28
+ <BaseFileUploader v-bind="args">
29
+ <template #default="{ dragging, disabled, uploading, selecting }">
30
+ <div
31
+ class="flex w-full items-center space-x-4 rounded-lg border-2 border-dashed border-slate-200 p-5 duration-100"
32
+ :class="[
33
+ dragging ? 'bg-slate-100' : 'bg-white',
34
+ disabled ? 'bg-slate-100 cursor-not-allowed' : 'hover:bg-slate-50',
35
+ ]"
36
+ >
37
+ <div class="rounded-full bg-slate-200 p-2">
38
+ <BaseIcon
39
+ icon="heroicons:arrow-up-on-square"
40
+ class="h-6 w-6"
41
+ :class="[disabled ? 'text-slate-400' : 'text-slate-500']"
42
+ />
43
+ </div>
44
+ <div class="text-left">
45
+ <p
46
+ class="mb-0 text-sm font-medium leading-tight"
47
+ :class="[disabled ? 'text-slate-400' : 'text-slate-900']"
48
+ >
49
+ {{ $t("sui.drop_or_click_to_upload") }}
50
+ </p>
51
+ <p class="text-sm text-slate-500 mt-1">Max 200kb</p>
52
+ <p class="text-sm text-slate-500">.jpg, .png</p>
53
+ </div>
54
+ </div>
55
+ </template>
56
+ <template #loading="{ dragging, disabled, uploading, selecting }">
57
+ <BaseLoadingCover
58
+ :model-value="args.loading || uploading || selecting"
59
+ :delay="0"
60
+ icon-class="text-primary-600 w-6 h-6"
61
+ backdrop-class="bg-white opacity-60"
62
+ />
63
+ </template>
64
+ </BaseFileUploader>
65
+ <BaseAppNotifications></BaseAppNotifications>
66
+ `,
67
+ });
68
+
69
+ export const Demo = Template.bind({});
70
+ Demo.args = {};
71
+
72
+ export const Disabled = Template.bind({});
73
+ Disabled.args = {
74
+ disabled: true,
75
+ };
76
+
77
+ export const Loading = Template.bind({});
78
+ Loading.args = {
79
+ loading: true,
80
+ };
@@ -4,7 +4,7 @@
4
4
  :button-class="buttonClass"
5
5
  :disabled="uploading || disabled"
6
6
  :accept="accept"
7
- @upload="onPictureUpload"
7
+ @select="onFileSelect"
8
8
  >
9
9
  <template #default="slotProps">
10
10
  <slot
@@ -22,6 +22,7 @@
22
22
  :disabled="slotProps.disabled"
23
23
  >
24
24
  <BaseLoadingCover
25
+ :delay="0"
25
26
  icon-class="text-primary-600 w-6 h-6"
26
27
  :model-value="loading || uploading || slotProps.selecting"
27
28
  />
@@ -37,6 +38,8 @@ import { PropType } from 'vue';
37
38
  import { UploadedFile } from '@/types/UploadedFile';
38
39
  import { toHumanList, fileSizeFormat } from '../utils';
39
40
  import { useNotificationsStore } from '../stores/notifications';
41
+ import BaseLoadingCover from './BaseLoadingCover.vue';
42
+ import BaseFilePicker from './BaseFilePicker.vue';
40
43
 
41
44
  const http = config.http;
42
45
  const i18n = useI18n();
@@ -88,8 +91,15 @@ const emit = defineEmits([
88
91
 
89
92
  const uploading = ref(false);
90
93
 
91
- async function onPictureUpload(file: File) {
94
+ async function onFileSelect(file: File) {
95
+ if (uploading.value) {
96
+ return;
97
+ }
98
+
99
+ uploading.value = true;
100
+
92
101
  if (!(await props.beforeUpload())) {
102
+ uploading.value = false;
93
103
  return;
94
104
  }
95
105
 
@@ -102,6 +112,8 @@ async function onPictureUpload(file: File) {
102
112
  x: fileSizeFormat(props.maxSize),
103
113
  }),
104
114
  });
115
+
116
+ uploading.value = false;
105
117
  return;
106
118
  }
107
119
 
@@ -124,6 +136,8 @@ async function onPictureUpload(file: File) {
124
136
  toHumanList(props.acceptedExtensions, i18n.t('sui.or')) +
125
137
  '.',
126
138
  });
139
+
140
+ uploading.value = false;
127
141
  return;
128
142
  }
129
143
  }
@@ -132,7 +146,6 @@ async function onPictureUpload(file: File) {
132
146
 
133
147
  formData.append('file', file);
134
148
 
135
- uploading.value = true;
136
149
  emit('upload:start');
137
150
 
138
151
  const response = await http.post(props.url ?? config.upload_url, formData);
@@ -0,0 +1,46 @@
1
+ import BaseInput from './BaseInput.vue';
2
+
3
+ export default {
4
+ title: 'Form/BaseInput',
5
+ component: BaseInput,
6
+ args: {
7
+ required: true,
8
+ type: 'text',
9
+ name: 'name',
10
+ class: 'w-full',
11
+ },
12
+ };
13
+
14
+ const Template = (args) => ({
15
+ components: {
16
+ BaseInput,
17
+ },
18
+ setup() {
19
+ const value = ref('');
20
+ return { args, value };
21
+ },
22
+ template: `
23
+ <form @submit.prevent="" class="border-none">
24
+ <BaseInput v-model="value" v-bind="args"></BaseInput>
25
+ </form>
26
+ `,
27
+ });
28
+
29
+ export const Demo = Template.bind({});
30
+ Demo.args = {
31
+ placeholder: 'Enter your name',
32
+ };
33
+
34
+ export const Number = Template.bind({});
35
+ Number.args = {
36
+ type: 'number',
37
+ step: 0.1,
38
+ placeholder: 'Enter a number',
39
+ };
40
+
41
+ export const Disabled = Template.bind({});
42
+ Disabled.args = {
43
+ modelValue: 'Disabled input!',
44
+ disabled: true,
45
+ placeholder: 'Enter your name',
46
+ };
@@ -4,10 +4,11 @@
4
4
  :value="modelValue"
5
5
  :type="type"
6
6
  :name="name"
7
+ :step="step"
7
8
  :disabled="disabled"
8
9
  :placeholder="placeholder"
9
10
  :required="required"
10
- class="rounded"
11
+ class="rounded border-slate-300 disabled:cursor-not-allowed disabled:text-slate-300"
11
12
  :autocomplete="autocomplete ? 'on' : 'off'"
12
13
  @keydown.enter="onEnter"
13
14
  @input="$emit('update:modelValue', transformInputValue($event))"
@@ -20,17 +21,24 @@ import { PropType } from 'vue';
20
21
 
21
22
  const props = defineProps({
22
23
  modelValue: {
23
- required: true,
24
+ default: '',
24
25
  type: [String, Number, null] as PropType<string | number | null>,
25
26
  },
26
27
  type: {
27
28
  type: String,
28
29
  default: 'text',
29
30
  },
31
+ step: {
32
+ default: undefined,
33
+ type: Number,
34
+ },
30
35
  autocomplete: {
31
36
  default: true,
32
37
  type: Boolean,
33
38
  },
39
+ /**
40
+ * Prevents submit when pressing 'Enter' while the input is focused.
41
+ */
34
42
  preventSubmit: {
35
43
  default: false,
36
44
  type: Boolean,