inertia-bootstrap-forms 2.1.21 → 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.
Files changed (59) hide show
  1. package/README.fa.md +468 -0
  2. package/README.md +322 -714
  3. package/dist/inertia-bootstrap-forms.cjs.js +1 -0
  4. package/dist/inertia-bootstrap-forms.es.js +3524 -30
  5. package/index.d.ts +143 -507
  6. package/package.json +89 -70
  7. package/src/AmountInput.vue +81 -76
  8. package/src/CaptchaInput.vue +87 -88
  9. package/src/CheckboxButtonInput.vue +56 -69
  10. package/src/CheckboxInput.vue +60 -79
  11. package/src/CheckboxToggle.vue +53 -136
  12. package/src/ChipsInput.vue +157 -0
  13. package/src/ComboboxInput.vue +556 -0
  14. package/src/DropzoneInput.vue +125 -103
  15. package/src/EditorInput.vue +47 -106
  16. package/src/EmailInput.vue +16 -15
  17. package/src/FileInput.vue +189 -179
  18. package/src/FormAdornInput.vue +75 -0
  19. package/src/FormContainer.vue +288 -251
  20. package/src/FormField.vue +59 -0
  21. package/src/FormLabel.vue +27 -27
  22. package/src/GroupControl.vue +55 -53
  23. package/src/LocationInput.vue +79 -72
  24. package/src/MobileInput.vue +21 -13
  25. package/src/MultiQuantityInput.vue +109 -181
  26. package/src/OTPInput.vue +144 -76
  27. package/src/PasswordInput.vue +151 -17
  28. package/src/PersianDatePickerInput.vue +63 -166
  29. package/src/QuantityInput.vue +100 -119
  30. package/src/RangeInput.vue +96 -0
  31. package/src/SimpleUploader.vue +8 -143
  32. package/src/StarRatingInput.vue +114 -138
  33. package/src/SubmitButton.vue +39 -24
  34. package/src/SwitchInput.vue +58 -83
  35. package/src/TelInput.vue +86 -120
  36. package/src/TextAreaInput.vue +29 -33
  37. package/src/TextInput.vue +42 -51
  38. package/src/UppyInput.vue +237 -324
  39. package/src/composables/useBsInstance.js +66 -0
  40. package/src/composables/useChoice.js +34 -0
  41. package/src/composables/useDisposables.js +59 -0
  42. package/src/composables/useField.js +121 -0
  43. package/src/composables/useFormApi.js +12 -0
  44. package/src/i18n.js +120 -0
  45. package/src/index.js +123 -42
  46. package/src/utils/bootstrap.js +48 -0
  47. package/src/utils/digits.js +21 -0
  48. package/src/utils/http.js +15 -0
  49. package/dist/choices-KiM-I_0P.js +0 -2955
  50. package/dist/dropdown-yh2aQrbw-XuCU1v1F.js +0 -1543
  51. package/dist/index-BRtKc2RA.js +0 -604
  52. package/dist/index-_x4UqcXz.js +0 -4938
  53. package/dist/inertia-bootstrap-forms.umd.js +0 -19
  54. package/dist/range-slider-pips-DTDOg3Q9.js +0 -3506
  55. package/dist/style.css +0 -1
  56. package/src/css/from-select.scss +0 -384
  57. package/src/css/loader.gif +0 -0
  58. package/src/css/location.scss +0 -44
  59. package/src/css/simple-upload.scss +0 -54
package/README.fa.md ADDED
@@ -0,0 +1,468 @@
1
+ <div dir="rtl">
2
+
3
+ # inertia-bootstrap-forms
4
+
5
+ > [English](./README.md) · **فارسی**
6
+
7
+ مجموعه کامپوننت‌های فرم **Bootstrap 6** برای **Vue 3 + Inertia.js** (لاراول). هر فیلد به‌صورت خودکار از طریق `<FormContainer>` به یک
8
+ [`useForm` اینرشیا](https://inertiajs.com/forms) متصل می‌شود و **مقدار تمام فیلدها در یک `v-model` مشترک (روی FormContainer) ذخیره می‌شود**؛
9
+ دیگر لازم نیست برای هر فیلد `v-model`، کلاس خطا یا `id` جداگانه تعریف کنید.
10
+
11
+ ```vue
12
+ <FormContainer v-model="formData" url="/tickets">
13
+ <FormField name="priority" label="اولویت">
14
+ <ComboboxInput name="priority" :options="priorities" />
15
+ </FormField>
16
+
17
+ <SubmitButton>
18
+ ثبت نهایی
19
+ <Icon icon="arrow-left" class="align-middle ms-1" />
20
+ </SubmitButton>
21
+ </FormContainer>
22
+ ```
23
+
24
+ توسعه و نگهداری: **[نوین ویژن](https://novinvision.com)** (Novin Vision). استفاده شخصی و تجاری رایگان است (MIT).
25
+
26
+ > **نسخه ۳ بازنویسی کامل برای Bootstrap 6 است.** پروژه‌های Bootstrap 5 باید روی `inertia-bootstrap-forms@2` بمانند.
27
+ > بخش [مهاجرت از نسخه ۲](#مهاجرت-از-نسخه-2) را ببینید.
28
+
29
+ ---
30
+
31
+ ## فهرست
32
+
33
+ 1. [تازه‌های نسخه ۳](#تازههای-نسخه-3)
34
+ 2. [پیش‌نیازها](#پیشنیازها)
35
+ 3. [نصب](#نصب)
36
+ 4. [مفاهیم اصلی](#مفاهیم-اصلی)
37
+ 5. [مرجع کامپوننت‌ها](#مرجع-کامپوننتها)
38
+ 6. [قرارداد endpoint آپلود](#قرارداد-endpoint-آپلود)
39
+ 7. [چندزبانگی](#چندزبانگی)
40
+ 8. [چرخه عمر، SSR و آزادسازی منابع](#چرخه-عمر-ssr-و-آزادسازی-منابع)
41
+ 9. [Composable ها](#composable-ها)
42
+ 10. [مهاجرت از نسخه ۲](#مهاجرت-از-نسخه-2)
43
+ 11. [نکات و محدودیت‌ها](#نکات-و-محدودیتها)
44
+ 12. [توسعه](#توسعه)
45
+
46
+ ---
47
+
48
+ ## تازه‌های نسخه 3
49
+
50
+ - بازنویسی بر پایه مارک‌آپ **Bootstrap 6**: `.check` / `.radio` / `.switch`، `.form-field`، `.btn-solid.theme-*`، `.menu`، `.alert.theme-*`، `.form-ghost` و …
51
+ - **کامپوننت‌های جدید:** `ComboboxInput` (جایگزین `Select2Input` و choices.js)، `ChipsInput`، `OTPInput` (بر پایه OTP بوت‌استرپ)، `RangeInput`، `FormAdornInput` و `FormField`.
52
+ - **هیچ استایلی همراه پکیج نیست.** نه فایل CSS و نه بلاک `<style>`؛ بوت‌استرپ 6 را در پروژه خودتان import کنید.
53
+ - همه کامپوننت‌ها event های Vue را با `emit` پشتیبانی می‌کنند (`update:modelValue`، `change` و eventهای اختصاصی) و تمام props لازم را دارند.
54
+ - پلاگین‌های جاوااسکریپت بوت‌استرپ (Combobox، Chips، OTP، Range، Menu) هنگام **mount** به‌صورت lazy بارگذاری و هنگام **unmount** آزاد (dispose) می‌شوند. لیسنرها، تایمرها، درخواست‌های XHR و نمونه‌های Dropzone و Uppy هم آزاد می‌شوند.
55
+ - سازگار با SSR: هیچ کدی هنگام import یا رندر سمت سرور به `window` / `document` دست نمی‌زند.
56
+ - دیگر به پیش‌نیازهای پنهان سراسری (`window.axios`، `maska`، `vue3-bootstrap-components`، FontAwesome) نیاز نیست.
57
+
58
+ ## پیشنیازها
59
+
60
+ | پیش‌نیاز | توضیح |
61
+ |---|---|
62
+ | `vue` ‎≥ 3.3 | |
63
+ | `@inertiajs/vue3` ‎≥ 2.0 | `FormContainer` از `useForm()` استفاده می‌کند |
64
+ | `bootstrap` ‎^6.0.0-alpha.1 | **CSS** الزامی است. پلاگین‌های JS را خود پکیج در صورت نیاز بارگذاری می‌کند. |
65
+ | `@floating-ui/dom` | وابستگی منوهای Bootstrap 6 (برای Combobox و MultiQuantityInput) |
66
+ | `dropzone` ‎^6 | فقط برای `DropzoneInput` |
67
+ | `leaflet` | فقط برای `LocationInput` |
68
+
69
+ کتابخانه‌های جانبی جزو dependencies خود پکیج هستند و lazy بارگذاری می‌شوند: `vue-tel-input`، `vue3-persian-datetime-picker`، `@tinymce/tinymce-vue`، `@vue-leaflet/vue-leaflet` و `@uppy/*`.
70
+
71
+ ## نصب
72
+
73
+ ```bash
74
+ npm install inertia-bootstrap-forms bootstrap@next @floating-ui/dom
75
+ # اختیاری
76
+ npm install dropzone leaflet
77
+ ```
78
+
79
+ ### import استایل‌ها در پروژه شما
80
+
81
+ پکیج هیچ CSS ای ندارد. بوت‌استرپ 6 (CSS کامپایل‌شده یا Sass خودتان) و فقط برای ویجت‌هایی که استفاده می‌کنید CSS کتابخانه‌های جانبی را import کنید:
82
+
83
+ ```js
84
+ // resources/js/app.js
85
+ import 'bootstrap/dist/css/bootstrap.min.css'; // Bootstrap 6 (الزامی)
86
+
87
+ import 'vue-tel-input/vue-tel-input.css'; // TelInput
88
+ import 'leaflet/dist/leaflet.css'; // LocationInput
89
+ import 'dropzone/dist/basic.css'; // DropzoneInput
90
+ import 'dropzone/dist/dropzone.css'; // DropzoneInput
91
+ import '@uppy/vue/css/style.css'; // UppyInput
92
+ ```
93
+
94
+ **نیازی به import جاوااسکریپت بوت‌استرپ نیست**؛ پلاگین‌های مورد نیاز کامپوننت‌ها را خود پکیج بارگذاری می‌کند.
95
+ اگر پروژه شما بوت‌استرپ را خودش bundle می‌کند و می‌خواهید فقط یک نسخه داشته باشید، کلاس‌ها را به پکیج بدهید:
96
+
97
+ ```js
98
+ import { Combobox, Chips, OtpInput, Range, Menu } from 'bootstrap';
99
+ import { configureBootstrap } from 'inertia-bootstrap-forms';
100
+
101
+ configureBootstrap({ Combobox, Chips, OtpInput, Range, Menu });
102
+ ```
103
+
104
+ ### ثبت کامپوننت‌ها
105
+
106
+ هر کامپوننتی را که لازم دارید import کنید، یا با نصب پلاگین همه را سراسری ثبت کنید:
107
+
108
+ ```js
109
+ import InertiaBootstrapForms from 'inertia-bootstrap-forms';
110
+
111
+ createInertiaApp({
112
+ setup({ el, App, props, plugin }) {
113
+ createApp({ render: () => h(App, props) })
114
+ .use(plugin)
115
+ .use(InertiaBootstrapForms, { locale: 'fa' }) // گزینه‌ها اختیاری هستند
116
+ .mount(el);
117
+ },
118
+ });
119
+ ```
120
+
121
+ ## مفاهیم اصلی
122
+
123
+ ### یک `v-model` برای کل فرم
124
+
125
+ ```vue
126
+ <script setup>
127
+ import { ref } from 'vue';
128
+ import { FormContainer, TextInput, ComboboxInput, SwitchInput, SubmitButton } from 'inertia-bootstrap-forms';
129
+
130
+ const formData = ref({ name: '', status: 'draft', published: false });
131
+ </script>
132
+
133
+ <template>
134
+ <FormContainer v-model="formData" url="/products" @success="onSaved">
135
+ <TextInput name="name" />
136
+ <ComboboxInput name="status" :options="['draft', 'active']" />
137
+ <SwitchInput name="published" label="منتشر شود" />
138
+ <SubmitButton />
139
+ </FormContainer>
140
+ </template>
141
+ ```
142
+
143
+ * `name` همان کلید داخل `formData` است. لازم نیست همهٔ فیلدها از ابتدا در آبجکت تعریف شده باشند: کلیدهای ناموجود به‌محض استفاده از فیلد به `formData` اضافه می‌شوند و همراه بقیه ارسال و ریست می‌شوند. تایپ در فیلد، `formData` را تغییر می‌دهد و تغییر `formData` از بیرون، فیلدها را به‌روز می‌کند.
144
+ * خطاهای اعتبارسنجی سمت سرور (`form.errors`) بر اساس `name` پیدا می‌شوند و کنترل را `.is-invalid` می‌کنند.
145
+ * در زمان `processing` بودن فرم، فیلدها غیرفعال می‌شوند.
146
+ * ارقام فارسی/عربی پیش از ارسال به ارقام انگلیسی تبدیل می‌شوند (برای خاموش کردن: `:normalize-digits="false"`). فایل‌ها (`File`) دست‌نخورده می‌مانند.
147
+ * مقدار **محدود به کانتینر نیست**: فیلدی که *بیرون* از `FormContainer` استفاده شود با `v-model` مخصوص خودش کار می‌کند.
148
+
149
+ ```vue
150
+ <TextInput v-model="title" /> <!-- مستقل -->
151
+ ```
152
+
153
+ ### Event ها
154
+
155
+ هر کامپوننت emits خودش را تعریف کرده است:
156
+
157
+ | Event | Payload | منبع |
158
+ |---|---|---|
159
+ | `update:modelValue` | `value` | همه فیلدها |
160
+ | `change` | `value, event?` | همه فیلدها |
161
+ | `input` | `value, event` | فیلدهای متنی، OTP، range |
162
+ | `focus` / `blur` | `event` | فیلدهای متنی، خانواده checkbox، range، chips |
163
+ | اختصاصی | [مرجع](#مرجع-کامپوننتها) | combobox، chips، OTP، آپلودرها … |
164
+
165
+ لیسنرهای native که اعلام نشده‌اند (`@keydown`، `@paste` و …) در کامپوننت‌های تک‌ریشه
166
+ (`TextInput`، `TextAreaInput`، `EmailInput`، `MobileInput`، `PasswordInput`، `FormAdornInput`) مستقیم به `<input>` منتقل می‌شوند.
167
+
168
+ ### Props مشترک همه فیلدها
169
+
170
+ | Prop | نوع | توضیح |
171
+ |---|---|---|
172
+ | `name` | `string` | کلید در v-model فرم (برای پیدا کردن خطا هم استفاده می‌شود) |
173
+ | `modelValue` | any | `v-model` مستقل (داخل `FormContainer` نادیده گرفته می‌شود) |
174
+ | `id` | `string` | جایگزین id خودکار (در `FormField` خودکار به label وصل می‌شود) |
175
+ | `disabled` | `boolean` | غیرفعال کردن |
176
+ | `readonly` | `boolean` | فقط‌خواندنی (یا غیرقابل‌تعامل برای کنترل‌هایی که readonly بومی ندارند) |
177
+ | `required` | `boolean` | `required` بومی |
178
+ | `invalid` | `boolean` | اجبار حالت نامعتبر؛ پیش‌فرض بر اساس خطاهای سرور است |
179
+
180
+ ### مقادیر گروهی / تکرارشونده
181
+
182
+ ```vue
183
+ <GroupControl name="items" :index="0">
184
+ <TextInput name="title" /> <!-- items[0].title -->
185
+ <QuantityInput name="qty" /> <!-- items[0].qty -->
186
+ </GroupControl>
187
+ ```
188
+
189
+ `index` اختیاری است؛ در صورت نبودن، موقعیت میان `.form-control-group` های هم‌سطح استفاده می‌شود. کلیدهای خطا مثل `items.0.title` خودکار پیدا می‌شوند.
190
+
191
+ ---
192
+
193
+ ## مرجع کامپوننت‌ها
194
+
195
+ ### ساختار فرم
196
+
197
+ #### `FormContainer`
198
+
199
+ | Prop | نوع | پیش‌فرض | توضیح |
200
+ |---|---|---|---|
201
+ | `modelValue` | `object` | `{}` | داده‌های فرم (`v-model`) |
202
+ | `url` | `string` | `''` | آدرس ارسال |
203
+ | `method` | `string` | `'post'` | متد HTTP |
204
+ | `only` | `string[]` | `[]` | گزینه `only` اینرشیا |
205
+ | `options` | `object` | `{}` | فیلدهای اضافه که در `modelValue` نیستند |
206
+ | `visitOptions` | `object` | `{}` | گزینه‌های اضافه visit اینرشیا (`preserveScroll`، `headers`، callbackها …) |
207
+ | `autoTab` | `boolean` | `true` | رفتن به فیلد بعدی هنگام رسیدن به `maxlength` |
208
+ | `resetOnSuccess` | `boolean` | `false` | ریست فرم پس از ارسال موفق |
209
+ | `submitHandler` | `(event, form) => any` | `null` | جایگزین ارسال پیش‌فرض |
210
+ | `normalizeDigits` | `boolean` | `true` | تبدیل ارقام فارسی/عربی به انگلیسی هنگام ارسال |
211
+ | `validate` | `boolean` | `false` | فعال‌سازی اعتبارسنجی سمت کلاینت Bootstrap 6 (`data-bs-validate`) |
212
+ | `htmlMessages` | `boolean` | `false` | نمایش پیام‌های سرور به‌صورت HTML (فقط برای محتوای مطمئن) |
213
+ | `hideAlerts` | `boolean` | `false` | مخفی کردن alert خطا / موفقیت داخلی |
214
+
215
+ Event ها: `update:modelValue`، `change`، `submit`، `reset`، `before`، `start`، `progress`، `success`، `error`، `cancel`، `finish`
216
+ (eventهای قدیمی `onStart`، `onFinish`، `onSuccess`، `onError` همچنان emit می‌شوند).
217
+ Slot ها: پیش‌فرض `{ form, submit, reset }`، `errors { errors, form }`، `message { message }`.
218
+ متدهای expose شده: `submit()`، `reset()`، `form`، `el`.
219
+
220
+ اگر سرور در props صفحه اینرشیا یک `message` برگرداند، به‌صورت alert موفقیت نمایش داده می‌شود.
221
+
222
+ #### `FormField`
223
+
224
+ همان `.form-field` بوت‌استرپ 6: label + کنترل + متن راهنما + بازخورد اعتبارسنجی. `id` تولیدشده خودکار به کنترل داخلی و label داده می‌شود.
225
+
226
+ | Prop | توضیح |
227
+ |---|---|
228
+ | `name` | کلید فیلد — برای خواندن خطای سرور |
229
+ | `label`، `help` | متن‌ها |
230
+ | `error` | جایگزین متن خطا |
231
+ | `required` | علامت الزامی روی label |
232
+ | `id` | جایگزین id تولیدشده |
233
+
234
+ Slot ها: پیش‌فرض، `label`، `help`، `error`.
235
+
236
+ ```vue
237
+ <FormField name="email" label="ایمیل" help="ایمیل شما نزد ما محفوظ است" required>
238
+ <EmailInput name="email" />
239
+ </FormField>
240
+ ```
241
+
242
+ #### `FormLabel`، `GroupControl`، `SubmitButton`
243
+
244
+ * `FormLabel` – `for`، `required`. داخل `FormField` یا از روی عنصر بعدی، کنترل را خودکار پیدا می‌کند.
245
+ * `GroupControl` – `name` (الزامی)، `index`.
246
+ * `SubmitButton` – `variant` (پیش‌فرض `solid`)، `theme` (پیش‌فرض `primary`)، `loading`، `disabled`، `locale`. کلاس `btn-solid theme-primary` را خودش می‌گذارد؛ با `:variant="null"` می‌توانید کلاس‌های خودتان را بدهید. هنگام `form.processing` / `form.uploading` اسپینر نشان می‌دهد. event: `click`. Slot ها: پیش‌فرض، `progress`.
247
+
248
+ ### ورودی‌های متنی
249
+
250
+ | کامپوننت | propهای اضافه | نکته |
251
+ |---|---|---|
252
+ | `TextInput` | `type`، `placeholder`، `size` (`sm`/`lg`) | `.form-control` |
253
+ | `TextAreaInput` | `rows`، `placeholder`، `size` | |
254
+ | `EmailInput` | `placeholder`، `locale` | `type="email"`؛ همه propهای `TextInput` منتقل می‌شوند |
255
+ | `MobileInput` | `placeholder`، `locale` | `type="tel"` |
256
+ | `PasswordInput` | `placeholder`، `toggle`، `locale` | `toggle` دکمه نمایش/مخفی‌کردن (form-adorn) اضافه می‌کند؛ event: `toggle` |
257
+ | `TelInput` | `placeholder`، `defaultCountry` (`ir`)، `preferredCountries`، `mode`، `validCharactersOnly`، `validate`، `dropdownOptions`، `inputOptions` | `vue-tel-input`؛ شماره بین‌المللی ذخیره می‌شود؛ eventها: `validate`، `country-changed` |
258
+ | `FormAdornInput` | `type`، `placeholder`، `startText`، `endText`، `size` | `.form-adorn` بوت‌استرپ 6. Slot ها: `start`، `end` |
259
+ | `AmountInput` | `placeholder`، `unit`، `min`، `max`، `size`، `locale` | عدد صحیح با جداکننده هزارگان؛ مقدار `number` ذخیره می‌شود |
260
+ | `QuantityInput` | `min` (0)، `max`، `step` (1)، `unit`، `locale` | شمارنده؛ eventها: `increment`، `decrement` |
261
+ | `MultiQuantityInput` | `options`، `min`، `max`، `totalMin`، `totalMax`، `unit`، `placement`، `locale` | Menu بوت‌استرپ با یک شمارنده برای هر گزینه؛ مقدار: `{ 0: 2, 1: 1 }` |
262
+
263
+ ```vue
264
+ <FormAdornInput name="website" start-text="https://">
265
+ <template #end><span class="form-adorn-icon"><svg …/></span></template>
266
+ </FormAdornInput>
267
+ ```
268
+
269
+ ### ورودی‌های انتخابی
270
+
271
+ | کامپوننت | propهای اضافه |
272
+ |---|---|
273
+ | `CheckboxInput` | `type` (`checkbox`/`radio`)، `value`، `trueValue`، `falseValue`، `checked`، `indeterminate`، `label` |
274
+ | `SwitchInput` | `value`، `trueValue`، `falseValue`، `checked`، `label` |
275
+ | `CheckboxButtonInput` | `type` (`radio`)، `value`، `variant` (`outline`)، `theme` (`primary`)، `checked` — `.btn-check` بوت‌استرپ |
276
+ | `CheckboxToggle` | `type`، `value`، `checked`، `hideInput` — کارت انتخابی (`.form-field-card`) |
277
+
278
+ قوانین مقدار مثل `v-model` بومی Vue است: مدل **آرایه** مقدارهای تیک‌خورده را جمع می‌کند، **radio** مقدار انتخاب‌شده را ذخیره می‌کند و یک چک‌باکس تکی `trueValue` / `falseValue` (پیش‌فرض `true` / `false`) را.
279
+
280
+ ```vue
281
+ <CheckboxInput name="roles" value="admin" label="مدیر" />
282
+ <CheckboxInput name="roles" value="editor" label="ویرایشگر" /> <!-- roles: ['admin','editor'] -->
283
+ <CheckboxInput name="plan" type="radio" value="pro" label="حرفه‌ای" />
284
+ ```
285
+
286
+ ### ‏`ComboboxInput` (Combobox بوت‌استرپ 6)
287
+
288
+ جایگزین `Select2Input` (choices.js). `Select2Input` به‌عنوان alias همچنان export می‌شود.
289
+
290
+ | Prop | نوع | پیش‌فرض | توضیح |
291
+ |---|---|---|---|
292
+ | `options` | `Array` | `[]` | رشته، عدد یا آبجکت `{ id \| value, name \| label, description, disabled }` |
293
+ | `valueKey` / `labelKey` | `string` | خودکار | نام فیلدهای آبجکت گزینه‌ها (`label` نام قدیمیِ `labelKey` است) |
294
+ | `descriptionKey` | `string` | `'description'` | متن ثانویه گزینه |
295
+ | `multiple` | `boolean` | `false` | انتخاب چندتایی (مدل آرایه) |
296
+ | `placeholder` | `string` | i18n | |
297
+ | `searchEnabled` | `boolean` | `false` | کادر جستجوی سمت کلاینت |
298
+ | `search` | `object` | `{ url: null }` | **جستجوی remote**: `{ url, method='POST', param='query', debounce=400, minChars=0, headers, resultsKey='data' }` |
299
+ | `clearable` | `boolean` | `false` | افزودن گزینه‌ای برای پاک کردن انتخاب (تک‌انتخابی) |
300
+ | `clearText`، `noResultsText`، `loadingText`، `searchPlaceholder`، `selectedText` | `string` | i18n | `selectedText` از `{count}` پشتیبانی می‌کند |
301
+ | `placement` | `string` | `'bottom-start'` | placement کتابخانه Floating UI |
302
+ | `size`، `hideDropdown`، `searchNormalize`، `locale` | | | |
303
+
304
+ Event ها: `update:modelValue`، `change`، `selected` (گزینه/گزینه‌ها)، `clear`، `search`، `search-success`، `search-error`، `show`، `shown`، `hide`، `hidden`.
305
+ متدهای expose شده: `show()`، `hide()`، `toggle()`، `clear()`، `focus()`.
306
+
307
+ ```vue
308
+ <ComboboxInput name="priority" :options="[{ id: 1, name: 'کم' }, { id: 2, name: 'زیاد', description: 'اول پاسخ داده می‌شود' }]" clearable />
309
+
310
+ <ComboboxInput name="customer" :search="{ url: '/api/customers', debounce: 300 }" /> <!-- remote -->
311
+
312
+ <ComboboxInput name="tags" multiple search-enabled :options="tags" />
313
+ ```
314
+
315
+ endpoint جستجو `?query=…` را دریافت می‌کند و باید آرایه JSON (یا `{ data: [] }`) برگرداند. کوکی `XSRF-TOKEN` خودکار ارسال می‌شود. گزینه‌های انتخاب‌شده حتی اگر در آخرین نتایج نباشند برچسب خود را حفظ می‌کنند.
316
+
317
+ ### ‏`ChipsInput` (Chips بوت‌استرپ 6)
318
+
319
+ مقدار: آرایه‌ای از رشته‌ها.
320
+
321
+ | Prop | پیش‌فرض | توضیح |
322
+ |---|---|---|
323
+ | `placeholder` | i18n | |
324
+ | `separator` | `','` | جداکننده متن تایپ/paste شده. `null` = غیرفعال |
325
+ | `allowDuplicates` | `false` | |
326
+ | `maxChips` | `null` | |
327
+ | `dismissible` | `true` | دکمه حذف روی chip |
328
+ | `dismissIcon` | `null` | مارک‌آپ آیکون حذف دلخواه |
329
+ | `createOnBlur` | `true` | |
330
+ | `validator` | `null` | `(value) => boolean`؛ مقدار ردشده event `rejected` می‌دهد |
331
+
332
+ Event ها: `update:modelValue`، `change`، `add`، `remove`، `select`، `rejected`، `focus`، `blur`. متدها: `add()`، `remove()`، `clear()`، `focus()`.
333
+
334
+ ### ‏`OTPInput` (OTP بوت‌استرپ 6)
335
+
336
+ | Prop | پیش‌فرض | توضیح |
337
+ |---|---|---|
338
+ | `length` (قدیمی: `maxLength`) | `6` | تعداد کاراکتر |
339
+ | `type` | `'numeric'` | `numeric` \| `alphanumeric` \| `alpha` |
340
+ | `mask` | `false` | نمایش `•` |
341
+ | `groups` | `null` | مثلاً `[3, 3]` |
342
+ | `separator` | `'·'` | |
343
+ | `autoSubmit` | `false` | ارسال `FormContainer` به‌محض کامل شدن کد |
344
+ | `autofocus` | `false` | |
345
+
346
+ Event ها: `update:modelValue`، `change`، `input`، `complete`، `focus`، `blur`. متدها: `focus()`، `clear()`.
347
+ پشت اسلات‌ها فقط یک input واقعی وجود دارد، بنابراین پر شدن خودکار از پیامک / password manager کار می‌کند.
348
+
349
+ ### ‏`RangeInput` (Range بوت‌استرپ 6)
350
+
351
+ | Prop | پیش‌فرض | توضیح |
352
+ |---|---|---|
353
+ | `min` / `max` / `step` | `0` / `100` / `1` | |
354
+ | `bubble` | `false` | حباب مقدار بالای دسته |
355
+ | `formatter` | `null` | `(value) => string` برای حباب و برچسب tickها |
356
+ | `ticks` | `[]` | `[0, 50, 100]` یا `[{ value, label }]` |
357
+
358
+ مقدار: `number`. Event ها: `update:modelValue`، `change`، `input`، `focus`، `blur`. نام قدیمی `RangeSliderInput` به‌عنوان alias باقی است.
359
+
360
+ ### سایر ورودی‌ها
361
+
362
+ | کامپوننت | ویژگی‌ها |
363
+ |---|---|
364
+ | `StarRatingInput` | `maxStars`، `precision` (تعداد گام برای هر ستاره)، `showValue`، `clearable`، `value`؛ پشتیبانی ماوس/لمس و کیبورد، سازگار با RTL، `role="slider"` |
365
+ | `CaptchaInput` | `url`، `placeholder`، `refreshOnSubmit`، `imageWidth`، `imageHeight`؛ event: `refresh` |
366
+ | `PersianDatePickerInput` | `calendar`، `view`، `min`، `max`، `range`، `format`، `inputFormat`، `pickerOptions`، `inputClass`؛ eventها: `open`، `close` |
367
+ | `EditorInput` | TinyMCE: ‏`options`، `height`، `placeholder`، `apiKey`، `tinymceScriptSrc`؛ event: `init` |
368
+ | `LocationInput` | Leaflet: ‏`center`، `zoom`، `height`، `tileUrl`، `attribution`؛ مقدار `{ lat, lng }`؛ event: `ready` |
369
+ | `FileInput` (`SimpleUploader`) | `multiple`، `endpoint`، `accept`، `headers`، `fieldName`؛ eventها: `added`، `progress`، `uploaded`، `error`، `removed` |
370
+ | `DropzoneInput` | `multiple`، `url`، `options`، `headers`؛ eventها: `ready`، `added`، `success`، `error`، `removed`، `complete` |
371
+ | `UppyInput` | `multiple`، `useXHR`، `XHRConfig`، `url`، `config`، `errorHandler`، `showRestrictionCaption`؛ همه eventهای Uppy دوباره emit می‌شوند |
372
+
373
+ ---
374
+
375
+ ## قرارداد endpoint آپلود
376
+
377
+ `FileInput`، `DropzoneInput` و `UppyInput` با یک شکل endpoint کار می‌کنند:
378
+
379
+ * ‏`POST {url}` — فیلد multipart با نام `file` ← پاسخ JSON ‏`{ "data": { "path": "tmp/abc.jpg" } }` (یا `{ "path": … }`).
380
+ * ‏`DELETE {url}` — بدنه درخواست، `path` ذخیره‌شده (`FileInput` و `DropzoneInput`) یا همان پاسخ JSON (`UppyInput`) است.
381
+
382
+ `FileInput` رشته(های) `path` را ذخیره می‌کند؛ `DropzoneInput` و `UppyInput` آبجکت(های) پاسخ را. مقدار `form.uploading` درصد پیشرفت آپلود است و `SubmitButton` تا پایان آپلودها غیرفعال می‌ماند.
383
+
384
+ ## چندزبانگی
385
+
386
+ دیکشنری‌های داخلی: `en` و `fa`. مقدار پیش‌فرض `locale: 'auto'` در صفحه‌های RTL (`dir="rtl"` / `lang="fa"`) زبان `fa` و در غیر این صورت `en` را انتخاب می‌کند.
387
+ برای جلوگیری از هرگونه اختلاف SSR / hydration، زبان را سراسری تنظیم کنید:
388
+
389
+ ```js
390
+ import { configureI18n } from 'inertia-bootstrap-forms';
391
+
392
+ configureI18n({ locale: 'fa' });
393
+ configureI18n({ messages: { fa: { 'select.noResults': 'موردی نیست' } } });
394
+ ```
395
+
396
+ هر کامپوننتی که متن دارد، prop به نام `locale` و propهای متنی صریح (`placeholder`، `noResultsText` و …) را هم می‌پذیرد.
397
+
398
+ ## چرخه عمر، SSR و آزادسازی منابع
399
+
400
+ * پلاگین‌های بوت‌استرپ در `onMounted` با `useBsInstance()` ساخته و در `onBeforeUnmount` dispose می‌شوند؛ با تغییر propهایی که فقط هنگام ساخت خوانده می‌شوند (`multiple`، `ticks`، `length` و …) خودکار دوباره ساخته می‌شوند.
401
+ * پلاگین‌ها با `import('bootstrap/js/dist/<plugin>.js')` به‌صورت lazy بارگذاری می‌شوند و bundler می‌تواند آن‌ها را code-split کند.
402
+ * ‏`useDisposables()` لیسنرها، تایمرها، `AbortController`ها، XHRها و نمونه‌های Dropzone و Uppy را ثبت می‌کند و هنگام unmount آزاد می‌کند (حتی اگر کامپوننت در حین بارگذاری پلاگین unmount شود).
403
+ * رندر سمت سرور فقط مارک‌آپ ایستا را تولید می‌کند؛ ویجت‌هایی که به DOM نیاز دارند (Combobox، Chips، OTP، Range، Leaflet، تاریخ‌ساز و …) پس از hydrate شروع به کار می‌کنند.
404
+
405
+ ## Composable ها
406
+
407
+ با همان زیرساخت می‌توانید فیلد اختصاصی بسازید:
408
+
409
+ ```vue
410
+ <script setup>
411
+ import { fieldProps, useField } from 'inertia-bootstrap-forms';
412
+
413
+ const props = defineProps({ ...fieldProps, color: String });
414
+ const emit = defineEmits(['update:modelValue', 'change']);
415
+ const { model, inputId, isInvalid, isDisabled, emitChange } = useField(props, emit);
416
+ </script>
417
+
418
+ <template>
419
+ <input type="color" v-model="model" :id="inputId" :disabled="isDisabled" :class="{ 'is-invalid': isInvalid }" @change="emitChange(model, $event)" />
420
+ </template>
421
+ ```
422
+
423
+ علاوه بر این export می‌شوند: `useFormApi()`، `useDisposables()`، `useBsInstance()`، `configureBootstrap()`، `configureI18n()`، `toEnglishDigits()`، `countryCodes`.
424
+
425
+ ---
426
+
427
+ ## مهاجرت از نسخه 2
428
+
429
+ | نسخه ۲ | نسخه ۳ |
430
+ |---|---|
431
+ | کلاس‌های Bootstrap 5 ‏(`.form-check`، `.btn-primary` و …) | Bootstrap 6 ‏(`.check`، `.btn-solid.theme-primary` و …). کلاس `.btn-primary` در بوت‌استرپ 6 وجود ندارد: `SubmitButton` خودش `btn-solid theme-primary` می‌گذارد — به‌جای `class="btn btn-primary"` از propهای `variant` / `theme` استفاده کنید |
432
+ | ‏`import 'inertia-bootstrap-forms/dist/style.css'` | **حذف شد** — هیچ CSS ای همراه پکیج نیست. بوت‌استرپ 6 و CSS کتابخانه‌های جانبی را خودتان import کنید |
433
+ | ‏`Select2Input` (choices.js) | ‏`ComboboxInput`؛ ‏`Select2Input` به‌عنوان alias می‌ماند. آبجکت `config` مربوط به choices.js حذف شد — از propهای مستندشده استفاده کنید. نام فیلدهای گزینه‌ها با `valueKey` / `labelKey` تعیین می‌شود (`label` به‌عنوان نام قدیمی هنوز کار می‌کند) |
434
+ | مقدار select همیشه رشته بود | مقدار select نوع اصلی گزینه را حفظ می‌کند (`id` عددی، عدد می‌ماند) |
435
+ | ‏`RangeSliderInput` (دو دسته، svelte-range-slider-pips) | ‏`RangeInput` (یک دسته، Bootstrap 6). بوت‌استرپ 6 range دوتایی ندارد |
436
+ | طول پیش‌فرض `OTPInput` برابر `5` | پیش‌فرض `6`؛ برای حفظ اندازه قبلی `:length="5"` بدهید. event ‏`complete` دارد؛ به‌جای `data-auto-submit-length` از `auto-submit` استفاده کنید |
437
+ | event ‏`change` در `CheckboxInput` خود event DOM را می‌داد | در همه فیلدها `change(value, event)` |
438
+ | ‏`StarRatingInput` رشته‌هایی مثل `"3.5"` ذخیره می‌کرد | عدد ذخیره می‌کند |
439
+ | ‏`FormContainer` پیام‌های سرور را با `v-html` نشان می‌داد | به‌طور پیش‌فرض متن ساده؛ برای HTML از `html-messages` استفاده کنید |
440
+ | ‏`FormContainer` متدهای کمکی را هم داخل داده emit می‌کرد | فقط داده‌های فرم emit می‌شود |
441
+ | ‏`FileInput` به `window.axios` نیاز داشت | از `XMLHttpRequest` و `fetch` استفاده می‌کند و کوکی `XSRF-TOKEN` را می‌فرستد |
442
+ | ‏`AmountInput` / `QuantityInput` به `maska` نیاز داشتند | داخلی است (ارقام فارسی پشتیبانی می‌شود) |
443
+ | ‏`vue3-bootstrap-components`، `choices.js`، `svelte-range-slider-pips` داخل bundle بودند | حذف شدند |
444
+ | ‏`name` الزامی بود | اختیاری است — برای استفاده مستقل با `v-model` می‌توان آن را نداد |
445
+ | پیش‌نیاز: CSS بوت‌استرپ 5 | پیش‌نیاز: ‏`bootstrap@^6.0.0-alpha.1` و `@floating-ui/dom` |
446
+
447
+ ## نکات و محدودیتها
448
+
449
+ * بوت‌استرپ 6 در حال حاضر در مرحله **alpha** است؛ نام کلاس‌ها و API پلاگین‌ها ممکن است تا انتشار پایدار تغییر کند.
450
+ * ‏Combobox بوت‌استرپ 6 روی همان عنصر toggle یک `Menu` هم می‌سازد و به همین دلیل بوت‌استرپ پیام *"Bootstrap doesn't allow more than one instance per element"* را در کنسول چاپ می‌کند. این پیام از خود بوت‌استرپ است و کامپوننت عادی کار می‌کند.
451
+ * ‏Datepicker بوت‌استرپ 6 تقویم جلالی ندارد؛ بنابراین `PersianDatePickerInput` همچنان از `vue3-persian-datetime-picker` استفاده می‌کند. ظاهر آن را با CSS خودتان تنظیم کنید.
452
+ * ‏`LocationInput` در صفحه‌های RTL: پنل‌های Leaflet به `left: auto; right: 0` نیاز دارند — اگر نقشه جابه‌جا نمایش داده شد، این قانون را در CSS خودتان اضافه کنید.
453
+ * ‏`AmountInput` / `QuantityInput` اعداد صحیح را پشتیبانی می‌کنند.
454
+ * ‏attribute ‏`data-bs-toggle` روی toggle های Combobox / Menu برای مدیریت کلیک توسط data-API بوت‌استرپ لازم است و کامپوننت‌ها آن را رندر می‌کنند.
455
+
456
+ ## توسعه
457
+
458
+ ```bash
459
+ npm install
460
+ npm test # vitest + jsdom (کامپوننت‌ها، چرخه عمر پلاگین‌های بوت‌استرپ، SSR)
461
+ npm run build # خروجی کتابخانه با vite ← dist/
462
+ ```
463
+
464
+ ---
465
+
466
+ توسعه و نگهداری: **[نوین ویژن](https://novinvision.com)** · مجوز MIT
467
+
468
+ </div>