@appsbd/vue3-appsbd-ui 1.0.18 → 1.0.20

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.
@@ -4,9 +4,9 @@ Advanced filter panel for list and table views. Supports quick/advance modes, si
4
4
 
5
5
  `isMultiple` is functional in `isQuickAdvance` mode for `dd` fields: selections toggle on/off and render as removable tag chips on the popover trigger (no `AbMultiSelect` involved). The popover's option list is searchable — locally against `options`, or via `isAjaxSearch` + `onSearch` for server-side search.
6
6
 
7
- `isBuilderFilter` is a Shopify-style filter builder: no field is visible until the user clicks the `+` button. That opens a popover which first lists every not-yet-applied `filterOptions` entry by `name`; picking one swaps the same popover to a value editor matching that field's `type` (`dd`→`AbMultiSelect`, `t`→`AbInputField`, `tr`→min/max inputs, `d`/`dr`→`AbDateTimePicker`) plus Cancel/Apply Filter buttons. A `"t"` (plain text/number) field also applies on `Enter` in its input — same no-op-when-empty guard as clicking Apply Filter. Apply pushes it in as a removable chip (e.g. "Email : gmail" — see `builderChipSeparator` slot below). By default (`isBuilderAutoSearch: false`), a global Search button is shown so search fires only on clicking Search, and Reset clears all applied chips. When `isBuilderAutoSearch: true`, every Apply and chip removal immediately fires `searchFilter` without showing a Search button. A field's `operators` is fixed by its schema entry, same as other modes — this mode does not add an is/is-not/contains operator picker, only the value input changes per `type`.
7
+ `isBuilderFilter` is a Shopify-style filter builder: no field is visible until the user clicks the `+` button. That opens a popover which first lists every not-yet-applied `filterOptions` entry by `name`; picking one swaps the same popover to a value editor matching that field's `type` (`dd`→`AbMultiSelect`, `t`→`AbInputField`, `tr`→min/max inputs, `d`/`dr`→`AbDateTimePicker`) plus Cancel/Apply Filter buttons. A `"t"` (plain text/number) field also applies on `Enter` in its input — same no-op-when-empty guard as clicking Apply Filter. Apply pushes it in as a removable chip (e.g. "Email : gmail" — see `builderChipSeparator` slot below). By default (`isBuilderAutoSearch: false`), a Search button is shown so search fires only on clicking Search, and Reset clears all applied chips. When `isBuilderAutoSearch: true`, every Apply and chip removal immediately fires `searchFilter` without showing a Search button (only Reset). The Search/Reset buttons are hidden entirely while no chip is applied — they appear right after the `+` button once the first chip is applied, and vanish again when Reset is clicked or the last chip is removed individually. A field's `operators` is fixed by its schema entry, same as other modes — this mode does not add an is/is-not/contains operator picker, only the value input changes per `type`.
8
8
 
9
- Standard-search, advance-search, and quick-advance field layouts are all plain flex (`d-flex flex-wrap gap-2`) — no Bootstrap grid. Each field grows/shrinks on its own, which means a field left alone on the last row will stretch to fill the remaining row width; give a `filterOptions` entry a `width`, `minWidth`, and/or `maxWidth` key (`String | Number` — a number is px, e.g. `maxWidth: 280`, or a CSS string like `"50%"`) to control that specific field's size. All three are forwarded straight to the underlying `AbInputField`/`AbMultiSelect`/`AbDateTimePicker`'s own same-named props, and also constrain that field's flex-item wrapper directly (`width` sets `flex: 0 0 <width>` so it stops growing/shrinking; `minWidth`/`maxWidth` clamp it while it keeps growing/shrinking with its siblings).
9
+ Standard-search, advance-search, and quick-advance field layouts are all plain flex (`d-flex flex-wrap gap-2`) — no Bootstrap grid. Each field grows/shrinks on its own, which means a field left alone on the last row will stretch to fill the remaining row width; give a `filterOptions` entry a `width`, `minWidth`, and/or `maxWidth` key (`String | Number` — a number is px, e.g. `maxWidth: 280`, or a CSS string like `"50%"`) to control that specific field's size. All three are forwarded straight to the underlying `AbInputField`/`AbMultiSelect`/`AbDateTimePicker`'s own same-named props, and also constrain that field's flex-item wrapper directly (`width` sets `flex: 0 0 min(<width>, 100%)` so it stops growing/shrinking; `minWidth`/`maxWidth` clamp it while it keeps growing/shrinking with its siblings). Every one of these is applied as `min(<value>, 100%)`, so a fixed value like `width: 300` never overflows a narrower container on small screens — it shrinks to fit instead.
10
10
 
11
11
  The standard-mode property-selector (and the scan field it swaps for) isn't part of `filterOptions` — it's not tied to one field, it lists all of them — so it has no `width`/`minWidth`/`maxWidth` key to set. It's also the only item in its row until a property is picked (or whenever `showScanFld` is on), so without sizing it it'll stretch to fill the row the same way an unsized `filterOptions` field would. Use the panel-level `propertySelectWidth`/`propertySelectMinWidth`/`propertySelectMaxWidth` props for that slot instead.
12
12
 
@@ -58,9 +58,9 @@ function handleSearch(data) {
58
58
  | `isMaxDate` | `Boolean` | `true` | Limit date pickers to today as max. |
59
59
  | `size` | `String` | `"xs"` | Control size. |
60
60
  | `advanceContainerClass` | `String` | `""` | Extra class on each field's flex-item wrapper in advance mode (base sizing is `flex: 1 1 240px`, wraps as needed — not a Bootstrap grid). To fix a single field's width instead, use that field's own `width` prop (`AbInputField`/`AbMultiSelect`/`AbDateTimePicker`). |
61
- | `propertySelectWidth` | `String` \| `Number` | `undefined` | Width of the standard-mode property-selector / scan-field slot (number = px). |
62
- | `propertySelectMinWidth` | `String` \| `Number` | `undefined` | `min-width` of that same slot. |
63
- | `propertySelectMaxWidth` | `String` \| `Number` | `undefined` | `max-width` of that same slot — it's the only item in its row until a property is picked (or in scan mode), so it stretches to fill it without one. |
61
+ | `propertySelectWidth` | `String` \| `Number` | `undefined` | Width of the standard-mode property-selector / scan-field slot (number = px). Applied as `min(<value>, 100%)`, so it shrinks on small screens instead of overflowing. |
62
+ | `propertySelectMinWidth` | `String` \| `Number` | `undefined` | `min-width` of that same slot (same `min(<value>, 100%)` clamp). |
63
+ | `propertySelectMaxWidth` | `String` \| `Number` | `undefined` | `max-width` of that same slot — it's the only item in its row until a property is picked (or in scan mode), so it stretches to fill it without one (same `min(<value>, 100%)` clamp). |
64
64
  | `isQuickAdvanceAutoSearch` | `Boolean` | `false` | Fire search automatically on field change (quick advance). |
65
65
  | `defaultFilterId` | `String` \| `Number` | `""` | Pre-selects a default filter property in standard search mode (when `!isSingle && !isAdvance && !isQuickAdvance`). |
66
66
 
@@ -82,3 +82,4 @@ function handleSearch(data) {
82
82
  | `hints` | Helper text. |
83
83
  | `builderChipSeparator` | `isBuilderFilter` mode only. Overrides the connector text rendered between an applied chip's field name and its value (e.g. "Email **:** gmail", "Revenue **:** 12 - 23" for `tr`/`dr` range fields). Receives `{ field }`. Default content is `:`. |
84
84
  | `builderAddLabel` | `isBuilderFilter` mode only. Renders before the static `+` icon inside the "Add filter" button — use it to add a label next to the icon. Not scoped. Empty by default. |
85
+ | `builderTitle` | `isBuilderFilter` mode only. Title rendered at the far left of the row, inside the same box style as the applied chips. Hidden until at least one chip is applied (and again after Reset / removing the last chip). Not scoped. Default content is `Filter Title`. |
@@ -101,7 +101,7 @@ item). If different options need different content, branch inside the template o
101
101
  <ab-form-check variant="card" :options="plans" v-model="plan" type="radio" name="plan">
102
102
  <template #option-label="{ check }">
103
103
  <template v-if="check.val === 'pro'">
104
- <ab-badge variant="danger">Popular</ab-badge> {{ check.title }}
104
+ <ab-badge color="danger">Popular</ab-badge> {{ check.title }}
105
105
  </template>
106
106
  <template v-else>{{ check.title }}</template>
107
107
  </template>
@@ -1,6 +1,6 @@
1
1
  # AbInputField — AI Reference
2
2
 
3
- Core text input with VeeValidate integration, localized feedback, and prefix/postfix/hint slots. Use for single-line text inputs; use `AbField` for a polymorphic field that can render selects, toggles, etc.
3
+ Core text input with VeeValidate integration, localized feedback, and prefix/postfix/hint slots. Use for single-line text inputs; use `AbField` for a polymorphic field that can render selects, toggles, etc. Pass `variant="stepper"` to turn it into a numeric stepper input (min/max/step clamping, +/- buttons) — [`AbNumberField`](./ai_ref_AbNumberField.md) is a thin wrapper exposing the same `variant` prop, defaulting to a plain `type="number"` input when it's unset.
4
4
 
5
5
  ## Usage
6
6
 
@@ -24,6 +24,66 @@ const form = reactive({ email: '' });
24
24
  </script>
25
25
  ```
26
26
 
27
+ Stepper variant:
28
+
29
+ ```vue
30
+ <template>
31
+ <ab-input-field
32
+ v-model="qty"
33
+ variant="stepper"
34
+ name="quantity"
35
+ label="Quantity"
36
+ :min="1"
37
+ :max="10"
38
+ :step="1"
39
+ hints="Clamped between 1 and 10"
40
+ />
41
+
42
+ <!-- Inner borders between minus/input/plus -->
43
+ <ab-input-field v-model="qty" variant="stepper" name="quantity2" label="Quantity" is-border />
44
+ </template>
45
+
46
+ <script setup>
47
+ import { ref } from 'vue';
48
+ const qty = ref(1);
49
+ </script>
50
+ ```
51
+
52
+ Inset label variant (label inside the bordered box):
53
+
54
+ ```vue
55
+ <ab-input-field v-model="username" variant="inset" name="username" label="Username" />
56
+ ```
57
+
58
+ Outlined variant (floating label: looks like the placeholder when empty, moves onto the top border on focus / when filled; `placeholder` shows on focus):
59
+
60
+ ```vue
61
+ <ab-input-field v-model="username" variant="outlined" name="username" label="Username" placeholder="Enter username" />
62
+ ```
63
+
64
+ Copyable (read-only value with a copy-to-clipboard button — no built-in confirmation UI, wire your own via the `copy-success`/`copy-error` events):
65
+
66
+ ```vue
67
+ <template>
68
+ <ab-input-field
69
+ v-model="apiKey"
70
+ name="api_key"
71
+ label="API Key"
72
+ readonly
73
+ copyable
74
+ @copy-success="(msg) => toast.success(msg)"
75
+ @copy-error="(msg) => toast.error(msg)"
76
+ />
77
+ </template>
78
+
79
+ <script setup>
80
+ import { ref } from 'vue';
81
+ import { useToast } from 'vue3-appsbd-ui';
82
+ const apiKey = ref('sk_live_...');
83
+ const { toast } = useToast();
84
+ </script>
85
+ ```
86
+
27
87
  ## Props
28
88
 
29
89
  | Name | Type | Default | Description |
@@ -33,13 +93,13 @@ const form = reactive({ email: '' });
33
93
  | `placeholder` | `String` | `""` | Input placeholder. |
34
94
  | `type` | `String` | `"text"` | Native input type (`text`, `email`, `password`, `number`, `tel`, …). |
35
95
  | `name` | `String` | `""` | VeeValidate field name. |
36
- | `isCodeInput` | `Boolean` | `false` | Applies monospace / code-input styling. |
96
+ | `isCodeInput` | `Boolean` | `false` | Renders a small, fixed-size, large-type single-character box — the same `.mega-input-field` style `AbPinInput` uses per digit. For one OTP/PIN digit only; pair with `maxlength="1"` yourself (not set automatically). Not a monospace/code-display style — too narrow for a token, key, or URL, which will visually scroll inside the box. |
37
97
  | `size` | `String` | `undefined` | Adjusts component size (`xs`, `sm`, `md`, `lg`, `xl`, `xxl`). |
38
98
  | `rules` | `String \| Object` | `""` | VeeValidate rules. |
39
99
  | `containerClass` | `String` | `undefined` | Class on the wrapper `<div>`. |
40
- | `width` | `String \| Number` | `undefined` | Explicit width on the wrapper `<div>`. A number is treated as px (e.g. `220` -> `220px`); a string is used as-is (e.g. `"50%"`). |
41
- | `minWidth` | `String \| Number` | `undefined` | `min-width` on the wrapper `<div>` (same number/string rules as `width`). |
42
- | `maxWidth` | `String \| Number` | `undefined` | `max-width` on the wrapper `<div>` (same number/string rules as `width`) — caps growth inside a flex/grid parent without a fixed `width`. |
100
+ | `width` | `String \| Number` | `undefined` | Explicit width on the wrapper `<div>`. A number is treated as px (e.g. `220` -> `220px`); a string is used as-is (e.g. `"50%"`). Applied as `min(<value>, 100%)`, so it shrinks to fit a narrower parent on small screens instead of overflowing it. |
101
+ | `minWidth` | `String \| Number` | `undefined` | `min-width` on the wrapper `<div>` (same number/string rules as `width`, same `min(<value>, 100%)` clamp). |
102
+ | `maxWidth` | `String \| Number` | `undefined` | `max-width` on the wrapper `<div>` (same number/string rules as `width`, same `min(<value>, 100%)` clamp) — caps growth inside a flex/grid parent without a fixed `width`. |
43
103
  | `hints` | `String` | `""` | Helper text beneath the input. |
44
104
  | `prefix` | `String` | `""` | Prefix text/icon (shortcut for `#prefix` slot). |
45
105
  | `postfix` | `String` | `""` | Postfix text/icon. |
@@ -53,6 +113,23 @@ const form = reactive({ email: '' });
53
113
  | `iconPosition` | `String` | `undefined` | Position of the custom `#icon` slot (`"left"`, `"right"`). |
54
114
  | `inputClass` | `String \| Object \| Array` | `undefined` | Additional class(es) for the input element. |
55
115
  | `inputStyle` | `String \| Object \| Array` | `undefined` | Additional style(s) for the input element. |
116
+ | `variant` | `String` | `undefined` | Layout variant. `"stepper"` renders a numeric stepper (forces `type="number"`, shows +/- buttons, enables `min`/`max`/`step` clamping). `"inset"` renders the label inside the bordered field, stacked above the value (border/focus/error styles unchanged; a left `#icon`/`prefix` gets a fixed width so the label aligns — use an icon, not long text). `"outlined"` renders a floating label: shown like a placeholder while empty/unfocused, floats onto the top border when focused or filled, the border is cut away behind it, no label background, RTL supported (real `placeholder` appears on focus; scales with `size`). |
117
+ | `min` | `Number \| String` | `null` | *(stepper variant)* Minimum allowed value. Clamps input and disables the minus button at the floor. |
118
+ | `max` | `Number \| String` | `null` | *(stepper variant)* Maximum allowed value. Clamps input and disables the plus button at the ceiling. |
119
+ | `step` | `Number \| String` | `1` | *(stepper variant)* Increment/decrement amount. |
120
+ | `showButtons` | `Boolean` | `true` | *(stepper variant)* Render the minus/plus controls. |
121
+ | `isBorder` | `Boolean` | `false` | *(stepper variant)* Show inner borders between minus/input/plus. Default is a seamless pill. |
122
+ | `copyable` | `Boolean` | `false` | Renders a plain, borderless postfix button that copies the current value to the clipboard (via the Clipboard API). No built-in confirmation UI — listen for `copy-success`/`copy-error` to show your own (e.g. `useToast()`). Pair with a native `readonly` attribute for a display-only copyable value. |
123
+ | `copySuccessMsg` | `String` | `"Copied to clipboard"` | Message text passed to the `copy-success` event after a successful copy (`copyable` only). Runs through the same translation pipeline as `label`/`hints`. |
124
+ | `copyErrorMsg` | `String` | `"Failed to copy"` | Message text passed to the `copy-error` event when copying fails, e.g. the Clipboard API is unavailable outside a secure context (`copyable` only). |
125
+
126
+ ## Read-only mode
127
+
128
+ Not a dedicated prop — pass the native `readonly` attribute (it falls through to the input via `$attrs`, same as `disabled`). Unlike `disabled`, it renders exactly like a normal enabled field (no greyed-out background), it keeps the value focusable/selectable/copyable, and it's still submitted with the form — use it when the value must not be *changed* but should still look and read like every other field. On `variant="stepper"`, `readonly` also disables the +/- buttons, so the value truly can't change (native `readonly` alone only blocks typing in the number input, not button clicks).
129
+
130
+ ```vue
131
+ <ab-input-field v-model="accountId" name="account_id" label="Account ID" readonly />
132
+ ```
56
133
 
57
134
  ## Events
58
135
 
@@ -63,6 +140,8 @@ const form = reactive({ email: '' });
63
140
  | `keyup` | `KeyboardEvent` | Forwarded native event. |
64
141
  | `keydown` | `KeyboardEvent` | Forwarded native event. |
65
142
  | `paste` | `ClipboardEvent` | Forwarded native event. |
143
+ | `copy-success` | `(message: String, value: String)` | *(`copyable` only)* Fired after a successful clipboard write. `message` is the already-translated `copySuccessMsg` — hand it directly to your own confirmation UI. |
144
+ | `copy-error` | `(message: String, error: Error)` | *(`copyable` only)* Fired when the clipboard write fails (e.g. Clipboard API unavailable outside a secure context). `message` is the already-translated `copyErrorMsg`. |
66
145
 
67
146
  ## Slots
68
147
 
@@ -9,8 +9,8 @@ Full-featured dialog with a built-in VeeValidate `<Form>`, loader overlay, and "
9
9
  <ab-button class="btn-primary" @click="isOpen = true">Open</ab-button>
10
10
 
11
11
  <ab-modal
12
+ v-if="isOpen"
12
13
  ref="modal"
13
- v-model:is-modal-visible="isOpen"
14
14
  modal-size="modal-lg"
15
15
  @on-submit="handleSubmit"
16
16
  @close="isOpen = false"
@@ -47,7 +47,6 @@ async function handleSubmit(values, { resetForm }) {
47
47
 
48
48
  | Name | Type | Default | Description |
49
49
  | --- | --- | --- | --- |
50
- | `isModalVisible` | `Boolean` | — | Visibility state (`v-model:is-modal-visible`). |
51
50
  | `modalSize` | `String` | `""` | Bootstrap size class (`modal-sm`, `modal-lg`, `modal-xl`). |
52
51
  | `modalMsg` | `[String, Object]` | `{}` | Default message for message-only mode. |
53
52
  | `hideHeader` | `Boolean` | `false` | Hide the header section. |
@@ -92,9 +91,13 @@ async function handleSubmit(values, { resetForm }) {
92
91
  | `close()` | Close the modal. |
93
92
  | `clearForm()` | Reset the VeeValidate form and close. |
94
93
  | `returnClear()` | Reset the form without closing. |
95
- | `showMsgOnly(msg, isHideFooter?)` | Switch to message-only mode. |
94
+ | `showMsgOnly(msg, isHideFooter?)` | Switch to message-only mode, replacing the body with `AbResponseMsg` rendering `msg`. `isHideFooter: true` swaps the footer for a single static "Close" button (`close()`) instead of the default/slotted footer — it does **not** auto-dismiss the modal; without user action the modal stays open indefinitely. |
96
95
  | `setMessageOnly(status)` | Toggle message-only mode programmatically. |
97
96
 
97
+ ## v-model
98
+
99
+ None — there is no internal show/hide state. Mount/unmount the whole component with `v-if` (or `v-show`) on a boolean in the parent, as shown in Usage above.
100
+
98
101
  ## Notes
99
102
 
100
103
  Use `AbEasyModal` when you only need a simple trigger-and-close dialog without a form.
@@ -27,9 +27,9 @@ Searchable single/multi/tag select. Custom built-in replacement for `@vueform/mu
27
27
  | `name` | `String` | `""` | VeeValidate field name. |
28
28
  | `rules` | `String \| Object` | `""` | VeeValidate rules. |
29
29
  | `containerClass` | `String` | `undefined` | Wrapper class. |
30
- | `width` | `String \| Number` | `undefined` | Explicit width on the wrapper `<div>`. A number is treated as px (e.g. `220` -> `220px`); a string is used as-is (e.g. `"50%"`). |
31
- | `minWidth` | `String \| Number` | `undefined` | `min-width` on the wrapper `<div>` (same number/string rules as `width`). |
32
- | `maxWidth` | `String \| Number` | `undefined` | `max-width` on the wrapper `<div>` (same number/string rules as `width`) — caps growth inside a flex/grid parent without a fixed `width`. |
30
+ | `width` | `String \| Number` | `undefined` | Explicit width on the wrapper `<div>`. A number is treated as px (e.g. `220` -> `220px`); a string is used as-is (e.g. `"50%"`). Applied as `min(<value>, 100%)`, so it shrinks to fit a narrower parent on small screens instead of overflowing it. |
31
+ | `minWidth` | `String \| Number` | `undefined` | `min-width` on the wrapper `<div>` (same number/string rules as `width`, same `min(<value>, 100%)` clamp). |
32
+ | `maxWidth` | `String \| Number` | `undefined` | `max-width` on the wrapper `<div>` (same number/string rules as `width`, same `min(<value>, 100%)` clamp) — caps growth inside a flex/grid parent without a fixed `width`. |
33
33
  | `hints` | `String` | `""` | Helper text. |
34
34
  | `multiple` | `Boolean` | `false` | Deprecated — use `mode` instead. |
35
35
  | `searchable` | `Boolean` | `false` | Enable option search/filter. |
@@ -1,14 +1,18 @@
1
1
  # AbNumberField — AI Reference
2
2
 
3
- Numeric input with minus/plus buttons, min/max clamping, custom step, and VeeValidate integration. Renders as a seamless pill by default; pass `is-border` to show internal dividers between buttons and input.
3
+ Numeric input with VeeValidate integration. By default it renders a plain native `type="number"` input; pass `variant="stepper"` to turn it into a stepper with minus/plus buttons, min/max clamping, and a custom step. `AbNumberField` is a thin wrapper around [`AbInputField`](./ai_ref_AbInputField.md) — `<ab-number-field variant="stepper" ... />` and `<ab-input-field variant="stepper" type="number" ... />` render identically. Renders the stepper as a seamless pill by default; pass `is-border` to show internal dividers between buttons and input.
4
4
 
5
5
  ## Usage
6
6
 
7
7
  ```vue
8
8
  <template>
9
- <ab-number-field v-model="qty" name="quantity" label="Quantity" :min="1" :max="10" :step="1" hints="Clamped between 1 and 10" />
9
+ <!-- Default: plain number input -->
10
+ <ab-number-field v-model="qty" name="quantity" label="Quantity" />
10
11
 
11
- <ab-number-field v-model="qty" name="quantity" label="Quantity" is-border />
12
+ <!-- Stepper: minus/plus buttons with min/max clamping -->
13
+ <ab-number-field v-model="qty" name="quantity" label="Quantity" variant="stepper" :min="1" :max="10" :step="1" hints="Clamped between 1 and 10" />
14
+
15
+ <ab-number-field v-model="qty" name="quantity" label="Quantity" variant="stepper" is-border />
12
16
  </template>
13
17
 
14
18
  <script setup>
@@ -30,12 +34,14 @@ const qty = ref(1);
30
34
  | `hints` | `String` | `""` | Helper text beneath the input. |
31
35
  | `prefix` | `String` | `""` | Prefix text/icon (shortcut for `#prefix` slot). |
32
36
  | `postfix` | `String` | `""` | Postfix text/icon. |
33
- | `min` | `Number \| String` | `null` | Minimum allowed value. Clamps input and disables the minus button at the floor. |
34
- | `max` | `Number \| String` | `null` | Maximum allowed value. Clamps input and disables the plus button at the ceiling. |
35
- | `step` | `Number \| String` | `1` | Increment/decrement amount. |
36
- | `showButtons` | `Boolean` | `true` | Render the minus/plus controls. |
37
+ | `variant` | `String` | `undefined` | Pass `"stepper"` to render minus/plus buttons with min/max/step clamping. Left unset, renders a plain native `type="number"` input with no buttons or clamping (min/max/step are still applied as native input attributes). |
38
+ | `min` | `Number \| String` | `null` | Minimum allowed value. With `variant="stepper"`, clamps the typed value and disables the minus button at the floor; otherwise it's only the native `min` attribute. |
39
+ | `max` | `Number \| String` | `null` | Maximum allowed value. With `variant="stepper"`, clamps the typed value and disables the plus button at the ceiling; otherwise it's only the native `max` attribute. |
40
+ | `step` | `Number \| String` | `1` | Increment/decrement amount (`variant="stepper"`), or the native `step` attribute otherwise. |
41
+ | `showButtons` | `Boolean` | `true` | Render the minus/plus controls. Only applies when `variant="stepper"`. |
37
42
  | `disabled` | `Boolean` | `false` | Disable the field and both buttons. |
38
- | `isBorder` | `Boolean` | `false` | Show inner borders between minus/input/plus. Default is a seamless pill. |
43
+ | `readonly` | `Boolean` | `false` | Make the field read-only and disable both buttons (`variant="stepper"`). Renders like a normal enabled field (no greyed-out background), unlike `disabled`. |
44
+ | `isBorder` | `Boolean` | `false` | Show inner borders between minus/input/plus. Default is a seamless pill. Only applies when `variant="stepper"`. |
39
45
  | `isLeftIconBorder` | `Boolean` | `false` | Render border between the prefix icon and the input. |
40
46
  | `isRightIconBorder` | `Boolean` | `false` | Render border between the postfix icon and the input. |
41
47
  | `size` | `String` | `undefined` | Input size: `"xs"`, `"sm"`, `"md"`, `"lg"`, `"xl"`. Falls back to `globalConfig.size`, then `"md"`. |
@@ -44,8 +50,8 @@ const qty = ref(1);
44
50
 
45
51
  | Event | Payload | Description |
46
52
  | --- | --- | --- |
47
- | `update:modelValue` | `number \| null` | v-model sync. Value is `number` after clamping, or `null` when cleared. |
48
- | `input` | `number` | Fired on input after clamping. |
53
+ | `update:modelValue` | `number \| null` | v-model sync. With `variant="stepper"`, value is `number` after clamping, or `null` when cleared. |
54
+ | `input` | `number` | Fired on input. With `variant="stepper"`, fired after clamping. |
49
55
  | `keyup` | `KeyboardEvent` | Forwarded native event. |
50
56
  | `keydown` | `KeyboardEvent` | Forwarded native event. |
51
57
  | `paste` | `ClipboardEvent` | Forwarded native event. |
@@ -55,6 +61,10 @@ const qty = ref(1);
55
61
  | Name | Description |
56
62
  | --- | --- |
57
63
  | `label` | Custom label content. |
58
- | `prefix` | Prefix icon / text (rendered before the minus button). |
59
- | `postfix` | Postfix icon / text (rendered after the plus button). |
64
+ | `prefix` | Prefix icon / text (rendered before the minus button when `variant="stepper"`). |
65
+ | `postfix` | Postfix icon / text (rendered after the plus button when `variant="stepper"`). |
60
66
  | `hints` | Helper text (overrides `hints` prop). |
67
+
68
+ ## v-model
69
+
70
+ Yes — `modelValue` + `update:modelValue`. Nullable; with `variant="stepper"` the value is clamped to `min`/`max` and coerced to `number` (or `null` when the field is cleared).
@@ -2,16 +2,25 @@
2
2
 
3
3
  Parent container for tabbed interfaces. Pair with `AbTab` children. Supports content-switching mode (default) and router-link mode for navigation-driven tabs.
4
4
 
5
+ **Default variation is `outline`** — when asked to generate `AbTabs` with no specific style requested, generate it as-is (don't pass `variation` at all) and it renders with the outlined design.
6
+
5
7
  ## Usage
6
8
 
7
9
  ```vue
8
10
  <template>
9
- <ab-tabs nav-class="nav-pills">
11
+ <!-- Default: outline variation (no props needed) -->
12
+ <ab-tabs>
10
13
  <ab-tab title="Overview" selected>Overview content</ab-tab>
11
14
  <ab-tab title="Billing">Billing content</ab-tab>
12
15
  <ab-tab title="Security">Security content</ab-tab>
13
16
  </ab-tabs>
14
17
 
18
+ <!-- Other variation -->
19
+ <ab-tabs variation="pills">
20
+ <ab-tab title="Overview" selected>Overview content</ab-tab>
21
+ <ab-tab title="Billing">Billing content</ab-tab>
22
+ </ab-tabs>
23
+
15
24
  <ab-tabs is-route-mode>
16
25
  <ab-tab title="Users" :to="{ name: 'users' }" />
17
26
  <ab-tab title="Teams" :to="{ name: 'teams' }" />
@@ -26,7 +35,8 @@ Parent container for tabbed interfaces. Pair with `AbTab` children. Supports con
26
35
  | `tabContainerClass` | `String` | — | Root wrapper class. |
27
36
  | `navContainerClass` | `String` | — | Wrapper around the nav list. |
28
37
  | `tabDetailsClass` | `String` | — | Wrapper around the tab content. |
29
- | `navClass` | `String` | `"nav-pills"` | Class on the nav list itself (e.g. `nav-tabs`, `nav-pills`). |
38
+ | `variation` | `String` | `"outline"` | Design variation of the nav. Generates a `nav-{variation}` class automatically — e.g. `"outline"` → `nav-outline`, `"pills"` → `nav-pills`, `"underline"` → `nav-underline`, `"bg-secondary"` → `nav-bg-secondary`. Pass `""` to skip auto-generation and rely on `navClass` only. |
39
+ | `navClass` | `String` | `""` | Extra classes on the nav list, merged after the `variation` class — use for size (`nav-xs`/`nav-md`/`nav-lg`/`nav-xl`), layout (`nav-fill`, `nav-vertical`), or color (`nav-secondary`) modifiers. |
30
40
  | `tabClass` | `String` | `""` | Class on each individual tab link. |
31
41
  | `isRouteMode` | `Boolean` | `false` | When `true`, tab clicks perform router navigation. |
32
42
 
@@ -25,6 +25,9 @@ Accessible switch/toggle control. Supports title + description layout and custom
25
25
  | `size` | `String` | `"md"` | Toggle size. |
26
26
  | `trueValue` | `any` | `"Y"` | Value when checked. |
27
27
  | `falseValue` | `any` | `"N"` | Value when unchecked. |
28
+ | `hasIcon` | `Boolean` | `false` | Renders default enable (check) and disable (cross) icons inside switch track. |
29
+ | `onIcon` | `Component \| String` | `Check` | Custom icon component or CSS class for enabled state. |
30
+ | `offIcon` | `Component \| String` | `X` | Custom icon component or CSS class for disabled state. |
28
31
  | `title` | `String \| null` | `null` | Title next to the toggle. |
29
32
  | `description` | `String \| null` | `null` | Description under the title. |
30
33
  | `hints` | `String \| null` | `null` | Helper text. |
@@ -43,4 +46,6 @@ Accessible switch/toggle control. Supports title + description layout and custom
43
46
  | `title` | Custom title. |
44
47
  | `description` | Custom description. |
45
48
  | `descriptionContainer` | Wraps the entire title + description block. |
49
+ | `on-icon` | Custom icon for enabled state inside track. |
50
+ | `off-icon` | Custom icon for disabled state inside track. |
46
51
  | `hints` | Custom hints. |
@@ -0,0 +1,212 @@
1
+ # EliteGrid — AI Reference
2
+
3
+ > **External dependency, not developed in this library.** `EliteGrid` ships from the separate `@appsbd/vue3-elite-grid` package (peer dependency, `>=2.0.4`) — it is only *imported* and demonstrated here (`playground/components/EliteGridTest.vue`), not authored or maintained in this repo's `src/`. Prefer an `Ab*` component when one covers the need (see `AbTable` for a simpler, in-library data grid); reach for `EliteGrid` only for the advanced data-grid features below (grouping, subrows, card view, column chooser) that `AbTable` doesn't have.
4
+ >
5
+ > This file mirrors the package's own docs at <https://apps.inotise.com/projects/elite-grid-docs/> (EliteGrid v2.0) — re-check that site if behavior here seems stale, since this library does not control its release cycle.
6
+
7
+ A feature-rich, CSS-variable-themeable data grid: pagination, column sorting, row grouping, row checkboxes with selection tracking, an actions column, card-view responsive layout, and recursive subrow (tree-grid) expansion.
8
+
9
+ ## Installation
10
+
11
+ ```bash
12
+ npm install @appsbd/vue3-elite-grid
13
+ npm install vue3-gettext shvl
14
+ ```
15
+
16
+ Requires `vue3-gettext` and `shvl` as its own peer dependencies (separate from this library's). Import the CSS once (already done for you if only using it via this library's playground):
17
+
18
+ ```js
19
+ import EliteGrid from '@appsbd/vue3-elite-grid';
20
+ import '@appsbd/vue3-elite-grid/dist/elite-grid.css';
21
+ // or, per the package's own docs: '@appsbd/vue3-elite-grid/elite-grid.css'
22
+ ```
23
+
24
+ It is **not** globally registered by this library's `install()` — import and use it (or its `EliteColumnModel` helper) directly per-component, as `EliteGridTest.vue` does.
25
+
26
+ ## Usage
27
+
28
+ ```vue
29
+ <template>
30
+ <elite-grid
31
+ :grid-data="gridData"
32
+ :columns="columns"
33
+ :is-show-row-index-column="true"
34
+ :is-show-row-checkbox="true"
35
+ selected-prop="email"
36
+ :show-action-column="true"
37
+ action-width="100px"
38
+ @loadData="loadData"
39
+ @selectionChange="handleSelection"
40
+ >
41
+ <template #actionProperty="{ rowitem }">
42
+ <ab-button class="btn-xs btn-primary" @click="edit(rowitem)">Edit</ab-button>
43
+ </template>
44
+ </elite-grid>
45
+ </template>
46
+
47
+ <script setup>
48
+ import { ref } from 'vue';
49
+ import EliteGrid, { EliteColumnModel } from '@appsbd/vue3-elite-grid';
50
+ import '@appsbd/vue3-elite-grid/dist/elite-grid.css';
51
+
52
+ const gridData = ref({ page: 1, total: 1, records: 0, limit: 10, rowdata: [] });
53
+
54
+ const columns = [
55
+ EliteColumnModel.getColumn({ name: 'name', title: 'Name' }),
56
+ EliteColumnModel.getColumn({ name: 'email', title: 'Email', align: 'center' }),
57
+ ];
58
+
59
+ function loadData({ page, limit }) {
60
+ // fetch page/limit worth of rows, then assign to gridData.value.rowdata (+ total/records)
61
+ }
62
+
63
+ function handleSelection(selectedRows) {
64
+ // selectedRows: values of each selected row's `selected-prop` field (or full row objects if unset)
65
+ }
66
+ </script>
67
+ ```
68
+
69
+ Grouping (any column with `is_group_by: true`) and recursive subtrees (`subrow-prop` + `subrow-icon-column`) are opt-in via column config / props — see the three worked variants in `playground/components/EliteGridTest.vue` (Default, Group By, Subtree).
70
+
71
+ ## Props
72
+
73
+ | Name | Type | Default | Description |
74
+ | --- | --- | --- | --- |
75
+ | `gridData` | `Object` | `{ page:1, total:1, records:0, limit:0, rowdata:[] }` | Main data object: pagination state + row array. |
76
+ | `columns` | `Array` | `[]` | Column definitions, each built via `EliteColumnModel.getColumn()` (see below). |
77
+ | `showHeader` | `Boolean` | `false` | Show the grid header slot area (`#slot-header`). |
78
+ | `isRounded` | `Boolean` | `true` | Rounded corners on the grid container. |
79
+ | `showLoader` | `Boolean` | `false` | Show a loading overlay (content via `#slot-loader`). |
80
+ | `showActionColumn` | `Boolean` | `false` | Show a rightmost actions column. |
81
+ | `actionTitle` | `String` | `"Action"` | Actions column header text. |
82
+ | `actionWidth` | `String` | `""` | CSS width for the actions column. |
83
+ | `isShowRowIndexColumn` | `Boolean` | `true` | Show the row index column (1, 2, 3…). |
84
+ | `isShowRowCheckbox` | `Boolean` | `false` | Show a checkbox selection column. |
85
+ | `checkboxClass` | `String` | `""` | CSS class applied to every checkbox. |
86
+ | `selectedProp` | `String` | `""` | Row property used as each selected row's value (e.g. `"id"`); omit to get full row objects in `selectionChange`. |
87
+ | `getRowClass` | `Function` | `(row, i) => ""` | Returns a CSS class string per row. |
88
+ | `oddColor` | `String` | `"inherit"` | Odd row background color. |
89
+ | `evenColor` | `String` | `"inherit"` | Even row background color. |
90
+ | `hoverColor` | `String` | `"#fbfbfb"` | Row hover background color. |
91
+ | `hidePagination` | `Boolean` | `false` | Hide the entire pagination bar. |
92
+ | `hidePageList` | `Boolean` | `false` | Hide the page-number buttons. |
93
+ | `hideRecordInfo` | `Boolean` | `false` | Hide the "Viewing X to Y of Z" text. |
94
+ | `hideLimitSelector` | `Boolean` | `false` | Hide the rows-per-page dropdown. |
95
+ | `limitList` | `Array` | `[10,20,50,100,200]` | Rows-per-page options. |
96
+ | `paginationLength` | `Number` | `5` | Max page-number buttons shown at once. |
97
+ | `paginationPosition` | `String` | `"right"` | `"right"` or `"left"` alignment for the pagination bar. |
98
+ | `isGroupSeparateHead` | `Boolean` | `false` | Repeat the header row inside every group (used with `is_group_by`). |
99
+ | `isCardView` | `Boolean` | `false` | Render rows as cards instead of a table. |
100
+ | `cardColumn` | `Number \| Array` | `3` | Card columns; an array is responsive `[xs,sm,md,lg,xl,xxl]`. |
101
+ | `cardItemBorderRadius` | `String` | — | Border radius for card items (card view only; e.g. `"8px"`). |
102
+ | `cardItemGap` | `String` | — | Gap between card items (card view only; e.g. `"20px"`). |
103
+ | `subrowProp` | `String` | `""` | Row property holding a nested children array (enables tree-grid subrows). |
104
+ | `subrowIconColumn` | `String` | `""` | Column name that renders the expand/collapse chevron icon. |
105
+ | `subrowDefaultExpanded` | `Boolean` | `false` | Expand every subrow by default, globally. |
106
+ | `subrowExpandProp` | `String` | — | Row property to read/persist a row's own expanded state (per-row override of the global default). |
107
+
108
+ ## Events
109
+
110
+ | Event | Payload | Description |
111
+ | --- | --- | --- |
112
+ | `loadData` | `{ page, limit, sort_prop, sort_ord }` | Fired when new data is needed — pagination change or column sort. Fetch that page/limit (and apply sort) and assign the result into `gridData`. |
113
+ | `columnStatusChange` | `col` (column object) | Fired when a column's visibility is toggled via the built-in column-chooser (⚙ icon in the index header). |
114
+ | `selectionChange` | `selectedRows` (Array) | Fired when checkbox selection changes — array of `selectedProp` values, or full row objects if `selectedProp` is unset. |
115
+
116
+ ## Slots
117
+
118
+ | Name | Scope | Description |
119
+ | --- | --- | --- |
120
+ | `#slot{col.name}` (e.g. `#slotEmail`) | `{ rowitem, index, col, val }` | Custom cell renderer for a given column. |
121
+ | `#header-{col.name}` | `{ col }` | Custom header cell for a given column. |
122
+ | `#actionProperty` | `{ rowitem, index }` | Actions column content per row (only with `show-action-column`). |
123
+ | `#slot-header` | — | Grid header/toolbar area (requires `show-header`). |
124
+ | `#slot-loader` | — | Custom loading-overlay content (requires `show-loader`). |
125
+ | `#slot-no-record` | — | Custom "no records" message. |
126
+ | `#subrow-icon` | `{ isExpanded, rowitem }` | Custom expand/collapse icon (with `subrow-prop`). |
127
+ | `#groupTitle` | `{ groupitem }` | Custom group header content (with `is_group_by` columns). |
128
+ | `#eg_pg-status` | `{ startRecord, endRecord, totalRecord }` | Custom pagination status text (replaces "Viewing X to Y of Z"). |
129
+ | `#card-item` | `{ item, itemColumns }` | Override an entire card (card view only). |
130
+
131
+ Column/header slots also receive a deeply-injected **`toggle-subrow()`** method and **`is-subrow-expanded`** prop, letting you build your own manual expand/collapse trigger inside any column slot instead of relying on `subrow-icon-column`.
132
+
133
+ ## Column Model — `EliteColumnModel.getColumn(options)`
134
+
135
+ ```js
136
+ import { EliteColumnModel } from '@appsbd/vue3-elite-grid';
137
+
138
+ const columns = [
139
+ EliteColumnModel.getColumn({ name: 'id', title: 'ID', width: '50px' }),
140
+ EliteColumnModel.getColumn({ name: 'name', title: 'Name', is_sortable: true }),
141
+ EliteColumnModel.getColumn({ name: 'address.city', title: 'City', is_group_by: true }),
142
+ ];
143
+ ```
144
+
145
+ | Property | Type | Default | Description |
146
+ | --- | --- | --- | --- |
147
+ | `name` | `String` | `""` | Data key; supports dot notation for nested values (e.g. `"address.city"`). |
148
+ | `title` | `String` | `""` | Column header label. |
149
+ | `align` | `String` | `"left"` | Cell content alignment: `"left"`, `"center"`, `"right"`. |
150
+ | `title_align` | `String` | `"left"` | Header alignment: `"left"`, `"center"`, `"right"`. |
151
+ | `width` | `String` | `null` | CSS width (e.g. `"50px"`, `"20%"`). |
152
+ | `hidden_in` | `Array` | `[]` | Hide this column at these breakpoints: `["xs","sm","md","lg","xl","xxl"]`. |
153
+ | `default_show` | `Boolean` | `true` | Visible by default; toggleable later via the built-in column chooser. |
154
+ | `is_sortable` | `Boolean` | `false` | Enable click-to-sort on this column's header (cycles asc → desc → none, fires `loadData` with `sort_prop`/`sort_ord`). |
155
+ | `sort_order` | `String` | `"asc"` | Initial sort direction: `"asc"` or `"desc"`. |
156
+ | `is_group_by` | `Boolean` | `false` | Use this column as the row-grouping key (pairs with `is-group-separate-head`). |
157
+ | `tooltip` | `String` | `""` | Tooltip text shown next to the column header. |
158
+
159
+ ## Exposed methods
160
+
161
+ | Method | Description |
162
+ | --- | --- |
163
+ | `clearSelection()` | Clears the current checkbox selection. Call via a template ref, e.g. `this.$refs.myGrid.clearSelection()`. |
164
+
165
+ ## CSS Variables
166
+
167
+ Theme the grid by overriding these on `:root` or a parent wrapper — no `Ab*` design tokens apply here, it's a fully separate `--eg-*` variable set.
168
+
169
+ | Variable | Default | Category |
170
+ | --- | --- | --- |
171
+ | `--eg-font-family` | `Inter, sans-serif, Arial` | Typography |
172
+ | `--eg-font-style` | `normal` | Typography |
173
+ | `--eg-font-weight` | `500` | Typography |
174
+ | `--eg-font-size` | `12px` | Typography |
175
+ | `--eg-margin` | `0px -7px` | Typography |
176
+ | `--eg-bg` | `#fff` | Backgrounds |
177
+ | `--eg-hover-bg` | `#fbfbfb` | Backgrounds |
178
+ | `--eg-loader-bg` | `rgba(0, 0, 0, 0.65)` | Backgrounds |
179
+ | `--eg-row-odd-color` | `inherit` | Backgrounds |
180
+ | `--eg-row-even-color` | `inherit` | Backgrounds |
181
+ | `--eg-primary-color` | `#3b82f6` | Core Colors |
182
+ | `--eg-text-color` | `#374151` | Core Colors |
183
+ | `--eg-no-record-color` | `#cf0c0c` | Core Colors |
184
+ | `--eg-cell-header-color` | `#f9fafc` | Table |
185
+ | `--eg-row-group-title-color` | `#41444b` | Table |
186
+ | `--eg-cell-index-color` | `#7f848d` | Table |
187
+ | `--eg-header-tooltip` | `#9f641b` | Table |
188
+ | `--eg-cell-padding` | `5px` | Table |
189
+ | `--eg-border-radius` | `5px` | Borders |
190
+ | `--eg-table-border-color` | `#e2e8f0` | Borders |
191
+ | `--eg-pg-border-color` | `#ccc` | Pagination |
192
+ | `--eg-pg-btn-bg` | `#3e44cc` | Pagination |
193
+ | `--eg-pg-btn-color` | `#fff` | Pagination |
194
+ | `--eg-pg-shodow-color` | `#ccc` | Pagination |
195
+ | `--eg-pg-btn-action-size` | `40px` | Pagination |
196
+ | `--eg-pg-btn-size` | `30px` | Pagination |
197
+ | `--eg-pg-margin-top` | `10px` | Pagination |
198
+ | `--eg-pg-margin-bottom` | `10px` | Pagination |
199
+ | `--eg-shodow-color` | `#3e3e3e` | Shadows |
200
+ | `--eg-shodow-rule` | `0px 3px 10px -7px var(--eg-shodow-color)` | Shadows |
201
+ | `--eg-dropdown-bg` | `#ffffff` | Popovers |
202
+ | `--eg-tooltip-bg` | `#333333` | Popovers |
203
+ | `--eg-tooltip-color` | `#ffffff` | Popovers |
204
+ | `--eg-card-column` | `3` | Card View |
205
+ | `--eg-card-column-radius` | `5px` | Card View |
206
+ | `--eg-card-column-gap` | `15px` | Card View |
207
+ | `--eg-card-container-margin` | `15px` | Card View |
208
+
209
+ ## Notes
210
+
211
+ - Full worked examples (default table, `is_group_by` grouping, recursive `subrow-prop` subtree) live in `playground/components/EliteGridTest.vue`, importing `EliteGrid` and `EliteColumnModel` directly from `@appsbd/vue3-elite-grid`.
212
+ - Since this is an external package, its own version/CHANGELOG governs its API — this library's `CHANGELOG.md` only tracks changes to `Ab*` components and this playground's example usage, not `@appsbd/vue3-elite-grid` itself.
@@ -12,14 +12,28 @@
12
12
 
13
13
  ---
14
14
 
15
- ## [Unreleased]
15
+ ## [1.0.19] - 2026-09-14
16
16
 
17
17
  ### ⚠️ Action Required — Breaking
18
18
 
19
+ - **`AbTabs`'s default nav design changed from pills to outline; `navClass` default changed from `"nav-pills"` to `""`.**
20
+ - Find: `<AbTabs ...>` usages with **no** `nav-class`/`navClass` prop at all (relying on the old implicit `"nav-pills"` default).
21
+ - Replace: add `variation="pills"` to keep the previous pill-shaped appearance. No change needed if the outline look is acceptable, or if `nav-class`/`navClass` was already being passed explicitly (that keeps working unchanged, though switching it to the new `variation` prop — e.g. `nav-class="nav-pills nav-xs"` → `variation="pills" nav-class="nav-xs"` — is recommended, see below).
22
+ - See [`.ai/ai_ref_AbTabs.md`](./ai_ref_AbTabs.md) for the full `variation` → class mapping.
23
+
24
+ - **`AbModal`'s `isModalVisible` prop removed.**
25
+ - Find: `isModalVisible` / `is-modal-visible` / `v-model:is-modal-visible` on `<AbModal>`.
26
+ - Replace: nothing to migrate functionally — the prop never actually controlled visibility (it was dead/unwired). Control visibility the way every existing usage in this library already does: wrap `<AbModal>` itself in `v-if="isOpen"` in the parent. Just remove the prop/binding if present.
27
+
19
28
  - **`AbFilterPanel`'s standard-search and advance-search field layouts switched from a Bootstrap `.row`/`.col-*` grid to plain flex; `advanceContainerClass` default changed from `"col-6"` to `""`.**
20
29
  - Find: `<AbFilterPanel ...>` usages in either default (standard) mode or `is-advance`/`isAdvance` mode that relied on the Bootstrap grid for field widths — either the default 2-columns-per-row layout, or an explicit `advance-container-class`/`advanceContainerClass` Bootstrap grid class (e.g. `"col-12 col-sm-6"`).
21
30
  - Replace: nothing required to keep working — fields now lay out via flex (`flex: 1 1 240px`, wraps automatically), which also fixes long field labels getting clipped. A Bootstrap grid class passed to `advance-container-class` no longer has a `.row` ancestor to size against and should be replaced with either a plain width class/CSS, or the new `width` prop on the individual field component (`AbInputField`/`AbMultiSelect`/`AbDateTimePicker`) inside `filterOptions`.
22
31
 
32
+ - **`AbNumberField` no longer always renders as a stepper — default changed to a plain `type="number"` input.**
33
+ - Find: `<AbNumberField ...>` usages with no `variant` prop (relying on the old implicit always-stepper look: minus/plus buttons, min/max clamping).
34
+ - Replace: add `variant="stepper"` to keep the previous appearance and behavior exactly. No change needed if the plain-input look is acceptable.
35
+ - See [`.ai/ai_ref_AbNumberField.md`](./ai_ref_AbNumberField.md) for the full `variant` prop.
36
+
23
37
  ### ℹ️ Also shipped (no action needed)
24
38
 
25
39
  - `AbFilterPanel`'s `.apbd-filter-chip-group` (quick-advance multi-select popover trigger) height fixed to match input-field heights at every size (`xs`…`xxl`), and no longer changes height (grows or shrinks) once a tag is selected — a `min-height` per size acts as a floor while still allowing the box to grow when tags wrap onto a second line.