sprintify-ui 0.2.18 → 0.2.19

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.
@@ -18428,87 +18428,96 @@ function w1(t, r) {
18428
18428
  let e = t.type.split("/").pop();
18429
18429
  return t instanceof File && (e = t.name.split(".").pop()), !(e && r && r.length && !r.includes(e));
18430
18430
  }
18431
- const x1 = ["disabled"], _1 = ["accept"], bs = /* @__PURE__ */ Se({
18431
+ const x1 = ["disabled"], _1 = ["accept", "multiple"], bs = /* @__PURE__ */ Se({
18432
18432
  __name: "BaseFilePicker",
18433
18433
  props: {
18434
18434
  disabled: { type: Boolean, default: !1 },
18435
18435
  twButton: { default: "" },
18436
18436
  maxSize: { default: 1024 * 1024 * 20 },
18437
18437
  accept: { default: void 0 },
18438
- acceptedExtensions: { default: void 0 }
18438
+ acceptedExtensions: { default: void 0 },
18439
+ multiple: { type: Boolean, default: !1 }
18439
18440
  },
18440
18441
  emits: ["select"],
18441
18442
  setup(t, { emit: r }) {
18442
18443
  const e = t, n = Rr(), o = se(!1), a = se(!1), l = se();
18443
18444
  async function s() {
18444
- var d;
18445
- e.disabled || (d = l.value) == null || d.click();
18446
- }
18447
- function u() {
18448
18445
  var c;
18449
- const d = ((c = l.value) == null ? void 0 : c.files) ?? [];
18450
- h(d);
18446
+ e.disabled || (c = l.value) == null || c.click();
18451
18447
  }
18452
- function f(d) {
18448
+ function u() {
18453
18449
  var p;
18450
+ const c = ((p = l.value) == null ? void 0 : p.files) ?? [];
18451
+ h(c);
18452
+ }
18453
+ function f(c) {
18454
+ var b;
18454
18455
  if (e.disabled)
18455
18456
  return;
18456
- const c = ((p = d == null ? void 0 : d.dataTransfer) == null ? void 0 : p.files) ?? [];
18457
- h(c);
18457
+ const p = ((b = c == null ? void 0 : c.dataTransfer) == null ? void 0 : b.files) ?? [];
18458
+ h(p);
18458
18459
  }
18459
- async function h(d) {
18460
- if (!e.disabled && !(!d || d.length == 0 || !(d[0] instanceof File))) {
18460
+ async function h(c) {
18461
+ if (!e.disabled && !(!c || c.length == 0 || !(c[0] instanceof File))) {
18461
18462
  o.value = !0;
18462
18463
  try {
18463
- const c = d[0];
18464
- if (!b1(c, e.maxSize)) {
18465
- n.push({
18466
- color: "danger",
18467
- title: $e("sui.error"),
18468
- text: $e("sui.the_file_size_must_not_exceed_x", {
18469
- x: da(e.maxSize)
18470
- })
18471
- }), o.value = !1;
18464
+ if (!e.multiple) {
18465
+ const b = c[0];
18466
+ if (!d(b))
18467
+ return;
18468
+ r("select", b);
18472
18469
  return;
18473
18470
  }
18474
- if (!w1(c, e.acceptedExtensions)) {
18475
- n.push({
18476
- color: "danger",
18477
- title: $e("sui.error"),
18478
- text: $e("sui.the_file_type_is_invalid") + " " + $e("sui.file_must_be_of_type") + " " + Uv(e.acceptedExtensions, $e("sui.or")) + "."
18479
- }), o.value = !1;
18480
- return;
18471
+ const p = [];
18472
+ for (const b of c) {
18473
+ if (!d(b))
18474
+ return;
18475
+ p.push(b);
18481
18476
  }
18482
- r("select", c);
18477
+ r("select", p);
18483
18478
  } finally {
18484
18479
  l.value && (l.value.value = ""), o.value = !1;
18485
18480
  }
18486
18481
  }
18487
18482
  }
18488
- return (d, c) => ($(), Z(Qe, null, [
18483
+ function d(c) {
18484
+ return b1(c, e.maxSize) ? w1(c, e.acceptedExtensions) ? !0 : (n.push({
18485
+ color: "danger",
18486
+ title: $e("sui.error"),
18487
+ text: $e("sui.the_file_type_is_invalid") + " " + $e("sui.file_must_be_of_type") + " " + Uv(e.acceptedExtensions, $e("sui.or")) + "."
18488
+ }), o.value = !1, !1) : (n.push({
18489
+ color: "danger",
18490
+ title: $e("sui.error"),
18491
+ text: $e("sui.the_file_size_must_not_exceed_x", {
18492
+ x: da(e.maxSize)
18493
+ })
18494
+ }), o.value = !1, !1);
18495
+ }
18496
+ return (c, p) => ($(), Z(Qe, null, [
18489
18497
  L("button", {
18490
18498
  type: "button",
18491
- class: pe(d.twButton),
18492
- disabled: d.disabled,
18493
- onDrop: c[0] || (c[0] = On((p) => {
18494
- a.value = !1, f(p);
18499
+ class: pe(c.twButton),
18500
+ disabled: c.disabled,
18501
+ onDrop: p[0] || (p[0] = On((b) => {
18502
+ a.value = !1, f(b);
18495
18503
  }, ["prevent"])),
18496
- onDragleave: c[1] || (c[1] = On((p) => a.value = !1, ["prevent"])),
18497
- onDragover: c[2] || (c[2] = On((p) => a.value = !0, ["prevent"])),
18498
- onDragenter: c[3] || (c[3] = On((p) => a.value = !0, ["prevent"])),
18504
+ onDragleave: p[1] || (p[1] = On((b) => a.value = !1, ["prevent"])),
18505
+ onDragover: p[2] || (p[2] = On((b) => a.value = !0, ["prevent"])),
18506
+ onDragenter: p[3] || (p[3] = On((b) => a.value = !0, ["prevent"])),
18499
18507
  onClick: s
18500
18508
  }, [
18501
- Be(d.$slots, "default", {
18509
+ Be(c.$slots, "default", {
18502
18510
  selecting: T(o),
18503
18511
  dragging: T(a),
18504
- disabled: d.disabled
18512
+ disabled: c.disabled
18505
18513
  })
18506
18514
  ], 42, x1),
18507
18515
  L("input", {
18508
18516
  ref_key: "input",
18509
18517
  ref: l,
18510
18518
  type: "file",
18511
- accept: d.accept,
18519
+ accept: c.accept,
18520
+ multiple: c.multiple,
18512
18521
  hidden: "true",
18513
18522
  onChange: u
18514
18523
  }, null, 40, _1)
@@ -18687,7 +18696,8 @@ const x1 = ["disabled"], _1 = ["accept"], bs = /* @__PURE__ */ Se({
18687
18696
  maxSize: { default: void 0 },
18688
18697
  accept: { default: void 0 },
18689
18698
  acceptedExtensions: { default: void 0 },
18690
- cropper: { type: [Object, Boolean, null], default: !0 }
18699
+ cropper: { type: [Object, Boolean, null], default: !0 },
18700
+ multiple: { type: Boolean, default: !1 }
18691
18701
  },
18692
18702
  emits: [
18693
18703
  "upload:start",
@@ -18697,34 +18707,27 @@ const x1 = ["disabled"], _1 = ["accept"], bs = /* @__PURE__ */ Se({
18697
18707
  ],
18698
18708
  setup(t, { emit: r }) {
18699
18709
  const e = t, n = Nt.http, o = Rr(), a = W(() => e.component == "BaseFilePickerCrop" ? Ef : bs), l = W(() => {
18700
- const h = {
18710
+ const d = {
18701
18711
  disabled: e.disabled || e.loading || s.value,
18702
18712
  twButton: e.twButton,
18703
18713
  maxSize: e.maxSize,
18704
18714
  accept: e.component == "BaseFilePickerCrop" ? void 0 : e.accept,
18705
- acceptedExtensions: e.acceptedExtensions
18715
+ acceptedExtensions: e.acceptedExtensions,
18716
+ multiple: e.component == "BaseFilePickerCrop" ? void 0 : e.multiple
18706
18717
  };
18707
- return e.component == "BaseFilePickerCrop" && (h.cropper = e.cropper), h;
18718
+ return e.component == "BaseFilePickerCrop" && (d.cropper = e.cropper), d;
18708
18719
  }), s = se(!1);
18709
- async function u(h) {
18720
+ async function u(d) {
18710
18721
  if (!s.value) {
18711
18722
  if (s.value = !0, !await e.beforeUpload()) {
18712
18723
  s.value = !1;
18713
18724
  return;
18714
18725
  }
18715
18726
  try {
18716
- const d = new FormData();
18717
- d.append("file", h), r("upload:start");
18718
- const p = (await n.post(e.url ?? Nt.upload_url, d)).data;
18719
- p.original_file = h;
18720
- const b = new FileReader();
18721
- b.onload = (m) => {
18722
- p.data_url = m.target.result, f(p);
18723
- }, b.onerror = (m) => {
18724
- f(p);
18725
- }, p.mime_type.includes("image") ? b.readAsDataURL(h) : f(p);
18726
- } catch (d) {
18727
- console.error(d), r("upload:fail"), o.push({
18727
+ const c = new FormData();
18728
+ Array.isArray(d) ? await Promise.all(d.map((p) => f(c, p))) : await f(c, d);
18729
+ } catch (c) {
18730
+ console.error(c), r("upload:fail"), o.push({
18728
18731
  color: "danger",
18729
18732
  title: $e("sui.error"),
18730
18733
  text: $e("sui.upload_failed")
@@ -18734,24 +18737,35 @@ const x1 = ["disabled"], _1 = ["accept"], bs = /* @__PURE__ */ Se({
18734
18737
  }
18735
18738
  }
18736
18739
  }
18737
- function f(h) {
18738
- r("upload:success", h);
18740
+ async function f(d, c) {
18741
+ d.append("file", c), r("upload:start");
18742
+ const b = (await n.post(e.url ?? Nt.upload_url, d)).data;
18743
+ b.original_file = c;
18744
+ const m = new FileReader();
18745
+ m.onload = (v) => {
18746
+ b.data_url = v.target.result, h(b);
18747
+ }, m.onerror = (v) => {
18748
+ h(b);
18749
+ }, b.mime_type.includes("image") ? m.readAsDataURL(c) : h(b);
18750
+ }
18751
+ function h(d) {
18752
+ r("upload:success", d);
18739
18753
  }
18740
- return (h, d) => ($(), Z("div", S1, [
18754
+ return (d, c) => ($(), Z("div", S1, [
18741
18755
  ($(), De(or(T(a)), Pt(T(l), { onSelect: u }), {
18742
- default: _e((c) => [
18743
- Be(h.$slots, "default", Pt({
18756
+ default: _e((p) => [
18757
+ Be(d.$slots, "default", Pt({
18744
18758
  uploading: T(s),
18745
- loading: h.loading
18746
- }, c)),
18747
- Be(h.$slots, "loading", Pt({
18759
+ loading: d.loading
18760
+ }, p)),
18761
+ Be(d.$slots, "loading", Pt({
18748
18762
  uploading: T(s),
18749
- loading: h.loading
18750
- }, c), () => [
18763
+ loading: d.loading
18764
+ }, p), () => [
18751
18765
  we(ws, {
18752
18766
  delay: 0,
18753
18767
  "tw-icon": "w-6 h-6 text-primary-600",
18754
- "model-value": h.loading || T(s) || c.selecting
18768
+ "model-value": d.loading || T(s) || p.selecting
18755
18769
  }, null, 8, ["model-value"])
18756
18770
  ])
18757
18771
  ]),
@@ -21837,6 +21851,10 @@ const Os = /* @__PURE__ */ Se({
21837
21851
  default: !1,
21838
21852
  type: Boolean
21839
21853
  },
21854
+ multiple: {
21855
+ default: !1,
21856
+ type: Boolean
21857
+ },
21840
21858
  listProps: {
21841
21859
  default: void 0,
21842
21860
  type: Object
@@ -21914,6 +21932,7 @@ const Os = /* @__PURE__ */ Se({
21914
21932
  accept: t.accept,
21915
21933
  "accepted-extensions": t.acceptedExtensions,
21916
21934
  url: t.uploadUrl,
21935
+ multiple: t.multiple,
21917
21936
  cropper: t.pickerComponent == "BaseFilePickerCrop" ? t.cropper : void 0,
21918
21937
  "onUpload:start": y,
21919
21938
  "onUpload:end": i,
@@ -21950,7 +21969,7 @@ const Os = /* @__PURE__ */ Se({
21950
21969
  ])
21951
21970
  ]),
21952
21971
  _: 3
21953
- }, 8, ["component", "max-size", "disabled", "accept", "accepted-extensions", "url", "cropper"]),
21972
+ }, 8, ["component", "max-size", "disabled", "accept", "accepted-extensions", "url", "multiple", "cropper"]),
21954
21973
  T(u).length ? ($(), Z("div", Px, [
21955
21974
  Be(w.$slots, "list", {
21956
21975
  modelValue: T(u),
@@ -32782,12 +32801,12 @@ const Y2 = { class: "relative flex space-x-3" }, G2 = { class: "text-sm leading-
32782
32801
  }
32783
32802
  };
32784
32803
  function h(p, b) {
32785
- var v, y;
32786
- const m = b.target;
32804
+ var y, i;
32805
+ const m = b.target, v = e.type === "alphanumeric" ? /^[A-Za-z0-9]$/ : /^[0-9]$/;
32787
32806
  p === 0 && b.key === "Backspace" || p !== e.numberOfCharacters + 1 && (s.value = p - 1, b.key === "Backspace" ? p > 0 && p <= e.numberOfCharacters && (m.value ? (m.value = "", l.value[p] = "", d()) : setTimeout(() => {
32788
- var i;
32789
- (i = a.value[s.value - 1]) == null || i.focus(), l.value[s.value] = "", d();
32790
- }, 10)) : b.key === "ArrowRight" ? (v = a.value[p]) == null || v.focus() : b.key === "ArrowLeft" ? (y = a.value[s.value - 1]) == null || y.focus() : l.value[p] = "");
32807
+ var w;
32808
+ (w = a.value[s.value - 1]) == null || w.focus(), l.value[s.value] = "", d();
32809
+ }, 10)) : b.key === "ArrowRight" ? (y = a.value[p]) == null || y.focus() : b.key === "ArrowLeft" ? (i = a.value[s.value - 1]) == null || i.focus() : v.test(b.key) && (l.value[p] = ""));
32791
32810
  }
32792
32811
  function d() {
32793
32812
  o.code = l.value.join("");
@@ -7,6 +7,10 @@ declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<{
7
7
  type: import("vue").PropType<string>;
8
8
  default: undefined;
9
9
  };
10
+ multiple: {
11
+ type: import("vue").PropType<boolean>;
12
+ default: boolean;
13
+ };
10
14
  twButton: {
11
15
  type: import("vue").PropType<string>;
12
16
  default: string;
@@ -28,6 +32,10 @@ declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<{
28
32
  type: import("vue").PropType<string>;
29
33
  default: undefined;
30
34
  };
35
+ multiple: {
36
+ type: import("vue").PropType<boolean>;
37
+ default: boolean;
38
+ };
31
39
  twButton: {
32
40
  type: import("vue").PropType<string>;
33
41
  default: string;
@@ -45,6 +53,7 @@ declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<{
45
53
  }, {
46
54
  disabled: boolean;
47
55
  accept: string;
56
+ multiple: boolean;
48
57
  twButton: string;
49
58
  maxSize: number;
50
59
  acceptedExtensions: string[];
@@ -12,6 +12,11 @@ declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<{
12
12
  type: import("vue").PropType<string>;
13
13
  default: undefined;
14
14
  };
15
+ multiple: {
16
+ type: import("vue").PropType<boolean>;
17
+ required: true;
18
+ default: boolean;
19
+ };
15
20
  cropper: {
16
21
  type: import("vue").PropType<boolean | Record<string, any> | BaseCropperConfig | null>;
17
22
  default: boolean;
@@ -53,6 +58,11 @@ declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<{
53
58
  type: import("vue").PropType<string>;
54
59
  default: undefined;
55
60
  };
61
+ multiple: {
62
+ type: import("vue").PropType<boolean>;
63
+ required: true;
64
+ default: boolean;
65
+ };
56
66
  cropper: {
57
67
  type: import("vue").PropType<boolean | Record<string, any> | BaseCropperConfig | null>;
58
68
  default: boolean;
@@ -90,6 +100,7 @@ declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<{
90
100
  component: "BaseFilePicker" | "BaseFilePickerCrop";
91
101
  disabled: boolean;
92
102
  accept: string;
103
+ multiple: boolean;
93
104
  cropper: boolean | Record<string, any> | BaseCropperConfig | null;
94
105
  loading: boolean;
95
106
  url: string;
@@ -60,6 +60,10 @@ declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<{
60
60
  default: boolean;
61
61
  type: BooleanConstructor;
62
62
  };
63
+ multiple: {
64
+ default: boolean;
65
+ type: BooleanConstructor;
66
+ };
63
67
  listProps: {
64
68
  default: undefined;
65
69
  type: PropType<Record<string, unknown>>;
@@ -125,6 +129,10 @@ declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<{
125
129
  default: boolean;
126
130
  type: BooleanConstructor;
127
131
  };
132
+ multiple: {
133
+ default: boolean;
134
+ type: BooleanConstructor;
135
+ };
128
136
  listProps: {
129
137
  default: undefined;
130
138
  type: PropType<Record<string, unknown>>;
@@ -148,6 +156,7 @@ declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<{
148
156
  min: number;
149
157
  max: number;
150
158
  accept: string;
159
+ multiple: boolean;
151
160
  cropper: boolean | BaseCropperConfig | null;
152
161
  layout: "list" | "gallery" | "images";
153
162
  maxSize: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sprintify-ui",
3
- "version": "0.2.18",
3
+ "version": "0.2.19",
4
4
  "scripts": {
5
5
  "build": "rimraf dist && vue-tsc && vite build",
6
6
  "build-fast": "rimraf dist && vite build",
@@ -72,6 +72,11 @@ FileExtension.args = {
72
72
  acceptedExtensions: ['xlsx', 'xls'],
73
73
  };
74
74
 
75
+ export const Multiple = Template.bind({});
76
+ Multiple.args = {
77
+ multiple: true
78
+ };
79
+
75
80
  export const Disabled = Template.bind({});
76
81
  Disabled.args = {
77
82
  disabled: true,
@@ -5,7 +5,7 @@
5
5
  :disabled="disabled"
6
6
  @drop.prevent="
7
7
  dragging = false;
8
- handleDrop($event);
8
+ handleDrop($event);
9
9
  "
10
10
  @dragleave.prevent="dragging = false"
11
11
  @dragover.prevent="dragging = true"
@@ -22,6 +22,7 @@
22
22
  ref="input"
23
23
  type="file"
24
24
  :accept="accept"
25
+ :multiple="multiple"
25
26
  hidden="true"
26
27
  @change="onInputChange"
27
28
  >
@@ -40,6 +41,7 @@ const props = withDefaults(
40
41
  maxSize?: number;
41
42
  accept?: string;
42
43
  acceptedExtensions?: string[];
44
+ multiple?: boolean;
43
45
  }>(),
44
46
  {
45
47
  disabled: false,
@@ -47,6 +49,7 @@ const props = withDefaults(
47
49
  maxSize: 1024 * 1024 * 20, // 20 MB,
48
50
  accept: undefined,
49
51
  acceptedExtensions: undefined,
52
+ multiple: false,
50
53
  }
51
54
  );
52
55
 
@@ -93,39 +96,30 @@ async function select(files: File[]) {
93
96
  selecting.value = true;
94
97
 
95
98
  try {
96
- const file = files[0];
97
-
98
- if (!maxSize(file, props.maxSize)) {
99
- notifications.push({
100
- color: 'danger',
101
- title: t('sui.error'),
102
- text: t('sui.the_file_size_must_not_exceed_x', {
103
- x: fileSizeFormat(props.maxSize),
104
- }),
105
- });
106
-
107
- selecting.value = false;
108
- return;
99
+ if (!props.multiple) {
100
+ const file = files[0];
101
+
102
+ if (!isValid(file)) {
103
+ return;
104
+ }
105
+
106
+ emit('select', file);
107
+
108
+ return
109
109
  }
110
110
 
111
- if (!validExtension(file, props.acceptedExtensions)) {
112
- notifications.push({
113
- color: 'danger',
114
- title: t('sui.error'),
115
- text:
116
- t('sui.the_file_type_is_invalid') +
117
- ' ' +
118
- t('sui.file_must_be_of_type') +
119
- ' ' +
120
- toHumanList(props.acceptedExtensions as string[], t('sui.or')) +
121
- '.',
122
- });
123
-
124
- selecting.value = false;
125
- return;
111
+ const listFiles = [] as File[];
112
+
113
+ for (const file of files) {
114
+ if (!isValid(file)) {
115
+ return;
116
+ }
117
+
118
+ listFiles.push(file);
126
119
  }
127
120
 
128
- emit('select', file);
121
+ emit('select', listFiles);
122
+
129
123
  } finally {
130
124
  if (input.value) {
131
125
  input.value.value = '';
@@ -133,4 +127,40 @@ async function select(files: File[]) {
133
127
  selecting.value = false;
134
128
  }
135
129
  }
130
+
131
+ function isValid(file: File) {
132
+ if (!maxSize(file, props.maxSize)) {
133
+ notifications.push({
134
+ color: 'danger',
135
+ title: t('sui.error'),
136
+ text: t('sui.the_file_size_must_not_exceed_x', {
137
+ x: fileSizeFormat(props.maxSize),
138
+ }),
139
+ });
140
+
141
+ selecting.value = false;
142
+
143
+ return false;
144
+ }
145
+
146
+ if (!validExtension(file, props.acceptedExtensions)) {
147
+ notifications.push({
148
+ color: 'danger',
149
+ title: t('sui.error'),
150
+ text:
151
+ t('sui.the_file_type_is_invalid') +
152
+ ' ' +
153
+ t('sui.file_must_be_of_type') +
154
+ ' ' +
155
+ toHumanList(props.acceptedExtensions as string[], t('sui.or')) +
156
+ '.',
157
+ });
158
+
159
+ selecting.value = false;
160
+
161
+ return false;
162
+ }
163
+
164
+ return true;
165
+ }
136
166
  </script>
@@ -9,8 +9,8 @@ export default {
9
9
  args: {
10
10
  twButton: 'w-full',
11
11
  acceptedExtensions: ['jpg', 'png'],
12
- maxSize: 1024 * 200, // 200kb
13
- url: 'https://dummyjson.com/posts/add',
12
+ maxSize: 1024 * 1024 * 20, // 20MB
13
+ url: 'https://faker.witify.io/api/todos/upload',
14
14
  },
15
15
  };
16
16
 
@@ -73,6 +73,11 @@ ImagePicker.args = {
73
73
  },
74
74
  };
75
75
 
76
+ export const Multiple = Template.bind({});
77
+ Multiple.args = {
78
+ multiple: true,
79
+ };
80
+
76
81
  export const Disabled = Template.bind({});
77
82
  Disabled.args = {
78
83
  disabled: true,
@@ -54,6 +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
58
  }>(),
58
59
  {
59
60
  component: 'BaseFilePicker',
@@ -68,6 +69,7 @@ const props = withDefaults(
68
69
  accept: undefined,
69
70
  acceptedExtensions: undefined,
70
71
  cropper: true,
72
+ multiple: false
71
73
  }
72
74
  );
73
75
 
@@ -92,6 +94,7 @@ const pickerProps = computed(() => {
92
94
  maxSize: props.maxSize,
93
95
  accept: props.component == 'BaseFilePickerCrop' ? undefined : props.accept,
94
96
  acceptedExtensions: props.acceptedExtensions,
97
+ multiple: props.component == 'BaseFilePickerCrop' ? undefined : props.multiple,
95
98
  } as Record<string, any>;
96
99
 
97
100
  if (props.component == 'BaseFilePickerCrop') {
@@ -103,7 +106,7 @@ const pickerProps = computed(() => {
103
106
 
104
107
  const uploading = ref(false);
105
108
 
106
- async function onFileSelect(file: File) {
109
+ async function onFileSelect(files: File | File[]) {
107
110
  if (uploading.value) {
108
111
  return;
109
112
  }
@@ -118,30 +121,10 @@ async function onFileSelect(file: File) {
118
121
  try {
119
122
  const formData = new FormData();
120
123
 
121
- formData.append('file', file);
122
-
123
- emit('upload:start');
124
-
125
- const response = await http.post(props.url ?? config.upload_url, formData);
126
-
127
- const payload = response.data as UploadedFile;
128
- payload.original_file = file;
129
-
130
- const reader = new FileReader();
131
-
132
- reader.onload = (e: any) => {
133
- payload.data_url = e.target.result;
134
- onSuccess(payload);
135
- };
136
-
137
- reader.onerror = (e: any) => {
138
- onSuccess(payload);
139
- };
140
-
141
- if (payload.mime_type.includes('image')) {
142
- reader.readAsDataURL(file);
124
+ if (Array.isArray(files)) {
125
+ await Promise.all(files.map(f => processFileUpload(formData, f)))
143
126
  } else {
144
- onSuccess(payload);
127
+ await processFileUpload(formData, files)
145
128
  }
146
129
  } catch (e: unknown) {
147
130
  console.error(e);
@@ -157,6 +140,34 @@ async function onFileSelect(file: File) {
157
140
  }
158
141
  }
159
142
 
143
+ async function processFileUpload(formData: FormData, file: File) {
144
+ formData.append('file', file);
145
+
146
+ emit('upload:start');
147
+
148
+ const response = await http.post(props.url ?? config.upload_url, formData);
149
+
150
+ const payload = response.data as UploadedFile;
151
+ payload.original_file = file;
152
+
153
+ const reader = new FileReader();
154
+
155
+ reader.onload = (e: any) => {
156
+ payload.data_url = e.target.result;
157
+ onSuccess(payload);
158
+ };
159
+
160
+ reader.onerror = (e: any) => {
161
+ onSuccess(payload);
162
+ };
163
+
164
+ if (payload.mime_type.includes('image')) {
165
+ reader.readAsDataURL(file);
166
+ } else {
167
+ onSuccess(payload);
168
+ }
169
+ }
170
+
160
171
  function onSuccess(payload: any) {
161
172
  emit('upload:success', payload);
162
173
  }
@@ -92,6 +92,12 @@ LayoutGallery.args = {
92
92
  layout: 'gallery',
93
93
  };
94
94
 
95
+ export const LayoutGalleryMultiple = Template.bind({});
96
+ LayoutGalleryMultiple.args = {
97
+ layout: 'gallery',
98
+ multiple: true
99
+ };
100
+
95
101
  export const LayoutGalleryDisabled = Template.bind({});
96
102
  LayoutGalleryDisabled.args = {
97
103
  layout: 'gallery',
@@ -9,6 +9,7 @@
9
9
  :accept="accept"
10
10
  :accepted-extensions="acceptedExtensions"
11
11
  :url="uploadUrl"
12
+ :multiple="multiple"
12
13
  :cropper="pickerComponent == 'BaseFilePickerCrop' ? cropper : undefined"
13
14
  @upload:start="onUploadStart"
14
15
  @upload:end="onUploadEnd"
@@ -179,6 +180,10 @@ const props = defineProps({
179
180
  default: false,
180
181
  type: Boolean,
181
182
  },
183
+ multiple: {
184
+ default: false,
185
+ type: Boolean,
186
+ },
182
187
  listProps: {
183
188
  default: undefined,
184
189
  type: Object as PropType<Record<string, unknown>>,
@@ -100,6 +100,7 @@ const handleCodeInput = (index: number, event: Event) => {
100
100
  // Handle code keydown events
101
101
  function handleCodeKeyDown(index: number, event: KeyboardEvent) {
102
102
  const input = event.target as HTMLInputElement;
103
+ const allowedKeys = props.type === 'alphanumeric' ? /^[A-Za-z0-9]$/ : /^[0-9]$/;
103
104
 
104
105
  if (index === 0 && event.key === 'Backspace') {
105
106
  return;
@@ -130,10 +131,9 @@ function handleCodeKeyDown(index: number, event: KeyboardEvent) {
130
131
  codeInputs.value[index]?.focus();
131
132
  } else if (event.key === 'ArrowLeft') {
132
133
  codeInputs.value[prvIndex.value - 1]?.focus();
133
- } else {
134
+ } else if (allowedKeys.test(event.key)) {
134
135
  uniqueCode.value[index] = '';
135
136
  }
136
-
137
137
  }
138
138
 
139
139
  // Function to update form.code with the concatenated values of uniqueCode
package/src/lang/en.json CHANGED
@@ -1,88 +1,88 @@
1
- {
2
- "cancel": "Cancel",
3
- "cropper": "Crop",
4
- "drag_to_reposition": "Drag to reposition",
5
- "save": "Save",
6
- "sui": {
7
- "address": "Address",
8
- "address_1_placeholder": "Postal address",
9
- "address_2_description": "Apartment, suite, unit, building",
10
- "and": "and",
11
- "apply": "Apply",
12
- "apply_filters": "Apply filters",
13
- "authentication_code": "Authentication code",
14
- "autocomplete_placeholder": "Type to start your search",
15
- "cancel": "Cancel",
16
- "city": "City",
17
- "clear": "Clear",
18
- "click_or_select_date": "Click or select date",
19
- "click_to_copy": "Click to copy",
20
- "columns": "Columns",
21
- "confirm": "Confirm",
22
- "copied": "Copied",
23
- "country": "Country",
24
- "create_new": "Create new",
25
- "day": "Day",
26
- "delete": "Delete",
27
- "delete_record": "Delete the record",
28
- "delete_record_description": "Are you sure to delete this record? This action is irreversible.",
29
- "deselect_all": "Deselect all",
30
- "download": "Download",
31
- "drag_to_reposition": "Drag to reposition",
32
- "drop_or_click_to_upload": "Drop or click to upload",
33
- "edit": "Edit",
34
- "error": "Error",
35
- "file_must_be_of_type": "The file must be of type",
36
- "filters": "Filters",
37
- "go_to_page": "Go to ",
38
- "invalid_value": "Invalid value",
39
- "just_now": "Just now",
40
- "maximum_x_decimal_places": "Maximum 1 decimal place|Maximum {count} decimal places",
41
- "min_x_characters": "{x} characters minimum",
42
- "month": "Month",
43
- "more": "More",
44
- "next": "Next",
45
- "next_month": "Next month",
46
- "none": "None",
47
- "nothing_found": "Nothing found",
48
- "notifications_empty": "You have no new notifications",
49
- "of": "of",
50
- "or": "or",
51
- "page": "Page",
52
- "pagination_detail_1": "Viewing",
53
- "pagination_detail_2": "of",
54
- "postal_code_zip_code": "Postal Code / Zip Code",
55
- "previous": "Previous",
56
- "previous_month": "Previous month",
57
- "read_more": "Read more",
58
- "region": "State / Province",
59
- "remove": "Remove",
60
- "remove_file": "Remove file?",
61
- "remove_file_description": "Are you sure you want to remove the file? This action is irreversible.",
62
- "save": "Save",
63
- "search": "Search",
64
- "see_all_notifications": "See all notifications",
65
- "select_an_item": "Select an item",
66
- "select_an_option": "Select an option",
67
- "success": "Success",
68
- "the_file_size_must_not_exceed_x": "The file size must not exceed {x}",
69
- "the_file_type_is_invalid": "The file type is invalid",
70
- "type_to_start_your_search": "Type to start your search",
71
- "units": {
72
- "b": "B",
73
- "gb": "GB",
74
- "kb": "kB",
75
- "mb": "MB",
76
- "tb": "TB"
77
- },
78
- "up_to_x": "Up to {x}",
79
- "upload_failed": "Upload failed",
80
- "whoops": "Whoops",
81
- "x_ago": "{duration} ago",
82
- "x_rows_selected": "1 item selected | {count} items selected",
83
- "year": "Year",
84
- "yes_delete": "Yes, delete",
85
- "you_can_upload_up_to_n_files": "You can upload one file at most|You can upload up to {count} files",
86
- "you_cannot_select_more_than_x_items": "You can't select more than one item|You can't select more than {count} items"
87
- }
88
- }
1
+ {
2
+ "cancel": "Cancel",
3
+ "cropper": "Crop",
4
+ "drag_to_reposition": "Drag to reposition",
5
+ "save": "Save",
6
+ "sui": {
7
+ "address": "Address",
8
+ "address_1_placeholder": "Postal address",
9
+ "address_2_description": "Apartment, suite, unit, building",
10
+ "and": "and",
11
+ "apply": "Apply",
12
+ "apply_filters": "Apply filters",
13
+ "authentication_code": "Authentication code",
14
+ "autocomplete_placeholder": "Type to start your search",
15
+ "cancel": "Cancel",
16
+ "city": "City",
17
+ "clear": "Clear",
18
+ "click_or_select_date": "Click or select date",
19
+ "click_to_copy": "Click to copy",
20
+ "columns": "Columns",
21
+ "confirm": "Confirm",
22
+ "copied": "Copied",
23
+ "country": "Country",
24
+ "create_new": "Create new",
25
+ "day": "Day",
26
+ "delete": "Delete",
27
+ "delete_record": "Delete the record",
28
+ "delete_record_description": "Are you sure to delete this record? This action is irreversible.",
29
+ "deselect_all": "Deselect all",
30
+ "download": "Download",
31
+ "drag_to_reposition": "Drag to reposition",
32
+ "drop_or_click_to_upload": "Drop or click to upload",
33
+ "edit": "Edit",
34
+ "error": "Error",
35
+ "file_must_be_of_type": "The file must be of type",
36
+ "filters": "Filters",
37
+ "go_to_page": "Go to ",
38
+ "invalid_value": "Invalid value",
39
+ "just_now": "Just now",
40
+ "maximum_x_decimal_places": "Maximum 1 decimal place|Maximum {count} decimal places",
41
+ "min_x_characters": "{x} characters minimum",
42
+ "month": "Month",
43
+ "more": "More",
44
+ "next": "Next",
45
+ "next_month": "Next month",
46
+ "none": "None",
47
+ "nothing_found": "Nothing found",
48
+ "notifications_empty": "You have no new notifications",
49
+ "of": "of",
50
+ "or": "or",
51
+ "page": "Page",
52
+ "pagination_detail_1": "Viewing",
53
+ "pagination_detail_2": "of",
54
+ "postal_code_zip_code": "Postal Code / Zip Code",
55
+ "previous": "Previous",
56
+ "previous_month": "Previous month",
57
+ "read_more": "Read more",
58
+ "region": "State / Province",
59
+ "remove": "Remove",
60
+ "remove_file": "Remove file?",
61
+ "remove_file_description": "Are you sure you want to remove the file? This action is irreversible.",
62
+ "save": "Save",
63
+ "search": "Search",
64
+ "see_all_notifications": "See all notifications",
65
+ "select_an_item": "Select an item",
66
+ "select_an_option": "Select an option",
67
+ "success": "Success",
68
+ "the_file_size_must_not_exceed_x": "The file size must not exceed {x}",
69
+ "the_file_type_is_invalid": "The file type is invalid",
70
+ "type_to_start_your_search": "Type to start your search",
71
+ "units": {
72
+ "b": "B",
73
+ "gb": "GB",
74
+ "kb": "kB",
75
+ "mb": "MB",
76
+ "tb": "TB"
77
+ },
78
+ "up_to_x": "Up to {x}",
79
+ "upload_failed": "Upload failed",
80
+ "whoops": "Whoops",
81
+ "x_ago": "{duration} ago",
82
+ "x_rows_selected": "1 item selected | {count} items selected",
83
+ "year": "Year",
84
+ "yes_delete": "Yes, delete",
85
+ "you_can_upload_up_to_n_files": "You can upload one file at most|You can upload up to {count} files",
86
+ "you_cannot_select_more_than_x_items": "You can't select more than one item|You can't select more than {count} items"
87
+ }
88
+ }
package/src/lang/fr.json CHANGED
@@ -1,88 +1,88 @@
1
- {
2
- "cancel": "Fermer",
3
- "cropper": "Recadrer",
4
- "drag_to_reposition": "Faites glisser pour repositionner",
5
- "save": "Sauvegarder",
6
- "sui": {
7
- "address": "Adresse",
8
- "address_1_placeholder": "Adresse postale",
9
- "address_2_description": "Appartement, suite, unité, immeuble",
10
- "and": "et",
11
- "apply": "Appliquer",
12
- "apply_filters": "Appliquer les filtres",
13
- "authentication_code": "Code d'authentification",
14
- "autocomplete_placeholder": "Taper pour lancer votre recherche",
15
- "cancel": "Annuler",
16
- "city": "Ville",
17
- "clear": "Effacer",
18
- "click_or_select_date": "Cliquer ou sélectionner une date",
19
- "click_to_copy": "Cliquer pour copier",
20
- "columns": "Colonnes",
21
- "confirm": "Confirmer",
22
- "copied": "Copié",
23
- "country": "Pays",
24
- "create_new": "Créer un nouveau",
25
- "day": "Jour",
26
- "delete": "Supprimer",
27
- "delete_record": "Supprimer l'item",
28
- "delete_record_description": "Supprimer cet item ? \nCette action est irréversible.",
29
- "deselect_all": "Tout déselectionner",
30
- "download": "Télécharger",
31
- "drag_to_reposition": "Faites glisser pour repositionner",
32
- "drop_or_click_to_upload": "Déposer ou cliquer pour télécharger",
33
- "edit": "Modifier",
34
- "error": "Erreur",
35
- "file_must_be_of_type": "Le fichier doit être de type",
36
- "filters": "Filtres",
37
- "go_to_page": "Page",
38
- "invalid_value": "Valeur invalide",
39
- "just_now": "à l’instant",
40
- "maximum_x_decimal_places": "Maximum 1 décimale|Maximum {count} décimales",
41
- "min_x_characters": "{x} caractères minimum",
42
- "month": "Mois",
43
- "more": "Plus",
44
- "next": "Suivant",
45
- "next_month": "Mois prochain",
46
- "none": "Aucun",
47
- "nothing_found": "Rien n'a été trouvé",
48
- "notifications_empty": "Vous n'avez aucune nouvelle notification",
49
- "of": "de",
50
- "or": "ou",
51
- "page": "Page",
52
- "pagination_detail_1": "Affichage de",
53
- "pagination_detail_2": "de",
54
- "postal_code_zip_code": "Code postal",
55
- "previous": "Précédent",
56
- "previous_month": "Mois précédent",
57
- "read_more": "Lire la suite",
58
- "region": "État / Province",
59
- "remove": "Retirer",
60
- "remove_file": "Retirer le fichier?",
61
- "remove_file_description": "Supprimer le fichier ? \nCette action est irréversible.",
62
- "save": "Sauvegarder",
63
- "search": "Rechercher",
64
- "see_all_notifications": "Voir toutes les notifications",
65
- "select_an_item": "Sélectionner un élément",
66
- "select_an_option": "Sélectionner une option",
67
- "success": "Succès",
68
- "the_file_size_must_not_exceed_x": "La taille du fichier ne doit pas dépasser {x}",
69
- "the_file_type_is_invalid": "Le type de fichier n'est pas valide",
70
- "type_to_start_your_search": "Taper pour lancer votre recherche",
71
- "units": {
72
- "b": "o",
73
- "gb": "Go",
74
- "kb": "Ko",
75
- "mb": "Mo",
76
- "tb": "To"
77
- },
78
- "up_to_x": "Jusqu'à {x}",
79
- "upload_failed": "Le téléchargement a échoué",
80
- "whoops": "Oups",
81
- "x_ago": "il y a {duration}",
82
- "x_rows_selected": "1 item sélectionné | \n{count} items sélectionnés",
83
- "year": "Année",
84
- "yes_delete": "Oui, supprimer",
85
- "you_can_upload_up_to_n_files": "Vous pouvez télécharger un fichier au maximum|Vous pouvez télécharger jusqu'à {count} fichiers",
86
- "you_cannot_select_more_than_x_items": "Vous ne pouvez pas sélectionner plus de un élément|Vous ne pouvez pas sélectionner plus de {count} éléments"
87
- }
88
- }
1
+ {
2
+ "cancel": "Fermer",
3
+ "cropper": "Recadrer",
4
+ "drag_to_reposition": "Faites glisser pour repositionner",
5
+ "save": "Sauvegarder",
6
+ "sui": {
7
+ "address": "Adresse",
8
+ "address_1_placeholder": "Adresse postale",
9
+ "address_2_description": "Appartement, suite, unité, immeuble",
10
+ "and": "et",
11
+ "apply": "Appliquer",
12
+ "apply_filters": "Appliquer les filtres",
13
+ "authentication_code": "Code d'authentification",
14
+ "autocomplete_placeholder": "Taper pour lancer votre recherche",
15
+ "cancel": "Annuler",
16
+ "city": "Ville",
17
+ "clear": "Effacer",
18
+ "click_or_select_date": "Cliquer ou sélectionner une date",
19
+ "click_to_copy": "Cliquer pour copier",
20
+ "columns": "Colonnes",
21
+ "confirm": "Confirmer",
22
+ "copied": "Copié",
23
+ "country": "Pays",
24
+ "create_new": "Créer un nouveau",
25
+ "day": "Jour",
26
+ "delete": "Supprimer",
27
+ "delete_record": "Supprimer l'item",
28
+ "delete_record_description": "Supprimer cet item ? \nCette action est irréversible.",
29
+ "deselect_all": "Tout déselectionner",
30
+ "download": "Télécharger",
31
+ "drag_to_reposition": "Faites glisser pour repositionner",
32
+ "drop_or_click_to_upload": "Déposer ou cliquer pour télécharger",
33
+ "edit": "Modifier",
34
+ "error": "Erreur",
35
+ "file_must_be_of_type": "Le fichier doit être de type",
36
+ "filters": "Filtres",
37
+ "go_to_page": "Page",
38
+ "invalid_value": "Valeur invalide",
39
+ "just_now": "à l’instant",
40
+ "maximum_x_decimal_places": "Maximum 1 décimale|Maximum {count} décimales",
41
+ "min_x_characters": "{x} caractères minimum",
42
+ "month": "Mois",
43
+ "more": "Plus",
44
+ "next": "Suivant",
45
+ "next_month": "Mois prochain",
46
+ "none": "Aucun",
47
+ "nothing_found": "Rien n'a été trouvé",
48
+ "notifications_empty": "Vous n'avez aucune nouvelle notification",
49
+ "of": "de",
50
+ "or": "ou",
51
+ "page": "Page",
52
+ "pagination_detail_1": "Affichage de",
53
+ "pagination_detail_2": "de",
54
+ "postal_code_zip_code": "Code postal",
55
+ "previous": "Précédent",
56
+ "previous_month": "Mois précédent",
57
+ "read_more": "Lire la suite",
58
+ "region": "État / Province",
59
+ "remove": "Retirer",
60
+ "remove_file": "Retirer le fichier?",
61
+ "remove_file_description": "Supprimer le fichier ? \nCette action est irréversible.",
62
+ "save": "Sauvegarder",
63
+ "search": "Rechercher",
64
+ "see_all_notifications": "Voir toutes les notifications",
65
+ "select_an_item": "Sélectionner un élément",
66
+ "select_an_option": "Sélectionner une option",
67
+ "success": "Succès",
68
+ "the_file_size_must_not_exceed_x": "La taille du fichier ne doit pas dépasser {x}",
69
+ "the_file_type_is_invalid": "Le type de fichier n'est pas valide",
70
+ "type_to_start_your_search": "Taper pour lancer votre recherche",
71
+ "units": {
72
+ "b": "o",
73
+ "gb": "Go",
74
+ "kb": "Ko",
75
+ "mb": "Mo",
76
+ "tb": "To"
77
+ },
78
+ "up_to_x": "Jusqu'à {x}",
79
+ "upload_failed": "Le téléchargement a échoué",
80
+ "whoops": "Oups",
81
+ "x_ago": "il y a {duration}",
82
+ "x_rows_selected": "1 item sélectionné | \n{count} items sélectionnés",
83
+ "year": "Année",
84
+ "yes_delete": "Oui, supprimer",
85
+ "you_can_upload_up_to_n_files": "Vous pouvez télécharger un fichier au maximum|Vous pouvez télécharger jusqu'à {count} fichiers",
86
+ "you_cannot_select_more_than_x_items": "Vous ne pouvez pas sélectionner plus de un élément|Vous ne pouvez pas sélectionner plus de {count} éléments"
87
+ }
88
+ }