@josercl/form-maker 1.2.0 → 1.3.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.
@@ -1,87 +1,43 @@
1
- <script setup>
1
+ <script setup lang="ts">
2
+ import type { FormMakerProps, InputType } from 'lib/model';
2
3
  import {
3
- computed, inject, provide, toRefs
4
+ computed,
5
+ inject,
6
+ provide,
7
+ type ComputedRef
4
8
  } from 'vue';
5
9
 
6
- const props = defineProps({
7
- loading: {
8
- type: Boolean,
9
- default: false,
10
- },
11
- hasActions: {
12
- type: Boolean,
13
- default: true,
14
- },
15
- modelValue: {
16
- type: Object,
17
- default: () => ({}),
18
- },
19
- fields: {
20
- type: Array,
21
- default: () => [],
22
- },
23
- hideDivider: {
24
- type: Boolean,
25
- default: false,
26
- },
27
- rowClass: {
28
- type: String,
29
- default: null,
30
- },
31
- columnClass: {
32
- type: String,
33
- default: null,
34
- },
35
- labelClass: {
36
- type: String,
37
- default: null,
38
- },
39
- inputGroupClass: {
40
- type: String,
41
- default: null,
42
- },
43
- inputWrapperClass: {
44
- type: String,
45
- default: null,
46
- },
47
- inputErrorClass: {
48
- type: String,
49
- default: null,
50
- },
51
- inputClass: {
52
- type: String,
53
- default: null,
54
- },
55
- errorClass: {
56
- type: String,
57
- default: null,
58
- },
59
- helpTextClass: {
60
- type: String,
61
- default: null,
62
- },
63
- submitButtonClass: {
64
- type: String,
65
- default: null,
66
- },
67
- submitButtonText: {
68
- type: String,
69
- default: 'Submit',
70
- },
71
- });
10
+ const props = withDefaults(defineProps<FormMakerProps>(), {
11
+ loading: false,
12
+ hasActions: true,
13
+ modelValue: () => ({}),
14
+ fields: () => [],
15
+ hideDivider: false,
16
+ rowClass: null,
17
+ columnClass: null,
18
+ labelClass: null,
19
+ inputGroupClass: null,
20
+ inputWrapperClass: null,
21
+ inputErrorClass: null,
22
+ inputClass: null,
23
+ errorClass: null,
24
+ helpTextClass: null,
25
+ submitButtonClass: null,
26
+ submitButtonText: 'Submit',
27
+ })
72
28
 
73
- const emit = defineEmits([ 'submit', 'update:modelValue' ]);
29
+ const emit = defineEmits(['submit', 'update:modelValue']);
74
30
 
75
31
  const handleSubmit = () => emit('submit');
76
32
 
77
- const formFields = computed(() => {
33
+ const formFields: ComputedRef<InputType[][]> = computed(() => {
78
34
  if (props.fields.length > 0) {
79
35
  return props.fields.map(row => {
80
- let newRow = row;
36
+ let newRow: InputType[] = row;
81
37
  if (!Array.isArray(row)) {
82
- newRow = [ row ];
38
+ newRow = [row];
83
39
  }
84
- return newRow.map(fieldSpec => {
40
+ return newRow.map((fieldSpec: InputType) => {
85
41
  if (!fieldSpec.id) {
86
42
  return {
87
43
  ...fieldSpec,
@@ -93,55 +49,41 @@ const formFields = computed(() => {
93
49
  });
94
50
  }
95
51
 
96
- return Object.keys(props.modelValue).map(key => [
52
+ return Object.keys(props.modelValue).map(key => ([
97
53
  {
98
54
  name: key,
99
55
  label: key,
100
56
  id: `formMaker_${key}`,
101
- },
102
- ]);
57
+ }
58
+ ]));
103
59
  });
104
60
 
105
- const {
106
- rowClass,
107
- columnClass,
108
- labelClass,
109
- inputGroupClass,
110
- inputWrapperClass,
111
- inputErrorClass,
112
- inputClass,
113
- errorClass,
114
- helpTextClass,
115
- submitButtonClass,
116
- modelValue,
117
- } = toRefs(props);
61
+ provide('labelClass', props.labelClass || inject('form-label'));
62
+ provide('inputGroupClass', props.inputGroupClass || inject('input-group'));
63
+ provide('inputWrapperClass', props.inputWrapperClass || inject('input-wrapper'));
64
+ provide('inputErrorClass', props.inputErrorClass || inject('input-error'));
65
+ provide('inputClass', props.inputClass || inject('input'));
66
+ provide('errorClass', props.errorClass || inject('error'));
67
+ provide('helpTextClass', props.helpTextClass || inject('help-text'));
118
68
 
119
- provide('labelClass', labelClass.value || inject('form-label'));
120
- provide('inputGroupClass', inputGroupClass.value || inject('input-group'));
121
- provide('inputWrapperClass', inputWrapperClass.value || inject('input-wrapper'));
122
- provide('inputErrorClass', inputErrorClass.value || inject('input-error'));
123
- provide('inputClass', inputClass.value || inject('input'));
124
- provide('errorClass', errorClass.value || inject('error'));
125
- provide('helpTextClass', helpTextClass.value || inject('help-text'));
69
+ const realRowClass = props.rowClass || inject('form-row');
70
+ const realColumnClass = props.columnClass || inject('form-column');
71
+ const realSubmitButtonClass = props.submitButtonClass || inject('submit-button');
126
72
 
127
- const realRowClass = rowClass.value || inject('form-row');
128
- const realColumnClass = columnClass.value || inject('form-column');
129
- const realSubmitButtonClass = submitButtonClass.value || inject('submit-button');
130
-
131
- const getObject = fieldName => {
132
- const fields = fieldName.split('.');
133
- let result = modelValue.value;
73
+ const getObject = (fieldName: string) => {
74
+ const fields = fieldName.split('.')
75
+ let result = props.modelValue
134
76
  for (let i = 0; i < fields.length - 1; i += 1) {
135
- const candidate = result[fields[i]];
77
+ const candidate = result[fields[i]]
136
78
  if (!candidate) {
137
- break;
79
+ break
138
80
  }
139
- result = candidate;
81
+ result = candidate
140
82
  }
141
- return result;
83
+ return result
142
84
  };
143
85
 
144
- const getProp = fieldName => {
86
+ const getProp = (fieldName: string) => {
145
87
  const fields = fieldName.split('.');
146
88
  return fields[fields.length - 1];
147
89
  };
@@ -149,35 +91,15 @@ const getProp = fieldName => {
149
91
  </script>
150
92
 
151
93
  <template>
152
- <form
153
- class="form-maker"
154
- @submit.prevent="handleSubmit"
155
- >
156
- <slot
157
- v-if="loading"
158
- name="loading"
159
- >
94
+ <form class="form-maker" @submit.prevent="handleSubmit">
95
+ <slot v-if="loading" name="loading">
160
96
  Loading...
161
97
  </slot>
162
98
  <slot>
163
- <div
164
- v-for="(fieldRow, i) in formFields"
165
- :class="realRowClass"
166
- :key="`fieldRow_${i}`"
167
- >
168
- <div
169
- v-for="(field, j) in fieldRow"
170
- :class="[realColumnClass, field.columnClass]"
171
- :key="`field_${i}_${j}`"
172
- >
173
- <slot
174
- :field="field"
175
- :name="`${field.name}`"
176
- >
177
- <form-maker-input
178
- v-model="getObject(field.name)[getProp(field.name)]"
179
- v-bind="field"
180
- />
99
+ <div v-for="(fieldRow, i) in formFields" :class="realRowClass" :key="`fieldRow_${i}`">
100
+ <div v-for="(field, j) in fieldRow" :class="[realColumnClass, field.columnClass]" :key="`field_${i}_${j}`">
101
+ <slot :field="field" :name="`${field.name}`">
102
+ <form-maker-input v-model="getObject(field.name)[getProp(field.name)]" v-bind="field" />
181
103
  </slot>
182
104
  </div>
183
105
  </div>
@@ -186,16 +108,9 @@ const getProp = fieldName => {
186
108
  <slot name="divider">
187
109
  <hr v-if="hasActions && !hideDivider">
188
110
  </slot>
189
- <slot
190
- v-if="hasActions"
191
- name="actions"
192
- >
111
+ <slot v-if="hasActions" name="actions">
193
112
  <slot name="submit-button">
194
- <button
195
- :class="realSubmitButtonClass"
196
- :disabled="loading"
197
- type="submit"
198
- >
113
+ <button :class="realSubmitButtonClass" :disabled="loading" type="submit">
199
114
  {{ submitButtonText }}
200
115
  </button>
201
116
  </slot>
@@ -1,23 +1,29 @@
1
- <script setup>
1
+ <script setup lang="ts">
2
2
  import { computed } from 'vue';
3
+ import RuleEvaluator from './RuleEvaluator.vue';
4
+ import useFormClasses from '../composables/useFormClasses';
3
5
  import useValidator from '../composables/useValidator';
4
6
  import useVModel from '../composables/useVModel';
5
- import { FormInputMixin, injectFormClasses } from './inputs/FormInputMixin.js';
7
+ import type { FormMakerInputProps } from 'lib/model';
6
8
 
7
9
  defineOptions({
8
10
  inheritAttrs: false,
9
11
  });
10
12
 
11
- const props = defineProps({
12
- ...FormInputMixin.props,
13
- rules: {
14
- type: Array,
15
- default: () => ([]),
16
- },
17
- });
18
- const emit = defineEmits(FormInputMixin.emits);
13
+ const props = withDefaults(defineProps<FormMakerInputProps>(), {
14
+ loading: false,
15
+ disabled: false,
16
+ error: undefined,
17
+ helpText: undefined,
18
+ id: undefined,
19
+ label: undefined,
20
+ placeholder: undefined,
21
+ rules: () => [],
22
+ type: 'text'
23
+ })
24
+ const emit = defineEmits([ 'update:modelValue' ]);
19
25
 
20
- const value = useVModel(props, emit);
26
+ const value = useVModel(() => props.modelValue, emit);
21
27
 
22
28
  const {
23
29
  labelClass,
@@ -27,7 +33,7 @@ const {
27
33
  inputErrorClass,
28
34
  errorClass,
29
35
  helpTextClass,
30
- } = injectFormClasses();
36
+ } = useFormClasses();
31
37
 
32
38
  const hasLabel = computed(() => Boolean(props.label));
33
39
  const hasHelpText = computed(() => Boolean(props.helpText));
@@ -0,0 +1,36 @@
1
+ <script setup lang="ts">
2
+ import type { RuleValidator } from 'lib/model';
3
+ import { watch } from 'vue';
4
+
5
+ interface RuleEvaluatorProps {
6
+ rules?: RuleValidator[]
7
+ modelValue: any
8
+ }
9
+
10
+ const props = withDefaults(defineProps<RuleEvaluatorProps>(), {
11
+ rules: () => ([]),
12
+ modelValue: undefined,
13
+ })
14
+
15
+ const emit = defineEmits(['update:modelValue'])
16
+
17
+ watch(
18
+ props.modelValue,
19
+ (newVal) => {
20
+ let error = null;
21
+ const nRules = props.rules?.length ?? 0;
22
+
23
+ for (let i = 0; i < nRules; i += 1) {
24
+ const rule = props.rules[i];
25
+ const validator = rule.validator(newVal);
26
+ if (!validator) {
27
+ error = rule.message;
28
+ break;
29
+ }
30
+ }
31
+ emit('update:modelValue', error);
32
+ },
33
+ { immediate: true }
34
+ )
35
+
36
+ </script>
@@ -0,0 +1,23 @@
1
+ import { h } from "vue"
2
+ import type { SetupContext } from "vue";
3
+
4
+ type SimpleComponentProps = {
5
+ as?: string
6
+ }
7
+
8
+ function SimpleComponent(
9
+ props: SimpleComponentProps,
10
+ context: SetupContext
11
+ ) {
12
+ return h(props.as, context.attrs, context.slots);
13
+ }
14
+
15
+ SimpleComponent.props = {
16
+ as: {
17
+ type: String,
18
+ required: false,
19
+ default: 'div'
20
+ }
21
+ }
22
+
23
+ export default SimpleComponent
@@ -1,17 +1,23 @@
1
- <script setup>
1
+ <script setup lang="ts">
2
+ import type { FormMakerInputProps } from 'lib/model';
2
3
  import useVModel from '../../composables/useVModel';
3
- import { FormInputMixin } from './FormInputMixin.js';
4
4
 
5
- const props = defineProps(FormInputMixin.props);
6
- const emit = defineEmits(FormInputMixin.emits);
5
+ const props = withDefaults(defineProps<FormMakerInputProps>(), {
6
+ loading: false,
7
+ disabled: false,
8
+ error: undefined,
9
+ helpText: undefined,
10
+ id: undefined,
11
+ label: undefined,
12
+ placeholder: undefined,
13
+ rules: () => [],
14
+ type: 'text'
15
+ });
16
+ const emit = defineEmits(['update:modelValue']);
7
17
 
8
- const value = useVModel(props, emit);
18
+ const value = useVModel(() => props.modelValue, emit);
9
19
  </script>
10
20
 
11
21
  <template>
12
- <input
13
- v-model="value"
14
- :type="type"
15
- v-bind="$props"
16
- >
22
+ <input v-model="value" :type="type" v-bind="{ ...$props, ...$attrs }">
17
23
  </template>
@@ -1,16 +1,27 @@
1
- <script setup>
2
- import { computed, toRefs } from 'vue';
3
- import { FormInputMixin } from './FormInputMixin.js';
1
+ <script setup lang="ts">
2
+ import { computed, type ComputedRef } from 'vue';
3
+ import type { FormMakerInputProps, SelectOption } from 'lib/model';
4
4
 
5
- const props = defineProps({
6
- ...FormInputMixin.props,
7
- options: { type: Array, default: () => [] },
8
- });
9
- const emit = defineEmits(FormInputMixin.emits);
5
+ interface CheckInputProps extends /* @vue-ignore */ FormMakerInputProps {
6
+ options?: boolean | SelectOption[]
7
+ }
10
8
 
11
- const { options, label } = toRefs(props);
9
+ const props = withDefaults(defineProps<CheckInputProps>(), {
10
+ loading: false,
11
+ disabled: false,
12
+ error: undefined,
13
+ helpText: undefined,
14
+ id: undefined,
15
+ label: undefined,
16
+ placeholder: undefined,
17
+ rules: () => [],
18
+ options: () => []
19
+ });
20
+ const emit = defineEmits(['update:modelValue'])
12
21
 
13
- const isBinary = computed(() => options.value.length === 0);
22
+ const isBinary = computed(() => {
23
+ return Array.isArray(props.options) && props.options.length === 0
24
+ })
14
25
 
15
26
  const val = computed(() => props.modelValue);
16
27
 
@@ -19,13 +30,13 @@ const selectedOptions = computed(() => {
19
30
  return arr.filter(x => !!x);
20
31
  });
21
32
 
22
- const fieldOptions = computed(() => {
33
+ const fieldOptions: ComputedRef<SelectOption[]> = computed(() => {
23
34
  if (!isBinary.value) {
24
- return options.value;
35
+ return props.options as SelectOption[];
25
36
  }
26
37
  return [
27
38
  {
28
- label: label.value,
39
+ label: props.label,
29
40
  value: true,
30
41
  },
31
42
  ];
@@ -1,8 +1,18 @@
1
- <script setup>
2
- import { FormInputMixin } from './FormInputMixin.js';
1
+ <script setup lang="ts">
2
+ import type { FormMakerInputProps } from 'lib/model';
3
3
 
4
- defineProps(FormInputMixin.props);
5
- const emit = defineEmits(FormInputMixin.emits);
4
+
5
+ withDefaults(defineProps<FormMakerInputProps>(), {
6
+ loading: false,
7
+ disabled: false,
8
+ error: undefined,
9
+ helpText: undefined,
10
+ id: undefined,
11
+ label: undefined,
12
+ placeholder: undefined,
13
+ rules: () => [],
14
+ });
15
+ const emit = defineEmits(['update:modelValue'])
6
16
 
7
17
  const handleFileSelect = event => {
8
18
  if (event.target.files.length) {
@@ -1,17 +1,26 @@
1
- <script setup>
2
- import { FormInputMixin } from './FormInputMixin.js';
1
+ <script setup lang="ts">
2
+ import type { FormMakerInputProps, SelectOption } from 'lib/model';
3
3
 
4
- defineProps({
5
- ...FormInputMixin.props,
6
- options: {
7
- type: Array,
8
- default: () => [],
9
- },
10
- });
4
+ interface RadioInputProps extends FormMakerInputProps {
5
+ options?: SelectOption[]
6
+ }
11
7
 
12
- const emit = defineEmits(FormInputMixin.emits);
8
+ withDefaults(defineProps<RadioInputProps>(), {
9
+ loading: false,
10
+ disabled: false,
11
+ error: undefined,
12
+ helpText: undefined,
13
+ id: undefined,
14
+ label: undefined,
15
+ placeholder: undefined,
16
+ options: () => [],
17
+ rules: () => [],
18
+ });
19
+ const emit = defineEmits(['update:modelValue'])
13
20
 
14
- const handleClick = e => emit('update:modelValue', e.target.value);
21
+ const handleClick = (value: any) => {
22
+ emit('update:modelValue', value)
23
+ }
15
24
  </script>
16
25
 
17
26
  <template>
@@ -25,7 +34,7 @@ const handleClick = e => emit('update:modelValue', e.target.value);
25
34
  :checked="option.value === modelValue"
26
35
  :value="option.value"
27
36
  type="radio"
28
- @change="handleClick"
37
+ @change="handleClick(option.value)"
29
38
  >
30
39
  {{ option.label }}
31
40
  </label>
@@ -1,23 +1,28 @@
1
- <script setup>
1
+ <script setup lang="ts">
2
2
  import { computed } from 'vue';
3
- import { FormInputMixin } from './FormInputMixin.js';
4
3
  import useVModel from '../../composables/useVModel';
4
+ import type { FormMakerInputProps, SelectOption } from 'lib/model';
5
5
 
6
- const props = defineProps({
7
- ...FormInputMixin.props,
8
- options: {
9
- type: Array,
10
- default: () => [],
11
- },
12
- optionGroups: {
13
- type: Object,
14
- default: () => ({}),
15
- },
16
- });
6
+ interface SelectInputProps extends FormMakerInputProps {
7
+ options?: SelectOption[],
8
+ optionGroups?: Record<string, SelectOption[]>
9
+ }
17
10
 
18
- const emit = defineEmits(FormInputMixin.emits);
11
+ const props = withDefaults(defineProps<SelectInputProps>(), {
12
+ loading: false,
13
+ disabled: false,
14
+ error: undefined,
15
+ helpText: undefined,
16
+ id: undefined,
17
+ label: undefined,
18
+ placeholder: undefined,
19
+ options: () => [],
20
+ optionGroups: () => ({}),
21
+ rules: () => [],
22
+ });
23
+ const emit = defineEmits(['update:modelValue'])
19
24
 
20
- const value = useVModel(props, emit);
25
+ const value = useVModel(() => props.modelValue, emit);
21
26
 
22
27
  const hasGroups = Object.keys(props.optionGroups).length > 0;
23
28
 
@@ -47,11 +52,11 @@ const fixedOptions = computed(() => props.options.map(o => {
47
52
  :label="key"
48
53
  >
49
54
  <option
50
- v-for="(optLabel, optValue) in opts"
51
- :key="`option_${optValue}`"
52
- :value="optValue"
55
+ v-for="opt in opts"
56
+ :key="`option_${opt.value}`"
57
+ :value="opt.value"
53
58
  >
54
- {{ optLabel }}
59
+ {{ opt.label }}
55
60
  </option>
56
61
  </optgroup>
57
62
  </template>
@@ -1,19 +1,25 @@
1
- <script setup>
1
+ <script setup lang="ts">
2
+ import type { TextareaHTMLAttributes } from 'vue';
2
3
  import useVModel from '../../composables/useVModel';
3
- import { FormInputMixin } from './FormInputMixin.js';
4
4
 
5
- const props = defineProps(FormInputMixin.props);
6
- const emit = defineEmits(FormInputMixin.emits);
5
+ interface TextAreaProps extends /* @vue-ignore */ TextareaHTMLAttributes {
6
+ modelValue: any
7
+ }
7
8
 
8
- const value = useVModel(props, emit);
9
+ const props = withDefaults(defineProps<TextAreaProps>(), {
10
+ disabled: false,
11
+ id: undefined,
12
+ placeholder: undefined,
13
+ });
14
+ const emit = defineEmits([ 'update:modelValue' ]);
15
+
16
+ const value = useVModel(() => props.modelValue, emit);
9
17
  </script>
10
18
 
11
19
  <template>
12
20
  <textarea
13
21
  :id="id"
14
22
  v-model="value"
15
- :disabled="disabled"
16
- :name="name"
17
- :placeholder="placeholder"
23
+ v-bind="{...$props, ...$attrs}"
18
24
  />
19
25
  </template>
@@ -1,15 +1,16 @@
1
1
  <script>
2
2
  import { h } from 'vue';
3
3
 
4
- export default {
5
- props: {
6
- id: {
7
- type: String,
8
- default: null,
9
- },
10
- },
11
- setup(props, { slots, attrs }) {
12
- return () => h('label', { ...attrs, for: props.id }, slots.default());
4
+ function FormMakerInputLabel(props, { slots, attrs }) {
5
+ return h('label', { ...attrs, for: props.id }, slots.default());
6
+ }
7
+
8
+ FormMakerInputLabel.props = {
9
+ id: {
10
+ type: String,
11
+ default: null,
13
12
  },
14
13
  };
14
+
15
+ export default FormMakerInputLabel;
15
16
  </script>
@@ -0,0 +1,23 @@
1
+ import { inject } from 'vue';
2
+
3
+ const useFormClasses = () => {
4
+ const labelClass = inject('labelClass', null);
5
+ const inputClass = inject('inputClass', null);
6
+ const inputWrapperClass = inject('inputWrapperClass', null);
7
+ const inputGroupClass = inject('inputGroupClass', null);
8
+ const inputErrorClass = inject('inputErrorClass', null);
9
+ const errorClass = inject('errorClass', null);
10
+ const helpTextClass = inject('helpTextClass', null);
11
+
12
+ return {
13
+ labelClass,
14
+ inputClass,
15
+ inputWrapperClass,
16
+ inputGroupClass,
17
+ inputErrorClass,
18
+ errorClass,
19
+ helpTextClass,
20
+ };
21
+ };
22
+
23
+ export default useFormClasses;