@mouseless/baked 1.5.2 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/module.mjs +2 -1
  3. package/dist/runtime/components/AwaitLoading.d.ts +7 -4
  4. package/dist/runtime/components/AwaitLoading.js +39 -11
  5. package/dist/runtime/components/Bake.d.vue.ts +1 -3
  6. package/dist/runtime/components/Bake.vue +100 -59
  7. package/dist/runtime/components/Bake.vue.d.ts +1 -3
  8. package/dist/runtime/components/DataContainer.vue +4 -7
  9. package/dist/runtime/components/DataPanel.vue +5 -7
  10. package/dist/runtime/components/DataTable.vue +12 -3
  11. package/dist/runtime/components/ErrorPage.vue +7 -6
  12. package/dist/runtime/components/ErrorPopover.d.vue.ts +15 -0
  13. package/dist/runtime/components/ErrorPopover.vue +22 -0
  14. package/dist/runtime/components/ErrorPopover.vue.d.ts +15 -0
  15. package/dist/runtime/components/FormPage.vue +36 -9
  16. package/dist/runtime/components/InlineError.d.vue.ts +15 -0
  17. package/dist/runtime/components/InlineError.vue +27 -0
  18. package/dist/runtime/components/InlineError.vue.d.ts +15 -0
  19. package/dist/runtime/components/Input.vue +8 -1
  20. package/dist/runtime/components/InputNumber.vue +16 -16
  21. package/dist/runtime/components/InputText.vue +15 -15
  22. package/dist/runtime/components/Inputs.vue +17 -1
  23. package/dist/runtime/components/Labeler.d.vue.ts +9 -25
  24. package/dist/runtime/components/Labeler.vue +31 -21
  25. package/dist/runtime/components/Labeler.vue.d.ts +9 -25
  26. package/dist/runtime/components/Layout.vue +5 -1
  27. package/dist/runtime/components/LoadingError.d.vue.ts +13 -0
  28. package/dist/runtime/components/LoadingError.vue +27 -0
  29. package/dist/runtime/components/LoadingError.vue.d.ts +13 -0
  30. package/dist/runtime/components/Message.d.vue.ts +8 -1
  31. package/dist/runtime/components/Message.vue +48 -17
  32. package/dist/runtime/components/Message.vue.d.ts +8 -1
  33. package/dist/runtime/components/NavLink.vue +1 -1
  34. package/dist/runtime/components/Paginator.vue +1 -3
  35. package/dist/runtime/components/Select.vue +32 -31
  36. package/dist/runtime/components/SelectButton.vue +38 -32
  37. package/dist/runtime/components/SimpleForm.vue +45 -11
  38. package/dist/runtime/components/TabbedPage.vue +5 -7
  39. package/dist/runtime/components/Text.vue +1 -1
  40. package/dist/runtime/components/Validation.d.vue.ts +16 -0
  41. package/dist/runtime/components/Validation.vue +33 -0
  42. package/dist/runtime/components/Validation.vue.d.ts +16 -0
  43. package/dist/runtime/composables/useBakeError.d.ts +13 -0
  44. package/dist/runtime/composables/useBakeError.js +51 -0
  45. package/dist/runtime/composables/useContext.d.ts +8 -0
  46. package/dist/runtime/composables/useContext.js +47 -2
  47. package/dist/runtime/composables/useDataMounter.d.ts +4 -2
  48. package/dist/runtime/composables/useDataMounter.js +15 -6
  49. package/dist/runtime/composables/useDefaultValidation.d.ts +4 -0
  50. package/dist/runtime/composables/useDefaultValidation.js +37 -0
  51. package/dist/runtime/composables/useValidation.d.ts +11 -0
  52. package/dist/runtime/composables/useValidation.js +48 -0
  53. package/dist/runtime/plugins/errorHandling.js +2 -2
  54. package/package.json +14 -14
@@ -5,33 +5,33 @@
5
5
  <Skeleton class="min-h-10" />
6
6
  </div>
7
7
  </template>
8
- <Labeler
9
- :label
10
- :path
11
- :mode="labelMode"
12
- :variant="labelVariant"
13
- >
14
- <Select
15
- v-bind="$attrs"
16
- v-model="selected"
17
- :input-id="path"
18
- :options="data"
19
- :placeholder="l(label)"
20
- :show-clear
21
- :filter
22
- :auto-filter-focus="filter"
23
- :filter-fields="[optionLabel]"
24
- reset-filter-on-hide
25
- class="w-full"
8
+ <Validation>
9
+ <Labeler
10
+ :label
11
+ :path
26
12
  >
27
- <template #value="slotProps">
28
- <span>{{ getValueLabel(slotProps) }}</span>
29
- </template>
30
- <template #option="slotProps">
31
- <span>{{ getOptionLabel(slotProps) }}</span>
32
- </template>
33
- </Select>
34
- </Labeler>
13
+ <Select
14
+ v-bind="$attrs"
15
+ v-model="selected"
16
+ :input-id="path"
17
+ :options="data"
18
+ :placeholder
19
+ :show-clear
20
+ :filter
21
+ :auto-filter-focus="filter"
22
+ :filter-fields="[optionLabel]"
23
+ reset-filter-on-hide
24
+ class="w-full"
25
+ >
26
+ <template #value="slotProps">
27
+ <span>{{ getValueLabel(slotProps) }}</span>
28
+ </template>
29
+ <template #option="slotProps">
30
+ <span>{{ getOptionLabel(slotProps) }}</span>
31
+ </template>
32
+ </Select>
33
+ </Labeler>
34
+ </Validation>
35
35
  </AwaitLoading>
36
36
  </template>
37
37
 
@@ -39,7 +39,7 @@
39
39
  import { ref, watch } from "vue";
40
40
  import { Select, Skeleton } from "primevue";
41
41
  import { useContext, useUiStates, useLocalization } from "#imports";
42
- import { AwaitLoading, Labeler } from "#components";
42
+ import { AwaitLoading, Labeler, Validation } from "#components";
43
43
  const context = useContext();
44
44
  const { localize: l } = useLocalization();
45
45
  const { value: { selectStates } } = useUiStates();
@@ -48,9 +48,10 @@ const { schema, data } = defineProps({
48
48
  data: { type: null, required: true }
49
49
  });
50
50
  const model = defineModel({ type: null, required: true });
51
- const { filter, label, labelMode, labelVariant, localizeLabel, optionLabel, optionValue, showClear, stateful, targetProp } = schema;
51
+ const { filter, label, localizeOptionLabels, optionLabel, optionValue, showClear, stateful, targetProp } = schema;
52
52
  const path = context.injectPath();
53
53
  const selected = ref();
54
+ const placeholder = label?.text ? l(label.text) : null;
54
55
  watch(
55
56
  [() => data, getModel],
56
57
  ([_data, _model]) => {
@@ -65,11 +66,11 @@ watch(
65
66
  watch(selected, (newSelected) => setModel(newSelected));
66
67
  function getOptionLabel(slotProps) {
67
68
  const result = slotProps.option[optionLabel] ?? slotProps.option;
68
- return localizeLabel ? l(result) : result;
69
+ return localizeOptionLabels ? l(result) : result;
69
70
  }
70
71
  function getValueLabel(slotProps) {
71
- const result = slotProps.value?.[optionLabel] ?? slotProps.value ?? label;
72
- return (localizeLabel ? l(result) : result) ?? "\xA0";
72
+ const result = slotProps.value?.[optionLabel] ?? slotProps.value ?? placeholder;
73
+ return (localizeOptionLabels ? l(result) : result) ?? "\xA0";
73
74
  }
74
75
  function getModel() {
75
76
  return targetProp ? model.value?.[targetProp] : model.value;
@@ -5,35 +5,42 @@
5
5
  <Skeleton class="min-h-10" />
6
6
  </div>
7
7
  </template>
8
- <Labeler
9
- :label
10
- :path
11
- :mode="labelMode == 'ifta' ? labelMode : null"
12
- :dt="{
13
- colorScheme: {
14
- light: {
15
- top: '-1rem',
16
- },
17
- dark: {
18
- top: '-1rem'
8
+ <Validation>
9
+ <Labeler
10
+ :label="{
11
+ ...label,
12
+ mode: label?.mode === 'ifta' ? label.mode : null,
13
+ text: label?.mode === 'ifta' ? label.text : null
14
+ }"
15
+ :path
16
+ :dt="{
17
+ colorScheme: {
18
+ light: {
19
+ top: '-1rem',
20
+ },
21
+ dark: {
22
+ top: '-1rem'
23
+ }
19
24
  }
20
- }
21
- }"
22
- >
23
- <SelectButton
24
- v-if="data"
25
- v-model="selected"
26
- :options="data"
27
- :allow-empty
28
- :data-key="optionValue"
29
- :option-label
30
- :pt="{ pcToggleButton: { root: { class: 'text-[length:inherit]' } } }"
25
+ }"
31
26
  >
32
- <template #option="slotProps">
33
- <span>{{ getOptionLabel(slotProps) }}</span>
34
- </template>
35
- </SelectButton>
36
- </Labeler>
27
+ <SelectButton
28
+ v-if="data"
29
+ v-bind="$attrs"
30
+ v-model="selected"
31
+ :options="data"
32
+ :allow-empty
33
+ :data-key="optionValue"
34
+ :option-label
35
+ class="!w-auto"
36
+ pt:pc-toggle-button:root="text-[length:inherit]"
37
+ >
38
+ <template #option="slotProps">
39
+ <span>{{ getOptionLabel(slotProps) }}</span>
40
+ </template>
41
+ </SelectButton>
42
+ </Labeler>
43
+ </Validation>
37
44
  </AwaitLoading>
38
45
  </template>
39
46
 
@@ -41,7 +48,7 @@
41
48
  import { ref, watch } from "vue";
42
49
  import { SelectButton, Skeleton } from "primevue";
43
50
  import { useContext, useLocalization, useUiStates } from "#imports";
44
- import { AwaitLoading, Labeler } from "#components";
51
+ import { AwaitLoading, Labeler, Validation } from "#components";
45
52
  const context = useContext();
46
53
  const { localize: l } = useLocalization();
47
54
  const { value: { selectButtonStates } } = useUiStates();
@@ -53,8 +60,7 @@ const model = defineModel({ type: null, required: true });
53
60
  const {
54
61
  allowEmpty = false,
55
62
  label,
56
- labelMode,
57
- localizeLabel,
63
+ localizeOptionLabels,
58
64
  optionLabel,
59
65
  optionValue,
60
66
  stateful,
@@ -76,7 +82,7 @@ watch(
76
82
  watch(selected, (newSelected) => setModel(newSelected));
77
83
  function getOptionLabel(slotProps) {
78
84
  const result = slotProps.option[optionLabel] ?? slotProps.option;
79
- return localizeLabel ? l(result) : result;
85
+ return localizeOptionLabels ? l(result) : result;
80
86
  }
81
87
  function getModel() {
82
88
  return targetProp ? model.value?.[targetProp] : model.value;
@@ -107,5 +113,5 @@ function setSelected(value) {
107
113
  </style>
108
114
 
109
115
  <style scoped>
110
- &:has(.p-iftalabel) .p-iftalabel{@apply mt-2}.grid .flex .b-component--SelectButton{@apply self-end}
116
+ .p-iftalabel:has(.p-selectbutton){@apply mt-4}
111
117
  </style>
@@ -1,6 +1,7 @@
1
1
  <template>
2
2
  <template v-if="dialogOptions">
3
3
  <Button
4
+ ref="submitRef"
4
5
  :schema="dialogOptions.open"
5
6
  v-bind="$attrs"
6
7
  @click="visible = true"
@@ -47,6 +48,9 @@
47
48
  />
48
49
  </template>
49
50
  </Dialog>
51
+ <ErrorPopover ref="errorPopoverRef">
52
+ <InlineError :error />
53
+ </ErrorPopover>
50
54
  </template>
51
55
  <template v-else>
52
56
  <div
@@ -66,43 +70,73 @@
66
70
  />
67
71
  </div>
68
72
  <Button
73
+ ref="submitRef"
74
+ v-tooltip.left="{
75
+ disabled: !showValidationSummary,
76
+ value: messages,
77
+ pt: { text: 'text-sm' }
78
+ }"
69
79
  :schema="submit"
70
80
  :ready
71
- @submit="$emit('submit', formData)"
81
+ @submit="$emit('submit', model)"
72
82
  />
83
+ <ErrorPopover ref="errorPopoverRef">
84
+ <InlineError :error />
85
+ </ErrorPopover>
73
86
  </div>
74
87
  </template>
75
88
  </template>
76
89
 
77
90
  <script setup>
78
- import { ref } from "vue";
91
+ import { computed, ref, watch } from "vue";
79
92
  import { Dialog } from "primevue";
80
- import { useLocalization } from "#imports";
81
- import { Button, Inputs } from "#components";
93
+ import { useBakeError, useLocalization, useValidation } from "#imports";
94
+ import { Button, ErrorPopover, InlineError, Inputs } from "#components";
95
+ const { handle: handleError } = useBakeError();
82
96
  const { localize: l } = useLocalization();
97
+ const { validate } = useValidation();
83
98
  const { schema } = defineProps({
84
99
  schema: { type: null, required: true }
85
100
  });
86
101
  const emit = defineEmits(["submit"]);
87
- const { dialogOptions, inputs, submit, title } = schema;
88
- const formData = ref({});
89
- const ready = ref(inputs.length === 0);
102
+ const { dialogOptions, inputs, submit, title, validations = [], showValidationSummary = false } = schema;
103
+ const model = ref({});
104
+ const readyData = ref({});
90
105
  const submitted = ref(false);
91
106
  const visible = ref(false);
107
+ const submitRef = ref();
108
+ const errorPopoverRef = ref();
109
+ const { error } = handleError();
110
+ const ready = computed(() => Object.values(readyData.value).every((v) => v) && isValid.value);
111
+ const { isValid, messages } = validate({
112
+ inputs,
113
+ model,
114
+ composables: validations
115
+ });
116
+ watch(error, (newError) => {
117
+ if (newError) {
118
+ errorPopoverRef.value.show(submitRef);
119
+ } else {
120
+ errorPopoverRef.value.hide();
121
+ }
122
+ });
92
123
  function onReady(value) {
93
- ready.value = value;
124
+ readyData.value = value;
94
125
  }
95
126
  function onChanged({ values }) {
96
- formData.value = values;
127
+ model.value = values;
97
128
  }
98
129
  function execute() {
130
+ if (!ready.value) {
131
+ return;
132
+ }
99
133
  submitted.value = true;
100
134
  visible.value = false;
101
135
  }
102
136
  function emitSubmit() {
103
- if (submitted.value) {
137
+ if (submitted.value && ready.value) {
104
138
  submitted.value = false;
105
- emit("submit", formData.value);
139
+ emit("submit", model.value);
106
140
  }
107
141
  }
108
142
  </script>
@@ -40,11 +40,9 @@
40
40
  </Bake>
41
41
  <Message
42
42
  v-if="showRequiredMessage"
43
- severity="info"
44
- >
45
- <i class="pi pi-info-circle" />
46
- <span class="ml-3">{{ lc("Select required values to view this page") }}</span>
47
- </Message>
43
+ :schema="{ severity: 'info', icon: 'pi pi-info-circle' }"
44
+ :data="lc('Select required values to view this page')"
45
+ />
48
46
  <div
49
47
  v-if="ready"
50
48
  class="pt-4 flex flex-col gap-4 items-center"
@@ -70,9 +68,9 @@
70
68
 
71
69
  <script setup>
72
70
  import { computed, onBeforeUnmount, onMounted, reactive, ref } from "vue";
73
- import { Message, Tab, TabList, Tabs } from "primevue";
71
+ import { Tab, TabList, Tabs } from "primevue";
74
72
  import { useContext, useLocalization, useReactionHandler } from "#imports";
75
- import { Bake, Contents, DeferredTabContent, Inputs } from "#components";
73
+ import { Bake, Contents, DeferredTabContent, Inputs, Message } from "#components";
76
74
  const context = useContext();
77
75
  const { localize: l } = useLocalization();
78
76
  const { localize: lc } = useLocalization({ group: "TabbedPage" });
@@ -34,7 +34,7 @@ const tooltip = computed(() => ({
34
34
  disabled: !lengthIsExceeded.value,
35
35
  pt: {
36
36
  root: {
37
- style: maxLength ? `min-width: ${maxLength / 4}rem;` : ""
37
+ style: maxLength ? `min-width: ${maxLength / 2}rem;` : ""
38
38
  }
39
39
  }
40
40
  }));
@@ -0,0 +1,16 @@
1
+ declare const _default: typeof __VLS_export;
2
+ export default _default;
3
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
4
+ type __VLS_WithSlots<T, S> = T & (new () => {
5
+ $slots: S;
6
+ });
7
+ declare const __VLS_base: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
8
+ type __VLS_Slots = {
9
+ default?: ((props: {}) => any) | undefined;
10
+ } & {
11
+ default?: ((props: {}) => any) | undefined;
12
+ } & {
13
+ message?: ((props: {
14
+ validation: any;
15
+ }) => any) | undefined;
16
+ };
@@ -0,0 +1,33 @@
1
+ <template>
2
+ <template v-if="!validation">
3
+ <slot />
4
+ </template>
5
+ <div
6
+ v-else
7
+ class="b-Validation flex flex-col gap-2"
8
+ >
9
+ <slot />
10
+ <slot
11
+ name="message"
12
+ :validation
13
+ >
14
+ <Message
15
+ v-if="validation.message && validation.persist"
16
+ :schema="{
17
+ severity: validation.severity,
18
+ variant: 'simple',
19
+ size: 'small'
20
+ }"
21
+ :data="validation.message || ''"
22
+ class="ml-3"
23
+ />
24
+ </slot>
25
+ </div>
26
+ </template>
27
+
28
+ <script setup>
29
+ import { useContext } from "#imports";
30
+ import { Message } from "#components";
31
+ const context = useContext();
32
+ const validation = context.injectValidation();
33
+ </script>
@@ -0,0 +1,16 @@
1
+ declare const _default: typeof __VLS_export;
2
+ export default _default;
3
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
4
+ type __VLS_WithSlots<T, S> = T & (new () => {
5
+ $slots: S;
6
+ });
7
+ declare const __VLS_base: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
8
+ type __VLS_Slots = {
9
+ default?: ((props: {}) => any) | undefined;
10
+ } & {
11
+ default?: ((props: {}) => any) | undefined;
12
+ } & {
13
+ message?: ((props: {
14
+ validation: any;
15
+ }) => any) | undefined;
16
+ };
@@ -0,0 +1,13 @@
1
+ export default function _default(): {
2
+ handle: () => {
3
+ error: import("vue").ComputedRef<{
4
+ title: any;
5
+ detail: any;
6
+ } | null>;
7
+ rawError: any;
8
+ };
9
+ normalize: (error: any) => import("vue").ComputedRef<{
10
+ title: any;
11
+ detail: any;
12
+ } | null>;
13
+ };
@@ -0,0 +1,51 @@
1
+ import { computed, unref } from "vue";
2
+ import { useContext } from "#imports";
3
+
4
+ export default function() {
5
+ const context = useContext();
6
+
7
+ function handle() {
8
+ const data = context.injectError({ handle: true });
9
+
10
+ return NormalizedError(data);
11
+ }
12
+
13
+ function normalize(error) {
14
+ return NormalizedError(error).error;
15
+ }
16
+
17
+ return {
18
+ handle,
19
+ normalize
20
+ };
21
+ }
22
+
23
+ function NormalizedError(rawError) {
24
+ const error = computed(() => {
25
+ const e = unref(rawError);
26
+ if(!e) {
27
+ return null;
28
+ }
29
+
30
+ if(e.name === "FetchError") {
31
+ const messages = e.data?.errors
32
+ ? Object.values(e.data.errors).flat().join(" ")
33
+ : null;
34
+
35
+ return {
36
+ title: e.data?.title ?? e.statusCode ?? "ERROR",
37
+ detail: messages ?? e.data?.detail ?? e.message ?? e.cause ?? "An error occured."
38
+ };
39
+ }
40
+
41
+ return {
42
+ title: e.statusCode ?? e.status ?? "ERROR",
43
+ detail: e.message ?? e.cause ?? "An error occured."
44
+ };
45
+ });
46
+
47
+ return {
48
+ error,
49
+ rawError
50
+ };
51
+ }
@@ -7,6 +7,10 @@ export default function _default(): {
7
7
  };
8
8
  injectDataDescriptor: () => null;
9
9
  provideDataDescriptor: (value: any) => void;
10
+ injectError: ({ handle }?: {
11
+ handle?: boolean | undefined;
12
+ }) => import("vue").Ref<{}, {}>;
13
+ provideError: (error: any) => import("vue").Ref<boolean, boolean>;
10
14
  injectEvents: () => any;
11
15
  provideEvents: (value: any) => void;
12
16
  injectExecuting: () => import("vue").Ref<boolean, boolean>;
@@ -17,4 +21,8 @@ export default function _default(): {
17
21
  providePageContext: (value: any) => void;
18
22
  injectParentContext: () => null;
19
23
  provideParentContext: (value: any) => void;
24
+ injectValidations: () => import("vue").Ref<{}, {}>;
25
+ provideValidations: (value: any) => void;
26
+ injectValidation: () => null;
27
+ provideValidation: (value: any) => void;
20
28
  };
@@ -24,6 +24,29 @@ export default function() {
24
24
  return provide("__bake_data_descriptor", value);
25
25
  }
26
26
 
27
+ function injectError({ handle = false } = {}) {
28
+ const { error, handled } = inject("__bake_error", {
29
+ error: ref({}),
30
+ handled: ref(false)
31
+ });
32
+
33
+ if(handle) {
34
+ handled.value = true;
35
+ }
36
+
37
+ return error;
38
+ }
39
+
40
+ function provideError(error) {
41
+ const handled = ref(false);
42
+ provide("__bake_error", {
43
+ error,
44
+ handled
45
+ });
46
+
47
+ return handled;
48
+ }
49
+
27
50
  function injectEvents() {
28
51
  return inject("__bake_events");
29
52
  }
@@ -64,12 +87,30 @@ export default function() {
64
87
  provide("__bake_parent_context", value);
65
88
  }
66
89
 
90
+ function injectValidations() {
91
+ return inject("__bake_validations", ref({}));
92
+ }
93
+
94
+ function provideValidations(value) {
95
+ provide("__bake_validations", value);
96
+ }
97
+
98
+ function injectValidation() {
99
+ return inject("__bake_validation", null);
100
+ }
101
+
102
+ function provideValidation(value) {
103
+ provide("__bake_validation", value);
104
+ }
105
+
67
106
  return {
68
107
  injectPath,
69
108
  providePath,
70
109
  injectContextData,
71
110
  injectDataDescriptor,
72
111
  provideDataDescriptor,
112
+ injectError,
113
+ provideError,
73
114
  injectEvents,
74
115
  provideEvents,
75
116
  injectExecuting,
@@ -79,6 +120,10 @@ export default function() {
79
120
  injectPageContext,
80
121
  providePageContext,
81
122
  injectParentContext,
82
- provideParentContext
123
+ provideParentContext,
124
+ injectValidations,
125
+ provideValidations,
126
+ injectValidation,
127
+ provideValidation
83
128
  };
84
- }
129
+ }
@@ -1,5 +1,7 @@
1
- export default function _default(): {
2
- mount: (schema: any) => import("vue").Ref<null, null>;
1
+ export default function _default({ defaultInlineError }?: {}): {
2
+ mount: (schema: any, { inlineError }?: {
3
+ inlineError?: any;
4
+ }) => import("vue").Ref<null, null>;
3
5
  onAfterMount: (callback: any) => void;
4
6
  onBeforeMount: (callback: any) => void;
5
7
  };
@@ -1,10 +1,13 @@
1
1
  import { onMounted, ref } from "vue";
2
2
  import { useContext, useDataFetcher } from "#imports";
3
3
 
4
- export default function() {
4
+ export default function({ defaultInlineError } = {}) {
5
5
  const context = useContext();
6
6
  const dataFetcher = useDataFetcher();
7
+
7
8
  const contextData = context.injectContextData();
9
+ const error = context.injectError();
10
+
8
11
  const mounts = [];
9
12
  let onAfterMountCallback = null;
10
13
  let onBeforeMountCallback = null;
@@ -15,8 +18,14 @@ export default function() {
15
18
  }
16
19
 
17
20
  await Promise.all(
18
- mounts.map(async({ value, schema }) => {
19
- value.value = await dataFetcher.fetch({ data: schema, contextData });
21
+ mounts.map(async({ value, schema, inlineError }) => {
22
+ try {
23
+ value.value = await dataFetcher.fetch({ data: schema, contextData });
24
+ } catch (err) {
25
+ if(!inlineError || err.statusCode !== 400) { throw err; }
26
+
27
+ error.value = err;
28
+ }
20
29
  })
21
30
  );
22
31
 
@@ -25,7 +34,7 @@ export default function() {
25
34
  }
26
35
  });
27
36
 
28
- function mount(schema) {
37
+ function mount(schema, { inlineError = defaultInlineError } = {}) {
29
38
  const value = ref(null);
30
39
 
31
40
  if(!schema) {
@@ -36,7 +45,7 @@ export default function() {
36
45
 
37
46
  const shouldLoad = dataFetcher.shouldLoad(schema);
38
47
  if(shouldLoad) {
39
- mounts.push({ value, schema });
48
+ mounts.push({ value, schema, inlineError });
40
49
  }
41
50
 
42
51
  return value;
@@ -55,4 +64,4 @@ export default function() {
55
64
  onAfterMount,
56
65
  onBeforeMount
57
66
  };
58
- }
67
+ }
@@ -0,0 +1,4 @@
1
+ export default function useDefaultValidation({ inputs, model }: {
2
+ inputs: any;
3
+ model: any;
4
+ }): {};
@@ -0,0 +1,37 @@
1
+ import { useLocalization } from "#imports";
2
+
3
+ const capitalize = str => str.charAt(0).toUpperCase() + str.slice(1);
4
+
5
+ export default function useDefaultValidation({ inputs, model }) {
6
+ const { localize: lc } = useLocalization({ group: "useDefaultValidation" });
7
+ const { localize: l } = useLocalization();
8
+
9
+ const result = {};
10
+ const validation = {
11
+ valid: false,
12
+ persist: false,
13
+ severity: "error",
14
+ message: ""
15
+ };
16
+
17
+ inputs.map(input => {
18
+ const value = model?.[input.name];
19
+ const isEmpty = value === undefined || value === null || String(value).trim() === "";
20
+ const label = input.component?.schema?.label?.text || capitalize(input.name);
21
+
22
+ result[input.name] = {
23
+ ...validation,
24
+ required: !!input.required,
25
+ valid: !input.required || !isEmpty,
26
+ persist: !(input.required && isEmpty)
27
+ };
28
+
29
+ if(input.required && isEmpty) {
30
+ result[input.name].message = lc("{label} cannot be empty", { label: l(label) });
31
+ }
32
+
33
+ return input;
34
+ });
35
+
36
+ return result;
37
+ }