@narrative.io/jsonforms-provider-protocols 1.1.0-beta.0 → 1.1.0-beta.10

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 (68) hide show
  1. package/README.md +61 -0
  2. package/dist/index.d.ts +1 -1
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +24 -2
  5. package/dist/index.js.map +1 -1
  6. package/dist/jsonforms-provider-protocols.css +1 -1
  7. package/dist/protocols/rest_api.d.ts +1 -0
  8. package/dist/protocols/rest_api.d.ts.map +1 -1
  9. package/dist/protocols/rest_api.js +6 -1
  10. package/dist/protocols/rest_api.js.map +1 -1
  11. package/dist/vue/components/ProviderMultiSelect.vue.d.ts +9 -0
  12. package/dist/vue/components/ProviderMultiSelect.vue.d.ts.map +1 -0
  13. package/dist/vue/components/ProviderMultiSelect.vue.js +8 -0
  14. package/dist/vue/components/ProviderMultiSelect.vue.js.map +1 -0
  15. package/dist/vue/components/ProviderMultiSelect.vue2.js +95 -0
  16. package/dist/vue/components/ProviderMultiSelect.vue2.js.map +1 -0
  17. package/dist/vue/composables/useDerive.d.ts +13 -0
  18. package/dist/vue/composables/useDerive.d.ts.map +1 -0
  19. package/dist/vue/composables/useDerive.js +71 -0
  20. package/dist/vue/composables/useDerive.js.map +1 -0
  21. package/dist/vue/index.d.ts +3 -2
  22. package/dist/vue/index.d.ts.map +1 -1
  23. package/dist/vue/index.js +26 -8
  24. package/dist/vue/index.js.map +1 -1
  25. package/dist/vue/primevue/JfBoolean.vue.d.ts +20 -32
  26. package/dist/vue/primevue/JfBoolean.vue.d.ts.map +1 -1
  27. package/dist/vue/primevue/JfBoolean.vue.js +38 -6
  28. package/dist/vue/primevue/JfBoolean.vue.js.map +1 -1
  29. package/dist/vue/primevue/JfEnum.vue.d.ts +20 -32
  30. package/dist/vue/primevue/JfEnum.vue.d.ts.map +1 -1
  31. package/dist/vue/primevue/JfEnum.vue.js +152 -5
  32. package/dist/vue/primevue/JfEnum.vue.js.map +1 -1
  33. package/dist/vue/primevue/JfEnum.vue2.js +1 -61
  34. package/dist/vue/primevue/JfEnum.vue2.js.map +1 -1
  35. package/dist/vue/primevue/JfEnumArray.vue.d.ts +20 -32
  36. package/dist/vue/primevue/JfEnumArray.vue.d.ts.map +1 -1
  37. package/dist/vue/primevue/JfEnumArray.vue.js +93 -13
  38. package/dist/vue/primevue/JfEnumArray.vue.js.map +1 -1
  39. package/dist/vue/primevue/JfNumber.vue.d.ts +20 -32
  40. package/dist/vue/primevue/JfNumber.vue.d.ts.map +1 -1
  41. package/dist/vue/primevue/JfNumber.vue.js +87 -13
  42. package/dist/vue/primevue/JfNumber.vue.js.map +1 -1
  43. package/dist/vue/primevue/JfText.vue.d.ts +20 -32
  44. package/dist/vue/primevue/JfText.vue.d.ts.map +1 -1
  45. package/dist/vue/primevue/JfText.vue.js +131 -16
  46. package/dist/vue/primevue/JfText.vue.js.map +1 -1
  47. package/dist/vue/primevue/JfTextArea.vue.d.ts +20 -32
  48. package/dist/vue/primevue/JfTextArea.vue.d.ts.map +1 -1
  49. package/dist/vue/primevue/JfTextArea.vue.js +49 -11
  50. package/dist/vue/primevue/JfTextArea.vue.js.map +1 -1
  51. package/dist/vue/primevue/index.d.ts +2 -75
  52. package/dist/vue/primevue/index.d.ts.map +1 -1
  53. package/dist/vue/primevue/index.js +46 -28
  54. package/dist/vue/primevue/index.js.map +1 -1
  55. package/package.json +1 -1
  56. package/src/index.ts +15 -2
  57. package/src/protocols/rest_api.ts +8 -1
  58. package/src/vue/components/ProviderMultiSelect.vue +108 -0
  59. package/src/vue/composables/useDerive.ts +105 -0
  60. package/src/vue/index.ts +36 -3
  61. package/src/vue/primevue/JfBoolean.vue +42 -5
  62. package/src/vue/primevue/JfEnum.vue +131 -20
  63. package/src/vue/primevue/JfEnumArray.vue +118 -14
  64. package/src/vue/primevue/JfNumber.vue +104 -13
  65. package/src/vue/primevue/JfText.vue +156 -13
  66. package/src/vue/primevue/JfTextArea.vue +57 -10
  67. package/src/vue/primevue/index.ts +48 -37
  68. package/src/vue/styles.css +5 -0
package/src/index.ts CHANGED
@@ -13,8 +13,21 @@ export * from "./core/types";
13
13
  // Protocol exports
14
14
  export { RestApiProtocol } from "./protocols/rest_api";
15
15
 
16
- // Vue exports
17
- export * as vue from "./vue";
16
+ // Vue exports - using named imports to avoid potential bundling issues
17
+ export {
18
+ providerRenderers,
19
+ primevueRenderers,
20
+ ProviderAutocomplete,
21
+ ProviderSelect,
22
+ ProviderMultiSelect,
23
+ useProvider,
24
+ JfText,
25
+ JfTextArea,
26
+ JfNumber,
27
+ JfEnum,
28
+ JfEnumArray,
29
+ JfBoolean,
30
+ } from "./vue";
18
31
 
19
32
  export interface ProviderConfig {
20
33
  protocols?: Protocol[];
@@ -17,6 +17,7 @@ export type RestApiCfg = {
17
17
  map: { label: string; value: string; meta?: Record<string, string> }; // relative to each item
18
18
  paginate?: { cursorPath: string; param: string; maxPages?: number };
19
19
  auth?: AuthConfig;
20
+ showError?: boolean; // Whether to show error messages, defaults to true
20
21
  };
21
22
 
22
23
  function buildAuthHeaders(
@@ -122,7 +123,13 @@ export const RestApiProtocol = (): Protocol<RestApiCfg> => ({
122
123
  }
123
124
 
124
125
  const res = await fetch(url.toString(), requestInit);
125
- if (!res.ok) throw new Error(`REST ${res.status}`);
126
+ if (!res.ok) {
127
+ if (cfg.showError !== false) {
128
+ throw new Error(`REST ${res.status}`);
129
+ }
130
+ // If showError is false, return empty items instead of throwing
131
+ return { items: [], ttl: 0 };
132
+ }
126
133
  const json = await res.json();
127
134
  const items = jp(json, cfg.items);
128
135
  for (const it of items) {
@@ -0,0 +1,108 @@
1
+ <script setup lang="ts">
2
+ import type { ControlElement, JsonSchema } from "@jsonforms/core";
3
+ import { useJsonFormsControl } from "@jsonforms/vue";
4
+ import { computed, inject } from "vue";
5
+ import { useProvider } from "../composables/useProvider";
6
+ import MultiSelect from "primevue/multiselect";
7
+
8
+ const props = defineProps<{
9
+ uischema: ControlElement;
10
+ schema: JsonSchema;
11
+ path: string;
12
+ }>();
13
+ const { control, handleChange } = useJsonFormsControl(props);
14
+
15
+ const binding = computed(() => {
16
+ const provider = control.value.uischema?.options?.provider;
17
+ // Ensure load property is set to 'mount' by default
18
+ if (provider && typeof provider === "object" && !provider.load) {
19
+ return { ...provider, load: "mount" };
20
+ }
21
+ return provider;
22
+ });
23
+
24
+ const deps = computed(
25
+ () =>
26
+ ((
27
+ (control.value.schema as Record<string, unknown>)?.[
28
+ "x-provider"
29
+ ] as Record<string, unknown>
30
+ )?.dependsOn as string[]) ?? [],
31
+ );
32
+ const depValues = computed(() => deps.value.map(() => null)); // you can resolve actual values via control.value.data & pointers
33
+
34
+ // Get the root form data from JSONForms context for template URL resolution
35
+ const injectedFormData = inject<{ value: unknown }>("formData", { value: {} });
36
+ const rootData = computed(() => injectedFormData.value || {});
37
+
38
+ const { items, loading, error } = useProvider(binding, {
39
+ data: rootData, // Pass the reactive reference
40
+ path: control.value.path,
41
+ dependsOnValues: depValues.value,
42
+ });
43
+
44
+ // Provider will automatically reload when rootData changes due to reactive cache key
45
+
46
+ // order-insensitive shallow equality for primitive arrays
47
+ const sameSet = (a: unknown[], b: unknown[]) => {
48
+ if (!Array.isArray(a) || !Array.isArray(b) || a.length !== b.length)
49
+ return false;
50
+ const s = new Set(b);
51
+ return a.every((v) => s.has(v));
52
+ };
53
+
54
+ // v-model with guard to avoid recursive updates
55
+ const value = computed({
56
+ get() {
57
+ const curr = Array.isArray(control.value.data) ? control.value.data : [];
58
+ // return a fresh copy so MultiSelect can't mutate JSONForms' array in place
59
+ return [...curr];
60
+ },
61
+ set(val) {
62
+ const next = Array.isArray(val) ? [...val] : [];
63
+ const curr = Array.isArray(control.value.data) ? control.value.data : [];
64
+ if (!sameSet(curr, next)) handleChange(control.value.path, next);
65
+ },
66
+ });
67
+
68
+ const placeholder = computed(() => {
69
+ if (loading.value) return "Loading…";
70
+ // Check for placeholder in uischema options
71
+ const uischemaPlaceholder = (
72
+ control.value.uischema as { options?: { placeholder?: string } }
73
+ )?.options?.placeholder;
74
+ return uischemaPlaceholder || "Select…";
75
+ });
76
+ </script>
77
+
78
+ <template>
79
+ <div class="flex flex-column gap-2">
80
+ <label v-if="control.schema.title" class="text-color text-left">{{
81
+ control.schema.title
82
+ }}</label>
83
+ <div v-if="control.description" class="text-color-secondary text-left">
84
+ {{ control.description }}
85
+ </div>
86
+ <MultiSelect
87
+ v-model="value"
88
+ class="w-full"
89
+ :options="items"
90
+ option-label="label"
91
+ option-value="value"
92
+ data-key="value"
93
+ display="chip"
94
+ :placeholder="placeholder"
95
+ :disabled="!control.enabled || loading"
96
+ :show-clear="true"
97
+ />
98
+ <small v-if="error" class="p-error" role="alert"
99
+ >Failed to load: {{ error }}</small
100
+ >
101
+ </div>
102
+ </template>
103
+
104
+ <style scoped>
105
+ :deep(.p-multiselect-label) {
106
+ text-align: left;
107
+ }
108
+ </style>
@@ -0,0 +1,105 @@
1
+ import { computed, watch, inject, type Ref } from "vue";
2
+ import { type ControlElement } from "@jsonforms/core";
3
+
4
+ interface DeriveOptions {
5
+ control: Ref<{
6
+ uischema: ControlElement;
7
+ path: string;
8
+ data: unknown;
9
+ }>;
10
+ handleChange: (path: string, value: unknown) => void;
11
+ }
12
+
13
+ export function useDerive({ control, handleChange }: DeriveOptions) {
14
+ // Get the root form data from JSONForms context
15
+ const injectedFormData = inject<{ value: unknown }>("formData", {
16
+ value: {},
17
+ });
18
+ const rootData = computed(() => injectedFormData.value || {});
19
+
20
+ // Get external data from context if available
21
+ const injectedExternalData = inject<{ value: unknown }>("externalData", {
22
+ value: {},
23
+ });
24
+ const externalData = computed(() => injectedExternalData.value || {});
25
+
26
+ // Extract derive configuration from uischema options
27
+ const deriveConfig = computed(() => {
28
+ const options = control.value.uischema?.options as
29
+ | { derive?: string; mode?: string }
30
+ | undefined;
31
+ return {
32
+ expression: options?.derive,
33
+ mode: options?.mode || "follow", // 'follow' = auto-update, 'manual' = user controlled
34
+ };
35
+ });
36
+
37
+ // Watch for changes in form data and external data and update derived field
38
+ watch(
39
+ [rootData, externalData, deriveConfig],
40
+ ([data, extData, config]) => {
41
+ if (!config.expression || config.mode !== "follow") {
42
+ return;
43
+ }
44
+
45
+ try {
46
+ const derivedValue = resolveDeriveExpression(
47
+ config.expression,
48
+ data,
49
+ extData,
50
+ );
51
+ if (derivedValue !== control.value.data) {
52
+ handleChange(control.value.path, derivedValue);
53
+ }
54
+ } catch (error) {
55
+ console.warn(
56
+ `Failed to derive value for ${control.value.path}:`,
57
+ error,
58
+ );
59
+ }
60
+ },
61
+ { deep: true, immediate: true },
62
+ );
63
+ }
64
+
65
+ function resolveDeriveExpression(
66
+ expression: string,
67
+ data: unknown,
68
+ externalData?: unknown,
69
+ ): unknown {
70
+ // Handle externalData() syntax
71
+ if (expression.startsWith("externalData(") && expression.endsWith(")")) {
72
+ const propertyPath = expression.slice(13, -1); // Remove "externalData(" and ")"
73
+ return resolvePropertyPath(propertyPath, externalData);
74
+ }
75
+
76
+ // Handle simple property paths like "country.name"
77
+ if (
78
+ !expression.includes("(") &&
79
+ !expression.includes("+") &&
80
+ !expression.includes("?")
81
+ ) {
82
+ return resolvePropertyPath(expression, data);
83
+ }
84
+
85
+ // For now, we'll only support simple property paths and externalData() calls
86
+ // Complex expressions would require a safe expression evaluator
87
+ return resolvePropertyPath(expression, data);
88
+ }
89
+
90
+ function resolvePropertyPath(path: string, data: unknown): unknown {
91
+ if (!path || !data) return "";
92
+
93
+ const keys = path.split(".");
94
+ let value: unknown = data;
95
+
96
+ for (const key of keys) {
97
+ if (value && typeof value === "object" && key in value) {
98
+ value = (value as Record<string, unknown>)[key];
99
+ } else {
100
+ return null;
101
+ }
102
+ }
103
+
104
+ return value;
105
+ }
package/src/vue/index.ts CHANGED
@@ -9,6 +9,7 @@ import {
9
9
  } from "@jsonforms/core";
10
10
  import ProviderAutocomplete from "./components/ProviderAutocomplete.vue";
11
11
  import ProviderSelect from "./components/ProviderSelect.vue";
12
+ import ProviderMultiSelect from "./components/ProviderMultiSelect.vue";
12
13
 
13
14
  // Custom tester that checks if provider option exists (as object or boolean)
14
15
  const hasProvider = (uischema: UISchemaElement) => {
@@ -34,16 +35,48 @@ const providerAutocompleteTester = rankWith(
34
35
  ),
35
36
  );
36
37
 
38
+ // Custom array tester - check both uischema control type and schema type
39
+ const isArrayControl = (uischema: UISchemaElement, schema: unknown) => {
40
+ const controlSchema = uischema as { type: string; scope?: string };
41
+ if (controlSchema.type !== "Control" || !controlSchema.scope) {
42
+ return false;
43
+ }
44
+
45
+ // Extract the property schema from the root schema
46
+ const rootSchema = schema as { properties?: Record<string, unknown> };
47
+ const propertyPath = controlSchema.scope.replace("#/properties/", "");
48
+ const propertySchema = rootSchema?.properties?.[propertyPath] as {
49
+ type?: string;
50
+ };
51
+
52
+ return propertySchema?.type === "array";
53
+ };
54
+
55
+ const providerMultiSelectTester = rankWith(
56
+ 108, // Highest priority for array controls with providers
57
+ and(isArrayControl, hasProvider),
58
+ );
59
+
37
60
  export const providerRenderers = [
38
- { tester: providerSelectTester, renderer: ProviderSelect },
61
+ { tester: providerMultiSelectTester, renderer: ProviderMultiSelect },
39
62
  { tester: providerAutocompleteTester, renderer: ProviderAutocomplete },
63
+ { tester: providerSelectTester, renderer: ProviderSelect },
40
64
  ];
41
65
 
42
66
  // Export PrimeVue renderers (styles are auto-injected)
43
67
  export { primevueRenderers } from "./primevue";
44
68
 
45
69
  // Export individual components
46
- export { ProviderAutocomplete, ProviderSelect };
70
+ export { ProviderAutocomplete, ProviderSelect, ProviderMultiSelect };
47
71
  export { useProvider } from "./composables/useProvider";
48
72
  export * from "./testers";
49
- export * from "./primevue";
73
+
74
+ // Export individual PrimeVue components using lazy evaluation to avoid circular deps
75
+ export {
76
+ JfText,
77
+ JfTextArea,
78
+ JfNumber,
79
+ JfEnum,
80
+ JfEnumArray,
81
+ JfBoolean,
82
+ } from "./primevue";
@@ -1,11 +1,48 @@
1
+ <script lang="ts">
2
+ export default {
3
+ name: "JfBoolean",
4
+ props: {
5
+ uischema: {
6
+ type: Object,
7
+ required: true,
8
+ },
9
+ schema: {
10
+ type: Object,
11
+ required: true,
12
+ },
13
+ path: {
14
+ type: String,
15
+ required: true,
16
+ },
17
+ enabled: {
18
+ type: Boolean,
19
+ default: undefined,
20
+ },
21
+ renderers: {
22
+ type: Array,
23
+ required: false,
24
+ },
25
+ cells: {
26
+ type: Array,
27
+ required: false,
28
+ },
29
+ config: {
30
+ type: Object,
31
+ required: false,
32
+ },
33
+ },
34
+ };
35
+ </script>
36
+
1
37
  <script setup lang="ts">
2
- import type { ControlElement } from "@jsonforms/core";
3
- import { rendererProps, useJsonFormsControl } from "@jsonforms/vue";
38
+ import type { ControlProps } from "@jsonforms/vue";
39
+ import { useJsonFormsControl } from "@jsonforms/vue";
40
+ import { getCurrentInstance } from "vue";
4
41
  import Checkbox from "primevue/checkbox";
5
42
 
6
- defineOptions({ name: "JfBoolean" });
7
-
8
- const props = defineProps(rendererProps<ControlElement>());
43
+ // Access props from the component instance
44
+ const instance = getCurrentInstance()!;
45
+ const props = instance.props as unknown as ControlProps;
9
46
  const { control, handleChange } = useJsonFormsControl(props);
10
47
 
11
48
  const onToggle = (val: boolean) => handleChange(control.value.path, val);
@@ -1,12 +1,52 @@
1
+ <script lang="ts">
2
+ // Define props manually to avoid any potential circular dependency issues
3
+ export default {
4
+ name: "JfEnum",
5
+ props: {
6
+ uischema: {
7
+ type: Object,
8
+ required: true,
9
+ },
10
+ schema: {
11
+ type: Object,
12
+ required: true,
13
+ },
14
+ path: {
15
+ type: String,
16
+ required: true,
17
+ },
18
+ enabled: {
19
+ type: Boolean,
20
+ default: undefined,
21
+ },
22
+ renderers: {
23
+ type: Array,
24
+ required: false,
25
+ },
26
+ cells: {
27
+ type: Array,
28
+ required: false,
29
+ },
30
+ config: {
31
+ type: Object,
32
+ required: false,
33
+ },
34
+ },
35
+ };
36
+ </script>
37
+
1
38
  <script setup lang="ts">
2
- import type { ControlElement, JsonSchema } from "@jsonforms/core";
3
- import { rendererProps, useJsonFormsControl } from "@jsonforms/vue";
4
- import { computed } from "vue";
39
+ import type { JsonSchema } from "@jsonforms/core";
40
+ import type { ControlProps } from "@jsonforms/vue";
41
+ import { useJsonFormsControl } from "@jsonforms/vue";
42
+ import { computed, ref, inject, getCurrentInstance } from "vue";
43
+ import { useProvider } from "../composables/useProvider";
44
+ import { useDerive } from "../composables/useDerive";
5
45
  import Dropdown from "primevue/dropdown";
6
46
 
7
- defineOptions({ name: "JfEnum" });
8
-
9
- const props = defineProps(rendererProps<ControlElement>());
47
+ // Access props from the component instance
48
+ const instance = getCurrentInstance()!;
49
+ const props = instance.props as unknown as ControlProps;
10
50
  const { control, handleChange } = useJsonFormsControl(props);
11
51
 
12
52
  type Opt = { label: string; value: unknown };
@@ -26,14 +66,87 @@ const toOptions = (schema?: JsonSchema): Opt[] => {
26
66
  return [];
27
67
  };
28
68
 
29
- const placeholder = computed<string | undefined>(
69
+ // Provider support
70
+ const binding = computed(() => {
71
+ const provider = control.value.uischema?.options?.provider;
72
+ // Ensure load property is set to 'mount' by default
73
+ if (provider && typeof provider === "object" && !provider.load) {
74
+ return { ...provider, load: "mount" };
75
+ }
76
+ return provider;
77
+ });
78
+
79
+ const deps = computed(
30
80
  () =>
31
- (control.value.uischema as { options?: { placeholder?: string } })?.options
32
- ?.placeholder ?? control.value.description,
81
+ ((
82
+ (control.value.schema as Record<string, unknown>)?.[
83
+ "x-provider"
84
+ ] as Record<string, unknown>
85
+ )?.dependsOn as string[]) ?? [],
33
86
  );
87
+ const depValues = computed(() => {
88
+ return deps.value.map((dep) => {
89
+ // Resolve dependency value from form data using JSON pointer-like path
90
+ const path = dep.startsWith("#/") ? dep.slice(2) : dep;
91
+ const keys = path.replace(/\//g, ".").split(".");
92
+ let value: unknown = rootData.value;
93
+ for (const key of keys) {
94
+ if (value && typeof value === "object" && key in value) {
95
+ value = (value as Record<string, unknown>)[key];
96
+ } else {
97
+ return null;
98
+ }
99
+ }
100
+ return value;
101
+ });
102
+ });
103
+
104
+ // Get the root form data from JSONForms context for template URL resolution
105
+ const injectedFormData = inject<{ value: unknown }>("formData", { value: {} });
106
+ const rootData = computed(() => injectedFormData.value || {});
107
+
108
+ // Use provider if available, otherwise fall back to schema enum/oneOf
109
+ const {
110
+ items: providerItems,
111
+ loading,
112
+ error,
113
+ } = useProvider(binding, {
114
+ data: rootData,
115
+ path: control.value.path,
116
+ dependsOnValues: depValues.value,
117
+ });
118
+
119
+ const placeholder = computed<string | undefined>(() => {
120
+ if (loading.value) return "Loading…";
121
+ return (
122
+ (control.value.uischema as { options?: { placeholder?: string } })?.options
123
+ ?.placeholder ?? control.value.description
124
+ );
125
+ });
126
+
127
+ const options = computed(() => {
128
+ // Use provider items if available, otherwise fall back to schema enum/oneOf
129
+ if (binding.value && providerItems.value.length > 0) {
130
+ return providerItems.value;
131
+ }
132
+ return toOptions(control.value.schema);
133
+ });
134
+
135
+ // Add derive functionality
136
+ useDerive({ control, handleChange });
137
+
138
+ // Track user interaction
139
+ const hasInteracted = ref(false);
140
+
141
+ const showErrors = computed(() => hasInteracted.value && control.value.errors);
34
142
 
35
- const options = computed(() => toOptions(control.value.schema));
36
- const onSelect = (val: unknown) => handleChange(control.value.path, val);
143
+ const onSelect = (val: unknown) => {
144
+ handleChange(control.value.path, val);
145
+ };
146
+
147
+ const onBlur = () => {
148
+ hasInteracted.value = true;
149
+ };
37
150
  </script>
38
151
 
39
152
  <template>
@@ -51,17 +164,15 @@ const onSelect = (val: unknown) => handleChange(control.value.path, val);
51
164
  option-value="value"
52
165
  :model-value="control.data ?? null"
53
166
  :placeholder="placeholder"
54
- :disabled="!control.enabled"
55
- :aria-invalid="!!control.errors || undefined"
167
+ :disabled="!control.enabled || loading"
168
+ :aria-invalid="!!showErrors || undefined"
56
169
  :show-clear="true"
57
170
  @update:model-value="onSelect"
171
+ @blur="onBlur"
58
172
  />
59
- <small v-if="control.errors" class="p-error">{{ control.errors }}</small>
173
+ <small v-if="error" class="p-error" role="alert"
174
+ >Failed to load: {{ error }}</small
175
+ >
176
+ <small v-else-if="showErrors" class="p-error">{{ control.errors }}</small>
60
177
  </div>
61
178
  </template>
62
-
63
- <style scoped>
64
- :deep(.p-dropdown-label) {
65
- text-align: left;
66
- }
67
- </style>