inertia-bootstrap-forms 2.1.20 → 3.0.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.
- package/README.fa.md +468 -0
- package/README.md +322 -714
- package/dist/inertia-bootstrap-forms.cjs.js +1 -0
- package/dist/inertia-bootstrap-forms.es.js +3524 -30
- package/index.d.ts +143 -507
- package/package.json +89 -70
- package/src/AmountInput.vue +81 -76
- package/src/CaptchaInput.vue +87 -88
- package/src/CheckboxButtonInput.vue +56 -69
- package/src/CheckboxInput.vue +60 -79
- package/src/CheckboxToggle.vue +53 -136
- package/src/ChipsInput.vue +157 -0
- package/src/ComboboxInput.vue +556 -0
- package/src/DropzoneInput.vue +125 -103
- package/src/EditorInput.vue +47 -106
- package/src/EmailInput.vue +16 -15
- package/src/FileInput.vue +189 -179
- package/src/FormAdornInput.vue +75 -0
- package/src/FormContainer.vue +288 -251
- package/src/FormField.vue +59 -0
- package/src/FormLabel.vue +27 -27
- package/src/GroupControl.vue +55 -53
- package/src/LocationInput.vue +79 -72
- package/src/MobileInput.vue +21 -13
- package/src/MultiQuantityInput.vue +109 -181
- package/src/OTPInput.vue +144 -76
- package/src/PasswordInput.vue +151 -17
- package/src/PersianDatePickerInput.vue +63 -166
- package/src/QuantityInput.vue +100 -119
- package/src/RangeInput.vue +96 -0
- package/src/SimpleUploader.vue +8 -143
- package/src/StarRatingInput.vue +114 -138
- package/src/SubmitButton.vue +39 -24
- package/src/SwitchInput.vue +58 -83
- package/src/TelInput.vue +86 -120
- package/src/TextAreaInput.vue +29 -33
- package/src/TextInput.vue +42 -51
- package/src/UppyInput.vue +237 -324
- package/src/composables/useBsInstance.js +66 -0
- package/src/composables/useChoice.js +34 -0
- package/src/composables/useDisposables.js +59 -0
- package/src/composables/useField.js +121 -0
- package/src/composables/useFormApi.js +12 -0
- package/src/i18n.js +120 -0
- package/src/index.js +123 -42
- package/src/utils/bootstrap.js +48 -0
- package/src/utils/digits.js +21 -0
- package/src/utils/http.js +15 -0
- package/dist/choices-KiM-I_0P.js +0 -2955
- package/dist/dropdown-yh2aQrbw-XuCU1v1F.js +0 -1543
- package/dist/index-BRtKc2RA.js +0 -604
- package/dist/index-_x4UqcXz.js +0 -4938
- package/dist/inertia-bootstrap-forms.umd.js +0 -19
- package/dist/range-slider-pips-DTDOg3Q9.js +0 -3506
- package/dist/style.css +0 -1
- package/src/css/from-select.scss +0 -384
- package/src/css/loader.gif +0 -0
- package/src/css/location.scss +0 -44
- package/src/css/simple-upload.scss +0 -54
package/src/FormLabel.vue
CHANGED
|
@@ -1,34 +1,34 @@
|
|
|
1
1
|
<script>
|
|
2
|
-
import {defineComponent} from
|
|
2
|
+
import { computed, defineComponent, inject, onMounted, ref } from 'vue';
|
|
3
3
|
|
|
4
4
|
export default defineComponent({
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
5
|
+
name: 'FormLabel',
|
|
6
|
+
props: {
|
|
7
|
+
/** id of the control. Detected automatically inside <FormField> or from the next element. */
|
|
8
|
+
for: { type: String, default: null },
|
|
9
|
+
required: { type: Boolean, default: false },
|
|
10
|
+
},
|
|
11
|
+
setup(props) {
|
|
12
|
+
const ctx = inject('formField', null);
|
|
13
|
+
const el = ref(null);
|
|
14
|
+
const detected = ref(null);
|
|
15
|
+
|
|
16
|
+
onMounted(() => {
|
|
17
|
+
if (props.for || ctx?.id) return;
|
|
18
|
+
const sibling = el.value?.nextElementSibling;
|
|
19
|
+
if (sibling) detected.value = sibling.id || sibling.querySelector('[id]')?.id || null;
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
const target = computed(() => props.for || ctx?.id || detected.value || undefined);
|
|
23
|
+
|
|
24
|
+
return { el, target };
|
|
25
|
+
},
|
|
26
|
+
});
|
|
22
27
|
</script>
|
|
23
28
|
|
|
24
29
|
<template>
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
30
|
+
<label ref="el" class="form-label" :for="target">
|
|
31
|
+
<slot />
|
|
32
|
+
<span v-if="required" class="fg-danger" aria-hidden="true"> *</span>
|
|
33
|
+
</label>
|
|
29
34
|
</template>
|
|
30
|
-
<style scoped>
|
|
31
|
-
label {
|
|
32
|
-
cursor: pointer;
|
|
33
|
-
}
|
|
34
|
-
</style>
|
package/src/GroupControl.vue
CHANGED
|
@@ -1,67 +1,69 @@
|
|
|
1
1
|
<script>
|
|
2
|
-
import {computed, defineComponent, inject} from
|
|
2
|
+
import { computed, defineComponent, inject, onMounted, provide, ref } from 'vue';
|
|
3
3
|
|
|
4
4
|
export default defineComponent({
|
|
5
|
+
name: 'GroupControl',
|
|
5
6
|
props: {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
},
|
|
7
|
+
/** Key of the object / array inside the form that holds the grouped values. */
|
|
8
|
+
name: { type: String, required: true },
|
|
9
|
+
/** Explicit item index (repeaters). When omitted it is detected from the DOM position. */
|
|
10
|
+
index: { type: [Number, String], default: null },
|
|
10
11
|
},
|
|
11
|
-
setup(props
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
setup(props) {
|
|
13
|
+
const form = inject('form', null);
|
|
14
|
+
const groupEl = ref(null);
|
|
15
|
+
const detected = ref('0');
|
|
16
|
+
|
|
17
|
+
const index = computed(() => (props.index !== null ? String(props.index) : detected.value));
|
|
18
|
+
|
|
19
|
+
onMounted(() => {
|
|
20
|
+
if (props.index !== null) return;
|
|
21
|
+
const siblings = groupEl.value?.parentNode?.parentNode?.querySelectorAll('.form-control-group');
|
|
22
|
+
siblings?.forEach((item, i) => {
|
|
23
|
+
if (item === groupEl.value) detected.value = String(i);
|
|
24
|
+
});
|
|
17
25
|
});
|
|
18
26
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
if (this.groupID && this.form[this.name]?.length >= 0) {
|
|
39
|
-
if(!this.form[this.name][this.groupID]){
|
|
40
|
-
this.form[this.name][this.groupID] = {};
|
|
27
|
+
const api = {
|
|
28
|
+
get name() {
|
|
29
|
+
return props.name;
|
|
30
|
+
},
|
|
31
|
+
get index() {
|
|
32
|
+
return index.value;
|
|
33
|
+
},
|
|
34
|
+
getData(field) {
|
|
35
|
+
const container = form?.value?.[props.name];
|
|
36
|
+
if (container === null || container === undefined) return null;
|
|
37
|
+
if (Array.isArray(container)) return container[Number(index.value)]?.[field] ?? null;
|
|
38
|
+
return container[field] ?? null;
|
|
39
|
+
},
|
|
40
|
+
setData(field, value) {
|
|
41
|
+
const f = form?.value;
|
|
42
|
+
if (!f) return;
|
|
43
|
+
|
|
44
|
+
if (f[props.name] === null || f[props.name] === undefined) {
|
|
45
|
+
f[props.name] = props.index !== null ? [] : {};
|
|
41
46
|
}
|
|
42
|
-
this.form[this.name][this.groupID][fieldName] = value;
|
|
43
|
-
}else{
|
|
44
|
-
this.form[this.name][fieldName] = value;
|
|
45
|
-
}
|
|
46
47
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
},
|
|
56
|
-
data() {
|
|
57
|
-
return {
|
|
58
|
-
groupID: '0',
|
|
48
|
+
if (Array.isArray(f[props.name])) {
|
|
49
|
+
const i = Number(index.value);
|
|
50
|
+
if (!f[props.name][i]) f[props.name][i] = {};
|
|
51
|
+
f[props.name][i][field] = value;
|
|
52
|
+
} else {
|
|
53
|
+
f[props.name][field] = value;
|
|
54
|
+
}
|
|
55
|
+
},
|
|
59
56
|
};
|
|
60
|
-
|
|
61
|
-
|
|
57
|
+
|
|
58
|
+
provide('group', computed(() => api));
|
|
59
|
+
|
|
60
|
+
return { groupEl, index };
|
|
61
|
+
},
|
|
62
|
+
});
|
|
62
63
|
</script>
|
|
64
|
+
|
|
63
65
|
<template>
|
|
64
|
-
<div class="form-control-group" :data-name="name" :data-group-id="
|
|
65
|
-
<slot/>
|
|
66
|
+
<div ref="groupEl" class="form-control-group" :data-name="name" :data-group-id="index">
|
|
67
|
+
<slot />
|
|
66
68
|
</div>
|
|
67
69
|
</template>
|
package/src/LocationInput.vue
CHANGED
|
@@ -1,84 +1,91 @@
|
|
|
1
1
|
<script>
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import {LMap, LTileLayer, LMarker} from "@vue-leaflet/vue-leaflet";
|
|
5
|
-
import {defineComponent, inject} from "vue";
|
|
2
|
+
import { computed, defineAsyncComponent, defineComponent, onMounted, ref, watch } from 'vue';
|
|
3
|
+
import { fieldProps, useField } from './composables/useField.js';
|
|
6
4
|
|
|
5
|
+
const LMap = defineAsyncComponent(() => import('@vue-leaflet/vue-leaflet').then((m) => m.LMap));
|
|
6
|
+
const LTileLayer = defineAsyncComponent(() => import('@vue-leaflet/vue-leaflet').then((m) => m.LTileLayer));
|
|
7
|
+
const LMarker = defineAsyncComponent(() => import('@vue-leaflet/vue-leaflet').then((m) => m.LMarker));
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Leaflet map picker. The value is `{ lat, lng }`.
|
|
11
|
+
* Import `leaflet/dist/leaflet.css` in your application.
|
|
12
|
+
*/
|
|
7
13
|
export default defineComponent({
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
default: false,
|
|
21
|
-
},
|
|
22
|
-
readonly: {
|
|
23
|
-
type: [Boolean, String],
|
|
24
|
-
default: false,
|
|
14
|
+
name: 'LocationInput',
|
|
15
|
+
components: { LMap, LTileLayer, LMarker },
|
|
16
|
+
emits: ['update:modelValue', 'change', 'ready'],
|
|
17
|
+
props: {
|
|
18
|
+
...fieldProps,
|
|
19
|
+
name: { type: String, default: 'location' },
|
|
20
|
+
/** Initial map centre when there is no value yet. */
|
|
21
|
+
center: { type: Object, default: () => ({ lat: 35.69293, lng: 51.36372 }) },
|
|
22
|
+
zoom: { type: Number, default: 13 },
|
|
23
|
+
height: { type: [Number, String], default: 300 },
|
|
24
|
+
tileUrl: { type: String, default: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png' },
|
|
25
|
+
attribution: { type: String, default: '© OpenStreetMap contributors' },
|
|
25
26
|
},
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
setup(props, { emit }) {
|
|
28
|
+
const field = useField(props, emit);
|
|
29
|
+
const ready = ref(false);
|
|
30
|
+
const zoomLevel = ref(props.zoom);
|
|
30
31
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
32
|
+
onMounted(() => {
|
|
33
|
+
ready.value = true; // Leaflet touches `window`, so it is rendered on the client only
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
const marker = computed(() => {
|
|
37
|
+
const v = field.model.value;
|
|
38
|
+
return v?.lat !== undefined && v?.lat !== null && v?.lng !== undefined && v?.lng !== null
|
|
39
|
+
? { lat: Number(v.lat), lng: Number(v.lng) }
|
|
40
|
+
: null;
|
|
41
|
+
});
|
|
40
42
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
methods: {
|
|
44
|
-
setMarker(event) {
|
|
45
|
-
if(this.readonly) return;
|
|
43
|
+
const mapCenter = computed(() => marker.value ?? props.center);
|
|
44
|
+
const locked = computed(() => props.readonly || field.isDisabled.value);
|
|
46
45
|
|
|
47
|
-
|
|
48
|
-
lat: event?.latlng?.lat || event?.target?._latlng?.lat,
|
|
49
|
-
lng: event?.latlng?.lng || event?.target?._latlng?.lng,
|
|
50
|
-
};
|
|
46
|
+
watch(() => props.zoom, (z) => (zoomLevel.value = z));
|
|
51
47
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
data() {
|
|
57
|
-
return {
|
|
58
|
-
zoom: 13,
|
|
59
|
-
marker: {
|
|
60
|
-
lat: this.modelValue?.lat || this.form[this.name]?.lat || null,
|
|
61
|
-
lng: this.modelValue?.lng || this.form[this.name]?.lng || null,
|
|
62
|
-
},
|
|
63
|
-
center: {
|
|
64
|
-
lat: this.modelValue?.lat || this.form[this.name]?.lat || 35.69293,
|
|
65
|
-
lng: this.modelValue?.lng || this.form[this.name]?.lng || 51.36372,
|
|
66
|
-
},
|
|
67
|
-
};
|
|
68
|
-
},
|
|
48
|
+
function setMarker(event) {
|
|
49
|
+
if (locked.value) return;
|
|
50
|
+
const latlng = event?.latlng ?? event?.target?._latlng;
|
|
51
|
+
if (!latlng) return;
|
|
69
52
|
|
|
70
|
-
}
|
|
53
|
+
const next = { lat: latlng.lat, lng: latlng.lng };
|
|
54
|
+
field.model.value = next;
|
|
55
|
+
field.emitChange(next, event);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
...field,
|
|
60
|
+
ready,
|
|
61
|
+
zoomLevel,
|
|
62
|
+
marker,
|
|
63
|
+
mapCenter,
|
|
64
|
+
locked,
|
|
65
|
+
setMarker,
|
|
66
|
+
heightCss: computed(() => (typeof props.height === 'number' ? `${props.height}px` : props.height)),
|
|
67
|
+
};
|
|
68
|
+
},
|
|
69
|
+
});
|
|
71
70
|
</script>
|
|
71
|
+
|
|
72
72
|
<template>
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
73
|
+
<div
|
|
74
|
+
class="location-map-container position-relative overflow-hidden"
|
|
75
|
+
:class="{ disabled: locked, 'is-invalid': isInvalid }"
|
|
76
|
+
:id="inputId"
|
|
77
|
+
:style="{ width: '100%', height: heightCss }"
|
|
78
|
+
>
|
|
79
|
+
<l-map
|
|
80
|
+
v-if="ready"
|
|
81
|
+
v-model:zoom="zoomLevel"
|
|
82
|
+
:use-global-leaflet="false"
|
|
83
|
+
:center="mapCenter"
|
|
84
|
+
@click="setMarker"
|
|
85
|
+
@ready="$emit('ready', $event)"
|
|
86
|
+
>
|
|
87
|
+
<l-tile-layer :url="tileUrl" :attribution="attribution" layer-type="base" name="OpenStreetMap" />
|
|
88
|
+
<l-marker v-if="marker" :lat-lng="marker" :draggable="!locked" @dragend="setMarker" />
|
|
89
|
+
</l-map>
|
|
90
|
+
</div>
|
|
84
91
|
</template>
|
package/src/MobileInput.vue
CHANGED
|
@@ -1,23 +1,31 @@
|
|
|
1
1
|
<script>
|
|
2
|
-
import
|
|
3
|
-
import
|
|
2
|
+
import { defineComponent } from 'vue';
|
|
3
|
+
import TextInput from './TextInput.vue';
|
|
4
|
+
import { useI18n } from './i18n.js';
|
|
4
5
|
|
|
6
|
+
/** Thin wrapper around TextInput (type="tel"). All TextInput props and events are forwarded. */
|
|
5
7
|
export default defineComponent({
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
},
|
|
8
|
+
name: 'MobileInput',
|
|
9
|
+
components: { TextInput },
|
|
9
10
|
props: {
|
|
10
|
-
name: {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
name: { type: String, default: 'mobile' },
|
|
12
|
+
placeholder: { type: String, default: null },
|
|
13
|
+
locale: { type: String, default: null },
|
|
14
|
+
},
|
|
15
|
+
setup(props) {
|
|
16
|
+
const { t } = useI18n(() => props.locale);
|
|
17
|
+
return { t };
|
|
15
18
|
},
|
|
16
|
-
})
|
|
19
|
+
});
|
|
17
20
|
</script>
|
|
21
|
+
|
|
18
22
|
<template>
|
|
19
23
|
<TextInput
|
|
24
|
+
type="tel"
|
|
25
|
+
inputmode="tel"
|
|
26
|
+
autocomplete="tel"
|
|
27
|
+
class="form-control-mobile fanum"
|
|
20
28
|
:name="name"
|
|
21
|
-
placeholder="
|
|
22
|
-
|
|
29
|
+
:placeholder="placeholder ?? t('mobile.placeholder')"
|
|
30
|
+
/>
|
|
23
31
|
</template>
|