@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
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@mouseless/baked",
3
3
  "configKey": "baked",
4
- "version": "1.5.2",
4
+ "version": "1.6.0",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "unknown"
package/dist/module.mjs CHANGED
@@ -11,7 +11,7 @@ const module$1 = defineNuxtModule({
11
11
  defaults: {},
12
12
  moduleDependencies: {
13
13
  "@nuxtjs/i18n": {
14
- version: "10.3.0",
14
+ version: "10.4.0",
15
15
  defaults: {
16
16
  strategy: "no_prefix",
17
17
  detectBrowserLanguage: {
@@ -64,6 +64,7 @@ const module$1 = defineNuxtModule({
64
64
  "3xl": "1920px"
65
65
  };
66
66
  _nuxt.options.runtimeConfig.public.error = app?.error;
67
+ _nuxt.options.runtimeConfig.public.inlineError = app?.inlineError;
67
68
  _nuxt.options.runtimeConfig.public.primevue = _options.primevue;
68
69
  _nuxt.options.runtimeConfig.public.components = _options.components;
69
70
  _nuxt.options.runtimeConfig.public.composables = _options.composables;
@@ -2,13 +2,16 @@ declare namespace _default {
2
2
  namespace props {
3
3
  namespace skeleton {
4
4
  export let type: ObjectConstructor;
5
- export let required: boolean;
6
5
  function _default(): void;
7
6
  export { _default as default };
8
7
  }
8
+ namespace noError {
9
+ let type_1: BooleanConstructor;
10
+ export { type_1 as type };
11
+ let _default_1: boolean;
12
+ export { _default_1 as default };
13
+ }
9
14
  }
10
- function setup(): (props: any) => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
11
- [key: string]: any;
12
- }> | undefined;
15
+ function setup(props: any): () => any;
13
16
  }
14
17
  export default _default;
@@ -1,27 +1,55 @@
1
- import { h, useSlots } from "vue";
1
+ import { h, unref, useSlots } from "vue";
2
2
  import { Skeleton } from "primevue";
3
- import { useContext } from "#imports";
3
+ import { LoadingError } from "#components";
4
+ import { useBakeError, useContext } from "#imports";
4
5
 
5
6
  export default {
6
7
  props: {
7
- skeleton: { type: Object, required: false, default: () => { } }
8
+ skeleton: { type: Object, default: () => { } },
9
+ noError: { type: Boolean, default: false }
8
10
  },
9
- setup() {
11
+ setup(props) {
12
+ const { handle: handleError } = useBakeError();
10
13
  const context = useContext();
11
14
  const slots = useSlots();
12
15
 
13
16
  const loading = context.injectLoading();
14
17
 
15
- return props => {
18
+ const { rawError, error } = props.noError ? { } : handleError();
19
+
20
+ return () => {
16
21
  if(loading.value) {
17
- if(slots.loading) {
18
- return slots.loading()[0];
19
- }
22
+ return render(slots.loading, {
23
+ fallback: () => h(Skeleton, props.skeleton)
24
+ });
25
+ }
20
26
 
21
- return h(Skeleton, props.skeleton);
27
+ if(rawError?.value) {
28
+ return render(slots.error, {
29
+ props: {
30
+ error: unref(error),
31
+ rawError: unref(rawError)
32
+ },
33
+ fallback: () => h(LoadingError, {
34
+ style: props.skeleton,
35
+ error: unref(error)
36
+ })
37
+ });
22
38
  }
23
39
 
24
- return slots.default()[0];
40
+ return render(slots.default);
25
41
  };
26
42
  }
27
- };
43
+ };
44
+
45
+ function render(slot, { fallback, props } = {}) {
46
+ if(!slot) {
47
+ return fallback ? fallback() : null;
48
+ }
49
+
50
+ const children = slot(props);
51
+
52
+ return children.length > 1
53
+ ? h("div", children)
54
+ : children[0];
55
+ }
@@ -17,8 +17,7 @@ declare const __VLS_base: import("vue").DefineComponent<import("vue").ExtractPro
17
17
  type: import("vue").PropType<any>;
18
18
  };
19
19
  }>, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
20
- loaded: (...args: any[]) => void;
21
- "update:modelValue": (value: any) => void;
20
+ "update:modelValue": (value: any) => any;
22
21
  }, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
23
22
  name: {
24
23
  type: StringConstructor;
@@ -32,7 +31,6 @@ declare const __VLS_base: import("vue").DefineComponent<import("vue").ExtractPro
32
31
  type: import("vue").PropType<any>;
33
32
  };
34
33
  }>> & Readonly<{
35
- onLoaded?: ((...args: any[]) => any) | undefined;
36
34
  "onUpdate:modelValue"?: ((value: any) => any) | undefined;
37
35
  }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
38
36
  type __VLS_Slots = {
@@ -1,27 +1,49 @@
1
1
  <template>
2
- <component
3
- :is="componentTemplate"
4
- v-if="visible"
5
- :key="loading"
6
- :class="classes"
7
- v-bind="getComponentProps()"
8
- >
9
- <template
10
- v-for="(_, slotName) in $slots"
11
- #[slotName]="slotProps"
2
+ <template v-if="!error || errorHandled || errorCausedByAction">
3
+ <component
4
+ :is="component"
5
+ v-if="visible"
6
+ ref="componentRef"
7
+ :key="loading"
8
+ :class="classes"
9
+ v-bind="{
10
+ ...$attrs,
11
+ ...baseAttrs,
12
+ ...dataAttrs(),
13
+ ...modelAttrs()
14
+ }"
12
15
  >
13
- <slot
14
- :name="slotName"
15
- v-bind="slotProps ?? {}"
16
- />
17
- </template>
18
- </component>
16
+ <template
17
+ v-for="(_, slotName) in $slots"
18
+ #[slotName]="slotProps"
19
+ >
20
+ <slot
21
+ :name="slotName"
22
+ v-bind="slotProps ?? {}"
23
+ />
24
+ </template>
25
+ </component>
26
+ <ErrorPopover
27
+ v-if="descriptor.action && !errorHandled"
28
+ ref="errorPopoverRef"
29
+ >
30
+ <InlineError :error />
31
+ </ErrorPopover>
32
+ </template>
33
+ <InlineError
34
+ v-else
35
+ :class="$attrs.class"
36
+ :style="$attrs.style"
37
+ :error
38
+ />
19
39
  </template>
20
40
 
21
41
  <script setup>
22
- import { nextTick, onBeforeUnmount, onMounted, ref } from "vue";
23
- import { useActionExecuter, useComponentResolver, useContext, useDataFetcher, useFormat, useReactionHandler } from "#imports";
42
+ import { onBeforeUnmount, onMounted, ref, watch } from "vue";
43
+ import { useActionExecuter, useBakeError, useComponentResolver, useContext, useDataFetcher, useFormat, useReactionHandler } from "#imports";
44
+ import { ErrorPopover, InlineError } from "#components";
24
45
  const actionExecuter = useActionExecuter();
46
+ const { normalize: normalizeError } = useBakeError();
25
47
  const componentResolver = useComponentResolver();
26
48
  const context = useContext();
27
49
  const dataFetcher = useDataFetcher();
@@ -35,28 +57,66 @@ const model = defineModel({ type: null });
35
57
  const emit = defineEmits(["loaded"]);
36
58
  const parentPath = context.injectPath();
37
59
  const parentLoading = context.injectLoading();
38
- const path = parentPath && parentPath !== "" ? `${parentPath}/${name}` : name;
39
60
  const events = context.injectEvents();
40
61
  const contextData = context.injectContextData();
41
- const componentTemplate = componentResolver.resolve(descriptor.type, "MissingComponent");
42
- const componentProps = buildComponentProps();
62
+ const component = componentResolver.resolve(descriptor.type, "MissingComponent");
63
+ const componentRef = ref();
64
+ const path = parentPath && parentPath !== "" ? `${parentPath}/${name}` : name;
43
65
  const data = ref(dataFetcher.get({ data: descriptor.data, contextData }));
44
66
  const shouldLoad = !parentLoading.value && dataFetcher.shouldLoad(descriptor.data);
45
67
  const loading = ref(shouldLoad);
46
- let executing = null;
47
68
  const visible = ref(true);
48
69
  const classes = [`b-component--${descriptor.type}`, ...asClasses(name)];
49
- let reactions = null;
70
+ const baseAttrs = {};
71
+ const rawError = ref();
72
+ const errorHandled = context.provideError(rawError);
73
+ const error = normalizeError(rawError);
50
74
  context.providePath(path);
51
75
  context.provideDataDescriptor(descriptor.data);
52
76
  context.provideParentContext({ ...contextData.parent, data });
53
77
  if (shouldLoad) {
54
78
  context.provideLoading(loading);
55
79
  }
80
+ let errorPopoverRef = null;
81
+ let errorCausedByAction = null;
82
+ let executing = null;
56
83
  if (descriptor.action) {
84
+ errorPopoverRef = ref();
85
+ errorCausedByAction = ref(false);
57
86
  executing = ref(false);
58
87
  context.provideExecuting(executing);
88
+ watch([executing, error], ([newExecuting, newError], [oldExecuting, oldError]) => {
89
+ if (newExecuting) {
90
+ rawError.value = null;
91
+ errorCausedByAction.value = false;
92
+ } else if (oldExecuting && newError && !oldError) {
93
+ errorCausedByAction.value = true;
94
+ }
95
+ });
96
+ watch(error, (newError) => {
97
+ if (newError) {
98
+ errorPopoverRef.value?.show(componentRef);
99
+ } else {
100
+ errorPopoverRef.value?.hide();
101
+ }
102
+ });
103
+ }
104
+ if (descriptor.schema) {
105
+ baseAttrs.schema = descriptor.schema;
106
+ }
107
+ if (component.emits?.includes("submit")) {
108
+ baseAttrs.onSubmit = executeAction;
59
109
  }
110
+ let dataAttrs = () => ({});
111
+ if (descriptor.data) {
112
+ dataAttrs = () => ({ data: data.value });
113
+ }
114
+ let modelAttrs = () => ({});
115
+ if (component.props?.modelValue) {
116
+ modelAttrs = () => ({ modelValue: model.value, "onUpdate:modelValue": (value) => model.value = value });
117
+ watch(model, executeAction);
118
+ }
119
+ let reactions = null;
60
120
  if (descriptor.reactions) {
61
121
  reactions = reactionHandler.create(`${path}:bake`, {
62
122
  reload(success) {
@@ -83,45 +143,21 @@ onBeforeUnmount(() => {
83
143
  });
84
144
  async function load() {
85
145
  loading.value = true;
86
- data.value = await dataFetcher.fetch({
87
- data: descriptor.data,
88
- contextData
89
- });
90
- loading.value = false;
91
- emit("loaded");
92
- }
93
- function buildComponentProps() {
94
- const result = {};
95
- if (descriptor.schema) {
96
- result.schema = descriptor.schema;
97
- }
98
- if (componentTemplate.emits?.includes("submit")) {
99
- result.onSubmit = onModelUpdate;
100
- }
101
- if (componentTemplate.props?.modelValue) {
102
- result["onUpdate:modelValue"] = onModelUpdate;
103
- }
104
- return result;
105
- }
106
- let __BIND_COUNT__ = 0;
107
- function getComponentProps() {
108
- const result = { ...componentProps };
109
- if (descriptor.data) {
110
- result.data = data.value;
111
- }
112
- if (componentTemplate.props?.modelValue) {
113
- result.modelValue = model.value;
114
- if (__BIND_COUNT__ < 2) {
115
- __BIND_COUNT__++;
116
- nextTick(() => onModelUpdate(model.value));
146
+ try {
147
+ data.value = await dataFetcher.fetch({
148
+ data: descriptor.data,
149
+ contextData
150
+ });
151
+ } catch (err) {
152
+ if (err?.status !== 400) {
153
+ throw err;
117
154
  }
155
+ rawError.value = err;
118
156
  }
119
- return result;
157
+ loading.value = false;
158
+ emit("loaded");
120
159
  }
121
- async function onModelUpdate(newModel) {
122
- if (componentTemplate.props?.modelValue) {
123
- model.value = newModel;
124
- }
160
+ async function executeAction(newModel) {
125
161
  if (!descriptor.action) {
126
162
  return;
127
163
  }
@@ -132,6 +168,11 @@ async function onModelUpdate(newModel) {
132
168
  contextData: { ...contextData, model: newModel },
133
169
  events
134
170
  });
171
+ } catch (err) {
172
+ if (err?.status !== 400) {
173
+ throw err;
174
+ }
175
+ rawError.value = err;
135
176
  } finally {
136
177
  executing.value = false;
137
178
  }
@@ -17,8 +17,7 @@ declare const __VLS_base: import("vue").DefineComponent<import("vue").ExtractPro
17
17
  type: import("vue").PropType<any>;
18
18
  };
19
19
  }>, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
20
- loaded: (...args: any[]) => void;
21
- "update:modelValue": (value: any) => void;
20
+ "update:modelValue": (value: any) => any;
22
21
  }, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
23
22
  name: {
24
23
  type: StringConstructor;
@@ -32,7 +31,6 @@ declare const __VLS_base: import("vue").DefineComponent<import("vue").ExtractPro
32
31
  type: import("vue").PropType<any>;
33
32
  };
34
33
  }>> & Readonly<{
35
- onLoaded?: ((...args: any[]) => any) | undefined;
36
34
  "onUpdate:modelValue"?: ((value: any) => any) | undefined;
37
35
  }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
38
36
  type __VLS_Slots = {
@@ -33,20 +33,17 @@
33
33
  />
34
34
  <Message
35
35
  v-else-if="!ready"
36
- severity="info"
37
- >
38
- <i class="pi pi-info-circle" />
39
- <span class="ml-3">{{ lc("Select required values to view this data") }}</span>
40
- </Message>
36
+ :schema="{ severity: 'info', icon: 'pi pi-info-circle' }"
37
+ :data="lc('Select required values to view this data')"
38
+ />
41
39
  </div>
42
40
  </div>
43
41
  </template>
44
42
 
45
43
  <script setup>
46
44
  import { ref } from "vue";
47
- import { Message } from "primevue";
48
45
  import { useContext, useLocalization } from "#imports";
49
- import { Bake, Inputs } from "#components";
46
+ import { Bake, Inputs, Message } from "#components";
50
47
  const context = useContext();
51
48
  const { localize: lc } = useLocalization({ group: "DataContainer" });
52
49
  const { schema } = defineProps({
@@ -71,20 +71,18 @@
71
71
  />
72
72
  <Message
73
73
  v-else-if="!ready"
74
- severity="info"
75
- >
76
- <i class="pi pi-info-circle" />
77
- <span class="ml-3">{{ lc("Select required values to view this data") }}</span>
78
- </Message>
74
+ :schema="{ severity: 'info', icon: 'pi pi-info-circle' }"
75
+ :data="lc('Select required values to view this data')"
76
+ />
79
77
  </template>
80
78
  </Panel>
81
79
  </template>
82
80
 
83
81
  <script setup>
84
82
  import { computed, ref, useTemplateRef } from "vue";
85
- import { Message, Panel, Button } from "primevue";
83
+ import { Panel, Button } from "primevue";
86
84
  import { useBreakpoints, useContext, useDataMounter, useUiStates, useLocalization } from "#imports";
87
- import { Bake, Inputs, PersistentPopover } from "#components";
85
+ import { Bake, Inputs, PersistentPopover, Message } from "#components";
88
86
  const { value: { panelStates } } = useUiStates();
89
87
  const { isMd } = useBreakpoints();
90
88
  const context = useContext();
@@ -38,7 +38,10 @@
38
38
  :frozen="column.frozen"
39
39
  >
40
40
  <template #body="{ data: row, index }">
41
- <AwaitLoading :skeleton="{ class: 'min-h-5' }">
41
+ <AwaitLoading
42
+ :skeleton="{ class: 'min-h-5' }"
43
+ no-error
44
+ >
42
45
  <ProvideParentContext
43
46
  v-if="data"
44
47
  :data="row.$getRow()"
@@ -87,7 +90,10 @@
87
90
  v-if="actions"
88
91
  #body="{ data: row, index }"
89
92
  >
90
- <AwaitLoading :skeleton="{ class: 'min-h-5' }">
93
+ <AwaitLoading
94
+ :skeleton="{ class: 'min-h-5' }"
95
+ no-error
96
+ >
91
97
  <ProvideParentContext
92
98
  v-if="data"
93
99
  :data="row.$getRow()"
@@ -119,7 +125,10 @@
119
125
  :class="{ 'text-right': column.alignRight }"
120
126
  >
121
127
  <template #footer>
122
- <AwaitLoading :skeleton="{ class: 'min-h-5' }">
128
+ <AwaitLoading
129
+ :skeleton="{ class: 'min-h-5' }"
130
+ no-error
131
+ >
123
132
  <Bake
124
133
  v-if="data"
125
134
  :name="`rows/footer/${column.key}`"
@@ -40,17 +40,18 @@
40
40
  type="dashed"
41
41
  class="my-8"
42
42
  />
43
- <Message severity="warn">
44
- <i class="pi pi-exclamation-circle mr-2" />
45
- {{ l(footerInfo) }}
46
- </Message>
43
+ <Message
44
+ :schema="{ severity: 'warn', icon: 'pi pi-exclamation-circle' }"
45
+ :data="l(footerInfo)"
46
+ />
47
47
  </div>
48
48
  </template>
49
49
 
50
50
  <script setup>
51
51
  import { computed } from "vue";
52
- import { Divider, Message, Tag } from "primevue";
52
+ import { Divider, Tag } from "primevue";
53
53
  import { useLocalization } from "#imports";
54
+ import { Message } from "#components";
54
55
  const { localize: l } = useLocalization();
55
56
  const { schema, data } = defineProps({
56
57
  schema: { type: null, required: true },
@@ -63,7 +64,7 @@ const {
63
64
  safeLinksMessage = "Try the links from the menu below to view the page you want to access:"
64
65
  } = schema;
65
66
  const statusCode = computed(() => {
66
- const code = data.value?.data?.status ?? data.value?.statusCode ?? 999;
67
+ const code = !data ? 999 : data.status ?? data.statusCode ?? data.value?.status ?? data.value?.statusCode ?? 999;
67
68
  return code === 999 ? "APP" : code;
68
69
  });
69
70
  const errorInfo = computed(() => {
@@ -0,0 +1,15 @@
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<{}, {
8
+ show: typeof show;
9
+ hide: typeof hide;
10
+ }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
11
+ type __VLS_Slots = {
12
+ default?: ((props: {}) => any) | undefined;
13
+ };
14
+ declare function show(target: any): void;
15
+ declare function hide(): void;
@@ -0,0 +1,22 @@
1
+ <template>
2
+ <Popover
3
+ ref="popoverRef"
4
+ class="bg-transparent border-none before:content-none after:content-none"
5
+ pt:content="p-0"
6
+ >
7
+ <slot />
8
+ </Popover>
9
+ </template>
10
+
11
+ <script setup>
12
+ import { ref } from "vue";
13
+ import { Popover } from "primevue";
14
+ defineExpose({ show, hide });
15
+ const popoverRef = ref();
16
+ function show(target) {
17
+ popoverRef.value.show({ currentTarget: target.value.$el });
18
+ }
19
+ function hide() {
20
+ popoverRef.value.hide();
21
+ }
22
+ </script>
@@ -0,0 +1,15 @@
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<{}, {
8
+ show: typeof show;
9
+ hide: typeof hide;
10
+ }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
11
+ type __VLS_Slots = {
12
+ default?: ((props: {}) => any) | undefined;
13
+ };
14
+ declare function show(target: any): void;
15
+ declare function hide(): void;
@@ -9,10 +9,19 @@
9
9
  #actions
10
10
  >
11
11
  <Button
12
+ ref="submitRef"
13
+ v-tooltip.left="{
14
+ disabled: !showValidationSummary,
15
+ value: messages,
16
+ pt: { text: 'text-sm' }
17
+ }"
12
18
  :schema="submit"
13
19
  :ready
14
20
  @submit="onSubmit"
15
21
  />
22
+ <ErrorPopover ref="errorPopoverRef">
23
+ <InlineError :error />
24
+ </ErrorPopover>
16
25
  </template>
17
26
  </Bake>
18
27
  <div class="flex justify-center">
@@ -57,7 +66,7 @@
57
66
  <div
58
67
  v-for="inputGroup in inputGroups"
59
68
  :key="inputGroup.key"
60
- class="w-full flex gap-4 max-md:flex-col self-end"
69
+ class="w-full flex gap-4 max-md:flex-col"
61
70
  :class="{
62
71
  'col-span-2': inputGroup.wide,
63
72
  'reset-min-w': inputGroup.inputs.length > 1
@@ -79,18 +88,36 @@
79
88
  </template>
80
89
 
81
90
  <script setup>
82
- import { computed, ref } from "vue";
83
- import { useLocalization } from "#imports";
84
- import { Button, Contents, Inputs } from "#components";
91
+ import { computed, ref, watch } from "vue";
92
+ import { useBakeError, useLocalization, useValidation } from "#imports";
93
+ import { Bake, Button, Contents, ErrorPopover, InlineError, Inputs } from "#components";
94
+ const { handle: handleError } = useBakeError();
85
95
  const { localize: l } = useLocalization();
96
+ const { validate } = useValidation();
86
97
  const { schema } = defineProps({
87
98
  schema: { type: null, required: true }
88
99
  });
89
100
  const emit = defineEmits(["submit"]);
90
- const { title, submit, sections } = schema;
91
- const formData = ref({});
101
+ const { title, submit, sections, validations = [], showValidationSummary = true } = schema;
102
+ const model = ref({});
92
103
  const readyData = ref({});
93
- const ready = computed(() => Object.values(readyData.value).every((v) => v));
104
+ const inputs = ref(sections.flatMap((section) => section.inputGroups.flatMap((group) => group.inputs)));
105
+ const ready = computed(() => Object.values(readyData.value).every((v) => v) && isValid.value);
106
+ const submitRef = ref();
107
+ const errorPopoverRef = ref();
108
+ const { error } = handleError();
109
+ const { isValid, messages } = validate({
110
+ model,
111
+ inputs: inputs.value,
112
+ composables: validations
113
+ });
114
+ watch(error, (newError) => {
115
+ if (newError) {
116
+ errorPopoverRef.value.show(submitRef);
117
+ } else {
118
+ errorPopoverRef.value.hide();
119
+ }
120
+ });
94
121
  function splitByWide(inputGroups) {
95
122
  const result = [];
96
123
  let cur = [];
@@ -110,13 +137,13 @@ function onReady(key, value) {
110
137
  Object.assign(readyData.value, { [key]: value });
111
138
  }
112
139
  function onChanged({ values }) {
113
- Object.assign(formData.value, values);
140
+ Object.assign(model.value, values);
114
141
  }
115
142
  function onSubmit() {
116
143
  if (!ready.value) {
117
144
  return;
118
145
  }
119
- emit("submit", formData.value);
146
+ emit("submit", model.value);
120
147
  }
121
148
  </script>
122
149
 
@@ -0,0 +1,15 @@
1
+ declare const _default: typeof __VLS_export;
2
+ export default _default;
3
+ declare const __VLS_export: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
4
+ error: {
5
+ type: null;
6
+ default: undefined;
7
+ };
8
+ }>, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
9
+ error: {
10
+ type: null;
11
+ default: undefined;
12
+ };
13
+ }>> & Readonly<{}>, {
14
+ error: any;
15
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
@@ -0,0 +1,27 @@
1
+ <template>
2
+ <Bake
3
+ v-if="error"
4
+ name="error"
5
+ :descriptor
6
+ >
7
+ <template #content>
8
+ {{ error.detail }}
9
+ </template>
10
+ </Bake>
11
+ </template>
12
+
13
+ <script setup>
14
+ import { computed } from "vue";
15
+ import { useRuntimeConfig } from "#imports";
16
+ const { public: { inlineError } } = useRuntimeConfig();
17
+ const { error } = defineProps({
18
+ error: { type: null, default: void 0 }
19
+ });
20
+ const descriptor = computed(() => ({
21
+ ...inlineError,
22
+ data: {
23
+ type: "Inline",
24
+ value: error.title
25
+ }
26
+ }));
27
+ </script>