adminforth 1.2.18 → 1.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.
@@ -20,29 +20,31 @@
20
20
  <tr v-for="column, i in editableColumns" :key="column.name"
21
21
  class="bg-ligftForm dark:bg-gray-800 border-b dark:border-gray-700"
22
22
  >
23
- <td class="px-6 py-4 whitespace-nowrap "> <!--align-top-->
23
+ <td class="px-6 py-4 whitespace-nowrap flex items-center"> <!--align-top-->
24
24
  {{ column.label }}
25
- <span :data-tooltip-target="`tooltip-show-${i}`" class="relative inline-block">
25
+ <span :data-tooltip-target="`tooltip-show-${i}`" class="ml-1 relative inline-block">
26
26
  <IconExclamationCircleSolid v-if="column.required[mode]" class="w-4 h-4"
27
27
  :class="(columnError(column) && validating) ? 'text-red-500 dark:text-red-400' : 'text-gray-400 dark:text-gray-500'"
28
28
  />
29
29
  </span>
30
30
  <div :id="`tooltip-show-${i}`"
31
- role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-gray-900 rounded-lg shadow-sm opacity-0 tooltip dark:bg-gray-700">
31
+ role="tooltip"
32
+ class="ml-1 absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-gray-900 rounded-lg shadow-sm opacity-0 tooltip dark:bg-gray-700">
32
33
  Required field
33
34
  <div class="tooltip-arrow" data-popper-arrow></div>
34
35
  </div>
35
36
  </td>
36
37
  <td class="px-6 py-4 whitespace-nowrap whitespace-pre-wrap relative">
37
-
38
38
  <template v-if="column?.components?.[props.source]?.file">
39
- <component
40
- :is="getCustomComponent(column.components[props.source])"
41
- :column="column"
42
- :value="currentValues[column.name]"
43
- @update:value="setCurrentValue(column.name, $event)"
44
- :meta="column.components[props.source].meta"
45
- />
39
+ <component
40
+ :is="getCustomComponent(column.components[props.source])"
41
+ :column="column"
42
+ :value="currentValues[column.name]"
43
+ @update:value="setCurrentValue(column.name, $event)"
44
+ :meta="column.components[props.source].meta"
45
+ :record="props.record"
46
+ @update:inValidity="customComponentsInValidity[column.name] = $event"
47
+ />
46
48
  </template>
47
49
  <template v-else>
48
50
  <Dropdown
@@ -122,10 +124,11 @@
122
124
  <IconEyeSolid class="w-6 h-6 text-gray-400" v-if="!unmasked[column.name]" />
123
125
  <IconEyeSlashSolid class="w-6 h-6 text-gray-400" v-else />
124
126
  </button>
125
- <div v-if="columnError(column) && validating" class="mt-1 text-xs text-red-500 dark:text-red-400">{{ columnError(column) }}</div>
126
-
127
- <div v-if="column.editingNote && column.editingNote[mode]" class="mt-1 text-xs text-gray-400 dark:text-gray-500">{{ column.editingNote[mode] }}</div>
128
127
  </template>
128
+ <div v-if="columnError(column) && validating" class="mt-1 text-xs text-red-500 dark:text-red-400">{{ columnError(column) }}</div>
129
+
130
+ <div v-if="column.editingNote && column.editingNote[mode]" class="mt-1 text-xs text-gray-400 dark:text-gray-500">{{ column.editingNote[mode] }}</div>
131
+
129
132
  </td>
130
133
  </tr>
131
134
 
@@ -167,9 +170,15 @@ const emit = defineEmits(['update:record', 'update:isValid']);
167
170
 
168
171
  const currentValues = ref({});
169
172
 
173
+ const customComponentsInValidity = ref({});
174
+
170
175
 
171
176
  const columnError = (column) => {
172
177
  const val = computed(() => {
178
+ if (customComponentsInValidity.value[column.name]) {
179
+ return customComponentsInValidity.value[column.name];
180
+ }
181
+
173
182
  if ( column.required[mode.value] && (currentValues.value[column.name] === undefined || currentValues.value[column.name] === null || currentValues.value[column.name] === '') ) {
174
183
  return 'This field is required';
175
184
  }
@@ -182,14 +191,17 @@ const columnError = (column) => {
182
191
  }
183
192
  }
184
193
  if ( ['integer', 'decimal', 'float'].includes(column.type) ) {
185
- if ( column.minValue !== undefined && currentValues.value[column.name] < column.minValue ) {
194
+ if ( column.minValue !== undefined
195
+ && currentValues.value[column.name] !== null
196
+ && currentValues.value[column.name] < column.minValue
197
+ ) {
186
198
  return `This field must be greater than ${column.minValue}`;
187
199
  }
188
200
  if ( column.maxValue !== undefined && currentValues.value[column.name] > column.maxValue ) {
189
201
  return `This field must be less than ${column.maxValue}`;
190
202
  }
191
203
  }
192
- if ( column.validation && column.validation.length ){
204
+ if ( column.validation && column.validation.length ) {
193
205
  const validationArray = column.validation;
194
206
  for (let i = 0; i < validationArray.length; i++) {
195
207
  if (validationArray[i].regExp) {
@@ -1,7 +1,7 @@
1
1
  <template>
2
2
 
3
3
 
4
- <div id="toast-default" class="flex items-center w-full max-w-xs p-4 text-gray-500 bg-white rounded-lg shadow dark:text-gray-400 dark:bg-gray-800" role="alert">
4
+ <div id="toast-default" class="flex items-center w-full p-4 text-gray-500 bg-white rounded-lg shadow dark:text-gray-400 dark:bg-gray-800" role="alert">
5
5
  <div v-if="toast.variant == 'info'" class="inline-flex items-center justify-center flex-shrink-0 w-8 h-8 text-blue-500 bg-blue-100 rounded-lg dark:bg-blue-800 dark:text-blue-200">
6
6
  <svg class="w-4 h-4" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 18 20">
7
7
  <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15.147 15.085a7.159 7.159 0 0 1-6.189 3.307A6.713 6.713 0 0 1 3.1 15.444c-2.679-4.513.287-8.737.888-9.548A4.373 4.373 0 0 0 5 1.608c1.287.953 6.445 3.218 5.537 10.5 1.5-1.122 2.706-3.01 2.853-6.14 1.433 1.049 3.993 5.395 1.757 9.117Z"/>
@@ -26,7 +26,9 @@
26
26
  </svg>
27
27
  <span class="sr-only">Check icon</span>
28
28
  </div>
29
- <div class="ms-3 text-sm font-normal">{{toast.message}}</div>
29
+
30
+ <div class="ms-3 text-sm font-normal max-w-xs" v-if="toast.messageHtml" v-html="toast.messageHtml"></div>
31
+ <div class="ms-3 text-sm font-normal max-w-xs" v-else>{{toast.message}}</div>
30
32
  <button @click="closeToast" type="button" class="ms-auto -mx-1.5 -my-1.5 bg-white text-gray-400 hover:text-gray-900 rounded-lg focus:ring-2 focus:ring-gray-300 p-1.5 hover:bg-gray-100 inline-flex items-center justify-center h-8 w-8 dark:text-gray-500 dark:hover:text-white dark:bg-gray-800 dark:hover:bg-gray-700" >
31
33
  <svg class="w-3 h-3" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">
32
34
  <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6"/>
@@ -43,10 +45,13 @@ import { useToastStore } from '@/stores/toast';
43
45
  const toastStore = useToastStore();
44
46
  const emit = defineEmits(['close']);
45
47
  const props = defineProps<{
46
- toast:{message: string;
47
- variant: string;
48
- id: string;
49
- timeout?: number|'unlimited';}
48
+ toast: {
49
+ message?: string;
50
+ messageHtml?: string;
51
+ variant: string;
52
+ id: string;
53
+ timeout?: number|'unlimited';
54
+ }
50
55
  }>();
51
56
  function closeToast() {
52
57
  emit('close');
@@ -67,6 +67,7 @@ export class FrontendAPI implements FrontendAPIInterface {
67
67
  alert(params: AlertParams): void {
68
68
  this.toastStore.addToast({
69
69
  message: params.message,
70
+ messageHtml: params.messageHtml,
70
71
  variant: params.variant,
71
72
  timeout: params.timeout
72
73
  })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "adminforth",
3
- "version": "1.2.18",
3
+ "version": "1.2.19",
4
4
  "description": "OpenSource Vue3 powered forth-generation admin panel",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -20,29 +20,31 @@
20
20
  <tr v-for="column, i in editableColumns" :key="column.name"
21
21
  class="bg-ligftForm dark:bg-gray-800 border-b dark:border-gray-700"
22
22
  >
23
- <td class="px-6 py-4 whitespace-nowrap "> <!--align-top-->
23
+ <td class="px-6 py-4 whitespace-nowrap flex items-center"> <!--align-top-->
24
24
  {{ column.label }}
25
- <span :data-tooltip-target="`tooltip-show-${i}`" class="relative inline-block">
25
+ <span :data-tooltip-target="`tooltip-show-${i}`" class="ml-1 relative inline-block">
26
26
  <IconExclamationCircleSolid v-if="column.required[mode]" class="w-4 h-4"
27
27
  :class="(columnError(column) && validating) ? 'text-red-500 dark:text-red-400' : 'text-gray-400 dark:text-gray-500'"
28
28
  />
29
29
  </span>
30
30
  <div :id="`tooltip-show-${i}`"
31
- role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-gray-900 rounded-lg shadow-sm opacity-0 tooltip dark:bg-gray-700">
31
+ role="tooltip"
32
+ class="ml-1 absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-gray-900 rounded-lg shadow-sm opacity-0 tooltip dark:bg-gray-700">
32
33
  Required field
33
34
  <div class="tooltip-arrow" data-popper-arrow></div>
34
35
  </div>
35
36
  </td>
36
37
  <td class="px-6 py-4 whitespace-nowrap whitespace-pre-wrap relative">
37
-
38
38
  <template v-if="column?.components?.[props.source]?.file">
39
- <component
40
- :is="getCustomComponent(column.components[props.source])"
41
- :column="column"
42
- :value="currentValues[column.name]"
43
- @update:value="setCurrentValue(column.name, $event)"
44
- :meta="column.components[props.source].meta"
45
- />
39
+ <component
40
+ :is="getCustomComponent(column.components[props.source])"
41
+ :column="column"
42
+ :value="currentValues[column.name]"
43
+ @update:value="setCurrentValue(column.name, $event)"
44
+ :meta="column.components[props.source].meta"
45
+ :record="props.record"
46
+ @update:inValidity="customComponentsInValidity[column.name] = $event"
47
+ />
46
48
  </template>
47
49
  <template v-else>
48
50
  <Dropdown
@@ -122,10 +124,11 @@
122
124
  <IconEyeSolid class="w-6 h-6 text-gray-400" v-if="!unmasked[column.name]" />
123
125
  <IconEyeSlashSolid class="w-6 h-6 text-gray-400" v-else />
124
126
  </button>
125
- <div v-if="columnError(column) && validating" class="mt-1 text-xs text-red-500 dark:text-red-400">{{ columnError(column) }}</div>
126
-
127
- <div v-if="column.editingNote && column.editingNote[mode]" class="mt-1 text-xs text-gray-400 dark:text-gray-500">{{ column.editingNote[mode] }}</div>
128
127
  </template>
128
+ <div v-if="columnError(column) && validating" class="mt-1 text-xs text-red-500 dark:text-red-400">{{ columnError(column) }}</div>
129
+
130
+ <div v-if="column.editingNote && column.editingNote[mode]" class="mt-1 text-xs text-gray-400 dark:text-gray-500">{{ column.editingNote[mode] }}</div>
131
+
129
132
  </td>
130
133
  </tr>
131
134
 
@@ -167,9 +170,15 @@ const emit = defineEmits(['update:record', 'update:isValid']);
167
170
 
168
171
  const currentValues = ref({});
169
172
 
173
+ const customComponentsInValidity = ref({});
174
+
170
175
 
171
176
  const columnError = (column) => {
172
177
  const val = computed(() => {
178
+ if (customComponentsInValidity.value[column.name]) {
179
+ return customComponentsInValidity.value[column.name];
180
+ }
181
+
173
182
  if ( column.required[mode.value] && (currentValues.value[column.name] === undefined || currentValues.value[column.name] === null || currentValues.value[column.name] === '') ) {
174
183
  return 'This field is required';
175
184
  }
@@ -182,14 +191,17 @@ const columnError = (column) => {
182
191
  }
183
192
  }
184
193
  if ( ['integer', 'decimal', 'float'].includes(column.type) ) {
185
- if ( column.minValue !== undefined && currentValues.value[column.name] < column.minValue ) {
194
+ if ( column.minValue !== undefined
195
+ && currentValues.value[column.name] !== null
196
+ && currentValues.value[column.name] < column.minValue
197
+ ) {
186
198
  return `This field must be greater than ${column.minValue}`;
187
199
  }
188
200
  if ( column.maxValue !== undefined && currentValues.value[column.name] > column.maxValue ) {
189
201
  return `This field must be less than ${column.maxValue}`;
190
202
  }
191
203
  }
192
- if ( column.validation && column.validation.length ){
204
+ if ( column.validation && column.validation.length ) {
193
205
  const validationArray = column.validation;
194
206
  for (let i = 0; i < validationArray.length; i++) {
195
207
  if (validationArray[i].regExp) {
@@ -1,7 +1,7 @@
1
1
  <template>
2
2
 
3
3
 
4
- <div id="toast-default" class="flex items-center w-full max-w-xs p-4 text-gray-500 bg-white rounded-lg shadow dark:text-gray-400 dark:bg-gray-800" role="alert">
4
+ <div id="toast-default" class="flex items-center w-full p-4 text-gray-500 bg-white rounded-lg shadow dark:text-gray-400 dark:bg-gray-800" role="alert">
5
5
  <div v-if="toast.variant == 'info'" class="inline-flex items-center justify-center flex-shrink-0 w-8 h-8 text-blue-500 bg-blue-100 rounded-lg dark:bg-blue-800 dark:text-blue-200">
6
6
  <svg class="w-4 h-4" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 18 20">
7
7
  <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15.147 15.085a7.159 7.159 0 0 1-6.189 3.307A6.713 6.713 0 0 1 3.1 15.444c-2.679-4.513.287-8.737.888-9.548A4.373 4.373 0 0 0 5 1.608c1.287.953 6.445 3.218 5.537 10.5 1.5-1.122 2.706-3.01 2.853-6.14 1.433 1.049 3.993 5.395 1.757 9.117Z"/>
@@ -26,7 +26,9 @@
26
26
  </svg>
27
27
  <span class="sr-only">Check icon</span>
28
28
  </div>
29
- <div class="ms-3 text-sm font-normal">{{toast.message}}</div>
29
+
30
+ <div class="ms-3 text-sm font-normal max-w-xs" v-if="toast.messageHtml" v-html="toast.messageHtml"></div>
31
+ <div class="ms-3 text-sm font-normal max-w-xs" v-else>{{toast.message}}</div>
30
32
  <button @click="closeToast" type="button" class="ms-auto -mx-1.5 -my-1.5 bg-white text-gray-400 hover:text-gray-900 rounded-lg focus:ring-2 focus:ring-gray-300 p-1.5 hover:bg-gray-100 inline-flex items-center justify-center h-8 w-8 dark:text-gray-500 dark:hover:text-white dark:bg-gray-800 dark:hover:bg-gray-700" >
31
33
  <svg class="w-3 h-3" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">
32
34
  <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6"/>
@@ -43,10 +45,13 @@ import { useToastStore } from '@/stores/toast';
43
45
  const toastStore = useToastStore();
44
46
  const emit = defineEmits(['close']);
45
47
  const props = defineProps<{
46
- toast:{message: string;
47
- variant: string;
48
- id: string;
49
- timeout?: number|'unlimited';}
48
+ toast: {
49
+ message?: string;
50
+ messageHtml?: string;
51
+ variant: string;
52
+ id: string;
53
+ timeout?: number|'unlimited';
54
+ }
50
55
  }>();
51
56
  function closeToast() {
52
57
  emit('close');
@@ -67,6 +67,7 @@ export class FrontendAPI implements FrontendAPIInterface {
67
67
  alert(params: AlertParams): void {
68
68
  this.toastStore.addToast({
69
69
  message: params.message,
70
+ messageHtml: params.messageHtml,
70
71
  variant: params.variant,
71
72
  timeout: params.timeout
72
73
  })
@@ -88,6 +88,11 @@ export type AlertParams = {
88
88
  */
89
89
  message?: string;
90
90
 
91
+ /**
92
+ * The message to display in the alert as HTML (can be used instead of message)
93
+ */
94
+ messageHtml?: string;
95
+
91
96
  /**
92
97
  * The variant of the alert
93
98
  */