@witchcraft/ui 0.3.11 → 0.3.13

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 (32) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/runtime/assets/utils.css +1 -1
  3. package/dist/runtime/components/LibColorInput/LibColorInput.d.vue.ts +2 -2
  4. package/dist/runtime/components/LibColorInput/LibColorInput.vue.d.ts +2 -2
  5. package/dist/runtime/components/LibColorPicker/LibColorPicker.d.vue.ts +3 -3
  6. package/dist/runtime/components/LibColorPicker/LibColorPicker.vue.d.ts +3 -3
  7. package/dist/runtime/components/LibFileInput/LibFileInput.d.vue.ts +5 -3
  8. package/dist/runtime/components/LibFileInput/LibFileInput.vue +141 -84
  9. package/dist/runtime/components/LibFileInput/LibFileInput.vue.d.ts +5 -3
  10. package/dist/runtime/components/LibInputDeprecated/LibInputDeprecated.d.vue.ts +3 -3
  11. package/dist/runtime/components/LibInputDeprecated/LibInputDeprecated.vue.d.ts +3 -3
  12. package/dist/runtime/components/LibNotifications/LibNotification.vue +16 -1
  13. package/dist/runtime/components/LibNotifications/LibNotificationTestMessageComponent.d.vue.ts +6 -0
  14. package/dist/runtime/components/LibNotifications/LibNotificationTestMessageComponent.vue +29 -0
  15. package/dist/runtime/components/LibNotifications/LibNotificationTestMessageComponent.vue.d.ts +6 -0
  16. package/dist/runtime/components/LibPopup/LibPopup.d.vue.ts +1 -1
  17. package/dist/runtime/components/LibPopup/LibPopup.vue.d.ts +1 -1
  18. package/dist/runtime/helpers/NotificationHandler.d.ts +5 -2
  19. package/dist/runtime/helpers/NotificationHandler.js +2 -1
  20. package/dist/runtime/types/index.d.ts +4 -0
  21. package/dist/runtime/utils/notifyIfError.d.ts +3 -1
  22. package/dist/runtime/utils/notifyIfError.js +4 -2
  23. package/package.json +2 -2
  24. package/src/runtime/assets/utils.css +4 -4
  25. package/src/runtime/components/LibFileInput/LibFileInput.stories.ts +13 -3
  26. package/src/runtime/components/LibFileInput/LibFileInput.vue +154 -92
  27. package/src/runtime/components/LibNotifications/LibNotification.stories.ts +22 -1
  28. package/src/runtime/components/LibNotifications/LibNotification.vue +16 -1
  29. package/src/runtime/components/LibNotifications/LibNotificationTestMessageComponent.vue +27 -0
  30. package/src/runtime/helpers/NotificationHandler.ts +6 -2
  31. package/src/runtime/types/index.ts +5 -0
  32. package/src/runtime/utils/notifyIfError.ts +6 -2
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "witchcraftUi",
3
3
  "configKey": "witchcraftUi",
4
- "version": "0.3.11",
4
+ "version": "0.3.13",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "3.6.1"
@@ -1 +1 @@
1
- @custom-variant dark (&:where(.dark, .dark *));@utility focus-outline-within{@reference outlined-within:outline-2 outlined-within:outline-accent-500 outlined-within:outline-offset-2}@utility focus-outline{@reference outlined:outline-2 outlined:outline-accent-500 outlined:outline-offset-2}@utility focus-outline-no-offset{@reference outlined:outline-2 outlined:outline-accent-500}@utility focus-outline-hidden{@reference outlined:outline-none}@utility bg-squares-gradient{--_square:var(--squareSize,5px);--_double_square:calc(var(--_square)*2);--_light_square:var(--lightSquare,var(--color-white));--_dark_square:var(--darkSquare,var(--color-black));background-color:var(--_light_square);background:repeating-conic-gradient(var(--_dark_square) 0 25%,var(--_light_square) 0 50%) 50% /var(--_double_square) var(--_double_square)}@utility square-light-*{--lightSquare:--value(--color- *)}@utility square-dark-*{--darkSquare:--value(--color- *)}@utility square-size-*{--squareSize:calc(--value(integer) * 1px)}@utility bg-bars-gradient{--_bg_color:var(--bars-bg-color,var(--color-accent-700));--_fg_color:var(--bars-fg-color,var(--color-accent-800));--_angle:var(--bars-angle,45deg);--_fg_width:var(--bars-fg-width,50%);--_bg_width:calc(100% - var(--_fg_width));background-color:var(--_bg_color);--_pos_1:calc(var(--_bg_width)/2);--_pos_2:calc(var(--_bg_width)/2 + var(--_fg_width)/2);--_pos_3:calc(var(--_bg_width) + var(--_fg_width)/2);background-image:repeating-linear-gradient(var(--_angle),var(--_bg_color),var(--_bg_color) var(--_pos_1),var(--_fg_color) var(--_pos_1),var(--_fg_color) var(--_pos_2),var(--_bg_color) var(--_pos_2),var(--_bg_color) var(--_pos_3),var(--_fg_color) var(--_pos_3),var(--_fg_color))}@utility bars-angle-*{--bars-angle:var(--value(integer) * 1deg)}@utility bars-fg-*{--bars-fg-color:--value(--color-*)}@utility bars-bg-*{--bars-bg-color:--value(--color-*)}@utility bars-w-*{--bars-fg-width:calc(--value(integer) * 1%, 50%)}@utility scrollbar-hidden{-ms-overflow-style:none;scrollbar-width:none;&::-webkit-scrollbar{display:none}}@utility styled-scrollbar{--_scrollbar_width:var(--scrollbar-width,calc(var(--spacing)*3));--_scrollbar_border_width:var(--scrollbar-border-width,calc(var(--spacing)/2));--_scrollbar_color:var(--scrollbar-color,--alpha(var(--color-accent-500)/40%));--_scrollbar_hover_color:var(--scrollbar-hover-color,--alpha(var(--color-accent-500)/80%));--_scrollbar_bg_color:var(--scrollbar-bg-color,var(--color-bg));.dark &{--_scrollbar_bg_color:var(--scrollbar-bg-color,var(--color-fg))}&::-webkit-scrollbar{height:var(--_scrollbar_width);width:var(--_scrollbar_width)}&::-webkit-scrollbar-corner,&::-webkit-scrollbar-track{background-color:transparent}&::-webkit-scrollbar-thumb,&::-webkit-scrollbar-track{border-radius:var(--_scrollbar_width)}&::-webkit-scrollbar-thumb{background-color:var(--_scrollbar_color);border:var(--_scrollbar_border_width) solid var(--_scrollbar_bg_color)}&::-webkit-scrollbar-thumb:hover{cursor:pointer}&::-webkit-scrollbar-thumb:active,&::-webkit-scrollbar-thumb:hover{background-color:var(--_scrollbar_hover_color);border-radius:var(--_scrollbar_width)}}@utility styled-scrollbar-w-*{--scrollbar-width:--value(integer)}@utility styled-scrollbar-border-w-*{--scrollbar-border-width:--value(integer)}@utility styled-scrollbar-*{--scrollbar-color:--value(--color-*)}@utility styled-scrollbar-bg-*{--scrollbar-bg-color:--value(--color-*)}@utility styled-resizer{--_resizer_width:var(--resizer-width,8px);--_resizer_color:var(--resizer-color,var(--color-neutral-300));.dark &{--_resizer_color:var(--resizer-color,var(--color-neutral-700))}&::-webkit-resizer{border-bottom-color:var(--_resizer_color);border-left-color:transparent;border-right-color:var(--_resizer_color);border-style:solid;border-top-color:transparent;border-width:var(--_resizer_width)}}@utility styled-resizer-w-*{--resizer-width:--value(integer)}@utility styled-resizer-color-*{--resizer-color:--value(--color-*)}@utility content-vertical-holder{--tw-content:"\200b";content:var(--tw-content)}@utility no-touch-action{touch-action:none}@utility bg-transparency-squares{@apply bg-squares-gradient square-light-white square-dark-neutral-300 square-size-6}@utility link-like{@apply cursor-pointer hover:text-accent-500}
1
+ @custom-variant dark (&:where(.dark, .dark *));@utility focus-outline-within{@apply outlined-within:outline-2 outlined-within:outline-accent-500 outlined-within:outline-offset-2}@utility focus-outline{@apply outlined:outline-2 outlined:outline-accent-500 outlined:outline-offset-2}@utility focus-outline-no-offset{@apply outlined:outline-2 outlined:outline-accent-500}@utility focus-outline-hidden{@apply outlined:outline-none}@utility bg-squares-gradient{--_square:var(--squareSize,5px);--_double_square:calc(var(--_square)*2);--_light_square:var(--lightSquare,var(--color-white));--_dark_square:var(--darkSquare,var(--color-black));background-color:var(--_light_square);background:repeating-conic-gradient(var(--_dark_square) 0 25%,var(--_light_square) 0 50%) 50% /var(--_double_square) var(--_double_square)}@utility square-light-*{--lightSquare:--value(--color- *)}@utility square-dark-*{--darkSquare:--value(--color- *)}@utility square-size-*{--squareSize:calc(--value(integer) * 1px)}@utility bg-bars-gradient{--_bg_color:var(--bars-bg-color,var(--color-accent-700));--_fg_color:var(--bars-fg-color,var(--color-accent-800));--_angle:var(--bars-angle,45deg);--_fg_width:var(--bars-fg-width,50%);--_bg_width:calc(100% - var(--_fg_width));background-color:var(--_bg_color);--_pos_1:calc(var(--_bg_width)/2);--_pos_2:calc(var(--_bg_width)/2 + var(--_fg_width)/2);--_pos_3:calc(var(--_bg_width) + var(--_fg_width)/2);background-image:repeating-linear-gradient(var(--_angle),var(--_bg_color),var(--_bg_color) var(--_pos_1),var(--_fg_color) var(--_pos_1),var(--_fg_color) var(--_pos_2),var(--_bg_color) var(--_pos_2),var(--_bg_color) var(--_pos_3),var(--_fg_color) var(--_pos_3),var(--_fg_color))}@utility bars-angle-*{--bars-angle:var(--value(integer) * 1deg)}@utility bars-fg-*{--bars-fg-color:--value(--color-*)}@utility bars-bg-*{--bars-bg-color:--value(--color-*)}@utility bars-w-*{--bars-fg-width:calc(--value(integer) * 1%, 50%)}@utility scrollbar-hidden{-ms-overflow-style:none;scrollbar-width:none;&::-webkit-scrollbar{display:none}}@utility styled-scrollbar{--_scrollbar_width:var(--scrollbar-width,calc(var(--spacing)*3));--_scrollbar_border_width:var(--scrollbar-border-width,calc(var(--spacing)/2));--_scrollbar_color:var(--scrollbar-color,--alpha(var(--color-accent-500)/40%));--_scrollbar_hover_color:var(--scrollbar-hover-color,--alpha(var(--color-accent-500)/80%));--_scrollbar_bg_color:var(--scrollbar-bg-color,var(--color-bg));.dark &{--_scrollbar_bg_color:var(--scrollbar-bg-color,var(--color-fg))}&::-webkit-scrollbar{height:var(--_scrollbar_width);width:var(--_scrollbar_width)}&::-webkit-scrollbar-corner,&::-webkit-scrollbar-track{background-color:transparent}&::-webkit-scrollbar-thumb,&::-webkit-scrollbar-track{border-radius:var(--_scrollbar_width)}&::-webkit-scrollbar-thumb{background-color:var(--_scrollbar_color);border:var(--_scrollbar_border_width) solid var(--_scrollbar_bg_color)}&::-webkit-scrollbar-thumb:hover{cursor:pointer}&::-webkit-scrollbar-thumb:active,&::-webkit-scrollbar-thumb:hover{background-color:var(--_scrollbar_hover_color);border-radius:var(--_scrollbar_width)}}@utility styled-scrollbar-w-*{--scrollbar-width:--value(integer)}@utility styled-scrollbar-border-w-*{--scrollbar-border-width:--value(integer)}@utility styled-scrollbar-*{--scrollbar-color:--value(--color-*)}@utility styled-scrollbar-bg-*{--scrollbar-bg-color:--value(--color-*)}@utility styled-resizer{--_resizer_width:var(--resizer-width,8px);--_resizer_color:var(--resizer-color,var(--color-neutral-300));.dark &{--_resizer_color:var(--resizer-color,var(--color-neutral-700))}&::-webkit-resizer{border-bottom-color:var(--_resizer_color);border-left-color:transparent;border-right-color:var(--_resizer_color);border-style:solid;border-top-color:transparent;border-width:var(--_resizer_width)}}@utility styled-resizer-w-*{--resizer-width:--value(integer)}@utility styled-resizer-color-*{--resizer-color:--value(--color-*)}@utility content-vertical-holder{--tw-content:"\200b";content:var(--tw-content)}@utility no-touch-action{touch-action:none}@utility bg-transparency-squares{@apply bg-squares-gradient square-light-white square-dark-neutral-300 square-size-6}@utility link-like{@apply cursor-pointer hover:text-accent-500}
@@ -34,16 +34,16 @@ declare const _default: __VLS_WithSlots<import("vue").DefineComponent<Props & {
34
34
  }> & Readonly<{
35
35
  "onUpdate:modelValue"?: ((value: RgbaColor) => any) | undefined;
36
36
  onCancel?: (() => any) | undefined;
37
- onSave?: (() => any) | undefined;
38
37
  "onUpdate:tempValue"?: ((value: RgbaColor | undefined) => any) | undefined;
38
+ onSave?: (() => any) | undefined;
39
39
  }>, {
40
40
  border: boolean;
41
41
  allowAlpha: boolean;
42
+ copyTransform: (val: HsvaColor, stringVal: string) => any;
42
43
  stringPrecision: number;
43
44
  customRepresentation: {
44
45
  fromHsvaToString: (hsva: HsvaColor, includeAlpha: boolean) => string;
45
46
  };
46
- copyTransform: (val: HsvaColor, stringVal: string) => any;
47
47
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>, {
48
48
  default?: (props: {
49
49
  stringColor: any;
@@ -34,16 +34,16 @@ declare const _default: __VLS_WithSlots<import("vue").DefineComponent<Props & {
34
34
  }> & Readonly<{
35
35
  "onUpdate:modelValue"?: ((value: RgbaColor) => any) | undefined;
36
36
  onCancel?: (() => any) | undefined;
37
- onSave?: (() => any) | undefined;
38
37
  "onUpdate:tempValue"?: ((value: RgbaColor | undefined) => any) | undefined;
38
+ onSave?: (() => any) | undefined;
39
39
  }>, {
40
40
  border: boolean;
41
41
  allowAlpha: boolean;
42
+ copyTransform: (val: HsvaColor, stringVal: string) => any;
42
43
  stringPrecision: number;
43
44
  customRepresentation: {
44
45
  fromHsvaToString: (hsva: HsvaColor, includeAlpha: boolean) => string;
45
46
  };
46
- copyTransform: (val: HsvaColor, stringVal: string) => any;
47
47
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>, {
48
48
  default?: (props: {
49
49
  stringColor: any;
@@ -39,18 +39,18 @@ declare const __VLS_component: import("vue").DefineComponent<__VLS_PublicProps,
39
39
  }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
40
40
  "onUpdate:modelValue"?: ((value: RgbaColor) => any) | undefined;
41
41
  onCancel?: (() => any) | undefined;
42
- onSave?: ((val: RgbaColor) => any) | undefined;
43
42
  "onUpdate:tempValue"?: ((value: RgbaColor | undefined) => any) | undefined;
43
+ onSave?: ((val: RgbaColor) => any) | undefined;
44
44
  }>, {
45
45
  border: boolean;
46
- valid: boolean;
47
46
  allowAlpha: boolean;
47
+ copyTransform: (val: HsvaColor, stringVal: string) => any;
48
48
  stringPrecision: number;
49
49
  customRepresentation: {
50
50
  fromHsvaToString: (hsva: HsvaColor, includeAlpha: boolean) => string;
51
51
  fromStringToHsva?: (string: string) => HsvaColor | undefined;
52
52
  };
53
- copyTransform: (val: HsvaColor, stringVal: string) => any;
53
+ valid: boolean;
54
54
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
55
55
  declare const _default: __VLS_WithSlots<typeof __VLS_component, __VLS_Slots>;
56
56
  export default _default;
@@ -39,18 +39,18 @@ declare const __VLS_component: import("vue").DefineComponent<__VLS_PublicProps,
39
39
  }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
40
40
  "onUpdate:modelValue"?: ((value: RgbaColor) => any) | undefined;
41
41
  onCancel?: (() => any) | undefined;
42
- onSave?: ((val: RgbaColor) => any) | undefined;
43
42
  "onUpdate:tempValue"?: ((value: RgbaColor | undefined) => any) | undefined;
43
+ onSave?: ((val: RgbaColor) => any) | undefined;
44
44
  }>, {
45
45
  border: boolean;
46
- valid: boolean;
47
46
  allowAlpha: boolean;
47
+ copyTransform: (val: HsvaColor, stringVal: string) => any;
48
48
  stringPrecision: number;
49
49
  customRepresentation: {
50
50
  fromHsvaToString: (hsva: HsvaColor, includeAlpha: boolean) => string;
51
51
  fromStringToHsva?: (string: string) => HsvaColor | undefined;
52
52
  };
53
- copyTransform: (val: HsvaColor, stringVal: string) => any;
53
+ valid: boolean;
54
54
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
55
55
  declare const _default: __VLS_WithSlots<typeof __VLS_component, __VLS_Slots>;
56
56
  export default _default;
@@ -1,11 +1,13 @@
1
1
  import { type HTMLAttributes, type InputHTMLAttributes } from "vue";
2
2
  import type { FileInputError } from "../../types/index.js";
3
3
  import { type LinkableByIdProps, type TailwindClassProp, type WrapperProps } from "../shared/props.js";
4
- declare const _default: __VLS_WithSlots<import("vue").DefineComponent<Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
5
- input: (val: File[]) => any;
4
+ declare const _default: __VLS_WithSlots<import("vue").DefineComponent<Props, {
5
+ clearFiles: () => void;
6
+ }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
7
+ input: (val: File[], clearFiles: () => void) => any;
6
8
  errors: (val: FileInputError[]) => any;
7
9
  }, string, import("vue").PublicProps, Readonly<Props> & Readonly<{
8
- onInput?: ((val: File[]) => any) | undefined;
10
+ onInput?: ((val: File[], clearFiles: () => void) => any) | undefined;
9
11
  onErrors?: ((val: FileInputError[]) => any) | undefined;
10
12
  }>, {
11
13
  multiple: boolean;
@@ -2,30 +2,52 @@
2
2
  <!-- todo aria errors -->
3
3
  <div
4
4
  :class="twMerge(
5
- `file-input
5
+ `
6
+ file-input
6
7
  justify-center
7
8
  border-2
8
9
  border-dashed
9
10
  border-accent-500/80
10
11
  focus-outline-within
11
12
  transition-[border-color,box-shadow]
12
- ease-out`,
13
+ ease-out
14
+ hover:bg-accent-500/10
15
+ outlined-focus-within
16
+ `,
13
17
  compact && `rounded-sm`,
14
- !compact && `flex w-full flex-col items-center gap-2 rounded-xl p-2 `,
18
+ !compact && `
19
+ flex
20
+ w-full
21
+ flex-col
22
+ items-stretch
23
+ gap-2
24
+ rounded-xl
25
+ p-2
26
+ `,
15
27
  errors.length > 0 && errorFlashing && `border-danger-400`,
28
+ isHovered && `bg-accent-500/10`,
16
29
  $.wrapperAttrs.class
17
30
  )"
18
31
  v-bind="{ ...$.wrapperAttrs, class: void 0 }"
32
+ @drop="onDrop"
33
+ @dragover.prevent="isHovered = true"
34
+ @dragleave="isHovered = false"
19
35
  >
20
36
  <div
21
37
  :class="twMerge(
22
38
  `
23
- file-input--wrapper
24
- relative justify-center`,
39
+ file-input--wrapper
40
+ relative
41
+ justify-center
42
+ @container
43
+ `,
25
44
  compact && `flex gap-2`,
26
- !compact && `input-wrapper
27
- flex flex-col items-center
28
- `
45
+ !compact && `
46
+ file-input
47
+ flex
48
+ flex-col
49
+ items-center
50
+ `
29
51
  )"
30
52
  >
31
53
  <label
@@ -36,7 +58,9 @@
36
58
  flex
37
59
  gap-1
38
60
  items-center
61
+ justify-center
39
62
  whitespace-nowrap
63
+ max-w-full
40
64
  `)"
41
65
  >
42
66
  <slot
@@ -46,23 +70,37 @@
46
70
  <icon><i-fa6-solid-arrow-up-from-bracket/></icon>
47
71
  </slot>
48
72
  <slot name="label">
49
- {{
50
- compact ? multiple ? t("file-input.compact-choose-file-plural") : t("file-input.compact-choose-file") : multiple ? t("file-input.non-compact-choose-file-plural") : t("file-input.non-compact-choose-file")
51
- }}
73
+ <div class="text-ellipsis overflow-hidden shrink-1 hidden @min-[15ch]:block">
74
+ {{
75
+ compact ? multiple ? t("file-input.compact-choose-file-plural") : t("file-input.compact-choose-file") : multiple ? t("file-input.non-compact-choose-file-plural") : t("file-input.non-compact-choose-file")
76
+ }}
77
+ </div>
52
78
  </slot>
53
- <span
79
+ <div
54
80
  v-if="compact && multiple"
55
81
  class="file-input--label-count"
56
82
  >
57
83
  {{ ` (${files.length})` }}
58
- </span>
84
+ </div>
85
+ <div
86
+ v-if="compact && !multiple && files.length > 0"
87
+ class="file-input--label-name text-ellipsis overflow-hidden shrink-9999 hidden @3xs:block"
88
+ >
89
+ {{ ` (${files[0]?.file.name})` }}
90
+ </div>
91
+ <div
92
+ v-if="compact && !multiple && files.length > 0"
93
+ class="file-input--label-name text-ellipsis overflow-hidden shrink-9999 @3xs:hidden"
94
+ >
95
+ {{ ` (...)` }}
96
+ </div>
59
97
  </label>
60
98
  <label
61
99
  v-if="!compact && formats?.length > 0"
62
- class="file-input--formats-label flex flex-col items-center text-sm"
100
+ class="file-input--formats-label flex-col items-center text-sm max-w-full hidden @min-[15ch]:flex"
63
101
  >
64
- <slot name="formats">{{ t("file-input.accepted-formats") }}: </slot>
65
- <div class="file-input--formats-list">
102
+ <slot name="formats"><div class="text-ellipsis overflow-hidden max-w-full">{{ t("file-input.accepted-formats") }}:</div> </slot>
103
+ <div class="file-input--formats-list overflow-hidden text-ellipsis max-w-full">
66
104
  {{ extensions.join(", ") }}
67
105
  </div>
68
106
  </label>
@@ -70,14 +108,14 @@
70
108
  :id="id ?? fallbackId"
71
109
  :class="twMerge(
72
110
  `
73
- file-input--input
74
- absolute
75
- inset-0
76
- z-0
77
- cursor-pointer
78
- text-[0]
79
- opacity-0
80
- `,
111
+ file-input--input
112
+ absolute
113
+ inset-[calc(var(--spacing)*-2)]
114
+ cursor-pointer
115
+ z-0
116
+ text-[0]
117
+ opacity-0
118
+ `,
81
119
  $.inputAttrs?.class
82
120
  )"
83
121
  type="file"
@@ -94,7 +132,7 @@
94
132
  v-if="!compact && files.length > 0"
95
133
  :class="twMerge(
96
134
  `file-input--previews
97
- flex items-stretch justify-center gap-2 flex-wrap
135
+ flex items-stretch justify-center gap-4 flex-wrap
98
136
  `,
99
137
  multiple && `
100
138
  w-full
@@ -102,32 +140,47 @@
102
140
  $.previewsAttrs?.class
103
141
  )"
104
142
  >
105
- <div class="file-input--preview-spacer flex-1"/>
106
143
  <div
107
- class="file-input--preview-wrapper
144
+ class="
145
+ file-input--preview-wrapper
108
146
  z-1
109
147
  relative
110
148
  flex
111
149
  min-w-0
112
150
  max-w-[150px]
113
151
  flex-initial
114
- flex-wrap
152
+ flex-col
115
153
  items-center
116
- gap-2 rounded-sm border border-neutral-400
117
- shadow-xs
118
- shadow-neutral-800/20
154
+ gap-1
155
+ p-1
156
+ rounded-sm
157
+ border
158
+ border-neutral-300
159
+ dark:border-neutral-800
160
+ shadow-md
161
+ shadow-neutral-800/30
162
+ bg-neutral-100
163
+ dark:bg-neutral-900
164
+ [&:hover_.file-input--remove-button]:opacity-100
119
165
  "
120
166
  v-for="entry of files"
121
167
  :key="entry.file.name"
122
168
  >
123
- <div class="file-input--remove-button flex flex-initial basis-full justify-start">
169
+ <div class="flex flex-initial basis-full justify-start items-center max-w-full gap-2 px-1">
124
170
  <lib-button
125
171
  :border="false"
172
+ class="file-input--remove-button rounded-full p-0"
126
173
  :aria-label="`Remove file ${entry.file.name}`"
127
174
  @click="removeFile(entry)"
128
175
  >
129
176
  <icon><i-fa6-solid-xmark/></icon>
130
177
  </lib-button>
178
+ <div
179
+ class="file-input--preview-filename min-w-0 flex-1 basis-0 truncate break-all rounded-sm text-sm"
180
+ :title="entry.file.name"
181
+ >
182
+ {{ entry.file.name }}
183
+ </div>
131
184
  </div>
132
185
 
133
186
  <div class="file-input--preview flex flex-initial basis-full justify-center">
@@ -154,25 +207,7 @@
154
207
  <icon><i-fa6-regular-file class="text-4xl opacity-50"/></icon>
155
208
  </div>
156
209
  </div>
157
- <div
158
- class="
159
- file-input--preview-filename
160
- min-w-0
161
- flex-1
162
- basis-0
163
- truncate
164
- break-all
165
- rounded-sm
166
- p-1
167
- text-sm
168
- "
169
- :title="entry.file.name"
170
- >
171
- {{ entry.file.name }}
172
- </div>
173
210
  </div>
174
-
175
- <div class="flex-1"/>
176
211
  </div>
177
212
  </div>
178
213
  </template>
@@ -193,8 +228,13 @@ const el = ref(null);
193
228
  const files = shallowReactive([]);
194
229
  const errors = shallowReactive([]);
195
230
  const errorFlashing = ref(false);
231
+ const isHovered = ref(false);
232
+ function clearFiles() {
233
+ el.value.value = "";
234
+ files.splice(0, files.length);
235
+ }
196
236
  watch(files, () => {
197
- emits("input", files.map((entry) => entry.file));
237
+ emits("input", files.map((entry) => entry.file), clearFiles);
198
238
  });
199
239
  watch(errors, () => {
200
240
  if (errors.length > 0) {
@@ -202,7 +242,8 @@ watch(errors, () => {
202
242
  setTimeout(() => {
203
243
  errorFlashing.value = false;
204
244
  }, 500);
205
- emits("errors", errors);
245
+ emits("errors", [...errors]);
246
+ errors.splice(0, errors.length);
206
247
  }
207
248
  });
208
249
  defineOptions({
@@ -229,44 +270,60 @@ const removeFile = (entry) => {
229
270
  files.splice(index, 1);
230
271
  };
231
272
  const extensionsList = computed(() => extensions.value.join(", "));
232
- const inputFile = async (e) => {
273
+ function onDrop(e) {
274
+ if ("dataTransfer" in e && e.dataTransfer && e.dataTransfer.files && e.dataTransfer.files.length > 0) {
275
+ el.value.files = e.dataTransfer.files;
276
+ e.preventDefault();
277
+ isHovered.value = false;
278
+ return updateFiles(el.value.files);
279
+ }
280
+ return void 0;
281
+ }
282
+ async function inputFile(e) {
233
283
  e.preventDefault();
234
284
  if (el.value.files) {
235
- const errs = [];
236
- for (const file of el.value.files) {
237
- const isImg = file.type.startsWith("image");
238
- const byPassValidation = props.formats.length === 0;
239
- const isValidMimeType = mimeTypes.value.find((_) => _.endsWith("/*") ? file.type.startsWith(_.slice(0, -2)) : _ === file.type) !== void 0;
240
- const isValidExtension = extensions.value.find((_) => file.name.endsWith(_)) !== void 0;
241
- if (!byPassValidation && (!isValidMimeType || !isValidExtension)) {
242
- const extension = file.name.match(/.*(\..*)/)?.[1] ?? "Unknown";
243
- const type = file.type === "" ? "" : ` (${file.type})`;
244
- const message = `File type ${extension}${type} is not allowed. Allowed file types are: ${extensionsList.value}.`;
245
- const err = new Error(message);
246
- err.file = file;
247
- err.isValidExtension = isValidExtension;
248
- err.isValidMimeType = isValidMimeType;
249
- errs.push(err);
250
- continue;
251
- }
252
- if (errs.length > 0) continue;
253
- if (!files.find((_) => _.file === file)) {
254
- if (props.multiple || files.length < 1) {
255
- files.push({ file, isImg });
256
- } else {
257
- files.splice(0, files.length, { file, isImg });
258
- }
259
- }
285
+ return updateFiles(el.value.files);
286
+ }
287
+ return void 0;
288
+ }
289
+ function updateFiles(filesList) {
290
+ const errs = [];
291
+ for (const file of filesList) {
292
+ const isImg = file.type.startsWith("image");
293
+ const byPassValidation = props.formats.length === 0;
294
+ const isValidMimeType = mimeTypes.value.find((_) => _.endsWith("/*") ? file.type.startsWith(_.slice(0, -2)) : _ === file.type) !== void 0;
295
+ const isValidExtension = extensions.value.find((_) => file.name.endsWith(_)) !== void 0;
296
+ if (!byPassValidation && (!isValidMimeType || !isValidExtension)) {
297
+ const extension = file.name.match(/.*(\..*)/)?.[1] ?? "Unknown";
298
+ const type = file.type === "" ? "" : ` (${file.type})`;
299
+ const message = `File type ${extension}${type} is not allowed. Allowed file types are: ${extensionsList.value}.`;
300
+ const err = new Error(message);
301
+ err.file = file;
302
+ err.isValidExtension = isValidExtension;
303
+ err.isValidMimeType = isValidMimeType;
304
+ errs.push(err);
305
+ continue;
260
306
  }
261
- if (errs.length > 0) {
262
- errors.splice(0, errors.length, ...errs);
263
- return false;
264
- } else if (errors.length > 0) {
265
- errors.splice(0, errors.length);
307
+ if (errs.length > 0) continue;
308
+ if (!files.find((_) => _.file === file)) {
309
+ if (props.multiple || files.length < 1) {
310
+ files.push({ file, isImg });
311
+ } else {
312
+ files.splice(0, files.length, { file, isImg });
313
+ }
266
314
  }
267
315
  }
316
+ if (errs.length > 0) {
317
+ errors.splice(0, errors.length, ...errs);
318
+ return false;
319
+ } else if (errors.length > 0) {
320
+ errors.splice(0, errors.length);
321
+ }
268
322
  return void 0;
269
- };
323
+ }
324
+ defineExpose({
325
+ clearFiles
326
+ });
270
327
  </script>
271
328
 
272
329
  <script>
@@ -1,11 +1,13 @@
1
1
  import { type HTMLAttributes, type InputHTMLAttributes } from "vue";
2
2
  import type { FileInputError } from "../../types/index.js";
3
3
  import { type LinkableByIdProps, type TailwindClassProp, type WrapperProps } from "../shared/props.js";
4
- declare const _default: __VLS_WithSlots<import("vue").DefineComponent<Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
5
- input: (val: File[]) => any;
4
+ declare const _default: __VLS_WithSlots<import("vue").DefineComponent<Props, {
5
+ clearFiles: () => void;
6
+ }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
7
+ input: (val: File[], clearFiles: () => void) => any;
6
8
  errors: (val: FileInputError[]) => any;
7
9
  }, string, import("vue").PublicProps, Readonly<Props> & Readonly<{
8
- onInput?: ((val: File[]) => any) | undefined;
10
+ onInput?: ((val: File[], clearFiles: () => void) => any) | undefined;
9
11
  onErrors?: ((val: FileInputError[]) => any) | undefined;
10
12
  }>, {
11
13
  multiple: boolean;
@@ -110,8 +110,8 @@ declare const _default: __VLS_WithSlots<import("vue").DefineComponent<Props & {
110
110
  blur: (val: FocusEvent) => any;
111
111
  input: (val: InputEvent) => any;
112
112
  submit: (val: string, suggestion?: any) => any;
113
- keydown: (val: KeyboardEvent) => any;
114
113
  focus: (val: FocusEvent) => any;
114
+ keydown: (val: KeyboardEvent) => any;
115
115
  indicatorClick: (val: MouseEvent) => any;
116
116
  }, string, import("vue").PublicProps, Readonly<Props & {
117
117
  values?: string[] | undefined;
@@ -122,10 +122,10 @@ declare const _default: __VLS_WithSlots<import("vue").DefineComponent<Props & {
122
122
  onInput?: ((val: InputEvent) => any) | undefined;
123
123
  onSubmit?: ((val: string, suggestion?: any) => any) | undefined;
124
124
  "onUpdate:modelValue"?: ((value: string) => any) | undefined;
125
+ onFocus?: ((val: FocusEvent) => any) | undefined;
126
+ onKeydown?: ((val: KeyboardEvent) => any) | undefined;
125
127
  "onUpdate:inputValue"?: ((value: string) => any) | undefined;
126
128
  "onUpdate:values"?: ((value: string[] | undefined) => any) | undefined;
127
- onKeydown?: ((val: KeyboardEvent) => any) | undefined;
128
- onFocus?: ((val: FocusEvent) => any) | undefined;
129
129
  onIndicatorClick?: ((val: MouseEvent) => any) | undefined;
130
130
  }>, {
131
131
  disabled: boolean;
@@ -110,8 +110,8 @@ declare const _default: __VLS_WithSlots<import("vue").DefineComponent<Props & {
110
110
  blur: (val: FocusEvent) => any;
111
111
  input: (val: InputEvent) => any;
112
112
  submit: (val: string, suggestion?: any) => any;
113
- keydown: (val: KeyboardEvent) => any;
114
113
  focus: (val: FocusEvent) => any;
114
+ keydown: (val: KeyboardEvent) => any;
115
115
  indicatorClick: (val: MouseEvent) => any;
116
116
  }, string, import("vue").PublicProps, Readonly<Props & {
117
117
  values?: string[] | undefined;
@@ -122,10 +122,10 @@ declare const _default: __VLS_WithSlots<import("vue").DefineComponent<Props & {
122
122
  onInput?: ((val: InputEvent) => any) | undefined;
123
123
  onSubmit?: ((val: string, suggestion?: any) => any) | undefined;
124
124
  "onUpdate:modelValue"?: ((value: string) => any) | undefined;
125
+ onFocus?: ((val: FocusEvent) => any) | undefined;
126
+ onKeydown?: ((val: KeyboardEvent) => any) | undefined;
125
127
  "onUpdate:inputValue"?: ((value: string) => any) | undefined;
126
128
  "onUpdate:values"?: ((value: string[] | undefined) => any) | undefined;
127
- onKeydown?: ((val: KeyboardEvent) => any) | undefined;
128
- onFocus?: ((val: FocusEvent) => any) | undefined;
129
129
  onIndicatorClick?: ((val: MouseEvent) => any) | undefined;
130
130
  }>, {
131
131
  disabled: boolean;
@@ -92,7 +92,7 @@
92
92
  </div>
93
93
  </div>
94
94
  <slot
95
- v-if="notification.message"
95
+ v-if="notification.message && !notification.component"
96
96
  name="message"
97
97
  v-bind="setSlotVar('message', {
98
98
  class: `
@@ -112,6 +112,21 @@
112
112
  {{ notification.message }}
113
113
  </div>
114
114
  </slot>
115
+ <Component
116
+ v-if="notification.component"
117
+ :is="notification.component"
118
+ v-bind="{
119
+ message: notification.message,
120
+ messageClasses: `
121
+ notification--message
122
+ whitespace-pre-wrap
123
+ text-neutral-800
124
+ dark:text-neutral-200
125
+ mb-1
126
+ `,
127
+ ...notification.componentProps ?? {}
128
+ }"
129
+ />
115
130
  <div class="notification--footer flex items-end justify-between">
116
131
  <div
117
132
  v-if="notification.code"
@@ -0,0 +1,6 @@
1
+ import type { CustomNotificationComponentProps } from "../../types/index.js";
2
+ type __VLS_Props = CustomNotificationComponentProps & {
3
+ customProp: string;
4
+ };
5
+ declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
6
+ export default _default;
@@ -0,0 +1,29 @@
1
+ <template>
2
+ <div class="border-2 border-accent-500 p-1 px-2 rounded-sm bg-accent-500/10">
3
+ <div class="text-lg">
4
+ Custom Message Component
5
+ </div>
6
+ <div class="font-bold">
7
+ Original message:
8
+ </div>
9
+ <div
10
+ :class="props.messageClasses"
11
+ >
12
+ {{ props.message }}
13
+ </div>
14
+ <div class="font-bold">
15
+ Custom Prop:
16
+ </div>
17
+ <div>
18
+ {{ props.customProp }}
19
+ </div>
20
+ </div>
21
+ </template>
22
+
23
+ <script setup>
24
+ const props = defineProps({
25
+ message: { type: String, required: true },
26
+ messageClasses: { type: String, required: false },
27
+ customProp: { type: String, required: true }
28
+ });
29
+ </script>
@@ -0,0 +1,6 @@
1
+ import type { CustomNotificationComponentProps } from "../../types/index.js";
2
+ type __VLS_Props = CustomNotificationComponentProps & {
3
+ customProp: string;
4
+ };
5
+ declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
6
+ export default _default;
@@ -22,12 +22,12 @@ declare const _default: __VLS_WithSlots<import("vue").DefineComponent<Props & {
22
22
  "onUpdate:modelValue"?: ((value: boolean) => any) | undefined;
23
23
  onClose?: (() => any) | undefined;
24
24
  }>, {
25
- canClose: boolean;
26
25
  useDialogForBackdrop: false;
27
26
  useBackdrop: boolean;
28
27
  preferredHorizontal: ("center" | "right" | "left" | "either" | "center-screen" | "right-most" | "left-most" | "center-most")[] | import("../../main.lib.js").PopupPositioner;
29
28
  preferredVertical: ("top" | "bottom" | "center" | "either" | "center-screen" | "top-most" | "bottom-most" | "center-most")[] | import("../../main.lib.js").PopupPositioner;
30
29
  avoidRepositioning: boolean;
30
+ canClose: boolean;
31
31
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>, {
32
32
  button?: (props: {
33
33
  extractEl: (_: any) => any;
@@ -22,12 +22,12 @@ declare const _default: __VLS_WithSlots<import("vue").DefineComponent<Props & {
22
22
  "onUpdate:modelValue"?: ((value: boolean) => any) | undefined;
23
23
  onClose?: (() => any) | undefined;
24
24
  }>, {
25
- canClose: boolean;
26
25
  useDialogForBackdrop: false;
27
26
  useBackdrop: boolean;
28
27
  preferredHorizontal: ("center" | "right" | "left" | "either" | "center-screen" | "right-most" | "left-most" | "center-most")[] | import("../../main.lib.js").PopupPositioner;
29
28
  preferredVertical: ("top" | "bottom" | "center" | "either" | "center-screen" | "top-most" | "bottom-most" | "center-most")[] | import("../../main.lib.js").PopupPositioner;
30
29
  avoidRepositioning: boolean;
30
+ canClose: boolean;
31
31
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>, {
32
32
  button?: (props: {
33
33
  extractEl: (_: any) => any;