@appsbd/vue3-appsbd-ui 1.0.13 → 1.0.14

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.
@@ -0,0 +1,211 @@
1
+ # AbChatInput — AI Reference
2
+
3
+ A ChatGPT-style auto-growing chat/message composer. Use for AI chat interfaces, comment boxes that need to grow with content, or any message input needing configurable Enter/Shift+Enter/Ctrl+Enter submit behavior. Pair with `AbChatInputMenuItem` for the built-in add/more dropdown.
4
+
5
+ ## Installation
6
+
7
+ Not globally registered — import it explicitly:
8
+
9
+ ```js
10
+ import { AbChatInput, AbChatInputMenuItem } from 'vue3-appsbd-ui';
11
+ ```
12
+
13
+ No extra peer deps beyond the library's own (`@lucide/vue` for the default "+" icon).
14
+
15
+ ## Usage
16
+
17
+ ```vue
18
+ <template>
19
+ <AbChatInput v-model="message" placeholder="Ask anything" @submit="onSubmit">
20
+ <template #right>
21
+ <button type="button" class="btn btn-primary btn-sm rounded-circle">
22
+ <ArrowUp :size="16" />
23
+ </button>
24
+ </template>
25
+ </AbChatInput>
26
+ </template>
27
+
28
+ <script setup>
29
+ import { ref } from 'vue';
30
+ import { AbChatInput } from 'vue3-appsbd-ui';
31
+ import { ArrowUp } from '@lucide/vue';
32
+
33
+ const message = ref('');
34
+ function onSubmit({ message, files }) {
35
+ console.log(message, files);
36
+ }
37
+ </script>
38
+ ```
39
+
40
+ Add/more dropdown (0 items = no trigger, 1 item = direct action with no popover, 2+ items = a real dropdown):
41
+
42
+ ```vue
43
+ <template>
44
+ <AbChatInput v-model="message">
45
+ <template #multiple-input>
46
+ <AbChatInputMenuItem v-for="item in menuItems" :key="item.name" v-bind="item" />
47
+ </template>
48
+ </AbChatInput>
49
+ </template>
50
+ ```
51
+
52
+ File attachments — selection stays the consumer's job, validation/storage/preview are internal. Every content
53
+ slot (`left`/`right`/`menu-trigger`/`single-input`/`multiple-input`) is scoped, handing back `addToAttach` (opens
54
+ a native file picker and routes the selection through `addFiles`) among other methods — no template
55
+ `<input type="file">` and no ref needed:
56
+
57
+ ```vue
58
+ <template>
59
+ <AbChatInput
60
+ v-model="message"
61
+ v-model:files="attachedFiles"
62
+ :max-file-size="2"
63
+ :allowed-extensions="['jpg', 'jpeg', 'png', 'pdf']"
64
+ >
65
+ <template #single-input="{ addToAttach }">
66
+ <AbChatInputMenuItem
67
+ :icon="Paperclip"
68
+ name="Upload file"
69
+ :action="() => addToAttach({ accept: '.jpg,.jpeg,.png,.pdf' })"
70
+ />
71
+ </template>
72
+ </AbChatInput>
73
+ </template>
74
+ ```
75
+
76
+ ## Voice input
77
+
78
+ Uses the browser's native Web Speech API (`SpeechRecognition`) — no extra dependency, no API key. A built-in mic
79
+ button renders in the `right` area (next to the `right` slot) whenever `showVoiceButton` is `true` (default).
80
+ Clicking it (or calling `startRecording()`) swaps the whole composer row for a full-width recording bar — a live
81
+ audio-reactive waveform (via `getUserMedia` + `AnalyserNode`) plus three controls:
82
+
83
+ - **✕ cancel** — discards everything spoken this session, reverting `modelValue` to what it was before recording started.
84
+ - **■ stop** — stops recording and keeps the transcribed text in the textarea (no submit).
85
+ - **↑ submit** — stops recording, keeps the text, then fires `submit` immediately.
86
+
87
+ Recognized speech is appended to the existing `modelValue` (not replaced), one final phrase at a time — interim
88
+ (still-speaking) text is not shown live.
89
+
90
+ A small language-code badge (e.g. `EN`, `BN`) sits on the mic button's corner. Clicking it opens an `AbPopover`
91
+ with a searchable list of ~130 BCP-47 locales; picking one sets that recognition session's `SpeechRecognition.lang`
92
+ and persists the choice to `localStorage` (key `apbd-chat-input-voice-lang`) so it's remembered next visit. This
93
+ never touches the browser's own language/locale settings — only this component's own recognizer instance.
94
+
95
+ Not supported in Firefox/Safari — the mic button still renders there but is `disabled`, with a tooltip explaining
96
+ why. Requires the browser's mic permission prompt to be accepted.
97
+
98
+ ## Props
99
+
100
+ | Name | Type | Default | Description |
101
+ | --- | --- | --- | --- |
102
+ | `modelValue` | `String` | `""` | Two-way-bound message text. |
103
+ | `files` | `Array` | `[]` | v-model:files — see "Attached file shape" below. |
104
+ | `placeholder` | `String` | `""` | Textarea placeholder (translated). |
105
+ | `disabled` | `Boolean` | `false` | Disables the textarea and the menu trigger. |
106
+ | `readonly` | `Boolean` | `false` | Makes the textarea read-only. |
107
+ | `loading` | `Boolean` | `false` | Visual loading state (CSS hook only — does not itself disable input). |
108
+ | `minHeight` | `Number` | `52` | Minimum textarea height in px. Also the threshold: past this, the composer switches to the "grown" stacked layout (see below). |
109
+ | `maxHeight` | `Number` | `200` | Maximum textarea height in px; internal scroll kicks in beyond this. |
110
+ | `submitOnEnter` | `Boolean` | `true` | `true`: Enter submits, Shift+Enter inserts a newline. `false`: Enter inserts a newline, Ctrl+Enter/Cmd+Enter submits. |
111
+ | `allowNewLine` | `Boolean` | `true` | If `false`, Enter never inserts a newline regardless of `submitOnEnter`. |
112
+ | `autoFocus` | `Boolean` | `false` | Focus the textarea on mount. |
113
+ | `autoPreview` | `Boolean` | `true` | When `true`, clicking a file chip opens the built-in preview lightbox. When `false`, only the `file-click` event fires. |
114
+ | `maxFileSize` | `Number` | `null` | Max size in MB per file. `null` = no limit. Enforced by `addFiles()`. |
115
+ | `allowedExtensions` | `Array` | `[]` | Case-insensitive allowed extensions (e.g. `['jpg','png','pdf']`). Empty = all types allowed. Enforced by `addFiles()`. |
116
+ | `containerClass` | `String` | `undefined` | Wrapper class; falls back to `globalConfig.inputContainerClass`. |
117
+ | `borderRadius` | `String` | `"1.5rem"` | Composer roundness — any CSS length (e.g. `"0.5rem"`, `"8px"`, `"1.5rem"`). |
118
+ | `filePreviewWidth` | `Number` | `24` | Width (px) of each attached file's thumbnail/icon in the chip. |
119
+ | `filePreviewHeight` | `Number` | `24` | Height (px) of each attached file's thumbnail/icon in the chip. |
120
+ | `showFileName` | `Boolean` | `true` | Show/hide the filename text next to the chip thumbnail. |
121
+ | `showVoiceButton` | `Boolean` | `true` | Show/hide the built-in mic button (see "Voice input" below). Set `false` to build custom voice UI via slots instead. |
122
+
123
+ ## Events
124
+
125
+ | Event | Payload | Description |
126
+ | --- | --- | --- |
127
+ | `update:modelValue` | `string` | v-model sync. |
128
+ | `update:files` | `FileEntry[]` | v-model:files sync — fired after `addFiles`/`removeFile`/`clearFiles`. |
129
+ | `submit` | `{ message: string, files: FileEntry[] }` | Fired on Enter/Ctrl+Enter/Cmd+Enter per `submitOnEnter`. Not fired when `disabled`/`readonly`. |
130
+ | `focus` / `blur` | `FocusEvent` | Forwarded native events. |
131
+ | `keydown` | `KeyboardEvent` | Forwarded native event; fires before any submit/newline logic runs. |
132
+ | `file-click` | `FileEntry` | A file chip was clicked (fires regardless of `autoPreview`). |
133
+ | `file-remove` | `FileEntry` | A file chip's remove button was clicked. |
134
+ | `file-reject` | `{ file: File, reason: string }` | A file failed `maxFileSize`/`allowedExtensions` validation. A `useToast` error is also shown. |
135
+ | `voice-start` | — | Voice recording started (mic button clicked or `startRecording()` called). |
136
+ | `voice-stop` | — | Recording stopped normally (■ button, ↑ submit button, `stopRecording()`, or the browser ending it on its own) — transcribed text is kept in `modelValue`. |
137
+ | `voice-cancel` | — | Recording was cancelled (✕ button or `cancelRecording()`) — `modelValue` is reverted to what it was before recording started. |
138
+ | `voice-error` | `Error \| SpeechRecognitionErrorEvent` | Mic permission was denied, or the recognition engine errored. A `useToast` error is also shown (except benign `no-speech`/`aborted` errors). |
139
+
140
+ ## Slots
141
+
142
+ | Name | Description |
143
+ | --- | --- |
144
+ | `left` | Left action area (e.g. an attachment button). Scoped — see below. |
145
+ | `right` | Right action area (e.g. send/mic button). Scoped — see below. |
146
+ | `menu-trigger` | Overrides the default "+" icon on the add/more trigger. Only rendered when the trigger itself renders (see below). Scoped — see below. |
147
+ | `single-input` | One static `AbChatInputMenuItem` for the add/more dropdown. Scoped — see below. |
148
+ | `multiple-input` | A `v-for`'d set of `AbChatInputMenuItem`s for the add/more dropdown. Scoped — see below. |
149
+
150
+ **All five slots are scoped**, receiving the same props so slot content can drive the composer directly:
151
+
152
+ | Scoped prop | Description |
153
+ | --- | --- |
154
+ | `addToAttach(options?)` | Opens a native file picker and routes the selection through `addFiles`. `options`: `{ accept, multiple }` (`multiple` defaults to `true`). |
155
+ | `addFiles(files)` | Same as the exposed method — validates and appends an already-obtained `File[]`/`FileList` (drag-drop, paste, etc.). |
156
+ | `removeFile(id)` | Same as the exposed method. |
157
+ | `clearFiles()` | Same as the exposed method. |
158
+ | `updateText(text)` | Replaces the message text (fires `update:modelValue`). |
159
+ | `focus()` | Focuses the textarea. |
160
+
161
+ A single-item `#single-input`/`#multiple-input` action can use these directly — `onMenuTriggerClick`'s no-dropdown
162
+ fast path reads the item's `action` prop with these same bindings applied, so `:action="addToAttach"` works there too.
163
+
164
+ **Layout — single line vs. "grown"**: below `minHeight` worth of content (one line), `left`/textarea/`right` share a single row, centered. Past that, the composer switches to a ChatGPT-style stacked layout: the textarea becomes its own full-width row on top, and `left`/`right` wrap onto a second full-width row below it (`right` stays pinned to that row's end even when `left` isn't rendered at all). This is purely CSS-driven off the same measured content height — no extra prop.
165
+
166
+ **Add/more trigger behavior** is driven by the total count of valid items across `single-input` + `multiple-input` combined, not by which slot they came from:
167
+ - 0 items → no trigger renders at all (fully backward-compatible with plain `left`-slot-only usage).
168
+ - 1 item → the trigger stays the default "+" icon; clicking it calls that item's `action` directly, no dropdown opens.
169
+ - 2+ items → clicking the trigger opens a dropdown (custom-positioned, teleported to `<body>`, matching the composer's full width — not `AbPopover`, which sizes/anchors to the trigger button instead) listing every item (with a divider between the two slot groups if both are non-empty). Opens below by default and flips above (capped to the available space, scrolling internally if needed) when there isn't room below — never clipped by the viewport or a scrolling ancestor.
170
+
171
+ ## v-model
172
+
173
+ Yes — `modelValue` + `update:modelValue` (string). Also a second v-model: `files` + `update:files` (`FileEntry[]`).
174
+
175
+ ### Attached file shape (`FileEntry`)
176
+
177
+ ```js
178
+ {
179
+ id, // stable string key, generated internally
180
+ file, // the raw File object
181
+ name,
182
+ size,
183
+ type,
184
+ extension, // lowercased, no dot
185
+ isImage, // boolean — drives chip thumbnail vs icon
186
+ previewKind, // 'image' | 'video' | 'audio' | 'pdf' | 'none' — drives lightbox rendering
187
+ url, // objectURL — revoked automatically on removeFile()/clearFiles()/unmount
188
+ fileIcon, // icon component for non-image chip display
189
+ }
190
+ ```
191
+
192
+ ## Exposed methods
193
+
194
+ Same methods as the scoped slot props above (see "Slots"), also available via a template ref for use outside
195
+ any slot — e.g. a toolbar button elsewhere on the page.
196
+
197
+ | Method | Description |
198
+ | --- | --- |
199
+ | `addFiles(files)` | Accepts a `File[]` or `FileList`. Validates each against `maxFileSize`/`allowedExtensions`; rejected files fire `file-reject` + a toast and are never added. Accepted files are appended and `update:files` fires once with the full list. |
200
+ | `removeFile(id)` | Removes one attached file by `id`, revoking its object URL. |
201
+ | `clearFiles()` | Removes all attached files, revoking every object URL. |
202
+ | `addToAttach(options?)` | Opens a native file picker and routes the selection through `addFiles`. `options`: `{ accept, multiple }`. |
203
+ | `updateText(text)` | Replaces the message text (fires `update:modelValue`). |
204
+ | `focus()` | Focuses the textarea. |
205
+ | `startRecording()` | Starts voice input (same as clicking the mic button). No-op if unsupported/disabled/readonly/already recording. |
206
+ | `stopRecording()` | Stops recording, keeping the transcribed text (same as the ■ button). |
207
+ | `cancelRecording()` | Stops recording and discards the transcribed text (same as the ✕ button). |
208
+
209
+ ## Provides
210
+
211
+ `abChatInputCloseMenu` (no-arg function) — consumed by `AbChatInputMenuItem` to auto-close the dropdown after its `action` fires. Not meant to be consumed by anything else.
@@ -0,0 +1,55 @@
1
+ # AbChatInputMenuItem — AI Reference
2
+
3
+ A single row (icon + name + optional description) for `AbChatInput`'s add/more dropdown. Not useful outside `AbChatInput` — see [.ai/ai_ref_AbChatInput.md](./ai_ref_AbChatInput.md).
4
+
5
+ ## Installation
6
+
7
+ Not globally registered — import it explicitly. Requires `@lucide/vue` (or any icon component) for the `icon` prop.
8
+
9
+ ## Usage
10
+
11
+ ```vue
12
+ <template>
13
+ <AbChatInput v-model="message">
14
+ <template #single-input>
15
+ <AbChatInputMenuItem :icon="Paperclip" name="Attach a file" description="jpg, png, pdf" :action="onAttach" />
16
+ </template>
17
+ </AbChatInput>
18
+ </template>
19
+
20
+ <script setup>
21
+ import { ref } from 'vue';
22
+ import { AbChatInput, AbChatInputMenuItem } from 'vue3-appsbd-ui';
23
+ import { Paperclip } from '@lucide/vue';
24
+
25
+ const message = ref('');
26
+ function onAttach() { /* open a file picker, etc. */ }
27
+ </script>
28
+ ```
29
+
30
+ ## Props
31
+
32
+ | Name | Type | Default | Description |
33
+ | --- | --- | --- | --- |
34
+ | `icon` | `Object \| Function` | `null` | Icon component (e.g. a `@lucide/vue` icon). |
35
+ | `name` | `String` | `""` | Item title (passed through `translateGetMsg`). |
36
+ | `description` | `String` | `""` | Optional secondary line (passed through `translateGetMsg`). |
37
+ | `action` | `Function` | `null` | Called on click. **Important:** when this item is the only one registered across `#single-input`/`#multiple-input`, `AbChatInput` reads this prop directly off the item's VNode and calls it without mounting the item — always pass a plain function, not something that depends on the component being mounted first. |
38
+
39
+ ## Events
40
+
41
+ | Event | Payload | Description |
42
+ | --- | --- | --- |
43
+ | `click` | — | Fired on click, after `action()`. |
44
+
45
+ ## Slots
46
+
47
+ None.
48
+
49
+ ## v-model
50
+
51
+ Not applicable — stateless presentational item.
52
+
53
+ ## Injects
54
+
55
+ `abChatInputCloseMenu` (optional, no-arg function) — provided by the parent `AbChatInput`; called after `action()`/`click` to close the dropdown. No consumer wiring needed.
@@ -1,6 +1,8 @@
1
1
  # AbFileUploader — AI Reference
2
2
 
3
- File upload input with drag-and-drop, size validation, and preview. Can render a default uploader UI or accept a custom one via slots.
3
+ File upload input with drag-and-drop, size/type validation, and preview. Supports an image-only
4
+ mode (default) or a generic file mode with extension-icon previews. Can render a default uploader
5
+ UI or accept a custom one via slots.
4
6
 
5
7
  > Also available inside `AbCustomField` via the `FU` field type — see [.ai/ai_ref_AbCustomField.md](./ai_ref_AbCustomField.md).
6
8
 
@@ -8,9 +10,17 @@ File upload input with drag-and-drop, size validation, and preview. Can render a
8
10
 
9
11
  ```vue
10
12
  <template>
11
- <ab-file-uploader v-model="file" label="Upload avatar" :max-size="1.5" @on-select-files="handleFiles" />
13
+ <ab-file-uploader v-model="avatar" label="Upload avatar" :max-size="1.5" @on-select-files="handleFiles" />
12
14
 
13
- <ab-file-uploader v-model="file" :max-size="5">
15
+ <!-- File mode: allowedTypes is required, non-image files show an extension icon -->
16
+ <ab-file-uploader
17
+ v-model="doc"
18
+ type="file"
19
+ :allowed-types="['pdf', 'doc', 'docx', 'xlsx']"
20
+ :max-size="5"
21
+ />
22
+
23
+ <ab-file-uploader v-model="avatar" :max-size="5">
14
24
  <template #dial>
15
25
  <div class="my-drop-zone">Drop files here</div>
16
26
  </template>
@@ -19,7 +29,8 @@ File upload input with drag-and-drop, size validation, and preview. Can render a
19
29
 
20
30
  <script setup>
21
31
  import { ref } from 'vue';
22
- const file = ref(null);
32
+ const avatar = ref(null);
33
+ const doc = ref(null);
23
34
  function handleFiles(files) { console.log('Selected files:', files); }
24
35
  </script>
25
36
  ```
@@ -32,13 +43,18 @@ function handleFiles(files) { console.log('Selected files:', files); }
32
43
  | `maxSize` | `Number` | `2.0` | Max file size in MB. |
33
44
  | `label` | `String` | `""` | Field label. |
34
45
  | `containerClass` | `String` | `undefined` | Wrapper class (falls back to globalConfig.inputContainerClass). |
46
+ | `type` | `'image' \| 'file'` | `'image'` | Upload mode. `image` only accepts images and always shows a thumbnail preview. `file` accepts any file listed in `allowedTypes`; non-image files render an extension icon instead of a preview. |
47
+ | `allowedTypes` | `Array<String>` | `[]` | Allowed extensions, no dot (e.g. `['pdf', 'docx']`). **Required when `type="file"`** — logs a console error and rejects every selected file when empty. Also drives the input's `accept` attribute. Ignored in `image` mode. |
48
+ | `showFileName` | `Boolean` | `null` | Whether to render the filename below the container. `null` auto-picks by `type` (off for `image`, on for `file`); pass `true`/`false` to override either mode. |
49
+ | `width` | `String \| Number` | `undefined` | Container width. Number is treated as px. Falls back to the default `100px`. |
50
+ | `height` | `String \| Number` | `undefined` | Container height. Number is treated as px. Falls back to the default `73px`. |
35
51
 
36
52
  ## Events
37
53
 
38
54
  | Event | Payload | Description |
39
55
  | --- | --- | --- |
40
- | `update:modelValue` | `file` | v-model sync. |
41
- | `onSelectFiles` | `files` | All selected files after size validation. |
56
+ | `update:modelValue` | `file \| null` | v-model sync. |
57
+ | `onSelectFiles` | `files` | All selected files after validation. |
42
58
 
43
59
  ## Slots
44
60
 
@@ -47,3 +63,15 @@ function handleFiles(files) { console.log('Selected files:', files); }
47
63
  | `label` | Custom label. |
48
64
  | `dial` | Custom uploader UI (replaces the default design). |
49
65
  | `default` | Alternative custom UI slot. |
66
+
67
+ ## v-model
68
+
69
+ Yes — `modelValue` + `update:modelValue`. Holds a `File` object (or a URL string for pre-existing
70
+ previews), or `null`.
71
+
72
+ ## Notes
73
+
74
+ - Icon mapping for non-image files (pdf, zip, doc/docx, xls/xlsx, ppt/pptx, video) comes from the
75
+ shared `useFileValidator()` composable's `getFileIconByExt`, the same mapping `AbChatInput` uses.
76
+ - In `image` mode, validation is unchanged from before: only jpg/jpeg/png/gif/webp are accepted and
77
+ checked against the file's actual MIME type.
@@ -1,6 +1,6 @@
1
1
  # AbFormCheck / AbRadioInput — AI Reference
2
2
 
3
- Checkbox or radio group with card, segmented, and pill visual variants. `AbRadioInput` is an alias export of `AbFormCheck`.
3
+ Checkbox or radio group with card, icon-card, card-flat-check, image-radio, segmented, and pill visual variants. `AbRadioInput` is an alias export of `AbFormCheck`.
4
4
 
5
5
  ## Usage
6
6
 
@@ -17,6 +17,41 @@ Checkbox or radio group with card, segmented, and pill visual variants. `AbRadio
17
17
  />
18
18
 
19
19
  <ab-form-check v-model="plan" type="radio" name="plan" variant="card" :options="plans" />
20
+
21
+ <!-- Custom check icon (card-family variants only) -->
22
+ <ab-form-check v-model="plan" type="radio" name="plan2" variant="card" :options="plans" :check-icon-component="Check" />
23
+
24
+ <!-- Flat, shadow-free card with the indicator on the right (card-family variants only) -->
25
+ <ab-form-check v-model="plan" type="radio" name="plan3" variant="card" :options="plans" flat-check check-right />
26
+
27
+ <!-- icon-card: icon centered on top by default (no containerClass needed) -->
28
+ <ab-form-check v-model="plan" type="radio" name="plan4" variant="icon-card" :options="plans" />
29
+
30
+ <!-- card-flat-check: flatCheck styling on by default -->
31
+ <ab-form-check v-model="plan" type="radio" name="plan5" variant="card-flat-check" :options="plans" />
32
+
33
+ <!-- isInline false on a card-family variant: full-width stacked column with a gap between cards -->
34
+ <ab-form-check v-model="plan" type="radio" name="plan6" variant="card" :options="plans" :is-inline="false" />
35
+
36
+ <!-- image-radio: big image tile, native input hidden, check badge overlays a corner -->
37
+ <ab-form-check
38
+ v-model="theme"
39
+ type="radio"
40
+ name="theme"
41
+ variant="image-radio"
42
+ card-width="140px"
43
+ selector-position="top-right"
44
+ :options="[
45
+ { val: 'light', title: 'Light', img_src: '/light.png' },
46
+ { val: 'dark', title: 'Dark', img_src: '/dark.png' },
47
+ ]"
48
+ />
49
+
50
+ <!-- image-radio chip mode: isInline=true swaps the big tile for a compact icon+text chip -->
51
+ <ab-form-check v-model="theme" type="radio" name="theme2" variant="image-radio" :options="themes" is-inline />
52
+
53
+ <!-- isBoxShadow: checked state as a colored box-shadow glow instead of a border-color change -->
54
+ <ab-form-check v-model="plan" type="radio" name="plan7" variant="card" :options="plans" is-box-shadow />
20
55
  </template>
21
56
  ```
22
57
 
@@ -29,16 +64,26 @@ Checkbox or radio group with card, segmented, and pill visual variants. `AbRadio
29
64
  | `rules` | `String` | `""` | VeeValidate rules. |
30
65
  | `val` | `any` | `null` | Initial value. |
31
66
  | `type` | `String` | `"checkbox"` | `"checkbox"` or `"radio"`. |
32
- | `isInline` | `Boolean` | `false` | Render options inline. |
33
- | `options` | `Array` | `[]` | Each: `{ val, title, description?, icon?, img_src?, iconComponent?, disable? }`. |
67
+ | `isInline` | `Boolean` | `false` | Render options inline. On `variant="image-radio"` this means something different: options always flow in a wrapping tile grid regardless of this prop; `isInline` instead toggles a compact icon+text chip look (`true`, check badge leads on the left like a normal radio/checkbox and only takes up space once checked) vs. the default big image tile (`false`, check badge overlays the image corner). |
68
+ | `options` | `Array` | `[]` | Each: `{ val, title, description?, icon?, img_src?, iconComponent?, disable?, class? }`. `description` is always rendered via `v-html` — only pass trusted/sanitized HTML. |
34
69
  | `name` | `String` | **required** | Field name. |
35
70
  | `limit` | `Number` | `0` | Max selections (0 = unlimited). |
36
- | `containerClass` | `String` | `"mb-3"` | Wrapper class. |
71
+ | `containerClass` | `String` | `"mb-3"` | Wrapper class. `variant="card"`/`"icon-card"`/`"card-flat-check"`/`"image-radio"` layout keywords: `left-top`, `middle-top` (icon position). `icon-card` and `image-radio` default to `middle-top` if neither keyword is passed. |
37
72
  | `size` | `String` | `"md"` | Control size. |
38
73
  | `iconSize` | `String` | `"24px"` | Icon size (when option has an icon). |
39
74
  | `iconGap` | `String` | `"10px"` | Gap between icon and label. |
40
- | `variant` | `String` | `"default"` | `default`, `card`, `segmented`, `pill`. |
41
- | `cardWidth` | `String` | `"auto"` | Width of card options (e.g. `"160px"`, `"50%"`, `"12rem"`). |
75
+ | `variant` | `String` | `"default"` | `default`, `card`, `icon-card` (card with the icon centered on top by default), `card-flat-check` (card with `flatCheck` styling on by default), `image-radio` (image-first card; native input hidden, check badge overlays the image's top-left corner), `segmented`, `pill`. |
76
+ | `cardWidth` | `String` | `"auto"` | Width of the card itself for card-family variants (e.g. `"160px"`, `"50%"`, `"12rem"`). Applies in both inline and stacked (`isInline` set to `false`) layouts. |
77
+ | `checkIcon` | `String` | `undefined` | CSS icon class rendered inside the checked input, replacing the native checkmark/dot. Card-family variants only. |
78
+ | `checkIconComponent` | `Component` | `undefined` | A Vue component (e.g. a Lucide icon) rendered inside the checked input, replacing the native checkmark/dot. Card-family variants only. On `variant="image-radio"`, a built-in circle-check icon is used automatically when neither this nor `checkIcon` is set. |
79
+ | `flatCheck` | `Boolean` | `false` | Shadow-free checked style for card-family variants: border-color change + accent-colored title instead of box-shadow, indicator vertically centered. Always on for `variant="card-flat-check"`. |
80
+ | `checkRight` | `Boolean` | `false` | Moves the checkbox/radio indicator to the right side of the card (card-family variants only). Typically combined with `flatCheck`. |
81
+ | `selectorPosition` | `String` | `"top-left"` | Corner the check badge overlays on the image: `"top-left"`, `"top-right"`, `"bottom-left"`, `"bottom-right"`. `variant="image-radio"` only. |
82
+ | `isBoxShadow` | `Boolean` | `false` | Card-family variants only (`card`, `icon-card`, `card-flat-check`, `image-radio`). Shows checked state as a colored box-shadow glow instead of a border-color change — the border stays transparent throughout, matching the classic `AbImageRadioInput` look. |
83
+
84
+ Card-family variants `card`, `icon-card`, `card-flat-check` with `isInline` set to `false` (the default) stack in a full-width column with a gap between cards, instead of the content-sized cards used when `isInline` set to `true`.
85
+
86
+ `variant="image-radio"` behaves differently from the rest of the card family: options always flow in a wrapping tile grid (never full-width stacked), the native checkbox/radio dot is always hidden, and `isInline` instead switches between two looks — `false` (default): a big image fills the tile with the title below and a check badge overlaying the corner set by `selectorPosition`; `true`: a compact icon+text chip where the check badge leads on the left (like a normal radio/checkbox) instead of overlaying a corner, and only takes up space once an option is checked.
42
87
 
43
88
  ## Events
44
89
 
@@ -48,12 +93,26 @@ Checkbox or radio group with card, segmented, and pill visual variants. `AbRadio
48
93
 
49
94
  ## Slots
50
95
 
96
+ Per-option slots are **not** dynamically named per option (no `-{title}`/`-{val}` suffix) — each is a single
97
+ scoped slot rendered once per option, scoped with `{ check, index }` (`check` is the current `options[index]`
98
+ item). If different options need different content, branch inside the template on `check.val`/`index`:
99
+
100
+ ```vue
101
+ <ab-form-check variant="card" :options="plans" v-model="plan" type="radio" name="plan">
102
+ <template #option-label="{ check }">
103
+ <template v-if="check.val === 'pro'">
104
+ <ab-badge variant="danger">Popular</ab-badge> {{ check.title }}
105
+ </template>
106
+ <template v-else>{{ check.title }}</template>
107
+ </template>
108
+ </ab-form-check>
109
+ ```
110
+
51
111
  | Name | Description |
52
112
  | --- | --- |
53
- | `label` | Group label. |
54
- | `description` | Group description. |
113
+ | `label` | Group label (the whole component's `label` prop/heading — not per-option). |
114
+ | `description` | Per-option description, scoped `{ check, index }`. Overrides the option's `description` key (the default content renders it via `v-html`). |
55
115
  | `help-text` | Helper text. |
56
- | `input-img-{option.title}` | Per-option image slot. |
57
- | `input-icon-{option.title}` | Per-option icon component slot. |
58
- | `label-{option.title}` | Per-option label slot. |
59
- | `description-{option.title}` | Per-option description slot. |
116
+ | `input-icon` | Per-option image/icon area, scoped `{ check, index }` (card-family/segmented/pill variants). Overrides `img_src`/`icon`/`iconComponent`. |
117
+ | `option-label` | Per-option title, scoped `{ check, index }` (card-family/segmented/pill variants). Overrides the option's `title`. Deliberately not named `label` — that name is already the group-level heading slot above. |
118
+ | `check-icon` | Per-option checked-state icon, scoped `{ check, index }` (card-family variants only). Default content falls back to `checkIcon`/`checkIconComponent`; overrides both when provided. |
@@ -43,6 +43,8 @@ const form = reactive({ email: '' });
43
43
  | `maxlength` | `any` | `null` | Native `maxlength`. |
44
44
  | `isLeftIconBorder` | `Boolean` | `false` | Renders border between the prefix icon and the input. |
45
45
  | `isRightIconBorder` | `Boolean` | `false` | Renders border between the postfix icon and the input. |
46
+
47
+ > Set both `isLeftIconBorder` and `isRightIconBorder` to `true` to show a border on both sides at once — the whole input group (icons + field) is treated as one unified box on focus.
46
48
  | `passIconPosition` | `String` | `"right"` | Position of the show password icon (`"left"`, `"right"`). |
47
49
  | `isHideLabel` | `Boolean` | `false` | Hide the label visually. |
48
50
  | `iconPosition` | `String` | `undefined` | Position of the custom `#icon` slot (`"left"`, `"right"`). |
@@ -38,6 +38,7 @@ const qty = ref(1);
38
38
  | `isBorder` | `Boolean` | `false` | Show inner borders between minus/input/plus. Default is a seamless pill. |
39
39
  | `isLeftIconBorder` | `Boolean` | `false` | Render border between the prefix icon and the input. |
40
40
  | `isRightIconBorder` | `Boolean` | `false` | Render border between the postfix icon and the input. |
41
+ | `size` | `String` | `undefined` | Input size: `"xs"`, `"sm"`, `"md"`, `"lg"`, `"xl"`. Falls back to `globalConfig.size`, then `"md"`. |
41
42
 
42
43
  ## Events
43
44
 
@@ -0,0 +1,58 @@
1
+ # abDate (Library)
2
+
3
+ Chhoto, dependency-free date/datetime/time formatting library (dayjs-er moto). Kono timezone conversion kore na — je value deya hoy sheta as-is (wall-clock) format hoy.
4
+
5
+ ## Usage
6
+
7
+ ```javascript
8
+ import { formatDate, formatDateTime, formatTime, formatCustom } from "@vue3-appsbd-ui/libraries/abDate";
9
+
10
+ formatDate("2026-08-17"); // globalConfig.dateDisplayFormat na thakle -> "2026-08-17"
11
+ formatDateTime("2026-08-17 14:05:09"); // globalConfig.datetimeDisplayFormat na thakle -> "2026-08-17 14:05:09"
12
+ formatTime(new Date()); // globalConfig.timeDisplayFormat na thakle -> "14:05:09"
13
+ formatCustom("2026-08-17", "DD/MM/YYYY"); // -> "17/08/2026"
14
+ ```
15
+
16
+ ## Default Format Resolution
17
+
18
+ `formatDate` / `formatDateTime` / `formatTime` resolve their format in this order:
19
+
20
+ 1. Explicit `format` argument.
21
+ 2. `AppsbdUIConfigure`'s `dateDisplayFormat` / `datetimeDisplayFormat` / `timeDisplayFormat` (see [ai_ref_global_config.md](./ai_ref_global_config.md)).
22
+ 3. Built-in MySQL default: `'YYYY-MM-DD'` / `'YYYY-MM-DD HH:mm:ss'` / `'HH:mm:ss'`.
23
+
24
+ ## API Reference
25
+
26
+ ### Functions
27
+
28
+ | Function | Signature | Description |
29
+ |---|---|---|
30
+ | `formatDate` | `(value, format?) => string` | Formats `value` as a date. Falls back to `globalConfig.dateDisplayFormat`, then `'YYYY-MM-DD'`. |
31
+ | `formatDateTime` | `(value, format?) => string` | Formats `value` as a date+time. Falls back to `globalConfig.datetimeDisplayFormat`, then `'YYYY-MM-DD HH:mm:ss'`. |
32
+ | `formatTime` | `(value, format?) => string` | Formats `value` as a time. Falls back to `globalConfig.timeDisplayFormat`, then `'HH:mm:ss'`. |
33
+ | `formatCustom` | `(value, format) => string` | Generic formatter — `format` is required, no default resolution. |
34
+
35
+ ### `value` Input Types
36
+
37
+ - `Date` — used as-is.
38
+ - `number` — treated as an epoch timestamp (`new Date(value)`).
39
+ - `string` — parsed manually (no native `Date` parsing quirks): `YYYY-MM-DD`, `YYYY-MM-DD HH:mm[:ss]`, `YYYY-MM-DDTHH:mm:ss(.sss)(Z|±HH:mm)`, or time-only `HH:mm[:ss]` (attached to today's date). Any trailing `Z`/offset is **ignored**, not applied as a shift. Unrecognized strings fall back to native `new Date(string)`.
40
+ - Falsy or unparseable input → all functions return `''`.
41
+
42
+ ### Format Tokens
43
+
44
+ Same token set as `AbDateTimePicker.vue`, plus real second support (`ss`/`s`):
45
+
46
+ | Token | Meaning | Example |
47
+ |---|---|---|
48
+ | `YYYY` / `YY` | 4/2-digit year | `2026` / `26` |
49
+ | `MMMM` / `MMM` | Full/short month name (uses `globalConfig.monthNames`/`monthNamesShort`, English default) | `August` / `Aug` |
50
+ | `MM` / `M` | 2/1-digit month | `08` / `8` |
51
+ | `DD` / `D` | 2/1-digit day | `17` / `17` |
52
+ | `HH` / `H` | 2/1-digit hour (24h) | `14` / `14` |
53
+ | `hh` / `h` | 2/1-digit hour (12h) | `02` / `2` |
54
+ | `mm` / `m` | 2/1-digit minute | `05` / `5` |
55
+ | `ss` / `s` | 2/1-digit second | `09` / `9` |
56
+ | `A` / `a` | Upper/lowercase AM-PM | `PM` / `pm` |
57
+
58
+ No timezone conversion is performed anywhere in this library.
@@ -14,10 +14,15 @@ function handleFileUpload(event) {
14
14
  const file = event.target.files[0];
15
15
  if (!file) return;
16
16
 
17
- const validatedFile = getFileInfo(file, 2.0);
18
- if (validatedFile) {
19
- // validatedFile.isImage
20
- // validatedFile.fileIcon
17
+ // Legacy / default: image-only, validated against a static jpg/png/gif/webp mime map.
18
+ const validatedImage = getFileInfo(file, 2.0);
19
+
20
+ // Custom extension list: any extension in the list is allowed (no mime map needed).
21
+ const validatedDoc = getFileInfo(file, 5.0, ["pdf", "doc", "docx", "xlsx"]);
22
+
23
+ if (validatedDoc) {
24
+ // validatedDoc.isImage
25
+ // validatedDoc.fileIcon
21
26
  }
22
27
  }
23
28
  </script>
@@ -27,5 +32,5 @@ function handleFileUpload(event) {
27
32
 
28
33
  | Method | Signature | Description |
29
34
  |---|---|---|
30
- | `getFileInfo(file, sizeLimitMb)` | `Function` | Validates a `File` object against allowed MIME types and max size (`sizeLimitMb`, default `1.0`). If invalid, spawns a toast error and returns `null`. If valid, returns the `file` augmented with `isImage` and `fileIcon` properties. |
35
+ | `getFileInfo(file, sizeLimitMb, allowedExtensions?)` | `Function` | Validates a `File` object and max size (`sizeLimitMb`, default `1.0`). Without `allowedExtensions`, only jpg/jpeg/png/gif/webp are allowed and cross-checked against the file's actual MIME type (legacy, image-only behavior). With `allowedExtensions` (array of extensions, no dot, e.g. `['pdf', 'docx']`), any extension in that list is allowed instead — no MIME cross-check. If invalid, spawns a toast error and returns `null`. If valid, returns the `file` augmented with `isImage` and `fileIcon` properties. |
31
36
  | `getFileIconByExt(ext, type)` | `Function` | Returns `{ isImage: boolean, fileIcon: Component\|String }` based on file extension and MIME type. Uses `@lucide/vue` icons for known formats (pdf, zip, doc, xls, ppt, mp4). |
package/AI_REFERENCE.md CHANGED
@@ -21,7 +21,7 @@
21
21
  | `AbField` | ✅ | Polymorphic field wrapper - renders any input type via a `type` prop. | [.ai/ai_ref_AbField.md](./.ai/ai_ref_AbField.md) |
22
22
  | `AbCustomField` | ✅ | Dynamic form scaffold driven by a `fieldInputs` array. | [.ai/ai_ref_AbCustomField.md](./.ai/ai_ref_AbCustomField.md) |
23
23
  | `AbDateTimePicker` | ✅ | Date / time / range picker. Also import `v-calendar/style.css` in consuming app. | [.ai/ai_ref_AbDateTimePicker.md](./.ai/ai_ref_AbDateTimePicker.md) |
24
- | `AbFormCheck` / `AbRadioInput` | ✅ | Checkbox / radio group (default, card, segmented, pill variants). `AbRadioInput` is an alias of `AbFormCheck`. | [.ai/ai_ref_AbFormCheck.md](./.ai/ai_ref_AbFormCheck.md) |
24
+ | `AbFormCheck` / `AbRadioInput` | ✅ | Checkbox / radio group (default, card, icon-card, card-flat-check, image-radio, segmented, pill variants). `AbRadioInput` is an alias of `AbFormCheck`. | [.ai/ai_ref_AbFormCheck.md](./.ai/ai_ref_AbFormCheck.md) |
25
25
  | `AbToggle` | ✅ | Accessible switch/toggle with title & description slots. | [.ai/ai_ref_AbToggle.md](./.ai/ai_ref_AbToggle.md) |
26
26
  | `AbInputTag` | ✅ | Multi-tag input with optional option list. | [.ai/ai_ref_AbInputTag.md](./.ai/ai_ref_AbInputTag.md) |
27
27
  | `AbMultiSelect` | ✅ | Searchable single/multi/tag select (custom built-in — replaces `@vueform/multiselect`). | [.ai/ai_ref_AbMultiSelect.md](./.ai/ai_ref_AbMultiSelect.md) |
@@ -39,7 +39,7 @@
39
39
  | `AbMainLayout` | — | Full-page application shell with left/right sidebar drawers, header, body, and footer slots. Mobile-responsive. | [.ai/ai_ref_AbMainLayout.md](./.ai/ai_ref_AbMainLayout.md) |
40
40
  | `AbSlider` | — | Single or range numeric slider. | [.ai/ai_ref_AbSlider.md](./.ai/ai_ref_AbSlider.md) |
41
41
  | `AbColorPicker` | — | Radio-style color swatch picker. | [.ai/ai_ref_AbColorPicker.md](./.ai/ai_ref_AbColorPicker.md) |
42
- | `AbFileUploader` | — | File upload field with drag-and-drop and size validation. | [.ai/ai_ref_AbFileUploader.md](./.ai/ai_ref_AbFileUploader.md) |
42
+ | `AbFileUploader` | — | File upload field with size validation; image mode (default, preview) or file mode (`allowedTypes` + extension icons). | [.ai/ai_ref_AbFileUploader.md](./.ai/ai_ref_AbFileUploader.md) |
43
43
  | `AbFilterPanel` | — | Advanced filter panel with search and scan modes. | [.ai/ai_ref_AbFilterPanel.md](./.ai/ai_ref_AbFilterPanel.md) |
44
44
  | `AbSettingsForm` | — | Settings-style form that auto-registers as a wizard step validator. | [.ai/ai_ref_AbSettingsForm.md](./.ai/ai_ref_AbSettingsForm.md) |
45
45
  | `AbScrollbar` | — | Styled custom scrollbar wrapper with scroll events. | [.ai/ai_ref_AbScrollbar.md](./.ai/ai_ref_AbScrollbar.md) |
@@ -56,16 +56,19 @@
56
56
  | `AbPricingContainer` | — | Layout wrapper for pricing tables. | [.ai/ai_ref_AbPricingContainer.md](./.ai/ai_ref_AbPricingContainer.md) |
57
57
  | `AbPricingTable` | — | Stylized, responsive pricing table component with feature list and package columns. | [.ai/ai_ref_AbPricingTable.md](./.ai/ai_ref_AbPricingTable.md) |
58
58
  | `AbChart` | — | Flexible chart component powered by Apache ECharts. | [.ai/ai_ref_AbChart.md](./.ai/ai_ref_AbChart.md) |
59
+ | `AbChatInput` | — | ChatGPT-style auto-growing composer with an item-count-driven add/more dropdown, internal file attachment preview, and built-in Web Speech API voice input. | [.ai/ai_ref_AbChatInput.md](./.ai/ai_ref_AbChatInput.md) |
60
+ | `AbChatInputMenuItem` | — | Presentational row (icon/name/description/action) for `AbChatInput`'s add/more dropdown. | [.ai/ai_ref_AbChatInputMenuItem.md](./.ai/ai_ref_AbChatInputMenuItem.md) |
59
61
  | `AbDarkModeToggler` | — | Light / dark theme toggle (pairs with `useTheme`). | [.ai/ai_ref_AbDarkModeToggler.md](./.ai/ai_ref_AbDarkModeToggler.md) |
60
62
  | `useAlert` | — | Procedural API to trigger SweetAlert-based modal alerts, notifications, and confirmations. | [.ai/ai_ref_useAlert.md](./.ai/ai_ref_useAlert.md) |
61
63
  | `abEventBus` / `emitterObj` | — | Lightweight global event bus using `tiny-emitter`. | [.ai/ai_ref_abEventBus.md](./.ai/ai_ref_abEventBus.md) |
62
64
  | `useTheme` | — | Composable for light/dark theme toggling with localStorage and system preference sync. | [.ai/ai_ref_useTheme.md](./.ai/ai_ref_useTheme.md) |
63
65
  | `useToast` | — | Procedural API to spawn toast notifications (success, error, warning, info). | [.ai/ai_ref_useToast.md](./.ai/ai_ref_useToast.md) |
64
- | `useFileValidator` | — | File validation utilities (size, MIME type) and file icon/image resolvers. | [.ai/ai_ref_useFileValidator.md](./.ai/ai_ref_useFileValidator.md) |
66
+ | `useFileValidator` | — | File validation utilities (size, MIME type, or a custom allowed-extensions list) and file icon/image resolvers. | [.ai/ai_ref_useFileValidator.md](./.ai/ai_ref_useFileValidator.md) |
65
67
  | `useResponsive` | — | Real-time viewport width tracker for responsive layout decisions (Bootstrap 5 aligned). | [.ai/ai_ref_useResponsive.md](./.ai/ai_ref_useResponsive.md) |
66
68
  | `abTranslate` | — | Localization utility wrapping `vue3-gettext` with automatic variable interpolation. | [.ai/ai_ref_abTranslate.md](./.ai/ai_ref_abTranslate.md) |
67
69
  | `abVeeRules` | — | Collection of pre-configured VeeValidate validation rules integrated with localization. | [.ai/ai_ref_abVeeRules.md](./.ai/ai_ref_abVeeRules.md) |
68
70
  | `abRequestParam` | — | Data-transfer classes to build structured API request parameters (pagination, sorting, filtering). | [.ai/ai_ref_abRequestParam.md](./.ai/ai_ref_abRequestParam.md) |
71
+ | `abDate` | — | Dependency-free date/datetime/time formatter (dayjs-style); no timezone conversion, defaults from global config. | [.ai/ai_ref_abDate.md](./.ai/ai_ref_abDate.md) |
69
72
 
70
73
  ## Directives & Controller APIs
71
74